@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,1338 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install-time bite-proof + README/pointer coverage for constraint-scaffold.sh (#8288, ADR-071
|
|
3
|
+
# amendment 2026-09-19). Proves that every scaffold run that reports success has observed the
|
|
4
|
+
# emitted gate, on a detached worktree of the target's HEAD, go pass -> fail -> pass — naming
|
|
5
|
+
# `no-client-to-server-secret` on the direct probe edge AND `no-client-to-server-secret-transitive`
|
|
6
|
+
# on the one-hop probe edge — and that a run which cannot prove it leaves the founder's tree as it
|
|
7
|
+
# found it. Also covers the append-once README block and the agent-instructions pointer, which are
|
|
8
|
+
# the LAST writes of a default-mode run (after the bite), never a refresh-mode write.
|
|
9
|
+
#
|
|
10
|
+
# Suite order (test-design): source pins -> stub-driven -> locate-or-install -> toolchain probe ->
|
|
11
|
+
# real. An `npm install` failure in the scripts shard therefore cannot `exit 2` before the
|
|
12
|
+
# toolchain-free segments have run and carried their own floor.
|
|
13
|
+
#
|
|
14
|
+
# ZERO PRODUCTION SEAMS. The script honours no CONSTRAINT_SCAFFOLD_TEST_* variable (a pin below
|
|
15
|
+
# asserts the literal is absent). Every failure arm is driven by a FIXTURE-OWNED stub `depcruise`
|
|
16
|
+
# reached through the fixture's `apps/web-platform/node_modules` symlink. The stub implements the
|
|
17
|
+
# three-branch table from the plan (D6): `--output-type baseline` -> `[]`; `--output-type json` ->
|
|
18
|
+
# `{"modules":[]}`; `--output-type err` -> the probe arm iff
|
|
19
|
+
# `components/__constraint_scaffold_bite_probe__/direct.tsx` exists in cwd (the runner `cd`s to
|
|
20
|
+
# the app dir), else the clean arm. It appends its argv to `calls.log` and exits 64 on an argv
|
|
21
|
+
# shape it does not recognise, and its `dependency-cruiser/package.json` carries the distinctive
|
|
22
|
+
# version `0.0.0-stub` so the real-toolchain case can prove it did NOT run the stub.
|
|
23
|
+
#
|
|
24
|
+
# Every fixture is synthesized under a mktemp root (cq-test-fixtures-synthesized-only); nothing
|
|
25
|
+
# is written into the real apps/web-platform tree or into this repository's CLAUDE.md.
|
|
26
|
+
set -uo pipefail
|
|
27
|
+
export TMPDIR="${TMPDIR:-/var/tmp}"
|
|
28
|
+
|
|
29
|
+
# Refuses an empty, relative, root or synthetic-fs fixture dir (byte-identical copy; the
|
|
30
|
+
# fixture-dir-operand-assert suite pins every tracked copy).
|
|
31
|
+
assert_fixture_dir() {
|
|
32
|
+
case "${1-}" in
|
|
33
|
+
"") printf 'FATAL: fixture dir is EMPTY; git -C "" would operate on %s\n' "$PWD" >&2; exit 2 ;;
|
|
34
|
+
*/../*|*/..) printf 'FATAL: fixture dir %s contains ..; refusing\n' "$1" >&2; exit 2 ;;
|
|
35
|
+
/proc/*|/sys/*|/dev/*) printf 'FATAL: fixture dir %s is a synthetic-fs path; refusing\n' "$1" >&2; exit 2 ;;
|
|
36
|
+
/|//|/.) printf 'FATAL: fixture dir resolves to the filesystem root; refusing\n' >&2; exit 2 ;;
|
|
37
|
+
/*) : ;;
|
|
38
|
+
*) printf 'FATAL: fixture dir %s is RELATIVE; refusing\n' "$1" >&2; exit 2 ;;
|
|
39
|
+
esac
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
REPO_ROOT="$(git -C "$(dirname "${BASH_SOURCE[0]}")" rev-parse --show-toplevel)"
|
|
43
|
+
assert_fixture_dir "$REPO_ROOT"
|
|
44
|
+
SKILL="$REPO_ROOT/plugins/soleur/skills/constraint-scaffold"
|
|
45
|
+
GEN="$SKILL/scripts/constraint-scaffold.sh"
|
|
46
|
+
REF="$SKILL/references"
|
|
47
|
+
APP="$REPO_ROOT/apps/web-platform"
|
|
48
|
+
|
|
49
|
+
# Hermetic git environment for every fixture (the repo's chokepoint, #7849): sweeps inherited
|
|
50
|
+
# GIT_*, pins discovery at $TMPROOT's parent, GIT_CONFIG_GLOBAL=/dev/null (a developer
|
|
51
|
+
# commit.gpgsign=true or signing key would otherwise reach 20+ fixture commits), synthesized identity.
|
|
52
|
+
# shellcheck source=../../../test/lib/git-fixture-env.sh
|
|
53
|
+
source "$REPO_ROOT/plugins/soleur/test/lib/git-fixture-env.sh"
|
|
54
|
+
|
|
55
|
+
RULE_DIRECT="no-client-to-server-secret"
|
|
56
|
+
RULE_TRANSITIVE="no-client-to-server-secret-transitive"
|
|
57
|
+
PROBE_DIR="components/__constraint_scaffold_bite_probe__"
|
|
58
|
+
POINTER_MARKER="<!-- constraint-scaffold:pointer -->"
|
|
59
|
+
README_START="<!-- constraint-scaffold:readme:start -->"
|
|
60
|
+
README_END="<!-- constraint-scaffold:readme:end -->"
|
|
61
|
+
VERDICT_RE="^constraint-scaffold: bite-proof pass -> fail\(${RULE_DIRECT}@direct, ${RULE_TRANSITIVE}@via-hop\) -> pass depcruise=[^ ]+$"
|
|
62
|
+
|
|
63
|
+
pass=0
|
|
64
|
+
fail=0
|
|
65
|
+
# The INDEPENDENT case counter (ADR-193 #2). Incremented at every CALL SITE, and NEVER inside
|
|
66
|
+
# ok()/bad() — the VERDICT helpers, which touch only the verdict counters. A counter bumped inside
|
|
67
|
+
# the verdict helpers moves WITH the verdict, so stubbing bad() drops the row and its count together
|
|
68
|
+
# and `pass + fail == cases` still holds under the exact fault it exists to catch. Incremented at
|
|
69
|
+
# top level only, never inside a `$( … )` command substitution.
|
|
70
|
+
cases=0
|
|
71
|
+
# Anti-vacuity thresholds (TOOLCHAIN_FREE_MIN_ASSERTIONS at the toolchain-SKIP door,
|
|
72
|
+
# MIN_ASSERTIONS at the trailer). Each literal is bound on the line DIRECTLY above its `if`,
|
|
73
|
+
# because guard-vacuity-floor's backward slice carries only contiguous simple assignments into
|
|
74
|
+
# its neutered-machinery mutant; bound anywhere else, the mutant dies unbound and the floor
|
|
75
|
+
# scores CONSTRUCTION instead of FIRES.
|
|
76
|
+
#
|
|
77
|
+
# Derived at commit time, never hand-summed: each constant is
|
|
78
|
+
# `grep -cE '^\s*cases=\$\(\(cases \+ 1\)\)$'` over its segment of THIS file (the anchored form
|
|
79
|
+
# skips the two prose mentions inside the accounting messages), minus the sites that are
|
|
80
|
+
# legitimately conditional, so the floor is a genuine LOWER BOUND rather than a snapshot:
|
|
81
|
+
# TOOLCHAIN_FREE_MIN_ASSERTIONS = 78 call sites above the locate-or-install block, minus the 2
|
|
82
|
+
# C-chmod sites that only run when not root = 76
|
|
83
|
+
# MIN_ASSERTIONS = that + the 18 call sites of the real segment = 94
|
|
84
|
+
ok() { printf 'ok - %s\n' "$1"; pass=$((pass + 1)); }
|
|
85
|
+
bad() { printf 'FAIL - %s\n' "$1"; fail=$((fail + 1)); }
|
|
86
|
+
# Instrument self-test — DIRECTION, not only presence: conservation (`pass + fail == cases`) and
|
|
87
|
+
# both floors count verdicts, so a bad() misrouted to the pass counter keeps every gate green while
|
|
88
|
+
# printing FAIL rows. Drive each helper once, require the RIGHT counter moved, reset. Reported via
|
|
89
|
+
# printf >&2 + exit 1, never through the helper it checks (ADR-193).
|
|
90
|
+
bad "instrument self-test (expected — proves bad() records a FAILURE)" >/dev/null
|
|
91
|
+
ok "instrument self-test (expected — proves ok() records a PASS)" >/dev/null
|
|
92
|
+
if [[ "$fail" != "1" || "$pass" != "1" ]]; then
|
|
93
|
+
printf '[FATAL] HELPER CONTROL BROKEN: after one bad() and one ok(), fail=%s pass=%s (want 1/1)\n' "$fail" "$pass" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
pass=0; fail=0
|
|
97
|
+
|
|
98
|
+
[[ -f "$GEN" ]] || { echo "fatal: missing $GEN"; exit 2; }
|
|
99
|
+
[[ -f "$REF/depcruise-config.template" ]] || { echo "fatal: missing depcruise-config.template"; exit 2; }
|
|
100
|
+
command -v node >/dev/null 2>&1 || { echo "fatal: node is required (the emitted runner parses JSON with it)"; exit 2; }
|
|
101
|
+
|
|
102
|
+
TMPROOT="$(mktemp -d)" || { echo "FATAL: cannot create scratch root" >&2; exit 1; }
|
|
103
|
+
: "${TMPROOT:?scratch root must be non-empty}"
|
|
104
|
+
[[ "$TMPROOT" == /* && -d "$TMPROOT" && ! -L "$TMPROOT" ]] || {
|
|
105
|
+
echo "FATAL: scratch root is not a plain absolute directory: '$TMPROOT'" >&2; exit 1; }
|
|
106
|
+
# Every fixture lives under $TMPROOT; one anchor dir gives the ceiling ($TMPROOT's parent) and the
|
|
107
|
+
# hermetic config to the whole suite, the scaffold under test included (it inherits the env).
|
|
108
|
+
mkdir -p "$TMPROOT/anchor"
|
|
109
|
+
git_fixture_env "$TMPROOT/anchor" || { echo "FATAL: git_fixture_env refused $TMPROOT/anchor" >&2; exit 1; }
|
|
110
|
+
# EXIT **INT TERM**: a ^C during the on-demand `npm install` below must still remove the root.
|
|
111
|
+
trap 'rm -rf -- "${TMPROOT:-}"' EXIT INT TERM
|
|
112
|
+
assert_fixture_dir "$TMPROOT"
|
|
113
|
+
|
|
114
|
+
# =============================================================================================
|
|
115
|
+
# Segment 1 — SOURCE PINS (no fixture run). Shape facts about constraint-scaffold.sh that the
|
|
116
|
+
# behavioural cases below rely on and that one diff could otherwise silently move.
|
|
117
|
+
# =============================================================================================
|
|
118
|
+
echo "# --- source pins ---"
|
|
119
|
+
|
|
120
|
+
# Pin: the default-mode failure-cleanup list names exactly the artifacts the refuse-if-exists
|
|
121
|
+
# guard protects (the script owns one list; a new artifact cannot be left behind on a failed bite
|
|
122
|
+
# without also being left out of the refuse loop, and vice-versa).
|
|
123
|
+
vars_of() { grep -oE '\$[A-Z_]+' | sort -u | tr '\n' ' '; }
|
|
124
|
+
REFUSE_VARS="$( { grep -E '^for f in .*"\$CFG".*; do$' "$GEN"; grep -oE '^\[\[ -e "\$BASELINE" \]\] && die "refuse-if-exists' "$GEN"; } | vars_of )"
|
|
125
|
+
CLEANUP_VARS="$( awk '/^remove_emitted_artifacts\(\)/{f=1} f && /^}/{f=0} f' "$GEN" | grep -E '^\s*for f in .*"\$CFG"' | vars_of )"
|
|
126
|
+
cases=$((cases + 1))
|
|
127
|
+
if [[ -n "$CLEANUP_VARS" && "$CLEANUP_VARS" == "$REFUSE_VARS" && "$REFUSE_VARS" == *'$BASELINE'* && "$REFUSE_VARS" == *'$FIXWORKFLOW_B'* ]]; then
|
|
128
|
+
ok "pin: default-mode cleanup list == refuse-if-exists list ($CLEANUP_VARS)"
|
|
129
|
+
else
|
|
130
|
+
bad "pin: cleanup list [$CLEANUP_VARS] != refuse-if-exists list [$REFUSE_VARS]"
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
# Pin: inside with_detached_worktree(), the `trap '_wt_cleanup` line PRECEDES `worktree add
|
|
134
|
+
# --detach` (ADR-129 — the trap owns the dir before the operation that can fail). Matched on the
|
|
135
|
+
# specific handler so a `trap - EXIT` line cannot satisfy it.
|
|
136
|
+
# Comment-STRIPPED: the script documents the very constructs these pins look for.
|
|
137
|
+
WT_BODY="$(awk '/^with_detached_worktree\(\)/{f=1} f{print} f && /^}/{exit}' "$GEN" | grep -vE '^\s*#')"
|
|
138
|
+
TRAP_LN="$(printf '%s\n' "$WT_BODY" | grep -nF "trap '_wt_cleanup" | head -1 | cut -d: -f1)"
|
|
139
|
+
ADD_LN="$(printf '%s\n' "$WT_BODY" | grep -n 'worktree add --detach' | head -1 | cut -d: -f1)"
|
|
140
|
+
cases=$((cases + 1))
|
|
141
|
+
if [[ -n "$TRAP_LN" && -n "$ADD_LN" && "$TRAP_LN" -lt "$ADD_LN" ]]; then
|
|
142
|
+
ok "pin: trap '_wt_cleanup' (line $TRAP_LN of the helper) precedes worktree add --detach (line $ADD_LN)"
|
|
143
|
+
else
|
|
144
|
+
bad "pin: trap '_wt_cleanup' must precede worktree add --detach inside with_detached_worktree (trap=$TRAP_LN add=$ADD_LN)"
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Pin: the INT/TERM arm clears EXIT inside itself and exits 143 (cleanup runs once; a TERM is a
|
|
148
|
+
# property, not an accident).
|
|
149
|
+
cases=$((cases + 1))
|
|
150
|
+
if printf '%s\n' "$WT_BODY" | grep -cF >/dev/null "trap '_wt_cleanup interrupted; trap - EXIT; exit 143' INT TERM"; then
|
|
151
|
+
ok "pin: INT/TERM arm is '_wt_cleanup interrupted; trap - EXIT; exit 143'"
|
|
152
|
+
else
|
|
153
|
+
bad "pin: INT/TERM arm missing or not the prescribed shape"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# Pin: the two rule-name literals in the script equal the `name:` fields of the config template,
|
|
157
|
+
# in order (a rename in the template reds here before it reds in a founder repo).
|
|
158
|
+
TEMPLATE_RULES="$(grep -oE 'name: "[^"]+"' "$REF/depcruise-config.template" | sed -E 's/name: "([^"]+)"/\1/' | tr '\n' ' ')"
|
|
159
|
+
SCRIPT_RULES="$( { grep -E '^RULE_DIRECT=' "$GEN"; grep -E '^RULE_TRANSITIVE=' "$GEN"; } | sed -E 's/^[A-Z_]+="([^"]+)"$/\1/' | tr '\n' ' ')"
|
|
160
|
+
cases=$((cases + 1))
|
|
161
|
+
if [[ -n "$SCRIPT_RULES" && "$SCRIPT_RULES" == "$TEMPLATE_RULES" && "$TEMPLATE_RULES" == "$RULE_DIRECT $RULE_TRANSITIVE " ]]; then
|
|
162
|
+
ok "pin: script rule literals == template name: fields ($TEMPLATE_RULES)"
|
|
163
|
+
else
|
|
164
|
+
bad "pin: script rule literals [$SCRIPT_RULES] != template name: fields [$TEMPLATE_RULES]"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# Pin: verdict_fail prints its stdout line BEFORE it dies (agent runtimes surface stdout and
|
|
168
|
+
# swallow stderr), and the stdout line is not itself redirected to stderr.
|
|
169
|
+
VF_BODY="$(awk '/^verdict_fail\(\)/{f=1} f{print} f && /^}/{exit}' "$GEN" | grep -vE '^\s*#')"
|
|
170
|
+
VF_PRINT_LN="$(printf '%s\n' "$VF_BODY" | grep -n "bite-proof FAILED" | grep -v '>&2' | head -1 | cut -d: -f1)"
|
|
171
|
+
VF_DIE_LN="$(printf '%s\n' "$VF_BODY" | grep -nE '^\s*(die |exit ")' | head -1 | cut -d: -f1)"
|
|
172
|
+
cases=$((cases + 1))
|
|
173
|
+
if [[ -n "$VF_PRINT_LN" && -n "$VF_DIE_LN" && "$VF_PRINT_LN" -lt "$VF_DIE_LN" ]]; then
|
|
174
|
+
ok "pin: verdict_fail prints 'bite-proof FAILED' on stdout (line $VF_PRINT_LN) before it exits (line $VF_DIE_LN)"
|
|
175
|
+
else
|
|
176
|
+
bad "pin: verdict_fail must print on stdout before it exits (print=$VF_PRINT_LN exit=$VF_DIE_LN)"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Pin: the default-mode tail ARMS the cleanup handler (top-level `trap '_wt_cleanup' EXIT`) before
|
|
180
|
+
# the first write (`mkdir -p "$TARGET/scripts"`) and DISARMS it (top-level `trap - EXIT INT TERM`)
|
|
181
|
+
# after the last prove_bite and before emit_readme — the window every 66..74 / mktemp / set -e
|
|
182
|
+
# failure falls inside. Column-0 call forms on a comment-stripped copy; the helper's own (indented)
|
|
183
|
+
# trap lines cannot satisfy them.
|
|
184
|
+
GEN_NOCOMMENT="$(grep -vE '^\s*#' "$GEN")"
|
|
185
|
+
ARM_LN="$(printf '%s\n' "$GEN_NOCOMMENT" | grep -nF "trap '_wt_cleanup' EXIT" | grep -vE '^[0-9]+:\s' | head -1 | cut -d: -f1)"
|
|
186
|
+
MKDIR_LN="$(printf '%s\n' "$GEN_NOCOMMENT" | grep -nE '^mkdir -p "\$TARGET/scripts"' | head -1 | cut -d: -f1)"
|
|
187
|
+
NC_PB_LN="$(printf '%s\n' "$GEN_NOCOMMENT" | grep -nE '^prove_bite\s*$' | tail -1 | cut -d: -f1)"
|
|
188
|
+
DISARM_LN="$(printf '%s\n' "$GEN_NOCOMMENT" | grep -nE '^trap - EXIT INT TERM$' | head -1 | cut -d: -f1)"
|
|
189
|
+
NC_ER_LN="$(printf '%s\n' "$GEN_NOCOMMENT" | grep -nE '^emit_readme\s*$' | head -1 | cut -d: -f1)"
|
|
190
|
+
cases=$((cases + 1))
|
|
191
|
+
if [[ -n "$ARM_LN" && -n "$MKDIR_LN" && -n "$NC_PB_LN" && -n "$DISARM_LN" && -n "$NC_ER_LN" \
|
|
192
|
+
&& "$ARM_LN" -lt "$MKDIR_LN" && "$MKDIR_LN" -lt "$NC_PB_LN" && "$NC_PB_LN" -lt "$DISARM_LN" && "$DISARM_LN" -lt "$NC_ER_LN" ]]; then
|
|
193
|
+
ok "pin: cleanup armed ($ARM_LN) < first write ($MKDIR_LN) < prove_bite ($NC_PB_LN) < disarmed ($DISARM_LN) < emit_readme ($NC_ER_LN)"
|
|
194
|
+
else
|
|
195
|
+
bad "pin: default-mode cleanup window wrong: arm=$ARM_LN mkdir=$MKDIR_LN prove_bite=$NC_PB_LN disarm=$DISARM_LN emit_readme=$NC_ER_LN (want ascending)"
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Pin: no test seam — the script honours no CONSTRAINT_SCAFFOLD_TEST_* variable.
|
|
199
|
+
SEAMS="$(grep -c 'CONSTRAINT_SCAFFOLD_TEST_' "$GEN" || true)"
|
|
200
|
+
cases=$((cases + 1))
|
|
201
|
+
if [[ "$SEAMS" == "0" ]]; then
|
|
202
|
+
ok "pin: no CONSTRAINT_SCAFFOLD_TEST_ seam in the script"
|
|
203
|
+
else
|
|
204
|
+
bad "pin: $SEAMS CONSTRAINT_SCAFFOLD_TEST_ occurrence(s) in the script (must be 0)"
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# Pin: prove_bite is called at BOTH mode exits, and the README/pointer emits come AFTER the
|
|
208
|
+
# default-mode prove_bite (R1 — written last so the clean-tree guard never sees them).
|
|
209
|
+
PB_CALLS="$(grep -cE '^\s*prove_bite\s*(#|$)' "$GEN" || true)"
|
|
210
|
+
PB_LAST_LN="$(grep -nE '^\s*prove_bite\s*(#|$)' "$GEN" | tail -1 | cut -d: -f1)"
|
|
211
|
+
ER_LN="$(grep -nE '^\s*emit_readme\s*(#|$)' "$GEN" | head -1 | cut -d: -f1)"
|
|
212
|
+
EP_LN="$(grep -nE '^\s*emit_pointer\s*(#|$)' "$GEN" | head -1 | cut -d: -f1)"
|
|
213
|
+
cases=$((cases + 1))
|
|
214
|
+
if [[ "$PB_CALLS" == "2" && -n "$ER_LN" && -n "$EP_LN" && "$PB_LAST_LN" -lt "$ER_LN" && "$ER_LN" -lt "$EP_LN" ]]; then
|
|
215
|
+
ok "pin: prove_bite called twice; emit_readme ($ER_LN) and emit_pointer ($EP_LN) follow the default-mode prove_bite ($PB_LAST_LN)"
|
|
216
|
+
else
|
|
217
|
+
bad "pin: prove_bite calls=$PB_CALLS (want 2); prove_bite=$PB_LAST_LN emit_readme=$ER_LN emit_pointer=$EP_LN (want ascending)"
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Pin: the header exit matrix lists 71, 72, 73 and 74.
|
|
221
|
+
MATRIX_OK=1
|
|
222
|
+
for code in 71 72 73 74; do
|
|
223
|
+
grep -qE "^# $code " "$GEN" || MATRIX_OK=0
|
|
224
|
+
done
|
|
225
|
+
cases=$((cases + 1))
|
|
226
|
+
if [[ "$MATRIX_OK" -eq 1 ]]; then
|
|
227
|
+
ok "pin: header exit matrix lists 71/72/73/74"
|
|
228
|
+
else
|
|
229
|
+
bad "pin: header exit matrix is missing one of 71/72/73/74"
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Pin: the emitter's strip/substitute expression is the one literal expression the parity suite
|
|
233
|
+
# also pins (one transform, two copies).
|
|
234
|
+
# Anchored on the ASSIGNMENT form (`content="$(sed …`), so a stale comment quoting the expression
|
|
235
|
+
# cannot stand in for the live one.
|
|
236
|
+
STRIP_EXPR="sed -e '1{/^<!-- Inspired by /d;}' -e \"s|__TARGET_DIR__|\$TARGET_REL|g\""
|
|
237
|
+
STRIP_CT="$(grep -E '^\s*content="\$\(sed ' "$GEN" | grep -cF -- "$STRIP_EXPR" || true)"
|
|
238
|
+
cases=$((cases + 1))
|
|
239
|
+
if [[ "$STRIP_CT" == "1" ]]; then
|
|
240
|
+
ok "pin: the README strip/substitute sed expression appears exactly once, on the content= assignment"
|
|
241
|
+
else
|
|
242
|
+
bad "pin: README strip/substitute sed expression on a content= assignment count=$STRIP_CT (want 1)"
|
|
243
|
+
fi
|
|
244
|
+
|
|
245
|
+
# =============================================================================================
|
|
246
|
+
# Fixture + stub builders.
|
|
247
|
+
# =============================================================================================
|
|
248
|
+
|
|
249
|
+
# make_repo <name> [opts...] -> prints the fixture repo root. A throwaway git repo carrying an
|
|
250
|
+
# apps/web-platform-shaped Next.js app, committed clean, with refs/remotes/origin/main == HEAD so
|
|
251
|
+
# the merge-base capture resolves. Options:
|
|
252
|
+
# --no-components omit components/ (drives the three-dirs precondition, 65)
|
|
253
|
+
# --claude commit a CLAUDE.md with prior content
|
|
254
|
+
# --agents commit an AGENTS.md with prior content
|
|
255
|
+
# --readme commit a server/README.md with prior content
|
|
256
|
+
# --populated an unrelated "use client" module with a TYPE-ONLY server import plus a
|
|
257
|
+
# client module value-importing a VALUE-SAFE server module (the direct rule
|
|
258
|
+
# baselines it; the transitive rule excludes it via pathNot)
|
|
259
|
+
# --claude-symlink commit CLAUDE.md as a symlink to a file OUTSIDE the repo
|
|
260
|
+
make_repo() {
|
|
261
|
+
local name="$1"; shift
|
|
262
|
+
local fx="$TMPROOT/$name"
|
|
263
|
+
assert_fixture_dir "$fx"
|
|
264
|
+
local app="$fx/apps/web-platform"
|
|
265
|
+
local want_components=1 claude=0 agents=0 readme=0 populated=0 claude_symlink=0
|
|
266
|
+
local o
|
|
267
|
+
for o in "$@"; do
|
|
268
|
+
case "$o" in
|
|
269
|
+
--no-components) want_components=0 ;;
|
|
270
|
+
--claude) claude=1 ;;
|
|
271
|
+
--agents) agents=1 ;;
|
|
272
|
+
--readme) readme=1 ;;
|
|
273
|
+
--populated) populated=1 ;;
|
|
274
|
+
--claude-symlink) claude_symlink=1 ;;
|
|
275
|
+
*) echo "make_repo: unknown option $o" >&2; exit 2 ;;
|
|
276
|
+
esac
|
|
277
|
+
done
|
|
278
|
+
mkdir -p "$app/app" "$app/server"
|
|
279
|
+
[[ "$want_components" -eq 1 ]] && mkdir -p "$app/components"
|
|
280
|
+
git -C "$fx" init -q
|
|
281
|
+
git -C "$fx" config user.email "test@example.com"
|
|
282
|
+
git -C "$fx" config user.name "test"
|
|
283
|
+
printf 'node_modules\n' > "$fx/.gitignore"
|
|
284
|
+
printf 'module.exports = {};\n' > "$app/next.config.js"
|
|
285
|
+
printf '{ "name": "fixture", "private": true, "dependencies": { "next": "15.0.0" } }\n' > "$app/package.json"
|
|
286
|
+
cat > "$app/tsconfig.json" <<'JSON'
|
|
287
|
+
{
|
|
288
|
+
"compilerOptions": {
|
|
289
|
+
"baseUrl": ".",
|
|
290
|
+
"module": "esnext",
|
|
291
|
+
"moduleResolution": "bundler",
|
|
292
|
+
"jsx": "preserve",
|
|
293
|
+
"paths": { "@/*": ["./*"] }
|
|
294
|
+
},
|
|
295
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
296
|
+
"exclude": ["node_modules"]
|
|
297
|
+
}
|
|
298
|
+
JSON
|
|
299
|
+
printf 'export default function Page() { return null; }\n' > "$app/app/page.tsx"
|
|
300
|
+
printf 'export const APP_NAME = "fixture";\nexport type Shape = { id: string };\n' > "$app/server/config.ts"
|
|
301
|
+
if [[ "$want_components" -eq 1 ]]; then
|
|
302
|
+
printf '"use client";\nexport const Greeting = () => "hello";\n' > "$app/components/greeting.tsx"
|
|
303
|
+
fi
|
|
304
|
+
if [[ "$populated" -eq 1 ]]; then
|
|
305
|
+
printf '"use client";\nimport type { Shape } from "@/server/config";\nexport const Typed = (_: Shape) => null;\n' > "$app/components/typed.tsx"
|
|
306
|
+
printf 'export const LEADERS = ["a"] as const;\n' > "$app/server/domain-leaders.ts"
|
|
307
|
+
printf '"use client";\nimport { LEADERS } from "@/server/domain-leaders";\nexport const Leaders = () => LEADERS;\n' > "$app/components/leaders.tsx"
|
|
308
|
+
fi
|
|
309
|
+
[[ "$claude" -eq 1 ]] && printf '# Fixture project\n\nPrior instructions.\n' > "$fx/CLAUDE.md"
|
|
310
|
+
[[ "$agents" -eq 1 ]] && printf '# Agents\n\nPrior agent notes.\n' > "$fx/AGENTS.md"
|
|
311
|
+
[[ "$readme" -eq 1 ]] && printf '# Server\n\nPrior README prose.\n' > "$app/server/README.md"
|
|
312
|
+
if [[ "$claude_symlink" -eq 1 ]]; then
|
|
313
|
+
mkdir -p "$TMPROOT/elsewhere-$name"
|
|
314
|
+
printf '# Global instructions (must stay untouched)\n' > "$TMPROOT/elsewhere-$name/CLAUDE.md"
|
|
315
|
+
ln -s "$TMPROOT/elsewhere-$name/CLAUDE.md" "$fx/CLAUDE.md"
|
|
316
|
+
fi
|
|
317
|
+
git -C "$fx" add -A
|
|
318
|
+
git -C "$fx" commit -q -m "seed app"
|
|
319
|
+
git -C "$fx" update-ref refs/remotes/origin/main HEAD
|
|
320
|
+
printf '%s' "$fx"
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
# point_node_modules <fixture-root> <node_modules-dir>: (re)point the untracked symlink.
|
|
324
|
+
point_node_modules() {
|
|
325
|
+
local fx="$1" nm="$2"
|
|
326
|
+
assert_fixture_dir "$fx"
|
|
327
|
+
rm -f -- "$fx/apps/web-platform/node_modules"
|
|
328
|
+
ln -s "$nm" "$fx/apps/web-platform/node_modules"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# make_stub_depcruise <dir> <clean-out> <clean-rc> <probe-out> <probe-rc> [<kill-pid-file>]
|
|
332
|
+
# Builds a node_modules-shaped dir whose `.bin/depcruise` implements the three-branch table (D6).
|
|
333
|
+
# <clean-out>/<probe-out> are files whose bytes the stub cats on the `err` clean/probe arms. With
|
|
334
|
+
# <kill-pid-file>, the `err`-without-probe arm first sends TERM to the pid in that file (the
|
|
335
|
+
# pre-probe pass runs after the scaffold's trap is installed; the `baseline` call precedes it).
|
|
336
|
+
# Two flag FILES a case may touch in <dir> after building: `stateful` makes the clean arm fail
|
|
337
|
+
# (probe bytes + probe rc) once a probe arm has run — a gate that stays red after the revert
|
|
338
|
+
# (drives 73); `reach-baseline` makes the `baseline` arm emit a reachability entry, which the
|
|
339
|
+
# emitted RUNNER refuses on its pre-probe pass (drives 71 through runner logic, not stub output).
|
|
340
|
+
make_stub_depcruise() {
|
|
341
|
+
local dir="$1" clean_out="$2" clean_rc="$3" probe_out="$4" probe_rc="$5" pidfile="${6:-}"
|
|
342
|
+
assert_fixture_dir "$dir"
|
|
343
|
+
mkdir -p "$dir/.bin" "$dir/dependency-cruiser"
|
|
344
|
+
cp "$clean_out" "$dir/clean.out"
|
|
345
|
+
cp "$probe_out" "$dir/probe.out"
|
|
346
|
+
printf '{ "name": "dependency-cruiser", "version": "0.0.0-stub" }\n' > "$dir/dependency-cruiser/package.json"
|
|
347
|
+
: > "$dir/calls.log"
|
|
348
|
+
{
|
|
349
|
+
printf '#!/usr/bin/env bash\n'
|
|
350
|
+
printf 'STUB_DIR=%q\n' "$dir"
|
|
351
|
+
printf 'CLEAN_RC=%q\nPROBE_RC=%q\nPIDFILE=%q\n' "$clean_rc" "$probe_rc" "$pidfile"
|
|
352
|
+
cat <<'STUB'
|
|
353
|
+
printf '%s\n' "$*" >> "$STUB_DIR/calls.log"
|
|
354
|
+
case " $* " in
|
|
355
|
+
*" --output-type json "*) printf '{"modules":[]}\n' ;;
|
|
356
|
+
*" --output-type baseline "*)
|
|
357
|
+
if [[ -e "$STUB_DIR/reach-baseline" ]]; then
|
|
358
|
+
printf '[{"type":"reachability","rule":{"name":"no-client-to-server-secret-transitive"}}]\n'
|
|
359
|
+
else
|
|
360
|
+
printf '[]\n'
|
|
361
|
+
fi ;;
|
|
362
|
+
*" --output-type err "*)
|
|
363
|
+
if [[ -e components/__constraint_scaffold_bite_probe__/direct.tsx ]]; then
|
|
364
|
+
touch "$STUB_DIR/probed"
|
|
365
|
+
cat "$STUB_DIR/probe.out"; exit "$PROBE_RC"
|
|
366
|
+
fi
|
|
367
|
+
if [[ -e "$STUB_DIR/stateful" && -e "$STUB_DIR/probed" ]]; then
|
|
368
|
+
cat "$STUB_DIR/probe.out"; exit "$PROBE_RC"
|
|
369
|
+
fi
|
|
370
|
+
if [[ -n "$PIDFILE" && -s "$PIDFILE" ]]; then kill -TERM "$(cat "$PIDFILE")" 2>/dev/null || true; fi
|
|
371
|
+
cat "$STUB_DIR/clean.out"; exit "$CLEAN_RC" ;;
|
|
372
|
+
*) echo "stub depcruise: unhandled argv: $*" >&2; exit 64 ;;
|
|
373
|
+
esac
|
|
374
|
+
STUB
|
|
375
|
+
} > "$dir/.bin/depcruise"
|
|
376
|
+
chmod +x "$dir/.bin/depcruise"
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
# run_gen <fixture-root> <tag> [args...] -> RC, and stdout/stderr in $TMPROOT/<tag>.out / .err.
|
|
380
|
+
# RUN_TMPDIR (optional) is exported as TMPDIR for the scaffold so a leftover mktemp dir is observable.
|
|
381
|
+
run_gen() {
|
|
382
|
+
local root="$1" tag="$2"; shift 2
|
|
383
|
+
assert_fixture_dir "$root"
|
|
384
|
+
RC=0
|
|
385
|
+
CONSTRAINT_SCAFFOLD_REPO_ROOT="$root" TMPDIR="${RUN_TMPDIR:-$TMPDIR}" bash "$GEN" "$@" \
|
|
386
|
+
>"$TMPROOT/$tag.out" 2>"$TMPROOT/$tag.err" || RC=$?
|
|
387
|
+
}
|
|
388
|
+
worktree_count() { git -C "$1" worktree list 2>/dev/null | grep -c . || true; }
|
|
389
|
+
porcelain() { git -C "$1" status --porcelain 2>/dev/null || true; }
|
|
390
|
+
count_marker() { grep -cF -- "$2" "$1" 2>/dev/null || true; }
|
|
391
|
+
|
|
392
|
+
# Canned depcruise outputs — copied VERBATIM from the one-hop measurement on a scratch fixture with
|
|
393
|
+
# the real toolchain (dependency-cruiser 16.10.4, task 0.5), then edited per case.
|
|
394
|
+
CANNED="$TMPROOT/canned"
|
|
395
|
+
mkdir -p "$CANNED"
|
|
396
|
+
printf '\n\xe2\x9c\x94 no dependency violations found (1 modules, 0 dependencies cruised)\n\n' > "$CANNED/clean.txt"
|
|
397
|
+
cat > "$CANNED/real.txt" <<'EOF'
|
|
398
|
+
|
|
399
|
+
error no-client-to-server-secret-transitive: components/__constraint_scaffold_bite_probe__/via-hop.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
400
|
+
components/__constraint_scaffold_bite_probe__/hop.ts →
|
|
401
|
+
server/__constraint_scaffold_bite_probe__.ts
|
|
402
|
+
error no-client-to-server-secret-transitive: components/__constraint_scaffold_bite_probe__/direct.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
403
|
+
server/__constraint_scaffold_bite_probe__.ts
|
|
404
|
+
error no-client-to-server-secret: components/__constraint_scaffold_bite_probe__/direct.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
405
|
+
|
|
406
|
+
x 3 dependency violations (3 errors, 0 warnings). 5 modules, 3 dependencies cruised.
|
|
407
|
+
EOF
|
|
408
|
+
# S-real-minus-direct-transitive: today's real output minus the direct-edge transitive block (the
|
|
409
|
+
# shape a reachability-semantics change would produce). Must still PASS the bite.
|
|
410
|
+
cat > "$CANNED/real-minus-direct-transitive.txt" <<'EOF'
|
|
411
|
+
|
|
412
|
+
error no-client-to-server-secret-transitive: components/__constraint_scaffold_bite_probe__/via-hop.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
413
|
+
components/__constraint_scaffold_bite_probe__/hop.ts →
|
|
414
|
+
server/__constraint_scaffold_bite_probe__.ts
|
|
415
|
+
error no-client-to-server-secret: components/__constraint_scaffold_bite_probe__/direct.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
416
|
+
|
|
417
|
+
x 2 dependency violations (2 errors, 0 warnings). 5 modules, 3 dependencies cruised.
|
|
418
|
+
EOF
|
|
419
|
+
# S-direct-only: only the direct-rule line on direct.tsx — the transitive rule is silent.
|
|
420
|
+
cat > "$CANNED/direct-only.txt" <<'EOF'
|
|
421
|
+
|
|
422
|
+
error no-client-to-server-secret: components/__constraint_scaffold_bite_probe__/direct.tsx → server/__constraint_scaffold_bite_probe__.ts
|
|
423
|
+
|
|
424
|
+
x 1 dependency violations (1 errors, 0 warnings). 5 modules, 3 dependencies cruised.
|
|
425
|
+
EOF
|
|
426
|
+
# S-bare-tokens: both rule names and both probe filenames, WITHOUT the `error <rule>: ` call-form.
|
|
427
|
+
cat > "$CANNED/bare-tokens.txt" <<'EOF'
|
|
428
|
+
|
|
429
|
+
no-client-to-server-secret no-client-to-server-secret-transitive components/__constraint_scaffold_bite_probe__/direct.tsx components/__constraint_scaffold_bite_probe__/via-hop.tsx
|
|
430
|
+
|
|
431
|
+
x 2 dependency violations (2 errors, 0 warnings). 5 modules, 3 dependencies cruised.
|
|
432
|
+
EOF
|
|
433
|
+
# S-ansi: today's real output as a FORCE_COLOR environment decorates it (chalk: red `error`, bold
|
|
434
|
+
# from-path) — a correct gate whose stream carries escapes. Must still PASS the bite.
|
|
435
|
+
sed -e 's/^ error \(no-client-to-server-secret[a-z-]*\): \([^ ]*\)/ \x1b[31merror\x1b[39m \1: \x1b[1m\2\x1b[22m/' "$CANNED/real.txt" > "$CANNED/real-ansi.txt"
|
|
436
|
+
# S-preprobe-violation: the CLEAN arm carries a real violation on a non-probe file.
|
|
437
|
+
cat > "$CANNED/preprobe-violation.txt" <<'EOF'
|
|
438
|
+
|
|
439
|
+
error no-client-to-server-secret: components/other.tsx → server/secret.ts
|
|
440
|
+
|
|
441
|
+
x 1 dependency violations (1 errors, 0 warnings). 4 modules, 2 dependencies cruised.
|
|
442
|
+
EOF
|
|
443
|
+
|
|
444
|
+
STUB_CLEAN="$TMPROOT/stub-clean"
|
|
445
|
+
make_stub_depcruise "$STUB_CLEAN" "$CANNED/clean.txt" 0 "$CANNED/real.txt" 3
|
|
446
|
+
|
|
447
|
+
# =============================================================================================
|
|
448
|
+
# Segment 2 — STUB-DRIVEN (needs node + git, no depcruise — runs in every shard).
|
|
449
|
+
# =============================================================================================
|
|
450
|
+
echo "# --- stub-driven: S-clean ---"
|
|
451
|
+
|
|
452
|
+
# --- C2: three-dirs precondition -> 65, nothing emitted ---------------------------------------
|
|
453
|
+
FX="$(make_repo c2-nocomponents --no-components)"
|
|
454
|
+
assert_fixture_dir "$FX"
|
|
455
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
456
|
+
run_gen "$FX" c2
|
|
457
|
+
cases=$((cases + 1))
|
|
458
|
+
if [[ "$RC" == "65" ]]; then
|
|
459
|
+
ok "C2: fixture without components/ exits 65"
|
|
460
|
+
else
|
|
461
|
+
bad "C2: expected 65 for a target lacking components/, got rc=$RC"
|
|
462
|
+
fi
|
|
463
|
+
cases=$((cases + 1))
|
|
464
|
+
if [[ ! -e "$FX/apps/web-platform/.dependency-cruiser.cjs" && ! -e "$FX/apps/web-platform/scripts" && -z "$(porcelain "$FX")" ]]; then
|
|
465
|
+
ok "C2: nothing emitted before the precondition (tree clean)"
|
|
466
|
+
else
|
|
467
|
+
bad "C2: artifacts present after 65: $(porcelain "$FX")"
|
|
468
|
+
fi
|
|
469
|
+
cases=$((cases + 1))
|
|
470
|
+
if grep -q 'lacks components/ (the emitted runner cruises app/ components/ server/' "$TMPROOT/c2.out"; then
|
|
471
|
+
ok "C2: the 65 message names the MISSING dir and the three required ones on STDOUT"
|
|
472
|
+
else
|
|
473
|
+
bad "C2: 65 message not on stdout (stdout: $(head -c 200 "$TMPROOT/c2.out"))"
|
|
474
|
+
fi
|
|
475
|
+
|
|
476
|
+
# --- C3a: CLAUDE.md present -> pointer appended there only; README block emitted -------------
|
|
477
|
+
FX_A="$(make_repo c3a-claude --claude)"
|
|
478
|
+
assert_fixture_dir "$FX_A"
|
|
479
|
+
point_node_modules "$FX_A" "$STUB_CLEAN"
|
|
480
|
+
run_gen "$FX_A" c3a
|
|
481
|
+
cases=$((cases + 1))
|
|
482
|
+
if [[ "$RC" == "0" ]]; then
|
|
483
|
+
ok "C3a: default mode exits 0 under the clean stub"
|
|
484
|
+
else
|
|
485
|
+
bad "C3a: expected 0, got rc=$RC (stdout: $(tail -3 "$TMPROOT/c3a.out" | tr '\n' ' ')) (stderr: $(tail -3 "$TMPROOT/c3a.err" | tr '\n' ' '))"
|
|
486
|
+
fi
|
|
487
|
+
cases=$((cases + 1))
|
|
488
|
+
if grep -qE "$VERDICT_RE" "$TMPROOT/c3a.out" && grep -qF 'depcruise=0.0.0-stub' "$TMPROOT/c3a.out"; then
|
|
489
|
+
ok "C3a: verdict line on stdout carries the stub version (read through the node_modules symlink)"
|
|
490
|
+
else
|
|
491
|
+
bad "C3a: verdict line missing or wrong version: $(grep -F 'bite-proof' "$TMPROOT/c3a.out" | head -2 | tr '\n' ' ')"
|
|
492
|
+
fi
|
|
493
|
+
cases=$((cases + 1))
|
|
494
|
+
if [[ "$(count_marker "$FX_A/CLAUDE.md" "$POINTER_MARKER")" == "1" && ! -e "$FX_A/AGENTS.md" ]]; then
|
|
495
|
+
ok "C3a: pointer appended to CLAUDE.md exactly once; AGENTS.md not created"
|
|
496
|
+
else
|
|
497
|
+
bad "C3a: pointer count=$(count_marker "$FX_A/CLAUDE.md" "$POINTER_MARKER") AGENTS.md exists=$([[ -e "$FX_A/AGENTS.md" ]] && echo yes || echo no)"
|
|
498
|
+
fi
|
|
499
|
+
cases=$((cases + 1))
|
|
500
|
+
if head -1 "$FX_A/CLAUDE.md" | grep -cF >/dev/null '# Fixture project' && grep -qF 'Prior instructions.' "$FX_A/CLAUDE.md"; then
|
|
501
|
+
ok "C3a: prior CLAUDE.md content preserved (append, not replace)"
|
|
502
|
+
else
|
|
503
|
+
bad "C3a: prior CLAUDE.md content lost"
|
|
504
|
+
fi
|
|
505
|
+
POINTER_LINE="$(grep -F -- "$POINTER_MARKER" "$FX_A/CLAUDE.md" | head -1)"
|
|
506
|
+
cases=$((cases + 1))
|
|
507
|
+
if [[ "$POINTER_LINE" == *"apps/web-platform/server/README.md"* && "$POINTER_LINE" != *"@apps/"* && "$POINTER_LINE" != @* && "$POINTER_LINE" == *"$POINTER_MARKER" ]]; then
|
|
508
|
+
ok "C3a: pointer line is plain prose naming apps/web-platform/server/README.md, no @-import, marker at end"
|
|
509
|
+
else
|
|
510
|
+
bad "C3a: pointer line shape wrong: '$POINTER_LINE'"
|
|
511
|
+
fi
|
|
512
|
+
cases=$((cases + 1))
|
|
513
|
+
if [[ "$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_START")" == "1" && "$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_END")" == "1" ]]; then
|
|
514
|
+
ok "C3a: README block emitted once (start + end markers)"
|
|
515
|
+
else
|
|
516
|
+
bad "C3a: README block markers start=$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_START") end=$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_END")"
|
|
517
|
+
fi
|
|
518
|
+
cases=$((cases + 1))
|
|
519
|
+
if [[ -s "$FX_A/apps/web-platform/server/README.md" ]] && ! grep -q 'Inspired by' "$FX_A/apps/web-platform/server/README.md" && ! grep -q '__TARGET_DIR__' "$FX_A/apps/web-platform/server/README.md" && grep -qF 'apps/web-platform' "$FX_A/apps/web-platform/server/README.md"; then
|
|
520
|
+
ok "C3a: emitted README has no attribution line, no __TARGET_DIR__ placeholder, names the target dir"
|
|
521
|
+
else
|
|
522
|
+
bad "C3a: emitted README carries the attribution line or an unsubstituted placeholder"
|
|
523
|
+
fi
|
|
524
|
+
EXPECTED_README="$(sed -e '1{/^<!-- Inspired by /d;}' -e "s|__TARGET_DIR__|apps/web-platform|g" "$REF/boundary-readme.template" 2>/dev/null)"
|
|
525
|
+
cases=$((cases + 1))
|
|
526
|
+
if [[ -n "$EXPECTED_README" ]] && diff -q <(printf '%s\n' "$EXPECTED_README") "$FX_A/apps/web-platform/server/README.md" >/dev/null 2>&1; then
|
|
527
|
+
ok "C3a: emitted README == emitter transform of boundary-readme.template"
|
|
528
|
+
else
|
|
529
|
+
bad "C3a: emitted README differs from the template transform"
|
|
530
|
+
fi
|
|
531
|
+
cases=$((cases + 1))
|
|
532
|
+
if [[ -f "$FX_A/apps/web-platform/.dependency-cruiser-known-violations.json" && "$(worktree_count "$FX_A")" == "1" && ! -e "$FX_A/apps/web-platform/$PROBE_DIR" ]]; then
|
|
533
|
+
ok "C3a: baseline present, worktree list clean, no probe dir in the founder tree"
|
|
534
|
+
else
|
|
535
|
+
bad "C3a: baseline=$([[ -f "$FX_A/apps/web-platform/.dependency-cruiser-known-violations.json" ]] && echo y || echo n) worktrees=$(worktree_count "$FX_A") probe-dir=$([[ -e "$FX_A/apps/web-platform/$PROBE_DIR" ]] && echo present || echo absent)"
|
|
536
|
+
fi
|
|
537
|
+
# The README/pointer are written LAST: the clean-tree guard inside baseline capture must never
|
|
538
|
+
# see them. Observable: the run exited 0 (a pre-capture write would have produced 67).
|
|
539
|
+
cases=$((cases + 1))
|
|
540
|
+
if ! grep -q 'working tree is dirty' "$TMPROOT/c3a.err"; then
|
|
541
|
+
ok "C3a: no 67 (README/pointer were not written before the clean-tree guard)"
|
|
542
|
+
else
|
|
543
|
+
bad "C3a: clean-tree guard saw an early write (67)"
|
|
544
|
+
fi
|
|
545
|
+
# The stub recorded the runner's three passes: baseline + (err, json) + err + (err, json).
|
|
546
|
+
ERR_CALLS="$(grep -c -- '--output-type err' "$STUB_CLEAN/calls.log" || true)"
|
|
547
|
+
cases=$((cases + 1))
|
|
548
|
+
if [[ "$ERR_CALLS" -ge 3 ]]; then
|
|
549
|
+
ok "C3a: stub argv log shows >= 3 'err' passes (pass -> fail -> pass) — got $ERR_CALLS"
|
|
550
|
+
else
|
|
551
|
+
bad "C3a: stub argv log shows only $ERR_CALLS 'err' passes (want >= 3)"
|
|
552
|
+
fi
|
|
553
|
+
|
|
554
|
+
# --- C4: idempotency — re-install after removing the gate; README block + pointer stay at 1 ----
|
|
555
|
+
rm -f -- "$FX_A/apps/web-platform/.dependency-cruiser.cjs" "$FX_A/apps/web-platform/scripts/constraint-gates.sh" \
|
|
556
|
+
"$FX_A/apps/web-platform/.github/workflows/constraint-gates.yml" "$FX_A/apps/web-platform/.github/workflows/fix-constraints-stage-a.yml" \
|
|
557
|
+
"$FX_A/apps/web-platform/.github/workflows/fix-constraints-stage-b.yml" "$FX_A/apps/web-platform/.dependency-cruiser-known-violations.json"
|
|
558
|
+
git -C "$FX_A" add -A
|
|
559
|
+
git -C "$FX_A" commit -q -m "keep README + pointer, drop the gate"
|
|
560
|
+
git -C "$FX_A" update-ref refs/remotes/origin/main HEAD
|
|
561
|
+
run_gen "$FX_A" c4
|
|
562
|
+
cases=$((cases + 1))
|
|
563
|
+
if [[ "$RC" == "0" && "$(count_marker "$FX_A/CLAUDE.md" "$POINTER_MARKER")" == "1" && "$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_START")" == "1" ]]; then
|
|
564
|
+
ok "C4: second default-mode run (rc=$RC) leaves pointer and README block at exactly 1 each"
|
|
565
|
+
else
|
|
566
|
+
bad "C4: rc=$RC pointer=$(count_marker "$FX_A/CLAUDE.md" "$POINTER_MARKER") readme-start=$(count_marker "$FX_A/apps/web-platform/server/README.md" "$README_START")"
|
|
567
|
+
fi
|
|
568
|
+
|
|
569
|
+
# --- C6: commit, then --refresh-baseline -> rc 0, verdict, README/pointer byte-identical --------
|
|
570
|
+
git -C "$FX_A" add -A
|
|
571
|
+
git -C "$FX_A" commit -q -m "install gate"
|
|
572
|
+
git -C "$FX_A" update-ref refs/remotes/origin/main HEAD
|
|
573
|
+
cp "$FX_A/CLAUDE.md" "$TMPROOT/c6-claude.before"
|
|
574
|
+
cp "$FX_A/apps/web-platform/server/README.md" "$TMPROOT/c6-readme.before"
|
|
575
|
+
run_gen "$FX_A" c6 --refresh-baseline
|
|
576
|
+
cases=$((cases + 1))
|
|
577
|
+
if [[ "$RC" == "0" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/c6.out"; then
|
|
578
|
+
ok "C6: --refresh-baseline exits 0 and prints the verdict line (bite runs in refresh mode too)"
|
|
579
|
+
else
|
|
580
|
+
bad "C6: rc=$RC verdict=$(grep -cE "$VERDICT_RE" "$TMPROOT/c6.out" || true) (stderr: $(tail -2 "$TMPROOT/c6.err" | tr '\n' ' '))"
|
|
581
|
+
fi
|
|
582
|
+
cases=$((cases + 1))
|
|
583
|
+
if cmp -s "$FX_A/CLAUDE.md" "$TMPROOT/c6-claude.before" && cmp -s "$FX_A/apps/web-platform/server/README.md" "$TMPROOT/c6-readme.before"; then
|
|
584
|
+
ok "C6: refresh mode touched neither CLAUDE.md nor server/README.md"
|
|
585
|
+
else
|
|
586
|
+
bad "C6: refresh mode modified CLAUDE.md or server/README.md"
|
|
587
|
+
fi
|
|
588
|
+
cases=$((cases + 1))
|
|
589
|
+
if [[ "$(worktree_count "$FX_A")" == "1" ]]; then
|
|
590
|
+
ok "C6: worktree list clean after refresh"
|
|
591
|
+
else
|
|
592
|
+
bad "C6: $(worktree_count "$FX_A") worktrees registered after refresh"
|
|
593
|
+
fi
|
|
594
|
+
|
|
595
|
+
# --- C3b: only AGENTS.md -> appended there; CLAUDE.md not created --------------------------------
|
|
596
|
+
FX="$(make_repo c3b-agents --agents)"
|
|
597
|
+
assert_fixture_dir "$FX"
|
|
598
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
599
|
+
run_gen "$FX" c3b
|
|
600
|
+
cases=$((cases + 1))
|
|
601
|
+
if [[ "$RC" == "0" && "$(count_marker "$FX/AGENTS.md" "$POINTER_MARKER")" == "1" && ! -e "$FX/CLAUDE.md" ]]; then
|
|
602
|
+
ok "C3b: pointer appended to AGENTS.md once; CLAUDE.md not created (rc=$RC)"
|
|
603
|
+
else
|
|
604
|
+
bad "C3b: rc=$RC AGENTS pointer=$(count_marker "$FX/AGENTS.md" "$POINTER_MARKER") CLAUDE.md exists=$([[ -e "$FX/CLAUDE.md" ]] && echo yes || echo no)"
|
|
605
|
+
fi
|
|
606
|
+
cases=$((cases + 1))
|
|
607
|
+
if grep -qF 'Prior agent notes.' "$FX/AGENTS.md"; then
|
|
608
|
+
ok "C3b: prior AGENTS.md content preserved"
|
|
609
|
+
else
|
|
610
|
+
bad "C3b: prior AGENTS.md content lost"
|
|
611
|
+
fi
|
|
612
|
+
|
|
613
|
+
# --- C3c: neither -> AGENTS.md created with exactly the one line ---------------------------------
|
|
614
|
+
FX="$(make_repo c3c-neither)"
|
|
615
|
+
assert_fixture_dir "$FX"
|
|
616
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
617
|
+
run_gen "$FX" c3c
|
|
618
|
+
AGENTS_LINES="$(grep -c . "$FX/AGENTS.md" 2>/dev/null || true)"
|
|
619
|
+
cases=$((cases + 1))
|
|
620
|
+
if [[ "$RC" == "0" && -f "$FX/AGENTS.md" && "$AGENTS_LINES" == "1" && "$(count_marker "$FX/AGENTS.md" "$POINTER_MARKER")" == "1" && ! -e "$FX/CLAUDE.md" ]]; then
|
|
621
|
+
ok "C3c: AGENTS.md created carrying exactly the pointer line (rc=$RC)"
|
|
622
|
+
else
|
|
623
|
+
bad "C3c: rc=$RC AGENTS.md lines=$AGENTS_LINES pointer=$(count_marker "$FX/AGENTS.md" "$POINTER_MARKER")"
|
|
624
|
+
fi
|
|
625
|
+
|
|
626
|
+
# --- C-symlink: CLAUDE.md -> elsewhere: refused, nothing written through the link, rc 0 -----------
|
|
627
|
+
FX="$(make_repo csymlink --claude-symlink)"
|
|
628
|
+
assert_fixture_dir "$FX"
|
|
629
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
630
|
+
cp "$TMPROOT/elsewhere-csymlink/CLAUDE.md" "$TMPROOT/csymlink-global.before"
|
|
631
|
+
run_gen "$FX" csymlink
|
|
632
|
+
cases=$((cases + 1))
|
|
633
|
+
if [[ "$RC" == "0" ]] && cmp -s "$TMPROOT/elsewhere-csymlink/CLAUDE.md" "$TMPROOT/csymlink-global.before" && [[ ! -e "$FX/AGENTS.md" ]]; then
|
|
634
|
+
ok "C-symlink: run exits 0; nothing written through the CLAUDE.md symlink; no AGENTS.md fallback"
|
|
635
|
+
else
|
|
636
|
+
bad "C-symlink: rc=$RC link-target-changed=$(cmp -s "$TMPROOT/elsewhere-csymlink/CLAUDE.md" "$TMPROOT/csymlink-global.before" && echo no || echo YES) AGENTS.md=$([[ -e "$FX/AGENTS.md" ]] && echo created || echo absent)"
|
|
637
|
+
fi
|
|
638
|
+
cases=$((cases + 1))
|
|
639
|
+
if grep -qiE 'WARNING.*symlink' "$TMPROOT/csymlink.out"; then
|
|
640
|
+
ok "C-symlink: refusal is a WARNING on stdout naming the symlink"
|
|
641
|
+
else
|
|
642
|
+
bad "C-symlink: no symlink warning on stdout: $(grep -i warn "$TMPROOT/csymlink.out" | head -1)"
|
|
643
|
+
fi
|
|
644
|
+
cases=$((cases + 1))
|
|
645
|
+
if grep -qE "$VERDICT_RE" "$TMPROOT/csymlink.out" && [[ -f "$FX/apps/web-platform/.dependency-cruiser.cjs" ]]; then
|
|
646
|
+
ok "C-symlink: the gate stayed installed and proven (verdict line present, prose is not the gate)"
|
|
647
|
+
else
|
|
648
|
+
bad "C-symlink: gate not installed/proven after the pointer refusal"
|
|
649
|
+
fi
|
|
650
|
+
|
|
651
|
+
# --- C-tmpdir: TMPDIR inside the fixture repo -> 69 before any worktree ---------------------------
|
|
652
|
+
FX="$(make_repo ctmpdir)"
|
|
653
|
+
assert_fixture_dir "$FX"
|
|
654
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
655
|
+
mkdir -p "$FX/tmp-inside"
|
|
656
|
+
RUN_TMPDIR="$FX/tmp-inside" run_gen "$FX" ctmpdir
|
|
657
|
+
cases=$((cases + 1))
|
|
658
|
+
if [[ "$RC" == "69" && "$(worktree_count "$FX")" == "1" ]]; then
|
|
659
|
+
ok "C-tmpdir: TMPDIR inside the repo is refused with 69 before any worktree is created"
|
|
660
|
+
else
|
|
661
|
+
bad "C-tmpdir: rc=$RC worktrees=$(worktree_count "$FX") (want 69 / 1)"
|
|
662
|
+
fi
|
|
663
|
+
cases=$((cases + 1))
|
|
664
|
+
if [[ -z "$(porcelain "$FX")" && -z "$(ls -A "$FX/tmp-inside" 2>/dev/null)" ]]; then
|
|
665
|
+
ok "C-tmpdir: founder tree clean afterwards (emitted artifacts removed, no mktemp residue inside the repo)"
|
|
666
|
+
else
|
|
667
|
+
bad "C-tmpdir: residue after 69: $(porcelain "$FX" | tr '\n' ' ') tmp-inside=[$(ls -A "$FX/tmp-inside" | tr '\n' ' ')]"
|
|
668
|
+
fi
|
|
669
|
+
|
|
670
|
+
# --- C-chmod (non-root): worktree add fails -> 69, no mktemp dir left ----------------------------
|
|
671
|
+
if [[ "$(id -u)" != "0" ]]; then
|
|
672
|
+
FX="$(make_repo cchmod)"
|
|
673
|
+
assert_fixture_dir "$FX"
|
|
674
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
675
|
+
mkdir -p "$TMPROOT/cchmod-tmp"
|
|
676
|
+
chmod a-w "$FX/.git"
|
|
677
|
+
RUN_TMPDIR="$TMPROOT/cchmod-tmp" run_gen "$FX" cchmod
|
|
678
|
+
chmod u+w "$FX/.git"
|
|
679
|
+
cases=$((cases + 1))
|
|
680
|
+
if [[ "$RC" == "69" ]]; then
|
|
681
|
+
ok "C-chmod: a failing worktree add exits 69"
|
|
682
|
+
else
|
|
683
|
+
bad "C-chmod: expected 69 when worktree add fails, got rc=$RC"
|
|
684
|
+
fi
|
|
685
|
+
cases=$((cases + 1))
|
|
686
|
+
if [[ -z "$(ls -A "$TMPROOT/cchmod-tmp" 2>/dev/null)" ]]; then
|
|
687
|
+
ok "C-chmod: no mktemp dir left behind (trap installed BEFORE worktree add)"
|
|
688
|
+
else
|
|
689
|
+
bad "C-chmod: mktemp residue after a failed worktree add: $(ls -A "$TMPROOT/cchmod-tmp" | tr '\n' ' ')"
|
|
690
|
+
fi
|
|
691
|
+
else
|
|
692
|
+
echo "# C-chmod skipped (running as root: chmod a-w does not block writes)"
|
|
693
|
+
fi
|
|
694
|
+
|
|
695
|
+
echo "# --- stub-driven: per-case stubs ---"
|
|
696
|
+
|
|
697
|
+
# stub_case <name> <clean-out> <clean-rc> <probe-out> <probe-rc> -> FX, RC, files in $TMPROOT/<name>.*
|
|
698
|
+
stub_case() {
|
|
699
|
+
local name="$1"
|
|
700
|
+
local stub="$TMPROOT/stub-$name"
|
|
701
|
+
make_stub_depcruise "$stub" "$2" "$3" "$4" "$5"
|
|
702
|
+
FX="$(make_repo "$name")"
|
|
703
|
+
assert_fixture_dir "$FX"
|
|
704
|
+
point_node_modules "$FX" "$stub"
|
|
705
|
+
run_gen "$FX" "$name"
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
# --- S-direct-only -> 72 (the transitive assertion is live on its own edge) ----------------------
|
|
709
|
+
stub_case s-direct-only "$CANNED/clean.txt" 0 "$CANNED/direct-only.txt" 1
|
|
710
|
+
cases=$((cases + 1))
|
|
711
|
+
if [[ "$RC" == "72" ]]; then
|
|
712
|
+
ok "S-direct-only: only the direct-rule line -> 72"
|
|
713
|
+
else
|
|
714
|
+
bad "S-direct-only: expected 72, got rc=$RC"
|
|
715
|
+
fi
|
|
716
|
+
cases=$((cases + 1))
|
|
717
|
+
if grep -qF 'bite-proof FAILED (72)' "$TMPROOT/s-direct-only.out" && grep -qF 'depcruise=0.0.0-stub' "$TMPROOT/s-direct-only.out"; then
|
|
718
|
+
ok "S-direct-only: failure line on STDOUT names the code and the depcruise version"
|
|
719
|
+
else
|
|
720
|
+
bad "S-direct-only: stdout lacks 'bite-proof FAILED (72)' + version: $(grep -F FAILED "$TMPROOT/s-direct-only.out" | head -1)"
|
|
721
|
+
fi
|
|
722
|
+
cases=$((cases + 1))
|
|
723
|
+
if grep -qF 'no-client-to-server-secret@direct' "$TMPROOT/s-direct-only.out" && ! grep -qF 'no-client-to-server-secret-transitive@via-hop' "$TMPROOT/s-direct-only.out"; then
|
|
724
|
+
ok "S-direct-only: the failure names which rule WAS seen on its edge (direct) and not the missing one"
|
|
725
|
+
else
|
|
726
|
+
bad "S-direct-only: rules-named list wrong: $(grep -F 'rules named' "$TMPROOT/s-direct-only.out" | head -1)"
|
|
727
|
+
fi
|
|
728
|
+
# S-fail72 (same stub, default mode): the founder tree is as it was.
|
|
729
|
+
cases=$((cases + 1))
|
|
730
|
+
if [[ -z "$(porcelain "$FX")" ]]; then
|
|
731
|
+
ok "S-fail72: git status --porcelain empty after a 72 in default mode (every emitted artifact removed)"
|
|
732
|
+
else
|
|
733
|
+
bad "S-fail72: residue after 72: $(porcelain "$FX" | tr '\n' ' ')"
|
|
734
|
+
fi
|
|
735
|
+
cases=$((cases + 1))
|
|
736
|
+
if [[ ! -e "$FX/apps/web-platform/scripts" && ! -e "$FX/apps/web-platform/.github/workflows" ]]; then
|
|
737
|
+
ok "S-fail72: scripts/ and .github/workflows/ dirs removed (rmdir of the two mkdir -p dirs)"
|
|
738
|
+
else
|
|
739
|
+
bad "S-fail72: scripts=$([[ -e "$FX/apps/web-platform/scripts" ]] && echo present || echo gone) workflows=$([[ -e "$FX/apps/web-platform/.github/workflows" ]] && echo present || echo gone)"
|
|
740
|
+
fi
|
|
741
|
+
cases=$((cases + 1))
|
|
742
|
+
if grep -qF 'removed the artifacts this run emitted' "$TMPROOT/s-direct-only.out" && grep -qF 'git worktree prune' "$TMPROOT/s-direct-only.out"; then
|
|
743
|
+
ok "S-fail72: stdout says the artifacts were removed and names 'git worktree prune'"
|
|
744
|
+
else
|
|
745
|
+
bad "S-fail72: cleanup message missing on stdout"
|
|
746
|
+
fi
|
|
747
|
+
cases=$((cases + 1))
|
|
748
|
+
if [[ "$(worktree_count "$FX")" == "1" ]]; then
|
|
749
|
+
ok "S-fail72: worktree removed after the failed bite"
|
|
750
|
+
else
|
|
751
|
+
bad "S-fail72: $(worktree_count "$FX") worktrees registered after a 72"
|
|
752
|
+
fi
|
|
753
|
+
cases=$((cases + 1))
|
|
754
|
+
if grep -qE '^\s*error no-client-to-server-secret: ' "$TMPROOT/s-direct-only.out"; then
|
|
755
|
+
ok "S-fail72: the captured runner log tail is echoed to stdout as evidence"
|
|
756
|
+
else
|
|
757
|
+
bad "S-fail72: no log tail on stdout"
|
|
758
|
+
fi
|
|
759
|
+
|
|
760
|
+
# --- S-lines-rc0 -> 72 (runner stopped failing on rc>0: both lines printed, exit 0) --------------
|
|
761
|
+
stub_case s-lines-rc0 "$CANNED/clean.txt" 0 "$CANNED/real.txt" 0
|
|
762
|
+
cases=$((cases + 1))
|
|
763
|
+
if [[ "$RC" == "72" ]]; then
|
|
764
|
+
ok "S-lines-rc0: both call-form lines but rc 0 -> 72 (the rc check is live)"
|
|
765
|
+
else
|
|
766
|
+
bad "S-lines-rc0: expected 72, got rc=$RC"
|
|
767
|
+
fi
|
|
768
|
+
cases=$((cases + 1))
|
|
769
|
+
if grep -qE 'bite-proof FAILED \(72\).*rc=0' "$TMPROOT/s-lines-rc0.out"; then
|
|
770
|
+
ok "S-lines-rc0: failure line reports rc=0"
|
|
771
|
+
else
|
|
772
|
+
bad "S-lines-rc0: failure line does not report rc=0: $(grep -F FAILED "$TMPROOT/s-lines-rc0.out" | head -1)"
|
|
773
|
+
fi
|
|
774
|
+
|
|
775
|
+
# --- S-bare-tokens -> 72 (bare rule names + filenames without the call-form) ---------------------
|
|
776
|
+
stub_case s-bare-tokens "$CANNED/clean.txt" 0 "$CANNED/bare-tokens.txt" 1
|
|
777
|
+
cases=$((cases + 1))
|
|
778
|
+
if [[ "$RC" == "72" ]]; then
|
|
779
|
+
ok "S-bare-tokens: bare tokens without 'error <rule>: ' -> 72 (call-form anchored)"
|
|
780
|
+
else
|
|
781
|
+
bad "S-bare-tokens: expected 72, got rc=$RC"
|
|
782
|
+
fi
|
|
783
|
+
|
|
784
|
+
# --- S-real-minus-direct-transitive -> verdict, rc 0 ---------------------------------------------
|
|
785
|
+
stub_case s-real-minus "$CANNED/clean.txt" 0 "$CANNED/real-minus-direct-transitive.txt" 2
|
|
786
|
+
cases=$((cases + 1))
|
|
787
|
+
if [[ "$RC" == "0" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/s-real-minus.out"; then
|
|
788
|
+
ok "S-real-minus-direct-transitive: the direct-edge transitive line is neither required nor forbidden (rc=0, verdict)"
|
|
789
|
+
else
|
|
790
|
+
bad "S-real-minus-direct-transitive: rc=$RC verdict=$(grep -cE "$VERDICT_RE" "$TMPROOT/s-real-minus.out" || true)"
|
|
791
|
+
fi
|
|
792
|
+
cases=$((cases + 1))
|
|
793
|
+
if grep -qF 'depcruise=0.0.0-stub' "$TMPROOT/s-real-minus.out"; then
|
|
794
|
+
ok "S-real-minus-direct-transitive: verdict version read through the symlink chain (0.0.0-stub)"
|
|
795
|
+
else
|
|
796
|
+
bad "S-real-minus-direct-transitive: verdict does not carry the stub version"
|
|
797
|
+
fi
|
|
798
|
+
|
|
799
|
+
# --- S-preprobe-violation -> 74 naming the file ----------------------------------------------------
|
|
800
|
+
stub_case s-preprobe "$CANNED/preprobe-violation.txt" 1 "$CANNED/real.txt" 3
|
|
801
|
+
cases=$((cases + 1))
|
|
802
|
+
if [[ "$RC" == "74" ]]; then
|
|
803
|
+
ok "S-preprobe-violation: a real violation on the clean pass -> 74 (not 71)"
|
|
804
|
+
else
|
|
805
|
+
bad "S-preprobe-violation: expected 74, got rc=$RC"
|
|
806
|
+
fi
|
|
807
|
+
cases=$((cases + 1))
|
|
808
|
+
if grep -qF 'bite-proof FAILED (74)' "$TMPROOT/s-preprobe.out" && grep -qF 'components/other.tsx' "$TMPROOT/s-preprobe.out"; then
|
|
809
|
+
ok "S-preprobe-violation: stdout lists the violating file (components/other.tsx)"
|
|
810
|
+
else
|
|
811
|
+
bad "S-preprobe-violation: stdout does not list the file: $(grep -F FAILED "$TMPROOT/s-preprobe.out" | head -1)"
|
|
812
|
+
fi
|
|
813
|
+
cases=$((cases + 1))
|
|
814
|
+
if grep -qE '1 real client->server-secret violation' "$TMPROOT/s-preprobe.out"; then
|
|
815
|
+
ok "S-preprobe-violation: the count of violations is reported"
|
|
816
|
+
else
|
|
817
|
+
bad "S-preprobe-violation: violation count missing"
|
|
818
|
+
fi
|
|
819
|
+
cases=$((cases + 1))
|
|
820
|
+
if [[ -z "$(porcelain "$FX")" ]]; then
|
|
821
|
+
ok "S-preprobe-violation: default-mode 74 also leaves the tree clean"
|
|
822
|
+
else
|
|
823
|
+
bad "S-preprobe-violation: residue after 74: $(porcelain "$FX" | tr '\n' ' ')"
|
|
824
|
+
fi
|
|
825
|
+
|
|
826
|
+
# --- S-term: stub TERMs the scaffold during the pre-probe pass -> 143, cleanup, one worktree -----
|
|
827
|
+
STUB_TERM="$TMPROOT/stub-term"
|
|
828
|
+
PIDFILE="$TMPROOT/s-term.pid"
|
|
829
|
+
: > "$PIDFILE"
|
|
830
|
+
make_stub_depcruise "$STUB_TERM" "$CANNED/clean.txt" 0 "$CANNED/real.txt" 3 "$PIDFILE"
|
|
831
|
+
FX="$(make_repo s-term)"
|
|
832
|
+
assert_fixture_dir "$FX"
|
|
833
|
+
point_node_modules "$FX" "$STUB_TERM"
|
|
834
|
+
mkdir -p "$TMPROOT/s-term-tmp"
|
|
835
|
+
CONSTRAINT_SCAFFOLD_REPO_ROOT="$FX" TMPDIR="$TMPROOT/s-term-tmp" bash "$GEN" >"$TMPROOT/s-term.out" 2>"$TMPROOT/s-term.err" &
|
|
836
|
+
TERM_PID=$!
|
|
837
|
+
printf '%s' "$TERM_PID" > "$PIDFILE"
|
|
838
|
+
RC=0
|
|
839
|
+
wait "$TERM_PID" || RC=$?
|
|
840
|
+
cases=$((cases + 1))
|
|
841
|
+
if [[ "$RC" == "143" ]]; then
|
|
842
|
+
ok "S-term: TERM during the pre-probe pass exits 143"
|
|
843
|
+
else
|
|
844
|
+
bad "S-term: expected 143, got rc=$RC (stdout: $(tail -2 "$TMPROOT/s-term.out" | tr '\n' ' '))"
|
|
845
|
+
fi
|
|
846
|
+
cases=$((cases + 1))
|
|
847
|
+
if grep -qF 'interrupted; removed:' "$TMPROOT/s-term.out"; then
|
|
848
|
+
ok "S-term: stdout carries the 'interrupted; removed: <list>' line"
|
|
849
|
+
else
|
|
850
|
+
bad "S-term: no 'interrupted; removed:' line on stdout"
|
|
851
|
+
fi
|
|
852
|
+
cases=$((cases + 1))
|
|
853
|
+
if [[ "$(worktree_count "$FX")" == "1" && -z "$(porcelain "$FX")" ]]; then
|
|
854
|
+
ok "S-term: one worktree registered, git status --porcelain empty"
|
|
855
|
+
else
|
|
856
|
+
bad "S-term: worktrees=$(worktree_count "$FX") porcelain=[$(porcelain "$FX" | tr '\n' ' ')]"
|
|
857
|
+
fi
|
|
858
|
+
cases=$((cases + 1))
|
|
859
|
+
if [[ -z "$(ls -A "$TMPROOT/s-term-tmp" 2>/dev/null)" ]]; then
|
|
860
|
+
ok "S-term: no leftover mktemp dir"
|
|
861
|
+
else
|
|
862
|
+
bad "S-term: mktemp residue: $(ls -A "$TMPROOT/s-term-tmp" | tr '\n' ' ')"
|
|
863
|
+
fi
|
|
864
|
+
cases=$((cases + 1))
|
|
865
|
+
if grep -q -- '--output-type baseline' "$STUB_TERM/calls.log" && grep -q -- '--output-type err' "$STUB_TERM/calls.log"; then
|
|
866
|
+
ok "S-term: the stub saw the baseline call and then the err call that delivered the TERM"
|
|
867
|
+
else
|
|
868
|
+
bad "S-term: stub argv log unexpected: $(tr '\n' '|' < "$STUB_TERM/calls.log")"
|
|
869
|
+
fi
|
|
870
|
+
|
|
871
|
+
# --- S-ansi: ANSI-decorated real output on the probe arm -> still PASSES (the anchors read a
|
|
872
|
+
# stripped stream; the runner exports NO_COLOR=1 as the first line of defence) ---------------------
|
|
873
|
+
stub_case s-ansi "$CANNED/clean.txt" 0 "$CANNED/real-ansi.txt" 3
|
|
874
|
+
cases=$((cases + 1))
|
|
875
|
+
if [[ "$RC" == "0" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/s-ansi.out"; then
|
|
876
|
+
ok "S-ansi: colour-decorated \`error <rule>:\` lines still satisfy both anchors (rc 0, verdict line present)"
|
|
877
|
+
else
|
|
878
|
+
bad "S-ansi: rc=$RC verdict=$(grep -cE "$VERDICT_RE" "$TMPROOT/s-ansi.out" || true) — an ANSI-decorated stream must not read as 72 (stdout: $(grep -F FAILED "$TMPROOT/s-ansi.out" | head -1))"
|
|
879
|
+
fi
|
|
880
|
+
|
|
881
|
+
# --- S-73: the gate stays red after the revert -> 73, tree clean ---------------------------------
|
|
882
|
+
STUB_73="$TMPROOT/stub-s-73"
|
|
883
|
+
make_stub_depcruise "$STUB_73" "$CANNED/clean.txt" 0 "$CANNED/real.txt" 3
|
|
884
|
+
touch "$STUB_73/stateful"
|
|
885
|
+
FX="$(make_repo s-73)"
|
|
886
|
+
assert_fixture_dir "$FX"
|
|
887
|
+
point_node_modules "$FX" "$STUB_73"
|
|
888
|
+
run_gen "$FX" s-73
|
|
889
|
+
cases=$((cases + 1))
|
|
890
|
+
if [[ "$RC" == "73" ]] && grep -qF 'bite-proof FAILED (73)' "$TMPROOT/s-73.out"; then
|
|
891
|
+
ok "S-73: a gate that does not return to green after the probes are removed exits 73 (verdict on stdout)"
|
|
892
|
+
else
|
|
893
|
+
bad "S-73: expected 73 + stdout verdict, got rc=$RC (stdout: $(grep -F FAILED "$TMPROOT/s-73.out" | head -1))"
|
|
894
|
+
fi
|
|
895
|
+
cases=$((cases + 1))
|
|
896
|
+
if [[ -z "$(porcelain "$FX")" ]]; then
|
|
897
|
+
ok "S-73: default-mode 73 leaves the tree clean"
|
|
898
|
+
else
|
|
899
|
+
bad "S-73: residue after 73: $(porcelain "$FX" | tr '\n' ' ')"
|
|
900
|
+
fi
|
|
901
|
+
|
|
902
|
+
# --- S-71: the emitted RUNNER refuses a reachability baseline entry on the pre-probe pass -> 71 ---
|
|
903
|
+
# (Runner logic, not stub bytes: the only stub shape that distinguishes "ran the emitted runner"
|
|
904
|
+
# from "called depcruise directly with the runner's argv".)
|
|
905
|
+
STUB_71="$TMPROOT/stub-s-71"
|
|
906
|
+
make_stub_depcruise "$STUB_71" "$CANNED/clean.txt" 0 "$CANNED/real.txt" 3
|
|
907
|
+
touch "$STUB_71/reach-baseline"
|
|
908
|
+
FX="$(make_repo s-71)"
|
|
909
|
+
assert_fixture_dir "$FX"
|
|
910
|
+
point_node_modules "$FX" "$STUB_71"
|
|
911
|
+
run_gen "$FX" s-71
|
|
912
|
+
cases=$((cases + 1))
|
|
913
|
+
if [[ "$RC" == "71" ]] && grep -qF 'bite-proof FAILED (71)' "$TMPROOT/s-71.out" && grep -qF 'suppress the transitive rule' "$TMPROOT/s-71.out"; then
|
|
914
|
+
ok "S-71: the runner's own pre-probe refusal surfaces as 71 with the runner's reason in the stdout log tail"
|
|
915
|
+
else
|
|
916
|
+
bad "S-71: expected 71 + runner reason on stdout, got rc=$RC (stdout: $(grep -F FAILED "$TMPROOT/s-71.out" | head -1))"
|
|
917
|
+
fi
|
|
918
|
+
cases=$((cases + 1))
|
|
919
|
+
if [[ -z "$(porcelain "$FX")" ]]; then
|
|
920
|
+
ok "S-71: default-mode 71 leaves the tree clean"
|
|
921
|
+
else
|
|
922
|
+
bad "S-71: residue after 71: $(porcelain "$FX" | tr '\n' ' ')"
|
|
923
|
+
fi
|
|
924
|
+
|
|
925
|
+
# --- S-int130: the runner exits 130 (terminal Ctrl-C reached the node child) -> treated as an
|
|
926
|
+
# interrupt, never as a 71/72 verdict; cleanup; exit 130 ----------------------------------------------
|
|
927
|
+
stub_case s-int130 "$CANNED/clean.txt" 130 "$CANNED/real.txt" 3
|
|
928
|
+
cases=$((cases + 1))
|
|
929
|
+
if [[ "$RC" == "130" ]] && grep -qF 'interrupted; removed:' "$TMPROOT/s-int130.out" && ! grep -qF 'bite-proof FAILED' "$TMPROOT/s-int130.out"; then
|
|
930
|
+
ok "S-int130: runner rc 130 exits 130 with the interrupted line and NO verdict"
|
|
931
|
+
else
|
|
932
|
+
bad "S-int130: rc=$RC (want 130) stdout: $(tail -2 "$TMPROOT/s-int130.out" | tr '\n' ' ')"
|
|
933
|
+
fi
|
|
934
|
+
cases=$((cases + 1))
|
|
935
|
+
if [[ -z "$(porcelain "$FX")" && "$(worktree_count "$FX")" == "1" ]]; then
|
|
936
|
+
ok "S-int130: tree clean, no worktree left registered"
|
|
937
|
+
else
|
|
938
|
+
bad "S-int130: porcelain=[$(porcelain "$FX" | tr '\n' ' ')] worktrees=$(worktree_count "$FX")"
|
|
939
|
+
fi
|
|
940
|
+
|
|
941
|
+
# --- S-noorigin: neither origin/main nor origin/HEAD -> 69 BEFORE any write; re-run is not 66 ------
|
|
942
|
+
FX="$(make_repo s-noorigin)"
|
|
943
|
+
assert_fixture_dir "$FX"
|
|
944
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
945
|
+
git -C "$FX" update-ref -d refs/remotes/origin/main
|
|
946
|
+
run_gen "$FX" s-noorigin
|
|
947
|
+
cases=$((cases + 1))
|
|
948
|
+
if [[ "$RC" == "69" ]] && grep -qF 'FAILED (69): no origin/main and no origin/HEAD' "$TMPROOT/s-noorigin.out"; then
|
|
949
|
+
ok "S-noorigin: no base ref exits 69 with the fetch instruction on STDOUT"
|
|
950
|
+
else
|
|
951
|
+
bad "S-noorigin: rc=$RC (want 69) stdout: $(tail -2 "$TMPROOT/s-noorigin.out" | tr '\n' ' ')"
|
|
952
|
+
fi
|
|
953
|
+
cases=$((cases + 1))
|
|
954
|
+
if [[ -z "$(porcelain "$FX")" && ! -e "$FX/apps/web-platform/scripts" ]]; then
|
|
955
|
+
ok "S-noorigin: nothing was written (porcelain empty, no scripts/ dir)"
|
|
956
|
+
else
|
|
957
|
+
bad "S-noorigin: residue after 69: $(porcelain "$FX" | tr '\n' ' ')"
|
|
958
|
+
fi
|
|
959
|
+
run_gen "$FX" s-noorigin-2
|
|
960
|
+
cases=$((cases + 1))
|
|
961
|
+
if [[ "$RC" == "69" ]]; then
|
|
962
|
+
ok "S-noorigin: the re-run is the same 69, not a 66 on a half-installed gate"
|
|
963
|
+
else
|
|
964
|
+
bad "S-noorigin: re-run rc=$RC (want 69; 66 means a half-installed gate was left behind)"
|
|
965
|
+
fi
|
|
966
|
+
|
|
967
|
+
# --- S-originhead: a founder repo on master (origin/HEAD -> origin/master, no origin/main) -> 0 ---
|
|
968
|
+
FX="$(make_repo s-originhead)"
|
|
969
|
+
assert_fixture_dir "$FX"
|
|
970
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
971
|
+
git -C "$FX" update-ref refs/remotes/origin/master HEAD
|
|
972
|
+
git -C "$FX" update-ref -d refs/remotes/origin/main
|
|
973
|
+
git -C "$FX" symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/master
|
|
974
|
+
run_gen "$FX" s-originhead
|
|
975
|
+
cases=$((cases + 1))
|
|
976
|
+
if [[ "$RC" == "0" ]] && grep -qF 'against origin/master merge-base' "$TMPROOT/s-originhead.err"; then
|
|
977
|
+
ok "S-originhead: falls back to the remote default branch (origin/master) and completes"
|
|
978
|
+
else
|
|
979
|
+
bad "S-originhead: rc=$RC (want 0) stderr: $(grep -F 'merge-base' "$TMPROOT/s-originhead.err" | head -1)"
|
|
980
|
+
fi
|
|
981
|
+
|
|
982
|
+
# --- S-mktemp: TMPDIR that does not exist -> mktemp fails -> 69, tree clean (not a raw set -e abort) --
|
|
983
|
+
FX="$(make_repo s-mktemp)"
|
|
984
|
+
assert_fixture_dir "$FX"
|
|
985
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
986
|
+
RUN_TMPDIR="$TMPROOT/does-not-exist/nested" run_gen "$FX" s-mktemp
|
|
987
|
+
cases=$((cases + 1))
|
|
988
|
+
if [[ "$RC" == "69" ]] && grep -qF 'FAILED (69): cannot create a temp dir' "$TMPROOT/s-mktemp.out"; then
|
|
989
|
+
ok "S-mktemp: an unusable TMPDIR exits 69 with the cause on STDOUT"
|
|
990
|
+
else
|
|
991
|
+
bad "S-mktemp: rc=$RC (want 69) stdout: $(tail -2 "$TMPROOT/s-mktemp.out" | tr '\n' ' ')"
|
|
992
|
+
fi
|
|
993
|
+
cases=$((cases + 1))
|
|
994
|
+
if [[ -z "$(porcelain "$FX")" && ! -e "$FX/apps/web-platform/scripts" ]]; then
|
|
995
|
+
ok "S-mktemp: every emitted artifact removed (the cleanup was armed before the first write)"
|
|
996
|
+
else
|
|
997
|
+
bad "S-mktemp: residue after 69: $(porcelain "$FX" | tr '\n' ' ')"
|
|
998
|
+
fi
|
|
999
|
+
|
|
1000
|
+
# --- S-symlink-server: server/ is a committed symlink to a dir OUTSIDE the repo -> the bite refuses
|
|
1001
|
+
# to write probes through it (71), tree clean, external dir untouched ---------------------------------
|
|
1002
|
+
FX="$(make_repo s-symlink-server)"
|
|
1003
|
+
assert_fixture_dir "$FX"
|
|
1004
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
1005
|
+
mkdir -p "$TMPROOT/ext-server"
|
|
1006
|
+
printf 'export const X = 1;\n' > "$TMPROOT/ext-server/config.ts"
|
|
1007
|
+
rm -rf -- "$FX/apps/web-platform/server"
|
|
1008
|
+
ln -s "$TMPROOT/ext-server" "$FX/apps/web-platform/server"
|
|
1009
|
+
git -C "$FX" add -A && git -C "$FX" commit -q -m "server as symlink" && git -C "$FX" update-ref refs/remotes/origin/main HEAD
|
|
1010
|
+
run_gen "$FX" s-symlink-server
|
|
1011
|
+
cases=$((cases + 1))
|
|
1012
|
+
if [[ "$RC" == "71" ]] && grep -qF 'server is a symlink' "$TMPROOT/s-symlink-server.out"; then
|
|
1013
|
+
ok "S-symlink-server: probes are never written through a symlinked server/ (71, reason on stdout)"
|
|
1014
|
+
else
|
|
1015
|
+
bad "S-symlink-server: rc=$RC (want 71) stdout: $(grep -F FAILED "$TMPROOT/s-symlink-server.out" | head -1)"
|
|
1016
|
+
fi
|
|
1017
|
+
cases=$((cases + 1))
|
|
1018
|
+
if [[ -z "$(porcelain "$FX")" && "$(ls -A "$TMPROOT/ext-server" | tr '\n' ' ')" == "config.ts " ]]; then
|
|
1019
|
+
ok "S-symlink-server: tree clean and the external directory holds only its own file"
|
|
1020
|
+
else
|
|
1021
|
+
bad "S-symlink-server: porcelain=[$(porcelain "$FX" | tr '\n' ' ')] ext=[$(ls -A "$TMPROOT/ext-server" | tr '\n' ' ')]"
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
# --- S-lock: a second run while another is in progress -> 69 naming the pid, nothing written; a
|
|
1025
|
+
# STALE lock (owner gone) is taken over and released after the run ------------------------------
|
|
1026
|
+
FX="$(make_repo s-lock)"
|
|
1027
|
+
assert_fixture_dir "$FX"
|
|
1028
|
+
point_node_modules "$FX" "$STUB_CLEAN"
|
|
1029
|
+
LOCK_DIR="$FX/.git/constraint-scaffold.lock"
|
|
1030
|
+
mkdir -p "$LOCK_DIR"
|
|
1031
|
+
printf '%s\n' "$$" > "$LOCK_DIR/pid" # this suite's own pid: alive for the whole run
|
|
1032
|
+
run_gen "$FX" s-lock-held
|
|
1033
|
+
cases=$((cases + 1))
|
|
1034
|
+
if [[ "$RC" == "69" ]] && grep -qF "FAILED (69): another constraint-scaffold run (pid $$) is in progress" "$TMPROOT/s-lock-held.out" && [[ -z "$(porcelain "$FX")" ]]; then
|
|
1035
|
+
ok "S-lock: a live lock is a 69 naming the owner pid on STDOUT, nothing written"
|
|
1036
|
+
else
|
|
1037
|
+
bad "S-lock: rc=$RC (want 69) stdout: $(grep -F FAILED "$TMPROOT/s-lock-held.out" | head -1) porcelain=[$(porcelain "$FX" | tr '\n' ' ')]"
|
|
1038
|
+
fi
|
|
1039
|
+
cases=$((cases + 1))
|
|
1040
|
+
if [[ -d "$LOCK_DIR" && "$(cat "$LOCK_DIR/pid")" == "$$" ]]; then
|
|
1041
|
+
ok "S-lock: the refused run left the live lock in place (it was not its to release)"
|
|
1042
|
+
else
|
|
1043
|
+
bad "S-lock: the refused run removed or rewrote a lock it did not own"
|
|
1044
|
+
fi
|
|
1045
|
+
sleep 0.01 & DEAD_PID=$!; wait "$DEAD_PID" 2>/dev/null || true
|
|
1046
|
+
printf '%s\n' "$DEAD_PID" > "$LOCK_DIR/pid" # a pid that has exited: a stale lock
|
|
1047
|
+
run_gen "$FX" s-lock-stale
|
|
1048
|
+
cases=$((cases + 1))
|
|
1049
|
+
if [[ "$RC" == "0" && ! -e "$LOCK_DIR" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/s-lock-stale.out"; then
|
|
1050
|
+
ok "S-lock: a stale lock is taken over, the run completes, and the lock is released"
|
|
1051
|
+
else
|
|
1052
|
+
bad "S-lock: stale-lock run rc=$RC lock_present=$([[ -e "$LOCK_DIR" ]] && echo yes || echo no)"
|
|
1053
|
+
fi
|
|
1054
|
+
|
|
1055
|
+
# --- locate (or install) the dependency-cruiser binary -----------------------
|
|
1056
|
+
# Fast path: the installed web-platform binary (present locally + in the
|
|
1057
|
+
# test-webplat CI shard). Fallback: the test-scripts shard has node+npm but no
|
|
1058
|
+
# web-platform deps, so install dependency-cruiser on demand into a temp dir.
|
|
1059
|
+
DEPCRUISE="$APP/node_modules/.bin/depcruise"
|
|
1060
|
+
if [[ ! -x "$DEPCRUISE" ]]; then
|
|
1061
|
+
echo "# dependency-cruiser not installed in apps/web-platform — installing on demand..." >&2
|
|
1062
|
+
# Seed an empty package.json so npm installs INTO this temp dir. Without it,
|
|
1063
|
+
# `npm install` in a bare dir walks UP the tree, finds a parent project, reports
|
|
1064
|
+
# "up to date", and installs nothing → binary missing (env-dependent fragility).
|
|
1065
|
+
printf '{"name":"constraint-scaffold-test","private":true}\n' > "$TMPROOT/package.json"
|
|
1066
|
+
# typescript is REQUIRED alongside dependency-cruiser: depcruise cannot parse
|
|
1067
|
+
# .ts/.tsx (nor honor tsConfig alias resolution + tsPreCompilationDeps) without
|
|
1068
|
+
# the TS compiler. Omitting it makes depcruise emit ZERO edges from the .tsx
|
|
1069
|
+
# fixtures → every positive-detection assertion silently fails (the scripts-shard
|
|
1070
|
+
# CI failure on PR #5770). The non-vacuity guard below fails loud if this regresses.
|
|
1071
|
+
( cd "$TMPROOT" && npm install --no-audit --no-fund dependency-cruiser@^16 typescript >/dev/null 2>&1 ) \
|
|
1072
|
+
|| { echo "fatal: could not install dependency-cruiser + typescript"; exit 2; }
|
|
1073
|
+
DEPCRUISE="$TMPROOT/node_modules/.bin/depcruise"
|
|
1074
|
+
[[ -x "$DEPCRUISE" ]] || { echo "fatal: dependency-cruiser binary still missing after install"; exit 2; }
|
|
1075
|
+
fi
|
|
1076
|
+
NODE_MODULES="$(cd "$(dirname "$DEPCRUISE")/.." && pwd)"
|
|
1077
|
+
REAL_VERSION="$(node -p 'require(process.argv[1] + "/dependency-cruiser/package.json").version' "$NODE_MODULES" 2>/dev/null || true)"
|
|
1078
|
+
|
|
1079
|
+
# --- Toolchain probe: can this depcruise actually PARSE the .tsx fixtures? -----
|
|
1080
|
+
# Same probe as boundary.test.sh: when depcruise parses ZERO modules from a fixture that
|
|
1081
|
+
# demonstrably contains .tsx client files, the TOOLCHAIN is the problem, not the gate — SKIP
|
|
1082
|
+
# cleanly (exit 0). Everything above this point is toolchain-FREE and carries its own floor.
|
|
1083
|
+
PROBE_FX="$(make_repo toolchain-probe --populated)"
|
|
1084
|
+
assert_fixture_dir "$PROBE_FX"
|
|
1085
|
+
point_node_modules "$PROBE_FX" "$NODE_MODULES"
|
|
1086
|
+
cp "$REF/depcruise-config.template" "$PROBE_FX/apps/web-platform/.dependency-cruiser.cjs"
|
|
1087
|
+
PARSED_COMPONENTS="$( cd "$PROBE_FX/apps/web-platform" && "$DEPCRUISE" --config .dependency-cruiser.cjs --output-type json components server 2>/dev/null | node -e '
|
|
1088
|
+
let s=""; process.stdin.on("data",d=>s+=d); process.stdin.on("end",()=>{
|
|
1089
|
+
let n=0; try { const j=JSON.parse(s);
|
|
1090
|
+
for (const m of j.modules||[]) if (/^components\//.test(m.source||"")) n++;
|
|
1091
|
+
} catch(e){ n=0; } process.stdout.write(String(n)); });' 2>/dev/null )"
|
|
1092
|
+
if ! [[ "$PARSED_COMPONENTS" =~ ^[0-9]+$ ]] || [[ "$PARSED_COMPONENTS" -lt 1 ]]; then
|
|
1093
|
+
echo "SKIP: depcruise parsed 0 component modules from the .tsx fixtures — toolchain"
|
|
1094
|
+
echo " unavailable in this shard. The bite-proof is validated end-to-end by the"
|
|
1095
|
+
echo " real-toolchain cases in the test-webplat shard + local runs."
|
|
1096
|
+
# This `exit 0` is a SECOND clean-exit door, so the trailer's conservation check and floor
|
|
1097
|
+
# are repeated here rather than referenced: a guarantee that only guards one of two exits is
|
|
1098
|
+
# not a guarantee. Everything above this point is toolchain-FREE and runs in EVERY shard, so
|
|
1099
|
+
# it carries its own floor. Conservation FIRST (ADR-193 #4), both reported directly.
|
|
1100
|
+
if [[ $((pass + fail)) -ne "$cases" ]]; then
|
|
1101
|
+
printf '\n[FATAL] accounting: pass+fail (%d) != cases (%d).\n' "$((pass + fail))" "$cases" >&2
|
|
1102
|
+
if [[ $((pass + fail)) -lt "$cases" ]]; then
|
|
1103
|
+
printf ' An assertion was counted but its verdict was not recorded — that is what a neutered ok()/bad() looks like.\n' >&2
|
|
1104
|
+
else
|
|
1105
|
+
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
|
|
1106
|
+
fi
|
|
1107
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
1108
|
+
exit 1
|
|
1109
|
+
fi
|
|
1110
|
+
TOOLCHAIN_FREE_MIN_ASSERTIONS=76
|
|
1111
|
+
if [[ "$cases" -lt "$TOOLCHAIN_FREE_MIN_ASSERTIONS" ]]; then
|
|
1112
|
+
printf '\n[FATAL] anti-vacuity floor (toolchain-free half): only %d assertion(s) ran, expected >= %d.\n' \
|
|
1113
|
+
"$cases" "$TOOLCHAIN_FREE_MIN_ASSERTIONS" >&2
|
|
1114
|
+
printf ' Arms were deleted or skipped before the toolchain probe; skipping the depcruise half is sanctioned, asserting nothing at all is not.\n' >&2
|
|
1115
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
1116
|
+
exit 1
|
|
1117
|
+
fi
|
|
1118
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions, SKIPPED at the toolchain probe)"
|
|
1119
|
+
[[ "$fail" -eq 0 ]]
|
|
1120
|
+
exit
|
|
1121
|
+
fi
|
|
1122
|
+
|
|
1123
|
+
# =============================================================================================
|
|
1124
|
+
# Segment 3 — REAL TOOLCHAIN.
|
|
1125
|
+
# =============================================================================================
|
|
1126
|
+
echo "# --- real toolchain (dependency-cruiser $REAL_VERSION) ---"
|
|
1127
|
+
|
|
1128
|
+
# --- C1: default mode, full run against the real depcruise ----------------------------------------
|
|
1129
|
+
FX="$(make_repo c1-real --claude)"
|
|
1130
|
+
assert_fixture_dir "$FX"
|
|
1131
|
+
point_node_modules "$FX" "$NODE_MODULES"
|
|
1132
|
+
run_gen "$FX" c1
|
|
1133
|
+
cases=$((cases + 1))
|
|
1134
|
+
if [[ "$RC" == "0" ]]; then
|
|
1135
|
+
ok "C1: default mode exits 0 with the real toolchain"
|
|
1136
|
+
else
|
|
1137
|
+
bad "C1: expected 0, got rc=$RC (stdout: $(tail -3 "$TMPROOT/c1.out" | tr '\n' ' ')) (stderr: $(tail -3 "$TMPROOT/c1.err" | tr '\n' ' '))"
|
|
1138
|
+
fi
|
|
1139
|
+
# C1-color: the same run with FORCE_COLOR=1 in the environment (some CI images, some shells set
|
|
1140
|
+
# it) — the real depcruise decorates its `error <rule>:` lines; the runner's NO_COLOR=1 plus the
|
|
1141
|
+
# scaffold's strip must keep this a pass, never a 72 that rolls a correct install back.
|
|
1142
|
+
FX_COLOR="$(make_repo c1-color)"
|
|
1143
|
+
assert_fixture_dir "$FX_COLOR"
|
|
1144
|
+
point_node_modules "$FX_COLOR" "$NODE_MODULES"
|
|
1145
|
+
FORCE_COLOR=1 run_gen "$FX_COLOR" c1-color
|
|
1146
|
+
cases=$((cases + 1))
|
|
1147
|
+
if [[ "$RC" == "0" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/c1-color.out"; then
|
|
1148
|
+
ok "C1-color: FORCE_COLOR=1 in the environment still proves the bite (rc 0, verdict line)"
|
|
1149
|
+
else
|
|
1150
|
+
bad "C1-color: rc=$RC verdict=$(grep -cE "$VERDICT_RE" "$TMPROOT/c1-color.out" || true) (stdout: $(grep -F FAILED "$TMPROOT/c1-color.out" | head -1))"
|
|
1151
|
+
fi
|
|
1152
|
+
C1_VERDICT="$(grep -E "$VERDICT_RE" "$TMPROOT/c1.out" | head -1)"
|
|
1153
|
+
cases=$((cases + 1))
|
|
1154
|
+
if [[ -n "$C1_VERDICT" && "$C1_VERDICT" == *"${RULE_DIRECT}@direct"* && "$C1_VERDICT" == *"${RULE_TRANSITIVE}@via-hop"* ]]; then
|
|
1155
|
+
ok "C1: verdict line names both rules on their edges: $C1_VERDICT"
|
|
1156
|
+
else
|
|
1157
|
+
bad "C1: verdict line missing or malformed: $(grep -F 'bite-proof' "$TMPROOT/c1.out" | head -1)"
|
|
1158
|
+
fi
|
|
1159
|
+
C1_VER="${C1_VERDICT##*depcruise=}"
|
|
1160
|
+
cases=$((cases + 1))
|
|
1161
|
+
if [[ -n "$REAL_VERSION" && "$C1_VER" == "$REAL_VERSION" && "$C1_VER" != "0.0.0-stub" && "$C1_VER" =~ ^[0-9]+\. ]]; then
|
|
1162
|
+
ok "C1: depcruise=$C1_VER equals the real dependency-cruiser/package.json version (not the stub)"
|
|
1163
|
+
else
|
|
1164
|
+
bad "C1: verdict version '$C1_VER' != real '$REAL_VERSION' (or looks like the stub)"
|
|
1165
|
+
fi
|
|
1166
|
+
cases=$((cases + 1))
|
|
1167
|
+
if [[ "$(count_marker "$FX/apps/web-platform/server/README.md" "$README_START")" == "1" && "$(count_marker "$FX/CLAUDE.md" "$POINTER_MARKER")" == "1" ]]; then
|
|
1168
|
+
ok "C1: README block once, pointer once"
|
|
1169
|
+
else
|
|
1170
|
+
bad "C1: readme-start=$(count_marker "$FX/apps/web-platform/server/README.md" "$README_START") pointer=$(count_marker "$FX/CLAUDE.md" "$POINTER_MARKER")"
|
|
1171
|
+
fi
|
|
1172
|
+
cases=$((cases + 1))
|
|
1173
|
+
if [[ -f "$FX/apps/web-platform/.dependency-cruiser-known-violations.json" ]] && node -e 'const b=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")); process.exit(Array.isArray(b)?0:1)' "$FX/apps/web-platform/.dependency-cruiser-known-violations.json"; then
|
|
1174
|
+
ok "C1: baseline present and a JSON array"
|
|
1175
|
+
else
|
|
1176
|
+
bad "C1: baseline missing or not a JSON array"
|
|
1177
|
+
fi
|
|
1178
|
+
cases=$((cases + 1))
|
|
1179
|
+
if [[ "$(worktree_count "$FX")" == "1" && ! -e "$FX/apps/web-platform/$PROBE_DIR" && ! -e "$FX/apps/web-platform/server/__constraint_scaffold_bite_probe__.ts" ]]; then
|
|
1180
|
+
ok "C1: worktree list clean; no probe file in the founder tree"
|
|
1181
|
+
else
|
|
1182
|
+
bad "C1: worktrees=$(worktree_count "$FX") probe residue=$(ls -d "$FX/apps/web-platform/$PROBE_DIR" "$FX/apps/web-platform/server/__constraint_scaffold_bite_probe__.ts" 2>/dev/null | tr '\n' ' ')"
|
|
1183
|
+
fi
|
|
1184
|
+
# The emitted gate is green on the founder tree afterwards (the runner's own invocation).
|
|
1185
|
+
C1_GATE_RC=0
|
|
1186
|
+
CONSTRAINT_GATES_DIR="$FX/apps/web-platform" bash "$FX/apps/web-platform/scripts/constraint-gates.sh" >"$TMPROOT/c1-gate.log" 2>&1 || C1_GATE_RC=$?
|
|
1187
|
+
cases=$((cases + 1))
|
|
1188
|
+
if [[ "$C1_GATE_RC" -eq 0 ]]; then
|
|
1189
|
+
ok "C1: the emitted runner is green on the founder tree after install"
|
|
1190
|
+
else
|
|
1191
|
+
bad "C1: emitted runner rc=$C1_GATE_RC on the founder tree: $(tail -2 "$TMPROOT/c1-gate.log" | tr '\n' ' ')"
|
|
1192
|
+
fi
|
|
1193
|
+
|
|
1194
|
+
# --- C8: populated tree (harness row d) ----------------------------------------------------------
|
|
1195
|
+
# An unrelated "use client" module with a TYPE-ONLY server import, plus a client module
|
|
1196
|
+
# value-importing a VALUE-SAFE server module: the direct rule baselines it (one `dependency`
|
|
1197
|
+
# entry captured at the merge-base), the transitive rule excludes it via pathNot, and the bite
|
|
1198
|
+
# must still report pass -> fail(both) -> pass on top of that baseline.
|
|
1199
|
+
FX="$(make_repo c8-populated --populated)"
|
|
1200
|
+
assert_fixture_dir "$FX"
|
|
1201
|
+
point_node_modules "$FX" "$NODE_MODULES"
|
|
1202
|
+
run_gen "$FX" c8
|
|
1203
|
+
cases=$((cases + 1))
|
|
1204
|
+
if [[ "$RC" == "0" ]] && grep -qE "$VERDICT_RE" "$TMPROOT/c8.out"; then
|
|
1205
|
+
ok "C8: populated tree -> rc 0 with the verdict line"
|
|
1206
|
+
else
|
|
1207
|
+
bad "C8: rc=$RC verdict=$(grep -cE "$VERDICT_RE" "$TMPROOT/c8.out" || true) (stdout: $(grep -F FAILED "$TMPROOT/c8.out" | head -1))"
|
|
1208
|
+
fi
|
|
1209
|
+
C8_DEP_CT="$(node -e 'const b=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String((Array.isArray(b)?b:[]).filter(e=>e&&e.type==="dependency").length))' "$FX/apps/web-platform/.dependency-cruiser-known-violations.json" 2>/dev/null || echo -1)"
|
|
1210
|
+
C8_REACH_CT="$(node -e 'const b=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8")); process.stdout.write(String((Array.isArray(b)?b:[]).filter(e=>e&&e.type==="reachability").length))' "$FX/apps/web-platform/.dependency-cruiser-known-violations.json" 2>/dev/null || echo -1)"
|
|
1211
|
+
cases=$((cases + 1))
|
|
1212
|
+
if [[ "$C8_DEP_CT" == "1" && "$C8_REACH_CT" == "0" ]]; then
|
|
1213
|
+
ok "C8: baseline carries exactly one direct value-safe entry and zero reachability entries"
|
|
1214
|
+
else
|
|
1215
|
+
bad "C8: baseline dependency entries=$C8_DEP_CT reachability entries=$C8_REACH_CT (want 1 / 0)"
|
|
1216
|
+
fi
|
|
1217
|
+
|
|
1218
|
+
# --- C-e: committed CLAUDE.md + server/README.md (harness row e — the P0 fixture) ----------------
|
|
1219
|
+
FX="$(make_repo ce-instructions --claude --readme)"
|
|
1220
|
+
assert_fixture_dir "$FX"
|
|
1221
|
+
point_node_modules "$FX" "$NODE_MODULES"
|
|
1222
|
+
run_gen "$FX" ce
|
|
1223
|
+
cases=$((cases + 1))
|
|
1224
|
+
if [[ "$RC" == "0" ]]; then
|
|
1225
|
+
ok "C-e: default mode succeeds with a committed CLAUDE.md and server/README.md (no 67)"
|
|
1226
|
+
else
|
|
1227
|
+
bad "C-e: rc=$RC (stderr: $(tail -2 "$TMPROOT/ce.err" | tr '\n' ' '))"
|
|
1228
|
+
fi
|
|
1229
|
+
cases=$((cases + 1))
|
|
1230
|
+
if [[ "$(count_marker "$FX/apps/web-platform/server/README.md" "$README_START")" == "1" && "$(count_marker "$FX/CLAUDE.md" "$POINTER_MARKER")" == "1" ]] \
|
|
1231
|
+
&& grep -qF 'Prior README prose.' "$FX/apps/web-platform/server/README.md" && grep -qF 'Prior instructions.' "$FX/CLAUDE.md"; then
|
|
1232
|
+
ok "C-e: appended once to each, prior content intact"
|
|
1233
|
+
else
|
|
1234
|
+
bad "C-e: readme-start=$(count_marker "$FX/apps/web-platform/server/README.md" "$README_START") pointer=$(count_marker "$FX/CLAUDE.md" "$POINTER_MARKER")"
|
|
1235
|
+
fi
|
|
1236
|
+
# Both committed files lacked no trailing newline; check the append started on its own line.
|
|
1237
|
+
cases=$((cases + 1))
|
|
1238
|
+
if grep -qE "^$README_START\$" "$FX/apps/web-platform/server/README.md" && grep -qE "^The client/server import boundary.*$POINTER_MARKER\$" "$FX/CLAUDE.md"; then
|
|
1239
|
+
ok "C-e: each appended block starts on its own line"
|
|
1240
|
+
else
|
|
1241
|
+
bad "C-e: an append landed mid-line"
|
|
1242
|
+
fi
|
|
1243
|
+
# Re-running refresh after committing must be a no-op on both prose files.
|
|
1244
|
+
git -C "$FX" add -A
|
|
1245
|
+
git -C "$FX" commit -q -m "install gate"
|
|
1246
|
+
git -C "$FX" update-ref refs/remotes/origin/main HEAD
|
|
1247
|
+
cp "$FX/CLAUDE.md" "$TMPROOT/ce-claude.before"
|
|
1248
|
+
run_gen "$FX" ce-refresh --refresh-baseline
|
|
1249
|
+
cases=$((cases + 1))
|
|
1250
|
+
if [[ "$RC" == "0" ]] && cmp -s "$FX/CLAUDE.md" "$TMPROOT/ce-claude.before" && grep -qE "$VERDICT_RE" "$TMPROOT/ce-refresh.out"; then
|
|
1251
|
+
ok "C-e: --refresh-baseline afterwards is rc 0 with the verdict and leaves CLAUDE.md untouched"
|
|
1252
|
+
else
|
|
1253
|
+
bad "C-e: refresh rc=$RC claude-changed=$(cmp -s "$FX/CLAUDE.md" "$TMPROOT/ce-claude.before" && echo no || echo YES)"
|
|
1254
|
+
fi
|
|
1255
|
+
|
|
1256
|
+
# --- C-warn: committed config with the transitive rule at severity "warn" -> 72 (real) -----------
|
|
1257
|
+
FX="$(make_repo cwarn)"
|
|
1258
|
+
assert_fixture_dir "$FX"
|
|
1259
|
+
point_node_modules "$FX" "$NODE_MODULES"
|
|
1260
|
+
mkdir -p "$FX/apps/web-platform/scripts"
|
|
1261
|
+
node -e '
|
|
1262
|
+
const fs=require("fs"); let s=fs.readFileSync(process.argv[1],"utf8");
|
|
1263
|
+
const i=s.indexOf("name: \"no-client-to-server-secret-transitive\"");
|
|
1264
|
+
const j=s.indexOf("severity: \"error\"", i);
|
|
1265
|
+
if (i<0||j<0) process.exit(3);
|
|
1266
|
+
s=s.slice(0,j)+"severity: \"warn\""+s.slice(j+"severity: \"error\"".length);
|
|
1267
|
+
fs.writeFileSync(process.argv[2], s);' "$REF/depcruise-config.template" "$FX/apps/web-platform/.dependency-cruiser.cjs"
|
|
1268
|
+
cp "$REF/shared-runner.template" "$FX/apps/web-platform/scripts/constraint-gates.sh"
|
|
1269
|
+
chmod +x "$FX/apps/web-platform/scripts/constraint-gates.sh"
|
|
1270
|
+
printf '[]\n' > "$FX/apps/web-platform/.dependency-cruiser-known-violations.json"
|
|
1271
|
+
git -C "$FX" add -A
|
|
1272
|
+
git -C "$FX" commit -q -m "gate with the transitive rule softened to warn"
|
|
1273
|
+
git -C "$FX" update-ref refs/remotes/origin/main HEAD
|
|
1274
|
+
run_gen "$FX" cwarn --refresh-baseline
|
|
1275
|
+
cases=$((cases + 1))
|
|
1276
|
+
if [[ "$RC" == "72" ]]; then
|
|
1277
|
+
ok "C-warn: a 'warn' transitive line is rejected by the '^\\s*error ' anchor -> 72 with the real toolchain"
|
|
1278
|
+
else
|
|
1279
|
+
bad "C-warn: expected 72, got rc=$RC (stdout: $(grep -F 'bite-proof' "$TMPROOT/cwarn.out" | head -1))"
|
|
1280
|
+
fi
|
|
1281
|
+
cases=$((cases + 1))
|
|
1282
|
+
if grep -qE '^\s*warn no-client-to-server-secret-transitive: .*via-hop\.tsx' "$TMPROOT/cwarn.out"; then
|
|
1283
|
+
ok "C-warn: the echoed log tail shows the 'warn' call-form on via-hop.tsx (the real output shape)"
|
|
1284
|
+
else
|
|
1285
|
+
bad "C-warn: log tail does not show the warn line"
|
|
1286
|
+
fi
|
|
1287
|
+
cases=$((cases + 1))
|
|
1288
|
+
if grep -qF 'refresh mode' "$TMPROOT/cwarn.out" && [[ -f "$FX/apps/web-platform/.dependency-cruiser.cjs" && -f "$FX/apps/web-platform/scripts/constraint-gates.sh" ]]; then
|
|
1289
|
+
ok "C-warn: refresh mode removes nothing (artifacts are committed) and says the baseline was rewritten"
|
|
1290
|
+
else
|
|
1291
|
+
bad "C-warn: refresh-mode failure message or artifacts wrong"
|
|
1292
|
+
fi
|
|
1293
|
+
cases=$((cases + 1))
|
|
1294
|
+
if [[ "$(worktree_count "$FX")" == "1" ]]; then
|
|
1295
|
+
ok "C-warn: worktree removed after the failed bite"
|
|
1296
|
+
else
|
|
1297
|
+
bad "C-warn: $(worktree_count "$FX") worktrees registered"
|
|
1298
|
+
fi
|
|
1299
|
+
|
|
1300
|
+
echo "---"
|
|
1301
|
+
|
|
1302
|
+
# --- Accounting conservation (ADR-193 #3) --------------------------------------------------
|
|
1303
|
+
# Ordered BEFORE the floor (ADR-193 #4): a neutered bad()/ok() deflates the verdict counters, so
|
|
1304
|
+
# the floor below would ALSO trip and would report the misleading "arms were deleted". This says
|
|
1305
|
+
# "a verdict was discarded" instead. Reported with `printf >&2` + `exit 1` DIRECTLY, never
|
|
1306
|
+
# through bad(): a check that reports by calling the verdict helper increments the very counter
|
|
1307
|
+
# the exit status reads, so neutering bad() silences the rows AND the check meant to notice the
|
|
1308
|
+
# silence. Every assertion records exactly one verdict, so pass+fail MUST equal cases; because
|
|
1309
|
+
# `cases` moves at the CALL SITE and not inside the verdict helpers, the identity is a real
|
|
1310
|
+
# constraint rather than a tautology. The literal `[FATAL] accounting` is load-bearing —
|
|
1311
|
+
# guard-vacuity-floor's ARM 10 builds its conservation population by grepping that exact string.
|
|
1312
|
+
if [[ $((pass + fail)) -ne "$cases" ]]; then
|
|
1313
|
+
printf '\n[FATAL] accounting: pass+fail (%d) != cases (%d).\n' "$((pass + fail))" "$cases" >&2
|
|
1314
|
+
if [[ $((pass + fail)) -lt "$cases" ]]; then
|
|
1315
|
+
printf ' An assertion was counted but its verdict was not recorded — that is what a neutered ok()/bad() looks like.\n' >&2
|
|
1316
|
+
else
|
|
1317
|
+
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
|
|
1318
|
+
fi
|
|
1319
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
1320
|
+
exit 1
|
|
1321
|
+
fi
|
|
1322
|
+
|
|
1323
|
+
# --- Anti-vacuity floor (ADR-193 #1) -------------------------------------------------------
|
|
1324
|
+
# Reads the INDEPENDENT `cases` counter, and reports with `printf >&2` + `exit 1` DIRECTLY.
|
|
1325
|
+
# A harness that silently asserted nothing (a fixture generator that no-ops, an editing slip that
|
|
1326
|
+
# drops a block) would otherwise print a clean smaller total and exit 0. See MIN_ASSERTIONS above
|
|
1327
|
+
# for how the number is derived and when to ratchet it.
|
|
1328
|
+
MIN_ASSERTIONS=94
|
|
1329
|
+
if [[ "$cases" -lt "$MIN_ASSERTIONS" ]]; then
|
|
1330
|
+
printf '\n[FATAL] anti-vacuity floor: only %d assertion(s) ran, expected >= %d.\n' \
|
|
1331
|
+
"$cases" "$MIN_ASSERTIONS" >&2
|
|
1332
|
+
printf ' Arms were deleted or skipped; a green run here would be a coverage loss.\n' >&2
|
|
1333
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
1334
|
+
exit 1
|
|
1335
|
+
fi
|
|
1336
|
+
|
|
1337
|
+
echo "bite-proof.test.sh: $pass passed, $fail failed ($cases assertions)"
|
|
1338
|
+
[[ "$fail" -eq 0 ]]
|