peaks-cli 1.2.3 → 1.2.5

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 (32) hide show
  1. package/dist/src/cli/commands/openspec-commands.js +31 -0
  2. package/dist/src/cli/commands/project-commands.js +51 -1
  3. package/dist/src/cli/commands/sop-commands.js +2 -2
  4. package/dist/src/cli/commands/workspace-commands.js +38 -4
  5. package/dist/src/services/memory/project-memory-service.d.ts +50 -0
  6. package/dist/src/services/memory/project-memory-service.js +412 -35
  7. package/dist/src/services/openspec/openspec-init-service.d.ts +23 -0
  8. package/dist/src/services/openspec/openspec-init-service.js +122 -0
  9. package/dist/src/services/session/index.d.ts +1 -1
  10. package/dist/src/services/session/index.js +1 -1
  11. package/dist/src/services/session/session-manager.d.ts +11 -0
  12. package/dist/src/services/session/session-manager.js +19 -0
  13. package/dist/src/services/skills/skill-presence-service.js +11 -0
  14. package/dist/src/services/sop/sop-check-service.d.ts +16 -0
  15. package/dist/src/services/sop/sop-check-service.js +35 -2
  16. package/dist/src/services/sop/sop-service.d.ts +8 -0
  17. package/dist/src/services/sop/sop-service.js +13 -2
  18. package/dist/src/services/sop/sop-types.d.ts +7 -0
  19. package/dist/src/services/workspace/workspace-service.d.ts +15 -0
  20. package/dist/src/services/workspace/workspace-service.js +60 -1
  21. package/dist/src/shared/version.d.ts +1 -1
  22. package/dist/src/shared/version.js +1 -1
  23. package/package.json +1 -1
  24. package/skills/peaks-prd/SKILL.md +36 -0
  25. package/skills/peaks-qa/SKILL.md +92 -2
  26. package/skills/peaks-rd/SKILL.md +70 -2
  27. package/skills/peaks-solo/SKILL.md +253 -40
  28. package/skills/peaks-solo/references/swarm-dispatch-contract.md +186 -0
  29. package/skills/peaks-sop/SKILL.md +17 -0
  30. package/skills/peaks-sop/references/sop-authoring.md +1 -1
  31. package/skills/peaks-txt/SKILL.md +16 -0
  32. package/skills/peaks-ui/SKILL.md +61 -2
@@ -9,6 +9,16 @@ Peaks-Cli Solo is the orchestration facade for the Peaks-Cli short skill family.
9
9
 
10
10
  Use this skill to identify the user scenario, recommend an execution mode, coordinate role skills, and produce the final handoff report. Do not collapse role responsibilities into this skill.
11
11
 
12
+ ## Skill-first architecture note (read once, internalise)
13
+
14
+ This skill is the **primary surface**. The `peaks <cmd>` CLI is **auxiliary** — invoked by the skill prompt when a primitive is the right tool (atomic side effect, machine-enforced gate, structured JSON for a downstream decision, or backstop the LLM cannot skip). Concretely:
15
+
16
+ - Behaviour that only an LLM in a skill prompt would use (e.g. "scan a handoff for memory blocks", "decide if a stable fact deserves persistence") lives **here in the SKILL.md**, not as a new CLI command.
17
+ - The CLI earns its keep when it is (a) hook- / script- / CI-invokable, (b) the consumer needs a structured JSON envelope to gate a downstream decision, or (c) it is a destructive side effect that needs an explicit `--apply` opt-in.
18
+ - When you reach for `peaks <X> --project <repo> --json` in a runbook step, that command is the **contract** you are calling; the LLM work around it (deciding what to pass, interpreting the response, deciding the next step) is what this skill owns.
19
+
20
+ See `.claude/rules/common/dev-preference.md` for the full dev policy and the decision template. The user-facing consequence: every iteration on this skill and the rest of the peaks-* family is judged first by "is this skill work or CLI work?", and only the latter opens a new command.
21
+
12
22
  ## Code-Change Red Line (BLOCKING — read before ANY tool call)
13
23
 
14
24
  **Peaks-Cli Solo is an orchestrator, NOT an implementer. You MUST NOT write, edit, or modify any application source code directly.**
@@ -17,12 +27,12 @@ Every code change — bugfix, feature, refactor, or config — MUST go through t
17
27
 
18
28
  ```
19
29
  peaks-solo (orchestrate only)
20
- Skill(skill="peaks-rd") ← ALL code changes happen HERE
30
+ RD work ← ALL code changes happen HERE
21
31
  → Unit tests written + pass (Peaks-Cli Gate B2)
22
32
  → Karpathy standards enforced (file-size ≤800 lines, TypeScript rules)
23
33
  → Code review evidence (Peaks-Cli Gate B3)
24
34
  → Security review evidence (Peaks-Cli Gate B4)
25
- Skill(skill="peaks-qa") ← ALL validation happens HERE
35
+ QA work ← ALL validation happens HERE
26
36
  → Functional test execution (Peaks-Cli Gate A2)
27
37
  → Performance check (Peaks-Cli Gate A4)
28
38
  → Security test (Peaks-Cli Gate A3)
@@ -30,35 +40,83 @@ peaks-solo (orchestrate only)
30
40
  → Verdict: pass | return-to-rd | blocked
31
41
  ```
32
42
 
