cleargate 0.14.0 → 0.15.1

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 (150) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/MANIFEST.json +72 -16
  3. package/dist/admin-api/index.cjs +0 -1
  4. package/dist/admin-api/index.js +1 -2
  5. package/dist/auth/factory.cjs +0 -1
  6. package/dist/auth/factory.js +2 -3
  7. package/dist/auth/require-token.cjs +0 -1
  8. package/dist/auth/require-token.js +1 -2
  9. package/dist/auth/token-store.cjs +0 -1
  10. package/dist/auth/token-store.js +1 -2
  11. package/dist/{bootstrap-root-QKSA5V75.js → bootstrap-root-2H5HVTCC.js} +1 -2
  12. package/dist/{chunk-PDE37WFQ.js → chunk-A7MSQUU7.js} +2 -3
  13. package/dist/{chunk-BTSZOEWC.js → chunk-P6KEDAK2.js} +0 -1
  14. package/dist/{chunk-E3X7IE5E.js → chunk-PY6FHGV5.js} +1 -2
  15. package/dist/{chunk-5DI2Z3C2.js → chunk-Y53ZZYYU.js} +1 -2
  16. package/dist/cli.cjs +1564 -1414
  17. package/dist/cli.js +1514 -1364
  18. package/dist/lib/ledger.cjs +0 -1
  19. package/dist/lib/ledger.js +1 -2
  20. package/dist/lib/lifecycle-reconcile.cjs +0 -1
  21. package/dist/lib/lifecycle-reconcile.js +2 -3
  22. package/dist/{whoami-EANGN46Z.js → whoami-JKQQPABQ.js} +3 -4
  23. package/package.json +4 -3
  24. package/templates/cleargate-planning/.claude/agents/architect-synth.md +2 -0
  25. package/templates/cleargate-planning/.claude/agents/architect.md +4 -2
  26. package/templates/cleargate-planning/.claude/agents/developer.md +4 -11
  27. package/templates/cleargate-planning/.claude/agents/qa.md +14 -6
  28. package/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +2 -2
  29. package/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +19 -1
  30. package/templates/cleargate-planning/.cleargate/config.example.yml +16 -0
  31. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.deferred-verify.red.node.test.ts +245 -0
  32. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +227 -0
  33. package/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +5 -4
  34. package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +75 -2
  35. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +48 -0
  36. package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +57 -1
  37. package/templates/cleargate-planning/.cleargate/scripts/provision_worktree_config.sh +155 -0
  38. package/templates/cleargate-planning/.cleargate/scripts/qa_red_lint.mjs +380 -0
  39. package/templates/cleargate-planning/.cleargate/scripts/run_script.sh +34 -1
  40. package/templates/cleargate-planning/.cleargate/scripts/test/cr077_eviction.red.sh +113 -0
  41. package/templates/cleargate-planning/.cleargate/scripts/test/cr078_init.test.sh +309 -0
  42. package/templates/cleargate-planning/.cleargate/scripts/test/cr079_provision.red.sh +262 -0
  43. package/templates/cleargate-planning/.cleargate/scripts/test/cr080_wrapper.test.sh +177 -0
  44. package/templates/cleargate-planning/.cleargate/scripts/test/cr081_qa_red_lint.red.sh +348 -0
  45. package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/.session-totals.json +1 -0
  46. package/templates/cleargate-planning/.cleargate/sprint-runs/_off-sprint/token-ledger.jsonl +222 -0
  47. package/templates/cleargate-planning/.cleargate/templates/sprint_context.md +17 -0
  48. package/templates/cleargate-planning/.cleargate/templates/story.md +1 -0
  49. package/templates/cleargate-planning/MANIFEST.json +72 -16
  50. package/dist/admin-api/index.cjs.map +0 -1
  51. package/dist/admin-api/index.js.map +0 -1
  52. package/dist/auth/factory.cjs.map +0 -1
  53. package/dist/auth/factory.js.map +0 -1
  54. package/dist/auth/require-token.cjs.map +0 -1
  55. package/dist/auth/require-token.js.map +0 -1
  56. package/dist/auth/token-store.cjs.map +0 -1
  57. package/dist/auth/token-store.js.map +0 -1
  58. package/dist/bootstrap-root-QKSA5V75.js.map +0 -1
  59. package/dist/chunk-5DI2Z3C2.js.map +0 -1
  60. package/dist/chunk-BTSZOEWC.js.map +0 -1
  61. package/dist/chunk-E3X7IE5E.js.map +0 -1
  62. package/dist/chunk-PDE37WFQ.js.map +0 -1
  63. package/dist/cli.cjs.map +0 -1
  64. package/dist/cli.js.map +0 -1
  65. package/dist/lib/ledger.cjs.map +0 -1
  66. package/dist/lib/ledger.js.map +0 -1
  67. package/dist/lib/lifecycle-reconcile.cjs.map +0 -1
  68. package/dist/lib/lifecycle-reconcile.js.map +0 -1
  69. package/dist/templates/cleargate-planning/.claude/agents/architect-reader.md +0 -61
  70. package/dist/templates/cleargate-planning/.claude/agents/architect-synth.md +0 -124
  71. package/dist/templates/cleargate-planning/.claude/agents/architect.md +0 -230
  72. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-contradict.md +0 -108
  73. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-ingest.md +0 -194
  74. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +0 -261
  75. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-query.md +0 -143
  76. package/dist/templates/cleargate-planning/.claude/agents/developer.md +0 -185
  77. package/dist/templates/cleargate-planning/.claude/agents/devops.md +0 -257
  78. package/dist/templates/cleargate-planning/.claude/agents/qa.md +0 -171
  79. package/dist/templates/cleargate-planning/.claude/agents/reporter.md +0 -274
  80. package/dist/templates/cleargate-planning/.claude/hooks/pending-task-sentinel.sh +0 -209
  81. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +0 -33
  82. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-test-ratchet.sh +0 -58
  83. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit.sh +0 -19
  84. package/dist/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +0 -162
  85. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-autonomy.sh +0 -58
  86. package/dist/templates/cleargate-planning/.claude/hooks/pre-tool-use-task.sh +0 -148
  87. package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +0 -75
  88. package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +0 -43
  89. package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +0 -590
  90. package/dist/templates/cleargate-planning/.claude/settings.json +0 -68
  91. package/dist/templates/cleargate-planning/.claude/skills/flashcard/SKILL.md +0 -102
  92. package/dist/templates/cleargate-planning/.claude/skills/sprint-execution/SKILL.md +0 -742
  93. package/dist/templates/cleargate-planning/.cleargate/FLASHCARD.md +0 -7
  94. package/dist/templates/cleargate-planning/.cleargate/config.example.yml +0 -67
  95. package/dist/templates/cleargate-planning/.cleargate/config.yml +0 -18
  96. package/dist/templates/cleargate-planning/.cleargate/delivery/archive/.gitkeep +0 -0
  97. package/dist/templates/cleargate-planning/.cleargate/delivery/pending-sync/.gitkeep +0 -0
  98. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-enforcement.md +0 -551
  99. package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +0 -878
  100. package/dist/templates/cleargate-planning/.cleargate/knowledge/mid-sprint-triage-rubric.md +0 -160
  101. package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +0 -213
  102. package/dist/templates/cleargate-planning/.cleargate/knowledge/sprint-closeout-checklist.md +0 -71
  103. package/dist/templates/cleargate-planning/.cleargate/scripts/_migrate-schema-v3.mjs +0 -120
  104. package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +0 -265
  105. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +0 -1012
  106. package/dist/templates/cleargate-planning/.cleargate/scripts/collision_surface.sh +0 -114
  107. package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +0 -62
  108. package/dist/templates/cleargate-planning/.cleargate/scripts/dedupe_frontmatter.mjs +0 -219
  109. package/dist/templates/cleargate-planning/.cleargate/scripts/file_surface_diff.sh +0 -320
  110. package/dist/templates/cleargate-planning/.cleargate/scripts/gate-checks.json +0 -15
  111. package/dist/templates/cleargate-planning/.cleargate/scripts/init_gate_config.sh +0 -38
  112. package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +0 -240
  113. package/dist/templates/cleargate-planning/.cleargate/scripts/launch_wave.mjs +0 -341
  114. package/dist/templates/cleargate-planning/.cleargate/scripts/lib/report-filename.mjs +0 -54
  115. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +0 -206
  116. package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +0 -371
  117. package/dist/templates/cleargate-planning/.cleargate/scripts/prefill_report.mjs +0 -280
  118. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_doc_refresh.mjs +0 -378
  119. package/dist/templates/cleargate-planning/.cleargate/scripts/prep_qa_context.mjs +0 -888
  120. package/dist/templates/cleargate-planning/.cleargate/scripts/run_script.sh +0 -209
  121. package/dist/templates/cleargate-planning/.cleargate/scripts/sprint_trends.mjs +0 -71
  122. package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +0 -127
  123. package/dist/templates/cleargate-planning/.cleargate/scripts/suggest_improvements.mjs +0 -717
  124. package/dist/templates/cleargate-planning/.cleargate/scripts/surface-whitelist.txt +0 -27
  125. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_assert_story_files.sh +0 -261
  126. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_file_surface.sh +0 -210
  127. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_flashcard_gate.sh +0 -190
  128. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_prep_qa_context.sh +0 -482
  129. package/dist/templates/cleargate-planning/.cleargate/scripts/test/test_test_ratchet.sh +0 -327
  130. package/dist/templates/cleargate-planning/.cleargate/scripts/test_ratchet.mjs +0 -261
  131. package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +0 -246
  132. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_bounce_readiness.mjs +0 -111
  133. package/dist/templates/cleargate-planning/.cleargate/scripts/validate_state.mjs +0 -184
  134. package/dist/templates/cleargate-planning/.cleargate/scripts/write_dispatch.sh +0 -172
  135. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +0 -126
  136. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +0 -130
  137. package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +0 -137
  138. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +0 -166
  139. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +0 -111
  140. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +0 -122
  141. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_context.md +0 -50
  142. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +0 -224
  143. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +0 -213
  144. package/dist/templates/cleargate-planning/CLAUDE.md +0 -66
  145. package/dist/templates/cleargate-planning/MANIFEST.json +0 -503
  146. package/dist/templates/synthesis/active-sprint.md +0 -30
  147. package/dist/templates/synthesis/open-gates.md +0 -38
  148. package/dist/templates/synthesis/product-state.md +0 -31
  149. package/dist/templates/synthesis/roadmap.md +0 -63
  150. package/dist/whoami-EANGN46Z.js.map +0 -1
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env bash
2
+ # cr078_init.test.sh — CR-078 verification harness.
3
+ #
4
+ # Verifies F1 (.active sentinel write) and F2 (SDR lane audit ingest) are
5
+ # correctly implemented in init_sprint.mjs.
6
+ #
7
+ # All assertions run in FULL ISOLATION — mktemp -d scratch project dirs with
8
+ # their own .cleargate/sprint-runs/ trees. The REAL repo .active is NEVER touched.
9
+ # Safety invariant: after this harness exits, cat <repo>/.cleargate/sprint-runs/.active
10
+ # MUST still read SPRINT-34 (the running sprint).
11
+ #
12
+ # Assertions:
13
+ # 1. .active write: init a scratch SPRINT-99 → cat <temp>/.active == SPRINT-99
14
+ # 2. WARN on differing prior: seed .active=SPRINT-50, init SPRINT-99 →
15
+ # stderr contains WARN, .active becomes SPRINT-99
16
+ # 3. Lane ingest via waves.json: seed lane_assignments marking one story fast →
17
+ # state.json lane==fast + lane_assigned_by==sdr-lane-audit; undeclared story stays standard
18
+ # 4. Lane ingest fallback via §2.4: no waves.json but Sprint Plan with §2.4 Lane Audit
19
+ # marking a story fast → same result
20
+ # 5. Regression: grep -c '\.active' init_sprint.mjs ≥ 1 (was 0 before CR-078)
21
+ #
22
+ # Exit 0 = PASS (all assertions pass); exit 1 = one or more FAIL.
23
+ #
24
+ # Harness self-cleans (trap EXIT removes temp dirs). NEVER touches the real repo .active.
25
+ # macOS bash 3.2 portable.
26
+ # FLASHCARD #test-harness #bash 2026-06-04: use CLEARGATE_REPO_ROOT + CLEARGATE_ADVISORY=1
27
+ # to isolate init_sprint.mjs into a scratch tmpdir without real delivery files.
28
+ set -uo pipefail
29
+
30
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
31
+ REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
32
+ INIT_SCRIPT="${REPO_ROOT}/.cleargate/scripts/init_sprint.mjs"
33
+ CONSTANTS_SCRIPT="${REPO_ROOT}/.cleargate/scripts/constants.mjs"
34
+
35
+ PASS=0
36
+ FAIL=0
37
+
38
+ # Track temp dirs for cleanup
39
+ TEMP_DIRS=()
40
+
41
+ pass() {
42
+ echo "PASS: $1"
43
+ PASS=$((PASS + 1))
44
+ }
45
+
46
+ fail() {
47
+ echo "FAIL: $1"
48
+ echo " detail: $2"
49
+ FAIL=$((FAIL + 1))
50
+ }
51
+
52
+ # ── Teardown / trap ───────────────────────────────────────────────────────────
53
+ cleanup() {
54
+ for d in "${TEMP_DIRS[@]+"${TEMP_DIRS[@]}"}"; do
55
+ if [[ -d "${d}" ]]; then
56
+ rm -rf "${d}"
57
+ fi
58
+ done
59
+ }
60
+ trap cleanup EXIT
61
+
62
+ # ── Helper: create a minimal scratch project dir ──────────────────────────────
63
+ # Each scratch dir gets its own .cleargate/sprint-runs/ tree.
64
+ # We seed a minimal constants.mjs symlink so the script can import it.
65
+ make_scratch() {
66
+ local tmpdir
67
+ tmpdir="$(mktemp -d)"
68
+ TEMP_DIRS+=("${tmpdir}")
69
+ # Create the sprint-runs dir
70
+ mkdir -p "${tmpdir}/.cleargate/sprint-runs"
71
+ # Create a minimal scripts dir (symlink to real scripts so import works)
72
+ mkdir -p "${tmpdir}/.cleargate/scripts"
73
+ # Symlink constants.mjs so the import resolves (init_sprint.mjs uses __dirname
74
+ # for the scripts dir, NOT CLEARGATE_REPO_ROOT — so we pass CLEARGATE_REPO_ROOT
75
+ # only for file-path resolution, not for module loading).
76
+ echo "${tmpdir}"
77
+ }
78
+
79
+ # ── Pre-check: init_sprint.mjs must exist ────────────────────────────────────
80
+ if [[ ! -f "${INIT_SCRIPT}" ]]; then
81
+ fail "pre-check" "init_sprint.mjs not found at ${INIT_SCRIPT}"
82
+ echo ""
83
+ echo "cr078_init.test.sh: ${PASS} passed, ${FAIL} failed"
84
+ exit 1
85
+ fi
86
+
87
+ # ── ASSERTION 5 (regression): grep count for .active references ───────────────
88
+ # Was 0 before CR-078; must be ≥ 1 after.
89
+ ACTIVE_COUNT="$(grep -c '\.active' "${INIT_SCRIPT}" || true)"
90
+ if [[ "${ACTIVE_COUNT}" -ge 1 ]]; then
91
+ pass "5-regression: init_sprint.mjs contains ≥1 .active reference (count=${ACTIVE_COUNT})"
92
+ else
93
+ fail "5-regression: init_sprint.mjs has 0 .active references — F1 not implemented" \
94
+ "grep -c '.active' returned ${ACTIVE_COUNT}"
95
+ fi
96
+
97
+ # ────────────────────────────────────────────────────────────────────────────
98
+ # ASSERTION 1: .active write — init SPRINT-99 in scratch dir → .active == SPRINT-99
99
+ # ────────────────────────────────────────────────────────────────────────────
100
+ SCRATCH1="$(make_scratch)"
101
+ mkdir -p "${SCRATCH1}/.cleargate/sprint-runs/SPRINT-99"
102
+
103
+ # Run init_sprint.mjs with CLEARGATE_REPO_ROOT pointing to scratch dir.
104
+ # CLEARGATE_ADVISORY=1 bypasses story-file assertion (no delivery/ in scratch dir).
105
+ INIT_STDERR1="$(
106
+ CLEARGATE_REPO_ROOT="${SCRATCH1}" CLEARGATE_ADVISORY=1 \
107
+ node "${INIT_SCRIPT}" SPRINT-99 --stories STORY-99-01 --force 2>&1 >/dev/null
108
+ )"
109
+ INIT_EXIT1=$?
110
+
111
+ ACTIVE_VAL1=""
112
+ if [[ -f "${SCRATCH1}/.cleargate/sprint-runs/.active" ]]; then
113
+ ACTIVE_VAL1="$(cat "${SCRATCH1}/.cleargate/sprint-runs/.active" | tr -d '\n')"
114
+ fi
115
+
116
+ if [[ "${ACTIVE_VAL1}" = "SPRINT-99" ]]; then
117
+ pass "1-active-write: .active == SPRINT-99 after init"
118
+ else
119
+ fail "1-active-write: .active not set to SPRINT-99" \
120
+ "got '${ACTIVE_VAL1}'; init exit=${INIT_EXIT1}; stderr: ${INIT_STDERR1}"
121
+ fi
122
+
123
+ # ────────────────────────────────────────────────────────────────────────────
124
+ # ASSERTION 2: WARN on differing prior — seed .active=SPRINT-50, init SPRINT-99
125
+ # ────────────────────────────────────────────────────────────────────────────
126
+ SCRATCH2="$(make_scratch)"
127
+ mkdir -p "${SCRATCH2}/.cleargate/sprint-runs/SPRINT-99"
128
+ # Pre-seed .active with a different sprint
129
+ printf 'SPRINT-50\n' > "${SCRATCH2}/.cleargate/sprint-runs/.active"
130
+
131
+ INIT_STDERR2="$(
132
+ CLEARGATE_REPO_ROOT="${SCRATCH2}" CLEARGATE_ADVISORY=1 \
133
+ node "${INIT_SCRIPT}" SPRINT-99 --stories STORY-99-01 --force 2>&1 >/dev/null
134
+ )"
135
+
136
+ ACTIVE_VAL2=""
137
+ if [[ -f "${SCRATCH2}/.cleargate/sprint-runs/.active" ]]; then
138
+ ACTIVE_VAL2="$(cat "${SCRATCH2}/.cleargate/sprint-runs/.active" | tr -d '\n')"
139
+ fi
140
+
141
+ # Check .active updated
142
+ if [[ "${ACTIVE_VAL2}" = "SPRINT-99" ]]; then
143
+ pass "2a-warn-prior: .active updated to SPRINT-99 despite prior SPRINT-50"
144
+ else
145
+ fail "2a-warn-prior: .active not updated" \
146
+ "got '${ACTIVE_VAL2}'"
147
+ fi
148
+
149
+ # Check WARN emitted
150
+ if echo "${INIT_STDERR2}" | grep -q "WARN: .active was SPRINT-50"; then
151
+ pass "2b-warn-prior: WARN message emitted on stderr"
152
+ else
153
+ fail "2b-warn-prior: expected WARN about prior .active on stderr" \
154
+ "stderr was: ${INIT_STDERR2}"
155
+ fi
156
+
157
+ # ────────────────────────────────────────────────────────────────────────────
158
+ # ASSERTION 3: Lane ingest via waves.json
159
+ # ────────────────────────────────────────────────────────────────────────────
160
+ SCRATCH3="$(make_scratch)"
161
+ mkdir -p "${SCRATCH3}/.cleargate/sprint-runs/SPRINT-99/plans"
162
+
163
+ # Seed waves.json with lane_assignments marking STORY-99-01 as fast
164
+ cat > "${SCRATCH3}/.cleargate/sprint-runs/SPRINT-99/plans/waves.json" <<'EOF'
165
+ {
166
+ "sprint": "SPRINT-99",
167
+ "generated_at": "2026-06-04T00:00:00Z",
168
+ "waves": [
169
+ {
170
+ "wave": "wave1",
171
+ "stories": ["STORY-99-01", "STORY-99-02"],
172
+ "parallel": false,
173
+ "rationale": "test wave"
174
+ }
175
+ ],
176
+ "lane_assignments": {
177
+ "STORY-99-01": "fast"
178
+ }
179
+ }
180
+ EOF
181
+
182
+ INIT_STDERR3="$(
183
+ CLEARGATE_REPO_ROOT="${SCRATCH3}" CLEARGATE_ADVISORY=1 \
184
+ node "${INIT_SCRIPT}" SPRINT-99 --stories STORY-99-01,STORY-99-02 --force 2>&1 >/dev/null
185
+ )"
186
+
187
+ STATE3="${SCRATCH3}/.cleargate/sprint-runs/SPRINT-99/state.json"
188
+ if [[ ! -f "${STATE3}" ]]; then
189
+ fail "3-waves-lane: state.json not written" \
190
+ "init stderr: ${INIT_STDERR3}"
191
+ else
192
+ LANE3_01="$(node -e "const s=require('${STATE3}'); process.stdout.write(s.stories['STORY-99-01'].lane)" 2>/dev/null || echo 'ERROR')"
193
+ LANE_BY3_01="$(node -e "const s=require('${STATE3}'); process.stdout.write(s.stories['STORY-99-01'].lane_assigned_by)" 2>/dev/null || echo 'ERROR')"
194
+ LANE3_02="$(node -e "const s=require('${STATE3}'); process.stdout.write(s.stories['STORY-99-02'].lane)" 2>/dev/null || echo 'ERROR')"
195
+ LANE_BY3_02="$(node -e "const s=require('${STATE3}'); process.stdout.write(s.stories['STORY-99-02'].lane_assigned_by)" 2>/dev/null || echo 'ERROR')"
196
+
197
+ if [[ "${LANE3_01}" = "fast" ]]; then
198
+ pass "3a-waves-lane: STORY-99-01 lane==fast (declared in waves.json)"
199
+ else
200
+ fail "3a-waves-lane: STORY-99-01 lane expected 'fast'" \
201
+ "got '${LANE3_01}'"
202
+ fi
203
+
204
+ if [[ "${LANE_BY3_01}" = "sdr-lane-audit" ]]; then
205
+ pass "3b-waves-lane: STORY-99-01 lane_assigned_by==sdr-lane-audit"
206
+ else
207
+ fail "3b-waves-lane: STORY-99-01 lane_assigned_by expected 'sdr-lane-audit'" \
208
+ "got '${LANE_BY3_01}'"
209
+ fi
210
+
211
+ if [[ "${LANE3_02}" = "standard" ]]; then
212
+ pass "3c-waves-lane: STORY-99-02 (undeclared) lane==standard"
213
+ else
214
+ fail "3c-waves-lane: STORY-99-02 lane expected 'standard' (undeclared)" \
215
+ "got '${LANE3_02}'"
216
+ fi
217
+
218
+ if [[ "${LANE_BY3_02}" = "migration-default" ]]; then
219
+ pass "3d-waves-lane: STORY-99-02 lane_assigned_by==migration-default (undeclared)"
220
+ else
221
+ fail "3d-waves-lane: STORY-99-02 lane_assigned_by expected 'migration-default'" \
222
+ "got '${LANE_BY3_02}'"
223
+ fi
224
+ fi
225
+
226
+ # ────────────────────────────────────────────────────────────────────────────
227
+ # ASSERTION 4: Lane ingest fallback via §2.4 — no waves.json, sprint plan with Lane Audit
228
+ # ────────────────────────────────────────────────────────────────────────────
229
+ SCRATCH4="$(make_scratch)"
230
+ mkdir -p "${SCRATCH4}/.cleargate/sprint-runs/SPRINT-99"
231
+ mkdir -p "${SCRATCH4}/.cleargate/delivery/pending-sync"
232
+
233
+ # Create a minimal sprint plan with a §2.4 Lane Audit table (no waves.json)
234
+ cat > "${SCRATCH4}/.cleargate/delivery/pending-sync/SPRINT-99_Test_Sprint.md" <<'EOF'
235
+ ---
236
+ status: Active
237
+ approved: true
238
+ ---
239
+ # SPRINT-99: Test Sprint
240
+
241
+ ## 0. Frontmatter
242
+ - **Sprint Goal:** Test lane ingest fallback
243
+
244
+ ## 2. Execution Strategy
245
+
246
+ ### 2.4 Lane Audit
247
+
248
+ | Story | Lane | Rationale (≤80 chars) |
249
+ |---|---|---|
250
+ | `STORY-99-01` | fast | Isolated fix, no agent surface |
251
+
252
+ ### 2.5 ADR-Conflict Flags
253
+ None.
254
+ EOF
255
+
256
+ INIT_STDERR4="$(
257
+ CLEARGATE_REPO_ROOT="${SCRATCH4}" CLEARGATE_ADVISORY=1 \
258
+ node "${INIT_SCRIPT}" SPRINT-99 --stories STORY-99-01,STORY-99-02 --force 2>&1 >/dev/null
259
+ )"
260
+
261
+ STATE4="${SCRATCH4}/.cleargate/sprint-runs/SPRINT-99/state.json"
262
+ if [[ ! -f "${STATE4}" ]]; then
263
+ fail "4-plan-lane: state.json not written" \
264
+ "init stderr: ${INIT_STDERR4}"
265
+ else
266
+ LANE4_01="$(node -e "const s=require('${STATE4}'); process.stdout.write(s.stories['STORY-99-01'].lane)" 2>/dev/null || echo 'ERROR')"
267
+ LANE_BY4_01="$(node -e "const s=require('${STATE4}'); process.stdout.write(s.stories['STORY-99-01'].lane_assigned_by)" 2>/dev/null || echo 'ERROR')"
268
+ LANE4_02="$(node -e "const s=require('${STATE4}'); process.stdout.write(s.stories['STORY-99-02'].lane)" 2>/dev/null || echo 'ERROR')"
269
+
270
+ if [[ "${LANE4_01}" = "fast" ]]; then
271
+ pass "4a-plan-lane: STORY-99-01 lane==fast (declared in §2.4 Lane Audit table)"
272
+ else
273
+ fail "4a-plan-lane: STORY-99-01 lane expected 'fast'" \
274
+ "got '${LANE4_01}'; stderr: ${INIT_STDERR4}"
275
+ fi
276
+
277
+ if [[ "${LANE_BY4_01}" = "sdr-lane-audit" ]]; then
278
+ pass "4b-plan-lane: STORY-99-01 lane_assigned_by==sdr-lane-audit"
279
+ else
280
+ fail "4b-plan-lane: STORY-99-01 lane_assigned_by expected 'sdr-lane-audit'" \
281
+ "got '${LANE_BY4_01}'"
282
+ fi
283
+
284
+ if [[ "${LANE4_02}" = "standard" ]]; then
285
+ pass "4c-plan-lane: STORY-99-02 (undeclared) lane==standard"
286
+ else
287
+ fail "4c-plan-lane: STORY-99-02 lane expected 'standard'" \
288
+ "got '${LANE4_02}'"
289
+ fi
290
+ fi
291
+
292
+ # ────────────────────────────────────────────────────────────────────────────
293
+ # SAFETY: Verify the real repo .active is still SPRINT-34
294
+ # ────────────────────────────────────────────────────────────────────────────
295
+ REAL_ACTIVE="$(cat "${REPO_ROOT}/.cleargate/sprint-runs/.active" | tr -d '\n')"
296
+ if [[ "${REAL_ACTIVE}" = "SPRINT-34" ]]; then
297
+ pass "safety: real repo .active still == SPRINT-34 (not clobbered)"
298
+ else
299
+ fail "SAFETY VIOLATION: real repo .active clobbered!" \
300
+ "expected SPRINT-34, got '${REAL_ACTIVE}'"
301
+ fi
302
+
303
+ # ── Summary ───────────────────────────────────────────────────────────────────
304
+ echo ""
305
+ echo "cr078_init.test.sh: ${PASS} passed, ${FAIL} failed"
306
+ if [[ "${FAIL}" -gt 0 ]]; then
307
+ exit 1
308
+ fi
309
+ exit 0
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bash
2
+ # cr079_provision.red.sh — CR-079 QA-Red provision test harness.
3
+ #
4
+ # RED: fails against clean baseline — provision_worktree_config.sh does not yet
5
+ # exist, so the .env is never provisioned into the test worktree, and the
6
+ # stray_env_files scan has no exemption for provisioned config.
7
+ # GREEN: passes after Developer implements CR-079 (provision script created +
8
+ # pre_gate_runner.sh exemption branch + config.yml worktree.provision_config).
9
+ #
10
+ # Assertions (per M1 §5 / CR §4):
11
+ # 1. Provision (symlink): provision script creates .env symlink in worktree
12
+ # pointing to absolute repo-root .env.
13
+ # 2. Scan PASS (exempted): with the provisioned .env present, stray_env_files
14
+ # scan records PASS (provisioned file is exempt, not stray).
15
+ # 3. Negative control: a non-provisioned .env.local in the worktree makes the
16
+ # scan record stray_env_files FAIL (exemption is scoped, not blanket).
17
+ # 4. Teardown: no dangling symlink at repo root; fixture .env cleaned up; no
18
+ # leftover worktree.
19
+ #
20
+ # The harness is self-cleaning even on assertion failure: trap EXIT removes the
21
+ # test worktree, any fixture .env created at repo root, and the test branch.
22
+ #
23
+ # Mirrors: cr077_eviction.red.sh / test_prep_qa_context.sh harness shape.
24
+ # macOS bash 3.2 portable.
25
+ # FLASHCARD #test-harness #bash 2026-06-03: never use `grep -c … || echo 0`
26
+ # (doubles on zero-match). Use grep -q or grep -c … | head -1.
27
+ #
28
+ # Exit 0 = PASS (all assertions pass); exit 1 = one or more FAIL.
29
+ set -uo pipefail
30
+
31
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
32
+ REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
33
+
34
+ PASS=0
35
+ FAIL=0
36
+
37
+ pass() {
38
+ echo "PASS: $1"
39
+ PASS=$((PASS + 1))
40
+ }
41
+
42
+ fail() {
43
+ echo "FAIL: $1"
44
+ echo " detail: $2"
45
+ FAIL=$((FAIL + 1))
46
+ }
47
+
48
+ # ── Constants ────────────────────────────────────────────────────────────────
49
+ TEST_BRANCH="test/079"
50
+ TEST_WORKTREE="${REPO_ROOT}/.worktrees/TEST-079"
51
+ PROVISION_SCRIPT="${REPO_ROOT}/.cleargate/scripts/provision_worktree_config.sh"
52
+ PRE_GATE_SCRIPT="${REPO_ROOT}/.cleargate/scripts/pre_gate_runner.sh"
53
+ FIXTURE_ENV="${REPO_ROOT}/.env"
54
+ # Report path written by pre_gate_runner.sh arch mode
55
+ SCAN_REPORT="${TEST_WORKTREE}/.cleargate/reports/pre-arch-scan.txt"
56
+
57
+ # Track whether the harness created the fixture .env at repo root
58
+ HARNESS_CREATED_ENV=0
59
+
60
+ # ── Teardown / trap ───────────────────────────────────────────────────────────
61
+ cleanup() {
62
+ # Remove test worktree (force — even if dirty)
63
+ if git -C "${REPO_ROOT}" worktree list 2>/dev/null | grep -q "${TEST_WORKTREE}"; then
64
+ git -C "${REPO_ROOT}" worktree remove --force "${TEST_WORKTREE}" 2>/dev/null || true
65
+ git -C "${REPO_ROOT}" worktree prune 2>/dev/null || true
66
+ fi
67
+ # Remove worktree directory if still present
68
+ if [[ -d "${TEST_WORKTREE}" ]]; then
69
+ rm -rf "${TEST_WORKTREE}"
70
+ fi
71
+ # Remove test branch if it exists
72
+ if git -C "${REPO_ROOT}" branch --list "${TEST_BRANCH}" | grep -q "${TEST_BRANCH}"; then
73
+ git -C "${REPO_ROOT}" branch -D "${TEST_BRANCH}" 2>/dev/null || true
74
+ fi
75
+ # Remove fixture .env only if harness created it and it's still a real file
76
+ if [[ "${HARNESS_CREATED_ENV}" -eq 1 && -f "${FIXTURE_ENV}" && ! -L "${FIXTURE_ENV}" ]]; then
77
+ rm -f "${FIXTURE_ENV}"
78
+ fi
79
+ }
80
+ trap cleanup EXIT
81
+
82
+ # ── Pre-check: fixture .env at repo root ─────────────────────────────────────
83
+ # The repo has no .env (gitignored). Create a fixture so the provision script
84
+ # has a real source file to symlink from.
85
+ if [[ ! -e "${FIXTURE_ENV}" ]]; then
86
+ printf 'CR079_FIXTURE=1\n' > "${FIXTURE_ENV}"
87
+ HARNESS_CREATED_ENV=1
88
+ fi
89
+
90
+ # ── Setup: create test worktree ───────────────────────────────────────────────
91
+ # Clean up any stale worktree/branch from a previous interrupted run.
92
+ if git -C "${REPO_ROOT}" worktree list 2>/dev/null | grep -q "TEST-079"; then
93
+ git -C "${REPO_ROOT}" worktree remove --force "${TEST_WORKTREE}" 2>/dev/null || true
94
+ git -C "${REPO_ROOT}" worktree prune 2>/dev/null || true
95
+ fi
96
+ if [[ -d "${TEST_WORKTREE}" ]]; then
97
+ rm -rf "${TEST_WORKTREE}"
98
+ fi
99
+ if git -C "${REPO_ROOT}" branch --list "${TEST_BRANCH}" | grep -q "${TEST_BRANCH}"; then
100
+ git -C "${REPO_ROOT}" branch -D "${TEST_BRANCH}" 2>/dev/null || true
101
+ fi
102
+
103
+ git -C "${REPO_ROOT}" worktree add "${TEST_WORKTREE}" -b "${TEST_BRANCH}" HEAD \
104
+ > /dev/null 2>&1
105
+ if [[ ! -d "${TEST_WORKTREE}" ]]; then
106
+ fail "setup-worktree" "git worktree add failed — ${TEST_WORKTREE} does not exist"
107
+ echo ""
108
+ echo "cr079_provision.red.sh: ${PASS} passed, ${FAIL} failed"
109
+ exit 1
110
+ fi
111
+
112
+ # ────────────────────────────────────────────────────────────────────────────
113
+ # ASSERTION 1: Provision script creates .env symlink in worktree
114
+ # ────────────────────────────────────────────────────────────────────────────
115
+ # On the clean baseline: provision_worktree_config.sh does not exist → fails.
116
+
117
+ if [[ ! -f "${PROVISION_SCRIPT}" ]]; then
118
+ fail "provision-script-exists" \
119
+ "provision_worktree_config.sh does not exist at ${PROVISION_SCRIPT}"
120
+ else
121
+ pass "provision-script-exists"
122
+ # Script exists — run it and check the resulting symlink
123
+ bash "${PROVISION_SCRIPT}" "${TEST_WORKTREE}" > /dev/null 2>&1 || true
124
+ fi
125
+
126
+ if [[ -L "${TEST_WORKTREE}/.env" ]]; then
127
+ SYMLINK_TARGET="$(readlink "${TEST_WORKTREE}/.env")"
128
+ if [[ "${SYMLINK_TARGET}" = "${FIXTURE_ENV}" ]]; then
129
+ pass "provision-symlink-target — readlink resolves to absolute repo-root .env"
130
+ else
131
+ fail "provision-symlink-target" \
132
+ "readlink='${SYMLINK_TARGET}' — expected absolute path '${FIXTURE_ENV}'"
133
+ fi
134
+ else
135
+ # Expected on clean baseline: provision script never ran → no symlink
136
+ fail "provision-symlink-exists" \
137
+ "${TEST_WORKTREE}/.env does not exist (provision script never ran or failed)"
138
+ fi
139
+
140
+ # ────────────────────────────────────────────────────────────────────────────
141
+ # ASSERTION 2: Scan records PASS for provisioned .env (exemption in place)
142
+ # ────────────────────────────────────────────────────────────────────────────
143
+ # Pre-condition: the .env must actually be present in the worktree for this
144
+ # assertion to be meaningful. On clean baseline, no .env was provisioned, so
145
+ # the scan trivially PASSes (nothing to flag). This means assertion 2 does NOT
146
+ # fail on the baseline solely due to the missing script — but we need it to
147
+ # FAIL to prove the exemption logic. We therefore MANUALLY place a .env symlink
148
+ # into the worktree here (mirroring what a real provision step would do), then
149
+ # run the scan. On clean baseline the scan will record FAIL because the
150
+ # exemption branch does not exist. After implementation the scan records PASS
151
+ # because the provisioned .env is exempt.
152
+
153
+ # Ensure a .env (symlink) is present in the worktree before running scan.
154
+ # If the provision step (assertion 1) already created it, this is a no-op.
155
+ if [[ ! -L "${TEST_WORKTREE}/.env" && ! -f "${TEST_WORKTREE}/.env" ]]; then
156
+ # Create the symlink manually for this assertion so we can test the scan
157
+ # exemption independently of the provision script.
158
+ ln -s "${FIXTURE_ENV}" "${TEST_WORKTREE}/.env"
159
+ fi
160
+
161
+ # Remove any stale scan report so we get a fresh run.
162
+ rm -f "${SCAN_REPORT}" 2>/dev/null || true
163
+
164
+ # Run pre_gate_runner.sh arch mode with ABSOLUTE worktree path (FLASHCARD
165
+ # #pre-gate #cwd-leak #worktree 2026-06-03: pass absolute path to avoid leak).
166
+ bash "${PRE_GATE_SCRIPT}" arch "${TEST_WORKTREE}" HEAD > /dev/null 2>&1 || true
167
+
168
+ if [[ -f "${SCAN_REPORT}" ]]; then
169
+ if grep -q '^\[PASS\] stray_env_files' "${SCAN_REPORT}"; then
170
+ pass "scan-exemption — stray_env_files PASS for provisioned .env"
171
+ else
172
+ # Expected on clean baseline: no exemption branch → records FAIL on .env
173
+ STRAY_LINE="$(grep 'stray_env_files' "${SCAN_REPORT}" | head -1 || echo '(no stray_env_files line)')"
174
+ fail "scan-exemption — stray_env_files should record PASS for provisioned .env" \
175
+ "report line: ${STRAY_LINE}"
176
+ fi
177
+ else
178
+ fail "scan-report-exists" \
179
+ "pre-arch-scan.txt not written at ${SCAN_REPORT}"
180
+ fi
181
+
182
+ # ────────────────────────────────────────────────────────────────────────────
183
+ # ASSERTION 3: Negative control — .env.local still triggers FAIL
184
+ # ────────────────────────────────────────────────────────────────────────────
185
+ # .env.local is in stray_env_files list but NOT in provision_config default.
186
+ # Drop it into the worktree, run a fresh scan, assert stray_env_files FAIL.
187
+
188
+ printf 'NOT_PROVISIONED=1\n' > "${TEST_WORKTREE}/.env.local"
189
+
190
+ # Remove the previous scan report so we get a completely fresh scan.
191
+ rm -f "${SCAN_REPORT}" 2>/dev/null || true
192
+
193
+ bash "${PRE_GATE_SCRIPT}" arch "${TEST_WORKTREE}" HEAD > /dev/null 2>&1 || true
194
+
195
+ if [[ -f "${SCAN_REPORT}" ]]; then
196
+ if grep -q '^\[FAIL\] stray_env_files' "${SCAN_REPORT}"; then
197
+ pass "negative-control — .env.local (non-provisioned) triggers stray_env_files FAIL"
198
+ else
199
+ NEG_LINE="$(grep 'stray_env_files' "${SCAN_REPORT}" | head -1 || echo '(no stray_env_files line)')"
200
+ fail "negative-control — .env.local should trigger stray_env_files FAIL (exemption must be scoped)" \
201
+ "report line: ${NEG_LINE}"
202
+ fi
203
+ else
204
+ fail "negative-control-report-exists" \
205
+ "pre-arch-scan.txt not written at ${SCAN_REPORT} for negative-control scan"
206
+ fi
207
+
208
+ # Remove the negative-control file so teardown sees a clean worktree
209
+ rm -f "${TEST_WORKTREE}/.env.local"
210
+
211
+ # ────────────────────────────────────────────────────────────────────────────
212
+ # ASSERTION 4: Teardown verification
213
+ # ────────────────────────────────────────────────────────────────────────────
214
+ # Remove the worktree and verify no dangling symlink at repo root and no
215
+ # leftover fixture .env. The EXIT trap is the safety net; here we verify
216
+ # explicitly so we can record PASS/FAIL before returning.
217
+
218
+ git -C "${REPO_ROOT}" worktree remove --force "${TEST_WORKTREE}" 2>/dev/null || true
219
+ git -C "${REPO_ROOT}" worktree prune 2>/dev/null || true
220
+ if [[ -d "${TEST_WORKTREE}" ]]; then
221
+ rm -rf "${TEST_WORKTREE}"
222
+ fi
223
+ if git -C "${REPO_ROOT}" branch --list "${TEST_BRANCH}" | grep -q "${TEST_BRANCH}"; then
224
+ git -C "${REPO_ROOT}" branch -D "${TEST_BRANCH}" 2>/dev/null || true
225
+ fi
226
+
227
+ # Assertion: no dangling symlink at repo root (.env symlink lives INSIDE the
228
+ # worktree pointing OUT; removing the worktree removes the symlink).
229
+ if [[ -L "${FIXTURE_ENV}" ]]; then
230
+ fail "teardown-no-dangling-symlink" \
231
+ ".env at repo root is a symlink after worktree removal — dangling"
232
+ else
233
+ pass "teardown-no-dangling-symlink — no dangling symlink at repo root"
234
+ fi
235
+
236
+ # Assertion: worktree is no longer registered
237
+ if git -C "${REPO_ROOT}" worktree list 2>/dev/null | grep -q "TEST-079"; then
238
+ fail "teardown-worktree-removed" \
239
+ ".worktrees/TEST-079 still registered in git worktree list"
240
+ else
241
+ pass "teardown-worktree-removed — .worktrees/TEST-079 removed"
242
+ fi
243
+
244
+ # Assertion: fixture .env at repo root is cleaned up (if harness created it)
245
+ if [[ "${HARNESS_CREATED_ENV}" -eq 1 ]]; then
246
+ rm -f "${FIXTURE_ENV}"
247
+ HARNESS_CREATED_ENV=0 # prevent double-remove in trap
248
+ if [[ -e "${FIXTURE_ENV}" ]]; then
249
+ fail "teardown-fixture-env-removed" \
250
+ "fixture .env still present at ${FIXTURE_ENV} after cleanup"
251
+ else
252
+ pass "teardown-fixture-env-removed — fixture .env cleaned up"
253
+ fi
254
+ fi
255
+
256
+ # ── Summary ───────────────────────────────────────────────────────────────────
257
+ echo ""
258
+ echo "cr079_provision.red.sh: ${PASS} passed, ${FAIL} failed"
259
+ if [[ "${FAIL}" -gt 0 ]]; then
260
+ exit 1
261
+ fi
262
+ exit 0