cleargate 0.2.0 → 0.3.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.
- package/LICENSE +21 -0
- package/dist/MANIFEST.json +59 -17
- package/dist/admin-api/index.cjs +88 -1
- package/dist/admin-api/index.cjs.map +1 -1
- package/dist/admin-api/index.d.cts +105 -1
- package/dist/admin-api/index.d.ts +105 -1
- package/dist/admin-api/index.js +77 -1
- package/dist/admin-api/index.js.map +1 -1
- package/dist/bootstrap-root-FGWDICDT.js +130 -0
- package/dist/bootstrap-root-FGWDICDT.js.map +1 -0
- package/dist/chunk-OM4FAEA7.js +184 -0
- package/dist/chunk-OM4FAEA7.js.map +1 -0
- package/dist/cli.cjs +7995 -3984
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4062 -561
- package/dist/cli.js.map +1 -1
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +72 -0
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +45 -3
- package/dist/templates/cleargate-planning/.claude/agents/qa.md +7 -3
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +72 -75
- package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +204 -0
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +10 -0
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +58 -0
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +19 -0
- package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +51 -0
- package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +1 -1
- package/dist/templates/cleargate-planning/.claude/settings.json +11 -0
- package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +31 -12
- package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +2 -1
- package/dist/templates/cleargate-planning/.cleargate/config.example.yml +37 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +407 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +146 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +250 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +57 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +320 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +15 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +38 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +187 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +132 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +307 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +280 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +123 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +110 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +247 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +27 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +261 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +210 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +190 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +327 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +261 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +154 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +111 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +164 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +9 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +9 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +29 -3
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +9 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +9 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +42 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +175 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +29 -0
- package/dist/templates/cleargate-planning/CLAUDE.md +3 -0
- package/dist/templates/cleargate-planning/MANIFEST.json +59 -17
- package/dist/whoami-CX7CXJD5.js +76 -0
- package/dist/whoami-CX7CXJD5.js.map +1 -0
- package/package.json +6 -2
- package/templates/cleargate-planning/.claude/agents/architect.md +72 -0
- package/templates/cleargate-planning/.claude/agents/developer.md +45 -3
- package/templates/cleargate-planning/.claude/agents/qa.md +7 -3
- package/templates/cleargate-planning/.claude/agents/reporter.md +72 -75
- package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +204 -0
- package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +10 -0
- package/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +58 -0
- package/templates/cleargate-planning/.claude/hooks/pre-commit.sh +19 -0
- package/templates/cleargate-planning/.claude/hooks/session-start.sh +51 -0
- package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +1 -1
- package/templates/cleargate-planning/.claude/settings.json +11 -0
- package/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +31 -12
- package/templates/cleargate-planning/.cleargate/FLASHCARD.md +2 -1
- package/templates/cleargate-planning/.cleargate/config.example.yml +37 -0
- package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +407 -0
- package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +146 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +250 -0
- package/templates/cleargate-planning/.cleargate/scripts/constants.mjs +57 -0
- package/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +320 -0
- package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +15 -0
- package/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +38 -0
- package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +187 -0
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +132 -0
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +307 -0
- package/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +280 -0
- package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +123 -0
- package/templates/cleargate-planning/.cleargate/scripts/state.schema.json +110 -0
- package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +247 -0
- package/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +27 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +261 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +210 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +190 -0
- package/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +327 -0
- package/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +261 -0
- package/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +154 -0
- package/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +111 -0
- package/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +164 -0
- package/templates/cleargate-planning/.cleargate/templates/Bug.md +9 -0
- package/templates/cleargate-planning/.cleargate/templates/CR.md +9 -0
- package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +29 -3
- package/templates/cleargate-planning/.cleargate/templates/epic.md +9 -0
- package/templates/cleargate-planning/.cleargate/templates/proposal.md +9 -0
- package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +42 -0
- package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +175 -0
- package/templates/cleargate-planning/.cleargate/templates/story.md +29 -0
- package/templates/cleargate-planning/CLAUDE.md +3 -0
- package/templates/cleargate-planning/MANIFEST.json +59 -17
|
@@ -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
|
-
- `
|
|
28
|
-
- `
|
|
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
|
-
- "`
|
|
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
|
-
- `
|
|
24
|
-
- `
|
|
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
|
-
- "
|
|
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
|
|
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,107 +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`.
|
|
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. `
|
|
15
|
-
- Path to the sprint file (e.g. `.cleargate/delivery/archive/SPRINT-
|
|
16
|
-
- Path to the token ledger (e.g. `.cleargate/sprint-runs/
|
|
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")`
|
|
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
|
-
6. **Synthesize** the report in this structure:
|
|
33
|
-
|
|
34
|
-
```markdown
|
|
35
|
-
# SPRINT-<NN> Report: <Sprint Title>
|
|
36
|
-
|
|
37
|
-
**Status:** ✅ Shipped | ⚠ Partial | ❌ Blocked
|
|
38
|
-
**Window:** YYYY-MM-DD → YYYY-MM-DD (N calendar days, M active dev hours)
|
|
39
|
-
**Stories:** N planned / M shipped / K carried over
|
|
23
|
+
1. **Read flashcards first.** `Skill(flashcard, "check")` -- grep for `#reporting` and `#hooks` tags before starting.
|
|
40
24
|
|
|
41
|
-
|
|
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).
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
3. **Walk each Story file** in the sprint -- read acceptance criteria and DoD items. Note which stories reached `Done`, `Escalated`, or `Parking Lot`.
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
One paragraph. The goal verbatim from the sprint file, followed by the plain-English answer.
|
|
34
|
+
4. **Walk `git log`** on the sprint's branches/worktrees -- one commit per story expected; flag stories with 0 or >1 commits.
|
|
47
35
|
|
|
48
|
-
|
|
49
|
-
- One bullet per user-facing capability (not per story). Group stories under their business outcome.
|
|
36
|
+
5. **Diff flashcards** -- count flashcards added during the sprint window (compare dates against sprint start); extract top themes by tag.
|
|
50
37
|
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
One line: "~$X across N agent invocations (M tokens cached, saving ~$Y vs. cold)."
|
|
45
|
+
6. **Synthesize** the report using the v2 template structure (§§1-6 in order):
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
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.
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
Required frontmatter: sprint_id, status, generated_at, generated_by, template_version: 1.
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
7. **Record a flashcard** on any reporting-specific friction encountered. `Skill(flashcard, "record: #reporting <lesson>")`.
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
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.
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
- Files: `path/a.ts`, `path/b.ts`
|
|
69
|
-
- Tests added: N (covering M Gherkin scenarios)
|
|
70
|
-
- Kickbacks: 0 (one-shot) | 1 (reason: …) | 2 (reasons: …)
|
|
71
|
-
- Deviations from plan: none | <describe>
|
|
72
|
-
- Flashcards recorded: [#tag] brief
|
|
73
|
-
- Commit: <sha>
|
|
65
|
+
## Fallback: Write-blocked Environment (STORY-014-10)
|
|
74
66
|
|
|
75
|
-
|
|
76
|
-
| Role | Invocations | Tokens | Cost | Tokens/story | Notes |
|
|
77
|
-
|---|---|---|---|---|---|
|
|
78
|
-
| Architect | | | | | |
|
|
79
|
-
| Developer | | | | | |
|
|
80
|
-
| QA | | | | | |
|
|
81
|
-
| Reporter | | | | | — this report |
|
|
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:
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
3-5 bullets — based on flashcards + kickback rate + plan-adherence rate.
|
|
69
|
+
1. **Return the full REPORT.md body on stdout**, wrapped between unambiguous delimiters:
|
|
85
70
|
|
|
86
|
-
|
|
87
|
-
|
|
71
|
+
```
|
|
72
|
+
===REPORT-BEGIN===
|
|
73
|
+
# Sprint Report — <sprint-id>
|
|
74
|
+
...
|
|
75
|
+
===REPORT-END===
|
|
76
|
+
```
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
Things deliberately deferred, with target sprint.
|
|
78
|
+
2. **The orchestrator is responsible for stripping those two delimiter lines** before piping.
|
|
91
79
|
|
|
92
|
-
|
|
80
|
+
3. **The orchestrator pipes the raw body** (no delimiters) to:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node .cleargate/scripts/close_sprint.mjs <sprint-id> --report-body-stdin < report-body.md
|
|
84
|
+
```
|
|
93
85
|
|
|
94
|
-
|
|
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)
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
**Flashcards added:** N (see `.cleargate/FLASHCARD.md`)
|
|
98
|
-
**Model rates used:** <date>
|
|
99
|
-
**Report generated:** <timestamp> by Reporter agent
|
|
100
|
-
```
|
|
92
|
+
4. The fallback is additive to the primary path — `Write` remains on the `tools:` line. Do not remove it.
|
|
101
93
|
|
|
102
|
-
|
|
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.
|
|
103
99
|
|
|
104
100
|
## Guardrails
|
|
105
|
-
- **Numbers before narrative.** Every claim in
|
|
106
|
-
- **Do not fabricate cost.** If you
|
|
107
|
-
- **Do not summarize the sprint file.** Assume the reader already read it. Add information;
|
|
108
|
-
- **One report. One file. Do not create drafts.** If
|
|
109
|
-
- **Length ceiling: 600 lines.** A longer report
|
|
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.
|
|
110
107
|
|
|
111
108
|
## What you are NOT
|
|
112
|
-
- Not a PM
|
|
113
|
-
- Not a Developer
|
|
114
|
-
- Not a Cheerleader
|
|
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}" "$@"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-commit-test-ratchet.sh — STORY-014-04: Pre-existing Test-Failure Ratchet
|
|
3
|
+
#
|
|
4
|
+
# Invoked by .claude/hooks/pre-commit.sh dispatcher (STORY-014-01).
|
|
5
|
+
# Runs test_ratchet.mjs in 'check' mode and blocks commit on regression.
|
|
6
|
+
#
|
|
7
|
+
# Bypass (discouraged): SKIP_TEST_RATCHET=1
|
|
8
|
+
# Timeout: 120s (enough for current cleargate-cli suite ~45s)
|
|
9
|
+
#
|
|
10
|
+
# macOS compatibility: 'timeout' is GNU coreutils; on macOS use 'gtimeout' (brew coreutils).
|
|
11
|
+
# Fallback: if neither is available, run without timeout and print a warning.
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
REPO_ROOT="${CLEARGATE_REPO_ROOT:-$(cd "${SCRIPT_DIR}/../.." && pwd)}"
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# Bypass
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
if [[ "${SKIP_TEST_RATCHET:-0}" == "1" ]]; then
|
|
22
|
+
echo "test-ratchet: SKIP_TEST_RATCHET=1 — bypassing test ratchet check (discouraged)" >&2
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
# Resolve timeout binary (GNU on Linux; gtimeout on macOS via brew)
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
TIMEOUT_CMD=""
|
|
30
|
+
if command -v timeout &>/dev/null; then
|
|
31
|
+
TIMEOUT_CMD="timeout 120"
|
|
32
|
+
elif command -v gtimeout &>/dev/null; then
|
|
33
|
+
TIMEOUT_CMD="gtimeout 120"
|
|
34
|
+
else
|
|
35
|
+
echo "test-ratchet: WARNING — 'timeout' not found; running without 120s guard" >&2
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# ---------------------------------------------------------------------------
|
|
39
|
+
# Run ratchet
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
RATCHET_SCRIPT="${REPO_ROOT}/.cleargate/scripts/test_ratchet.mjs"
|
|
42
|
+
|
|
43
|
+
if [[ ! -f "${RATCHET_SCRIPT}" ]]; then
|
|
44
|
+
echo "test-ratchet: ERROR — ratchet script not found at ${RATCHET_SCRIPT}" >&2
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
export CLEARGATE_REPO_ROOT="${REPO_ROOT}"
|
|
49
|
+
|
|
50
|
+
${TIMEOUT_CMD} node "${RATCHET_SCRIPT}" check
|
|
51
|
+
STATUS=$?
|
|
52
|
+
|
|
53
|
+
if [[ ${STATUS} -eq 124 ]]; then
|
|
54
|
+
echo "test-ratchet: ERROR — ratchet timed out after 120s; commit blocked" >&2
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
exit ${STATUS}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-commit.sh — Dispatcher: chains all pre-commit-*.sh hooks in lexical order.
|
|
3
|
+
#
|
|
4
|
+
# Install: ln -sf ../../.claude/hooks/pre-commit.sh .git/hooks/pre-commit
|
|
5
|
+
#
|
|
6
|
+
# Each pre-commit-*.sh is expected to exit 0 on success or non-zero to block.
|
|
7
|
+
# The dispatcher exits on the first non-zero exit code.
|
|
8
|
+
|
|
9
|
+
set -euo pipefail
|
|
10
|
+
|
|
11
|
+
HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
|
|
13
|
+
for hook in "${HOOK_DIR}"/pre-commit-*.sh; do
|
|
14
|
+
[[ -f "${hook}" ]] || continue
|
|
15
|
+
[[ -x "${hook}" ]] || continue
|
|
16
|
+
bash "${hook}" || exit $?
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
exit 0
|
|
@@ -2,3 +2,54 @@
|
|
|
2
2
|
set -u
|
|
3
3
|
REPO_ROOT="${CLAUDE_PROJECT_DIR}"
|
|
4
4
|
node "${REPO_ROOT}/cleargate-cli/dist/cli.js" doctor --session-start 2>/dev/null || true
|
|
5
|
+
|
|
6
|
+
# ── §14.9 SessionStart sync nudge (STORY-010-08) ─────────────────────────────
|
|
7
|
+
# Daily-throttled: probe remote for updates at most once per 24h.
|
|
8
|
+
# Never auto-pulls or auto-pushes. Exits 0 regardless of outcome.
|
|
9
|
+
MARKER="${REPO_ROOT}/.cleargate/.sync-marker.json"
|
|
10
|
+
NOW_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
11
|
+
NOW_EPOCH=$(date +%s)
|
|
12
|
+
|
|
13
|
+
if [ ! -f "${MARKER}" ]; then
|
|
14
|
+
# First run — write marker with current timestamp and skip MCP call (24h grace).
|
|
15
|
+
mkdir -p "$(dirname "${MARKER}")"
|
|
16
|
+
printf '{"last_check":"%s"}' "${NOW_ISO}" > "${MARKER}"
|
|
17
|
+
else
|
|
18
|
+
# Parse last_check epoch from marker using node (portable, avoids jq dep)
|
|
19
|
+
LAST_CHECK_ISO=$(node -e "try{const m=JSON.parse(require('fs').readFileSync('${MARKER}','utf8'));process.stdout.write(m.last_check||'1970-01-01T00:00:00Z')}catch{process.stdout.write('1970-01-01T00:00:00Z')}" 2>/dev/null || echo "1970-01-01T00:00:00Z")
|
|
20
|
+
LAST_EPOCH=$(node -e "process.stdout.write(String(Math.floor(new Date('${LAST_CHECK_ISO}').getTime()/1000)))" 2>/dev/null || echo "0")
|
|
21
|
+
ELAPSED=$(( NOW_EPOCH - LAST_EPOCH ))
|
|
22
|
+
|
|
23
|
+
if [ "${ELAPSED}" -ge 86400 ]; then
|
|
24
|
+
# ≥24h since last check — run probe (3s timeout, R7 mitigation)
|
|
25
|
+
RESULT_FILE=$(mktemp)
|
|
26
|
+
# Cross-platform 3-second timeout: prefer `timeout` (Linux); fall back to
|
|
27
|
+
# background-process kill (macOS where GNU coreutils may be absent).
|
|
28
|
+
if command -v timeout > /dev/null 2>&1; then
|
|
29
|
+
timeout 3 node "${REPO_ROOT}/cleargate-cli/dist/cli.js" sync --check > "${RESULT_FILE}" 2>/dev/null || true
|
|
30
|
+
else
|
|
31
|
+
node "${REPO_ROOT}/cleargate-cli/dist/cli.js" sync --check > "${RESULT_FILE}" 2>/dev/null &
|
|
32
|
+
_PROBE_PID=$!
|
|
33
|
+
(sleep 3 && kill "${_PROBE_PID}" 2>/dev/null) &
|
|
34
|
+
_KILL_PID=$!
|
|
35
|
+
wait "${_PROBE_PID}" 2>/dev/null || true
|
|
36
|
+
kill "${_KILL_PID}" 2>/dev/null || true
|
|
37
|
+
wait "${_KILL_PID}" 2>/dev/null || true
|
|
38
|
+
fi
|
|
39
|
+
UPDATES=$(node -e "
|
|
40
|
+
try {
|
|
41
|
+
var data = require('fs').readFileSync(process.argv[1], 'utf8').trim();
|
|
42
|
+
var obj = JSON.parse(data);
|
|
43
|
+
process.stdout.write(String(obj.updates || 0));
|
|
44
|
+
} catch(e) {
|
|
45
|
+
process.stdout.write('0');
|
|
46
|
+
}
|
|
47
|
+
" "${RESULT_FILE}" 2>/dev/null || echo "0")
|
|
48
|
+
rm -f "${RESULT_FILE}"
|
|
49
|
+
if [ "${UPDATES}" -gt 0 ] 2>/dev/null; then
|
|
50
|
+
printf '📡 ClearGate: %s remote updates since yesterday — run `cleargate sync` to reconcile.\n' "${UPDATES}"
|
|
51
|
+
fi
|
|
52
|
+
# Marker is updated by sync --check itself; no re-write needed here.
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
exit 0
|