@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,147 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-telemetry.sh - contract check for log-metric.sh + aggregate-metrics.mjs.
3
- #
4
- # Verifies:
5
- # 1. log-metric.sh appends valid JSONL (one line, parses, has required fields).
6
- # 2. Type coercion works (booleans, ints, strings).
7
- # 3. Concurrent appends don't interleave (10 parallel writers → 10 lines).
8
- # 4. aggregate-metrics.mjs handles missing file gracefully.
9
- # 5. aggregate-metrics.mjs computes accept_rate / reject_rate correctly.
10
- # 6. aggregate-metrics.mjs --json produces valid JSON.
11
-
12
- set -uo pipefail
13
-
14
- HERE="$(cd "$(dirname "$0")" && pwd)"
15
- ROOT="$(cd "$HERE/../.." && pwd)"
16
- TMP="$(mktemp -d -t multi-agent-smoke-XXXX)"
17
- trap 'rm -rf "$TMP"' EXIT
18
-
19
- PASS=0
20
- FAIL=0
21
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
22
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
23
-
24
- need() { command -v "$1" >/dev/null 2>&1 || { echo "error: $1 is required" >&2; exit 127; }; }
25
- need node
26
- need jq
27
-
28
- LOG="$ROOT/pipeline/scripts/log-metric.sh"
29
- AGG="$ROOT/pipeline/scripts/aggregate-metrics.mjs"
30
- export METRICS_FILE="$TMP/metrics.jsonl"
31
-
32
- echo "→ 1. log-metric writes a JSONL line with required fields"
33
- "$LOG" TASK-1 4 review.completed raw_count=8 accepted=3 deferred=2 rejected=3 approved=true
34
- LINE=$(tail -1 "$METRICS_FILE")
35
- if echo "$LINE" | jq -e '.ts and .task_id and .phase and .event and .details' >/dev/null; then
36
- pass "JSONL line has all required fields"
37
- else
38
- fail "missing required field in: $LINE"
39
- fi
40
-
41
- echo ""
42
- echo "→ 2. type coercion: bool, int, string"
43
- TYPES=$(echo "$LINE" | jq -r '.details | "approved=" + (.approved | type) + " accepted=" + (.accepted | type) + " raw_count=" + (.raw_count | type)')
44
- if [ "$TYPES" = "approved=boolean accepted=number raw_count=number" ]; then
45
- pass "bool/int coerce correctly: $TYPES"
46
- else
47
- fail "type coercion wrong: $TYPES"
48
- fi
49
- "$LOG" TASK-1 4 triage.edge_case case=high-rejection-rate
50
- STR_TYPE=$(tail -1 "$METRICS_FILE" | jq -r '.details.case | type')
51
- if [ "$STR_TYPE" = "string" ]; then
52
- pass "non-numeric strings stay strings"
53
- else
54
- fail "string coercion wrong: got $STR_TYPE"
55
- fi
56
-
57
- echo ""
58
- echo "→ 3. concurrent appends (10 parallel writers → 10 lines)"
59
- rm -f "$METRICS_FILE"
60
- PIDS=()
61
- for i in 1 2 3 4 5 6 7 8 9 10; do
62
- "$LOG" TASK-$i 4 review.completed raw_count=$i accepted=0 deferred=0 rejected=0 approved=true &
63
- PIDS+=($!)
64
- done
65
- FAILED=0
66
- for pid in "${PIDS[@]}"; do
67
- wait "$pid" || FAILED=$((FAILED+1))
68
- done
69
- if [ "$FAILED" -gt 0 ]; then
70
- fail "$FAILED of 10 background log-metric calls failed"
71
- fi
72
- LINE_COUNT=$(wc -l < "$METRICS_FILE" | tr -d ' ')
73
- if [ "$LINE_COUNT" = "10" ]; then
74
- pass "10 concurrent writes produced 10 lines (no interleaving)"
75
- else
76
- fail "expected 10 lines, got $LINE_COUNT"
77
- fi
78
- PARSE_OK=$(awk '{ print }' "$METRICS_FILE" | jq -e '.task_id' >/dev/null && echo ok || echo fail)
79
- if [ "$PARSE_OK" = "ok" ]; then
80
- pass "all 10 lines parse as valid JSON"
81
- else
82
- fail "concurrent append corrupted JSON"
83
- fi
84
-
85
- echo ""
86
- echo "→ 4. aggregator handles missing file gracefully"
87
- EXIT_OK=0
88
- node "$AGG" --json >/dev/null 2>&1 || EXIT_OK=$?
89
- # Above call uses default METRICS_FILE which IS set, so let's force missing.
90
- unset METRICS_FILE
91
- EXIT_MISSING=0
92
- METRICS_FILE="$TMP/does-not-exist.jsonl" node "$AGG" --json > "$TMP/missing-out.json" 2>&1 || EXIT_MISSING=$?
93
- if [ "$EXIT_MISSING" = "0" ] && jq -e '.ok == false' "$TMP/missing-out.json" >/dev/null 2>&1; then
94
- pass "missing file → exit 0 + ok:false"
95
- else
96
- fail "missing file handling wrong (exit=$EXIT_MISSING)"
97
- fi
98
- export METRICS_FILE="$TMP/metrics.jsonl"
99
-
100
- echo ""
101
- echo "→ 5. accept_rate and reject_rate computed correctly"
102
- rm -f "$METRICS_FILE"
103
- "$LOG" T 4 review.completed raw_count=10 accepted=4 deferred=2 rejected=4 approved=true
104
- "$LOG" T 4 review.completed raw_count=10 accepted=2 deferred=4 rejected=4 approved=true
105
- # Total: raw=20, accepted=6, deferred=6, rejected=8 → accept=0.30, reject=0.40
106
- SUMMARY=$(node "$AGG" --json)
107
- ACCEPT_RATE=$(echo "$SUMMARY" | jq -r '.triage_totals.accept_rate')
108
- REJECT_RATE=$(echo "$SUMMARY" | jq -r '.triage_totals.reject_rate')
109
- if [ "$ACCEPT_RATE" = "0.3" ] && [ "$REJECT_RATE" = "0.4" ]; then
110
- pass "accept_rate=$ACCEPT_RATE reject_rate=$REJECT_RATE (expected 0.3 / 0.4)"
111
- else
112
- fail "rates wrong: accept=$ACCEPT_RATE reject=$REJECT_RATE"
113
- fi
114
-
115
- echo ""
116
- echo "→ 6. --json output is valid JSON"
117
- if echo "$SUMMARY" | jq empty >/dev/null 2>&1; then
118
- pass "--json output parses"
119
- else
120
- fail "--json output is not valid JSON"
121
- fi
122
-
123
- echo ""
124
- echo "→ 7. cost_per_model section aggregates per-model totals"
125
- rm -f "$METRICS_FILE"
126
- "$LOG" T 4 review.completed model=opus duration_ms=12000 tokens_in=4500 tokens_out=600
127
- "$LOG" T 4 review.completed model=opus duration_ms=8000 tokens_in=3000 tokens_out=400
128
- "$LOG" T 4 review.completed model=sonnet duration_ms=5000 tokens_in=4500 tokens_out=350
129
- SUMMARY=$(node "$AGG" --json)
130
- OPUS_CALLS=$(echo "$SUMMARY" | jq -r '.cost_per_model.opus.calls')
131
- OPUS_TOKENS_IN=$(echo "$SUMMARY" | jq -r '.cost_per_model.opus.tokens_in')
132
- SONNET_DURATION=$(echo "$SUMMARY" | jq -r '.cost_per_model.sonnet.duration_ms')
133
- [ "$OPUS_CALLS" = "2" ] && pass "opus calls summed (=2)" || fail "opus calls expected 2, got $OPUS_CALLS"
134
- [ "$OPUS_TOKENS_IN" = "7500" ] && pass "opus tokens_in summed (=7500)" || fail "opus tokens_in expected 7500, got $OPUS_TOKENS_IN"
135
- [ "$SONNET_DURATION" = "5000" ] && pass "sonnet duration_ms summed (=5000)" || fail "sonnet duration_ms expected 5000, got $SONNET_DURATION"
136
-
137
- echo ""
138
- echo "→ 8. cost section absent when no model fields present"
139
- rm -f "$METRICS_FILE"
140
- "$LOG" T 0 init.completed phases=8
141
- SUMMARY=$(node "$AGG" --json)
142
- COST_KEYS=$(echo "$SUMMARY" | jq -r '.cost_per_model | keys | length')
143
- [ "$COST_KEYS" = "0" ] && pass "cost_per_model is empty when no model fields emitted" || fail "expected 0 keys, got $COST_KEYS"
144
-
145
- echo ""
146
- echo "══ telemetry smoke: $PASS passed, $FAIL failed ══"
147
- [ "$FAIL" -eq 0 ]
@@ -1,183 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-test-gap.sh - v8.3.0
3
- #
4
- # Verifies the Phase 5 test-gap pipeline:
5
- # 1. Rules files load + parse for ios/android/python/node
6
- # 2. iOS fixture: scanner finds save/load/fetch as gaps; testRenders
7
- # isn't a gap because it's the test, not source
8
- # 3. Android fixture: composable_fun symbol gets `important` severity
9
- # 4. Python fixture: detects async_def + top_def, ignores test files
10
- # 5. Node fixture: catches named_export Parser; tokenize is covered by
11
- # paired test → no gap
12
- # 6. validate-test-gap.mjs accepts valid output, rejects malformed
13
- # 7. --severity-promote bumps suggestion → important
14
- # 8. Empty / wrong-stack diff exits 2
15
- # 9. phase-5-test.md ref doc declares the Test Gap Report step
16
- # 10. prefs.schema.json exposes testGap settings group
17
- #
18
- # Exit 0 = all pass, 1 = any failure.
19
-
20
- set -euo pipefail
21
-
22
- ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
23
- SCAN="$ROOT/pipeline/scripts/test-gap-scan.mjs"
24
- VAL="$ROOT/pipeline/scripts/validate-test-gap.mjs"
25
- PHASE5="$ROOT/pipeline/multi-agent-refs/phases/phase-5-test.md"
26
- PREFS="$ROOT/pipeline/schemas/prefs.schema.json"
27
- RULES_DIR="$ROOT/pipeline/scripts/test-gap-rules"
28
- FIX_IOS="$ROOT/pipeline/scripts/fixtures/diff-risk-ios.diff"
29
- FIX_AND="$ROOT/pipeline/scripts/fixtures/diff-risk-android.diff"
30
- FIX_PY="$ROOT/pipeline/scripts/fixtures/test-gap-python.diff"
31
- FIX_NODE="$ROOT/pipeline/scripts/fixtures/test-gap-node.diff"
32
-
33
- pass=0
34
- fail=0
35
- failures=()
36
- record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
37
- record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
38
-
39
- printf '→ smoke-test-gap (v8.3.0): Phase 5 test-gap detector contract\n'
40
-
41
- # --- 1: rules files exist + parse for each stack ---
42
- for stack in ios android python node; do
43
- if jq -e '.stack and .sourceExtensions and .publicApiPatterns' "$RULES_DIR/$stack.json" >/dev/null 2>&1; then
44
- record_pass "$stack rules parse and contain stack/extensions/patterns"
45
- else
46
- record_fail "$stack rules malformed"
47
- fi
48
- done
49
-
50
- # --- 2: iOS fixture - KeychainStore.save/load + ApiClient.fetch detected ---
51
- out_ios=$(node "$SCAN" --diff "$FIX_IOS" --stack ios 2>/dev/null)
52
- gap_count=$(jq '.totals.gapCount' <<< "$out_ios")
53
- if [ "$gap_count" -ge 3 ]; then
54
- record_pass "iOS: at least 3 gaps reported (got $gap_count)"
55
- else
56
- record_fail "iOS: expected >=3 gaps, got $gap_count"
57
- fi
58
- if jq -e '.gaps[] | select(.symbol == "save")' <<< "$out_ios" >/dev/null 2>&1; then
59
- record_pass "iOS: KeychainStore.save flagged as gap"
60
- else
61
- record_fail "iOS: KeychainStore.save missing from gaps"
62
- fi
63
- if jq -e '.gaps[] | select(.symbol == "fetch")' <<< "$out_ios" >/dev/null 2>&1; then
64
- record_pass "iOS: ApiClient.fetch flagged as gap"
65
- else
66
- record_fail "iOS: ApiClient.fetch missing from gaps"
67
- fi
68
- # Test method itself shouldn't show up as a gap
69
- if jq -e '.gaps[] | select(.symbol == "testRenders")' <<< "$out_ios" >/dev/null 2>&1; then
70
- record_fail "iOS: testRenders should not be a gap (it's already a test)"
71
- else
72
- record_pass "iOS: test methods excluded from gap list"
73
- fi
74
-
75
- # --- 3: Android fixture - composable_fun → important severity ---
76
- out_and=$(node "$SCAN" --diff "$FIX_AND" --stack android 2>/dev/null)
77
- if jq -e '.gaps[] | select(.kind == "composable_fun" and .severity == "important")' <<< "$out_and" >/dev/null 2>&1; then
78
- record_pass "Android: composable_fun gets 'important' severity"
79
- else
80
- record_fail "Android: composable_fun severity wrong"
81
- fi
82
- if jq -e '.gaps[] | select(.symbol == "login")' <<< "$out_and" >/dev/null 2>&1; then
83
- record_pass "Android: AuthRepository.login flagged"
84
- else
85
- record_fail "Android: AuthRepository.login missing"
86
- fi
87
-
88
- # --- 4: Python fixture - async_def + top_def detected, test file ignored ---
89
- out_py=$(node "$SCAN" --diff "$FIX_PY" --stack python 2>/dev/null)
90
- if jq -e '.gaps[] | select(.kind == "async_def" and .symbol == "refund")' <<< "$out_py" >/dev/null 2>&1; then
91
- record_pass "Python: async_def refund detected"
92
- else
93
- record_fail "Python: async_def refund missing"
94
- fi
95
- if jq -e '.gaps[] | select(.symbol == "normalize_currency")' <<< "$out_py" >/dev/null 2>&1; then
96
- record_pass "Python: normalize_currency detected"
97
- else
98
- record_fail "Python: normalize_currency missing"
99
- fi
100
- if jq -e '.totals.testsScanned >= 1' <<< "$out_py" >/dev/null 2>&1; then
101
- record_pass "Python: test file recognised in counter"
102
- else
103
- record_fail "Python: testsScanned should include the new test_processor.py"
104
- fi
105
-
106
- # --- 5: Node fixture - Parser flagged, tokenize covered by paired test ---
107
- out_node=$(node "$SCAN" --diff "$FIX_NODE" --stack node 2>/dev/null)
108
- if jq -e '.gaps[] | select(.symbol == "Parser")' <<< "$out_node" >/dev/null 2>&1; then
109
- record_pass "Node: Parser export flagged"
110
- else
111
- record_fail "Node: Parser export missing from gaps"
112
- fi
113
- if jq -e '.gaps[] | select(.symbol == "tokenize")' <<< "$out_node" >/dev/null 2>&1; then
114
- record_fail "Node: tokenize should be covered by added test, not flagged"
115
- else
116
- record_pass "Node: tokenize correctly excluded (paired test mentions it)"
117
- fi
118
-
119
- # --- 6: Validator accepts/rejects ---
120
- set +e
121
- echo "$out_ios" | node "$VAL" - >/dev/null 2>&1
122
- rc_ok=$?
123
- echo '{"schemaVersion":"0.0.0","gaps":[]}' | node "$VAL" - >/dev/null 2>&1
124
- rc_bad=$?
125
- set -e
126
- if [ "$rc_ok" -eq 0 ]; then record_pass "validator accepts valid output"; else record_fail "validator rejected valid output"; fi
127
- if [ "$rc_bad" -ne 0 ]; then record_pass "validator rejects malformed"; else record_fail "validator should reject malformed"; fi
128
-
129
- # --- 7: --severity-promote ---
130
- out_prom=$(node "$SCAN" --diff "$FIX_IOS" --stack ios --severity-promote 2>/dev/null)
131
- if jq -e 'all(.gaps[]; .severity == "important")' <<< "$out_prom" >/dev/null 2>&1; then
132
- record_pass "--severity-promote forces all gaps to 'important'"
133
- else
134
- record_fail "--severity-promote did not promote severities"
135
- fi
136
-
137
- # --- 8: Empty diff exits 2 ---
138
- empty=$(mktemp)
139
- set +e
140
- node "$SCAN" --diff "$empty" --stack ios >/dev/null 2>&1
141
- rc=$?
142
- set -e
143
- rm -f "$empty"
144
- if [ "$rc" -eq 2 ]; then
145
- record_pass "empty diff exits 2"
146
- else
147
- record_fail "empty diff should exit 2 (got $rc)"
148
- fi
149
-
150
- # Wrong-stack: iOS diff scanned with python rules → no python sources → exit 2
151
- set +e
152
- node "$SCAN" --diff "$FIX_IOS" --stack python >/dev/null 2>&1
153
- rc=$?
154
- set -e
155
- if [ "$rc" -eq 2 ]; then
156
- record_pass "iOS diff with python rules exits 2 (no matching sources)"
157
- else
158
- record_fail "wrong-stack should exit 2 (got $rc)"
159
- fi
160
-
161
- # --- 9: phase-5-test.md declares the Test Gap Report step ---
162
- if grep -qE 'Test Gap Report|test-gap-scan\.mjs' "$PHASE5"; then
163
- record_pass "phase-5-test.md declares Test Gap Report step"
164
- else
165
- record_fail "phase-5-test.md missing Test Gap Report step"
166
- fi
167
-
168
- # --- 10: prefs schema exposes testGap group ---
169
- if jq -e '.properties.global.properties.testGap' "$PREFS" >/dev/null 2>&1; then
170
- record_pass "prefs schema exposes testGap settings group"
171
- else
172
- record_fail "prefs.schema.json missing global.testGap group"
173
- fi
174
-
175
- # --- Summary ---
176
- total=$((pass + fail))
177
- printf '\n→ smoke-test-gap: %d/%d passed\n' "$pass" "$total"
178
- if [ "$fail" -ne 0 ]; then
179
- printf '\nFailures:\n'
180
- for f in "${failures[@]}"; do printf ' - %s\n' "$f"; done
181
- exit 1
182
- fi
183
- exit 0
@@ -1,67 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-token-budget.sh - verify phase docs stay within token budget.
3
- # Reads token-budget.json and checks each phase doc's char count / 4.
4
- # Fails if any phase exceeds its max_tokens limit.
5
-
6
- set -euo pipefail
7
-
8
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
- PIPELINE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
10
- BUDGET_FILE="$PIPELINE_DIR/schemas/token-budget.json"
11
- PHASES_DIR="$PIPELINE_DIR/multi-agent-refs/phases"
12
-
13
- if [ ! -f "$BUDGET_FILE" ]; then
14
- echo "FAIL: token-budget.json not found at $BUDGET_FILE" >&2
15
- exit 1
16
- fi
17
-
18
- command -v jq >/dev/null 2>&1 || { echo "jq is required" >&2; exit 64; }
19
-
20
- PASS=0
21
- FAIL=0
22
- WARN=0
23
-
24
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
25
- warn() { WARN=$((WARN+1)); echo " ⚠ $1"; }
26
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
27
-
28
- TOTAL_TOKENS=0
29
- TOTAL_MAX=$(jq -r '.total_max_tokens' "$BUDGET_FILE")
30
-
31
- echo "→ Token budget check (chars/4 estimate)"
32
- echo ""
33
-
34
- for phase_key in $(jq -r '.phases | keys[]' "$BUDGET_FILE"); do
35
- file="$PHASES_DIR/${phase_key}.md"
36
- max_tokens=$(jq -r ".phases[\"$phase_key\"].max_tokens" "$BUDGET_FILE")
37
- warn_tokens=$(jq -r ".phases[\"$phase_key\"].warn_tokens" "$BUDGET_FILE")
38
-
39
- if [ ! -f "$file" ]; then
40
- fail "$phase_key: file not found ($file)"
41
- continue
42
- fi
43
-
44
- chars=$(wc -c < "$file" | tr -d ' ')
45
- tokens=$(( (chars + 3) / 4 ))
46
- TOTAL_TOKENS=$((TOTAL_TOKENS + tokens))
47
-
48
- if [ "$tokens" -gt "$max_tokens" ]; then
49
- fail "$phase_key: ${tokens} tokens > max ${max_tokens} (${chars} chars)"
50
- elif [ "$tokens" -gt "$warn_tokens" ]; then
51
- warn "$phase_key: ${tokens} tokens > warn ${warn_tokens} (within max ${max_tokens})"
52
- else
53
- pass "$phase_key: ${tokens} tokens ≤ ${warn_tokens} warn (max ${max_tokens})"
54
- fi
55
- done
56
-
57
- echo ""
58
- echo "→ Total phase doc budget"
59
- if [ "$TOTAL_TOKENS" -gt "$TOTAL_MAX" ]; then
60
- fail "total: ${TOTAL_TOKENS} tokens > max ${TOTAL_MAX}"
61
- else
62
- pass "total: ${TOTAL_TOKENS} tokens ≤ max ${TOTAL_MAX}"
63
- fi
64
-
65
- echo ""
66
- echo "══ token-budget smoke: $((PASS)) passed, $((WARN)) warnings, $((FAIL)) failed ══"
67
- [ "$FAIL" -eq 0 ]
@@ -1,82 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-token-preflight.sh - contract for the Phase 0 Step 0.7 token expiry pre-flight
3
- # and the Figma MCP token lifecycle (silent refresh + generation-script escalation).
4
- #
5
- # Verifies:
6
- # 1. phase-0-init.md has Step 0.7 with the probe table and figma-mcp-critical path.
7
- # 2. Silent renewal runs BEFORE any question; autopilot never prompts.
8
- # 3. keychain.md documents the Figma MCP lifecycle (refresh entry, tokenScripts, init cross-ref).
9
- # 4. lib/figma-mcp-refresh.sh exists, is executable, parses, and never prints token values.
10
- # 5. prefs.schema.json + preferences-template.json declare global.tokenScripts.
11
- # 6. Network failure is not treated as expiry; mid-run 401 stays as safety net.
12
-
13
- set -euo pipefail
14
-
15
- HERE="$(cd "$(dirname "$0")" && pwd)"
16
- ROOT="$(cd "$HERE/../.." && pwd)"
17
- PHASE0="$ROOT/pipeline/multi-agent-refs/phases/phase-0-init.md"
18
- KEYCHAIN="$ROOT/pipeline/multi-agent-refs/keychain.md"
19
- REFRESH="$ROOT/pipeline/lib/figma-mcp-refresh.sh"
20
- SCHEMA="$ROOT/pipeline/schemas/prefs.schema.json"
21
- TEMPLATE="$ROOT/pipeline/preferences-template.json"
22
-
23
- PASS=0
24
- FAIL=0
25
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
26
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
27
-
28
- need() { command -v "$1" >/dev/null 2>&1 || { echo "error: $1 required" >&2; exit 127; }; }
29
- need jq
30
-
31
- for f in "$PHASE0" "$KEYCHAIN" "$REFRESH" "$SCHEMA" "$TEMPLATE"; do
32
- [ -f "$f" ] || { echo "error: missing $f" >&2; exit 1; }
33
- done
34
-
35
- echo "→ 1. Step 0.7 pre-flight section"
36
- grep -Fq "Step 0.7 - Token expiry pre-flight" "$PHASE0" && pass "Step 0.7 present" || fail "Step 0.7 missing"
37
- for marker in "rest/api/2/myself" "mcp.figma.com/mcp" "serviceStatus" "CRITICAL"; do
38
- grep -Fq "$marker" "$PHASE0" && pass "probe marker: $marker" || fail "probe marker missing: $marker"
39
- done
40
-
41
- echo ""
42
- echo "→ 2. silent renewal first, autopilot never prompts"
43
- grep -Fq "figma-mcp-refresh.sh" "$PHASE0" && pass "silent renewal wired" || fail "silent renewal missing"
44
- grep -Fq "Silent renewal first" "$PHASE0" && pass "renewal precedes question" || fail "renewal-first rule missing"
45
- grep -Fq "never prompt" "$PHASE0" && pass "autopilot no-prompt rule" || fail "autopilot no-prompt rule missing"
46
- grep -Fq "Regenerate now (script)" "$PHASE0" && pass "script escalation option" || fail "script escalation option missing"
47
- grep -Fq "Continue degraded" "$PHASE0" && pass "degrade option" || fail "degrade option missing"
48
-
49
- echo ""
50
- echo "→ 3. keychain.md lifecycle section"
51
- grep -Fq "Figma MCP token lifecycle" "$KEYCHAIN" && pass "lifecycle section present" || fail "lifecycle section missing"
52
- grep -Fq "_Refresh" "$KEYCHAIN" && pass "refresh Keychain entry documented" || fail "refresh entry missing"
53
- grep -Fq "tokenScripts" "$KEYCHAIN" && pass "tokenScripts documented" || fail "tokenScripts missing"
54
- grep -Fq "Step 0.7" "$KEYCHAIN" && pass "init cross-ref present" || fail "init cross-ref missing"
55
- grep -Fq "never in synced command files" "$KEYCHAIN" && pass "personal-path hygiene rule" || fail "personal-path hygiene rule missing"
56
-
57
- echo ""
58
- echo "→ 4. figma-mcp-refresh.sh integrity"
59
- [ -x "$REFRESH" ] && pass "executable" || fail "not executable"
60
- bash -n "$REFRESH" && pass "parses (bash -n)" || fail "syntax error"
61
- grep -Fq "grant_type=refresh_token" "$REFRESH" && pass "refresh grant" || fail "refresh grant missing"
62
- if grep -E 'echo .*\$(NEW_ACCESS|NEW_REFRESH|REFRESH_TOKEN)' "$REFRESH" >/dev/null; then
63
- fail "token value printed to stdout"
64
- else
65
- pass "no token values printed"
66
- fi
67
- grep -Fq 'security add-generic-password' "$REFRESH" && pass "saves back to Keychain" || fail "Keychain save missing"
68
-
69
- echo ""
70
- echo "→ 5. prefs schema + template declare tokenScripts"
71
- TS_TYPE=$(jq -r '.properties.global.properties.tokenScripts.type // "missing"' "$SCHEMA")
72
- [ "$TS_TYPE" = "object" ] && pass "schema tokenScripts is object" || fail "schema tokenScripts type = '$TS_TYPE'"
73
- jq -e '.global | has("tokenScripts")' "$TEMPLATE" >/dev/null && pass "template has tokenScripts" || fail "template missing tokenScripts"
74
-
75
- echo ""
76
- echo "→ 6. network failure is not expiry; 401 safety net stays"
77
- grep -Fq "NOT treated as expiry" "$PHASE0" && pass "network-failure rule" || fail "network-failure rule missing"
78
- grep -Fq "safety net" "$PHASE0" && pass "401 safety net documented" || fail "401 safety net missing"
79
-
80
- echo ""
81
- echo "══ token-preflight smoke: $PASS passed, $FAIL failed ══"
82
- [ "$FAIL" -eq 0 ]
@@ -1,191 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-tracker-contract.sh - v7.9.1
3
- #
4
- # Validates that every mode dispatch file (dev/SKILL.md, dev-local/SKILL.md, autopilot/SKILL.md,
5
- # etc.) carries the mandatory phase-tracker.sh invocation contract.
6
- #
7
- # Without this, agent prompts can fall through to TaskCreate/TaskList UI which
8
- # does NOT track elapsed time or token counts - users see only static
9
- # checkboxes with no progress info. The 2026-04-27 regression that prompted
10
- # this smoke: 8 mode dispatch files shipped through v7.6 → v7.9 with zero
11
- # tracker references; agent went straight to Claude Code's plan UI.
12
- #
13
- # What this enforces:
14
- # 1. Each mode file references phase-tracker.sh at least N times (init +
15
- # add + update + tokens + render at minimum)
16
- # 2. Each mode file links to refs/tracker-contract.md
17
- # 3. The shared contract file exists and documents the invocation pattern
18
- #
19
- # What this does NOT enforce:
20
- # - Runtime - that the agent actually invokes the tracker. That's a contract
21
- # test the orchestrator carries; this smoke catches the prompt-side gap.
22
-
23
- set -uo pipefail
24
-
25
- REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
26
- CMDS_DIR="$REPO_ROOT/pipeline/commands/multi-agent"
27
- CONTRACT_FILE="$HOME/.claude/multi-agent-refs/tracker-contract.md"
28
-
29
- # Mode dispatch files that must reference the tracker contract.
30
- # Updated when new mode files ship; no auto-discovery (false positives on
31
- # helpers like _account-picker, status, log).
32
- MODE_FILES=(
33
- dev/SKILL.md
34
- dev-local/SKILL.md
35
- dev-autopilot/SKILL.md
36
- dev-local-autopilot/SKILL.md
37
- autopilot/SKILL.md
38
- local/SKILL.md
39
- local-autopilot/SKILL.md
40
- )
41
-
42
- # Minimum tracker references per file (init + at least one of the verbs).
43
- # Set conservatively - the section template emits 6 references; bump if
44
- # template is shortened in future.
45
- MIN_REFS=4
46
-
47
- PASS=0
48
- FAIL=0
49
- pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
50
- fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
51
-
52
- # ──────────────────────────────────────────────────────────────────────────
53
- echo "→ 1. Tracker contract reference file exists"
54
- if [ -f "$CONTRACT_FILE" ]; then
55
- pass "refs/tracker-contract.md present"
56
- else
57
- fail "refs/tracker-contract.md missing - mode files have nowhere to link to"
58
- fi
59
-
60
- # ──────────────────────────────────────────────────────────────────────────
61
- echo "→ 2. Contract file documents the full invocation pattern"
62
- if [ -f "$CONTRACT_FILE" ]; then
63
- for verb in "init" "add" "update" "tokens" "render"; do
64
- if grep -q "phase-tracker.sh $verb" "$CONTRACT_FILE"; then
65
- pass "contract documents '$verb' verb"
66
- else
67
- fail "contract missing '$verb' verb in invocation pattern"
68
- fi
69
- done
70
- fi
71
-
72
- # ──────────────────────────────────────────────────────────────────────────
73
- echo "→ 3. Every mode file references phase-tracker.sh"
74
- for mode_file in "${MODE_FILES[@]}"; do
75
- full="$CMDS_DIR/$mode_file"
76
- if [ ! -f "$full" ]; then
77
- fail "$mode_file missing"
78
- continue
79
- fi
80
- refs=$(grep -c "phase-tracker" "$full" || true)
81
- if [ "$refs" -ge "$MIN_REFS" ]; then
82
- pass "$mode_file: $refs tracker references (≥ $MIN_REFS)"
83
- else
84
- fail "$mode_file: only $refs tracker references (< $MIN_REFS expected)"
85
- fi
86
- done
87
-
88
- # ──────────────────────────────────────────────────────────────────────────
89
- echo "→ 4. Every mode file links to refs/tracker-contract.md"
90
- for mode_file in "${MODE_FILES[@]}"; do
91
- full="$CMDS_DIR/$mode_file"
92
- [ -f "$full" ] || continue
93
- if grep -q "tracker-contract" "$full"; then
94
- pass "$mode_file → links to tracker-contract.md"
95
- else
96
- fail "$mode_file missing link to refs/tracker-contract.md"
97
- fi
98
- done
99
-
100
- # ──────────────────────────────────────────────────────────────────────────
101
- echo "→ 5. Anti-pattern guard - mode files warn against TaskCreate fallback"
102
- for mode_file in "${MODE_FILES[@]}"; do
103
- full="$CMDS_DIR/$mode_file"
104
- [ -f "$full" ] || continue
105
- if grep -qE "TaskCreate|checkbox UI" "$full"; then
106
- pass "$mode_file mentions anti-pattern (TaskCreate / checkbox UI)"
107
- else
108
- fail "$mode_file has no anti-pattern guard"
109
- fi
110
- done
111
-
112
- # ──────────────────────────────────────────────────────────────────────────
113
- echo "→ 6. Phase 0 init pattern present in dispatch templates"
114
- # This catches the specific bug from 2026-04-27: tracker was never init'd
115
- # because dispatch files said "see refs" without inlining the call.
116
- for mode_file in "${MODE_FILES[@]}"; do
117
- full="$CMDS_DIR/$mode_file"
118
- [ -f "$full" ] || continue
119
- if grep -q 'phase-tracker.sh init' "$full"; then
120
- pass "$mode_file inlines 'phase-tracker.sh init' (refs-only is not enough)"
121
- else
122
- fail "$mode_file does not inline init call"
123
- fi
124
- done
125
-
126
- # ──────────────────────────────────────────────────────────────────────────
127
- echo "→ 7. Live-tracker UX contract (now/cost actions, mirror rule, continuation)"
128
- if grep -q 'phase-tracker.sh now' "$CONTRACT_FILE" \
129
- && grep -q 'cost <N>' "$CONTRACT_FILE"; then
130
- pass "tracker-contract documents now + cost actions"
131
- else
132
- fail "tracker-contract missing now/cost action docs"
133
- fi
134
- if grep -q '\[cached\]' "$CONTRACT_FILE" || grep -q '\[cached_count\]' "$CONTRACT_FILE"; then
135
- pass "tracker-contract documents the 4th cached tokens arg"
136
- else
137
- fail "tracker-contract missing the cached tokens arg"
138
- fi
139
- if grep -q 'Progress-line mirror' "$CONTRACT_FILE"; then
140
- pass "tracker-contract declares the progress-line mirror rule"
141
- else
142
- fail "tracker-contract missing the progress-line mirror rule"
143
- fi
144
- if grep -q 'Continuation runs' "$CONTRACT_FILE" \
145
- && grep -q 'never re-initialized' "$CONTRACT_FILE"; then
146
- pass "tracker-contract declares the continuation-runs rule"
147
- else
148
- fail "tracker-contract missing the continuation-runs rule"
149
- fi
150
- if grep -q 'Completion tile suffix' "$CONTRACT_FILE"; then
151
- pass "tracker-contract declares the completion tile suffix"
152
- else
153
- fail "tracker-contract missing the completion tile suffix"
154
- fi
155
-
156
- # ──────────────────────────────────────────────────────────────────────────
157
- echo "→ 8. Continuation wiring in finish + manual-test + phase-5"
158
- FINISH_FILE="$CMDS_DIR/finish/SKILL.md"
159
- if grep -q '\[ ! -f "\$STATE" \]' "$FINISH_FILE"; then
160
- pass "finish.md guards init behind a state-file existence check"
161
- else
162
- fail "finish.md must not init the tracker unconditionally"
163
- fi
164
- if grep -q 'Continuing' "$FINISH_FILE" && grep -q 'cost total' "$FINISH_FILE"; then
165
- pass "finish.md prints the inherited-history line via cost total"
166
- else
167
- fail "finish.md missing the continuation summary line"
168
- fi
169
- MANUAL_FILE="$CMDS_DIR/manual-test/SKILL.md"
170
- if grep -q 'now 5 "awaiting local test (user)"' "$MANUAL_FILE"; then
171
- pass "manual-test.md sets the waiting state via now 5"
172
- else
173
- fail "manual-test.md missing the now 5 waiting state"
174
- fi
175
- PHASE5_FILE="$HOME/.claude/multi-agent-refs/phases/phase-5-test.md"
176
- if grep -q 'now 5 "awaiting local test (user)"' "$PHASE5_FILE"; then
177
- pass "phase-5-test.md sets the waiting state via now 5"
178
- else
179
- fail "phase-5-test.md missing the now 5 waiting state"
180
- fi
181
- RESUME_FILE="$CMDS_DIR/resume/SKILL.md"
182
- if grep -q 'Rebuild the phase tiles' "$RESUME_FILE"; then
183
- pass "resume.md rebuilds the phase tiles from state"
184
- else
185
- fail "resume.md missing the tile-rebuild step"
186
- fi
187
-
188
- # ──────────────────────────────────────────────────────────────────────────
189
- echo ""
190
- echo "══ tracker-contract smoke: $PASS passed, $FAIL failed ══"
191
- [ "$FAIL" -eq 0 ]