@codedology/code-forge 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +49 -9
  3. package/docs/reference/blocks.json +587 -1
  4. package/docs/reference/config.md +36 -0
  5. package/package.json +2 -2
  6. package/schema/code-forge.schema.json +89 -0
  7. package/skill/SKILL.md +1 -1
  8. package/skill/references/adapters/claude-code.md +1 -1
  9. package/skill/references/adapters/solo.md +2 -2
  10. package/skill/references/adapters/subprocess.md +12 -2
  11. package/skill/references/code.md +14 -7
  12. package/skill/references/ledger.md +2 -2
  13. package/skill/references/proof.md +77 -2
  14. package/skill/references/review.md +39 -7
  15. package/skill/references/security.md +4 -4
  16. package/skill/scripts/forge +1 -1
  17. package/skill/templates/coder-brief.md +6 -6
  18. package/src/autopilot/binnacle.mjs +9 -3
  19. package/src/cli/block.mjs +262 -31
  20. package/src/cli/jev.mjs +22 -3
  21. package/src/cli/keys.mjs +156 -21
  22. package/src/cli/proof.mjs +35 -7
  23. package/src/cli/review-file.mjs +17 -11
  24. package/src/cli/spawn.mjs +44 -3
  25. package/src/config/validate.mjs +320 -5
  26. package/src/doctor/index.mjs +2 -1
  27. package/src/doctor/local.mjs +27 -6
  28. package/src/doctor/probes.mjs +38 -0
  29. package/src/engines/builders/codex.mjs +44 -4
  30. package/src/engines/sandbox-dirs.mjs +257 -0
  31. package/src/keys/store.mjs +128 -21
  32. package/src/proof/junit.mjs +530 -0
  33. package/src/proof/red-green.mjs +57 -9
  34. package/src/proof/red-parse.mjs +2 -0
  35. package/src/proof/runners.mjs +472 -0
  36. package/src/proof/xcresult.mjs +285 -0
  37. package/src/review/engine.mjs +141 -42
  38. package/src/review/fixloop.mjs +75 -23
  39. package/src/review/gate-check.mjs +3 -2
  40. package/src/review/lenses/A.md +2 -0
  41. package/src/review/lenses/B.md +2 -0
  42. package/src/review/lenses/full.md +2 -0
  43. package/src/review/lenses/quick.md +1 -0
  44. package/src/review/lenses/recheck.md +2 -0
  45. package/src/review/packet.mjs +48 -14
  46. package/src/review/paired.mjs +80 -0
  47. package/src/review/reopen.mjs +4 -6
  48. package/src/review/reviewed-lines.mjs +94 -0
  49. package/src/review/route.mjs +232 -0
  50. package/src/review/schema-fallback.mjs +77 -22
  51. package/src/review/session-retry.mjs +44 -9
  52. package/src/review/test-quality.mjs +201 -0
  53. package/src/review/verb-forms.mjs +39 -0
  54. package/src/session/spawn.mjs +39 -2
  55. package/src/state/block-end.mjs +10 -6
  56. package/src/state/block.mjs +13 -3
  57. package/src/state/config-snapshot.mjs +39 -0
  58. package/src/state/run.mjs +4 -0
  59. package/src/util/forbidden.mjs +2 -2
  60. package/src/worker/engine.mjs +43 -15
  61. package/src/worker/loop.mjs +98 -15
  62. package/src/worker/ticket.mjs +9 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,99 @@ waits on a second real consumer (plan §10.4, Q17).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.11.0] — 2026-10-13
