pan-wizard 3.31.0 → 3.32.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 (47) hide show
  1. package/README.md +28 -24
  2. package/agents/pan-design-checker.md +18 -0
  3. package/agents/pan-optimizer.md +5 -1
  4. package/agents/pan-plan-checker.md +23 -0
  5. package/agents/pan-reviewer.md +19 -0
  6. package/agents/pan-verifier.md +23 -0
  7. package/commands/pan/design-phase.md +8 -0
  8. package/commands/pan/exec-phase.md +1 -1
  9. package/commands/pan/git.md +2 -2
  10. package/commands/pan/learn.md +2 -0
  11. package/commands/pan/optimize.md +14 -2
  12. package/commands/pan/review-deep.md +5 -5
  13. package/hooks/dist/pan-context-monitor.js +1 -1
  14. package/hooks/dist/pan-cost-logger.js +63 -11
  15. package/hooks/dist/pan-state-reinject.js +2 -2
  16. package/hooks/dist/pan-stop-guard.js +2 -2
  17. package/hooks/dist/pan-trace-logger.js +283 -18
  18. package/package.json +1 -1
  19. package/pan-wizard-core/bin/lib/commands.cjs +2 -1
  20. package/pan-wizard-core/bin/lib/constants.cjs +28 -0
  21. package/pan-wizard-core/bin/lib/cost.cjs +3 -3
  22. package/pan-wizard-core/bin/lib/findings.cjs +381 -0
  23. package/pan-wizard-core/bin/lib/focus.cjs +72 -19
  24. package/pan-wizard-core/bin/lib/foreign-planning.cjs +24 -6
  25. package/pan-wizard-core/bin/lib/hygiene.cjs +12 -2
  26. package/pan-wizard-core/bin/lib/init.cjs +4 -1
  27. package/pan-wizard-core/bin/lib/optimize.cjs +349 -21
  28. package/pan-wizard-core/bin/lib/review-deep.cjs +36 -0
  29. package/pan-wizard-core/bin/lib/state.cjs +7 -2
  30. package/pan-wizard-core/bin/lib/verdict.cjs +431 -0
  31. package/pan-wizard-core/bin/lib/verify-preflight.cjs +4 -2
  32. package/pan-wizard-core/bin/lib/verify-scope.cjs +156 -0
  33. package/pan-wizard-core/bin/lib/verify.cjs +25 -2
  34. package/pan-wizard-core/bin/pan-tools.cjs +59 -5
  35. package/pan-wizard-core/learnings/README.md +10 -7
  36. package/pan-wizard-core/mcp/tool-registry.cjs +2 -0
  37. package/pan-wizard-core/references/verdict-contract.md +101 -0
  38. package/pan-wizard-core/templates/verification-report.md +16 -0
  39. package/pan-wizard-core/workflows/exec-phase.md +65 -47
  40. package/pan-wizard-core/workflows/health.md +2 -0
  41. package/pan-wizard-core/workflows/learn.md +4 -2
  42. package/pan-wizard-core/workflows/milestone-audit.md +15 -3
  43. package/pan-wizard-core/workflows/optimize.md +14 -1
  44. package/pan-wizard-core/workflows/plan-phase.md +20 -15
  45. package/pan-wizard-core/workflows/verify-phase.md +3 -13
  46. package/pan-zcode/README.md +3 -3
  47. package/scripts/git-hooks/pre-commit +2 -1
@@ -28,6 +28,7 @@
28
28
  * history-digest Aggregate all summary.md data
29
29
  * summary-extract <path> [--fields] Extract structured data from summary.md
30
30
  * state-snapshot Structured parse of state.md (alias: state snapshot)
31
+ * findings record|list|dispose|debt Judges' verdicts and their findings ledger (evidence loop)
31
32
  * phase-plan-index <phase> Index plans with waves and status
32
33
  * websearch <query> Search web via Brave API (if configured)
33
34
  * [--limit N] [--freshness day|week|month]
@@ -206,6 +207,7 @@ const cost = require('./lib/cost.cjs');
206
207
  const costRebuild = require('./lib/cost-rebuild.cjs');
207
208
  const preview = require('./lib/preview.cjs');
208
209
  const reviewDeep = require('./lib/review-deep.cjs');
210
+ const findings = require('./lib/findings.cjs');
209
211
  const knowledge = require('./lib/knowledge.cjs');
210
212
  const skillAlign = require('./lib/skill-align.cjs');
211
213
  const hygiene = require('./lib/hygiene.cjs');
