@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.
- package/CHANGELOG.md +207 -0
- package/install/_common.mjs +48 -0
- package/install/_dev-only-files.mjs +125 -5
- package/install/claude.mjs +14 -8
- package/install/copilot.mjs +5 -8
- package/package.json +17 -2
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/lib/credential-store.sh +20 -0
- package/pipeline/multi-agent-refs/_account-picker.md +1 -1
- package/pipeline/multi-agent-refs/_dev-context.md +1 -1
- package/pipeline/multi-agent-refs/_repo-picker.md +1 -1
- package/pipeline/multi-agent-refs/features/review-multi-repo.md +65 -0
- package/pipeline/multi-agent-refs/features/url-enrichment.md +93 -0
- package/pipeline/multi-agent-refs/phases/operations.md +28 -0
- package/pipeline/multi-agent-refs/phases/phase-0-init.md +3 -83
- package/pipeline/multi-agent-refs/phases/phase-2-planning.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +1 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +50 -62
- package/pipeline/schemas/prefs.schema.json +6 -0
- package/pipeline/scripts/_smoke-root.sh +61 -0
- package/pipeline/scripts/agent-guard.py +57 -3
- package/pipeline/scripts/audit-log.sh +25 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +21 -7
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-2-plan.json +0 -30
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/01-ios-bugfix-darkmode/task.json +0 -12
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-2-plan.json +0 -43
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-review.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/expected/phase-4-triage.json +0 -35
- package/pipeline/eval/golden-tasks/02-android-feature-compose/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/02-android-feature-compose/task.json +0 -12
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/03-backend-python-ratelimit/task.json +0 -12
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-2-plan.json +0 -40
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-review.json +0 -20
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/expected/phase-4-triage.json +0 -15
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/04-frontend-next-hydration/task.json +0 -12
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/05-ios-security-keychain/task.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-2-plan.json +0 -41
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/expected/phase-4-triage.json +0 -6
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/06-android-refactor-usecase/task.json +0 -12
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-1-analysis.json +0 -29
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-2-plan.json +0 -42
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-review.json +0 -28
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/expected/phase-4-triage.json +0 -27
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/07-backend-node-idempotency/task.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-1-analysis.json +0 -25
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-2-plan.json +0 -31
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-review.json +0 -12
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/expected/phase-4-triage.json +0 -18
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/metadata.json +0 -14
- package/pipeline/eval/golden-tasks/08-ios-auth-consensus-unverified/task.json +0 -12
- package/pipeline/eval/golden-tasks/README.md +0 -65
- package/pipeline/eval/intent-cases.json +0 -40
- package/pipeline/eval/run-metrics-fixture.json +0 -93
- package/pipeline/eval/triage/01-empty-findings/expected.json +0 -6
- package/pipeline/eval/triage/01-empty-findings/input.json +0 -5
- package/pipeline/eval/triage/01-empty-findings/notes.md +0 -7
- package/pipeline/eval/triage/02-real-blocker/expected.json +0 -15
- package/pipeline/eval/triage/02-real-blocker/input.json +0 -14
- package/pipeline/eval/triage/02-real-blocker/notes.md +0 -7
- package/pipeline/eval/triage/03-out-of-scope-defer/expected.json +0 -18
- package/pipeline/eval/triage/03-out-of-scope-defer/input.json +0 -14
- package/pipeline/eval/triage/03-out-of-scope-defer/notes.md +0 -10
- package/pipeline/eval/triage/04-false-positive-reject/expected.json +0 -18
- package/pipeline/eval/triage/04-false-positive-reject/input.json +0 -14
- package/pipeline/eval/triage/04-false-positive-reject/notes.md +0 -10
- package/pipeline/eval/triage/05-mixed-classification/expected.json +0 -43
- package/pipeline/eval/triage/05-mixed-classification/input.json +0 -38
- package/pipeline/eval/triage/05-mixed-classification/notes.md +0 -17
- package/pipeline/eval/triage/06-severity-mismatch/expected.json +0 -15
- package/pipeline/eval/triage/06-severity-mismatch/input.json +0 -14
- package/pipeline/eval/triage/06-severity-mismatch/notes.md +0 -9
- package/pipeline/eval/triage/07-duplicate-reviewers/expected.json +0 -27
- package/pipeline/eval/triage/07-duplicate-reviewers/input.json +0 -22
- package/pipeline/eval/triage/07-duplicate-reviewers/notes.md +0 -9
- package/pipeline/eval/triage/08-style-misclassified/expected.json +0 -18
- package/pipeline/eval/triage/08-style-misclassified/input.json +0 -14
- package/pipeline/eval/triage/08-style-misclassified/notes.md +0 -9
- package/pipeline/eval/triage/09-cascading-finding/expected.json +0 -23
- package/pipeline/eval/triage/09-cascading-finding/input.json +0 -22
- package/pipeline/eval/triage/09-cascading-finding/notes.md +0 -9
- package/pipeline/eval/triage/10-deferred-crossref/expected.json +0 -18
- package/pipeline/eval/triage/10-deferred-crossref/input.json +0 -14
- package/pipeline/eval/triage/10-deferred-crossref/notes.md +0 -9
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/expected.json +0 -27
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/input.json +0 -22
- package/pipeline/eval/triage/11-vercel-token-leak-blocker/notes.md +0 -14
- package/pipeline/eval/triage/README.md +0 -54
- package/pipeline/scripts/benchmark-phase-0.sh +0 -128
- package/pipeline/scripts/check-md-links.mjs +0 -88
- package/pipeline/scripts/eval-golden-tasks-live.mjs +0 -302
- package/pipeline/scripts/eval-golden-tasks.mjs +0 -224
- package/pipeline/scripts/eval-intent.mjs +0 -107
- package/pipeline/scripts/eval-mine-corpus.mjs +0 -211
- package/pipeline/scripts/eval-triage.mjs +0 -171
- package/pipeline/scripts/fixtures/diff-risk-android.diff +0 -40
- package/pipeline/scripts/fixtures/diff-risk-ios.diff +0 -48
- package/pipeline/scripts/fixtures/diff-risk-test-removal.diff +0 -40
- package/pipeline/scripts/fixtures/install-layout.tsv +0 -19
- package/pipeline/scripts/fixtures/pack-expected-count.txt +0 -1
- package/pipeline/scripts/fixtures/test-gap-node.diff +0 -30
- package/pipeline/scripts/fixtures/test-gap-python.diff +0 -32
- package/pipeline/scripts/lint-mcp-refs.mjs +0 -218
- package/pipeline/scripts/lint-skills.mjs +0 -154
- package/pipeline/scripts/run-smokes.mjs +0 -130
- package/pipeline/scripts/scorecard.mjs +0 -258
- package/pipeline/scripts/smoke-add-detail.sh +0 -137
- package/pipeline/scripts/smoke-agent-guard.sh +0 -74
- package/pipeline/scripts/smoke-agent-log-cost.sh +0 -262
- package/pipeline/scripts/smoke-agent-model-routing.sh +0 -87
- package/pipeline/scripts/smoke-ask-choice.sh +0 -42
- package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +0 -36
- package/pipeline/scripts/smoke-bitbucket-contract.sh +0 -255
- package/pipeline/scripts/smoke-changelog-version.sh +0 -47
- package/pipeline/scripts/smoke-channels-approval-gate.sh +0 -60
- package/pipeline/scripts/smoke-channels-flow.sh +0 -130
- package/pipeline/scripts/smoke-ci-workflows.sh +0 -88
- package/pipeline/scripts/smoke-clarify.sh +0 -148
- package/pipeline/scripts/smoke-command-inventory.sh +0 -81
- package/pipeline/scripts/smoke-commands-skills-parity.sh +0 -87
- package/pipeline/scripts/smoke-community-gates.sh +0 -75
- package/pipeline/scripts/smoke-compliance-skills.sh +0 -119
- package/pipeline/scripts/smoke-config-hygiene.sh +0 -58
- package/pipeline/scripts/smoke-context-budget.sh +0 -72
- package/pipeline/scripts/smoke-cost-budget.sh +0 -70
- package/pipeline/scripts/smoke-cost-summary.sh +0 -139
- package/pipeline/scripts/smoke-cross-phase-cohesion.sh +0 -128
- package/pipeline/scripts/smoke-description-tr.sh +0 -82
- package/pipeline/scripts/smoke-dev-critic.sh +0 -144
- package/pipeline/scripts/smoke-diff-explain.sh +0 -147
- package/pipeline/scripts/smoke-diff-risk.sh +0 -190
- package/pipeline/scripts/smoke-dynamic-skill-loading.sh +0 -160
- package/pipeline/scripts/smoke-eval-live.sh +0 -136
- package/pipeline/scripts/smoke-evidence-gate.sh +0 -93
- package/pipeline/scripts/smoke-extract-conventions.sh +0 -163
- package/pipeline/scripts/smoke-fetchers-offline.sh +0 -448
- package/pipeline/scripts/smoke-figma-dispatch.sh +0 -112
- package/pipeline/scripts/smoke-gate-hooks.sh +0 -74
- package/pipeline/scripts/smoke-gc-tmp.sh +0 -130
- package/pipeline/scripts/smoke-gc-worktrees.sh +0 -125
- package/pipeline/scripts/smoke-generate-issue.sh +0 -120
- package/pipeline/scripts/smoke-handoff-contract.sh +0 -92
- package/pipeline/scripts/smoke-identity-isolation.sh +0 -70
- package/pipeline/scripts/smoke-install-layout.sh +0 -248
- package/pipeline/scripts/smoke-intent-guard.sh +0 -86
- package/pipeline/scripts/smoke-issue-comment-template.sh +0 -86
- package/pipeline/scripts/smoke-issue-jira-triad.sh +0 -120
- package/pipeline/scripts/smoke-keychain.sh +0 -158
- package/pipeline/scripts/smoke-language-axis.sh +0 -109
- package/pipeline/scripts/smoke-learning-curve.sh +0 -61
- package/pipeline/scripts/smoke-learnings-ledger.sh +0 -86
- package/pipeline/scripts/smoke-lib-scripts.sh +0 -448
- package/pipeline/scripts/smoke-mcp-gate.sh +0 -68
- package/pipeline/scripts/smoke-md-links.sh +0 -8
- package/pipeline/scripts/smoke-md2confluence.sh +0 -126
- package/pipeline/scripts/smoke-metrics-cache-ratio.sh +0 -72
- package/pipeline/scripts/smoke-migrate-state.sh +0 -102
- package/pipeline/scripts/smoke-mode-dispatch-drift.sh +0 -161
- package/pipeline/scripts/smoke-model-fallback.sh +0 -89
- package/pipeline/scripts/smoke-multi-repo-integration.sh +0 -116
- package/pipeline/scripts/smoke-multi-repo-worktree.sh +0 -61
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +0 -194
- package/pipeline/scripts/smoke-no-token-prompt.sh +0 -85
- package/pipeline/scripts/smoke-own-punctuation.sh +0 -103
- package/pipeline/scripts/smoke-pack-contents.sh +0 -140
- package/pipeline/scripts/smoke-pat-audit.sh +0 -128
- package/pipeline/scripts/smoke-per-repo-memory.sh +0 -156
- package/pipeline/scripts/smoke-phase-0-multi-repo.sh +0 -170
- package/pipeline/scripts/smoke-phase-6-multi.sh +0 -79
- package/pipeline/scripts/smoke-phase-banner.sh +0 -101
- package/pipeline/scripts/smoke-phase-tracker.sh +0 -324
- package/pipeline/scripts/smoke-phase0-bridge-contract.sh +0 -241
- package/pipeline/scripts/smoke-phase4-gates.sh +0 -45
- package/pipeline/scripts/smoke-phase4-triage.sh +0 -229
- package/pipeline/scripts/smoke-plan-approval-gate.sh +0 -71
- package/pipeline/scripts/smoke-plan-safety.sh +0 -139
- package/pipeline/scripts/smoke-plan-todos.sh +0 -196
- package/pipeline/scripts/smoke-pr-review-actions.sh +0 -152
- package/pipeline/scripts/smoke-pre-commit.sh +0 -170
- package/pipeline/scripts/smoke-pref-migration.sh +0 -226
- package/pipeline/scripts/smoke-prefs-language.sh +0 -134
- package/pipeline/scripts/smoke-progress-contract.sh +0 -127
- package/pipeline/scripts/smoke-prune-logs.sh +0 -137
- package/pipeline/scripts/smoke-purge.sh +0 -138
- package/pipeline/scripts/smoke-push-retry.sh +0 -75
- package/pipeline/scripts/smoke-repo-map.sh +0 -300
- package/pipeline/scripts/smoke-review-readiness.sh +0 -92
- package/pipeline/scripts/smoke-review-watch.sh +0 -146
- package/pipeline/scripts/smoke-routines.sh +0 -84
- package/pipeline/scripts/smoke-run-aggregator.sh +0 -216
- package/pipeline/scripts/smoke-run-metrics.sh +0 -50
- package/pipeline/scripts/smoke-search.sh +0 -187
- package/pipeline/scripts/smoke-shadow-git.sh +0 -224
- package/pipeline/scripts/smoke-skill-authoring.sh +0 -137
- package/pipeline/scripts/smoke-skill-language.sh +0 -83
- package/pipeline/scripts/smoke-skill-manifest.sh +0 -138
- package/pipeline/scripts/smoke-skill-scan.sh +0 -198
- package/pipeline/scripts/smoke-source-parity.sh +0 -85
- package/pipeline/scripts/smoke-subagent-validators.sh +0 -108
- package/pipeline/scripts/smoke-sync-parity.sh +0 -92
- package/pipeline/scripts/smoke-tasklist-ordering.sh +0 -112
- package/pipeline/scripts/smoke-telemetry.sh +0 -147
- package/pipeline/scripts/smoke-test-gap.sh +0 -183
- package/pipeline/scripts/smoke-token-budget.sh +0 -67
- package/pipeline/scripts/smoke-token-preflight.sh +0 -82
- package/pipeline/scripts/smoke-tracker-contract.sh +0 -191
- package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +0 -73
- package/pipeline/scripts/smoke-triage-memory.sh +0 -174
- package/pipeline/scripts/smoke-update-check.sh +0 -135
- package/pipeline/scripts/smoke-url-enrichment.sh +0 -70
- package/pipeline/scripts/smoke-validate-analysis-doc.sh +0 -161
- package/pipeline/scripts/smoke-validator-contradiction.sh +0 -67
- package/pipeline/scripts/smoke-validator-gates.sh +0 -164
- package/pipeline/scripts/smoke-vercel-deploy-redact.sh +0 -129
- package/pipeline/scripts/smoke-verify-by-test.sh +0 -148
- package/pipeline/scripts/smoke-wiki-integration.sh +0 -122
- package/pipeline/scripts/smoke-work-summary.sh +0 -163
- package/pipeline/scripts/smoke-workflow-audit.sh +0 -101
- package/pipeline/scripts/smoke-worktree-path-convention.sh +0 -86
- package/pipeline/scripts/smoke-wrapper-preservation.sh +0 -68
- package/pipeline/scripts/smoke-write-state.sh +0 -159
- package/pipeline/scripts/sync-parity-check.sh +0 -135
- package/pipeline/scripts/test-gap-rules/android.json +0 -25
- package/pipeline/scripts/test-gap-rules/ios.json +0 -34
- package/pipeline/scripts/test-gap-rules/node.json +0 -29
- package/pipeline/scripts/test-gap-rules/python.json +0 -25
- package/pipeline/scripts/validate-schemas.mjs +0 -88
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# smoke-pr-review-actions.sh
|
|
4
|
-
#
|
|
5
|
-
# Asserts the review-actions contract is wired correctly: the new channel
|
|
6
|
-
# doc, the orchestrator, the decision rule, both languages, the github +
|
|
7
|
-
# bitbucket-server provider switch, and the absence of the legacy monolith
|
|
8
|
-
# comment artifacts.
|
|
9
|
-
#
|
|
10
|
-
# This is a lint, not a behavioural test. Behavioural drift is caught at
|
|
11
|
-
# runtime by the chat summary + the actual PR state on the provider.
|
|
12
|
-
|
|
13
|
-
set -euo pipefail
|
|
14
|
-
|
|
15
|
-
SELF_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
16
|
-
PIPELINE_ROOT=$(cd "$SELF_DIR/.." && pwd)
|
|
17
|
-
|
|
18
|
-
cd "$PIPELINE_ROOT"
|
|
19
|
-
|
|
20
|
-
FAIL=0
|
|
21
|
-
|
|
22
|
-
CHANNEL="multi-agent-refs/channels/pr-review-actions.md"
|
|
23
|
-
REVIEW="commands/multi-agent/review/SKILL.md"
|
|
24
|
-
POSTER="lib/post-pr-review.sh"
|
|
25
|
-
|
|
26
|
-
# 1. New canonical template + new poster exist.
|
|
27
|
-
if [ ! -f "$CHANNEL" ]; then
|
|
28
|
-
echo "FAIL [smoke-pr-review-actions]: $CHANNEL missing"
|
|
29
|
-
exit 1
|
|
30
|
-
fi
|
|
31
|
-
if [ ! -x "$POSTER" ]; then
|
|
32
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER missing or not executable"
|
|
33
|
-
FAIL=$((FAIL + 1))
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# 2. Legacy monolith artifacts must not exist or be referenced.
|
|
37
|
-
LEGACY_TEMPLATE="multi-agent-refs/channels/pr-review-comment.md"
|
|
38
|
-
LEGACY_RENDERER="lib/render-pr-review-body.sh"
|
|
39
|
-
if [ -e "$LEGACY_TEMPLATE" ]; then
|
|
40
|
-
echo "FAIL [smoke-pr-review-actions]: $LEGACY_TEMPLATE must not exist (monolith comment template forbidden)"
|
|
41
|
-
FAIL=$((FAIL + 1))
|
|
42
|
-
fi
|
|
43
|
-
if [ -e "$LEGACY_RENDERER" ]; then
|
|
44
|
-
echo "FAIL [smoke-pr-review-actions]: $LEGACY_RENDERER must not exist (monolith comment renderer forbidden)"
|
|
45
|
-
FAIL=$((FAIL + 1))
|
|
46
|
-
fi
|
|
47
|
-
# Reference scan - no spec files should still WIRE the legacy artifacts.
|
|
48
|
-
# Match only call-site patterns (bash <path>, --body-file <path>), NOT prose
|
|
49
|
-
# references. Migration notes like "render-pr-review-body.sh is GONE" are
|
|
50
|
-
# allowed; an actual `bash render-pr-review-body.sh` call is not.
|
|
51
|
-
LEAK=$(grep -rEl '(bash|sh|source) [^ ]*render-pr-review-body\.sh|--body-file [^ ]*pr-review-comment\.md' \
|
|
52
|
-
commands/ lib/ scripts/ 2>/dev/null \
|
|
53
|
-
| grep -v "$(basename "$0")" \
|
|
54
|
-
| grep -v "CHANGELOG\." \
|
|
55
|
-
| head -1 || true)
|
|
56
|
-
if [ -n "$LEAK" ]; then
|
|
57
|
-
echo "FAIL [smoke-pr-review-actions]: stale call-site to legacy artifact in: $LEAK"
|
|
58
|
-
FAIL=$((FAIL + 1))
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
# 3. Decision rule is documented verbatim in the channel doc.
|
|
62
|
-
declare -a DECISION_ANCHORS=(
|
|
63
|
-
"0 accepted blocking AND 0 accepted important"
|
|
64
|
-
"≥1 accepted blocking OR ≥1 accepted important"
|
|
65
|
-
"Suggestions never block"
|
|
66
|
-
"APPROVE"
|
|
67
|
-
"NEEDS_WORK"
|
|
68
|
-
)
|
|
69
|
-
for a in "${DECISION_ANCHORS[@]}"; do
|
|
70
|
-
if ! grep -qF "$a" "$CHANNEL" "$REVIEW"; then
|
|
71
|
-
echo "FAIL [smoke-pr-review-actions]: decision rule anchor missing across $CHANNEL + $REVIEW: $a"
|
|
72
|
-
FAIL=$((FAIL + 1))
|
|
73
|
-
fi
|
|
74
|
-
done
|
|
75
|
-
|
|
76
|
-
# 4. Both languages render - the template documents tr + en shapes.
|
|
77
|
-
if ! grep -qE 'outputLanguage *= *"tr"|Turkish \(outputLanguage' "$CHANNEL"; then
|
|
78
|
-
echo "FAIL [smoke-pr-review-actions]: $CHANNEL must document the tr inline-comment shape"
|
|
79
|
-
FAIL=$((FAIL + 1))
|
|
80
|
-
fi
|
|
81
|
-
if ! grep -qE 'outputLanguage *= *"en"|English \(outputLanguage' "$CHANNEL"; then
|
|
82
|
-
echo "FAIL [smoke-pr-review-actions]: $CHANNEL must document the en inline-comment shape"
|
|
83
|
-
FAIL=$((FAIL + 1))
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# 5. Provider switch covers github + bitbucket-server explicitly.
|
|
87
|
-
declare -a PROVIDER_HINTS=(
|
|
88
|
-
"github"
|
|
89
|
-
"bitbucket-server"
|
|
90
|
-
)
|
|
91
|
-
for p in "${PROVIDER_HINTS[@]}"; do
|
|
92
|
-
if ! grep -qF "$p" "$REVIEW" "$CHANNEL" "$POSTER"; then
|
|
93
|
-
echo "FAIL [smoke-pr-review-actions]: provider '$p' missing across review.md + channel + poster"
|
|
94
|
-
FAIL=$((FAIL + 1))
|
|
95
|
-
fi
|
|
96
|
-
done
|
|
97
|
-
|
|
98
|
-
# 6. review.md wires post-pr-review.sh and references the new channel doc.
|
|
99
|
-
if ! grep -qF "post-pr-review.sh" "$REVIEW"; then
|
|
100
|
-
echo "FAIL [smoke-pr-review-actions]: $REVIEW must wire post-pr-review.sh"
|
|
101
|
-
FAIL=$((FAIL + 1))
|
|
102
|
-
fi
|
|
103
|
-
if ! grep -qF "refs/channels/pr-review-actions.md" "$REVIEW"; then
|
|
104
|
-
echo "FAIL [smoke-pr-review-actions]: $REVIEW must reference refs/channels/pr-review-actions.md"
|
|
105
|
-
FAIL=$((FAIL + 1))
|
|
106
|
-
fi
|
|
107
|
-
|
|
108
|
-
# 7. post-pr-review.sh enforces the decision rule and the language axis.
|
|
109
|
-
if ! grep -qF "outputLanguage" "$POSTER"; then
|
|
110
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER must read prefs.global.outputLanguage"
|
|
111
|
-
FAIL=$((FAIL + 1))
|
|
112
|
-
fi
|
|
113
|
-
if ! grep -qE 'DECISION=approve|DECISION=needs_work' "$POSTER"; then
|
|
114
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER must compute DECISION=approve|needs_work"
|
|
115
|
-
FAIL=$((FAIL + 1))
|
|
116
|
-
fi
|
|
117
|
-
if ! grep -qE '"status":"APPROVED"' "$POSTER"; then
|
|
118
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER must include the Bitbucket APPROVED participant payload"
|
|
119
|
-
FAIL=$((FAIL + 1))
|
|
120
|
-
fi
|
|
121
|
-
if ! grep -qE '"status":"NEEDS_WORK"' "$POSTER"; then
|
|
122
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER must include the Bitbucket NEEDS_WORK participant payload"
|
|
123
|
-
FAIL=$((FAIL + 1))
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
# 8. No suggestion severity ever posts inline (hard prohibition in template).
|
|
127
|
-
if ! grep -qE 'No suggestion-severity inline comments|Suggestions are chat-only|never get inline comments by default' "$CHANNEL"; then
|
|
128
|
-
echo "FAIL [smoke-pr-review-actions]: $CHANNEL must declare suggestions are chat-only (never inline)"
|
|
129
|
-
FAIL=$((FAIL + 1))
|
|
130
|
-
fi
|
|
131
|
-
if grep -qE 'severity.*suggestion.*inline' "$POSTER"; then
|
|
132
|
-
echo "FAIL [smoke-pr-review-actions]: $POSTER must NOT post suggestion-severity findings inline"
|
|
133
|
-
FAIL=$((FAIL + 1))
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
# 9. Bitbucket URL parser is wired into review.md Step 1.
|
|
137
|
-
if ! grep -qE 'bitbucket\.\*/projects/\*/repos/\*/pull-requests/\*' "$REVIEW"; then
|
|
138
|
-
echo "FAIL [smoke-pr-review-actions]: $REVIEW must parse bitbucket.* PR URLs in Step 1"
|
|
139
|
-
FAIL=$((FAIL + 1))
|
|
140
|
-
fi
|
|
141
|
-
if ! grep -qF "bbProjectKey" "$REVIEW"; then
|
|
142
|
-
echo "FAIL [smoke-pr-review-actions]: $REVIEW must persist bbProjectKey in agent-state.review.input"
|
|
143
|
-
FAIL=$((FAIL + 1))
|
|
144
|
-
fi
|
|
145
|
-
|
|
146
|
-
if [ "$FAIL" -gt 0 ]; then
|
|
147
|
-
echo ""
|
|
148
|
-
echo "smoke-pr-review-actions: $FAIL violation(s)."
|
|
149
|
-
exit 1
|
|
150
|
-
fi
|
|
151
|
-
|
|
152
|
-
echo "smoke-pr-review-actions: ok"
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-pre-commit.sh - verify pre-commit-check.sh catches all secret patterns.
|
|
3
|
-
# Uses a real temp git repo to test the actual hook behavior end-to-end.
|
|
4
|
-
# No mocks, no test-only flags - tests exactly what runs in production.
|
|
5
|
-
|
|
6
|
-
set -uo pipefail
|
|
7
|
-
|
|
8
|
-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
-
HOOK="$SCRIPT_DIR/pre-commit-check.sh"
|
|
10
|
-
|
|
11
|
-
if [ ! -f "$HOOK" ]; then
|
|
12
|
-
echo "FAIL: pre-commit-check.sh not found at $HOOK" >&2
|
|
13
|
-
exit 1
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
PASS=0
|
|
17
|
-
FAIL=0
|
|
18
|
-
|
|
19
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
20
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
21
|
-
|
|
22
|
-
TMPDIR=$(mktemp -d) || { echo "FAIL: mktemp" >&2; exit 1; }
|
|
23
|
-
trap 'rm -rf "$TMPDIR"' EXIT
|
|
24
|
-
|
|
25
|
-
# Bootstrap a real git repo with an initial commit
|
|
26
|
-
git -C "$TMPDIR" init -q
|
|
27
|
-
git -C "$TMPDIR" config user.email "test@test.com"
|
|
28
|
-
git -C "$TMPDIR" config user.name "Test"
|
|
29
|
-
echo "init" > "$TMPDIR/init.txt"
|
|
30
|
-
git -C "$TMPDIR" add init.txt
|
|
31
|
-
git -C "$TMPDIR" commit -q -m "init"
|
|
32
|
-
|
|
33
|
-
# Stage a file with content and run the hook inside the temp repo
|
|
34
|
-
stage_and_check() {
|
|
35
|
-
local filename="$1" content="$2"
|
|
36
|
-
echo "$content" > "$TMPDIR/$filename"
|
|
37
|
-
git -C "$TMPDIR" add "$filename"
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
reset_stage() {
|
|
41
|
-
git -C "$TMPDIR" reset -q HEAD -- . 2>/dev/null
|
|
42
|
-
git -C "$TMPDIR" checkout -q -- . 2>/dev/null
|
|
43
|
-
rm -f "$TMPDIR"/test-* "$TMPDIR"/.env* 2>/dev/null
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
run_hook() {
|
|
47
|
-
(cd "$TMPDIR" && bash "$HOOK") >/dev/null 2>&1
|
|
48
|
-
return $?
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
check_detects() {
|
|
52
|
-
local desc="$1" filename="$2" content="$3"
|
|
53
|
-
reset_stage
|
|
54
|
-
stage_and_check "$filename" "$content"
|
|
55
|
-
if run_hook; then
|
|
56
|
-
fail "$desc (hook passed, expected block)"
|
|
57
|
-
else
|
|
58
|
-
pass "$desc"
|
|
59
|
-
fi
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
check_passes() {
|
|
63
|
-
local desc="$1" filename="$2" content="$3"
|
|
64
|
-
reset_stage
|
|
65
|
-
stage_and_check "$filename" "$content"
|
|
66
|
-
if run_hook; then
|
|
67
|
-
pass "$desc"
|
|
68
|
-
else
|
|
69
|
-
fail "$desc (hook blocked, expected pass)"
|
|
70
|
-
fi
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
echo "→ 1. Secret pattern detection"
|
|
74
|
-
|
|
75
|
-
check_detects "API key (api_key=long_string)" \
|
|
76
|
-
"test-file.swift" \
|
|
77
|
-
'let api_key = "ghp_aBcDeFgHiJkLmNoPqRsT1234567890"'
|
|
78
|
-
|
|
79
|
-
check_detects "AWS access key (AKIA prefix)" \
|
|
80
|
-
"test-file.py" \
|
|
81
|
-
'aws_key = "AKIAIOSFODNN7EXAMPLE"'
|
|
82
|
-
|
|
83
|
-
check_detects "RSA private key header" \
|
|
84
|
-
"test-file.pem" \
|
|
85
|
-
'-----BEGIN RSA PRIVATE KEY-----'
|
|
86
|
-
|
|
87
|
-
check_detects "Generic private key header" \
|
|
88
|
-
"test-file.key" \
|
|
89
|
-
'-----BEGIN PRIVATE KEY-----'
|
|
90
|
-
|
|
91
|
-
check_detects "EC private key header" \
|
|
92
|
-
"test-file.key" \
|
|
93
|
-
'-----BEGIN EC PRIVATE KEY-----'
|
|
94
|
-
|
|
95
|
-
check_detects "GCP service account JSON" \
|
|
96
|
-
"test-file.json" \
|
|
97
|
-
'{"type": "service_account", "project_id": "my-project"}'
|
|
98
|
-
|
|
99
|
-
check_detects "GitHub PAT (ghp_ 36 chars)" \
|
|
100
|
-
"test-file.txt" \
|
|
101
|
-
'token: ghp_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789'
|
|
102
|
-
|
|
103
|
-
check_detects "Slack token (xoxb-)" \
|
|
104
|
-
"test-file.txt" \
|
|
105
|
-
'slack = "xoxb-123456789012-abcdefghijklmnop"'
|
|
106
|
-
|
|
107
|
-
check_detects "Google API key (AIza)" \
|
|
108
|
-
"test-file.txt" \
|
|
109
|
-
'gmaps = "AIzaSyA1234567890abcdefghijklmnopqrstuv"'
|
|
110
|
-
|
|
111
|
-
check_detects "JWT three-segment token" \
|
|
112
|
-
"test-file.txt" \
|
|
113
|
-
'auth = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N"'
|
|
114
|
-
|
|
115
|
-
check_detects "High-entropy custom secret (no known prefix)" \
|
|
116
|
-
"test-file.txt" \
|
|
117
|
-
'session = "Zx9Kq2Lm7Pw4Nv8Rt3Yb6Hc1Df5Gj0Sa2Wd7Qe4Uo9Ik"'
|
|
118
|
-
|
|
119
|
-
check_detects "Secret in a filename WITH SPACES (no false-negative)" \
|
|
120
|
-
"test secret file.txt" \
|
|
121
|
-
'token: ghp_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789'
|
|
122
|
-
|
|
123
|
-
echo ""
|
|
124
|
-
echo "→ 2. File-name based detection"
|
|
125
|
-
|
|
126
|
-
check_detects ".env file blocked by name" \
|
|
127
|
-
".env" \
|
|
128
|
-
"DATABASE_URL=postgres://localhost/db"
|
|
129
|
-
|
|
130
|
-
check_detects ".env.local blocked by name" \
|
|
131
|
-
".env.local" \
|
|
132
|
-
"SECRET=foo"
|
|
133
|
-
|
|
134
|
-
check_detects "Credentials JSON blocked by name" \
|
|
135
|
-
"credentials.json" \
|
|
136
|
-
'{"user": "admin"}'
|
|
137
|
-
|
|
138
|
-
echo ""
|
|
139
|
-
echo "→ 3. Clean files pass"
|
|
140
|
-
|
|
141
|
-
check_passes "Normal Swift code" \
|
|
142
|
-
"test-file.swift" \
|
|
143
|
-
'import Foundation
|
|
144
|
-
let greeting = "Hello, World!"
|
|
145
|
-
func add(_ a: Int, _ b: Int) -> Int { a + b }'
|
|
146
|
-
|
|
147
|
-
check_passes "Empty file" \
|
|
148
|
-
"test-file.txt" \
|
|
149
|
-
""
|
|
150
|
-
|
|
151
|
-
check_passes "Git SHA / hex hash is not flagged as high-entropy" \
|
|
152
|
-
"test-file.txt" \
|
|
153
|
-
'commit a1b2c3d4e5f6789012345678901234567890abcd resolved'
|
|
154
|
-
|
|
155
|
-
check_passes "Lockfile integrity hash is exempt" \
|
|
156
|
-
"test-yarn.lock" \
|
|
157
|
-
'resolved "https://r.npm/x" integrity sha512-Zx9Kq2Lm7Pw4Nv8Rt3Yb6Hc1Df5Gj0Sa2Wd7Qe4Uo9IkLp=='
|
|
158
|
-
|
|
159
|
-
echo ""
|
|
160
|
-
echo "→ 4. No staged files = clean exit"
|
|
161
|
-
reset_stage
|
|
162
|
-
if run_hook; then
|
|
163
|
-
pass "No staged files exits 0"
|
|
164
|
-
else
|
|
165
|
-
fail "No staged files exits 0"
|
|
166
|
-
fi
|
|
167
|
-
|
|
168
|
-
echo ""
|
|
169
|
-
echo "══ pre-commit smoke: $PASS passed, $FAIL failed ══"
|
|
170
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-pref-migration.sh - exercises migrate-prefs.mjs against representative
|
|
3
|
-
# v2.0.0 and pre-2.0.0 fixtures, asserts the output is v2.1.0-shaped.
|
|
4
|
-
#
|
|
5
|
-
# Fixtures are synthesized inline (no external files) to keep the test self-contained.
|
|
6
|
-
# Run after any change to migrate-prefs.mjs or prefs.schema.json.
|
|
7
|
-
|
|
8
|
-
set -uo pipefail
|
|
9
|
-
|
|
10
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
-
MIGRATE="$HERE/migrate-prefs.mjs"
|
|
12
|
-
TMP="$(mktemp -d -t ma-smoke-migration-XXXX)"
|
|
13
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
14
|
-
|
|
15
|
-
PASS=0
|
|
16
|
-
FAIL=0
|
|
17
|
-
pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
|
|
18
|
-
fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
|
|
19
|
-
|
|
20
|
-
if [ ! -f "$MIGRATE" ]; then
|
|
21
|
-
fail "migrate-prefs.mjs not found at $MIGRATE"
|
|
22
|
-
echo "══ pref-migration smoke: aborted ══"
|
|
23
|
-
exit 2
|
|
24
|
-
fi
|
|
25
|
-
|
|
26
|
-
# Helper: write fixture to $TMP/<name>.json, run migration, read back
|
|
27
|
-
run_fixture() {
|
|
28
|
-
local name="$1"
|
|
29
|
-
local content="$2"
|
|
30
|
-
local file="$TMP/$name.json"
|
|
31
|
-
printf '%s' "$content" > "$file"
|
|
32
|
-
node "$MIGRATE" --file "$file" >/dev/null 2>&1
|
|
33
|
-
cat "$file"
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
assert_json_has() {
|
|
37
|
-
local name="$1"
|
|
38
|
-
local json="$2"
|
|
39
|
-
local path="$3" # e.g. .schemaVersion or .global.identities
|
|
40
|
-
local expected="$4"
|
|
41
|
-
local actual
|
|
42
|
-
actual=$(echo "$json" | node -e "
|
|
43
|
-
const j = JSON.parse(require('fs').readFileSync(0, 'utf8'));
|
|
44
|
-
const p = '$path'.split('.').filter(Boolean);
|
|
45
|
-
let v = j; for (const k of p) v = v?.[k];
|
|
46
|
-
if (Array.isArray(v)) console.log('array:' + v.length);
|
|
47
|
-
else if (typeof v === 'object' && v !== null) console.log('object:' + Object.keys(v).length);
|
|
48
|
-
else console.log(String(v));
|
|
49
|
-
" 2>/dev/null)
|
|
50
|
-
if [ "$actual" = "$expected" ]; then
|
|
51
|
-
pass "$name: $path == $expected"
|
|
52
|
-
else
|
|
53
|
-
fail "$name: $path expected '$expected', got '$actual'"
|
|
54
|
-
fi
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
# ──────────────────────────────────────────────────────────────────────────
|
|
58
|
-
echo "→ Fixture 1: v2.0.0 with gitIdentities (legacy)"
|
|
59
|
-
F1=$(run_fixture "f1-v2.0.0-legacy" '{
|
|
60
|
-
"schemaVersion": "2.0.0",
|
|
61
|
-
"global": {
|
|
62
|
-
"gitIdentities": [{"name": "Test User", "email": "test@example.com"}],
|
|
63
|
-
"keychainMapping": {"jira": "user_Jira_Access_Token"}
|
|
64
|
-
},
|
|
65
|
-
"projects": {}
|
|
66
|
-
}')
|
|
67
|
-
assert_json_has "f1" "$F1" ".schemaVersion" "2.4.0"
|
|
68
|
-
assert_json_has "f1" "$F1" ".global.identities" "array:1"
|
|
69
|
-
assert_json_has "f1" "$F1" ".global.gitIdentities" "undefined"
|
|
70
|
-
assert_json_has "f1" "$F1" ".global.settings" "object:8"
|
|
71
|
-
assert_json_has "f1" "$F1" ".global.keychainMapping" "object:10"
|
|
72
|
-
assert_json_has "f1" "$F1" ".global.platformIdentityRouting" "object:0"
|
|
73
|
-
assert_json_has "f1" "$F1" ".global.recentGroups" "array:0"
|
|
74
|
-
assert_json_has "f1" "$F1" ".global.recentAccounts" "array:0"
|
|
75
|
-
assert_json_has "f1" "$F1" ".global.accounts" "array:0"
|
|
76
|
-
assert_json_has "f1" "$F1" ".global.routines" "array:0"
|
|
77
|
-
|
|
78
|
-
echo ""
|
|
79
|
-
echo "→ Fixture 2: no schemaVersion (pre-2.0.0 fossil)"
|
|
80
|
-
F2=$(run_fixture "f2-no-version" '{
|
|
81
|
-
"global": {"gitIdentities": []},
|
|
82
|
-
"projects": {}
|
|
83
|
-
}')
|
|
84
|
-
assert_json_has "f2" "$F2" ".schemaVersion" "2.4.0"
|
|
85
|
-
assert_json_has "f2" "$F2" ".global.identities" "array:0"
|
|
86
|
-
|
|
87
|
-
echo ""
|
|
88
|
-
echo "→ Fixture 3: v2.4.0 already (no-op)"
|
|
89
|
-
F3_FILE="$TMP/f3-v2.4.0.json"
|
|
90
|
-
cat > "$F3_FILE" <<'EOF'
|
|
91
|
-
{
|
|
92
|
-
"schemaVersion": "2.4.0",
|
|
93
|
-
"global": {
|
|
94
|
-
"identities": [],
|
|
95
|
-
"keychainMapping": {
|
|
96
|
-
"jira": null, "bitbucket_token": null, "bitbucket_user": null,
|
|
97
|
-
"github": null, "confluence": null, "figma": null,
|
|
98
|
-
"figma_mcp": null, "fortify": null,
|
|
99
|
-
"firebase": null, "jenkins": null
|
|
100
|
-
},
|
|
101
|
-
"platformIdentityRouting": {},
|
|
102
|
-
"recentProjects": [],
|
|
103
|
-
"recentBranches": {},
|
|
104
|
-
"recentGroups": [],
|
|
105
|
-
"serviceStatus": {},
|
|
106
|
-
"settings": {
|
|
107
|
-
"branchTtlDays": 15, "pushRetryMax": 5, "buildRetryMax": 3,
|
|
108
|
-
"serviceStatusCacheSeconds": 300, "repoScanDays": 90,
|
|
109
|
-
"identityRoutingEnabled": false, "multiRepoEnabled": false,
|
|
110
|
-
"pushMustSucceed": true
|
|
111
|
-
},
|
|
112
|
-
"triageCrossCheck": {
|
|
113
|
-
"enabled": false, "samplePct": 10, "model": "sonnet", "blockOnDisagreement": false
|
|
114
|
-
},
|
|
115
|
-
"promptLanguage": "en",
|
|
116
|
-
"reportChannels": {"pr": true, "jira": true, "confluence": false, "wiki": false},
|
|
117
|
-
"reportContent": {"normalAnalysis": true, "technicalAnalysis": false, "testScenarios": true, "autoDiff": false, "manualNote": false},
|
|
118
|
-
"wikiScope": ["main", "ios", "screenshots", "index"],
|
|
119
|
-
"autopilotReportTimeoutSeconds": 1800,
|
|
120
|
-
"recentAccounts": [],
|
|
121
|
-
"accounts": [],
|
|
122
|
-
"routines": []
|
|
123
|
-
},
|
|
124
|
-
"projects": {}
|
|
125
|
-
}
|
|
126
|
-
EOF
|
|
127
|
-
# Hash before
|
|
128
|
-
BEFORE=$(shasum -a 256 "$F3_FILE" | awk '{print $1}')
|
|
129
|
-
node "$MIGRATE" --file "$F3_FILE" >/dev/null 2>&1
|
|
130
|
-
AFTER=$(shasum -a 256 "$F3_FILE" | awk '{print $1}')
|
|
131
|
-
if [ "$BEFORE" = "$AFTER" ]; then
|
|
132
|
-
pass "f3: idempotent (v2.3.0 unchanged by re-run)"
|
|
133
|
-
else
|
|
134
|
-
fail "f3: v2.3.0 file mutated by re-run (hash changed)"
|
|
135
|
-
fi
|
|
136
|
-
|
|
137
|
-
echo ""
|
|
138
|
-
echo "→ Fixture 4: obsolete fields pruned"
|
|
139
|
-
F4=$(run_fixture "f4-obsolete" '{
|
|
140
|
-
"global": {
|
|
141
|
-
"defaultPriority": "Medium",
|
|
142
|
-
"defaultIssueType": "Story",
|
|
143
|
-
"gitIdentities": []
|
|
144
|
-
},
|
|
145
|
-
"projects": {}
|
|
146
|
-
}')
|
|
147
|
-
assert_json_has "f4" "$F4" ".global.defaultPriority" "undefined"
|
|
148
|
-
assert_json_has "f4" "$F4" ".global.defaultIssueType" "undefined"
|
|
149
|
-
|
|
150
|
-
echo ""
|
|
151
|
-
echo "→ Fixture 5: keychainMapping values preserved"
|
|
152
|
-
F5=$(run_fixture "f5-km-preserve" '{
|
|
153
|
-
"global": {
|
|
154
|
-
"gitIdentities": [],
|
|
155
|
-
"keychainMapping": {
|
|
156
|
-
"jira": "user_Jira_Access_Token",
|
|
157
|
-
"github": "user_Github_Access_Token"
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
"projects": {}
|
|
161
|
-
}')
|
|
162
|
-
# Parse and inspect specific keychain values
|
|
163
|
-
JIRA_VAL=$(echo "$F5" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.jira)")
|
|
164
|
-
if [ "$JIRA_VAL" = "user_Jira_Access_Token" ]; then
|
|
165
|
-
pass "f5: existing keychainMapping.jira value preserved"
|
|
166
|
-
else
|
|
167
|
-
fail "f5: keychainMapping.jira lost or changed: got '$JIRA_VAL'"
|
|
168
|
-
fi
|
|
169
|
-
|
|
170
|
-
echo ""
|
|
171
|
-
echo "→ Fixture 6: v2.1.0 firebase_sa + firebase_project → firebase (consolidation)"
|
|
172
|
-
F6=$(run_fixture "f6-firebase-consolidate" '{
|
|
173
|
-
"schemaVersion": "2.1.0",
|
|
174
|
-
"global": {
|
|
175
|
-
"identities": [{
|
|
176
|
-
"name": "corporate",
|
|
177
|
-
"email": "x@y.com",
|
|
178
|
-
"servicePatMap": {
|
|
179
|
-
"firebase_sa": "user_Firebase_Access_Json",
|
|
180
|
-
"firebase_project": "user_Firebase_Project_Id"
|
|
181
|
-
}
|
|
182
|
-
}],
|
|
183
|
-
"keychainMapping": {
|
|
184
|
-
"jira": null, "bitbucket_token": null, "bitbucket_user": null,
|
|
185
|
-
"github": null, "confluence": null, "figma_pat": "user_Figma_Access_Token",
|
|
186
|
-
"figma_user": "user_figma_email", "figma_mcp": null, "fortify": null,
|
|
187
|
-
"firebase_sa": "user_Firebase_Access_Json",
|
|
188
|
-
"firebase_project": "user_Firebase_Project_Id",
|
|
189
|
-
"jenkins": null
|
|
190
|
-
},
|
|
191
|
-
"platformIdentityRouting": {},
|
|
192
|
-
"recentProjects": [], "recentBranches": {}, "recentGroups": [],
|
|
193
|
-
"serviceStatus": {},
|
|
194
|
-
"settings": {
|
|
195
|
-
"branchTtlDays": 15, "pushRetryMax": 5, "buildRetryMax": 3,
|
|
196
|
-
"serviceStatusCacheSeconds": 300, "repoScanDays": 90,
|
|
197
|
-
"identityRoutingEnabled": false, "multiRepoEnabled": false,
|
|
198
|
-
"pushMustSucceed": true
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
"projects": {}
|
|
202
|
-
}')
|
|
203
|
-
# keychainMapping.firebase_sa must be gone, firebase_project must be gone, firebase must carry the old firebase_sa value
|
|
204
|
-
FB_SA=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.firebase_sa === undefined ? 'absent' : 'present')")
|
|
205
|
-
FB_PROJ=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.firebase_project === undefined ? 'absent' : 'present')")
|
|
206
|
-
FB=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.firebase)")
|
|
207
|
-
SPM_SA=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.identities[0].servicePatMap.firebase_sa === undefined ? 'absent' : 'present')")
|
|
208
|
-
SPM_PROJ=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.identities[0].servicePatMap.firebase_project === undefined ? 'absent' : 'present')")
|
|
209
|
-
SPM_FB=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.identities[0].servicePatMap.firebase)")
|
|
210
|
-
[ "$FB_SA" = "absent" ] && pass "f6: keychainMapping.firebase_sa removed" || fail "f6: keychainMapping.firebase_sa still present"
|
|
211
|
-
[ "$FB_PROJ" = "absent" ] && pass "f6: keychainMapping.firebase_project removed" || fail "f6: keychainMapping.firebase_project still present"
|
|
212
|
-
[ "$FB" = "user_Firebase_Access_Json" ] && pass "f6: keychainMapping.firebase carries old firebase_sa value" || fail "f6: keychainMapping.firebase wrong value: '$FB'"
|
|
213
|
-
[ "$SPM_SA" = "absent" ] && pass "f6: servicePatMap.firebase_sa removed" || fail "f6: servicePatMap.firebase_sa still present"
|
|
214
|
-
[ "$SPM_PROJ" = "absent" ] && pass "f6: servicePatMap.firebase_project removed" || fail "f6: servicePatMap.firebase_project still present"
|
|
215
|
-
[ "$SPM_FB" = "user_Firebase_Access_Json" ] && pass "f6: servicePatMap.firebase carries old value" || fail "f6: servicePatMap.firebase wrong value: '$SPM_FB'"
|
|
216
|
-
# figma consolidation: figma_pat → figma, figma_user removed
|
|
217
|
-
FIG_PAT=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.figma_pat === undefined ? 'absent' : 'present')")
|
|
218
|
-
FIG_USER=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.figma_user === undefined ? 'absent' : 'present')")
|
|
219
|
-
FIG=$(echo "$F6" | node -e "const j=JSON.parse(require('fs').readFileSync(0,'utf8')); console.log(j.global.keychainMapping.figma)")
|
|
220
|
-
[ "$FIG_PAT" = "absent" ] && pass "f6: keychainMapping.figma_pat removed" || fail "f6: keychainMapping.figma_pat still present"
|
|
221
|
-
[ "$FIG_USER" = "absent" ] && pass "f6: keychainMapping.figma_user removed" || fail "f6: keychainMapping.figma_user still present"
|
|
222
|
-
[ "$FIG" = "user_Figma_Access_Token" ] && pass "f6: keychainMapping.figma carries old figma_pat value" || fail "f6: keychainMapping.figma wrong value: '$FIG'"
|
|
223
|
-
|
|
224
|
-
echo ""
|
|
225
|
-
echo "══ pref-migration smoke: $PASS passed, $FAIL failed ══"
|
|
226
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-prefs-language.sh - contract check for `/multi-agent language`.
|
|
3
|
-
#
|
|
4
|
-
# Does NOT touch the user's real $HOME/.claude/multi-agent-preferences.json.
|
|
5
|
-
# Uses a throwaway tempfile + node shim that applies the documented write
|
|
6
|
-
# rules (atomic rename, schema validation, enum check).
|
|
7
|
-
#
|
|
8
|
-
# Verifies:
|
|
9
|
-
# 1. promptLanguage is part of the prefs schema and enum is {en, tr}.
|
|
10
|
-
# 2. "en" and "tr" round-trip cleanly.
|
|
11
|
-
# 3. Unknown values ("de", "xx") are rejected.
|
|
12
|
-
# 4. Missing promptLanguage in prefs is valid (field is optional).
|
|
13
|
-
# 5. Schema file is structurally valid.
|
|
14
|
-
|
|
15
|
-
set -euo pipefail
|
|
16
|
-
|
|
17
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
18
|
-
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
19
|
-
SCHEMA="$ROOT/pipeline/schemas/prefs.schema.json"
|
|
20
|
-
TMP="$(mktemp -d -t multi-agent-smoke-XXXX)"
|
|
21
|
-
trap 'rm -rf "$TMP"' EXIT
|
|
22
|
-
|
|
23
|
-
PASS=0
|
|
24
|
-
FAIL=0
|
|
25
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
26
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
27
|
-
|
|
28
|
-
need() { command -v "$1" >/dev/null 2>&1 || { echo "error: $1 is required" >&2; exit 127; }; }
|
|
29
|
-
need node
|
|
30
|
-
need jq
|
|
31
|
-
|
|
32
|
-
[ -f "$SCHEMA" ] || { echo "error: schema not found at $SCHEMA" >&2; exit 1; }
|
|
33
|
-
|
|
34
|
-
echo "→ schema declares promptLanguage with enum {en, tr}"
|
|
35
|
-
ENUM_JSON=$(jq '.properties.global.properties.promptLanguage.enum' "$SCHEMA")
|
|
36
|
-
if [ "$ENUM_JSON" = '[
|
|
37
|
-
"en",
|
|
38
|
-
"tr"
|
|
39
|
-
]' ] || [ "$(echo "$ENUM_JSON" | jq -c '.')" = '["en","tr"]' ]; then
|
|
40
|
-
pass "promptLanguage.enum is exactly [en, tr]"
|
|
41
|
-
else
|
|
42
|
-
fail "promptLanguage.enum unexpected: $ENUM_JSON"
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
DEFAULT=$(jq -r '.properties.global.properties.promptLanguage.default' "$SCHEMA")
|
|
46
|
-
if [ "$DEFAULT" = "en" ]; then
|
|
47
|
-
pass "default promptLanguage is 'en' (safe for open-source)"
|
|
48
|
-
else
|
|
49
|
-
fail "default promptLanguage is '$DEFAULT' (expected 'en')"
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
echo ""
|
|
53
|
-
echo "→ prefs with promptLanguage=en round-trips"
|
|
54
|
-
cat > "$TMP/prefs-en.json" <<'EOF'
|
|
55
|
-
{
|
|
56
|
-
"schemaVersion": "2.0.0",
|
|
57
|
-
"global": {
|
|
58
|
-
"promptLanguage": "en"
|
|
59
|
-
},
|
|
60
|
-
"projects": {}
|
|
61
|
-
}
|
|
62
|
-
EOF
|
|
63
|
-
node -e "
|
|
64
|
-
const s = JSON.parse(require('fs').readFileSync('$TMP/prefs-en.json','utf-8'));
|
|
65
|
-
if (s.global.promptLanguage !== 'en') process.exit(1);
|
|
66
|
-
" && pass "en value parses" || fail "en value failed"
|
|
67
|
-
|
|
68
|
-
echo ""
|
|
69
|
-
echo "→ prefs with promptLanguage=tr round-trips"
|
|
70
|
-
cat > "$TMP/prefs-tr.json" <<'EOF'
|
|
71
|
-
{
|
|
72
|
-
"schemaVersion": "2.0.0",
|
|
73
|
-
"global": {
|
|
74
|
-
"promptLanguage": "tr"
|
|
75
|
-
},
|
|
76
|
-
"projects": {}
|
|
77
|
-
}
|
|
78
|
-
EOF
|
|
79
|
-
node -e "
|
|
80
|
-
const s = JSON.parse(require('fs').readFileSync('$TMP/prefs-tr.json','utf-8'));
|
|
81
|
-
if (s.global.promptLanguage !== 'tr') process.exit(1);
|
|
82
|
-
" && pass "tr value parses" || fail "tr value failed"
|
|
83
|
-
|
|
84
|
-
echo ""
|
|
85
|
-
echo "→ unknown language 'de' is not in enum"
|
|
86
|
-
if ! echo "$ENUM_JSON" | jq -e '. | index("de")' >/dev/null 2>&1; then
|
|
87
|
-
pass "'de' correctly absent from enum"
|
|
88
|
-
else
|
|
89
|
-
fail "'de' should not be in enum yet"
|
|
90
|
-
fi
|
|
91
|
-
|
|
92
|
-
echo ""
|
|
93
|
-
echo "→ prefs without promptLanguage is valid (optional field)"
|
|
94
|
-
cat > "$TMP/prefs-missing.json" <<'EOF'
|
|
95
|
-
{
|
|
96
|
-
"schemaVersion": "2.0.0",
|
|
97
|
-
"global": {},
|
|
98
|
-
"projects": {}
|
|
99
|
-
}
|
|
100
|
-
EOF
|
|
101
|
-
node -e "
|
|
102
|
-
const s = JSON.parse(require('fs').readFileSync('$TMP/prefs-missing.json','utf-8'));
|
|
103
|
-
if ('promptLanguage' in s.global) process.exit(1);
|
|
104
|
-
" && pass "missing promptLanguage is tolerated" || fail "missing field check failed"
|
|
105
|
-
|
|
106
|
-
echo ""
|
|
107
|
-
echo "→ 5. triageCrossCheck schema is well-formed"
|
|
108
|
-
TCC_DEFAULT=$(jq -r '.properties.global.properties.triageCrossCheck.properties.enabled.default' "$SCHEMA")
|
|
109
|
-
[ "$TCC_DEFAULT" = "false" ] && pass "triageCrossCheck.enabled defaults to false (opt-in)" || fail "expected false, got $TCC_DEFAULT"
|
|
110
|
-
TCC_PCT_DEFAULT=$(jq -r '.properties.global.properties.triageCrossCheck.properties.samplePct.default' "$SCHEMA")
|
|
111
|
-
[ "$TCC_PCT_DEFAULT" = "10" ] && pass "samplePct defaults to 10" || fail "expected 10, got $TCC_PCT_DEFAULT"
|
|
112
|
-
TCC_MODEL_ENUM=$(jq -c '.properties.global.properties.triageCrossCheck.properties.model.enum' "$SCHEMA")
|
|
113
|
-
[ "$TCC_MODEL_ENUM" = '["sonnet"]' ] && pass "model enum locked to [sonnet]" || fail "expected [sonnet], got $TCC_MODEL_ENUM"
|
|
114
|
-
TCC_PCT_MIN=$(jq -r '.properties.global.properties.triageCrossCheck.properties.samplePct.minimum' "$SCHEMA")
|
|
115
|
-
TCC_PCT_MAX=$(jq -r '.properties.global.properties.triageCrossCheck.properties.samplePct.maximum' "$SCHEMA")
|
|
116
|
-
[ "$TCC_PCT_MIN" = "1" ] && [ "$TCC_PCT_MAX" = "100" ] && pass "samplePct bounded [1,100]" || fail "samplePct bounds wrong"
|
|
117
|
-
|
|
118
|
-
echo ""
|
|
119
|
-
echo "→ setup.md documents Step 0 - Language Selection"
|
|
120
|
-
SETUP_DOC="$ROOT/pipeline/commands/multi-agent/setup/SKILL.md"
|
|
121
|
-
if [ -f "$SETUP_DOC" ] && grep -q "^### Step 0 - Language Selection" "$SETUP_DOC"; then
|
|
122
|
-
pass "setup.md has Step 0 - Language Selection"
|
|
123
|
-
else
|
|
124
|
-
fail "setup.md missing Step 0 - Language Selection heading"
|
|
125
|
-
fi
|
|
126
|
-
if [ -f "$SETUP_DOC" ] && grep -q "promptLanguage" "$SETUP_DOC"; then
|
|
127
|
-
pass "setup.md references promptLanguage"
|
|
128
|
-
else
|
|
129
|
-
fail "setup.md never mentions promptLanguage"
|
|
130
|
-
fi
|
|
131
|
-
|
|
132
|
-
echo ""
|
|
133
|
-
echo "══ prefs-language smoke: $PASS passed, $FAIL failed ══"
|
|
134
|
-
[ "$FAIL" -eq 0 ]
|