@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,780 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Guard 1 (#8377, ADR-235) — resolve-regenerable-conflicts.sh fails CLOSED.
|
|
3
|
+
#
|
|
4
|
+
# ── THE PROPERTY ────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
# The resolver commits a merge ONLY when every conflicted path is a CONTENT conflict on a
|
|
6
|
+
# resolvable path, the renderer rendered each one from the merged tree's tracked sources, and
|
|
7
|
+
# nothing else moved. On any other input the tree is byte-identical to entry and nothing is
|
|
8
|
+
# committed. Every non-happy row asserts BOTH a non-zero exit AND an unchanged tree fingerprint.
|
|
9
|
+
#
|
|
10
|
+
# ── THE ARM CONTRACT (ADR-235 amendment 2026-09-23) ─────────────────────────────────────────
|
|
11
|
+
# An arm is an argv PREFIX; the resolver appends `--root <staging> --out <file>`. <staging>
|
|
12
|
+
# holds ONLY the tracked LikeC4 sources copied out of the merged tree's objects; the arm writes
|
|
13
|
+
# its artifact to <file>, never to the worktree. The stubs below refuse any other argv shape.
|
|
14
|
+
#
|
|
15
|
+
# ── WHY THE SUT RUNS FROM A SANDBOX PLUGIN COPY ─────────────────────────────────────────────
|
|
16
|
+
# The production arm runs render-c4-model.sh from BESIDE THE RESOLVER. A stub is injected the
|
|
17
|
+
# way a real install is laid out: a copy of the SUT in <root>/scripts/ with the stub as its
|
|
18
|
+
# sibling and a <root>/.claude-plugin/plugin.json. Copies are taken from $SUT at run time, so a
|
|
19
|
+
# mutation battery that edits $SUT is measured by every row; the ratchet rows read $SUT itself.
|
|
20
|
+
#
|
|
21
|
+
# tree_fp = HEAD + porcelain status (incl. untracked) + every index entry + MERGE_HEAD presence.
|
|
22
|
+
export TMPDIR="${TMPDIR:-/var/tmp}"
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
SUT="$SCRIPT_DIR/resolve-regenerable-conflicts.sh"
|
|
28
|
+
|
|
29
|
+
passes=0; fails=0; CASES_RUN=0; FAILED=()
|
|
30
|
+
pass() { passes=$((passes + 1)); echo " PASS: $1"; }
|
|
31
|
+
fail() { fails=$((fails + 1)); FAILED+=("$1"); echo " FAIL: $1" >&2; }
|
|
32
|
+
|
|
33
|
+
_iv_p="$passes"; _iv_f="$fails"; _iv_n="${#FAILED[@]}"
|
|
34
|
+
{ pass "self-test"; fail "self-test"; } >/dev/null 2>&1
|
|
35
|
+
if [[ "$passes" -ne $((_iv_p + 1)) || "$fails" -ne $((_iv_f + 1)) || "${#FAILED[@]}" -ne $((_iv_n + 1)) ]]; then
|
|
36
|
+
printf '[FATAL] instrument self-test: the verdict helpers did not both record\n' >&2; exit 1
|
|
37
|
+
fi
|
|
38
|
+
passes=0; fails=0; FAILED=(); CASES_RUN=0
|
|
39
|
+
|
|
40
|
+
[[ -f "$SUT" ]] || { echo "[FATAL] SUT not found at $SUT" >&2; exit 1; }
|
|
41
|
+
|
|
42
|
+
assert_fixture_dir() {
|
|
43
|
+
case "${1-}" in
|
|
44
|
+
"") printf 'FATAL: fixture dir is EMPTY; git -C "" would operate on %s\n' "$PWD" >&2; exit 2 ;;
|
|
45
|
+
*/../*|*/..) printf 'FATAL: fixture dir %s contains ..; refusing\n' "$1" >&2; exit 2 ;;
|
|
46
|
+
/proc/*|/sys/*|/dev/*) printf 'FATAL: fixture dir %s is a synthetic-fs path; refusing\n' "$1" >&2; exit 2 ;;
|
|
47
|
+
/|//|/.) printf 'FATAL: fixture dir resolves to the filesystem root; refusing\n' >&2; exit 2 ;;
|
|
48
|
+
/*) : ;;
|
|
49
|
+
*) printf 'FATAL: fixture dir %s is RELATIVE; refusing\n' "$1" >&2; exit 2 ;;
|
|
50
|
+
esac
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
SANDBOX="$(mktemp -d)"; assert_fixture_dir "$SANDBOX"
|
|
54
|
+
SANDBOX="$(cd "$SANDBOX" && pwd -P)"
|
|
55
|
+
trap 'rm -rf "$SANDBOX"' EXIT
|
|
56
|
+
# The resolver's staging dir lives under XDG_CACHE_HOME; keep it inside the sandbox.
|
|
57
|
+
export XDG_CACHE_HOME="$SANDBOX/xdg-cache"
|
|
58
|
+
# The shared fixture env: a prefix sweep of every inherited GIT_* (so a hook-exported GIT_DIR cannot
|
|
59
|
+
# aim these writes at the caller's repo), a discovery ceiling, hermetic config and a pinned
|
|
60
|
+
# identity. Load-bearing here beyond hygiene: scripts/test-all.sh arms core.hooksPath through
|
|
61
|
+
# GIT_CONFIG_* for the whole run, and without the sweep no fixture hook would run in CI.
|
|
62
|
+
# shellcheck source=../test/lib/git-fixture-env.sh
|
|
63
|
+
source "$SCRIPT_DIR/../test/lib/git-fixture-env.sh"
|
|
64
|
+
git_fixture_env "$SANDBOX" || { printf '[FATAL] git_fixture_env refused %s\n' "$SANDBOX" >&2; exit 1; }
|
|
65
|
+
|
|
66
|
+
# Control: a fixture repo's own .git/hooks must RUN. Several rows (a commit hook that rewrites the
|
|
67
|
+
# merge, a signal sent from inside git commit) pass vacuously when hooks are silently disabled —
|
|
68
|
+
# which an inherited `core.hooksPath` in GIT_CONFIG_* does, and outranks the fixture's local config.
|
|
69
|
+
_hk="$SANDBOX/hook-control"; assert_fixture_dir "$_hk"
|
|
70
|
+
git init -q "$_hk" && mkdir -p "$_hk/.git/hooks"
|
|
71
|
+
printf '#!/bin/sh
|
|
72
|
+
: > "%s/hook.ran"
|
|
73
|
+
' "$SANDBOX" > "$_hk/.git/hooks/pre-commit"; chmod +x "$_hk/.git/hooks/pre-commit"
|
|
74
|
+
git -C "$_hk" -c user.email=t@t -c user.name=t commit -q --allow-empty -m control >/dev/null 2>&1
|
|
75
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
76
|
+
[[ -e "$SANDBOX/hook.ran" ]] && pass "control: a fixture repo's pre-commit hook runs" \
|
|
77
|
+
|| fail "control: fixture hooks do NOT run (an inherited core.hooksPath?) — every hook row below would pass vacuously"
|
|
78
|
+
|
|
79
|
+
DIAG="knowledge-base/engineering/architecture/diagrams"
|
|
80
|
+
MODEL="$DIAG/model.likec4.json"
|
|
81
|
+
SRC="$DIAG/model.c4"
|
|
82
|
+
SECOND="$DIAG/second.json"
|
|
83
|
+
|
|
84
|
+
_git() { git -C "$1" -c user.email=t@t -c user.name=t "${@:2}"; }
|
|
85
|
+
|
|
86
|
+
tree_fp() {
|
|
87
|
+
local r="$1"
|
|
88
|
+
{
|
|
89
|
+
_git "$r" rev-parse HEAD 2>/dev/null || echo "NOHEAD"
|
|
90
|
+
_git "$r" status --porcelain --untracked-files=all 2>/dev/null
|
|
91
|
+
_git "$r" ls-files -s 2>/dev/null
|
|
92
|
+
[[ -f "$r/.git/MERGE_HEAD" ]] && echo "MERGE_IN_PROGRESS"
|
|
93
|
+
} | git hash-object --stdin
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# ── SANDBOX PLUGIN ROOTS ────────────────────────────────────────────────────────────────────
|
|
97
|
+
# mkplugin <name> <renderer-mode|none> [plugin-name] — echoes the plugin root.
|
|
98
|
+
mkplugin() {
|
|
99
|
+
local root="$SANDBOX/plugins-$1/soleur" mode="$2" pname="${3:-soleur}"; assert_fixture_dir "$root"
|
|
100
|
+
mkdir -p "$root/scripts" "$root/.claude-plugin"
|
|
101
|
+
printf '{\n "name": "%s",\n "description": "fixture"\n}\n' "$pname" > "$root/.claude-plugin/plugin.json"
|
|
102
|
+
cp "$SUT" "$root/scripts/resolve-regenerable-conflicts.sh"
|
|
103
|
+
[[ "$mode" == "none" ]] || _write_renderer "$root/scripts/render-c4-model.sh" "$mode"
|
|
104
|
+
(cd "$root" && pwd -P)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# _write_renderer <file> <mode> — a stub with the real renderer's CONTRACT as the resolver
|
|
108
|
+
# calls it: exactly `--root <existing dir> --out <path>` (exit 64 otherwise). The `ok` output
|
|
109
|
+
# DERIVES from the staged model.c4 and names its author, so the committed artifact shows WHICH
|
|
110
|
+
# tree it saw and WHICH renderer wrote it.
|
|
111
|
+
_write_renderer() {
|
|
112
|
+
local f="$1" mode="$2"; assert_fixture_dir "$f"
|
|
113
|
+
{
|
|
114
|
+
printf '#!/usr/bin/env bash\nset -euo pipefail\n'
|
|
115
|
+
printf '[[ "$#" -eq 4 && "$1" == "--root" && -d "$2" && "$3" == "--out" ]] || { echo "stub renderer: bad argv: $*" >&2; exit 64; }\n'
|
|
116
|
+
printf 'R="$2"; O="$4"\n'
|
|
117
|
+
case "$mode" in
|
|
118
|
+
ok) _emit_ok_body "plugin" ;;
|
|
119
|
+
ok-alt) _emit_ok_body "alt-plugin" ;;
|
|
120
|
+
second) printf 'echo "{\\"second\\":\\"regenerated\\"}" | tee "$O" >/dev/null\n' ;;
|
|
121
|
+
fail) printf 'echo "Invalid model.c4"; echo " Line 3: boom (stub diagnostic)"; echo "ERROR: stub renderer refused" >&2; exit 9\n' ;;
|
|
122
|
+
noop) printf 'exit 0\n' ;;
|
|
123
|
+
markers) printf 'printf "%%s\\n" "<<<<<<< ours" "x" "=======" "y" ">>>>>>> theirs" | tee "$O" >/dev/null\n' ;;
|
|
124
|
+
slow) printf 'touch "%s/slow.started"; sleep 5\n' "$SANDBOX"; _emit_ok_body "plugin" ;;
|
|
125
|
+
listroot) printf '(cd "$R" && find . -mindepth 1 -print) | tee "%s/listroot.txt" >/dev/null\n' "$SANDBOX"; _emit_ok_body "plugin" ;;
|
|
126
|
+
*) echo "[FATAL] unknown renderer mode $mode" >&2; exit 2 ;;
|
|
127
|
+
esac
|
|
128
|
+
true
|
|
129
|
+
} > "$f"
|
|
130
|
+
chmod +x "$f"
|
|
131
|
+
}
|
|
132
|
+
_emit_ok_body() { # <author>
|
|
133
|
+
printf 'f="$R/%s"\n' "$SRC"
|
|
134
|
+
printf 'printf %s "$(head -1 "$f")" "$(tail -1 "$f")" | tee "$O" >/dev/null\n' "'{\"from\":\"%s|%s\",\"by\":\"$1\"}\\n'"
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
# mkrepo <name> [bare|wrapper|decoy] — two branches whose .c4 SOURCE text-merges cleanly while
|
|
138
|
+
# the compiled single-line JSON conflicts (the real shape).
|
|
139
|
+
# bare a self-hosted repo: no scripts/, no plugins/ (AC1).
|
|
140
|
+
# wrapper a committed scripts/regenerate-c4-model.sh: not an arm (must never run).
|
|
141
|
+
# decoy a merged tree shipping its own plugins/soleur renderer + genuine-looking plugin.json.
|
|
142
|
+
# Extra files go in the BASE commit, so they are neither untracked nor a conflict of their own.
|
|
143
|
+
mkrepo() {
|
|
144
|
+
local r="$SANDBOX/$1" shape="${2:-bare}"; assert_fixture_dir "$r"
|
|
145
|
+
mkdir -p "$r/$DIAG"
|
|
146
|
+
printf 'first\nx\nx\nx\nx\nx\nx\nx\nx\nlast\n' > "$r/$SRC"
|
|
147
|
+
printf '{"v":"base"}\n' > "$r/$MODEL"
|
|
148
|
+
printf '{"v":"base2"}\n' > "$r/$SECOND"
|
|
149
|
+
printf 'readme base\n' > "$r/README.md"
|
|
150
|
+
case "$shape" in
|
|
151
|
+
bare) : ;;
|
|
152
|
+
wrapper)
|
|
153
|
+
mkdir -p "$r/scripts"
|
|
154
|
+
printf '#!/usr/bin/env bash\ntouch "%s/WRAPPER_RAN"\n' "$SANDBOX" > "$r/scripts/regenerate-c4-model.sh"
|
|
155
|
+
chmod +x "$r/scripts/regenerate-c4-model.sh" ;;
|
|
156
|
+
decoy)
|
|
157
|
+
mkdir -p "$r/plugins/soleur/scripts" "$r/plugins/soleur/.claude-plugin"
|
|
158
|
+
printf '{\n "name": "soleur"\n}\n' > "$r/plugins/soleur/.claude-plugin/plugin.json"
|
|
159
|
+
printf '#!/usr/bin/env bash\ntouch "%s/DECOY_RAN"\n' "$SANDBOX" > "$r/plugins/soleur/scripts/render-c4-model.sh"
|
|
160
|
+
chmod +x "$r/plugins/soleur/scripts/render-c4-model.sh" ;;
|
|
161
|
+
*) echo "[FATAL] unknown repo shape $shape" >&2; exit 2 ;;
|
|
162
|
+
esac
|
|
163
|
+
_git "$r" init -q -b main
|
|
164
|
+
# Identity in LOCAL config: the SUT runs its own merge and commit, which read config
|
|
165
|
+
# (#8384: 14 rows failed on a CI runner without a global identity).
|
|
166
|
+
git -C "$r" config user.email t@t
|
|
167
|
+
git -C "$r" config user.name t
|
|
168
|
+
_git "$r" add -A >/dev/null; _git "$r" commit -q -m base
|
|
169
|
+
|
|
170
|
+
_git "$r" checkout -q -b feature
|
|
171
|
+
sed -i 's/^last$/last-FEATURE/' "$r/$SRC"
|
|
172
|
+
printf '{"v":"feature"}\n' > "$r/$MODEL"
|
|
173
|
+
_git "$r" add -A >/dev/null; _git "$r" commit -q -m feature
|
|
174
|
+
|
|
175
|
+
_git "$r" checkout -q main
|
|
176
|
+
sed -i 's/^first$/first-MAIN/' "$r/$SRC"
|
|
177
|
+
printf '{"v":"main"}\n' > "$r/$MODEL"
|
|
178
|
+
_git "$r" add -A >/dev/null; _git "$r" commit -q -m main
|
|
179
|
+
|
|
180
|
+
_git "$r" checkout -q feature
|
|
181
|
+
printf '%s' "$r"
|
|
182
|
+
}
|
|
183
|
+
# on_main <repo> <cmd…> — run <cmd…> <repo> on the base side (main), commit, return to feature.
|
|
184
|
+
on_main() {
|
|
185
|
+
local r="$1"; shift
|
|
186
|
+
_git "$r" checkout -q main; "$@" "$r"; _git "$r" add -A >/dev/null; _git "$r" commit -q -m "main extra"
|
|
187
|
+
_git "$r" checkout -q feature
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
P_OK="$(mkplugin ok ok)"
|
|
191
|
+
P_FAIL="$(mkplugin fail fail)"
|
|
192
|
+
P_NOOP="$(mkplugin noop noop)"
|
|
193
|
+
P_MARK="$(mkplugin markers markers)"
|
|
194
|
+
P_SLOW="$(mkplugin slow slow)"
|
|
195
|
+
P_LIST="$(mkplugin listroot listroot)"
|
|
196
|
+
P_SECOND="$(mkplugin second second)"
|
|
197
|
+
P_LONELY="$(mkplugin lonely none)"
|
|
198
|
+
P_NOID="$(mkplugin noid ok not-soleur)"
|
|
199
|
+
P_ALT="$(mkplugin alt ok-alt)"
|
|
200
|
+
for _p in "$P_OK" "$P_FAIL" "$P_NOOP" "$P_MARK" "$P_SLOW" "$P_LIST" "$P_SECOND" "$P_LONELY" "$P_NOID" "$P_ALT"; do
|
|
201
|
+
[[ -f "$_p/scripts/resolve-regenerable-conflicts.sh" ]] || { echo "[FATAL] sandbox plugin not built: $_p" >&2; exit 2; }
|
|
202
|
+
done
|
|
203
|
+
|
|
204
|
+
# run_at <plugin-root> <repo> <base-ref> [resolvable-tsv] — CLAUDE_PLUGIN_ROOT is UNSET unless a
|
|
205
|
+
# row sets it on purpose, so an ambient value cannot rescue a broken sibling lookup.
|
|
206
|
+
run_at() {
|
|
207
|
+
local pr="$1" r="$2" base="$3" tsv="${4:-}"
|
|
208
|
+
local out rc=0
|
|
209
|
+
if [[ -n "$tsv" ]]; then
|
|
210
|
+
out="$(cd "$r" && env -u CLAUDE_PLUGIN_ROOT bash "$pr/scripts/resolve-regenerable-conflicts.sh" --test-resolvable "$tsv" "$base" 2>&1)" || rc=$?
|
|
211
|
+
else
|
|
212
|
+
out="$(cd "$r" && env -u CLAUDE_PLUGIN_ROOT bash "$pr/scripts/resolve-regenerable-conflicts.sh" "$base" 2>&1)" || rc=$?
|
|
213
|
+
fi
|
|
214
|
+
printf '%s\n%s' "$out" "$rc"
|
|
215
|
+
}
|
|
216
|
+
run_sut() { run_at "$P_OK" "$@"; }
|
|
217
|
+
sut_rc() { tail -n1 <<<"$1"; }
|
|
218
|
+
sut_out() { sed '$d' <<<"$1"; }
|
|
219
|
+
# refused <name> <res> <fp_before> <repo> [expect-substring] — the fail-closed triple.
|
|
220
|
+
refused() {
|
|
221
|
+
local name="$1" res="$2" fpb="$3" r="$4" want="${5:-}"
|
|
222
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
223
|
+
[[ "$(sut_rc "$res")" != "0" ]] && pass "$name -> non-zero" \
|
|
224
|
+
|| fail "$name -> exited 0; the resolver committed something it should have refused"
|
|
225
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
226
|
+
[[ "$(tree_fp "$r")" == "$fpb" ]] && pass "$name -> tree byte-identical to entry" \
|
|
227
|
+
|| fail "$name -> the tree CHANGED (rc=$(sut_rc "$res")): $(_git "$r" status --porcelain --untracked-files=all | tr '\n' ' ') — $(sut_out "$res")"
|
|
228
|
+
if [[ -n "$want" ]]; then
|
|
229
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
230
|
+
grep -qF -- "$want" <<<"$(sut_out "$res")" && pass "$name -> says '$want'" \
|
|
231
|
+
|| fail "$name -> did not say '$want': $(sut_out "$res")"
|
|
232
|
+
fi
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
echo "=== resolve-regenerable-conflicts (Guard 1) ==="
|
|
236
|
+
|
|
237
|
+
# ── HAPPY PATH (AC1/AC2) — a bare self-hosted repo, the plugin's own renderer ───────────────
|
|
238
|
+
echo ""
|
|
239
|
+
echo "--- happy path: a lone resolvable content conflict is merged and committed ---"
|
|
240
|
+
r="$(mkrepo happy)"; assert_fixture_dir "$r"
|
|
241
|
+
before_head="$(_git "$r" rev-parse HEAD)"
|
|
242
|
+
res="$(run_sut "$r" main)"
|
|
243
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
244
|
+
[[ "$(sut_rc "$res")" == "0" ]] && pass "happy: exit 0" || fail "happy: exit $(sut_rc "$res") — $(sut_out "$res")"
|
|
245
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
246
|
+
if [[ "$(_git "$r" rev-parse HEAD^1 2>/dev/null)" == "$before_head" && "$(_git "$r" rev-parse HEAD^2 2>/dev/null)" == "$(_git "$r" rev-parse main)" ]]; then
|
|
247
|
+
pass "happy: a two-parent merge commit (HEAD, main) exists"
|
|
248
|
+
else
|
|
249
|
+
fail "happy: no merge commit with parents (HEAD, main)"
|
|
250
|
+
fi
|
|
251
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
252
|
+
[[ -z "$(_git "$r" status --porcelain --untracked-files=all)" ]] && pass "happy (AC2): the tree is clean after the commit" \
|
|
253
|
+
|| fail "happy: tree dirty after commit: $(_git "$r" status --porcelain --untracked-files=all)"
|
|
254
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
255
|
+
committed="$(_git "$r" show HEAD:"$MODEL")"
|
|
256
|
+
if [[ "$committed" == *'first-MAIN|last-FEATURE'*'"by":"plugin"'* ]]; then
|
|
257
|
+
pass "happy (AC1): the artifact was rendered by the PLUGIN renderer from the MERGED sources"
|
|
258
|
+
else
|
|
259
|
+
fail "happy: unexpected artifact content: $committed"
|
|
260
|
+
fi
|
|
261
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
262
|
+
if grep -qxF "SOLEUR_REGEN_ON_CONFLICT paths=$MODEL arm=plugin root_src=sibling rc=0" <<<"$(sut_out "$res")"; then
|
|
263
|
+
pass "happy: the success marker names the path, arm=plugin and root_src=sibling (no filesystem path)"
|
|
264
|
+
else
|
|
265
|
+
fail "happy: success marker missing or reshaped: $(sut_out "$res")"
|
|
266
|
+
fi
|
|
267
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
268
|
+
grep -q '\[regen-on-conflict\] regenerating ' <<<"$(sut_out "$res")" \
|
|
269
|
+
&& pass "happy: a progress line precedes the (minutes-long) render" \
|
|
270
|
+
|| fail "happy: no progress line before the render"
|
|
271
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
272
|
+
[[ -z "$(find "$XDG_CACHE_HOME/soleur" -mindepth 1 -maxdepth 1 2>/dev/null)" ]] \
|
|
273
|
+
&& pass "happy: the private staging dir was removed" \
|
|
274
|
+
|| fail "happy: staging left behind: $(ls "$XDG_CACHE_HOME/soleur")"
|
|
275
|
+
|
|
276
|
+
# A relative XDG_CACHE_HOME is invalid (XDG spec) and ignored: staging goes under $HOME/.cache,
|
|
277
|
+
# never into a directory relative to the repository being merged.
|
|
278
|
+
r="$(mkrepo relxdg)"; assert_fixture_dir "$r"
|
|
279
|
+
_home="$SANDBOX/relxdg-home"; assert_fixture_dir "$_home"; mkdir -p "$_home"
|
|
280
|
+
res="$(XDG_CACHE_HOME=relcache HOME="$_home" run_sut "$r" main)"
|
|
281
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
282
|
+
if [[ "$(sut_rc "$res")" == "0" && ! -e "$r/relcache" && -d "$_home/.cache/soleur" ]]; then
|
|
283
|
+
pass "a relative XDG_CACHE_HOME is ignored; staging goes under \$HOME/.cache"
|
|
284
|
+
else
|
|
285
|
+
fail "relative XDG_CACHE_HOME: rc=$(sut_rc "$res"), in-repo relcache=$([[ -e "$r/relcache" ]] && echo yes || echo no) — $(sut_out "$res")"
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
# P1 (must-PASS): the base ref given as a raw SHA.
|
|
289
|
+
r="$(mkrepo p1)"; assert_fixture_dir "$r"
|
|
290
|
+
res="$(run_sut "$r" "$(_git "$r" rev-parse main)")"
|
|
291
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
292
|
+
[[ "$(sut_rc "$res")" == "0" ]] && pass "P1: a SHA base ref exits 0 and merges" \
|
|
293
|
+
|| fail "P1: a SHA base ref exited $(sut_rc "$res") — $(sut_out "$res")"
|
|
294
|
+
|
|
295
|
+
# Two resolvable members, each regenerated by its own arm (arm-agnostic success).
|
|
296
|
+
_main_second() { printf '{"v":"main2"}\n' > "$1/$SECOND"; }
|
|
297
|
+
r="$(mkrepo twomembers)"; assert_fixture_dir "$r"
|
|
298
|
+
on_main "$r" _main_second
|
|
299
|
+
printf '{"v":"feature2"}\n' > "$r/$SECOND"; _git "$r" commit -q -am f2
|
|
300
|
+
printf '%s\tbash\t%s\n%s\tbash\t%s\n' "$MODEL" "$P_OK/scripts/render-c4-model.sh" \
|
|
301
|
+
"$SECOND" "$P_SECOND/scripts/render-c4-model.sh" > "$r.tsv"
|
|
302
|
+
res="$(run_sut "$r" main "$r.tsv")"
|
|
303
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
304
|
+
if [[ "$(sut_rc "$res")" == "0" && "$(_git "$r" show HEAD:"$MODEL")" == *'"by":"plugin"'* \
|
|
305
|
+
&& "$(_git "$r" show HEAD:"$SECOND")" == *'"second":"regenerated"'* ]]; then
|
|
306
|
+
pass "two members: each path is written by ITS OWN arm and both are committed"
|
|
307
|
+
else
|
|
308
|
+
fail "two members: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
# ── FAILURES OF THE RENDER: all happen before the worktree is touched ──────────────────────
|
|
312
|
+
echo ""
|
|
313
|
+
echo "--- a failing / empty / marker-laden render refuses with the tree untouched ---"
|
|
314
|
+
r="$(mkrepo row2)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(run_at "$P_FAIL" "$r" main)"
|
|
315
|
+
refused "row 2: the renderer fails" "$res" "$fpb" "$r" "regen failed"
|
|
316
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
317
|
+
grep -q 'Line 3: boom' <<<"$(sut_out "$res")" \
|
|
318
|
+
&& pass "AC6: the refusal carries the renderer's diagnostic text, not only an exit code" \
|
|
319
|
+
|| fail "AC6: the diagnostic was swallowed: $(sut_out "$res")"
|
|
320
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
321
|
+
grep -qx 'SOLEUR_REGEN_ON_CONFLICT rc=1 class=failed reason=render-failed' <<<"$(sut_out "$res")" \
|
|
322
|
+
&& pass "row 2: the stdout marker says class=failed reason=render-failed" \
|
|
323
|
+
|| fail "row 2: no machine-readable refusal marker: $(sut_out "$res")"
|
|
324
|
+
|
|
325
|
+
r="$(mkrepo noop)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(run_at "$P_NOOP" "$r" main)"
|
|
326
|
+
refused "a renderer that writes nothing" "$res" "$fpb" "$r" "did not produce"
|
|
327
|
+
|
|
328
|
+
r="$(mkrepo markers)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(run_at "$P_MARK" "$r" main)"
|
|
329
|
+
refused "a renderer that emits conflict markers" "$res" "$fpb" "$r" "still contains conflict markers"
|
|
330
|
+
|
|
331
|
+
# ── FAIL-CLOSED TABLE: inputs that are not a lone content conflict on a resolvable path ────
|
|
332
|
+
echo ""
|
|
333
|
+
echo "--- fail-closed table ---"
|
|
334
|
+
_main_readme() { printf 'readme MAIN\n' > "$1/README.md"; }
|
|
335
|
+
r="$(mkrepo row1)"; assert_fixture_dir "$r"; on_main "$r" _main_readme
|
|
336
|
+
printf 'readme FEATURE\n' > "$r/README.md"; _git "$r" commit -q -am readme
|
|
337
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
338
|
+
refused "row 1: a non-resolvable path also conflicts" "$res" "$fpb" "$r" "not regenerable: README.md"
|
|
339
|
+
|
|
340
|
+
r="$(mkrepo row4)"; assert_fixture_dir "$r"; : > "$r.tsv"; fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main "$r.tsv")"
|
|
341
|
+
refused "row 4: the resolvable set is empty" "$res" "$fpb" "$r" "reason=not-regenerable"
|
|
342
|
+
|
|
343
|
+
r="$(mkrepo row6)"; assert_fixture_dir "$r"
|
|
344
|
+
_git "$r" checkout -q main; _git "$r" rm -q "$MODEL"; _git "$r" commit -q -m del; _git "$r" checkout -q feature
|
|
345
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
346
|
+
refused "row 6: modify/delete, not content" "$res" "$fpb" "$r" "reason=unresolvable-kind"
|
|
347
|
+
|
|
348
|
+
r="$(mkrepo row7)"; assert_fixture_dir "$r"
|
|
349
|
+
printf 'DO NOT WRITE THROUGH ME\n' > "$r/victim.txt"
|
|
350
|
+
rm -f "$r/$MODEL"; ln -s "../../../../victim.txt" "$r/$MODEL"; _git "$r" add -A >/dev/null; _git "$r" commit -q -m symlink
|
|
351
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
352
|
+
refused "row 7: the resolvable path is a symlink on one side (distinct types)" "$res" "$fpb" "$r"
|
|
353
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
354
|
+
[[ "$(cat "$r/victim.txt")" == "DO NOT WRITE THROUGH ME" ]] && pass "row 7: the link target was not written through" \
|
|
355
|
+
|| fail "row 7: the link target was modified"
|
|
356
|
+
|
|
357
|
+
r="$(mkrepo row7b)"; assert_fixture_dir "$r"
|
|
358
|
+
printf 'victim\n' > "$r/$DIAG/victim.txt"; printf 'other\n' > "$r/$DIAG/other.txt"
|
|
359
|
+
rm -f "$r/$MODEL"; ln -s "victim.txt" "$r/$MODEL"; _git "$r" add -A >/dev/null; _git "$r" commit -q -m v
|
|
360
|
+
_git "$r" checkout -q main; rm -f "$r/$MODEL"; ln -s "other.txt" "$r/$MODEL"; _git "$r" add -A >/dev/null; _git "$r" commit -q -m o
|
|
361
|
+
_git "$r" checkout -q feature
|
|
362
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
363
|
+
refused "row 7b: both sides symlink the artifact to different targets" "$res" "$fpb" "$r" "symlink"
|
|
364
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
365
|
+
[[ "$(cat "$r/$DIAG/victim.txt")" == "victim" ]] && pass "row 7b: the link target was not written through" \
|
|
366
|
+
|| fail "row 7b: the regen wrote through the symlink"
|
|
367
|
+
|
|
368
|
+
r="$(mkrepo row8)"; assert_fixture_dir "$r"; _git "$r" merge main --no-edit >/dev/null 2>&1 || true
|
|
369
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
370
|
+
refused "row 8: a merge is already in progress" "$res" "$fpb" "$r" "merge is already in progress"
|
|
371
|
+
|
|
372
|
+
r="$(mkrepo dirty)"; assert_fixture_dir "$r"; printf 'operator work\n' > "$r/scratch.txt"; _git "$r" add scratch.txt >/dev/null
|
|
373
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
374
|
+
refused "a dirty (staged) tree" "$res" "$fpb" "$r" "not clean"
|
|
375
|
+
|
|
376
|
+
r="$(mkrepo untrackedhidden)"; assert_fixture_dir "$r"; git -C "$r" config status.showUntrackedFiles no
|
|
377
|
+
printf 'operator notes\n' > "$r/NOTES.txt"
|
|
378
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
379
|
+
refused "status.showUntrackedFiles=no cannot hide an untracked file" "$res" "$fpb" "$r" "not clean (NOTES.txt)"
|
|
380
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
381
|
+
[[ "$(cat "$r/NOTES.txt" 2>/dev/null)" == "operator notes" ]] && pass "the operator's untracked file survives" \
|
|
382
|
+
|| fail "the operator's untracked file was lost"
|
|
383
|
+
|
|
384
|
+
r="$(mkrepo noconflict)"; assert_fixture_dir "$r"; _git "$r" checkout -q -B feature main >/dev/null 2>&1
|
|
385
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
386
|
+
refused "no conflict at all" "$res" "$fpb" "$r" "reason=no-conflict"
|
|
387
|
+
|
|
388
|
+
r="$(mkrepo args)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"
|
|
389
|
+
res="$(run_sut "$r" "")"; refused "an empty base ref" "$res" "$fpb" "$r" "no base ref"
|
|
390
|
+
res="$(run_sut "$r" "no/such/ref")"; refused "an unresolvable base ref" "$res" "$fpb" "$r" "does not resolve"
|
|
391
|
+
|
|
392
|
+
# ── STAGING: only tracked regular LikeC4 sources from the merged tree reach the render ─────
|
|
393
|
+
echo ""
|
|
394
|
+
echo "--- staging: what reaches the renderer ---"
|
|
395
|
+
_main_junk() {
|
|
396
|
+
mkdir -p "$1/$DIAG/node_modules/.bin" "$1/$DIAG/nested"
|
|
397
|
+
printf '#!/bin/sh\n' > "$1/$DIAG/node_modules/.bin/likec4"
|
|
398
|
+
printf 'script-shell=./evil.sh\n' > "$1/$DIAG/.npmrc"
|
|
399
|
+
printf 'readme\n' > "$1/$DIAG/README.md"
|
|
400
|
+
printf 'model {}\n' > "$1/$DIAG/extra.like-c4"
|
|
401
|
+
printf 'model {}\n' > "$1/$DIAG/nested/café.c4"
|
|
402
|
+
printf 'model {}\n' > "$1/$DIAG/nested/views.likec4"
|
|
403
|
+
}
|
|
404
|
+
r="$(mkrepo stagedroot)"; assert_fixture_dir "$r"; on_main "$r" _main_junk
|
|
405
|
+
printf 'machine-local\n' > "$r/$DIAG/local.c4"; printf 'local.c4\n' >> "$r/.git/info/exclude"
|
|
406
|
+
rm -f "$SANDBOX/listroot.txt"
|
|
407
|
+
res="$(run_at "$P_LIST" "$r" main)"
|
|
408
|
+
_list="$(cat "$SANDBOX/listroot.txt" 2>/dev/null)"
|
|
409
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
410
|
+
[[ "$(sut_rc "$res")" == "0" ]] && pass "staging: a tree carrying node_modules, .npmrc and odd names still merges" \
|
|
411
|
+
|| fail "staging: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
412
|
+
for _want in "./$DIAG/model.c4" "./$DIAG/extra.like-c4" "./$DIAG/nested/café.c4" "./$DIAG/nested/views.likec4"; do
|
|
413
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
414
|
+
grep -qxF -- "$_want" <<<"$_list" && pass "staging: $_want is rendered" || fail "staging: $_want is missing from the render root: $_list"
|
|
415
|
+
done
|
|
416
|
+
for _never in node_modules .npmrc README.md local.c4 model.likec4.json; do
|
|
417
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
418
|
+
grep -qF -- "$_never" <<<"$_list" && fail "staging: $_never reached the render root" || pass "staging: $_never never reaches the render root"
|
|
419
|
+
done
|
|
420
|
+
|
|
421
|
+
_refuse_tree() { # <name> <want> <setup-fn> — a base-side addition that must be refused
|
|
422
|
+
local name="$1" want="$2" setup="$3" rr fpb res
|
|
423
|
+
rr="$(mkrepo "$(printf '%s' "$name" | tr -cd 'a-z0-9')")"
|
|
424
|
+
on_main "$rr" "$setup"
|
|
425
|
+
rm -f "$SANDBOX/listroot.txt"
|
|
426
|
+
fpb="$(tree_fp "$rr")"
|
|
427
|
+
res="$(run_at "$P_LIST" "$rr" main)"
|
|
428
|
+
refused "$name" "$res" "$fpb" "$rr" "$want"
|
|
429
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
430
|
+
[[ ! -e "$SANDBOX/listroot.txt" ]] && pass "$name -> refused BEFORE the renderer ran" \
|
|
431
|
+
|| fail "$name -> the renderer ran anyway"
|
|
432
|
+
}
|
|
433
|
+
_add_link_file() { ln -s /etc/hostname "$1/$DIAG/leak.c4"; }
|
|
434
|
+
_add_link_dir() { mkdir -p "$1/outside"; printf 'model {}\n' > "$1/outside/o.c4"; ln -s ../../../../outside "$1/$DIAG/ext"; }
|
|
435
|
+
_add_config() { printf 'import "node:fs"; export default {}\n' > "$1/$DIAG/likec4.config.mjs"; }
|
|
436
|
+
_add_rc() { printf '{}\n' > "$1/$DIAG/.likec4rc"; }
|
|
437
|
+
_add_gitlink() { assert_fixture_dir "$1"; mkdir -p "$1/$DIAG/sub"; git -C "$1" update-index --add --cacheinfo "160000,$(git -C "$1" rev-parse HEAD),$DIAG/sub"; }
|
|
438
|
+
_refuse_tree "a tracked symlinked source file" "reason=symlink-source" _add_link_file
|
|
439
|
+
_refuse_tree "a tracked symlinked source directory" "reason=symlink-source" _add_link_dir
|
|
440
|
+
_refuse_tree "a likec4 js config" "reason=likec4-config" _add_config
|
|
441
|
+
_refuse_tree "a likec4rc config" "reason=likec4-config" _add_rc
|
|
442
|
+
_refuse_tree "a submodule under the diagrams dir" "reason=gitlink-source" _add_gitlink
|
|
443
|
+
|
|
444
|
+
# A path the merge ADDS that exists here ignored would be overwritten by `git merge`.
|
|
445
|
+
_main_env() { printf 'from main\n' > "$1/local.env"; }
|
|
446
|
+
r="$(mkrepo collide)"; assert_fixture_dir "$r"; on_main "$r" _main_env
|
|
447
|
+
printf 'OPERATOR SECRET\n' > "$r/local.env"; printf 'local.env\n' >> "$r/.git/info/exclude"
|
|
448
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
449
|
+
refused "the merge would overwrite an ignored local file" "$res" "$fpb" "$r" "reason=would-overwrite"
|
|
450
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
451
|
+
[[ "$(cat "$r/local.env")" == "OPERATOR SECRET" ]] && pass "the ignored local file is untouched" \
|
|
452
|
+
|| fail "the ignored local file was overwritten"
|
|
453
|
+
|
|
454
|
+
# ── THE RENDER WINDOW: the operator keeps working; nothing of theirs is lost or committed ──
|
|
455
|
+
echo ""
|
|
456
|
+
echo "--- the render window ---"
|
|
457
|
+
_slow_run() { # <repo> <action-fn> — runs the resolver on P_SLOW, calls <action-fn> <repo> <pid> mid-render
|
|
458
|
+
local rr="$1" action="$2" pid rc=0
|
|
459
|
+
assert_fixture_dir "$SANDBOX"; rm -f "$SANDBOX/slow.started"
|
|
460
|
+
( cd "$rr" && exec env -u CLAUDE_PLUGIN_ROOT bash "$P_SLOW/scripts/resolve-regenerable-conflicts.sh" main ) \
|
|
461
|
+
> "$SANDBOX/slow.out" 2>&1 &
|
|
462
|
+
pid=$!
|
|
463
|
+
for _ in $(seq 1 100); do [[ -e "$SANDBOX/slow.started" ]] && break; sleep 0.1; done
|
|
464
|
+
"$action" "$rr" "$pid"
|
|
465
|
+
wait "$pid" || rc=$?
|
|
466
|
+
printf '%s\n%s' "$(cat "$SANDBOX/slow.out")" "$rc"
|
|
467
|
+
}
|
|
468
|
+
_act_edit() { printf 'mid-render edit\n' >> "$1/README.md"; printf 'new\n' > "$1/NEW.txt"; }
|
|
469
|
+
_act_stage() { assert_fixture_dir "$1"; printf 'x\n' > "$1/staged.txt"; git -C "$1" add staged.txt; }
|
|
470
|
+
_act_commit() { assert_fixture_dir "$1"; git -C "$1" -c user.email=t@t -c user.name=t commit -q --allow-empty -m mid; }
|
|
471
|
+
_act_term() { kill -TERM "$2" 2>/dev/null; }
|
|
472
|
+
_act_kill() { kill -KILL "$2" 2>/dev/null; }
|
|
473
|
+
|
|
474
|
+
r="$(mkrepo editduring)"; assert_fixture_dir "$r"
|
|
475
|
+
res="$(_slow_run "$r" _act_edit)"
|
|
476
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
477
|
+
[[ "$(sut_rc "$res")" != "0" ]] && grep -q 'reason=dirty-tree' <<<"$(sut_out "$res")" \
|
|
478
|
+
&& pass "an edit made during the render refuses the merge" \
|
|
479
|
+
|| fail "edit during render: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
480
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
481
|
+
if [[ "$(tail -1 "$r/README.md")" == "mid-render edit" && -f "$r/NEW.txt" && ! -f "$r/.git/MERGE_HEAD" \
|
|
482
|
+
&& "$(_git "$r" rev-parse HEAD)" == "$(_git "$r" rev-parse feature)" ]]; then
|
|
483
|
+
pass "the operator's mid-render edit and new file both survive, nothing was merged"
|
|
484
|
+
else
|
|
485
|
+
fail "operator work lost or a merge happened: $(_git "$r" status --porcelain --untracked-files=all | tr '\n' ' ')"
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
r="$(mkrepo stageduring)"; assert_fixture_dir "$r"
|
|
489
|
+
res="$(_slow_run "$r" _act_stage)"
|
|
490
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
491
|
+
if [[ "$(sut_rc "$res")" != "0" ]] && ! _git "$r" log -1 --name-only --format= | grep -c >/dev/null staged.txt \
|
|
492
|
+
&& _git "$r" diff --cached --name-only | grep -cx >/dev/null staged.txt; then
|
|
493
|
+
pass "a file staged during the render is never committed and stays staged"
|
|
494
|
+
else
|
|
495
|
+
fail "a mid-render staged file was committed or lost: $(sut_out "$res")"
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
r="$(mkrepo headmoved)"; assert_fixture_dir "$r"
|
|
499
|
+
res="$(_slow_run "$r" _act_commit)"
|
|
500
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
501
|
+
[[ "$(sut_rc "$res")" != "0" ]] && grep -q 'reason=head-moved' <<<"$(sut_out "$res")" && [[ ! -f "$r/.git/MERGE_HEAD" ]] \
|
|
502
|
+
&& pass "a commit made during the render refuses the (now stale) merge" \
|
|
503
|
+
|| fail "HEAD moved during render: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
504
|
+
|
|
505
|
+
r="$(mkrepo sigterm)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"
|
|
506
|
+
_t0=$SECONDS
|
|
507
|
+
res="$(_slow_run "$r" _act_term)"
|
|
508
|
+
_dt=$((SECONDS - _t0))
|
|
509
|
+
refused "SIGTERM during the render" "$res" "$fpb" "$r" "reason=signal"
|
|
510
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
511
|
+
[[ "$_dt" -lt 5 ]] && pass "SIGTERM is handled at once, not after the render finishes (${_dt}s)" \
|
|
512
|
+
|| fail "SIGTERM waited for the render (${_dt}s)"
|
|
513
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
514
|
+
[[ -z "$(find "$XDG_CACHE_HOME/soleur" -mindepth 1 -maxdepth 1 2>/dev/null)" ]] \
|
|
515
|
+
&& pass "SIGTERM: the staging dir was removed" || fail "SIGTERM left staging behind"
|
|
516
|
+
|
|
517
|
+
r="$(mkrepo sigkill)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"
|
|
518
|
+
res="$(_slow_run "$r" _act_kill)"
|
|
519
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
520
|
+
[[ "$(tree_fp "$r")" == "$fpb" ]] && pass "SIGKILL during the render leaves the tree byte-identical (no MERGE_HEAD)" \
|
|
521
|
+
|| fail "SIGKILL left the tree changed: $(_git "$r" status --porcelain | tr '\n' ' ')"
|
|
522
|
+
sleep 5; rm -rf "${XDG_CACHE_HOME:?}/soleur"
|
|
523
|
+
|
|
524
|
+
# ── A COMMIT HOOK THAT REWRITES THE MERGE is undone and refused ─────────────────────────────
|
|
525
|
+
r="$(mkrepo hook)"; assert_fixture_dir "$r"; mkdir -p "$r/.git/hooks"
|
|
526
|
+
printf '#!/bin/sh\necho hooked >> README.md\ngit add README.md\n' > "$r/.git/hooks/pre-commit"; chmod +x "$r/.git/hooks/pre-commit"
|
|
527
|
+
fpb="$(tree_fp "$r")"; res="$(run_sut "$r" main)"
|
|
528
|
+
refused "a pre-commit hook that changes the merge commit" "$res" "$fpb" "$r" "reason=commit-hook-changed-tree"
|
|
529
|
+
|
|
530
|
+
# The undo is VERIFIED, not asserted: a hook that also leaves an unstaged edit makes
|
|
531
|
+
# `reset --keep` refuse, and the resolver must say so rather than claim it undid the commit.
|
|
532
|
+
r="$(mkrepo hookkeep)"; assert_fixture_dir "$r"; mkdir -p "$r/.git/hooks"
|
|
533
|
+
printf '#!/bin/sh\necho hooked >> README.md\ngit add README.md\necho unstaged >> README.md\n' > "$r/.git/hooks/pre-commit"; chmod +x "$r/.git/hooks/pre-commit"
|
|
534
|
+
_head0="$(_git "$r" rev-parse HEAD)"; res="$(run_sut "$r" main)"
|
|
535
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
536
|
+
if [[ "$(sut_rc "$res")" != "0" && "$(sut_out "$res")" == *"reason=commit-hook-changed-tree"* \
|
|
537
|
+
&& "$(sut_out "$res")" == *"could NOT undo"* && "$(_git "$r" rev-parse HEAD)" != "$_head0" ]]; then
|
|
538
|
+
pass "an undo that reset --keep refuses is reported as NOT undone"
|
|
539
|
+
else
|
|
540
|
+
fail "unverified undo: rc=$(sut_rc "$res") head-moved=$([[ "$(_git "$r" rev-parse HEAD)" != "$_head0" ]] && echo yes || echo no) — $(sut_out "$res")"
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# Signals that land after the merge started. The helper TERMs the OUTERMOST ancestor running the
|
|
544
|
+
# resolver (a command substitution forks a copy with the same argv). Linux /proc only.
|
|
545
|
+
# HAZARD: the helper TERMs the outermost ancestor whose argv matches its pattern. A MUTANT of the match line (-v, a dropped -x, a catch-all
|
|
546
|
+
# pattern) matches every ancestor and walks up to the top of the user's session. The walk is bounded at this suite's own PID, but run any
|
|
547
|
+
# mutant of this block ONLY as `unshare -Urpf --kill-child --mount-proc bash <this file>` (the test shell becomes PID 1; the walk stops there).
|
|
548
|
+
if [[ -r /proc/self/stat ]]; then
|
|
549
|
+
export SOLEUR_TEST_SUITE_PID=$$ # read by the helper below; unset or empty means the helper signals nothing (the mid-merge row then fails; the commit row cannot tell)
|
|
550
|
+
_killer="$SANDBOX/kill-resolver.sh"; assert_fixture_dir "$SANDBOX"
|
|
551
|
+
cat > "$_killer" <<'EOF'
|
|
552
|
+
#!/bin/sh
|
|
553
|
+
# Walk up to the TOP of the contiguous run of ancestors whose argv holds the resolver script as a
|
|
554
|
+
# whole argument, and stop at the first non-matching ancestor above it — never further, or any
|
|
555
|
+
# outer shell whose command line merely mentions the path would be signalled too.
|
|
556
|
+
# The walk is also bounded at SOLEUR_TEST_SUITE_PID. A mutant of the grep line below (-v, no -x, a catch-all) matches every ancestor:
|
|
557
|
+
# run such a mutant ONLY under `unshare -Urpf --kill-child --mount-proc` (see the HAZARD note above the block).
|
|
558
|
+
p=$PPID; target=""
|
|
559
|
+
[ -n "${SOLEUR_TEST_SUITE_PID:-}" ] || exit 0
|
|
560
|
+
while [ -n "$p" ] && [ "$p" -gt 1 ] && [ "$p" != "$SOLEUR_TEST_SUITE_PID" ]; do
|
|
561
|
+
if tr '\0' '\n' < "/proc/$p/cmdline" 2>/dev/null | grep -cx >/dev/null '.*/scripts/resolve-regenerable-conflicts\.sh'; then
|
|
562
|
+
target=$p
|
|
563
|
+
elif [ -n "$target" ]; then
|
|
564
|
+
break
|
|
565
|
+
fi
|
|
566
|
+
p=$(awk '{print $4}' "/proc/$p/stat" 2>/dev/null)
|
|
567
|
+
done
|
|
568
|
+
[ -n "$target" ] && kill -TERM "$target"
|
|
569
|
+
exit 0
|
|
570
|
+
EOF
|
|
571
|
+
chmod +x "$_killer"
|
|
572
|
+
|
|
573
|
+
# During `git commit`: the commit completes, so the success marker is the truth.
|
|
574
|
+
r="$(mkrepo sigcommit)"; assert_fixture_dir "$r"; mkdir -p "$r/.git/hooks"
|
|
575
|
+
printf '#!/bin/sh\n%s\n' "$_killer" > "$r/.git/hooks/pre-commit"; chmod +x "$r/.git/hooks/pre-commit"
|
|
576
|
+
res="$(run_sut "$r" main)"
|
|
577
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
578
|
+
if [[ "$(sut_rc "$res")" == "0" && "$(sut_out "$res")" == *"SOLEUR_REGEN_ON_CONFLICT paths="*"rc=0"* \
|
|
579
|
+
&& "$(_git "$r" rev-parse HEAD^2 2>/dev/null)" == "$(_git "$r" rev-parse main)" ]]; then
|
|
580
|
+
pass "a signal during git commit still ends with a marker that matches the repo (committed)"
|
|
581
|
+
else
|
|
582
|
+
fail "signal during commit: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
583
|
+
fi
|
|
584
|
+
|
|
585
|
+
# While the merge is in progress (a clean filter on the artifact fires once MERGE_HEAD exists):
|
|
586
|
+
# unwound, and the marker class is `interrupted`, the same class as a pre-merge signal.
|
|
587
|
+
r="$(mkrepo sigmerge)"; assert_fixture_dir "$r"
|
|
588
|
+
printf '#!/bin/sh\n[ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ] && [ ! -e %s/sig.once ] && { : > %s/sig.once; %s; }\ncat\n' \
|
|
589
|
+
"$SANDBOX" "$SANDBOX" "$_killer" > "$SANDBOX/sig-clean.sh"; chmod +x "$SANDBOX/sig-clean.sh"
|
|
590
|
+
rm -f "$SANDBOX/sig.once"
|
|
591
|
+
printf '%s filter=sig\n' "$MODEL" > "$r/.git/info/attributes"
|
|
592
|
+
git -C "$r" config filter.sig.clean "$SANDBOX/sig-clean.sh"
|
|
593
|
+
_head0="$(_git "$r" rev-parse HEAD)"; res="$(run_sut "$r" main)"
|
|
594
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
595
|
+
if [[ -e "$SANDBOX/sig.once" && "$(sut_out "$res")" == *"rc=1 class=interrupted reason=interrupted"* \
|
|
596
|
+
&& ! -e "$r/.git/MERGE_HEAD" && "$(_git "$r" rev-parse HEAD)" == "$_head0" ]]; then
|
|
597
|
+
pass "a signal mid-merge unwinds and reports class=interrupted"
|
|
598
|
+
else
|
|
599
|
+
fail "signal mid-merge: fired=$([[ -e "$SANDBOX/sig.once" ]] && echo yes || echo no) rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
600
|
+
fi
|
|
601
|
+
else
|
|
602
|
+
echo " SKIP: no /proc — the in-commit and mid-merge signal rows need it"
|
|
603
|
+
CASES_RUN=$((CASES_RUN + 2)); passes=$((passes + 2))
|
|
604
|
+
fi
|
|
605
|
+
|
|
606
|
+
# An absolute XDG_CACHE_HOME the staging guard refuses still ends with a marker (rc 1, not a bare exit 2).
|
|
607
|
+
r="$(mkrepo procxdg)"; assert_fixture_dir "$r"
|
|
608
|
+
res="$(XDG_CACHE_HOME=/proc/soleur-no-such-dir run_sut "$r" main)"
|
|
609
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
610
|
+
[[ "$(sut_rc "$res")" == "1" && "$(sut_out "$res")" == *"reason=no-staging"* ]] \
|
|
611
|
+
&& pass "an unusable absolute XDG_CACHE_HOME is refused with a marker" \
|
|
612
|
+
|| fail "unusable XDG_CACHE_HOME: rc=$(sut_rc "$res") — $(sut_out "$res")"
|
|
613
|
+
|
|
614
|
+
# ══ PLUGIN ROOT: where the renderer comes from ═════════════════════════════════════════════
|
|
615
|
+
echo ""
|
|
616
|
+
echo "--- plugin root: self-hosted, wrapper, decoy, CLAUDE_PLUGIN_ROOT ---"
|
|
617
|
+
rm -f "$SANDBOX/WRAPPER_RAN" "$SANDBOX/DECOY_RAN"
|
|
618
|
+
r="$(mkrepo withwrapper wrapper)"; assert_fixture_dir "$r"; res="$(run_sut "$r" main)"
|
|
619
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
620
|
+
[[ "$(sut_rc "$res")" == "0" && ! -e "$SANDBOX/WRAPPER_RAN" && "$(_git "$r" show HEAD:"$MODEL")" == *'"by":"plugin"'* ]] \
|
|
621
|
+
&& pass "a repo carrying scripts/regenerate-c4-model.sh still renders through the plugin (the wrapper is not an arm)" \
|
|
622
|
+
|| fail "the repo wrapper ran or the merge failed: rc=$(sut_rc "$res")"
|
|
623
|
+
|
|
624
|
+
r="$(mkrepo decoy decoy)"; assert_fixture_dir "$r"; res="$(run_sut "$r" main)"
|
|
625
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
626
|
+
[[ "$(sut_rc "$res")" == "0" && ! -e "$SANDBOX/DECOY_RAN" && "$(_git "$r" show HEAD:"$MODEL")" == *'"by":"plugin"'* ]] \
|
|
627
|
+
&& pass "AC4: a decoy renderer inside the merged tree is never executed" \
|
|
628
|
+
|| fail "AC4: the decoy ran or the merge failed: rc=$(sut_rc "$res")"
|
|
629
|
+
|
|
630
|
+
# m1 — the plugin dir is absolutized BEFORE the cd; invoked by a RELATIVE path from a subdir.
|
|
631
|
+
r="$(mkrepo relative)"; assert_fixture_dir "$r"
|
|
632
|
+
_rel="$(realpath --relative-to="$r/knowledge-base" "$P_OK/scripts/resolve-regenerable-conflicts.sh")"
|
|
633
|
+
_rel_rc=0; _rel_out="$(cd "$r/knowledge-base" && env -u CLAUDE_PLUGIN_ROOT bash "$_rel" main 2>&1)" || _rel_rc=$?
|
|
634
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
635
|
+
[[ "$_rel_rc" -eq 0 ]] && pass "m1: a relative invocation from a subdirectory still finds the sibling renderer" \
|
|
636
|
+
|| fail "m1: relative invocation rc=$_rel_rc — $_rel_out"
|
|
637
|
+
|
|
638
|
+
_cpr() { # <plugin-for-resolver> <repo> <CLAUDE_PLUGIN_ROOT value>
|
|
639
|
+
local rc=0 out; out="$(cd "$2" && CLAUDE_PLUGIN_ROOT="$3" bash "$1/scripts/resolve-regenerable-conflicts.sh" main 2>&1)" || rc=$?
|
|
640
|
+
printf '%s\n%s' "$out" "$rc"
|
|
641
|
+
}
|
|
642
|
+
r="$(mkrepo cprfallback)"; assert_fixture_dir "$r"; res="$(_cpr "$P_LONELY" "$r" "$P_OK")"
|
|
643
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
644
|
+
[[ "$(sut_rc "$res")" == "0" && "$(sut_out "$res")" == *"root_src=env rc=0"* ]] \
|
|
645
|
+
&& pass "no sibling renderer: an absolute CLAUDE_PLUGIN_ROOT supplies the plugin root" \
|
|
646
|
+
|| fail "CLAUDE_PLUGIN_ROOT fallback: $(sut_out "$res")"
|
|
647
|
+
|
|
648
|
+
r="$(mkrepo cprorder)"; assert_fixture_dir "$r"; res="$(_cpr "$P_OK" "$r" "$P_ALT")"
|
|
649
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
650
|
+
[[ "$(sut_rc "$res")" == "0" && "$(_git "$r" show HEAD:"$MODEL")" == *'"by":"plugin"'* && "$(sut_out "$res")" == *"root_src=sibling"* ]] \
|
|
651
|
+
&& pass "a sibling renderer wins over CLAUDE_PLUGIN_ROOT when both resolve" \
|
|
652
|
+
|| fail "CLAUDE_PLUGIN_ROOT displaced the sibling: $(sut_out "$res")"
|
|
653
|
+
|
|
654
|
+
# A RELATIVE CLAUDE_PLUGIN_ROOT resolves against the repo being merged: never used. The fixture
|
|
655
|
+
# makes it resolve to a VALID plugin inside the repo (ignored, so the tree stays clean), so a
|
|
656
|
+
# resolver that accepted it would render and commit — the refusal is the only thing in the way.
|
|
657
|
+
r="$(mkrepo cprrelative)"; assert_fixture_dir "$r"
|
|
658
|
+
mkdir -p "$r/rel"; cp -R "$P_OK/." "$r/rel/"; printf 'rel/\n' >> "$r/.git/info/exclude"
|
|
659
|
+
fpb="$(tree_fp "$r")"
|
|
660
|
+
res="$(_cpr "$P_LONELY" "$r" "rel")"
|
|
661
|
+
refused "a RELATIVE CLAUDE_PLUGIN_ROOT (resolving to a valid plugin inside the repo)" "$res" "$fpb" "$r" "reason=no-plugin-root"
|
|
662
|
+
|
|
663
|
+
r="$(mkrepo noroot)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(run_at "$P_LONELY" "$r" main)"
|
|
664
|
+
refused "no plugin root at all" "$res" "$fpb" "$r" "reason=no-plugin-root"
|
|
665
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
666
|
+
grep -q 'set CLAUDE_PLUGIN_ROOT' <<<"$(sut_out "$res")" && pass "the no-root refusal names the remedy" \
|
|
667
|
+
|| fail "the no-root refusal is not actionable: $(sut_out "$res")"
|
|
668
|
+
|
|
669
|
+
r="$(mkrepo noid)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(run_at "$P_NOID" "$r" main)"
|
|
670
|
+
refused "a sibling plugin.json that does not name soleur" "$res" "$fpb" "$r" "does not name soleur"
|
|
671
|
+
r="$(mkrepo noidenv)"; assert_fixture_dir "$r"; fpb="$(tree_fp "$r")"; res="$(_cpr "$P_LONELY" "$r" "$P_NOID")"
|
|
672
|
+
refused "a CLAUDE_PLUGIN_ROOT whose plugin.json does not name soleur" "$res" "$fpb" "$r" "does not name soleur"
|
|
673
|
+
|
|
674
|
+
# A no-conflict run never needs a plugin root: classification comes first.
|
|
675
|
+
r="$(mkrepo rootlast)"; assert_fixture_dir "$r"; _git "$r" checkout -q -B feature main >/dev/null 2>&1
|
|
676
|
+
res="$(run_at "$P_LONELY" "$r" main)"
|
|
677
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
678
|
+
grep -q 'reason=no-conflict' <<<"$(sut_out "$res")" && pass "a missing plugin root does not mask 'no conflict' (classification first)" \
|
|
679
|
+
|| fail "classification did not come first: $(sut_out "$res")"
|
|
680
|
+
|
|
681
|
+
# ── THE SEAM IS NOT REACHABLE FROM THE ENVIRONMENT (and the seam itself does execute) ──────
|
|
682
|
+
assert_fixture_dir "$SANDBOX"
|
|
683
|
+
printf '%s\tbash\t-c\ttouch "$0/PWNED"\t%s\n' "$MODEL" "$SANDBOX" > "$SANDBOX/evil.tsv"
|
|
684
|
+
rm -f "$SANDBOX/PWNED"
|
|
685
|
+
r="$(mkrepo envseam)"; assert_fixture_dir "$r"
|
|
686
|
+
(cd "$r" && RESOLVABLE_OVERRIDE="$SANDBOX/evil.tsv" bash "$P_OK/scripts/resolve-regenerable-conflicts.sh" main >/dev/null 2>&1)
|
|
687
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
688
|
+
[[ ! -e "$SANDBOX/PWNED" ]] && pass "the resolvable set is not reachable from the environment (argv-only seam)" \
|
|
689
|
+
|| fail "RESOLVABLE_OVERRIDE still directs the resolver from the environment"
|
|
690
|
+
r="$(mkrepo envseamctl)"; assert_fixture_dir "$r"
|
|
691
|
+
res="$(run_sut "$r" main "$SANDBOX/evil.tsv")"
|
|
692
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
693
|
+
[[ -e "$SANDBOX/PWNED" ]] && pass "control: the same TSV through --test-resolvable DOES execute (the row above can fail)" \
|
|
694
|
+
|| fail "control: the test seam did not execute — the env-seam row proves nothing: $(sut_out "$res")"
|
|
695
|
+
|
|
696
|
+
# ── RATCHETS ────────────────────────────────────────────────────────────────────────────────
|
|
697
|
+
# Every assignment to the resolvable set or its argvs must carry a named marker, and the marker
|
|
698
|
+
# set is pinned. Structural: a new assignment site WITHOUT a marker fails too.
|
|
699
|
+
_sites="$(grep -nE '^[^#]*RESOLVABLE_(PATHS|ARGVS)(\[[^]]*\])?\+?=' "$SUT" | grep -vE 'RESOLVABLE_(PATHS|ARGVS)=\(\)')"
|
|
700
|
+
_n_sites="$(grep -c . <<<"$_sites")"
|
|
701
|
+
_unmarked="$(grep -vE '# (ARGV-SOURCE|RESOLVABLE-SET): [a-z-]+[[:space:]]*$' <<<"$_sites" || true)"
|
|
702
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
703
|
+
if [[ "$_n_sites" -eq 3 && -z "$_unmarked" ]]; then
|
|
704
|
+
pass "every resolvable-set/argv assignment site carries a marker (3 sites)"
|
|
705
|
+
else
|
|
706
|
+
fail "resolvable-set/argv assignment sites changed ($_n_sites; unmarked: $_unmarked) — an ADR-235 amendment is required"
|
|
707
|
+
fi
|
|
708
|
+
_default_paths="$(grep -E 'RESOLVABLE-SET: default[[:space:]]*$' "$SUT" | sed -n 's/^[^#]*RESOLVABLE_PATHS=(\(.*\))[[:space:]]*#.*$/\1/p')"
|
|
709
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
710
|
+
[[ "$(grep -c 'RESOLVABLE-SET: default' "$SUT")" -eq 1 && "$_default_paths" == "\"$MODEL\"" ]] \
|
|
711
|
+
&& pass "resolvable set: exactly one member, model.likec4.json (grow it deliberately + amend ADR-235)" \
|
|
712
|
+
|| fail "resolvable set changed: [$_default_paths]"
|
|
713
|
+
_argv_sources="$(grep -E '# ARGV-SOURCE: [a-z-]+[[:space:]]*$' "$SUT" | sed -E 's/.*# ARGV-SOURCE: ([a-z-]+)[[:space:]]*$/\1/' | LC_ALL=C sort | tr '\n' ' ')"
|
|
714
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
715
|
+
[[ "$_argv_sources" == "plugin test-seam " ]] && pass "argv sources: exactly {plugin, test-seam}" \
|
|
716
|
+
|| fail "argv sources changed: [$_argv_sources]"
|
|
717
|
+
|
|
718
|
+
# The resolver must NEVER push — in any git spelling (options before the subcommand count).
|
|
719
|
+
_PUSH_RE='(^|[^-[:alnum:]])git([[:space:]]+(-[Cc][[:space:]]+[^[:space:]]+|--?[[:alnum:]-]+(=[^[:space:]]+)?))*[[:space:]]+push([[:space:]]|$)'
|
|
720
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
721
|
+
if grep -v '^[[:space:]]*#' "$SUT" | grep -cE >/dev/null "$_PUSH_RE"; then
|
|
722
|
+
fail "the resolver contains a git push — callers own the push"
|
|
723
|
+
else
|
|
724
|
+
pass "the resolver never pushes"
|
|
725
|
+
fi
|
|
726
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
727
|
+
grep -qE "$_PUSH_RE" <<<'git -C /srv/repo push -q origin HEAD' && pass "control: the no-push pattern sees an option-prefixed push" \
|
|
728
|
+
|| fail "control: the no-push pattern misses git -C <dir> push"
|
|
729
|
+
|
|
730
|
+
# ── tree_fp POSITIVE CONTROL ────────────────────────────────────────────────────────────────
|
|
731
|
+
_fpctl="$(mkrepo fpcontrol)"; assert_fixture_dir "$_fpctl"; _fp0="$(tree_fp "$_fpctl")"
|
|
732
|
+
printf 'dirty\n' >> "$_fpctl/$SRC"
|
|
733
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
734
|
+
[[ "$(tree_fp "$_fpctl")" != "$_fp0" ]] && pass "tree_fp control: a modified tracked file changes the fingerprint" \
|
|
735
|
+
|| fail "tree_fp is not observing — every 'tree untouched' verdict is vacuous"
|
|
736
|
+
_git "$_fpctl" checkout -q -- "$SRC"; printf 'u\n' > "$_fpctl/untracked.txt"
|
|
737
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
738
|
+
[[ "$(tree_fp "$_fpctl")" != "$_fp0" ]] && pass "tree_fp control: an untracked file changes the fingerprint" \
|
|
739
|
+
|| fail "tree_fp cannot see untracked files"
|
|
740
|
+
|
|
741
|
+
# ── AC9: NO DOCUMENTED INVOCATION IS REPO-RELATIVE ─────────────────────────────────────────
|
|
742
|
+
# Scoped to what an agent or hook executes; ADRs, plans and learnings quote history.
|
|
743
|
+
_REPO="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
744
|
+
_AC9_RE='(bash|sh|source|\.)[[:space:]]+["'"'"']?(\./)?(plugins/soleur/scripts/(resolve-regenerable-conflicts|render-c4-model)|scripts/regenerate-c4-model)\.sh'
|
|
745
|
+
_ac9_rc=0
|
|
746
|
+
_ac9="$(git -C "$_REPO" grep -nE "$_AC9_RE" -- plugins/soleur/skills plugins/soleur/commands plugins/soleur/agents \
|
|
747
|
+
plugins/soleur/hooks AGENTS.rules.md ':!*.test.sh' ':!*.test.ts' 2>&1)" || _ac9_rc=$?
|
|
748
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
749
|
+
if [[ "$_ac9_rc" -eq 1 && -z "$_ac9" ]]; then
|
|
750
|
+
pass "AC9: no skill, command, agent, plugin hook or rule invokes the resolver or renderer repo-relatively"
|
|
751
|
+
elif [[ "$_ac9_rc" -eq 0 ]]; then
|
|
752
|
+
fail "AC9: repo-relative invocation(s) remain — use \"\${CLAUDE_PLUGIN_ROOT}/scripts/…\": $(tr '\n' ' ' <<<"$_ac9")"
|
|
753
|
+
else
|
|
754
|
+
fail "AC9: git grep could not run (rc=$_ac9_rc): $_ac9"
|
|
755
|
+
fi
|
|
756
|
+
for _ctl in 'bash plugins/soleur/scripts/resolve-regenerable-conflicts.sh origin/main' \
|
|
757
|
+
'bash "plugins/soleur/scripts/render-c4-model.sh"' 'bash scripts/regenerate-c4-model.sh'; do
|
|
758
|
+
CASES_RUN=$((CASES_RUN + 1))
|
|
759
|
+
grep -qE "$_AC9_RE" <<<"$_ctl" && pass "AC9 control: the sweep sees: $_ctl" \
|
|
760
|
+
|| fail "AC9 control: the sweep cannot match: $_ctl"
|
|
761
|
+
done
|
|
762
|
+
|
|
763
|
+
echo ""
|
|
764
|
+
echo "cases_run=$CASES_RUN passes=$passes fails=$fails ledger=${#FAILED[@]}"
|
|
765
|
+
|
|
766
|
+
_min_cases=140
|
|
767
|
+
if [[ "$CASES_RUN" -lt "$_min_cases" ]]; then
|
|
768
|
+
printf '[FATAL] assertion floor: only %s case(s) ran, floor is %s\n' "$CASES_RUN" "$_min_cases" >&2; exit 1
|
|
769
|
+
fi
|
|
770
|
+
if [[ $((passes + fails)) -ne "$CASES_RUN" ]]; then
|
|
771
|
+
printf '[FATAL] %s verdicts for %s cases — a case decided nothing\n' "$((passes + fails))" "$CASES_RUN" >&2; exit 1
|
|
772
|
+
fi
|
|
773
|
+
if [[ "${#FAILED[@]}" -ne "$fails" ]]; then
|
|
774
|
+
printf '[FATAL] ledger/counter disagree: %s vs %s\n' "${#FAILED[@]}" "$fails" >&2; exit 1
|
|
775
|
+
fi
|
|
776
|
+
if [[ "${#FAILED[@]}" -ne 0 ]]; then
|
|
777
|
+
printf '[FATAL] %s failing assertion(s):\n' "${#FAILED[@]}" >&2
|
|
778
|
+
printf ' - %s\n' "${FAILED[@]}" >&2; exit 1
|
|
779
|
+
fi
|
|
780
|
+
echo "resolve-regenerable-conflicts: all $passes assertions passed"
|