43
+ **Mechanism for "RD work" / "QA work" depends on the orchestration mode** (full details in "Peaks-Cli Swarm parallel phase" and "How Solo invokes another role"):
44
+
45
+ | Mode | Swarm side (after PRD) | Repair loop side (RD↔QA) |
46
+ |---|---|---|
47
+ | `full-auto` / `swarm` | `Task(subagent_type="general-purpose")` sub-agent running `peaks-rd`/`peaks-qa`/`peaks-ui` body | `Task(...)` sub-agent per cycle |
48
+ | `assisted` / `strict` / inline-fallback | Solo executes the role steps inline in the main loop (the `peaks-solo` skill IS the role's owner) | Solo executes inline |
49
+
50
+ In all modes, the work itself follows the same `peaks-rd` and `peaks-qa` contracts. The only difference is whether the role's body is being read by a sub-agent Task prompt or by Solo's own main loop. **Never bypass the role contracts regardless of which path runs.**
51
+
33
52
  **Violations (BLOCKING — Solo must refuse to proceed):**
34
53
 
35
- 1. Writing implementation code directly instead of calling `Skill(skill="peaks-rd")`
36
- 2. Declaring work "done" without invoking `Skill(skill="peaks-qa")` after RD
54
+ 1. Writing implementation code directly instead of routing through the RD contract (whether inline or via sub-agent)
55
+ 2. Declaring work "done" without producing QA evidence after RD
37
56
  3. Skipping unit tests ("it's a small change")
38
57
  4. Skipping code review or security review
39
58
  5. Skipping QA functional/performance/security validation
40
59
 
41
- **If you catch yourself about to write code in this skill, STOP. Call `Skill(skill="peaks-rd")` instead.**
60
+ **If you catch yourself about to write code in this skill, STOP. Hand off to the RD contract path immediately** (sub-agent Task in full-auto, inline execution in assisted/strict).
42
61
 
43
62
  **Before declaring workflow complete, run:** `peaks workflow verify-pipeline --rid <rid> --project <repo> --json`
44
63
 
45
64
  ## Peaks-Cli Startup sequence (MANDATORY — execute in order)
46
65
 
47
- ### Peaks-Cli Step 0: Anchor the workflow (MANDATORY FIRST ACTIONS — no bail-out)
66
+ ### Peaks-Cli Step 0.5: OpenSpec first-run opt-in (conditional)
48
67
 
49
- The instant Peaks-Cli Solo is invoked, **before** the mode-selection question, before any analysis, and before you decide whether the request "needs" the full pipeline, you MUST run these two commands and see their output:
68
+ After the workspace is anchored, before project scan, Solo checks whether
69
+ the project already has an `openspec/` directory. The lifecycle
70
+ (`render → validate → show → to-rd → validate → archive`) only applies
71
+ when `openspec/` exists; without it, RD/QA/SC silently skip the
72
+ openspec-aware paths and you lose change-proposal tracking, commit
73
+ boundaries from `tasks.md`, and the historical archive.
74
+
75
+ To make that opt-in visible instead of silent, Solo runs:
50
76
 
51
77
  ```bash
52
- # Session ID is auto-generated when omitted; the command returns it in the JSON output
53
- peaks workspace init --project <repo> --json
54
- peaks skill presence:set peaks-solo --project <repo> --gate startup
78
+ # 1. Detect whether the project already has openspec/.
79
+ ls <repo>/openspec/changes 2>&1
80
+ # 2. If absent, ask the user once — only on the first Solo run in this
81
+ # project. The decision is sticky: write it to .peaks/.peaks-openspec-opt-in.json
82
+ # so subsequent Solo invocations do not re-ask.
83
+ test -f <repo>/.peaks/.peaks-openspec-opt-in.json || \
84
+ echo "{\"enabled\": <bool>}" > <repo>/.peaks/.peaks-openspec-opt-in.json
55
85
  ```
56
86
 
57
- If `workspace init` fails with "required option '--session-id' not specified", the CLI version predates auto-generation. Generate a session ID manually and pass it:
87
+ **AskUserQuestion** (only when `openspec/` is absent and the opt-in
88
+ file is missing):
89
+
90
+ | Option | What it does |
91
+ |---|---|
92
+ | Enable OpenSpec for this project (Recommended) | Run `peaks openspec init --project <repo> --apply`. After that, every Solo run uses the change-proposal lifecycle for the same project. |
93
+ | Skip for now | Do nothing. Solo proceeds without openspec; the question is re-asked on the next first-run detection. |
94
+ | Never ask again for this project | Write `{enabled: false, sticky: true}`. Solo stops asking. The user can re-enable later by removing `.peaks/.peaks-openspec-opt-in.json` and re-running. |
95
+
96
+ The first option is the recommended default because it gives Solo the
97
+ full change-proposal lifecycle (proposal / tasks / design / specs
98
+ deltas, archive on ship, commit boundaries from `tasks.md`). It costs
99
+ only a single scaffolded directory and pays back the first time the
100
+ project needs a real review trail.
101
+
102
+ If the user picks "Enable", the only required follow-up is to make
103
+ sure `openspec/changes/` is added to git (it is part of the project
104
+ repo, not a tool-managed artefact). Solo does not run `git add` for
105
+ the user; that is the user's commit boundary.
106
+
107
+ ### Peaks-Cli Step 0: Anchor the workflow (MANDATORY FIRST ACTIONS — no bail-out)
108
+
109
+ The instant Peaks-Cli Solo is invoked, **before** the mode-selection question, before any analysis, and before you decide whether the request "needs" the full pipeline, you MUST run these two commands and see their output:
58
110
 
59
111
  ```bash
60
- SESSION_ID="$(date +%Y-%m-%d)-session-$(openssl rand -hex 3)"
61
- peaks workspace init --project <repo> --session-id "$SESSION_ID" --json
112
+ # Session ID is auto-generated when omitted; the command returns it in the JSON output.
113
+ # Do NOT pass --session-id manually — the CLI is the single source of truth for the
114
+ # project session binding. If you forge a session id with `openssl rand` and pass it
115
+ # via --session-id, peaks workspace init will write it to .peaks/.session.json but
116
+ # the binding only sticks if no prior session is open. To avoid the "two sessions
117
+ # in .peaks/" confusion that bites Solo, always omit --session-id here and let the
118
+ # CLI auto-generate.
119
+ peaks workspace init --project <repo> --json
62
120
  peaks skill presence:set peaks-solo --project <repo> --gate startup
63
121
  ```
64
122
 
@@ -68,6 +126,9 @@ peaks skill presence:set peaks-solo --project <repo> --gate startup
68
126
 
69
127
  **Anti-bail-out rule (BLOCKING):** You MUST NOT exit the peaks-solo workflow, hand control back, or produce a final answer before Step 0 has run. If you catch yourself thinking "this is just analysis, I don't need the workflow" — STOP. Run Step 0, set presence, then continue. A pure-analysis request runs the **lightweight analysis branch** (project scan + standards dry-run + handoff with a Standards-increment section), but it still anchors the workspace and keeps presence active. Declining to anchor is a workflow violation.
70
128
 
129
+ **Session conflict resolution (read once, internalise):** If `peaks workspace init` returns `code: "CONFLICTING_SESSION"` with a body like
130
+ `{"existingSessionId":"<Y>","requestedSessionId":"<X>"}`, the project is already bound to a different in-flight session `<Y>` (the one you or a prior run was working on). The fix is **NOT** to pass `--allow-session-rebind` to clobber `<Y>` — that destroys an active session's data. Instead: finish or abandon `<Y>` first (use `peaks session list --json` to see what it is, then `peaks session finish --id <Y>` or `peaks session abandon --id <Y>` — see your session command's help for the exact verbs). Only after `<Y>` is closed should you re-run `peaks workspace init`. The same rule applies to `peaks workspace init --session-id "<manually-forged>"` — do not pre-forge session ids; the CLI's auto-generated value is the binding.
131
+
71
132
  `presence:set` accepts no `--mode` here on purpose — mode is unknown until Step 1. It is re-run with the selected mode in Step 2. Setting presence early guarantees the status header/line shows `peaks-solo` from the very first turn even if the user never reaches mode selection.