@@ -331,7 +333,7 @@ async function main() {
331
333
  + ' --planning-dir <path> act on an arbitrary project-relative planning tree\n'
332
334
  + ' --all-tracks (hygiene) act on the root tree AND every discovered track\n'
333
335
  + ' env: PAN_TRACK, PAN_PLANNING_DIR (flags win)\n'
334
- + '\nCommands: state, resolve-model, estimate-cost, find-phase, git, distill, experiment, commit, verify-summary, template, frontmatter, verify, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-set, config-get, history-digest, phases, roadmap, requirements, phase, milestone, validate, progress, context-budget, todo, scaffold, init, phase-plan-index, state-snapshot, summary-extract, rollback-snapshot, batch-commit, websearch, focus, preflight, dashboard, hud, report, learnings, deps, drift-check, memory, bridge, whatif, knowledge, skills, hygiene, review-deep, preview, cost, models, squad, worktree, campaign, bus, cache, retro, codebase, standards, optimize, doc-lint, learn, links';
336
+ + '\nCommands: state, resolve-model, estimate-cost, find-phase, git, distill, experiment, commit, verify-summary, template, frontmatter, verify, generate-slug, current-timestamp, list-todos, verify-path-exists, config-ensure-section, config-set, config-get, history-digest, phases, roadmap, requirements, phase, milestone, validate, progress, context-budget, todo, scaffold, init, phase-plan-index, state-snapshot, summary-extract, rollback-snapshot, batch-commit, websearch, focus, preflight, dashboard, hud, report, learnings, deps, drift-check, memory, bridge, whatif, knowledge, skills, hygiene, review-deep, findings, preview, cost, models, squad, worktree, campaign, bus, cache, retro, codebase, standards, optimize, doc-lint, learn, links';
335
337
 
336
338
  if (!command) {
337
339
  error(USAGE);
@@ -412,7 +414,7 @@ async function main() {
412
414
  } else if (subcommand === 'load' || !subcommand) {
413
415
  state.cmdStateLoad(cwd, raw);
414
416
  } else {
415
- error(`Unknown state subcommand: ${subcommand}. Available: json, update, get, patch, advance-plan, record-metric, update-progress, add-decision, add-blocker, resolve-blocker, record-session, load, snapshot`);
417
+ error(`Unknown state subcommand: ${subcommand}. Available: json, update, get, patch, compact, advance-plan, record-metric, update-progress, add-decision, add-blocker, resolve-blocker, record-session, load, snapshot`);
416
418
  }
417
419
  break;
418
420
  }
@@ -607,8 +609,10 @@ async function main() {
607
609
  verify.cmdVerifyReconcile(cwd, args[2], raw);
608
610
  } else if (subcommand === 'stubs') {
609
611
  verify.cmdVerifyStubs(cwd, { gate: args.includes('--gate') }, raw);
612
+ } else if (subcommand === 'scope') {
613
+ verify.cmdVerifyScope(cwd, args[2], raw);
610
614
  } else {
611
- error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links, reconcile, stubs');
615
+ error('Unknown verify subcommand. Available: plan-structure, phase-completeness, references, commits, artifacts, key-links, reconcile, stubs, scope');
612
616
  }
613
617
  break;
614
618
  }
@@ -914,7 +918,13 @@ async function main() {
914
918
  output({ error: 'No batch file found. Run focus plan first.' }, raw);
915
919
  break;
916
920
  }
917
- items = batch.batch || [];
921
+ if (batch.error) {
922
+ // The newest batch is the one /pan:focus-exec runs; classifying another would
923
+ // hand it waves for the wrong items, so an unusable newest batch is an error.
924
+ output({ error: batch.error }, raw);
925
+ break;
926
+ }
927
+ items = batch.batch;
918
928
  }
919
929
  output(focus.classifyStageDependencies(items), raw);
