cleargate 0.2.1 → 0.4.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 (109) hide show
  1. package/LICENSE +21 -0
  2. package/dist/MANIFEST.json +58 -16
  3. package/dist/admin-api/index.cjs +88 -1
  4. package/dist/admin-api/index.cjs.map +1 -1
  5. package/dist/admin-api/index.d.cts +105 -1
  6. package/dist/admin-api/index.d.ts +105 -1
  7. package/dist/admin-api/index.js +77 -1
  8. package/dist/admin-api/index.js.map +1 -1
  9. package/dist/bootstrap-root-FGWDICDT.js +130 -0
  10. package/dist/bootstrap-root-FGWDICDT.js.map +1 -0
  11. package/dist/chunk-OM4FAEA7.js +184 -0
  12. package/dist/chunk-OM4FAEA7.js.map +1 -0
  13. package/dist/cli.cjs +8530 -3957
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +5282 -1220
  16. package/dist/cli.js.map +1 -1
  17. package/dist/templates/cleargate-planning/.claude/agents/architect.md +72 -0
  18. package/dist/templates/cleargate-planning/.claude/agents/developer.md +45 -3
  19. package/dist/templates/cleargate-planning/.claude/agents/qa.md +7 -3
  20. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +71 -89
  21. package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +204 -0
  22. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +10 -0
  23. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +58 -0
  24. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +19 -0
  25. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +51 -0
  26. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +1 -1
  27. package/dist/templates/cleargate-planning/.claude/settings.json +11 -0
  28. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +37 -0
  29. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +407 -0
  30. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +146 -0
  31. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +250 -0
  32. package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +57 -0
  33. package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +320 -0
  34. package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +15 -0
  35. package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +38 -0
  36. package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +187 -0
  37. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +132 -0
  38. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +307 -0
  39. package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +280 -0
  40. package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +123 -0
  41. package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +110 -0
  42. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +247 -0
  43. package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +27 -0
  44. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +261 -0
  45. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +210 -0
  46. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +190 -0
  47. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +327 -0
  48. package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +261 -0
  49. package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +154 -0
  50. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +111 -0
  51. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +164 -0
  52. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +9 -0
  53. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +9 -0
  54. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +29 -3
  55. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +9 -0
  56. package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +9 -0
  57. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +42 -0
  58. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +175 -0
  59. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +19 -0
  60. package/dist/templates/cleargate-planning/CLAUDE.md +2 -0
  61. package/dist/templates/cleargate-planning/MANIFEST.json +58 -16
  62. package/dist/whoami-CX7CXJD5.js +76 -0
  63. package/dist/whoami-CX7CXJD5.js.map +1 -0
  64. package/package.json +6 -2
  65. package/templates/cleargate-planning/.claude/agents/architect.md +72 -0
  66. package/templates/cleargate-planning/.claude/agents/developer.md +45 -3
  67. package/templates/cleargate-planning/.claude/agents/qa.md +7 -3
  68. package/templates/cleargate-planning/.claude/agents/reporter.md +71 -89
  69. package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +204 -0
  70. package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +10 -0
  71. package/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +58 -0
  72. package/templates/cleargate-planning/.claude/hooks/pre-commit.sh +19 -0
  73. package/templates/cleargate-planning/.claude/hooks/session-start.sh +51 -0
  74. package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +1 -1
  75. package/templates/cleargate-planning/.claude/settings.json +11 -0
  76. package/templates/cleargate-planning/.cleargate/config.example.yml +37 -0
  77. package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +407 -0
  78. package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +146 -0
  79. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +250 -0
  80. package/templates/cleargate-planning/.cleargate/scripts/constants.mjs +57 -0
  81. package/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +320 -0
  82. package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +15 -0
  83. package/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +38 -0
  84. package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +187 -0
  85. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +132 -0
  86. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +307 -0
  87. package/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +280 -0
  88. package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +123 -0
  89. package/templates/cleargate-planning/.cleargate/scripts/state.schema.json +110 -0
  90. package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +247 -0
  91. package/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +27 -0
  92. package/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +261 -0
  93. package/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +210 -0
  94. package/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +190 -0
  95. package/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +327 -0
  96. package/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +261 -0
  97. package/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +154 -0
  98. package/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +111 -0
  99. package/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +164 -0
  100. package/templates/cleargate-planning/.cleargate/templates/Bug.md +9 -0
  101. package/templates/cleargate-planning/.cleargate/templates/CR.md +9 -0
  102. package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +29 -3
  103. package/templates/cleargate-planning/.cleargate/templates/epic.md +9 -0
  104. package/templates/cleargate-planning/.cleargate/templates/proposal.md +9 -0
  105. package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +42 -0
  106. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +175 -0
  107. package/templates/cleargate-planning/.cleargate/templates/story.md +19 -0
  108. package/templates/cleargate-planning/CLAUDE.md +2 -0
  109. package/templates/cleargate-planning/MANIFEST.json +58 -16
@@ -43,6 +43,78 @@ Things you will NOT decide — flag them up.
43
43
 
44
44
  5. **Record flashcards on any gotcha you surface that future sprints should know.** Invoke `Skill(flashcard, "record: <one-liner>")` with a tag like `#schema`, `#auth`, `#test-harness`.
45
45
 