10
+
11
+ ### Added
12
+
13
+ - `npm run release` moves the README "In the next release:" bullets under the new version, refuses an empty [Unreleased]
14
+ or a missing README anchor in preflight, restores README on a failed check, and runs `gen-config-doc --check`;
15
+ `docs/releasing.md` gets a releasing checklist (B108).
16
+ - `pairsFor` and `summarize` (src/review/paired.mjs) and an optional `## paired` packet section after `## context`,
17
+ dropped first over budget; `attachFiles` attaches an untracked file only when it is an owned, changed file of the
18
+ block and not ignored or secret-like (B97).
19
+ - The review engine, the fix-loop recheck and the worker send `## paired` (the block's owned changed peers and
20
+ `test_files`); the needs_file path passes the block's owned and changed files so a new untracked owned test file can
21
+ be attached; the lenses say a test named in `## paired` exists (B98).
22
+
23
+ ### Changed
24
+
25
+ - `block close` prints the `reviewed <path> ticket <id>` lines itself, for every file the review gate accepts; `--json`
26
+ and the done file carry `reviewed: [{file, ticket, via}]`; the coder report no longer needs hand ticket lines, and a
27
+ mismatched hand line under `--report` is a WARN, not a refusal (B96, #16).
28
+ - A quick review packet over `quick_in` steps up to one full L2 session when it fits `full_in`, before `split_required`;
29
+ the `review.plan` row carries `step_up: {from, tokens_in, budget}` (B109, #16).
30
+ - A cached round-1 `split_required` is re-queued by `review-file` (`retry: true`) and by `review-file --fresh`, with the
31
+ same ticket id; a recheck `split_required` stop still needs `block reopen` or `block waive` (B110, #16).
32
+ - A recheck with no fix hunks counts no round and returns `reviewed`, `approved: false`, `reason: 'no-fix-hunks'`; a
33
+ recheck answer that lacks a `resolved` entry for an open finding of its packet is a `schema` miss; the worker's
34
+ recheck summary adds `status`, `tokens_in`, `tokens_out` and `reason` (B111, #16).
35
+ - A review ticket reads the diffs of the block's other changed files only when the review asks for them, once per ticket
36
+ (B112)
37
+
38
+ ### Fixed
39
+
40
+ - A worker stops by itself, without taking a new ticket, when its run record or its HOME is removed (B113)
41
+ - Tests stop every worker they start, also when a test fails or times out; the final no-leak step of `npm test` fails
42
+ when a worker from this suite is still alive (B113)
43
+
44
+ ## [0.10.0] — 2026-10-13
45
+
46
+ ### Added
47
+
48
+ - `keys.cache_hours` (1–24 h, default 8) and `keys set --op … --cache-hours <n>` set how long a key read from 1Password
49
+ stays cached; a lower value shortens an existing cached key at the next read, `keys set --op` refuses an invalid value
50
+ and other readers warn and use 8 h (B84, #14).
51
+ - `keys list` and `doctor` warn when a cached 1Password key has less than 1 hour left, and `jev ask` prints a re-link
52
+ command (`code-forge keys set jev --op <ref>`, the ref shell-quoted when needed) when the cached key expired and
53
+ 1Password did not answer; the key index keeps the `op://` reference (B85, #14).
54
+ - `src/proof/junit.mjs`: a fail-closed JUnit XML reader and RED/GREEN classifier for `proof red-green` (no DTD or
55
+ entities, 8 MiB cap, only failed assertions count as RED; a SwiftPM bare failure needs a matching XCTest console
56
+ line); used by `proof.runners` from B87 (B86, #7).
57
+ - `proof.runners`: `proof red-green` proves a non-`node:test` file through the matching runner's argv and its JUnit
58
+ report; the list is frozen in the run snapshot, validated by rule 17 `proof-runners-invalid`, and the row carries
59
+ `runner`, `env_keys` and `failed_tests` (B87, #7).
60
+ - `swift` adapter for `proof.runners`: anchored `{test_filter}` (file base name or `Suite/test` case), reads the XCTest
61
+ report and its Swift Testing sibling, `swiftc` compile errors give `RED_INVALID compile`; opt-in live check
62
+ `CODE_FORGE_LIVE_SWIFT=1` verified on Swift 6.3.3 (B88, #7).
63
+ - PHP red→green through the `junit` adapter, verified live on Laravel 13 with PHPUnit 12 and Pest 4
64
+ (`CODE_FORGE_LIVE_PHP=1`); captured reports replace the hand-written PHP fixtures (B89, #7).
65
+ - The `xcodebuild` adapter for `proof.runners`: `xcode` keys, `{only_testing}` and `{result_bundle}` tokens, a precheck
66
+ for full Xcode and an available simulator (`xcode-missing`, `destination-unavailable`), a fail-closed `xcresulttool`
67
+ reader, and `RED_INVALID compile` for a build failure; fixtures captured from Xcode 26.6 (B102, #7).
68
+ - A live `xcodebuild` red→green check on an iOS simulator (`CODE_FORGE_LIVE_XCODE=1`), verified with Xcode 26.6 on an
69
+ iPhone 17 simulator; it skips with a reason without full Xcode or an available simulator (B103, #7).
70
+ - `review.routes`: size-based review routing. A file with at least `min_lines` lines is reviewed (L2) or judged (L3) by
71
+ the rule's model, with its own session timeout and second level; one signed `review.route` row per routed level;
72
+ config rule 18 `review-routes-invalid` (B90, #10).
73
+ - A `## test-quality` section in review packets lists the lines of a test file that read a source file and assert on its
74
+ text; every reviewer lens gets one rule to judge those lines (a hint, not a finding); recheck packets get the rule but
75
+ no section (B92, #10).
76
+ - `coder.sandbox` with `profile: workspace|toolchain|full` and `add_dirs`: `toolchain` gives the Codex coder one
77
+ `--add-dir` per entry, `full` runs it with `-s danger-full-access` and `doctor` warns; entries that are, hold or sit
78
+ inside a protected dir (home, `~/.ssh`, `~/.codex`, `~/.code-forge`, the run temp root, …) are refused
79
+ `sandbox-dir-refused` after symlinks are followed; the key is fixed for the run and `forge spawn --role coder --run`
80
+ refuses a changed live value `sandbox-changed` (B93, #10).
81
+
82
+ ### Changed
83
+
84
+ - A timed-out review session is a broken result like a schema miss: at a packet's second broken result the second level
85
+ (`review.second_levels.<level>`, or a routed rule's `second`) is tried once in the same ticket;
86
+ `review.session_timeout` notes carry `packet_hash` and are signed, and `review.schema_fallback` gains `trigger:
87
+ schema|timeout` (B91, #10).
88
+ - The skill sends ONE fix message per block: after the coder's first COMPLETE it waits for every `review-file` ticket
89
+ and the block's tests, then sends every failing test and every open finding together; a later finding waits for the
90
+ next round, and the per-file round cap still counts per file (B94, #10).
91
+ - `block waive` and `block reopen` are human-only: they ask for a yes at a terminal, have no `--yes`, exit 2 and record
92
+ nothing with stdin or stdout not a terminal, and refuse a bad id, path, control character, unowned file or unknown
93
+ finding before asking; an autopilot grant does not change this; the prompt does not prove a human, since a
94
+ pseudo-terminal passes it (B80, #10).
95
+
96
+ ### Fixed
97
+
98
+ - The JUnit reader accepts the real SwiftPM 6.3 bare XCTest failure (`message="failure"`), not only `failed`; captured
99
+ Swift reports replace the hand-written fixtures (B88, #7).
100
+ - A missing class in Laravel 13's JSON-escaped AI-agent console summary is now read as `import`, not `error` (B89, #7).
101
+
9
102
  ## [0.9.0] — 2026-10-12
10
103
 
11
104
  ### Added
package/README.md CHANGED
@@ -8,10 +8,45 @@ for cross-model, parallel, evidence-gated feature delivery — on Claude Code, C
8
8
  > `examples/` have all landed (`docs/reference/blocks.json` lists every build block). What changed
9
9
  > in each version is in [CHANGELOG.md](CHANGELOG.md).
10
10
 
11
- ## What's new in 0.8
11
+ ## What's new in 0.11
12
12
 
13
13
  In the next release:
14
14
 
15
+ In 0.11.0:
16
+
17
+ - **The release moves the README What's new list (B108).** `npm run release` now moves the "In the next release:" bullets under "In X.Y.Z:". A minor or major release starts a new "What's new in X.Y" heading. The empty anchor stays for the next block. The release refuses an empty CHANGELOG [Unreleased] or a missing anchor before it changes anything, and a failed check puts the README back with the other files. The checks now include `gen-config-doc --check`. `docs/releasing.md` has a releasing checklist.
18
+ - **The close writes the ticket lines (B96).** The coder's final report no longer needs a `reviewed <path> ticket <id>` line per file. `block close` prints one for every file the review gate accepts (approved, waived or answered), with the queue's ticket. `--json` and the block's done file carry them as `reviewed: [{file, ticket, via}]`. With `--report`, a hand line that names another ticket prints `WARN report ticket mismatch <path>` on stderr, and the close goes on.
19
+ - **A big-diff file steps up to a full review (B109).** A low-risk file whose diff is too big for the quick review budget used to stop with `split_required`, and splitting it only made the diff bigger. Now the review plans again as one full L2 session. If the diff fits the full budget, the file is reviewed. The `review.plan` row records the step as `step_up`.
20
+ - **A file that was too big can be reviewed again (B110).** A first-round `split_required` result ran no review. After you raise the budget and run `run reload`, `review-file` (or `review-file --fresh`) tries again with the same ticket. A recheck stop still needs `block reopen` or `block waive`.
21
+ - **A recheck that did not really review never stalls (B111).** A recheck on unchanged content runs no session and counts no round; it returns `reason: 'no-fix-hunks'`. A recheck answer that skips an open finding is a broken answer, not a "still open". So the stall rule sees only real rechecks. The recheck summary now shows each session's status and tokens.
22
+ - **Paired files for the reviewer, part 1 (B97).** code-forge can now find a changed file's test file (or a test's source file) by name, and list its test names or exported symbols in a small `## paired` packet section. The section is dropped first when the packet is too big. A reviewer can also ask for a new, untracked test file when the block owns and changed it. B98 sends these to the reviewers.
23
+ - **Reviewers see the paired test file (B98).** When a block changes `src/a.mjs` and `test/a.test.mjs`, the reviewer of `src/a.mjs` now gets the test names in a `## paired` section, in the first review and in rechecks. Tests named with `block open --tests` count too. The reviewer is told those tests exist, so it no longer reports them as missing. It can still ask for a test file's body, even a new untracked one the block owns.
24
+ - **Faster reviews (B112).** A review ticket reads the diffs of the block's other changed files only when the review asks for them, and only once.
25
+ - **No leftover workers (B113).** A worker stops by itself when its run record or its HOME is removed. `npm test` now fails when a test leaves a worker running.
26
+
27
+ ## What's new in 0.10
28
+
29
+ In 0.10.0:
30
+
31
+ - **Choose how long a 1Password key stays cached (B84).** Set `keys.cache_hours` (1 to 24, default 8) in `.code-forge.yml`, or `keys set <name> --op <ref> --cache-hours <n>` for one key. A lower value shortens keys that are already cached at the next read.
32
+ - **Warnings before a cached key runs out (B85).** `keys list` and `doctor` warn when a 1Password key has less than 1 hour left. When the cached key expired and 1Password does not answer, `jev ask` prints the exact command to re-link it: `code-forge keys set jev --op <ref>`.
33
+ - **A test report reader for red-green proof (B86).** code-forge can now read a JUnit XML test report (XCTest, Swift Testing, PHPUnit, Pest, pytest). A missing, broken or empty report, or an error that is not a failed assertion, never counts as red. B87 connects it to `proof red-green`.
34
+ - **Red-green proof for any test runner (B87).** A new `proof.runners` list in `.code-forge.yml` names a runner per test-file glob: an adapter (`junit` or `swift`), an argv with `{test_file}`, `{test_id}`, `{test_filter}` and `{report}`, optional `env` and `timeout_s`. `proof red-green` runs that argv and reads its JUnit report, so PHPUnit, Pest, XCTest or pytest tests can be proven. The list is fixed at `run start`; a bad entry refuses the proof and never falls back to `node:test`.
35
+ - **Red-green proof for Swift (B88).** A `proof.runners` entry with `adapter: swift` runs `swift test --parallel --filter {test_filter} --xunit-output {report}` and reads both the XCTest and the Swift Testing reports. The test filter is anchored, so a similar suite or test name is never picked up. A test that does not compile at base is `RED_INVALID compile` (a test of a new API needs the human waiver). Checked live with Swift 6.3.3 and Xcode 26.6.
36
+ - **Red-green proof for PHP (B89).** A `proof.runners` entry with `adapter: junit` and `[php, artisan, test, "{test_file}", --filter, "{test_filter}", --log-junit, "{report}"]` proves PHPUnit and Pest tests in a Laravel app. A test of a class the block added is `RED_INVALID import`, also when Laravel prints its AI-agent JSON summary. Checked live with PHP 8.4, Laravel 13, PHPUnit 12 and Pest 4.
37
+ - **Red-green proof for Xcode projects (B102).** A `proof.runners` entry with `adapter: xcodebuild` and an `xcode` object (`scheme`, `destination`, `test_target`) proves XCTest and Swift Testing tests through `xcodebuild test` on a simulator or on macOS. code-forge checks first that full Xcode and the simulator are there, runs only the named suite with `-only-testing`, and reads the result bundle. A build failure is `RED_INVALID compile`. code-forge never boots, creates or deletes a simulator.
38
+ - **Xcode red-green checked live (B103).** An opt-in live check (`CODE_FORGE_LIVE_XCODE=1`) proves XCTest and Swift Testing tests red→green through `xcodebuild test` on an iPhone simulator. It skips with a reason when full Xcode or a simulator is missing. Verified with Xcode 26.6.
39
+ - **Big files go to a big-context reviewer (B90).** Set `review.routes` once, for example `{min_lines: 400, model: <id>, session_timeout_s: 900}`. Files with that many lines get that model and timeout. No more editing `.code-forge.yml` per file. Each routed file gets a signed `review.route` row.
40
+ - **Timeouts reach the second level (B91).** A timeout now counts as a broken result, like an answer that fails the schema. At the second broken result on one file, the second-level model is tried at once, in the same ticket. You no longer wait for a second ticket.
41
+ - **Test-quality hint (B92).** A test that reads a source file and checks its text is a weak test. The review packet now lists those lines for the reviewer. The reviewer decides if it is a problem. The list is a hint, not a finding.
42
+ - **More room for the Codex coder (B93).** New setting `coder.sandbox`. With `profile: toolchain`, the Codex coder may also write to the folders you list in `add_dirs`, such as a build cache. With `profile: full`, the Codex sandbox is off and `doctor` warns you. Secret folders like `~/.ssh` are always refused. The setting is fixed for the whole run.
43
+ - **One fix round per block (B94).** The skill now waits for every review and test of a block. Then it sends the coder ONE fix message with every failing test and every open finding. It never sends findings one at a time.
44
+ - **Waive and reopen ask you at a terminal (B80).** `block waive` and `block reopen` now ask the human for a yes in their own terminal. There is no `--yes`. With no terminal they record nothing. The prompt is a speed bump, not proof of a human: a pseudo-terminal passes it.
45
+
46
+ ## What's new in 0.9
47
+
48
+ In 0.9.0:
49
+
15
50
  - **A lost review state is rebuilt, not reset.** When the review state file of a file is gone (for example, macOS cleaned its temp folder), the worker rebuilds it from the signed ledger and writes a `review.state_restored` row. Rounds, open findings and stops stay as they were. If the ledger cannot rebuild it, the ticket is refused as `review-state-lost`, not `tampered` (#15).
16
51
  - **Review state no longer lives in the temp folder.** It is saved under `~/.code-forge/runs/<run>/review-state/`, so a temp cleanup or a worker restart does not touch it. A state file from an older run is moved there once, after a check against its signed anchor. `code-forge doctor` warns when a state file of an active run is missing and says whether the ledger can rebuild it (#15).
17
52
  - **Waiving every finding is a real way out.** A file whose signed review on its current content left findings, all cleared by accepted waivers, now counts as reviewed (`reviewed_by_waiver`, never approved). `block close` writes one signed `gate.review_waived_all` row for it. A critical still needs a human waiver, and a finding with an unknown severity does too (#9).
@@ -28,6 +63,8 @@ In the next release:
28
63
  - **The review exits, in one place (B76).** `skill/references/review.md` §5, `code.md` and `docs/how-it-works.md` name the ways out of a stopped file in order: fix and the human's `block reopen`, `review-file --answers`, `block waive`, re-decompose. They also say `block attempt` never resets review state and that a lost state file is rebuilt from the ledger.
29
64
  - **`review-file --fresh` fails closed (B83).** When a cached result cannot be put back, the ticket gets a refused `fresh-restore-failed` marker (or keeps its claim), so the worker does not run it by itself. `block reopen` clears it. Messages name the error code only.
30
65
 
66
+ ## What's new in 0.8
67
+
31
68
  In 0.8.0:
32
69
 
33
70
  - **Run only the tests a block touches.** `block open --tests <paths>` names the block's test
@@ -257,7 +294,7 @@ code-forge validate # loads and validates .code-forge.yml
257
294
  code-forge resolve L1 # {provider, model, effort, fallback, cli} for a level
258
295
  code-forge doctor --quick # config + links + key resolution only
259
296
  code-forge list # what is installed where, and whether it still resolves
260
- code-forge keys list # NAME / SOURCE / BACKEND / EXPIRES — never a value
297
+ code-forge keys list # NAME / SOURCE / BACKEND / EXPIRES — never a value; WARN under 1 h left
261
298
  code-forge models # the model catalog per provider
262
299
  code-forge --help # every verb, one per line
263
300
  code-forge version
@@ -291,7 +328,7 @@ usage line.
291
328
  | `validate` | `code-forge validate [--file <path>]` |
292
329
  | `resolve` | `code-forge resolve <L0\|L1\|L2\|L3>` |
293
330
  | `run` | `code-forge run start [--cwd <dir>] [--run <id>] [--engine <e>] [--worker-pid <pid>]` · `run start --reattach --run <id> [--worker-pid <pid>]` · `run status --run <id>` (with spent and budget USD) · `run reload --run <id>` (re-read `.code-forge.yml` mid-run) · `run end --run <id>` |
294
- | `block` | `code-forge block open <id> --run <r> [--level L<n>] --owned <paths…> --acceptance <file> [--brief <file>] [--attempt <n>] [--base <sha>] [--lines <n>] [--kind code\|docs\|contract]` · `block attempt\|rebase <id> --run <r>` · `block close <id> --run <r> [--transcript <file>] [--report <file>] [--no-require-reviews]` · `block claim <id> <path> --run <r>` · `block stop <id> --run <r> --reason <text>` · `block waive <id> <finding-id> --run <r> --file <path> --reason <text>` (human-only) |
331
+ | `block` | `code-forge block open <id> --run <r> [--level L<n>] --owned <paths…> --acceptance <file> [--brief <file>] [--attempt <n>] [--base <sha>] [--lines <n>] [--kind code\|docs\|contract]` · `block attempt\|rebase <id> --run <r>` · `block close <id> --run <r> [--transcript <file>] [--report <file>] [--no-require-reviews]` · `block claim <id> <path> --run <r>` · `block stop <id> --run <r> --reason <text>` · `block waive <id> <finding-id> --run <r> --file <path> --reason <text>` · `block reopen <id> --run <r> --file <path> --reason <text>` (both human-only: you run them in your own terminal, they ask for a yes, and there is no `--yes`) |
295
332
  | `worker` | `code-forge worker --run <id> [--cwd <dir>] [--poll-ms <n>] [--once]` — started detached by `run start`, never by a coder |
296
333
  | `review` | `code-forge review [--base <ref>] [--files <path…>] [--acceptance <file> \| --intent "<text>"] [--run <id>] [--max <seconds>] [--json] [--keep-run]` — review only: no coder, no proof, never closes a block |
297
334
  | `review-file` | `code-forge review-file <path> --block <id> [--run <id>]` (enqueue) · `review-file --wait <ticket> [--max <seconds>s]` (poll, default 90s; still running at `--max` prints `status: pending, reason: wait_timeout`) |
@@ -300,10 +337,10 @@ usage line.
300
337
  | `author` | `code-forge author --job plan\|harden --brief <file> [--facts <file>] [--draft <file>] [--answers <file>] [--out <file>] [--run <id>] [--timeout <seconds>]` |
301
338
  | `facts` | `code-forge facts --brief <file> [--sources <path>…] [--out <file>] [--run <id>] [--timeout <seconds>]` |
302
339
  | `plan` | `code-forge plan check <plan-file> [--facts <sheet>] [--slug <slug>]` — every block's level must be the lane recorded by `jev ask lane --block <id>`; prints `lanes: B1 L1 (rules) · …` |
303
- | `proof` | `code-forge proof tier --file <path> --risk <0-3> [--security] [--cwd <dir>]` · `proof export <block> --run <r> [--remove]` · `proof lock\|unlock\|restore <block> --run <r>` · `proof red-green <block> --run <r> --test <file[::case]> [--mechanism revert\|assertion-deletion]` |
340
+ | `proof` | `code-forge proof tier --file <path> --risk <0-3> [--security] [--cwd <dir>]` · `proof export <block> --run <r> [--remove]` · `proof lock\|unlock\|restore <block> --run <r>` · `proof red-green <block> --run <r> --test <file[::case]> [--mechanism revert\|assertion-deletion]` (a non-`node:test` file runs through its `proof.runners` entry) |
304
341
  | `gates` | `code-forge gates detect\|run\|secret-scan\|safe-edit\|scope\|acceptance\|transcript-grep --cwd <dir> …` |
305
342
  | `jev` | `code-forge jev ask <question-id> --state <file> [--cwd <dir>] [--slug <slug>] [--key-ref <ref>] [--block <id>] [--plan <file>] [--rules]` — `--rules` answers `lane`, `risk` or `security_sensitive` without Jev; writes to the project's ledger slug |
306
- | `keys` | `code-forge keys list \| set <name> [--op <item-id\|link\|op://ref>] \| test <name> [--ref <ref>] \| remove <name>` |
343
+ | `keys` | `code-forge keys list \| set <name> [--op <item-id\|link\|op://ref> [--cache-hours <1-24>]] \| test <name> [--ref <ref>] \| remove <name>` |
307
344
  | `ledger` | `code-forge ledger tail --slug <slug> [--n\|-n <count>]` · `ledger calibration --slug <slug> [--question <id>]` · `ledger outcome --slug <slug> --pr <n> --ci red\|green\|reverted` · `ledger outcome --slug <slug> --scan-git [--cwd <dir>] [--days <n>]` · `ledger add coder --run <r> --block <b> --usd <n> [--note "..."]` |
308
345
  | `report` | `code-forge report --slug <slug> [--json] [--export <dir>]` — 13 sections, then the spend per run (open blocks included) and the total |
309
346
  | `models` | `code-forge models [--refresh --from-cli-caches]` |
@@ -326,8 +363,8 @@ CLI verbs like any other and every one of them is exercised by this package's ow
326
363
  `doctor` always prints this line — the signature is not a defense against an attacker who
327
364
  already runs code as you, and no part of code-forge claims otherwise.
328
365
  - **Keys never reach a coder process.** Provider and Jev keys resolve only inside the worker or the
329
- orchestrator's own CLI calls (`env:<NAME>` → OS keychain → 1Password, cached briefly → ask at
330
- setup only). `keys list`/`keys test` never print a value; every stdout/stderr path is redacted.
366
+ orchestrator's own CLI calls (`env:<NAME>` → OS keychain → 1Password, cached for
367
+ `keys.cache_hours`: default 8 h, at most 24 h → ask at setup only). `keys list`/`keys test` never print a value; every stdout/stderr path is redacted.
331
368
  - **A forbidden-command list** stops a coder from starting its own worker, waiving a review, or
332
369
  running other orchestrator-only actions — rendered into each provider's own deny-list mechanism
333
370
  (`--disallowedTools` for Claude, an execpolicy rules file plus prose for Codex).
@@ -345,7 +382,8 @@ CLI verbs like any other and every one of them is exercised by this package's ow
345
382
  the warnings and the command list off. Every field is listed in [docs/privacy.md](docs/privacy.md).
346
383
  - **No mutation testing.** Neither this package's own tests nor the product it ships run mutation
347
384
  testing (Ricardo's ruling R14/Q16, 2026-09-25). Proof is plain unit and feature tests with exact
348
- assertions, plus a red→green runner that proves a new test fails before the fix and passes after.
385
+ assertions, plus a red→green runner that proves a new test fails before the fix and passes after (`node:test`
386
+ built in; any other runner through a `proof.runners` entry that writes a JUnit report).
349
387
 
350
388
  ## Configuration reference
351
389
 
@@ -364,6 +402,7 @@ new in 0.5, `autopilot.min_confidence` in 0.6):
364
402
  | `review.multimodel_for_docs` | `false` | let docs and contract blocks use multimodel review too |
365
403
  | `review.allow_open_book_codex` | `false` | let Codex run reviewer, judge, System 2 and plan author anyway |
366
404
  | `review.session_timeout_s` | `300` | a review session that runs longer is killed and retried once |
405
+ | `review.routes` | none | big files go to another model: a file with at least `min_lines` lines is reviewed by the rule's `model`, with its own `session_timeout_s` and `second` level |
367
406
  | `review.parallel_tickets` | `3` | review tickets the worker runs at once (1 to 16); `1` reviews one file at a time |
368
407
  | `review.provider_concurrency` | anthropic `4`, openai `2`, xai `2` | review sessions at once per provider |
369
408
  | `autopilot.min_confidence` | `0.7` | the lowest delegate confidence that is acted on; below it the question goes to you |
@@ -390,5 +429,6 @@ depends on, kept in sync with the maintainer's design plan.
390
429
 
391
430
  Releasing: record changes with `npm run changelog -- <type> "<text>"` and fixed error reports with
392
431
  `npm run known-fix -- add <fp> <version> "<summary>"`, then `npm run release -- <patch|minor|major|x.y.z> [--dry-run]`
393
- bumps, commits and tags (it never pushes or publishes). Publish with `npm publish` from your
432
+ bumps, moves the README's "In the next release:" bullets under the new version, checks the config
433
+ doc, commits and tags (it never pushes or publishes). Publish with `npm publish` from your
394
434
  machine, then push the tag: CI only creates the GitHub release. See [docs/releasing.md](docs/releasing.md).