@codedology/code-forge 0.9.0 → 0.10.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 (55) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +29 -8
  3. package/docs/reference/blocks.json +385 -1
  4. package/docs/reference/config.md +36 -0
  5. package/package.json +1 -1
  6. package/schema/code-forge.schema.json +89 -0
  7. package/skill/references/adapters/claude-code.md +1 -1
  8. package/skill/references/adapters/solo.md +2 -2
  9. package/skill/references/adapters/subprocess.md +12 -2
  10. package/skill/references/code.md +8 -3
  11. package/skill/references/ledger.md +2 -2
  12. package/skill/references/proof.md +77 -2
  13. package/skill/references/review.md +11 -4
  14. package/skill/references/security.md +4 -4
  15. package/skill/scripts/forge +1 -1
  16. package/skill/templates/coder-brief.md +2 -0
  17. package/src/autopilot/binnacle.mjs +9 -3
  18. package/src/cli/block.mjs +164 -24
  19. package/src/cli/jev.mjs +22 -3
  20. package/src/cli/keys.mjs +156 -21
  21. package/src/cli/proof.mjs +35 -7
  22. package/src/cli/spawn.mjs +44 -3
  23. package/src/config/validate.mjs +320 -5
  24. package/src/doctor/index.mjs +2 -1
  25. package/src/doctor/local.mjs +27 -6
  26. package/src/doctor/probes.mjs +38 -0
  27. package/src/engines/builders/codex.mjs +44 -4
  28. package/src/engines/sandbox-dirs.mjs +257 -0
  29. package/src/keys/store.mjs +128 -21
  30. package/src/proof/junit.mjs +530 -0
  31. package/src/proof/red-green.mjs +57 -9
  32. package/src/proof/red-parse.mjs +2 -0
  33. package/src/proof/runners.mjs +472 -0
  34. package/src/proof/xcresult.mjs +285 -0
  35. package/src/review/engine.mjs +82 -30
  36. package/src/review/fixloop.mjs +45 -13
  37. package/src/review/gate-check.mjs +3 -2
  38. package/src/review/lenses/A.md +1 -0
  39. package/src/review/lenses/B.md +1 -0
  40. package/src/review/lenses/full.md +1 -0
  41. package/src/review/lenses/quick.md +1 -0
  42. package/src/review/lenses/recheck.md +1 -0
  43. package/src/review/reopen.mjs +4 -6
  44. package/src/review/route.mjs +232 -0
  45. package/src/review/schema-fallback.mjs +77 -22
  46. package/src/review/session-retry.mjs +44 -9
  47. package/src/review/test-quality.mjs +201 -0
  48. package/src/review/verb-forms.mjs +39 -0
  49. package/src/session/spawn.mjs +39 -2
  50. package/src/state/block.mjs +13 -3
  51. package/src/state/config-snapshot.mjs +39 -0
  52. package/src/state/run.mjs +4 -0
  53. package/src/util/forbidden.mjs +2 -2
  54. package/src/worker/loop.mjs +79 -14
  55. package/src/worker/ticket.mjs +9 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,64 @@ waits on a second real consumer (plan §10.4, Q17).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.10.0] — 2026-10-13