72
133
 
73
134
  ### Peaks-Cli Step 1: Mode selection
@@ -595,19 +656,112 @@ ls <repo>/.claude/rules/common/coding-style.md \
595
656
  ```
596
657
 
597
658
 
598
- ## Peaks-Cli Swarm parallel phase
659
+ ## Peaks-Cli Swarm parallel phase (sub-agent fan-out, conditional)
660
+
661
+ The Swarm phase is **conditional**, not unconditional. It only runs when there is a real, user-confirmed requirement. Solo derives the fan-out set from the PRD type and the request content — never from a default of "always launch three".
662
+
663
+ ### Swarm gate (decide BEFORE fan-out)
664
+
665
+ Before launching any sub-agent, Solo must compute the **swarm plan** from three signals:
666
+
667
+ 1. **PRD state** — `prd/requests/<rid>.md` must be in state `confirmed-by-user` or `handed-off`. If not, STOP. The Swarm is downstream of PRD, not a substitute for it.
668
+ 2. **Request type** (`--type` from `peaks request init`):
669
+ - `feature` / `refactor` / `bugfix` → RD(planning) and QA(test-cases) are always in the swarm
670
+ - `config` / `docs` / `chore` → no swarm. RD/QA artefacts are not required by Gates B/C/D for these types. Skip the Swarm phase entirely and proceed to step 4 (RD implementation) with only the PRD in hand.
671
+ 3. **Frontend touch** — does the request affect user-visible behavior? This is decided by:
672
+ - Reading `.peaks/<session-id>/rd/project-scan.md` `## Project mode` for `frontendOnly` (project-shape signal)
673
+ - **AND** scanning the PRD body for frontend keywords: 页面 / 组件 / 表单 / 弹窗 / 表格 / 样式 / 布局 / 交互 / UI / UX / page / component / form / modal / table / styling / layout / interaction
674
+ - UI joins the swarm when (a) is `true` OR (b) matches. Both signals required `false` to skip UI.
675
+
676
+ Solo records the swarm plan in `.peaks/<session-id>/sc/swarm-plan.json` so SC and TXT can audit what was launched:
677
+
678
+ ```json
679
+ {
680
+ "rid": "<rid>",
681
+ "type": "feature",
682
+ "frontendOnly": true,
683
+ "frontendKeywordHit": true,
684
+ "subAgents": ["ui", "rd-planning", "qa-test-cases"]
685
+ }
686
+ ```
687
+
688
+ Sub-agent presence in this list = Solo launched a Task for it. Absence = the role was skipped with documented reason.
689
+
690
+ ### Mode-driven fan-out shape
691
+
692
+ | Mode | How the swarm plan is decided | What Solo does |
693
+ |---|---|---|
694
+ | `full-auto` | Compute plan from signals above, no question to user | Auto-launch all sub-agents in the plan in parallel |
695
+ | `swarm` | Same as `full-auto` | Same as `full-auto` (this profile name is historical — behavior is identical) |
696
+ | `assisted` | `AskUserQuestion` with three options: (a) Full — UI + RD(planning) + QA(test-cases); (b) Backend-only — RD(planning) + QA(test-cases); (c) Sequential — run RD first, then QA, skip UI | Use the user's choice as the plan |
697
+ | `strict` | Same as `assisted` (the question is informational; strict still enforces confirmation gates later) | Same as `assisted` |
698
+
699
+ In all modes, **the plan must be written to `sc/swarm-plan.json` before any Task call.** Solo updates `.peaks/.active-skill.json` to `gate=swarm-fan-out` at this point.
700
+
701
+ ### Sub-agent mechanism (Task tool, NOT Skill tool)
702
+
703
+ **Solo is itself a skill running in the current session. To invoke a role in the Swarm, Solo MUST use the `Task` tool with `subagent_type="general-purpose"` and a prompt that embeds the role's contract — NOT the `Skill` tool.** The `Skill` tool is single-stack and blocking; using it for "parallel" work was the v1.x illusion of concurrency. The Task tool is the only mechanism that gives real fan-out in Claude Code.
704
+
705
+ Each sub-agent Task call looks like:
706
+
707
+ ```
708
+ Task(
709
+ subagent_type="general-purpose",
710
+ description="<role> for rid=<rid>",
711
+ prompt="<paste peaks-<role>/SKILL.md body, minus the self-presence / Step 0 blocks, plus
712
+ the runtime arguments: project=<repo>, session-id=<sid>, request-id=<rid>, mode=<mode>>
713
+ plus the explicit output contract: 'Write your artefacts to the paths listed below and
714
+ return only the list of paths. Do not call Skill(...). Do not set presence. Do not
715
+ hand back prose.'"
716
+ )
717
+ ```
718
+
719
+ The role's required artefact paths (also see peaks-ui/rd/qa SKILL.md and `references/swarm-dispatch-contract.md`):
720
+
721
+ | Role | Writes | Reads (PRD-side) |
722
+ |---|---|---|
723
+ | `ui` | `.peaks/<sid>/ui/design-draft.md`, `.peaks/<sid>/ui/requests/<rid>.md` | PRD body, project-scan, archetype |
724
+ | `rd-planning` | `.peaks/<sid>/rd/tech-doc.md` (feature/refactor) or `.peaks/<sid>/rd/bug-analysis.md` (bugfix) | PRD body, project-scan, existing-system, codegraph |
725
+ | `qa-test-cases` | `.peaks/<sid>/qa/test-cases/<rid>.md` | PRD body, RD planning artefact, project-scan, codegraph |
726
+
727
+ **Solo launches all sub-agents in the swarm plan in a single message (multiple Task tool calls in parallel)** — this is what gives real concurrency. Do not sequentialize them. Solo then waits for all to return, runs `ls` checks against the paths above (Peaks-Cli Gate B), and only then advances to RD implementation.
728
+
729
+ **Hard prohibitions on sub-agents** (also passed in each Task prompt):
730
+
731
+ - Do NOT call `Skill(skill="...")` — sub-agents must not recursively activate skills, that defeats the fan-out.
732
+ - Do NOT call `peaks skill presence:set` — only the main Solo loop owns `.peaks/.active-skill.json`. Sub-agents write to a per-agent marker file `.peaks/<sid>/system/sub-agent-<role>.json` if they need to record state, but never the main presence file.
733
+ - Do NOT open interactive user prompts. If a sub-agent needs clarification, it must return a `blocked` verdict in its return string and let Solo handle the user message.
734
+ - Do NOT commit, push, install hooks, or apply settings.json mutations. Only Solo holds those permissions.
735
+
736
+ After every sub-agent Task returns, Solo **restores presence** once (not per-agent), then continues to Gate B verification:
737
+
738
+ ```bash
739
+ peaks skill presence:set peaks-solo --project <repo> --mode <mode> --gate swarm-converged
740
+ ```
741
+
742
+ ### Degradation when swarm roles fail or are absent
743
+
744
+ | Condition | Solo action | TXT handoff note |
745
+ |---|---|---|
746
+ | UI sub-agent returns blocked/error | RD continues with PRD visual descriptions | `ui-design-missing` |
747
+ | RD planning sub-agent returns blocked/error | RD continues with PRD-derived planning | `tech-doc-missing` |
748
+ | QA test-cases sub-agent returns blocked/error | RD continues; QA backfills test cases before verdict | `qa-test-cases-missing` |
749
+ | Two or more of the above | Fall back to sequential: `peaks request transition rd → spec-locked` then inline RD run, then QA | `swarm-degraded-to-sequential` |
750
+ | All three fail | Pause workflow; surface to user; request confirmation to continue | `swarm-aborted` |
751
+
752
+ Skipping the entire swarm (when `--type` is `config|docs|chore`) is not a degradation — record `swarm-skipped: type=<type>` and proceed.
599
753
 