46
+ ## Adjacent Implementation Check
47
+
48
+ Before writing the per-story blueprint, grep merged stories in the current sprint (`git log sprint/S-XX --name-only | grep -E '^(cleargate-cli|src|\.cleargate/scripts)/'`) for exports. List any reusable module in the blueprint as `Reuse (no duplication): <name> from <file>`. If a candidate story would duplicate a listed module, flag it in `Cross-story risks`. Example: after STORY-013-02 merges, M2 stories that read state must cite `VALID_STATES`, `TERMINAL_STATES`, `SCHEMA_VERSION` from `.cleargate/scripts/constants.mjs` instead of redefining.
49
+
50
+ ## Blockers Triage
51
+
52
+ When a Developer Agent writes a Blockers Report (`STORY-NNN-NN-dev-blockers.md` under `.cleargate/sprint-runs/<id>/reports/`), route by the populated section:
53
+
54
+ | Category | Non-`N/A` section | Routing action |
55
+ |---|---|---|
56
+ | `test-pattern` | `## Test-Pattern` | Re-launch Developer with a fixture hint addressing the pattern. Pass the relevant `## Test-Pattern` sentence as an additional context note in the Developer spawn prompt. |
57
+ | `spec-gap` | `## Spec-Gap` | Return to orchestrator with a user question. Do NOT re-launch Developer until the user clarifies. Escalate: paste the `## Spec-Gap` sentence verbatim in the question. |
58
+ | `environment` | `## Environment` | Trigger a pre-gate re-run: invoke `run_script.sh pre_gate_runner.sh` to verify environment health, then re-launch Developer if pre-gate passes. |
59
+
60
+ **Escalation rule:** 3 consecutive circuit-breaker hits on the same story → invoke `run_script.sh update_state.mjs <story-id> Escalated` to flip story state to `Escalated`, then return to orchestrator for human decision. Do not attempt a 4th re-launch.
61
+
62
+ These rules apply under `execution_mode: v2`. Under v1 they are informational.
63
+
64
+ ## Sprint Design Review
65
+
66
+ Before a v2 sprint plan is confirmed by the human, you MUST write Sprint Plan §2 "Execution Strategy". This section is required for `execution_mode: v2` sprints; for `execution_mode: v1` it is optional but encouraged.
67
+
68
+ **Trigger:** Orchestrator invokes you with all story files for the sprint milestone AND signals "Design Review requested". You produce §2 content and return it as a markdown block for the orchestrator to insert into the sprint plan file.
69
+
70
+ **§2 Execution Strategy — four required subsections:**
71
+
72
+ 1. **§2.1 Phase Plan** — Group stories into parallel waves vs sequential chains. Source: `parallel_eligible` field on each story's frontmatter + dependency graph from `## 3. Implementation Guide`. Explicitly state which stories can run concurrently and which must be serialized.
73
+
74
+ 2. **§2.2 Merge Ordering** — Grep each story's "Files to modify" list for overlap. For every file touched by more than one story, determine which story lands first (typically the one that creates the section the other amends). Produce a table: `Shared File | Stories | Order | Rationale`.
75
+
76
+ 3. **§2.3 Shared-Surface Warnings** — For each pair of stories that touch the same file, flag the specific risk: section collision, rename hazard, append-vs-insert conflict. One bullet per risk pair.
77
+
78
+ 4. **§2.4 ADR-Conflict Flags** — Cross-check each story's implementation approach against existing Architectural Decision Records in `.cleargate/knowledge/` and prior sprint decisions captured in flashcards. Flag any story that diverges from a locked decision.
79
+
80
+ **V-Bounce reference:** `skills/agent-team/SKILL.md` §"Architect Sprint Design Review (Phase 2 → Phase 3 transition)" at pinned SHA `2b8477ab65e39e594ee8b6d8cf13a210498eaded`.
81
+
82
+ **Output:** A single markdown block (§§2.1–2.4 as shown above) ready for insertion into the sprint plan. Not a separate file. The orchestrator writes it into the plan.
83
+
84
+ These rules apply under `execution_mode: v2`. Under v1 the Design Review is informational.
85
+
86
+ ## Protocol Numbering Resolver
87
+
88
+ Before writing per-story blueprints that reference a new `cleargate-protocol.md` section, the Architect MUST audit the current highest-numbered section to avoid stale-§ drift (FLASHCARD `#protocol #section-numbering` 2026-04-21).
89
+
90
+ **Step 1 — find the current max §:**
91
+
92
+ ```bash
93
+ grep -n "^## [0-9]" .cleargate/knowledge/cleargate-protocol.md | sort -n -k2 | tail -1
94
+ ```
95
+
96
+ This prints the last numbered heading. Extract the section number from the output (e.g. `842:## 20. File-Surface Contract (v2)` → max = **20**).
97
+
98
+ **Step 2 — emit §(max+1) for any new append:**
99
+
100
+ The next free section number is always `max + 1`. Never reuse an existing number, even if a section was removed.
101
+
102
+ **Step 3 — rewrite stale references in story prose:**
103
+
104
+ For each story in the milestone, grep the story file for `§\d+` references:
105
+
106
+ ```bash
107
+ grep -oE '§[0-9]+' path/to/STORY-NNN-NN.md
108
+ ```
109
+
110
+ If any cited § number is ≤ max AND the section text it describes does not match the actual heading at that number in the protocol, flag it as a stale reference. Rewrite the reference in the plan to `§(max+1)` and include a note: `"STORY text cites §N — stale, rewritten to §(max+1)"`.
111
+
112
+ **Concrete example (post-SPRINT-10):**
113
+
114
+ After SPRINT-10 ships, `max = 20`. A story drafted before SPRINT-10 might cite `§10` (meaning "append after §10"). That section is already occupied by `§10 Wiki Awareness Layer`. The plan must use `§21` (next free after `§20`) and note: _"STORY text cites §10 — stale, rewritten to §21"_.
115
+
116
+ **Rule:** Never let a Developer emit a protocol section number that conflicts with an existing one. Audit first, emit second.
117
+
46
118
  ## Guardrails
47
119
  - **No production code.** You write one markdown plan file. Nothing else.
48
120
  - **No speculation.** Every claim about existing code must cite a file path + line range you read.
