@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
@@ -24,11 +24,15 @@
24
24
  * a coding level, R1) takes it, so for an L2 block the rung IS the +1 at round 2. After the
25
25
  * patch the block continues at L2 (§3.6, Fable ruling A): `l3_rung_used = true`,
26
26
  * `rounds_at_level = 0`, and the `patch_check` goes through `decideNext` like any recheck —
27
- * clean ⇒ complete; open set not smaller than before the patch ⇒ `stopped: l3_patch_exhausted`
28
- * (`trigger: review_stall`); round ≥ cap ⇒ `stopped: review_cap`; else `fix` at L2 with
29
- * `l3_patch: true` in the brief. A later stall ⇒ `l3_patch_exhausted`; round 4 still open ⇒
30
- * `review_cap` (the rung is spent). Either way the ledger `review.cap` row lists the open
31
- * ids — the human decides (fix by hand, `block waive`, or re-decompose).
27
+ * clean ⇒ complete; the patch closed NO finding that was open before it (ids compared, not
28
+ * counts) ⇒ `stopped: l3_patch_exhausted` (`trigger: review_stall`); round ≥ cap ⇒
29
+ * `stopped: review_cap`; else `fix` at L2 with `l3_patch: true` in the brief. B73 (issue #12):
30
+ * a patch that closed ≥ 1 old id below the cap is progress, not a stall, even when new findings
31
+ * in its fix hunks keep the open set from shrinking. A later stall at L2 ⇒ `l3_patch_exhausted`
32
+ * (the rung is spent, never a second one); round 4 still open ⇒ `review_cap`. Either way the
33
+ * ledger `review.cap` row lists the open ids — the human decides (`STOP_EXITS`,
34
+ * `stop-exits.mjs`): `block reopen` after a fix, `block waive` per open finding,
35
+ * `review-file --answers` for a fix in another owned file, or re-decompose.
32
36
  * 6. Ledger per round: `review.round {file, round, level, kind, open_before, closed,
33
37
  * new_in_hunks, late, open_after, tokens_in, tokens_out}`; a converged file gets
34
38
  * `review.approved {file, content_hash, round}` (the worker signs every row it writes).
@@ -497,17 +501,20 @@ async function recheckSession(packet, deps) {
497
501
  /**
498
502
  * After a counted round with open findings: cap first (rule 6), else stall / ladder (2b, 2).
499
503
  * @param {FileState} state @param {number} openBefore @param {ReturnType<typeof settings>} s
500
- * @param {'full' | 'recheck' | 'patch_check'} kind - a `patch_check` compares against the open
501
- * set before the patch (the rung's own shrink test), whatever the round number.
504
+ * @param {'full' | 'recheck' | 'patch_check'} kind - a `patch_check` is judged by `closedIds`
505
+ * (B73), whatever the round number.
506
+ * @param {number} closedIds - how many ids open before the round are not open after it.
502
507
  * @returns {NextStep}
503
508
  */
504
- function decideNext(state, openBefore, s, kind) {
509
+ function decideNext(state, openBefore, s, kind, closedIds) {
505
510
  if (state.open.length === 0) return { action: 'complete' };
506
511
  if (kind === 'patch_check') {
507
- // the block's one rung is spent: a patch that did not shrink the set stops FIRST — even when
508
- // the rung was taken at the cap — then the cap; else the block continues at L2 below
509
- if (reviewRoundStalled(openBefore, state.open.length)) return { action: 'stop', reason: 'l3_patch_exhausted', trigger: 'review_stall' };
512
+ // the block's one rung is spent: a patch that closed no old finding stops FIRST — even when
513
+ // the rung was taken at the cap — then the cap; else (B73: ≥ 1 old id closed is progress,
514
+ // whatever new findings its fix hunks brought) the block continues at L2: a fix, no stall
515
+ if (closedIds === 0) return { action: 'stop', reason: 'l3_patch_exhausted', trigger: 'review_stall' };
510
516
  if (state.round >= s.maxRounds) return { action: 'stop', reason: 'review_cap', trigger: 'review_cap' };
517
+ return { action: 'fix', level: state.level, trigger: null };
511
518
  }
512
519
  if (state.round >= s.maxRounds) {
513
520
  const esc = escalationAfterAttempt({ currentLevel: state.level, roundsAtFile: state.round, maxRoundsPerFile: s.maxRounds, openFixNowFinding: true, l3RungAlreadyUsed: state.l3_rung_used });
@@ -515,7 +522,7 @@ function decideNext(state, openBefore, s, kind) {
515
522
  }
516
523
  const esc = escalationAfterAttempt({
517
524
  currentLevel: state.level,
518
- reviewStall: (kind === 'patch_check' || state.round >= 2) && reviewRoundStalled(openBefore, state.open.length),
525
+ reviewStall: state.round >= 2 && reviewRoundStalled(openBefore, state.open.length),
519
526
  openFixNowFinding: true,
520
527
  reviewRoundsAtLevel: state.rounds_at_level,
521
528
  reviewRoundsPerLevel: s.perLevel,
@@ -740,9 +747,12 @@ export async function runRound(state, deps, opts = {}) {
740
747
  draft.status = 'complete';
741
748
  draft.next = { action: 'complete' };
742
749
  } else {
743
- // after a patch_check the block continues at L2 (§3.6): the rung is spent, so a stall now
744
- // stops (`l3_patch_exhausted`) and the cap stops (`review_cap`); a shrunk set is fixed at L2
745
- draft.next = decideNext(draft, openBefore, s, kind);
750
+ // after a patch_check the block continues at L2 (§3.6): the rung is spent, so a patch that
751
+ // closed no old id stops (`l3_patch_exhausted`) and the cap stops (`review_cap`); a patch that
752
+ // closed one is fixed at L2 (B73). Closed = an id open before the round, not open after it.
753
+ const after = new Set(draft.open.map((f) => f.id));
754
+ const closedIds = new Set(state.open.map((f) => f.id).filter((id) => !after.has(id))).size;
755
+ draft.next = decideNext(draft, openBefore, s, kind, closedIds);
746
756
  if (draft.next.action === 'stop') draft.status = 'stopped';
747
757
  }
748
758
  const rows = [...lateRows, roundRow];