@mmerterden/multi-agent-pipeline 12.7.0 → 12.9.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 (247) hide show
  1. package/CHANGELOG.md +207 -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/features/review-multi-repo.md +65 -0
  13. package/pipeline/multi-agent-refs/features/url-enrichment.md +93 -0
  14. package/pipeline/multi-agent-refs/phases/operations.md +28 -0
  15. package/pipeline/multi-agent-refs/phases/phase-0-init.md +3 -83
  16. package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
  17. package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
  18. package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -62
  19. package/pipeline/schemas/prefs.schema.json +6 -0
  20. package/pipeline/scripts/_smoke-root.sh +61 -0
  21. package/pipeline/scripts/agent-guard.py +57 -3
  22. package/pipeline/scripts/audit-log.sh +25 -0
  23. package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
  24. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
  25. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
  26. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
  27. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
  28. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
  29. package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
  30. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
  31. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
  32. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
  33. package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
  34. package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
  35. package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
  36. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
  37. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
  38. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
  39. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
  40. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
  41. package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
  42. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
  43. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
  44. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
  45. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
  46. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
  47. package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
  48. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
  49. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
  50. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
  51. package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
  52. package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
  53. package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
  54. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
  55. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
  56. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
  57. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
  58. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
  59. package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
  60. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
  61. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
  62. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
  63. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
  64. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
  65. package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
  66. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
  67. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
  68. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
  69. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
  70. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
  71. package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
  72. package/pipeline/eval/golden-tasks/README.md +0 -65
  73. package/pipeline/eval/intent-cases.json +0 -40
  74. package/pipeline/eval/run-metrics-fixture.json +0 -93
  75. package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
  76. package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
  77. package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
  78. package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
  79. package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
  80. package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
  81. package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
  82. package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
  83. package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
  84. package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
  85. package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
  86. package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
  87. package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
  88. package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
  89. package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
  90. package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
  91. package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
  92. package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
  93. package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
  94. package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
  95. package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
  96. package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
  97. package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
  98. package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
  99. package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
  100. package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
  101. package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
  102. package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
  103. package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
  104. package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
  105. package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
  106. package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
  107. package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
  108. package/pipeline/eval/triage/README.md +0 -54
  109. package/pipeline/scripts/benchmark-phase-0.sh +0 -128
  110. package/pipeline/scripts/check-md-links.mjs +0 -88
  111. package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
  112. package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
  113. package/pipeline/scripts/eval-intent.mjs +0 -107
  114. package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
  115. package/pipeline/scripts/eval-triage.mjs +0 -171
  116. package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
  117. package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
  118. package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
  119. package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
  120. package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
  121. package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
  122. package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
  123. package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
  124. package/pipeline/scripts/lint-skills.mjs +0 -154
  125. package/pipeline/scripts/run-smokes.mjs +0 -130
  126. package/pipeline/scripts/scorecard.mjs +0 -258
  127. package/pipeline/scripts/smoke-add-detail.sh +0 -137
  128. package/pipeline/scripts/smoke-agent-guard.sh +0 -74
  129. package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
  130. package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
  131. package/pipeline/scripts/smoke-ask-choice.sh +0 -42
  132. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
  133. package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
  134. package/pipeline/scripts/smoke-changelog-version.sh +0 -47
  135. package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
  136. package/pipeline/scripts/smoke-channels-flow.sh +0 -130
  137. package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
  138. package/pipeline/scripts/smoke-clarify.sh +0 -148
  139. package/pipeline/scripts/smoke-command-inventory.sh +0 -81
  140. package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
  141. package/pipeline/scripts/smoke-community-gates.sh +0 -75
  142. package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
  143. package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
  144. package/pipeline/scripts/smoke-context-budget.sh +0 -72
  145. package/pipeline/scripts/smoke-cost-budget.sh +0 -70
  146. package/pipeline/scripts/smoke-cost-summary.sh +0 -139
  147. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
  148. package/pipeline/scripts/smoke-description-tr.sh +0 -82
  149. package/pipeline/scripts/smoke-dev-critic.sh +0 -144
  150. package/pipeline/scripts/smoke-diff-explain.sh +0 -147
  151. package/pipeline/scripts/smoke-diff-risk.sh +0 -190
  152. package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
  153. package/pipeline/scripts/smoke-eval-live.sh +0 -136
  154. package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
  155. package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
  156. package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
  157. package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
  158. package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
  159. package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
  160. package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
  161. package/pipeline/scripts/smoke-generate-issue.sh +0 -120
  162. package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
  163. package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
  164. package/pipeline/scripts/smoke-install-layout.sh +0 -248
  165. package/pipeline/scripts/smoke-intent-guard.sh +0 -86
  166. package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
  167. package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
  168. package/pipeline/scripts/smoke-keychain.sh +0 -158
  169. package/pipeline/scripts/smoke-language-axis.sh +0 -109
  170. package/pipeline/scripts/smoke-learning-curve.sh +0 -61
  171. package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
  172. package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
  173. package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
  174. package/pipeline/scripts/smoke-md-links.sh +0 -8
  175. package/pipeline/scripts/smoke-md2confluence.sh +0 -126
  176. package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
  177. package/pipeline/scripts/smoke-migrate-state.sh +0 -102
  178. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
  179. package/pipeline/scripts/smoke-model-fallback.sh +0 -89
  180. package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
  181. package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
  182. package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
  183. package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
  184. package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
  185. package/pipeline/scripts/smoke-pack-contents.sh +0 -140
  186. package/pipeline/scripts/smoke-pat-audit.sh +0 -128
  187. package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
  188. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
  189. package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
  190. package/pipeline/scripts/smoke-phase-banner.sh +0 -101
  191. package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
  192. package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
  193. package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
  194. package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
  195. package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
  196. package/pipeline/scripts/smoke-plan-safety.sh +0 -139
  197. package/pipeline/scripts/smoke-plan-todos.sh +0 -196
  198. package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
  199. package/pipeline/scripts/smoke-pre-commit.sh +0 -170
  200. package/pipeline/scripts/smoke-pref-migration.sh +0 -226
  201. package/pipeline/scripts/smoke-prefs-language.sh +0 -134
  202. package/pipeline/scripts/smoke-progress-contract.sh +0 -127
  203. package/pipeline/scripts/smoke-prune-logs.sh +0 -137
  204. package/pipeline/scripts/smoke-purge.sh +0 -138
  205. package/pipeline/scripts/smoke-push-retry.sh +0 -75
  206. package/pipeline/scripts/smoke-repo-map.sh +0 -300
  207. package/pipeline/scripts/smoke-review-readiness.sh +0 -92
  208. package/pipeline/scripts/smoke-review-watch.sh +0 -146
  209. package/pipeline/scripts/smoke-routines.sh +0 -84
  210. package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
  211. package/pipeline/scripts/smoke-run-metrics.sh +0 -50
  212. package/pipeline/scripts/smoke-search.sh +0 -187
  213. package/pipeline/scripts/smoke-shadow-git.sh +0 -224
  214. package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
  215. package/pipeline/scripts/smoke-skill-language.sh +0 -83
  216. package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
  217. package/pipeline/scripts/smoke-skill-scan.sh +0 -198
  218. package/pipeline/scripts/smoke-source-parity.sh +0 -85
  219. package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
  220. package/pipeline/scripts/smoke-sync-parity.sh +0 -92
  221. package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
  222. package/pipeline/scripts/smoke-telemetry.sh +0 -147
  223. package/pipeline/scripts/smoke-test-gap.sh +0 -183
  224. package/pipeline/scripts/smoke-token-budget.sh +0 -67
  225. package/pipeline/scripts/smoke-token-preflight.sh +0 -82
  226. package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
  227. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
  228. package/pipeline/scripts/smoke-triage-memory.sh +0 -174
  229. package/pipeline/scripts/smoke-update-check.sh +0 -135
  230. package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
  231. package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
  232. package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
  233. package/pipeline/scripts/smoke-validator-gates.sh +0 -164
  234. package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
  235. package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
  236. package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
  237. package/pipeline/scripts/smoke-work-summary.sh +0 -163
  238. package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
  239. package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
  240. package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
  241. package/pipeline/scripts/smoke-write-state.sh +0 -159
  242. package/pipeline/scripts/sync-parity-check.sh +0 -135
  243. package/pipeline/scripts/test-gap-rules/android.json +0 -25
  244. package/pipeline/scripts/test-gap-rules/ios.json +0 -34
  245. package/pipeline/scripts/test-gap-rules/node.json +0 -29
  246. package/pipeline/scripts/test-gap-rules/python.json +0 -25
  247. package/pipeline/scripts/validate-schemas.mjs +0 -88
