Dry-run vs apply
Every write verb defaults to dry-run. --apply is opt-in and
requires authentication. The dry-run output is byte-identical to
what apply would emit — the only difference is whether a PR is opened.
The default
Section titled “The default”$ petrova open_decision kahn-hq --input /tmp/decision.jsonopen_decision → dry_run 4f8c3e21d97a upholds: MR-4, MR-7 branch: petrova/open-decision/phase-7-close create docs/decisions/2026-04-29-phase-7-close.md (1842 B)No PR has been opened. No GitHub API call has been made. The output shows exactly what the apply would produce: branch name, file paths, sizes, idempotency key.
Switching to apply
Section titled “Switching to apply”$ petrova open_decision kahn-hq --input /tmp/decision.json --applyopen_decision → applied 4f8c3e21d97a PR #1234 petrova/open-decision/phase-7-close https://github.com/.../pull/1234The --apply flag is mandatory. There is no “always apply” config
flag and no way to set it via input JSON — it must be on the command
line every invocation. This is intentional: apply is irreversible
relative to the GitHub side (you’d have to close the PR), so the
explicit flag forces a moment of attention.
Auth requirement
Section titled “Auth requirement”--apply errors out before any GitHub call if neither
PETROVA_GITHUB_TOKEN nor the GitHub App env triple is set:
error: AUTH_MISSING — set PETROVA_GITHUB_TOKEN before --applyDry-run runs without auth. This means a fleet (or human) can compose and validate a verb invocation entirely offline, then only set credentials when ready to emit.
The byte-identical guarantee
Section titled “The byte-identical guarantee”The output of dry-run mode and apply mode are produced from the same internal pipeline. Apply additionally hits GitHub; it does not re-derive the diff. So:
- The
branchyou see in dry-run is the branch that will be created. - The
files[]list is the exact set of paths and contents that will be committed. - The
idempotency_keyis the key that will appear in the PR body.
The only fields added by apply are pr.url, pr.number, and
pr.html_url — populated from GitHub’s response.
When to skip dry-run
Section titled “When to skip dry-run”There is one legitimate skip: a fleet has a standing instruction for a specific verb on a specific repo, and is invoking that verb in a tight retry loop where the dry-run pass would just be noise.
In every other case — first-time invocation, manual operation, ad-hoc fixes — show the dry-run to the human first. The cost is a few hundred milliseconds; the benefit is catching a wrong path or missing constraint before a PR exists.
See also
Section titled “See also”- Idempotency keys — re-runs of apply are no-ops.
- Verbs — the pipeline shared by both modes.
- CLI auth — setting up credentials for apply.