@mmerterden/multi-agent-pipeline 12.7.0 → 12.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/install/_common.mjs +48 -0
  3. package/install/_dev-only-files.mjs +125 -5
  4. package/install/claude.mjs +14 -8
  5. package/install/copilot.mjs +5 -8
  6. package/package.json +17 -2
  7. package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
  8. package/pipeline/lib/credential-store.sh +20 -0
  9. package/pipeline/multi-agent-refs/_account-picker.md +1 -1
  10. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  11. package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
  12. package/pipeline/multi-agent-refs/phases/operations.md +28 -0
  13. package/pipeline/multi-agent-refs/phases/phase-0-init.md +1 -0
  14. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
  15. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
  16. package/pipeline/multi-agent-refs/phases/phase-4-review.md +49 -4
  17. package/pipeline/schemas/prefs.schema.json +6 -0
  18. package/pipeline/scripts/_smoke-root.sh +61 -0
  19. package/pipeline/scripts/audit-log.sh +25 -0
  20. package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
  21. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
  22. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
  23. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
  24. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
  25. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
  26. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
  27. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
  28. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
  29. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
  30. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
  31. package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
  32. package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
  33. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
  34. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
  35. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
  36. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
  37. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
  38. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
  39. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
  40. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
  41. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
  42. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
  43. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
  44. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
  45. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
  46. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
  47. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
  48. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
  49. package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
  50. package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
  51. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
  52. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
  53. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
  54. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
  55. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
  56. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
  57. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
  58. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
  59. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
  60. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
  61. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
  62. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
  63. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
  64. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
  65. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
  66. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
  67. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
  68. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
  69. package/pipeline/eval/golden-tasks/README.md +0 -65
  70. package/pipeline/eval/intent-cases.json +0 -40
  71. package/pipeline/eval/run-metrics-fixture.json +0 -93
  72. package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
  73. package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
  74. package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
  75. package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
  76. package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
  77. package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
  78. package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
  79. package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
  80. package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
  81. package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
  82. package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
  83. package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
  84. package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
  85. package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
  86. package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
  87. package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
  88. package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
  89. package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
  90. package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
  91. package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
  92. package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
  93. package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
  94. package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
  95. package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
  96. package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
  97. package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
  98. package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
  99. package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
  100. package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
  101. package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
  102. package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
  103. package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
  104. package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
  105. package/pipeline/eval/triage/README.md +0 -54
  106. package/pipeline/scripts/benchmark-phase-0.sh +0 -128
  107. package/pipeline/scripts/check-md-links.mjs +0 -88
  108. package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
  109. package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
  110. package/pipeline/scripts/eval-intent.mjs +0 -107
  111. package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
  112. package/pipeline/scripts/eval-triage.mjs +0 -171
  113. package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
  114. package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
  115. package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
  116. package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
  117. package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
  118. package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
  119. package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
  120. package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
  121. package/pipeline/scripts/lint-skills.mjs +0 -154
  122. package/pipeline/scripts/run-smokes.mjs +0 -130
  123. package/pipeline/scripts/scorecard.mjs +0 -258
  124. package/pipeline/scripts/smoke-add-detail.sh +0 -137
  125. package/pipeline/scripts/smoke-agent-guard.sh +0 -74
  126. package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
  127. package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
  128. package/pipeline/scripts/smoke-ask-choice.sh +0 -42
  129. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
  130. package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
  131. package/pipeline/scripts/smoke-changelog-version.sh +0 -47
  132. package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
  133. package/pipeline/scripts/smoke-channels-flow.sh +0 -130
  134. package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
  135. package/pipeline/scripts/smoke-clarify.sh +0 -148
  136. package/pipeline/scripts/smoke-command-inventory.sh +0 -81
  137. package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
  138. package/pipeline/scripts/smoke-community-gates.sh +0 -75
  139. package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
  140. package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
  141. package/pipeline/scripts/smoke-context-budget.sh +0 -72
  142. package/pipeline/scripts/smoke-cost-budget.sh +0 -70
  143. package/pipeline/scripts/smoke-cost-summary.sh +0 -139
  144. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
  145. package/pipeline/scripts/smoke-description-tr.sh +0 -82
  146. package/pipeline/scripts/smoke-dev-critic.sh +0 -144
  147. package/pipeline/scripts/smoke-diff-explain.sh +0 -147
  148. package/pipeline/scripts/smoke-diff-risk.sh +0 -190
  149. package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
  150. package/pipeline/scripts/smoke-eval-live.sh +0 -136
  151. package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
  152. package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
  153. package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
  154. package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
  155. package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
  156. package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
  157. package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
  158. package/pipeline/scripts/smoke-generate-issue.sh +0 -120
  159. package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
  160. package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
  161. package/pipeline/scripts/smoke-install-layout.sh +0 -248
  162. package/pipeline/scripts/smoke-intent-guard.sh +0 -86
  163. package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
  164. package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
  165. package/pipeline/scripts/smoke-keychain.sh +0 -158
  166. package/pipeline/scripts/smoke-language-axis.sh +0 -109
  167. package/pipeline/scripts/smoke-learning-curve.sh +0 -61
  168. package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
  169. package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
  170. package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
  171. package/pipeline/scripts/smoke-md-links.sh +0 -8
  172. package/pipeline/scripts/smoke-md2confluence.sh +0 -126
  173. package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
  174. package/pipeline/scripts/smoke-migrate-state.sh +0 -102
  175. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
  176. package/pipeline/scripts/smoke-model-fallback.sh +0 -89
  177. package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
  178. package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
  179. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
  180. package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
  181. package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
  182. package/pipeline/scripts/smoke-pack-contents.sh +0 -140
  183. package/pipeline/scripts/smoke-pat-audit.sh +0 -128
  184. package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
  185. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
  186. package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
  187. package/pipeline/scripts/smoke-phase-banner.sh +0 -101
  188. package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
  189. package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
  190. package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
  191. package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
  192. package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
  193. package/pipeline/scripts/smoke-plan-safety.sh +0 -139
  194. package/pipeline/scripts/smoke-plan-todos.sh +0 -196
  195. package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
  196. package/pipeline/scripts/smoke-pre-commit.sh +0 -170
  197. package/pipeline/scripts/smoke-pref-migration.sh +0 -226
  198. package/pipeline/scripts/smoke-prefs-language.sh +0 -134
  199. package/pipeline/scripts/smoke-progress-contract.sh +0 -127
  200. package/pipeline/scripts/smoke-prune-logs.sh +0 -137
  201. package/pipeline/scripts/smoke-purge.sh +0 -138
  202. package/pipeline/scripts/smoke-push-retry.sh +0 -75
  203. package/pipeline/scripts/smoke-repo-map.sh +0 -300
  204. package/pipeline/scripts/smoke-review-readiness.sh +0 -92
  205. package/pipeline/scripts/smoke-review-watch.sh +0 -146
  206. package/pipeline/scripts/smoke-routines.sh +0 -84
  207. package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
  208. package/pipeline/scripts/smoke-run-metrics.sh +0 -50
  209. package/pipeline/scripts/smoke-search.sh +0 -187
  210. package/pipeline/scripts/smoke-shadow-git.sh +0 -224
  211. package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
  212. package/pipeline/scripts/smoke-skill-language.sh +0 -83
  213. package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
  214. package/pipeline/scripts/smoke-skill-scan.sh +0 -198
  215. package/pipeline/scripts/smoke-source-parity.sh +0 -85
  216. package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
  217. package/pipeline/scripts/smoke-sync-parity.sh +0 -92
  218. package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
  219. package/pipeline/scripts/smoke-telemetry.sh +0 -147
  220. package/pipeline/scripts/smoke-test-gap.sh +0 -183
  221. package/pipeline/scripts/smoke-token-budget.sh +0 -67
  222. package/pipeline/scripts/smoke-token-preflight.sh +0 -82
  223. package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
  224. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
  225. package/pipeline/scripts/smoke-triage-memory.sh +0 -174
  226. package/pipeline/scripts/smoke-update-check.sh +0 -135
  227. package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
  228. package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
  229. package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
  230. package/pipeline/scripts/smoke-validator-gates.sh +0 -164
  231. package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
  232. package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
  233. package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
  234. package/pipeline/scripts/smoke-work-summary.sh +0 -163
  235. package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
  236. package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
  237. package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
  238. package/pipeline/scripts/smoke-write-state.sh +0 -159
  239. package/pipeline/scripts/sync-parity-check.sh +0 -135
  240. package/pipeline/scripts/test-gap-rules/android.json +0 -25
  241. package/pipeline/scripts/test-gap-rules/ios.json +0 -34
  242. package/pipeline/scripts/test-gap-rules/node.json +0 -29
  243. package/pipeline/scripts/test-gap-rules/python.json +0 -25
  244. package/pipeline/scripts/validate-schemas.mjs +0 -88