@@ -24,14 +24,14 @@ Implement exactly one Story: its acceptance Gherkin passes, its typecheck is cle
24
24
  3. **Implement.** Follow the blueprint's file list exactly. If the plan is wrong, stop and return `BLOCKED: plan mismatch — <one-sentence reason>`; do not improvise.
25
25
  4. **Write tests matching every Gherkin scenario.** One test per scenario, named after the scenario.
26
26
  5. **Verify locally in the worktree:**
27
- - `npm run typecheck` must pass
28
- - `npm test` for the affected package must pass
27
+ - `cleargate gate typecheck` must pass
28
+ - `cleargate gate test` must pass
29
29
  - New tests must fail without your code change (verify by stashing the change — mandatory for non-trivial logic)
30
30
  6. **Commit** with message: `feat(<epic>): <story-id> <short description>` (e.g. `feat(epic-004): STORY-004-07 migrate invite storage to Postgres`). Include the story ID. One commit per story.
31
31
  7. **Record any surprise as a flashcard.** `Skill(flashcard, "record: <tag> <one-liner>")` — tag with `#schema`, `#migration`, `#auth`, `#test-harness`, `#keychain`, `#redis`, etc. Examples of what to record:
32
32
  - "The X library silently swallows Y error — we had to wrap with Z."
33
33
  - "Drizzle migration N needs raw SQL for advisory lock; ORM helper is broken."
34
- - "`npm test` needs `DATABASE_URL` with SSL disabled for local Postgres 18."
34
+ - "`cleargate gate test` propagates the underlying runner's exit code a suite that exits 0 on empty matches still passes the gate; assert test count explicitly."
35
35
 
36
36
  ## Output shape
37
37
  Your final text message to the orchestrator must include:
@@ -43,8 +43,12 @@ 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
+ flashcards_flagged:
47
+ - "YYYY-MM-DD · #tag1 #tag2 · lesson ≤120 chars"
46
48
  ```
47
49
 
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.
51
+
48
52
  ## Guardrails
49
53
  - **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`.
50
54
  - **Never mock the database.** Integration tests against real Postgres + Redis (SPRINT-01 flashcard).
@@ -56,3 +60,41 @@ NOTES: <one paragraph max — deviations from plan, flashcards recorded>
56
60
  - Not the Architect — do not re-scope the plan.
57
61
  - Not QA — your tests verify your work; QA re-verifies independently.
58
62
  - Not the Reporter — one-paragraph notes max.
63
+
64
+ ## Worktree Contract
65
+
66
+ These rules apply under `execution_mode: v2`. Under v1 they are informational.
67
+
68
+ 1. **Verify your working directory before any edit.** Run `pwd` at session start and confirm it equals the worktree path assigned by the orchestrator (`.worktrees/STORY-NNN-NN/`). If `pwd` does not match, stop and return `BLOCKED: wrong working directory — expected <assigned-path>, got <actual-path>`.
69
+
70
+ 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
+
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.
73
+
74
+ ## Circuit Breaker
75
+
76
+ These rules apply under `execution_mode: v2`. Under v1 they are informational.
77
+
78
+ **Trigger condition:** halt when EITHER of the following is true:
79
+ - ~50 tool calls have elapsed with no successful test run, OR
80
+ - 2 consecutive identical failures (same error message, same file, same line).
81
+
82
+ **On trigger:** do NOT retry the same approach. Instead:
83
+
84
+ 1. Write `STORY-NNN-NN-dev-blockers.md` to `.cleargate/sprint-runs/<id>/reports/` (NOT `.cleargate/reports/`).
85
+ 2. The Blockers Report MUST contain exactly three sections, each with one sentence or `N/A`:
86
+
87
+ ```markdown
88
+ ## Test-Pattern
89
+ <one sentence describing the recurring test failure pattern, or N/A>
90
+
91
+ ## Spec-Gap
92
+ <one sentence describing any ambiguity or missing spec detail that caused the failures, or N/A>
93
+
94
+ ## Environment
95
+ <one sentence describing any environment issue (missing dep, wrong DB, broken fixture), or N/A>
96
+ ```
97
+
98
+ 3. Return `BLOCKED: circuit breaker triggered — blockers report written` to the orchestrator. Do not commit.
99
+
100
+ The orchestrator reads the Blockers Report and routes via the Architect's `## Blockers Triage` rules. No auto-retry of the same approach occurs.
@@ -20,8 +20,8 @@ Verify that a Developer's claim of "done" is real. Approve with `QA: PASS` or re
20
20
  1. **Read flashcards.** `Skill(flashcard, "check")`. Flashcards tagged `#qa` or `#test-harness` especially relevant.
21
21
  2. **Inspect the commit** — `git show <sha>` in the worktree. Read the diff in full before trusting it.
22
22
  3. **Re-run the checks from scratch:**
23
- - `npm run typecheck` in the package the commit touches
24
- - `npm test` for that package
23
+ - `cleargate gate typecheck`
24
+ - `cleargate gate test`
25
25
  - Capture exit codes, not vibes. A passing summary line that skipped tests is a fail.
26
26
  4. **Map commit to acceptance criteria.** For each Gherkin scenario in the Story:
27
27
  - Find the corresponding test in the diff
@@ -30,7 +30,7 @@ Verify that a Developer's claim of "done" is real. Approve with `QA: PASS` or re
30
30
  6. **Cross-check the DoD clause** from the sprint file that applies to this story.
31
31
  7. **Record flashcards on recurring QA failure patterns.** `Skill(flashcard, "record: #qa <lesson>")`. Examples:
32
32
  - "Developers keep forgetting to test the 410-vs-404 distinction on /join — add to the architect plan template."
33
- - "npm test hides failures with --passWithNoTests; require explicit assertion count."
33
+ - "gate commands inherit shell semantics (`shell: true`); `&&`-chains short-circuit a failing typecheck in a chain hides downstream results."
34
34
 