600
- After PRD reaches `confirmed-by-user`, Solo launches peaks-ui, peaks-rd(planning), and peaks-qa(test-cases) simultaneously using parallel Agent calls. All three derive independently from the same PRD and write to separate artifact paths. Solo waits for all three, checks convergence (Peaks-Cli Gate B), then enters RD implementation.
754
+ ### Frontend-only trigger pre-flight
601
755
 
602
- ### Degradation when swarm roles fail
756
+ Before computing the swarm plan, Solo runs the keyword scan deterministically:
603
757
 
604
- 1. **UI missing**: RD continues with PRD visual descriptions; note "ui-design-missing" in TXT.
605
- 2. **RD planning missing**: RD continues; note "tech-doc-missing" in TXT.
606
- 3. **QA test-cases missing**: RD continues; QA must backfill test cases before issuing verdict.
607
- 4. **Two or more missing**: Fall back to sequential mode (PRDRD QA); note "swarm-degraded-to-sequential".
608
- 5. **All three missing**: Pause workflow; report to user; request confirmation to continue.
758
+ 1. Read `.peaks/<session-id>/prd/requests/<rid>.md` body.
759
+ 2. Lowercase + strip markdown; check regex `\b(页面|组件|表单|弹窗|表格|样式|布局|交互|UI|UX|page|component|form|modal|table|styling|layout|interaction|frontend|前端)\b`.
760
+ 3. If match count 1 `frontendKeywordHit=true`.
761
+ 4. If `frontendOnly` (from project-scan) is `true` and no keyword hitUI joins anyway (frontend-only project, even non-visual changes may need visual sanity for regressions).
762
+ 5. If `frontendOnly` is `false` and no keyword hit UI skipped.
609
763
 
610
- **UI phase mandatory for frontend**: When the request affects user-visible behavior (pages, components, forms, modals, tables, styling, interaction, or layout), Peaks-Cli Solo MUST invoke `peaks-ui` in the swarm parallel phase alongside RD planning and QA test-case generation. UI produces design drafts that RD implementation later consumes. Skipping UI for frontend work is a blocking violation. The only valid reason to skip UI is when the request is purely backend (API, database, CLI, config, or build tooling).
764
+ Solo records the pre-flight result in `sc/swarm-plan.json` so the audit trail shows why UI was or was not included.
611
765
 
612
766
  ## Peaks-Cli Mandatory RD QA repair loop (AUTO-PROCEED)
613
767
 
