cleargate 0.8.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +190 -0
- package/README.md +11 -0
- package/dist/MANIFEST.json +259 -28
- package/dist/{chunk-OM4FAEA7.js → chunk-Q3BTSXCK.js} +69 -3
- package/dist/chunk-Q3BTSXCK.js.map +1 -0
- package/dist/cli.cjs +2621 -548
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2548 -560
- package/dist/cli.js.map +1 -1
- package/dist/lib/ledger.cjs +120 -0
- package/dist/lib/ledger.cjs.map +1 -0
- package/dist/lib/ledger.d.cts +64 -0
- package/dist/lib/ledger.d.ts +64 -0
- package/dist/lib/ledger.js +96 -0
- package/dist/lib/ledger.js.map +1 -0
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +10 -8
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +29 -2
- package/dist/templates/cleargate-planning/.claude/agents/qa.md +50 -1
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
- package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
- package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
- package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
- package/dist/templates/cleargate-planning/.claude/settings.json +4 -0
- package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
- package/dist/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
- package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
- package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
- package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
- package/dist/templates/cleargate-planning/.cleargate/templates/proposal.md +17 -4
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
- package/dist/templates/cleargate-planning/CLAUDE.md +28 -10
- package/dist/templates/cleargate-planning/MANIFEST.json +259 -28
- package/dist/{whoami-CX7CXJD5.js → whoami-W4U6DPVG.js} +17 -17
- package/dist/whoami-W4U6DPVG.js.map +1 -0
- package/package.json +13 -2
- package/templates/cleargate-planning/.claude/agents/architect.md +10 -8
- package/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +108 -0
- package/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +49 -3
- package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +6 -1
- package/templates/cleargate-planning/.claude/agents/developer.md +29 -2
- package/templates/cleargate-planning/.claude/agents/qa.md +50 -1
- package/templates/cleargate-planning/.claude/agents/reporter.md +31 -9
- package/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +148 -0
- package/templates/cleargate-planning/.claude/hooks/session-start.sh +6 -0
- package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +314 -96
- package/templates/cleargate-planning/.claude/settings.json +4 -0
- package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +473 -0
- package/templates/cleargate-planning/.cleargate/config.example.yml +19 -0
- package/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +542 -0
- package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +102 -428
- package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +31 -0
- package/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +71 -0
- package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +24 -2
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +387 -27
- package/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +219 -0
- package/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +54 -0
- package/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +378 -0
- package/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +888 -0
- package/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +71 -0
- package/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +355 -13
- package/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +20 -20
- package/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +482 -0
- package/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +125 -0
- package/templates/cleargate-planning/.cleargate/templates/Bug.md +24 -1
- package/templates/cleargate-planning/.cleargate/templates/CR.md +32 -1
- package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +48 -14
- package/templates/cleargate-planning/.cleargate/templates/epic.md +37 -3
- package/templates/cleargate-planning/.cleargate/templates/hotfix.md +50 -0
- package/templates/cleargate-planning/.cleargate/templates/initiative.md +98 -29
- package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +23 -4
- package/templates/cleargate-planning/.cleargate/templates/story.md +55 -3
- package/templates/cleargate-planning/CLAUDE.md +28 -10
- package/templates/cleargate-planning/MANIFEST.json +259 -28
- package/dist/chunk-OM4FAEA7.js.map +0 -1
- package/dist/whoami-CX7CXJD5.js.map +0 -1
- package/templates/cleargate-planning/.cleargate/templates/proposal.md +0 -61
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_prep_qa_context.sh
|
|
3
|
+
# Tests for prep_qa_context.mjs — 6 Gherkin scenarios:
|
|
4
|
+
# 1. Happy path — all sections present, size ≤20KB, schema_version:1
|
|
5
|
+
# 2. Missing story file — degrades to one-liner, exit 0
|
|
6
|
+
# 3. Missing baseline cache — baseline_unavailable:true, exit 0
|
|
7
|
+
# 4. Bundle size cap warning — oversized fixture warns stderr, still writes
|
|
8
|
+
# 5. Legacy STATUS=done — format:legacy in JSON, SCHEMA_INCOMPLETE in prose
|
|
9
|
+
# 6. Usage error — no args → exit 2, stderr contains "Usage:"
|
|
10
|
+
#
|
|
11
|
+
# Fixtures use mktemp -d. CLEARGATE_SPRINT_DIR + CLEARGATE_PENDING_SYNC_DIR
|
|
12
|
+
# env overrides for test isolation (FLASHCARD 2026-04-21 #test-harness #scripts #env).
|
|
13
|
+
# macOS bash 3.2 portable: no mapfile/readarray (FLASHCARD 2026-04-21 #bash #macos #portability).
|
|
14
|
+
# Exit 0 = all pass; exit 1 = one or more failures.
|
|
15
|
+
set -uo pipefail
|
|
16
|
+
|
|
17
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
18
|
+
SCRIPTS_DIR="${REPO_ROOT}/.cleargate/scripts"
|
|
19
|
+
PREP_SCRIPT="${SCRIPTS_DIR}/prep_qa_context.mjs"
|
|
20
|
+
|
|
21
|
+
PASS=0
|
|
22
|
+
FAIL=0
|
|
23
|
+
|
|
24
|
+
pass() {
|
|
25
|
+
echo "PASS: $1"
|
|
26
|
+
PASS=$((PASS + 1))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
fail() {
|
|
30
|
+
echo "FAIL: $1"
|
|
31
|
+
echo " detail: $2"
|
|
32
|
+
FAIL=$((FAIL + 1))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
make_tmpdir() {
|
|
36
|
+
mktemp -d
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ── Fixture helpers ────────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
# Write a minimal valid state.json with one Active story
|
|
42
|
+
write_state_json() {
|
|
43
|
+
local dir="$1"
|
|
44
|
+
local story_id="${2:-STORY-AAA-01}"
|
|
45
|
+
local story_state="${3:-Bouncing}"
|
|
46
|
+
local lane="${4:-standard}"
|
|
47
|
+
cat > "${dir}/state.json" << EOF
|
|
48
|
+
{
|
|
49
|
+
"schema_version": 2,
|
|
50
|
+
"sprint_id": "SPRINT-fixture",
|
|
51
|
+
"execution_mode": "v2",
|
|
52
|
+
"sprint_status": "Active",
|
|
53
|
+
"stories": {
|
|
54
|
+
"${story_id}": {
|
|
55
|
+
"state": "${story_state}",
|
|
56
|
+
"qa_bounces": 0,
|
|
57
|
+
"arch_bounces": 0,
|
|
58
|
+
"worktree": null,
|
|
59
|
+
"updated_at": "2026-05-01T00:00:00Z",
|
|
60
|
+
"notes": "",
|
|
61
|
+
"lane": "${lane}",
|
|
62
|
+
"lane_assigned_by": "test",
|
|
63
|
+
"lane_demoted_at": null,
|
|
64
|
+
"lane_demotion_reason": null
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"last_action": "test setup",
|
|
68
|
+
"updated_at": "2026-05-01T00:00:00Z"
|
|
69
|
+
}
|
|
70
|
+
EOF
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Write a minimal milestone plan
|
|
74
|
+
write_milestone_plan() {
|
|
75
|
+
local dir="$1"
|
|
76
|
+
local story_id="${2:-STORY-AAA-01}"
|
|
77
|
+
mkdir -p "${dir}/plans"
|
|
78
|
+
cat > "${dir}/plans/M1.md" << EOF
|
|
79
|
+
# Milestone M1 — test fixture
|
|
80
|
+
|
|
81
|
+
### ${story_id} — test story
|
|
82
|
+
|
|
83
|
+
- This is a test blueprint.
|
|
84
|
+
- Some content here.
|
|
85
|
+
|
|
86
|
+
## Another Section
|
|
87
|
+
EOF
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Write a minimal story file in the pendingsync dir
|
|
91
|
+
write_story_file() {
|
|
92
|
+
local dir="$1"
|
|
93
|
+
local story_id="${2:-STORY-AAA-01}"
|
|
94
|
+
cat > "${dir}/${story_id}_Test_Story.md" << EOF
|
|
95
|
+
---
|
|
96
|
+
story_id: ${story_id}
|
|
97
|
+
status: Approved
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# ${story_id}: Test Story
|
|
101
|
+
|
|
102
|
+
## Gherkin
|
|
103
|
+
|
|
104
|
+
Given a test fixture
|
|
105
|
+
When the script runs
|
|
106
|
+
Then it should pass
|
|
107
|
+
EOF
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Write a .baseline-failures.json file
|
|
111
|
+
write_baseline_failures() {
|
|
112
|
+
local dir="$1"
|
|
113
|
+
cat > "${dir}/.baseline-failures.json" << 'EOF'
|
|
114
|
+
[
|
|
115
|
+
{"file": "cleargate-cli/test/sprint.test.ts", "count": 1}
|
|
116
|
+
]
|
|
117
|
+
EOF
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Create a fake git worktree in tmpdir
|
|
121
|
+
# Sets up .git as a file pointing to a real git repo for git -C to work
|
|
122
|
+
setup_fake_worktree() {
|
|
123
|
+
local worktree_dir="$1"
|
|
124
|
+
local story_id="${2:-STORY-AAA-01}"
|
|
125
|
+
|
|
126
|
+
# Use the real repo but pretend we're checking the story branch
|
|
127
|
+
# The worktree dir has a .git file (linked worktree style)
|
|
128
|
+
# For simplicity, we just create a real git repo with one commit
|
|
129
|
+
|
|
130
|
+
local git_dir="${worktree_dir}/.git_fake_$$"
|
|
131
|
+
mkdir -p "${git_dir}"
|
|
132
|
+
git -C "${worktree_dir}" init --quiet 2>/dev/null || true
|
|
133
|
+
git -C "${worktree_dir}" config user.email "test@test.com" 2>/dev/null || true
|
|
134
|
+
git -C "${worktree_dir}" config user.name "Test" 2>/dev/null || true
|
|
135
|
+
|
|
136
|
+
# Create a dummy file and commit
|
|
137
|
+
echo "test" > "${worktree_dir}/test-fixture.txt"
|
|
138
|
+
git -C "${worktree_dir}" add test-fixture.txt 2>/dev/null || true
|
|
139
|
+
git -C "${worktree_dir}" commit --quiet -m "test(fixture): initial commit
|
|
140
|
+
|
|
141
|
+
STATUS: done
|
|
142
|
+
COMMIT: abc1234
|
|
143
|
+
TYPECHECK: pass
|
|
144
|
+
TESTS: 3 passed, 0 failed
|
|
145
|
+
FILES_CHANGED: test-fixture.txt
|
|
146
|
+
NOTES: test fixture commit" 2>/dev/null || true
|
|
147
|
+
|
|
148
|
+
# Create a 'main' branch alias (local ref) so diff --name-only main..HEAD works
|
|
149
|
+
git -C "${worktree_dir}" branch -f main HEAD 2>/dev/null || true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
# Create worktree with a legacy STATUS=done commit
|
|
153
|
+
setup_legacy_status_worktree() {
|
|
154
|
+
local worktree_dir="$1"
|
|
155
|
+
|
|
156
|
+
git -C "${worktree_dir}" init --quiet 2>/dev/null || true
|
|
157
|
+
git -C "${worktree_dir}" config user.email "test@test.com" 2>/dev/null || true
|
|
158
|
+
git -C "${worktree_dir}" config user.name "Test" 2>/dev/null || true
|
|
159
|
+
|
|
160
|
+
echo "test" > "${worktree_dir}/test-file.txt"
|
|
161
|
+
git -C "${worktree_dir}" add test-file.txt 2>/dev/null || true
|
|
162
|
+
git -C "${worktree_dir}" commit --quiet -m "feat(test): legacy commit
|
|
163
|
+
|
|
164
|
+
STATUS: done
|
|
165
|
+
COMMIT: def5678
|
|
166
|
+
TYPECHECK: pass
|
|
167
|
+
TESTS: 2 passed, 0 failed
|
|
168
|
+
FILES_CHANGED: test-file.txt
|
|
169
|
+
NOTES: legacy format without r_coverage or plan_deviations" 2>/dev/null || true
|
|
170
|
+
|
|
171
|
+
git -C "${worktree_dir}" branch -f main HEAD 2>/dev/null || true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# ─── Scenario 1: Happy path ───────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
run_scenario_1() {
|
|
177
|
+
local tmpdir pendingsync_dir worktree_dir output_path
|
|
178
|
+
tmpdir="$(make_tmpdir)"
|
|
179
|
+
pendingsync_dir="$(make_tmpdir)"
|
|
180
|
+
worktree_dir="$(make_tmpdir)"
|
|
181
|
+
|
|
182
|
+
write_state_json "${tmpdir}" "STORY-AAA-01" "Bouncing" "standard"
|
|
183
|
+
write_milestone_plan "${tmpdir}" "STORY-AAA-01"
|
|
184
|
+
write_story_file "${pendingsync_dir}" "STORY-AAA-01"
|
|
185
|
+
write_baseline_failures "${tmpdir}"
|
|
186
|
+
setup_fake_worktree "${worktree_dir}" "STORY-AAA-01"
|
|
187
|
+
|
|
188
|
+
output_path="${tmpdir}/.qa-context-STORY-AAA-01.md"
|
|
189
|
+
|
|
190
|
+
local exit_code stderr_out
|
|
191
|
+
stderr_out=$(CLEARGATE_SPRINT_DIR="${tmpdir}" CLEARGATE_PENDING_SYNC_DIR="${pendingsync_dir}" \
|
|
192
|
+
node "${PREP_SCRIPT}" STORY-AAA-01 "${worktree_dir}" --output "${output_path}" 2>&1 >/dev/null)
|
|
193
|
+
exit_code=$?
|
|
194
|
+
|
|
195
|
+
if [ "${exit_code}" -ne 0 ]; then
|
|
196
|
+
fail "Scenario 1: happy path — expected exit 0, got ${exit_code}" "${stderr_out}"
|
|
197
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [ ! -f "${output_path}" ]; then
|
|
201
|
+
fail "Scenario 1: happy path — output file not written at ${output_path}" "file missing"
|
|
202
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
# Check size ≤20KB (20480 bytes)
|
|
206
|
+
local bundle_size
|
|
207
|
+
bundle_size=$(wc -c < "${output_path}" | tr -d ' ')
|
|
208
|
+
if [ "${bundle_size}" -gt 20480 ]; then
|
|
209
|
+
fail "Scenario 1: happy path — bundle size ${bundle_size} bytes exceeds 20KB" "too large"
|
|
210
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Check all 8 section headers
|
|
214
|
+
local section
|
|
215
|
+
for section in "Worktree + Commit" "Spec Sources" "Baseline" "Adjacent Files" "Cross-Story Map" "Flashcard Slice" "Lane" "Dev Handoff"; do
|
|
216
|
+
if ! grep -qF "${section}" "${output_path}"; then
|
|
217
|
+
fail "Scenario 1: happy path — missing section '${section}' in bundle" "$(head -30 "${output_path}")"
|
|
218
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
219
|
+
fi
|
|
220
|
+
done
|
|
221
|
+
|
|
222
|
+
# Check JSON block parses to schema_version: 1
|
|
223
|
+
local schema_version
|
|
224
|
+
schema_version=$(node -e "
|
|
225
|
+
const fs = require('fs');
|
|
226
|
+
const content = fs.readFileSync('${output_path}', 'utf8');
|
|
227
|
+
const m = content.match(/\`\`\`json\\n([\\s\\S]*?)\\n\`\`\`/);
|
|
228
|
+
if (!m) { process.stdout.write('NO_JSON\\n'); process.exit(0); }
|
|
229
|
+
const obj = JSON.parse(m[1]);
|
|
230
|
+
process.stdout.write(String(obj.schema_version) + '\\n');
|
|
231
|
+
" 2>/dev/null)
|
|
232
|
+
|
|
233
|
+
if [ "${schema_version}" != "1" ]; then
|
|
234
|
+
fail "Scenario 1: happy path — expected schema_version 1, got '${schema_version}'" "json parse issue"
|
|
235
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
pass "Scenario 1: prep_qa_context happy path — all 8 sections, ≤20KB, schema_version:1"
|
|
239
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# ─── Scenario 2: Missing story file → one-liner ───────────────────────────────
|
|
243
|
+
|
|
244
|
+
run_scenario_2() {
|
|
245
|
+
local tmpdir pendingsync_dir worktree_dir output_path
|
|
246
|
+
tmpdir="$(make_tmpdir)"
|
|
247
|
+
pendingsync_dir="$(make_tmpdir)"
|
|
248
|
+
worktree_dir="$(make_tmpdir)"
|
|
249
|
+
|
|
250
|
+
write_state_json "${tmpdir}" "STORY-AAA-01" "Bouncing" "standard"
|
|
251
|
+
write_milestone_plan "${tmpdir}" "STORY-AAA-01"
|
|
252
|
+
# Deliberately NO story file in pendingsync_dir
|
|
253
|
+
write_baseline_failures "${tmpdir}"
|
|
254
|
+
setup_fake_worktree "${worktree_dir}" "STORY-AAA-01"
|
|
255
|
+
|
|
256
|
+
output_path="${tmpdir}/.qa-context-STORY-AAA-01.md"
|
|
257
|
+
|
|
258
|
+
local exit_code
|
|
259
|
+
CLEARGATE_SPRINT_DIR="${tmpdir}" CLEARGATE_PENDING_SYNC_DIR="${pendingsync_dir}" \
|
|
260
|
+
node "${PREP_SCRIPT}" STORY-AAA-01 "${worktree_dir}" --output "${output_path}" >/dev/null 2>&1
|
|
261
|
+
exit_code=$?
|
|
262
|
+
|
|
263
|
+
if [ "${exit_code}" -ne 0 ]; then
|
|
264
|
+
fail "Scenario 2: missing story — expected exit 0, got ${exit_code}" "exit code mismatch"
|
|
265
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
266
|
+
fi
|
|
267
|
+
|
|
268
|
+
if [ ! -f "${output_path}" ]; then
|
|
269
|
+
fail "Scenario 2: missing story — output file not written" "file missing"
|
|
270
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# Spec Sources section must contain the story-not-found one-liner
|
|
274
|
+
if ! grep -qF "Story file not found" "${output_path}"; then
|
|
275
|
+
fail "Scenario 2: missing story — expected 'Story file not found' one-liner in Spec Sources" \
|
|
276
|
+
"$(grep -A5 'Spec Sources' "${output_path}" | head -10)"
|
|
277
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
# Other sections still present (not impacted)
|
|
281
|
+
if ! grep -qF "## Baseline" "${output_path}"; then
|
|
282
|
+
fail "Scenario 2: missing story — Baseline section missing (other sections impacted)" "missing section"
|
|
283
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
pass "Scenario 2: missing story file degrades to one-liner, other sections unaffected"
|
|
287
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
# ─── Scenario 3: Missing baseline cache → baseline_unavailable:true ──────────
|
|
291
|
+
|
|
292
|
+
run_scenario_3() {
|
|
293
|
+
local tmpdir pendingsync_dir worktree_dir output_path
|
|
294
|
+
tmpdir="$(make_tmpdir)"
|
|
295
|
+
pendingsync_dir="$(make_tmpdir)"
|
|
296
|
+
worktree_dir="$(make_tmpdir)"
|
|
297
|
+
|
|
298
|
+
write_state_json "${tmpdir}" "STORY-AAA-01" "Bouncing" "standard"
|
|
299
|
+
write_milestone_plan "${tmpdir}" "STORY-AAA-01"
|
|
300
|
+
write_story_file "${pendingsync_dir}" "STORY-AAA-01"
|
|
301
|
+
# Deliberately NO .baseline-failures.json
|
|
302
|
+
setup_fake_worktree "${worktree_dir}" "STORY-AAA-01"
|
|
303
|
+
|
|
304
|
+
output_path="${tmpdir}/.qa-context-STORY-AAA-01.md"
|
|
305
|
+
|
|
306
|
+
local exit_code
|
|
307
|
+
CLEARGATE_SPRINT_DIR="${tmpdir}" CLEARGATE_PENDING_SYNC_DIR="${pendingsync_dir}" \
|
|
308
|
+
node "${PREP_SCRIPT}" STORY-AAA-01 "${worktree_dir}" --output "${output_path}" >/dev/null 2>&1
|
|
309
|
+
exit_code=$?
|
|
310
|
+
|
|
311
|
+
if [ "${exit_code}" -ne 0 ]; then
|
|
312
|
+
fail "Scenario 3: missing baseline — expected exit 0, got ${exit_code}" "exit code mismatch"
|
|
313
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# Check JSON baseline_unavailable === true
|
|
317
|
+
local baseline_unavailable
|
|
318
|
+
baseline_unavailable=$(node -e "
|
|
319
|
+
const fs = require('fs');
|
|
320
|
+
const content = fs.readFileSync('${output_path}', 'utf8');
|
|
321
|
+
const m = content.match(/\`\`\`json\\n([\\s\\S]*?)\\n\`\`\`/);
|
|
322
|
+
if (!m) { process.stdout.write('NO_JSON\\n'); process.exit(0); }
|
|
323
|
+
const obj = JSON.parse(m[1]);
|
|
324
|
+
process.stdout.write(String(obj.baseline.baseline_unavailable) + '\\n');
|
|
325
|
+
" 2>/dev/null)
|
|
326
|
+
|
|
327
|
+
if [ "${baseline_unavailable}" != "true" ]; then
|
|
328
|
+
fail "Scenario 3: missing baseline — expected baseline_unavailable=true, got '${baseline_unavailable}'" "json check"
|
|
329
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
# Prose should contain recompute one-liner
|
|
333
|
+
if ! grep -q "cleargate gate test" "${output_path}"; then
|
|
334
|
+
fail "Scenario 3: missing baseline — expected recompute one-liner in Baseline section" \
|
|
335
|
+
"$(grep -A5 '## Baseline' "${output_path}" | head -10)"
|
|
336
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
pass "Scenario 3: missing baseline cache → baseline_unavailable:true, recompute one-liner present"
|
|
340
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# ─── Scenario 4: Bundle size cap warning ─────────────────────────────────────
|
|
344
|
+
|
|
345
|
+
run_scenario_4() {
|
|
346
|
+
local tmpdir pendingsync_dir worktree_dir output_path
|
|
347
|
+
tmpdir="$(make_tmpdir)"
|
|
348
|
+
pendingsync_dir="$(make_tmpdir)"
|
|
349
|
+
worktree_dir="$(make_tmpdir)"
|
|
350
|
+
|
|
351
|
+
write_state_json "${tmpdir}" "STORY-AAA-01" "Bouncing" "standard"
|
|
352
|
+
write_milestone_plan "${tmpdir}" "STORY-AAA-01"
|
|
353
|
+
write_story_file "${pendingsync_dir}" "STORY-AAA-01"
|
|
354
|
+
|
|
355
|
+
# Create an oversized fixture: write a very large baseline-failures.json
|
|
356
|
+
# with 500+ entries to bloat the JSON block past 20KB
|
|
357
|
+
local big_json="${tmpdir}/.baseline-failures.json"
|
|
358
|
+
printf '[' > "${big_json}"
|
|
359
|
+
local i=0
|
|
360
|
+
while [ $i -lt 400 ]; do
|
|
361
|
+
if [ $i -gt 0 ]; then printf ',' >> "${big_json}"; fi
|
|
362
|
+
printf '{"file":"cleargate-cli/test/very/long/path/to/some/test/file/number_%d_with_extra_padding_so_it_is_bigger/test.spec.ts","count":%d}' $i $i >> "${big_json}"
|
|
363
|
+
i=$((i + 1))
|
|
364
|
+
done
|
|
365
|
+
printf ']' >> "${big_json}"
|
|
366
|
+
|
|
367
|
+
setup_fake_worktree "${worktree_dir}" "STORY-AAA-01"
|
|
368
|
+
|
|
369
|
+
output_path="${tmpdir}/.qa-context-STORY-AAA-01.md"
|
|
370
|
+
|
|
371
|
+
local exit_code stderr_out
|
|
372
|
+
stderr_out=$(CLEARGATE_SPRINT_DIR="${tmpdir}" CLEARGATE_PENDING_SYNC_DIR="${pendingsync_dir}" \
|
|
373
|
+
node "${PREP_SCRIPT}" STORY-AAA-01 "${worktree_dir}" --output "${output_path}" 2>&1 >/dev/null)
|
|
374
|
+
exit_code=$?
|
|
375
|
+
|
|
376
|
+
# Must still exit 0 (R4: write anyway)
|
|
377
|
+
if [ "${exit_code}" -ne 0 ]; then
|
|
378
|
+
fail "Scenario 4: size cap — expected exit 0 even when oversized, got ${exit_code}" "${stderr_out}"
|
|
379
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# Bundle must still be written
|
|
383
|
+
if [ ! -f "${output_path}" ]; then
|
|
384
|
+
fail "Scenario 4: size cap — bundle not written even when oversized" "file missing"
|
|
385
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
# Stderr must contain warning about exceeding 20KB
|
|
389
|
+
if ! echo "${stderr_out}" | grep -qi "exceeds 20KB"; then
|
|
390
|
+
fail "Scenario 4: size cap — expected stderr warning about 20KB target" "stderr: ${stderr_out}"
|
|
391
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
392
|
+
fi
|
|
393
|
+
|
|
394
|
+
pass "Scenario 4: oversized fixture → exit 0, bundle written, stderr warns about 20KB"
|
|
395
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# ─── Scenario 5: Legacy STATUS=done → format:legacy ──────────────────────────
|
|
399
|
+
|
|
400
|
+
run_scenario_5() {
|
|
401
|
+
local tmpdir pendingsync_dir worktree_dir output_path
|
|
402
|
+
tmpdir="$(make_tmpdir)"
|
|
403
|
+
pendingsync_dir="$(make_tmpdir)"
|
|
404
|
+
worktree_dir="$(make_tmpdir)"
|
|
405
|
+
|
|
406
|
+
write_state_json "${tmpdir}" "STORY-AAA-01" "Bouncing" "standard"
|
|
407
|
+
write_milestone_plan "${tmpdir}" "STORY-AAA-01"
|
|
408
|
+
write_story_file "${pendingsync_dir}" "STORY-AAA-01"
|
|
409
|
+
setup_legacy_status_worktree "${worktree_dir}"
|
|
410
|
+
|
|
411
|
+
output_path="${tmpdir}/.qa-context-STORY-AAA-01.md"
|
|
412
|
+
|
|
413
|
+
local exit_code
|
|
414
|
+
CLEARGATE_SPRINT_DIR="${tmpdir}" CLEARGATE_PENDING_SYNC_DIR="${pendingsync_dir}" \
|
|
415
|
+
node "${PREP_SCRIPT}" STORY-AAA-01 "${worktree_dir}" --output "${output_path}" >/dev/null 2>&1
|
|
416
|
+
exit_code=$?
|
|
417
|
+
|
|
418
|
+
if [ "${exit_code}" -ne 0 ]; then
|
|
419
|
+
fail "Scenario 5: legacy handoff — expected exit 0, got ${exit_code}" "exit code mismatch"
|
|
420
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
# Check JSON dev_handoff.format === "legacy"
|
|
424
|
+
local handoff_format
|
|
425
|
+
handoff_format=$(node -e "
|
|
426
|
+
const fs = require('fs');
|
|
427
|
+
const content = fs.readFileSync('${output_path}', 'utf8');
|
|
428
|
+
const m = content.match(/\`\`\`json\\n([\\s\\S]*?)\\n\`\`\`/);
|
|
429
|
+
if (!m) { process.stdout.write('NO_JSON\\n'); process.exit(0); }
|
|
430
|
+
const obj = JSON.parse(m[1]);
|
|
431
|
+
process.stdout.write(String(obj.dev_handoff.format) + '\\n');
|
|
432
|
+
" 2>/dev/null)
|
|
433
|
+
|
|
434
|
+
if [ "${handoff_format}" != "legacy" ]; then
|
|
435
|
+
fail "Scenario 5: legacy handoff — expected dev_handoff.format=legacy, got '${handoff_format}'" "json check"
|
|
436
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
437
|
+
fi
|
|
438
|
+
|
|
439
|
+
# Prose must contain SCHEMA_INCOMPLETE warning
|
|
440
|
+
if ! grep -qF "SCHEMA_INCOMPLETE" "${output_path}"; then
|
|
441
|
+
fail "Scenario 5: legacy handoff — expected SCHEMA_INCOMPLETE in Dev Handoff section" \
|
|
442
|
+
"$(grep -A5 '## Dev Handoff' "${output_path}" | head -10)"
|
|
443
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"; return
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
pass "Scenario 5: legacy STATUS=done → format:legacy in JSON, SCHEMA_INCOMPLETE in prose"
|
|
447
|
+
rm -rf "${tmpdir}" "${pendingsync_dir}" "${worktree_dir}"
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
# ─── Scenario 6: Usage error — no args ────────────────────────────────────────
|
|
451
|
+
|
|
452
|
+
run_scenario_6() {
|
|
453
|
+
local exit_code stderr_out
|
|
454
|
+
|
|
455
|
+
stderr_out=$(node "${PREP_SCRIPT}" 2>&1 >/dev/null)
|
|
456
|
+
exit_code=$?
|
|
457
|
+
|
|
458
|
+
if [ "${exit_code}" -ne 2 ]; then
|
|
459
|
+
fail "Scenario 6: usage error — expected exit 2, got ${exit_code}" "exit code mismatch"
|
|
460
|
+
return
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
if ! echo "${stderr_out}" | grep -qi "Usage:"; then
|
|
464
|
+
fail "Scenario 6: usage error — stderr must contain 'Usage:'" "stderr: ${stderr_out}"
|
|
465
|
+
return
|
|
466
|
+
fi
|
|
467
|
+
|
|
468
|
+
pass "Scenario 6: no args → exit 2, stderr contains 'Usage:'"
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
# ─── Run all scenarios ────────────────────────────────────────────────────────
|
|
472
|
+
|
|
473
|
+
run_scenario_1
|
|
474
|
+
run_scenario_2
|
|
475
|
+
run_scenario_3
|
|
476
|
+
run_scenario_4
|
|
477
|
+
run_scenario_5
|
|
478
|
+
run_scenario_6
|
|
479
|
+
|
|
480
|
+
echo ""
|
|
481
|
+
echo "Results: ${PASS} passed, ${FAIL} failed"
|
|
482
|
+
[ "${FAIL}" -eq 0 ] && exit 0 || exit 1
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# write_dispatch.sh — write a dispatch marker JSON file before each Task() spawn.
|
|
3
|
+
#
|
|
4
|
+
# The token-ledger.sh SubagentStop hook reads this file (keyed by session_id) to
|
|
5
|
+
# get explicit attribution (work_item_id + agent_type) rather than relying on
|
|
6
|
+
# transcript-grep heuristics.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash .cleargate/scripts/write_dispatch.sh <work_item_id> <agent_type>
|
|
10
|
+
#
|
|
11
|
+
# FALLBACK PATH (CR-026): The primary dispatch-marker path is the PreToolUse:Task hook
|
|
12
|
+
# at `.claude/hooks/pre-tool-use-task.sh`, which auto-writes the marker on every Task()
|
|
13
|
+
# spawn without manual orchestrator intervention. This script is retained for one-off
|
|
14
|
+
# Architect dispatches or spawns whose Task() prompt does not contain a parseable
|
|
15
|
+
# work-item marker. Use it only when the PreToolUse:Task hook cannot determine the
|
|
16
|
+
# work_item_id from the prompt (e.g., a generic Architect spawn not tied to a sprint item).
|
|
17
|
+
#
|
|
18
|
+
# Args:
|
|
19
|
+
# $1 work_item_id — e.g. STORY-020-02, CR-016, BUG-021
|
|
20
|
+
# $2 agent_type — one of: developer|architect|qa|reporter|cleargate-wiki-contradict
|
|
21
|
+
#
|
|
22
|
+
# Env (optional):
|
|
23
|
+
# CLAUDE_SESSION_ID — session UUID of the orchestrator session
|
|
24
|
+
# ORCHESTRATOR_PROJECT_DIR — override for repo root (cross-project routing)
|
|
25
|
+
#
|
|
26
|
+
# Exit codes:
|
|
27
|
+
# 0 success
|
|
28
|
+
# 1 missing required args
|
|
29
|
+
# 2 no .active sprint sentinel found
|
|
30
|
+
#
|
|
31
|
+
# Output: .cleargate/sprint-runs/<sprint>/.dispatch-<session-id>.json
|
|
32
|
+
# Log: .cleargate/hook-log/write_dispatch.log
|
|
33
|
+
|
|
34
|
+
set -u
|
|
35
|
+
|
|
36
|
+
# ─── Resolve repo root ──────────────────────────────────────────────────────
|
|
37
|
+
REPO_ROOT="${ORCHESTRATOR_PROJECT_DIR:-${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}}"
|
|
38
|
+
LOG_DIR="${REPO_ROOT}/.cleargate/hook-log"
|
|
39
|
+
mkdir -p "${LOG_DIR}"
|
|
40
|
+
LOG="${LOG_DIR}/write_dispatch.log"
|
|
41
|
+
|
|
42
|
+
# ─── Validate args ──────────────────────────────────────────────────────────
|
|
43
|
+
if [[ $# -lt 2 || -z "${1:-}" || -z "${2:-}" ]]; then
|
|
44
|
+
printf '[%s] error: usage: write_dispatch.sh <work_item_id> <agent_type>\n' "$(date -u +%FT%TZ)" >> "${LOG}"
|
|
45
|
+
printf 'Usage: write_dispatch.sh <work_item_id> <agent_type>\n' >&2
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
WORK_ITEM_ID="${1}"
|
|
50
|
+
AGENT_TYPE="${2}"
|
|
51
|
+
|
|
52
|
+
# ─── Resolve active sprint ──────────────────────────────────────────────────
|
|
53
|
+
ACTIVE_SENTINEL="${REPO_ROOT}/.cleargate/sprint-runs/.active"
|
|
54
|
+
if [[ ! -f "${ACTIVE_SENTINEL}" ]]; then
|
|
55
|
+
printf '[%s] error: no .active sentinel at %s\n' "$(date -u +%FT%TZ)" "${ACTIVE_SENTINEL}" >> "${LOG}"
|
|
56
|
+
printf 'error: no active sprint sentinel at %s\n' "${ACTIVE_SENTINEL}" >&2
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
SPRINT_ID="$(tr -d '[:space:]' < "${ACTIVE_SENTINEL}")"
|
|
61
|
+
if [[ -z "${SPRINT_ID}" ]]; then
|
|
62
|
+
printf '[%s] error: .active sentinel is empty\n' "$(date -u +%FT%TZ)" >> "${LOG}"
|
|
63
|
+
printf 'error: .active sentinel is empty\n' >&2
|
|
64
|
+
exit 2
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
SPRINT_DIR="${REPO_ROOT}/.cleargate/sprint-runs/${SPRINT_ID}"
|
|
68
|
+
mkdir -p "${SPRINT_DIR}"
|
|
69
|
+
|
|
70
|
+
# ─── Resolve session_id ─────────────────────────────────────────────────────
|
|
71
|
+
SESSION_ID="${CLAUDE_SESSION_ID:-}"
|
|
72
|
+
|
|
73
|
+
if [[ -z "${SESSION_ID}" ]]; then
|
|
74
|
+
# Fall back to scanning the most recent transcript filename (UUID) under
|
|
75
|
+
# ~/.claude/projects/-*-ClearGate/ pattern.
|
|
76
|
+
TRANSCRIPT_DIR="${HOME}/.claude/projects"
|
|
77
|
+
if [[ -d "${TRANSCRIPT_DIR}" ]]; then
|
|
78
|
+
SESSION_ID="$(find "${TRANSCRIPT_DIR}" -maxdepth 2 -name '*.jsonl' 2>/dev/null \
|
|
79
|
+
| sort -t '/' -k1 2>/dev/null | tail -1 \
|
|
80
|
+
| xargs -I{} basename {} .jsonl 2>/dev/null || true)"
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if [[ -z "${SESSION_ID}" ]]; then
|
|
85
|
+
# Final fallback: generate a pseudo-id from timestamp
|
|
86
|
+
SESSION_ID="fallback-$(date -u +%s)"
|
|
87
|
+
printf '[%s] warn: no CLAUDE_SESSION_ID and no transcript found; using %s\n' "$(date -u +%FT%TZ)" "${SESSION_ID}" >> "${LOG}"
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
# ─── Resolve cleargate version ───────────────────────────────────────────────
|
|
91
|
+
# Read from cleargate-cli/package.json if available; otherwise use "unknown"
|
|
92
|
+
PKG_JSON="${REPO_ROOT}/cleargate-cli/package.json"
|
|
93
|
+
CG_VERSION="unknown"
|
|
94
|
+
if [[ -f "${PKG_JSON}" ]]; then
|
|
95
|
+
CG_VERSION="$(jq -r '.version // "unknown"' "${PKG_JSON}" 2>/dev/null || echo "unknown")"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# ─── Write dispatch file atomically ─────────────────────────────────────────
|
|
99
|
+
DISPATCH_TARGET="${SPRINT_DIR}/.dispatch-${SESSION_ID}.json"
|
|
100
|
+
SPAWNED_AT="$(date -u +%FT%TZ)"
|
|
101
|
+
|
|
102
|
+
DISPATCH_JSON="$(jq -cn \
|
|
103
|
+
--arg work_item_id "${WORK_ITEM_ID}" \
|
|
104
|
+
--arg agent_type "${AGENT_TYPE}" \
|
|
105
|
+
--arg spawned_at "${SPAWNED_AT}" \
|
|
106
|
+
--arg session_id "${SESSION_ID}" \
|
|
107
|
+
--arg writer "write_dispatch.sh@cleargate-${CG_VERSION}" \
|
|
108
|
+
'{
|
|
109
|
+
work_item_id: $work_item_id,
|
|
110
|
+
agent_type: $agent_type,
|
|
111
|
+
spawned_at: $spawned_at,
|
|
112
|
+
session_id: $session_id,
|
|
113
|
+
writer: $writer
|
|
114
|
+
}')"
|
|
115
|
+
|
|
116
|
+
# Atomic write via mktemp + mv (rename is atomic on POSIX same-fs)
|
|
117
|
+
TMP="$(mktemp "${SPRINT_DIR}/.dispatch-tmp-XXXXXX")"
|
|
118
|
+
printf '%s\n' "${DISPATCH_JSON}" > "${TMP}"
|
|
119
|
+
mv "${TMP}" "${DISPATCH_TARGET}"
|
|
120
|
+
|
|
121
|
+
printf '[%s] wrote dispatch: sprint=%s session=%s work_item=%s agent=%s\n' \
|
|
122
|
+
"${SPAWNED_AT}" "${SPRINT_ID}" "${SESSION_ID}" "${WORK_ITEM_ID}" "${AGENT_TYPE}" >> "${LOG}"
|
|
123
|
+
|
|
124
|
+
printf '%s\n' "${DISPATCH_TARGET}"
|
|
125
|
+
exit 0
|
|
@@ -8,13 +8,28 @@ YAML Frontmatter: Bug ID, Parent Ref, Status, Severity, Reporter, Approved gate.
|
|
|
8
8
|
§5 Verification Protocol: The failing test that proves the bug exists and proves the fix resolves it.
|
|
9
9
|
Output location: .cleargate/delivery/pending-sync/BUG-{ID}.md
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
POST-WRITE BRIEF
|
|
12
|
+
After Writing this document, render a Brief in chat with the following sections,
|
|
13
|
+
mechanically extracted from the document's own structure:
|
|
14
|
+
|
|
15
|
+
- Summary ← §1 The Anomaly (repro)
|
|
16
|
+
- Open Questions ← §0.5 Open Questions
|
|
17
|
+
- Edge Cases ← §2 Impact (edge conditions)
|
|
18
|
+
- Risks ← §2 Impact
|
|
19
|
+
- Ambiguity ← bottom-of-doc ambiguity gate block
|
|
20
|
+
|
|
21
|
+
Halt for human review. When ambiguity reaches 🟢, proceed to call cleargate_push_item.
|
|
22
|
+
Do NOT ask separately for push confirmation — Brief approval covers it.
|
|
23
|
+
|
|
12
24
|
Do NOT output these instructions.
|
|
13
25
|
</instructions>
|
|
14
26
|
|
|
15
27
|
---
|
|
16
28
|
bug_id: "BUG-{ID}"
|
|
17
29
|
parent_ref: "EPIC-{ID} | STORY-{ID}"
|
|
30
|
+
parent_cleargate_id: null # canonical cleargate-id of parent work item; null for top-level
|
|
31
|
+
sprint_cleargate_id: null # canonical cleargate-id of owning sprint; null for off-sprint items
|
|
32
|
+
carry_over: false # set true to skip lifecycle reconciliation at sprint close
|
|
18
33
|
status: "Draft | Triaged | In Fix | Verified"
|
|
19
34
|
severity: "P0-Critical | P1-High | P2-Medium | P3-Low"
|
|
20
35
|
reporter: "{name}"
|
|
@@ -48,6 +63,14 @@ last_synced_body_sha: null # sha256 of body at last sync
|
|
|
48
63
|
|
|
49
64
|
# BUG-{ID}: {Bug Name}
|
|
50
65
|
|
|
66
|
+
## 0.5 Open Questions
|
|
67
|
+
|
|
68
|
+
> Populate during drafting. Resolve every entry before flipping ambiguity to 🟢.
|
|
69
|
+
|
|
70
|
+
- **Question:** {edge case, contradiction, or missing detail}
|
|
71
|
+
- **Recommended:** {agent's proposed answer}
|
|
72
|
+
- **Human decision:** {populated during Brief review}
|
|
73
|
+
|
|
51
74
|
## 1. The Anomaly (Expected vs. Actual)
|
|
52
75
|
**Expected Behavior:** {What the system should do under normal conditions.}
|
|
53
76
|
|