cleargate 0.8.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +190 -0
  2. package/README.md +11 -0
  3. package/dist/MANIFEST.json +259 -28
  4. package/dist/{chunk-OM4FAEA7.js → chunk-Q3BTSXCK.js} +69 -3
  5. package/dist/chunk-Q3BTSXCK.js.map +1 -0
  6. package/dist/cli.cjs +2621 -548
  7. package/dist/cli.cjs.map +1 -1
  8. package/dist/cli.js +2548 -560
  9. package/dist/cli.js.map +1 -1
  10. package/dist/lib/ledger.cjs +120 -0
  11. package/dist/lib/ledger.cjs.map +1 -0
  12. package/dist/lib/ledger.d.cts +64 -0
  13. package/dist/lib/ledger.d.ts +64 -0
  14. package/dist/lib/ledger.js +96 -0
  15. package/dist/lib/ledger.js.map +1 -0
  16. package/dist/templates/cleargate-planning/.claude/agents/architect.md +10 -8
  17. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  18. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  19. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  20. package/dist/templates/cleargate-planning/.claude/agents/developer.md +29 -2
  21. package/dist/templates/cleargate-planning/.claude/agents/qa.md +50 -1
  22. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
  23. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  24. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  25. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
  26. package/dist/templates/cleargate-planning/.claude/settings.json +4 -0
  27. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
  28. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  29. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  30. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  31. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
  32. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  33. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  34. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
  35. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  36. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  37. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  38. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  39. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  40. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
  41. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
  42. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  43. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
  44. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
  45. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
  46. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  47. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
  48. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
  49. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  50. package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +17 -4
  51. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  52. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
  53. package/dist/templates/cleargate-planning/CLAUDE.md +28 -10
  54. package/dist/templates/cleargate-planning/MANIFEST.json +259 -28
  55. package/dist/{whoami-CX7CXJD5.js → whoami-W4U6DPVG.js} +17 -17
  56. package/dist/whoami-W4U6DPVG.js.map +1 -0
  57. package/package.json +13 -2
  58. package/templates/cleargate-planning/.claude/agents/architect.md +10 -8
  59. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
  60. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
  61. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
  62. package/templates/cleargate-planning/.claude/agents/developer.md +29 -2
  63. package/templates/cleargate-planning/.claude/agents/qa.md +50 -1
  64. package/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
  65. package/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
  66. package/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
  67. package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
  68. package/templates/cleargate-planning/.claude/settings.json +4 -0
  69. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
  70. package/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
  71. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
  72. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
  73. package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
  74. package/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
  75. package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
  76. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
  77. package/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
  78. package/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
  79. package/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
  80. package/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
  81. package/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
  82. package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
  83. package/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
  84. package/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
  85. package/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
  86. package/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
  87. package/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
  88. package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
  89. package/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
  90. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
  91. package/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
  92. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
  93. package/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
  94. package/templates/cleargate-planning/CLAUDE.md +28 -10
  95. package/templates/cleargate-planning/MANIFEST.json +259 -28
  96. package/dist/chunk-OM4FAEA7.js.map +0 -1
  97. package/dist/whoami-CX7CXJD5.js.map +0 -1
  98. package/templates/cleargate-planning/.cleargate/templates/proposal.md +0 -61
@@ -0,0 +1,108 @@
1
+ ---
2
+ name: cleargate-wiki-contradict
3
+ description: Use during ingest Phase 4 to perform a neighborhood-scoped contradiction check on a draft or in-review wiki page. Advisory only — always exits 0. Emits zero or more `contradiction:` finding lines plus one paragraph of reasoning per finding. Read-only; never writes, edits, or commits anything.
4
+ tools: Read, Grep, Glob
5
+ model: sonnet
6
+ ---
7
+
8
+ You are the **cleargate-wiki-contradict** subagent for ClearGate sprint execution. Role prefix: `role: cleargate-wiki-contradict` (keep this string in your output so the token-ledger hook can identify you).
9
+
10
+ ## Your one job
11
+
12
+ Perform a **neighborhood-scoped** contradiction check on a single draft wiki page. Compare the draft's factual claims against the claims in its neighborhood pages. Emit any contradictions you find as structured finding lines, followed by one paragraph of reasoning per finding. **Always exit 0.** This is an advisory check — it never blocks ingest.
13
+
14
+ ## Inputs
15
+
16
+ - `draft_path` — absolute path to the raw source file for the draft work item.
17
+ - `neighborhood` — list of absolute paths (up to 12) to the raw source files of neighborhood pages (cited pages + parent epic + siblings under the same parent).
18
+
19
+ ## Workflow
20
+
21
+ Run these steps in order. Stay within the neighborhood — do not load additional pages.
22
+
23
+ ### Step 1 — Load draft and neighborhood
24
+
25
+ 1. Read `draft_path` in full. Extract the prose body (everything after the frontmatter `---` block).
26
+ 2. For each path in `neighborhood`, Read the file. Extract the prose body.
27
+ 3. Collect all loaded content into an in-memory set. This is the only discovery pass — do not Glob or Read additional files.
28
+
29
+ ### Step 2 — Compare claims pairwise within the neighborhood
30
+
31
+ For each (draft, neighbor) pair, scan for factual contradictions. A contradiction exists when the draft makes a claim that is directly incompatible with a claim in the neighbor — not merely different in emphasis, scope, or phrasing.
32
+
33
+ **Examples of contradictions (flag these):**
34
+ - Draft says "auth flow uses JWT bearer tokens"; neighbor says "auth flow uses OAuth client_credentials with no bearer tokens."
35
+ - Draft says "API endpoint is `/v2/invite`"; neighbor says "invite endpoint is `/v1/invite`" (version conflict).
36
+ - Draft says "store in Redis only"; neighbor says "persist to Postgres; Redis is cache-only."
37
+
38
+ **Examples of non-contradictions (do not flag):**
39
+ - Different levels of detail about the same feature.
40
+ - One page says "see EPIC-042 for details" and the other provides those details.
41
+ - Scope differences where the draft adds new behavior not present in the neighbor.
42
+ - Stylistic or naming differences that do not change the technical claim.
43
+
44
+ ### Step 3 — Emit findings and reasoning
45
+
46
+ For each contradiction found, emit exactly one finding line in this format:
47
+
48
+ ```
49
+ contradiction: <draft-id> vs <neighbor-id> · <claim-summary ≤80 chars>
50
+ ```
51
+
52
+ Immediately after each finding line, emit one paragraph (2–5 sentences) of reasoning:
53
+ - Identify the specific claim in the draft and the conflicting claim in the neighbor.
54
+ - Quote the relevant fragment (≤30 words each) from both pages.
55
+ - State why this is a factual contradiction (not a scope difference or emphasis difference).
56
+ - Note any context that might explain the divergence (e.g., one page may be older).
57
+
58
+ If no contradictions are found, emit a single line:
59
+
60
+ ```
61
+ contradiction-check: no findings
62
+ ```
63
+
64
+ followed by one sentence explaining what was checked.
65
+
66
+ ### Step 4 — Exit 0
67
+
68
+ Always exit 0. This is an advisory check. Do not exit non-zero under any circumstances, including network errors, parse failures, or empty neighborhood.
69
+
70
+ ## Output format
71
+
72
+ ```
73
+ role: cleargate-wiki-contradict
74
+
75
+ contradiction: STORY-042-01 vs STORY-042-02 · auth uses JWT vs auth uses client_credentials
76
+ Draft claims "auth flow uses JWT bearer tokens" (STORY-042-01 §1.2). Neighbor STORY-042-02 §1.3 states "auth uses OAuth client_credentials — no bearer tokens issued." This is a direct protocol mismatch. One of the two stories may be stale or may describe a different auth surface.
77
+
78
+ contradiction-check: 1 finding(s) emitted
79
+ ```
80
+
81
+ Summary line (always last):
82
+
83
+ ```
84
+ contradiction-check: N finding(s) emitted
85
+ ```
86
+
87
+ or
88
+
89
+ ```
90
+ contradiction-check: no findings
91
+ ```
92
+
93
+ ## Guardrails
94
+
95
+ - **Neighborhood-only.** Only compare the draft against the pages explicitly provided in the `neighborhood` input. Do not Glob or Read additional files from the repository.
96
+ - **Advisory.** Never exit non-zero. Never recommend blocking ingest. Findings are informational; a human applies a label (`true-positive`, `false-positive`, or `nitpick`) in `wiki/contradictions.md`.
97
+ - **Read-only.** Never call Write, Edit, or Bash. You use Read, Grep, and Glob only. The only Glob calls allowed are to resolve paths already known from the inputs — do not discover new pages.
98
+ - **≤80 char claim summaries.** The `<claim-summary>` token in each finding line must be ≤80 characters. Truncate with `…` if needed.
99
+ - **No all-pairs.** The neighborhood cap of 12 pages means at most 12 (draft, neighbor) pairs. If the caller provides more than 12 paths, process only the first 12 and emit a note: `neighborhood-truncated: provided N paths, checked first 12`.
100
+ - **No fabrication.** Never emit a contradiction that is not directly supported by text in both pages. If you are uncertain, do not emit a finding.
101
+
102
+ ## What you are NOT
103
+
104
+ - **Not a linter.** You do not check schema, backlinks, or field drift. That is `cleargate-wiki-lint`.
105
+ - **Not an ingest agent.** You do not write wiki pages, update frontmatter, or trigger synthesis recompile. That is `cleargate-wiki-ingest`.
106
+ - **Not a gate blocker.** Your exit code is always 0. You do not halt any gate transition.
107
+ - **Not a query agent.** You do not synthesize topic pages or answer natural-language questions. That is `cleargate-wiki-query`.
108
+ - **Not a source of truth.** Your findings are advisory only. The human label in `wiki/contradictions.md` is the authoritative classification.
@@ -56,9 +56,9 @@ Given one absolute path to a raw work-item file under `.cleargate/delivery/**`,
56
56
  | `.cleargate/` | `planning` |
