Repository navigation
feat(contract): agent CLI contract for every shipped script (ADR-029) - #58
Merged
Merged
Conversation
Promoted from .writ/issues/features/2026-10-12-agent-facing-writ-cli.md. Four stories implementing ADR-029: contract doc and ADR amendment, stop shipping dev-only scripts, contract test, conformance fixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds .writ/docs/agent-cli-contract.md: two output shapes (data JSON, check verdict line), usage-error rule, bounded waits, network opt-in, and a 34-row classification of every shipped script. Amends ADR-029 rules 1 and 2. 13 doc tests pass on Python 3.9; full suite 2288 passed. Drift: 2 Medium (strict check rule across all modes; ~15 scripts flagged, conformance split into Stories 4 and 5), 1 Small. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ac-trace reported AC-1.5 untested: no test named it. Adds a test that every test in the file names a Story 1 AC ID. 14 passed on Python 3.9. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer kept the check-shape rule strict across every mode (drift-log DEV-001, DEV-002). Story 4 now covers Python data and check tools plus atomic writes; new Story 5 covers lint-skill.sh, gen-skill.sh, check-agent-parity.sh, and app-verify.py line 1, and closes the issue. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pdate (Story 2) install.sh, update.sh and unlink.sh no longer ship pipeline-baseline, measure-invocation, harness-cost, lean-decision, prune-ledger, spawn-cap. update.sh now resolves scripts/ manifest paths at the project root, treats filter-rejected scripts as stale, and warns about (and keeps the baseline of) a locally modified stale script. New shell test covers both upstream cases and a second update run. Bash suite green; pytest 2289 passed; eval.sh Findings 0. Drift: 1 Medium (update.sh scope, approved), 2 Small. Files three pre-existing issues found on the way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ebt (Story 3) New scripts/tests/test_agent_cli_contract.py checks every shipped script against .writ/docs/agent-cli-contract.md: table completeness, --help, bad flag, network/agent-launch/wait scans, shape and exit-code smokes. 14 known violations tracked as strict xfail for Stories 4 and 5. Amends the check-tool exit-code rule to match the scripts (maintainer decision): exit 0 when the check ran, 1 fail, 2 usage. 269 passed, 14 xfailed on 3.9; full suite 2544 passed; eval.sh Findings 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CLI contract, Story 4) _py_search_roots walked parent folders with `while True`, which the contract test reads as an unbounded wait. Iterate over the folder and its `parents` instead, so the filesystem root bounds the loop. Output is unchanged. - scripts/test-integrity.py: for-loop over (parent, *parents) - scripts/tests/test_test_integrity.py: pin the walk (nested file, file outside the project) - test_agent_cli_contract.py: delist (test-integrity.py, bounded_wait) - agent-cli-contract.md: drop the test-integrity note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(agent CLI contract, Story 4) revert-resolve.py is a data tool, but its default success output was a human summary and JSON sat behind --json. JSON (indent 2, unchanged) is now the default; --pretty prints the former summary; --json is a hidden no-op so older command files keep working. - scripts/revert-resolve.py: flags and docstring CLI block - scripts/tests/test_revert_resolve.py: JSON default equals the old --json document, --json no-op, --pretty equals the human render - commands/revert.md: drop --json from the resolver call - test_agent_cli_contract.py: delist (revert-resolve.py, shape) - agent-cli-contract.md: drop the revert-resolve note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eport (agent CLI contract, Story 4) knowledge-consolidate.py is a data tool, but its default output was the markdown report and JSON sat behind --json. Compact JSON is now the default; --pretty prints the report byte for byte as before; --json is a hidden no-op, so commands/retro.md's `--json` call keeps working unedited. - scripts/knowledge-consolidate.py: flags and docstring usage block - scripts/tests/fixtures/knowledge-consolidate/: fixture ledger plus the dry-run and apply reports captured from the unmodified script - scripts/tests/test_knowledge_consolidate.py: --pretty matches each capture; JSON default; --json no-op; dry run writes nothing - commands/knowledge.md: --pretty on the dry-run and apply calls so the user sees the same report - test_agent_cli_contract.py: delist (knowledge-consolidate.py, shape) - agent-cli-contract.md: drop the knowledge-consolidate note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…contract, Story 4)
change-surface.py is a data tool, but `classify` printed one bare class
word. It now prints one JSON object `{"class": CLASS}`; --pretty prints
the bare word; --json is a hidden no-op. Classification logic and exit
codes are unchanged.
- scripts/change-surface.py: JSON output, --pretty, docstring
- scripts/tests/test_change_surface.py: read the class from JSON; add
JSON-shape, --pretty, and --json no-op cases
- commands/implement-story.md + .lean.md: Gate 2.5 reads
`change_surface` from the JSON `class` field
- scripts/tests/test_gate_replay.py: read the replayed class from the JSON
- scripts/tests/test_lean_commands.py: repin implement-story.md (prose
kept byte-neutral for the governor's command budget)
- test_agent_cli_contract.py: delist (change-surface.py, shape)
- agent-cli-contract.md: drop the change-surface note
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a blocker (agent CLI contract, Story 4)
boundary-map.py is a data tool. `crossings` printed verdict/route/reason
text lines, and `compute` exited 1 with empty stdout on a malformed
story.
- crossings prints one JSON object {verdict, route, reasons, summary};
--pretty prints the former lines byte for byte; --json is a hidden
no-op. Exit codes are unchanged (0 when it ran, 2 on usage).
- compute prints {"blocker": {"code": "malformed_story", "summary": ...}}
on a story error, still exit 1.
- load_map treats a saved blocker as an unreadable map, so the Gate 0.5
tee of a blocker still routes `unverifiable` / `map_unreadable` rather
than reading as an empty map that flags every file.
- commands/implement-story.md: Gate 0.5 treats a blocker as empty; Gate
2.5 reads `gate3_route` from `route`; Gate 3 passes `reasons` as
boundary_overlap_summary (byte-neutral for the command budget).
.lean.md: Gate 0.5 blocker sentence (it has no crossings step).
- scripts/tests/test_boundary_map.py: crossings cases read the JSON;
--pretty, --json no-op, saved-blocker map, and compute blocker cases
- scripts/tests/test_quality_gate_wiring.py, test_lean_commands.py:
follow the prose change and repin
- test_agent_cli_contract.py: smokes may carry a mode (keyed
`<script> <mode>`), one default per script; adds the
`boundary-map.py crossings` smoke; delists (boundary-map.py, shape)
- agent-cli-contract.md: drop the boundary-map note
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (agent CLI contract, Story 4) goal-emit.py is a check tool, but a `pass` emit printed the /goal invoke text after the summary line, so the summary was not last. The invoke text now sits between the verdict line and the summary. GOAL.md and VERIFY.md are each written to a temp file in the emit directory and renamed over the target (os.replace), so an interrupted write leaves the old file whole and no temp file behind; written files keep the umask permissions a plain write gave them. - scripts/goal-emit.py: _emit detail block, _write_atomic, docstring - scripts/tests/test_goal_emit.py: invoke between verdict and summary; interrupted rewrite keeps old files; interrupted first write leaves nothing; permissions - scripts/tests/test_goal_emit_gold.py: read the invoke between the verdict and summary lines - commands/create-goal.md, implement-phase.md + .lean.md, adapters/claude-code.md: the invoke text is above the summary (implement-phase.md byte-neutral) - scripts/tests/test_lean_commands.py: repin implement-phase.md - test_agent_cli_contract.py: add the `goal-emit.py emit` pass-path smoke - agent-cli-contract.md: drop the goal-emit note; the State sentence now names only roadmap-sync.py as an in-place writer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ast (agent CLI contract, Story 4) verdict-provenance.py is a check tool, but it printed findings, then notes, then a `gates: ...` summary, with no verdict line. It now prints `pass` or `fail` alone on line 1, then the findings and notes as before, then `verdict-provenance: <verdict> (gates: ...)` last. Exit codes are unchanged. - scripts/verdict-provenance.py: verdict line, summary prefix, docstring - scripts/eval.sh: check_verdict_provenance skips the verdict line and relays the summary's metrics as the same `Metrics: gates: ...` note - scripts/tests/test_verdict_provenance.py: findings sit between the verdict and the summary; clean-run shape case - test_agent_cli_contract.py: delist (verdict-provenance.py, shape) - agent-cli-contract.md: drop the verdict-provenance note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…LI contract, Story 4) review-panel.py is a check tool. `status` and `tally` already printed a verdict first, but `trial-init`, `trial-prepare`, `trial-record`, and `trial-label` printed `initialized`, `prepared`, `recorded`, or `labeled` on line 1. Each now prints `pass` on line 1 when it did its job; detail lines and the `review-panel:` summary follow unchanged. Refusals still exit 2. No command or adapter parses these words. The contract doc named only trial-init and trial-prepare; trial-record and trial-label had the same defect and are fixed with them, since the check shape covers every subcommand. - scripts/review-panel.py: `pass` line 1 in the four trial subcommands - scripts/tests/test_review_panel.py: line-1 verdict for init, prepare, record, and label - test_agent_cli_contract.py: smokes may carry a `setup(tmp)` hook; add `review-panel.py trial-init` and `review-panel.py trial-prepare` smokes (prepare runs against a throwaway git repo built in the temp dir) - agent-cli-contract.md: drop the review-panel note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CLI contract, Story 4) gen-codex-agent-tomls.py is a check tool. --check already conformed, but write mode printed bare `wrote <path>` lines, and an unmapped stem exited 1 with only a stderr message. Write mode now prints `pass`, the `wrote` lines, and `gen-codex-agent-tomls: pass (<n> written, 0 unmapped)` last. An unmapped stem still writes nothing; it prints `fail`, one `reason: unmapped <stem>` line each, and the summary, exit 1. The generated TOMLs are unchanged. Callers (commands/refresh-command.md, adapters/codex.md) only run the command and need no edit. - scripts/gen-codex-agent-tomls.py: write-mode output, docstring - scripts/tests/test_gen_codex_agent_tomls.py: write-mode shape; the unmapped abort reads the fail shape on stdout - test_agent_cli_contract.py: delist (gen-codex-agent-tomls.py, shape) - agent-cli-contract.md: drop the gen-codex-agent-tomls note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Story 4) The contract's State rule says each state file is written through a temp file and a rename. append-row rewrote .writ/product/roadmap.md in place, so an interrupted write could leave a truncated roadmap. It now writes a temp file in the roadmap's folder and os.replace()s it over the target, keeping the file's mode; on any failure the temp file is removed and the old roadmap is untouched. Output is unchanged, so commands/release.md, eval.sh, and eval-roadmap-sync.py need no edit. - scripts/roadmap-sync.py: _write_atomic - scripts/tests/test_roadmap_sync.py: an interrupted rename keeps the roadmap and leaves no temp file; the file mode survives - agent-cli-contract.md: drop the State section's in-place-writer sentence (goal-emit.py went atomic earlier in this story) - test_agent_cli_contract.py: drop the empty Story 4 heading in KNOWN_VIOLATIONS; note the `<script> <mode>` key in the docstring Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Story file, README, drift log (4 Small), and context for the ten per-script fixes aaf1bc1..ad75824. KNOWN_VIOLATIONS holds only Story 5's 7 entries. Full suite 2586 passed, 7 xfailed on 3.9; eval.sh Findings 0. Story 5 gains review-panel tally/trial-report verdict words and the Gate 2.5 --surface wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ontract, Story 5) Story 4 reads Completed, and ac-trace counts AC-4.5 as untested: the atomic-write tests exist, but no test-shaped file attributes an AC-4.5 token to 2026-10-12-agent-cli-contract. review-override therefore failed Story 4 and bash scripts/eval.sh reported 2 findings at 8dffc8f. The comment above the roadmap-sync interrupted-write test now names the spec and AC-4.5. No test logic changes. eval.sh --check=review-override reports 0 findings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…I contract, Story 5)
lint-skill.sh printed one line per file and a trailing ❌ tally, had no
--help (it linted a file named --help), and exited 2 when a file was
missing. It now prints the check shape:
line 1 pass | fail | unverifiable
then one `reason:` line per clean file, finding, warning, or missing
file (indented Line/Description/Remediation details follow)
last lint-skill: <verdict> (<counts>)
Exit 0 on pass or unverifiable (a file was not found), 1 on fail, 2 on a
usage error: no files or an unknown flag, with usage on stderr and stdout
empty. --help prints usage and exits 0. Findings keep their ❌/⚠️ /✅
markers, so the existing lint tests and eval-skill-lifecycle.py read the
output unchanged.
- scripts/tests/test_shell_check_shape.sh: new; pass, fail, missing-file,
mixed, usage, and --help cases
- commands/new-skill.md: Phase 2 and the exit criteria read line 1;
unverifiable never proceeds
- commands/refresh-command.md: Phase 5 branches on line 1
- test_lint_entry_level.sh: finding_count no longer filters the old tally
- agent-cli-contract.md: drop the lint-skill.sh note
- test_agent_cli_contract.py: delist lint-skill.sh help and shape
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct, Story 5) --check printed a bare diff (nothing when clean), write mode reported on stderr only, an unknown flag exited 1, a missing manifest exited 2, and a missing SKILL.md exited 3. Every mode now prints the check shape: line 1 pass | fail | unverifiable, `reason:` and detail lines, and a `gen-skill: <verdict> (...)` summary last. - --check: pass when SKILL.md matches; fail with a drift reason and the diff as detail lines (exit 1) - write: pass after writing SKILL.md through a temp file beside it that keeps its mode (mktemp in /tmp made it 0600 and crossed filesystems) - --dry-run: pass, the generated catalog, then the summary - any mode: a manifest that fails validation is fail with one `reason: YAML error in .writ/manifest.yaml: ...` line (exit 1); write mode leaves SKILL.md untouched - missing manifest or SKILL.md: unverifiable, exit 0 - unknown flag: usage on stderr, stdout empty, exit 2 Callers: commands/new-skill.md reads line 1 in its exit criteria, Step 3.3, and Completion; the CI step in .github/workflows/eval.yml and test_model_tier_migration.sh require `pass` instead of exit 0, so a missing SKILL.md no longer passes. eval.sh's --dry-run exit-code check still means "manifest invalid" and is unchanged. Tests: test_shell_check_shape.sh runs every mode on a minimal manifest tree in a temp copy. The contract test smokes write mode the same way: a Smoke setup may return a script copy to run in place of scripts/<script>. Delists gen-skill.sh shape; drops its contract-doc note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent CLI contract, Story 5) check-agent-parity.sh printed⚠️ lines or a `parity OK` line with no verdict, ignored every argument (--help ran the check), and always exited 0. It now prints the check shape: line 1 pass | fail | unverifiable then one `reason:` line per missing counterpart last check-agent-parity: <verdict> (...) Exit 0 on pass or unverifiable (no agents/ folder), 1 on fail (drift), 2 on any argument other than --help (usage on stderr, stdout empty). --help prints usage and exits 0. The caller keeps drift a warning. commands/refresh-command.md Phase 6 reads line 1, says `fail` exits 1 and never blocks, and gains an `unverifiable` branch; its Cross-References row no longer says "always exit 0". adapters/codex.md's maintainer checklist reads the `reason:` lines. - test_shell_check_shape.sh: aligned, drift, no agents/, usage, --help, and this repo, from a temp copy - test_agent_cli_contract.py: delist check-agent-parity.sh help, bad_flag, and shape - agent-cli-contract.md: drop the check-agent-parity.sh note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CLI contract, Story 5)
app-verify.py printed its `app-verify:` summary on line 1, `touched`
printed bare feature IDs, and unverifiable runs (no recipe, invalid
recipe, refused, unsupported platform, no runnable feature) exited 2.
Every subcommand now prints the check shape:
line 1 pass | fail | unverifiable
then `reason:` lines (validate findings, failed run features, the
unverifiable or refused reason); `touched` prints one
`feature: <id>` line per matched feature instead
last the `app-verify:` summary (unchanged wording; a refusal still
reads `app-verify: refused (...)`)
Exit 0 when the check ran (pass or unverifiable), 1 on fail. --json
still prints one object after the summary; its `verdict` values (valid,
ok, refused, ...) are unchanged.
Callers, in this commit:
- commands/implement-story.md Gate 4.5 and the .lean twin branch on line
1: `unverifiable` replaces exit 2, and "no `feature:` lines" replaces
"printed no IDs". Gate 2.5 now says to pass the classifier's `class`
as `--surface`. Two Gate 1 clauses were tightened (a "positioned"
aside and a "see agents/coding-agent.md" pointer the gate header
already names) to keep the file at 36973 bytes.
- commands/create-uat-plan.md Steps 1.3, 4.3, 4.4 read line 1 and the
`feature:` lines.
- .writ/docs/app-verification-format.md: validate's verdicts.
- scripts/eval.sh check_app_verify expects pass/0, fail/1, and
unverifiable/0 (refused) and checks line 1; test_eval_app_verify.sh
stubs print the new shape, plus a summary-first stub that must yield a
line-1 finding per recipe.
- test_lean_commands.py: implement-story.md hash repinned with a dated note.
agents/coding-agent.md, agents/visual-qa-agent.md, and adapters/codex.md
name the script without quoting its output; codex TOMLs stay in sync.
Contract test: smokes `app-verify.py touched` on the pass fixture
recipe, delists app-verify.py shape (KNOWN_VIOLATIONS is now {}), adds
AC-5.4 and AC-5.5 regression tests (no Story 5 script listed; no caller
quotes the retired output), and lets each test cite a Story 3 or Story 5
AC. The app-verify contract note now records only what remains specific:
`feature:` lines, refused as unverifiable, the --json order, and the
network/wait allowances.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…agent CLI contract, Story 5)
`tally` printed `block` and `trial-report` printed `keep` or `remove` on
line 1, words outside the check vocabulary. A panel word that is not a
verdict now follows as the first `reason:` line:
tally fail, reason: block (exit 1, unchanged)
trial-report pass, reason: keep | reason: remove (exit 0, unchanged)
`pass` and `unverifiable` print as before. Summaries and --json are
unchanged; the JSON `verdict` still holds the panel word.
Callers: Gate 3's review-panel paragraph in commands/implement-story.md
and the .lean twin reads "`fail` (`reason: block`) is a Gate 3 FAIL";
the rest of the paragraph, Gate 3.5's panel-block sentence, and the
advisory/pass/unverifiable/skipped/off list are unchanged. To keep
implement-story.md at 36973 bytes, the Gate 0.5 /prototype note names
the command instead of its file path and Gate 2's build-smoke sentence
drops "structural"; Gate 4.5 reads "its line says" again and "no
`feature:` line". eval.sh check_review_panel pins the new sentence;
test_lean_commands.py repins the hash with a dated note.
- test_review_panel.py: tally block, combination rule, trial-report
keep/remove expectations, and the eval pin-mutation literal
- test_agent_cli_contract.py: smokes `review-panel.py tally` (two-vendor
fixture, fail/exit 1) and `trial-report` (a finished keep trial in
{tmp}); the AC-5.4 stale-caller list adds "`block` is a Gate 3 FAIL"
- agent-cli-contract.md: Notes now say every shipped script conforms;
add a review-panel note for the reason-line panel word; drop the
arch-lint note that only said it conforms
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ation issue (agent CLI contract, Story 5) - features/2026-10-12-agent-facing-writ-cli.md: append `## Resolution` (2026-10-12, branch feature/agent-cli-contract, spec .writ/specs/2026-10-12-agent-cli-contract/spec.md): the contract doc and ADR-029 amendment, the six dev-only scripts no longer shipping with update.sh's stale-copy handling, the contract test, and every shipped script conforming with KNOWN_VIOLATIONS empty. - improvements/2026-10-12-script-install-location.md: new, in the /create-issue template (Type: Improvement). Asks whether shipped scripts should leave the project-root scripts/ folder the way pstack keeps tools in the skill folder, citing ADR-029's negative consequence. install.sh keeps a same-named project file as "assumed modified", so commands then run the project's file; --force overwrites it. No existing issue covered the install location. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- gen-skill write mode prints the check shape when SKILL.md is read-only - create-uat-plan handles line 1 unverifiable from app-verify validate - eval.sh reads gen-skill --dry-run line 1 instead of the exit code - contract doc and BR4: --json may follow the summary (app-verify) - live-repo parity test holds the shape, not zero drift - AC-4.5 citations: goal-emit atomic writes and a doc-notes test 2614 passed on 3.9; bash loop green; eval.sh Findings 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
exit-criteria implement-spec verdict met (5/5 stories terminal; full suite 2614 passed after the final story). Drift this run: 12 small, 8 medium, 0 large. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sellke
force-pushed
the
feature/agent-cli-contract
branch
from
October 19, 2026 21:46
1d6278e to
631e043
Compare
Keep both lean-command pin notes. The merged command files hash to create-spec from the prototype reframe and implement-phase / implement-story from the agent CLI contract. Co-authored-by: Cursor <cursoragent@cursor.com>
sellke
marked this pull request as ready for review
October 20, 2026 19:29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements ADR-029. Writ doesn't get a
writCLI product. Instead, every script it ships to installed projects follows one agent contract, and a pytest test enforces it. Data tools print JSON. Check tools printpass/fail/unverifiableon line 1 and a summary last, and exit 0 when the check ran, 1 on fail, 2 on usage. No script spawns agents, waits without a bound, or calls the network without an opt-in. Six dev-only scripts stop shipping, andupdate.shnow handles stalescripts/files correctly.Changes
Contract: new
.writ/docs/agent-cli-contract.mdwith the rules and a classification of all 34 shipped scripts. ADR-029 gets a dated amendment covering the two output shapes, the exit codes, usage errors and bounded waits.Install and update:
install.sh,update.shandunlink.shno longer shippipeline-baseline,measure-invocation,harness-cost,lean-decision,prune-ledgerorspawn-cap.update.shnow:scripts/manifest paths at the project root (before, they were looked up under the platform folder and skipped);Enforcement: new
scripts/tests/test_agent_cli_contract.pychecks every shipped script, read from the realis_shippable_script:--helpand bad-flag handling;Its
KNOWN_VIOLATIONSlist started at 14 entries and is now empty.Script fixes: one commit per script, each carrying its callers and tests.
revert-resolve,knowledge-consolidateandchange-surfacedefault to JSON, with--prettyfor the old output and--jsonkept as a no-op.boundary-map crossingsprints JSON, andcomputeprints a blocker.goal-emit,verdict-provenance,review-panel(all modes),gen-codex-agent-tomlsandapp-verifyprint the verdict first.lint-skill.sh,gen-skill.shandcheck-agent-parity.shprint the verdict first and handle--helpand usage errors.test-integrity's parent-folder walk is bounded by aforloop.roadmap-syncandgoal-emitwrite atomically.Callers:
commands/implement-story.mdGates 0.5, 2.5, 3 and 4.5, plus the.leantwin. The file stays at its exact byte pin.revert.md,knowledge.md,create-goal.md,implement-phase.md,create-uat-plan.md,new-skill.md,refresh-command.md,update-writ.mdand the adapters.scripts/eval.shand.github/workflows/eval.yml. The gen-skill CI step now requires line 1 to bepass.Issues: resolves
.writ/issues/features/2026-10-12-agent-facing-writ-cli.md. Files four problems found along the way:update.shdeletes a modified stale file on its second run;update.shdrops the Codex config baseline;scripts/folder.Spec Reference
.writ/specs/2026-10-12-agent-cli-contract/spec.md(Complete)story-1-contract-doc-and-adr-amendment.md,story-2-stop-shipping-dev-scripts.md,story-3-contract-test.md,story-4-conformance-fixes.md,story-5-shell-and-app-verify-conformance.mdTest Results
/shipdid not run tests before the original PR was opened. The/implement-specintegration run on this branch after the final story:uv run pytest: 2614 passed, 2 skippedbash scripts/eval.sh: Findings 0metThis
/ship(2026-10-20) mergedorigin/main. The only conflict was the lean-command pin comments inscripts/tests/test_lean_commands.py; both histories were kept, and the hashes already matched the merged command files. Re-run before merge:uv run pytest: 2614 passed, 2 skippedscripts/tests/test_*.sh: 0 failuresbash scripts/eval.sh: Findings 0Drift Report
12 Small, 8 Medium, 0 Large (
drift-log.md). The Medium entries were each decided or approved by the maintainer during the run:update.shstale handling goes beyond the filter change. The spec wrongly assumed the old stale logic coveredscripts/. (Story 2)unverifiableexits 0. (Story 3)--helprule is stricter, and the smoke covers one mode per script, with modes added as they were fixed. (Story 3)eval.shafter Story 4 was marked complete, and fixed in Story 5. (Story 5)Review Notes
No standalone
/reviewwas run. Each story passed Gate 3:The configured cross-vendor review panel (
.writ/config.md) did not run.review-panel.py status --platform claude-codereturnsunverifiable(platform_cannot_spawn_other_vendors), and that status line was not printed in the story reports.Things to check before merging:
/knowledgeusers JSON instead of the markdown report.update.shcopies both together.🤖 Generated with Claude Code