@@ -1,163 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-work-summary.sh - v7.1.0
3
- #
4
- # Verifies the Phase 7 Work Summary renderer contract:
5
- # 1. Missing state + no flags → exit 2 (caller should skip section)
6
- # 2. Full state fixture → renders header, scope, review, phases
7
- # 3. Scope section marks ✅ for done tasks and ⏳ for pending
8
- # 4. Review outcome suppressed when all buckets empty
9
- # 5. Phase tick strip uses correct marks (completed ✅ · in_progress ▶ · skipped ⏭)
10
- # 6. Post-hoc invocation with --branch + --base-branch (no state file) still renders header
11
- # 7. prefs schema exposes reportContent.workSummary toggle
12
- # 8. channels.md documents the Work Summary option
13
- #
14
- # Exit 0 all pass, 1 any failure.
15
-
16
- set -euo pipefail
17
-
18
- ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
19
- RENDER="$ROOT/pipeline/scripts/render-work-summary.sh"
20
- SCHEMA="$ROOT/pipeline/schemas/prefs.schema.json"
21
- CHANNELS_DOC="$ROOT/pipeline/commands/multi-agent/channels/SKILL.md"
22
-
23
- pass=0; fail=0; failures=()
24
- record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
25
- record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
26
-
27
- printf '→ smoke-work-summary (v7.1.0): Phase 7 Work Summary renderer contract\n'
28
-
29
- # --- 1: missing state → exit 2 ---
30
- tmp=$(mktemp -d); pushd "$tmp" >/dev/null
31
- set +e
32
- bash "$RENDER" GHOST-1 >/dev/null 2>&1
33
- rc=$?
34
- set -e
35
- if [ "$rc" = "2" ]; then
36
- record_pass "exit 2 when no state + no flags"
37
- else
38
- record_fail "expected exit 2 for empty state (got $rc)"
39
- fi
40
- popd >/dev/null; rm -rf "$tmp"
41
-
42
- # --- 2-5: full fixture ---
43
- tmp=$(mktemp -d); pushd "$tmp" >/dev/null
44
- mkdir -p .worktrees/PROJ-2001
45
- cat > .worktrees/PROJ-2001/agent-state.json <<'JSON'
46
- {"taskId":"PROJ-2001","branch":"feature/PROJ-2001-checkout-v2","baseBranch":"develop","prNumber":"#256",
47
- "planTodos":[
48
- {"id":"T1","title":"Add checkout acceptance tests","status":"done"},
49
- {"id":"T2","title":"Wire CheckoutViewModel","status":"done"},
50
- {"id":"T3","title":"Optimize cart recomposition","status":"deferred"}
51
- ],
52
- "reviewConsensus":{
53
- "accepted":[{"severity":"blocking","file":"ViewModel.swift","line":42,"issue":"race","fix":"lock","reviewer":"opus"}],
54
- "deferred":[],
55
- "rejected":[{"finding":{"severity":"suggestion","file":"x.swift","line":0,"issue":"style","reviewer":"sonnet"},"reason":"style-only"}],
56
- "approved":false
57
- }}
58
- JSON
59
- cat > .worktrees/PROJ-2001/phase-tracker.json <<'JSON'
60
- {"taskId":"PROJ-2001","phases":{
61
- "0":{"status":"completed"},"1":{"status":"completed"},"2":{"status":"completed"},
62
- "3":{"status":"completed"},"4":{"status":"completed"},"5":{"status":"skipped"},
63
- "6":{"status":"completed"},"7":{"status":"in_progress"}
64
- }}
65
- JSON
66
- out=$(bash "$RENDER" PROJ-2001)
67
- popd >/dev/null; rm -rf "$tmp"
68
-
69
- if grep -q '^### Work Summary' <<< "$out" && \
70
- grep -q 'PROJ-2001' <<< "$out" && \
71
- grep -q 'feature/PROJ-2001-checkout-v2' <<< "$out" && \
72
- grep -q '#256' <<< "$out"; then
73
- record_pass "renders task header with jira+branch+base+PR"
74
- else
75
- record_fail "task header missing fields"
76
- fi
77
-
78
- if grep -q '✅ T1' <<< "$out" && grep -q '✅ T2' <<< "$out"; then
79
- record_pass "scope marks done tasks with ✅"
80
- else
81
- record_fail "done tasks not marked ✅ (output: $(echo "$out" | head -20))"
82
- fi
83
-
84
- # deferred is not "done" - should get ⏳
85
- if grep -qE '⏳ T3' <<< "$out"; then
86
- record_pass "scope marks non-done tasks with ⏳"
87
- else
88
- record_fail "non-done tasks not marked ⏳"
89
- fi
90
-
91
- if grep -q '1 accepted · 0 deferred · 1 rejected · approved=false' <<< "$out"; then
92
- record_pass "review outcome line matches triage counts"
93
- else
94
- record_fail "review outcome line wrong (expected '1 accepted · 0 deferred · 1 rejected · approved=false')"
95
- fi
96
-
97
- if grep -q '0 Init ✅' <<< "$out" && \
98
- grep -q '5 Test ⏭' <<< "$out" && \
99
- grep -q '7 Report ▶' <<< "$out"; then
100
- record_pass "phase tick strip renders correct marks"
101
- else
102
- record_fail "phase tick marks incorrect"
103
- fi
104
-
105
- # --- 4b: review outcome suppressed when empty ---
106
- tmp=$(mktemp -d); pushd "$tmp" >/dev/null
107
- mkdir -p .worktrees/PROJ-2002
108
- cat > .worktrees/PROJ-2002/agent-state.json <<'JSON'
109
- {"taskId":"PROJ-2002","branch":"fix/PROJ-2002","baseBranch":"main",
110
- "planTodos":[{"id":"T1","title":"tiny fix","status":"done"}],
111
- "reviewConsensus":{"accepted":[],"deferred":[],"rejected":[],"approved":true}}
112
- JSON
113
- out=$(bash "$RENDER" PROJ-2002)
114
- popd >/dev/null; rm -rf "$tmp"
115
-
116
- if ! grep -q '#### Review outcome' <<< "$out"; then
117
- record_pass "review outcome hidden when all buckets empty"
118
- else
119
- record_fail "review outcome should be hidden when empty"
120
- fi
121
-
122
- # --- 6: post-hoc flags, no state ---
123
- tmp=$(mktemp -d); pushd "$tmp" >/dev/null
124
- out=$(bash "$RENDER" POST-HOC-1 --branch "bugfix/post-hoc" --base-branch develop 2>&1 || true)
125
- popd >/dev/null; rm -rf "$tmp"
126
- if grep -q '^### Work Summary' <<< "$out" && grep -q 'POST-HOC-1' <<< "$out"; then
127
- record_pass "post-hoc flags render header without state"
128
- else
129
- record_fail "post-hoc invocation should still render header"
130
- fi
131
-
132
- # --- 7: prefs schema ---
133
- if jq -e '.properties.global.properties.reportContent.properties.workSummary.default == false' "$SCHEMA" >/dev/null 2>&1; then
134
- record_pass "prefs schema exposes reportContent.workSummary (default false)"
135
- else
136
- record_fail "prefs schema missing reportContent.workSummary"
137
- fi
138
-
139
- # --- 8: channels.md docs ---
140
- if grep -q '### Work Summary' "$CHANNELS_DOC" && \
141
- grep -q 'Yapılan iş özeti' "$CHANNELS_DOC" && \
142
- grep -q 'Work summary generation' "$CHANNELS_DOC"; then
143
- record_pass "channels.md documents Work Summary content option + menu row + generation spec"
144
- else
145
- record_fail "channels.md missing Work Summary docs"
146
- fi
147
-
148
- # --- 9: v7.1.1 preview-first flow documented ---
149
- if grep -q 'Step 3a' "$CHANNELS_DOC" && \
150
- grep -q 'summary-first' "$CHANNELS_DOC" && \
151
- grep -q 'auto-tick' "$CHANNELS_DOC"; then
152
- record_pass "channels.md documents v7.1.1 preview-before-select flow"
153
- else
154
- record_fail "channels.md missing Step 3a summary-first flow docs"
155
- fi
156
-
157
- printf '\n══ work-summary smoke: %d passed, %d failed ══\n' "$pass" "$fail"
158
- if [ "$fail" -gt 0 ]; then
159
- printf '\nFailures:\n'
160
- for m in "${failures[@]}"; do printf ' - %s\n' "$m"; done
161
- exit 1
162
- fi
163
- exit 0
@@ -1,101 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # smoke-workflow-audit.sh - security + correctness audit of
4
- # `.github/workflows/` with the two dedicated external linters:
5
- #
6
- # - zizmor GitHub Actions security auditor (template injection,
7
- # credential persistence, cache poisoning, unpinned actions,
8
- # excessive permissions, ...). Runs offline, no network.
9
- # - actionlint workflow correctness linter (expression syntax, matrix
10
- # typos, shellcheck of run: blocks, ...).
11
- #
12
- # Any finding from either tool fails the smoke.
13
- #
14
- # WHY THIS IS STRICT BY DEFAULT
15
- #
16
- # Until now a missing binary produced a SKIP and the smoke exited 0, so it
17
- # reported "0 passed, 0 failed, 2 skipped" on the dev machine AND on the CI
18
- # runner - neither had the tools. ci-lite.yml justified not installing them by
19
- # saying to run the smoke locally "where the tools exist", but they did not
20
- # exist there either. The audit had therefore never run anywhere, while
21
- # appearing as a green step. When it was finally run it found nine real issues,
22
- # one of them high severity (a dependency cache restored into the job that
23
- # signs and publishes the release tarball).
24
- #
25
- # So the failure mode is inverted: absent tooling is a failure, and skipping is
26
- # something you opt into deliberately and locally.
27
- #
28
- # brew install zizmor actionlint # macOS
29
- # pipx install zizmor && go install github.com/rhysd/actionlint/cmd/actionlint@latest
30
- #
31
- # WORKFLOW_AUDIT_ALLOW_MISSING=1 downgrades a missing binary back to a SKIP for
32
- # a local run. It is ignored when CI is set: CI installs both, pinned, and a
33
- # runner that cannot must fail loudly rather than pass quietly.
34
- #
35
- # Structural baseline checks that need no external binary live in
36
- # smoke-ci-workflows.sh - this smoke is the deeper, tool-backed layer.
37
- #
38
- # Exit codes: 0=pass, 1=findings or missing tooling, 2=workflows missing
39
-
40
- set -uo pipefail
41
-
42
- REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
43
- WF_DIR="$REPO_ROOT/.github/workflows"
44
-
45
- [ -d "$WF_DIR" ] || { echo "FAIL: .github/workflows/ missing"; exit 2; }
46
-
47
- # In CI the escape hatch does not apply, whatever the environment says.
48
- ALLOW_MISSING="${WORKFLOW_AUDIT_ALLOW_MISSING:-0}"
49
- [ -n "${CI:-}" ] && ALLOW_MISSING=0
50
-
51
- PASS=0
52
- FAIL=0
53
- SKIPPED=0
54
- pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
55
- fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
56
- skip() { SKIPPED=$((SKIPPED + 1)); echo " ↷ SKIP: $1"; }
57
-
58
- # A tool that is not installed is a failure unless a local run opted out.
59
- missing_tool() {
60
- local tool="$1" install="$2"
61
- if [ "$ALLOW_MISSING" = "1" ]; then
62
- skip "$tool not on PATH, WORKFLOW_AUDIT_ALLOW_MISSING=1 ($install)"
63
- else
64
- fail "$tool not on PATH - the audit cannot run ($install)"
65
- fi
66
- }
67
-
68
- # ──────────────────────────────────────────────────────────────────────────
69
- echo "→ 1. zizmor (security audit, offline)"
70
- if command -v zizmor >/dev/null 2>&1; then
71
- # Flag names shifted across zizmor releases (--offline vs --no-online-audits);
72
- # probe --help so the smoke works with whichever is installed.
73
- HELP=$(zizmor --help 2>&1 || true)
74
- OFFLINE_FLAG="--offline"
75
- echo "$HELP" | grep -q -- '--offline' || OFFLINE_FLAG="--no-online-audits"
76
- if OUT=$(zizmor "$WF_DIR" --min-severity medium "$OFFLINE_FLAG" 2>&1); then
77
- pass "zizmor: no findings at medium+ severity"
78
- else
79
- fail "zizmor reported findings (medium+ severity)"
80
- echo "$OUT" | sed 's/^/ /'
81
- fi
82
- else
83
- missing_tool zizmor "brew install zizmor"
84
- fi
85
-
86
- # ──────────────────────────────────────────────────────────────────────────
87
- echo "→ 2. actionlint (workflow correctness)"
88
- if command -v actionlint >/dev/null 2>&1; then
89
- if OUT=$(cd "$REPO_ROOT" && actionlint 2>&1); then
90
- pass "actionlint: no findings"
91
- else
92
- fail "actionlint reported findings"
93
- echo "$OUT" | sed 's/^/ /'
94
- fi
95
- else
96
- missing_tool actionlint "brew install actionlint"
97
- fi
98
-
99
- echo ""
100
- echo "══ smoke-workflow-audit: $PASS passed, $FAIL failed, $SKIPPED skipped ══"
101
- [ "$FAIL" -gt 0 ] && exit 1 || exit 0
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # smoke-worktree-path-convention.sh
4
- #
5
- # Asserts the pipeline never creates worktrees outside `{projectRoot}/{worktreeBasePath}/{taskId}`.
6
- # Per `feedback_worktree_path_convention.md`:
7
- # - Worktree is ALWAYS under the project root, NEVER under $HOME directly.
8
- # - Default basePath is `.worktrees`, override via `prefs.global.worktreeBasePath`.
9
- # - Multi-repo tasks: each repo gets its own independent worktree under that repo's root.
10
- #
11
- # This is a static-analysis lint, not a runtime probe. It greps the pipeline
12
- # source for forbidden worktree paths in shell scripts, markdown contracts,
13
- # and orchestrator command files. A behavioural check (live `git worktree list`)
14
- # is out of scope - that would require a real run.
15
-
16
- set -euo pipefail
17
-
18
- SELF_DIR=$(cd "$(dirname "$0")" && pwd)
19
- PIPELINE_ROOT=$(cd "$SELF_DIR/.." && pwd)
20
-
21
- cd "$PIPELINE_ROOT"
22
-
23
- FAIL=0
24
-
25
- # Forbidden patterns: any worktree path that lives under $HOME directly,
26
- # or uses repo-name--taskId style outside a project root.
27
- declare -a FORBIDDEN=(
28
- '\$HOME/\.worktrees' # $HOME/.worktrees/foo - wrong (must be under projectRoot)
29
- '~/\.worktrees' # ~/.worktrees/foo - same
30
- '\$HOME/[a-zA-Z0-9_-]+--[a-zA-Z0-9_-]+' # $HOME/repo--taskid pattern
31
- '~/[a-zA-Z0-9_-]+--[a-zA-Z0-9_-]+' # ~/repo--taskid pattern
32
- 'git worktree add.*\$HOME/[^.]' # `git worktree add $HOME/something`
33
- 'git worktree add.*~/[^/]' # `git worktree add ~/something`
34
- )
35
-
36
- # In-scope files: shell + markdown phase contracts + orchestrator skills.
37
- TARGETS=(
38
- "scripts/*.sh"
39
- "scripts/*.mjs"
40
- "lib/*.sh"
41
- "commands/multi-agent/SKILL.md"
42
- "commands/multi-agent/*.md"
43
- "multi-agent-refs/*.md"
44
- "multi-agent-refs/phases/*.md"
45
- "skills/shared/core/multi-agent/SKILL.md"
46
- )
47
-
48
- # Allowlist file paths - reference docs that mention the wrong pattern only
49
- # to teach the rule are tagged with `# allow: worktree-bad-example` annotation
50
- # on the offending line.
51
- for pattern in "${FORBIDDEN[@]}"; do
52
- for glob in "${TARGETS[@]}"; do
53
- while IFS= read -r match; do
54
- [ -z "$match" ] && continue
55
- file="${match%%:*}"
56
- # Allow lines that are rule-explanation examples (annotated).
57
- if echo "$match" | grep -q '# allow: worktree-bad-example'; then continue; fi
58
- # Allow this very smoke gate's own forbidden-pattern array.
59
- case "$file" in
60
- */smoke-worktree-path-convention.sh) continue ;;
61
- */feedback_worktree_path_convention.md) continue ;;
62
- esac
63
- echo "FAIL [smoke-worktree-path-convention]: forbidden worktree path in $file"
64
- echo " $match"
65
- FAIL=$((FAIL + 1))
66
- done < <(grep -rEn "$pattern" "pipeline/$glob" 2>/dev/null || true)
67
- done
68
- done
69
-
70
- # Positive assertion: phase-0-init.md MUST mention the convention text.
71
- PHASE0="multi-agent-refs/phases/phase-0-init.md"
72
- if [ -f "pipeline/$PHASE0" ]; then
73
- if ! grep -qE 'projectRoot.*worktreeBasePath|\.worktrees/.*taskId' "pipeline/$PHASE0"; then
74
- echo "FAIL [smoke-worktree-path-convention]: phase-0-init.md must document {projectRoot}/{worktreeBasePath}/{taskId} convention"
75
- FAIL=$((FAIL + 1))
76
- fi
77
- fi
78
-
79
- if [ "$FAIL" -gt 0 ]; then
80
- echo ""
81
- echo "smoke-worktree-path-convention: $FAIL violation(s)."
82
- echo "Worktree path contract: {projectRoot}/{worktreeBasePath default .worktrees}/{taskId}. Never under \$HOME directly. See feedback_worktree_path_convention.md."
83
- exit 1
84
- fi
85
-
86
- echo "smoke-worktree-path-convention: ok"
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-wrapper-preservation.sh - local-only alias wrappers must survive install/update.
3
- #
4
- # Local-only wrappers (frontmatter `local-only: true`) live ONLY under
5
- # ~/.claude/commands/multi-agent/ (never in pipeline/, never synced). install
6
- # wipes that namespace before copying from pipeline; without the snapshot+restore
7
- # carve-out in install/claude.mjs installCommands, wrappers are destroyed on every
8
- # run. This asserts they survive a second install.
9
-
10
- set -uo pipefail
11
- HERE="$(cd "$(dirname "$0")" && pwd)"
12
- ROOT="$(cd "$HERE/../.." && pwd)"
13
- INSTALL_JS="$ROOT/install.js"
14
-
15
- PASS=0; FAIL=0
16
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
17
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
18
-
19
- TMP="$(mktemp -d)"
20
- trap 'rm -rf "$TMP"' EXIT
21
-
22
- echo "→ 1. first install lays down the namespace"
23
- if HOME="$TMP" node "$INSTALL_JS" --claude >/dev/null 2>&1; then
24
- pass "install --claude (1st) exited 0"
25
- else
26
- fail "install --claude (1st) failed"
27
- echo "══ wrapper-preservation smoke: $PASS passed, $FAIL failed ══"; exit 1
28
- fi
29
-
30
- WRAP_DIR="$TMP/.claude/commands/multi-agent/zz-wrapper-probe"
31
- mkdir -p "$WRAP_DIR"
32
- cat > "$WRAP_DIR/SKILL.md" <<'EOF'
33
- ---
34
- description: "probe local-only wrapper"
35
- allowed-tools: Skill
36
- local-only: true
37
- ---
38
- Use the Skill tool to invoke `some-plugin:some-skill`.
39
- EOF
40
- [ -f "$WRAP_DIR/SKILL.md" ] && pass "seeded a local-only wrapper" || fail "could not seed wrapper"
41
-
42
- echo ""
43
- echo "→ 2. second install must PRESERVE the wrapper"
44
- if HOME="$TMP" node "$INSTALL_JS" --claude >/dev/null 2>&1; then
45
- pass "install --claude (2nd) exited 0"
46
- else
47
- fail "install --claude (2nd) failed"
48
- fi
49
- if [ -f "$WRAP_DIR/SKILL.md" ] && grep -q '^local-only: true' "$WRAP_DIR/SKILL.md"; then
50
- pass "local-only wrapper survived re-install"
51
- else
52
- fail "local-only wrapper was DESTROYED by re-install (carve-out regressed)"
53
- fi
54
-
55
- echo ""
56
- echo "→ 3. a normal (non-local-only) stray dir is still wiped"
57
- STRAY="$TMP/.claude/commands/multi-agent/zz-stray-probe"
58
- mkdir -p "$STRAY"; printf -- '---\ndescription: "stray"\n---\n' > "$STRAY/SKILL.md"
59
- HOME="$TMP" node "$INSTALL_JS" --claude >/dev/null 2>&1
60
- if [ -f "$STRAY/SKILL.md" ]; then
61
- fail "non-local-only stray survived (wipe too weak)"
62
- else
63
- pass "non-local-only stray correctly wiped"
64
- fi
65
-
66
- echo ""
67
- echo "══ wrapper-preservation smoke: $PASS passed, $FAIL failed ══"
68
- [ "$FAIL" -eq 0 ]
@@ -1,159 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-write-state.sh - verify write-state.mjs atomic write + merge + lock behavior.
3
-
4
- set -uo pipefail
5
-
6
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
7
- WRITER="$SCRIPT_DIR/write-state.mjs"
8
-
9
- if [ ! -f "$WRITER" ]; then
10
- echo "FAIL: write-state.mjs not found" >&2
11
- exit 1
12
- fi
13
-
14
- PASS=0
15
- FAIL=0
16
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
17
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
18
-
19
- TMPDIR=$(mktemp -d) || { echo "FAIL: mktemp" >&2; exit 1; }
20
- trap 'rm -rf "$TMPDIR"' EXIT
21
-
22
- STATE="$TMPDIR/state.json"
23
-
24
- # 1. Initial write creates file
25
- echo '{"currentPhase":0,"status":"in_progress"}' | node "$WRITER" "$STATE"
26
- if [ -f "$STATE" ] && grep -q '"currentPhase": 0' "$STATE"; then
27
- pass "initial write creates file"
28
- else
29
- fail "initial write missing/malformed"
30
- fi
31
-
32
- # 2. Merge preserves existing fields
33
- echo '{"currentPhase":3}' | node "$WRITER" "$STATE"
34
- if grep -q '"currentPhase": 3' "$STATE" && grep -q '"status": "in_progress"' "$STATE"; then
35
- pass "merge preserves untouched fields"
36
- else
37
- fail "merge dropped fields"
38
- fi
39
-
40
- # 3. Nested merge
41
- echo '{"phases":{"3":{"status":"done"}}}' | node "$WRITER" "$STATE"
42
- echo '{"phases":{"4":{"status":"in_progress"}}}' | node "$WRITER" "$STATE"
43
- if grep -q '"3"' "$STATE" && grep -q '"4"' "$STATE"; then
44
- pass "nested merge preserves sibling keys"
45
- else
46
- fail "nested merge overwrote sibling keys"
47
- fi
48
-
49
- # 4. --replace overwrites completely
50
- echo '{"currentPhase":7}' | node "$WRITER" --replace "$STATE"
51
- if ! grep -q '"status"' "$STATE" && grep -q '"currentPhase": 7' "$STATE"; then
52
- pass "--replace overwrites full state"
53
- else
54
- fail "--replace did not fully overwrite"
55
- fi
56
-
57
- # 5. Invalid JSON exits 1
58
- echo 'not-json' | node "$WRITER" "$TMPDIR/other.json" 2>/dev/null
59
- if [ $? -eq 1 ]; then
60
- pass "invalid JSON exits 1"
61
- else
62
- fail "invalid JSON did not exit 1"
63
- fi
64
-
65
- # 6. Empty stdin exits 1
66
- : | node "$WRITER" "$TMPDIR/empty.json" 2>/dev/null
67
- if [ $? -eq 1 ]; then
68
- pass "empty stdin exits 1"
69
- else
70
- fail "empty stdin did not exit 1"
71
- fi
72
-
73
- # 7. Missing path arg exits 3
74
- echo '{}' | node "$WRITER" 2>/dev/null
75
- if [ $? -eq 3 ]; then
76
- pass "missing path exits 3"
77
- else
78
- fail "missing path did not exit 3"
79
- fi
80
-
81
- # 8. Atomic - no .tmp leftover after success
82
- if ls "$TMPDIR"/.state.json.*.tmp 2>/dev/null | head -1 | grep -q .; then
83
- fail "leftover .tmp file after success"
84
- else
85
- pass "no .tmp leftover after successful write"
86
- fi
87
-
88
- # 9. No .lock leftover after success
89
- if [ -f "$STATE.lock" ]; then
90
- fail "leftover .lock file after success"
91
- else
92
- pass "no .lock leftover after successful write"
93
- fi
94
-
95
- # 10. Concurrent writers don't corrupt state
96
- #
97
- # Was 5 writers, which lost an update roughly one run in five on a loaded
98
- # machine and never on an idle one, so it read as a flaky test rather than the
99
- # data-loss bug it was. 20 writers puts enough writers in the acquire window to
100
- # make a regression show up on an idle machine too.
101
- STATE2="$TMPDIR/concurrent.json"
102
- WRITERS=20
103
- echo '{"counter":0}' | node "$WRITER" "$STATE2"
104
- for i in $(seq 1 "$WRITERS"); do
105
- echo "{\"w$i\":$i}" | node "$WRITER" "$STATE2" &
106
- done
107
- wait
108
- MISSING=0
109
- for i in $(seq 1 "$WRITERS"); do
110
- grep -q "\"w$i\": $i" "$STATE2" || MISSING=$((MISSING+1))
111
- done
112
- if [ "$MISSING" -eq 0 ]; then
113
- pass "$WRITERS concurrent writers all persisted"
114
- else
115
- fail "$MISSING of $WRITERS concurrent writes lost"
116
- fi
117
-
118
- # 11. A lock with no readable PID must not be treated as stale
119
- #
120
- # This is the exact shape of the bug above. The lock used to be created empty
121
- # and filled on the next line, so a writer that arrived in between read an
122
- # unparseable PID, called the lock stale, and deleted it while its owner was
123
- # still working. Both then wrote and one update vanished.
124
- #
125
- # An empty lock now has to block until the staleness window elapses, so a
126
- # writer given a 1s acquire budget must give up with exit 2 rather than help
127
- # itself to the lock.
128
- STATE3="$TMPDIR/emptylock.json"
129
- echo '{"seed":1}' | node "$WRITER" "$STATE3"
130
- : > "$STATE3.lock"
131
- LOCK_RC=0
132
- echo '{"stolen":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=1000 WRITE_STATE_LOCK_STALE_MS=30000 \
133
- node "$WRITER" "$STATE3" >/dev/null 2>&1 || LOCK_RC=$?
134
- if [ "$LOCK_RC" -eq 2 ] && ! grep -q '"stolen"' "$STATE3"; then
135
- pass "empty lock is not stolen (exit 2, state untouched)"
136
- else
137
- fail "empty lock was treated as stale (exit $LOCK_RC) - the live-lock race is back"
138
- fi
139
- rm -f "$STATE3.lock"
140
-
141
- # 12. A lock older than the staleness window is still reclaimed
142
- #
143
- # The counterpart to 11: refusing to reclaim an unreadable PID must not mean a
144
- # genuinely abandoned lock blocks forever.
145
- STATE4="$TMPDIR/stalelock.json"
146
- echo '{"seed":1}' | node "$WRITER" "$STATE4"
147
- : > "$STATE4.lock"
148
- STALE_RC=0
149
- echo '{"recovered":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=3000 WRITE_STATE_LOCK_STALE_MS=1 \
150
- node "$WRITER" "$STATE4" >/dev/null 2>&1 || STALE_RC=$?
151
- if [ "$STALE_RC" -eq 0 ] && grep -q '"recovered"' "$STATE4"; then
152
- pass "abandoned lock past the staleness window is reclaimed"
153
- else
154
- fail "stale lock not reclaimed (exit $STALE_RC) - writers would wedge"
155
- fi
156
-
157
- echo ""
158
- echo "══ write-state smoke: $PASS passed, $FAIL failed ══"
159
- [ "$FAIL" -eq 0 ] || exit 1