@codedology/code-forge 0.7.7 → 0.9.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 (69) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +68 -2
  3. package/docs/README.md +2 -1
  4. package/docs/reference/blocks.json +390 -1
  5. package/docs/reference/config.md +26 -8
  6. package/package.json +1 -1
  7. package/schema/code-forge.schema.json +81 -4
  8. package/skill/SKILL.md +6 -6
  9. package/skill/references/code.md +45 -7
  10. package/skill/references/ledger.md +8 -4
  11. package/skill/references/proof.md +44 -3
  12. package/skill/references/review.md +25 -5
  13. package/skill/references/security.md +1 -1
  14. package/skill/scripts/forge +1 -1
  15. package/skill/templates/coder-brief.md +19 -2
  16. package/src/autopilot/binnacle.mjs +21 -3
  17. package/src/cli/block.mjs +427 -41
  18. package/src/cli/context.mjs +175 -0
  19. package/src/cli/gates.mjs +125 -15
  20. package/src/cli/ledger.mjs +46 -2
  21. package/src/cli/proof.mjs +62 -16
  22. package/src/cli/report.mjs +7 -4
  23. package/src/cli/review-file.mjs +324 -18
  24. package/src/cli/status.mjs +212 -0
  25. package/src/config/validate.mjs +45 -11
  26. package/src/context/pack.mjs +319 -0
  27. package/src/doctor/index.mjs +5 -4
  28. package/src/doctor/local.mjs +91 -3
  29. package/src/engines/sentinel.mjs +27 -6
  30. package/src/gates/detect.mjs +21 -8
  31. package/src/gates/expand.mjs +202 -0
  32. package/src/gates/owned-count.mjs +66 -0
  33. package/src/gates/run.mjs +49 -12
  34. package/src/install/wizard/answers.mjs +34 -5
  35. package/src/install/wizard/flags.mjs +8 -1
  36. package/src/install/wizard/project-file.mjs +2 -0
  37. package/src/install/wizard/run.mjs +6 -0
  38. package/src/install/wizard/steps.mjs +24 -4
  39. package/src/install/wizard/summary.mjs +13 -5
  40. package/src/ledger/export.mjs +1 -1
  41. package/src/ledger/outside-review.mjs +164 -0
  42. package/src/ledger/report.mjs +17 -5
  43. package/src/ledger/write.mjs +7 -1
  44. package/src/proof/export.mjs +93 -10
  45. package/src/proof/lock.mjs +28 -5
  46. package/src/proof/worktree.mjs +482 -0
  47. package/src/review/answers.mjs +445 -0
  48. package/src/review/deletion.mjs +106 -0
  49. package/src/review/engine.mjs +36 -2
  50. package/src/review/fixloop.mjs +25 -15
  51. package/src/review/gate-check.mjs +384 -83
  52. package/src/review/open-findings.mjs +59 -0
  53. package/src/review/reopen.mjs +258 -0
  54. package/src/review/stop-exits.mjs +15 -0
  55. package/src/state/block-end.mjs +290 -0
  56. package/src/state/block.mjs +103 -39
  57. package/src/state/brief-status.mjs +131 -0
  58. package/src/state/config-snapshot.mjs +51 -0
  59. package/src/state/registry.mjs +35 -4
  60. package/src/state/run.mjs +10 -1
  61. package/src/util/breadcrumbs.mjs +1 -0
  62. package/src/util/forbidden.mjs +46 -1
  63. package/src/util/gate-token.mjs +8 -0
  64. package/src/util/tmp.mjs +16 -4
  65. package/src/worker/engine.mjs +230 -24
  66. package/src/worker/loop.mjs +29 -8
  67. package/src/worker/queue.mjs +296 -15
  68. package/src/worker/review-state.mjs +346 -51
  69. package/src/worker/ticket.mjs +46 -6