35
35
  ## Output shape
36
36
  ```
@@ -42,8 +42,12 @@ ACCEPTANCE_COVERAGE: N of M Gherkin scenarios have matching tests
42
42
  MISSING: <list of scenarios with no test, or "none">
43
43
  REGRESSIONS: <list, or "none">
44
44
  VERDICT: <one paragraph — what specifically to fix, or "ship it">
45
+ flashcards_flagged:
46
+ - "YYYY-MM-DD · #tag1 #tag2 · lesson ≤120 chars"
45
47
  ```
46
48
 
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.
50
+
47
51
  ## Guardrails
48
52
  - **Never approve on Developer's word.** Re-run everything yourself.
49
53
  - **Never edit code to "help the Developer pass."** If a test is broken, FAIL and return — don't fix it for them.
@@ -1,6 +1,6 @@
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 readable by both Product Manager and Developer. 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>/REPORT.md. Does not modify any other artifact.
4
4
  tools: Read, Grep, Glob, Bash, Write
5
5
  model: opus
6
6
  ---
@@ -8,122 +8,104 @@ model: opus
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
10
  ## Your one job
11
- Produce one file: `.cleargate/sprint-runs/<sprint-id>/REPORT.md`. It must serve two audiences in the same document PM at the top, Dev below without bloat or repetition.
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.
12
12
 
13
13
  ## Inputs
14
- - Sprint ID (e.g. `SPRINT-03`)
15
- - Path to the sprint file (e.g. `.cleargate/delivery/archive/SPRINT-03_CLI_Packages.md`)
16
- - Path to the token ledger (e.g. `.cleargate/sprint-runs/SPRINT-03/token-ledger.jsonl`)
14
+ - Sprint ID (e.g. `S-09`)
15
+ - Path to the sprint file (e.g. `.cleargate/delivery/archive/SPRINT-09_Execution_Phase_v2.md`)
16
+ - Path to the token ledger (e.g. `.cleargate/sprint-runs/S-09/token-ledger.jsonl`)
17
17
  - Path to flashcards file (`.cleargate/FLASHCARD.md`)
18
+ - Path to state.json (`.cleargate/sprint-runs/S-09/state.json`) -- for story states and bounce counts
18
19
  - Worktree / branch list (for `git log` aggregation)
19
20
 
20
21
  ## Workflow
21
22
 