920
930
  } else if (subcommand === 'reflection') {
@@ -1177,6 +1187,44 @@ async function main() {
1177
1187
  break;
1178
1188
  }
1179
1189
 
1190
+ case 'findings': {
1191
+ // Evidence loop (ADR-0049): judges' verdicts recorded from their reports, and
1192
+ // the dispositions workflows give the findings they continue past.
1193
+ const subcommand = args[1];
1194
+ const filters = {
1195
+ phase: getArgValue(args, '--phase'),
1196
+ agent: getArgValue(args, '--agent'),
1197
+ status: getArgValue(args, '--status'),
1198
+ class: getArgValue(args, '--class'),
1199
+ milestone: getArgValue(args, '--milestone'),
1200
+ };
1201
+ if (subcommand === 'record') {
1202
+ findings.cmdFindingsRecord(cwd, {
1203
+ phase: filters.phase,
1204
+ agent: filters.agent,
1205
+ file: getArgValue(args, '--file'),
1206
+ text: getArgValue(args, '--text'),
1207
+ stdin: args.includes('--stdin'),
1208
+ }, raw);
1209
+ } else if (subcommand === 'list') {
1210
+ findings.cmdFindingsList(cwd, filters, raw);
1211
+ } else if (subcommand === 'dispose') {
1212
+ findings.cmdFindingsDispose(cwd, {
1213
+ ids: findings.findingIdsIn(args.slice(2)),
1214
+ phase: filters.phase,
1215
+ agent: filters.agent,
1216
+ open: args.includes('--open'),
1217
+ as: getArgValue(args, '--as'),
1218
+ reason: getArgValue(args, '--reason'),
1219
+ }, raw);
1220
+ } else if (subcommand === 'debt') {
1221
+ findings.cmdFindingsDebt(cwd, { milestone: filters.milestone }, raw);
1222
+ } else {
1223
+ error('Unknown findings subcommand. Available: record, list, dispose, debt');
1224
+ }
1225
+ break;
1226
+ }
1227
+
1180
1228
  case 'preview': {
1181
1229
  const subcommand = args[1];
1182
1230
  if (subcommand === 'phase') {
@@ -1408,17 +1456,23 @@ async function main() {
1408
1456
  } else if (subcommand === 'learn') {
1409
1457
  optimize.cmdOptimizeLearn(cwd, {
1410
1458
  sessionId: getArgValue(args, '--session'),
1459
+ sessions: getArgValue(args, '--sessions'),
1411
1460
  }, raw);
1412
1461
  } else if (subcommand === 'apply') {
1413
1462
  optimize.cmdOptimizeApply(cwd, {
1414
1463
  reportPath: getArgValue(args, '--report'),
1415
1464
  }, raw);
1465
+ } else if (subcommand === 'revert') {
1466
+ optimize.cmdOptimizeRevert(cwd, {
1467
+ applyId: args[2] && !args[2].startsWith('--') ? args[2] : null,
1468
+ last: args.includes('--last'),
1469
+ }, raw);
1416
1470
  } else if (subcommand === 'list') {
1417
1471
  optimize.cmdOptimizeList(cwd, raw);
1418
1472
  } else if (subcommand === 'stats') {
1419
1473
  optimize.cmdOptimizeStats(cwd, raw);
1420
1474
  } else {
1421
- error('Unknown optimize subcommand. Available: trace, learn, apply, list, stats');
1475
+ error('Unknown optimize subcommand. Available: trace, learn, apply, revert, list, stats');
1422
1476
  }
1423
1477
  break;
1424
1478
  }
@@ -4,7 +4,7 @@ This directory holds AI-derived behavioral patterns extracted from real PAN
4
4
  Wizard sessions via the **self-improvement loop** (v3.7.0+, see
5
5
  [ADR-0026](../../docs/decisions/ADR-0026-self-improvement-loop.md)).
6
6
 
7
- Patterns are produced by running `pan-tools learn promote --pattern <id>` over
7
+ Patterns are promoted by hand with `pan-tools learn promote` (see Lifecycle) from
8
8
  harvested experiment data. They are **advisory** — orchestrators weight them
9
9
  against current context, not as hard rules.
10
10
 
@@ -46,9 +46,10 @@ patterns:
46
46
 
47
47
  ## Lifecycle
48
48
 
49
- 1. **Promote** — `pan-tools learn promote --pattern <id> --scope universal --topic <name>` appends a pattern to the topic file (creates the file if absent).
50
- 2. **Unpromote** — `pan-tools learn unpromote --pattern <id> --topic <name>` removes a pattern (for rollback).
49
+ 1. **Promote** — `pan-tools learn promote --pattern <id> --scope universal --topic <name> --summary <text> --rule <text>` appends a pattern to the topic file (creates the file if absent); `--evidence`, `--applies-in` and `--source-experiments` fill in the rest of the entry.
50
+ 2. **Unpromote** — `pan-tools learn unpromote --pattern <id> --scope <universal|internal> --topic <name>` removes a pattern (for rollback) and deletes the topic file once it holds none.
51
51
  3. **List** — `pan-tools learn list-promoted` shows the inventory across both tiers.
52
+ 4. **Index** — `pan-tools learn build-index` regenerates `index.json`, the topic index `pan-tools learn topics-for --agent <name>` reads; promote and unpromote do not update it.
52
53
 
53
54
  ## Why two tiers
54
55
 
@@ -58,13 +59,15 @@ apply when the project *is* PAN. Examples:
58
59
  - **PAN-internal**: "Always commit individually, never `git add -A`" (because of source repo's pre-commit hooks)
59
60
  - **Universal**: "Run the full test suite before marking a phase complete"
60
61
 
61
- The promote step uses a heuristic filter on file paths in the pattern's
62
- evidence. References to `pan-wizard-core/`, `bin/install.js`, `commands/pan/*`
63
- suggest `internal` scope. The human running `promote` makes the final call.
62
+ The promote step warns when a universal-scope pattern reads like a prompt
63
+ fragment rather than a structural pattern (P-RES-007), and `pan-tools learn lint`
64
+ warns when a universal pattern's heading or Rule names PAN-internal terms such as
65
+ `pan-wizard-core` or `pan-tools`, a candidate for `internal` scope. The human
66
+ running `promote` makes the final call.
64
67
 
65
68
  ## Maintenance
66
69
 
67
70
  These files are **AI-managed**. Direct human edits create drift between
68
- the frontmatter `pattern_ids` list and the body content. For human-authored
71
+ the frontmatter `patterns` list and the body content. For human-authored
69
72
  behavioral content, use `references/` instead — that's the canonical
70
73
  hand-authored channel (e.g., `references/guardrails.md` shipped in v3.6.0).
@@ -69,6 +69,8 @@ const RESOURCES = [
69
69
  description: 'Doc↔code link graph verdict: forward links, backlink contracts, and anchor targets, with finding codes.' },
70
70
  { uri: 'pan://cost', name: 'Token cost', verb: 'cost', args: ['report'],
71
71
  description: 'Aggregated token spend from the .planning/metrics ledger. Reads as zeros on a project with no recorded calls.' },
72
+ { uri: 'pan://findings', name: 'Findings', verb: 'findings', args: ['list'],
73
+ description: 'Every finding PAN\'s judges recorded (verifier, plan checker, reviewer, design checker), with its folded status — open, fixed, deferred, dismissed or decision — and the reason given. Reads as an empty list on a project with no recorded verdicts.' },
72
74
  ];
73
75
 
74
76
  /** Actionable pan-tools verbs → MCP tools (each spawns `node pan-tools.cjs <verb>`). */
@@ -0,0 +1,101 @@
1
+ # The `pan-verdict` block
2
+
3
+ A judging agent ends its report with one fenced `pan-verdict` block. The block is the machine-readable form of the verdict the report already gives in prose. The orchestrator records it with `pan-tools findings record`, and that record is what the workflow branches on. It also feeds the findings ledger that milestone audits read for tech debt. The prose stays for people, and the block states the same verdict for code.
4
+
5
+ ## Rules
6
+
7
+ 1. **Place it last.** The block is the final element of your return. When a report holds more than one block, the recorder reads the last one.
8
+ 2. **Valid JSON only.** No comments and no trailing commas. One line or pretty-printed are both fine.
9
+ 3. **Same verdict as the prose.** `verdict` is the word your report already uses, and `outcome` is what that word means (see the table below). The block never disagrees with the report.
10
+ 4. **One finding per issue.** List the findings the report lists, each as its own entry, most severe first. A clean pass has `"findings": []`.
11
+ 5. **Plain summaries.** State each finding in one sentence of 300 characters or fewer. Put the location in `where`, not in `summary`.
12
+
13
+ ````markdown
14
+ ```pan-verdict
15
+ {"contract":"1.0","agent":"pan-reviewer","phase":"03","verdict":"NEEDS_FIXES","outcome":"fail","findings":[{"class":"defect","severity":"high","where":"src/utils/parser.ts:42","summary":"parse() drops the final token of every line"}]}
16
+ ```
17
+ ````
18
+
19
+ ## Fields
20
+
21
+ | Field | Required | Value |
22
+ |---|---|---|
23
+ | `contract` | yes | `"1.0"` |
24
+ | `agent` | yes | your agent name, e.g. `pan-reviewer` |
25
+ | `phase` | when you know it | the phase number you were asked about, e.g. `"03"` |
26
+ | `verdict` | yes | your own verdict word (table below) |
27
+ | `outcome` | yes | `pass`, `fail` or `needs_human` |
28
+ | `findings` | yes (may be empty) | a list of `{class, severity, where, summary}` |
29
+
30
+ | Agent | `verdict` → `outcome` |
31
+ |---|---|
32
+ | pan-plan-checker | `passed` → `pass` · `issues_found` → `fail` |
33
+ | pan-reviewer | `PASS` → `pass` · `PASS_WITH_WARNINGS` → `pass` · `NEEDS_FIXES` → `fail` |
34
+ | pan-design-checker | `PASS` → `pass` · `GAPS` → `fail` |
35
+
36
+ The verifier does not emit this block. Its verification.md frontmatter (`status`, `gaps`, `human_verification`, `unrequested`) is its machine contract, and the recorder reads that directly.
37
+
38
+ ## `class`: what kind of finding
39
+
40
+ | Class | Use when |
41
+ |---|---|
42
+ | `missing` | Something required is absent: a requirement with no task, a must-have not built, an unplanned key link |
43
+ | `partial` | Present but incomplete: a task missing its verify/done fields, a stub, a half-derived criterion |
44
+ | `contradicts` | It conflicts with a requirement, a locked decision (context.md) or the codebase's established architecture |
45
+ | `unrequested` | Work, a file or a behaviour that no plan or requirement asked for (scope creep) |
46
+ | `defect` | Incorrect behaviour or a code error; a broken dependency order in a plan |
47
+ | `risk` | A security, reliability or scale exposure; a plan whose scope will not fit its budget |
48
+ | `quality` | Convention, naming, style or maintainability, with no correctness impact |
49
+ | `human` | Cannot be settled by a check, so a person must look |
50
+
51
+ ## `severity`: how much it matters
52
+
53
+ `critical` · `high` · `medium` · `low` · `info`. This is the same ladder `/pan:review-deep` uses.
54
+
55
+ ## Mapping for each judge
56
+
57
+ **pan-plan-checker.**
58
+ - Classes by dimension:
59
+ - Requirement coverage, key links, test coverage, Nyquist → `missing`
60
+ - Task completeness, verification derivation, spec sufficiency, decision trace → `partial`
61
+ - Dependency correctness → `defect`
62
+ - Scope sanity → `risk`
63
+ - Context compliance → `contradicts`
64
+ - Standards → `quality`
65
+ - Severity: blocker → `high`, warning → `medium`, info → `info`.
66
+ - `where`: `NN-MM-plan.md` or `NN-MM-plan.md#task-N`.
67
+
68
+ **pan-reviewer.**
69
+ - Classes by category: Convention → `quality`, Security → `risk`, Code Quality → `defect`, work no plan asked for → `unrequested`.
70
+ - Severity: ERROR → `high`, WARNING → `medium`, INFO → `info`.
71
+ - `where`: `path:line`.
72
+
73
+ **pan-design-checker.**
74
+ - Classes by dimension:
75
+ - Requirement coverage → `missing`
76
+ - Architecture conformance → `contradicts`
77
+ - Threat coverage → `risk`
78
+ - Scope discipline → `unrequested`
79
+ - Every other dimension → `partial`
80
+ - Severity: each gap is `high`. A gap you report as a caveat on the final iteration is `medium`.
81
+ - `where`: the design section, e.g. `03-design.md#interfaces`.
82
+
83
+ ## Examples
84
+
85
+ A plan check that passes:
86
+
87
+ ````markdown
88
+ ```pan-verdict
89
+ {"contract":"1.0","agent":"pan-plan-checker","phase":"05","verdict":"passed","outcome":"pass","findings":[]}
90
+ ```
91
+ ````
92
+
93
+ A design check with two gaps:
94
+
95
+ ````markdown
96
+ ```pan-verdict
97
+ {"contract":"1.0","agent":"pan-design-checker","phase":"05","verdict":"GAPS","outcome":"fail","findings":[
98
+ {"class":"missing","severity":"high","where":"05-design.md#requirements","summary":"REQ-12 (export to CSV) maps to no design element"},
99
+ {"class":"risk","severity":"high","where":"05-design.md#threats","summary":"the upload endpoint accepts any file type and no threat entry covers it"}]}
100
+ ```
101
+ ````
@@ -12,6 +12,9 @@ phase: XX-name
12
12
  verified: YYYY-MM-DDTHH:MM:SSZ
13
13
  status: passed | gaps_found | human_needed
14
14
  score: N/M must-haves verified
15
+ unrequested: # Only when the scope check (`pan-tools verify scope <phase>`) found work no plan asked for
16
+ - path: "src/admin/panel.tsx"
17
+ what: "An admin panel no plan or requirement asked for"
15
18
  ---
16
19
 
17
20
  # Phase {X}: {Name} Verification Report
@@ -89,6 +92,19 @@ None — all verifiable items checked programmatically.
89
92
  **Expected:** {What should happen}
90
93
  **Why human:** {Why can't verify programmatically}
91
94
 
95
+ ## Unrequested Work
96
+
97
+ {If the scope check found none:}
98
+ None: every change traces to a plan.
99
+
100
+ {Otherwise, one row per `unrequested:` entry:}
101
+
102
+ | File | What it adds | Why no plan covers it |
103
+ |------|--------------|-----------------------|
104
+ | src/admin/panel.tsx | An admin panel | No requirement mentions admin tooling |
105
+
106
+ Unrequested work is recorded, not blocking: the status does not change because of it.
107
+
92
108
  ## Gaps Summary
93
109
 
94
110
  {If no gaps:}
@@ -425,13 +425,21 @@ Task(
425
425
  )
426
426
  ```
427
427
 
428
- 3. Handle review results:
428
+ 3. **Save the report.** Use the Write tool to write the reviewer's returned text, verbatim and in full (including its closing `pan-verdict` block), to `{phase_dir}/{phase_number}-review.md`. The reviewer is read-only by design, so the orchestrator saves the report. `/pan:review-deep` and the findings ledger read this file.
429
+
430
+ 4. **Record the verdict, then act on it.** The record is what this step branches on. It also adds the findings to the ledger (`.planning/findings.jsonl`) and logs the verdict to the trace, so no separate trace call is needed:
431
+ ```bash
432
+ REVIEW_FILE="{phase_dir}/{phase_number}-review.md"
433
+ REVIEW_VERDICT=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings record --phase "${PHASE_NUMBER}" --agent pan-reviewer --file "$REVIEW_FILE" --raw 2>/dev/null) \
434
+ || REVIEW_VERDICT=$(grep -A1 '^### Verdict' "$REVIEW_FILE" | tail -1 | tr -d ' ')
435
+ ```
436
+ The fallback reads the word under `### Verdict` when the report carries no valid `pan-verdict` block.
429
437
 
430
438
  | Verdict | Action |
431
439
  |---------|--------|
432
440
  | `PASS` | Continue to verification |
433
- | `PASS_WITH_WARNINGS` | Report warnings, continue to verification |
434
- | `NEEDS_FIXES` | Present ERROR findings to user: "Fix before verification?" or "Continue anyway?" |
441
+ | `PASS_WITH_WARNINGS` | Report the warnings, then record that the phase continues past them (below), and continue to verification |
442
+ | `NEEDS_FIXES` | Present the ERROR findings to the user: "Fix before verification?" or "Continue anyway?" |
435
443
 
436
444
  ```
437
445
  ## Code Review Results
@@ -442,34 +450,60 @@ Task(
442
450
  {If errors: list top 5 with file:line}
443
451
  ```
444
452
 
445
- **If user chooses "Continue anyway":** Proceed to verification. Review findings are informational, not blocking.
453
+ **Never continue past findings silently.** Whenever the phase moves on with review findings still open, record why. The reason is what a milestone audit later reports as tech debt:
454
+ - `PASS_WITH_WARNINGS`:
455
+ ```bash
456
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings dispose --phase "${PHASE_NUMBER}" --agent pan-reviewer --open --as deferred --reason "accepted at review: PASS_WITH_WARNINGS"
457
+ ```
458
+ - **The user chooses "Continue anyway"** at `NEEDS_FIXES`: the same command with `--reason "continued past NEEDS_FIXES at the user's choice"`, then proceed to verification.
459
+ - **The user chooses "Fix before verification"**: apply the fixes, then run the review once more (steps 2–4). The new record closes the findings it no longer reports.
446
460
 
447
- 4. **Circular optimization — log reviewer corrections to trace bus (W1 fix):**
461
+ 5. **No separate trace logging.** The record logs `verdict_passed` or `verdict_failed` for the reviewer on every runtime. The `reviewer_correction` and `reviewer_warnings` trace calls that used to sit here read the error count from a shell variable nothing ever set.
448
462
 
449
- When verdict is `NEEDS_FIXES`, write a `reviewer_correction` event so the optimizer can see the primary quality signal:
450
- ```bash
451
- # Extract error count from review output (parse "Errors: N" line)
452
- REVIEW_ERROR_COUNT=$(echo "$REVIEW_OUTPUT" | grep -E "^- Errors: [0-9]+" | grep -oE "[0-9]+" | head -1)
453
- REVIEW_ERROR_COUNT=${REVIEW_ERROR_COUNT:-1}
454
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
455
- --type error --category reviewer_correction \
456
- --description "Phase ${PHASE_NUMBER} reviewer: NEEDS_FIXES — ${REVIEW_ERROR_COUNT} error(s) found" \
457
- --agent pan-reviewer --impact major \
458
- --context "{\"phase\":\"${PHASE_NUMBER}\",\"verdict\":\"NEEDS_FIXES\",\"error_count\":${REVIEW_ERROR_COUNT}}" \
459
- 2>/dev/null || true
463
+ 6. **Deep review — only when `--deep-review` is in $ARGUMENTS.** The security pass and cross-check that `/pan:review-deep` runs, done inline right after the normal review. If the normal review was skipped (`--skip-review` or `--fast`), say that `--deep-review` needs the review it builds on, and continue without it.
464
+
465
+ a. Spawn the hardener. Its output path is `.planning/reviews/${PHASE_NUMBER}/hardener.md`:
466
+ ```
467
+ Task(
468
+ subagent_type="pan-hardener",
469
+ model="$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs resolve-model pan-hardener --raw)",
470
+ prompt="
471
+ <files_to_read>
472
+ - {phase_dir}/{phase_number}-review.md (the first-pass review)
473
+ - the phase plans: {phase_dir}/*-plan.md
474
+ Changed files to audit:
475
+ ${CHANGED_FILES_LIST}
476
+ </files_to_read>
477
+ <output_path>.planning/reviews/${PHASE_NUMBER}/hardener.md</output_path>
478
+ <framework_scope>OWASP Top 10 (2025) and STRIDE, across every changed file.</framework_scope>
479
+ "
480
+ )
460
481
  ```
461
482
 
462
- When verdict is `PASS_WITH_WARNINGS`, log a softer signal:
463
- ```bash
464
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
465
- --type decision --category reviewer_warnings \
466
- --description "Phase ${PHASE_NUMBER} reviewer: PASS_WITH_WARNINGS — warnings present" \
467
- --agent pan-reviewer --impact trivial \
468
- --context "{\"phase\":\"${PHASE_NUMBER}\",\"verdict\":\"PASS_WITH_WARNINGS\"}" \
469
- 2>/dev/null || true
483
+ b. Spawn the meta-reviewer on both first-pass reports. Its output path is `.planning/reviews/${PHASE_NUMBER}/meta.md`:
470
484
  ```
485
+ Task(
486
+ subagent_type="pan-meta-reviewer",
487
+ model="$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs resolve-model pan-meta-reviewer --raw)",
488
+ prompt="
489
+ <files_to_read>
490
+ - {phase_dir}/{phase_number}-review.md
491
+ - .planning/reviews/${PHASE_NUMBER}/hardener.md
492
+ </files_to_read>
493
+ <output_path>.planning/reviews/${PHASE_NUMBER}/meta.md</output_path>
494
+ "
495
+ )
496
+ ```
497
+ If either agent returns its report instead of writing the output path, save the returned text there with the Write tool before the merge.
471
498
 
472
- When verdict is `PASS`, no trace event needed — clean passes are the expected baseline.
499
+ c. Merge the three reports:
500
+ ```bash
501
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs review-deep merge "${PHASE_NUMBER}" \
502
+ --reviewer-file "{phase_dir}/{phase_number}-review.md" \
503
+ --hardener-file ".planning/reviews/${PHASE_NUMBER}/hardener.md" \
504
+ --meta-file ".planning/reviews/${PHASE_NUMBER}/meta.md"
505
+ ```
506
+ This writes `.planning/reviews/${PHASE_NUMBER}/deep-review.md` and prints its verdict (`ok` · `ok_with_minor` · `fix_before_merge` · `review_required` · `block`). Report the verdict and the finding count. On `review_required` or `block`, stop before verification and present the high and critical findings to the user, as with `NEEDS_FIXES`.
473
507
  </step>
474
508
 
475
509
  <step name="close_parent_artifacts">
@@ -539,31 +573,15 @@ Create verification.md.",
539
573
  )
540
574
  ```
541
575
 
542
- Read status:
576
+ Record the verification and read its status:
543
577
  ```bash
544
- VERIF_STATUS=$(grep "^status:" "$PHASE_DIR"/*-verification.md | cut -d: -f2 | tr -d ' ')
578
+ VERIF_FILE=$(ls "$PHASE_DIR"/*-verification.md 2>/dev/null | head -1)
579
+ VERIF_STATUS=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings record --phase "${PHASE_NUMBER}" --file "$VERIF_FILE" --raw 2>/dev/null) \
580
+ || VERIF_STATUS=$(grep "^status:" "$VERIF_FILE" | cut -d: -f2 | tr -d ' ')
545
581
  ```
582
+ `findings record` reads the verdict from the verification frontmatter (`status`, `gaps`, `human_verification`, `unrequested`) and adds its findings to the ledger. A re-verification after a fix round is the next attempt: the gaps it no longer reports are closed, and the retry is logged. Recording the same file twice is a no-op, so verify-phase recording it too is safe. The grep fallback keeps the old reading when the record cannot parse the file.
546
583
 
547
- **P-1806 fix (v3.7.8):** the trace event for verify-phase outcome is logged HERE (in exec-phase.md after the verifier returns) rather than only at the end of verify-phase.md. When verification runs inline (orchestrator-as-verifier in auto mode, no separate Task spawn), the trace.log block at the bottom of verify-phase.md never executes — the orchestrator just writes verification.md and continues. By logging here, the event fires regardless of the verification path taken. Surfaced by the wookie autonomous build (v3.7.5–v3.7.7): all 5 phases used inline verification, no `verification_passed` events appeared in the trace despite v3.7.5 adding the trace block to verify-phase.md.
548
-
549
- ```bash
550
- if [ "$VERIF_STATUS" = "passed" ]; then
551
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
552
- --type decision --category verification_passed \
553
- --description "Phase ${PHASE_NUMBER} verification passed (logged from exec-phase, P-1806)" \
554
- --agent pan-verifier --impact minor 2>/dev/null || true
555
- elif [ "$VERIF_STATUS" = "gaps_found" ]; then
556
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
557
- --type error --category verification_gaps \
558
- --description "Phase ${PHASE_NUMBER} verification found gaps" \
559
- --agent pan-verifier --impact major 2>/dev/null || true
560
- elif [ "$VERIF_STATUS" = "human_needed" ]; then
561
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
562
- --type decision --category verification_human_needed \
563
- --description "Phase ${PHASE_NUMBER} verification awaiting human review" \
564
- --agent pan-verifier --impact minor 2>/dev/null || true
565
- fi
566
- ```
584
+ **Trace logging happens in the record.** The record above logs the verification outcome (`verdict_passed`, `verdict_failed` or `verdict_needs_human`) here, after the verifier returns. That covers inline verification too, which is the reason P-1806 moved the trace call here (v3.7.8). No separate `optimize trace log` call is needed.
567
585
 
568
586
  | Status | Action |
569
587
  |--------|--------|
@@ -163,6 +163,8 @@ Report final status.
163
163
  | I001 | info | Plan without SUMMARY (may be in progress) | No |
164
164
  | I002 | info | Phase in ROADMAP ahead of current phase, not planned yet | No |
165
165
  | I003 | info | Focus-model or campaign tree — the phase-model checks do not apply | No |
166
+ | I004 | info | planning-with-files shares the tree — PAN leaves its files alone | No |
167
+ | I005 | info | The planning tree is gitignored while commit_docs is true — planning commits commit nothing | No |
166
168
  | STATE_REQ_DRIFT | warning | state.md complete but REQUIREMENTS.md has unchecked boxes | Yes |
167
169
  | STATE_ROADMAP_DRIFT | warning | state.md complete but roadmap.md has unchecked plan boxes | Yes |
168
170
  | VERIFICATION_GATE_MISSING | warning | Phase has completed plans but no verification record | No |
@@ -31,10 +31,10 @@ If `--session <id>` was specified, use that session ID.
31
31
 
32
32
  Run:
33
33
  ```
34
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize learn [--session <id>]
34
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize learn [--session <id> | --sessions <n>]
35
35
  ```
36
36
 
37
- This produces `.planning/optimization/reports/{session}-analysis.json`.
37
+ This produces `.planning/optimization/reports/{session}-analysis.json`. With `--sessions <n>` it pools the last n sessions into `pooled-<n>-{session}-analysis.json`: a recommendation should explain failures that recur across runs, not one run's accident.
38
38
 
39
39
  Read the output and note:
40
40
  - `summary.errors` — how many error events
@@ -42,6 +42,8 @@ Read the output and note:
42
42
  - `summary.memory_misses` — how many memory miss events
43
43
  - `summary.wasted_tokens` — tokens wasted on redundancies
44
44
  - `top_error_patterns` — most frequent error categories
45
+ - `top_tool_error_patterns` — failed tool calls captured from the subagents' own transcripts, ranked by how many spawns hit them
46
+ - `verdict_stats` — each judge's pass/fail counts, retries, and the retries that resolved a failure
45
47
  - `top_memory_misses` — most frequent memory miss topics
46
48
 
47
49
  ## Step 3 — Invoke pan-optimizer agent
@@ -82,6 +82,18 @@ From each verification.md, extract:
82
82
 
83
83
  If a phase is missing verification.md, flag it as "unverified phase" — this is a blocker.
84
84
 
85
+ ### 2b. Read the Recorded Findings
86
+
87
+ What the judges found in this milestone, and what was decided about each finding, is on record. Read it rather than reconstructing it from prose:
88
+
89
+ ```bash
90
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings debt --milestone "{version}"
91
+ ```
92
+
93
+ - **`deferred`** findings were continued past on purpose, and each carries the reason recorded at the time. They are the milestone's tech debt: put every one in `tech_debt`, with its id and reason.
94
+ - **`open`** findings were never disposed (no fix, deferral, dismissal or decision). List them under "Undisposed findings" in the report. They are not blockers by themselves: the verification gate handles blockers. They do mean the milestone cannot be `passed`, so the status is at least `tech_debt`.
95
+ - A dismissal or decision is settled and not debt. `findings list --milestone "{version}" --status dismissed` shows the dismissals and their reasons when the report needs them.
96
+
85
97
  ## 3. Spawn Integration Checker
86
98
 
87
99
  With phase context collected:
@@ -181,11 +193,11 @@ gaps: # Critical blockers
181
193
  evidence: "{specific evidence or lack thereof}"
182
194
  integration: [...]
183
195
  flows: [...]
184
- tech_debt: # Non-critical, deferred
196
+ tech_debt: # Non-critical, deferred — from `findings debt` (step 2b) plus anything the verifications list
185
197
  - phase: 01-auth
186
198
  items:
199
+ - "[f_81ab3c9d20] no password strength validation — deferred: accepted at review: PASS_WITH_WARNINGS"
187
200
  - "TODO: add rate limiting"
188
- - "Warning: no password strength validation"
189
201
  - phase: 03-dashboard
190
202
  items:
191
203
  - "Deferred: mobile responsive layout"
@@ -197,7 +209,7 @@ Plus full markdown report with tables for requirements, phases, integration, tec
197
209
  **Status values:**
198
210
  - `passed` — all requirements met, no critical gaps, minimal tech debt
199
211
  - `gaps_found` — critical blockers exist
200
- - `tech_debt` — no blockers but accumulated deferred items need review
212
+ - `tech_debt` — no blockers but accumulated deferred items need review, or recorded findings were never disposed
201
213
 
202
214
  ## 7. Present Results
203
215
 
@@ -8,6 +8,7 @@ Manage the circular optimization loop — apply reports, check stats, control tr
8
8
  |------------|--------|
9
9
  | `apply` | Apply safe recommendations from most recent report |
10
10
  | `apply --report <file>` | Apply from a specific report |
11
+ | `revert <apply_id>` / `revert --last` | Undo one apply exactly |
11
12
  | `list` | List all reports |
12
13
  | `stats` | Show cumulative stats |
13
14
  | `trace init` | Start a new trace session |
@@ -40,8 +41,9 @@ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize apply [--report <path>
40
41
 
41
42
  Show the user:
42
43
  - **Applied** — each item that was written (memory entries, notes)
43
- - **Skipped** — items that already exist or had unknown types
44
+ - **Skipped** — items that already exist, that an earlier apply already wrote, or that had unknown types
44
45
  - **Still needs review** — prompt/workflow suggestions in `suggestions.md`
46
+ - **The `apply_id`**, and how to undo the apply: `/pan:optimize revert <apply_id>`
45
47
 
46
48
  If memory entries were written, tell the user:
47
49
  > Memory entries have been added to `.planning/memory/`. They will be loaded on the next agent run, reducing future memory misses for these topics.
@@ -54,6 +56,17 @@ If `.planning/optimization/suggestions.md` exists, tell the user to review it fo
54
56
 
55
57
  ---
56
58
 
59
+ ## revert
60
+
61
+ ```
62
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize revert <apply_id>
63
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize revert --last
64
+ ```
65
+
66
+ Show the user each reverted action and each refused one, with its reason. A refusal means someone edited the file after the apply, or a later apply also wrote it. Revert that later apply first; never force it. The status is `reverted`, `partial`, `refused` or `nothing_to_revert`.
67
+
68
+ ---
69
+
57
70
  ## trace init
58
71
 
59
72
  ### Step 1 — Extract description
@@ -222,7 +222,7 @@ In that case, log a `decision` trace event (`type: "decision", category: "skip-r
222
222
 
223
223
  This is a workflow-level optimization — the planner still produces a plan, just without per-phase research.md. Phase 2+ phases with substantive build work still go through full research.
224
224
 
225
- **P-1602 phase_record_compact (v3.7.5+):** When `workflow.phase_record_compact: true` AND the lightweight-phase bypass above triggers, also skip per-phase context.md creation (step 4) and emit a single combined `${PHASE_NUM}-record.md` after planning containing: goal, locked decisions (from project-level context), plan summary, must_haves. Reduces 4-file phase output (context, research, plan, summary) to a 2-file output (record, plan) for trivial phases. Off by default — opt-in via `pan-tools config-set workflow.phase_record_compact true`. Substantive phases (>1 plan or non-trivial change_class) still produce full per-phase artifacts regardless of this flag.
225
+ **P-1602 phase_record_compact (v3.7.5+):** When `workflow.phase_record_compact: true` AND the lightweight-phase bypass above triggers, also skip per-phase context.md creation (step 4) and emit a single combined `${PHASE_NUM}-record.md` after planning containing: goal, locked decisions (from project-level context), plan summary, must_haves. Reduces the planning output (context, research, plan) to a record and a plan for trivial phases; execution still writes the plan's summary. Off by default — opt-in via `pan-tools config-set workflow.phase_record_compact true`. Substantive phases (>1 plan or non-trivial change_class) still produce full per-phase artifacts regardless of this flag.
226
226
 
227
227
  **If `has_research` is true (from init) AND no `--research` flag:** Use existing, skip to step 6.
228
228
 
@@ -448,20 +448,20 @@ Task(
448
448
 
449
449
  ## 11. Handle Checker Return
450
450
 
451
- - **`## VERIFICATION PASSED`:** Display confirmation, proceed to step 13.
452
- ```bash
453
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
454
- --type decision --category plan_verified \
455
- --description "Plan-checker passed for phase ${PHASE_NUMBER}" \
456
- --agent pan-plan-checker --impact trivial 2>/dev/null || true
457
- ```
458
- - **`## ISSUES FOUND`:** Display issues, check iteration count, proceed to step 12.
459
- ```bash
460
- node ~/.claude/pan-wizard-core/bin/pan-tools.cjs optimize trace log \
461
- --type error --category plan_checker_issues \
462
- --description "Plan-checker found issues in phase ${PHASE_NUMBER} plans" \
463
- --agent pan-plan-checker --impact minor 2>/dev/null || true
464
- ```
451
+ Save the checker's returned text, verbatim and in full (including its closing `pan-verdict` block), to `{phase_dir}/{padded_phase}-plan-check.md` with the Write tool. Then record it, and branch on the record. The record also adds the issues to the findings ledger and logs the check to the trace; there is no separate trace call:
452
+
453
+ ```bash
454
+ CHECK_FILE="{phase_dir}/{padded_phase}-plan-check.md"
455
+ CHECK_VERDICT=$(node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings record --phase "${PHASE_NUMBER}" --agent pan-plan-checker --file "$CHECK_FILE" --raw 2>/dev/null) \
456
+ || CHECK_VERDICT=$(grep -q '^## VERIFICATION PASSED' "$CHECK_FILE" && echo passed || echo issues_found)
457
+ ```
458
+
459
+ The fallback reads the heading when the report carries no valid `pan-verdict` block.
460
+
461
+ - **`passed`:** Display confirmation, proceed to step 13.
462
+ - **`issues_found`:** Display issues, check iteration count, proceed to step 12.
463
+
464
+ Each re-check in the revision loop overwrites the same file and is recorded again as the next attempt. The issues a revision resolved are closed by that record.
465
465
 
466
466
  ## 12. Revision Loop (Max 3 Iterations)
467
467
 
@@ -510,6 +510,11 @@ Display: `Max iterations reached. {N} issues remain:` + issue list
510
510
 
511
511
  Offer: 1) Force proceed, 2) Provide guidance and retry, 3) Abandon
512
512
 
513
+ **On "Force proceed":** record that planning continues past the remaining issues. Execution and the milestone audit will see them as deferred, with this reason:
514
+ ```bash
515
+ node ~/.claude/pan-wizard-core/bin/pan-tools.cjs findings dispose --phase "${PHASE_NUMBER}" --agent pan-plan-checker --open --as deferred --reason "force proceed after 3 plan revision iterations"
516
+ ```
517
+
513
518
  ## 13. Present Final Status
514
519
 
515
520
  **Sync state.md first** — without this, state.md says "Ready to plan" until the