@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,224 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-shadow-git.sh
3
- #
4
- # Verifies the shadow-git checkpoint surface:
5
- # 1. pipeline/lib/shadow-git.sh exists, parses cleanly
6
- # 2. --help / unknown subcommand exits non-zero with usage
7
- # 3. doctor runs cleanly on an empty environment
8
- # 4. Lifecycle: init → snapshot → list → restore (--files) → prune
9
- # 5. Baseline snapshot is auto-created by init
10
- # 6. snapshot with no changes is a no-op (exits 0, no new commit)
11
- # 7. node_modules / Pods / DerivedData NOT tracked even when ignored
12
- # isn't already in the project's .gitignore
13
- # 8. restore actually reverts file contents
14
- # 9. prune removes the shadow dir
15
- # 10. prefs schema exposes shadowGit.{enabled, mode, pruneAfterDays}
16
- # 11. shadowGit.enabled defaults to false (opt-in)
17
- # 12. shadowGit.mode default is per-todo-step
18
- # 13. Phase 3 doc documents shadow-git.sh wiring (init + snapshot + restore)
19
- #
20
- # Exit 0 = all pass, 1 = any failure.
21
-
22
- set -uo pipefail
23
-
24
- ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
25
- SG="$ROOT/pipeline/lib/shadow-git.sh"
26
- PREFS="$ROOT/pipeline/schemas/prefs.schema.json"
27
- P3="$ROOT/pipeline/multi-agent-refs/phases/phase-3-dev.md"
28
-
29
- pass=0
30
- fail=0
31
- failures=()
32
- record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
33
- record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
34
-
35
- printf '→ smoke-shadow-git: per-tool-call checkpoint contract\n'
36
-
37
- # 1. Script exists + parses
38
- if [ ! -f "$SG" ]; then
39
- record_fail "pipeline/lib/shadow-git.sh missing"
40
- elif ! bash -n "$SG" 2>/dev/null; then
41
- record_fail "shadow-git.sh syntax error"
42
- else
43
- record_pass "shadow-git.sh exists and parses"
44
- fi
45
-
46
- # 2. Unknown subcommand
47
- if bash "$SG" garbage >/dev/null 2>&1; then
48
- record_fail "unknown subcommand should exit non-zero"
49
- else
50
- record_pass "unknown subcommand rejected"
51
- fi
52
-
53
- # 3. Doctor on empty environment
54
- if bash "$SG" doctor >/dev/null 2>&1; then
55
- record_pass "doctor runs cleanly"
56
- else
57
- record_fail "doctor crashed on empty environment"
58
- fi
59
-
60
- # 4-9. Lifecycle on a temp project
61
- TMP=$(mktemp -d)
62
- # A failed cd here would run the relative `rm -rf DerivedData` below in the
63
- # caller's directory instead of the sandbox.
64
- cd "$TMP" || { record_fail "cannot cd into sandbox $TMP"; exit 1; }
65
- git init --quiet
66
- echo "node_modules/" > .gitignore
67
- echo "hello" > a.txt
68
- mkdir -p node_modules && echo "trash" > node_modules/x.txt
69
-
70
- TASK_ID="SMOKE-SG-$$"
71
- if bash "$SG" init "$TASK_ID" "$TMP" >/dev/null 2>&1; then
72
- record_pass "init creates shadow repo"
73
- else
74
- record_fail "init failed"
75
- fi
76
-
77
- # Baseline snapshot exists
78
- list_after_init=$(bash "$SG" list "$TASK_ID" 2>/dev/null | awk 'NF{c++} END{print c+0}')
79
- if [ "$list_after_init" -eq 1 ]; then
80
- record_pass "init created exactly one baseline snapshot"
81
- else
82
- record_fail "init should create baseline; got $list_after_init snapshots"
83
- fi
84
-
85
- # 6. No-op snapshot
86
- bash "$SG" snapshot "$TASK_ID" "$TMP" "no-op-attempt" >/dev/null 2>&1
87
- list_after_noop=$(bash "$SG" list "$TASK_ID" 2>/dev/null | awk 'NF{c++} END{print c+0}')
88
- if [ "$list_after_noop" -eq 1 ]; then
89
- record_pass "snapshot with no changes is a no-op (still 1 commit)"
90
- else
91
- record_fail "no-op snapshot created a phantom commit"
92
- fi
93
-
94
- # 7. node_modules NOT tracked
95
- if bash "$SG" list "$TASK_ID" >/dev/null 2>&1 \
96
- && ! git --git-dir="$HOME/.claude/state/shadow-git/$TASK_ID/.git" --work-tree="$TMP" \
97
- ls-files 2>/dev/null | grep -q "^node_modules/"; then
98
- record_pass "node_modules NOT tracked by shadow repo"
99
- else
100
- record_fail "node_modules leaked into shadow tracking"
101
- fi
102
-
103
- # 7b. Forced excludes apply even when NOT in the project .gitignore
104
- # (DerivedData is not in this fixture's .gitignore; it must be excluded via
105
- # the shadow repo's .git/info/exclude).
106
- mkdir -p DerivedData && echo "buildjunk" > DerivedData/junk.txt
107
- bash "$SG" snapshot "$TASK_ID" "$TMP" "derived-data-probe" >/dev/null 2>&1
108
- if ! git --git-dir="$HOME/.claude/state/shadow-git/$TASK_ID/.git" --work-tree="$TMP" \
109
- ls-files 2>/dev/null | grep -q "^DerivedData/"; then
110
- record_pass "DerivedData NOT tracked (forced exclude in .git/info/exclude)"
111
- else
112
- record_fail "DerivedData leaked into shadow tracking (forced excludes dead)"
113
- fi
114
- rm -rf DerivedData
115
-
116
- # Make a real edit + a brand-new file + snapshot
117
- echo "hello-changed" > a.txt
118
- echo "post-baseline" > b.txt
119
- sha_edit=$(bash "$SG" snapshot "$TASK_ID" "$TMP" "edited a.txt" 2>/dev/null | head -1)
120
- if [ -n "$sha_edit" ] && [ "${#sha_edit}" -ge 7 ]; then
121
- record_pass "snapshot after edit produced a sha: $sha_edit"
122
- else
123
- record_fail "snapshot after edit returned no sha"
124
- fi
125
-
126
- list_now=$(bash "$SG" list "$TASK_ID" 2>/dev/null | awk 'NF{c++} END{print c+0}')
127
- if [ "$list_now" -eq 2 ]; then
128
- record_pass "list now shows 2 snapshots"
129
- else
130
- record_fail "expected 2 snapshots, got $list_now"
131
- fi
132
-
133
- # 8. Restore the baseline
134
- echo "never-snapshotted" > c.txt # created after the last snapshot
135
- baseline_sha=$(bash "$SG" list "$TASK_ID" 2>/dev/null | tail -1 | awk '{print $1}')
136
- bash "$SG" restore "$TASK_ID" "$TMP" "$baseline_sha" --files >/dev/null 2>&1
137
- if [ "$(cat "$TMP/a.txt")" = "hello" ]; then
138
- record_pass "restore reverted a.txt to baseline content"
139
- else
140
- record_fail "restore did not revert a.txt (got '$(cat "$TMP/a.txt")')"
141
- fi
142
-
143
- # 8b. Restore removes files that did not exist in the snapshot
144
- if [ ! -f "$TMP/b.txt" ]; then
145
- record_pass "restore removed b.txt (committed after baseline)"
146
- else
147
- record_fail "restore left b.txt in place (created after snapshot must be removed)"
148
- fi
149
- if [ ! -f "$TMP/c.txt" ]; then
150
- record_pass "restore removed c.txt (never snapshotted)"
151
- else
152
- record_fail "restore left c.txt in place (untracked new file must be removed)"
153
- fi
154
- if [ -f "$TMP/node_modules/x.txt" ]; then
155
- record_pass "restore left ignored node_modules untouched"
156
- else
157
- record_fail "restore deleted ignored files (node_modules must survive clean)"
158
- fi
159
-
160
- # 9. Prune
161
- bash "$SG" prune "$TASK_ID" >/dev/null 2>&1
162
- if [ ! -d "$HOME/.claude/state/shadow-git/$TASK_ID" ]; then
163
- record_pass "prune removed the shadow repo"
164
- else
165
- record_fail "prune did not remove the shadow dir"
166
- fi
167
-
168
- # 9b. Task-id validation: traversal and separator ids are rejected before any
169
- # rm -rf (a "prune .." must never delete ~/.claude/state).
170
- for bad_id in ".." "." "a/b" "-x!"; do
171
- if bash "$SG" prune "$bad_id" >/dev/null 2>&1; then
172
- record_fail "prune accepted unsafe task id '$bad_id'"
173
- else
174
- record_pass "prune rejects unsafe task id '$bad_id'"
175
- fi
176
- done
177
- if [ -d "$HOME/.claude/state" ]; then
178
- record_pass "~/.claude/state survived unsafe prune attempts"
179
- else
180
- record_fail "~/.claude/state is GONE after unsafe prune attempts"
181
- fi
182
-
183
- cd - >/dev/null || true
184
- rm -rf "$TMP"
185
-
186
- # 10-12. Prefs schema
187
- for prop in enabled mode pruneAfterDays; do
188
- if jq -e ".properties.global.properties.shadowGit.properties.${prop}" "$PREFS" >/dev/null 2>&1; then
189
- record_pass "prefs schema exposes shadowGit.${prop}"
190
- else
191
- record_fail "prefs schema missing shadowGit.${prop}"
192
- fi
193
- done
194
- if jq -e '.properties.global.properties.shadowGit.properties.enabled
195
- | has("default") and .default == false' "$PREFS" >/dev/null 2>&1; then
196
- record_pass "shadowGit.enabled defaults to false"
197
- else
198
- record_fail "shadowGit.enabled should default to false"
199
- fi
200
- mode_default=$(jq -r '.properties.global.properties.shadowGit.properties.mode.default // empty' "$PREFS")
201
- if [ "$mode_default" = "per-todo-step" ]; then
202
- record_pass "shadowGit.mode defaults to per-todo-step"
203
- else
204
- record_fail "shadowGit.mode default should be per-todo-step (got: ${mode_default:-missing})"
205
- fi
206
-
207
- # 13. Phase 3 doc wiring
208
- if [ ! -f "$P3" ]; then
209
- record_fail "phase-3-dev.md missing"
210
- elif grep -qE 'shadow-git\.sh"? init' "$P3" \
211
- && grep -qE 'shadow-git\.sh"? snapshot' "$P3" \
212
- && grep -qE 'shadow-git\.sh"? restore' "$P3"; then
213
- record_pass "phase-3-dev.md documents init + snapshot + restore"
214
- else
215
- record_fail "phase-3-dev.md missing one of init/snapshot/restore wiring"
216
- fi
217
-
218
- printf '\n══ shadow-git smoke: %d passed, %d failed ══\n' "$pass" "$fail"
219
- if [ "$fail" -gt 0 ]; then
220
- printf '\nFailures:\n'
221
- for msg in "${failures[@]}"; do printf ' - %s\n' "$msg"; done
222
- exit 1
223
- fi
224
- exit 0
@@ -1,137 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-skill-authoring.sh - authoring-hygiene audit for OUR authored skills.
3
- #
4
- # Based on Anthropic's skill-authoring guide + alexop.dev "Stop Bloating Your
5
- # CLAUDE.md" (April 2026 community consensus).
6
- #
7
- # SCOPE: only skills we author. Specifically:
8
- #
9
- # pipeline/skills/shared/core/ - our orchestration + compliance skills
10
- #
11
- # INTENTIONALLY NOT AUDITED (upstream imports / different schema):
12
- #
13
- # pipeline/skills/shared/external/ - curated third-party skills (own frontmatter)
14
- #
15
- # Rules:
16
- # 1. Every SKILL.md has YAML frontmatter (--- ... ---)
17
- # 2. Frontmatter `name` matches the directory name
18
- # 3. `description` non-empty and ≥ 20 chars
19
- # 4. Body length ≤ 500 lines - WARN (split to SKILL.md + REFERENCE.md)
20
- # 5. Body length ≤ 1000 lines - HARD (catalog clearly doesn't belong in SKILL.md)
21
- #
22
- # Rule 4 is warn-only. Rule 5 fails the suite. Rules 1-3 fail the suite.
23
- #
24
- # Rationale: progressive disclosure is an emerging 2026 best practice. Large
25
- # skills poison the context window; splitting out REFERENCE.md keeps the
26
- # action-description short (what the model sees on skill dispatch) while
27
- # keeping the catalog discoverable on demand.
28
-
29
- set -euo pipefail
30
-
31
- HERE="$(cd "$(dirname "$0")" && pwd)"
32
- ROOT="$(cd "$HERE/../.." && pwd)"
33
-
34
- WARN_LINES=500
35
- FAIL_LINES=1000
36
- MIN_DESC_CHARS=20
37
-
38
- PASS=0
39
- WARN=0
40
- FAIL=0
41
-
42
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
43
- warn() { WARN=$((WARN+1)); echo " ⚠ $1"; }
44
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
45
-
46
- # Only our authored skill trees - upstream imports live under external/
47
- # and are exempt (own frontmatter conventions).
48
- TARGETS=(
49
- "$ROOT/pipeline/skills/shared/core"
50
- )
51
-
52
- # Collect the list once.
53
- SKILLS=$(find "${TARGETS[@]}" -name 'SKILL.md' -type f 2>/dev/null | sort)
54
- SKILL_COUNT=$(echo "$SKILLS" | grep -c . || true)
55
-
56
- echo "→ Scope: $SKILL_COUNT authored SKILL.md files under shared/core/"
57
- echo " (external/ intentionally excluded - upstream conventions)"
58
- echo ""
59
-
60
- echo "→ 1. Every SKILL.md has YAML frontmatter (--- ... ---)"
61
- MISSING_FM=0
62
- while IFS= read -r f; do
63
- [ -z "$f" ] && continue
64
- if ! head -1 "$f" | grep -q '^---$'; then
65
- fail "frontmatter missing: ${f#$ROOT/}"
66
- MISSING_FM=$((MISSING_FM+1))
67
- fi
68
- done <<< "$SKILLS"
69
- [ "$MISSING_FM" -eq 0 ] && pass "all authored skills have frontmatter"
70
-
71
- echo ""
72
- echo "→ 2. Frontmatter name matches directory name"
73
- MISMATCH=0
74
- while IFS= read -r f; do
75
- [ -z "$f" ] && continue
76
- dir_name=$(basename "$(dirname "$f")")
77
- fm_name=$(awk -F: '/^name:/ {sub(/^name:[[:space:]]*/,"",$0); sub(/^name:[[:space:]]*/,""); gsub(/"/,""); gsub(/^[[:space:]]+/,""); gsub(/[[:space:]]+$/,""); print; exit}' "$f" 2>/dev/null || true)
78
- if [ -z "$fm_name" ]; then
79
- fail "frontmatter missing name: ${f#$ROOT/}"
80
- MISMATCH=$((MISMATCH+1))
81
- elif [ "$dir_name" != "$fm_name" ]; then
82
- fail "dir='${dir_name}' vs name='${fm_name}': ${f#$ROOT/}"
83
- MISMATCH=$((MISMATCH+1))
84
- fi
85
- done <<< "$SKILLS"
86
- [ "$MISMATCH" -eq 0 ] && pass "all names match directory names"
87
-
88
- echo ""
89
- echo "→ 3. description field non-empty and ≥ $MIN_DESC_CHARS chars"
90
- SHORT_DESC=0
91
- while IFS= read -r f; do
92
- [ -z "$f" ] && continue
93
- desc=$(awk '
94
- BEGIN { in_fm=0 }
95
- /^---$/ { in_fm++; next }
96
- in_fm==1 && /^description:/ {
97
- sub(/^description:[[:space:]]*/,"");
98
- gsub(/^["'\''"]/,"");
99
- gsub(/["'\''"]$/,"");
100
- print;
101
- exit
102
- }
103
- ' "$f" 2>/dev/null || true)
104
- len=${#desc}
105
- if [ "$len" -lt "$MIN_DESC_CHARS" ]; then
106
- fail "description too short (${len} chars, need ≥${MIN_DESC_CHARS}): ${f#$ROOT/}"
107
- SHORT_DESC=$((SHORT_DESC+1))
108
- fi
109
- done <<< "$SKILLS"
110
- [ "$SHORT_DESC" -eq 0 ] && pass "all descriptions meet minimum length"
111
-
112
- echo ""
113
- echo "→ 4. Body length within budget (warn > $WARN_LINES, hard-fail > $FAIL_LINES)"
114
- WARN_COUNT=0
115
- HARD_COUNT=0
116
- while IFS= read -r f; do
117
- [ -z "$f" ] && continue
118
- total=$(wc -l < "$f")
119
- # Find the closing --- of frontmatter (line of 2nd ^---$).
120
- fm_end=$(awk '/^---$/ { n++; if (n==2) { print NR; exit } }' "$f")
121
- [ -z "$fm_end" ] && fm_end=0
122
- body=$((total - fm_end))
123
- if [ "$body" -gt "$FAIL_LINES" ]; then
124
- fail "body $body lines > $FAIL_LINES (HARD - split into REFERENCE.md): ${f#$ROOT/}"
125
- HARD_COUNT=$((HARD_COUNT+1))
126
- elif [ "$body" -gt "$WARN_LINES" ]; then
127
- warn "body $body lines > $WARN_LINES (consider SKILL.md + REFERENCE.md split): ${f#$ROOT/}"
128
- WARN_COUNT=$((WARN_COUNT+1))
129
- fi
130
- done <<< "$SKILLS"
131
- [ "$HARD_COUNT" -eq 0 ] && [ "$WARN_COUNT" -eq 0 ] && pass "all authored skills fit budget"
132
-
133
- echo ""
134
- echo "══ skill-authoring smoke: $PASS passed, $WARN warnings, $FAIL failed ══"
135
-
136
- # Warnings don't fail the suite; hard failures do.
137
- [ "$FAIL" -eq 0 ]
@@ -1,83 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-skill-language.sh - guard the "skill UI is always English" rule.
3
- #
4
- # Memory rule: skill picker / confirmation / error UI exposed by the CLI host
5
- # stays English regardless of the user's prefs.global.outputLanguage. The
6
- # host renders SKILL.md frontmatter (`description`, `argument-hint`) in its
7
- # selector - so those fields must be English.
8
- #
9
- # This smoke fails if any SKILL.md description/argument-hint field contains
10
- # Turkish-only characters (`ç`, `ğ`, `ı`, `ö`, `ş`, `ü`, plus uppercase
11
- # variants `Ç`, `Ğ`, `İ`, `Ö`, `Ş`, `Ü`) - a cheap proxy for "non-English
12
- # prose snuck into a host-visible field."
13
- #
14
- # What this DOES NOT enforce:
15
- # - The body of a SKILL.md may contain Turkish (the `language: tr` field
16
- # declares a runtime body language). Only frontmatter description and
17
- # argument-hint are checked.
18
- # - Loanwords and proper nouns in EN that happen to use diacritics are
19
- # rejected - adjust the allowlist below if a legitimate case appears.
20
- #
21
- # Exit codes: 0=pass, 1=violation, 2=structural error.
22
- set -uo pipefail
23
-
24
- REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
25
- SKILLS_ROOT="$REPO_ROOT/pipeline/skills/shared/core"
26
-
27
- if [ ! -d "$SKILLS_ROOT" ]; then
28
- echo "FAIL: $SKILLS_ROOT missing" >&2
29
- exit 2
30
- fi
31
-
32
- # Turkish-specific characters (lower + upper).
33
- TR_CHARS='çğıöşüÇĞİÖŞÜ'
34
-
35
- PASS=0
36
- FAIL=0
37
- pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
38
- fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
39
-
40
- extract_field() {
41
- local file="$1"
42
- local field="$2"
43
- awk -v f="$field" '
44
- /^---[[:space:]]*$/ { fm = !fm; next }
45
- fm && $0 ~ "^"f":" {
46
- sub("^"f":[[:space:]]*", "")
47
- gsub(/^"|"$/, "")
48
- print
49
- exit
50
- }
51
- ' "$file"
52
- }
53
-
54
- echo "→ Scanning SKILL.md files under $SKILLS_ROOT for Turkish chars in host-visible fields"
55
- echo ""
56
-
57
- while IFS= read -r -d '' skill_file; do
58
- rel="${skill_file#$REPO_ROOT/}"
59
-
60
- desc=$(extract_field "$skill_file" "description")
61
- hint=$(extract_field "$skill_file" "argument-hint")
62
-
63
- if [ -n "$desc" ]; then
64
- if [[ "$desc" =~ [$TR_CHARS] ]]; then
65
- fail "$rel: description contains Turkish chars → '$desc'"
66
- else
67
- pass "$rel: description is ASCII-clean"
68
- fi
69
- fi
70
-
71
- if [ -n "$hint" ]; then
72
- if [[ "$hint" =~ [$TR_CHARS] ]]; then
73
- fail "$rel: argument-hint contains Turkish chars → '$hint'"
74
- else
75
- pass "$rel: argument-hint is ASCII-clean"
76
- fi
77
- fi
78
- done < <(find "$SKILLS_ROOT" -name "SKILL.md" -print0)
79
-
80
- echo ""
81
- echo "══ smoke-skill-language: $PASS passed, $FAIL failed ══"
82
-
83
- [ "$FAIL" -eq 0 ] || exit 1
@@ -1,138 +0,0 @@
1
- #!/usr/bin/env bash
2
- # smoke-skill-manifest.sh - v6.2.H
3
- #
4
- # Asserts the sign-skills / verify-skills contract:
5
- # 1. sign-skills.sh produces a valid .skill-manifest.json with schemaVersion + entries
6
- # 2. verify-skills.sh exits 0 on clean baseline
7
- # 3. verify-skills.sh exits 2 when a SKILL.md is mutated
8
- # 4. verify-skills.sh exits 2 when a SKILL.md is deleted
9
- # 5. verify-skills.sh exits 3 when a new SKILL.md is added (not in manifest)
10
- # 6. verify-skills.sh fail-opens (exit 0) when manifest is absent
11
- #
12
- # Works on a temp sandbox - never touches the repo's real manifest.
13
-
14
- set -euo pipefail
15
-
16
- ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
17
- SIGN="$ROOT/pipeline/scripts/sign-skills.sh"
18
- VERIFY="$ROOT/pipeline/scripts/verify-skills.sh"
19
-
20
- pass=0
21
- fail=0
22
- failures=()
23
- record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
24
- record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
25
-
26
- printf '→ smoke-skill-manifest (v6.2.H): sign + verify integrity contract\n'
27
-
28
- sandbox=$(mktemp -d)
29
- skills="$sandbox/skills"
30
- mkdir -p "$skills/group-a" "$skills/group-b"
31
-
32
- cat > "$skills/group-a/SKILL.md" <<'S'
33
- ---
34
- name: group-a
35
- description: test skill A
36
- ---
37
- body A
38
- S
39
-
40
- cat > "$skills/group-b/SKILL.md" <<'S'
41
- ---
42
- name: group-b
43
- description: test skill B
44
- ---
45
- body B
46
- S
47
-
48
- # --- 1: sign produces manifest ---
49
- bash "$SIGN" --root "$skills" >/dev/null
50
- if [ -f "$skills/.skill-manifest.json" ] && \
51
- jq -e '.schemaVersion and .entries and (.skillCount == 2)' "$skills/.skill-manifest.json" >/dev/null; then
52
- record_pass "sign wrote valid manifest with 2 entries"
53
- else
54
- record_fail "sign output malformed"
55
- fi
56
-
57
- # --- 2: verify clean ---
58
- if bash "$VERIFY" --root "$skills" --quiet; then
59
- record_pass "verify exits 0 on clean baseline"
60
- else
61
- record_fail "verify should exit 0 on clean baseline"
62
- fi
63
-
64
- # --- 3: mutation → drift (exit 2) ---
65
- echo 'tampered' >> "$skills/group-a/SKILL.md"
66
- set +e
67
- bash "$VERIFY" --root "$skills" --quiet
68
- rc=$?
69
- set -e
70
- if [ "$rc" = "2" ]; then
71
- record_pass "verify detects mutation (exit 2)"
72
- else
73
- record_fail "verify should exit 2 on mutation (got $rc)"
74
- fi
75
-
76
- # restore
77
- bash "$SIGN" --root "$skills" >/dev/null
78
-
79
- # --- 4: deletion → missing (exit 2) ---
80
- rm "$skills/group-a/SKILL.md"
81
- set +e
82
- bash "$VERIFY" --root "$skills" --quiet
83
- rc=$?
84
- set -e
85
- if [ "$rc" = "2" ]; then
86
- record_pass "verify detects deletion (exit 2)"
87
- else
88
- record_fail "verify should exit 2 on deletion (got $rc)"
89
- fi
90
-
91
- # restore
92
- mkdir -p "$skills/group-a"
93
- cat > "$skills/group-a/SKILL.md" <<'S'
94
- ---
95
- name: group-a
96
- description: test skill A
97
- ---
98
- body A
99
- S
100
- bash "$SIGN" --root "$skills" >/dev/null
101
-
102
- # --- 5: addition → extra (exit 3) ---
103
- mkdir -p "$skills/group-c"
104
- cat > "$skills/group-c/SKILL.md" <<'S'
105
- ---
106
- name: group-c
107
- description: new skill not in manifest
108
- ---
109
- body C
110
- S
111
-
112
- set +e
113
- bash "$VERIFY" --root "$skills" --quiet
114
- rc=$?
115
- set -e
116
- if [ "$rc" = "3" ]; then
117
- record_pass "verify detects extra file (exit 3)"
118
- else
119
- record_fail "verify should exit 3 on extra file (got $rc)"
120
- fi
121
-
122
- # --- 6: missing manifest → fail-open exit 0 ---
123
- rm "$skills/.skill-manifest.json"
124
- if bash "$VERIFY" --root "$skills" --quiet; then
125
- record_pass "verify fail-opens when manifest absent (first install)"
126
- else
127
- record_fail "verify should fail-open with no manifest"
128
- fi
129
-
130
- rm -rf "$sandbox"
131
-
132
- printf '\n══ skill-manifest smoke: %d passed, %d failed ══\n' "$pass" "$fail"
133
- if [ "$fail" -gt 0 ]; then
134
- printf '\nFailures:\n'
135
- for m in "${failures[@]}"; do printf ' - %s\n' "$m"; done
136
- exit 1
137
- fi
138
- exit 0