@mmerterden/multi-agent-pipeline 12.5.0 → 12.7.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 +213 -0
- package/README.md +19 -19
- package/docs/FIGMA_PIPELINE.md +34 -34
- package/docs/adr/0001-three-model-triage.md +12 -12
- package/docs/adr/0002-instruction-driven-flag.md +5 -5
- package/docs/adr/0003-unified-shared-skills.md +5 -5
- package/docs/adr/0004-zero-dependency-philosophy.md +5 -5
- package/docs/adr/0005-lazy-phase-docs.md +2 -2
- package/docs/adr/0006-skills-core-external-split.md +6 -6
- package/docs/adr/0007-multi-tool-adapter-framework.md +19 -19
- package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +19 -19
- package/docs/adr/README.md +1 -1
- package/docs/best-practices.md +3 -3
- package/docs/features.md +48 -28
- package/docs/performance.md +16 -16
- package/docs/recovery-guide.md +39 -39
- package/index.js +11 -5
- package/install/_common.mjs +5 -11
- package/install/_copilot-instructions.mjs +2 -2
- package/install/_dev-only-files.mjs +2 -1
- package/install/_platform-filter.mjs +1 -1
- package/install/_telemetry.mjs +1 -1
- package/install/claude.mjs +10 -9
- package/install/copilot.mjs +10 -19
- package/install/index.mjs +7 -15
- package/install/templates/copilot-instructions.md +54 -54
- package/install.js +1 -1
- package/package.json +15 -11
- package/pipeline/agents/security-auditor.md +1 -1
- package/pipeline/commands/archive-guard.md +5 -5
- package/pipeline/commands/multi-agent/SKILL.md +3 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/build-optimize/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/create-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +287 -0
- package/pipeline/commands/multi-agent/dev/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/finish/SKILL.md +6 -6
- package/pipeline/commands/multi-agent/forget/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/help/SKILL.md +46 -6
- package/pipeline/commands/multi-agent/issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/kill/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/language/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/local-autopilot/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/log/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/purge/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/refactor/SKILL.md +107 -19
- package/pipeline/commands/multi-agent/resume/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/review-issue/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/review-jira/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/routines/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/save/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/scan/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/search/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/setup/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +3 -3
- package/pipeline/commands/multi-agent/status/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/sync/SKILL.md +123 -16
- package/pipeline/commands/multi-agent/test/SKILL.md +2 -2
- package/pipeline/commands/multi-agent/uninstall/SKILL.md +1 -1
- package/pipeline/commands/multi-agent/update/SKILL.md +3 -3
- package/pipeline/commands/sim-test.md +5 -5
- package/pipeline/eval/run-metrics-fixture.json +60 -13
- package/pipeline/lib/account-resolver.sh +1 -1
- package/pipeline/lib/channels-multi-repo.sh +1 -1
- package/pipeline/lib/context-link-extractor.sh +1 -1
- package/pipeline/lib/credential-store.sh +33 -1
- package/pipeline/lib/fetch-confluence.sh +1 -1
- package/pipeline/lib/fetch-crashlytics.sh +1 -1
- package/pipeline/lib/fetch-fortify.sh +1 -1
- package/pipeline/lib/fetch-graylog.sh +1 -1
- package/pipeline/lib/fetch-swagger.sh +1 -1
- package/pipeline/lib/issue-fetcher.sh +1 -1
- package/pipeline/lib/multi-repo-pipeline.sh +1 -1
- package/pipeline/lib/repo-cache.sh +1 -1
- package/pipeline/lib/submodule-detector.sh +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/component-generation.md +121 -0
- package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
- package/pipeline/multi-agent-refs/features/model-fallback.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-3-dev.md +2 -2
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -1
- package/pipeline/multi-agent-refs/phases/phase-5-test.md +4 -4
- package/pipeline/preferences-template.json +23 -12
- package/pipeline/schemas/agent-state.schema.json +125 -5
- package/pipeline/schemas/analysis-output.schema.json +18 -4
- package/pipeline/schemas/analysis-spec.schema.json +120 -32
- package/pipeline/schemas/clarify-output.schema.json +15 -5
- package/pipeline/schemas/design-check-config.schema.json +183 -0
- package/pipeline/schemas/dev-critic-output.schema.json +20 -5
- package/pipeline/schemas/figma-project-config.schema.json +42 -10
- package/pipeline/schemas/learnings-ledger.schema.json +10 -2
- package/pipeline/schemas/migrations/figma-config-1.0.0-to-2.0.0.mjs +1 -4
- package/pipeline/schemas/migrations/prefs-2.0.0-to-2.1.0.mjs +24 -7
- package/pipeline/schemas/migrations/state-2.0.0-to-2.1.0.mjs +30 -12
- package/pipeline/schemas/plan-todos.schema.json +6 -3
- package/pipeline/schemas/planning-output.schema.json +5 -1
- package/pipeline/schemas/prefs.schema.json +215 -197
- package/pipeline/schemas/test-gap.schema.json +5 -5
- package/pipeline/schemas/token-budget.json +8 -8
- package/pipeline/schemas/triage-corpus.schema.json +1 -1
- package/pipeline/scripts/README.md +7 -5
- package/pipeline/scripts/aggregate-metrics.mjs +18 -6
- package/pipeline/scripts/build-skills-index.mjs +6 -2
- package/pipeline/scripts/build-stack-plugins.mjs +142 -39
- package/pipeline/scripts/check-derived-drift.mjs +196 -0
- package/pipeline/scripts/check-md-links.mjs +6 -2
- package/pipeline/scripts/classify-plan-safety.mjs +26 -8
- package/pipeline/scripts/cost-budget-check.mjs +11 -6
- package/pipeline/scripts/cost-table.json +1 -1
- package/pipeline/scripts/diff-explain.mjs +7 -3
- package/pipeline/scripts/diff-risk-score.mjs +13 -3
- package/pipeline/scripts/eval-golden-tasks-live.mjs +8 -3
- package/pipeline/scripts/eval-golden-tasks.mjs +21 -9
- package/pipeline/scripts/eval-intent.mjs +8 -4
- package/pipeline/scripts/eval-mine-corpus.mjs +14 -4
- package/pipeline/scripts/evidence-gate.mjs +7 -2
- package/pipeline/scripts/fixtures/install-layout.tsv +7 -7
- package/pipeline/scripts/gen-mode-dispatch.mjs +38 -21
- package/pipeline/scripts/gen-skills-index.mjs +18 -3
- package/pipeline/scripts/learning-curve.mjs +13 -3
- package/pipeline/scripts/learnings-ledger.mjs +119 -34
- package/pipeline/scripts/lint-mcp-refs.mjs +218 -0
- package/pipeline/scripts/lint-skills.mjs +20 -9
- package/pipeline/scripts/localize-commands.mjs +6 -1
- package/pipeline/scripts/match-skills.mjs +15 -4
- package/pipeline/scripts/memory-load.sh +5 -1
- package/pipeline/scripts/migrate-prefs.mjs +33 -16
- package/pipeline/scripts/phase-tracker.sh +3 -1
- package/pipeline/scripts/render-work-summary.sh +4 -1
- package/pipeline/scripts/repo-map.mjs +110 -64
- package/pipeline/scripts/review-scope.mjs +7 -1
- package/pipeline/scripts/routine-registry.mjs +4 -9
- package/pipeline/scripts/run-aggregator.mjs +11 -5
- package/pipeline/scripts/run-metrics.mjs +13 -8
- package/pipeline/scripts/run-smokes.mjs +57 -3
- package/pipeline/scripts/scorecard.mjs +258 -0
- package/pipeline/scripts/smoke-command-inventory.sh +81 -0
- package/pipeline/scripts/smoke-commands-skills-parity.sh +1 -1
- package/pipeline/scripts/smoke-compliance-skills.sh +4 -4
- package/pipeline/scripts/smoke-context-budget.sh +72 -0
- package/pipeline/scripts/smoke-cross-cli-behavior.sh +12 -2
- package/pipeline/scripts/smoke-generate-issue.sh +6 -5
- package/pipeline/scripts/smoke-model-fallback.sh +1 -1
- package/pipeline/scripts/smoke-no-mcp-in-dev-phases.sh +86 -7
- package/pipeline/scripts/smoke-own-punctuation.sh +103 -0
- package/pipeline/scripts/smoke-per-repo-memory.sh +2 -2
- package/pipeline/scripts/smoke-review-readiness.sh +3 -2
- package/pipeline/scripts/smoke-schema-validation.sh +19 -5
- package/pipeline/scripts/smoke-shadow-git.sh +4 -2
- package/pipeline/scripts/smoke-workflow-audit.sh +43 -11
- package/pipeline/scripts/smoke-write-state.sh +49 -5
- package/pipeline/scripts/test-gap-rules/android.json +11 -11
- package/pipeline/scripts/test-gap-rules/ios.json +16 -11
- package/pipeline/scripts/test-gap-rules/node.json +19 -7
- package/pipeline/scripts/test-gap-rules/python.json +10 -4
- package/pipeline/scripts/test-gap-scan.mjs +44 -12
- package/pipeline/scripts/test-integrity-gate.mjs +5 -1
- package/pipeline/scripts/token-budget-report.mjs +44 -21
- package/pipeline/scripts/triage-memory.mjs +142 -32
- package/pipeline/scripts/uninstall.mjs +75 -31
- package/pipeline/scripts/validate-analysis-doc.mjs +15 -5
- package/pipeline/scripts/validate-diff-risk.mjs +32 -18
- package/pipeline/scripts/validate-test-gap.mjs +17 -7
- package/pipeline/scripts/validate-triage.mjs +17 -5
- package/pipeline/scripts/write-state.mjs +32 -9
- package/pipeline/skills/.skill-manifest.json +24 -8
- package/pipeline/skills/.skills-index.json +125 -89
- package/pipeline/skills/shared/README.md +65 -61
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +10 -8
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +26 -279
- package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +248 -0
- package/pipeline/skills/shared/core/multi-agent-dev/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-dev-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +23 -1
- package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-local-autopilot/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +82 -18
- package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +51 -9
- package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -2
- package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +1 -1
- package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +1 -1
- package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -4
- package/pipeline/skills/shared/external/agentflow/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/android_ui_verification/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +1 -1
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +12 -12
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -1
- package/pipeline/skills/shared/external/context-compression/SKILL.md +1 -1
- package/pipeline/skills/shared/external/council/SKILL.md +3 -3
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -1
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/docker-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +1 -1
- package/pipeline/skills/shared/external/firebase/SKILL.md +1 -1
- package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +1 -1
- package/pipeline/skills/shared/external/help-skills/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-content/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-status/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-components-system/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-foundations/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-inputs/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-platforms/SKILL.md +1 -1
- package/pipeline/skills/shared/external/hig-technologies/SKILL.md +1 -1
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -1
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +1 -1
- package/pipeline/skills/shared/external/ios-developer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +1 -1
- package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +1 -1
- package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -1
- package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/observability-engineer/SKILL.md +1 -1
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -1
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -1
- package/pipeline/skills/shared/external/search-first/SKILL.md +2 -2
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +12 -12
- package/pipeline/skills/shared/external/skill-creator/audit.md +21 -21
- package/pipeline/skills/shared/external/skill-creator/checklist.md +3 -3
- package/pipeline/skills/shared/external/skill-creator/examples.md +10 -10
- package/pipeline/skills/shared/external/skill-creator/label-check.md +17 -17
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +86 -50
- package/pipeline/skills/shared/external/skill-creator/template.md +9 -9
- package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +1 -1
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -1
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -1
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -1
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -1
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -1
- package/pipeline/skills/shared/external/xcode-build-benchmark/schemas/build-benchmark.schema.json +9 -49
- package/pipeline/skills/skills-index.md +63 -59
- package/pipeline/scripts/smoke-plugin-validate.sh +0 -64
|
@@ -7,20 +7,30 @@
|
|
|
7
7
|
# Phase 1; any entry in state.telemetry.mcpCalls[] with phase >= 2 is a
|
|
8
8
|
# violation.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
# --state-file <path> Optional override (default: ~/.claude/state/run/latest.json)
|
|
10
|
+
# Two modes.
|
|
12
11
|
#
|
|
13
|
-
#
|
|
14
|
-
# - State file missing -> WARN, exit 0 (no run to check)
|
|
12
|
+
# CHECK MODE (--state-file <path>): audit that one state file.
|
|
15
13
|
# - telemetry.mcpCalls field absent -> PASS, exit 0 (older run, no telemetry)
|
|
16
14
|
# - All entries phase < 2 -> PASS, exit 0
|
|
17
15
|
# - One or more violations -> FAIL summary on stdout, exit 1
|
|
16
|
+
# - Missing / corrupt file -> exit 2
|
|
17
|
+
#
|
|
18
|
+
# SELF-TEST MODE (no arguments): drive check mode over fixtures that cover
|
|
19
|
+
# every verdict, then audit the live state file if one happens to exist.
|
|
20
|
+
#
|
|
21
|
+
# Why: with no --state-file this script used to print "no run to check" and
|
|
22
|
+
# exit 0. On a clean clone, on CI, and on any machine that had not just
|
|
23
|
+
# finished a pipeline run - which is nearly always - the gate for Locked
|
|
24
|
+
# decision 30 therefore asserted nothing while reporting success. A gate whose
|
|
25
|
+
# green tick means "I had nothing to look at" is indistinguishable from one
|
|
26
|
+
# that passed, and this suite had three of those.
|
|
18
27
|
#
|
|
19
28
|
# stdout: human-readable summary only. stderr: empty in normal operation.
|
|
20
29
|
|
|
21
30
|
set -euo pipefail
|
|
22
31
|
|
|
23
32
|
STATE_FILE="${HOME}/.claude/state/run/latest.json"
|
|
33
|
+
STATE_FILE_EXPLICIT=0
|
|
24
34
|
|
|
25
35
|
while [ "$#" -gt 0 ]; do
|
|
26
36
|
case "$1" in
|
|
@@ -31,16 +41,19 @@ while [ "$#" -gt 0 ]; do
|
|
|
31
41
|
exit 2
|
|
32
42
|
fi
|
|
33
43
|
STATE_FILE="$1"
|
|
44
|
+
STATE_FILE_EXPLICIT=1
|
|
34
45
|
shift
|
|
35
46
|
;;
|
|
36
47
|
--state-file=*)
|
|
37
48
|
STATE_FILE="${1#--state-file=}"
|
|
49
|
+
STATE_FILE_EXPLICIT=1
|
|
38
50
|
shift
|
|
39
51
|
;;
|
|
40
52
|
-h|--help)
|
|
41
53
|
echo "Usage: $0 [--state-file <path>]"
|
|
42
54
|
echo ""
|
|
43
|
-
echo "
|
|
55
|
+
echo "No arguments: run the fixture self-test suite."
|
|
56
|
+
echo "Default state file when checking live: ~/.claude/state/run/latest.json"
|
|
44
57
|
exit 0
|
|
45
58
|
;;
|
|
46
59
|
*)
|
|
@@ -55,9 +68,75 @@ if ! command -v jq >/dev/null 2>&1; then
|
|
|
55
68
|
exit 2
|
|
56
69
|
fi
|
|
57
70
|
|
|
71
|
+
# ── self-test mode ─────────────────────────────────────────────────────────
|
|
72
|
+
if [ "$STATE_FILE_EXPLICIT" -eq 0 ]; then
|
|
73
|
+
SELF="$0"
|
|
74
|
+
TMP=$(mktemp -d)
|
|
75
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
76
|
+
PASS=0
|
|
77
|
+
FAIL=0
|
|
78
|
+
|
|
79
|
+
expect() {
|
|
80
|
+
local name="$1" want="$2" file="$3"
|
|
81
|
+
local got=0
|
|
82
|
+
bash "$SELF" --state-file "$file" >/dev/null 2>&1 || got=$?
|
|
83
|
+
if [ "$got" -eq "$want" ]; then
|
|
84
|
+
PASS=$((PASS + 1)); echo " ✓ $name (exit $got)"
|
|
85
|
+
else
|
|
86
|
+
FAIL=$((FAIL + 1)); echo " ✗ $name (exit $got, expected $want)"
|
|
87
|
+
fi
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
echo "→ Locked decision 30 gate, fixture self-test"
|
|
91
|
+
|
|
92
|
+
printf '%s' '{"telemetry":{"mcpCalls":[{"tool":"mcp__figma__get_design_context","phase":1}]}}' \
|
|
93
|
+
> "$TMP/phase1-only.json"
|
|
94
|
+
expect "MCP call in Phase 1 passes" 0 "$TMP/phase1-only.json"
|
|
95
|
+
|
|
96
|
+
printf '%s' '{"telemetry":{"mcpCalls":[{"tool":"mcp__figma__get_design_context","phase":3}]}}' \
|
|
97
|
+
> "$TMP/phase3.json"
|
|
98
|
+
expect "MCP call in Phase 3 is a violation" 1 "$TMP/phase3.json"
|
|
99
|
+
|
|
100
|
+
printf '%s' '{"telemetry":{"mcpCalls":[{"tool":"a","phase":1},{"tool":"b","phase":2}]}}' \
|
|
101
|
+
> "$TMP/mixed.json"
|
|
102
|
+
expect "one violation among clean entries still fails" 1 "$TMP/mixed.json"
|
|
103
|
+
|
|
104
|
+
printf '%s' '{"telemetry":{"mcpCalls":[]}}' > "$TMP/empty.json"
|
|
105
|
+
expect "empty call list passes" 0 "$TMP/empty.json"
|
|
106
|
+
|
|
107
|
+
printf '%s' '{"telemetry":{}}' > "$TMP/no-field.json"
|
|
108
|
+
expect "absent telemetry.mcpCalls passes (older state format)" 0 "$TMP/no-field.json"
|
|
109
|
+
|
|
110
|
+
printf '%s' '{"telemetry":{"mcpCalls":[{"tool":"a"}]}}' > "$TMP/no-phase.json"
|
|
111
|
+
expect "entry with no phase defaults to Phase 0 and passes" 0 "$TMP/no-phase.json"
|
|
112
|
+
|
|
113
|
+
printf '%s' 'not json at all' > "$TMP/corrupt.json"
|
|
114
|
+
expect "corrupt state is an error, never a silent pass" 2 "$TMP/corrupt.json"
|
|
115
|
+
|
|
116
|
+
expect "missing state file is an error in check mode" 2 "$TMP/does-not-exist.json"
|
|
117
|
+
|
|
118
|
+
# The live run, when there is one. Absence is not a failure here - the
|
|
119
|
+
# fixtures above already proved the gate works.
|
|
120
|
+
if [ -f "$STATE_FILE" ]; then
|
|
121
|
+
echo "→ live state file"
|
|
122
|
+
if bash "$SELF" --state-file "$STATE_FILE"; then
|
|
123
|
+
PASS=$((PASS + 1)); echo " ✓ live run clean"
|
|
124
|
+
else
|
|
125
|
+
FAIL=$((FAIL + 1)); echo " ✗ live run has violations"
|
|
126
|
+
fi
|
|
127
|
+
else
|
|
128
|
+
echo "→ live state file: none at $STATE_FILE (fixtures already exercised every path)"
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
echo ""
|
|
132
|
+
echo "══ smoke-no-mcp-in-dev-phases: $PASS passed, $FAIL failed ══"
|
|
133
|
+
[ "$FAIL" -gt 0 ] && exit 1 || exit 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# ── check mode ─────────────────────────────────────────────────────────────
|
|
58
137
|
if [ ! -f "$STATE_FILE" ]; then
|
|
59
|
-
echo "
|
|
60
|
-
exit
|
|
138
|
+
echo "FAIL: state file not found at $STATE_FILE"
|
|
139
|
+
exit 2
|
|
61
140
|
fi
|
|
62
141
|
|
|
63
142
|
# Validate JSON parse first; corrupt state cannot be silently passed.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# smoke-own-punctuation.sh - the punctuation rule applies to this repo's own
|
|
4
|
+
# shipped files, not only to the documents it generates.
|
|
5
|
+
#
|
|
6
|
+
# validate-analysis-doc.mjs has enforced the locked policy (no em-dash, en-dash,
|
|
7
|
+
# ellipsis or section sign) on generated analysis documents for a long time,
|
|
8
|
+
# and smoke-generate-issue.sh / smoke-review-readiness.sh enforce it on issue
|
|
9
|
+
# and review payloads. Nothing applied it to the pipeline's own tree, so 77
|
|
10
|
+
# shipped files carried 719 em-dashes, 10 en-dashes and 24 ellipses while the
|
|
11
|
+
# rule was described as enforced.
|
|
12
|
+
#
|
|
13
|
+
# ALLOWED holds the files that carry these characters as data rather than
|
|
14
|
+
# prose: the detector's own table and the grep patterns that look for them.
|
|
15
|
+
# Stripping those would disable the very checks this rule depends on. Every
|
|
16
|
+
# entry needs a reason, and the list is expected to stay this short.
|
|
17
|
+
#
|
|
18
|
+
# Exit codes: 0=clean, 1=violations
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
23
|
+
cd "$REPO_ROOT"
|
|
24
|
+
|
|
25
|
+
# file path -> why the characters are legitimately present
|
|
26
|
+
ALLOWED_PATHS="
|
|
27
|
+
pipeline/scripts/validate-analysis-doc.mjs|the detector's own table of banned characters
|
|
28
|
+
pipeline/scripts/smoke-generate-issue.sh|greps for the banned characters
|
|
29
|
+
pipeline/scripts/smoke-review-readiness.sh|greps for the banned characters
|
|
30
|
+
pipeline/scripts/smoke-md2confluence.sh|dirty-input fixture plus the forbidden list
|
|
31
|
+
pipeline/scripts/smoke-own-punctuation.sh|this file names the characters it bans
|
|
32
|
+
pipeline/lib/md2confluence-v3.py|the converter's codepoint-to-ASCII mapping table
|
|
33
|
+
"
|
|
34
|
+
|
|
35
|
+
# The same characters written as escapes. prefs.schema.json carried 50 of them
|
|
36
|
+
# in its descriptions and was invisible to a search for the literal character,
|
|
37
|
+
# so the first version of this gate passed it. A rule that only sees one
|
|
38
|
+
# encoding of what it bans is not enforcing the rule.
|
|
39
|
+
ESCAPED='\\u2014|\\u2013|\\u2026|\\u00a7'
|
|
40
|
+
|
|
41
|
+
is_allowed() {
|
|
42
|
+
printf '%s\n' "$ALLOWED_PATHS" | grep -q "^$1|"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
PASS=0
|
|
46
|
+
FAIL=0
|
|
47
|
+
pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
|
|
48
|
+
fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
|
|
49
|
+
|
|
50
|
+
echo "→ 1. no em-dash, en-dash, ellipsis or section sign in shipped files"
|
|
51
|
+
|
|
52
|
+
# CHANGELOG history is left as written; rewriting released notes to satisfy a
|
|
53
|
+
# later style rule would falsify the record.
|
|
54
|
+
VIOLATIONS=""
|
|
55
|
+
while IFS= read -r f; do
|
|
56
|
+
case "$f" in CHANGELOG*.md) continue ;; esac
|
|
57
|
+
is_allowed "$f" && continue
|
|
58
|
+
VIOLATIONS="${VIOLATIONS}${f}\n"
|
|
59
|
+
done < <(git grep -lI -e $'—' -e $'–' -e $'…' -e $'§' -- . 2>/dev/null || true)
|
|
60
|
+
|
|
61
|
+
if [ -z "$VIOLATIONS" ]; then
|
|
62
|
+
pass "no banned punctuation outside the allowlist"
|
|
63
|
+
else
|
|
64
|
+
COUNT=$(printf '%b' "$VIOLATIONS" | grep -c . || true)
|
|
65
|
+
fail "$COUNT file(s) carry banned punctuation"
|
|
66
|
+
printf '%b' "$VIOLATIONS" | sed 's/^/ /'
|
|
67
|
+
echo " fix: plain ASCII - or : or parentheses; 'section 4' rather than the sign"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo "→ 2. same characters written as unicode escapes"
|
|
71
|
+
ESC_VIOLATIONS=""
|
|
72
|
+
while IFS= read -r f; do
|
|
73
|
+
case "$f" in CHANGELOG*.md) continue ;; esac
|
|
74
|
+
is_allowed "$f" && continue
|
|
75
|
+
ESC_VIOLATIONS="${ESC_VIOLATIONS}${f}\n"
|
|
76
|
+
done < <(git grep -lI -E "$ESCAPED" -- . 2>/dev/null || true)
|
|
77
|
+
|
|
78
|
+
if [ -z "$ESC_VIOLATIONS" ]; then
|
|
79
|
+
pass "no escaped forms outside the allowlist"
|
|
80
|
+
else
|
|
81
|
+
COUNT=$(printf '%b' "$ESC_VIOLATIONS" | grep -c . || true)
|
|
82
|
+
fail "$COUNT file(s) carry the banned characters as unicode escapes"
|
|
83
|
+
printf '%b' "$ESC_VIOLATIONS" | sed 's/^/ /'
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
echo "→ 3. every allowlist entry still exists and still needs the exemption"
|
|
87
|
+
while IFS='|' read -r path reason; do
|
|
88
|
+
[ -z "$path" ] && continue
|
|
89
|
+
if [ ! -f "$path" ]; then
|
|
90
|
+
fail "allowlisted path no longer exists: $path"
|
|
91
|
+
elif ! grep -qI -e $'—' -e $'–' -e $'…' -e $'§' "$path" 2>/dev/null \
|
|
92
|
+
&& ! grep -qIE "$ESCAPED" "$path" 2>/dev/null; then
|
|
93
|
+
# Either encoding justifies the entry: md2confluence-v3.py holds them as
|
|
94
|
+
# escapes, the detector table holds them literally.
|
|
95
|
+
fail "$path is allowlisted ($reason) but is now clean - drop it from the list"
|
|
96
|
+
else
|
|
97
|
+
pass "allowlist justified: ${path##*/} ($reason)"
|
|
98
|
+
fi
|
|
99
|
+
done < <(printf '%s\n' "$ALLOWED_PATHS" | grep '|')
|
|
100
|
+
|
|
101
|
+
echo ""
|
|
102
|
+
echo "══ smoke-own-punctuation: $PASS passed, $FAIL failed ══"
|
|
103
|
+
[ "$FAIL" -gt 0 ] && exit 1 || exit 0
|
|
@@ -35,7 +35,7 @@ repo="$sandbox/repo"
|
|
|
35
35
|
mkdir -p "$repo"
|
|
36
36
|
|
|
37
37
|
# --- 1: pref off → silent exit 0 ---
|
|
38
|
-
printf '{"global":{"perRepoMemory":false}}' > "$HOME/.claude/preferences.json"
|
|
38
|
+
printf '{"global":{"perRepoMemory":false}}' > "$HOME/.claude/multi-agent-preferences.json"
|
|
39
39
|
out=$(bash "$LOAD" "$repo" 2>&1 || true)
|
|
40
40
|
if [ -z "$out" ]; then
|
|
41
41
|
record_pass "load exits silent when pref off"
|
|
@@ -44,7 +44,7 @@ else
|
|
|
44
44
|
fi
|
|
45
45
|
|
|
46
46
|
# --- 2: pref on + no memory → silent exit 0 ---
|
|
47
|
-
printf '{"global":{"perRepoMemory":true}}' > "$HOME/.claude/preferences.json"
|
|
47
|
+
printf '{"global":{"perRepoMemory":true}}' > "$HOME/.claude/multi-agent-preferences.json"
|
|
48
48
|
out=$(bash "$LOAD" "$repo" 2>&1 || true)
|
|
49
49
|
if [ -z "$out" ]; then
|
|
50
50
|
record_pass "load exits silent when pref on but no memory present"
|
|
@@ -21,6 +21,7 @@ SK_J="$ROOT/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md"
|
|
|
21
21
|
SK_I="$ROOT/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md"
|
|
22
22
|
DISPATCHER="$ROOT/pipeline/commands/multi-agent/SKILL.md"
|
|
23
23
|
CONTRACT="$ROOT/pipeline/multi-agent-refs/cross-cli-contract.md"
|
|
24
|
+
CMD_COUNT=$(find "$ROOT/pipeline/commands/multi-agent" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
|
|
24
25
|
HELP="$ROOT/pipeline/commands/multi-agent/help/SKILL.md"
|
|
25
26
|
|
|
26
27
|
PASS=0; FAIL=0
|
|
@@ -61,12 +62,12 @@ grep -Fqi "confirm" "$REF" && pass "ref has confirm-before-post gate" || fail "r
|
|
|
61
62
|
grep -Fqi "rubric" "$REF" && pass "ref has readiness rubric" || fail "ref missing rubric"
|
|
62
63
|
|
|
63
64
|
echo ""
|
|
64
|
-
echo "→ 5. registration + count (
|
|
65
|
+
echo "→ 5. registration + count (derived)"
|
|
65
66
|
grep -Fq '| `review-jira ' "$DISPATCHER" && pass "dispatcher desc: review-jira" || fail "dispatcher desc missing review-jira"
|
|
66
67
|
grep -Fq '| `review-issue ' "$DISPATCHER" && pass "dispatcher desc: review-issue" || fail "dispatcher desc missing review-issue"
|
|
67
68
|
grep -Fq "review-jira/SKILL.md" "$DISPATCHER" && pass "dispatcher routing: review-jira" || fail "dispatcher routing missing review-jira"
|
|
68
69
|
grep -Fq "review-issue/SKILL.md" "$DISPATCHER" && pass "dispatcher routing: review-issue" || fail "dispatcher routing missing review-issue"
|
|
69
|
-
grep -Fq "## 1. Command Inventory (
|
|
70
|
+
grep -Fq "## 1. Command Inventory ($CMD_COUNT commands)" "$CONTRACT" && pass "contract count = $CMD_COUNT (derived)" || fail "contract count wrong"
|
|
70
71
|
grep -Eq '(^|[ ,])review-jira([ ,]|$)' "$CONTRACT" && pass "contract inventory has review-jira" || fail "contract missing review-jira"
|
|
71
72
|
grep -Eq '(^|[ ,])review-issue([ ,]|$)' "$CONTRACT" && pass "contract inventory has review-issue" || fail "contract missing review-issue"
|
|
72
73
|
grep -Fq "multi-agent:review-jira" "$HELP" && pass "help lists review-jira" || fail "help missing review-jira"
|
|
@@ -87,17 +87,31 @@ done
|
|
|
87
87
|
|
|
88
88
|
# ──────────────────────────────────────────────────────────────────────────
|
|
89
89
|
echo ""
|
|
90
|
-
echo "→ 4. prefs.schema.json - keychainMapping
|
|
90
|
+
echo "→ 4. prefs.schema.json - keychainMapping covers every resolved service"
|
|
91
|
+
# A floor, not a pin: new credentials get added over time, and pinning the count
|
|
92
|
+
# made every addition a false failure. What matters is that the keys shipped
|
|
93
|
+
# flows resolve are all declared.
|
|
91
94
|
KM_COUNT=$(node -e "
|
|
92
95
|
const s = JSON.parse(require('fs').readFileSync('$PREFS_SCHEMA', 'utf8'));
|
|
93
96
|
const km = s.properties.global.properties.keychainMapping.properties;
|
|
94
97
|
console.log(Object.keys(km).length);
|
|
95
98
|
" 2>/dev/null)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
pass "keychainMapping has $KM_COUNT services (≥12 expected)"
|
|
99
|
+
if [ "${KM_COUNT:-0}" -ge 12 ] 2>/dev/null; then
|
|
100
|
+
pass "keychainMapping declares $KM_COUNT services (≥12 expected)"
|
|
99
101
|
else
|
|
100
|
-
fail "keychainMapping
|
|
102
|
+
fail "keychainMapping declares ${KM_COUNT:-0} services, expected ≥12"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
MISSING_KEYS=$(node -e "
|
|
106
|
+
const s = JSON.parse(require('fs').readFileSync('$PREFS_SCHEMA', 'utf8'));
|
|
107
|
+
const km = Object.keys(s.properties.global.properties.keychainMapping.properties);
|
|
108
|
+
const required = ['jira','bitbucket_token','bitbucket_user','github','confluence','figma','figma_pat','figma_mcp','firebase','npm'];
|
|
109
|
+
console.log(required.filter((k) => !km.includes(k)).join(','));
|
|
110
|
+
" 2>/dev/null)
|
|
111
|
+
if [ -z "$MISSING_KEYS" ]; then
|
|
112
|
+
pass "every credential the pipeline resolves is declared"
|
|
113
|
+
else
|
|
114
|
+
fail "keychainMapping missing declared-and-used key(s): $MISSING_KEYS"
|
|
101
115
|
fi
|
|
102
116
|
|
|
103
117
|
# ──────────────────────────────────────────────────────────────────────────
|
|
@@ -59,7 +59,9 @@ fi
|
|
|
59
59
|
|
|
60
60
|
# 4-9. Lifecycle on a temp project
|
|
61
61
|
TMP=$(mktemp -d)
|
|
62
|
-
cd
|
|
62
|
+
# A failed cd here would run the relative `rm -rf DerivedData` below in the
|
|
63
|
+
# caller's directory instead of the sandbox.
|
|
64
|
+
cd "$TMP" || { record_fail "cannot cd into sandbox $TMP"; exit 1; }
|
|
63
65
|
git init --quiet
|
|
64
66
|
echo "node_modules/" > .gitignore
|
|
65
67
|
echo "hello" > a.txt
|
|
@@ -178,7 +180,7 @@ else
|
|
|
178
180
|
record_fail "~/.claude/state is GONE after unsafe prune attempts"
|
|
179
181
|
fi
|
|
180
182
|
|
|
181
|
-
cd - >/dev/null
|
|
183
|
+
cd - >/dev/null || true
|
|
182
184
|
rm -rf "$TMP"
|
|
183
185
|
|
|
184
186
|
# 10-12. Prefs schema
|
|
@@ -4,20 +4,38 @@
|
|
|
4
4
|
# `.github/workflows/` with the two dedicated external linters:
|
|
5
5
|
#
|
|
6
6
|
# - zizmor GitHub Actions security auditor (template injection,
|
|
7
|
-
# credential persistence, unpinned actions,
|
|
8
|
-
#
|
|
7
|
+
# credential persistence, cache poisoning, unpinned actions,
|
|
8
|
+
# excessive permissions, ...). Runs offline, no network.
|
|
9
9
|
# - actionlint workflow correctness linter (expression syntax, matrix
|
|
10
10
|
# typos, shellcheck of run: blocks, ...).
|
|
11
11
|
#
|
|
12
|
-
# Both tools are optional: each is skipped with a SKIP line when its binary
|
|
13
|
-
# is not on PATH (billing-friendly CI runners install neither; run locally
|
|
14
|
-
# via `brew install zizmor actionlint` for full coverage). Structural
|
|
15
|
-
# baseline checks that need no external binary live in
|
|
16
|
-
# smoke-ci-workflows.sh - this smoke is the deeper, tool-backed layer.
|
|
17
|
-
#
|
|
18
12
|
# Any finding from either tool fails the smoke.
|
|
19
13
|
#
|
|
20
|
-
#
|
|
14
|
+
# WHY THIS IS STRICT BY DEFAULT
|
|
15
|
+
#
|
|
16
|
+
# Until now a missing binary produced a SKIP and the smoke exited 0, so it
|
|
17
|
+
# reported "0 passed, 0 failed, 2 skipped" on the dev machine AND on the CI
|
|
18
|
+
# runner - neither had the tools. ci-lite.yml justified not installing them by
|
|
19
|
+
# saying to run the smoke locally "where the tools exist", but they did not
|
|
20
|
+
# exist there either. The audit had therefore never run anywhere, while
|
|
21
|
+
# appearing as a green step. When it was finally run it found nine real issues,
|
|
22
|
+
# one of them high severity (a dependency cache restored into the job that
|
|
23
|
+
# signs and publishes the release tarball).
|
|
24
|
+
#
|
|
25
|
+
# So the failure mode is inverted: absent tooling is a failure, and skipping is
|
|
26
|
+
# something you opt into deliberately and locally.
|
|
27
|
+
#
|
|
28
|
+
# brew install zizmor actionlint # macOS
|
|
29
|
+
# pipx install zizmor && go install github.com/rhysd/actionlint/cmd/actionlint@latest
|
|
30
|
+
#
|
|
31
|
+
# WORKFLOW_AUDIT_ALLOW_MISSING=1 downgrades a missing binary back to a SKIP for
|
|
32
|
+
# a local run. It is ignored when CI is set: CI installs both, pinned, and a
|
|
33
|
+
# runner that cannot must fail loudly rather than pass quietly.
|
|
34
|
+
#
|
|
35
|
+
# Structural baseline checks that need no external binary live in
|
|
36
|
+
# smoke-ci-workflows.sh - this smoke is the deeper, tool-backed layer.
|
|
37
|
+
#
|
|
38
|
+
# Exit codes: 0=pass, 1=findings or missing tooling, 2=workflows missing
|
|
21
39
|
|
|
22
40
|
set -uo pipefail
|
|
23
41
|
|
|
@@ -26,6 +44,10 @@ WF_DIR="$REPO_ROOT/.github/workflows"
|
|
|
26
44
|
|
|
27
45
|
[ -d "$WF_DIR" ] || { echo "FAIL: .github/workflows/ missing"; exit 2; }
|
|
28
46
|
|
|
47
|
+
# In CI the escape hatch does not apply, whatever the environment says.
|
|
48
|
+
ALLOW_MISSING="${WORKFLOW_AUDIT_ALLOW_MISSING:-0}"
|
|
49
|
+
[ -n "${CI:-}" ] && ALLOW_MISSING=0
|
|
50
|
+
|
|
29
51
|
PASS=0
|
|
30
52
|
FAIL=0
|
|
31
53
|
SKIPPED=0
|
|
@@ -33,6 +55,16 @@ pass() { PASS=$((PASS + 1)); echo " ✓ $1"; }
|
|
|
33
55
|
fail() { FAIL=$((FAIL + 1)); echo " ✗ $1"; }
|
|
34
56
|
skip() { SKIPPED=$((SKIPPED + 1)); echo " ↷ SKIP: $1"; }
|
|
35
57
|
|
|
58
|
+
# A tool that is not installed is a failure unless a local run opted out.
|
|
59
|
+
missing_tool() {
|
|
60
|
+
local tool="$1" install="$2"
|
|
61
|
+
if [ "$ALLOW_MISSING" = "1" ]; then
|
|
62
|
+
skip "$tool not on PATH, WORKFLOW_AUDIT_ALLOW_MISSING=1 ($install)"
|
|
63
|
+
else
|
|
64
|
+
fail "$tool not on PATH - the audit cannot run ($install)"
|
|
65
|
+
fi
|
|
66
|
+
}
|
|
67
|
+
|
|
36
68
|
# ──────────────────────────────────────────────────────────────────────────
|
|
37
69
|
echo "→ 1. zizmor (security audit, offline)"
|
|
38
70
|
if command -v zizmor >/dev/null 2>&1; then
|
|
@@ -48,7 +80,7 @@ if command -v zizmor >/dev/null 2>&1; then
|
|
|
48
80
|
echo "$OUT" | sed 's/^/ /'
|
|
49
81
|
fi
|
|
50
82
|
else
|
|
51
|
-
|
|
83
|
+
missing_tool zizmor "brew install zizmor"
|
|
52
84
|
fi
|
|
53
85
|
|
|
54
86
|
# ──────────────────────────────────────────────────────────────────────────
|
|
@@ -61,7 +93,7 @@ if command -v actionlint >/dev/null 2>&1; then
|
|
|
61
93
|
echo "$OUT" | sed 's/^/ /'
|
|
62
94
|
fi
|
|
63
95
|
else
|
|
64
|
-
|
|
96
|
+
missing_tool actionlint "brew install actionlint"
|
|
65
97
|
fi
|
|
66
98
|
|
|
67
99
|
echo ""
|
|
@@ -93,21 +93,65 @@ else
|
|
|
93
93
|
fi
|
|
94
94
|
|
|
95
95
|
# 10. Concurrent writers don't corrupt state
|
|
96
|
+
#
|
|
97
|
+
# Was 5 writers, which lost an update roughly one run in five on a loaded
|
|
98
|
+
# machine and never on an idle one, so it read as a flaky test rather than the
|
|
99
|
+
# data-loss bug it was. 20 writers puts enough writers in the acquire window to
|
|
100
|
+
# make a regression show up on an idle machine too.
|
|
96
101
|
STATE2="$TMPDIR/concurrent.json"
|
|
102
|
+
WRITERS=20
|
|
97
103
|
echo '{"counter":0}' | node "$WRITER" "$STATE2"
|
|
98
|
-
for i in 1
|
|
104
|
+
for i in $(seq 1 "$WRITERS"); do
|
|
99
105
|
echo "{\"w$i\":$i}" | node "$WRITER" "$STATE2" &
|
|
100
106
|
done
|
|
101
107
|
wait
|
|
102
|
-
# All 5 keys should be present (deep merge)
|
|
103
108
|
MISSING=0
|
|
104
|
-
for i in 1
|
|
109
|
+
for i in $(seq 1 "$WRITERS"); do
|
|
105
110
|
grep -q "\"w$i\": $i" "$STATE2" || MISSING=$((MISSING+1))
|
|
106
111
|
done
|
|
107
112
|
if [ "$MISSING" -eq 0 ]; then
|
|
108
|
-
pass "
|
|
113
|
+
pass "$WRITERS concurrent writers all persisted"
|
|
109
114
|
else
|
|
110
|
-
fail "$MISSING concurrent writes lost"
|
|
115
|
+
fail "$MISSING of $WRITERS concurrent writes lost"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# 11. A lock with no readable PID must not be treated as stale
|
|
119
|
+
#
|
|
120
|
+
# This is the exact shape of the bug above. The lock used to be created empty
|
|
121
|
+
# and filled on the next line, so a writer that arrived in between read an
|
|
122
|
+
# unparseable PID, called the lock stale, and deleted it while its owner was
|
|
123
|
+
# still working. Both then wrote and one update vanished.
|
|
124
|
+
#
|
|
125
|
+
# An empty lock now has to block until the staleness window elapses, so a
|
|
126
|
+
# writer given a 1s acquire budget must give up with exit 2 rather than help
|
|
127
|
+
# itself to the lock.
|
|
128
|
+
STATE3="$TMPDIR/emptylock.json"
|
|
129
|
+
echo '{"seed":1}' | node "$WRITER" "$STATE3"
|
|
130
|
+
: > "$STATE3.lock"
|
|
131
|
+
LOCK_RC=0
|
|
132
|
+
echo '{"stolen":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=1000 WRITE_STATE_LOCK_STALE_MS=30000 \
|
|
133
|
+
node "$WRITER" "$STATE3" >/dev/null 2>&1 || LOCK_RC=$?
|
|
134
|
+
if [ "$LOCK_RC" -eq 2 ] && ! grep -q '"stolen"' "$STATE3"; then
|
|
135
|
+
pass "empty lock is not stolen (exit 2, state untouched)"
|
|
136
|
+
else
|
|
137
|
+
fail "empty lock was treated as stale (exit $LOCK_RC) - the live-lock race is back"
|
|
138
|
+
fi
|
|
139
|
+
rm -f "$STATE3.lock"
|
|
140
|
+
|
|
141
|
+
# 12. A lock older than the staleness window is still reclaimed
|
|
142
|
+
#
|
|
143
|
+
# The counterpart to 11: refusing to reclaim an unreadable PID must not mean a
|
|
144
|
+
# genuinely abandoned lock blocks forever.
|
|
145
|
+
STATE4="$TMPDIR/stalelock.json"
|
|
146
|
+
echo '{"seed":1}' | node "$WRITER" "$STATE4"
|
|
147
|
+
: > "$STATE4.lock"
|
|
148
|
+
STALE_RC=0
|
|
149
|
+
echo '{"recovered":true}' | WRITE_STATE_LOCK_TIMEOUT_MS=3000 WRITE_STATE_LOCK_STALE_MS=1 \
|
|
150
|
+
node "$WRITER" "$STATE4" >/dev/null 2>&1 || STALE_RC=$?
|
|
151
|
+
if [ "$STALE_RC" -eq 0 ] && grep -q '"recovered"' "$STATE4"; then
|
|
152
|
+
pass "abandoned lock past the staleness window is reclaimed"
|
|
153
|
+
else
|
|
154
|
+
fail "stale lock not reclaimed (exit $STALE_RC) - writers would wedge"
|
|
111
155
|
fi
|
|
112
156
|
|
|
113
157
|
echo ""
|
|
@@ -5,20 +5,20 @@
|
|
|
5
5
|
"sourceExtensions": [".kt", ".java"],
|
|
6
6
|
"testPathSuffixes": ["Test.kt", "Tests.kt", "Test.java"],
|
|
7
7
|
"testPathContains": ["/src/test/", "/src/androidTest/", "/test/", "/tests/"],
|
|
8
|
-
"excludePathGlobs": [
|
|
9
|
-
"build/", ".gradle/", "generated/",
|
|
10
|
-
"**/BuildConfig.java", "**/R.java"
|
|
11
|
-
],
|
|
8
|
+
"excludePathGlobs": ["build/", ".gradle/", "generated/", "**/BuildConfig.java", "**/R.java"],
|
|
12
9
|
"publicApiPatterns": [
|
|
13
|
-
{"id": "open_class", "regex": "\\bopen\\s+class\\s+([A-Z][A-Za-z0-9_]*)"},
|
|
14
|
-
{"id": "open_fun",
|
|
15
|
-
{"id": "public_fun", "regex": "^\\s*(?:public\\s+)?fun\\s+([a-z_][A-Za-z0-9_]*)"},
|
|
16
|
-
{"id": "public_class", "regex": "^\\s*(?:public\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)"},
|
|
17
|
-
{"id": "jvmstatic",
|
|
18
|
-
{"id": "interface",
|
|
10
|
+
{ "id": "open_class", "regex": "\\bopen\\s+class\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
11
|
+
{ "id": "open_fun", "regex": "\\bopen\\s+fun\\s+([a-z_][A-Za-z0-9_]*)" },
|
|
12
|
+
{ "id": "public_fun", "regex": "^\\s*(?:public\\s+)?fun\\s+([a-z_][A-Za-z0-9_]*)" },
|
|
13
|
+
{ "id": "public_class", "regex": "^\\s*(?:public\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
14
|
+
{ "id": "jvmstatic", "regex": "@JvmStatic\\b" },
|
|
15
|
+
{ "id": "interface", "regex": "^\\s*(?:public\\s+)?interface\\s+([A-Z][A-Za-z0-9_]*)" }
|
|
19
16
|
],
|
|
20
17
|
"composablePatterns": [
|
|
21
|
-
{
|
|
18
|
+
{
|
|
19
|
+
"id": "composable_fun",
|
|
20
|
+
"regex": "@Composable\\s+(?:public\\s+|internal\\s+)?fun\\s+([A-Z][A-Za-z0-9_]*)"
|
|
21
|
+
}
|
|
22
22
|
],
|
|
23
23
|
"expectedTestKindsForComposable": ["compose-ui-test"],
|
|
24
24
|
"hint": "Cover @Composable functions with createComposeRule() + onNodeWithText/Tag/ContentDescription assertions; ViewModels with runTest + Turbine."
|
|
@@ -6,22 +6,27 @@
|
|
|
6
6
|
"testPathSuffixes": ["Tests.swift", "Test.swift"],
|
|
7
7
|
"testPathContains": ["/Tests/", "/UnitTests/", "/SnapshotTests/"],
|
|
8
8
|
"excludePathGlobs": [
|
|
9
|
-
"Pods/",
|
|
10
|
-
"
|
|
9
|
+
"Pods/",
|
|
10
|
+
"Carthage/",
|
|
11
|
+
".build/",
|
|
12
|
+
"DerivedData/",
|
|
13
|
+
"Generated/",
|
|
14
|
+
"Sourcery/",
|
|
15
|
+
"_Generated.swift",
|
|
11
16
|
"Tests/Fixtures/"
|
|
12
17
|
],
|
|
13
18
|
"publicApiPatterns": [
|
|
14
|
-
{"id": "public_func",
|
|
15
|
-
{"id": "public_class",
|
|
16
|
-
{"id": "public_struct", "regex": "\\bpublic\\s+struct\\s+([A-Z][A-Za-z0-9_]*)"},
|
|
17
|
-
{"id": "public_enum",
|
|
18
|
-
{"id": "public_proto",
|
|
19
|
-
{"id": "public_init",
|
|
20
|
-
{"id": "objc_export",
|
|
19
|
+
{ "id": "public_func", "regex": "\\bpublic\\s+func\\s+([A-Za-z_][A-Za-z0-9_]*)" },
|
|
20
|
+
{ "id": "public_class", "regex": "\\bpublic\\s+(?:final\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
21
|
+
{ "id": "public_struct", "regex": "\\bpublic\\s+struct\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
22
|
+
{ "id": "public_enum", "regex": "\\bpublic\\s+enum\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
23
|
+
{ "id": "public_proto", "regex": "\\bpublic\\s+protocol\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
24
|
+
{ "id": "public_init", "regex": "\\bpublic\\s+init\\b" },
|
|
25
|
+
{ "id": "objc_export", "regex": "@objc(?:Members)?\\s+(?:public\\s+)?(?:func|class|var)" }
|
|
21
26
|
],
|
|
22
27
|
"swiftUiViewPatterns": [
|
|
23
|
-
{"id": "view_struct",
|
|
24
|
-
{"id": "config_struct",
|
|
28
|
+
{ "id": "view_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*)\\s*:\\s*View\\b" },
|
|
29
|
+
{ "id": "config_struct", "regex": "struct\\s+([A-Z][A-Za-z0-9_]*Configuration)\\b" }
|
|
25
30
|
],
|
|
26
31
|
"expectedTestKindsForView": ["snapshot", "viewinspector", "unit"],
|
|
27
32
|
"snapshotPathHints": ["SnapshotTests", "__Snapshots__"],
|
|
@@ -3,15 +3,27 @@
|
|
|
3
3
|
"stack": "node",
|
|
4
4
|
"description": "Node.js / TypeScript test gap rules. Sources in *.ts/*.tsx/*.js/*.mjs; tests in *.test.* / *.spec.* / __tests__/.",
|
|
5
5
|
"sourceExtensions": [".ts", ".tsx", ".js", ".mjs", ".cjs"],
|
|
6
|
-
"testPathSuffixes": [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
6
|
+
"testPathSuffixes": [
|
|
7
|
+
".test.ts",
|
|
8
|
+
".test.tsx",
|
|
9
|
+
".test.js",
|
|
10
|
+
".test.mjs",
|
|
11
|
+
".spec.ts",
|
|
12
|
+
".spec.tsx",
|
|
13
|
+
".spec.js",
|
|
14
|
+
".spec.mjs"
|
|
11
15
|
],
|
|
16
|
+
"testPathContains": ["/__tests__/", "/test/", "/tests/"],
|
|
17
|
+
"excludePathGlobs": ["node_modules/", "dist/", "build/", ".next/", "coverage/", "*.d.ts"],
|
|
12
18
|
"publicApiPatterns": [
|
|
13
|
-
{
|
|
14
|
-
|
|
19
|
+
{
|
|
20
|
+
"id": "named_export",
|
|
21
|
+
"regex": "^export\\s+(?:async\\s+)?(?:function|class|const|let|interface|type|enum)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "default_export",
|
|
25
|
+
"regex": "^export\\s+default\\s+(?:async\\s+)?(?:function|class)\\s+([A-Za-z_$][A-Za-z0-9_$]*)"
|
|
26
|
+
}
|
|
15
27
|
],
|
|
16
28
|
"hint": "Cover named exports with vitest/jest test('<name>', ...). React components: React Testing Library (render + screen + userEvent)."
|
|
17
29
|
}
|
|
@@ -7,13 +7,19 @@
|
|
|
7
7
|
"testPathPrefixes": ["test_"],
|
|
8
8
|
"testPathContains": ["/tests/", "/test/"],
|
|
9
9
|
"excludePathGlobs": [
|
|
10
|
-
"__pycache__/",
|
|
10
|
+
"__pycache__/",
|
|
11
|
+
".venv/",
|
|
12
|
+
"venv/",
|
|
13
|
+
".tox/",
|
|
14
|
+
"build/",
|
|
15
|
+
"dist/",
|
|
16
|
+
"*.egg-info/",
|
|
11
17
|
"**/migrations/**"
|
|
12
18
|
],
|
|
13
19
|
"publicApiPatterns": [
|
|
14
|
-
{"id": "top_def",
|
|
15
|
-
{"id": "top_class", "regex": "^class\\s+([A-Z][A-Za-z0-9_]*)"},
|
|
16
|
-
{"id": "async_def", "regex": "^async\\s+def\\s+([a-z_][a-z0-9_]*)"}
|
|
20
|
+
{ "id": "top_def", "regex": "^def\\s+([a-z_][a-z0-9_]*)" },
|
|
21
|
+
{ "id": "top_class", "regex": "^class\\s+([A-Z][A-Za-z0-9_]*)" },
|
|
22
|
+
{ "id": "async_def", "regex": "^async\\s+def\\s+([a-z_][a-z0-9_]*)" }
|
|
17
23
|
],
|
|
18
24
|
"hint": "Cover with pytest test_<symbol> style. Async with pytest-asyncio. Avoid name collision with private helpers (prefix _)."
|
|
19
25
|
}
|