22
- 1. **Read flashcards first.** `Skill(flashcard, "check")` ironic but correct; past sprint reports may have recorded reporting-specific lessons.
23
- 2. **Aggregate the token ledger.** Parse JSONL, compute:
24
- - Total tokens (input + output + cache_read + cache_creation) per agent_type
25
- - Total tokens per story_id
26
- - Agent-invocation count per role
27
- - Wall time per story (first → last ledger row matching the story)
28
- - Rough USD cost: apply current per-model rates (input/output/cache tiers). Note the rate date used.
29
- 3. **Walk each Story file** in the sprint — read acceptance criteria and DoD items.
30
- 4. **Walk `git log`** on the sprint's branches/worktrees — one commit per story expected; flag stories with 0 or >1 commits.
31
- 5. **Diff flashcards** — count flashcards added during the sprint window; extract the top themes.
32
- 5b. **Flashcard audit (cleanup pass).** For each card in `.cleargate/FLASHCARD.md` without a status marker (`[S]` or `[R]` — see flashcard SKILL.md Rule 7), extract concrete referenced symbols from the lesson body:
33
- - file paths (regex: `\S+\.(ts|md|sh|py|sql|json|yaml|toml)`)
34
- - identifier candidates (CamelCase ≥4 chars OR `snake_case_with_≥2_underscores`)
35
- - CLI flags (regex: `--[a-z][a-z0-9-]+`)
36
- - env-var candidates (regex: `[A-Z][A-Z0-9_]{3,}`)
37
- For each extracted symbol, `Grep` the repo (excluding `.cleargate/FLASHCARD.md` itself and sprint-runs/*). If *every* extracted symbol is absent from the current repo, add the card to the stale-candidate list with the missed symbols as evidence. If a card has zero extractable symbols, skip it (unflaggable — leave active). Do NOT modify FLASHCARD.md. Output belongs in the report under "Flashcard audit"; a human approves the batch and applies markers separately.
38
- 6. **Synthesize** the report in this structure:
39
-
40
- ```markdown
41
- # SPRINT-<NN> Report: <Sprint Title>
42
-
43
- **Status:** ✅ Shipped | ⚠ Partial | ❌ Blocked
44
- **Window:** YYYY-MM-DD → YYYY-MM-DD (N calendar days, M active dev hours)
45
- **Stories:** N planned / M shipped / K carried over
46
-
47
- ---
23
+ 1. **Read flashcards first.** `Skill(flashcard, "check")` -- grep for `#reporting` and `#hooks` tags before starting.
48
24
 
49
- ## For Product Management
25
+ 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.
27
+ - **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
+ - **Source 3 (tertiary): task-notification** -- if task-notification totals are available (e.g. from orchestrator notes), record them; otherwise mark as `N/A`.
29
+ - **Divergence flag:** if any two sources diverge by >20%, flag it in §3 AND in §5 Tooling as a Red Friction finding.
30
+ - Compute per-agent_type totals, per-story_id totals, agent invocation counts, wall time (first to last ledger row per story), rough USD cost (apply current model rates; note the rate date).
50
31
 
51
- ### Sprint goal did we hit it?
52
- One paragraph. The goal verbatim from the sprint file, followed by the plain-English answer.
32
+ 3. **Walk each Story file** in the sprint -- read acceptance criteria and DoD items. Note which stories reached `Done`, `Escalated`, or `Parking Lot`.
53
33
 
54
- ### Headline deliverables
55
- - One bullet per user-facing capability (not per story). Group stories under their business outcome.
34
+ 4. **Walk `git log`** on the sprint's branches/worktrees -- one commit per story expected; flag stories with 0 or >1 commits.
56
35
 
57
- ### Risks that materialized
58
- From the sprint's risk table — which mitigations fired, which were unused, what surprised us.
36
+ 5. **Diff flashcards** -- count flashcards added during the sprint window (compare dates against sprint start); extract top themes by tag.
59
37
 
60
- ### Cost envelope
61
- One line: "~$X across N agent invocations (M tokens cached, saving ~$Y vs. cold)."
62
-
63
- ### What's unblocked for next sprint
64
- Bullet list tying this sprint's outputs to downstream dependencies.
38
+ 5b. **Flashcard audit (stale-detection pass).** For each card in `.cleargate/FLASHCARD.md` without a status marker (`[S]` or `[R]` -- see flashcard SKILL.md Rule 7), extract concrete referenced symbols from the lesson body:
39
+ - file paths (regex: `\S+\.(ts|md|sh|py|sql|json|yaml|toml)`)
40
+ - identifier candidates (CamelCase 4+ chars OR `snake_case_with_2+_underscores`)
41
+ - CLI flags (regex: `--[a-z][a-z0-9-]+`)
42
+ - env-var candidates (regex: `[A-Z][A-Z0-9_]{3,}`)
43
+ For each extracted symbol, `Grep` the repo (excluding `.cleargate/FLASHCARD.md` itself and sprint-runs/*). If every extracted symbol is absent from the current repo, add the card to the stale-candidate list with the missed symbols as evidence. If a card has zero extractable symbols, skip it. Do NOT modify FLASHCARD.md. Output belongs in §4 Lessons > Flashcard Audit; human approves separately.
65
44
 
66
- ---
45
+ 6. **Synthesize** the report using the v2 template structure (§§1-6 in order):
67
46
 
68
- ## For Developers
47
+ §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
49
+ (CR:bug | CR:spec-clarification | CR:scope-change | CR:approach-change; UR:review-feedback | UR:bug).
50
+ §3 Execution Metrics: full table including Bug-Fix Tax, Enhancement Tax, first-pass success rate,
51
+ and three-source token reconciliation with divergence flag.
52
+ §4 Lessons: new flashcards table + stale-candidate audit table (from step 5b) + supersede candidates.
53
+ §5 Framework Self-Assessment: five subsections (Templates/Handoffs/Skills/Process/Tooling),
54
+ each as a rating table (Green/Yellow/Red). If §3 divergence flag = YES, Tooling shows Red.
55
+ §6 Change Log: append-only table; initial row = generation timestamp.
69
56
 
70
- ### Per-story walkthrough
71
- For each shipped story, one compact block:
57
+ Required frontmatter: sprint_id, status, generated_at, generated_by, template_version: 1.
72
58
 
73
- **STORY-NNN-NN: Title** · L<complexity> · <cost_usd> · <wall_time>
74
- - Files: `path/a.ts`, `path/b.ts`
75
- - Tests added: N (covering M Gherkin scenarios)
76
- - Kickbacks: 0 (one-shot) | 1 (reason: …) | 2 (reasons: …)
77
- - Deviations from plan: none | <describe>
78
- - Flashcards recorded: [#tag] brief
79
- - Commit: <sha>
59
+ 7. **Record a flashcard** on any reporting-specific friction encountered. `Skill(flashcard, "record: #reporting <lesson>")`.
80
60
 
81
- ### Agent efficiency breakdown
82
- | Role | Invocations | Tokens | Cost | Tokens/story | Notes |
83
- |---|---|---|---|---|---|
84
- | Architect | | | | | |
85
- | Developer | | | | | |
86
- | QA | | | | | |
87
- | Reporter | | | | | — this report |
61
+ ## v2-adoption note
62
+ This reporter spec was adopted in SPRINT-09 (STORY-013-07) as the Sprint Report v2 rollout.
63
+ Per sprint DoD line 119 dogfood check: this note confirms the v2 template is active.
88
64
 
89
- ### What the loop got right
90
- 3-5 bullets — based on flashcards + kickback rate + plan-adherence rate.
65
+ ## Fallback: Write-blocked Environment (STORY-014-10)
91
66
 
92
- ### What the loop got wrong
93
- 3-5 bullets — blockers, repeated mistakes, plan misses, QA kickback patterns. Each bullet points at a **concrete loop improvement** (flashcard, agent-definition tweak, hook adjustment, sprint-plan template change).
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:
94
68
 
95
- ### Flashcard audit
96
- Candidates for `[S]` (stale) marker — referenced symbols no longer present in the repo. Human approves the batch before markers are applied.
69
+ 1. **Return the full REPORT.md body on stdout**, wrapped between unambiguous delimiters:
97
70
 
98
- | Card (date · lead-tag · lesson head) | Evidence (symbols not found) | Proposed marker |
99
- |---|---|---|
100
- | 2026-02-15 · #tsup · tsup single-bundle... | `tsup.config.ts`, `--bundle` | `[S]` |
71
+ ```
72
+ ===REPORT-BEGIN===
73
+ # Sprint Report <sprint-id>
74
+ ...
75
+ ===REPORT-END===
76
+ ```
101
77
 
102
- If zero candidates: state "No stale flashcards detected." If there are candidate supersede pairs (a newer card whose lesson directly contradicts an older card's advice), list them under a "Supersede candidates" sub-table with the proposed `[R] → superseded-by <short-ref>` marker for the older card.
78
+ 2. **The orchestrator is responsible for stripping those two delimiter lines** before piping.
103
79
 
104
- ### Open follow-ups
105
- Things deliberately deferred, with target sprint.
80
+ 3. **The orchestrator pipes the raw body** (no delimiters) to:
106
81
 
107
- ---
82
+ ```bash
83
+ node .cleargate/scripts/close_sprint.mjs <sprint-id> --report-body-stdin < report-body.md
84
+ ```
108
85
 
109
- ## Meta
86
+ `--report-body-stdin` **replaces** the Step-4 gate (it implies ack). The script:
87
+ - refuses empty stdin (`empty report body — refusing to write`)
88
+ - refuses a pre-existing `REPORT.md` (`delete it or skip stdin mode`)
89
+ - atomic-writes via tmp+rename
90
+ - falls through to Step 5 (sprint_status flip) + Step 6 (suggest_improvements)
110
91
 
111
- **Token ledger:** `.cleargate/sprint-runs/<sprint-id>/token-ledger.jsonl` (N rows)
112
- **Flashcards added:** N (see `.cleargate/FLASHCARD.md`)
113
- **Model rates used:** <date>
114
- **Report generated:** <timestamp> by Reporter agent
115
- ```
92
+ 4. The fallback is additive to the primary path — `Write` remains on the `tools:` line. Do not remove it.
116
93
 
117
- 7. **Record a flashcard** on any reporting-specific friction. `Skill(flashcard, "record: #reporting <lesson>")`.
94
+ ## Reporter Rewrite Fallback Plan (R8)
95
+ If SPRINT-09 Reporter regresses post-swap of this reporter.md, rollback path:
96
+ `git revert` the M2 commit range. The SPRINT-08-shaped fixture at
97
+ `.cleargate/sprint-runs/S-09/fixtures/sprint-08-shaped/` was used to validate this
98
+ spec before atomic swap.
118
99
 
119
100
  ## Guardrails
120
- - **Numbers before narrative.** Every claim in the PM section must be backed by a ledger row, a commit, or a flashcard entry cite them.
121
- - **Do not fabricate cost.** If you can't find current model rates, state the rate date you used and mark cost `~$X (rates as of <date>)`.
122
- - **Do not summarize the sprint file.** Assume the reader already read it. Add information; don't restate.
123
- - **One report. One file. Do not create drafts.** If you're uncertain, emit what you have and flag the uncertainty inline.
124
- - **Length ceiling: 600 lines.** A longer report won't be read.
101
+ - **Numbers before narrative.** Every claim in §1 must be backed by a ledger row, commit, or flashcard -- cite them.
102
+ - **Do not fabricate cost.** If you cannot find current model rates, state the rate date and mark cost `~$X (rates as of <date>)`.
103
+ - **Do not summarize the sprint file.** Assume the reader already read it. Add information; do not restate.
104
+ - **One report. One file. Do not create drafts.** If uncertain, emit what you have and flag inline.
105
+ - **Length ceiling: 600 lines.** A longer report will not be read.
106
+ - **All six sections required.** §§1-6 must all be present with non-empty content. A missing section is a hard failure.
125
107
 
126
108
  ## What you are NOT
127
- - Not a PM you inform decisions, you don't make them.
128
- - Not a Developer you don't prescribe fixes.
129
- - Not a Cheerleader if the sprint went badly, say so plainly. The loop improves from honesty.
109
+ - Not a PM -- you inform decisions, you do not make them.
110
+ - Not a Developer -- you do not prescribe fixes.
111
+ - Not a Cheerleader -- if the sprint went badly, say so plainly. The loop improves from honesty.
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env bash
2
+ # PreToolUse hook for Task (Agent subagent dispatch).
3
+ #
4
+ # Purpose: when the orchestrator spawns a subagent via the Task tool, record the
5
+ # dispatch metadata (agent_type, work_item_id, turn_index) into a sentinel file
6
+ # under the active sprint dir. The SubagentStop hook reads the newest sentinel
7
+ # to attribute the token-ledger row correctly.
8
+ #
9
+ # Why: SubagentStop fires on the ORCHESTRATOR's session with the orchestrator's
10
+ # transcript_path. Without a sentinel, the hook can only grep the full
11
+ # transcript and every row tags against the orchestrator — per-story cost is
12
+ # uncomputable. The sentinel provides (a) ground-truth agent_type and
13
+ # work_item_id, and (b) a turn_index pivot so the post-hook can compute the
14
+ # delta instead of the cumulative sum.
15
+ #
16
+ # Input: JSON on stdin from Claude Code with fields:
17
+ # session_id, transcript_path, cwd, hook_event_name, tool_name, tool_input
18
+ # For tool_name == "Task", tool_input has: subagent_type, description, prompt.
19
+ #
20
+ # Output: writes .cleargate/sprint-runs/<sprint-id>/.pending-task-<turn_index>.json
21
+ # with { agent_type, work_item_id, turn_index, started_at }
22
+ #
23
+ # Robustness: under v1 never blocks the tool call (exit 0 always). Under v2,
24
+ # exits non-zero to block Task spawn when unprocessed flashcards exist.
25
+ # Set SKIP_FLASHCARD_GATE=1 to bypass the flashcard gate in both modes.
26
+
27
+ set -u
28
+
29
+ REPO_ROOT="${ORCHESTRATOR_PROJECT_DIR:-${CLAUDE_PROJECT_DIR}}"
30
+ LOG_DIR="${REPO_ROOT}/.cleargate/hook-log"
31
+ mkdir -p "${LOG_DIR}"
32
+ HOOK_LOG="${LOG_DIR}/pending-task-sentinel.log"
33
+ ACTIVE_SENTINEL="${REPO_ROOT}/.cleargate/sprint-runs/.active"
34
+
35
+ # Read stdin once — must happen before the grouped block
36
+ INPUT="$(cat)"
37
+
38
+ # Determine active sprint (needed for flashcard gate and sentinel)
39
+ SPRINT_ID=""
40
+ if [[ -f "${ACTIVE_SENTINEL}" ]]; then
41
+ SPRINT_ID="$(tr -d '[:space:]' < "${ACTIVE_SENTINEL}")"
42
+ fi
43
+ [[ -z "${SPRINT_ID}" ]] && SPRINT_ID="_off-sprint"
44
+ SPRINT_DIR="${REPO_ROOT}/.cleargate/sprint-runs/${SPRINT_ID}"
45
+ mkdir -p "${SPRINT_DIR}"
46
+
47
+ # --- Flashcard gate (STORY-014-03) ---
48
+ # Runs BEFORE the logged block so stderr goes to real process stderr (not log),
49
+ # allowing Claude Code to surface the message to the orchestrator.
50
+ # Bypass: set SKIP_FLASHCARD_GATE=1 in environment.
51
+ TOOL_NAME_EARLY="$(printf '%s' "${INPUT}" | jq -r '.tool_name // empty')"
52
+
53
+ if [[ "${TOOL_NAME_EARLY}" == "Task" && "${SKIP_FLASHCARD_GATE:-0}" != "1" && "${SPRINT_ID}" != "_off-sprint" ]]; then
54
+ # Read execution_mode from state.json
55
+ EXEC_MODE="v1"
56
+ if [[ -f "${SPRINT_DIR}/state.json" ]]; then
57
+ EXEC_MODE="$(jq -r '.execution_mode // "v1"' "${SPRINT_DIR}/state.json" 2>/dev/null)"
58
+ [[ -z "${EXEC_MODE}" || "${EXEC_MODE}" == "null" ]] && EXEC_MODE="v1"
59
+ fi
60
+
61
+ # Collect flagged cards from all STORY-*-dev.md and STORY-*-qa.md in SPRINT_DIR (flat layout).
62
+ UNPROCESSED_CARDS=()
63
+ UNPROCESSED_HASHES=()
64
+
65
+ # Use ls -t (portable) to process report files; portable array accumulation (bash 3.2 safe).
66
+ REPORT_FILES=()
67
+ while IFS= read -r f; do
68
+ REPORT_FILES+=("$f")
69
+ done < <(ls -t "${SPRINT_DIR}"/STORY-*-dev.md "${SPRINT_DIR}"/STORY-*-qa.md 2>/dev/null)
70
+
71
+ for REPORT_FILE in "${REPORT_FILES[@]}"; do
72
+ [[ ! -f "${REPORT_FILE}" ]] && continue
73
+ # Parse flashcards_flagged list. Handles two formats:
74
+ # YAML key (frontmatter): Markdown section heading:
75
+ # flashcards_flagged: [] ## flashcards_flagged
76
+ # flashcards_flagged:
77
+ # - "card text" - "card text"
78
+ # - bare card text - bare card text
79
+ IN_BLOCK=0
80
+ BLOCK_TYPE="" # "yaml" or "md"
81
+ while IFS= read -r line; do
82
+ # YAML inline empty list — no cards in this format
83
+ if [[ "${line}" =~ ^flashcards_flagged:[[:space:]]*\[\] ]]; then
84
+ break
85
+ fi
86
+ # YAML key (block form) — matches "flashcards_flagged:" or "flashcards_flagged: " with nothing after
87
+ if [[ "${line}" =~ ^flashcards_flagged:[[:space:]]*$ ]]; then
88
+ IN_BLOCK=1
89
+ BLOCK_TYPE="yaml"
90
+ continue
91
+ fi
92
+ # Markdown section heading (## flashcards_flagged or ## Flashcards_flagged)
93
+ if [[ "${line}" =~ ^##[[:space:]]+[Ff]lashcards_flagged ]]; then
94
+ IN_BLOCK=1
95
+ BLOCK_TYPE="md"
96
+ continue
97
+ fi
98
+
99
+ if [[ "${IN_BLOCK}" == "1" ]]; then
100
+ # Stop conditions differ by block type
101
+ if [[ "${BLOCK_TYPE}" == "yaml" ]]; then
102
+ # Stop at next top-level YAML key (non-indented, non-list, non-blank line)
103
+ if [[ "${line}" =~ ^[a-zA-Z_] ]]; then
104
+ break
105
+ fi
106
+ elif [[ "${BLOCK_TYPE}" == "md" ]]; then
107
+ # Stop at next markdown heading (any level)
108
+ if [[ "${line}" =~ ^# ]]; then
109
+ break
110
+ fi
111
+ fi
112
+ # Match list items: "- ..." (leading whitespace allowed)
113
+ if [[ "${line}" =~ ^[[:space:]]*-[[:space:]]+(.*) ]]; then
114
+ CARD="${BASH_REMATCH[1]}"
115
+ # Strip surrounding quotes (double or single)
116
+ CARD="${CARD#\"}"
117
+ CARD="${CARD%\"}"
118
+ CARD="${CARD#\'}"
119
+ CARD="${CARD%\'}"
120
+ [[ -z "${CARD}" ]] && continue
121
+ # Compute SHA-1, first 12 chars (portable: shasum -a 1 per flashcard #bash #macos)
122
+ HASH="$(printf '%s' "${CARD}" | shasum -a 1 | cut -c1-12)"
123
+ MARKER="${SPRINT_DIR}/.processed-${HASH}"
124
+ if [[ ! -f "${MARKER}" ]]; then
125
+ UNPROCESSED_CARDS+=("${CARD}")
126
+ UNPROCESSED_HASHES+=("${HASH}")
127
+ fi
128
+ fi
129
+ fi
130
+ done < "${REPORT_FILE}"
131
+ done
132
+
133
+ if [[ "${#UNPROCESSED_CARDS[@]}" -gt 0 ]]; then
134
+ printf '[%s] flashcard-gate: %d unprocessed card(s) found (mode=%s)\n' \
135
+ "$(date -u +%FT%TZ)" "${#UNPROCESSED_CARDS[@]}" "${EXEC_MODE}" >> "${HOOK_LOG}"
136
+ if [[ "${EXEC_MODE}" == "v2" ]]; then
137
+ # Block Task spawn — exit 1 with diagnostic on stderr (real stderr, not log)
138
+ printf 'FLASHCARD GATE BLOCKED: %d unprocessed flashcard(s) must be processed before spawning next Task.\n' \
139
+ "${#UNPROCESSED_CARDS[@]}" >&2
140
+ for i in "${!UNPROCESSED_CARDS[@]}"; do
141
+ CARD="${UNPROCESSED_CARDS[$i]}"
142
+ HASH="${UNPROCESSED_HASHES[$i]}"
143
+ printf ' card: %s\n' "${CARD}" >&2
144
+ printf ' mark processed: touch %s/.processed-%s\n' "${SPRINT_DIR}" "${HASH}" >&2
145
+ done
146
+ exit 1
147
+ else
148
+ # v1: advisory warning only, continue to sentinel write
149
+ printf 'FLASHCARD GATE WARNING (v1 advisory): %d unprocessed flashcard(s).\n' \
150
+ "${#UNPROCESSED_CARDS[@]}" >&2
151
+ for i in "${!UNPROCESSED_CARDS[@]}"; do
152
+ CARD="${UNPROCESSED_CARDS[$i]}"
153
+ HASH="${UNPROCESSED_HASHES[$i]}"
154
+ printf ' card: %s\n' "${CARD}" >&2
155
+ printf ' mark processed: touch %s/.processed-%s\n' "${SPRINT_DIR}" "${HASH}" >&2
156
+ done
157
+ fi
158
+ fi
159
+ fi
160
+ # --- End flashcard gate ---
161
+
162
+ {
163
+ TOOL_NAME="$(printf '%s' "${INPUT}" | jq -r '.tool_name // empty')"
164
+ if [[ "${TOOL_NAME}" != "Task" ]]; then
165
+ # Not a subagent dispatch — no sentinel needed.
166
+ exit 0
167
+ fi
168
+
169
+ TRANSCRIPT_PATH="$(printf '%s' "${INPUT}" | jq -r '.transcript_path // empty')"
170
+ AGENT_TYPE="$(printf '%s' "${INPUT}" | jq -r '.tool_input.subagent_type // "unknown"')"
171
+ PROMPT="$(printf '%s' "${INPUT}" | jq -r '.tool_input.prompt // empty')"
172
+
173
+ # Extract work_item_id from prompt — by convention first line is STORY=NNN-NN
174
+ # or an inline PROPOSAL-NNN / EPIC-NNN / CR-NNN / BUG-NNN reference.
175
+ WORK_ITEM_ID="$(printf '%s' "${PROMPT}" | grep -oE '(STORY|PROPOSAL|EPIC|CR|BUG)[-=]?[0-9]+(-[0-9]+)?' | head -1 | sed 's/=/-/g')"
176
+ [[ -z "${WORK_ITEM_ID}" ]] && WORK_ITEM_ID=""
177
+
178
+ # Compute turn_index: count of assistant turns in the orchestrator transcript so far.
179
+ TURN_INDEX=0
180
+ if [[ -n "${TRANSCRIPT_PATH}" && -f "${TRANSCRIPT_PATH}" ]]; then
181
+ TURN_INDEX="$(jq -cs '[.[] | select(.type == "assistant" and .message.usage)] | length' "${TRANSCRIPT_PATH}" 2>/dev/null)"
182
+ [[ -z "${TURN_INDEX}" || "${TURN_INDEX}" == "null" ]] && TURN_INDEX=0
183
+ fi
184
+
185
+ STARTED_AT="$(date -u +%FT%TZ)"
186
+ SENTINEL_FILE="${SPRINT_DIR}/.pending-task-${TURN_INDEX}.json"
187
+
188
+ # Write the sentinel atomically (tmp + mv).
189
+ TMP="${SENTINEL_FILE}.tmp.$$"
190
+ jq -cn \
191
+ --arg agent "${AGENT_TYPE}" \
192
+ --arg work_item "${WORK_ITEM_ID}" \
193
+ --argjson idx "${TURN_INDEX}" \
194
+ --arg started "${STARTED_AT}" \
195
+ '{agent_type: $agent, work_item_id: $work_item, turn_index: $idx, started_at: $started}' \
196
+ > "${TMP}" 2>/dev/null \
197
+ && mv "${TMP}" "${SENTINEL_FILE}" \
198
+ && printf '[%s] wrote sentinel sprint=%s agent=%s work_item=%s turn=%s\n' \
199
+ "${STARTED_AT}" "${SPRINT_ID}" "${AGENT_TYPE}" "${WORK_ITEM_ID}" "${TURN_INDEX}" \
200
+ >> "${HOOK_LOG}" \
201
+ || printf '[%s] failed to write sentinel %s\n' "${STARTED_AT}" "${SENTINEL_FILE}" >> "${HOOK_LOG}"
202
+ } 2>> "${HOOK_LOG}"
203
+
204
+ exit 0
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ # pre-commit-surface-gate.sh
3
+ set -euo pipefail
4
+ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
5
+ SCRIPT="${REPO_ROOT}/.cleargate/scripts/file_surface_diff.sh"
6
+ if [[ ! -f "${SCRIPT}" ]]; then
7
+ echo "[surface-gate] WARNING: file_surface_diff.sh not found — skipping" >&2
8
+ exit 0
9
+ fi
10
+ exec bash "${SCRIPT}" "$@"