CLI

Commands, options, scenario naming, timeouts, and exit codes.

Commands

CommandPurpose
granttrace initExplicitly create private ignored local state
granttrace doctorDiagnose local and optional live prerequisites safely
granttrace doctor --repairRemove only a lock that is proven stale and safe
granttrace record NAME -- COMMANDAuto-initialize, record one scenario, and review its diff
granttrace scenario listList local scenario recordings
granttrace scenario remove NAMERemove one local recording
granttrace checkCompare all recordings with the accepted contract
granttrace check --acceptAtomically accept the reviewed contract
granttrace frontier listList complete nondominated permission assignments
granttrace frontier select NUMBERExplicitly choose one listed assignment
granttrace keep add/remove/listManage reasoned, unproven access
granttrace prove NAME -- COMMANDProve one accepted scenario live
granttrace analyze OBSERVATIONSInspect one lower-level NDJSON file

Run granttrace <command> --help for its complete usage.

record

granttrace record [--no-review] <name> [--timeout 15m] -- <command> [args...]

The scenario name is 1–64 lowercase letters, numbers, hyphens, or underscores. It cannot begin or end with a hyphen or underscore. The legacy --scenario <name> spelling remains compatible.

Durations accept ms, s, or m; the effective minimum is one second. Recording defaults to 15 minutes and allows at most 60 minutes.

The child command is passed as an argument array with shell: false. Its output streams to the terminal and is not retained. On first use, recording creates private .granttrace/ state and adds it to .gitignore before launching the child. Unsafe, symlinked, or stale existing state still blocks execution instead of being silently repaired.

For the standard Node.js path, GrantTrace injects a preload that observes supported GitHub REST requests through global fetch, including requests from standard Octokit clients. Automatic capture observes only the exact https://api.github.com origin and ignores off-origin responses even when they include a permission-like header. Custom fetch implementations, transports, unusual runtimes, and subprocesses that discard the injected Node options need the explicit fallback.

After a successful recording, record calculates and prints the aggregate contract diff. If the diff is safe and stdin and stdout are interactive TTYs, it asks for explicit acceptance with a default of no. In noninteractive environments it never prompts or accepts; a semantic change exits 6.

--no-review saves the recording without calculating the aggregate diff. It exists for multi-scenario automation, where each intermediate aggregate is intentionally incomplete. Use it only when a later granttrace check is guaranteed.

check

granttrace check [--accept] [--format <text|json|markdown>]
                 [--github-step-summary]
                 [--observations <path>] [--lock <path>]

Without --accept, the command never changes the contract. The path flags are advanced overrides for inspecting a different observation directory or contract file.

--format text is the default and preserves the human-readable terminal report. --format json emits a versioned JSON object, and --format markdown emits a PR-ready Markdown report. Structured reports are written to stdout for every completed check, including nonzero review and evidence outcomes. The process exit code remains authoritative and has the same meaning in every format.

The JSON object has schemaVersion: 1, a stable status, exitCode, an enumerated reason or null, aggregate summary counts, observed permissions, manual keeps without their free-form reasons, the mandatory baseline, a categorized changes object, safe findings, and migration identifiers. Arrays use the deterministic contract and diff ordering. JSON and Markdown deliberately omit scenario names, repository and resource identities, raw URLs, commands, credentials, manual reasons, paths, and raw error messages. Permission identifiers are required machine-readable contract data and are not redacted, including syntactically valid user-supplied manual-keep keys. Use real GitHub permission names and never encode an identity, tenant, repository, path, or secret in a permission name. Only templates recognized by the pinned catalog are included; any other route template is represented as null in JSON and an em dash in Markdown.

--github-step-summary explicitly opts in to appending the same safe Markdown report to the file named by GITHUB_STEP_SUMMARY. It works only when GITHUB_ACTIONS=true and that runner-provided value is an absolute path to an existing, bounded regular file with no symlink or hard-link ambiguity. The path may live on GitHub's separate /github/file_commands container mount and is not required to be under RUNNER_TEMP. GrantTrace does not create, truncate, or follow links for this file. Terminal output still uses the selected --format; the summary is an additional destination. A missing or unsafe summary target is an analysis failure and exits 5 without following or modifying that target.

Contract acceptance, frontier selection, and manual-keep mutation are refused when CI is enabled or GITHUB_ACTIONS=true, before acquiring the mutation lock or writing. Review and update contracts from a trusted local checkout.

Removing a recording is only a local change. The next check presents the scenario, route, attribution, and permission contraction for review. Removing the final recording can therefore produce a deterministic zero-scenario v3 contract after check --accept; validated manual keeps remain until explicitly removed.

An accepted nondefault frontier selection is preserved when it is still a complete member of the recomputed frontier. If evidence makes it invalid, the deterministic default is proposed through the usual exit-6 review. Only an explicit interactive confirmation or check --accept persists that proposal.

frontier

granttrace frontier list
granttrace frontier select <number>

list is read-only and marks the current selection. select accepts only a numbered complete assignment already stored in permissionFrontier; partial assignments and arbitrary permission text are not accepted. It changes selectedPermissions atomically without changing the evidence or frontier. Review and commit the resulting granttrace.lock.json diff. A selection that would duplicate a manual keep is blocked rather than removing the keep. Selection is blocked until any v1/v2 contract migration is accepted as v3, and it is refused in CI. list remains available because it does not mutate.

keep

granttrace keep add <permission>:<read|write> --reason <text>
granttrace keep remove <permission>
granttrace keep list

Manual-keep reasons are validated review text and are omitted from structured check output. Permission keys are emitted, so use real GitHub permission names and never put identities or secrets in the key.

prove

granttrace prove <name> [--timeout 15m] -- <command> [args...]

The scenario must exist in an accepted schema-v3 contract. See live proof. Proof requires initialized safe local state and complete live setup. It defaults to 15 minutes and allows at most 30 minutes; durations accept ms, s, or m with an effective one-second minimum.

doctor

FAIL blocks local execution. WARN means an attempted optional live setup is incomplete or unsafe but does not block offline record/check work. INFO means optional live proof is not configured. Stale sessions are a FAIL: inspect the artifact and any possible mutation residue before removing it and retrying.

doctor --repair is intentionally narrow. It removes an owned, private operation lock only when its recorded process no longer exists. It can also remove an otherwise empty lock after it has remained unchanged for at least one hour, covering a crash between directory creation and owner-record creation. Live processes, unverifiable process states, malformed owner records, young empty locks, symlinks, unsafe modes, and unexpected files are never removed.

Exit codes

ExitMeaning
0Success
2Invalid command usage
3No supported operation was observed
4Child test failed, timed out, or could not start
5Invalid or unsafe artifact, configuration failure, or analysis failure
6Contract review or schema migration required
7Unknown, unsupported, malformed, or contradictory evidence
8Live proof, negative-control, or cleanup failure
130Child interrupted by a terminal signal

The default CLI output is stable plain text without ANSI color. JSON and Markdown check output are also deterministic and contain no ANSI styling.