@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,734 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Self-tests for the constraint-scaffold L1 import-boundary gate (ADR-071).
|
|
3
|
+
# Runs in the scripts shard (scripts/test-all.sh globs
|
|
4
|
+
# plugins/soleur/skills/*/test/*.test.sh). Proves the gate is NOT vacuous:
|
|
5
|
+
#
|
|
6
|
+
# AC3 value import of server/** via the @/server/... alias FAILS;
|
|
7
|
+
# an `import type` of the same PASSES; 0 couldNotResolve edges into
|
|
8
|
+
# the secret set (alias resolution is live).
|
|
9
|
+
# AC6 the secret (`to`) set is explicit and non-empty.
|
|
10
|
+
# AC6b a parenthesized/metacharacter route-group path is matched
|
|
11
|
+
# (regex-escaping works); an empty from-set while "use client" files
|
|
12
|
+
# exist is a HARD ERROR, not a silently-disabled rule.
|
|
13
|
+
# AC5 neither app/ nor components/ present (empty input) fails closed;
|
|
14
|
+
# a broken .cjs makes the shared runner fail closed.
|
|
15
|
+
#
|
|
16
|
+
# Hermetic: every fixture is synthesized under a mktemp dir; nothing is written
|
|
17
|
+
# into the real apps/web-platform tree.
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
REPO_ROOT="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
|
21
|
+
APP="$REPO_ROOT/apps/web-platform"
|
|
22
|
+
CFG="$APP/.dependency-cruiser.cjs"
|
|
23
|
+
RUNNER="$APP/scripts/constraint-gates.sh"
|
|
24
|
+
|
|
25
|
+
pass=0
|
|
26
|
+
fail=0
|
|
27
|
+
# The INDEPENDENT case counter (ADR-193 #2). Incremented at every CALL SITE, and NEVER
|
|
28
|
+
# inside ok()/bad() — the VERDICT helpers, which touch only the verdict counters. That
|
|
29
|
+
# separation is the whole point: a counter bumped inside the verdict helpers moves WITH the
|
|
30
|
+
# verdict, so stubbing bad() drops the row and its count together and `pass + fail == cases`
|
|
31
|
+
# still holds under the exact fault it exists to catch (ADR-193 Context, third failure mode —
|
|
32
|
+
# measured on this very ok()/bad() shape). Incremented at top level only, never inside a
|
|
33
|
+
# `$( … )` command substitution: a subshell discards the increment while reading as correct.
|
|
34
|
+
cases=0
|
|
35
|
+
# Anti-vacuity thresholds. Declared here (not adjacent to their `if`) so BOTH exit doors bind
|
|
36
|
+
# them and so guard-vacuity-floor's backward slice-widening finds them bound under `set -u`.
|
|
37
|
+
#
|
|
38
|
+
# TOOLCHAIN_FREE_MIN_ASSERTIONS guards the toolchain-SKIP exit. Derived as a genuine lower
|
|
39
|
+
# bound, not a snapshot: 12 fixed assertions (1 AC4 + 8 drift-trip + 1 drift-no-false-positive
|
|
40
|
+
# + 1 P2 anchoring + 1 AC5b direct-baseline) plus at least 1 VALUE_SAFE_PATH module — the
|
|
41
|
+
# module count is product content that #5850 plans to shrink by relocating modules out of
|
|
42
|
+
# server/**, so pinning it at today's 4 would set a landmine; the `vs_checked -lt 1` floor
|
|
43
|
+
# guards that loop separately.
|
|
44
|
+
# MIN_ASSERTIONS adds the 22 fixed assertions that run past the toolchain probe (3 AC3/AC6b +
|
|
45
|
+
# 3 directive-form + 7 transitive 4.x + AC3 couldNotResolve + AC6 + AC6b empty-from-set +
|
|
46
|
+
# AC5 empty-input + AC5 broken-cjs + AC10 + 4.8 + 4.10 + 4.11).
|
|
47
|
+
#
|
|
48
|
+
# These two are the CONSERVATIVE BASE (12 + 1 module, and + 22), used only when the
|
|
49
|
+
# VALUE_SAFE_PATH extraction fails. On the normal path they are RATCHETED below to
|
|
50
|
+
# `12 + <module count read from $CFG> (+ 22)` the moment that list is known. The bases alone
|
|
51
|
+
# left slack equal to `module_count - 1` — today 3 fungible slots on the very gate this suite
|
|
52
|
+
# exists to pin (the L1 client -> server-secret import boundary), so three assertions could be
|
|
53
|
+
# deleted with the floor still green. Deriving from the config keeps the shrink-safety the
|
|
54
|
+
# fixed `1` was chosen for (#5850 relocates modules out of server/**, and the floor now
|
|
55
|
+
# follows that down automatically) while removing the slack, and it stays non-tautological
|
|
56
|
+
# because the operand comes from $CFG, not from the loop the floor is watching.
|
|
57
|
+
TOOLCHAIN_FREE_MIN_ASSERTIONS=13
|
|
58
|
+
MIN_ASSERTIONS=35
|
|
59
|
+
ok() { printf 'ok - %s\n' "$1"; pass=$((pass + 1)); }
|
|
60
|
+
bad() { printf 'FAIL - %s\n' "$1"; fail=$((fail + 1)); }
|
|
61
|
+
|
|
62
|
+
[[ -f "$CFG" ]] || { echo "fatal: missing $CFG (run constraint-scaffold.sh)"; exit 2; }
|
|
63
|
+
[[ -f "$RUNNER" ]] || { echo "fatal: missing $RUNNER (run constraint-scaffold.sh)"; exit 2; }
|
|
64
|
+
|
|
65
|
+
BASELINE="$APP/.dependency-cruiser-known-violations.json"
|
|
66
|
+
[[ -f "$BASELINE" ]] || { echo "fatal: missing $BASELINE (run constraint-scaffold.sh)"; exit 2; }
|
|
67
|
+
|
|
68
|
+
# =============================================================================
|
|
69
|
+
# Toolchain-FREE assertions (#5777, ADR-071 amendment). These parse the committed
|
|
70
|
+
# baseline JSON + grep real source — NO dependency-cruiser needed — so they run in
|
|
71
|
+
# EVERY shard, before the toolchain SKIP guard below. They are the always-on half
|
|
72
|
+
# of the D2/D3/D4 invariants.
|
|
73
|
+
# =============================================================================
|
|
74
|
+
|
|
75
|
+
# --- AC4 (D3.1): committed baseline suppresses the transitive rule NOWHERE -------
|
|
76
|
+
# A baselined entry that names the transitive rule is never benign: dependency-cruiser
|
|
77
|
+
# softens it per-ORIGIN (from+rule.name, ignoring `to`/`via` AND `type`), so one such
|
|
78
|
+
# entry blinds that client to every future transitive secret. Key on the transitive
|
|
79
|
+
# rule NAME (not just type:"reachability") — a hand-authored type:"module" entry
|
|
80
|
+
# naming the rule suppresses it identically while a type-only count reads 0. A
|
|
81
|
+
# non-array baseline => -1 => fails closed.
|
|
82
|
+
TRANSITIVE_RULE="no-client-to-server-secret-transitive"
|
|
83
|
+
count_suppressors() { # $1 = baseline path; echoes count of transitive-suppressing entries, -1 on non-array/parse-err
|
|
84
|
+
node -e '
|
|
85
|
+
const fs = require("fs");
|
|
86
|
+
const RULE = process.argv[2];
|
|
87
|
+
try { const b = JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
|
|
88
|
+
if (!Array.isArray(b)) { process.stdout.write("-1"); }
|
|
89
|
+
else process.stdout.write(String(b.filter(e => e && (e.type==="reachability" || (e.rule && e.rule.name===RULE))).length));
|
|
90
|
+
} catch(e){ process.stdout.write("-1"); }' "$1" "$TRANSITIVE_RULE" 2>/dev/null
|
|
91
|
+
}
|
|
92
|
+
REACH_CT="$(count_suppressors "$BASELINE")"
|
|
93
|
+
cases=$((cases + 1))
|
|
94
|
+
if [[ "$REACH_CT" == "0" ]]; then
|
|
95
|
+
ok "AC4(D3.1): committed baseline has zero transitive-rule-suppressing entries"
|
|
96
|
+
else
|
|
97
|
+
bad "AC4(D3.1): committed baseline has $REACH_CT transitive-suppressing entries (must be 0; non-array/parse err = -1)"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# --- AC5b (D3.3): direct-rule non-regression floor ------------------------------
|
|
101
|
+
# tsPreCompilationDeps:false makes the direct rule's value-edge set a strict SUBSET
|
|
102
|
+
# of v1's (type-only edges are elided; value edges are never elided), so a flip
|
|
103
|
+
# regression can only SHRINK the direct baseline below the recorded floor of 10 — a
|
|
104
|
+
# legitimate NEW value-safe direct import GROWS it (still fine). Assert a lower bound,
|
|
105
|
+
# not equality, so this plugin self-test does not false-fail on unrelated product
|
|
106
|
+
# growth with a misleading "flip regression" message. REACH_CT==0 (above) is the
|
|
107
|
+
# load-bearing security invariant; this is the flip-correctness tripwire.
|
|
108
|
+
DEP_CT="$(node -e '
|
|
109
|
+
const fs = require("fs");
|
|
110
|
+
try { const b = JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
|
|
111
|
+
process.stdout.write(String((Array.isArray(b)?b:[]).filter(e=>e&&e.type==="dependency").length));
|
|
112
|
+
} catch(e){ process.stdout.write("-1"); }' "$BASELINE" 2>/dev/null)"
|
|
113
|
+
# ADR-193 #1: this floor's failure arm reports with `printf >&2` + `exit 1` DIRECTLY rather
|
|
114
|
+
# than through bad(). It measures a SYSTEM UNDER TEST (the committed baseline) rather than the
|
|
115
|
+
# suite itself, but that makes no difference to the argument: routed through bad() it
|
|
116
|
+
# incremented the same `fail` counter the exit status reads, so neutering bad() silenced this
|
|
117
|
+
# floor together with every assertion row. Its operand ($DEP_CT) is independent of the verdict
|
|
118
|
+
# counters, so no ordering conflict with the conservation check arises (ADR-193 #4).
|
|
119
|
+
DIRECT_BASELINE_FLOOR=10
|
|
120
|
+
if ! [[ "$DEP_CT" =~ ^[0-9]+$ ]] || [[ "$DEP_CT" -lt "$DIRECT_BASELINE_FLOOR" ]]; then
|
|
121
|
+
printf '\n[FATAL] AC5b(D3.3) direct-rule non-regression floor: baseline count is %s, expected >= %d (-1 = non-array/parse error).\n' \
|
|
122
|
+
"$DEP_CT" "$DIRECT_BASELINE_FLOOR" >&2
|
|
123
|
+
printf ' A tsPreCompilationDeps flip regression would shrink the direct value-edge set. If you LEGITIMATELY removed a client->value-safe-module direct import, lower this floor; otherwise investigate a flip regression.\n' >&2
|
|
124
|
+
exit 1
|
|
125
|
+
fi
|
|
126
|
+
cases=$((cases + 1))
|
|
127
|
+
ok "AC5b(D3.3): direct-rule baseline count is $DEP_CT (>= floor $DIRECT_BASELINE_FLOOR; flip did not shrink the value-edge set)"
|
|
128
|
+
|
|
129
|
+
# --- AC5 (D4/D3c): VALUE_SAFE_PATH modules are drift-proof value-safe -----------
|
|
130
|
+
# Each module the transitive rule excludes via to.pathNot MUST NOT gain a secret,
|
|
131
|
+
# because it is exempt from BOTH the direct baseline and the transitive pathNot (the
|
|
132
|
+
# deepest fail-open). This is a best-effort content tripwire, NOT a full secret
|
|
133
|
+
# scanner — #5850 (relocation out of server/**) is the structural fix. It flags:
|
|
134
|
+
# (a) a process.env read (dot OR bracket access) — dependency-cruiser cannot see
|
|
135
|
+
# this, so it is the guard's UNIQUE contribution;
|
|
136
|
+
# (b) a value import/re-export edge (single- OR multi-line; import type / export
|
|
137
|
+
# type are erased at build -> safe) — backstopped by the transitive rule for
|
|
138
|
+
# edges to NON-value-safe modules, checked here for defense in depth;
|
|
139
|
+
# (c) a hardcoded secret LITERAL (known token prefixes / PEM header) with no import
|
|
140
|
+
# and no env read — otherwise uncaught by anything.
|
|
141
|
+
# Derive the module list FROM the .cjs VALUE_SAFE_PATH alternation so the two never drift.
|
|
142
|
+
check_value_safe_drift() { # returns 0 = value-safe, 1 = drift detected
|
|
143
|
+
local f="$1"
|
|
144
|
+
# (a) process.env value read — dot (`process.env.X`) OR bracket (`process["env"]`, `process['env']`).
|
|
145
|
+
grep -qE 'process\.env|process[[:space:]]*\[[[:space:]]*["'\''`]env' "$f" && return 1
|
|
146
|
+
# (c) hardcoded secret literal (canonical token prefixes + PEM header). Same shape
|
|
147
|
+
# the repo-wide secret scanners use; a value-safe registry must hold no secret.
|
|
148
|
+
grep -qE 'BEGIN [A-Z ]*PRIVATE KEY|ghp_[A-Za-z0-9._~+/=-]{20,}|gho_[A-Za-z0-9._~+/=-]{20,}|ghu_[A-Za-z0-9._~+/=-]{20,}|ghs_[A-Za-z0-9._~+/=-]{20,}|ghr_[A-Za-z0-9._~+/=-]{20,}|github_pat_[A-Za-z0-9._~+/=-]{20,}|sk-ant-[A-Za-z0-9_-]{20,}|sk_(test|live)_[A-Za-z0-9]{20,}|sbp_[A-Za-z0-9]{20,}|xoxb-[A-Za-z0-9-]{20,}|AKIA[0-9A-Z]{16}|dp\.(pt|st|sa|ct)\.[A-Za-z0-9_-]{40,}' "$f" && return 1
|
|
149
|
+
# (b1) a value IMPORT statement: a line beginning with `import ` (the single-line
|
|
150
|
+
# form OR the opener of a multi-line import) that is NOT a whole-clause `import type`.
|
|
151
|
+
# A mixed `import { type A, realX }` opener begins with `import ` (not `import type`),
|
|
152
|
+
# so its value half is still flagged.
|
|
153
|
+
if grep -E '^[[:space:]]*import[[:space:]]' "$f" \
|
|
154
|
+
| grep -vE '^[[:space:]]*import[[:space:]]+type[[:space:]]' | grep -c >/dev/null .; then
|
|
155
|
+
return 1
|
|
156
|
+
fi
|
|
157
|
+
# (b2) a value RE-EXPORT edge: `export ... from "..."` that is NOT `export type ... from`.
|
|
158
|
+
# (Plain `export const/function/type X = …` declarations carry no `from` and are safe.)
|
|
159
|
+
if grep -E '^[[:space:]]*export[[:space:]].*[[:space:]]from[[:space:]]*["'\''`]' "$f" \
|
|
160
|
+
| grep -vE '^[[:space:]]*export[[:space:]]+type[[:space:]]' | grep -c >/dev/null .; then
|
|
161
|
+
return 1
|
|
162
|
+
fi
|
|
163
|
+
# (b3) dynamic `import(...)` / `require(...)` edge.
|
|
164
|
+
grep -qE '\b(import|require)[[:space:]]*\(' "$f" && return 1
|
|
165
|
+
return 0
|
|
166
|
+
}
|
|
167
|
+
# Non-vacuity: each drift class MUST trip the guard; a clean import-type-only module
|
|
168
|
+
# MUST pass. Build the secret literal by concatenation so no contiguous token is
|
|
169
|
+
# committed to this test file (GitHub push protection) while the runtime fixture holds
|
|
170
|
+
# the real shape.
|
|
171
|
+
DRIFT_TMP="$(mktemp -d)"
|
|
172
|
+
# Own the cleanup with a trap, not just the `rm -rf` at the end of this block. The
|
|
173
|
+
# trailing rm only runs on the happy path — any early exit between here and there (a
|
|
174
|
+
# `set -e` abort in a future edit, an operator ^C, a SIGTERM) skips it and leaks the
|
|
175
|
+
# directory. /tmp here is a 4 GiB RAM-backed tmpfs, so leaked scratch is not free.
|
|
176
|
+
trap 'rm -rf -- "${DRIFT_TMP:-}"' EXIT INT TERM
|
|
177
|
+
printf 'export const X = process.env.SECRET_KEY;\n' > "$DRIFT_TMP/bad-env-dot.ts"
|
|
178
|
+
printf 'export const X = process["env"]["SECRET_KEY"];\n' > "$DRIFT_TMP/bad-env-bracket.ts"
|
|
179
|
+
SK="sk_""live_"; printf 'export const K = "%s%s";\n' "$SK" "0123456789abcdefABCDEF0123456789" > "$DRIFT_TMP/bad-literal.ts"
|
|
180
|
+
# New-shape GitHub installation token (ghs_<APPID>_<JWT>, ~390-520 chars over the
|
|
181
|
+
# canonical credential alphabet). Prefix split-assembled for the same reason as SK
|
|
182
|
+
# above; the 64-char tail (x6 = 384 + 4 = 388 total) carries every newly-admitted
|
|
183
|
+
# byte, with `.` at tail offset 13 so the legacy [A-Za-z0-9_]{20,} run dies below
|
|
184
|
+
# its floor — a reverted class CANNOT trip on this fixture, so the widened arm is
|
|
185
|
+
# what the drift row below actually proves.
|
|
186
|
+
GHS="ghs""_"
|
|
187
|
+
GHS_SEG='kJ8xQ2mW9pL4.vR7tN3bS_5y~cH0gF+uA6wE9zX2M7/K4dP8qT1jG6vB0nC=hZ-i'
|
|
188
|
+
printf 'export const K = "%s%s";\n' "$GHS" "$GHS_SEG$GHS_SEG$GHS_SEG$GHS_SEG$GHS_SEG$GHS_SEG" > "$DRIFT_TMP/bad-ghs-literal.ts"
|
|
189
|
+
printf 'import {\n SECRET_TOKEN,\n} from "@/server/secret";\nexport const g = () => SECRET_TOKEN;\n' > "$DRIFT_TMP/bad-multiline.ts"
|
|
190
|
+
printf 'import { X } from "@/server/secret";\nexport const g = () => X;\n' > "$DRIFT_TMP/bad-import.ts"
|
|
191
|
+
printf 'export { SECRET_TOKEN } from "@/server/secret";\n' > "$DRIFT_TMP/bad-reexport.ts"
|
|
192
|
+
printf 'export const load = () => import("@/server/secret");\n' > "$DRIFT_TMP/bad-dynamic.ts"
|
|
193
|
+
printf 'import type { T } from "@/lib/types";\nexport const LEADERS = ["a"] as const;\nexport function isX(s: string): s is T { return true; }\n' > "$DRIFT_TMP/good.ts"
|
|
194
|
+
for bad in bad-env-dot bad-env-bracket bad-literal bad-ghs-literal bad-multiline bad-import bad-reexport bad-dynamic; do
|
|
195
|
+
cases=$((cases + 1))
|
|
196
|
+
if ! check_value_safe_drift "$DRIFT_TMP/$bad.ts"; then
|
|
197
|
+
ok "AC5(D4): drift guard TRIPS on $bad (non-vacuous)"
|
|
198
|
+
else
|
|
199
|
+
bad "AC5(D4): drift guard did NOT trip on $bad (vacuous fail-open!)"
|
|
200
|
+
fi
|
|
201
|
+
done
|
|
202
|
+
# good.ts mirrors the real modules' shape: an `import type`, an `export const` array,
|
|
203
|
+
# and an `export function` declaration — none are value edges.
|
|
204
|
+
cases=$((cases + 1))
|
|
205
|
+
if check_value_safe_drift "$DRIFT_TMP/good.ts"; then
|
|
206
|
+
ok "AC5(D4): drift guard PASSES a registry with import-type + export-const/function only (no false positive)"
|
|
207
|
+
else
|
|
208
|
+
bad "AC5(D4): drift guard false-positived on a clean value-safe registry"
|
|
209
|
+
fi
|
|
210
|
+
rm -rf "$DRIFT_TMP"
|
|
211
|
+
|
|
212
|
+
# --- P2 (VALUE_SAFE_PATH anchoring): dot-suffixed siblings are NOT excluded ------
|
|
213
|
+
# The pathNot regex is end-anchored to the file extension; a bare trailing `\.` would
|
|
214
|
+
# prefix-match `server/providers.server.ts` and silently exempt it from the transitive
|
|
215
|
+
# rule. Read the RUNTIME pathNot value off the required config (avoids source-escaping
|
|
216
|
+
# ambiguity) and assert sibling non-match + real-module match.
|
|
217
|
+
cases=$((cases + 1))
|
|
218
|
+
if node -e '
|
|
219
|
+
const cfg = require(process.argv[1]);
|
|
220
|
+
const rule = (cfg.forbidden||[]).find(r=>r.name==="no-client-to-server-secret-transitive");
|
|
221
|
+
if (!rule || !rule.to || !rule.to.pathNot) process.exit(2);
|
|
222
|
+
const re = new RegExp(rule.to.pathNot);
|
|
223
|
+
const must = ["server/providers.ts","server/scope-grants/action-class-map.ts"];
|
|
224
|
+
const mustNot = ["server/providers.server.ts","server/providers.keys.ts","server/domain-leaders.data.ts","server/providers-internal.ts"];
|
|
225
|
+
process.exit(must.every(p=>re.test(p)) && mustNot.every(p=>!re.test(p)) ? 0 : 1);
|
|
226
|
+
' "$CFG" 2>/dev/null; then
|
|
227
|
+
ok "AC5(D4/P2): VALUE_SAFE_PATH matches real modules but NOT dot-suffixed siblings (end-anchored)"
|
|
228
|
+
else
|
|
229
|
+
bad "AC5(D4/P2): VALUE_SAFE_PATH anchoring wrong — a dot-suffixed sibling (providers.server.ts) is excluded, or a real module is not matched"
|
|
230
|
+
fi
|
|
231
|
+
# Extract the module basenames from the .cjs VALUE_SAFE_PATH alternation and assert
|
|
232
|
+
# each real server module is still value-safe.
|
|
233
|
+
VS_MODULES="$(grep -E 'VALUE_SAFE_PATH[[:space:]]*=' -A1 "$CFG" | grep -oE '\([^)]*\)' | head -1 | tr -d '()' | tr '|' ' ')"
|
|
234
|
+
if [[ -z "$VS_MODULES" ]]; then
|
|
235
|
+
cases=$((cases + 1))
|
|
236
|
+
bad "AC5(D4): could not extract VALUE_SAFE_PATH module list from $CFG"
|
|
237
|
+
else
|
|
238
|
+
# Ratchet both floors onto the module count the config actually declares (see the derivation
|
|
239
|
+
# note beside their declaration). Read from $CFG, never from the loop below, so deleting the
|
|
240
|
+
# loop still trips the floor rather than shrinking it in lockstep.
|
|
241
|
+
read -ra _vs_declared <<< "$VS_MODULES"
|
|
242
|
+
vs_expected=${#_vs_declared[@]}
|
|
243
|
+
if [[ "$vs_expected" -gt 1 ]]; then
|
|
244
|
+
TOOLCHAIN_FREE_MIN_ASSERTIONS=$((12 + vs_expected))
|
|
245
|
+
MIN_ASSERTIONS=$((TOOLCHAIN_FREE_MIN_ASSERTIONS + 22))
|
|
246
|
+
printf 'note - anti-vacuity floors derived from %d declared VALUE_SAFE_PATH module(s): toolchain-free >= %d, full >= %d\n' \
|
|
247
|
+
"$vs_expected" "$TOOLCHAIN_FREE_MIN_ASSERTIONS" "$MIN_ASSERTIONS"
|
|
248
|
+
fi
|
|
249
|
+
vs_checked=0
|
|
250
|
+
for m in $VS_MODULES; do
|
|
251
|
+
mf="$APP/server/${m}.ts"
|
|
252
|
+
cases=$((cases + 1))
|
|
253
|
+
if [[ ! -f "$mf" ]]; then
|
|
254
|
+
bad "AC5(D4): VALUE_SAFE_PATH lists server/${m} but $mf does not exist"
|
|
255
|
+
continue
|
|
256
|
+
fi
|
|
257
|
+
vs_checked=$((vs_checked + 1))
|
|
258
|
+
if check_value_safe_drift "$mf"; then
|
|
259
|
+
ok "AC5(D4): server/${m}.ts is value-safe (no process.env value read / no value import)"
|
|
260
|
+
else
|
|
261
|
+
bad "AC5(D4): server/${m}.ts DRIFTED out of value-safe (reads process.env or value-imports) — it is pathNot-excluded, so a secret there ships green; fix it or remove from VALUE_SAFE_PATH (see #5850)"
|
|
262
|
+
fi
|
|
263
|
+
done
|
|
264
|
+
# ADR-193 #1: reports directly, never through bad(). This is the floor that notices the
|
|
265
|
+
# VALUE_SAFE_PATH loop asserted NOTHING; routed through bad() it was silenced by the same
|
|
266
|
+
# neutered-helper fault it exists to witness. Its operand ($vs_checked) is independent of
|
|
267
|
+
# the verdict counters.
|
|
268
|
+
if [[ "$vs_checked" -lt 1 ]]; then
|
|
269
|
+
printf '\n[FATAL] AC5(D4) VALUE_SAFE_PATH floor: zero modules checked, expected >= 1.\n' >&2
|
|
270
|
+
printf ' The .cjs VALUE_SAFE_PATH extraction or the module glob broke — every drift assertion in this block was skipped, so a green run here is vacuous.\n' >&2
|
|
271
|
+
exit 1
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# --- locate (or install) the dependency-cruiser binary -----------------------
|
|
276
|
+
# Fast path: the installed web-platform binary (present locally + in the
|
|
277
|
+
# test-webplat CI shard). Fallback: the test-scripts shard has node+npm but no
|
|
278
|
+
# web-platform deps, so install dependency-cruiser on demand into a temp dir.
|
|
279
|
+
TMPROOT="$(mktemp -d)" || { echo "FATAL: cannot create scratch root" >&2; exit 1; }
|
|
280
|
+
: "${TMPROOT:?scratch root must be non-empty}"
|
|
281
|
+
[[ "$TMPROOT" == /* && -d "$TMPROOT" && ! -L "$TMPROOT" ]] || {
|
|
282
|
+
echo "FATAL: scratch root is not a plain absolute directory: '$TMPROOT'" >&2; exit 1; }
|
|
283
|
+
# EXIT **INT TERM**, not EXIT alone. bash traps REPLACE rather than stack, so an
|
|
284
|
+
# EXIT-only registration here left the earlier `… INT TERM` handler live and still
|
|
285
|
+
# pointing at DRIFT_TMP — which is already deleted by this point. A ^C during the
|
|
286
|
+
# `npm install` below (the longest step in this file) would then run that stale handler,
|
|
287
|
+
# never touch TMPROOT, and orphan a full node_modules tree onto the 4 GiB tmpfs: the
|
|
288
|
+
# exact leak class this PR exists to close, reintroduced in the second half of the file.
|
|
289
|
+
trap 'rm -rf -- "${TMPROOT:-}"' EXIT INT TERM
|
|
290
|
+
|
|
291
|
+
DEPCRUISE="$APP/node_modules/.bin/depcruise"
|
|
292
|
+
if [[ ! -x "$DEPCRUISE" ]]; then
|
|
293
|
+
echo "# dependency-cruiser not installed in apps/web-platform — installing on demand..." >&2
|
|
294
|
+
# Seed an empty package.json so npm installs INTO this temp dir. Without it,
|
|
295
|
+
# `npm install` in a bare dir walks UP the tree, finds a parent project, reports
|
|
296
|
+
# "up to date", and installs nothing → binary missing (env-dependent fragility).
|
|
297
|
+
printf '{"name":"constraint-scaffold-test","private":true}\n' > "$TMPROOT/package.json"
|
|
298
|
+
# typescript is REQUIRED alongside dependency-cruiser: depcruise cannot parse
|
|
299
|
+
# .ts/.tsx (nor honor tsConfig alias resolution + tsPreCompilationDeps) without
|
|
300
|
+
# the TS compiler. Omitting it makes depcruise emit ZERO edges from the .tsx
|
|
301
|
+
# fixtures → every positive-detection assertion silently fails (the scripts-shard
|
|
302
|
+
# CI failure on PR #5770). The non-vacuity guard below fails loud if this regresses.
|
|
303
|
+
( cd "$TMPROOT" && npm install --no-audit --no-fund dependency-cruiser@^16 typescript >/dev/null 2>&1 ) \
|
|
304
|
+
|| { echo "fatal: could not install dependency-cruiser + typescript"; exit 2; }
|
|
305
|
+
DEPCRUISE="$TMPROOT/node_modules/.bin/depcruise"
|
|
306
|
+
[[ -x "$DEPCRUISE" ]] || { echo "fatal: dependency-cruiser binary still missing after install"; exit 2; }
|
|
307
|
+
fi
|
|
308
|
+
NODE_MODULES="$(cd "$(dirname "$DEPCRUISE")/.." && pwd)"
|
|
309
|
+
|
|
310
|
+
# --- build a hermetic fixture Next.js-shaped app -----------------------------
|
|
311
|
+
# Returns the fixture path on stdout.
|
|
312
|
+
make_fixture() {
|
|
313
|
+
local fx="$TMPROOT/fixture-$1"
|
|
314
|
+
mkdir -p "$fx/server" "$fx/components/leakdir" "$fx/components/(a|b)"
|
|
315
|
+
ln -s "$NODE_MODULES" "$fx/node_modules"
|
|
316
|
+
cat > "$fx/tsconfig.json" <<'JSON'
|
|
317
|
+
{
|
|
318
|
+
"compilerOptions": {
|
|
319
|
+
"baseUrl": ".",
|
|
320
|
+
"module": "esnext",
|
|
321
|
+
"moduleResolution": "bundler",
|
|
322
|
+
"jsx": "preserve",
|
|
323
|
+
"paths": { "@/*": ["./*"] }
|
|
324
|
+
},
|
|
325
|
+
"include": ["**/*.ts", "**/*.tsx"]
|
|
326
|
+
}
|
|
327
|
+
JSON
|
|
328
|
+
printf 'export const SECRET_TOKEN = "sk-do-not-ship";\nexport type SecretShape = { id: string };\n' > "$fx/server/secret.ts"
|
|
329
|
+
# client module taking a VALUE import on a server secret -> must be flagged
|
|
330
|
+
printf '"use client";\nimport { SECRET_TOKEN } from "@/server/secret";\nexport const Leak = () => SECRET_TOKEN;\n' > "$fx/components/leakdir/leak.tsx"
|
|
331
|
+
# client module taking a TYPE-ONLY import of the same -> must NOT be flagged
|
|
332
|
+
printf '"use client";\nimport type { SecretShape } from "@/server/secret";\nexport const T = (_: SecretShape) => null;\n' > "$fx/components/leakdir/typeonly.tsx"
|
|
333
|
+
# client module under a regex-metacharacter route-group dir "(a|b)" -> must be
|
|
334
|
+
# flagged. If the from-path were NOT regex-escaped, ^components/(a|b)/...$
|
|
335
|
+
# would match literal "a"/"b" (never the literal "(a|b)" source) and the leak
|
|
336
|
+
# would be silently missed.
|
|
337
|
+
printf '"use client";\nimport { SECRET_TOKEN } from "@/server/secret";\nexport const P = () => SECRET_TOKEN;\n' > "$fx/components/(a|b)/parenleak.tsx"
|
|
338
|
+
# client module with a leading license/eslint LINE-comment before the
|
|
339
|
+
# directive (valid Next.js). If the directive test required the directive to
|
|
340
|
+
# be the exact first non-empty line, this is misclassified as non-client and
|
|
341
|
+
# its value import goes UNFLAGGED (silent leak). -> must be flagged.
|
|
342
|
+
printf '// SPDX-License-Identifier: MIT\n"use client";\nimport { SECRET_TOKEN } from "@/server/secret";\nexport const B = () => SECRET_TOKEN;\n' > "$fx/components/leakdir/bannerleak.tsx"
|
|
343
|
+
# client module with a leading BLOCK-comment banner before the directive.
|
|
344
|
+
printf '/* banner\n spanning lines */\n"use client";\nimport { SECRET_TOKEN } from "@/server/secret";\nexport const K = () => SECRET_TOKEN;\n' > "$fx/components/leakdir/blockbannerleak.tsx"
|
|
345
|
+
# client module with a trailing comment on the directive line: `"use client"; // x`.
|
|
346
|
+
printf '"use client"; // hydration boundary\nimport { SECRET_TOKEN } from "@/server/secret";\nexport const H = () => SECRET_TOKEN;\n' > "$fx/components/leakdir/trailingleak.tsx"
|
|
347
|
+
|
|
348
|
+
# ---- #5777 TRANSITIVE-rule fixtures: helper in lib/ (outside app/components/ --
|
|
349
|
+
# dependency-cruiser still FOLLOWS the edge into lib/ for reachability even though
|
|
350
|
+
# lib/ is not a cruise root). `server/secret.ts` is NOT in VALUE_SAFE_PATH, so the
|
|
351
|
+
# transitive rule genuinely fires; `server/domain-leaders.ts` IS (pathNot target).
|
|
352
|
+
mkdir -p "$fx/lib" "$fx/components/trans"
|
|
353
|
+
printf 'export const LEADERS = ["a"] as const;\n' > "$fx/server/domain-leaders.ts"
|
|
354
|
+
# 4.1 NEGATIVE transitive (client -> lib helper(value) -> server/secret(value)) -> MUST FLAG
|
|
355
|
+
printf 'import { SECRET_TOKEN } from "@/server/secret";\nexport const fmtT = () => SECRET_TOKEN;\n' > "$fx/lib/leak-helper.ts"
|
|
356
|
+
printf '"use client";\nimport { fmtT } from "@/lib/leak-helper";\nexport const Trans = () => fmtT();\n' > "$fx/components/trans/transitive.tsx"
|
|
357
|
+
# 4.2 POSITIVE first-hop type-only (client -> import type helper -> server value) -> MUST NOT FLAG
|
|
358
|
+
printf 'import { SECRET_TOKEN } from "@/server/secret";\nexport const g = () => SECRET_TOKEN;\nexport type GT = string;\n' > "$fx/lib/typed-helper.ts"
|
|
359
|
+
printf '"use client";\nimport type { GT } from "@/lib/typed-helper";\nexport const TFirst = (_: GT) => null;\n' > "$fx/components/trans/typeonly-firsthop.tsx"
|
|
360
|
+
# 4.3 NEGATIVE mixed import { type A, realValue } on BOTH hops -> value edge MUST survive -> MUST FLAG
|
|
361
|
+
printf 'import { type SecretShape, SECRET_TOKEN } from "@/server/secret";\nexport type MHT = SecretShape;\nexport const mfmt = (_: SecretShape) => SECRET_TOKEN;\n' > "$fx/lib/mixed-helper.ts"
|
|
362
|
+
printf '"use client";\nimport { type MHT, mfmt } from "@/lib/mixed-helper";\nexport const Mx = (_: MHT) => mfmt(_);\n' > "$fx/components/trans/mixed.tsx"
|
|
363
|
+
# 4.4 NEGATIVE barrel/re-export (export * AND named export-from) -> MUST FLAG
|
|
364
|
+
printf 'export * from "@/server/secret";\n' > "$fx/lib/barrel.ts"
|
|
365
|
+
printf '"use client";\nimport { SECRET_TOKEN } from "@/lib/barrel";\nexport const Bx = () => SECRET_TOKEN;\n' > "$fx/components/trans/barrel.tsx"
|
|
366
|
+
printf 'export { SECRET_TOKEN } from "@/server/secret";\n' > "$fx/lib/named-barrel.ts"
|
|
367
|
+
printf '"use client";\nimport { SECRET_TOKEN } from "@/lib/named-barrel";\nexport const NBx = () => SECRET_TOKEN;\n' > "$fx/components/trans/named-barrel.tsx"
|
|
368
|
+
# 4.5 NEGATIVE dynamic import (statically resolvable) -> MUST FLAG
|
|
369
|
+
printf 'export async function load(){ const m = await import("@/server/secret"); return m.SECRET_TOKEN; }\n' > "$fx/lib/dyn-helper.ts"
|
|
370
|
+
printf '"use client";\nimport { load } from "@/lib/dyn-helper";\nexport const Dx = () => load();\n' > "$fx/components/trans/dynamic.tsx"
|
|
371
|
+
# 4.6 POSITIVE pathNot target (client -> lib helper(value) -> server/domain-leaders(value)) -> MUST NOT FLAG
|
|
372
|
+
printf 'import { LEADERS } from "@/server/domain-leaders";\nexport const safe = () => LEADERS;\n' > "$fx/lib/safe-helper.ts"
|
|
373
|
+
printf '"use client";\nimport { safe } from "@/lib/safe-helper";\nexport const SafeTarget = () => safe();\n' > "$fx/components/trans/safe-target.tsx"
|
|
374
|
+
|
|
375
|
+
cp "$CFG" "$fx/.dependency-cruiser.cjs"
|
|
376
|
+
printf '%s' "$fx"
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
FX="$(make_fixture main)"
|
|
380
|
+
|
|
381
|
+
# --- Toolchain probe: can this depcruise actually PARSE the .tsx fixtures? -----
|
|
382
|
+
# The lightweight `test-scripts` CI shard has no apps/web-platform/node_modules,
|
|
383
|
+
# so depcruise is provisioned on-demand; that install can lack the exact TS
|
|
384
|
+
# toolchain depcruise needs to traverse .tsx (observed: "0 modules cruised" even
|
|
385
|
+
# with typescript present). When depcruise parses ZERO modules from a fixture
|
|
386
|
+
# that demonstrably contains .tsx client files, the TOOLCHAIN is the problem, not
|
|
387
|
+
# the gate — SKIP cleanly (exit 0) rather than emit false failures. The gate's
|
|
388
|
+
# real end-to-end coverage is the `constraint-gates` dogfood workflow (cruises
|
|
389
|
+
# the live apps/web-platform tree) plus local/webplat runs that use the committed
|
|
390
|
+
# depcruise. A gate REGRESSION (parses modules but misses a violation) still
|
|
391
|
+
# FAILS below — only a non-parsing toolchain skips. Non-vacuous by construction:
|
|
392
|
+
# the skip is loud and names where the real validation lives.
|
|
393
|
+
PARSED_COMPONENTS="$( cd "$FX" && "$DEPCRUISE" --config .dependency-cruiser.cjs --output-type json components server 2>/dev/null | node -e '
|
|
394
|
+
let s=""; process.stdin.on("data",d=>s+=d); process.stdin.on("end",()=>{
|
|
395
|
+
let n=0; try { const j=JSON.parse(s);
|
|
396
|
+
for (const m of j.modules||[]) if (/^components\//.test(m.source||"")) n++;
|
|
397
|
+
} catch(e){ n=0; } process.stdout.write(String(n)); });' 2>/dev/null )"
|
|
398
|
+
if ! [[ "$PARSED_COMPONENTS" =~ ^[0-9]+$ ]] || [[ "$PARSED_COMPONENTS" -lt 1 ]]; then
|
|
399
|
+
echo "SKIP: depcruise parsed 0 component modules from the .tsx fixtures — toolchain"
|
|
400
|
+
echo " unavailable in this shard. The L1 gate is validated end-to-end by the"
|
|
401
|
+
echo " constraint-gates dogfood workflow (live apps/web-platform) + local runs."
|
|
402
|
+
# This `exit 0` is a SECOND clean-exit door, so the trailer's conservation check and floor
|
|
403
|
+
# are repeated here rather than referenced: a guarantee that only guards one of two exits is
|
|
404
|
+
# not a guarantee. Everything above this point is toolchain-FREE and runs in EVERY shard, so
|
|
405
|
+
# it carries its own floor. Conservation FIRST (ADR-193 #4), both reported directly.
|
|
406
|
+
if [[ $((pass + fail)) -ne "$cases" ]]; then
|
|
407
|
+
printf '\n[FATAL] accounting: pass+fail (%d) != cases (%d).\n' "$((pass + fail))" "$cases" >&2
|
|
408
|
+
if [[ $((pass + fail)) -lt "$cases" ]]; then
|
|
409
|
+
printf ' An assertion was counted but its verdict was not recorded — that is what a neutered ok()/bad() looks like.\n' >&2
|
|
410
|
+
else
|
|
411
|
+
printf ' A verdict was recorded at a call site with no `cases=$((cases + 1))` before it. This is a harness bug, not a product failure: add the increment at that call site.\n' >&2
|
|
412
|
+
fi
|
|
413
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
414
|
+
exit 1
|
|
415
|
+
fi
|
|
416
|
+
if [[ "$cases" -lt "$TOOLCHAIN_FREE_MIN_ASSERTIONS" ]]; then
|
|
417
|
+
printf '\n[FATAL] anti-vacuity floor (toolchain-free half): only %d assertion(s) ran, expected >= %d.\n' \
|
|
418
|
+
"$cases" "$TOOLCHAIN_FREE_MIN_ASSERTIONS" >&2
|
|
419
|
+
printf ' Arms were deleted or skipped before the toolchain probe; skipping the depcruise half is sanctioned, asserting nothing at all is not.\n' >&2
|
|
420
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
421
|
+
exit 1
|
|
422
|
+
fi
|
|
423
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
424
|
+
[[ "$fail" -eq 0 ]]
|
|
425
|
+
exit
|
|
426
|
+
fi
|
|
427
|
+
|
|
428
|
+
# --- AC3 + AC6b: positive/negative + regex-escaping --------------------------
|
|
429
|
+
ERR_OUT="$( cd "$FX" && "$DEPCRUISE" --config .dependency-cruiser.cjs --output-type err components server 2>&1 )"
|
|
430
|
+
|
|
431
|
+
cases=$((cases + 1))
|
|
432
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/leakdir/leak.tsx'; then
|
|
433
|
+
ok "AC3: value import of server/** via @/server alias is flagged"
|
|
434
|
+
else
|
|
435
|
+
bad "AC3: value import of server/** via @/server alias was NOT flagged"
|
|
436
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
437
|
+
fi
|
|
438
|
+
|
|
439
|
+
cases=$((cases + 1))
|
|
440
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/leakdir/typeonly.tsx'; then
|
|
441
|
+
bad "AC3: import type of server/** was flagged (type-only must be allowed)"
|
|
442
|
+
else
|
|
443
|
+
ok "AC3: import type of server/** is allowed (not flagged)"
|
|
444
|
+
fi
|
|
445
|
+
|
|
446
|
+
cases=$((cases + 1))
|
|
447
|
+
if printf '%s' "$ERR_OUT" | grep -cF >/dev/null 'components/(a|b)/parenleak.tsx'; then
|
|
448
|
+
ok "AC6b: regex-metacharacter route-group path matched (regex-escaping works)"
|
|
449
|
+
else
|
|
450
|
+
bad "AC6b: parenthesized-path client file NOT matched — regex-escaping is broken"
|
|
451
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
452
|
+
fi
|
|
453
|
+
|
|
454
|
+
# --- #2: directive preceded by a leading comment banner is still client -------
|
|
455
|
+
cases=$((cases + 1))
|
|
456
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/leakdir/bannerleak.tsx'; then
|
|
457
|
+
ok "#2: leading line-comment before \"use client\" still classified client (flagged)"
|
|
458
|
+
else
|
|
459
|
+
bad "#2: leading line-comment banner client file NOT flagged (fail-open misclassification)"
|
|
460
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
461
|
+
fi
|
|
462
|
+
cases=$((cases + 1))
|
|
463
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/leakdir/blockbannerleak.tsx'; then
|
|
464
|
+
ok "#2: leading block-comment before \"use client\" still classified client (flagged)"
|
|
465
|
+
else
|
|
466
|
+
bad "#2: leading block-comment banner client file NOT flagged (fail-open misclassification)"
|
|
467
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
468
|
+
fi
|
|
469
|
+
cases=$((cases + 1))
|
|
470
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/leakdir/trailingleak.tsx'; then
|
|
471
|
+
ok "#2: \"use client\"; // trailing-comment form still classified client (flagged)"
|
|
472
|
+
else
|
|
473
|
+
bad "#2: trailing-comment directive form client file NOT flagged (fail-open misclassification)"
|
|
474
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
475
|
+
fi
|
|
476
|
+
|
|
477
|
+
# --- #5777 TRANSITIVE rule assertions (AC3, D1/D2). ERR_OUT is the same
|
|
478
|
+
# `components server` cruise; dependency-cruiser follows edges into lib/ for
|
|
479
|
+
# reachability. Anti-vacuity (4.9): we are PAST the toolchain SKIP guard, so these
|
|
480
|
+
# MUST execute and MUST fail (not skip) if a negative fixture is not flagged.
|
|
481
|
+
# 4.1 NEGATIVE transitive via lib/ helper -> MUST FLAG
|
|
482
|
+
cases=$((cases + 1))
|
|
483
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/transitive.tsx'; then
|
|
484
|
+
ok "4.1: transitive value chain (client -> lib helper -> server/secret) is flagged"
|
|
485
|
+
else
|
|
486
|
+
bad "4.1: transitive value chain NOT flagged (the #5777 gap is still open)"
|
|
487
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
488
|
+
fi
|
|
489
|
+
# 4.2 POSITIVE first-hop type-only -> MUST NOT FLAG (type-only edge elided globally)
|
|
490
|
+
cases=$((cases + 1))
|
|
491
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/typeonly-firsthop.tsx'; then
|
|
492
|
+
bad "4.2: first-hop import-type chain was FLAGGED (type-only must be elided -> false positive)"
|
|
493
|
+
else
|
|
494
|
+
ok "4.2: first-hop import-type chain is not flagged (type-only elided, position-independent)"
|
|
495
|
+
fi
|
|
496
|
+
# 4.3 NEGATIVE mixed import { type A, realValue } -> value edge survives -> MUST FLAG
|
|
497
|
+
cases=$((cases + 1))
|
|
498
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/mixed.tsx'; then
|
|
499
|
+
ok "4.3: mixed { type A, realValue } chain is flagged (value edge survives the flip)"
|
|
500
|
+
else
|
|
501
|
+
bad "4.3: mixed-import chain NOT flagged — a value edge was wrongly elided as type-only (silent fail-open)"
|
|
502
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
503
|
+
fi
|
|
504
|
+
# 4.4 NEGATIVE barrel (export *) + named export-from -> MUST FLAG
|
|
505
|
+
cases=$((cases + 1))
|
|
506
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/barrel.tsx'; then
|
|
507
|
+
ok "4.4a: barrel re-export (export * from) chain is flagged"
|
|
508
|
+
else
|
|
509
|
+
bad "4.4a: barrel (export *) chain NOT flagged"
|
|
510
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
511
|
+
fi
|
|
512
|
+
cases=$((cases + 1))
|
|
513
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/named-barrel.tsx'; then
|
|
514
|
+
ok "4.4b: named export-from re-export chain is flagged"
|
|
515
|
+
else
|
|
516
|
+
bad "4.4b: named export-from chain NOT flagged"
|
|
517
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
518
|
+
fi
|
|
519
|
+
# 4.5 NEGATIVE dynamic import() -> MUST FLAG
|
|
520
|
+
cases=$((cases + 1))
|
|
521
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/dynamic.tsx'; then
|
|
522
|
+
ok "4.5: dynamic import() chain is flagged (reachability traverses dynamic edges)"
|
|
523
|
+
else
|
|
524
|
+
bad "4.5: dynamic import() chain NOT flagged"
|
|
525
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
526
|
+
fi
|
|
527
|
+
# 4.6 POSITIVE pathNot target (server/domain-leaders) -> MUST NOT FLAG by transitive rule
|
|
528
|
+
cases=$((cases + 1))
|
|
529
|
+
if printf '%s' "$ERR_OUT" | grep -c >/dev/null 'components/trans/safe-target.tsx'; then
|
|
530
|
+
bad "4.6: pathNot-target chain (client -> helper -> server/domain-leaders) was FLAGGED (pathNot broken)"
|
|
531
|
+
printf '%s\n' "$ERR_OUT" | sed 's/^/ /'
|
|
532
|
+
else
|
|
533
|
+
ok "4.6: pathNot-target chain is not flagged (value-safe target excluded, no baseline double-count)"
|
|
534
|
+
fi
|
|
535
|
+
|
|
536
|
+
# --- AC3: 0 couldNotResolve edges into the secret set ------------------------
|
|
537
|
+
JSON_OUT="$( cd "$FX" && "$DEPCRUISE" --config .dependency-cruiser.cjs --output-type json components server 2>/dev/null )"
|
|
538
|
+
UNRESOLVED_INTO_SERVER="$(
|
|
539
|
+
printf '%s' "$JSON_OUT" | node -e '
|
|
540
|
+
let s = ""; process.stdin.on("data", d => s += d); process.stdin.on("end", () => {
|
|
541
|
+
let n = 0;
|
|
542
|
+
try {
|
|
543
|
+
const j = JSON.parse(s);
|
|
544
|
+
for (const m of j.modules || [])
|
|
545
|
+
for (const dep of m.dependencies || [])
|
|
546
|
+
if (/^server\//.test(dep.resolved || "") && dep.couldNotResolve) n++;
|
|
547
|
+
} catch (e) { n = -1; }
|
|
548
|
+
process.stdout.write(String(n));
|
|
549
|
+
});
|
|
550
|
+
' 2>/dev/null
|
|
551
|
+
)"
|
|
552
|
+
cases=$((cases + 1))
|
|
553
|
+
if [[ "$UNRESOLVED_INTO_SERVER" == "0" ]]; then
|
|
554
|
+
ok "AC3: 0 couldNotResolve edges into server/ (tsConfig alias resolution live)"
|
|
555
|
+
else
|
|
556
|
+
bad "AC3: couldNotResolve edges into server/ = $UNRESOLVED_INTO_SERVER (expected 0)"
|
|
557
|
+
fi
|
|
558
|
+
|
|
559
|
+
# --- AC6: the secret (`to`) set is explicit and non-empty --------------------
|
|
560
|
+
cases=$((cases + 1))
|
|
561
|
+
if grep -q 'SECRET_PATH = "\^server/"' "$CFG"; then
|
|
562
|
+
ok "AC6: secret (to) set is explicit and non-empty (^server/)"
|
|
563
|
+
else
|
|
564
|
+
bad "AC6: secret (to) set not found / not the expected ^server/ in .cjs"
|
|
565
|
+
fi
|
|
566
|
+
|
|
567
|
+
# --- AC6b: empty from-set while "use client" files exist -> HARD ERROR --------
|
|
568
|
+
EMPTY_OUT="$( cd "$FX" && CONSTRAINT_SCAFFOLD_TEST_FORCE_EMPTY=1 node -e 'require("./.dependency-cruiser.cjs")' 2>&1 )"
|
|
569
|
+
EMPTY_RC=$?
|
|
570
|
+
cases=$((cases + 1))
|
|
571
|
+
if [[ "$EMPTY_RC" -ne 0 ]] && printf '%s' "$EMPTY_OUT" | grep -c >/dev/null 'from-set is empty'; then
|
|
572
|
+
ok "AC6b: empty from-set while client files exist throws (not silently disabled)"
|
|
573
|
+
else
|
|
574
|
+
bad "AC6b: empty from-set did NOT hard-fail (rc=$EMPTY_RC): $EMPTY_OUT"
|
|
575
|
+
fi
|
|
576
|
+
|
|
577
|
+
# --- AC5: empty input (no app/ and no components/) fails closed ---------------
|
|
578
|
+
EMPTYDIR="$TMPROOT/no-client-dirs"
|
|
579
|
+
mkdir -p "$EMPTYDIR/server"
|
|
580
|
+
ln -s "$NODE_MODULES" "$EMPTYDIR/node_modules"
|
|
581
|
+
cp "$CFG" "$EMPTYDIR/.dependency-cruiser.cjs"
|
|
582
|
+
NOINPUT_OUT="$( cd "$EMPTYDIR" && node -e 'require("./.dependency-cruiser.cjs")' 2>&1 )"
|
|
583
|
+
NOINPUT_RC=$?
|
|
584
|
+
cases=$((cases + 1))
|
|
585
|
+
if [[ "$NOINPUT_RC" -ne 0 ]] && printf '%s' "$NOINPUT_OUT" | grep -cE >/dev/null 'neither app/ nor components/'; then
|
|
586
|
+
ok "AC5: empty input (no client dirs) fails closed (distinct from 'no client modules')"
|
|
587
|
+
else
|
|
588
|
+
bad "AC5: empty input did NOT fail closed (rc=$NOINPUT_RC): $NOINPUT_OUT"
|
|
589
|
+
fi
|
|
590
|
+
|
|
591
|
+
# --- AC5: broken .cjs -> shared runner fails closed --------------------------
|
|
592
|
+
BROKEN="$TMPROOT/broken-app"
|
|
593
|
+
mkdir -p "$BROKEN/app" "$BROKEN/components" "$BROKEN/server" "$BROKEN/scripts"
|
|
594
|
+
ln -s "$NODE_MODULES" "$BROKEN/node_modules"
|
|
595
|
+
printf 'module.exports = { this is not valid javascript\n' > "$BROKEN/.dependency-cruiser.cjs"
|
|
596
|
+
printf '[]\n' > "$BROKEN/.dependency-cruiser-known-violations.json"
|
|
597
|
+
BROKEN_OUT="$( CONSTRAINT_GATES_DIR="$BROKEN" bash "$RUNNER" 2>&1 )"
|
|
598
|
+
BROKEN_RC=$?
|
|
599
|
+
cases=$((cases + 1))
|
|
600
|
+
if [[ "$BROKEN_RC" -ne 0 ]] && printf '%s' "$BROKEN_OUT" | grep -c >/dev/null 'config/binary error'; then
|
|
601
|
+
ok "AC5: broken .cjs makes the shared runner fail closed (rc=$BROKEN_RC)"
|
|
602
|
+
else
|
|
603
|
+
bad "AC5: broken .cjs did NOT fail the runner closed (rc=$BROKEN_RC): $BROKEN_OUT"
|
|
604
|
+
fi
|
|
605
|
+
|
|
606
|
+
# --- AC10: shared runner is GREEN on a clean baselined fixture ----------------
|
|
607
|
+
# In v2 the reachable rule is a SUPERSET of the direct rule (a direct edge is a
|
|
608
|
+
# length-1 path), so a direct leak to a NON-value-safe module ALSO produces a
|
|
609
|
+
# type:"reachability" violation that must NOT be baselined (the D3 runner guard
|
|
610
|
+
# rejects it). The real app is green because its baselined direct edges all target
|
|
611
|
+
# VALUE_SAFE modules (pathNot-excluded from the reachable rule). Mirror that exactly:
|
|
612
|
+
# a client that directly value-imports a value-safe server module -> the direct rule
|
|
613
|
+
# flags it (a baselineable dependency), the reachable rule excludes it (no
|
|
614
|
+
# reachability entry) -> baseline all -> runner green.
|
|
615
|
+
CLEAN="$BROKEN-clean"
|
|
616
|
+
mkdir -p "$CLEAN/app" "$CLEAN/components/c" "$CLEAN/server" "$CLEAN/scripts"
|
|
617
|
+
ln -s "$NODE_MODULES" "$CLEAN/node_modules"
|
|
618
|
+
cp "$FX/tsconfig.json" "$CLEAN/tsconfig.json"
|
|
619
|
+
cp "$CFG" "$CLEAN/.dependency-cruiser.cjs"
|
|
620
|
+
printf 'export const LEADERS = ["a"] as const;\n' > "$CLEAN/server/domain-leaders.ts"
|
|
621
|
+
printf '"use client";\nimport { LEADERS } from "@/server/domain-leaders";\nexport const V = () => LEADERS;\n' > "$CLEAN/components/c/valuesafe.tsx"
|
|
622
|
+
( cd "$CLEAN" && "$DEPCRUISE" --config .dependency-cruiser.cjs --output-type baseline app components server > .dependency-cruiser-known-violations.json 2>/dev/null )
|
|
623
|
+
CLEAN_OUT="$( CONSTRAINT_GATES_DIR="$CLEAN" bash "$RUNNER" 2>&1 )"
|
|
624
|
+
CLEAN_RC=$?
|
|
625
|
+
cases=$((cases + 1))
|
|
626
|
+
if [[ "$CLEAN_RC" -eq 0 ]]; then
|
|
627
|
+
ok "AC10: shared runner is green (rc=0) when all (value-safe direct) violations are baselined"
|
|
628
|
+
else
|
|
629
|
+
bad "AC10: shared runner not green on baselined fixture (rc=$CLEAN_RC): $CLEAN_OUT"
|
|
630
|
+
fi
|
|
631
|
+
|
|
632
|
+
# --- 4.8: real-runner rc != 0 on an un-baselined TRANSITIVE leak ---------------
|
|
633
|
+
# The full FX fixture contains transitive leaks. With an EMPTY baseline (0
|
|
634
|
+
# reachability entries -> the D3 runner guard passes), the cruise itself must fail
|
|
635
|
+
# on the transitive reachability violation and reach the runner's 'dependency
|
|
636
|
+
# violations' fail branch — proving a reachability violation is enforced end-to-end
|
|
637
|
+
# through the real runner (not only via --output-type json).
|
|
638
|
+
LEAKY="$BROKEN-transitive-leak"
|
|
639
|
+
cp -r "$FX" "$LEAKY"
|
|
640
|
+
rm -f "$LEAKY/node_modules"; ln -s "$NODE_MODULES" "$LEAKY/node_modules"
|
|
641
|
+
mkdir -p "$LEAKY/app" "$LEAKY/scripts"
|
|
642
|
+
# Remove DIRECT-leak fixtures so the ONLY violation the runner sees is transitive —
|
|
643
|
+
# proves the reachable rule (not the direct rule) drives the non-zero exit.
|
|
644
|
+
rm -rf "$LEAKY/components/leakdir" "$LEAKY/components/(a|b)"
|
|
645
|
+
rm -f "$LEAKY/components/trans/typeonly-firsthop.tsx" "$LEAKY/components/trans/safe-target.tsx" \
|
|
646
|
+
"$LEAKY/lib/typed-helper.ts" "$LEAKY/lib/safe-helper.ts"
|
|
647
|
+
printf '[]\n' > "$LEAKY/.dependency-cruiser-known-violations.json"
|
|
648
|
+
LEAKY_OUT="$( CONSTRAINT_GATES_DIR="$LEAKY" bash "$RUNNER" 2>&1 )"
|
|
649
|
+
LEAKY_RC=$?
|
|
650
|
+
cases=$((cases + 1))
|
|
651
|
+
if [[ "$LEAKY_RC" -ne 0 ]] && printf '%s' "$LEAKY_OUT" | grep -c >/dev/null 'import-boundary violation'; then
|
|
652
|
+
ok "4.8: real runner fails closed (rc=$LEAKY_RC) on an un-baselined transitive leak"
|
|
653
|
+
else
|
|
654
|
+
bad "4.8: real runner did NOT fail on a transitive leak (rc=$LEAKY_RC): $LEAKY_OUT"
|
|
655
|
+
fi
|
|
656
|
+
|
|
657
|
+
# --- 4.10: runner rejects a type:"module" baseline entry naming the transitive rule
|
|
658
|
+
# (P1 regression). dependency-cruiser softens the transitive rule per-origin on
|
|
659
|
+
# from+rule.name IGNORING type, so a hand-authored type:"module" entry would suppress
|
|
660
|
+
# a real transitive leak while a type:"reachability"-only guard reads 0. The runner
|
|
661
|
+
# guard must key on the rule name and fail closed. This is the exact bypass a
|
|
662
|
+
# careless/hostile baseline edit would use.
|
|
663
|
+
MODENTRY="$BROKEN-modentry"
|
|
664
|
+
mkdir -p "$MODENTRY/app" "$MODENTRY/components" "$MODENTRY/server" "$MODENTRY/scripts"
|
|
665
|
+
ln -s "$NODE_MODULES" "$MODENTRY/node_modules"
|
|
666
|
+
cp "$CFG" "$MODENTRY/.dependency-cruiser.cjs"
|
|
667
|
+
cat > "$MODENTRY/.dependency-cruiser-known-violations.json" <<'JSON'
|
|
668
|
+
[{"type":"module","from":"components/c/leak.tsx","to":"server/secret.ts","rule":{"severity":"error","name":"no-client-to-server-secret-transitive"}}]
|
|
669
|
+
JSON
|
|
670
|
+
MODENTRY_OUT="$( CONSTRAINT_GATES_DIR="$MODENTRY" bash "$RUNNER" 2>&1 )"
|
|
671
|
+
MODENTRY_RC=$?
|
|
672
|
+
cases=$((cases + 1))
|
|
673
|
+
if [[ "$MODENTRY_RC" -ne 0 ]] && printf '%s' "$MODENTRY_OUT" | grep -c >/dev/null 'suppress the transitive rule'; then
|
|
674
|
+
ok "4.10: runner rejects a type:\"module\" baseline entry naming the transitive rule (rc=$MODENTRY_RC)"
|
|
675
|
+
else
|
|
676
|
+
bad "4.10: runner did NOT reject a type:\"module\" transitive-suppressing baseline entry (rc=$MODENTRY_RC) — P1 bypass open: $MODENTRY_OUT"
|
|
677
|
+
fi
|
|
678
|
+
|
|
679
|
+
# --- 4.11: runner fails closed on a non-array (but valid-JSON) baseline ----------
|
|
680
|
+
# `{}` / `null` / `42` parse fine but are not an entry array; the guard must treat
|
|
681
|
+
# them as -1 (fail closed), not as an empty array (0 -> pass).
|
|
682
|
+
NONARRAY="$BROKEN-nonarray"
|
|
683
|
+
mkdir -p "$NONARRAY/app" "$NONARRAY/components" "$NONARRAY/server" "$NONARRAY/scripts"
|
|
684
|
+
ln -s "$NODE_MODULES" "$NONARRAY/node_modules"
|
|
685
|
+
cp "$CFG" "$NONARRAY/.dependency-cruiser.cjs"
|
|
686
|
+
printf '{}\n' > "$NONARRAY/.dependency-cruiser-known-violations.json"
|
|
687
|
+
NONARRAY_OUT="$( CONSTRAINT_GATES_DIR="$NONARRAY" bash "$RUNNER" 2>&1 )"
|
|
688
|
+
NONARRAY_RC=$?
|
|
689
|
+
cases=$((cases + 1))
|
|
690
|
+
if [[ "$NONARRAY_RC" -ne 0 ]]; then
|
|
691
|
+
ok "4.11: runner fails closed on a non-array baseline (rc=$NONARRAY_RC)"
|
|
692
|
+
else
|
|
693
|
+
bad "4.11: runner passed a non-array baseline (rc=$NONARRAY_RC) — fail-open: $NONARRAY_OUT"
|
|
694
|
+
fi
|
|
695
|
+
|
|
696
|
+
echo "---"
|
|
697
|
+
|
|
698
|
+
# --- Accounting conservation (ADR-193 #3) --------------------------------------------------
|
|
699
|
+
# Ordered BEFORE the floor (ADR-193 #4): a neutered bad()/ok() deflates the verdict counters, so
|
|
700
|
+
# the floor below would ALSO trip and would report the misleading "arms were deleted". This says
|
|
701
|
+
# "a verdict was discarded" instead. Reported with `printf >&2` + `exit 1` DIRECTLY, never
|
|
702
|
+
# through bad(): a check that reports by calling the verdict helper increments the very counter
|
|
703
|
+
# the exit status reads, so neutering bad() silences the rows AND the check meant to notice the
|
|
704
|
+
# silence. Every assertion records exactly one verdict, so pass+fail MUST equal cases; because
|
|
705
|
+
# `cases` moves at the CALL SITE and not inside the verdict helpers, the identity is a real
|
|
706
|
+
# constraint rather than a tautology. The literal `[FATAL] accounting` is load-bearing —
|
|
707
|
+
# guard-vacuity-floor's ARM 10 builds its conservation population by grepping that exact string.
|
|
708
|
+
if [[ $((pass + fail)) -ne "$cases" ]]; then
|
|
709
|
+
printf '\n[FATAL] accounting: pass+fail (%d) != cases (%d).\n' "$((pass + fail))" "$cases" >&2
|
|
710
|
+
if [[ $((pass + fail)) -lt "$cases" ]]; then
|
|
711
|
+
printf ' An assertion was counted but its verdict was not recorded — that is what a neutered ok()/bad() looks like.\n' >&2
|
|
712
|
+
else
|
|
713
|
+
printf ' A verdict was recorded at a call site with no `cases=$((cases + 1))` before it. This is a harness bug, not a product failure: add the increment at that call site.\n' >&2
|
|
714
|
+
fi
|
|
715
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
716
|
+
exit 1
|
|
717
|
+
fi
|
|
718
|
+
|
|
719
|
+
# --- Anti-vacuity floor (ADR-193 #1) -------------------------------------------------------
|
|
720
|
+
# Reads the INDEPENDENT `cases` counter, and reports with `printf >&2` + `exit 1` DIRECTLY.
|
|
721
|
+
# This suite previously carried NO suite-level floor at all: a harness that silently asserted
|
|
722
|
+
# nothing (a fixture generator that no-ops, an editing slip that drops a block) printed a clean
|
|
723
|
+
# smaller total and exited 0. See MIN_ASSERTIONS above for how the number is derived and when
|
|
724
|
+
# to ratchet it.
|
|
725
|
+
if [[ "$cases" -lt "$MIN_ASSERTIONS" ]]; then
|
|
726
|
+
printf '\n[FATAL] anti-vacuity floor: only %d assertion(s) ran, expected >= %d.\n' \
|
|
727
|
+
"$cases" "$MIN_ASSERTIONS" >&2
|
|
728
|
+
printf ' Arms were deleted or skipped; a green run here would be a coverage loss.\n' >&2
|
|
729
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
730
|
+
exit 1
|
|
731
|
+
fi
|
|
732
|
+
|
|
733
|
+
echo "boundary.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
734
|
+
[[ "$fail" -eq 0 ]]
|