package/CHANGELOG.md CHANGED
@@ -6,6 +6,103 @@ waits on a second real consumer (plan §10.4, Q17).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.0] — 2026-10-12
10
+
11
+ ### Added
12
+
13
+ - review-file --answers <ids> --of <source>: a fresh L2 session judges a fix made in another owned file; resolved ids
14
+ get signed review.answered rows bound to both content hashes, and the close gate counts a still-current answer like a
15
+ waiver (answered list on gate.review_waived_all).
16
+ - block reopen <id> --run <r> --file <path> --reason <text>: a human-only verb that restarts a stopped file on changed
17
+ content (or a lost one on its current content) with a full round 1; signed review.reopened row, one per anchor seq;
18
+ refusal codes reopen-same-content, reopen-tampered, reopen-not-stopped, reopen-block-not-open, reopen-not-owned,
19
+ reopen-config, reopen-already-recorded, reopen-write-failed.
20
+ - A stopped `review-file` answer carries `exits`, and the gate's `review_cap` and stopped-file `unreviewed` detail end
21
+ with the same exits (B73, issue #12).
22
+ - The review packet of a deleted owned file carries a `## deleted file` note: up to 20 tracked or untracked paths that
23
+ still name its basename (one bounded `git grep`, secret-like and control-character paths left out), `no references
24
+ found`, or `references not checked` (B74, issue #8).
25
+ - `review-file --fresh` drops a cached result that never counted and queues the ticket again; counted results refuse
26
+ `fresh-needs-reopen`, tampered ones `fresh-tampered`, and the cached marker is never moved (B75, issue #10 item 4).
27
+
28
+ ### Changed
29
+
30
+ - Review state is saved under ~/.code-forge/runs/<run>/review-state/ instead of the temp root; older state files are
31
+ moved once after an anchor check; doctor warns about missing state files of active runs (#15)
32
+ - Review state: the reviewed text is stored as a git blob (reviewed_content_blob in the signed review.state body), so a
33
+ restored state keeps reviewed_content after the file changed; a missing, malformed or mismatching blob falls back to
34
+ the full-file review.
35
+ - A `patch_check` that closes at least one finding open before the patch (by id) and is under
36
+ `review.max_rounds_per_file` continues the fix loop at L2; a patch that closes nothing still stops
37
+ `l3_patch_exhausted` (B73, issue #12).
38
+ - The skill and docs name the review exits in order, say where review state lives and how the ledger rebuilds it; the
39
+ coder brief names `review-file --answers`, and the security reference lists `block reopen` as forbidden to a coder
40
+ (B76).
41
+
42
+ ### Fixed
43
+
44
+ - A lost review state file is rebuilt from the signed ledger and recorded as review.state_restored; rounds, findings and
45
+ stops stay; with no ledger copy the ticket is refused as review-state-lost, not tampered (#15)
46
+ - A file whose every finding is cleared by accepted waivers counts as reviewed (reviewed_by_waiver) and block close
47
+ writes a signed gate.review_waived_all row; unknown severities are human-only (#9)
48
+ - Test isolation guard: new files of a run that existed before the test process (its lock, key or run directory) no
49
+ longer count as leaks; a new run id or any other new path still fails the process.
50
+ - `review-file --answers` reads the severity of every `review.cap` id from the signed refusal rows, so an id the close
51
+ cannot count is refused before a reviewer session (B79).
52
+ - The answers packet checks the source file with the secret-path rule, redacts each part before it is cut, keeps every
53
+ part and the whole packet inside the budget, and refuses `packet-too-large` when the fixed text alone is over it
54
+ (B79).
55
+ - `block reopen` refuses a non-string `tmp.root` as `reopen-config`, and `reopen-write-failed` names the error code,
56
+ never a path (B79).
57
+ - The ledger rotation race test forces the order of the two racers with an `afterLink` hook instead of scheduling them,
58
+ so it no longer fails at random under load (B81).
59
+ - The worker restart test waits up to 5 s for killed sessions to exit instead of checking once, so it no longer fails at
60
+ random under load (B82).
61
+ - `review-file --fresh`: a failed restore of the done marker no longer leaves the ticket looking pending; it writes a
62
+ refused `fresh-restore-failed` marker or keeps the claim, `block reopen` clears leftover claims, and the messages say
63
+ which state was left (B83).
64
+
65
+ ## [0.8.0] — 2026-10-12
66
+
67
+ ### Added
68
+
69
+ - B60: related-test gates (issue #13 item 3). block open --tests <paths> names the test files of a block; a gate argv
70
+ item exactly {test_files} becomes those files, or the full suite runs when the block owns more than
71
+ gates.full_suite_threshold_files files or no test file is known. Gate rows carry scope, reason and test_count. A gate
72
+ can be {argv, env}; env values are never printed or recorded, and child output masks values of 8 or more characters.
73
+ - B61: Laravel under export isolation is verified. A Laravel or plain PHP project on PHPUnit without Pest is detected as
74
+ php-phpunit. Export copies a Composer vendor folder instead of linking it, so PHP autoloads the code of the block and
75
+ not the main tree. An opt-in live check (CODE_FORGE_LIVE_PHP=1) runs a Laravel app on PHPUnit and on Pest through the
76
+ export.
77
+ - proof.isolation: worktree — each block gets its own git worktree and branch (proof.worktree.dir/branch/base); owned
78
+ files need to be disjoint only inside one tree; block close --remove-worktree removes the tree and keeps the branch
79
+ (issue #13)
80
+ - Worktree blocks: gates run --block, review-file, the worker, block close and proof export/red-green all measure the
81
+ block's own tree (against its base), never the main checkout; a missing, malformed, off-branch or foreign tree is
82
+ refused (issue #13)
83
+ - Context pack: a top-level `context` key (rules, lessons, commands, max_lines) and `code-forge context pack --run
84
+ --block [--json]`, which writes the pack, prints its path and token estimate and appends a signed `context.pack` row;
85
+ review packets carry the rules and lessons (issue #13)
86
+ - Coder test proof: the coder report may carry `TESTS <command> exit=<n>`; with `context.require_coder_tests: true`
87
+ (fixed at run start) `block close --report` refuses a report without a green TESTS line and writes a signed
88
+ `coder.tests` row (issue #13)
89
+ - code-forge status: at most 10 lines per run (state, mode, spend, open blocks with level, model, last gate, open
90
+ findings) or one --json line; every block verb takes --json and prints one JSON line (issue #13)
91
+ - Block-end signal: every block end writes <run root>/blocks/<id>.done.json; hooks.on_block_end runs a command once per
92
+ block end (no shell, timeout, signed hook.ran row); a failing hook only warns (issue #13)
93
+ - ledger outside-review records an outside reviewer's rounds and findings per PR (signed outside_review row, link
94
+ redacted); report gets a 14th section, outside_review, with the code-forge cost of the PR's blocks next to the rounds
95
+ (issue #13)
96
+ - docs/brief-mode.md walks a brief-only run with an outside reviewer, related tests, a worktree, the context pack and
97
+ the no-poll flow; a test runs the page's config and steps; the skill says when to use brief mode or plan mode (issue
98
+ #13)
99
+
100
+ ### Changed
101
+
102
+ - BREAKING: autonomy.coder_may_push and autonomy.coder_may_open_draft_pr now default to false, so a coder no longer
103
+ pushes or opens a draft PR unless the config allows it; init asks both (default no) and takes --coder-may-push /
104
+ --coder-may-open-draft-pr yes|no; set both to true to keep the old behaviour (issue #13)
105
+
9
106
  ## [0.7.7] — 2026-10-11
10
107
 
11
108
  ### Added
package/README.md CHANGED
@@ -8,9 +8,75 @@ 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
12
+
13
+ In the next release:
14
+
15
+ - **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
+ - **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
+ - **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).
18
+ - **Tests no longer fail because of your own live run.** The test guard used to fail when a code-forge run you had open wrote its lock file during `npm test`. Now new files of a run that existed before the tests are not counted as leaks. A new run or any other new file still fails the tests.
19
+ - **A restore brings back the exact reviewed text.** The reviewed text is now also kept as a git object, and the signed ledger points to it. After a lost state file is rebuilt, the next round reviews only what changed since the last review, even when the file changed. If git has removed the object, the round reviews the full file, as before (#15).
20
+ - **A fix in another file can answer a finding.** `review-file <file> --block <id> --answers <ids> --of <source>` asks a fresh reviewer whether a change in another owned file of the block fixes findings of the source file. Each finding it judges `resolved` gets a signed `review.answered` row, and the source file can then close as `reviewed_by_waiver` with an `answered` list. If either file changes later, the answer no longer counts. The answering file still needs its own approval (#9).
21
+ - **`block reopen` restarts a stopped file.** After you change a file whose review stopped, `code-forge block reopen <id> --run <r> --file <path> --reason <text>` starts a fresh round 1 for it. A file whose review state is lost can be reopened on its current content. Only a human may run it: a coder's command line is blocked, like `block waive`. The block's one L3 step stays used. The same content is refused, so you cannot simply roll again (#12, #15).
22
+ - **Safer answers packets and clearer reopen errors (B79).** An id named only by a `review.cap` row with no recorded severity is refused before any reviewer session. The answers packet checks the source file for secrets, stays inside the packet budget and refuses `packet-too-large` instead of going over it. `block reopen` refuses a bad `tmp.root` and names the cause of a failed write.
23
+ - **A last-chance patch that fixes something is progress (B73).** When the L3 patch closes at least one old finding and the file is under the round cap, the fix loop goes on at L2 instead of stopping `l3_patch_exhausted`. A stopped answer from `review-file` and the gate's `review_cap` detail now list the exits: `block reopen`, `block waive`, `review-file --answers`, re-decompose.
24
+ - **A steadier test suite (B81).** The ledger rotation race test forces the race order through a test hook, so it no longer fails at random under load.
25
+ - **Deleted files get a useful review (B74).** When a block deletes an owned file, the review packet says the file is deleted and lists up to 20 files that still name it, so the reviewer can look for dangling references. A deletion still needs an approval.
26
+ - **`review-file --fresh` (B75).** Runs a file's review again only when the cached result never counted (`stale`, `unavailable`, or a non-verdict refusal such as `other-run`). A counted result is refused `fresh-needs-reopen` and names `block reopen` and `block waive`; a tampered one is refused `fresh-tampered`. A coder may use it.
27
+ - **Another steadier test (B82).** The worker restart test waits for killed sessions to exit instead of checking once, so it no longer fails at random under load.
28
+ - **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
+ - **`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
+
31
+ In 0.8.0:
32
+
33
+ - **Run only the tests a block touches.** `block open --tests <paths>` names the block's test
34
+ files. A gate argv item exactly `{test_files}` becomes those files. When the block owns more
35
+ files than `gates.full_suite_threshold_files`, or no test file is known, the full suite runs.
36
+ The ledger row says which one ran. A gate can also be `{argv, env}`. Env values are never
37
+ printed. Gate env is not a secret store.
38
+ - **Laravel works under export, checked live.** A Laravel app on PHPUnit (no Pest) is now
39
+ detected. Export copies the Composer `vendor` folder instead of linking it, so the tests load
40
+ the block's code, not the main tree's. A live check (`CODE_FORGE_LIVE_PHP=1`) runs a Laravel
41
+ app on PHPUnit and on Pest through the export.
42
+ - **One git worktree per block.** With `proof.isolation: worktree`, `block open` gives each block
43
+ its own git worktree and branch (`proof.worktree.dir`, `branch`, `base`). Blocks in different
44
+ trees may own the same files. Nothing is pushed. `block close --remove-worktree` removes the
45
+ tree and keeps the branch. Gates, reviews, the close check and the mutation proof of that
46
+ block all run on the block's tree, never on the main checkout.
47
+ - **A context pack for coders.** Set `context: {rules, lessons, commands, max_lines}`.
48
+ `code-forge context pack --run <r> --block <id>` writes one file with the block's header,
49
+ the build and test commands, the project rules and the lessons, cut to `max_lines`. It prints
50
+ the path and a token estimate, and writes a signed `context.pack` row. Review packets now
51
+ carry the same rules and lessons. With no `context` key, nothing changes.
52
+ - **Coders prove a green test run.** A coder prints `TESTS <command> exit=<n>` just before its
53
+ COMPLETE sentinel. With `context.require_coder_tests: true` (fixed at `run start`),
54
+ `block close --report` refuses a report with no such line or a non-zero exit, even when
55
+ reviews are waived, and writes a signed `coder.tests` row when it passes.
56
+ - **Short status.** `code-forge status [--run <id>] [--json]` prints at most 10 lines per run:
57
+ state, mode, spend, and one line per open block with its level, model, last gate and open
58
+ findings. It is read-only. Every `block` verb takes `--json` and prints one JSON line, so an
59
+ orchestrator never has to read logs or poll transcripts.
60
+ - **A signal when a block ends.** Every block end writes `~/.code-forge/runs/<run>/blocks/<id>.done.json`
61
+ (status, commit, spend, open findings). With `hooks.on_block_end` (fixed at `run start`),
62
+ code-forge also runs your command once per block end, with the block id, status and file path
63
+ in its env, under a timeout, and writes a signed `hook.ran` row. A failing hook only warns.
64
+ - **Breaking: coders no longer push or open draft PRs by default.** `autonomy.coder_may_push` and
65
+ `autonomy.coder_may_open_draft_pr` now default to `false`. `init` asks both (default no) and
66
+ writes your answers. To keep the old behaviour, set both to `true` in `.code-forge.yml`.
67
+ - **Outside review rounds next to code-forge cost.** `code-forge ledger outside-review --run <r>
68
+ --pr <n> --rounds <n> --findings <n> [--link <url>] [--block <id>]…` records how many rounds an
69
+ outside reviewer needed on a PR (a later call for the same PR replaces it). `report` gets a
70
+ 14th section, `outside_review`: per PR, the rounds and findings next to the code-forge cost
71
+ of its blocks.
72
+ - **A walk-through for brief-only runs.** [docs/brief-mode.md](docs/brief-mode.md) walks one task
73
+ from a brief to a closed block with an outside reviewer: related tests, a worktree, the context
74
+ pack, the done file and hook, `status`, and the outside review rounds in `report`. A test runs
75
+ the page's own config and steps, so the page and the CLI stay the same.
76
+
11
77
  ## What's new in 0.7
12
78
 
13
- Next release:
79
+ In 0.7.7:
14
80
 
15
81
  - **Work from a brief, with no plan.** `run start --mode brief` marks the run as brief-only.
16
82
  Then `block open` without `--level` picks the lane from the rules: the owned files,
@@ -219,7 +285,7 @@ usage line.
219
285
 
220
286
  | Verb | Usage |
221
287
  |---|---|
222
- | `init` | `code-forge init [--no-interaction] [--tools recommended\|current] [--yes-tool <tool>]… [--harness a,b] [-g\|-p] [--copy] [--provider P] [--level Ln=model[:effort][@provider]]… [--refresh-models] [--multimodel on\|off] [--second-provider P] [--jev-ref <item-id\|link\|op://…> \| --jev-env NAME \| --no-jev] [--engine auto\|solo\|harness] [--solo-project N] [--gate name=cmd]… [--proof isolation=export\|lock \| high=a,b \| link_dirs=a,b \| copy_untracked=a,b]… [--skip-doctor]` |
288
+ | `init` | `code-forge init [--no-interaction] [--tools recommended\|current] [--yes-tool <tool>]… [--harness a,b] [-g\|-p] [--copy] [--provider P] [--level Ln=model[:effort][@provider]]… [--refresh-models] [--multimodel on\|off] [--second-provider P] [--jev-ref <item-id\|link\|op://…> \| --jev-env NAME \| --no-jev] [--engine auto\|solo\|harness] [--solo-project N] [--gate name=cmd]… [--proof isolation=export\|lock \| high=a,b \| link_dirs=a,b \| copy_untracked=a,b]… [--coder-may-push yes\|no] [--coder-may-open-draft-pr yes\|no] [--skip-doctor]` |
223
289
  | `doctor` | `code-forge doctor [--quick] [--json] [--cwd <dir>]` |
224
290
  | `tools` | `code-forge tools [--json]` · `code-forge tools install [<id>…] [--yes] [--dry-run]` — see and install the recommended tools (claude, codex, gemini, grok, op, solo) |
225
291
  | `validate` | `code-forge validate [--file <path>]` |
package/docs/README.md CHANGED
@@ -15,11 +15,12 @@ disagree, the verb is right: each verb prints its own usage line when its argume
15
15
  | 5 | [tutorial-first-test.md](tutorial-first-test.md) | run one real end-to-end test: build the code-forge marketing site in a new repo |
16
16
  | 5a | [review-only.md](review-only.md) | only review code you already wrote: a branch, uncommitted work or some files, with CI exit codes |
17
17
  | 5b | [autopilot.md](autopilot.md) | step away while a run goes on: grant a delegate a few decisions for a set time, and review the live log (Binnacle and Full log) when you return |
18
+ | 5c | [brief-mode.md](brief-mode.md) | code one small task from a brief, with no plan: a brief-mode run with an outside reviewer, related tests, worktrees, the context pack and the done file, step by step |
18
19
  | 6 | [examples/marketing-site/brief.md](examples/marketing-site/brief.md) | copy the brief the tutorial uses |
19
20
  | 7 | [marketing.md](marketing.md) | read the product story: the problem, the promise, the honest limits |
20
21
 
21
22
  New to the tool? Read 1 → 3 → 4, then do 5. Keep 2 open as a glossary. Only want reviews? Read 4,
22
- then 5a. Going away while a run goes on? Read 5b.
23
+ then 5a. Going away while a run goes on? Read 5b. One small task, no plan? Read 5c.
23
24
 
24
25
  ## Reference pages (already in the repo)
25
26
 
@@ -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": 62,
7
+ "block_count": 88,
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": {
@@ -850,6 +850,395 @@
850
850
  "B58"
851
851
  ],
852
852
  "note": "root-added 2026-10-11; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/state/{run,block,config-snapshot}.mjs, src/cli/{run,block,report}.mjs, src/ledger/report.mjs, src/review/engine.mjs (shared migration/policy path checks), docs/{how-it-works,concepts}.md, skill/SKILL.md, skill/references/plan.md"
853
+ },
854
+ "B60": {
855
+ "title": "Related-test gates (issue #13 item 3): block open --tests <paths> sets the block's test files (exact paths or directories; * ? and a leading - refused); a gate argv item exactly {test_files} expands to them (else owned entries that look like tests) and is dropped for the full suite when the block owns more than gates.full_suite_threshold_files files (tracked and new, not ignored; globs and directories count the files they cover) or no test file is known; gate.red/gate.done carry scope, reason, test_count, run and block; a gate may be {argv, env}: env values are never printed or recorded, and child output masks values of 8+ characters; plain gates run exactly as before",
856
+ "wave": 13,
857
+ "level": "L1",
858
+ "status": "landed",
859
+ "owned": [
860
+ "src/gates/expand.mjs",
861
+ "src/gates/owned-count.mjs",
862
+ "src/util/gate-token.mjs",
863
+ "test/gates/placeholders.test.mjs"
864
+ ],
865
+ "depends_on": [
866
+ "B59"
867
+ ],
868
+ "note": "root-added 2026-10-11; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/gates/{run,detect}.mjs, src/proof/{export,lock}.mjs, src/cli/{gates,block}.mjs, src/state/{block,registry}.mjs, src/config/validate.mjs, src/doctor/local.mjs, src/install/wizard/{answers,steps,summary}.mjs, src/autopilot/binnacle.mjs, schema/code-forge.schema.json, docs/reference/config.md, docs/{how-it-works,concepts}.md, skill/SKILL.md, skill/references/{proof,code,ledger}.md"
869
+ },
870
+ "B61": {
871
+ "title": "Laravel under export, verified (issue #13 item 3): composer.json + vendor/bin/phpunit without Pest is the php-phpunit stack (php artisan test --compact with artisan, else vendor/bin/phpunit; PHP is detected before Node); export copies a Composer vendor (link_dirs entry holding composer/autoload_real.php, resolved through realpath, reflink when possible) instead of linking it, because PHP resolves __DIR__ through symlinks and a linked vendor autoloaded app code from the main tree; absolute links into the main tree are pointed into the export; an opt-in live test (CODE_FORGE_LIVE_PHP=1) runs a Laravel skeleton on PHPUnit and on Pest green through the export while the main tree is red; proof.md marks PHP under export VERIFIED",
872
+ "wave": 13,
873
+ "level": "L2",
874
+ "status": "landed",
875
+ "owned": [
876
+ "test/proof/laravel-live.test.mjs"
877
+ ],
878
+ "depends_on": [
879
+ "B60"
880
+ ],
881
+ "note": "root-added 2026-10-11; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/gates/detect.mjs, src/proof/export.mjs, src/install/wizard/answers.mjs (comment), test/gates/detect.test.mjs, test/proof/export.test.mjs, test/init/fix-r3.test.mjs (Laravel+PHPUnit now php-phpunit), docs/{how-it-works,concepts,getting-started}.md, skill/references/proof.md"
882
+ },
883
+ "B62a": {
884
+ "title": "Worktree isolation, open and close (issue #13 item 4): proof.isolation worktree gives each block its own git worktree and branch (proof.worktree {dir, branch, base}, dir resolved from the repository top level, <repo-name> is its basename, a symlinked dir is recorded by its realpath); both keys are read from the run's config snapshot and run reload refuses to change them; block open adds the tree (GIT_* stripped), re-checks the branch just before the add, rolls the tree and its own branch back on a later failure and names any leftover; a re-open reuses or re-adds the tree on the same branch and keeps the tree's base (a different --base is refused, block rebase refuses a worktree block); owned files must be disjoint only inside one tree; block close --remove-worktree removes the tree (never the branch) and writes worktree.removed or a signed worktree.remove_failed; --remove-worktree is on the coder forbidden list",
885
+ "wave": 13,
886
+ "level": "L2",
887
+ "status": "landed",
888
+ "owned": [
889
+ "src/proof/worktree.mjs",
890
+ "test/proof/worktree.test.mjs"
891
+ ],
892
+ "depends_on": [
893
+ "B61"
894
+ ],
895
+ "note": "root-added 2026-10-11; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/state/{block,registry,run,config-snapshot}.mjs, src/cli/block.mjs, src/util/forbidden.mjs, schema/code-forge.schema.json, docs/reference/config.md, docs/{how-it-works,concepts}.md, skill/references/{proof,code,ledger,security}.md, test/state/registry.test.mjs, test/util/forbidden.test.mjs, test/config/schema-coverage.test.mjs, test/engines/{codex-home,builders/argv-snapshots,builders/forbidden-rendering}.test.mjs"
896
+ },
897
+ "B62b": {
898
+ "title": "Worktree isolation, gates, reviews and the close check (issue #13 item 4): for a block with a worktree, gates run --block runs in the tree (--cwd must name the workspace's place in the tree, else exit 2; --cwd=<dir> is refused), {test_files} and the threshold count in the tree, gate rows name the tree; review-file queues in the main tree but hashes the tree's content, the ticket id includes the tree, and the worker reads content and the diff against worktree.base_sha from the run record's tree (a mismatch, a missing, malformed, off-branch or foreign tree is unavailable or refused); block close measures changed and unowned files in the tree against its base with no sibling or tree_at_open exemption; proof export and red-green build the export from the tree and never write to it, and proof lock is refused for a worktree block; nothing is pushed, merged or checked out, and the main tree's HEAD and index stay unchanged; blocks without a worktree behave as before",
899
+ "wave": 13,
900
+ "level": "L1",
901
+ "status": "landed",
902
+ "owned": [
903
+ "test/proof/worktree-flow.test.mjs"
904
+ ],
905
+ "depends_on": [
906
+ "B62a"
907
+ ],
908
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/cli/{block,gates,proof,review-file}.mjs, src/proof/worktree.mjs, src/review/gate-check.mjs, src/worker/{engine,loop,queue,ticket}.mjs, test/proof/worktree.test.mjs, test/review/engine.test.mjs, test/worker/queue.test.mjs, docs/{how-it-works,concepts}.md, skill/references/{proof,review,code,ledger}.md"
909
+ },
910
+ "B63a": {
911
+ "title": "Context pack (issue #13 item 5, issue #10 item 2): a top-level context key (rules, lessons, commands, max_lines default 300); code-forge context pack --run --block [--json] writes <run-root>/context/<block>.md with a header, the redacted commands in POSIX quoting, the rules and the lessons, cut to max_lines (lessons first, then rules from the last file back, each cut marked), prints the path and a token estimate, and appends a signed context.pack row unless the block's last row has the same sha256; paths outside the repository, missing files and control characters are refused with nothing written; review packets (first and recheck) carry the rules and lessons from the main tree in the 60-line digest slot, read from the live config, and an unreadable file makes the ticket unavailable (context-unreadable); with no context key everything behaves as before",
912
+ "wave": 13,
913
+ "level": "L1",
914
+ "status": "landed",
915
+ "owned": [
916
+ "src/context/pack.mjs",
917
+ "src/cli/context.mjs",
918
+ "src/worker/engine.mjs",
919
+ "test/context/pack.test.mjs"
920
+ ],
921
+ "depends_on": [
922
+ "B62b"
923
+ ],
924
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/util/breadcrumbs.mjs, schema/code-forge.schema.json, test/config/schema-coverage.test.mjs, docs/{how-it-works,concepts}.md, docs/reference/config.md, skill/SKILL.md, skill/references/{code,review,ledger}.md"
925
+ },
926
+ "B63b": {
927
+ "title": "Coders prove a green test run (issue #13 item 5, issue #10 item 1): parseSentinel returns tests {command, exit} from the last whole-line TESTS <command> exit=<n> in the returned sentinel's segment (after the previous sentinel of any block; 0-255, no leading zeros); context.require_coder_tests (run-start snapshot only, run reload refuses a change) makes block close refuse with no --report, an unreadable report, no COMPLETE sentinel, no TESTS line or a non-zero exit, even when reviews are waived or external, and on a pass writes a signed coder.tests row with the close rows; the coder brief template gets a context pack slot and the TESTS line; with the key false or absent close behaves as before",
928
+ "wave": 13,
929
+ "level": "L1",
930
+ "status": "landed",
931
+ "owned": [
932
+ "test/engines/sentinel-tests.test.mjs"
933
+ ],
934
+ "depends_on": [
935
+ "B63a"
936
+ ],
937
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/engines/sentinel.mjs, src/cli/block.mjs, src/context/pack.mjs, src/state/{config-snapshot,run}.mjs, schema/code-forge.schema.json, test/config/schema-coverage.test.mjs, test/skill/structure.test.mjs, docs/{how-it-works,concepts}.md, docs/reference/config.md, skill/SKILL.md, skill/references/{code,ledger}.md, skill/templates/coder-brief.md"
938
+ },
939
+ "B64a": {
940
+ "title": "Short status (issue #13 item 6, part 1): code-forge status [--run] [--json] is read-only and prints at most 10 lines per run (run id, state, mode, spend; one line per open block with level, coder model, last gate and the open findings the close gate would refuse; an overflow line and a closed/stopped tally), or one JSON line; every block verb takes --json and prints one JSON line, with stdout empty on failure except a gate-refused close; without --json block output is unchanged",
941
+ "wave": 13,
942
+ "level": "L1",
943
+ "status": "landed",
944
+ "owned": [
945
+ "src/cli/status.mjs",
946
+ "src/state/brief-status.mjs",
947
+ "test/cli/status.test.mjs"
948
+ ],
949
+ "depends_on": [
950
+ "B63b"
951
+ ],
952
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/cli/block.mjs, docs/{how-it-works,concepts}.md, skill/SKILL.md, skill/references/code.md"
953
+ },
954
+ "B64b": {
955
+ "title": "Block-end signal (issue #13 item 6, part 2): every block end (close, stop, a stop from close) writes <run root>/blocks/<id>.done.json atomically (status, event, reason, commit, block spend, open findings, time); hooks.on_block_end {argv, timeout_s}, fixed at run start, runs once per end outside the run lock with no shell and four CODE_FORGE_* env vars, its process group killed at the timeout, and writes one signed hook.ran row; a failing hook only warns; a coder transcript running the hook command is refused at close",
956
+ "wave": 13,
957
+ "level": "L1",
958
+ "status": "landed",
959
+ "owned": [
960
+ "src/state/block-end.mjs",
961
+ "src/review/open-findings.mjs",
962
+ "test/state/block-end.test.mjs"
963
+ ],
964
+ "depends_on": [
965
+ "B64a"
966
+ ],
967
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/cli/block.mjs, src/cli/status.mjs, src/state/{block,config-snapshot,run}.mjs, src/util/forbidden.mjs, schema, docs/{how-it-works,concepts}.md, docs/reference/config.md, skill/SKILL.md, skill/references/{code,ledger}.md"
968
+ },
969
+ "B65": {
970
+ "title": "Safer push and draft-PR defaults (issue #13 item 7): autonomy.coder_may_push and autonomy.coder_may_open_draft_pr default to false; init asks both (default no, the draft-PR question only after a push yes, pre-filled on a re-run), writes both keys, and takes --coder-may-push / --coder-may-open-draft-pr yes|no; a draft PR without push is refused; the skill gates every hand-back, push or PR on the same checks",
971
+ "wave": 13,
972
+ "level": "L1",
973
+ "status": "landed",
974
+ "owned": [
975
+ "test/init/autonomy.test.mjs"
976
+ ],
977
+ "depends_on": [
978
+ "B64b"
979
+ ],
980
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); breaking default change; also amends src/install/wizard/{answers,flags,project-file,run,steps}.mjs, schema, docs/{how-it-works,concepts,getting-started,overview}.md, docs/reference/config.md, skill/references/code.md, test/init/{flags,settings}.test.mjs, test/fixtures/repos/laravel-vue/expected.code-forge.yml"
981
+ },
982
+ "B66": {
983
+ "title": "Outside review rounds per PR next to code-forge cost",
984
+ "wave": 13,
985
+ "level": "L1",
986
+ "status": "landed",
987
+ "owned": [
988
+ "src/ledger/outside-review.mjs",
989
+ "test/ledger/outside-review.test.mjs"
990
+ ],
991
+ "depends_on": [
992
+ "B65"
993
+ ],
994
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends src/cli/ledger.mjs, src/cli/report.mjs, src/ledger/report.mjs, src/ledger/export.mjs and the 14-section pins"
995
+ },
996
+ "B67": {
997
+ "title": "Docs and skill for brief-only runs, context packs and the no-poll flow",
998
+ "wave": 13,
999
+ "level": "L1",
1000
+ "status": "landed",
1001
+ "owned": [
1002
+ "docs/brief-mode.md",
1003
+ "test/integration/brief-mode-walkthrough.test.mjs"
1004
+ ],
1005
+ "depends_on": [
1006
+ "B66"
1007
+ ],
1008
+ "note": "root-added 2026-10-12; GitHub issue #13 adoption plan (plans/adoption-13/plan.md); also amends skill/SKILL.md, skill/references/code.md, docs/README.md, docs/how-it-works.md and test/skill/structure.test.mjs"
1009
+ },
1010
+ "B68": {
1011
+ "title": "A lost review-state file is rebuilt from the signed ledger, never reset",
1012
+ "wave": 14,
1013
+ "level": "L2",
1014
+ "status": "landed",
1015
+ "owned": [
1016
+ "src/worker/review-state.mjs",
1017
+ "test/worker/review-state-restore.test.mjs"
1018
+ ],
1019
+ "depends_on": [
1020
+ "B67"
1021
+ ],
1022
+ "note": "root-added 2026-10-12; GitHub issue #15 review-exits plan (plans/review-exits/plan.md); review.state anchors carry a slim signed state_body; also amends src/worker/engine.mjs, src/worker/loop.mjs, skill/references/review.md, skill/references/ledger.md and test/worker/fixloop-wiring.test.mjs"
1023
+ },
1024
+ "B69": {
1025
+ "title": "Review state lives under the code-forge home, not the temp root",
1026
+ "wave": 14,
1027
+ "level": "L1",
1028
+ "status": "landed",
1029
+ "owned": [
1030
+ "src/worker/review-state.mjs",
1031
+ "src/doctor/local.mjs",
1032
+ "test/worker/review-state-durable.test.mjs"
1033
+ ],
1034
+ "depends_on": [
1035
+ "B68"
1036
+ ],
1037
+ "note": "root-added 2026-10-12; GitHub issue #15 review-exits plan (plans/review-exits/plan.md); state at <home>/runs/<run>/review-state, one-time checked move from the temp root, doctor review-state row; also amends src/util/tmp.mjs (runRootPath), src/worker/engine.mjs, src/doctor/index.mjs, skill/references/review.md, docs/how-it-works.md, docs/getting-started.md and the B68/fixloop-wiring tests"
1038
+ },
1039
+ "B70": {
1040
+ "title": "A fully waived review counts as reviewed",
1041
+ "wave": 14,
1042
+ "level": "L2",
1043
+ "status": "landed",
1044
+ "owned": [
1045
+ "src/review/gate-check.mjs",
1046
+ "test/review/waived-all.test.mjs"
1047
+ ],
1048
+ "depends_on": [
1049
+ "B69"
1050
+ ],
1051
+ "note": "root-added 2026-10-12; GitHub issue #9 review-exits plan (plans/review-exits/plan.md); reviewed_by_waiver status and signed gate.review_waived_all row before block.close; unknown severity is human-only; also amends src/cli/block.mjs, skill/references/review.md, skill/references/ledger.md, docs/how-it-works.md and docs/concepts.md"
1052
+ },
1053
+ "B77": {
1054
+ "title": "Test guard ignores live runs of the developer",
1055
+ "wave": 14,
1056
+ "level": "L1",
1057
+ "status": "landed",
1058
+ "owned": [
1059
+ "test/helpers/isolate.mjs",
1060
+ "test/helpers-isolate.test.mjs"
1061
+ ],
1062
+ "depends_on": [
1063
+ "B70"
1064
+ ],
1065
+ "note": "root-added 2026-10-12; tool fix found while building (plans/pipeline.md); leakedEntries: a new path of a run whose record was in the baseline is not a leak; also amends test/no-leak.test.mjs"
1066
+ },
1067
+ "B78": {
1068
+ "title": "Full reviewed text comes back after a restore",
1069
+ "wave": 14,
1070
+ "level": "L1",
1071
+ "status": "landed",
1072
+ "owned": [
1073
+ "src/worker/review-state.mjs",
1074
+ "test/worker/review-state-restore.test.mjs"
1075
+ ],
1076
+ "depends_on": [
1077
+ "B77"
1078
+ ],
1079
+ "note": "root-added 2026-10-12; tool fix found while building (plans/pipeline.md), follows B68 (issue #15); reviewed_content_blob in the signed state_body, git hash-object/cat-file with a discovery ceiling, sha-checked, falls back to B68; also amends src/worker/engine.mjs, skill/references/review.md and skill/references/ledger.md"
1080
+ },
1081
+ "B71": {
1082
+ "title": "review-file --answers: a reviewer judges a fix made in another owned file",
1083
+ "wave": 14,
1084
+ "level": "L2",
1085
+ "status": "landed",
1086
+ "owned": [
1087
+ "src/review/answers.mjs",
1088
+ "test/review/answers.test.mjs"
1089
+ ],
1090
+ "depends_on": [
1091
+ "B78"
1092
+ ],
1093
+ "note": "root-added 2026-10-12; GitHub issue #9 review-exits plan (plans/review-exits/plan.md); answers ticket kind, signed review.answered rows bound to both content hashes, gate counts a still-current answer like a waiver (answered list on gate.review_waived_all), unknown severities fail closed; also amends src/review/gate-check.mjs, src/cli/review-file.mjs, src/cli/block.mjs, src/worker/queue.mjs, src/worker/engine.mjs, src/worker/loop.mjs, skill/references/review.md, skill/references/ledger.md and docs/how-it-works.md"
1094
+ },
1095
+ "B72": {
1096
+ "title": "block reopen: a human restarts a stopped or lost file on new content",
1097
+ "wave": 14,
1098
+ "level": "L2",
1099
+ "status": "landed",
1100
+ "owned": [
1101
+ "src/review/reopen.mjs",
1102
+ "test/review/reopen.test.mjs"
1103
+ ],
1104
+ "depends_on": [
1105
+ "B71"
1106
+ ],
1107
+ "note": "root-added 2026-10-12; GitHub issues #12 and #15 review-exits plan (plans/review-exits/plan.md); human-only verb on CODER_ONLY_FORBIDDEN, signed review.reopened row bound to the latest anchor seq, stop_content_hash in the state, one row per at_seq, L3 rung stays spent; also amends src/cli/block.mjs, src/util/forbidden.mjs, src/worker/engine.mjs, src/worker/queue.mjs, the forbidden-list pin tests, skill/references/review.md, skill/references/ledger.md, skill/references/code.md and docs/how-it-works.md"
1108
+ },
1109
+ "B79": {
1110
+ "title": "Review-exit leftovers: cap-row severity, answers packet secrets and budget, reopen config and write cause",
1111
+ "wave": 14,
1112
+ "level": "L1",
1113
+ "status": "landed",
1114
+ "owned": [
1115
+ "src/review/answers.mjs",
1116
+ "src/review/gate-check.mjs",
1117
+ "src/cli/block.mjs",
1118
+ "src/review/reopen.mjs",
1119
+ "test/review/answers.test.mjs",
1120
+ "test/review/reopen.test.mjs"
1121
+ ],
1122
+ "depends_on": [
1123
+ "B72"
1124
+ ],
1125
+ "note": "root-added 2026-10-12; leftovers from the B71 and B72 round-3 reviews; also amends B71 (answers packet, openAt) and B72 (reopen config, write failure)"
1126
+ },
1127
+ "B73": {
1128
+ "title": "A patch check that closes findings is progress; stop answers name the exits",
1129
+ "wave": 14,
1130
+ "level": "L2",
1131
+ "status": "landed",
1132
+ "owned": [
1133
+ "src/review/fixloop.mjs",
1134
+ "src/review/stop-exits.mjs",
1135
+ "src/cli/review-file.mjs",
1136
+ "src/review/gate-check.mjs",
1137
+ "test/review/patch-progress.test.mjs",
1138
+ "test/worker/fix-list.test.mjs",
1139
+ "test/autopilot/actions.test.mjs",
1140
+ "docs/how-it-works.md",
1141
+ "skill/references/review.md"
1142
+ ],
1143
+ "depends_on": [
1144
+ "B79"
1145
+ ],
1146
+ "note": "root-added 2026-10-12; issue #12 items 9-10; also amends B71 (review-file stopped answer) and the gate review_cap/unreviewed detail"
1147
+ },
1148
+ "B81": {
1149
+ "title": "Deterministic ledger rotation race test",
1150
+ "wave": 14,
1151
+ "level": "L1",
1152
+ "status": "landed",
1153
+ "owned": [
1154
+ "src/ledger/write.mjs",
1155
+ "test/ledger/write.test.mjs"
1156
+ ],
1157
+ "depends_on": [
1158
+ "B73"
1159
+ ],
1160
+ "note": "root-added 2026-10-12; the claimRotatedName two-racers test failed once under full-suite load during the B73 land; an afterLink hook forces the race order"
1161
+ },
1162
+ "B74": {
1163
+ "title": "A deleted owned file is reviewed with a deletion note",
1164
+ "wave": 14,
1165
+ "level": "L1",
1166
+ "status": "landed",
1167
+ "owned": [
1168
+ "src/review/engine.mjs",
1169
+ "src/review/deletion.mjs",
1170
+ "test/worker/deletion-review.test.mjs",
1171
+ "docs/how-it-works.md",
1172
+ "skill/references/review.md"
1173
+ ],
1174
+ "depends_on": [
1175
+ "B81"
1176
+ ],
1177
+ "note": "root-added 2026-10-12; issue #8; the deletion flow already worked end to end (pinned by test); adds the deletion note with bounded reference search"
1178
+ },
1179
+ "B75": {
1180
+ "title": "review-file --fresh re-runs only results that never counted",
1181
+ "wave": 14,
1182
+ "level": "L1",
1183
+ "status": "landed",
1184
+ "owned": [
1185
+ "src/cli/review-file.mjs",
1186
+ "src/worker/queue.mjs",
1187
+ "test/worker/fresh.test.mjs",
1188
+ "test/worker/queue.test.mjs",
1189
+ "skill/references/review.md",
1190
+ "skill/references/code.md"
1191
+ ],
1192
+ "depends_on": [
1193
+ "B74"
1194
+ ],
1195
+ "note": "root-added 2026-10-12; issue #10 item 4; classify the done marker in place, claim only a re-runnable one by atomic rename, restore by link; leftovers in B83"
1196
+ },
1197
+ "B82": {
1198
+ "title": "Deterministic worker restart kill test",
1199
+ "wave": 14,
1200
+ "level": "L1",
1201
+ "status": "landed",
1202
+ "owned": [
1203
+ "test/worker/worker.test.mjs"
1204
+ ],
1205
+ "depends_on": [
1206
+ "B75"
1207
+ ],
1208
+ "note": "root-added 2026-10-12; the 'new worker re-reads 3 queued tickets' test checked a killed pid once and failed under load (zombie window); it now waits for ESRCH with a 5 s deadline"
1209
+ },
1210
+ "B76": {
1211
+ "title": "the skill and docs name the review exits in order",
1212
+ "wave": 14,
1213
+ "level": "L1",
1214
+ "status": "landed",
1215
+ "owned": [
1216
+ "skill/references/review.md",
1217
+ "skill/references/code.md",
1218
+ "skill/references/security.md",
1219
+ "skill/templates/coder-brief.md",
1220
+ "docs/how-it-works.md",
1221
+ "test/skill/structure.test.mjs"
1222
+ ],
1223
+ "depends_on": [
1224
+ "B75"
1225
+ ],
1226
+ "note": "root-added 2026-10-12; group A docs; exits in order (fix + block reopen, --answers, block waive, re-decompose), block attempt never resets review state, a lost state is rebuilt from the ledger, when --fresh helps"
1227
+ },
1228
+ "B83": {
1229
+ "title": "review-file --fresh fails closed when a marker cannot be restored",
1230
+ "wave": 14,
1231
+ "level": "L1",
1232
+ "status": "landed",
1233
+ "owned": [
1234
+ "src/worker/queue.mjs",
1235
+ "src/cli/review-file.mjs",
1236
+ "test/worker/fresh.test.mjs"
1237
+ ],
1238
+ "depends_on": [
1239
+ "B76"
1240
+ ],
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"
853
1242
  }
854
1243
  }
855
1244
  }