@@ -1,448 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # smoke-fetchers-offline.sh - offline invocation tests for the pipeline/lib
4
- # fetchers. No network, no real tokens.
5
- #
6
- # Asserts, per fetcher:
7
- # 1. usage / arg-validation paths exit with the documented code
8
- # 2. URL/host parsing succeeds for a representative URL. This is the
9
- # permanent regression guard for the env-prefix bug where
10
- # `VAR=x PARSED=$(python3 ...)` never exported VAR to the child, so
11
- # fetch-confluence always failed with "could not determine Confluence
12
- # host" before reaching the credential stage.
13
- # 3. with stub credentials (fake credential-store in a temp HOME, fake curl
14
- # on PATH) the failure moves past parsing to the documented
15
- # credential/network exit code
16
- # 4. static source checks: no bash-4-only constructs (macOS /bin/bash is
17
- # 3.2) and no secret-bearing argv patterns (curl -H auth headers,
18
- # --data-urlencode secrets, security add-generic-password -w on argv)
19
- #
20
- # Fetchers run under /bin/bash when available so bash-4-isms fail loudly on
21
- # macOS instead of passing under a newer Homebrew bash.
22
- #
23
- # Exit codes: 0=pass, 1=failure
24
-
25
- set -euo pipefail
26
-
27
- REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
28
- LIB="$REPO_ROOT/pipeline/lib"
29
- SCRIPTS="$REPO_ROOT/pipeline/scripts"
30
-
31
- # Prefer the system bash (3.2 on macOS) so bash-4-isms regress loudly here.
32
- SH="/bin/bash"
33
- [ -x "$SH" ] || SH="$(command -v bash)"
34
-
35
- PASS=0
36
- FAIL=0
37
- note() { echo " -> $*"; }
38
- pass() { echo " ok $*"; PASS=$((PASS + 1)); }
39
- fail() { echo " FAIL $*"; FAIL=$((FAIL + 1)); }
40
-
41
- WORK=$(mktemp -d "${TMPDIR:-/tmp}/smoke-fetchers.XXXXXX")
42
- trap 'rm -rf "$WORK"' EXIT
43
-
44
- OUT="$WORK/out.log"
45
- ERR="$WORK/err.log"
46
-
47
- # expect_rc <expected-rc> <label> <cmd...>
48
- # Runs cmd, captures stdout/stderr to $OUT/$ERR, asserts the exit code.
49
- expect_rc() {
50
- local expected="$1" label="$2"
51
- shift 2
52
- local rc=0
53
- "$@" >"$OUT" 2>"$ERR" || rc=$?
54
- if [ "$rc" -eq "$expected" ]; then
55
- pass "$label (rc=$rc)"
56
- else
57
- fail "$label (rc=$rc, expected $expected)"
58
- tail -5 "$ERR" | sed 's/^/ /'
59
- fi
60
- }
61
-
62
- assert_in_file() {
63
- local needle="$1" file="$2" label="$3"
64
- if grep -q -e "$needle" "$file" 2>/dev/null; then
65
- pass "$label"
66
- else
67
- fail "$label (pattern '$needle' not found)"
68
- fi
69
- }
70
-
71
- assert_not_in_file() {
72
- local needle="$1" file="$2" label="$3"
73
- if grep -q -e "$needle" "$file" 2>/dev/null; then
74
- fail "$label (pattern '$needle' present)"
75
- else
76
- pass "$label"
77
- fi
78
- }
79
-
80
- # -----------------------------------------------------------------------------
81
- note "0. fixtures: temp HOME with resolver + fake credential store, fake curls"
82
- # -----------------------------------------------------------------------------
83
- FAKE_HOME="$WORK/home"
84
- mkdir -p "$FAKE_HOME/.claude/lib"
85
- cp "$LIB/credential-store-resolver.sh" "$FAKE_HOME/.claude/lib/credential-store-resolver.sh"
86
-
87
- # Fake credential store: empty by default, returns FAKE_CRED_VALUE when set.
88
- cat > "$FAKE_HOME/.claude/lib/credential-store.sh" <<'FAKE'
89
- #!/bin/bash
90
- case "${1:-}" in
91
- get)
92
- if [ -n "${FAKE_CRED_VALUE:-}" ]; then printf '%s' "$FAKE_CRED_VALUE"; exit 0; fi
93
- exit 1 ;;
94
- platform) echo "fake" ;;
95
- *) exit 3 ;;
96
- esac
97
- FAKE
98
- chmod +x "$FAKE_HOME/.claude/lib/credential-store.sh"
99
-
100
- # Fake curl variants, each in its own PATH-prepend dir.
101
- BIN_CURL_FAIL="$WORK/bin-curl-fail"
102
- mkdir -p "$BIN_CURL_FAIL"
103
- cat > "$BIN_CURL_FAIL/curl" <<'FAKE'
104
- #!/bin/bash
105
- echo "curl: (7) offline stub - connection refused" >&2
106
- exit 7
107
- FAKE
108
- chmod +x "$BIN_CURL_FAIL/curl"
109
-
110
- # Swagger meta stub: honours --output, emits the --write-out triple with a
111
- # content type that matches neither json nor yaml, forcing the URL-suffix
112
- # lowercase path (the former bash-4 ${URL,,} site).
113
- BIN_CURL_META="$WORK/bin-curl-meta"
114
- mkdir -p "$BIN_CURL_META"
115
- cat > "$BIN_CURL_META/curl" <<'FAKE'
116
- #!/bin/bash
117
- out=""
118
- prev=""
119
- for a in "$@"; do
120
- if [ "$prev" = "--output" ]; then out="$a"; fi
121
- prev="$a"
122
- done
123
- if [ -n "$out" ]; then printf '{}' > "$out"; fi
124
- printf '200\napplication/octet-stream\n2\n'
125
- exit 0
126
- FAKE
127
- chmod +x "$BIN_CURL_META/curl"
128
-
129
- # 401 stub: honours -o, prints the HTTP code the way curl -w '%{http_code}' does.
130
- BIN_CURL_401="$WORK/bin-curl-401"
131
- mkdir -p "$BIN_CURL_401"
132
- cat > "$BIN_CURL_401/curl" <<'FAKE'
133
- #!/bin/bash
134
- out=""
135
- prev=""
136
- for a in "$@"; do
137
- if [ "$prev" = "-o" ] || [ "$prev" = "--output" ]; then out="$a"; fi
138
- prev="$a"
139
- done
140
- if [ -n "$out" ]; then printf '{}' > "$out"; fi
141
- printf '401'
142
- exit 0
143
- FAKE
144
- chmod +x "$BIN_CURL_401/curl"
145
-
146
- # Fake security + uname (forces the macOS branch of credential-store.sh) so
147
- # the no-secret-on-argv property of `security -i` writes can be asserted on
148
- # any platform without touching a real keychain.
149
- BIN_DARWIN="$WORK/bin-darwin"
150
- mkdir -p "$BIN_DARWIN"
151
- cat > "$BIN_DARWIN/uname" <<'FAKE'
152
- #!/bin/bash
153
- echo "Darwin"
154
- FAKE
155
- cat > "$BIN_DARWIN/security" <<'FAKE'
156
- #!/bin/bash
157
- printf '%s\n' "$*" >> "$SMOKE_CAPTURE_DIR/security-argv.log"
158
- if [ "${1:-}" = "-i" ]; then
159
- cat >> "$SMOKE_CAPTURE_DIR/security-stdin.log"
160
- fi
161
- exit 0
162
- FAKE
163
- chmod +x "$BIN_DARWIN/uname" "$BIN_DARWIN/security"
164
-
165
- # -----------------------------------------------------------------------------
166
- note "1. fetch-confluence.sh"
167
- # -----------------------------------------------------------------------------
168
- expect_rc 4 "no args exits 4 (usage)" \
169
- "$SH" "$LIB/fetch-confluence.sh"
170
- expect_rc 4 "--help exits 4 (usage)" \
171
- "$SH" "$LIB/fetch-confluence.sh" --help
172
-
173
- # Regression guard for the env-prefix parse bug: a plain page URL must get
174
- # past host/pageId resolution and fail at the credential stage (exit 2),
175
- # never with the parse error.
176
- expect_rc 2 "URL parse reaches credential stage (viewpage.action?pageId)" \
177
- env HOME="$FAKE_HOME" USER=smoke \
178
- "$SH" "$LIB/fetch-confluence.sh" \
179
- "https://confluence.example.com/pages/viewpage.action?pageId=12345"
180
- assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
181
- assert_not_in_file "could not determine Confluence host" "$ERR" \
182
- "no host-parse error (env-prefix regression)"
183
-
184
- expect_rc 2 "URL parse reaches credential stage (cloud /wiki/spaces form)" \
185
- env HOME="$FAKE_HOME" USER=smoke \
186
- "$SH" "$LIB/fetch-confluence.sh" \
187
- "https://tenant.atlassian.net/wiki/spaces/ENG/pages/99887/Some+Page"
188
- assert_in_file "missing-token" "$ERR" "cloud form also blocked on token only"
189
-
190
- # With a stub token and a failing curl, the failure must be the documented
191
- # network error (3), proving parsing and auth wiring are past.
192
- expect_rc 3 "stub token + offline curl exits 3 (network stage)" \
193
- env HOME="$FAKE_HOME" USER=smoke FAKE_CRED_VALUE=fake-token \
194
- PATH="$BIN_CURL_FAIL:$PATH" \
195
- "$SH" "$LIB/fetch-confluence.sh" \
196
- "https://confluence.example.com/pages/viewpage.action?pageId=12345"
197
- assert_in_file "Confluence GET failed" "$ERR" "network-stage error message"
198
-
199
- # -----------------------------------------------------------------------------
200
- note "2. fetch-crashlytics.sh"
201
- # -----------------------------------------------------------------------------
202
- expect_rc 4 "no args exits 4 (usage)" \
203
- "$SH" "$LIB/fetch-crashlytics.sh"
204
- expect_rc 4 "--help exits 4 (usage)" \
205
- "$SH" "$LIB/fetch-crashlytics.sh" --help
206
-
207
- CRASH_URL="https://console.firebase.google.com/project/my-proj/crashlytics/app/ios:com.example.app/issues/abc123"
208
- expect_rc 2 "URL parse reaches credential stage" \
209
- env HOME="$FAKE_HOME" USER=smoke \
210
- "$SH" "$LIB/fetch-crashlytics.sh" "$CRASH_URL"
211
- assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
212
-
213
- # Stub SA JSON whose project_id mismatches the URL: proves parse + credential
214
- # read + base64 decode + JSON validation all pass, fully offline (exit 5).
215
- MISMATCH_SA_B64=$(printf '{"project_id":"other-proj"}' | base64 | tr -d '\n')
216
- expect_rc 5 "stub SA JSON reaches project-mismatch check (exit 5)" \
217
- env HOME="$FAKE_HOME" USER=smoke FAKE_CRED_VALUE="$MISMATCH_SA_B64" \
218
- "$SH" "$LIB/fetch-crashlytics.sh" "$CRASH_URL"
219
- assert_in_file "project_id" "$ERR" "mismatch error mentions project_id"
220
-
221
- # The decoded SA JSON must never touch a predictable /tmp path.
222
- assert_not_in_file "/tmp/.fc-sa" "$LIB/fetch-crashlytics.sh" \
223
- "predictable /tmp SA path removed from source"
224
-
225
- # -----------------------------------------------------------------------------
226
- note "3. fetch-swagger.sh"
227
- # -----------------------------------------------------------------------------
228
- expect_rc 4 "no args exits 4 (usage)" \
229
- "$SH" "$LIB/fetch-swagger.sh"
230
-
231
- expect_rc 2 "offline curl exits 2 (network stage)" \
232
- env PATH="$BIN_CURL_FAIL:$PATH" \
233
- "$SH" "$LIB/fetch-swagger.sh" "https://spec.example.com/openapi.json"
234
-
235
- # Uppercase .YAML suffix with a non-matching content type exercises the
236
- # lowercase-URL branch (formerly bash-4 ${URL,,}, dies on /bin/bash 3.2).
237
- expect_rc 0 "uppercase .YAML suffix detected via lowercase branch" \
238
- env PATH="$BIN_CURL_META:$PATH" \
239
- "$SH" "$LIB/fetch-swagger.sh" "https://spec.example.com/openapi.YAML"
240
- assert_in_file '"format": "yaml"' "$OUT" "output reports yaml format"
241
-
242
- # -----------------------------------------------------------------------------
243
- note "4. fetch-fortify.sh"
244
- # -----------------------------------------------------------------------------
245
- expect_rc 6 "no host configured exits 6" \
246
- env HOME="$FAKE_HOME" USER=smoke \
247
- "$SH" "$LIB/fetch-fortify.sh" --version-id 123
248
- expect_rc 4 "host override + no version id exits 4 (usage)" \
249
- env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
250
- "$SH" "$LIB/fetch-fortify.sh"
251
-
252
- FORTIFY_URL="https://ssc.example.com/ssc/html/ssc/version/123/fix/ABCD-1"
253
- expect_rc 2 "URL parse reaches credential stage" \
254
- env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
255
- "$SH" "$LIB/fetch-fortify.sh" "$FORTIFY_URL"
256
- assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
257
-
258
- expect_rc 0 "stub token + offline curl degrades to empty result (exit 0)" \
259
- env HOME="$FAKE_HOME" USER=smoke FORTIFY_HOST_OVERRIDE=ssc.example.com \
260
- FAKE_CRED_VALUE=fake-token PATH="$BIN_CURL_FAIL:$PATH" \
261
- "$SH" "$LIB/fetch-fortify.sh" "$FORTIFY_URL"
262
- assert_in_file '"gateOutcome"' "$OUT" "normalized JSON still emitted"
263
-
264
- # -----------------------------------------------------------------------------
265
- note "4b. fetch-graylog.sh"
266
- # -----------------------------------------------------------------------------
267
- expect_rc 6 "no host configured exits 6" \
268
- env HOME="$FAKE_HOME" USER=smoke \
269
- "$SH" "$LIB/fetch-graylog.sh" --trx T123
270
- expect_rc 4 "host override + no ids exits 4 (usage)" \
271
- env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
272
- "$SH" "$LIB/fetch-graylog.sh"
273
-
274
- expect_rc 2 "host override + id reaches credential stage" \
275
- env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
276
- "$SH" "$LIB/fetch-graylog.sh" --trx T123 --conv C456
277
- assert_in_file "missing-token" "$ERR" "blocked JSON names missing-token"
278
-
279
- # Non-blocking contract: with a stub token and a failing curl, the fetch must
280
- # degrade to an empty normalized result (messages present) and exit 0 so a log
281
- # fetch never blocks a run.
282
- expect_rc 0 "stub token + offline curl degrades to empty result (exit 0)" \
283
- env HOME="$FAKE_HOME" USER=smoke GRAYLOG_HOST_OVERRIDE=logs.example.com \
284
- FAKE_CRED_VALUE=fake-token PATH="$BIN_CURL_FAIL:$PATH" \
285
- "$SH" "$LIB/fetch-graylog.sh" --trx T123
286
- assert_in_file '"messages"' "$OUT" "normalized JSON still emitted"
287
-
288
- # -----------------------------------------------------------------------------
289
- note "5. figma-screenshot.sh"
290
- # -----------------------------------------------------------------------------
291
- expect_rc 1 "no args exits 1 (usage)" \
292
- "$SH" "$LIB/figma-screenshot.sh"
293
- expect_rc 0 "--help exits 0" \
294
- "$SH" "$LIB/figma-screenshot.sh" --help
295
-
296
- expect_rc 3 "no PAT resolvable exits 3 (Tier 3 required)" \
297
- env -u FIGMA_PAT HOME="$FAKE_HOME" USER=smoke \
298
- "$SH" "$LIB/figma-screenshot.sh" --file-key abc123 --node-id 1-2 \
299
- --output-dir "$WORK/shots-no-pat"
300
-
301
- expect_rc 2 "stub PAT + 401 curl exits 2 (auth stage, past parsing)" \
302
- env HOME="$FAKE_HOME" USER=smoke FIGMA_PAT=fake-pat \
303
- PATH="$BIN_CURL_401:$PATH" \
304
- "$SH" "$LIB/figma-screenshot.sh" --file-key abc123 --node-id 1-2 \
305
- --output-dir "$WORK/shots-401"
306
-
307
- # -----------------------------------------------------------------------------
308
- note "6. figma-mcp-refresh.sh"
309
- # -----------------------------------------------------------------------------
310
- expect_rc 2 "missing prefs exits 2" \
311
- env HOME="$FAKE_HOME" USER=smoke \
312
- "$SH" "$LIB/figma-mcp-refresh.sh"
313
-
314
- if command -v jq >/dev/null 2>&1; then
315
- PREFS_HOME="$WORK/home-prefs"
316
- mkdir -p "$PREFS_HOME/.claude"
317
- printf '{"global":{"keychainMapping":{"figma_mcp":"Smoke_Figma_MCP"}}}\n' \
318
- > "$PREFS_HOME/.claude/multi-agent-preferences.json"
319
- export SMOKE_CAPTURE_DIR="$WORK"
320
- expect_rc 2 "mapped key + empty keychain exits 2 (no refresh token)" \
321
- env HOME="$PREFS_HOME" USER=smoke SMOKE_CAPTURE_DIR="$WORK" \
322
- PATH="$BIN_DARWIN:$PATH" \
323
- "$SH" "$LIB/figma-mcp-refresh.sh"
324
- else
325
- note " (jq not installed - skipping mapped-key case)"
326
- fi
327
-
328
- # -----------------------------------------------------------------------------
329
- note "7. credential-store.sh + keychain-save.sh"
330
- # -----------------------------------------------------------------------------
331
- expect_rc 0 "platform subcommand works" \
332
- "$SH" "$LIB/credential-store.sh" platform
333
- expect_rc 3 "get without key exits 3 (usage)" \
334
- "$SH" "$LIB/credential-store.sh" get
335
- expect_rc 1 "keychain-save without service name exits 1 (usage)" \
336
- "$SH" "$SCRIPTS/keychain-save.sh"
337
-
338
- # set <key> - : the secret must travel via stdin to `security -i`, never on
339
- # the security argv. Fake uname forces the macOS branch; fake security logs
340
- # argv and stdin separately.
341
- rm -f "$WORK/security-argv.log" "$WORK/security-stdin.log"
342
- SECRET_VALUE="topsecret-smoke-value-123"
343
- rc=0
344
- printf '%s' "$SECRET_VALUE" \
345
- | env PATH="$BIN_DARWIN:$PATH" KEYCHAIN_DELEGATE=0 SMOKE_CAPTURE_DIR="$WORK" \
346
- "$SH" "$LIB/credential-store.sh" set smoke-test-key - >"$OUT" 2>"$ERR" || rc=$?
347
- if [ "$rc" -eq 0 ]; then
348
- pass "set <key> - via stdin succeeds (rc=0)"
349
- else
350
- fail "set <key> - via stdin failed (rc=$rc)"
351
- tail -5 "$ERR" | sed 's/^/ /'
352
- fi
353
- assert_in_file "^-i" "$WORK/security-argv.log" "security invoked in -i stdin mode"
354
- assert_not_in_file "$SECRET_VALUE" "$WORK/security-argv.log" "secret absent from security argv"
355
- assert_in_file "$SECRET_VALUE" "$WORK/security-stdin.log" "secret delivered via stdin"
356
-
357
- # -----------------------------------------------------------------------------
358
- note "7b. fetch-figma-annotations.sh (Tier-2 annotation ingestion)"
359
- # -----------------------------------------------------------------------------
360
- expect_rc 1 "no args exits 1 (usage)" \
361
- "$SH" "$LIB/fetch-figma-annotations.sh"
362
-
363
- # Missing token -> blocked JSON + exit 2 (no network).
364
- expect_rc 2 "missing token exits 2 with blocked JSON" \
365
- env HOME="$FAKE_HOME" USER=smoke \
366
- "$SH" "$LIB/fetch-figma-annotations.sh" --file-key ABC --node-id "1:23"
367
- assert_in_file "missing-token" "$OUT" "blocked JSON names missing-token"
368
-
369
- # Fake curl returns a /nodes doc carrying a TR:/EN: annotation; with a stub
370
- # token the parser must reach + parse it (regression guard for the parser).
371
- BIN_CURL_ANNOT="$WORK/bin-curl-annot"
372
- mkdir -p "$BIN_CURL_ANNOT"
373
- cat > "$BIN_CURL_ANNOT/curl" <<'FAKE'
374
- #!/bin/bash
375
- out=""; prev=""
376
- for a in "$@"; do
377
- [ "$prev" = "-o" ] && out="$a"
378
- prev="$a"
379
- done
380
- if [ -n "$out" ]; then
381
- cat > "$out" <<'JSON'
382
- {"nodes":{"1:23":{"document":{"id":"1:23","name":"Title","type":"TEXT","characters":"Giriniz","annotations":[{"label":"TR: Ucusunuzu secin\nEN: Select your flight"}]}}}}
383
- JSON
384
- fi
385
- printf '200'
386
- exit 0
387
- FAKE
388
- chmod +x "$BIN_CURL_ANNOT/curl"
389
-
390
- expect_rc 0 "stub token + annotation doc parses (exit 0)" \
391
- env HOME="$FAKE_HOME" USER=smoke FIGMA_PAT=fake-token \
392
- PATH="$BIN_CURL_ANNOT:$PATH" \
393
- "$SH" "$LIB/fetch-figma-annotations.sh" --file-key ABC --node-id "1:23" --lang-prefixes TR,EN
394
- assert_in_file "\"mode\": \"prefixed\"" "$OUT" "annotation parsed as prefixed"
395
- assert_in_file "Select your flight" "$OUT" "EN copy extracted from annotation"
396
-
397
- # -----------------------------------------------------------------------------
398
- note "8. static source checks (bash-3.2 compat + no secrets on argv)"
399
- # -----------------------------------------------------------------------------
400
- OWNED_FILES=(
401
- "$LIB/fetch-confluence.sh"
402
- "$LIB/fetch-crashlytics.sh"
403
- "$LIB/fetch-swagger.sh"
404
- "$LIB/fetch-fortify.sh"
405
- "$LIB/fetch-graylog.sh"
406
- "$LIB/fetch-figma-annotations.sh"
407
- "$LIB/figma-mcp-refresh.sh"
408
- "$LIB/figma-screenshot.sh"
409
- "$LIB/credential-store.sh"
410
- "$SCRIPTS/keychain-save.sh"
411
- )
412
-
413
- if grep -nE '\$\{[A-Za-z_0-9]+(,,|\^\^)|mapfile |readarray |declare -A' "${OWNED_FILES[@]}"; then
414
- fail "bash-4-only construct found (must run on /bin/bash 3.2)"
415
- else
416
- pass "no bash-4-only constructs"
417
- fi
418
-
419
- if grep -nE -- '-H "(Authorization|X-Figma-Token)' "${OWNED_FILES[@]}"; then
420
- fail "auth header passed to curl via argv (-H)"
421
- else
422
- pass "no auth headers on curl argv"
423
- fi
424
-
425
- if grep -nE -- '--data-urlencode "(refresh_token|client_secret)' "${OWNED_FILES[@]}"; then
426
- fail "OAuth secret passed to curl via argv (--data-urlencode)"
427
- else
428
- pass "no OAuth secrets on curl argv"
429
- fi
430
-
431
- if grep -nE 'add-generic-password[^|]*-w "\$' "${OWNED_FILES[@]}"; then
432
- fail "keychain secret passed to security via argv (-w)"
433
- else
434
- pass "no keychain secrets on security argv"
435
- fi
436
-
437
- # shellcheck disable=SC2016 # literal $HOME is the pattern being hunted
438
- if grep -nE '\$HOME/\.claude/lib/credential-store\.sh|~/\.claude/lib/credential-store\.sh' "${OWNED_FILES[@]}"; then
439
- fail "hardcoded credential-store path (must go through the resolver)"
440
- else
441
- pass "credential-store access routed through the resolver"
442
- fi
443
-
444
- # -----------------------------------------------------------------------------
445
- echo ""
446
- echo "smoke-fetchers-offline: $PASS passed, $FAIL failed"
447
- [ "$FAIL" -eq 0 ] || exit 1
448
- exit 0
@@ -1,112 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-figma-dispatch.sh - Phase 3 component dispatch contract (plugin-based).
3
- #
4
- # Verifies that multi-agent Phase 3 dispatches component tasks to the
5
- # ai-<platform>-engineering-toolkit MARKETPLACE PLUGIN (create-component,
6
- # fallback create-ui-component) instead of a bundled figma-to-component skill.
7
- #
8
- # Runs offline - scans docs only, no skill invocation.
9
-
10
- set -euo pipefail
11
-
12
- HERE="$(cd "$(dirname "$0")" && pwd)"
13
- ROOT="$(cd "$HERE/../.." && pwd)"
14
- PHASE0="$ROOT/pipeline/multi-agent-refs/phases/phase-0-init.md"
15
- PHASE3="$ROOT/pipeline/multi-agent-refs/phases/phase-3-dev.md"
16
- DISPATCH="$ROOT/pipeline/multi-agent-refs/component-dispatch.md"
17
-
18
- PASS=0
19
- FAIL=0
20
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
21
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
22
-
23
- [ -f "$PHASE0" ] || { echo "error: $PHASE0 missing" >&2; exit 1; }
24
- [ -f "$PHASE3" ] || { echo "error: $PHASE3 missing" >&2; exit 1; }
25
- [ -f "$DISPATCH" ] || { echo "error: $DISPATCH missing" >&2; exit 1; }
26
-
27
- echo "→ 1. no bundled figma orchestrator remains in the tree"
28
- if [ -e "$ROOT/pipeline/skills/figma-ios" ] || [ -e "$ROOT/pipeline/skills/figma-android" ] || [ -e "$ROOT/pipeline/skills/figma-common" ]; then
29
- fail "bundled pipeline/skills/figma-* tree still present (should be plugin-only)"
30
- else
31
- pass "no pipeline/skills/figma-* tree (component skills live in the plugin)"
32
- fi
33
-
34
- echo ""
35
- echo "→ 2. Phase 0 routes component tasks to the plugin component skill"
36
- if grep -Fq "create-component" "$PHASE0"; then
37
- pass "Phase 0 references the plugin create-component skill"
38
- else
39
- fail "Phase 0 does NOT reference create-component"
40
- fi
41
- if grep -Fq "figma-to-swiftui" "$PHASE0" || grep -Fq "skills/figma-ios/figma-to-component" "$PHASE0"; then
42
- fail "Phase 0 still references a legacy/bundled figma orchestrator path"
43
- else
44
- pass "Phase 0 has no legacy/bundled figma orchestrator reference"
45
- fi
46
-
47
- echo ""
48
- echo "→ 3. dispatch guards taskType and resolves the plugin (dual-name)"
49
- if grep -Fq 'taskType === "component"' "$DISPATCH"; then
50
- pass "dispatch mentions taskType guard"
51
- else
52
- fail "dispatch missing taskType guard"
53
- fi
54
- if grep -Fq "create-component" "$DISPATCH" && grep -Fq "create-ui-component" "$DISPATCH"; then
55
- pass "dispatch documents dual-name resolution (create-component / create-ui-component)"
56
- else
57
- fail "dispatch missing dual-name plugin resolution"
58
- fi
59
-
60
- echo ""
61
- echo "→ 4. dispatch layer owns subphase state + phase-tracker"
62
- if grep -Fq 'subphases[]' "$DISPATCH" && grep -Fq "phase-tracker" "$DISPATCH"; then
63
- pass "dispatch documents state.phases[3].subphases[] + phase-tracker"
64
- else
65
- fail "dispatch missing subphases[]/phase-tracker documentation"
66
- fi
67
-
68
- echo ""
69
- echo "→ 5. phase docs carry the progress-contract adoption marker"
70
- if grep -Fq "<!-- progress-contract: applied -->" "$PHASE3"; then
71
- pass "Phase 3 carries the adoption marker"
72
- else
73
- fail "Phase 3 missing the adoption marker"
74
- fi
75
- if grep -Fq "<!-- progress-contract: applied -->" "$PHASE0"; then
76
- pass "Phase 0 carries the adoption marker"
77
- else
78
- fail "Phase 0 missing the adoption marker"
79
- fi
80
-
81
- echo ""
82
- echo "→ 6. Phase 3 emits the plugin dispatch progress line"
83
- if grep -qE '→ dispatching create-component' "$PHASE3"; then
84
- pass "Phase 3 documents '→ dispatching create-component <name>' emission"
85
- else
86
- fail "Phase 3 missing dispatch progress line"
87
- fi
88
-
89
- echo ""
90
- echo "→ 7. dispatch has failure + retry semantics"
91
- if grep -Fq "retryCount" "$DISPATCH"; then
92
- pass "dispatch documents retryCount"
93
- else
94
- fail "dispatch missing retryCount documentation"
95
- fi
96
-
97
- echo ""
98
- echo "→ 8. dispatch covers multi-repo + intentional cross-CLI divergence"
99
- if grep -Fq 'state.projects[]' "$DISPATCH"; then
100
- pass "dispatch documents multi-repo report"
101
- else
102
- fail "dispatch missing multi-repo report paragraph"
103
- fi
104
- if grep -Fiq "Copilot" "$DISPATCH"; then
105
- pass "dispatch documents the Copilot cross-CLI divergence"
106
- else
107
- fail "dispatch missing cross-CLI divergence note"
108
- fi
109
-
110
- echo ""
111
- echo "══ figma-dispatch smoke: $PASS passed, $FAIL failed ══"
112
- [ "$FAIL" -eq 0 ]
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-gate-hooks.sh - the recommended Claude Code hooks template must be
3
- # valid JSON, wire the secret scan as a PreToolUse hook on git commit, and point
4
- # at the installed pre-commit-check.sh. This is the one gate that is OS-enforced
5
- # (a non-zero exit blocks the commit); the template is what makes it real for
6
- # end users instead of only the maintainer's local settings.
7
- #
8
- # Exit 0 = all pass, 1 = any failure.
9
-
10
- set -uo pipefail
11
-
12
- ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
13
- TMPL="$ROOT/install/templates/claude-hooks.json"
14
- PASS=0; FAIL=0
15
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
16
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
17
- command -v node >/dev/null 2>&1 || { echo "error: node required" >&2; exit 127; }
18
-
19
- [ -f "$TMPL" ] || { echo "FAIL: hooks template missing at $TMPL" >&2; exit 1; }
20
-
21
- # 1. valid JSON
22
- if node -e 'JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"))' "$TMPL" 2>/dev/null; then
23
- pass "hooks template is valid JSON"
24
- else
25
- fail "hooks template is not valid JSON"
26
- fi
27
-
28
- # 2. wires a PreToolUse hook on git commit -> pre-commit-check.sh
29
- node -e '
30
- const t = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
31
- const pre = t.hooks && t.hooks.PreToolUse;
32
- if (!Array.isArray(pre) || pre.length === 0) { console.error("no PreToolUse"); process.exit(1); }
33
- const onCommit = pre.find(h => /git commit/.test(h.matcher || ""));
34
- if (!onCommit) { console.error("no git-commit matcher"); process.exit(1); }
35
- const cmds = (onCommit.hooks || []).map(h => h.command || "").join(" ");
36
- if (!/pre-commit-check\.sh/.test(cmds)) { console.error("does not call pre-commit-check.sh"); process.exit(1); }
37
- ' "$TMPL" 2>/dev/null && pass "template wires secret scan as PreToolUse on git commit" || fail "template does not wire the secret-scan hook"
38
-
39
- # 3. the referenced script exists in the source tree
40
- if [ -f "$ROOT/pipeline/scripts/pre-commit-check.sh" ]; then
41
- pass "pre-commit-check.sh exists to back the hook"
42
- else
43
- fail "pre-commit-check.sh missing"
44
- fi
45
-
46
- # 3b. template also wires agent-guard.sh on git commit AND git push
47
- node -e '
48
- const t = JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));
49
- const pre = (t.hooks && t.hooks.PreToolUse) || [];
50
- const commit = pre.find(h => /git commit/.test(h.matcher || ""));
51
- const push = pre.find(h => /git push/.test(h.matcher || ""));
52
- const cmds = e => (e && e.hooks || []).map(h => h.command || "").join(" ");
53
- if (!/agent-guard\.sh/.test(cmds(commit))) { console.error("guard not on git commit"); process.exit(1); }
54
- if (!/agent-guard\.sh/.test(cmds(push))) { console.error("guard not on git push"); process.exit(1); }
55
- ' "$TMPL" 2>/dev/null && pass "template wires agent-guard on git commit + git push" || fail "template does not wire agent-guard on both matchers"
56
-
57
- # 3c. the guard scripts exist in the source tree
58
- if [ -f "$ROOT/pipeline/scripts/agent-guard.sh" ] && [ -f "$ROOT/pipeline/scripts/agent-guard.py" ]; then
59
- pass "agent-guard.sh + agent-guard.py exist to back the hook"
60
- else
61
- fail "agent-guard scripts missing"
62
- fi
63
-
64
- # 4. picker-contract documents the hook template + the phase-enforced caveat
65
- PC="$ROOT/pipeline/multi-agent-refs/picker-contract.md"
66
- if grep -q "claude-hooks.json" "$PC" && grep -q "phase-enforced" "$PC"; then
67
- pass "picker-contract references the template + the phase-enforced gates caveat"
68
- else
69
- fail "picker-contract does not document the hooks template honestly"
70
- fi
71
-
72
- echo ""
73
- echo "══ gate-hooks smoke: $PASS passed, $FAIL failed ══"
74
- [ "$FAIL" -eq 0 ]