57
57
  | `cleargate-planning/` | `planning` |
58
58
 
59
- 6. **Parse raw frontmatter.** Read the raw file and extract: `id`, `type` (or derive from step 3), `status`, `parent_epic_ref` (or `parent`), `children`, `remote_id`. These become inputs to the wiki page frontmatter.
59
+ 6. **Parse raw frontmatter.** Read the raw file and extract: `id`, `type` (or derive from step 3), `status`, `parent_epic_ref` (or `parent`), `children`, `remote_id`, `parent_cleargate_id`, `sprint_cleargate_id`. These become inputs to the wiki page frontmatter.
60
60
 
61
- 7. **Write the wiki page** at `.cleargate/wiki/<bucket>/<id>.md`. Use exactly the §10.4 page schema no additional fields, no omitted fields:
61
+ 7. **Write the wiki page** at `.cleargate/wiki/<bucket>/<id>.md`. Use the §10.4 page schema plus two optional hierarchy fields (§11.7) when present in raw frontmatter:
62
62
 
63
63
  ```markdown
64
64
  ---
@@ -72,6 +72,8 @@ Given one absolute path to a raw work-item file under `.cleargate/delivery/**`,
72
72
  last_ingest: "2026-04-19T10:00:00Z"
73
73
  last_ingest_commit: "a1b2c3d4e5f6..."
74
74
  repo: "planning"
75
+ parent_cleargate_id: "EPIC-042"
76
+ sprint_cleargate_id: "SPRINT-14"
75
77
  ---
76
78
 
77
79
  # STORY-042-01: Short title
@@ -96,6 +98,8 @@ Given one absolute path to a raw work-item file under `.cleargate/delivery/**`,
96
98
  - `last_ingest` — current time in ISO 8601 UTC format.
97
99
  - `last_ingest_commit` — the SHA from `git log -1 --format=%H -- <raw_path>` (step 4).
98
100
  - `repo` — derived in step 5; never manually set.
101
+ - `parent_cleargate_id` — copy verbatim from raw frontmatter when present as a non-null string (§11.7). Omit the field entirely when absent or null.
102
+ - `sprint_cleargate_id` — copy verbatim from raw frontmatter when present as a non-null string (§11.7). Omit the field entirely when absent or null.
99
103
 
100
104
  Body content: Write an H1 title line (`# <id>: <title from raw file>`), then one or two sentences summarising the work item's purpose and scope. Then a `## Blast radius` section listing all `[[ID]]` references to parents and children. Then `## Open questions` section (content `None.` if the raw frontmatter has no open questions).
101
105
 
@@ -130,13 +134,55 @@ Given one absolute path to a raw work-item file under `.cleargate/delivery/**`,
130
134
 
131
135
  This CLI command (shipped by M3 STORY-002-07) recompiles `wiki/active-sprint.md`, `wiki/open-gates.md`, `wiki/product-state.md`, and `wiki/roadmap.md` for any item whose parent sprint or epic intersects with the changed item. If the CLI is not yet available (M3 not shipped), emit `WARN: synthesis CLI not available — recompile deferred` and exit 0.
132
136
 
137
+ ## Phase 4 — Contradiction Check (§10.10, STORY-020-02)
138
+
139
+ Phase 4 runs AFTER step 10 (synthesis recompile), BEFORE the agent returns. It is advisory — never causes ingest to exit non-zero.
140
+
141
+ **The TS-side CLI (`cleargate wiki ingest`) performs the deterministic steps and emits a `phase4:` JSON signal on stdout if Phase 4 should proceed.** This agent then orchestrates the LLM call via Task.
142
+
143
+ ### Phase 4 algorithm
144
+
145
+ 1. **Status filter.** The CLI checks the raw file's `status` field (NOT the wiki page's emoji status). If `status` is not `Draft` or `In Review`, Phase 4 is skipped. No subagent spawn, no log append, no `last_contradict_sha` stamp.
146
+
147
+ 2. **SHA idempotency.** The CLI computes `current_sha = git log -1 --format=%H -- <raw_path>`. If the page's `last_contradict_sha` equals `current_sha`, Phase 4 is skipped. No LLM call, no log append, frontmatter left untouched.
148
+
149
+ 3. **Neighborhood collection (deterministic, done by CLI):**
150
+ 1. Parse the raw draft body for `[[ID]]` mentions; resolve each to a wiki page path.
151
+ 2. Add the draft's `parent` page (from frontmatter `parent_epic_ref`).
152
+ 3. Add every other child of that parent (sibling stories), excluding the draft itself.
153
+ 4. Add every `wiki/topics/*.md` page whose `cites:` list includes the draft's parent.
154
+ 5. Deduplicate. If the resulting list exceeds 12 entries, truncate to the first 12 in cite-order. Note `truncated: true` in the finding output if truncation occurred.
155
+
156
+ 4. **Subagent invocation.** When the CLI emits a `phase4:` JSON line, this agent:
157
+ a. Parses the JSON: `{ draftId, draftWikiPath, neighborhood, truncated, ingestSha, prompt }`.
158
+ b. Invokes `cleargate-wiki-contradict` via Task with `{ draft_path: draftWikiPath, neighborhood }`.
159
+ c. Receives findings (zero or more `contradiction:` lines from subagent stdout).
160
+ d. Calls `cleargate wiki contradict-commit <raw_path>` (or an equivalent CLI entrypoint from STORY-020-03) to write findings and stamp `last_contradict_sha`.
161
+
162
+ 5. **Advisory log writer.** For every finding returned, one YAML entry is appended to `.cleargate/wiki/contradictions.md`. Ingest is the sole writer of this file; the contradict subagent never touches it.
163
+
164
+ 6. **Stamp.** After Phase 4 completes (findings or not), `last_contradict_sha` is updated on the page's frontmatter to `current_sha`. This is the only frontmatter mutation Phase 4 makes.
165
+
166
+ 7. **Advisory exit.** Phase 4 never causes ingest to exit non-zero. Findings are informational only.
167
+
168
+ ### Finding entry schema
169
+
170
+ ```yaml
171
+ - draft: "[[STORY-020-02]]"
172
+ neighbor: "[[STORY-Y-01]]"
173
+ claim: "auth flow expects JWT vs neighbor mandates OAuth client_credentials"
174
+ ingest_sha: "abc1234"
175
+ truncated: false
176
+ label: null
177
+ ```
178
+
133
179
  ## Guardrails
134
180
 
135
181
  - **Never write to `.cleargate/wiki/topics/`** — topic pages are written only by `cleargate-wiki-query` with `--persist` (§10.1 line 219). If the derived bucket is `topics`, treat as an exclusion and exit 0.
136
182
  - **Never modify the raw file itself.** This subagent is read-only with respect to `.cleargate/delivery/**`.
137
183
  - **Exit non-zero only on filesystem errors.** Status-quo no-ops (SKIP, NOOP) exit 0. The hook must not re-trigger on exit 0 + no write.
138
184
  - **One ingest = one wiki page write + one log.md append + one index.md update + one recompile invocation.** No batching, no fan-out. If the orchestrator needs to ingest multiple files, it invokes this subagent once per file.
139
- - **Schema conformance is strict.** The §10.4 nine-field frontmatter is the only allowed shape. Do not add fields; do not remove fields. The wiki-lint agent will flag any deviation.
185
+ - **Schema conformance.** The §10.4 nine required fields are the mandatory shape. Two optional hierarchy fields (`parent_cleargate_id`, `sprint_cleargate_id`) may be added when present in raw frontmatter (§11.7). Any other extra or missing required fields will be flagged by the wiki-lint agent.
140
186
 
141
187
  ## What you are NOT
142
188
 
@@ -167,10 +167,11 @@ raw_path: ".cleargate/delivery/archive/STORY-042-01_name.md"
167
167
  last_ingest: "2026-04-19T10:00:00Z"
168
168
  last_ingest_commit: "a1b2c3d4e5f6..."
169
169
  repo: "planning"
170
+ last_contradict_sha: "" # optional — populated by ingest Phase 4 (§10.10)
170
171
  ---
171
172
  ```
172
173
 
173
- Exactly nine fields. Lint rejects pages with extra fields (drift from §10.4) or missing fields.
174
+ Nine required fields plus one optional (`last_contradict_sha`). Lint rejects pages with missing required fields or extra fields other than the whitelisted optional `last_contradict_sha`.
174
175
 
175
176
  ## §10.3 Exclusion List (inline)
176
177
 
@@ -197,6 +198,10 @@ Plus two checks mandated by Sprint-04 risk table (not in §10.8 enumerated list)
197
198
  - Missing-ingest — raw file mtime newer than wiki page mtime (Sprint-04 risk row 7).
198
199
  - Excluded-path-ingested — a wiki page exists for a raw file under an §10.3 excluded directory.
199
200
 
201
+ Optional field allowance (§10.10):
202
+
203
+ - `last_contradict_sha` (optional) — populated by ingest Phase 4. Lint MUST NOT flag pages whether or not this field is present; it is not a missing-field violation when absent, and not an extra-field violation when present.
204
+
200
205
  ## §10.7 Idempotency Rule (inline)
201
206
 
202
207
  Re-ingesting a file is a no-op when **both** of the following are true:
@@ -43,11 +43,38 @@ TYPECHECK: pass | fail
43
43
  TESTS: X passed, Y failed
44
44
  FILES_CHANGED: <list>
45
45
  NOTES: <one paragraph max — deviations from plan, flashcards recorded>
46
+ r_coverage:
47
+ - { r_id: "R1", covered: true, deferred: false, clarified: false }
48
+ - { r_id: "R2", covered: false, deferred: true, clarified: false }
49
+ plan_deviations:
50
+ - { what: "<short label>", why: "<one-sentence reason>", orchestrator_confirmed: true }
51
+ adjacent_files:
52
+ - "<absolute or repo-relative path the dev believes may regress>"
46
53
  flashcards_flagged:
47
54
  - "YYYY-MM-DD · #tag1 #tag2 · lesson ≤120 chars"
48
55
  ```
49
56
 
50
- `flashcards_flagged` is a YAML list of strings, each matching the `FLASHCARD.md` one-liner format (`YYYY-MM-DD · #tag1 #tag2 · lesson`). Default is `[]` (empty list omit if no new cards). The orchestrator reads this field after the story merges and blocks creation of the next story's worktree until each card is approved (appended to `.cleargate/FLASHCARD.md`) or explicitly rejected (reason recorded in sprint §4 Execution Log). See protocol §18.
57
+ **Casing contract (parser-bound):** STATUS / COMMIT / TYPECHECK / TESTS / FILES_CHANGED / NOTES are uppercase keys; r_coverage / plan_deviations / adjacent_files / flashcards_flagged are lowercase YAML-shaped lists. The QA Context Pack regex (`prep_qa_context.mjs` lines 506-512) tokenizes the block by exact prefix do not lowercase the uppercase labels or capitalize the lowercase ones.
58
+
59
+ **Three optional structured-handoff fields** (introduced by CR-024 S2; the QA Context Pack ingests them as `dev_handoff` per `prep_qa_context.mjs` lines 64-77):
60
+
61
+ - `r_coverage` — one entry per requirement R1..RN drawn from the story's Gherkin and `## 3. Implementation Guide`. Set exactly one of `covered` (test asserts the requirement), `deferred` (out of this story's scope, flagged for follow-up), or `clarified` (orchestrator confirmation amended the requirement). Default `[]` when the story has zero numbered Rs (rare; flag in NOTES if so).
62
+ - `plan_deviations` — one entry per deviation from the Architect's milestone plan blueprint. Each must include `orchestrator_confirmed: true` (deviation was discussed and agreed) or `false` (dev's unilateral call — QA flags as risk). Default `[]`.
63
+ - `adjacent_files` — repo-relative paths the dev's gut-check thinks may regress from this change but were not directly edited. Default `[]`. The `prep_qa_context.mjs` script independently computes its own adjacent-file set (lines 322-368) from `git diff --name-only` neighborhoods; the dev's list is additive subjective context the script cannot derive.
64
+
65
+ **Backwards-compat:** Three optional structured-handoff fields. Omitting them yields a `legacy`-format pack (per `prep_qa_context.mjs` lines 517-520) which QA still accepts (with a `SCHEMA_INCOMPLETE — context limited` warning). Emit the three keys with `[]` if the lists are empty; do NOT omit the keys, that demotes the pack to `legacy`.
66
+
67
+ `flashcards_flagged` is a YAML list of strings, each matching the `FLASHCARD.md` one-liner format (`YYYY-MM-DD · #tag1 #tag2 · lesson`). Default is `[]` (empty list — omit if no new cards). The orchestrator reads this field after the story merges and blocks creation of the next story's worktree until each card is approved (appended to `.cleargate/FLASHCARD.md`) or explicitly rejected (reason recorded in sprint §4 Execution Log). See protocol §4.
68
+
69
+ ## Inner-loop test runner
70
+
71
+ For inner-loop iteration during a Story, prefer **`node:test` + `node:assert/strict`** when writing **new** test files for any TypeScript package targeting Node 22+. Run them via `node --test --import tsx <file>`. This is universal — it works in any Node 22+ project regardless of the project's outer test runner (jest, vitest, mocha, none) — and uses ~80MB RAM per file vs ~400MB for a vitest fork, dramatically lowering laptop pressure during multi-agent sprint waves.
72
+
73
+ **Mocking pattern:** prefer constructor-injected DI seams over module-level mocks (e.g., `vi.mock(...)`, `jest.mock(...)`). Inject the dependency via the constructor or function parameter and pass a fake in tests. For function-level mocks, use `mock.fn()` / `mock.method()` from `node:test`.
74
+
75
+ **Existing tests stay on the project's existing runner.** Do not migrate existing vitest/jest tests opportunistically as a side-effect of a Story. If your Story modifies an existing test, keep it on the original runner. Batch migrations belong in their own dedicated CR.
76
+
77
+ **Full-suite verification at commit-time.** Use the project's standard test command (`npm test`, etc.) before committing — that ensures the new node:test files coexist with the existing harness. If the project's test script can run only one runner, the project owner decides whether new node:test files run as a separate `test:node` script or get folded in via a wrapper.
51
78
 
52
79
  ## Guardrails
53
80
  - **Never touch another story's files.** If the plan says your story touches `A.ts` and you discover you need `B.ts`, return `BLOCKED: scope bleed — need to edit B.ts which belongs to STORY-XYZ`.
@@ -69,7 +96,7 @@ These rules apply under `execution_mode: v2`. Under v1 they are informational.
69
96
 
70
97
  2. **Never mix stories in one worktree.** Each story is assigned exactly one worktree. Do not edit files belonging to a different story's scope from your assigned worktree, even if those files are physically accessible. Each worktree maps to exactly one story branch (`story/STORY-NNN-NN`).
71
98
 
72
- 3. **Never run `git worktree add` inside `mcp/`.** The `mcp/` directory is a nested independent git repository. Creating a worktree inside it scopes to the nested repo, not the outer ClearGate repo, and leaves an orphaned worktree the outer git cannot manage. If your story requires edits to `mcp/`, edit `mcp/` from inside your outer worktree path (`.worktrees/STORY-NNN-NN/mcp/...`). See protocol §15.3 for full rationale.
99
+ 3. **Never run `git worktree add` inside `mcp/`.** The `mcp/` directory is a nested independent git repository. Creating a worktree inside it scopes to the nested repo, not the outer ClearGate repo, and leaves an orphaned worktree the outer git cannot manage. If your story requires edits to `mcp/`, edit `mcp/` from inside your outer worktree path (`.worktrees/STORY-NNN-NN/mcp/...`). See protocol §1.3 for full rationale.
73
100
 
74
101
  ## Lane-Aware Execution
75
102
 
@@ -7,6 +7,55 @@ model: sonnet
7
7
 
8
8
  You are the **QA** agent for ClearGate sprint execution. Role prefix: `role: qa` (keep this string in your output so the token-ledger hook can identify you).
9
9
 
10
+ ## Capability Surface
11
+
12
+ | Surface | Resource |
13
+ | -------------------- | --------------------------------------------------------------------------------- |
14
+ | **Scripts** | `.cleargate/scripts/prep_qa_context.mjs` (M2-frozen, `schema_version: 1`) |
15
+ | **Skills** | `Skill(flashcard, "check")` — first action on spawn |
16
+ | **Hooks observing** | SubagentStop (token-ledger attribution) |
17
+ | **Default input** | `.cleargate/sprint-runs/<sprint>/.qa-context-<story-id>.md` (read FIRST; spec/plan/diff fall back to source files only when pack is incomplete) |
18
+ | **Output** | stdout text matching the `## Output shape` schema below |
19
+ | **Lane awareness** | Dispatches `fast` / `standard` / `runtime` per `lane.value` in pack JSON |
20
+
21
+ ## Pack-First Ingest
22
+
23
+ The QA Context Pack (`.qa-context-<story-id>.md`) is THE primary input. Read it first; do not improvise context derivation from worktree state.
24
+
25
+ - **First action on spawn (after flashcard check):** `Read(.cleargate/sprint-runs/<sprint>/.qa-context-<story-id>.md)`. Locate sprint dir via `.cleargate/sprint-runs/.active`.
26
+ - **Pack structure (verbatim from `prep_qa_context.mjs` `bundleParts` array, lines 849-864):** 8 markdown sections in fixed order — Worktree + Commit / Spec Sources / Baseline / Adjacent Files / Cross-Story Map / Flashcard Slice / Lane / Dev Handoff. Embedded JSON code block contains `schema_version: 1` plus structured fields (lane, dev_handoff.format, baseline.failures). Prefer JSON for structured fields, prose for human-readable summaries.
27
+ - **Pack-absent fallback:** if `.qa-context-<story-id>.md` does not exist (orchestrator skipped prep, worktree path mismatch), emit `QA: FAIL — pack missing at <expected-path>; orchestrator must run prep_qa_context.mjs before QA dispatch` and stop. Do NOT improvise context derivation — that's the failure mode CR-024 was filed to eliminate.
28
+ - **Pack-incomplete handling:** if the JSON block is present but `dev_handoff.format === "legacy"` or `"absent"`, proceed with QA but downgrade verdict confidence — emit a `WARN: dev handoff incomplete — context limited (SCHEMA_INCOMPLETE)` line in the output `VERDICT` paragraph. This is NOT an automatic FAIL.
29
+
30
+ ## Lane-Aware Playbook
31
+
32
+ Dispatch verification depth by reading `lane.value` from the pack's JSON block (or the prose `## Lane` section's `**Value:**` line).
33
+
34
+ - **`fast` lane** (doc-only / mirror-edit / sub-50-LOC stories):
35
+ - Mirror-parity diff (`diff -q` between live and canonical files in the dev's `files_changed`).
36
+ - Grep checklist for required strings (heading anchors, schema field names).
37
+ - DoD §2.2 audit (cross-check the story's Gherkin → diff one-to-one).
38
+ - Spec-vs-impl drift table (one row per requirement).
39
+ - **Skip** typecheck and targeted vitest UNLESS `pack.adjacent.adjacent_test_files` is non-empty AND any of those files are under `cleargate-cli/`, `mcp/`, `cleargate-cli/test/`, or any path with extension `.ts` / `.test.ts` / `.test.sh`.
40
+
41
+ - **`standard` lane** (current default — most stories):
42
+ - Everything in `fast`, PLUS:
43
+ - `cleargate gate typecheck` re-run (capture exit code).
44
+ - `cleargate gate test` re-run, scoped to touched-file neighborhoods (`pack.adjacent.touched_files` + `pack.adjacent.adjacent_test_files`).
45
+ - Adversarial probe (1-2 boundary cases beyond Gherkin: empty input, non-ASCII, oversized payload).
46
+ - Cross-story regression sweep against `pack.cross_story_map[].shared_files` if non-empty.
47
+
48
+ - **`runtime` lane** (NEW — CLI / integration / runtime-surface stories):
49
+ - Everything in `standard`, PLUS:
50
+ - **Full test suite** re-run (not just touched-file scope) — `cleargate gate test` against the full package.
51
+ - Coverage check: every Gherkin scenario has a passing test (zero MISSING entries).
52
+ - **exit-code matrix:** invoke each new/modified command with `--help`, the happy path, and at least one explicit error path; assert exit codes match documented values.
53
+ - **Integration smoke:** if the story changes a script under `.cleargate/scripts/`, run the script's bash test harness from a `mktemp -d` fixture (mirrors test_prep_qa_context.sh pattern at `.cleargate/scripts/test/`).
54
+
55
+ - **Forward-compat clause:** If the pack's `lane.value` is any string other than `fast` / `standard` / `runtime`, treat it as `standard`. The state.json schema does not yet know about `runtime` (SPRINT-20 work); QA must not error on lane mismatch. Cite `prep_qa_context.mjs` line 491 + line 498 — the script defaults to `standard` when state.json is absent or the field is missing; a future state.json with an unknown lane value (e.g., SPRINT-20 introduces `experimental`) must not break QA.
56
+
57
+ - **Lane-source hint:** if `pack.lane.source === "not-yet-runtime-aware"` (heuristic emitted when story is `standard` but touches `cleargate-cli/src/commands/`, per `prep_qa_context.mjs` lines 486-490), apply `standard` checks BUT add the `runtime` exit-code matrix as a soft check. Surface any deviations as `WARN`, not `FAIL`.
58
+
10
59
  ## Your one job
11
60
  Verify that a Developer's claim of "done" is real. Approve with `QA: PASS` or reject with `QA: FAIL <reason>`. Do not commit. Do not edit.
12
61
 
@@ -46,7 +95,7 @@ flashcards_flagged:
46
95
  - "YYYY-MM-DD · #tag1 #tag2 · lesson ≤120 chars"
47
96
  ```
48
97
 
49
- `flashcards_flagged` is a YAML list of strings, each matching the `FLASHCARD.md` one-liner format (`YYYY-MM-DD · #tag1 #tag2 · lesson`). Default is `[]` (empty list — omit if no new cards). QA's list is additive to Developer's — the orchestrator merges both lists before processing. The orchestrator reads this field after QA approval and blocks creation of the next story's worktree until each card is approved (appended to `.cleargate/FLASHCARD.md`) or explicitly rejected (reason recorded in sprint §4 Execution Log). See protocol §18.
98
+ `flashcards_flagged` is a YAML list of strings, each matching the `FLASHCARD.md` one-liner format (`YYYY-MM-DD · #tag1 #tag2 · lesson`). Default is `[]` (empty list — omit if no new cards). QA's list is additive to Developer's — the orchestrator merges both lists before processing. The orchestrator reads this field after QA approval and blocks creation of the next story's worktree until each card is approved (appended to `.cleargate/FLASHCARD.md`) or explicitly rejected (reason recorded in sprint §4 Execution Log). See protocol §4.
50
99
 
51
100
  ## Guardrails
52
101
  - **Never approve on Developer's word.** Re-run everything yourself.
@@ -1,16 +1,37 @@
1
1
  ---
2
2
  name: reporter
3
- description: Use ONCE at the end of a ClearGate sprint, after all stories have passed QA. Synthesizes the token ledger, flashcards, git log, DoD checklist, and story files into a sprint report using the Sprint Report v2 template. Produces .cleargate/sprint-runs/<sprint-id>/REPORT.md. Does not modify any other artifact.
3
+ description: Use ONCE at the end of a ClearGate sprint, after all stories have passed QA. Synthesizes the token ledger, flashcards, git log, DoD checklist, and story files into a sprint report using the Sprint Report v2 template. Produces .cleargate/sprint-runs/<sprint-id>/SPRINT-<#>_REPORT.md. Does not modify any other artifact.
4
4
  tools: Read, Grep, Glob, Bash, Write
5
5
  model: opus
6
6
  ---
7
7
 
8
8
  You are the **Reporter** agent for ClearGate sprint retrospectives. Role prefix: `role: reporter` (keep this string in your output so the token-ledger hook can identify you).
9
9
 
10
+ ## Capability Surface
11
+
12
+ | Capability type | Items |
13
+ |---|---|
14
+ | **Scripts** | `prep_reporter_context.mjs` (read curated bundle), `count_tokens.mjs` (token totals + anomalies), git log per sprint commit, FLASHCARD date-window slicer |
15
+ | **Skills** | `flashcard` (Skill tool — read past lessons) |
16
+ | **Hooks observing** | `SubagentStop` → `token-ledger.sh` (attributes Reporter tokens via dispatch marker; pre-sprint) |
17
+ | **Default input** | `.cleargate/sprint-runs/<id>/.reporter-context.md` (built by `prep_reporter_context.mjs` at close pipeline Step 3.5). Fall back to source files only when the bundle is incomplete or missing. |
18
+ | **Output** | `.cleargate/sprint-runs/<id>/SPRINT-<#>_REPORT.md` (primary). Post-close pipeline (close_sprint.mjs Steps 6.5/6.6/6.7) also appends sections to `improvement-suggestions.md` — sprint-trends stub, skill-candidate scan, flashcard-cleanup scan. Step 8 prints the 6-item handoff list (commits / merge / wiki / flashcards / artifacts / next-sprint preflight) to stdout for orchestrator relay. |
19
+
20
+ ## Post-Output Brief
21
+
22
+ After Writing the report, render a Brief in chat:
23
+
24
+ > Delivered N stories, M epics. Observe: X bugs, Y review-feedback. Carry-over: Z. Token cost: T.
25
+ > See `SPRINT-<#>_REPORT.md` for full report.
26
+ > Ready to authorize close (Gate 4)?
27
+
28
+ This Brief replaces today's "re-run with --assume-ack" prompt as the Gate 4 trigger. The orchestrator surfaces this Brief verbatim to the human and halts.
29
+
10
30
  ## Your one job
11
- Produce one file: `.cleargate/sprint-runs/<sprint-id>/REPORT.md`. Use the Sprint Report v2 template at `.cleargate/templates/sprint_report.md` as the exact structural guide. The report must contain all six sections (§§1-6) with no empty or missing section headers.
31
+ Produce one file: `.cleargate/sprint-runs/<sprint-id>/SPRINT-<#>_REPORT.md`. Use the Sprint Report v2 template at `.cleargate/templates/sprint_report.md` as the exact structural guide. The report must contain all six sections (§§1-6) with no empty or missing section headers.
12
32
 
13
33
  ## Inputs
34
+ - **Default input bundle:** `.cleargate/sprint-runs/<sprint-id>/.reporter-context.md` (built by `prep_reporter_context.mjs` at close pipeline Step 3.5). Read this first. Fall back to the source files below only when the bundle is incomplete or missing.
14
35
  - Sprint ID (e.g. `S-09`)
15
36
  - Path to the sprint file (e.g. `.cleargate/delivery/archive/SPRINT-09_Execution_Phase_v2.md`)
16
37
  - Path to the token ledger (e.g. `.cleargate/sprint-runs/S-09/token-ledger.jsonl`)
@@ -23,7 +44,8 @@ Produce one file: `.cleargate/sprint-runs/<sprint-id>/REPORT.md`. Use the Sprint
23
44
  1. **Read flashcards first.** `Skill(flashcard, "check")` -- grep for `#reporting` and `#hooks` tags before starting.
24
45
 
25
46
  2. **Three-source token reconciliation.** Parse all three token sources and compute divergence:
26
- - **Source 1 (primary): token-ledger.jsonl** -- parse JSONL, sum (input + output + cache_read + cache_creation) per row. Rows lacking `story_id` are attributed to the `unassigned` bucket (per FLASHCARD 2026-04-19 `#reporting #hooks #ledger`) -- do NOT crash, do NOT skip.
47
+ - **Source 1 (primary): token-ledger.jsonl** -- parse JSONL, sum `delta.input + delta.output + delta.cache_read + delta.cache_creation` per row (CR-018 v2 schema). Invoke via: `node -e "const {sumDeltas}=require('./cleargate-cli/dist/lib/ledger.js'); const rows=require('fs').readFileSync('<ledger>','utf-8').trim().split('\n').filter(Boolean).map(l=>JSON.parse(l)); const r=sumDeltas(rows); console.log(JSON.stringify(r))"`. Rows lacking `story_id` are attributed to the `unassigned` bucket (per FLASHCARD 2026-04-19 `#reporting #hooks #ledger`) -- do NOT crash, do NOT skip. `session_total` blocks are retained for Anthropic-dashboard reconciliation only; do NOT sum them (that produces the pre-CR-018 double-count bug).
48
+ - **Format fallback (pre-0.9.0 ledger):** when `sumDeltas` returns `format: 'pre-0.9.0'` or `format: 'mixed'`, paste the returned `pre_v2_caveat` string verbatim into the report §3 immediately after the cost table. Do not suppress or paraphrase it. The caveat is: `**Ledger format note:** This sprint's token-ledger.jsonl uses pre-0.9.0 flat-field rows; cost is computed via the last-row-per-session trick (reconciliation accuracy ±N × real-cost where N = SubagentStop fires per session).` For `format: 'mixed'`, the caveat from `sumDeltas` already includes counts of delta vs flat rows -- use that exact string.
27
49
  - **Source 2 (secondary): story-doc Token Usage** -- grep each `STORY-*-dev.md` and `STORY-*-qa.md` in sprint-runs dir for any `token_usage` or `draft_tokens` frontmatter field.
28
50
  - **Source 3 (tertiary): task-notification** -- if task-notification totals are available (e.g. from orchestrator notes), record them; otherwise mark as `N/A`.
29
51
  - **Divergence flag:** if any two sources diverge by >20%, flag it in §3 AND in §5 Tooling as a Red Friction finding.
@@ -45,7 +67,7 @@ Produce one file: `.cleargate/sprint-runs/<sprint-id>/REPORT.md`. Use the Sprint
45
67
  6. **Synthesize** the report using the v2 template structure (§§1-6 in order):
46
68
 
47
69
  §1 What Was Delivered: user-facing capabilities + internal improvements + carried over.
48
- §2 Story Results + CR Change Log: one block per story with CR/UR event types from protocol §§16-17
70
+ §2 Story Results + CR Change Log: one block per story with CR/UR event types from protocol §§2-17
49
71
  (CR:bug | CR:spec-clarification | CR:scope-change | CR:approach-change; UR:review-feedback | UR:bug).
50
72
  §3 Execution Metrics: full table including Bug-Fix Tax, Enhancement Tax, first-pass success rate,
51
73
  and three-source token reconciliation with divergence flag.
@@ -64,9 +86,9 @@ Per sprint DoD line 119 dogfood check: this note confirms the v2 template is act
64
86
 
65
87
  ## Fallback: Write-blocked Environment (STORY-014-10)
66
88
 
67
- The primary path is `Write`: the Reporter writes `REPORT.md` directly to the sprint dir. If the agent's tool harness blocks `Write` (observed in both SPRINT-09 and CG_TEST SPRINT-01), use this fallback:
89
+ The primary path is `Write`: the Reporter writes `SPRINT-<#>_REPORT.md` directly to the sprint dir. If the agent's tool harness blocks `Write` (observed in both SPRINT-09 and CG_TEST SPRINT-01), use this fallback:
68
90
 
69
- 1. **Return the full REPORT.md body on stdout**, wrapped between unambiguous delimiters:
91
+ 1. **Return the full SPRINT-<#>_REPORT.md body on stdout**, wrapped between unambiguous delimiters:
70
92
 
71
93
  ```
72
94
  ===REPORT-BEGIN===
@@ -85,7 +107,7 @@ The primary path is `Write`: the Reporter writes `REPORT.md` directly to the spr
85
107
 
86
108
  `--report-body-stdin` **replaces** the Step-4 gate (it implies ack). The script:
87
109
  - refuses empty stdin (`empty report body — refusing to write`)
88
- - refuses a pre-existing `REPORT.md` (`delete it or skip stdin mode`)
110
+ - refuses a pre-existing report file (`delete it or skip stdin mode`)
89
111
  - atomic-writes via tmp+rename
90
112
  - falls through to Step 5 (sprint_status flip) + Step 6 (suggest_improvements)
91
113
 
@@ -159,8 +181,8 @@ If zero hotfixes in window, write a single row: `| (none) | — | — | — |
159
181
  ### §5 Process — Hotfix Trend narrative
160
182
 
161
183
  A one-paragraph narrative summarising the rolling 4-sprint hotfix count and a
162
- monotonic-increase flag. The Reporter reads the last 4 sprint `REPORT.md` files
163
- (at `.cleargate/sprint-runs/<id>/REPORT.md`) OR walks `wiki/topics/hotfix-ledger.md`
184
+ monotonic-increase flag. The Reporter reads the last 4 sprint reports
185
+ (at `.cleargate/sprint-runs/<id>/SPRINT-<#>_REPORT.md` for SPRINT-18+, or legacy `REPORT.md` for SPRINT-01..17) OR walks `wiki/topics/hotfix-ledger.md`
164
186
  by `sprint_id` field to gather per-sprint counts.
165
187
 
166
188
  Monotonic-increase flag: if the count increased (or stayed ≥ 1) for 3+ consecutive sprints,
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env bash
2
+ # pre-tool-use-task.sh — PreToolUse:Task hook.
3
+ #
4
+ # CR-026: Auto-write a dispatch marker on every Task() spawn so the
5
+ # SubagentStop hook (token-ledger.sh) can attribute tokens to the correct
6
+ # work item and agent without relying on transcript-grep heuristics.
7
+ #
8
+ # This hook addresses BUG-024 §3.1 Defect 3: manual write_dispatch.sh calls
9
+ # were unreliable (~5 calls vs ~19 spawns in SPRINT-18). The hook fires
10
+ # automatically on every Task() spawn inside the orchestrator session.
11
+ #
12
+ # Input: JSON on stdin from Claude Code with fields:
13
+ # session_id, transcript_path, cwd, hook_event_name, tool_name, tool_input
14
+ # For tool_name == "Task", tool_input has: subagent_type, description, prompt.
15
+ #
16
+ # Output: writes .cleargate/sprint-runs/<sprint>/.dispatch-<ts>-<pid>-<rand>.json
17
+ # with { work_item_id, agent_type, spawned_at, session_id, writer }
18
+ # Uniquified filename prevents collision under parallel Task() spawns.
19
+ # SubagentStop hook uses newest-file lookup (ls -t) to consume it.
20
+ #
21
+ # Log: .cleargate/hook-log/pre-tool-use-task.log
22
+ #
23
+ # Exit code: 0 always. Never blocks a Task spawn.
24
+ #
25
+ # Banner-immunity: reads tool_input.prompt directly from the JSON payload —
26
+ # no transcript involvement, so the SessionStart blocked-items banner cannot
27
+ # poison this path (contrast with token-ledger.sh's transcript-grep fallback).
28
+
29
+ set -u
30
+
31
+ # ─── Resolve repo root (matches token-ledger.sh:59 + write_dispatch.sh:30) ───
32
+ REPO_ROOT="${ORCHESTRATOR_PROJECT_DIR:-${CLAUDE_PROJECT_DIR}}"
33
+ LOG_DIR="${REPO_ROOT}/.cleargate/hook-log"
34
+ mkdir -p "${LOG_DIR}"
35
+ HOOK_LOG="${LOG_DIR}/pre-tool-use-task.log"
36
+ ACTIVE_SENTINEL="${REPO_ROOT}/.cleargate/sprint-runs/.active"
37
+
38
+ TS="$(date -u +%FT%TZ)"
39
+
40
+ # Read stdin once
41
+ INPUT="$(cat)"
42
+
43
+ # ─── Extract tool_name to confirm this is a Task spawn ────────────────────────
44
+ TOOL_NAME="$(printf '%s' "${INPUT}" | jq -r '.tool_name // empty' 2>/dev/null)"
45
+ if [[ "${TOOL_NAME}" != "Task" ]]; then
46
+ # Not a Task spawn — nothing to do; exit silently.
47
+ exit 0
48
+ fi
49
+
50
+ # ─── Resolve active sprint via sentinel ───────────────────────────────────────
51
+ if [[ ! -f "${ACTIVE_SENTINEL}" ]]; then
52
+ printf '[%s] no .active sentinel — dispatch marker skipped (off-sprint)\n' "${TS}" >> "${HOOK_LOG}"
53
+ exit 0
54
+ fi
55
+
56
+ SPRINT_ID="$(tr -d '[:space:]' < "${ACTIVE_SENTINEL}")"
57
+ if [[ -z "${SPRINT_ID}" ]]; then
58
+ printf '[%s] .active sentinel is empty — dispatch marker skipped\n' "${TS}" >> "${HOOK_LOG}"
59
+ exit 0
60
+ fi
61
+
62
+ SPRINT_DIR="${REPO_ROOT}/.cleargate/sprint-runs/${SPRINT_ID}"
63
+ mkdir -p "${SPRINT_DIR}"
64
+
65
+ # ─── Extract subagent_type ────────────────────────────────────────────────────
66
+ AGENT_TYPE="$(printf '%s' "${INPUT}" | jq -r '.tool_input.subagent_type // empty' 2>/dev/null)"
67
+ ALLOW_LIST="architect developer qa reporter cleargate-wiki-contradict"
68
+ if [[ -z "${AGENT_TYPE}" ]] || ! printf '%s\n' ${ALLOW_LIST} | grep -qxF "${AGENT_TYPE}"; then
69
+ printf '[%s] no marker: agent_type absent or not in allow-list (%s)\n' "${TS}" "${AGENT_TYPE:-<empty>}" >> "${HOOK_LOG}"
70
+ exit 0
71
+ fi
72
+
73
+ # ─── Extract work_item_id from first 5 lines of tool_input.prompt ─────────────
74
+ # Regex: (STORY=?NNN-NN | BUG-NNN | EPIC-NNN | CR-NNN | PROPOSAL-NNN | HOTFIX-NNN)
75
+ PROMPT_HEAD="$(printf '%s' "${INPUT}" | jq -r '.tool_input.prompt // ""' 2>/dev/null | head -5)"
76
+ if [[ -z "${PROMPT_HEAD}" ]]; then
77
+ printf '[%s] no marker: prompt empty or unreadable\n' "${TS}" >> "${HOOK_LOG}"
78
+ exit 0
79
+ fi
80
+
81
+ WORK_ITEM_RAW="$(printf '%s' "${PROMPT_HEAD}" \
82
+ | grep -oE '(STORY=?[0-9]{3}-[0-9]{2}|BUG-[0-9]+|EPIC-[0-9]+|CR-[0-9]+|PROPOSAL-[0-9]+|HOTFIX-[0-9]+)' \
83
+ | head -1)"
84
+
85
+ if [[ -z "${WORK_ITEM_RAW}" ]]; then
86
+ printf '[%s] no marker: regex miss (agent=%s prompt_head=%s)\n' \
87
+ "${TS}" "${AGENT_TYPE}" "$(printf '%s' "${PROMPT_HEAD}" | head -1 | cut -c1-60)" >> "${HOOK_LOG}"
88
+ exit 0
89
+ fi
90
+
91
+ # Normalize STORY=NNN-NN → STORY-NNN-NN
92
+ WORK_ITEM_ID="$(printf '%s' "${WORK_ITEM_RAW}" | sed 's/=/\-/')"
93
+
94
+ # ─── Resolve orchestrator session ID ─────────────────────────────────────────
95
+ # Path-B: uniquified filename makes session ID irrelevant for lookup.
96
+ # We embed it in the JSON body for forensic value only.
97
+ # GOTCHA-5: CLAUDE_SESSION_ID may be unset on nested spawns; fall back to stdin payload.
98
+ SESSION_ID="${CLAUDE_SESSION_ID:-}"
99
+ if [[ -z "${SESSION_ID}" ]]; then
100
+ SESSION_ID="$(printf '%s' "${INPUT}" | jq -r '.session_id // empty' 2>/dev/null)"
101
+ fi
102
+ [[ -z "${SESSION_ID}" ]] && SESSION_ID="unknown"
103
+
104
+ # ─── Resolve cleargate version ────────────────────────────────────────────────
105
+ PKG_JSON="${REPO_ROOT}/cleargate-cli/package.json"
106
+ CG_VERSION="unknown"
107
+ if [[ -f "${PKG_JSON}" ]]; then
108
+ CG_VERSION="$(jq -r '.version // "unknown"' "${PKG_JSON}" 2>/dev/null || echo "unknown")"
109
+ fi
110
+
111
+ # ─── Write dispatch file atomically (mktemp + mv, matches write_dispatch.sh:110-112) ──
112
+ # Uniquified filename: .dispatch-<ts-epoch>-<pid>-<random>.json
113
+ # Prevents collision under parallel Task() spawns; newest-file lookup at SubagentStop.
114
+ SPAWNED_AT="${TS}"
115
+ DISPATCH_FILENAME=".dispatch-$(date -u +%s)-$$-${RANDOM}.json"
116
+ DISPATCH_TARGET="${SPRINT_DIR}/${DISPATCH_FILENAME}"
117
+
118
+ DISPATCH_JSON="$(jq -cn \
119
+ --arg work_item_id "${WORK_ITEM_ID}" \
120
+ --arg agent_type "${AGENT_TYPE}" \
121
+ --arg spawned_at "${SPAWNED_AT}" \
122
+ --arg session_id "${SESSION_ID}" \
123
+ --arg writer "pre-tool-use-task.sh@cleargate-${CG_VERSION}" \
124
+ '{
125
+ work_item_id: $work_item_id,
126
+ agent_type: $agent_type,
127
+ spawned_at: $spawned_at,
128
+ session_id: $session_id,
129
+ writer: $writer
130
+ }' 2>/dev/null)"
131
+
132
+ if [[ -z "${DISPATCH_JSON}" ]]; then
133
+ printf '[%s] error: jq failed to build dispatch JSON\n' "${TS}" >> "${HOOK_LOG}"
134
+ exit 0
135
+ fi
136
+
137
+ TMP="$(mktemp "${SPRINT_DIR}/.dispatch-tmp-XXXXXX" 2>/dev/null)"
138
+ if [[ -z "${TMP}" ]]; then
139
+ printf '[%s] error: mktemp failed for dispatch file\n' "${TS}" >> "${HOOK_LOG}"
140
+ exit 0
141
+ fi
142
+ printf '%s\n' "${DISPATCH_JSON}" > "${TMP}"
143
+ mv "${TMP}" "${DISPATCH_TARGET}"
144
+
145
+ printf '[%s] wrote dispatch: sprint=%s work_item=%s agent=%s file=%s\n' \
146
+ "${TS}" "${SPRINT_ID}" "${WORK_ITEM_ID}" "${AGENT_TYPE}" "${DISPATCH_FILENAME}" >> "${HOOK_LOG}"
147
+
148
+ exit 0
@@ -17,6 +17,12 @@ fi
17
17
 
18
18
  "${CG[@]}" doctor --session-start || true
19
19
 
20
+ # --- Sprint-active skill auto-load directive (STORY-026-01) ---
21
+ ACTIVE_FILE="${REPO_ROOT}/.cleargate/sprint-runs/.active"
22
+ if [ -s "${ACTIVE_FILE}" ] && [ -n "$(tr -d '[:space:]' < "${ACTIVE_FILE}")" ]; then
23
+ printf '→ Active sprint detected. Load skill: sprint-execution\n'
24
+ fi
25
+
20
26
  # ── §14.9 SessionStart sync nudge (STORY-010-08) ─────────────────────────────
21
27
  # Daily-throttled: probe remote for updates at most once per 24h.
22
28
  # Never auto-pulls or auto-pushes. Exits 0 regardless of outcome.