@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,229 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-phase4-triage.sh - contract check for Phase 4 triage output.
|
|
3
|
-
#
|
|
4
|
-
# Does NOT hit real review agents. Instead:
|
|
5
|
-
# 1. Synthesizes representative triage-output fixtures (valid + each edge case).
|
|
6
|
-
# 2. Validates them against pipeline/schemas/triage-output.schema.json
|
|
7
|
-
# using a minimal JSON-structure check (zero-dep - same style as
|
|
8
|
-
# validate-schemas.mjs).
|
|
9
|
-
# 3. Confirms the pipeline's documented edge-case rules hold for:
|
|
10
|
-
# - no-findings short-circuit (approved: true)
|
|
11
|
-
# - over-rejection guard threshold (>0.80 rejected)
|
|
12
|
-
# - schema rejects invented reviewer (e.g. "haiku" - removed in v2.1.0)
|
|
13
|
-
# - accepted finding without `fix` is rejected (fix is required on accepted)
|
|
14
|
-
|
|
15
|
-
set -euo pipefail
|
|
16
|
-
|
|
17
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
18
|
-
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
19
|
-
SCHEMA="$ROOT/pipeline/schemas/triage-output.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
|
-
# Helper: delegate to the real runtime validator so this smoke test exercises
|
|
35
|
-
# the same code path the pipeline uses in production. Exit codes:
|
|
36
|
-
# 0 = valid and clean
|
|
37
|
-
# 1 = structural / field violation
|
|
38
|
-
# 2 = over-rejection guard tripped
|
|
39
|
-
# 3 = contradiction auto-corrected
|
|
40
|
-
check_via_validator() {
|
|
41
|
-
local file="$1"
|
|
42
|
-
local expect_exit="$2" # numeric exit code
|
|
43
|
-
local reason="$3"
|
|
44
|
-
local actual_exit=0
|
|
45
|
-
node "$ROOT/pipeline/scripts/validate-triage.mjs" "$file" >/tmp/validator-out 2>/tmp/validator-err || actual_exit=$?
|
|
46
|
-
if [ "$actual_exit" = "$expect_exit" ]; then
|
|
47
|
-
pass "$reason (exit=$actual_exit)"
|
|
48
|
-
else
|
|
49
|
-
fail "$reason (expected exit=$expect_exit, got $actual_exit; stderr: $(cat /tmp/validator-err))"
|
|
50
|
-
fi
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
echo "→ fixture 1: empty findings (short-circuit)"
|
|
54
|
-
cat > "$TMP/empty.json" <<'EOF'
|
|
55
|
-
{"accepted": [], "deferred": [], "rejected": [], "approved": true}
|
|
56
|
-
EOF
|
|
57
|
-
check_via_validator "$TMP/empty.json" 0 "empty triage output is valid (clean)"
|
|
58
|
-
|
|
59
|
-
echo ""
|
|
60
|
-
echo "→ fixture 2: one accepted blocking finding"
|
|
61
|
-
cat > "$TMP/accepted.json" <<'EOF'
|
|
62
|
-
{
|
|
63
|
-
"accepted": [
|
|
64
|
-
{"severity":"blocking","file":"src/auth.ts","line":42,"issue":"SQL injection via untrusted input","fix":"parameterize the query with prepared statement","reviewer":"opus"}
|
|
65
|
-
],
|
|
66
|
-
"deferred": [],
|
|
67
|
-
"rejected": [],
|
|
68
|
-
"approved": false
|
|
69
|
-
}
|
|
70
|
-
EOF
|
|
71
|
-
check_via_validator "$TMP/accepted.json" 0 "accepted blocking with fix + reviewer is valid"
|
|
72
|
-
|
|
73
|
-
echo ""
|
|
74
|
-
echo "→ fixture 3: accepted without fix (must reject - exit 1)"
|
|
75
|
-
cat > "$TMP/no-fix.json" <<'EOF'
|
|
76
|
-
{
|
|
77
|
-
"accepted": [
|
|
78
|
-
{"severity":"blocking","file":"src/auth.ts","line":42,"issue":"SQL injection","reviewer":"opus"}
|
|
79
|
-
],
|
|
80
|
-
"deferred": [],
|
|
81
|
-
"rejected": [],
|
|
82
|
-
"approved": false
|
|
83
|
-
}
|
|
84
|
-
EOF
|
|
85
|
-
check_via_validator "$TMP/no-fix.json" 1 "accepted without fix triggers exit 1"
|
|
86
|
-
|
|
87
|
-
echo ""
|
|
88
|
-
echo "→ fixture 4: reviewer=haiku (removed in v2.1.0, must reject - exit 1)"
|
|
89
|
-
cat > "$TMP/haiku.json" <<'EOF'
|
|
90
|
-
{
|
|
91
|
-
"accepted": [
|
|
92
|
-
{"severity":"suggestion","file":"x.ts","line":1,"issue":"naming","fix":"rename","reviewer":"haiku"}
|
|
93
|
-
],
|
|
94
|
-
"deferred": [],
|
|
95
|
-
"rejected": [],
|
|
96
|
-
"approved": true
|
|
97
|
-
}
|
|
98
|
-
EOF
|
|
99
|
-
check_via_validator "$TMP/haiku.json" 1 "haiku reviewer triggers exit 1"
|
|
100
|
-
|
|
101
|
-
echo ""
|
|
102
|
-
echo "→ fixture 5: over-rejection guard (5 rejected, 0 others - exit 2)"
|
|
103
|
-
cat > "$TMP/over-reject.json" <<'EOF'
|
|
104
|
-
{
|
|
105
|
-
"accepted": [],
|
|
106
|
-
"deferred": [],
|
|
107
|
-
"rejected": [
|
|
108
|
-
{"finding":{"severity":"blocking","file":"a.ts","line":1,"issue":"i111"},"reason":"false positive"},
|
|
109
|
-
{"finding":{"severity":"blocking","file":"b.ts","line":2,"issue":"i222"},"reason":"false positive"},
|
|
110
|
-
{"finding":{"severity":"important","file":"c.ts","line":3,"issue":"i333"},"reason":"duplicate"},
|
|
111
|
-
{"finding":{"severity":"blocking","file":"d.ts","line":4,"issue":"i444"},"reason":"style only"},
|
|
112
|
-
{"finding":{"severity":"suggestion","file":"e.ts","line":5,"issue":"i555"},"reason":"already correct"}
|
|
113
|
-
],
|
|
114
|
-
"approved": true
|
|
115
|
-
}
|
|
116
|
-
EOF
|
|
117
|
-
check_via_validator "$TMP/over-reject.json" 2 "100% rejection triggers over-rejection guard (exit 2)"
|
|
118
|
-
|
|
119
|
-
echo ""
|
|
120
|
-
echo "→ fixture 6: contradiction (approved=false but no blocker - exit 3 with corrected)"
|
|
121
|
-
cat > "$TMP/contradiction.json" <<'EOF'
|
|
122
|
-
{
|
|
123
|
-
"accepted": [
|
|
124
|
-
{"severity":"suggestion","file":"x.ts","line":1,"issue":"name change","fix":"rename to FooBar","reviewer":"opus"}
|
|
125
|
-
],
|
|
126
|
-
"deferred": [],
|
|
127
|
-
"rejected": [],
|
|
128
|
-
"approved": false
|
|
129
|
-
}
|
|
130
|
-
EOF
|
|
131
|
-
check_via_validator "$TMP/contradiction.json" 3 "contradiction auto-correct (exit 3)"
|
|
132
|
-
# The validator exits 3 (non-zero) but we still want its stdout.
|
|
133
|
-
CORRECTED_APPROVED=$(node "$ROOT/pipeline/scripts/validate-triage.mjs" "$TMP/contradiction.json" 2>/dev/null || true)
|
|
134
|
-
if echo "$CORRECTED_APPROVED" | jq -e '.corrected.approved == true' >/dev/null 2>&1; then
|
|
135
|
-
pass "corrected.approved == true"
|
|
136
|
-
else
|
|
137
|
-
fail "corrected.approved was not true"
|
|
138
|
-
fi
|
|
139
|
-
|
|
140
|
-
echo ""
|
|
141
|
-
echo "→ fixture 7: optional consensus block, unverified verdict (valid - exit 0)"
|
|
142
|
-
cat > "$TMP/consensus-unverified.json" <<'EOF'
|
|
143
|
-
{
|
|
144
|
-
"accepted": [],
|
|
145
|
-
"deferred": [],
|
|
146
|
-
"rejected": [],
|
|
147
|
-
"approved": true,
|
|
148
|
-
"consensus": {
|
|
149
|
-
"reviewerCount": 2,
|
|
150
|
-
"verdict": "unverified",
|
|
151
|
-
"disagreements": [
|
|
152
|
-
{"file":"Auth/KeychainStore.swift","line":40,"issue":"token persisted without access-control flag","note":"both approved a keychain change - agreement unverified, confirm manually"}
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
EOF
|
|
157
|
-
check_via_validator "$TMP/consensus-unverified.json" 0 "optional consensus (unverified) is valid"
|
|
158
|
-
|
|
159
|
-
echo ""
|
|
160
|
-
echo "→ fixture 8: consensus split verdict (valid - exit 0)"
|
|
161
|
-
cat > "$TMP/consensus-split.json" <<'EOF'
|
|
162
|
-
{
|
|
163
|
-
"accepted": [
|
|
164
|
-
{"severity":"blocking","file":"src/pay.ts","line":7,"issue":"amount parsed as float loses precision","fix":"use integer minor units","reviewer":"opus"}
|
|
165
|
-
],
|
|
166
|
-
"deferred": [],
|
|
167
|
-
"rejected": [],
|
|
168
|
-
"approved": false,
|
|
169
|
-
"consensus": {
|
|
170
|
-
"reviewerCount": 2,
|
|
171
|
-
"verdict": "split",
|
|
172
|
-
"disagreements": [
|
|
173
|
-
{"file":"src/pay.ts","line":7,"issue":"float money parsing","note":"Opus blocking, Sonnet approved"}
|
|
174
|
-
]
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
EOF
|
|
178
|
-
check_via_validator "$TMP/consensus-split.json" 0 "consensus split verdict is valid"
|
|
179
|
-
|
|
180
|
-
echo ""
|
|
181
|
-
echo "→ fixture 9: bad consensus verdict enum (must reject - exit 1)"
|
|
182
|
-
cat > "$TMP/consensus-bad.json" <<'EOF'
|
|
183
|
-
{
|
|
184
|
-
"accepted": [],
|
|
185
|
-
"deferred": [],
|
|
186
|
-
"rejected": [],
|
|
187
|
-
"approved": true,
|
|
188
|
-
"consensus": {"reviewerCount": 2, "verdict": "maybe"}
|
|
189
|
-
}
|
|
190
|
-
EOF
|
|
191
|
-
check_via_validator "$TMP/consensus-bad.json" 1 "bad consensus verdict triggers exit 1"
|
|
192
|
-
|
|
193
|
-
echo ""
|
|
194
|
-
echo "→ fixture 10: consensus missing required reviewerCount (must reject - exit 1)"
|
|
195
|
-
cat > "$TMP/consensus-noreqcount.json" <<'EOF'
|
|
196
|
-
{
|
|
197
|
-
"accepted": [],
|
|
198
|
-
"deferred": [],
|
|
199
|
-
"rejected": [],
|
|
200
|
-
"approved": true,
|
|
201
|
-
"consensus": {"verdict": "unanimous-pass"}
|
|
202
|
-
}
|
|
203
|
-
EOF
|
|
204
|
-
check_via_validator "$TMP/consensus-noreqcount.json" 1 "consensus without reviewerCount triggers exit 1"
|
|
205
|
-
|
|
206
|
-
echo ""
|
|
207
|
-
echo "→ fixture 11: consensus unanimous-block contradicts approved=true (exit 1)"
|
|
208
|
-
cat > "$TMP/consensus-contra1.json" <<'EOF'
|
|
209
|
-
{"accepted":[],"deferred":[],"rejected":[],"approved":true,"consensus":{"reviewerCount":2,"verdict":"unanimous-block"}}
|
|
210
|
-
EOF
|
|
211
|
-
check_via_validator "$TMP/consensus-contra1.json" 1 "unanimous-block + approved=true is rejected"
|
|
212
|
-
|
|
213
|
-
echo ""
|
|
214
|
-
echo "→ fixture 12: unanimous-pass contradicts an accepted blocker (exit 1)"
|
|
215
|
-
cat > "$TMP/consensus-contra2.json" <<'EOF'
|
|
216
|
-
{"accepted":[{"severity":"blocking","file":"a.ts","line":1,"issue":"sql injection here","fix":"parameterize the query","reviewer":"opus"}],"deferred":[],"rejected":[],"approved":false,"consensus":{"reviewerCount":2,"verdict":"unanimous-pass"}}
|
|
217
|
-
EOF
|
|
218
|
-
check_via_validator "$TMP/consensus-contra2.json" 1 "unanimous-pass + accepted blocker is rejected"
|
|
219
|
-
|
|
220
|
-
echo ""
|
|
221
|
-
echo "→ fixture 13: single reviewer cannot be unanimous (exit 1)"
|
|
222
|
-
cat > "$TMP/consensus-solo.json" <<'EOF'
|
|
223
|
-
{"accepted":[],"deferred":[],"rejected":[],"approved":true,"consensus":{"reviewerCount":1,"verdict":"unanimous-pass"}}
|
|
224
|
-
EOF
|
|
225
|
-
check_via_validator "$TMP/consensus-solo.json" 1 "reviewerCount 1 + unanimous verdict is rejected"
|
|
226
|
-
|
|
227
|
-
echo ""
|
|
228
|
-
echo "══ phase4-triage smoke: $PASS passed, $FAIL failed ══"
|
|
229
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-plan-approval-gate.sh - contract check for Phase 2 Plan Approval Gate (v5.3.0).
|
|
3
|
-
#
|
|
4
|
-
# Verifies:
|
|
5
|
-
# 1. phase-2-planning.md declares Step 5 as the gate (clarification + approval)
|
|
6
|
-
# 2. Gate is explicitly scoped to non-autopilot, non-dev modes (autopilot + --dev skip)
|
|
7
|
-
# 3. Clarification cap is 2 rounds
|
|
8
|
-
# 4. agent-state schema exposes the six audit fields in phases.*
|
|
9
|
-
# 5. Mode matrix in modes.md, autopilot.md, dev.md, dev-autopilot.md mention the gate
|
|
10
|
-
# 6. SKILL.md autopilot matrix shows "Gate skip"
|
|
11
|
-
|
|
12
|
-
set -uo pipefail
|
|
13
|
-
|
|
14
|
-
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
15
|
-
ROOT="$(cd "$HERE/../.." && pwd)"
|
|
16
|
-
|
|
17
|
-
PASS=0
|
|
18
|
-
FAIL=0
|
|
19
|
-
pass() { PASS=$((PASS+1)); echo " ✓ $1"; }
|
|
20
|
-
fail() { FAIL=$((FAIL+1)); echo " ✗ $1"; }
|
|
21
|
-
|
|
22
|
-
PHASE2="$ROOT/pipeline/multi-agent-refs/phases/phase-2-planning.md"
|
|
23
|
-
SCHEMA="$ROOT/pipeline/schemas/agent-state.schema.json"
|
|
24
|
-
SKILL="$ROOT/pipeline/skills/shared/core/multi-agent/SKILL.md"
|
|
25
|
-
MODES="$ROOT/pipeline/multi-agent-refs/phases/modes.md"
|
|
26
|
-
AUTOPILOT="$ROOT/pipeline/commands/multi-agent/autopilot/SKILL.md"
|
|
27
|
-
DEV="$ROOT/pipeline/commands/multi-agent/dev/SKILL.md"
|
|
28
|
-
DEVAUTO="$ROOT/pipeline/commands/multi-agent/dev-autopilot/SKILL.md"
|
|
29
|
-
|
|
30
|
-
echo "→ 1. Phase 2 doc declares the gate + sub-modes"
|
|
31
|
-
grep -q "Plan Approval Gate" "$PHASE2" && pass "phase-2 has 'Plan Approval Gate'" || fail "phase-2 missing gate heading"
|
|
32
|
-
grep -q "Clarification Mode" "$PHASE2" && pass "phase-2 has Clarification Mode (5a)" || fail "phase-2 missing 5a"
|
|
33
|
-
grep -q "Approval Loop" "$PHASE2" && pass "phase-2 has Approval Loop (5b)" || fail "phase-2 missing 5b"
|
|
34
|
-
|
|
35
|
-
echo ""
|
|
36
|
-
echo "→ 2. Gate is scoped - autopilot + --dev skip"
|
|
37
|
-
grep -q "state.autopilot === true" "$PHASE2" && pass "phase-2 guards against autopilot" || fail "phase-2 missing autopilot guard"
|
|
38
|
-
grep -q "state.onlyDevelop === true" "$PHASE2" && pass "phase-2 guards against --dev" || fail "phase-2 missing --dev guard"
|
|
39
|
-
|
|
40
|
-
echo ""
|
|
41
|
-
echo "→ 3. Clarification cap = 2 rounds"
|
|
42
|
-
grep -qi "max 2 rounds" "$PHASE2" && pass "phase-2 states 'max 2 rounds'" || fail "phase-2 missing '2 rounds' cap"
|
|
43
|
-
grep -q '"clarificationRounds"' "$SCHEMA" && pass "schema exposes clarificationRounds" || fail "schema missing clarificationRounds"
|
|
44
|
-
grep -A4 '"clarificationRounds"' "$SCHEMA" | grep -q '"maximum": 2' && pass "clarificationRounds max=2" || fail "clarificationRounds schema cap wrong"
|
|
45
|
-
|
|
46
|
-
echo ""
|
|
47
|
-
echo "→ 4. Schema exposes all six audit fields"
|
|
48
|
-
for field in clarificationRounds clarificationQuestions clarificationAnswers planIterations planApprovedAt planEditRequests; do
|
|
49
|
-
grep -q "\"$field\"" "$SCHEMA" && pass "schema has $field" || fail "schema missing $field"
|
|
50
|
-
done
|
|
51
|
-
|
|
52
|
-
echo ""
|
|
53
|
-
echo "→ 5. Mode matrices reference the gate"
|
|
54
|
-
grep -qi "Plan Approval Gate" "$MODES" && pass "modes.md references gate" || fail "modes.md missing gate"
|
|
55
|
-
grep -qi "Gate skip\|Plan Approval Gate" "$AUTOPILOT" && pass "autopilot.md references gate skip" || fail "autopilot.md missing gate mention"
|
|
56
|
-
grep -qi "Plan Approval Gate\|plan gate\|Plan Approval" "$DEV" && pass "dev.md references gate (skipped)" || fail "dev.md missing gate mention"
|
|
57
|
-
grep -qi "Plan Approval Gate" "$DEVAUTO" && pass "dev-autopilot.md references gate (skipped)" || fail "dev-autopilot.md missing gate mention"
|
|
58
|
-
|
|
59
|
-
echo ""
|
|
60
|
-
echo "→ 6. SKILL.md autopilot matrix shows Gate skip semantics"
|
|
61
|
-
grep -qi "Plan Approval Gate" "$SKILL" && pass "SKILL.md references gate" || fail "SKILL.md missing gate"
|
|
62
|
-
grep -qi "Gate skip" "$SKILL" && pass "SKILL.md autopilot matrix shows 'Gate skip'" || fail "SKILL.md autopilot matrix missing 'Gate skip'"
|
|
63
|
-
|
|
64
|
-
echo ""
|
|
65
|
-
echo "→ 7. Free-text edit contract (no sentinel required)"
|
|
66
|
-
grep -qi "free-text" "$PHASE2" && pass "phase-2 documents the free-text edit path" || fail "phase-2 missing free-text edit path"
|
|
67
|
-
grep -q 'planEditRequests' "$PHASE2" && pass "phase-2 references planEditRequests" || fail "phase-2 missing planEditRequests"
|
|
68
|
-
|
|
69
|
-
echo ""
|
|
70
|
-
echo "══ plan-approval-gate smoke: $PASS passed, $FAIL failed ══"
|
|
71
|
-
[ "$FAIL" -eq 0 ]
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-plan-safety.sh - v7.0.G
|
|
3
|
-
#
|
|
4
|
-
# Verifies the Phase 2 autopilot safety classifier:
|
|
5
|
-
# 1. Low-risk plan: score low, recommendPause=false
|
|
6
|
-
# 2. High-risk plan (destructive + security + migration): recommendPause=true
|
|
7
|
-
# 3. File-count thresholds fire: medium (8-15) vs high (>15)
|
|
8
|
-
# 4. Security-path detection on /auth/, /keychain/, /credentials/, etc.
|
|
9
|
-
# 5. delete-without-test rule fires when destructive task has no sibling test
|
|
10
|
-
# 6. Malformed JSON input → exit 1
|
|
11
|
-
# 7. Schema pref exposes autopilotSafetyGate default true
|
|
12
|
-
#
|
|
13
|
-
# Exit 0 all pass, 1 any failure.
|
|
14
|
-
|
|
15
|
-
set -euo pipefail
|
|
16
|
-
|
|
17
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
18
|
-
CLS="$ROOT/pipeline/scripts/classify-plan-safety.mjs"
|
|
19
|
-
SCHEMA="$ROOT/pipeline/schemas/prefs.schema.json"
|
|
20
|
-
|
|
21
|
-
pass=0; fail=0; failures=()
|
|
22
|
-
record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
|
|
23
|
-
record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
|
|
24
|
-
|
|
25
|
-
printf '→ smoke-plan-safety (v7.0.G): autopilot safety classifier contract\n'
|
|
26
|
-
|
|
27
|
-
# --- 1: low-risk ---
|
|
28
|
-
out=$(cat <<'JSON' | node "$CLS" -
|
|
29
|
-
{"tasks":[{"id":"T1","title":"Rename a test helper","type":"test","files":["tests/helpers/fixtures.ts"]}],"approved":true}
|
|
30
|
-
JSON
|
|
31
|
-
)
|
|
32
|
-
if [ "$(jq -r .recommendPause <<< "$out")" = "false" ]; then
|
|
33
|
-
record_pass "low-risk plan does not recommend pause"
|
|
34
|
-
else
|
|
35
|
-
record_fail "low-risk plan should not recommend pause (got: $out)"
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
# --- 2: high-risk (multi-signal) ---
|
|
39
|
-
out=$(cat <<'JSON' | node "$CLS" -
|
|
40
|
-
{"approach":"Drop auth schema and remove legacy keychain storage","tasks":[
|
|
41
|
-
{"id":"T1","title":"Drop auth_tokens migration","type":"code","files":["schemas/migrations/008.sql"]},
|
|
42
|
-
{"id":"T2","title":"Remove KeychainStore","type":"refactor","files":["Sources/Auth/KeychainStore.swift","Sources/Security/TokenManager.swift"]}
|
|
43
|
-
],"approved":true}
|
|
44
|
-
JSON
|
|
45
|
-
)
|
|
46
|
-
if [ "$(jq -r .recommendPause <<< "$out")" = "true" ]; then
|
|
47
|
-
record_pass "high-risk plan recommends pause"
|
|
48
|
-
else
|
|
49
|
-
record_fail "high-risk plan should recommend pause (got: $out)"
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
# --- 3: file-count thresholds ---
|
|
53
|
-
files_json=$(jq -cn '[range(0;10)] | map("src/m\(.).kt")')
|
|
54
|
-
out=$(cat <<JSON | node "$CLS" -
|
|
55
|
-
{"tasks":[{"id":"T1","title":"touch 10 files","type":"code","files":$files_json}],"approved":true}
|
|
56
|
-
JSON
|
|
57
|
-
)
|
|
58
|
-
rules=$(jq -r '.reasons[].rule' <<< "$out" | tr '\n' ' ')
|
|
59
|
-
if echo "$rules" | grep -q 'file-count-medium'; then
|
|
60
|
-
record_pass "file-count-medium fires at 10 files"
|
|
61
|
-
else
|
|
62
|
-
record_fail "file-count-medium should fire at 10 files (got rules: $rules)"
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
files_json=$(jq -cn '[range(0;20)] | map("src/m\(.).kt")')
|
|
66
|
-
out=$(cat <<JSON | node "$CLS" -
|
|
67
|
-
{"tasks":[{"id":"T1","title":"touch 20 files","type":"code","files":$files_json}],"approved":true}
|
|
68
|
-
JSON
|
|
69
|
-
)
|
|
70
|
-
rules=$(jq -r '.reasons[].rule' <<< "$out" | tr '\n' ' ')
|
|
71
|
-
if echo "$rules" | grep -q 'file-count-high'; then
|
|
72
|
-
record_pass "file-count-high fires at 20 files"
|
|
73
|
-
else
|
|
74
|
-
record_fail "file-count-high should fire at 20 files (got rules: $rules)"
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
# --- 4: security-path ---
|
|
78
|
-
out=$(cat <<'JSON' | node "$CLS" -
|
|
79
|
-
{"tasks":[{"id":"T1","title":"add test","type":"test","files":["tests/keychain/helper.ts"]}],"approved":true}
|
|
80
|
-
JSON
|
|
81
|
-
)
|
|
82
|
-
if jq -e '.reasons[] | select(.rule=="security-path")' <<< "$out" >/dev/null; then
|
|
83
|
-
record_pass "security-path detects /keychain/"
|
|
84
|
-
else
|
|
85
|
-
record_fail "security-path should detect /keychain/"
|
|
86
|
-
fi
|
|
87
|
-
|
|
88
|
-
# --- 5: delete-without-test rule ---
|
|
89
|
-
out=$(cat <<'JSON' | node "$CLS" -
|
|
90
|
-
{"tasks":[
|
|
91
|
-
{"id":"T1","title":"Remove old feature flag handler","type":"refactor","files":["Sources/FeatureFlags/OldHandler.swift"]}
|
|
92
|
-
],"approved":true}
|
|
93
|
-
JSON
|
|
94
|
-
)
|
|
95
|
-
if jq -e '.reasons[] | select(.rule=="delete-without-test")' <<< "$out" >/dev/null; then
|
|
96
|
-
record_pass "delete-without-test fires when destructive task has no paired test task"
|
|
97
|
-
else
|
|
98
|
-
record_fail "delete-without-test should fire here"
|
|
99
|
-
fi
|
|
100
|
-
|
|
101
|
-
# --- delete WITH paired test should NOT fire ---
|
|
102
|
-
out=$(cat <<'JSON' | node "$CLS" -
|
|
103
|
-
{"tasks":[
|
|
104
|
-
{"id":"T1","title":"Add regression test for old flag removal","type":"test","files":["Sources/FeatureFlags/OldHandlerTests.swift"]},
|
|
105
|
-
{"id":"T2","title":"Remove old feature flag handler","type":"refactor","files":["Sources/FeatureFlags/OldHandler.swift"]}
|
|
106
|
-
],"approved":true}
|
|
107
|
-
JSON
|
|
108
|
-
)
|
|
109
|
-
if ! jq -e '.reasons[] | select(.rule=="delete-without-test")' <<< "$out" >/dev/null; then
|
|
110
|
-
record_pass "delete-without-test does NOT fire when paired test exists"
|
|
111
|
-
else
|
|
112
|
-
record_fail "delete-without-test should NOT fire when paired test exists"
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# --- 6: malformed input ---
|
|
116
|
-
set +e
|
|
117
|
-
echo 'not json' | node "$CLS" - >/dev/null 2>&1
|
|
118
|
-
rc=$?
|
|
119
|
-
set -e
|
|
120
|
-
if [ "$rc" = "1" ]; then
|
|
121
|
-
record_pass "exit 1 on malformed JSON"
|
|
122
|
-
else
|
|
123
|
-
record_fail "expected exit 1 on malformed JSON (got $rc)"
|
|
124
|
-
fi
|
|
125
|
-
|
|
126
|
-
# --- 7: schema pref ---
|
|
127
|
-
if jq -e '.properties.global.properties.autopilotSafetyGate.default == true' "$SCHEMA" >/dev/null 2>&1; then
|
|
128
|
-
record_pass "prefs schema exposes autopilotSafetyGate with default true"
|
|
129
|
-
else
|
|
130
|
-
record_fail "prefs schema missing autopilotSafetyGate or wrong default"
|
|
131
|
-
fi
|
|
132
|
-
|
|
133
|
-
printf '\n══ plan-safety smoke: %d passed, %d failed ══\n' "$pass" "$fail"
|
|
134
|
-
if [ "$fail" -gt 0 ]; then
|
|
135
|
-
printf '\nFailures:\n'
|
|
136
|
-
for m in "${failures[@]}"; do printf ' - %s\n' "$m"; done
|
|
137
|
-
exit 1
|
|
138
|
-
fi
|
|
139
|
-
exit 0
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# smoke-plan-todos.sh
|
|
3
|
-
#
|
|
4
|
-
# Verifies the Plan-as-Todo surface:
|
|
5
|
-
# 1. pipeline/lib/plan-todos.sh exists, parses cleanly
|
|
6
|
-
# 2. Helper accepts: init, set, start, complete, fail, skip, next, list, status, show
|
|
7
|
-
# 3. Unknown subcommand exits 64
|
|
8
|
-
# 4. Schema pipeline/schemas/plan-todos.schema.json valid + required fields
|
|
9
|
-
# 5. Lifecycle test on a temp state dir: set → next → start → complete → status
|
|
10
|
-
# 6. next() respects deps (t2 deps on t1; with t1 pending, next returns t1; with t1 in_progress, next is empty)
|
|
11
|
-
# 7. start() refuses when deps unsatisfied
|
|
12
|
-
# 8. list() output uses [x]/[~]/[ ] markers
|
|
13
|
-
# 9. prefs schema exposes planTodos.enabled (default false)
|
|
14
|
-
# 10. Phase 2 doc documents Step 4.5
|
|
15
|
-
# 11. Phase 3 doc documents plan-todos.sh iteration
|
|
16
|
-
# 12. Phase 7 doc references Plan Todo Rollup
|
|
17
|
-
#
|
|
18
|
-
# Exit 0 = all pass, 1 = any failure.
|
|
19
|
-
|
|
20
|
-
set -uo pipefail
|
|
21
|
-
|
|
22
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
23
|
-
HELPER="$ROOT/pipeline/lib/plan-todos.sh"
|
|
24
|
-
SCHEMA="$ROOT/pipeline/schemas/plan-todos.schema.json"
|
|
25
|
-
PREFS="$ROOT/pipeline/schemas/prefs.schema.json"
|
|
26
|
-
P2="$ROOT/pipeline/multi-agent-refs/phases/phase-2-planning.md"
|
|
27
|
-
P3="$ROOT/pipeline/multi-agent-refs/phases/phase-3-dev.md"
|
|
28
|
-
P7="$ROOT/pipeline/multi-agent-refs/phases/phase-7-report.md"
|
|
29
|
-
|
|
30
|
-
pass=0
|
|
31
|
-
fail=0
|
|
32
|
-
failures=()
|
|
33
|
-
record_pass() { pass=$((pass + 1)); printf ' \033[0;32mPASS\033[0m %s\n' "$1"; }
|
|
34
|
-
record_fail() { fail=$((fail + 1)); failures+=("$1"); printf ' \033[0;31mFAIL\033[0m %s\n' "$1"; }
|
|
35
|
-
|
|
36
|
-
printf '→ smoke-plan-todos: Plan-as-Todo live list contract\n'
|
|
37
|
-
|
|
38
|
-
# 1. Helper exists + parses
|
|
39
|
-
if [ ! -f "$HELPER" ]; then
|
|
40
|
-
record_fail "pipeline/lib/plan-todos.sh missing"
|
|
41
|
-
elif ! bash -n "$HELPER" 2>/dev/null; then
|
|
42
|
-
record_fail "plan-todos.sh syntax error"
|
|
43
|
-
else
|
|
44
|
-
record_pass "plan-todos.sh exists and parses"
|
|
45
|
-
fi
|
|
46
|
-
|
|
47
|
-
# 2. Subcommands recognized (each prints either usage or runs)
|
|
48
|
-
SUBS=(init set start complete fail skip next list status show)
|
|
49
|
-
for s in "${SUBS[@]}"; do
|
|
50
|
-
if bash "$HELPER" "$s" --probe-noop 2>/dev/null >/dev/null; then
|
|
51
|
-
record_pass "subcommand '$s' accepted (probe path)"
|
|
52
|
-
elif bash "$HELPER" "$s" 2>&1 | grep -qE "usage: $s"; then
|
|
53
|
-
record_pass "subcommand '$s' prints usage"
|
|
54
|
-
else
|
|
55
|
-
# Some subcommands fail without args via exit 64 - that also confirms they're known
|
|
56
|
-
bash "$HELPER" "$s" 2>/dev/null
|
|
57
|
-
rc=$?
|
|
58
|
-
if [ "$rc" = "64" ] || [ "$rc" = "1" ]; then
|
|
59
|
-
record_pass "subcommand '$s' recognized"
|
|
60
|
-
else
|
|
61
|
-
record_fail "subcommand '$s' not recognized (exit $rc)"
|
|
62
|
-
fi
|
|
63
|
-
fi
|
|
64
|
-
done
|
|
65
|
-
|
|
66
|
-
# 3. Unknown subcommand exits 64
|
|
67
|
-
if bash "$HELPER" garbage 2>/dev/null >/dev/null; then
|
|
68
|
-
record_fail "unknown subcommand should exit non-zero"
|
|
69
|
-
else
|
|
70
|
-
record_pass "unknown subcommand rejected"
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
|
-
# 4. Schema valid
|
|
74
|
-
if [ ! -f "$SCHEMA" ]; then
|
|
75
|
-
record_fail "plan-todos.schema.json missing"
|
|
76
|
-
elif ! jq empty "$SCHEMA" 2>/dev/null; then
|
|
77
|
-
record_fail "plan-todos.schema.json is not valid JSON"
|
|
78
|
-
else
|
|
79
|
-
record_pass "plan-todos.schema.json parses"
|
|
80
|
-
for prop in title todos; do
|
|
81
|
-
if jq -e ".properties.${prop}" "$SCHEMA" >/dev/null 2>&1; then
|
|
82
|
-
record_pass "schema requires '${prop}'"
|
|
83
|
-
else
|
|
84
|
-
record_fail "schema missing '${prop}'"
|
|
85
|
-
fi
|
|
86
|
-
done
|
|
87
|
-
if jq -e '.properties.todos.items.properties.status.enum
|
|
88
|
-
| (index("pending") != null and index("in_progress") != null
|
|
89
|
-
and index("completed") != null and index("skipped") != null
|
|
90
|
-
and index("failed") != null)' "$SCHEMA" >/dev/null 2>&1; then
|
|
91
|
-
record_pass "schema enforces 5-state status enum"
|
|
92
|
-
else
|
|
93
|
-
record_fail "schema status enum incomplete"
|
|
94
|
-
fi
|
|
95
|
-
fi
|
|
96
|
-
|
|
97
|
-
# 5-8. Lifecycle test against a temp state dir
|
|
98
|
-
# Use a temp directory derived from $HOME so the path stays cross-user.
|
|
99
|
-
TEST_TASK="SMOKE-PT-$$"
|
|
100
|
-
TEST_PROJECT_KEY=$(printf '%s' "$HOME/test-plan-todos-smoke" | tr '/' '-')
|
|
101
|
-
TEST_PROJECT_ROOT="$HOME/.claude/projects/$TEST_PROJECT_KEY"
|
|
102
|
-
TEST_DIR="$TEST_PROJECT_ROOT/state/$TEST_TASK"
|
|
103
|
-
mkdir -p "$TEST_DIR"
|
|
104
|
-
echo '{"task":{"id":"'"$TEST_TASK"'"}}' > "$TEST_DIR/agent-state.json"
|
|
105
|
-
PLAN='{"title":"Smoke","todos":[
|
|
106
|
-
{"id":"t1","task":"first","status":"pending","deps":[]},
|
|
107
|
-
{"id":"t2","task":"second","status":"pending","deps":["t1"]}
|
|
108
|
-
]}'
|
|
109
|
-
if bash "$HELPER" set "$TEST_TASK" "$PLAN" 2>/dev/null; then
|
|
110
|
-
record_pass "set writes plan blob"
|
|
111
|
-
else
|
|
112
|
-
record_fail "set failed on fixture"
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
next1=$(bash "$HELPER" next "$TEST_TASK" 2>/dev/null | cut -f1)
|
|
116
|
-
if [ "$next1" = "t1" ]; then
|
|
117
|
-
record_pass "next returns t1 (deps empty)"
|
|
118
|
-
else
|
|
119
|
-
record_fail "next should return t1, got '$next1'"
|
|
120
|
-
fi
|
|
121
|
-
|
|
122
|
-
bash "$HELPER" start "$TEST_TASK" t1 2>/dev/null
|
|
123
|
-
|
|
124
|
-
next2=$(bash "$HELPER" next "$TEST_TASK" 2>/dev/null | cut -f1)
|
|
125
|
-
if [ -z "$next2" ]; then
|
|
126
|
-
record_pass "next is empty while t1 is in_progress (deps not yet satisfied for t2)"
|
|
127
|
-
else
|
|
128
|
-
record_fail "next should be empty during t1 in_progress, got '$next2'"
|
|
129
|
-
fi
|
|
130
|
-
|
|
131
|
-
# start t2 should refuse (deps not satisfied)
|
|
132
|
-
if bash "$HELPER" start "$TEST_TASK" t2 2>/dev/null; then
|
|
133
|
-
record_fail "start should refuse t2 while t1 not completed"
|
|
134
|
-
else
|
|
135
|
-
record_pass "start refuses t2 when t1 dep unsatisfied"
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
bash "$HELPER" complete "$TEST_TASK" t1 "smoke note" 2>/dev/null
|
|
139
|
-
|
|
140
|
-
next3=$(bash "$HELPER" next "$TEST_TASK" 2>/dev/null | cut -f1)
|
|
141
|
-
if [ "$next3" = "t2" ]; then
|
|
142
|
-
record_pass "next returns t2 after t1 completed"
|
|
143
|
-
else
|
|
144
|
-
record_fail "next should return t2 after t1 completed, got '$next3'"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
LIST_OUT=$(bash "$HELPER" list "$TEST_TASK" 2>/dev/null)
|
|
148
|
-
if echo "$LIST_OUT" | grep -qE '\[x\].*t1' \
|
|
149
|
-
&& echo "$LIST_OUT" | grep -qE '\[ \].*t2'; then
|
|
150
|
-
record_pass "list renders [x] for completed and [ ] for pending"
|
|
151
|
-
else
|
|
152
|
-
record_fail "list markdown markers incorrect"
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
STATUS_OUT=$(bash "$HELPER" status "$TEST_TASK" 2>/dev/null)
|
|
156
|
-
if echo "$STATUS_OUT" | grep -qE '1/2 done'; then
|
|
157
|
-
record_pass "status reports '1/2 done'"
|
|
158
|
-
else
|
|
159
|
-
record_fail "status should report '1/2 done', got '$STATUS_OUT'"
|
|
160
|
-
fi
|
|
161
|
-
|
|
162
|
-
# Cleanup the test state
|
|
163
|
-
rm -rf "$TEST_PROJECT_ROOT"
|
|
164
|
-
|
|
165
|
-
# 9. Prefs schema toggle
|
|
166
|
-
if jq -e '.properties.global.properties.planTodos.properties.enabled
|
|
167
|
-
| has("default") and .default == false' "$PREFS" >/dev/null 2>&1; then
|
|
168
|
-
record_pass "prefs schema exposes planTodos.enabled (default false)"
|
|
169
|
-
else
|
|
170
|
-
record_fail "prefs schema missing planTodos.enabled or wrong default"
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
# 10-12. Phase docs wiring
|
|
174
|
-
if grep -qE 'Step 4\.5 - Emit Plan Todo List' "$P2"; then
|
|
175
|
-
record_pass "phase-2-planning.md documents Step 4.5"
|
|
176
|
-
else
|
|
177
|
-
record_fail "phase-2-planning.md missing Step 4.5"
|
|
178
|
-
fi
|
|
179
|
-
if grep -qE 'plan-todos\.sh"? next' "$P3"; then
|
|
180
|
-
record_pass "phase-3-dev.md documents plan-todos.sh iteration"
|
|
181
|
-
else
|
|
182
|
-
record_fail "phase-3-dev.md missing plan-todos.sh wiring"
|
|
183
|
-
fi
|
|
184
|
-
if grep -qE 'Plan Todo Rollup' "$P7"; then
|
|
185
|
-
record_pass "phase-7-report.md references Plan Todo Rollup"
|
|
186
|
-
else
|
|
187
|
-
record_fail "phase-7-report.md missing Plan Todo Rollup section"
|
|
188
|
-
fi
|
|
189
|
-
|
|
190
|
-
printf '\n══ plan-todos smoke: %d passed, %d failed ══\n' "$pass" "$fail"
|
|
191
|
-
if [ "$fail" -gt 0 ]; then
|
|
192
|
-
printf '\nFailures:\n'
|
|
193
|
-
for msg in "${failures[@]}"; do printf ' - %s\n' "$msg"; done
|
|
194
|
-
exit 1
|
|
195
|
-
fi
|
|
196
|
-
exit 0
|