@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
- # smoke-lib-scripts.sh - sanity checks for picker library scripts.
3
- # Verifies each lib emits the expected JSON shape so the picker pipeline can
4
- # rely on it. Network-dependent paths (real Jira/GH fetch) are skipped; only
5
- # offline-detectable behavior is asserted.
6
- #
7
- # Covered:
8
- # credential-store.sh doctor, platform detection, get/set/delete cycle (macOS only)
9
- # account-resolver.sh returns JSON array with required fields
10
- # issue-fetcher.sh input-kind detection + maturity object shape
11
- # multi-repo-pipeline.sh bridge → schema-valid agent-state.json v2.1.0
12
-
13
- set -uo pipefail
14
-
15
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16
- REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
17
- LIB_DIR="$REPO_ROOT/pipeline/lib"
18
-
19
- PASS=0
20
- FAIL=0
21
-
22
- ok() { echo " ✓ $1"; PASS=$((PASS+1)); }
23
- fail() { echo " ✗ $1" >&2; FAIL=$((FAIL+1)); }
24
-
25
- # --- credential-store.sh -----------------------------------------------------
26
- echo "→ credential-store.sh"
27
- CRED="$LIB_DIR/credential-store.sh"
28
- if [ ! -x "$CRED" ]; then
29
- fail "credential-store.sh missing or not executable"
30
- else
31
- PLATFORM=$("$CRED" platform 2>/dev/null || echo "")
32
- case "$PLATFORM" in
33
- macos|windows|linux|unknown) ok "platform detected: $PLATFORM" ;;
34
- *) fail "unexpected platform output: $PLATFORM" ;;
35
- esac
36
-
37
- if "$CRED" doctor 2>&1 | grep -qE '^Backend:'; then
38
- ok "doctor reports backend"
39
- else
40
- fail "doctor missing Backend: line"
41
- fi
42
-
43
- # macOS only: full get/set/delete round-trip with throwaway key
44
- if [ "$PLATFORM" = "macos" ]; then
45
- KEY="multi-agent-smoke-test-$$"
46
- VAL="smoke-value-$(date +%s)"
47
- if "$CRED" set "$KEY" "$VAL" 2>/dev/null; then
48
- readback=$("$CRED" get "$KEY" 2>/dev/null || echo "")
49
- if [ "$readback" = "$VAL" ]; then
50
- ok "set/get round-trip works"
51
- else
52
- fail "get returned '$readback' instead of '$VAL'"
53
- fi
54
- "$CRED" delete "$KEY" 2>/dev/null
55
- empty=$("$CRED" get "$KEY" 2>/dev/null || echo "")
56
- if [ -z "$empty" ]; then
57
- ok "delete clears the entry"
58
- else
59
- fail "entry still present after delete"
60
- fi
61
- else
62
- fail "set failed on macOS"
63
- fi
64
- fi
65
- fi
66
-
67
- # --- account-resolver.sh -----------------------------------------------------
68
- echo "→ account-resolver.sh"
69
- RESOLVER="$LIB_DIR/account-resolver.sh"
70
- if [ ! -x "$RESOLVER" ]; then
71
- fail "account-resolver.sh missing or not executable"
72
- else
73
- out=$("$RESOLVER" 2>/dev/null || echo "[]")
74
- if printf '%s' "$out" | python3 -c 'import json,sys; json.loads(sys.stdin.read())' 2>/dev/null; then
75
- ok "emits valid JSON"
76
- else
77
- fail "JSON output invalid: $(printf '%s' "$out" | head -c 200)"
78
- fi
79
-
80
- # When non-empty, every account must have the required fields
81
- python3 - <<PY
82
- import json, sys
83
- try:
84
- data = json.loads('''$out''')
85
- except Exception as e:
86
- print(f"PARSE_FAIL:{e}")
87
- sys.exit(0)
88
-
89
- if not data:
90
- print("EMPTY")
91
- sys.exit(0)
92
-
93
- required = {"id","label","prefix","providers","tokens","userKeys","users","jiraHost","bitbucketHost","isWork"}
94
- missing = []
95
- for i, acc in enumerate(data):
96
- miss = required - set(acc.keys())
97
- if miss:
98
- missing.append((i, sorted(miss)))
99
- if missing:
100
- print(f"MISSING_FIELDS:{missing}")
101
- else:
102
- print(f"OK:{len(data)} accounts, all required fields present")
103
- PY
104
- status=$(python3 - <<PY
105
- import json
106
- data = json.loads('''$out''')
107
- print("ok" if data and all({"id","label","prefix","providers","tokens","userKeys","users","jiraHost","bitbucketHost","isWork"} <= set(a.keys()) for a in data) else ("empty" if not data else "fields-missing"))
108
- PY
109
- )
110
- case "$status" in
111
- ok) ok "all accounts have required schema fields" ;;
112
- empty) ok "no accounts on this machine (skipping schema check)" ;;
113
- fields-missing) fail "some accounts missing required fields" ;;
114
- esac
115
- fi
116
-
117
- # --- issue-fetcher.sh: input-kind detection (no network) ---------------------
118
- echo "→ issue-fetcher.sh"
119
- FETCHER="$LIB_DIR/issue-fetcher.sh"
120
- if [ ! -x "$FETCHER" ]; then
121
- fail "issue-fetcher.sh missing or not executable"
122
- else
123
- # detect_kind is internal; we exercise via freetext + jira-id paths that
124
- # don't hit the network. Freetext is the safest offline assertion.
125
- out=$("$FETCHER" "Login crash on iOS 17" 2>/dev/null || echo "")
126
- if printf '%s' "$out" | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); assert d["kind"]=="freetext", d["kind"]; assert d["maturity"]["score"] is None, "freetext score should be None"' 2>/dev/null; then
127
- ok "freetext path emits kind=freetext + maturity.score=None"
128
- else
129
- fail "freetext path output unexpected: $(printf '%s' "$out" | head -c 200)"
130
- fi
131
-
132
- # Jira-id detection without token (fetch will fail, but kind detection should still work)
133
- out=$(ACCOUNT_JIRA_TOKEN_KEY="" ACCOUNT_JIRA_HOST="" "$FETCHER" "PROJ-1" 2>/dev/null || echo "")
134
- if printf '%s' "$out" | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); assert d["kind"]=="jira", d["kind"]; assert d["key"]=="PROJ-1", d["key"]' 2>/dev/null; then
135
- ok "jira-id path emits kind=jira + key parsed"
136
- else
137
- fail "jira-id path output unexpected"
138
- fi
139
-
140
- # Bare "#316" and "316" must land in the gh-num branch (needsRepoPicker
141
- # descriptor when no default owner/repo), NOT gh-short with an empty repo
142
- # prefix that builds https://github.com/<owner>//issues/316.
143
- for bare in "#316" "316"; do
144
- out=$(ACCOUNT_DEFAULT_OWNER="" ACCOUNT_DEFAULT_REPO="" "$FETCHER" "$bare" 2>/dev/null || echo "")
145
- if printf '%s' "$out" | python3 -c 'import json,sys; d=json.loads(sys.stdin.read()); assert d["kind"]=="github", d["kind"]; assert d["key"]=="316", d["key"]; assert d.get("needsRepoPicker") is True, d' 2>/dev/null; then
146
- ok "bare '$bare' classified as gh-num (key=316, needsRepoPicker)"
147
- else
148
- fail "bare '$bare' misclassified: $(printf '%s' "$out" | head -c 200)"
149
- fi
150
- done
151
- fi
152
-
153
- # --- multi-repo-pipeline.sh: bridge --------------------------------------
154
- echo "→ multi-repo-pipeline.sh bridge"
155
- MRP="$LIB_DIR/multi-repo-pipeline.sh"
156
- if [ ! -x "$MRP" ]; then
157
- fail "multi-repo-pipeline.sh missing or not executable"
158
- else
159
- TMP=$(mktemp -d)
160
- cat > "$TMP/state.json" <<EOF
161
- {
162
- "taskId": "PROJ-1-smoke-$$",
163
- "shortId": 1,
164
- "branch": "feature/PROJ-1",
165
- "baseBranch": "develop",
166
- "issueRef": {"kind":"jira","key":"PROJ-1","title":"Smoke test"},
167
- "primary": {"name":"my-app","cloneUrl":"git@github.com:org/my-app.git","provider":"github"},
168
- "extras": [{"name":"common","cloneUrl":"git@github.com:org/common.git","provider":"github"}],
169
- "worktreeRoot": "$TMP/worktree",
170
- "commitType": "feat",
171
- "identity": {"name":"Smoke","email":"smoke@example.com"}
172
- }
173
- EOF
174
- bridged=$("$MRP" bridge "$TMP/state.json" 2>/dev/null || echo "")
175
- if [ -z "$bridged" ] || [ ! -f "$bridged" ]; then
176
- fail "bridge did not write agent-state.json"
177
- else
178
- if python3 - <<PY 2>/dev/null
179
- import json
180
- import jsonschema # may not be installed in CI; fall back to shape check
181
- PY
182
- then
183
- python3 - "$bridged" "$REPO_ROOT/pipeline/schemas/agent-state.schema.json" <<'PY' && ok "bridge output schema-valid (jsonschema)" || fail "bridge output schema-invalid"
184
- import json, sys
185
- from jsonschema import Draft202012Validator
186
- data = json.load(open(sys.argv[1]))
187
- schema = json.load(open(sys.argv[2]))
188
- errs = list(Draft202012Validator(schema).iter_errors(data))
189
- if errs:
190
- for e in errs: print("✗", "/".join(str(p) for p in e.absolute_path), e.message)
191
- sys.exit(1)
192
- PY
193
- else
194
- # Fallback: shape-only check
195
- if python3 - "$bridged" <<'PY' 2>/dev/null
196
- import json, sys
197
- d = json.load(open(sys.argv[1]))
198
- required = {"schemaVersion","taskId","shortId","project","branch","baseBranch","currentPhase","status","startedAt","phases","identity","projects"}
199
- missing = required - set(d.keys())
200
- assert not missing, f"missing: {missing}"
201
- assert d["schemaVersion"] == "2.1.0"
202
- assert len(d["projects"]) == 2
203
- PY
204
- then
205
- ok "bridge output has required fields (jsonschema unavailable; shape check)"
206
- else
207
- fail "bridge output missing required fields"
208
- fi
209
- fi
210
- rm -rf "$(dirname "$bridged")"
211
- fi
212
- rm -rf "$TMP"
213
- fi
214
-
215
- # --- multi-repo-pipeline.sh: prepare + commit + teardown (offline) -----------
216
- # Uses local bare-repo fixtures so we can exercise prepare/commit/teardown
217
- # without network access. push and pr remain unverified (network-bound).
218
- echo "→ multi-repo-pipeline.sh prepare/commit/teardown (offline fixtures)"
219
- MRP="$LIB_DIR/multi-repo-pipeline.sh"
220
- if [ ! -x "$MRP" ]; then
221
- fail "multi-repo-pipeline.sh not executable (already reported above)"
222
- else
223
- FIXTURE_ROOT=$(mktemp -d)
224
- REPO_A_BARE="$FIXTURE_ROOT/repo-a.git"
225
- REPO_B_BARE="$FIXTURE_ROOT/repo-b.git"
226
- WORKTREE_ROOT="$FIXTURE_ROOT/wt"
227
-
228
- # Build two bare-repo fixtures with one initial commit each.
229
- for bare in "$REPO_A_BARE" "$REPO_B_BARE"; do
230
- git init --quiet --bare -b main "$bare"
231
- seed=$(mktemp -d)
232
- git -C "$seed" init --quiet -b main
233
- echo "init" > "$seed/README.md"
234
- git -C "$seed" add README.md
235
- git -C "$seed" -c user.email=t@e.com -c user.name=T commit --quiet -m "init"
236
- git -C "$seed" remote add origin "$bare"
237
- git -C "$seed" push --quiet origin main
238
- rm -rf "$seed"
239
- done
240
-
241
- cat > "$FIXTURE_ROOT/state.json" <<EOF
242
- {
243
- "taskId":"OFFLINE-1",
244
- "shortId":1,
245
- "branch":"feature/OFFLINE-1",
246
- "baseBranch":"main",
247
- "issueRef":{"kind":"jira","key":"OFFLINE-1","title":"smoke fixture"},
248
- "primary":{"name":"repo-a","cloneUrl":"$REPO_A_BARE","provider":"github"},
249
- "extras":[{"name":"repo-b","cloneUrl":"$REPO_B_BARE","provider":"github"}],
250
- "worktreeRoot":"$WORKTREE_ROOT",
251
- "commitType":"feat",
252
- "identity":{"name":"T","email":"t@e.com"}
253
- }
254
- EOF
255
-
256
- # prepare
257
- if "$MRP" prepare "$FIXTURE_ROOT/state.json" >/dev/null 2>&1; then
258
- if [ -d "$WORKTREE_ROOT/repo-a/.git" ] && [ -d "$WORKTREE_ROOT/repo-b/.git" ]; then
259
- ok "prepare clones every repo into worktreeRoot"
260
- else
261
- fail "prepare did not create both worktrees"
262
- fi
263
- branch_a=$(git -C "$WORKTREE_ROOT/repo-a" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
264
- branch_b=$(git -C "$WORKTREE_ROOT/repo-b" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
265
- if [ "$branch_a" = "feature/OFFLINE-1" ] && [ "$branch_b" = "feature/OFFLINE-1" ]; then
266
- ok "prepare checks out the task branch in every repo"
267
- else
268
- fail "prepare branch mismatch: a=$branch_a b=$branch_b"
269
- fi
270
- else
271
- fail "prepare exited non-zero on offline fixture"
272
- fi
273
-
274
- # commit
275
- echo "change-a" > "$WORKTREE_ROOT/repo-a/feature.txt"
276
- echo "change-b" > "$WORKTREE_ROOT/repo-b/feature.txt"
277
- git -C "$WORKTREE_ROOT/repo-a" config user.email t@e.com >/dev/null 2>&1
278
- git -C "$WORKTREE_ROOT/repo-a" config user.name T >/dev/null 2>&1
279
- git -C "$WORKTREE_ROOT/repo-b" config user.email t@e.com >/dev/null 2>&1
280
- git -C "$WORKTREE_ROOT/repo-b" config user.name T >/dev/null 2>&1
281
-
282
- if "$MRP" commit "$FIXTURE_ROOT/state.json" >/dev/null 2>&1; then
283
- msg_a=$(git -C "$WORKTREE_ROOT/repo-a" log -1 --pretty=%s 2>/dev/null || echo "")
284
- msg_b=$(git -C "$WORKTREE_ROOT/repo-b" log -1 --pretty=%s 2>/dev/null || echo "")
285
- if printf '%s' "$msg_a" | grep -q '\[OFFLINE-1\]' && printf '%s' "$msg_b" | grep -q '\[OFFLINE-1\]'; then
286
- ok "commit creates one commit per repo with [JIRA-KEY] suffix"
287
- else
288
- fail "commit messages missing key suffix: a=$msg_a b=$msg_b"
289
- fi
290
- else
291
- fail "commit exited non-zero"
292
- fi
293
-
294
- # bridge (re-verify schema-valid output uses the repo names)
295
- bridged=$("$MRP" bridge "$FIXTURE_ROOT/state.json" 2>/dev/null || echo "")
296
- if [ -n "$bridged" ] && python3 -c '
297
- import json, sys
298
- d = json.load(open(sys.argv[1]))
299
- assert len(d["projects"]) == 2
300
- assert d["projects"][0]["project"] == "repo-a"
301
- assert d["projects"][1]["project"] == "repo-b"
302
- ' "$bridged" 2>/dev/null; then
303
- ok "bridge populates projects[] from primary + extras"
304
- rm -rf "$(dirname "$bridged")"
305
- else
306
- fail "bridge output missing projects[] entries"
307
- fi
308
-
309
- # teardown guard: a worktreeRoot of "." (the current project root) must be
310
- # refused loudly, never rm -rf'd.
311
- UNSAFE_STATE="$FIXTURE_ROOT/unsafe-state.json"
312
- sed 's|"worktreeRoot":"[^"]*"|"worktreeRoot":"."|' "$FIXTURE_ROOT/state.json" > "$UNSAFE_STATE"
313
- GUARD_DIR=$(mktemp -d)
314
- ( cd "$GUARD_DIR" && "$MRP" teardown "$UNSAFE_STATE" ) >/dev/null 2>&1
315
- guard_rc=$?
316
- if [ "$guard_rc" -ne 0 ] && [ -d "$GUARD_DIR" ]; then
317
- ok "teardown refuses worktreeRoot='.' (exit=$guard_rc, dir intact)"
318
- else
319
- fail "teardown did not refuse unsafe worktreeRoot='.' (exit=$guard_rc)"
320
- fi
321
- rm -rf "$GUARD_DIR"
322
-
323
- # teardown
324
- "$MRP" teardown "$FIXTURE_ROOT/state.json" >/dev/null 2>&1
325
- if [ ! -d "$WORKTREE_ROOT" ]; then
326
- ok "teardown removes worktreeRoot"
327
- else
328
- fail "teardown left $WORKTREE_ROOT in place"
329
- fi
330
-
331
- rm -rf "$FIXTURE_ROOT"
332
- fi
333
-
334
- # --- channels-multi-repo.sh: render fan-out (offline) ------------------------
335
- echo "→ channels-multi-repo.sh renderers"
336
- CMR="$LIB_DIR/channels-multi-repo.sh"
337
- if [ ! -x "$CMR" ]; then
338
- fail "channels-multi-repo.sh missing or not executable"
339
- else
340
- TMP=$(mktemp -d)
341
- cat > "$TMP/multi.json" <<'EOF'
342
- {
343
- "schemaVersion":"2.1.0","taskId":"X","shortId":1,"project":"my-app",
344
- "branch":"feature/X","baseBranch":"develop","currentPhase":6,"status":"in_progress",
345
- "startedAt":"2026-04-27T12:00:00Z","phases":{},"identity":{"name":"T","email":"t@e.com"},
346
- "projects":[
347
- {"project":"my-app","projectRoot":"/tmp/a","branch":"feature/X","baseBranch":"develop","identity":{"name":"T","email":"t@e.com"},"pr":{"number":42,"url":"https://github.com/org/my-app/pull/42"}},
348
- {"project":"common","projectRoot":"/tmp/b","branch":"feature/X","baseBranch":"develop","identity":{"name":"T","email":"t@e.com"},"pr":{"number":17,"url":"https://github.com/org/common/pull/17"}}
349
- ]
350
- }
351
- EOF
352
- cat > "$TMP/single.json" <<'EOF'
353
- {
354
- "schemaVersion":"2.1.0","taskId":"Y","shortId":1,"project":"my-app",
355
- "branch":"feature/Y","baseBranch":"develop","currentPhase":6,"status":"in_progress",
356
- "startedAt":"2026-04-27T12:00:00Z","phases":{},"identity":{"name":"T","email":"t@e.com"},
357
- "pr":{"number":1,"url":"https://github.com/org/my-app/pull/1"}
358
- }
359
- EOF
360
- echo "body content" > "$TMP/body.md"
361
-
362
- # targets: multi-repo gives 2 entries with cross-links populated
363
- count=$("$CMR" targets "$TMP/multi.json" 2>/dev/null | python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d))')
364
- if [ "$count" = "2" ]; then
365
- ok "targets emits one entry per project (multi-repo)"
366
- else
367
- fail "targets count $count != 2"
368
- fi
369
-
370
- # render-pr: extras body must mention 'Part of:' primary URL
371
- rendered=$("$CMR" render-pr "$TMP/multi.json" "$TMP/body.md" "common" 2>/dev/null)
372
- if printf '%s' "$rendered" | grep -q "Part of: https://github.com/org/my-app"; then
373
- ok "render-pr injects 'Part of:' link on extras"
374
- else
375
- fail "render-pr missing 'Part of:' on extras"
376
- fi
377
- rendered=$("$CMR" render-pr "$TMP/multi.json" "$TMP/body.md" "my-app" 2>/dev/null)
378
- if printf '%s' "$rendered" | grep -q "Related: https://github.com/org/common"; then
379
- ok "render-pr injects 'Related:' link on primary"
380
- else
381
- fail "render-pr missing 'Related:' on primary"
382
- fi
383
-
384
- # render-jira: bulleted PR list at top
385
- rendered=$("$CMR" render-jira "$TMP/multi.json" "$TMP/body.md" 2>/dev/null)
386
- bullet_count=$(printf '%s' "$rendered" | grep -c '^\* PR:' || true)
387
- if [ "$bullet_count" = "2" ]; then
388
- ok "render-jira prepends bulleted PR list (2 entries)"
389
- else
390
- fail "render-jira bullet count $bullet_count != 2"
391
- fi
392
-
393
- # render-jira/-conf with ZERO PR urls: must pass the body through unchanged
394
- # (the old `grep -c || echo 0` idiom emitted "0\n0" here and prepended
395
- # bogus "* PR:" bullets to the posted body).
396
- cat > "$TMP/noprs.json" <<'EOF'
397
- {
398
- "schemaVersion":"2.1.0","taskId":"Z","shortId":1,"project":"my-app",
399
- "branch":"feature/Z","baseBranch":"develop","currentPhase":6,"status":"in_progress",
400
- "startedAt":"2026-04-27T12:00:00Z","phases":{},"identity":{"name":"T","email":"t@e.com"},
401
- "projects":[
402
- {"project":"my-app","projectRoot":"/tmp/a","branch":"feature/Z","baseBranch":"develop","identity":{"name":"T","email":"t@e.com"},"pr":null},
403
- {"project":"common","projectRoot":"/tmp/b","branch":"feature/Z","baseBranch":"develop","identity":{"name":"T","email":"t@e.com"},"pr":null}
404
- ]
405
- }
406
- EOF
407
- rendered=$("$CMR" render-jira "$TMP/noprs.json" "$TMP/body.md" 2>/dev/null)
408
- if [ "$rendered" = "body content" ]; then
409
- ok "render-jira with zero PR urls passes body through unchanged"
410
- else
411
- fail "render-jira with zero PR urls mutated body: $rendered"
412
- fi
413
- rendered=$("$CMR" render-conf "$TMP/noprs.json" "$TMP/body.md" 2>/dev/null)
414
- if [ "$rendered" = "body content" ]; then
415
- ok "render-conf with zero PR urls passes body through unchanged"
416
- else
417
- fail "render-conf with zero PR urls mutated body: $rendered"
418
- fi
419
-
420
- # render-conf: ## Related PRs heading
421
- rendered=$("$CMR" render-conf "$TMP/multi.json" "$TMP/body.md" 2>/dev/null)
422
- if printf '%s' "$rendered" | grep -q "## Related PRs"; then
423
- ok "render-conf prepends 'Related PRs' heading"
424
- else
425
- fail "render-conf missing heading block"
426
- fi
427
-
428
- # single-repo passthrough: render-pr on single state must return body unchanged
429
- rendered=$("$CMR" render-pr "$TMP/single.json" "$TMP/body.md" "my-app" 2>/dev/null)
430
- if [ "$rendered" = "body content" ]; then
431
- ok "single-repo passthrough leaves body unchanged"
432
- else
433
- fail "single-repo passthrough mutated body: $rendered"
434
- fi
435
-
436
- rm -rf "$TMP"
437
- fi
438
-
439
- # --- Summary -----------------------------------------------------------------
440
- echo ""
441
- TOTAL=$((PASS+FAIL))
442
- if [ $FAIL -eq 0 ]; then
443
- echo "══ lib-scripts smoke: $PASS passed, 0 failed ══"
444
- exit 0
445
- else
446
- echo "══ lib-scripts smoke: $PASS passed, $FAIL failed (of $TOTAL) ══" >&2
447
- exit 1
448
- fi
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-mcp-gate.sh - fixture-based test harness for smoke-no-mcp-in-dev-phases.sh.
3
- #
4
- # The gate script doubles as the production check (run against a live
5
- # state.telemetry.mcpCalls[] after a pipeline run) and is also picked up by the
6
- # smoke-*.sh glob - but bare, with no live run, it only WARNs. This harness
7
- # drives the gate with crafted state files so the actual decision logic
8
- # (PASS / FAIL / WARN / parse-error) is exercised in CI on every run.
9
-
10
- set -uo pipefail
11
-
12
- REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
13
- GATE="$REPO_ROOT/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh"
14
-
15
- PASS=0
16
- FAIL=0
17
- pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
18
- fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
19
-
20
- cleanup() { [ -n "${TMP:-}" ] && rm -rf "$TMP"; }
21
- trap cleanup EXIT
22
- TMP=$(mktemp -d)
23
-
24
- run_gate() { bash "$GATE" --state-file "$1" >/dev/null 2>&1; echo $?; }
25
-
26
- echo "→ 1. Clean run (all MCP calls in Phase 1) PASSES"
27
- cat > "$TMP/clean.json" <<'EOF'
28
- {"telemetry":{"mcpCalls":[
29
- {"tool":"mcp__claude_ai_Figma__get_design_context","phase":1,"timestamp":"2026-05-29T10:00:00Z"},
30
- {"tool":"mcp__claude_ai_Figma__get_screenshot","phase":1,"timestamp":"2026-05-29T10:00:05Z"}
31
- ]}}
32
- EOF
33
- [ "$(run_gate "$TMP/clean.json")" = "0" ] && pass "phase-1-only telemetry exits 0" || fail "phase-1-only telemetry should exit 0"
34
-
35
- echo "→ 2. Violation (MCP call in Phase 3) FAILS"
36
- cat > "$TMP/viol.json" <<'EOF'
37
- {"telemetry":{"mcpCalls":[
38
- {"tool":"mcp__claude_ai_Figma__get_design_context","phase":1,"timestamp":"2026-05-29T10:00:00Z"},
39
- {"tool":"mcp__claude_ai_Figma__get_screenshot","phase":3,"timestamp":"2026-05-29T11:00:00Z"}
40
- ]}}
41
- EOF
42
- [ "$(run_gate "$TMP/viol.json")" = "1" ] && pass "phase-3 MCP call exits 1" || fail "phase-3 MCP call should exit 1 (gate is a no-op?)"
43
-
44
- echo "→ 3. Phase 2+ boundary (phase 2 is a violation)"
45
- cat > "$TMP/viol2.json" <<'EOF'
46
- {"telemetry":{"mcpCalls":[{"tool":"mcp__claude_ai_Figma__get_metadata","phase":2,"timestamp":"2026-05-29T10:30:00Z"}]}}
47
- EOF
48
- [ "$(run_gate "$TMP/viol2.json")" = "1" ] && pass "phase-2 MCP call exits 1" || fail "phase-2 MCP call should exit 1"
49
-
50
- echo "→ 4. Empty mcpCalls array PASSES"
51
- cat > "$TMP/empty.json" <<'EOF'
52
- {"telemetry":{"mcpCalls":[]}}
53
- EOF
54
- [ "$(run_gate "$TMP/empty.json")" = "0" ] && pass "empty mcpCalls exits 0" || fail "empty mcpCalls should exit 0"
55
-
56
- echo "→ 5. Absent telemetry field PASSES (backward compat with pre-v9 runs)"
57
- cat > "$TMP/absent.json" <<'EOF'
58
- {"taskId":"#1","status":"complete"}
59
- EOF
60
- [ "$(run_gate "$TMP/absent.json")" = "0" ] && pass "absent telemetry exits 0" || fail "absent telemetry should exit 0"
61
-
62
- echo "→ 6. Corrupt JSON state is a hard error (not silently passed)"
63
- echo '{not valid json' > "$TMP/corrupt.json"
64
- [ "$(run_gate "$TMP/corrupt.json")" = "2" ] && pass "corrupt state exits 2" || fail "corrupt state should exit 2"
65
-
66
- echo ""
67
- echo "══ mcp-gate smoke: $PASS passed, $FAIL failed ══"
68
- [ "$FAIL" -eq 0 ]
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-md-links.sh - thin wrapper so the internal markdown link gate runs
3
- # inside the standard smoke discovery (run-smokes.mjs picks up smoke-*.sh).
4
- # Logic lives in check-md-links.mjs.
5
- set -euo pipefail
6
-
7
- ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
8
- node "$ROOT/pipeline/scripts/check-md-links.mjs"
@@ -1,126 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-md2confluence.sh - offline sanity checks for md2confluence-v3.py.
3
- # Imports the module from its file path and exercises the pure conversion
4
- # layer: front-matter parsing, the TR/EN punctuation gate (fancy punctuation
5
- # stripped, diacritics preserved), and markdown -> storage-XML rendering for
6
- # headings, tables, and code blocks. No network, no credentials, no Confluence.
7
-
8
- set -uo pipefail
9
-
10
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
11
- REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
12
- MOD="$REPO_ROOT/pipeline/lib/md2confluence-v3.py"
13
-
14
- if ! command -v python3 >/dev/null 2>&1; then
15
- echo "smoke-md2confluence: python3 not found; skipping" >&2
16
- exit 0
17
- fi
18
-
19
- if [ ! -f "$MOD" ]; then
20
- echo " ✗ md2confluence-v3.py missing at $MOD" >&2
21
- exit 1
22
- fi
23
-
24
- MOD_PATH="$MOD" python3 - <<'PYEOF'
25
- import importlib.util
26
- import os
27
- import sys
28
-
29
- spec = importlib.util.spec_from_file_location("md2c", os.environ["MOD_PATH"])
30
- mod = importlib.util.module_from_spec(spec)
31
- sys.modules["md2c"] = mod # dataclass introspection needs the module registered
32
- spec.loader.exec_module(mod)
33
-
34
- passed = 0
35
- failed = 0
36
-
37
- def ok(msg):
38
- global passed
39
- print(f" ✓ {msg}")
40
- passed += 1
41
-
42
- def fail(msg):
43
- global failed
44
- print(f" ✗ {msg}", file=sys.stderr)
45
- failed += 1
46
-
47
- # --- parse_front_matter ------------------------------------------------------
48
- print("→ parse_front_matter")
49
- fm, body = mod.parse_front_matter("---\nfeature: Sample\nlanguage: tr\n---\n# Heading\nbody text\n")
50
- if fm.get("feature") == "Sample" and fm.get("language") == "tr":
51
- ok("front-matter keys parsed")
52
- else:
53
- fail(f"front-matter parse wrong: {fm}")
54
- if body.startswith("# Heading"):
55
- ok("body separated from front-matter")
56
- else:
57
- fail(f"body not separated: {body[:40]!r}")
58
-
59
- fm2, body2 = mod.parse_front_matter("# No front matter\n")
60
- if fm2 == {} and body2.startswith("# No"):
61
- ok("no-front-matter input passes through")
62
- else:
63
- fail("no-front-matter input mangled")
64
-
65
- # --- apply_punctuation_gate ----------------------------------------------------
66
- print("→ apply_punctuation_gate")
67
- dirty = "Geliştirme Özeti — kullanıcı için “detay” …"
68
- clean, warnings = mod.apply_punctuation_gate(dirty, "tr")
69
- forbidden = ["—", "–", "…", "“", "”", "‘", "’"]
70
- if not any(ch in clean for ch in forbidden):
71
- ok("fancy punctuation stripped")
72
- else:
73
- fail(f"fancy punctuation survived: {clean!r}")
74
- for ch in ("ş", "Ö", "ı", "ç"):
75
- if ch not in clean:
76
- fail(f"diacritic {ch!r} was lost - gate must not ASCII-fold")
77
- break
78
- else:
79
- ok("Turkish diacritics preserved")
80
-
81
- # --- markdown_to_storage ---------------------------------------------------------
82
- print("→ markdown_to_storage")
83
- md = "\n".join([
84
- "# Title",
85
- "",
86
- "Intro paragraph.",
87
- "",
88
- "| A | B |",
89
- "|---|---|",
90
- "| 1 | 2 |",
91
- "",
92
- "```swift",
93
- "let x = 1",
94
- "```",
95
- ])
96
- res = mod.markdown_to_storage(
97
- md,
98
- attachments_available=set(),
99
- mermaid_fallback=True,
100
- tooltip_macro_supported=False,
101
- )
102
- xml = res.storage_xml
103
- checks = [
104
- ("<h1>", "h1 heading rendered"),
105
- ("<table>", "table rendered"),
106
- ("ac:structured-macro", "code block rendered as macro"),
107
- ]
108
- for needle, label in checks:
109
- if needle in xml:
110
- ok(label)
111
- else:
112
- fail(f"{label}: {needle!r} not found in storage XML")
113
-
114
- if res.images_referenced == []:
115
- ok("no phantom image references")
116
- else:
117
- fail(f"unexpected images referenced: {res.images_referenced}")
118
-
119
- # --- summary -------------------------------------------------------------------
120
- print()
121
- if failed == 0:
122
- print(f"══ md2confluence smoke: {passed} passed, 0 failed ══")
123
- sys.exit(0)
124
- print(f"══ md2confluence smoke: {passed} passed, {failed} failed ══", file=sys.stderr)
125
- sys.exit(1)
126
- PYEOF