@@ -622,19 +776,24 @@ After PRD reaches `confirmed-by-user`, Solo launches peaks-ui, peaks-rd(planning
622
776
 
623
777
  After `peaks-rd` finishes any implementation, repair, or code-output slice, Peaks-Cli Solo MUST automatically route the result to `peaks-qa` without waiting for user confirmation. This is not optional in full-auto mode. Solo must not declare the workflow complete, emit a TXT handoff, or stop at RD completion.
624
778
 
625
- **How Solo invokes another role skill (mechanism, not metaphor):**
779
+ **How Solo invokes another role (mechanism, not metaphor):**
780
+
781
+ Solo is itself a skill running in the current session. There are **two distinct mechanisms** in this skill, and they MUST NOT be confused:
782
+
783
+ 1. **Swarm fan-out (planning side, after PRD confirmed)** — uses the `Task` tool with `subagent_type="general-purpose"` to launch real concurrent sub-agents. See "Peaks-Cli Swarm parallel phase" above for the full contract. Sub-agents do NOT call Skill(...) back into the role; they execute the role's instructions inline from the prompt.
784
+ 2. **Sequential handoff (execution side, RD↔QA repair loop)** — Solo is the only loop, and after RD or QA finishes (whether as a sub-agent or directly), Solo drives the next step from the orchestrator seat. Do NOT use the `Skill` tool to "reactivate" peaks-rd or peaks-qa in the main loop; doing so is the v1.x anti-pattern that masqueraded as "calling the role" but actually just re-prompted the same session. From v1.3 onward, the main loop drives roles via the CLI gate (`peaks request transition`) and reads back artefacts (`peaks request show ... --json`); the actual RD/QA work is either done inline by Solo (when Solo has just been re-invoked by the user) or by a Task sub-agent (in swarm mode).
626
785
 
627
- Solo is itself a skill running in the current session. To "invoke peaks-rd" or "peaks-qa", Solo MUST use the `Skill` tool with the role's name (e.g. `Skill(skill="peaks-rd")` or `Skill(skill="peaks-qa")`), passing the `<request-id>` and `<session-id>` as arguments so the role reads the same artifacts Solo wrote. Do NOT re-implement the role's logic inline in Solo. Do NOT use the `Agent` tool with a sub-agent — role skills are skills, not agents. After the role skill returns, Solo reads the artifacts the role wrote (via the request artifact path or `peaks request show <rid> --role <role>`) to decide the next step.
786
+ After RD completes (whether inline or sub-agent), Solo does not stop it must advance to QA. There is no "RD done, ask the user" state in full-auto mode. The only valid stops are: (a) QA verdict=pass, (b) repair cap hit, (c) explicit user cancel.
628
787
 
629
- **Presence restoration after role skill returns (MANDATORY):** Role skills (peaks-rd, peaks-qa, peaks-ui) call `peaks skill presence:set <role>` internally, which overwrites `.peaks/.active-skill.json`. After EVERY role skill returns whether success, repair-needed, or failure Solo MUST immediately restore the orchestrator presence by re-running the same presence command from Step 2:
788
+ **Presence restoration after RD/QA work returns (MANDATORY):** In v1.x, role skills called `peaks skill presence:set <role>` internally and stomped on `.peaks/.active-skill.json`. From v1.3 onward, sub-agents in the Swarm path are forbidden from calling `peaks skill presence:set` (see "Sub-agent dispatch" in each role's SKILL.md), so the main loop's presence file is preserved across the fan-out window by construction. The one place Solo still has to actively restore presence is **once after the fan-out returns** (gate=swarm-converged) and again **after each RD↔QA repair iteration** (gate=repair-cycle-<N>). Use the same command from Step 2 with the current mode and the gate that has just advanced:
630
789
 
631
790
  ```bash
632
791
  peaks skill presence:set peaks-solo --project <repo> --mode <mode> --gate <current-gate>
633
792
  ```
634
793
 
635
- This keeps the CLAUDE.md status header accurate (`Peaks-Cli Skill: peaks-solo`) instead of showing a stale role name. Use the current mode and gate values; the gate may have advanced since startup. Skipping this step causes the header to display the last role skill name permanently.
794
+ This keeps the CLAUDE.md status header accurate (`Peaks-Cli Skill: peaks-solo`) instead of showing a stale role name. Use the current mode and gate values; the gate may have advanced since startup. Skipping this step causes the header to display the last-known gate permanently.
636
795
 
637
- **Full-auto auto-proceed rule**: In the `full-auto` profile, when RD transitions to `qa-handoff`, Solo immediately invokes `peaks-qa` via the Skill tool with the same `<request-id>`. Do not pause, do not ask the user, do not summarize RD results as if they were final. The only valid reason to skip QA is when `--type` is `docs` or `chore` (no acceptance surface).
796
+ **Full-auto auto-proceed rule**: In the `full-auto` profile, when RD transitions to `qa-handoff`, Solo immediately drives QA — by launching a `Task(subagent_type="general-purpose", ...)` sub-agent carrying the `peaks-qa` body (swarm path), or by running QA inline in the main loop (assisted/strict path). Do not pause, do not ask the user, do not summarize RD results as if they were final. The only valid reason to skip QA is when `--type` is `docs` or `chore` (no acceptance surface).
638
797
 
639
798
  A QA report with any failing, blocked, missing, or unverified acceptance item is not a pass.
640
799
 
@@ -650,9 +809,12 @@ When `peaks-qa` returns `verdict=return-to-rd`, Solo does NOT manually rewrite R
650
809
  --project <repo> --json
651
810
  ```
652
811
  `spec-locked` is the canonical "needs more RD work" state. The reason is mandatory in repair cycles so the artifact history shows the loop.
653
- 3. Invoke `peaks-rd` via the Skill tool. Pass the request id and the path to the QA findings; peaks-rd reads `qa/test-reports/<rid>.md` and the QA `requests/<rid>.md` for the verdict.
812
+ 3. Re-launch `peaks-rd` work. Two paths, mode-driven:
813
+ - **Swarm / full-auto**: launch a fresh `Task(subagent_type="general-purpose", ...)` sub-agent with the same `peaks-rd` body used in the Swarm phase, plus the QA findings path so it can read the failure list. Solo restores presence after the sub-agent returns.
814
+ - **Assisted / strict / inline-fallback**: Solo executes the RD repair steps directly in the main loop, since there is no concurrent fan-out to coordinate.
815
+ In both paths, pass the QA findings path so the repair sees what failed.
654
816
  4. peaks-rd fixes the reported issues only (red-line scope: do not modify unrelated surfaces), regenerates code-review and security-review evidence if changes touched reviewed surfaces, then transitions `rd → implemented → qa-handoff` again.
655
- 5. Solo invokes `peaks-qa` again with the same `<request-id>` (the same Skill call as before). QA re-runs gates against the new diff.
817
+ 5. Solo re-runs QA (sub-agent Task in swarm/full-auto, inline in assisted/strict) with the same `<request-id>`. QA re-runs gates against the new diff.
656
818
  6. Repeat steps 1-5 until QA returns `verdict=pass`, or the cap below fires.
657
819
  **After each repair iteration** (after peaks-rd and peaks-qa both return), Solo MUST restore presence:
658
820
  ```bash
@@ -707,14 +869,40 @@ peaks request lint <rid> --role prd --project <repo> --json
707
869
  peaks request transition <rid> --role prd --state confirmed-by-user --project <repo> --json
708
870
  peaks request transition <rid> --role prd --state handed-off --project <repo> --json
709
871
 
710
- # 3. Peaks-Cli Swarm parallel — launch UI + RD(planning) + QA(test-cases) simultaneously
711
- # Pass the same --type chosen for PRD so RD/QA gate matrix lines up.
712
- peaks request init --role ui --id <rid> --project <repo> --apply --type <type> --json
713
- peaks request transition <rid> --role ui --state direction-locked --project <repo> --json
714
- peaks request transition <rid> --role ui --state handed-off --project <repo> --json
715
- peaks request init --role rd --id <rid> --project <repo> --apply --type <type> --json
716
- peaks request transition <rid> --role rd --state spec-locked --project <repo> --json
717
- peaks request init --role qa --id <rid> --project <repo> --apply --type <type> --json
872
+ # 3. Peaks-Cli Swarm parallel — sub-agent fan-out (Task tool, NOT Skill tool)
873
+ # Solo computes the swarm plan from --type + frontendOnly + frontend-keyword scan,
874
+ # writes it to .peaks/<sid>/sc/swarm-plan.json, then launches one
875
+ # Task(subagent_type="general-purpose", ...) call per sub-agent in the same message.
876
+ # See "Peaks-Cli Swarm parallel phase" above for the full decision table and the
877
+ # prompt template; the role's required artefact paths are listed there.
878
+ # Hard rule: do NOT call Skill(skill="peaks-rd" | "peaks-qa" | "peaks-ui") from
879
+ # the Swarm phase that's the v1.x anti-pattern.
880
+ #
881
+ # 3a. Pre-fan-out: Solo initialises every role's request artefact slot in the main
882
+ # loop so sub-agents find a stable rid <-> artefact binding. Each role's
883
+ # sub-agent may also call peaks request init itself (idempotent on the same rid);
884
+ # Solo's call here is the source of truth. Only init roles that are in the
885
+ # swarm plan — roles not in the plan do not get a slot yet.
886
+ peaks skill presence:set peaks-solo --project <repo> --mode <mode> --gate swarm-fan-out
887
+ # for each role in swarm-plan.subAgents:
888
+ # peaks request init --role ui --id <rid> --project <repo> --apply --type <type> --json
889
+ # peaks request init --role rd --id <rid> --project <repo> --apply --type <type> --json
890
+ # peaks request init --role qa --id <rid> --project <repo> --apply --type <type> --json
891
+ # e.g. if plan = [ui, rd, qa]: run init for ui, rd, qa.
892
+ # If plan = [rd, qa]: run for rd, qa only.
893
+ # If plan = [] (config|docs|chore skip): no inits here, jump to step 4 directly.
894
+ # 3b. Solo issues N Task(subagent_type="general-purpose", ...) calls in ONE message
895
+ # (N = len(swarm-plan.subAgents)). Each prompt embeds the role's body minus
896
+ # Step 0 / presence, plus the runtime args (rid / sid / mode / type / paths).
897
+ # 3c. After fan-out, Solo restores presence once and runs Gate B (ls checks):
898
+ peaks skill presence:set peaks-solo --project <repo> --mode <mode> --gate swarm-converged
899
+ ls .peaks/<sid>/prd/requests/<rid>.md # PRD artefact must exist (Gate B hard)
900
+ # feature / refactor → ls .peaks/<sid>/rd/tech-doc.md
901
+ # bugfix → ls .peaks/<sid>/rd/bug-analysis.md
902
+ ls .peaks/<sid>/qa/test-cases/<rid>.md # QA test-cases (skipped for docs|chore)
903
+ # ui (only when in plan):
904
+ ls .peaks/<sid>/ui/design-draft.md 2>&1 # non-blocking (Gate B info)
905
+ # Apply the degradation rules in the main SKILL.md if any artefact is missing.
718
906
  # → Peaks-Cli Gate B convergence check. Assisted/Strict: [CONFIRM]
719
907
 
720
908
  # 4. Peaks-Cli RD planning artifact (the file required by the prerequisite gate)
@@ -762,10 +950,30 @@ peaks openspec validate <cid> --project <repo> --json
762
950
  peaks openspec archive <cid> --project <repo> --apply --json
763
951
 
764
952
  # 10. Peaks-Cli TXT handoff — invoke peaks-txt which embeds memory markers and extracts
765
- # peaks-txt writes the handoff capsule to .peaks/<id>/txt/handoff.md with embedded
766
- # <!-- peaks-memory:start --> blocks, then runs memory extract on it.
767
- # --apply is REQUIRED to write .peaks/memory; without it the command only previews.
953
+ # peaks-txt writes the handoff capsule to .peaks/<id>/txt/handoff.md. Inside the
954
+ # capsule body, peaks-txt embeds <!-- peaks-memory:start --> blocks for every
955
+ # stable project fact surfaced this session.
956
+ #
957
+ # 10a. Skill-side scan (do this BEFORE the AskUserQuestion below):
958
+ # grep -n "peaks-memory:start" .peaks/<id>/txt/handoff.md
959
+ # Record the count. This is the skill doing the work, not a CLI command —
960
+ # we deliberately do not ship a `peaks memory scan` because the LLM is
961
+ # the only consumer and the LLM has grep.
962
+
963
+ # 10b. AskUserQuestion (only if 10a returned count >= 1):
964
+ # "The TXT handoff has N peaks-memory:start blocks. Persist to .peaks/memory/?
965
+ # (a) Apply all — `peaks memory extract --project <repo>
966
+ # --artifact .peaks/<id>/txt/handoff.md --apply --json`
967
+ # (b) Apply selectively — re-edit handoff.md first, then re-apply
968
+ # (c) Skip for now — blocks stay in the handoff only, no .peaks/memory/ write"
969
+ # If 10a returned 0 AND the session surfaced a stable project fact
970
+ # (decision / convention / approved refactor), STOP — peaks-txt must go
971
+ # back and embed at least one block before Solo can advance.
972
+
973
+ # 10c. After the user picks (a) or (b), run:
768
974
  peaks memory extract --project <repo> --artifact .peaks/<id>/txt/handoff.md --apply --json
975
+ # --apply is REQUIRED to write .peaks/memory/; without it the command only
976
+ # previews. The extract regenerates index.json in the same call.
769
977
 
770
978
  # 11. Peaks-Cli Final snapshot
771
979
  peaks project dashboard --project <repo> --json
@@ -836,6 +1044,11 @@ Use Peaks-Cli TXT for the compact handoff capsule: mode, validated decisions, ar
836
1044
 
837
1045
  Do NOT call `peaks skill presence:clear --project <repo>` at workflow end. The presence file and header remain active so the user stays inside the workflow context. The user can continue with follow-up requirements naturally — no need to re-invoke `/peaks-solo`. The header continues to display the active skill and current gate.
838
1046
 
1047
+ Before ending, extract durable memories from this session:
1048
+ ```bash
1049
+ peaks project memories:extract --session-id <session-id> --project <repo> --json
1050
+ ```
1051
+
839
1052
  ## Peaks-Cli External references and lifecycle
840
1053
 
841
1054
  **Codegraph**: Optional project-analysis before RD handoff. Use `peaks codegraph affected --project <path> <changed-files...> --json` for regression-surface hints. Output as untrusted supporting evidence only; never commit `.codegraph/` artifacts.
@@ -0,0 +1,186 @@
1
+ # Swarm dispatch contract
2
+
3
+ > Reference for `peaks-solo` Swarm phase and the role sub-agents (`peaks-ui` / `peaks-rd` planning / `peaks-qa` test-cases). Defines the **mechanism** of fan-out: how Solo launches sub-agents, what the sub-agent prompt must contain, what the sub-agent must return, and how Solo reduces the result.
4
+
5
+ ## 1. Why this exists
6
+
7
+ The previous "Swarm" used `Skill(skill="peaks-rd")` calls. That is **single-stack and blocking** — there is no concurrency. Three sequential `Skill` calls run in order on the same main loop, not in parallel. The "parallel Agent calls" wording in the old SKILL.md was a v1.x illusion.
8
+
9
+ This contract moves the Swarm to the `Task` tool with `subagent_type="general-purpose"`, which is the only Claude Code mechanism that gives real concurrent fan-out. Solo launches all sub-agents in a single message; the platform schedules them concurrently.
10
+
11
+ ## 2. When to swarm (decision)
12
+
13
+ Solo runs the swarm only after `peaks request show <rid> --role prd` is in state `confirmed-by-user` or `handed-off`. Before that, there is nothing for the sub-agents to derive from.
14
+
15
+ Solo computes the **swarm plan** from three signals (see "Swarm gate" in the main SKILL.md):
16
+
17
+ 1. `--type` from `peaks request init` (already on the PRD artefact).
18
+ 2. `frontendOnly` from `.peaks/<session-id>/rd/project-scan.md` `## Project mode`.
19
+ 3. Frontend keyword scan over the PRD body.
20
+
21
+ The plan is written to `.peaks/<session-id>/sc/swarm-plan.json` before any Task call, so SC and TXT can audit what was launched. Solo updates `.peaks/.active-skill.json` to `gate=swarm-fan-out` at this point.
22
+
23
+ | `--type` | Frontend signal | Plan |
24
+ |---|---|---|
25
+ | `feature` / `refactor` / `bugfix` | keyword hit OR `frontendOnly=true` | `[ui, rd-planning, qa-test-cases]` |
26
+ | `feature` / `refactor` / `bugfix` | no keyword hit AND `frontendOnly=false` | `[rd-planning, qa-test-cases]` |
27
+ | `config` / `docs` / `chore` | (any) | `[]` (swarm skipped, recorded in plan) |
28
+
29
+ In `assisted` and `strict` modes, Solo replaces signal (3) with a three-option `AskUserQuestion` (see "Mode-driven fan-out shape" in the main SKILL.md) and uses the user's choice as the plan.
30
+
31
+ ## 3. Sub-agent invocation template
32
+
33
+ Solo emits one Task call per sub-agent in the plan, all in the same message:
34
+
35
+ ```js
36
+ Task({
37
+ subagent_type: "general-purpose",
38
+ description: "<role> for rid=<rid>",
39
+ prompt: <see role-specific sections below>
40
+ })
41
+ ```
42
+
43
+ ### 3.1 Common prompt header (all sub-agents)
44
+
45
+ ```
46
+ You are a sub-agent invoked by peaks-solo. You are NOT the main Claude session.
47
+ Your job: execute ONE role from the peaks skill family and write its artefact(s).
48
+ Return a compact JSON envelope — do not write prose.
49
+
50
+ ## Hard prohibitions
51
+ - Do NOT call Skill(skill="..."). You are the role.
52
+ - Do NOT call `peaks skill presence:set` — the main loop owns .peaks/.active-skill.json.
53
+ If you need to record state, write to .peaks/<session-id>/system/sub-agent-<role>.json.
54
+ - Do NOT commit, push, install hooks, or apply settings.json mutations.
55
+ - Do NOT ask the user interactive questions. If you need clarification, return
56
+ {"status":"blocked","blockedReason":"<text>"} and let the main loop handle it.
57
+
58
+ ## Runtime arguments (provided by Solo)
59
+ - project: <repo> (git repo root, NOT a sub-package)
60
+ - session-id: <sid> (from .peaks/.active-skill.json or .peaks/.session.json)
61
+ - request-id: <rid> (PRD id)
62
+ - type: <type> (feature | bugfix | refactor | config | docs | chore)
63
+ - mode: <mode> (full-auto | swarm | assisted | strict)
64
+ - project-scan-path: <path> (read this for component library / CSS / build tool)
65
+ - existing-system-path: <path> (legacy projects only)
66
+ - frontendOnly: <bool> (from project-scan)
67
+ - frontendKeywordHit: <bool> (Solo's PRD scan result)
68
+ ```
69
+
70
+ ### 3.2 peaks-ui sub-agent prompt
71
+
72
+ Append after the common header:
73
+
74
+ ```
75
+ ## Role: UI design direction
76
+ You are running peaks-ui. Produce design direction, not code. RD will implement.
77
+
78
+ Steps:
79
+ 1. peaks request init --role ui --id <rid> --project <repo> --apply --type <type> --json
80
+ 2. peaks request show <rid> --role prd --project <repo> --json
81
+ 3. Read <project-scan-path> for component library / CSS framework.
82
+ 4. Run the prototype fidelity gate: Figma file? PRD visuals? Headed browser?
83
+ 5. Write TWO artefacts:
84
+ - .peaks/<sid>/ui/design-draft.md
85
+ - .peaks/<sid>/ui/requests/<rid>.md
86
+ 6. Return:
87
+ {
88
+ "role": "ui",
89
+ "rid": "<rid>",
90
+ "status": "ok" | "blocked" | "skipped",
91
+ "artefacts": [".peaks/<sid>/ui/design-draft.md", ".peaks/<sid>/ui/requests/<rid>.md"],
92
+ "warnings": [],
93
+ "blockedReason": null
94
+ }
95
+
96
+ If you determine the request is non-visual (no UI surface, no design impact),
97
+ return {"status":"skipped","reason":"non-frontend-request"} so Solo can record
98
+ the misfire in sc/swarm-plan.json.
99
+ ```
100
+
101
+ ### 3.3 peaks-rd (planning) sub-agent prompt
102
+
103
+ ```
104
+ ## Role: RD planning
105
+ You are running peaks-rd's planning step. Produce the planning artefact, not code.
106
+ Code is written in a later sub-agent or inline run.
107
+
108
+ Steps:
109
+ 1. peaks request init --role rd --id <rid> --project <repo> --apply --type <type> --json
110
+ 2. peaks request show <rid> --role prd --project <repo> --json
111
+ 3. peaks request show <rid> --role ui --project <repo> --json (if ui in plan)
112
+ 4. Read <project-scan-path>. If absent and Solo did not pre-create it, create it
113
+ by running `peaks scan archetype --project <repo> --json` and copying the JSON
114
+ into rd/project-scan.md.
115
+ 5. Read <existing-system-path> if archetype is legacy-*.
116
+ 6. Write the type-appropriate planning artefact:
117
+ - feature | refactor → .peaks/<sid>/rd/tech-doc.md
118
+ - bugfix → .peaks/<sid>/rd/bug-analysis.md
119
+ - config | docs | chore → no planning artefact required. Return skipped.
120
+ 7. Return:
121
+ {
122
+ "role": "rd-planning",
123
+ "rid": "<rid>",
124
+ "status": "ok" | "blocked" | "skipped",
125
+ "artefacts": [".peaks/<sid>/rd/tech-doc.md"], // or [] when skipped
126
+ "warnings": [],
127
+ "blockedReason": null
128
+ }
129
+ ```
130
+
131
+ ### 3.4 peaks-qa (test-cases) sub-agent prompt
132
+
133
+ ```
134
+ ## Role: QA test-case generation (planning, not execution)
135
+ You are running peaks-qa's planning step. Produce test cases, do NOT execute them.
136
+ The execution step runs after RD implementation in a separate sub-agent.
137
+
138
+ Steps:
139
+ 1. peaks request init --role qa --id <rid> --project <repo> --apply --type <type> --json
140
+ 2. peaks request show <rid> --role prd --project <repo> --json
141
+ 3. peaks request show <rid> --role rd --project <repo> --json
142
+ 4. Read <project-scan-path>.
143
+ 5. Write .peaks/<sid>/qa/test-cases/<rid>.md with test cases linked to PRD
144
+ acceptance items (use **Acceptance:** A1, A2 style).
145
+ 6. Return:
146
+ {
147
+ "role": "qa-test-cases",
148
+ "rid": "<rid>",
149
+ "status": "ok" | "blocked" | "skipped",
150
+ "artefacts": [".peaks/<sid>/qa/test-cases/<rid>.md"],
151
+ "warnings": [],
152
+ "blockedReason": null
153
+ }
154
+
155
+ If --type is docs|chore, return {"status":"skipped","reason":"type=<type>"} —
156
+ no acceptance surface to plan tests for.
157
+ ```
158
+
159
+ ## 4. Reducer (Solo side)
160
+
161
+ After all sub-agent Tasks return, Solo:
162
+
163
+ 1. Restores presence ONCE (not per-agent):
164
+ ```
165
+ peaks skill presence:set peaks-solo --project <repo> --mode <mode> --gate swarm-converged
166
+ ```
167
+ 2. Runs `ls` checks against `sc/swarm-plan.json` — every promised artefact must exist (Gate B hard). If any are missing, apply the degradation rules in "Degradation when swarm roles fail or are absent" (main SKILL.md).
168
+ 3. Updates `sc/swarm-plan.json` with the final status of each sub-agent (ok / blocked / skipped / error).
169
+ 4. Advances to step 4 (RD implementation) with all the artefacts in hand.
170
+
171
+ ## 5. Presence restoration (single-shot)
172
+
173
+ Sub-agents are explicitly forbidden from calling `peaks skill presence:set`. That means `.peaks/.active-skill.json` does not move during the fan-out. Solo sets it to `gate=swarm-fan-out` before fan-out and to `gate=swarm-converged` once after all Tasks return. The status header (the `Peaks-Cli Skill: peaks-solo | Peaks-Cli Gate: <gate>` line) therefore reads consistently across the fan-out window.
174
+
175
+ If a sub-agent is misbehaving and writes to `.peaks/.active-skill.json` anyway, the next Solo presence-set (after fan-out) overwrites it — the bug self-heals on the next gate advance, but the swarm-phase display may be off. The hard prohibition is there to prevent this; Solo should still treat the file as a single-writer resource.
176
+
177
+ ## 6. Why not a `peaks-swarm` skill?
178
+
179
+ A skill cannot itself trigger sub-agents — the Skill tool runs in the main loop. The orchestrator (peaks-solo) has to be in the main loop and has to use the Task tool directly. Putting swarm logic into a separate skill would either re-introduce the "single-stack blocking" anti-pattern or require a custom slash command that bypasses the Skill tool. The current design keeps swarm control in peaks-solo where it belongs.
180
+
181
+ ## 7. Tests / dogfood
182
+
183
+ - `peaks scan archetype --project <repo> --json` must keep emitting `frontendOnly` and `frontendOnlyReason`. Both fields are read here and by the existing pre-RD scan contract.
184
+ - `peaks request show <rid> --role prd --json` must surface the `--type` chosen at `peaks request init`. Sub-agents pass it through unchanged.
185
+ - `peaks skill presence:set` must remain single-writer-friendly (the sub-agents do not call it).
186
+ - Smoke test: a full-auto peaks-solo run on a `legacy-frontend` project with a UI-affecting PRD should produce `sc/swarm-plan.json` containing all three sub-agents and three corresponding artefacts in `ui/`, `rd/`, `qa/`.
@@ -97,6 +97,22 @@ The three gate primitives are domain-neutral, so the same engine governs very di
97
97
 
98
98
  The one boundary to explain: a gate must reduce to **a file existing, text matching in a file, or a command's exit code**. A purely human-judgment gate ("did the editor approve?") is expressed by reifying it into a signal — e.g. require an `approved.md` file, or that a status file contains "approved". The `command` gate is the universal adapter for anything scriptable.
99
99
 
100
+ ### Literal-word trap and `stripMeta` (added by PRD 006 on 2026-06-02)
101
+
102
+ A naive `grep` gate for a content-publishing SOP has a self-referential failure mode: the author writing *about* the gate's pattern ("we use a `grep absent: "TODO"` gate to block leftover T-O-D-O") ends up triggering the gate they just described. This is rare in code-review SOPs and very common in content/publishing SOPs.
103
+
104
+ **Opt-in workaround**: add `stripMeta: true` to the gate's check. The evaluator strips HTML comments (`<!-- … -->`), fenced code blocks (` ``` … ``` `), and C-style block comments (`/* … */`) from the file content *before* applying the regex. The author's discussion of the gate in those areas is no longer counted.
105
+
106
+ ```json
107
+ {
108
+ "id": "no-todo",
109
+ "phase": "publish",
110
+ "check": { "type": "grep", "file": "post.md", "pattern": "TODO", "absent": true, "stripMeta": true }
111
+ }
112
+ ```
113
+
114
+ Default `false` preserves byte-identical behavior for existing SOPs. The stripper is conservative on malformed input (unclosed fences / block comments fall through un-stripped). Not covered by this slice: inline code (`` `TODO` ``) and blockquotes (`> TODO`) — both are content the author explicitly chose to render, so they remain subject to the regex. If a future dogfood surfaces a need to strip those too, open a follow-up PRD.
115
+
100
116
  ## Un-bypassable enforcement (optional, opt-in)
101
117
 
102
118
  By default a SOP gate only blocks the `peaks sop advance` command — nothing forces the agent through it. To make a gate **physically un-bypassable**, a SOP can declare **guards** that bind a concrete irreversible Bash action to a phase, and the user installs a PreToolUse hook:
@@ -162,6 +178,7 @@ peaks hooks status --project <repo>
162
178
  peaks gate bypass --sop <sop-id> --phase <phase> --reason "<why>" --project <repo>
163
179
 
164
180
  # 9. hand the SOP to the user; clear presence when done
181
+ peaks project memories:extract --session-id <session-id> --project <repo> --json # extract durable memories
165
182
  peaks skill presence:clear --project <repo>
166
183
  ```
167
184