@soleur/opencode 0.0.0-stage → 3.354.2
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/.claude-plugin/agents.manifest.json +476 -0
- package/.claude-plugin/plugin.json +42 -0
- package/README.md +44 -2
- package/agents/community/.gitkeep +0 -0
- package/agents/engineering/cto.md +87 -0
- package/agents/engineering/design/ddd-architect.md +95 -0
- package/agents/engineering/discovery/agent-finder.md +214 -0
- package/agents/engineering/discovery/functional-discovery.md +182 -0
- package/agents/engineering/infra/infra-security.md +151 -0
- package/agents/engineering/infra/platform-strategist.md +109 -0
- package/agents/engineering/infra/terraform-architect.md +171 -0
- package/agents/engineering/prompt-engineer.md +37 -0
- package/agents/engineering/research/best-practices-researcher.md +115 -0
- package/agents/engineering/research/framework-docs-researcher.md +93 -0
- package/agents/engineering/research/git-history-analyzer.md +53 -0
- package/agents/engineering/research/learnings-researcher.md +285 -0
- package/agents/engineering/research/repo-research-analyst.md +118 -0
- package/agents/engineering/review/agent-native-reviewer.md +279 -0
- package/agents/engineering/review/architecture-strategist.md +61 -0
- package/agents/engineering/review/code-quality-analyst.md +92 -0
- package/agents/engineering/review/code-simplicity-reviewer.md +109 -0
- package/agents/engineering/review/data-integrity-guardian.md +82 -0
- package/agents/engineering/review/data-migration-expert.md +98 -0
- package/agents/engineering/review/deployment-verification-agent.md +183 -0
- package/agents/engineering/review/dhh-rails-reviewer.md +46 -0
- package/agents/engineering/review/kieran-rails-reviewer.md +87 -0
- package/agents/engineering/review/legacy-code-expert.md +55 -0
- package/agents/engineering/review/observability-coverage-reviewer.md +147 -0
- package/agents/engineering/review/pattern-recognition-specialist.md +59 -0
- package/agents/engineering/review/performance-oracle.md +130 -0
- package/agents/engineering/review/security-sentinel.md +167 -0
- package/agents/engineering/review/semgrep-sast.md +36 -0
- package/agents/engineering/review/test-design-reviewer.md +150 -0
- package/agents/engineering/review/user-impact-reviewer.md +70 -0
- package/agents/engineering/workflow/pr-comment-resolver.md +69 -0
- package/agents/finance/budget-analyst.md +29 -0
- package/agents/finance/cfo.md +44 -0
- package/agents/finance/financial-reporter.md +30 -0
- package/agents/finance/revenue-analyst.md +30 -0
- package/agents/legal/clo.md +115 -0
- package/agents/legal/legal-compliance-auditor.md +110 -0
- package/agents/legal/legal-document-generator.md +134 -0
- package/agents/marketing/analytics-analyst.md +25 -0
- package/agents/marketing/brand-architect.md +181 -0
- package/agents/marketing/cmo.md +76 -0
- package/agents/marketing/conversion-optimizer.md +19 -0
- package/agents/marketing/copywriter.md +20 -0
- package/agents/marketing/fact-checker.md +70 -0
- package/agents/marketing/growth-strategist.md +145 -0
- package/agents/marketing/paid-media-strategist.md +28 -0
- package/agents/marketing/pricing-strategist.md +17 -0
- package/agents/marketing/programmatic-seo-specialist.md +25 -0
- package/agents/marketing/retention-strategist.md +23 -0
- package/agents/marketing/seo-aeo-analyst.md +164 -0
- package/agents/operations/coo.md +50 -0
- package/agents/operations/ops-advisor.md +78 -0
- package/agents/operations/ops-provisioner.md +101 -0
- package/agents/operations/ops-research.md +52 -0
- package/agents/operations/service-automator.md +278 -0
- package/agents/product/business-validator.md +206 -0
- package/agents/product/competitive-intelligence.md +89 -0
- package/agents/product/cpo.md +63 -0
- package/agents/product/design/ux-design-lead.md +209 -0
- package/agents/product/spec-flow-analyzer.md +122 -0
- package/agents/sales/cro.md +44 -0
- package/agents/sales/deal-architect.md +29 -0
- package/agents/sales/outbound-strategist.md +29 -0
- package/agents/sales/pipeline-analyst.md +29 -0
- package/agents/support/cco.md +48 -0
- package/agents/support/community-manager.md +570 -0
- package/agents/support/ticket-triage.md +70 -0
- package/bin/soleur-opencode.js +29 -0
- package/commands/go.md +660 -0
- package/commands/help.md +273 -0
- package/commands/sync.md +977 -0
- package/commands/version.md +103 -0
- package/hooks/antigravity-hooks.json +84 -0
- package/hooks/antigravity-session-start.sh +15 -0
- package/hooks/bare-root-cwd-guard.sh +131 -0
- package/hooks/browser-snapshot-credential-guard.sh +200 -0
- package/hooks/codex-session-start.sh +9 -0
- package/hooks/compaction-state.sh +525 -0
- package/hooks/destructive-command-guard.sh +1387 -0
- package/hooks/devin-session-start.sh +90 -0
- package/hooks/ext-filter-detect.sh +171 -0
- package/hooks/hooks.json +133 -0
- package/hooks/lib/hook-tool-kind.sh +24 -0
- package/hooks/lib/log-rotation.sh +176 -0
- package/hooks/lib/shell-argv.pl +576 -0
- package/hooks/memory-backstop.sh +1114 -0
- package/hooks/operator-stage-approval.sh +391 -0
- package/hooks/stop-hook.sh +367 -0
- package/hooks/unkept-promise-hook.sh +383 -0
- package/hooks/version-banner.sh +127 -0
- package/hooks/welcome-hook.sh +50 -0
- package/install.js +29 -0
- package/lib/agent-registry.ts +154 -0
- package/lib/c4-canonical-cli.mjs +34 -0
- package/lib/c4-canonical.mjs +46 -0
- package/lib/c4-from-components.ts +554 -0
- package/lib/go-routing.ts +81 -0
- package/lib/grok-inspect-contract.ts +311 -0
- package/lib/harness-model-map.ts +101 -0
- package/lib/harness-parity.ts +726 -0
- package/lib/harness-tool-map.ts +180 -0
- package/lib/harness.ts +716 -0
- package/lib/heartbeat-live-reconcile.ts +1153 -0
- package/lib/heartbeat-manifest.ts +361 -0
- package/lib/kb-coverage.ts +252 -0
- package/lib/pr-merge-poll.ts +103 -0
- package/lib/workflow-fidelity.ts +388 -0
- package/opencode/INSTRUCTIONS.md +171 -0
- package/opencode/agents/engineering/cto.md +10 -0
- package/opencode/agents/engineering/design/ddd-architect.md +10 -0
- package/opencode/agents/engineering/discovery/agent-finder.md +10 -0
- package/opencode/agents/engineering/discovery/functional-discovery.md +10 -0
- package/opencode/agents/engineering/infra/infra-security.md +10 -0
- package/opencode/agents/engineering/infra/platform-strategist.md +10 -0
- package/opencode/agents/engineering/infra/terraform-architect.md +10 -0
- package/opencode/agents/engineering/prompt-engineer.md +10 -0
- package/opencode/agents/engineering/research/best-practices-researcher.md +10 -0
- package/opencode/agents/engineering/research/framework-docs-researcher.md +10 -0
- package/opencode/agents/engineering/research/git-history-analyzer.md +10 -0
- package/opencode/agents/engineering/research/learnings-researcher.md +10 -0
- package/opencode/agents/engineering/research/repo-research-analyst.md +10 -0
- package/opencode/agents/engineering/review/agent-native-reviewer.md +10 -0
- package/opencode/agents/engineering/review/architecture-strategist.md +10 -0
- package/opencode/agents/engineering/review/code-quality-analyst.md +10 -0
- package/opencode/agents/engineering/review/code-simplicity-reviewer.md +10 -0
- package/opencode/agents/engineering/review/data-integrity-guardian.md +10 -0
- package/opencode/agents/engineering/review/data-migration-expert.md +10 -0
- package/opencode/agents/engineering/review/deployment-verification-agent.md +10 -0
- package/opencode/agents/engineering/review/dhh-rails-reviewer.md +10 -0
- package/opencode/agents/engineering/review/kieran-rails-reviewer.md +10 -0
- package/opencode/agents/engineering/review/legacy-code-expert.md +10 -0
- package/opencode/agents/engineering/review/observability-coverage-reviewer.md +10 -0
- package/opencode/agents/engineering/review/pattern-recognition-specialist.md +10 -0
- package/opencode/agents/engineering/review/performance-oracle.md +10 -0
- package/opencode/agents/engineering/review/security-sentinel.md +10 -0
- package/opencode/agents/engineering/review/semgrep-sast.md +10 -0
- package/opencode/agents/engineering/review/test-design-reviewer.md +10 -0
- package/opencode/agents/engineering/review/user-impact-reviewer.md +10 -0
- package/opencode/agents/engineering/workflow/pr-comment-resolver.md +10 -0
- package/opencode/agents/finance/budget-analyst.md +10 -0
- package/opencode/agents/finance/cfo.md +10 -0
- package/opencode/agents/finance/financial-reporter.md +10 -0
- package/opencode/agents/finance/revenue-analyst.md +10 -0
- package/opencode/agents/legal/clo.md +10 -0
- package/opencode/agents/legal/legal-compliance-auditor.md +10 -0
- package/opencode/agents/legal/legal-document-generator.md +10 -0
- package/opencode/agents/marketing/analytics-analyst.md +10 -0
- package/opencode/agents/marketing/brand-architect.md +10 -0
- package/opencode/agents/marketing/cmo.md +10 -0
- package/opencode/agents/marketing/conversion-optimizer.md +10 -0
- package/opencode/agents/marketing/copywriter.md +10 -0
- package/opencode/agents/marketing/fact-checker.md +10 -0
- package/opencode/agents/marketing/growth-strategist.md +10 -0
- package/opencode/agents/marketing/paid-media-strategist.md +10 -0
- package/opencode/agents/marketing/pricing-strategist.md +10 -0
- package/opencode/agents/marketing/programmatic-seo-specialist.md +10 -0
- package/opencode/agents/marketing/retention-strategist.md +10 -0
- package/opencode/agents/marketing/seo-aeo-analyst.md +10 -0
- package/opencode/agents/operations/coo.md +10 -0
- package/opencode/agents/operations/ops-advisor.md +10 -0
- package/opencode/agents/operations/ops-provisioner.md +10 -0
- package/opencode/agents/operations/ops-research.md +10 -0
- package/opencode/agents/operations/service-automator.md +10 -0
- package/opencode/agents/product/business-validator.md +10 -0
- package/opencode/agents/product/competitive-intelligence.md +10 -0
- package/opencode/agents/product/cpo.md +10 -0
- package/opencode/agents/product/design/ux-design-lead.md +10 -0
- package/opencode/agents/product/spec-flow-analyzer.md +10 -0
- package/opencode/agents/sales/cro.md +10 -0
- package/opencode/agents/sales/deal-architect.md +10 -0
- package/opencode/agents/sales/outbound-strategist.md +10 -0
- package/opencode/agents/sales/pipeline-analyst.md +10 -0
- package/opencode/agents/support/cco.md +10 -0
- package/opencode/agents/support/community-manager.md +10 -0
- package/opencode/agents/support/ticket-triage.md +10 -0
- package/opencode/commands/go.md +10 -0
- package/opencode/commands/help.md +10 -0
- package/opencode/commands/sync.md +10 -0
- package/opencode/commands/version.md +11 -0
- package/opencode/opencode.json +8 -0
- package/opencode/plugin.ts +818 -0
- package/package.json +36 -4
- package/scripts/admin-merge-ready.sh +545 -0
- package/scripts/admin-merge-ready.test.sh +753 -0
- package/scripts/affected-tests.sh +908 -0
- package/scripts/affected-tests.test.sh +1797 -0
- package/scripts/alpha-metrics.sh +78 -0
- package/scripts/alpha-metrics.test.sh +115 -0
- package/scripts/ancestor-signal-helpers.baseline.txt +16 -0
- package/scripts/audit-flag-flip.sh +107 -0
- package/scripts/check-red-on-main.sh +316 -0
- package/scripts/ci-head-verdict.sh +411 -0
- package/scripts/cloud-detect.sh +193 -0
- package/scripts/delegation-verify.sh +657 -0
- package/scripts/delegation-verify.test.sh +1424 -0
- package/scripts/deploy-arm.sh +475 -0
- package/scripts/domain-model-drift.sh +419 -0
- package/scripts/emit-decision.sh +144 -0
- package/scripts/emit-decision.test.sh +144 -0
- package/scripts/ext-filter-divergence-corpus.txt +15 -0
- package/scripts/ext-filter-divergence.sh +189 -0
- package/scripts/generate-c4-from-components.ts +442 -0
- package/scripts/grok-fidelity-gate.sh +68 -0
- package/scripts/grok-pre-push-gate.sh +232 -0
- package/scripts/harness-discovery-smoke.ts +906 -0
- package/scripts/harness-parity-census.ts +92 -0
- package/scripts/inngest-luks-alert-armed-probe.ts +29 -0
- package/scripts/install-opencode-adapter.ts +509 -0
- package/scripts/lib/affected-tests/common.sh +365 -0
- package/scripts/lib/affected-tests/go.sh +157 -0
- package/scripts/lib/affected-tests/js.sh +188 -0
- package/scripts/lib/affected-tests/nx-turbo.sh +98 -0
- package/scripts/lib/affected-tests/python.sh +185 -0
- package/scripts/lib/context-sanitize.sh +49 -0
- package/scripts/lib/domain-model-lib.sh +180 -0
- package/scripts/lib/ext-filter-grammar.tsv +34 -0
- package/scripts/lib/operator-script.sh +1236 -0
- package/scripts/lib/proc.sh +580 -0
- package/scripts/lib/session-state.sh +748 -0
- package/scripts/lib/tmp-classify.sh +843 -0
- package/scripts/lib/wrapper-boundary.sh +47 -0
- package/scripts/lint-ratchet.py +1547 -0
- package/scripts/monitor-pr-checks.sh +441 -0
- package/scripts/pipeline-tally.sh +756 -0
- package/scripts/pipeline-tally.test.sh +740 -0
- package/scripts/plugin-version.sh +528 -0
- package/scripts/precommit-guard.sh +115 -0
- package/scripts/precommit-guard.test.sh +254 -0
- package/scripts/reconcile-live-heartbeats.ts +705 -0
- package/scripts/render-c4-model.sh +217 -0
- package/scripts/resolve-git-root.sh +55 -0
- package/scripts/resolve-regenerable-conflicts.sh +363 -0
- package/scripts/resolve-regenerable-conflicts.test.sh +780 -0
- package/scripts/ruff-requirements.txt +22 -0
- package/scripts/run-in-pid-namespace.sh +98 -0
- package/scripts/run-in-pid-namespace.test.sh +290 -0
- package/scripts/scan-ancestor-signal-helpers.py +556 -0
- package/scripts/scan-ancestor-signal-helpers.test.sh +670 -0
- package/scripts/setup-ralph-loop.sh +238 -0
- package/scripts/sync-cursor-name-map.ts +316 -0
- package/scripts/sync-grok-agent-compat.ts +188 -0
- package/scripts/sync-npm-package.ts +165 -0
- package/scripts/sync-opencode-adapter.ts +498 -0
- package/scripts/sync-pr-behind.sh +647 -0
- package/scripts/sync-pr-behind.test.sh +252 -0
- package/scripts/taste-profile-update.sh +180 -0
- package/scripts/taste-profile-update.test.sh +167 -0
- package/scripts/write-budget-marker.sh +23 -0
- package/scripts/write-kb-coverage.ts +113 -0
- package/skills/admin-ip-refresh/SKILL.md +81 -0
- package/skills/admin-ip-refresh/references/admin-ip-refresh-procedure.md +201 -0
- package/skills/agent-browser/SKILL.md +698 -0
- package/skills/agent-browser/scripts/playwright-mcp-profile-slot.sh +304 -0
- package/skills/agent-browser/scripts/playwright-mcp-redact-proxy.py +964 -0
- package/skills/agent-browser/scripts/redact-a11y-snapshot.py +443 -0
- package/skills/agent-browser/test/fixtures/capture-playwright-mcp-fixtures.py +217 -0
- package/skills/agent-browser/test/fixtures/fake-passthrough-proxy.py +24 -0
- package/skills/agent-browser/test/fixtures/fake-playwright-mcp.py +131 -0
- package/skills/agent-browser/test/fixtures/make-proxy-mutant.py +48 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/dialog-link-injection.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/empty-content.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-and-result.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-code-bool.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-code-string.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-message-tree.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-no-data.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-string.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/error-with-data.json +9 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/evaluate-escaped-object.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/invalid-utf8.json +4 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/link-result.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/list-two-results.json +6 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/meta-in-result.json +13 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/no-content.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/quoted-key.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/resource-block.json +18 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/result-and-method.json +11 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/result-string.json +3 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped-one-row.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/structured-content.json +13 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/tools-list-no-tools.json +5 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/tree-no-credential.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/two-text-blocks.json +14 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/unparsable-line.json +3 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-brace.json +10 -0
- package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-link-substring.json +10 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/close.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/evaluate.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/find.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/framing.json +6 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/initialize.json +14 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate-link.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/roots-list-request.json +5 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/screenshot.json +17 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-filename.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-json.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-raw.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot.json +12 -0
- package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/tools-list.json +827 -0
- package/skills/agent-browser/test/fixtures/proxy-session-driver.py +138 -0
- package/skills/agent-browser/test/fixtures/session-facts.py +102 -0
- package/skills/agent-browser/test/playwright-mcp-lifetime.test.sh +1620 -0
- package/skills/agent-browser/test/playwright-mcp-redact-proxy.test.sh +1599 -0
- package/skills/agent-browser/test/redact-a11y-snapshot.test.sh +527 -0
- package/skills/agent-native-architecture/SKILL.md +479 -0
- package/skills/agent-native-architecture/references/action-parity-discipline.md +430 -0
- package/skills/agent-native-architecture/references/agent-execution-patterns.md +483 -0
- package/skills/agent-native-architecture/references/agent-native-testing.md +605 -0
- package/skills/agent-native-architecture/references/architecture-patterns.md +493 -0
- package/skills/agent-native-architecture/references/dynamic-context-injection.md +358 -0
- package/skills/agent-native-architecture/references/files-universal-interface.md +314 -0
- package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +381 -0
- package/skills/agent-native-architecture/references/mcp-tool-design.md +524 -0
- package/skills/agent-native-architecture/references/mobile-patterns.md +897 -0
- package/skills/agent-native-architecture/references/product-implications.md +478 -0
- package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +342 -0
- package/skills/agent-native-architecture/references/self-modification.md +294 -0
- package/skills/agent-native-architecture/references/shared-workspace-architecture.md +700 -0
- package/skills/agent-native-architecture/references/system-prompt-design.md +263 -0
- package/skills/agent-native-audit/SKILL.md +300 -0
- package/skills/agent-native-audit/workflows/agent-native-audit.workflow.js +423 -0
- package/skills/andrew-kane-gem-writer/SKILL.md +189 -0
- package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/skills/andrew-kane-gem-writer/references/resources.md +126 -0
- package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/skills/architecture/SKILL.md +436 -0
- package/skills/architecture/references/adr-template.md +154 -0
- package/skills/architecture/references/likec4-reference.md +136 -0
- package/skills/architecture/references/nfr-reference.md +126 -0
- package/skills/archive-kb/SKILL.md +86 -0
- package/skills/archive-kb/scripts/archive-kb.sh +300 -0
- package/skills/archive-kb/test/archive-kb-partial-run.test.sh +276 -0
- package/skills/atdd-developer/SKILL.md +65 -0
- package/skills/brainstorm/SKILL.md +701 -0
- package/skills/brainstorm/references/brainstorm-brand-workshop.md +153 -0
- package/skills/brainstorm/references/brainstorm-domain-config.md +63 -0
- package/skills/brainstorm/references/brainstorm-validation-workshop.md +65 -0
- package/skills/brainstorm/references/ui-surface-terms.md +42 -0
- package/skills/brainstorm-techniques/SKILL.md +231 -0
- package/skills/brainstorm-techniques/references/decision-principles.md +150 -0
- package/skills/brainstorm-techniques/references/phase-boundaries.md +103 -0
- package/skills/brainstorm-techniques/references/standing-delegation.md +407 -0
- package/skills/brainstorm-techniques/references/technical-ruling-delegation.md +174 -0
- package/skills/campaign-calendar/SKILL.md +145 -0
- package/skills/cf-token-scope/SKILL.md +164 -0
- package/skills/cf-token-scope/references/widen-playbook.md +114 -0
- package/skills/cf-token-scope/scripts/cf-token-scope.sh +253 -0
- package/skills/changelog/SKILL.md +142 -0
- package/skills/code-to-prd/SKILL.md +66 -0
- package/skills/code-to-prd/references/banner-template.md +12 -0
- package/skills/code-to-prd/references/prd-template.md +41 -0
- package/skills/code-to-prd/scripts/code-to-prd.sh +634 -0
- package/skills/code-to-prd/test/code-to-prd.test.sh +447 -0
- package/skills/code-to-prd/test/fixture/.env.example +7 -0
- package/skills/code-to-prd/test/fixture/app/about/page.tsx +11 -0
- package/skills/code-to-prd/test/fixture/app/api/health/route.ts +10 -0
- package/skills/code-to-prd/test/fixture/app/page.tsx +14 -0
- package/skills/code-to-prd/test/fixture/next.config.mjs +3 -0
- package/skills/code-to-prd/test/fixture/package.json +6 -0
- package/skills/cohort-status/SKILL.md +62 -0
- package/skills/community/SKILL.md +210 -0
- package/skills/community/scripts/bsky-community.sh +644 -0
- package/skills/community/scripts/bsky-setup.sh +631 -0
- package/skills/community/scripts/community-router.sh +93 -0
- package/skills/community/scripts/discord-community.sh +487 -0
- package/skills/community/scripts/discord-setup.sh +784 -0
- package/skills/community/scripts/github-community.sh +706 -0
- package/skills/community/scripts/hn-community.sh +233 -0
- package/skills/community/scripts/lib/hmac-sha1-b64.sh +126 -0
- package/skills/community/scripts/linkedin-community.sh +750 -0
- package/skills/community/scripts/linkedin-setup.sh +758 -0
- package/skills/community/scripts/x-community.sh +890 -0
- package/skills/community/scripts/x-setup.sh +713 -0
- package/skills/community/test/community-argv.test.sh +2922 -0
- package/skills/community/test/github-community.test.sh +981 -0
- package/skills/competitive-analysis/SKILL.md +69 -0
- package/skills/competitive-analysis/references/peer-plugin-audit.md +218 -0
- package/skills/compound/SKILL.md +673 -0
- package/skills/compound/scripts/token-efficiency-report.sh +334 -0
- package/skills/compound/test/phase-16.test.sh +379 -0
- package/skills/compound-capture/SKILL.md +791 -0
- package/skills/compound-capture/assets/critical-pattern-template.md +37 -0
- package/skills/compound-capture/assets/resolution-template.md +116 -0
- package/skills/compound-capture/references/yaml-schema.md +67 -0
- package/skills/compound-capture/schema.yaml +181 -0
- package/skills/constraint-scaffold/SKILL.md +175 -0
- package/skills/constraint-scaffold/references/boundary-readme.template +35 -0
- package/skills/constraint-scaffold/references/constraint-gates-workflow.template +69 -0
- package/skills/constraint-scaffold/references/depcruise-config.template +230 -0
- package/skills/constraint-scaffold/references/fix-constraints-stage-a.template +240 -0
- package/skills/constraint-scaffold/references/fix-constraints-stage-b.template +313 -0
- package/skills/constraint-scaffold/references/shared-runner.template +135 -0
- package/skills/constraint-scaffold/scripts/constraint-scaffold.sh +589 -0
- package/skills/constraint-scaffold/test/bite-proof.test.sh +1338 -0
- package/skills/constraint-scaffold/test/boundary.test.sh +734 -0
- package/skills/constraint-scaffold/test/emit-fix-constraints.test.sh +193 -0
- package/skills/constraint-scaffold/test/generator.test.sh +93 -0
- package/skills/constraint-scaffold/test/parity.test.sh +285 -0
- package/skills/content-writer/SKILL.md +242 -0
- package/skills/content-writer/scripts/blog-jargon-scan.sh +42 -0
- package/skills/cron-delete/SKILL.md +56 -0
- package/skills/cron-list/SKILL.md +54 -0
- package/skills/deepen-plan/SKILL.md +937 -0
- package/skills/deepen-plan/workflows/deepen-plan.workflow.js +488 -0
- package/skills/deploy/SKILL.md +115 -0
- package/skills/deploy/references/hetzner-setup.md +67 -0
- package/skills/deploy/scripts/deploy.sh +37 -0
- package/skills/deploy-docs/SKILL.md +95 -0
- package/skills/dhh-rails-style/SKILL.md +203 -0
- package/skills/dhh-rails-style/references/architecture.md +717 -0
- package/skills/dhh-rails-style/references/controllers.md +323 -0
- package/skills/dhh-rails-style/references/frontend.md +556 -0
- package/skills/dhh-rails-style/references/gems.md +316 -0
- package/skills/dhh-rails-style/references/models.md +388 -0
- package/skills/dhh-rails-style/references/testing.md +354 -0
- package/skills/discord-content/SKILL.md +126 -0
- package/skills/docs-site/SKILL.md +214 -0
- package/skills/drain-labeled-backlog/SKILL.md +203 -0
- package/skills/drain-labeled-backlog/scripts/group-by-area.sh +208 -0
- package/skills/drain-labeled-backlog/workflows/drain-labeled-backlog.workflow.js +433 -0
- package/skills/drain-prs/SKILL.md +143 -0
- package/skills/drain-prs/scripts/triage-prs.sh +192 -0
- package/skills/drain-prs/test/argv-ceiling.test.sh +183 -0
- package/skills/dspy-ruby/SKILL.md +634 -0
- package/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/skills/dspy-ruby/references/core-concepts.md +268 -0
- package/skills/dspy-ruby/references/optimization.md +623 -0
- package/skills/dspy-ruby/references/providers.md +335 -0
- package/skills/eval-harness/README.md +194 -0
- package/skills/eval-harness/SKILL.md +157 -0
- package/skills/eval-harness/enums/go-routes.json +13 -0
- package/skills/eval-harness/enums/incident-threshold.json +5 -0
- package/skills/eval-harness/enums/lane.json +5 -0
- package/skills/eval-harness/enums/tool-selection.json +20 -0
- package/skills/eval-harness/enums/triage-levels.json +5 -0
- package/skills/eval-harness/gated-skills.json +34 -0
- package/skills/eval-harness/models.generated.json +5 -0
- package/skills/eval-harness/promptfooconfig-go-routing.yaml +35 -0
- package/skills/eval-harness/promptfooconfig-incident-threshold.yaml +32 -0
- package/skills/eval-harness/promptfooconfig-lane-inference.yaml +33 -0
- package/skills/eval-harness/promptfooconfig-ticket-triage.yaml +34 -0
- package/skills/eval-harness/promptfooconfig-tool-selection.yaml +33 -0
- package/skills/eval-harness/prompts/go-baseline.txt +6 -0
- package/skills/eval-harness/prompts/go-skill.txt +21 -0
- package/skills/eval-harness/prompts/incident-threshold-baseline.txt +6 -0
- package/skills/eval-harness/prompts/incident-threshold-skill.txt +12 -0
- package/skills/eval-harness/prompts/lane-inference-baseline.txt +6 -0
- package/skills/eval-harness/prompts/lane-inference-skill.txt +15 -0
- package/skills/eval-harness/prompts/tool-selection-baseline.txt +19 -0
- package/skills/eval-harness/prompts/tool-selection-skill.txt +22 -0
- package/skills/eval-harness/prompts/triage-baseline.txt +7 -0
- package/skills/eval-harness/prompts/triage-skill.txt +10 -0
- package/skills/eval-harness/scripts/eval-gate.cjs +318 -0
- package/skills/eval-harness/scripts/extract-block.cjs +87 -0
- package/skills/eval-harness/scripts/gate-classification.cjs +28 -0
- package/skills/eval-harness/scripts/gen-models.sh +41 -0
- package/skills/eval-harness/scripts/gen-skill-prompt.cjs +177 -0
- package/skills/eval-harness/scripts/measure-classification.cjs +30 -0
- package/skills/eval-harness/scripts/parse-label.cjs +93 -0
- package/skills/eval-harness/scripts/verdict.cjs +132 -0
- package/skills/eval-harness/tasks/go-routing.jsonl +8 -0
- package/skills/eval-harness/tasks/incident-threshold.jsonl +7 -0
- package/skills/eval-harness/tasks/lane-inference.jsonl +7 -0
- package/skills/eval-harness/tasks/ticket-triage.jsonl +6 -0
- package/skills/eval-harness/tasks/tool-selection.jsonl +15 -0
- package/skills/eval-harness/test/eval-gate.test.sh +132 -0
- package/skills/eval-harness/test/extract-block.test.sh +87 -0
- package/skills/eval-harness/test/gate-classification.test.sh +77 -0
- package/skills/eval-harness/test/gen-models.test.sh +75 -0
- package/skills/eval-harness/test/measure-classification.test.sh +74 -0
- package/skills/eval-harness/test/parse-label.test.sh +129 -0
- package/skills/eval-harness/test/registry-completeness.test.sh +109 -0
- package/skills/eval-harness/test/verdict.test.sh +123 -0
- package/skills/every-style-editor/SKILL.md +142 -0
- package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/skills/feature-tweet/SKILL.md +199 -0
- package/skills/feature-video/SKILL.md +412 -0
- package/skills/feature-video/scripts/check_deps.sh +281 -0
- package/skills/file-todos/SKILL.md +267 -0
- package/skills/file-todos/assets/todo-template.md +166 -0
- package/skills/fix-issue/SKILL.md +278 -0
- package/skills/fix-issue/references/agent-authored-exclusion.md +120 -0
- package/skills/fix-issue/references/exclude-label-jq-snippet.md +97 -0
- package/skills/flag-bootstrap/SETUP.md +142 -0
- package/skills/flag-create/SKILL.md +145 -0
- package/skills/flag-create/scripts/create.sh +273 -0
- package/skills/flag-delete/SKILL.md +167 -0
- package/skills/flag-delete/scripts/delete.sh +288 -0
- package/skills/flag-list/SKILL.md +99 -0
- package/skills/flag-list/scripts/list.sh +275 -0
- package/skills/flag-set-role/SKILL.md +229 -0
- package/skills/flag-set-role/scripts/flip.sh +774 -0
- package/skills/frontend-anti-slop/SKILL.md +106 -0
- package/skills/frontend-anti-slop/references/anti-patterns.md +199 -0
- package/skills/frontend-anti-slop/references/slop-rules.md +65 -0
- package/skills/frontend-anti-slop/scripts/tier1-scan.ts +525 -0
- package/skills/frontend-design/SKILL.md +77 -0
- package/skills/gdpr-gate/NOTICE +136 -0
- package/skills/gdpr-gate/SKILL.md +341 -0
- package/skills/gdpr-gate/references/fields.md +160 -0
- package/skills/gdpr-gate/references/layers/api-layer.md +355 -0
- package/skills/gdpr-gate/references/layers/auth-sessions.md +290 -0
- package/skills/gdpr-gate/references/layers/data-in-transit.md +331 -0
- package/skills/gdpr-gate/references/layers/data-lifecycle.md +341 -0
- package/skills/gdpr-gate/references/layers/frontend.md +249 -0
- package/skills/gdpr-gate/references/layers/testing-seeding.md +256 -0
- package/skills/gdpr-gate/references/leakage-vectors.md +248 -0
- package/skills/gdpr-gate/references/legacy/legal-consent-v1-prose.md +48 -0
- package/skills/gdpr-gate/references/legal-consent.md +140 -0
- package/skills/gdpr-gate/references/non-negotiables.md +60 -0
- package/skills/gdpr-gate/scripts/gdpr-gate.sh +271 -0
- package/skills/gdpr-gate/scripts/notice-frontmatter.sh +337 -0
- package/skills/gdpr-gate/scripts/path-denylist.txt +39 -0
- package/skills/gdpr-gate/scripts/repo-scan.sh +158 -0
- package/skills/gdpr-gate/scripts/vendor-drift-classify.sh +120 -0
- package/skills/gdpr-gate/scripts/vendor-pin-integrity.sh +237 -0
- package/skills/gemini-imagegen/SKILL.md +283 -0
- package/skills/gemini-imagegen/requirements.txt +5 -0
- package/skills/gemini-imagegen/scripts/_error_handling.py +146 -0
- package/skills/gemini-imagegen/scripts/compose_images.py +150 -0
- package/skills/gemini-imagegen/scripts/edit_image.py +137 -0
- package/skills/gemini-imagegen/scripts/gemini_images.py +256 -0
- package/skills/gemini-imagegen/scripts/generate_image.py +147 -0
- package/skills/gemini-imagegen/scripts/multi_turn_chat.py +223 -0
- package/skills/gemini-imagegen/scripts/test_error_handling.py +179 -0
- package/skills/git-worktree/SKILL.md +534 -0
- package/skills/git-worktree/scripts/git-repo-readiness-diag.sh +98 -0
- package/skills/git-worktree/scripts/worktree-manager.sh +4773 -0
- package/skills/git-worktree/test/create-from-origin-main.test.sh +116 -0
- package/skills/git-worktree/test/lease-protects-active.test.sh +1056 -0
- package/skills/git-worktree/test/no-repo-fail-loud.test.sh +88 -0
- package/skills/git-worktree/test/orphan-reaper-honest-count.test.sh +379 -0
- package/skills/git-worktree/test/reap-archive-persistence.test.sh +725 -0
- package/skills/git-worktree/test/stale-lock-sweep.test.sh +176 -0
- package/skills/go/SKILL.md +54 -0
- package/skills/growth/SKILL.md +164 -0
- package/skills/harvest-debt/SKILL.md +78 -0
- package/skills/harvest-debt/scripts/harvest-debt.sh +114 -0
- package/skills/heal-skill/SKILL.md +173 -0
- package/skills/help/SKILL.md +46 -0
- package/skills/incident/NOTICE +39 -0
- package/skills/incident/SKILL.md +395 -0
- package/skills/incident/scripts/dry-run.sh +448 -0
- package/skills/incident/scripts/redact-engine.py +412 -0
- package/skills/incident/scripts/redact-sentinel.sh +44 -0
- package/skills/incident/templates/pir.md +139 -0
- package/skills/incident/test/fixtures/dry-run-incident.json +26 -0
- package/skills/incident/test/fixtures/dry-run-secret-leak.json +26 -0
- package/skills/incident/test/fixtures/legacy-bash-scanner.sh +82 -0
- package/skills/incident/test/fixtures/positive-corpus.md +87 -0
- package/skills/incident/test/redact-sentinel.test.sh +1420 -0
- package/skills/invoice/SKILL.md +206 -0
- package/skills/kb-glossary/SKILL.md +71 -0
- package/skills/kb-glossary/references/glossary-format.md +111 -0
- package/skills/kb-glossary/references/rejected-request-register.md +115 -0
- package/skills/kb-search/SKILL.md +276 -0
- package/skills/kb-search/scripts/kb-search-cache.sh +123 -0
- package/skills/legal-audit/SKILL.md +161 -0
- package/skills/legal-generate/NOTICE +242 -0
- package/skills/legal-generate/SKILL.md +244 -0
- package/skills/legal-generate/references/templates/advisor-agreement/template.md +12 -0
- package/skills/legal-generate/references/templates/business-associate-agreement/template.md +112 -0
- package/skills/legal-generate/references/templates/cookie-notice/template.md +82 -0
- package/skills/legal-generate/references/templates/dpa-global/template.md +439 -0
- package/skills/legal-generate/references/templates/dpa-us/template.md +222 -0
- package/skills/legal-generate/references/templates/employee-offer-letter/template.md +75 -0
- package/skills/legal-generate/references/templates/master-services-agreement/template.md +252 -0
- package/skills/legal-generate/references/templates/mutual-nda/template.md +66 -0
- package/skills/legal-generate/references/templates/one-way-nda/template.md +38 -0
- package/skills/legal-generate/references/templates/privacy-policy-gdpr/template.md +427 -0
- package/skills/legal-generate/references/templates/privacy-policy-us/template.md +313 -0
- package/skills/legal-generate/references/templates/terms-of-use/template.md +75 -0
- package/skills/legal-generate/scripts/strip-vendor-credit.sh +96 -0
- package/skills/linear-fetch/SKILL.md +247 -0
- package/skills/linear-fetch/scripts/assert-no-linear-telemetry.sh +66 -0
- package/skills/linear-fetch/scripts/redact-linear-urls.sh +51 -0
- package/skills/linear-fetch/scripts/render-caller-template.sh +36 -0
- package/skills/linear-fetch/test/assert-no-linear-telemetry.test.sh +113 -0
- package/skills/linear-fetch/test/parity.test.sh +112 -0
- package/skills/linear-fetch/test/persist-safe-integration.test.sh +191 -0
- package/skills/linear-fetch/test/redact-linear-urls.test.sh +226 -0
- package/skills/merge-pr/SKILL.md +670 -0
- package/skills/model-launch-review/SKILL.md +255 -0
- package/skills/model-launch-review/scripts/audit-models.sh +501 -0
- package/skills/one-shot/SKILL.md +316 -0
- package/skills/one-shot/references/token-discipline.md +74 -0
- package/skills/operator-bootstrap/SKILL.md +412 -0
- package/skills/operator-bootstrap/template.sh +439 -0
- package/skills/operator-digest/SKILL.md +287 -0
- package/skills/operator-digest/assets/operator-digest.workflow.yml +175 -0
- package/skills/operator-digest/scripts/digest-scrub.sh +119 -0
- package/skills/operator-digest/scripts/provision-operator-digest-repo.sh +147 -0
- package/skills/operator-rephrase/SKILL.md +98 -0
- package/skills/pencil-setup/SKILL.md +201 -0
- package/skills/pencil-setup/scripts/check_deps.sh +556 -0
- package/skills/pencil-setup/scripts/copy_adapter.sh +80 -0
- package/skills/pencil-setup/scripts/package-lock.json +1172 -0
- package/skills/pencil-setup/scripts/package.json +14 -0
- package/skills/pencil-setup/scripts/pencil-error-enrichment.mjs +21 -0
- package/skills/pencil-setup/scripts/pencil-mcp-adapter.mjs +733 -0
- package/skills/pencil-setup/scripts/pencil-response-classify.mjs +38 -0
- package/skills/pencil-setup/scripts/pencil-save-gate.mjs +26 -0
- package/skills/pencil-setup/scripts/sanitize-filename.mjs +25 -0
- package/skills/plan/SKILL.md +1140 -0
- package/skills/plan/references/differential-migration-testing.md +185 -0
- package/skills/plan/references/plan-community-discovery.md +42 -0
- package/skills/plan/references/plan-founder-check.md +137 -0
- package/skills/plan/references/plan-functional-overlap.md +25 -0
- package/skills/plan/references/plan-host-fanout.md +51 -0
- package/skills/plan/references/plan-issue-templates.md +779 -0
- package/skills/plan/references/plan-network-outage-checklist.md +131 -0
- package/skills/plan/references/plan-scope-check.md +126 -0
- package/skills/plan/references/plan-sharp-edges.md +331 -0
- package/skills/plan/references/plan-signoff-lifecycle.md +12 -0
- package/skills/plan/references/webapp-cwv-observability.md +143 -0
- package/skills/plan-review/SKILL.md +91 -0
- package/skills/plan-review/lib/named-panel.mjs +58 -0
- package/skills/plan-review/workflows/plan-review.workflow.js +477 -0
- package/skills/postmerge/SKILL.md +642 -0
- package/skills/postmerge/references/deploy-status-debugging.md +141 -0
- package/skills/postmerge/references/graceful-degradation.md +20 -0
- package/skills/postmerge/references/sentry-error-count-delta.md +137 -0
- package/skills/preflight/SKILL.md +1553 -0
- package/skills/preflight/references/check-13-founder-check.md +286 -0
- package/skills/preflight/scripts/founder-check.py +1640 -0
- package/skills/preflight/scripts/parse-form-a.awk +107 -0
- package/skills/preflight/scripts/probe-verb-gate.sh +108 -0
- package/skills/product-roadmap/SKILL.md +357 -0
- package/skills/product-roadmap/scripts/roadmap-reconcile.sh +327 -0
- package/skills/provision-cloudflare/SKILL.md +67 -0
- package/skills/provision-cloudflare/scripts/provision-cloudflare.sh +271 -0
- package/skills/provision-doppler/SKILL.md +58 -0
- package/skills/provision-doppler/scripts/provision-doppler.sh +358 -0
- package/skills/provision-github/SKILL.md +59 -0
- package/skills/provision-github/scripts/provision-github.sh +300 -0
- package/skills/provision-hetzner/SKILL.md +76 -0
- package/skills/provision-hetzner/scripts/provision-hetzner.sh +262 -0
- package/skills/provision-hetzner/test/fixture/knowledge-base/legal/tenant-dpa-register.md +26 -0
- package/skills/provision-hetzner/test/fixture/provision-hetzner-dry-run.golden +34 -0
- package/skills/provision-hetzner/test/provision-hetzner-characterization.test.sh +227 -0
- package/skills/qa/SKILL.md +263 -0
- package/skills/qa/references/browser-preflight.md +22 -0
- package/skills/questionnaire-generate/SKILL.md +328 -0
- package/skills/questionnaire-generate/references/questionnaire.template +60 -0
- package/skills/rclone/SKILL.md +162 -0
- package/skills/rclone/scripts/check_setup.sh +63 -0
- package/skills/release-announce/SKILL.md +65 -0
- package/skills/release-docs/SKILL.md +92 -0
- package/skills/reproduce-bug/SKILL.md +358 -0
- package/skills/resolve-debt/SKILL.md +85 -0
- package/skills/resolve-debt/scripts/resolve-debt.py +584 -0
- package/skills/resolve-parallel/SKILL.md +45 -0
- package/skills/resolve-parallel/workflows/resolve-parallel.workflow.js +415 -0
- package/skills/resolve-pr-parallel/SKILL.md +68 -0
- package/skills/resolve-pr-parallel/workflows/resolve-pr-parallel.workflow.js +419 -0
- package/skills/resolve-todo-parallel/SKILL.md +57 -0
- package/skills/resolve-todo-parallel/workflows/resolve-todo-parallel.workflow.js +398 -0
- package/skills/review/SKILL.md +1620 -0
- package/skills/review/references/anti-slop-scanner-hook.md +40 -0
- package/skills/review/references/defect-classes-migrations.md +9 -0
- package/skills/review/references/generated-input-oracle-surfaces.md +15 -0
- package/skills/review/references/review-e2e-testing.md +116 -0
- package/skills/review/references/review-evidence-comment.md +36 -0
- package/skills/review/references/review-todo-structure.md +165 -0
- package/skills/review/references/risk-tier-and-fix-rounds.md +97 -0
- package/skills/review/references/semgrep-custom-rules.yaml +174 -0
- package/skills/review/references/ultrathink-phases.md +98 -0
- package/skills/review/references/wfs.md +6 -0
- package/skills/review/scripts/emit-review-trailer.sh +499 -0
- package/skills/review/scripts/ensure-semgrep.sh +99 -0
- package/skills/review/scripts/fix-round-seats.sh +197 -0
- package/skills/review/test/emit-review-trailer.test.sh +387 -0
- package/skills/review/test/ensure-semgrep.test.sh +111 -0
- package/skills/review/test/fix-round-seats.test.sh +237 -0
- package/skills/review/workflows/README.md +147 -0
- package/skills/review/workflows/review.workflow.js +667 -0
- package/skills/schedule/SKILL.md +796 -0
- package/skills/seo-aeo/SKILL.md +139 -0
- package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
- package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
- package/skills/ship/SKILL.md +3023 -0
- package/skills/ship/references/adr-ordinal-collision.md +47 -0
- package/skills/ship/references/affected-gate-contract.md +115 -0
- package/skills/ship/references/ci-run-diagnosis.md +29 -0
- package/skills/ship/references/ci-workflow-authoring.md +68 -0
- package/skills/ship/references/cleanup-merged-archival.md +39 -0
- package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
- package/skills/ship/references/cto-ruling-armed-push.md +5 -0
- package/skills/ship/references/decision-challenges-render.md +71 -0
- package/skills/ship/references/delegated-act-authoring-form.md +7 -0
- package/skills/ship/references/detached-battery-verdict.md +13 -0
- package/skills/ship/references/epic-close-sweep.md +62 -0
- package/skills/ship/references/feature-tweet-draft.md +42 -0
- package/skills/ship/references/followthrough-issue-body-template.md +47 -0
- package/skills/ship/references/followthrough-stub-template.sh +92 -0
- package/skills/ship/references/merge-queue-dequeue.md +39 -0
- package/skills/ship/references/operator-holds.md +179 -0
- package/skills/ship/references/pipeline-tally-render.md +43 -0
- package/skills/ship/references/pr-overlap.md +153 -0
- package/skills/ship/references/queue-mode.md +7 -0
- package/skills/ship/references/ready-run-wait.md +36 -0
- package/skills/ship/references/red-on-main-quarantine.md +110 -0
- package/skills/ship/references/register-update-pr-pattern.md +88 -0
- package/skills/ship/references/settle-then-admin-merge.md +130 -0
- package/skills/ship/references/supabase-column-probe.md +13 -0
- package/skills/ship/scripts/auto-close-scan.sh +76 -0
- package/skills/ship/scripts/battery-owed.sh +403 -0
- package/skills/ship/scripts/hold-gate-eval.sh +410 -0
- package/skills/ship/scripts/net-issue-flow.sh +665 -0
- package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
- package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
- package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
- package/skills/skill-creator/SKILL.md +299 -0
- package/skills/skill-creator/references/api-security.md +248 -0
- package/skills/skill-creator/references/authoring-levers.md +110 -0
- package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
- package/skills/skill-creator/references/best-practices.md +431 -0
- package/skills/skill-creator/references/common-patterns.md +639 -0
- package/skills/skill-creator/references/core-principles.md +193 -0
- package/skills/skill-creator/references/executable-code.md +188 -0
- package/skills/skill-creator/references/iteration-and-testing.md +509 -0
- package/skills/skill-creator/references/official-spec.md +193 -0
- package/skills/skill-creator/references/recommended-structure.md +180 -0
- package/skills/skill-creator/references/skill-structure.md +396 -0
- package/skills/skill-creator/references/using-scripts.md +122 -0
- package/skills/skill-creator/references/using-templates.md +120 -0
- package/skills/skill-creator/references/workflows-and-validation.md +541 -0
- package/skills/skill-creator/scripts/init_skill.py +322 -0
- package/skills/skill-creator/scripts/package_skill.py +110 -0
- package/skills/skill-creator/scripts/quick_validate.py +64 -0
- package/skills/skill-creator/templates/router-skill.md +73 -0
- package/skills/skill-creator/templates/simple-skill.md +34 -0
- package/skills/skill-creator/workflows/add-reference.md +96 -0
- package/skills/skill-creator/workflows/add-script.md +104 -0
- package/skills/skill-creator/workflows/add-template.md +83 -0
- package/skills/skill-creator/workflows/add-workflow.md +128 -0
- package/skills/skill-creator/workflows/audit-skill.md +159 -0
- package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
- package/skills/skill-creator/workflows/create-new-skill.md +207 -0
- package/skills/skill-creator/workflows/get-guidance.md +134 -0
- package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
- package/skills/skill-creator/workflows/verify-skill.md +226 -0
- package/skills/skill-security-scan/SKILL.md +145 -0
- package/skills/skill-security-scan/references/disclaimer.md +21 -0
- package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
- package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
- package/skills/skill-security-scan/references/override-mechanism.md +171 -0
- package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
- package/skills/skill-security-scan/references/regex-patterns.md +136 -0
- package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
- package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
- package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
- package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
- package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
- package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
- package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
- package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
- package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
- package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
- package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
- package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
- package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
- package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
- package/skills/skill-security-scan/scripts/lib.sh +197 -0
- package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
- package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
- package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
- package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
- package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
- package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
- package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
- package/skills/social-distribute/SKILL.md +485 -0
- package/skills/spec-templates/SKILL.md +274 -0
- package/skills/sync/SKILL.md +49 -0
- package/skills/test-browser/SKILL.md +464 -0
- package/skills/test-browser/references/feature-map.md +161 -0
- package/skills/test-fix-loop/SKILL.md +159 -0
- package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
- package/skills/triage/SKILL.md +402 -0
- package/skills/trigger-cron/SKILL.md +136 -0
- package/skills/trigger-cron/scripts/trigger.sh +198 -0
- package/skills/user-set-role/SKILL.md +147 -0
- package/skills/user-set-role/scripts/set-role.sh +218 -0
- package/skills/user-story-writer/SKILL.md +77 -0
- package/skills/ux-audit/SKILL.md +251 -0
- package/skills/ux-audit/references/finding.schema.json +62 -0
- package/skills/ux-audit/references/route-list.yaml +95 -0
- package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
- package/skills/ux-audit/scripts/bot-signin.ts +135 -0
- package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
- package/skills/verification-scaffold/SKILL.md +73 -0
- package/skills/verification-scaffold/references/gitleaks.toml.template +13 -0
- package/skills/verification-scaffold/references/guardrail-audit.md +156 -0
- package/skills/verification-scaffold/references/guards/block-stash-hook.sh.template +72 -0
- package/skills/verification-scaffold/references/guards/commit-on-main-hook.sh.template +82 -0
- package/skills/verification-scaffold/references/guards/orphan-test-check.sh.template +102 -0
- package/skills/verification-scaffold/references/guards/precommit-guard.sh.template +115 -0
- package/skills/verification-scaffold/references/guards/secret-scan-hook.sh.template +176 -0
- package/skills/verification-scaffold/references/guards/verification-gates.yml.template +49 -0
- package/skills/verification-scaffold/references/guards/wrapper-boundary.sh.template +47 -0
- package/skills/verification-scaffold/references/guards.tsv +21 -0
- package/skills/verification-scaffold/references/verification-readme.template +55 -0
- package/skills/verification-scaffold/scripts/verification-scaffold.sh +785 -0
- package/skills/verification-scaffold/test/bite-proof.test.sh +159 -0
- package/skills/verification-scaffold/test/generator.test.sh +319 -0
- package/skills/verification-scaffold/test/manifest.test.sh +144 -0
- package/skills/work/SKILL.md +1480 -0
- package/skills/work/references/end-of-work-resume.md +7 -0
- package/skills/work/references/hook-test-main-cwd.md +24 -0
- package/skills/work/references/key-principles.md +83 -0
- package/skills/work/references/property-testing.md +122 -0
- package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
- package/skills/work/references/work-agent-teams.md +97 -0
- package/skills/work/references/work-lifecycle-parallel.md +147 -0
- package/skills/work/references/work-lockfile-bumps.md +97 -0
- package/skills/work/references/work-scratch-sandboxes.md +212 -0
- package/skills/work/references/work-subagent-fanout.md +61 -0
- package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
- package/skills/xcode-test/SKILL.md +360 -0
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# affected-tests.sh — the plugin-generic, stack-detecting affected-only test
|
|
3
|
+
# gate (#9307 deliverable 2). One entry point for soleur:work Phase 2,
|
|
4
|
+
# soleur:ship Phase 4, soleur:review's lead gate and grok-pre-push-gate.sh, in
|
|
5
|
+
# ANY repository — including ones with no scripts/test-all.sh.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# bash affected-tests.sh [--full] [--base <ref>] [--explain] [--self-check]
|
|
9
|
+
#
|
|
10
|
+
# Dispatch precedence (exactly this order):
|
|
11
|
+
# 1. `test_command` in repo-root `.soleur-test-gate.json` — run via `bash -c`
|
|
12
|
+
# from the repo root; bypasses selection entirely.
|
|
13
|
+
# 2. A repo runner ADVERTISING the Soleur capability — `scripts/test-all.sh`
|
|
14
|
+
# whose `--print-selection` flag is registered at a case-label or
|
|
15
|
+
# usage-table position (a comment-only mention does NOT count). The probe
|
|
16
|
+
# runs BEFORE any gate-side classification: a capability-positive runner
|
|
17
|
+
# owns the diff decision (its own inert/global/empty handling is strictly
|
|
18
|
+
# better informed), so a resolved runner is ALWAYS delegated to —
|
|
19
|
+
# `--affected`, or `--full` only under this script's `--full`. A foreign
|
|
20
|
+
# script of that name is NEVER invoked with Soleur flags — it falls
|
|
21
|
+
# through to 3.
|
|
22
|
+
# 3. Stack adapters, in order: nx/turbo, JS/TS (vitest `related` SUBCOMMAND /
|
|
23
|
+
# jest --findRelatedTests), Python (pytest + testmon/xdist), Go (`go list`
|
|
24
|
+
# reverse-dependency closure). The gate's own base resolution, changed-set
|
|
25
|
+
# census and global/inert classification apply ONLY to this arm — under
|
|
26
|
+
# delegation the runner resolves its own base and classification, and
|
|
27
|
+
# `--base` steers the adapters alone.
|
|
28
|
+
#
|
|
29
|
+
# Exit contract — produced ONLY by finish() in lib/affected-tests/common.sh,
|
|
30
|
+
# the single exit site for the whole gate:
|
|
31
|
+
# 0 every selected test passed, OR NO_TESTS_AFFECTED for an all-inert diff
|
|
32
|
+
# 1 a selected test failed, or the delegated runner was signal-killed (rc 3
|
|
33
|
+
# maps to 1 reason=runner-signal); a failure seen before budget expiry
|
|
34
|
+
# wins over deferral
|
|
35
|
+
# 4 AFFECTED_GATE_REFUSED — no-stack, bad-config key=<k>,
|
|
36
|
+
# vacuous-test-command, no-base-ref, not-a-repo (--self-check outside a
|
|
37
|
+
# repository), toolchain-missing, runner-refused,
|
|
38
|
+
# foreign-runner (a foreign scripts/test-all.sh AND no adapter resolves)
|
|
39
|
+
# 6 AFFECTED_GATE_BUDGET_DEFERRED — budget expired AND a CI test workflow was
|
|
40
|
+
# detected; CI stays the authoritative gate
|
|
41
|
+
# 7 no test suite detected at all — disclosed note, never labelled passed
|
|
42
|
+
#
|
|
43
|
+
# Output contract:
|
|
44
|
+
# human: `Ran X of the repo's N test files affected by this change:
|
|
45
|
+
# passed.` (N is the repo-wide census, not the affected subset) —
|
|
46
|
+
# never the words "tests verified"; beside it the literal verdict
|
|
47
|
+
# `affected-suite gate passed.`
|
|
48
|
+
# inert: `NO_TESTS_AFFECTED inert_rule=<pat>` + `No tests ran: docs-only change.`
|
|
49
|
+
# marker: `AFFECTED_GATE ran=<X> of=<N> skipped=<Y>
|
|
50
|
+
# mode=<selected|full|custom|explain|none|help> reason=<…> stack=<…>
|
|
51
|
+
# ci=<file|none>` on every verdict path including refusals; counts
|
|
52
|
+
# are `-` on paths that produce no honest count (delegated runner,
|
|
53
|
+
# opaque test_command, refusals) — a delegated or custom rc 0 still
|
|
54
|
+
# prints `affected-suite gate passed.`
|
|
55
|
+
#
|
|
56
|
+
# `.soleur-test-gate.json` (repo root, committed, every key optional; unknown
|
|
57
|
+
# key / malformed JSON / wrong type → rc 4 reason=bad-config key=<k>):
|
|
58
|
+
# test_command string full-run command; true/`:`/`exit 0`/bare `echo`,
|
|
59
|
+
# or any command whose LAST ;/&&/||/| segment is a
|
|
60
|
+
# vacuous primitive (`cmd || true`, `cmd ; exit 0`,
|
|
61
|
+
# `bash -c 'exit 0'`) → rc 4 reason=vacuous-test-command
|
|
62
|
+
# (a documented denylist, not a proof)
|
|
63
|
+
# global_paths string[] REPLACES built-ins; any hit forces the full command
|
|
64
|
+
# inert_paths string[] REPLACES built-ins (`**/*.md` default); an all-inert
|
|
65
|
+
# diff → rc 0 NO_TESTS_AFFECTED; a mixed diff is never
|
|
66
|
+
# inert. A match-everything pattern (`*`, `**`,
|
|
67
|
+
# `**/*`, anything matching an arbitrary path) is a
|
|
68
|
+
# permanent green → rc 4 bad-config key=inert_paths
|
|
69
|
+
# budget_seconds number wall-clock cap, enforced ONLY when a CI test
|
|
70
|
+
# workflow is detected; default 900 (unmeasured);
|
|
71
|
+
# must be a positive integer
|
|
72
|
+
# max_workers number overrides the width formula; positive integer
|
|
73
|
+
# budget_ci boolean forces CI-deferral eligibility without a workflow
|
|
74
|
+
#
|
|
75
|
+
# Base-ref order (adapter arm only — a delegated runner resolves its own):
|
|
76
|
+
# --base > @{upstream} > origin/HEAD > origin/main >
|
|
77
|
+
# origin/master > merge-base over every remote-tracking ref (newest wins).
|
|
78
|
+
# Changed set = committed-vs-base ∪ staged ∪ unstaged ∪ untracked. A shallow
|
|
79
|
+
# clone gets ONE bounded deepen, then rc 4 reason=no-base-ref — never a silent
|
|
80
|
+
# full, never a silent empty.
|
|
81
|
+
#
|
|
82
|
+
# CI detection: `.github/workflows/*.yml|*.yaml` carrying a pull_request/push
|
|
83
|
+
# trigger AND a test-command shape, or `budget_ci: true`. No CI → the budget is
|
|
84
|
+
# ignored, run to completion, an upfront line says so.
|
|
85
|
+
#
|
|
86
|
+
# Worker width (unmeasured heuristic): max(1, ncpu/2) / (1 + other running
|
|
87
|
+
# affected-tests.sh processes) — counted on the argv SLOT, never pgrep -f or
|
|
88
|
+
# ps|grep; max_workers overrides. Maps to vitest/jest --maxWorkers, pytest -n,
|
|
89
|
+
# go test -p, nx --parallel, turbo --concurrency.
|
|
90
|
+
#
|
|
91
|
+
# Portability contract (asserted by affected-tests.test.sh): no bare `timeout`
|
|
92
|
+
# (timeout→gtimeout→bounded-watchdog in lib common.sh), no `nproc` outside the
|
|
93
|
+
# ncpu fallback chain, no `sed -i`, `date -d`, `readlink -f`, `stat -c`. Bash
|
|
94
|
+
# 3.2-safe: no `declare -A`, no `mapfile`, `${arr[@]+"${arr[@]}"}` under set -u.
|
|
95
|
+
#
|
|
96
|
+
# External binaries invoked across this script and its lib:
|
|
97
|
+
# core: bash git grep find sort tr cut head tail awk sed cat date sleep
|
|
98
|
+
# kill ps getconf sysctl nproc mktemp rm wc
|
|
99
|
+
# config: python3 node jq (first resolvable, schema is validated in bash)
|
|
100
|
+
# runners: scripts/test-all.sh (capability-probed), vitest jest npx npm pnpm
|
|
101
|
+
# yarn bun pytest go nx turbo
|
|
102
|
+
# bounds: timeout gtimeout (either may be absent — watchdog covers)
|
|
103
|
+
#
|
|
104
|
+
# Cloud (Devin): the gate resolves its libs by BASH_SOURCE and needs no plugin
|
|
105
|
+
# root — CLAUDE_PLUGIN_ROOT may be unset. A detected stack with no runnable
|
|
106
|
+
# toolchain → rc 4 reason=toolchain-missing.
|
|
107
|
+
|
|
108
|
+
set -euo pipefail
|
|
109
|
+
|
|
110
|
+
GATE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
111
|
+
# shellcheck source=plugins/soleur/scripts/lib/affected-tests/common.sh
|
|
112
|
+
source "$GATE_DIR/lib/affected-tests/common.sh"
|
|
113
|
+
# shellcheck source=plugins/soleur/scripts/lib/affected-tests/nx-turbo.sh
|
|
114
|
+
source "$GATE_DIR/lib/affected-tests/nx-turbo.sh"
|
|
115
|
+
# shellcheck source=plugins/soleur/scripts/lib/affected-tests/js.sh
|
|
116
|
+
source "$GATE_DIR/lib/affected-tests/js.sh"
|
|
117
|
+
# shellcheck source=plugins/soleur/scripts/lib/affected-tests/python.sh
|
|
118
|
+
source "$GATE_DIR/lib/affected-tests/python.sh"
|
|
119
|
+
# shellcheck source=plugins/soleur/scripts/lib/affected-tests/go.sh
|
|
120
|
+
source "$GATE_DIR/lib/affected-tests/go.sh"
|
|
121
|
+
|
|
122
|
+
# Scratch files (config dump, changed-set census, adapter logs) are registered
|
|
123
|
+
# in AFFECTED_SCRATCH by affected_mktemp — remove them on every exit shape,
|
|
124
|
+
# including the early refusal paths that never reach an adapter's own rm.
|
|
125
|
+
# EXIT alone leaves signal semantics intact — trapping INT/TERM on the same
|
|
126
|
+
# handler would swallow the signal and resume at the interrupted statement.
|
|
127
|
+
trap 'affected_scratch_clean' EXIT
|
|
128
|
+
trap 'affected_scratch_clean; exit 130' INT
|
|
129
|
+
trap 'affected_scratch_clean; exit 143' TERM
|
|
130
|
+
|
|
131
|
+
usage() {
|
|
132
|
+
sed -n '1,20p' "$0" | grep -E '^#' | sed 's/^# \{0,1\}//'
|
|
133
|
+
cat <<'EOF'
|
|
134
|
+
Usage:
|
|
135
|
+
bash affected-tests.sh [--full] [--base <ref>] [--explain] [--self-check]
|
|
136
|
+
|
|
137
|
+
--full bypass selection; run the detected full command
|
|
138
|
+
--base <ref> override base-ref resolution for the changed set
|
|
139
|
+
--explain print the selection and per-path reasons; run nothing
|
|
140
|
+
--self-check validate config + toolchain probe only; never runs tests
|
|
141
|
+
EOF
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# --- args ---------------------------------------------------------------------
|
|
145
|
+
ARG_FULL=0
|
|
146
|
+
ARG_BASE=""
|
|
147
|
+
ARG_EXPLAIN=0
|
|
148
|
+
ARG_SELFCHECK=0
|
|
149
|
+
while (( $# > 0 )); do
|
|
150
|
+
case "$1" in
|
|
151
|
+
--full) ARG_FULL=1 ;;
|
|
152
|
+
--base) shift
|
|
153
|
+
(( $# > 0 )) || { echo "affected-tests: --base needs a ref" >&2
|
|
154
|
+
finish 4 none bad-usage - none - - -; }
|
|
155
|
+
ARG_BASE="$1" ;;
|
|
156
|
+
--explain) ARG_EXPLAIN=1 ;;
|
|
157
|
+
--self-check) ARG_SELFCHECK=1 ;;
|
|
158
|
+
-h|--help) usage; finish 0 help help - none - - - ;;
|
|
159
|
+
*) echo "affected-tests: unknown argument '$1' (try --help)" >&2
|
|
160
|
+
finish 4 none bad-usage - none - - - ;;
|
|
161
|
+
esac
|
|
162
|
+
shift
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
# --- repository root ------------------------------------------------------------
|
|
166
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
167
|
+
if [[ -z "$REPO_ROOT" ]]; then
|
|
168
|
+
# Outside a repository the cause is not a missing base ref — name it.
|
|
169
|
+
(( ARG_SELFCHECK )) && finish 4 none not-a-repo - none - - -
|
|
170
|
+
finish 4 none no-base-ref - none - - -
|
|
171
|
+
fi
|
|
172
|
+
cd "$REPO_ROOT" || finish 4 none no-base-ref - none - - -
|
|
173
|
+
|
|
174
|
+
# --- CI detection (needed early: it arms the budget and fills ci=) ----------------
|
|
175
|
+
CI_NAME=none
|
|
176
|
+
affected_detect_ci() {
|
|
177
|
+
local f
|
|
178
|
+
for f in .github/workflows/*.yml .github/workflows/*.yaml; do
|
|
179
|
+
[[ -f "$f" ]] || continue
|
|
180
|
+
# pull_request or push trigger AND a test-command shape in the same file —
|
|
181
|
+
# a workflow that merely exists is not a gate to defer to. `#` comments are
|
|
182
|
+
# stripped BEFORE matching: a deploy workflow that only TALKS about tests
|
|
183
|
+
# in a comment is not a test gate to defer to.
|
|
184
|
+
#
|
|
185
|
+
# The trigger must be a yaml DECLARATION, not a substring — a bare
|
|
186
|
+
# `pull_request|push` grep reads `run: git push` or `pushd deploy/` inside a
|
|
187
|
+
# step as a CI test gate. Accepted shapes, tracked inside the `on:` block:
|
|
188
|
+
# on: push on: [push, pull_request] on: {push: …}
|
|
189
|
+
# on:\n push: on:\n pull_request: on:\n - push
|
|
190
|
+
sed 's/#.*//' "$f" | awk '
|
|
191
|
+
# `on:` is a TOP-LEVEL yaml key — column 0 only. An indented `on:` inside a
|
|
192
|
+
# `run: |` block scalar is script text, not a trigger declaration.
|
|
193
|
+
/^on[[:space:]]*:/ {
|
|
194
|
+
rest = $0
|
|
195
|
+
sub(/^on[[:space:]]*:[[:space:]]*/, "", rest)
|
|
196
|
+
# Scalar / flow sequence / flow map on the on: line itself.
|
|
197
|
+
if (rest ~ /(^|[][{},[:space:]])(pull_request|push)([]},:[:space:]]|$)/) found = 1
|
|
198
|
+
inon = 1
|
|
199
|
+
next
|
|
200
|
+
}
|
|
201
|
+
# The block ends at the next top-level (column-0) key; blank lines do not.
|
|
202
|
+
inon && /^[^[:space:]]/ { inon = 0 }
|
|
203
|
+
# Block-map key (`push:`/`pull_request:`) or sequence entry (`- push`).
|
|
204
|
+
inon && /^[[:space:]]*-?[[:space:]]*(pull_request|push)[[:space:]]*:/ { found = 1 }
|
|
205
|
+
inon && /^[[:space:]]*-[[:space:]]*(pull_request|push)[[:space:]]*$/ { found = 1 }
|
|
206
|
+
END { exit(!found) }
|
|
207
|
+
' || continue
|
|
208
|
+
if grep -qE 'test-all\.sh|npm[[:space:]]+(run[[:space:]]+)?test|yarn[[:space:]]+test|pnpm[[:space:]]+(run[[:space:]]+)?test|bun[[:space:]]+test|vitest|jest|pytest|go[[:space:]]+test|cargo[[:space:]]+test|ctest|make[[:space:]]+test|deno[[:space:]]+test' < <(sed 's/#.*//' "$f"); then
|
|
209
|
+
CI_NAME="$(affected_marker_field "$f")"
|
|
210
|
+
return 0
|
|
211
|
+
fi
|
|
212
|
+
done
|
|
213
|
+
return 1
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# --- config: .soleur-test-gate.json ------------------------------------------------
|
|
217
|
+
CFG_TEST_COMMAND=""
|
|
218
|
+
CFG_BUDGET_SECONDS=900 # default, unmeasured
|
|
219
|
+
CFG_MAX_WORKERS=""
|
|
220
|
+
CFG_BUDGET_CI=false
|
|
221
|
+
CFG_GLOBAL_SET=0
|
|
222
|
+
CFG_INERT_SET=0
|
|
223
|
+
CFG_GLOBAL_PATHS=()
|
|
224
|
+
CFG_INERT_PATHS=()
|
|
225
|
+
|
|
226
|
+
# Dump the JSON object as NUL-separated KEY\0TYPE\0VALUE\0 triples on stdout —
|
|
227
|
+
# array elements arrive as one `elem:<type>` record each, an empty array as a
|
|
228
|
+
# bare `array` record so present-but-empty stays distinct from absent. The
|
|
229
|
+
# SCHEMA is validated by the caller; these emitters only decode. rc 3 = the
|
|
230
|
+
# file is not a JSON object (malformed).
|
|
231
|
+
_affected_json_dump() {
|
|
232
|
+
local f="$1"
|
|
233
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
234
|
+
# -I: isolated mode keeps the repo's cwd OFF sys.path — a repo-controlled
|
|
235
|
+
# pytest.py/sitecustomize.py must never execute during a config read.
|
|
236
|
+
python3 -I - "$f" <<'PYEOF'
|
|
237
|
+
import json, sys
|
|
238
|
+
try:
|
|
239
|
+
d = json.load(open(sys.argv[1]))
|
|
240
|
+
except Exception:
|
|
241
|
+
sys.exit(3)
|
|
242
|
+
if not isinstance(d, dict):
|
|
243
|
+
sys.exit(3)
|
|
244
|
+
def jt(v):
|
|
245
|
+
if isinstance(v, bool): return 'boolean'
|
|
246
|
+
if isinstance(v, (int, float)): return 'number'
|
|
247
|
+
if isinstance(v, str): return 'string'
|
|
248
|
+
if isinstance(v, list): return 'array'
|
|
249
|
+
if v is None: return 'null'
|
|
250
|
+
return 'object'
|
|
251
|
+
out = sys.stdout
|
|
252
|
+
for k, v in d.items():
|
|
253
|
+
if isinstance(v, list):
|
|
254
|
+
if not v:
|
|
255
|
+
out.write(k + '\0array\0\0')
|
|
256
|
+
for e in v:
|
|
257
|
+
out.write(k + '\0elem:' + jt(e) + '\0' + (e if isinstance(e, str) else json.dumps(e)) + '\0')
|
|
258
|
+
else:
|
|
259
|
+
out.write(k + '\0' + jt(v) + '\0' + (v if isinstance(v, str) else json.dumps(v)) + '\0')
|
|
260
|
+
PYEOF
|
|
261
|
+
return $?
|
|
262
|
+
fi
|
|
263
|
+
if command -v node >/dev/null 2>&1; then
|
|
264
|
+
node - "$f" <<'JSEOF'
|
|
265
|
+
const fs = require('fs');
|
|
266
|
+
let d;
|
|
267
|
+
try { d = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); } catch (e) { process.exit(3); }
|
|
268
|
+
if (typeof d !== 'object' || d === null || Array.isArray(d)) process.exit(3);
|
|
269
|
+
const jt = v => v === null ? 'null' : Array.isArray(v) ? 'array' : typeof v;
|
|
270
|
+
for (const [k, v] of Object.entries(d)) {
|
|
271
|
+
if (Array.isArray(v)) {
|
|
272
|
+
if (!v.length) process.stdout.write(k + '\0array\0\0');
|
|
273
|
+
for (const e of v) process.stdout.write(k + '\0elem:' + jt(e) + '\0' + (typeof e === 'string' ? e : JSON.stringify(e)) + '\0');
|
|
274
|
+
} else {
|
|
275
|
+
process.stdout.write(k + '\0' + jt(v) + '\0' + (typeof v === 'string' ? v : JSON.stringify(v)) + '\0');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
JSEOF
|
|
279
|
+
return $?
|
|
280
|
+
fi
|
|
281
|
+
if command -v jq >/dev/null 2>&1; then
|
|
282
|
+
# Same contract as the py/node emitters: a non-object top level is not a
|
|
283
|
+
# config — `[]` decodes to ZERO records here (silent accept) while py/node
|
|
284
|
+
# exit 3. Refuse it identically before the dump.
|
|
285
|
+
jq -e 'type == "object"' "$f" >/dev/null 2>&1 || return 3
|
|
286
|
+
jq -j '
|
|
287
|
+
to_entries[] | .key as $k | (.value | type) as $t |
|
|
288
|
+
if $t == "array" then
|
|
289
|
+
if (.value | length) == 0 then ($k, "\u0000", "array", "\u0000", "\u0000")
|
|
290
|
+
else (.value[] | ($k, "\u0000", ("elem:" + (. | type)), "\u0000",
|
|
291
|
+
(if (. | type) == "string" then . else tojson end), "\u0000"))
|
|
292
|
+
end
|
|
293
|
+
else ($k, "\u0000", $t, "\u0000",
|
|
294
|
+
(if $t == "string" then .value else (.value | tojson) end), "\u0000")
|
|
295
|
+
end' "$f" 2>/dev/null
|
|
296
|
+
return $?
|
|
297
|
+
fi
|
|
298
|
+
# No JSON engine: a present config we cannot parse is a toolchain fact, and
|
|
299
|
+
# the fail direction is CLOSED.
|
|
300
|
+
return 2
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
# Load and validate. Fail-closed everywhere: malformed, unknown key, or wrong
|
|
304
|
+
# type → rc 4 reason=bad-config key=<k>; a vacuous test_command →
|
|
305
|
+
# reason=vacuous-test-command.
|
|
306
|
+
affected_load_config() {
|
|
307
|
+
local cfg=".soleur-test-gate.json"
|
|
308
|
+
[[ -f "$cfg" ]] || return 0
|
|
309
|
+
local tmp rc=0
|
|
310
|
+
affected_mktemp cfg || finish 4 none mktemp-failed - "$CI_NAME" - - -; tmp="$AFFECTED_TMP"
|
|
311
|
+
_affected_json_dump "$cfg" >"$tmp" || rc=$?
|
|
312
|
+
if (( rc == 2 )); then
|
|
313
|
+
finish 4 none "toolchain-missing" config "$CI_NAME" - - -
|
|
314
|
+
fi
|
|
315
|
+
if (( rc != 0 )); then
|
|
316
|
+
finish 4 none "bad-config key=$(affected_marker_field "$cfg")" config "$CI_NAME" - - -
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
local k t v badkey=""
|
|
320
|
+
while IFS= read -r -d '' k && IFS= read -r -d '' t && IFS= read -r -d '' v; do
|
|
321
|
+
case "$k" in
|
|
322
|
+
test_command)
|
|
323
|
+
[[ "$t" == string ]] || { badkey="$k"; break; }
|
|
324
|
+
CFG_TEST_COMMAND="$v" ;;
|
|
325
|
+
global_paths|inert_paths)
|
|
326
|
+
if [[ "$t" == array ]]; then
|
|
327
|
+
[[ "$k" == global_paths ]] && CFG_GLOBAL_SET=1 || CFG_INERT_SET=1
|
|
328
|
+
elif [[ "$t" == elem:string ]]; then
|
|
329
|
+
[[ "$k" == global_paths ]] && { CFG_GLOBAL_SET=1; CFG_GLOBAL_PATHS+=("$v"); } \
|
|
330
|
+
|| { CFG_INERT_SET=1; CFG_INERT_PATHS+=("$v"); }
|
|
331
|
+
else
|
|
332
|
+
badkey="$k"; break
|
|
333
|
+
fi ;;
|
|
334
|
+
budget_seconds)
|
|
335
|
+
[[ "$t" == number ]] || { badkey="$k"; break; }
|
|
336
|
+
v="${v%%.*}"
|
|
337
|
+
# Clamped: a zero/negative/decimal-floor-0 budget would defer instantly
|
|
338
|
+
# or arm a nonsense deadline — that is a config error, fail closed.
|
|
339
|
+
[[ "$v" =~ ^[0-9]+$ ]] && (( v >= 1 )) || { badkey="$k"; break; }
|
|
340
|
+
CFG_BUDGET_SECONDS="$v" ;;
|
|
341
|
+
max_workers)
|
|
342
|
+
[[ "$t" == number ]] || { badkey="$k"; break; }
|
|
343
|
+
v="${v%%.*}"
|
|
344
|
+
[[ "$v" =~ ^[0-9]+$ ]] && (( v >= 1 )) || { badkey="$k"; break; }
|
|
345
|
+
CFG_MAX_WORKERS="$v" ;;
|
|
346
|
+
budget_ci)
|
|
347
|
+
[[ "$t" == boolean ]] || { badkey="$k"; break; }
|
|
348
|
+
CFG_BUDGET_CI="$v" ;;
|
|
349
|
+
*) badkey="$k"; break ;;
|
|
350
|
+
esac
|
|
351
|
+
done <"$tmp"
|
|
352
|
+
if [[ -n "$badkey" ]]; then
|
|
353
|
+
finish 4 none "bad-config key=$(affected_marker_field "$badkey")" config "$CI_NAME" - - -
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
if [[ -n "$CFG_TEST_COMMAND" ]]; then
|
|
357
|
+
# Vacuous escape hatches are refused — `true`, `:`, `exit 0`, a bare echo,
|
|
358
|
+
# and every composition that ends in a swallower: `cmd || true`,
|
|
359
|
+
# `cmd ; exit 0`, `cmd && true`, `cmd | true`, `bash -c 'exit 0'`,
|
|
360
|
+
# `true # comment`. The check is a documented DENYLIST (common.sh
|
|
361
|
+
# _affected_cmd_vacuous): comments are stripped, whitespace normalised, the
|
|
362
|
+
# command split on ;/&&/||/| — a vacuous LAST segment decides the whole
|
|
363
|
+
# command's rc, so it fails closed. What is not listed still runs verbatim.
|
|
364
|
+
if _affected_cmd_vacuous "$CFG_TEST_COMMAND"; then
|
|
365
|
+
finish 4 custom vacuous-test-command custom "$CI_NAME" - - -
|
|
366
|
+
fi
|
|
367
|
+
fi
|
|
368
|
+
}
|
|
369
|
+
affected_detect_ci || true # a miss is the common case; never fatal
|
|
370
|
+
affected_load_config
|
|
371
|
+
if [[ "$CI_NAME" == none && "$CFG_BUDGET_CI" == true ]]; then
|
|
372
|
+
CI_NAME="config:budget_ci"
|
|
373
|
+
fi
|
|
374
|
+
|
|
375
|
+
# --- self-check -------------------------------------------------------------------
|
|
376
|
+
if (( ARG_SELFCHECK )); then
|
|
377
|
+
# Config load + toolchain probe only; never runs tests. Bounded, not instant:
|
|
378
|
+
# every probe is `command -v` or a bounded --version, so the worst case is
|
|
379
|
+
# the per-stack timeout sum (~80 s over 4 stacks), the happy path seconds.
|
|
380
|
+
command -v git >/dev/null 2>&1 || finish 4 none toolchain-missing config "$CI_NAME" - - -
|
|
381
|
+
# A capability-positive runner owns the run under delegation — adapter
|
|
382
|
+
# toolchains are then unreachable, so a missing one is informational only.
|
|
383
|
+
_sc_runner=0
|
|
384
|
+
if [[ -f scripts/test-all.sh ]] \
|
|
385
|
+
&& grep -Eq '^[[:space:]]*(--[[:alnum:]_-]+\|)*--print-selection([])|,[:space:]])' scripts/test-all.sh; then
|
|
386
|
+
_sc_runner=1
|
|
387
|
+
fi
|
|
388
|
+
# Probe each detected stack's toolchain: detection is free, and each tool()
|
|
389
|
+
# --version probe is already bounded.
|
|
390
|
+
_sc_trc=0; _sc_missing=0
|
|
391
|
+
for _sc_stack in nxt js py go; do
|
|
392
|
+
if "affected_${_sc_stack}_detect"; then
|
|
393
|
+
_sc_trc=0
|
|
394
|
+
"affected_${_sc_stack}_tool" || _sc_trc=$?
|
|
395
|
+
case "$_sc_trc" in
|
|
396
|
+
0) echo "AFFECTED_GATE_SELF_CHECK stack=${_sc_stack} toolchain=ok" ;;
|
|
397
|
+
2) echo "AFFECTED_GATE_SELF_CHECK stack=${_sc_stack} toolchain=no-test-tool" ;;
|
|
398
|
+
*) echo "AFFECTED_GATE_SELF_CHECK stack=${_sc_stack} toolchain=missing"
|
|
399
|
+
_sc_missing=1 ;;
|
|
400
|
+
esac
|
|
401
|
+
fi
|
|
402
|
+
done
|
|
403
|
+
# A detected stack whose toolchain cannot run is not "ok" WHEN IT CAN GATE —
|
|
404
|
+
# under delegation the adapters never run, so the gap is disclosed per-stack
|
|
405
|
+
# above but does not refuse the check.
|
|
406
|
+
if (( _sc_missing && _sc_runner == 0 )); then
|
|
407
|
+
echo "AFFECTED_GATE_SELF_CHECK toolchain-missing"
|
|
408
|
+
finish 4 none toolchain-missing - "$CI_NAME" - - -
|
|
409
|
+
fi
|
|
410
|
+
echo "AFFECTED_GATE_SELF_CHECK ok"
|
|
411
|
+
finish 0 none ok - "$CI_NAME" - - -
|
|
412
|
+
fi
|
|
413
|
+
|
|
414
|
+
# --- the custom-command arm -------------------------------------------------------
|
|
415
|
+
# --explain under test_command answers BEFORE the arm can run it: the custom
|
|
416
|
+
# command IS the whole selection, so explain emits mode=custom + the command.
|
|
417
|
+
if (( ARG_EXPLAIN )) && [[ -n "$CFG_TEST_COMMAND" ]]; then
|
|
418
|
+
echo "SELECTION mode=custom reason=custom-command"
|
|
419
|
+
printf 'COMMAND %s\n' "$CFG_TEST_COMMAND"
|
|
420
|
+
finish 0 explain custom-command custom "$CI_NAME" - - -
|
|
421
|
+
fi
|
|
422
|
+
if [[ -n "$CFG_TEST_COMMAND" ]]; then
|
|
423
|
+
if [[ "$CI_NAME" != none ]]; then
|
|
424
|
+
AFFECTED_BUDGET_ACTIVE=1
|
|
425
|
+
AFFECTED_DEADLINE=$(( $(affected_now) + CFG_BUDGET_SECONDS ))
|
|
426
|
+
else
|
|
427
|
+
echo "affected-tests: no CI test workflow detected — wall-clock budget not enforced; running to completion."
|
|
428
|
+
fi
|
|
429
|
+
rc=0
|
|
430
|
+
affected_bound bash -c "$CFG_TEST_COMMAND" || rc=$?
|
|
431
|
+
case "$rc" in
|
|
432
|
+
0) finish 0 custom custom-command custom "$CI_NAME" - - - ;;
|
|
433
|
+
124|137)
|
|
434
|
+
(( rc == 137 )) && echo "affected-tests: the command exited 137 (SIGKILL — OOM or kill escalation), not necessarily budget expiry; deferral still applies." >&2
|
|
435
|
+
if [[ "$CI_NAME" != none ]]; then
|
|
436
|
+
finish 6 custom budget-deferred custom "$CI_NAME" - - -
|
|
437
|
+
fi
|
|
438
|
+
finish 1 custom failed custom "$CI_NAME" - - - ;;
|
|
439
|
+
*) finish 1 custom failed custom "$CI_NAME" - - - ;;
|
|
440
|
+
esac
|
|
441
|
+
fi
|
|
442
|
+
|
|
443
|
+
# --- dispatch 2: the capability-positive repo runner -----------------------------------
|
|
444
|
+
# The probe reads the REGISTRATION, not the filename and never a comment: the
|
|
445
|
+
# flag must sit at a case-label or usage-table position — first token on the
|
|
446
|
+
# line (optionally inside a `a|b)` alternation) followed by a flag-table
|
|
447
|
+
# terminator.
|
|
448
|
+
#
|
|
449
|
+
# ORDERING IS THE CONTRACT: the probe runs BEFORE the gate's own base
|
|
450
|
+
# resolution, changed-set census and global/inert classification. A runner that
|
|
451
|
+
# registers --print-selection computes all of those itself, better informed —
|
|
452
|
+
# so a resolved runner is ALWAYS delegated to (--affected; --full only when the
|
|
453
|
+
# operator asked). Delegating an all-inert or global-paths diff is not waste:
|
|
454
|
+
# the runner's own classifier decides, and in this repo the always-on ratchets
|
|
455
|
+
# are live edges the gate's built-ins cannot see. Gate-side classification
|
|
456
|
+
# below is the adapter arm's alone.
|
|
457
|
+
RUNNER=""
|
|
458
|
+
if [[ -f scripts/test-all.sh ]] \
|
|
459
|
+
&& grep -Eq '^[[:space:]]*(--[[:alnum:]_-]+\|)*--print-selection([])|,[:space:]])' scripts/test-all.sh; then
|
|
460
|
+
RUNNER="scripts/test-all.sh"
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
if [[ -n "$RUNNER" ]]; then
|
|
464
|
+
_flag=--affected
|
|
465
|
+
(( ARG_FULL )) && _flag=--full
|
|
466
|
+
_dmode=selected
|
|
467
|
+
(( ARG_FULL )) && _dmode=full
|
|
468
|
+
if (( ARG_EXPLAIN )); then
|
|
469
|
+
# The runner owns the selection; invoking --print-selection here is left to
|
|
470
|
+
# the operator (it can cost real time), so explain reports the delegation.
|
|
471
|
+
echo "SELECTION mode=delegated runner=$RUNNER flag=$_flag"
|
|
472
|
+
echo "runner owns selection — inspect with: bash $RUNNER --print-selection"
|
|
473
|
+
finish 0 explain delegated runner "$CI_NAME" - - -
|
|
474
|
+
fi
|
|
475
|
+
if [[ "$CI_NAME" != none ]]; then
|
|
476
|
+
AFFECTED_BUDGET_ACTIVE=1
|
|
477
|
+
AFFECTED_DEADLINE=$(( $(affected_now) + CFG_BUDGET_SECONDS ))
|
|
478
|
+
else
|
|
479
|
+
echo "affected-tests: no CI test workflow detected — wall-clock budget not enforced; running to completion."
|
|
480
|
+
fi
|
|
481
|
+
rc=0
|
|
482
|
+
affected_bound bash "$RUNNER" "$_flag" || rc=$?
|
|
483
|
+
# Delegated-runner rc mapping: 4 (REFUSED — nothing ran) → 4
|
|
484
|
+
# reason=runner-refused; 3 (UNRESOLVED — killed/declined suites) → 1
|
|
485
|
+
# reason=runner-signal; 2 (usage) → 4 runner-refused; the rest pass through
|
|
486
|
+
# as a plain verdict. rc 124/137 means OUR bound expired — deferral only
|
|
487
|
+
# ever applies with a detected CI, which is the armed-budget condition.
|
|
488
|
+
case "$rc" in
|
|
489
|
+
0) finish 0 "$_dmode" delegated runner "$CI_NAME" - - - ;;
|
|
490
|
+
124|137)
|
|
491
|
+
(( rc == 137 )) && echo "affected-tests: the runner exited 137 (SIGKILL — OOM or kill escalation), not necessarily budget expiry; deferral still applies." >&2
|
|
492
|
+
if [[ "$AFFECTED_BUDGET_ACTIVE" == 1 && "$CI_NAME" != none ]]; then
|
|
493
|
+
finish 6 "$_dmode" budget-deferred runner "$CI_NAME" - - -
|
|
494
|
+
fi
|
|
495
|
+
finish 1 "$_dmode" failed runner "$CI_NAME" - - - ;;
|
|
496
|
+
3) finish 1 "$_dmode" runner-signal runner "$CI_NAME" - - - ;;
|
|
497
|
+
2|4) finish 4 "$_dmode" runner-refused runner "$CI_NAME" - - - ;;
|
|
498
|
+
*) finish 1 "$_dmode" failed runner "$CI_NAME" - - - ;;
|
|
499
|
+
esac
|
|
500
|
+
fi
|
|
501
|
+
|
|
502
|
+
# --- adapter arm: base ref + changed set (delegated runners never reach here) ---------
|
|
503
|
+
BASE=""
|
|
504
|
+
BASE_SRC=""
|
|
505
|
+
affected_resolve_base() {
|
|
506
|
+
if [[ -n "$ARG_BASE" ]]; then BASE="$ARG_BASE"; BASE_SRC="--base"; return 0; fi
|
|
507
|
+
if git rev-parse --verify --quiet '@{upstream}' >/dev/null 2>&1; then
|
|
508
|
+
BASE='@{upstream}'; BASE_SRC="upstream"; return 0
|
|
509
|
+
fi
|
|
510
|
+
if git rev-parse --verify --quiet 'origin/HEAD' >/dev/null 2>&1; then
|
|
511
|
+
BASE='origin/HEAD'; BASE_SRC="origin/HEAD"; return 0
|
|
512
|
+
fi
|
|
513
|
+
local b
|
|
514
|
+
for b in origin/main origin/master; do
|
|
515
|
+
if git rev-parse --verify --quiet "$b" >/dev/null 2>&1; then
|
|
516
|
+
BASE="$b"; BASE_SRC="$b"; return 0
|
|
517
|
+
fi
|
|
518
|
+
done
|
|
519
|
+
# merge-base fallback: newest fork point over every remote-tracking ref.
|
|
520
|
+
local best="" best_ts=0 ref mb ts
|
|
521
|
+
while IFS= read -r ref; do
|
|
522
|
+
mb="$(git merge-base HEAD "$ref" 2>/dev/null || true)"
|
|
523
|
+
[[ -n "$mb" ]] || continue
|
|
524
|
+
ts="$(git show -s --format=%ct "$mb" 2>/dev/null || true)"
|
|
525
|
+
[[ "$ts" =~ ^[0-9]+$ ]] || ts=0
|
|
526
|
+
if (( ts >= best_ts )); then best="$mb"; best_ts=$ts; fi
|
|
527
|
+
done < <(git for-each-ref --format='%(refname)' refs/remotes 2>/dev/null | head -n 50)
|
|
528
|
+
if [[ -n "$best" ]]; then BASE="$best"; BASE_SRC="merge-base"; return 0; fi
|
|
529
|
+
return 1
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
affected_resolve_base || {
|
|
533
|
+
# One bounded deepen on a shallow clone — then refuse, never a silent
|
|
534
|
+
# full and never a silent empty.
|
|
535
|
+
if [[ "$(git rev-parse --is-shallow-repository 2>/dev/null || true)" == "true" ]]; then
|
|
536
|
+
# --no-tags: the deepen buys commit history for the merge-base, never tag
|
|
537
|
+
# objects — and a bare `git fetch` in the battery closure is a tag-write
|
|
538
|
+
# surface the repo-write-boundary census refuses (battery-tag-authorship).
|
|
539
|
+
affected_timeout 60 git fetch --no-tags --deepen=200 >/dev/null 2>&1 || true
|
|
540
|
+
affected_resolve_base || finish 4 none no-base-ref - "$CI_NAME" - - -
|
|
541
|
+
else
|
|
542
|
+
finish 4 none no-base-ref - "$CI_NAME" - - -
|
|
543
|
+
fi
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
# Changed set: committed-vs-base ∪ staged ∪ unstaged ∪ untracked. Rename-aware
|
|
547
|
+
# (-M): the new path is what exists on disk. The committed arm is the one that
|
|
548
|
+
# can fail outright (unresolvable base) — it is captured ONCE: its rc is the
|
|
549
|
+
# fail-closed probe and its stdout the first leg of the union, so a bad --base
|
|
550
|
+
# refuses rather than reading as an empty diff and no diff runs twice.
|
|
551
|
+
AFFECTED_CHANGED=()
|
|
552
|
+
affected_mktemp changed || finish 4 none mktemp-failed - "$CI_NAME" - - -; CHANGED_TMP="$AFFECTED_TMP"
|
|
553
|
+
_base_rc=0
|
|
554
|
+
# -c core.quotePath=false: without it git emits non-ASCII paths C-quoted
|
|
555
|
+
# ("dir/\303\251.txt"), which never matches a glob and misses the file system.
|
|
556
|
+
_base_diff="$(git -c core.quotePath=false diff --name-only -M "${BASE}...HEAD" -- 2>/dev/null)" || _base_rc=$?
|
|
557
|
+
if (( _base_rc != 0 )); then
|
|
558
|
+
if [[ "$(git rev-parse --is-shallow-repository 2>/dev/null || true)" == "true" ]]; then
|
|
559
|
+
affected_timeout 60 git fetch --no-tags --deepen=200 >/dev/null 2>&1 || true
|
|
560
|
+
_base_rc=0
|
|
561
|
+
_base_diff="$(git -c core.quotePath=false diff --name-only -M "${BASE}...HEAD" -- 2>/dev/null)" || _base_rc=$?
|
|
562
|
+
fi
|
|
563
|
+
if (( _base_rc != 0 )); then
|
|
564
|
+
finish 4 none no-base-ref - "$CI_NAME" - - -
|
|
565
|
+
fi
|
|
566
|
+
fi
|
|
567
|
+
# Each census leg's rc is captured — never `|| true`. A leg that fails toward
|
|
568
|
+
# empty silently NARROWS the selection, and the untracked leg is the worst case
|
|
569
|
+
# (a failed `ls-files --others` drops every NEW test file from the union). Any
|
|
570
|
+
# failed leg makes the changed set undecidable → coverage-favoring forced full
|
|
571
|
+
# (reason=census-degraded), the same contract as an empty diff — never a silent
|
|
572
|
+
# narrow, never a refusal for a recoverable shape.
|
|
573
|
+
_census_rc=0
|
|
574
|
+
_leg_staged="$(git -c core.quotePath=false diff --name-only --cached -M -- 2>/dev/null)" \
|
|
575
|
+
|| _census_rc=$?
|
|
576
|
+
_leg_unstaged="$(git -c core.quotePath=false diff --name-only -M -- 2>/dev/null)" \
|
|
577
|
+
|| _census_rc=$?
|
|
578
|
+
_leg_untracked="$(git -c core.quotePath=false ls-files --others --exclude-standard 2>/dev/null)" \
|
|
579
|
+
|| _census_rc=$?
|
|
580
|
+
{
|
|
581
|
+
printf '%s\n' "$_base_diff"
|
|
582
|
+
printf '%s\n' "$_leg_staged"
|
|
583
|
+
printf '%s\n' "$_leg_unstaged"
|
|
584
|
+
printf '%s\n' "$_leg_untracked"
|
|
585
|
+
} | grep -v '^[[:space:]]*$' | sort -u >"$CHANGED_TMP" || true
|
|
586
|
+
unset _base_rc _base_diff _leg_staged _leg_unstaged _leg_untracked
|
|
587
|
+
while IFS= read -r _p; do
|
|
588
|
+
[[ -n "$_p" ]] && AFFECTED_CHANGED+=("$_p")
|
|
589
|
+
done <"$CHANGED_TMP"
|
|
590
|
+
|
|
591
|
+
# --- classifiers ---------------------------------------------------------------------
|
|
592
|
+
# A pattern containing `/` matches the whole path; `**/` also matches zero
|
|
593
|
+
# directories (its `**/` prefix is stripped for the second try). A bare
|
|
594
|
+
# pattern matches the basename.
|
|
595
|
+
affected_glob_match() {
|
|
596
|
+
local p="$1" pat="$2" alt
|
|
597
|
+
if [[ "$pat" == */* ]]; then
|
|
598
|
+
[[ "$p" == $pat ]] && return 0
|
|
599
|
+
alt="${pat//\*\//}"
|
|
600
|
+
[[ "$alt" != "$pat" && "$p" == $alt ]] && return 0
|
|
601
|
+
return 1
|
|
602
|
+
fi
|
|
603
|
+
[[ "${p##*/}" == $pat ]]
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
GLOBAL_PATHS_BUILTIN=(
|
|
607
|
+
# manifests + lockfiles (any depth — a monorepo's nested manifests count too)
|
|
608
|
+
package.json package-lock.json npm-shrinkwrap.json yarn.lock pnpm-lock.yaml
|
|
609
|
+
pnpm-workspace.yaml bun.lock bun.lockb bunfig.toml
|
|
610
|
+
pyproject.toml poetry.lock Pipfile Pipfile.lock uv.lock requirements.txt
|
|
611
|
+
go.mod go.sum Cargo.toml Cargo.lock Gemfile Gemfile.lock composer.json
|
|
612
|
+
composer.lock
|
|
613
|
+
tsconfig tsconfig.json 'tsconfig.*' 'tsconfig*'
|
|
614
|
+
'vitest.config.*' 'jest.config.*' conftest.py '.github/workflows/**'
|
|
615
|
+
)
|
|
616
|
+
INERT_PATHS_BUILTIN=('**/*.md')
|
|
617
|
+
|
|
618
|
+
GLOBAL_PATHS=("${GLOBAL_PATHS_BUILTIN[@]}")
|
|
619
|
+
INERT_PATHS=("${INERT_PATHS_BUILTIN[@]}")
|
|
620
|
+
(( CFG_GLOBAL_SET )) && GLOBAL_PATHS=(${CFG_GLOBAL_PATHS[@]+"${CFG_GLOBAL_PATHS[@]}"})
|
|
621
|
+
if (( CFG_INERT_SET )); then
|
|
622
|
+
# A match-everything inert pattern (`*`, `**`, `**/*` — anything matching an
|
|
623
|
+
# arbitrary path) is a PERMANENT green: every diff would classify all-inert
|
|
624
|
+
# and exit 0. Refuse the config rather than certify nothing. Probe with an
|
|
625
|
+
# improbable nested path through the same matcher the classifier uses.
|
|
626
|
+
for _ip in ${CFG_INERT_PATHS[@]+"${CFG_INERT_PATHS[@]}"}; do
|
|
627
|
+
if affected_glob_match "zz9q8/probe.nonexistent" "$_ip"; then
|
|
628
|
+
finish 4 none "bad-config key=inert_paths" config "$CI_NAME" - - -
|
|
629
|
+
fi
|
|
630
|
+
done
|
|
631
|
+
INERT_PATHS=(${CFG_INERT_PATHS[@]+"${CFG_INERT_PATHS[@]}"})
|
|
632
|
+
fi
|
|
633
|
+
|
|
634
|
+
GLOBAL_HIT=""
|
|
635
|
+
ALL_INERT=1
|
|
636
|
+
INERT_RULE="-"
|
|
637
|
+
for _p in ${AFFECTED_CHANGED[@]+"${AFFECTED_CHANGED[@]}"}; do
|
|
638
|
+
_hit=0
|
|
639
|
+
for _g in ${GLOBAL_PATHS[@]+"${GLOBAL_PATHS[@]}"}; do
|
|
640
|
+
if affected_glob_match "$_p" "$_g"; then
|
|
641
|
+
GLOBAL_HIT="$_p"; _hit=1; break
|
|
642
|
+
fi
|
|
643
|
+
done
|
|
644
|
+
if (( _hit == 0 )); then
|
|
645
|
+
_inert=0
|
|
646
|
+
for _i in ${INERT_PATHS[@]+"${INERT_PATHS[@]}"}; do
|
|
647
|
+
if affected_glob_match "$_p" "$_i"; then _inert=1; INERT_RULE="$_i"; break; fi
|
|
648
|
+
done
|
|
649
|
+
(( _inert )) || ALL_INERT=0
|
|
650
|
+
else
|
|
651
|
+
ALL_INERT=0
|
|
652
|
+
fi
|
|
653
|
+
done
|
|
654
|
+
|
|
655
|
+
# --- explain -------------------------------------------------------------------------
|
|
656
|
+
if (( ARG_EXPLAIN )); then
|
|
657
|
+
echo "AFFECTED_EXPLAIN base=${BASE:-none} base_source=${BASE_SRC:-none} changed=${#AFFECTED_CHANGED[@]}"
|
|
658
|
+
_xn=0
|
|
659
|
+
for _p in ${AFFECTED_CHANGED[@]+"${AFFECTED_CHANGED[@]}"}; do
|
|
660
|
+
_xn=$((_xn + 1))
|
|
661
|
+
if (( _xn > 200 )); then
|
|
662
|
+
# The PATH dump is capped — a giant diff gets the verdict and a count,
|
|
663
|
+
# not an unbounded listing.
|
|
664
|
+
echo "PATH … and $(( ${#AFFECTED_CHANGED[@]} - 200 )) more"
|
|
665
|
+
break
|
|
666
|
+
fi
|
|
667
|
+
_cls=changed; _rule=-
|
|
668
|
+
for _g in ${GLOBAL_PATHS[@]+"${GLOBAL_PATHS[@]}"}; do
|
|
669
|
+
affected_glob_match "$_p" "$_g" && { _cls=global; _rule="$_g"; break; }
|
|
670
|
+
done
|
|
671
|
+
if [[ "$_cls" == changed ]]; then
|
|
672
|
+
for _i in ${INERT_PATHS[@]+"${INERT_PATHS[@]}"}; do
|
|
673
|
+
affected_glob_match "$_p" "$_i" && { _cls=inert; _rule="$_i"; break; }
|
|
674
|
+
done
|
|
675
|
+
fi
|
|
676
|
+
echo "PATH $(affected_marker_field "$_p") class=${_cls} rule=$(affected_marker_field "$_rule")"
|
|
677
|
+
done
|
|
678
|
+
fi
|
|
679
|
+
|
|
680
|
+
# --- verdicts that need no runner ----------------------------------------------------
|
|
681
|
+
if (( _census_rc != 0 )); then
|
|
682
|
+
# A census leg failed — the partial union cannot be trusted for selection OR
|
|
683
|
+
# for inert/global classification. Coverage-favoring: forced full.
|
|
684
|
+
echo "affected-tests: a changed-set census leg failed (rc=$_census_rc) — the selection cannot be trusted; running the full command (reason=census-degraded)." >&2
|
|
685
|
+
ARG_FULL=1
|
|
686
|
+
EMPTY_REASON="census-degraded"
|
|
687
|
+
elif (( ${#AFFECTED_CHANGED[@]} == 0 )); then
|
|
688
|
+
# An empty diff is an undecidable selection — coverage-favoring, not a pass.
|
|
689
|
+
# (explain emits ONE SELECTION line at the block below; nothing extra here.)
|
|
690
|
+
ARG_FULL=1
|
|
691
|
+
EMPTY_REASON="empty-diff"
|
|
692
|
+
else
|
|
693
|
+
EMPTY_REASON=""
|
|
694
|
+
fi
|
|
695
|
+
unset _census_rc
|
|
696
|
+
|
|
697
|
+
if (( ALL_INERT )) && (( ${#AFFECTED_CHANGED[@]} > 0 )) && (( ARG_FULL == 0 )) && [[ -z "$GLOBAL_HIT" ]]; then
|
|
698
|
+
(( ARG_EXPLAIN )) && echo "SELECTION mode=none reason=inert inert_rule=$(affected_marker_field "$INERT_RULE")"
|
|
699
|
+
# UNIT NOTE: on this path of= counts CHANGED files (all inert), not the
|
|
700
|
+
# repo test-file census other paths emit — units differ by design here.
|
|
701
|
+
finish 0 none inert - "$CI_NAME" 0 "${#AFFECTED_CHANGED[@]}" "${#AFFECTED_CHANGED[@]}"
|
|
702
|
+
fi
|
|
703
|
+
|
|
704
|
+
MODE=selected
|
|
705
|
+
REASON=ok
|
|
706
|
+
if (( ARG_FULL )); then
|
|
707
|
+
MODE=full
|
|
708
|
+
REASON="${EMPTY_REASON:-full-requested}"
|
|
709
|
+
fi
|
|
710
|
+
if [[ -n "$GLOBAL_HIT" && "$ARG_FULL" == 0 ]]; then
|
|
711
|
+
MODE=full
|
|
712
|
+
REASON="global-paths"
|
|
713
|
+
fi
|
|
714
|
+
|
|
715
|
+
if (( ARG_EXPLAIN )); then
|
|
716
|
+
echo "SELECTION mode=${MODE} reason=${REASON} global_hit=$(affected_marker_field "${GLOBAL_HIT:--}") ci=${CI_NAME}"
|
|
717
|
+
finish 0 explain "$REASON" - "$CI_NAME" - - -
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
# --- budget arming -----------------------------------------------------------------
|
|
721
|
+
if [[ "$CI_NAME" != none ]]; then
|
|
722
|
+
AFFECTED_BUDGET_ACTIVE=1
|
|
723
|
+
AFFECTED_DEADLINE=$(( $(affected_now) + CFG_BUDGET_SECONDS ))
|
|
724
|
+
else
|
|
725
|
+
echo "affected-tests: no CI test workflow detected — wall-clock budget not enforced; running to completion."
|
|
726
|
+
fi
|
|
727
|
+
|
|
728
|
+
WIDTH="$(affected_workers "$CFG_MAX_WORKERS")"
|
|
729
|
+
|
|
730
|
+
# --- dispatch 3: stack adapters ---------------------------------------------------------
|
|
731
|
+
# Worst-of aggregation with precedence 1 > 4 > 6 > 0; rc 7 only when every
|
|
732
|
+
# detected adapter reports no suite AND nothing else resolved the gate.
|
|
733
|
+
WORST=0
|
|
734
|
+
SAW_SUITE=0
|
|
735
|
+
STACKS=""
|
|
736
|
+
RAN_SUM=0
|
|
737
|
+
OF_SUM=0
|
|
738
|
+
RAN_KNOWN=1
|
|
739
|
+
OF_KNOWN=1
|
|
740
|
+
GATE_ESCALATED_FULL=0
|
|
741
|
+
|
|
742
|
+
_affected_rank() { # rank <rc> → precedence weight
|
|
743
|
+
case "$1" in
|
|
744
|
+
1) echo 4 ;; 4) echo 3 ;; 6) echo 2 ;; 0) echo 1 ;; *) echo 4 ;;
|
|
745
|
+
esac
|
|
746
|
+
}
|
|
747
|
+
_affected_aggregate() { # aggregate <rc>
|
|
748
|
+
local rc="$1" cw ww
|
|
749
|
+
cw="$(_affected_rank "$WORST")"
|
|
750
|
+
ww="$(_affected_rank "$rc")"
|
|
751
|
+
(( ww > cw )) && WORST="$rc"
|
|
752
|
+
return 0
|
|
753
|
+
}
|
|
754
|
+
_affected_add_counts() { # add ran/of independently — each keeps `-` honesty
|
|
755
|
+
local r="$1" o="$2"
|
|
756
|
+
if [[ "$r" =~ ^[0-9]+$ ]]; then RAN_SUM=$((RAN_SUM + r)); else RAN_KNOWN=0; fi
|
|
757
|
+
if [[ "$o" =~ ^[0-9]+$ ]]; then OF_SUM=$((OF_SUM + o)); else OF_KNOWN=0; fi
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
# try_adapter <fn-name> [label] [precomputed-tool-rc] — detect, toolchain-check,
|
|
761
|
+
# select, run, retry-full on a vacuous selection (the empty-selection arm), then
|
|
762
|
+
# aggregate. <fn-name> is the suffix in affected_<fn-name>_* (hyphen-free,
|
|
763
|
+
# bash-3.2); <label> is what the marker prints. The optional third argument is a
|
|
764
|
+
# tool() rc the caller already probed — passing it skips a second bounded
|
|
765
|
+
# --version probe (nx/turbo's detect-probe needs the rc before deciding whether
|
|
766
|
+
# to fall through to js).
|
|
767
|
+
try_adapter() {
|
|
768
|
+
local name="$1" label="${2:-$1}"
|
|
769
|
+
# Per-adapter output flags reset HERE, not only inside adapters that set
|
|
770
|
+
# them — a js escalation must never stain a later adapter's marker.
|
|
771
|
+
ADP_ESCALATED_FULL=0
|
|
772
|
+
ADP_VACUOUS=0
|
|
773
|
+
if ! "affected_${name}_detect"; then
|
|
774
|
+
return 0
|
|
775
|
+
fi
|
|
776
|
+
local tool_rc="${3:-}"
|
|
777
|
+
if [[ -z "$tool_rc" ]]; then
|
|
778
|
+
tool_rc=0
|
|
779
|
+
"affected_${name}_tool" || tool_rc=$?
|
|
780
|
+
fi
|
|
781
|
+
if (( tool_rc == 2 )); then
|
|
782
|
+
return 0 # stack marker present but no test tool configured — not a suite
|
|
783
|
+
fi
|
|
784
|
+
if (( tool_rc != 0 )); then
|
|
785
|
+
echo "affected-tests: stack=$label detected but its toolchain is missing or unrunnable." >&2
|
|
786
|
+
_affected_aggregate 4
|
|
787
|
+
STACKS="${STACKS:+$STACKS+}$label"
|
|
788
|
+
AGG_REASON="toolchain-missing"
|
|
789
|
+
return 0
|
|
790
|
+
fi
|
|
791
|
+
STACKS="${STACKS:+$STACKS+}$label"
|
|
792
|
+
|
|
793
|
+
local run_mode="$MODE" arc=0
|
|
794
|
+
if [[ "$run_mode" == selected ]]; then
|
|
795
|
+
"affected_${name}_run" selected "$WIDTH" "$BASE" || arc=$?
|
|
796
|
+
if (( arc == 5 )) || [[ "${ADP_VACUOUS:-0}" == 1 ]]; then
|
|
797
|
+
# Empty native selection on a non-inert diff → the full command, loudly.
|
|
798
|
+
echo "affected-tests: stack=$label selected nothing for this diff — falling back to the full suite (reason=empty-selection)." >&2
|
|
799
|
+
arc=0
|
|
800
|
+
"affected_${name}_run" full "$WIDTH" "$BASE" || arc=$?
|
|
801
|
+
if (( arc == 5 )) || [[ "${ADP_VACUOUS:-0}" == 1 ]]; then
|
|
802
|
+
# A stack that runs nothing even in full mode has no suite here.
|
|
803
|
+
return 0
|
|
804
|
+
fi
|
|
805
|
+
REASON=empty-selection
|
|
806
|
+
run_mode=full
|
|
807
|
+
fi
|
|
808
|
+
else
|
|
809
|
+
"affected_${name}_run" full "$WIDTH" "$BASE" || arc=$?
|
|
810
|
+
if (( arc == 5 )) || [[ "${ADP_VACUOUS:-0}" == 1 ]]; then
|
|
811
|
+
return 0
|
|
812
|
+
fi
|
|
813
|
+
fi
|
|
814
|
+
|
|
815
|
+
SAW_SUITE=1
|
|
816
|
+
# An adapter that could not select (e.g. js scripts.test) ran FULL instead —
|
|
817
|
+
# the marker must not call that `mode=selected`.
|
|
818
|
+
(( ${ADP_ESCALATED_FULL:-0} )) && GATE_ESCALATED_FULL=1
|
|
819
|
+
_affected_add_counts "${ADP_RAN:--}" "${ADP_OF:--}"
|
|
820
|
+
case "$arc" in
|
|
821
|
+
0) _affected_aggregate 0 ;;
|
|
822
|
+
124|137)
|
|
823
|
+
(( arc == 137 )) && echo "affected-tests: stack=$label exited 137 (SIGKILL — OOM or kill escalation), not necessarily budget expiry; deferral still applies." >&2
|
|
824
|
+
if [[ "$AFFECTED_BUDGET_ACTIVE" == 1 && "$CI_NAME" != none ]]; then
|
|
825
|
+
_affected_aggregate 6
|
|
826
|
+
else
|
|
827
|
+
_affected_aggregate 1
|
|
828
|
+
fi ;;
|
|
829
|
+
*) _affected_aggregate 1 ;;
|
|
830
|
+
esac
|
|
831
|
+
return 0
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
AGG_REASON=""
|
|
835
|
+
# nx/turbo FIRST: a workspace scheduler's affected graph subsumes per-package
|
|
836
|
+
# JS selection — but only when it actually RAN a suite. A resolved tool whose
|
|
837
|
+
# affected graph produced nothing (vacuous in both selected and full) must not
|
|
838
|
+
# suppress the js adapter that could still find tests.
|
|
839
|
+
NXT_SUPPRESS=0
|
|
840
|
+
if affected_nxt_detect; then
|
|
841
|
+
_trc=0
|
|
842
|
+
affected_nxt_tool || _trc=$?
|
|
843
|
+
if (( _trc == 0 )); then
|
|
844
|
+
_saw_before=$SAW_SUITE
|
|
845
|
+
try_adapter nxt nx-turbo 0 # tool already resolved — skip the re-probe
|
|
846
|
+
(( SAW_SUITE > _saw_before )) && NXT_SUPPRESS=1
|
|
847
|
+
elif (( _trc == 4 )); then
|
|
848
|
+
echo "affected-tests: stack=nx-turbo detected but its toolchain is missing or unrunnable; falling through to js." >&2
|
|
849
|
+
fi
|
|
850
|
+
fi
|
|
851
|
+
(( NXT_SUPPRESS )) || try_adapter js
|
|
852
|
+
try_adapter py
|
|
853
|
+
try_adapter go
|
|
854
|
+
|
|
855
|
+
if (( SAW_SUITE == 0 )); then
|
|
856
|
+
# Nothing ran. Distinguish the refusals from the disclosed no-suite verdict:
|
|
857
|
+
# a toolchain-missing aggregate is already a refusal; a repo with test files
|
|
858
|
+
# but no resolvable stack refuses; no test files at all is rc 7.
|
|
859
|
+
if (( WORST == 4 )); then
|
|
860
|
+
finish 4 "$MODE" "${AGG_REASON:-toolchain-missing}" "${STACKS:--}" "$CI_NAME" - - -
|
|
861
|
+
fi
|
|
862
|
+
_tests_exist=0
|
|
863
|
+
# Process substitution, not `… | grep -q .`: under pipefail grep's early exit
|
|
864
|
+
# SIGPIPEs the producer and a match can read as a miss (#9217).
|
|
865
|
+
if grep -q . < <(affected_find_files any '*.test.*' '*.spec.*' 'test_*.py' '*_test.py' '*_test.go'); then
|
|
866
|
+
_tests_exist=1
|
|
867
|
+
fi
|
|
868
|
+
if (( _tests_exist )); then
|
|
869
|
+
# Tests exist but no runner resolved. A present-but-foreign
|
|
870
|
+
# scripts/test-all.sh gets the more actionable reason.
|
|
871
|
+
if [[ -f scripts/test-all.sh ]]; then
|
|
872
|
+
finish 4 "$MODE" foreign-runner - "$CI_NAME" - - -
|
|
873
|
+
fi
|
|
874
|
+
finish 4 "$MODE" no-stack - "$CI_NAME" - - -
|
|
875
|
+
fi
|
|
876
|
+
finish 7 none no-tests - "$CI_NAME" 0 0 0
|
|
877
|
+
fi
|
|
878
|
+
|
|
879
|
+
OUT_RAN="-"; OUT_OF="-"; SKIP_SUM="-"
|
|
880
|
+
(( RAN_KNOWN )) && OUT_RAN="$RAN_SUM"
|
|
881
|
+
(( OF_KNOWN )) && OUT_OF="$OF_SUM"
|
|
882
|
+
if (( RAN_KNOWN && OF_KNOWN )); then
|
|
883
|
+
if (( OF_SUM >= RAN_SUM )); then
|
|
884
|
+
SKIP_SUM=$((OF_SUM - RAN_SUM))
|
|
885
|
+
else
|
|
886
|
+
# ran>of can only mean the two counts use different units (an adapter
|
|
887
|
+
# counted items/packages where of counted files). Never emit a negative
|
|
888
|
+
# skipped — disclose the divergence instead.
|
|
889
|
+
SKIP_SUM="-"
|
|
890
|
+
echo "affected-tests: ran=$RAN_SUM exceeds the repo census of=$OF_SUM — count units diverged; skipped is not derivable." >&2
|
|
891
|
+
fi
|
|
892
|
+
fi
|
|
893
|
+
# Marker honesty: an adapter that escalated selected→full (no native selector)
|
|
894
|
+
# makes the emitted mode full with a named reason — never a selected claim
|
|
895
|
+
# over a whole-suite run. MODE itself is untouched: a later adapter's own
|
|
896
|
+
# selection is still scoped.
|
|
897
|
+
OUT_MODE="$MODE"
|
|
898
|
+
if (( GATE_ESCALATED_FULL )) && [[ "$OUT_MODE" == selected ]]; then
|
|
899
|
+
OUT_MODE=full
|
|
900
|
+
[[ "$REASON" == ok ]] && REASON=no-selector
|
|
901
|
+
fi
|
|
902
|
+
case "$WORST" in
|
|
903
|
+
0) finish 0 "$OUT_MODE" "$REASON" "${STACKS:--}" "$CI_NAME" "$OUT_RAN" "$OUT_OF" "$SKIP_SUM" ;;
|
|
904
|
+
4) finish 4 "$OUT_MODE" "${AGG_REASON:-failed}" "${STACKS:--}" "$CI_NAME" \
|
|
905
|
+
"$OUT_RAN" "$OUT_OF" "$SKIP_SUM" ;;
|
|
906
|
+
6) finish 6 "$OUT_MODE" budget-deferred "${STACKS:--}" "$CI_NAME" "$OUT_RAN" "$OUT_OF" "$SKIP_SUM" ;;
|
|
907
|
+
*) finish 1 "$OUT_MODE" failed "${STACKS:--}" "$CI_NAME" "$OUT_RAN" "$OUT_OF" "$SKIP_SUM" ;;
|
|
908
|
+
esac
|