10
+
11
+ ### Added
12
+
13
+ - `keys.cache_hours` (1–24 h, default 8) and `keys set --op … --cache-hours <n>` set how long a key read from 1Password
14
+ stays cached; a lower value shortens an existing cached key at the next read, `keys set --op` refuses an invalid value
15
+ and other readers warn and use 8 h (B84, #14).
16
+ - `keys list` and `doctor` warn when a cached 1Password key has less than 1 hour left, and `jev ask` prints a re-link
17
+ command (`code-forge keys set jev --op <ref>`, the ref shell-quoted when needed) when the cached key expired and
18
+ 1Password did not answer; the key index keeps the `op://` reference (B85, #14).
19
+ - `src/proof/junit.mjs`: a fail-closed JUnit XML reader and RED/GREEN classifier for `proof red-green` (no DTD or
20
+ entities, 8 MiB cap, only failed assertions count as RED; a SwiftPM bare failure needs a matching XCTest console
21
+ line); used by `proof.runners` from B87 (B86, #7).
22
+ - `proof.runners`: `proof red-green` proves a non-`node:test` file through the matching runner's argv and its JUnit
23
+ report; the list is frozen in the run snapshot, validated by rule 17 `proof-runners-invalid`, and the row carries
24
+ `runner`, `env_keys` and `failed_tests` (B87, #7).
25
+ - `swift` adapter for `proof.runners`: anchored `{test_filter}` (file base name or `Suite/test` case), reads the XCTest
26
+ report and its Swift Testing sibling, `swiftc` compile errors give `RED_INVALID compile`; opt-in live check
27
+ `CODE_FORGE_LIVE_SWIFT=1` verified on Swift 6.3.3 (B88, #7).
28
+ - PHP red→green through the `junit` adapter, verified live on Laravel 13 with PHPUnit 12 and Pest 4
29
+ (`CODE_FORGE_LIVE_PHP=1`); captured reports replace the hand-written PHP fixtures (B89, #7).
30
+ - The `xcodebuild` adapter for `proof.runners`: `xcode` keys, `{only_testing}` and `{result_bundle}` tokens, a precheck
31
+ for full Xcode and an available simulator (`xcode-missing`, `destination-unavailable`), a fail-closed `xcresulttool`
32
+ reader, and `RED_INVALID compile` for a build failure; fixtures captured from Xcode 26.6 (B102, #7).
33
+ - A live `xcodebuild` red→green check on an iOS simulator (`CODE_FORGE_LIVE_XCODE=1`), verified with Xcode 26.6 on an
34
+ iPhone 17 simulator; it skips with a reason without full Xcode or an available simulator (B103, #7).
35
+ - `review.routes`: size-based review routing. A file with at least `min_lines` lines is reviewed (L2) or judged (L3) by
36
+ the rule's model, with its own session timeout and second level; one signed `review.route` row per routed level;
37
+ config rule 18 `review-routes-invalid` (B90, #10).
38
+ - A `## test-quality` section in review packets lists the lines of a test file that read a source file and assert on its
39
+ text; every reviewer lens gets one rule to judge those lines (a hint, not a finding); recheck packets get the rule but
40
+ no section (B92, #10).
41
+ - `coder.sandbox` with `profile: workspace|toolchain|full` and `add_dirs`: `toolchain` gives the Codex coder one
42
+ `--add-dir` per entry, `full` runs it with `-s danger-full-access` and `doctor` warns; entries that are, hold or sit
43
+ inside a protected dir (home, `~/.ssh`, `~/.codex`, `~/.code-forge`, the run temp root, …) are refused
44
+ `sandbox-dir-refused` after symlinks are followed; the key is fixed for the run and `forge spawn --role coder --run`
45
+ refuses a changed live value `sandbox-changed` (B93, #10).
46
+
47
+ ### Changed
48
+
49
+ - A timed-out review session is a broken result like a schema miss: at a packet's second broken result the second level
50
+ (`review.second_levels.<level>`, or a routed rule's `second`) is tried once in the same ticket;
51
+ `review.session_timeout` notes carry `packet_hash` and are signed, and `review.schema_fallback` gains `trigger:
52
+ schema|timeout` (B91, #10).
53
+ - The skill sends ONE fix message per block: after the coder's first COMPLETE it waits for every `review-file` ticket
54
+ and the block's tests, then sends every failing test and every open finding together; a later finding waits for the
55
+ next round, and the per-file round cap still counts per file (B94, #10).
56
+ - `block waive` and `block reopen` are human-only: they ask for a yes at a terminal, have no `--yes`, exit 2 and record
57
+ nothing with stdin or stdout not a terminal, and refuse a bad id, path, control character, unowned file or unknown
58
+ finding before asking; an autopilot grant does not change this; the prompt does not prove a human, since a
59
+ pseudo-terminal passes it (B80, #10).
60
+
61
+ ### Fixed
62
+
63
+ - The JUnit reader accepts the real SwiftPM 6.3 bare XCTest failure (`message="failure"`), not only `failed`; captured
64
+ Swift reports replace the hand-written fixtures (B88, #7).
65
+ - A missing class in Laravel 13's JSON-escaped AI-agent console summary is now read as `import`, not `error` (B89, #7).
66
+
9
67
  ## [0.9.0] — 2026-10-12
10
68
 
11
69
  ### Added
package/README.md CHANGED
@@ -8,10 +8,27 @@ 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.9
12
12
 
13
13
  In the next release:
14
14
 
15
+ - **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.
16
+ - **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>`.
17
+ - **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`.
18
+ - **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`.
19
+ - **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.
20
+ - **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.
21
+ - **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.
22
+ - **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.
23
+ - **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.
24
+ - **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.
25
+ - **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.
26
+ - **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.
27
+ - **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.
28
+ - **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.
29
+
30
+ In 0.9.0:
31
+
15
32
  - **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
33
  - **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
34
  - **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 +45,8 @@ In the next release:
28
45
  - **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
46
  - **`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
47
 
48
+ ## What's new in 0.8
49
+
31
50
  In 0.8.0:
32
51
 
33
52
  - **Run only the tests a block touches.** `block open --tests <paths>` names the block's test
@@ -257,7 +276,7 @@ code-forge validate # loads and validates .code-forge.yml
257
276
  code-forge resolve L1 # {provider, model, effort, fallback, cli} for a level
258
277
  code-forge doctor --quick # config + links + key resolution only
259
278
  code-forge list # what is installed where, and whether it still resolves
260
- code-forge keys list # NAME / SOURCE / BACKEND / EXPIRES — never a value
279
+ code-forge keys list # NAME / SOURCE / BACKEND / EXPIRES — never a value; WARN under 1 h left
261
280
  code-forge models # the model catalog per provider
262
281
  code-forge --help # every verb, one per line
263
282
  code-forge version
@@ -291,7 +310,7 @@ usage line.
291
310
  | `validate` | `code-forge validate [--file <path>]` |
292
311
  | `resolve` | `code-forge resolve <L0\|L1\|L2\|L3>` |
293
312
  | `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) |
313
+ | `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
314
  | `worker` | `code-forge worker --run <id> [--cwd <dir>] [--poll-ms <n>] [--once]` — started detached by `run start`, never by a coder |
296
315
  | `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
316
  | `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 +319,10 @@ usage line.
300
319
  | `author` | `code-forge author --job plan\|harden --brief <file> [--facts <file>] [--draft <file>] [--answers <file>] [--out <file>] [--run <id>] [--timeout <seconds>]` |
301
320
  | `facts` | `code-forge facts --brief <file> [--sources <path>…] [--out <file>] [--run <id>] [--timeout <seconds>]` |
302
321
  | `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]` |
322
+ | `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
323
  | `gates` | `code-forge gates detect\|run\|secret-scan\|safe-edit\|scope\|acceptance\|transcript-grep --cwd <dir> …` |
305
324
  | `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>` |
325
+ | `keys` | `code-forge keys list \| set <name> [--op <item-id\|link\|op://ref> [--cache-hours <1-24>]] \| test <name> [--ref <ref>] \| remove <name>` |
307
326
  | `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
327
  | `report` | `code-forge report --slug <slug> [--json] [--export <dir>]` — 13 sections, then the spend per run (open blocks included) and the total |
309
328
  | `models` | `code-forge models [--refresh --from-cli-caches]` |
@@ -326,8 +345,8 @@ CLI verbs like any other and every one of them is exercised by this package's ow
326
345
  `doctor` always prints this line — the signature is not a defense against an attacker who
327
346
  already runs code as you, and no part of code-forge claims otherwise.
328
347
  - **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.
348
+ orchestrator's own CLI calls (`env:<NAME>` → OS keychain → 1Password, cached for
349
+ `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
350
  - **A forbidden-command list** stops a coder from starting its own worker, waiving a review, or
332
351
  running other orchestrator-only actions — rendered into each provider's own deny-list mechanism
333
352
  (`--disallowedTools` for Claude, an execpolicy rules file plus prose for Codex).
@@ -345,7 +364,8 @@ CLI verbs like any other and every one of them is exercised by this package's ow
345
364
  the warnings and the command list off. Every field is listed in [docs/privacy.md](docs/privacy.md).
346
365
  - **No mutation testing.** Neither this package's own tests nor the product it ships run mutation
347
366
  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.
367
+ assertions, plus a red→green runner that proves a new test fails before the fix and passes after (`node:test`
368
+ built in; any other runner through a `proof.runners` entry that writes a JUnit report).
349
369
 
350
370
  ## Configuration reference
351
371
 
@@ -364,6 +384,7 @@ new in 0.5, `autopilot.min_confidence` in 0.6):
364
384
  | `review.multimodel_for_docs` | `false` | let docs and contract blocks use multimodel review too |
365
385
  | `review.allow_open_book_codex` | `false` | let Codex run reviewer, judge, System 2 and plan author anyway |
366
386
  | `review.session_timeout_s` | `300` | a review session that runs longer is killed and retried once |
387
+ | `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
388
  | `review.parallel_tickets` | `3` | review tickets the worker runs at once (1 to 16); `1` reviews one file at a time |
368
389
  | `review.provider_concurrency` | anthropic `4`, openai `2`, xai `2` | review sessions at once per provider |
369
390
  | `autopilot.min_confidence` | `0.7` | the lowest delegate confidence that is acted on; below it the question goes to you |
@@ -4,7 +4,7 @@
4
4
  "plans/code-forge-plan-v1.3.md §10.4",
5
5
  "plans/NIGHT-LOG.md"
6
6
  ],
7
- "block_count": 88,
7
+ "block_count": 102,
8
8
  "baseline_ids": ["B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8"],
9
9
  "baseline_note": "Wave 0-1 blocks, landed before v1.3; depends_on may name them, but they have no entry under blocks.",
10
10
  "blocks": {
@@ -1239,6 +1239,390 @@
1239
1239
  "B76"
1240
1240
  ],
1241
1241
  "note": "root-added 2026-10-12; B75 leftovers (plans/reviews/b75/r3.txt a-d); a failed link-back writes a refused fresh-restore-failed marker or keeps the claim; a kept claim holds the ticket; block reopen clears claims"
1242
+ },
1243
+ "B84": {
1244
+ "title": "keys.cache_hours and keys set --cache-hours set the 1Password cache time",
1245
+ "wave": 14,
1246
+ "level": "L2",
1247
+ "status": "landed",
1248
+ "owned": [
1249
+ "schema/code-forge.schema.json",
1250
+ "docs/reference/config.md",
1251
+ "src/keys/store.mjs",
1252
+ "src/cli/keys.mjs",
1253
+ "src/cli/jev.mjs",
1254
+ "src/doctor/local.mjs",
1255
+ "src/worker/loop.mjs",
1256
+ "test/keys/cache-hours.test.mjs",
1257
+ "test/keys/cli.test.mjs",
1258
+ "test/config/schema-coverage.test.mjs",
1259
+ "test/config/schema.test.mjs",
1260
+ "docs/getting-started.md",
1261
+ "docs/how-it-works.md",
1262
+ "skill/references/security.md",
1263
+ "README.md"
1264
+ ],
1265
+ "depends_on": [
1266
+ "B83"
1267
+ ],
1268
+ "note": "root-added 2026-10-12; issue #14 part 1 (plans/keys-cache/plan.md); 1-24 h, default 8; a lower value shortens a cached key at the next read; bad config refuses set --op, warns elsewhere"
1269
+ },
1270
+ "B85": {
1271
+ "title": "key expiry warnings and the re-link line",
1272
+ "wave": 14,
1273
+ "level": "L2",
1274
+ "status": "landed",
1275
+ "owned": [
1276
+ "src/keys/store.mjs",
1277
+ "src/cli/keys.mjs",
1278
+ "src/cli/jev.mjs",
1279
+ "src/doctor/local.mjs",
1280
+ "test/keys/expiry-warn.test.mjs",
1281
+ "docs/getting-started.md",
1282
+ "docs/how-it-works.md",
1283
+ "skill/references/security.md",
1284
+ "README.md"
1285
+ ],
1286
+ "depends_on": [
1287
+ "B84"
1288
+ ],
1289
+ "note": "root-added 2026-10-12; issue #14 part 2 (plans/keys-cache/plan.md); index keeps the op ref; WARN under 1 h in keys list and doctor; jev ask prints the re-link line when the cache expired and 1Password fails; refs shell-quoted"
1290
+ },
1291
+ "B86": {
1292
+ "title": "fail-closed JUnit XML report reader for proof red-green",
1293
+ "wave": 14,
1294
+ "level": "L2",
1295
+ "status": "landed",
1296
+ "owned": [
1297
+ "src/proof/junit.mjs",
1298
+ "test/proof/junit.test.mjs",
1299
+ "test/fixtures/junit/xctest.xml",
1300
+ "test/fixtures/junit/swift-testing.xml",
1301
+ "test/fixtures/junit/phpunit.xml",
1302
+ "test/fixtures/junit/pest.xml",
1303
+ "test/fixtures/junit/pytest.xml",
1304
+ "skill/references/proof.md",
1305
+ "test/skill/structure.test.mjs"
1306
+ ],
1307
+ "depends_on": [
1308
+ "B85"
1309
+ ],
1310
+ "note": "root-added 2026-10-13; issue #7 part 1 (plans/red-green/plan.md); hand-written reader, no DTD/entities, 8 MiB cap, O_NOFOLLOW|O_NONBLOCK; RED only on assertion failures; SwiftPM bare failure needs a matching XCTest console line; not wired into proof yet (B87)"
1311
+ },
1312
+ "B87": {
1313
+ "title": "proof.runners: prove non-node:test files through a runner argv and its JUnit report",
1314
+ "wave": 14,
1315
+ "level": "L2",
1316
+ "status": "landed",
1317
+ "owned": [
1318
+ "src/proof/runners.mjs",
1319
+ "src/proof/red-green.mjs",
1320
+ "src/cli/proof.mjs",
1321
+ "src/config/validate.mjs",
1322
+ "schema/code-forge.schema.json",
1323
+ "src/state/config-snapshot.mjs",
1324
+ "src/state/block.mjs",
1325
+ "src/state/run.mjs",
1326
+ "src/proof/junit.mjs",
1327
+ "docs/concepts.md",
1328
+ "docs/how-it-works.md",
1329
+ "docs/tutorial-first-test.md",
1330
+ "docs/reference/config.md",
1331
+ "skill/references/proof.md",
1332
+ "skill/references/code.md",
1333
+ "README.md",
1334
+ "test/proof/runners.test.mjs",
1335
+ "test/proof/red-green.test.mjs",
1336
+ "test/proof/junit.test.mjs",
1337
+ "test/config/validate.test.mjs",
1338
+ "test/config/schema-coverage.test.mjs",
1339
+ "test/skill/structure.test.mjs"
1340
+ ],
1341
+ "depends_on": [
1342
+ "B86"
1343
+ ],
1344
+ "note": "root-added 2026-10-13; issue #7 part 2 (plans/red-green/plan.md, C-D1=a); runners frozen in the run snapshot; argv only, GIT_* stripped; rule 17 proof-runners-invalid; no-hook match refused exit 2; env_keys names only; swift runs as junit until B88"
1345
+ },
1346
+ "B88": {
1347
+ "title": "swift adapter for proof.runners, checked live with swift test",
1348
+ "wave": 14,
1349
+ "level": "L2",
1350
+ "status": "landed",
1351
+ "owned": [
1352
+ "src/proof/runners.mjs",
1353
+ "src/proof/junit.mjs",
1354
+ "src/proof/red-green.mjs",
1355
+ "test/proof/swift-adapter.test.mjs",
1356
+ "test/proof/swift-live.test.mjs",
1357
+ "test/proof/junit.test.mjs",
1358
+ "test/proof/runners.test.mjs",
1359
+ "test/fixtures/junit/xctest.xml",
1360
+ "test/fixtures/junit/xctest-pass.xml",
1361
+ "test/fixtures/junit/xctest-none.xml",
1362
+ "test/fixtures/junit/swift-testing.xml",
1363
+ "test/fixtures/junit/swift-testing-pass.xml",
1364
+ "test/fixtures/junit/swift-testing-none.xml",
1365
+ "test/fixtures/junit/swift-testing-crash.xml",
1366
+ "test/fixtures/junit/pest.xml",
1367
+ "test/fixtures/junit/pytest.xml",
1368
+ "skill/references/proof.md",
1369
+ "test/skill/structure.test.mjs"
1370
+ ],
1371
+ "depends_on": [
1372
+ "B87"
1373
+ ],
1374
+ "note": "root-added 2026-10-13; issue #7 part 3 (plans/red-green/plan.md §1.3); live VERIFIED Swift 6.3.3 / Xcode 26.6 / macOS 26 arm64; real SwiftPM bare failure is message=\"failure\"; --parallel needed for the XCTest report; filters anchored; swiftc compile shapes; B87 r3 leftovers folded in"
1375
+ },
1376
+ "B89": {
1377
+ "title": "PHP red-green through the junit adapter, checked live on Laravel (PHPUnit and Pest)",
1378
+ "wave": 14,
1379
+ "level": "L1",
1380
+ "status": "landed",
1381
+ "owned": [
1382
+ "test/proof/laravel-live.test.mjs",
1383
+ "test/fixtures/junit/phpunit.xml",
1384
+ "test/fixtures/junit/phpunit-pass.xml",
1385
+ "test/fixtures/junit/phpunit-new-class.xml",
1386
+ "test/fixtures/junit/phpunit-full.xml",
1387
+ "test/fixtures/junit/pest.xml",
1388
+ "test/fixtures/junit/pest-pass.xml",
1389
+ "test/fixtures/junit/pest-new-class.xml",
1390
+ "src/proof/red-parse.mjs",
1391
+ "test/proof/red-parse.test.mjs",
1392
+ "test/proof/junit.test.mjs",
1393
+ "skill/references/proof.md",
1394
+ "docs/how-it-works.md",
1395
+ "test/skill/structure.test.mjs"
1396
+ ],
1397
+ "depends_on": [
1398
+ "B88"
1399
+ ],
1400
+ "note": "root-added 2026-10-13; issue #7 part 4 (plans/red-green/plan.md §1.4); live VERIFIED PHP 8.4.25, Laravel 13.35.0, PHPUnit 12.5.39, Pest 4.7.8 through php artisan test; red-parse reads Laravel 13's JSON-escaped agent console (folded-in candidate B108); no <warning> seen"
1401
+ },
1402
+ "B102": {
1403
+ "title": "The xcodebuild adapter for proof.runners: Xcode precheck, -only-testing, result bundle reader",
1404
+ "wave": 14,
1405
+ "level": "L2",
1406
+ "status": "landed",
1407
+ "owned": [
1408
+ "src/proof/xcresult.mjs",
1409
+ "src/proof/runners.mjs",
1410
+ "src/proof/junit.mjs",
1411
+ "src/config/validate.mjs",
1412
+ "schema/code-forge.schema.json",
1413
+ "docs/reference/config.md",
1414
+ "skill/references/proof.md",
1415
+ "test/proof/xcodebuild-adapter.test.mjs",
1416
+ "test/proof/runners.test.mjs",
1417
+ "test/config/schema-coverage.test.mjs",
1418
+ "test/skill/structure.test.mjs",
1419
+ "test/fixtures/xcresult/bare-fail.json",
1420
+ "test/fixtures/xcresult/build-failed.console",
1421
+ "test/fixtures/xcresult/build-failed.json",
1422
+ "test/fixtures/xcresult/build-for-testing-failed.console",
1423
+ "test/fixtures/xcresult/near-names.json",
1424
+ "test/fixtures/xcresult/swift-testing-case.json",
1425
+ "test/fixtures/xcresult/swift-testing-noparens.json",
1426
+ "test/fixtures/xcresult/swift-testing-pass.json",
1427
+ "test/fixtures/xcresult/swift-testing.console",
1428
+ "test/fixtures/xcresult/swift-testing.json",
1429
+ "test/fixtures/xcresult/thrown.console",
1430
+ "test/fixtures/xcresult/thrown.json",
1431
+ "test/fixtures/xcresult/xctest-case.json",
1432
+ "test/fixtures/xcresult/xctest-pass.json",
1433
+ "test/fixtures/xcresult/xctest.console",
1434
+ "test/fixtures/xcresult/xctest.json"
1435
+ ],
1436
+ "depends_on": [
1437
+ "B89"
1438
+ ],
1439
+ "note": "root-added 2026-10-13; issue #7 part 5 (plans/red-green/plan.md §1.6); fixtures captured from Xcode 26.6 (17F113), iPhone 17 simulator iOS 26.5, xcresulttool schema 0.1.0; Swift Testing -only-testing needs (); exit 0 with no test, no bundle, an unreadable bundle or an out-of-scope test is RED_INVALID error; parameterized Swift Testing, repetitions, multi-destination, Runtime Warning and Expected Failure make the bundle unreadable (fail closed)"
1440
+ },
1441
+ "B103": {
1442
+ "title": "A live xcodebuild red-green check on an iOS simulator, opt-in",
1443
+ "wave": 14,
1444
+ "level": "L2",
1445
+ "status": "landed",
1446
+ "owned": [
1447
+ "test/proof/xcodebuild-live.test.mjs",
1448
+ "skill/references/proof.md",
1449
+ "docs/how-it-works.md",
1450
+ "test/skill/structure.test.mjs"
1451
+ ],
1452
+ "depends_on": [
1453
+ "B102"
1454
+ ],
1455
+ "note": "root-added 2026-10-13; issue #7 part 6 (plans/red-green/plan.md §1.6); live VERIFIED Xcode 26.6 (17F113), iPhone 17 simulator iOS 26.5; XCTest and Swift Testing, file and case runs RED assertion → GREEN, new function RED_INVALID compile, ghost destination destination-unavailable; live JSON matched the B102 fixtures (kept)"
1456
+ },
1457
+ "B90": {
1458
+ "title": "Size-based review routing: review.routes",
1459
+ "wave": 14,
1460
+ "level": "L2",
1461
+ "status": "landed",
1462
+ "owned": [
1463
+ "src/review/route.mjs",
1464
+ "src/worker/loop.mjs",
1465
+ "src/worker/ticket.mjs",
1466
+ "src/config/validate.mjs",
1467
+ "schema/code-forge.schema.json",
1468
+ "docs/reference/config.md",
1469
+ "skill/references/review.md",
1470
+ "docs/how-it-works.md",
1471
+ "docs/review-only.md",
1472
+ "test/review/route.test.mjs",
1473
+ "test/config/validate-routes.test.mjs",
1474
+ "test/config/validate.test.mjs",
1475
+ "test/config/schema-coverage.test.mjs",
1476
+ "test/skill/structure.test.mjs"
1477
+ ],
1478
+ "depends_on": [
1479
+ "B103"
1480
+ ],
1481
+ "note": "root-added 2026-10-13; issue #10 item 4 (plans/iteration/plan.md B90); rule 18 review-routes-invalid; signed review.route rows before the first session, once per ticket; no routes = byte-equal to 8fadc12"
1482
+ },
1483
+ "B91": {
1484
+ "title": "Two broken results (schema or timeout) move a packet to the second level",
1485
+ "wave": 14,
1486
+ "level": "L2",
1487
+ "status": "landed",
1488
+ "owned": [
1489
+ "src/review/session-retry.mjs",
1490
+ "src/review/schema-fallback.mjs",
1491
+ "src/review/engine.mjs",
1492
+ "src/review/fixloop.mjs",
1493
+ "src/autopilot/binnacle.mjs",
1494
+ "skill/references/review.md",
1495
+ "docs/how-it-works.md",
1496
+ "docs/review-only.md",
1497
+ "test/review/broken-fallback.test.mjs",
1498
+ "test/review/schema-fallback.test.mjs",
1499
+ "test/worker/schema-fallback-wiring.test.mjs",
1500
+ "test/autopilot/binnacle.test.mjs",
1501
+ "test/skill/structure.test.mjs"
1502
+ ],
1503
+ "depends_on": [
1504
+ "B90"
1505
+ ],
1506
+ "note": "root-added 2026-10-13; issue #10 (plans/iteration/plan.md B91); review.session_timeout notes carry packet_hash and are signed; review.schema_fallback gains trigger schema|timeout; no second level = as before plus packet_hash"
1507
+ },
1508
+ "B92": {
1509
+ "title": "Test-quality hint: a test that reads source text is listed for the reviewer",
1510
+ "wave": 14,
1511
+ "level": "L1",
1512
+ "status": "landed",
1513
+ "owned": [
1514
+ "src/review/test-quality.mjs",
1515
+ "src/review/engine.mjs",
1516
+ "src/review/lenses/A.md",
1517
+ "src/review/lenses/B.md",
1518
+ "src/review/lenses/full.md",
1519
+ "src/review/lenses/quick.md",
1520
+ "src/review/lenses/recheck.md",
1521
+ "skill/references/code.md",
1522
+ "skill/references/review.md",
1523
+ "skill/templates/coder-brief.md",
1524
+ "docs/how-it-works.md",
1525
+ "test/review/test-quality.test.mjs",
1526
+ "test/review/fixloop.test.mjs",
1527
+ "test/review/route.test.mjs",
1528
+ "test/skill/structure.test.mjs"
1529
+ ],
1530
+ "depends_on": [
1531
+ "B91"
1532
+ ],
1533
+ "note": "root-added 2026-10-13; issue #10 item 3 (plans/iteration/plan.md B92); a pure detector lists test lines that read a source file and assert on its text; the packet gets a ## test-quality hint section, the lens rule tells the reviewer to judge it; rechecks get the rule but no section; lens A/B order test sorted (flake fixed)"
1534
+ },
1535
+ "B93": {
1536
+ "title": "coder.sandbox: a run-fixed sandbox profile for the Codex coder",
1537
+ "wave": 14,
1538
+ "level": "L2",
1539
+ "status": "landed",
1540
+ "owned": [
1541
+ "src/engines/builders/codex.mjs",
1542
+ "src/session/spawn.mjs",
1543
+ "src/cli/spawn.mjs",
1544
+ "src/engines/sandbox-dirs.mjs",
1545
+ "src/state/config-snapshot.mjs",
1546
+ "src/state/run.mjs",
1547
+ "schema/code-forge.schema.json",
1548
+ "src/config/validate.mjs",
1549
+ "docs/reference/config.md",
1550
+ "src/doctor/probes.mjs",
1551
+ "src/doctor/index.mjs",
1552
+ "skill/references/adapters/subprocess.md",
1553
+ "skill/references/security.md",
1554
+ "docs/brief-mode.md",
1555
+ "docs/how-it-works.md",
1556
+ "test/engines/sandbox-profile.test.mjs",
1557
+ "test/config/validate.test.mjs",
1558
+ "test/config/schema-coverage.test.mjs",
1559
+ "test/skill/structure.test.mjs"
1560
+ ],
1561
+ "depends_on": [
1562
+ "B92"
1563
+ ],
1564
+ "note": "root-added 2026-10-13; issue #10 item 1 (plans/iteration/plan.md B93); profiles workspace (default, today's argv), toolchain (--add-dir per entry), full (danger-full-access, doctor WARN); refused dirs checked after a per-component symlink walk with native realpath; rule 19 sandbox-dir-refused, rule 20 sandbox-dirs-unused (WARN); immutable key; spawn --role coder --run checks the snapshot (sandbox-changed)"
1565
+ },
1566
+ "B94": {
1567
+ "title": "Skill: one fix round per block",
1568
+ "wave": 14,
1569
+ "level": "L1",
1570
+ "status": "landed",
1571
+ "owned": [
1572
+ "skill/references/code.md",
1573
+ "skill/references/review.md",
1574
+ "skill/references/adapters/solo.md",
1575
+ "skill/references/adapters/claude-code.md",
1576
+ "skill/references/adapters/subprocess.md",
1577
+ "docs/how-it-works.md",
1578
+ "test/skill/structure.test.mjs",
1579
+ "docs/concepts.md",
1580
+ "docs/brief-mode.md",
1581
+ "docs/autopilot.md",
1582
+ "skill/references/ledger.md"
1583
+ ],
1584
+ "depends_on": [
1585
+ "B93"
1586
+ ],
1587
+ "note": "root-added 2026-10-13; issue #10 item 5 (plans/iteration/plan.md B94); skill and docs only; the full rule word for word in code.md §4, solo.md §3 step 7 and how-it-works; one fixed pointer sentence in the other docs; the per-file cap counts per file"
1588
+ },
1589
+ "B80": {
1590
+ "title": "Human-only verbs: waive and reopen ask at a terminal",
1591
+ "wave": 14,
1592
+ "level": "L2",
1593
+ "status": "landed",
1594
+ "owned": [
1595
+ "docs/concepts.md",
1596
+ "docs/getting-started.md",
1597
+ "docs/how-it-works.md",
1598
+ "docs/tutorial-first-test.md",
1599
+ "skill/references/adapters/claude-code.md",
1600
+ "skill/references/adapters/solo.md",
1601
+ "skill/references/code.md",
1602
+ "skill/references/ledger.md",
1603
+ "skill/references/proof.md",
1604
+ "skill/references/review.md",
1605
+ "skill/references/security.md",
1606
+ "src/cli/block.mjs",
1607
+ "src/review/gate-check.mjs",
1608
+ "src/review/reopen.mjs",
1609
+ "src/review/verb-forms.mjs",
1610
+ "src/util/forbidden.mjs",
1611
+ "test/cli/block-human-confirm.test.mjs",
1612
+ "test/cli/status.test.mjs",
1613
+ "test/helpers/human-confirm.mjs",
1614
+ "test/review/answers.test.mjs",
1615
+ "test/review/close-gate.test.mjs",
1616
+ "test/review/gate-check.test.mjs",
1617
+ "test/review/reopen.test.mjs",
1618
+ "test/review/waived-all.test.mjs",
1619
+ "test/skill/structure.test.mjs",
1620
+ "test/state/block-end.test.mjs"
1621
+ ],
1622
+ "depends_on": [
1623
+ "B94"
1624
+ ],
1625
+ "note": "root-added 2026-10-13; issue #10 owner item 1 (plans/iteration/plan.md B80, D1 = a); block waive and block reopen ask the human for a yes at a terminal, no --yes, no terminal exits 2 and records nothing; pre-checks run before the question; a pseudo-terminal passes the check (honest limit in security.md §3); D1 (b) is a later option"
1242
1626
  }
1243
1627
  }
1244
1628
  }