@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,665 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# net-issue-flow.sh — BLOCKING per-PR net-issue-flow gate.
|
|
4
|
+
#
|
|
5
|
+
# Contract
|
|
6
|
+
# --------
|
|
7
|
+
# usage: net-issue-flow.sh [PR_NUMBER]
|
|
8
|
+
# stdout: a human-readable CLOSING / FILED / NET block, always emitted,
|
|
9
|
+
# enumerating the actual issue numbers behind each count.
|
|
10
|
+
# exit 0: NET <= 0, or an override is present, or a transient failure
|
|
11
|
+
# (fail-OPEN — see below).
|
|
12
|
+
# exit 1: NET > 0 and no override. THE BLOCKING PATH.
|
|
13
|
+
#
|
|
14
|
+
# Threshold
|
|
15
|
+
# ---------
|
|
16
|
+
# Blocks at NET > 0: every PR must close at least as many issues as it files.
|
|
17
|
+
# NOT NET > +1. At the measured ~132 merged PRs/week, a +1 allowance authorizes
|
|
18
|
+
# +132 issues/week against an observed +144/week — it would cut backlog growth
|
|
19
|
+
# by ~8% and then report success. NET > 0 is the only threshold that flattens
|
|
20
|
+
# the queue. Pinned by case 4 of plugins/soleur/test/net-issue-flow.test.sh.
|
|
21
|
+
#
|
|
22
|
+
# Why the FILED query looks the way it does
|
|
23
|
+
# -----------------------------------------
|
|
24
|
+
# Five independently-measured defects each make a BLOCKING gate silently
|
|
25
|
+
# always-pass — which is strictly worse than the advisory surface it replaces,
|
|
26
|
+
# because it also carries the authority of having passed:
|
|
27
|
+
# 1. `--search` returns EMPTY cross-repo under a GitHub App / action token.
|
|
28
|
+
# 2. `gh issue list` defaults to --limit 30 (measured 30 returned vs 271 real).
|
|
29
|
+
# 3. The `(Ref|Closes|Fixes) #N` keyword filter matches only ~40% of real
|
|
30
|
+
# filings; a bare `#N` mention is the common shape.
|
|
31
|
+
# 4. `--label deferred-scope-out` covers only ~8% of what PRs actually file.
|
|
32
|
+
# 5. (#7759) The FILED query asks the ISSUE side only, so a filing that cites
|
|
33
|
+
# the ORIGINATING ISSUE rather than the PR is invisible: `Filing:`
|
|
34
|
+
# under-counts and the gate passes net-positive. Measured live twice — PR
|
|
35
|
+
# #7702 read `Filing: 0 / Net: -1 / PASS` and, once its three filings named
|
|
36
|
+
# the PR, `Filing: 3 / Net: +2 / BLOCKED`; PR #7841 reproduced it.
|
|
37
|
+
# Remedy: a SECOND attribution arm reading the PR own body. An issue counts
|
|
38
|
+
# when it postdates the PR AND (its body cites #N — unchanged — OR its
|
|
39
|
+
# number is on the PR `Filed:`/`Tracks:`/`Refs:` line). Every OTHER post-PR
|
|
40
|
+
# number the body mentions is REPORTED as a possible unattributed filing
|
|
41
|
+
# and counted toward nothing. Widening the match to issues citing an issue
|
|
42
|
+
# the PR closes was rejected: attribution would run transitively through a
|
|
43
|
+
# third party, so a sibling PR filings would count against this one.
|
|
44
|
+
# Counting bare `#N` was rejected on measurement over 300 PRs — it
|
|
45
|
+
# attributes 9 issues to two different PRs each and flips 25 (8.3%) from
|
|
46
|
+
# PASS to BLOCK with an unmeasured false-positive rate. See ADR-206.
|
|
47
|
+
# So: no --search, --limit 500, --state all, no label filter, bare-#N matching,
|
|
48
|
+
# a client-side full-ISO createdAt comparison (never `cut -c1-10`, which
|
|
49
|
+
# collapses same-day precision), and both attribution arms above.
|
|
50
|
+
#
|
|
51
|
+
# One interaction worth stating because it collapses an ADR-155 conjunct: a
|
|
52
|
+
# `Filed: #N` declaration now BOTH admits a row into FILED and satisfies
|
|
53
|
+
# exemption condition 4 (the companion), so for rows admitted via THAT route the
|
|
54
|
+
# four conjuncts are no longer independent. Rows admitted via the other arm (the
|
|
55
|
+
# issue body cites the PR) still need a separate companion, so the collapse is
|
|
56
|
+
# scoped, not total. Condition 2 — the human-gated `[mandates-filing]` corpus
|
|
57
|
+
# edit — still bounds the blast radius either way.
|
|
58
|
+
#
|
|
59
|
+
# An earlier revision of this comment justified that collapse by asserting the
|
|
60
|
+
# exemption "was INERT, because 0 of 33 whole-line `Mandated-By:` issues cite a
|
|
61
|
+
# PR". That measurement was wrong and the conclusion drawn from it does not
|
|
62
|
+
# hold. Re-measured 2026-09-07 by classifying each cited number through the
|
|
63
|
+
# GitHub API on `.pull_request` presence, which is the only sound discriminator
|
|
64
|
+
# here because issues and PRs share one number space in this repo:
|
|
65
|
+
#
|
|
66
|
+
# 33 whole-line `Mandated-By:` issues; all 33 cite at least one `#N`
|
|
67
|
+
# 66 distinct cited numbers, of which 21 are pull requests
|
|
68
|
+
# 20 of the 33 cite at least one PR, over 29 (issue, cited-PR) pairs
|
|
69
|
+
#
|
|
70
|
+
# One of those pairs is #7710 citing #7702 — the very case #7759 was filed
|
|
71
|
+
# about. So the exemption had real candidates before this change; the honest
|
|
72
|
+
# statement is that it was under-reached, not dead.
|
|
73
|
+
#
|
|
74
|
+
# Exits: the general override, and the narrow mandated-filing exemption
|
|
75
|
+
# ---------------------------------------------------------------------
|
|
76
|
+
# The escape hatch is deliberate, not default. A PR can be legitimately
|
|
77
|
+
# net-positive for several unrelated reasons — an architectural pivot, a
|
|
78
|
+
# discovered defect in another subsystem that must stay its own issue, or a
|
|
79
|
+
# filing forced by a SKILL.md phase mandate that carries no rule id. The
|
|
80
|
+
# override covers all of them:
|
|
81
|
+
# - PR body carries `<!-- gate-override: net-issue-flow -->` plus a one-line
|
|
82
|
+
# justification per filed issue, or
|
|
83
|
+
# - SOLEUR_SKIP_NET_ISSUE_FLOW_GATE=1 in the environment.
|
|
84
|
+
# Do NOT describe it as an architectural-pivot-only hatch. It was, and the
|
|
85
|
+
# result was that every other legitimate use had to be mis-described to fit —
|
|
86
|
+
# which trains the reflex of reaching for the override without reading it.
|
|
87
|
+
#
|
|
88
|
+
# Separately, one narrow case is resolved WITHOUT the blanket hatch: a filing
|
|
89
|
+
# another repo rule REQUIRED. `wg-block-pr-ready-on-undeferred-operator-steps`
|
|
90
|
+
# mandates a tracking issue before `gh pr ready`, so obeying it forced a
|
|
91
|
+
# violation here and neither documented exit applied ("fix inline" is a SIZE
|
|
92
|
+
# test; the blocker was AUTHORITY. "close something" needs a superseded issue,
|
|
93
|
+
# and a mandated tracker supersedes nothing). Such an issue is subtracted from
|
|
94
|
+
# NET when ALL of the following hold — see the derivation block below:
|
|
95
|
+
# 1. its body carries `Mandated-By: <rule-id>` on a line of its own,
|
|
96
|
+
# 2. that id carries `[mandates-filing]` in the MERGE-BASE corpus,
|
|
97
|
+
# 3. the issue is OPEN, and
|
|
98
|
+
# 4. the PR body carries a `Tracks|Refs #<issue>` companion.
|
|
99
|
+
# It is NOT self-serve: (2) is a human-gated corpus edit under ADR-092, so an
|
|
100
|
+
# agent can only ever name one of N already-blessed rules. It is also not
|
|
101
|
+
# unforgeable — this gate runs in a process the claimant controls, and
|
|
102
|
+
# SOLEUR_SKIP_NET_ISSUE_FLOW_GATE=1 sits a few lines above. What it buys is a
|
|
103
|
+
# closed vocabulary and per-rule attribution, not tamper-evidence; do not
|
|
104
|
+
# describe it as the latter. Every condition fails CLOSED. See ADR-155.
|
|
105
|
+
#
|
|
106
|
+
# Fail-open, not fail-silent
|
|
107
|
+
# --------------------------
|
|
108
|
+
# A gh/API error yields exit 0 so an outage cannot wedge every merge — but each
|
|
109
|
+
# fail-open emits telemetry via emit_incident. A gate that fails open silently
|
|
110
|
+
# is indistinguishable from a gate that passes.
|
|
111
|
+
#
|
|
112
|
+
# The fail-open event_type is `warn`, NOT `transient`: rule-metrics-aggregate.sh
|
|
113
|
+
# counts only deny/bypass/applied/warn, so a `transient` row would increment
|
|
114
|
+
# nothing — and the operator could not tell "gate never fired" from "gate
|
|
115
|
+
# fail-opened on every invocation", which is precisely the claim above.
|
|
116
|
+
#
|
|
117
|
+
set -uo pipefail
|
|
118
|
+
export LC_ALL=C
|
|
119
|
+
|
|
120
|
+
MARKER='<!-- gate-override: net-issue-flow -->'
|
|
121
|
+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
122
|
+
|
|
123
|
+
# Telemetry. Never wrap emit_incident in $(...) or a pipe — its output IS the
|
|
124
|
+
# telemetry write.
|
|
125
|
+
# CODE ROOT, not a data root (#7450; ADR-179's classification). Sourced, so it
|
|
126
|
+
# executes in THIS shell — never derive it from `git rev-parse --show-toplevel`,
|
|
127
|
+
# which on the review path is the contributor's checked-out tree. This is a ship
|
|
128
|
+
# gate, so a same-named file in a hostile PR would run with the gate's authority.
|
|
129
|
+
# CLAUDE_PROJECT_DIR comes from the harness, not from the tree under review.
|
|
130
|
+
#
|
|
131
|
+
# REPO_ROOT above is unchanged, but NOT because its other uses are all data-root
|
|
132
|
+
# reads — an earlier revision of this comment claimed that and it was FALSE.
|
|
133
|
+
# `python3 "$REPO_ROOT/scripts/lint-rule-bodies.py"` below EXECUTES a repo-root
|
|
134
|
+
# path, which is a code root by ADR-179's classification table. It is the table's
|
|
135
|
+
# "repo-root `scripts/` class: executes, target outside the payload" row, whose
|
|
136
|
+
# stated disposition is **route to #7453** — so it is deferred, not resolved, and
|
|
137
|
+
# this comment must not read as though the file were finished. The `source` above
|
|
138
|
+
# is the part #7450 fixes; the `python3` below is the part #7453 owns.
|
|
139
|
+
# Resolution, in trust order, and NEVER from `git rev-parse --show-toplevel`:
|
|
140
|
+
# 1. CLAUDE_PROJECT_DIR — supplied by the harness, not by the tree under review.
|
|
141
|
+
# Measured 2026-08-12: unset in a plain Claude Code session and in git hooks,
|
|
142
|
+
# so it cannot be the ONLY arm without silently retiring this telemetry.
|
|
143
|
+
# 2. This script's OWN location. Layout-invariant per ADR-178, and crucially NOT
|
|
144
|
+
# CWD-derived: a `gh pr checkout` cannot redirect it, because by the time this
|
|
145
|
+
# line runs the anchor decision has already been made — if a hostile script
|
|
146
|
+
# were executing, sourcing its sibling lib adds nothing. On a plugin INSTALL
|
|
147
|
+
# the walk lands somewhere with no such lib, so it no-ops, which is correct.
|
|
148
|
+
# The `-f` test is the safety net for both arms: a wrong root simply no-ops.
|
|
149
|
+
_incidents_root="${CLAUDE_PROJECT_DIR:-$(cd -P "$(dirname "${BASH_SOURCE[0]}")/../../../../.." 2>/dev/null && pwd -P)}"
|
|
150
|
+
_incidents="${_incidents_root:+${_incidents_root}/.claude/hooks/lib/incidents.sh}"
|
|
151
|
+
if [[ -n "$_incidents" && -r "$_incidents" ]]; then
|
|
152
|
+
# shellcheck disable=SC1090
|
|
153
|
+
source "$_incidents" 2>/dev/null || true
|
|
154
|
+
fi
|
|
155
|
+
# Emit under an explicit rule_id. Per-rule attribution MUST ride in the
|
|
156
|
+
# STRUCTURED rule_id, never the free-text prefix — the aggregator keys every
|
|
157
|
+
# counter on rule_id and does not parse rule_text_prefix, so an attribution
|
|
158
|
+
# written into the prefix is unreadable by construction. See ADR-155 (b).
|
|
159
|
+
# Every id here keeps the `net-issue-flow` prefix, which rule-metrics-aggregate.sh
|
|
160
|
+
# already exempts from the orphan gate.
|
|
161
|
+
_NIF_TMP=()
|
|
162
|
+
|
|
163
|
+
_emit_as() {
|
|
164
|
+
if declare -F emit_incident >/dev/null 2>&1; then
|
|
165
|
+
emit_incident "$1" "$2" "$3" || true
|
|
166
|
+
fi
|
|
167
|
+
}
|
|
168
|
+
_emit() { _emit_as "net-issue-flow" "$@"; }
|
|
169
|
+
|
|
170
|
+
_fail_open() {
|
|
171
|
+
printf '\n'
|
|
172
|
+
printf 'net-issue-flow: TRANSIENT — could not compute net flow (%s).\n' "$1"
|
|
173
|
+
printf ' Failing OPEN so an API outage cannot wedge every merge.\n'
|
|
174
|
+
printf ' This is recorded as telemetry, not swallowed.\n'
|
|
175
|
+
_emit warn "net-issue-flow fail-open: $1"
|
|
176
|
+
exit 0
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if [[ "${SOLEUR_SKIP_NET_ISSUE_FLOW_GATE:-0}" == "1" ]]; then
|
|
180
|
+
printf 'net-issue-flow: SKIPPED via SOLEUR_SKIP_NET_ISSUE_FLOW_GATE=1.\n'
|
|
181
|
+
_emit bypass "net-issue-flow skipped via env"
|
|
182
|
+
exit 0
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
PR_NUMBER="${1:-}"
|
|
186
|
+
if [[ -z "$PR_NUMBER" ]]; then
|
|
187
|
+
PR_NUMBER="$(gh pr view --json number --jq .number 2>/dev/null)" \
|
|
188
|
+
|| _fail_open "could not resolve PR number"
|
|
189
|
+
fi
|
|
190
|
+
[[ "$PR_NUMBER" =~ ^[0-9]+$ ]] || _fail_open "PR number is not a positive integer: '$PR_NUMBER'"
|
|
191
|
+
|
|
192
|
+
PR_BODY="$(gh pr view "$PR_NUMBER" --json body --jq .body 2>/dev/null)" \
|
|
193
|
+
|| _fail_open "could not read PR #${PR_NUMBER} body"
|
|
194
|
+
|
|
195
|
+
PR_CREATED_AT="$(gh pr view "$PR_NUMBER" --json createdAt --jq .createdAt 2>/dev/null)" \
|
|
196
|
+
|| _fail_open "could not read PR #${PR_NUMBER} createdAt"
|
|
197
|
+
[[ -n "$PR_CREATED_AT" ]] || _fail_open "PR #${PR_NUMBER} createdAt was empty"
|
|
198
|
+
|
|
199
|
+
# --- Fence-strip the PR body ONCE, before anything reads it ------------------
|
|
200
|
+
# Every consumer of the PR body must see the stripped form. Computing this
|
|
201
|
+
# lazily next to the override check (where it used to live) left CLOSING reading
|
|
202
|
+
# the RAW body, so a fenced `Closes #4242` — a quoted commit message, diff, or
|
|
203
|
+
# issue body, which is routine in this repo's PR descriptions — bought a unit of
|
|
204
|
+
# NET credit against a BLOCKING gate. Measured: raw body yields CLOSING=1 on a
|
|
205
|
+
# fenced keyword, stripped yields 0. Same self-override class the marker match
|
|
206
|
+
# already guards, reached through the CLOSING path instead.
|
|
207
|
+
# `END { if (in_fence) exit 2 }` mirrors ship-operator-step-gate.sh and
|
|
208
|
+
# ship-soak-followthrough-gate.sh. Without it an unbalanced fence degrades to
|
|
209
|
+
# "strip nothing after the opener" — and the jq stripper in this same file
|
|
210
|
+
# already fails closed, so the two halves disagreed.
|
|
211
|
+
PR_FENCE_RC=0
|
|
212
|
+
PR_BODY_SCAN="$(printf '%s\n' "$PR_BODY" | awk '
|
|
213
|
+
/^[[:space:]]*```/ { in_fence = !in_fence; next }
|
|
214
|
+
!in_fence { print }
|
|
215
|
+
END { if (in_fence) exit 2 }
|
|
216
|
+
')" || PR_FENCE_RC=$?
|
|
217
|
+
|
|
218
|
+
# An unbalanced fence is NOT a degraded read to be swallowed. Both strippers
|
|
219
|
+
# yield the empty string on it, and the two halves fail in OPPOSITE directions:
|
|
220
|
+
# CLOSING drops to 0 (fail-closed, harmless), while `$declared` drops to empty —
|
|
221
|
+
# which deletes the entire declared-filing arm. For a filing that cites the
|
|
222
|
+
# originating ISSUE rather than the PR, that arm is the ONLY one that can see it,
|
|
223
|
+
# so a single unclosed ``` turns a net-positive PR into `Filing: 0 / PASS`, with
|
|
224
|
+
# no residual line and no telemetry. Measured on the pristine gate (#7896 review).
|
|
225
|
+
#
|
|
226
|
+
# The header above used to claim this path "fails closed" by analogy with the two
|
|
227
|
+
# sibling ship gates. That is true of the EXEMPTION (a claim that cannot match
|
|
228
|
+
# grants nothing) and exactly backwards for FILED. Abort instead: the body is
|
|
229
|
+
# unparseable, and a gate that cannot read its input must not report a verdict.
|
|
230
|
+
if [[ "$PR_FENCE_RC" -ne 0 ]]; then
|
|
231
|
+
printf '\nnet-issue-flow: BLOCKED — PR #%s has an unbalanced code fence.\n\n' "$PR_NUMBER"
|
|
232
|
+
printf 'The body cannot be parsed, so neither the closing keywords nor the\n'
|
|
233
|
+
printf '`Filed:` declaration can be read, and a verdict computed from an\n'
|
|
234
|
+
printf 'unreadable body would be meaningless. Close the fence and re-run.\n'
|
|
235
|
+
_emit_as net-issue-flow-unbalanced-fence deny "PR body has an unbalanced code fence"
|
|
236
|
+
exit 1
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# --- CLOSING: issues this PR closes via close-keywords in its body -----------
|
|
240
|
+
# WORD-BOUNDARY the keywords, and drop the PR own number.
|
|
241
|
+
#
|
|
242
|
+
# Without `\b` the alternation matches INSIDE a longer word: `unclosed #4242`,
|
|
243
|
+
# `disclosed #N` and `foreclosed #N` each bought a full -1 of NET credit for a
|
|
244
|
+
# close GitHub will never perform. Measured (#7896 review): a body reading
|
|
245
|
+
# "This is unclosed #4242." reported `Closing: 1 (#4242)`.
|
|
246
|
+
#
|
|
247
|
+
# `$prnum` is stripped here for the same reason the declared arm strips it: #N
|
|
248
|
+
# inside PR N is a self-reference, and `Closes #<own PR>` was a free unit of
|
|
249
|
+
# credit. The declared arm already had this guard; CLOSING did not.
|
|
250
|
+
#
|
|
251
|
+
# Both are pre-existing, and both got materially cheaper to exploit with this
|
|
252
|
+
# change: before the declared-filing arm, FILED was usually 0 on the shapes that
|
|
253
|
+
# matter, so there was nothing for a bogus CLOSING to neutralise.
|
|
254
|
+
CLOSING_NUMS="$(printf '%s\n' "$PR_BODY_SCAN" \
|
|
255
|
+
| grep -oiE '(^|[^A-Za-z])(close[sd]?|fix(e[sd])?|resolve[sd]?) #[0-9]+' \
|
|
256
|
+
| grep -oE '[0-9]+' \
|
|
257
|
+
| grep -vxF "$PR_NUMBER" \
|
|
258
|
+
| sort -un || true)"
|
|
259
|
+
CLOSING=0
|
|
260
|
+
[[ -n "$CLOSING_NUMS" ]] && CLOSING="$(printf '%s\n' "$CLOSING_NUMS" | grep -c . || true)"
|
|
261
|
+
|
|
262
|
+
# --- Mandating rules: the qualifying set, DERIVED from the corpus ------------
|
|
263
|
+
# Not a list in this script. A restated list is a second pin, and the copy that
|
|
264
|
+
# drifts is the one that runs. The set is every rule line in AGENTS.rules.md
|
|
265
|
+
# carrying the `[mandates-filing]` marker.
|
|
266
|
+
#
|
|
267
|
+
# Read from the MERGE-BASE, never the worktree and never a bare `:path`:
|
|
268
|
+
# * the worktree is author-controlled, so a PR could tag a rule and exempt
|
|
269
|
+
# itself in the same diff;
|
|
270
|
+
# * `git show ":AGENTS.rules.md"` (no ref) reads the STAGED INDEX — also
|
|
271
|
+
# author-controlled, and measured to return rc=0 with a full id list, so
|
|
272
|
+
# the read "succeeds" and no empty-set warning fires. That is the worst
|
|
273
|
+
# shape: a fail-open that looks like a successful read.
|
|
274
|
+
# Hence the explicit SHA guard: nothing reaches `git show` unless the base
|
|
275
|
+
# resolved to something that looks like an object name.
|
|
276
|
+
#
|
|
277
|
+
# Extraction is restricted to `^(hr|wg)-` so the derived set is BY CONSTRUCTION
|
|
278
|
+
# a subset of the ADR-092 ack gate's coverage (`GATED_PREFIX_RE` in
|
|
279
|
+
# lint-rule-bodies.py). Without it, a `cq-*` rule could carry the marker and
|
|
280
|
+
# grant exemption authority with no ack required — 27 of 101 ids sit outside
|
|
281
|
+
# that gate.
|
|
282
|
+
#
|
|
283
|
+
# Per-LINE matching (marker and id on the same line), not adjacency: robust to
|
|
284
|
+
# tag placement and to a trailing `**Why:**` clause.
|
|
285
|
+
MANDATING_IDS=""
|
|
286
|
+
CORPUS_NOTE=""
|
|
287
|
+
MB="$(git merge-base origin/main HEAD 2>/dev/null || true)"
|
|
288
|
+
if [[ ! "$MB" =~ ^[0-9a-f]{7,40}$ ]]; then
|
|
289
|
+
CORPUS_NOTE="corpus unreadable: merge-base did not resolve — exemption unavailable"
|
|
290
|
+
_emit_as net-issue-flow-mandated-filing-corpus-unreadable warn "merge-base did not resolve pr=${PR_NUMBER}"
|
|
291
|
+
else
|
|
292
|
+
CORPUS_TEXT="$(git show "${MB}:AGENTS.rules.md" 2>/dev/null || true)"
|
|
293
|
+
if [[ -z "$CORPUS_TEXT" ]]; then
|
|
294
|
+
CORPUS_NOTE="corpus unreadable: AGENTS.rules.md absent at merge-base — exemption unavailable"
|
|
295
|
+
_emit_as net-issue-flow-mandated-filing-corpus-unreadable warn "AGENTS.rules.md absent at merge-base pr=${PR_NUMBER}"
|
|
296
|
+
else
|
|
297
|
+
# Derived by the ADR-092 gate's OWN parser, not by a second grep here.
|
|
298
|
+
# `parse_bodies` requires four conjuncts (gated `## SECTION`, `- ` at column
|
|
299
|
+
# 0, not pointer-shaped, `^(hr|wg)-`); a shell grep enforces only the last,
|
|
300
|
+
# so it is a strict SUPERSET along the line-shape axis — an indented
|
|
301
|
+
# sub-bullet or a prose line carrying the marker would be honoured here
|
|
302
|
+
# while being invisible to the ack gate, the manifest and lint-rule-ids.py.
|
|
303
|
+
# Measured on the shipped corpus: the grep form derived 4 ids to
|
|
304
|
+
# parse_bodies' 2. Fails CLOSED on any error (missing python3, parse
|
|
305
|
+
# failure, non-zero exit) — an empty set means the exemption is unavailable.
|
|
306
|
+
MANDATING_IDS="$(printf '%s\n' "$CORPUS_TEXT" \
|
|
307
|
+
| python3 "$REPO_ROOT/scripts/lint-rule-bodies.py" --emit-mandating-ids 2>/dev/null \
|
|
308
|
+
|| true)"
|
|
309
|
+
if [[ -z "$MANDATING_IDS" ]]; then
|
|
310
|
+
CORPUS_NOTE="corpus read OK, zero rules tagged — exemption inactive"
|
|
311
|
+
_emit_as net-issue-flow-mandated-filing-zero-tagged warn "zero rules tagged at base=${MB:0:12} pr=${PR_NUMBER}"
|
|
312
|
+
fi
|
|
313
|
+
fi
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# "read failed" and "read OK, nothing tagged" get DIFFERENT rule_ids above.
|
|
317
|
+
# Collapsing them is the same defect this gate's header condemns: the operator
|
|
318
|
+
# could not tell "never fired" from "fail-opened on every invocation".
|
|
319
|
+
MANDATING_JSON="$(printf '%s\n' "$MANDATING_IDS" \
|
|
320
|
+
| jq -R -s -c 'split("\n") | map(select(length > 0))' 2>/dev/null)" \
|
|
321
|
+
|| _fail_open "could not encode the mandating-rule set"
|
|
322
|
+
|
|
323
|
+
# --- FILED: issues created after this PR that bare-reference it --------------
|
|
324
|
+
# No --search, no label filter, --state all, --limit 500. See header.
|
|
325
|
+
# `state` is new: the exemption requires the issue to be OPEN, and an ABSENT
|
|
326
|
+
# state must fail closed rather than read as OPEN.
|
|
327
|
+
ISSUES_JSON="$(gh issue list --state all --limit 500 --json number,body,createdAt,state 2>/dev/null)" \
|
|
328
|
+
|| _fail_open "could not list issues"
|
|
329
|
+
[[ -n "$ISSUES_JSON" ]] || _fail_open "issue list returned empty"
|
|
330
|
+
|
|
331
|
+
# ONE jq pass over the ~2 MB payload. A per-issue subprocess loop for the
|
|
332
|
+
# fence-strip measured >=1.7 s of fork overhead alone against ~1.0 s for this;
|
|
333
|
+
# the gate's total budget is what the hook's timeout has to clear.
|
|
334
|
+
# The PR body goes to jq via --rawfile, NOT --arg. Linux caps a single argv
|
|
335
|
+
# element at MAX_ARG_STRLEN (32 * PAGESIZE = 131072 bytes on this host, measured:
|
|
336
|
+
# 131000 bytes runs, 131072 exits 126 "Argument list too long"). Past that jq is
|
|
337
|
+
# never exec-ed, the capture fails, and control lands on _fail_open -- which
|
|
338
|
+
# prints TRANSIENT and exits 0. A PR author padding their own body past the cap
|
|
339
|
+
# therefore turned this BLOCKING gate into an unconditional PASS, indistinguishable
|
|
340
|
+
# in telemetry from a real GitHub outage. That is a sixth member of the
|
|
341
|
+
# always-pass family this file header enumerates, and it sat on the very jq call
|
|
342
|
+
# the declared-filing arm was added to. A file has no such cap.
|
|
343
|
+
_PRBODY_FILE="$(mktemp -t net-issue-flow-prbody.XXXXXXXX)"
|
|
344
|
+
_NIF_TMP+=("$_PRBODY_FILE")
|
|
345
|
+
trap 'rm -f -- "${_NIF_TMP[@]}"' EXIT
|
|
346
|
+
printf '%s' "$PR_BODY" > "$_PRBODY_FILE"
|
|
347
|
+
|
|
348
|
+
GATE_ROWS="$(printf '%s' "$ISSUES_JSON" | jq -r \
|
|
349
|
+
--arg pr "$PR_NUMBER" \
|
|
350
|
+
--arg since "$PR_CREATED_AT" \
|
|
351
|
+
--rawfile prbody "$_PRBODY_FILE" \
|
|
352
|
+
--arg closing "$CLOSING_NUMS" \
|
|
353
|
+
--argjson ok "$MANDATING_JSON" '
|
|
354
|
+
# Fenced blocks are stripped from BOTH corpora before matching. An UNBALANCED
|
|
355
|
+
# fence yields the empty string, so nothing can match and the issue fails
|
|
356
|
+
# closed — matching the two sibling ship gates, which end with
|
|
357
|
+
# `END { if (in_fence) exit 2 }`. This gate previously had neither.
|
|
358
|
+
def sf:
|
|
359
|
+
(. // "") | split("\n")
|
|
360
|
+
| reduce .[] as $l ({inf: false, out: []};
|
|
361
|
+
if ($l | test("^[ \t]*```")) then .inf = (.inf | not)
|
|
362
|
+
elif .inf then .
|
|
363
|
+
else .out += [$l] end)
|
|
364
|
+
| if .inf then "" else (.out | join("\n")) end;
|
|
365
|
+
|
|
366
|
+
# A claim is a WHOLE LINE. `\r` is mandatory: GitHub returns CRLF bodies for
|
|
367
|
+
# web-authored text, and a [ \t]-only anchor fails closed on a correct claim.
|
|
368
|
+
# The KEY tolerates only `Mandated-By:`/`mandated-by:` case variants on its
|
|
369
|
+
# first letters (NOT `MANDATED-BY:`); the ID is matched case-insensitively and
|
|
370
|
+
# downcased, so `Mandated-By: WG-BLOCK-...` resolves.
|
|
371
|
+
def claims:
|
|
372
|
+
sf | split("\n")
|
|
373
|
+
| map(select(test("^[ \t\r]*[Mm]andated-[Bb]y:[ \t\r]*[A-Za-z0-9-]+[ \t\r]*$")))
|
|
374
|
+
| map(capture("[Mm]andated-[Bb]y:[ \t\r]*(?<id>[A-Za-z0-9-]+)").id | ascii_downcase);
|
|
375
|
+
|
|
376
|
+
# --- #7759: the DECLARED filing set, derived from the PR own body -------
|
|
377
|
+
#
|
|
378
|
+
# Both sets are derived HERE, inside the pass that already receives $prbody —
|
|
379
|
+
# never in bash. A bash form (`grep -oE … | jq … || _fail_open`) fails the gate
|
|
380
|
+
# OPEN under `set -uo pipefail` on every PR whose body names no issue, because
|
|
381
|
+
# grep exits 1 on no-match; measured at 46 of 300 PRs. It would also invert the
|
|
382
|
+
# deliberately fail-CLOSED unbalanced-fence path into a fail-open.
|
|
383
|
+
#
|
|
384
|
+
# LOOKBEHIND, not a consuming boundary group: `(^|[^0-9A-Za-z])` consumes the
|
|
385
|
+
# separator, so `#1#2` yields only [1] — the second member of every adjacent
|
|
386
|
+
# pair is dropped. Measured.
|
|
387
|
+
def refs: [ scan("(?<![0-9A-Za-z])#([0-9]+)") | .[0] | tonumber ];
|
|
388
|
+
|
|
389
|
+
($prbody | sf) as $pb
|
|
390
|
+
| ($closing | split("\n") | map(select(length > 0) | tonumber)) as $closenums
|
|
391
|
+
| ($pr | tonumber) as $prnum
|
|
392
|
+
|
|
393
|
+
# DECLARED — the only set that COUNTS. A whole-line assertion of the filing
|
|
394
|
+
# relationship, produced by /ship Phase 6, not inferred from prose.
|
|
395
|
+
#
|
|
396
|
+
# ONE keyword, and the colon is MANDATORY. The first cut of this arm accepted
|
|
397
|
+
# `(Filed|Tracks|Refs):?`, which reads as generous and is not: `Tracks #N` and
|
|
398
|
+
# `Refs #N` are PRE-EXISTING vocabulary in this repo (the
|
|
399
|
+
# wg-block-pr-ready-on-undeferred-operator-steps companion, and ~15 script
|
|
400
|
+
# headers citing provenance), and `Refs:` is ordinary prose. Measured on a real
|
|
401
|
+
# line in this repo — `Refs: #6588, #6897, #6604, #6570. Prior decision: #6918`
|
|
402
|
+
# — the wide form admitted FIVE issues as this filings of this PR, including the one
|
|
403
|
+
# the line itself labels "Prior decision". That is the sibling-attribution failure
|
|
404
|
+
# (P4) the ADR uses to reject option 2 of the issue, reintroduced through the
|
|
405
|
+
# arm that replaced it. `Filed:` is the one shape with a producer behind it.
|
|
406
|
+
#
|
|
407
|
+
# A leading list marker and surrounding emphasis are tolerated because they are
|
|
408
|
+
# what an author actually writes; the anchor still forbids mid-line prose, so a
|
|
409
|
+
# sentence mentioning the word cannot declare anything. Case-insensitive on the
|
|
410
|
+
# keyword only.
|
|
411
|
+
#
|
|
412
|
+
# `$closenums` is deliberately NOT subtracted. It used to be, and that let one
|
|
413
|
+
# `Closes #N` line delete a declared filing from FILED *and* claim a close
|
|
414
|
+
# credit for it — a two-unit NET swing, with the number then absent from every
|
|
415
|
+
# printed line. Measured: `Filed: #7001` + `Closes #7001` reported
|
|
416
|
+
# `Closing: 1 / Filing: 0 / Net: -1 / PASS`. A number on both lines is a
|
|
417
|
+
# contradiction in the PR body; it is surfaced below and left in BOTH terms, so
|
|
418
|
+
# it nets to zero honestly instead of buying credit.
|
|
419
|
+
| ( [ $pb | split("\n")[]
|
|
420
|
+
| select(test("^[ \t\r]*([-*+][ \t]+)?[*_]*[Ff][Ii][Ll][Ee][Dd][*_]*:[*_]*[ \t]*#[0-9]"))
|
|
421
|
+
| refs[] ]
|
|
422
|
+
| unique | map(select(. != $prnum)) ) as $declared
|
|
423
|
+
|
|
424
|
+
# CONTRADICTORY — declared as filed AND named by a close keyword. Reported, and
|
|
425
|
+
# left in both terms rather than cancelled.
|
|
426
|
+
| ( $declared | map(select(IN($closenums[]))) ) as $contradictory
|
|
427
|
+
|
|
428
|
+
| [ .[]
|
|
429
|
+
| select((.createdAt // "") >= $since)
|
|
430
|
+
# SIBLING DISJUNCTION over the same array, with the createdAt guard kept
|
|
431
|
+
# as its own conjunct above. The body-cites-PR arm is unchanged.
|
|
432
|
+
| select(
|
|
433
|
+
((.body // "") | test("(^|[^0-9A-Za-z])#" + $pr + "([^0-9]|$)"))
|
|
434
|
+
# `.number | IN($declared[])`, NOT `$declared | index(.number)`:
|
|
435
|
+
# the pipe re-binds `.` to $declared, so `.number` would index an
|
|
436
|
+
# ARRAY with a string and abort the whole pass into _fail_open.
|
|
437
|
+
or (.number | IN($declared[]))
|
|
438
|
+
)
|
|
439
|
+
]
|
|
440
|
+
| unique_by(.number) | sort_by(.number)
|
|
441
|
+
| map(
|
|
442
|
+
. as $i
|
|
443
|
+
| ($i.body | claims) as $c
|
|
444
|
+
| (
|
|
445
|
+
# This rung FIRST: with an empty set every claim would otherwise be
|
|
446
|
+
# reported as "names a rule that does not carry [mandates-filing]",
|
|
447
|
+
# which is false and actively misleading during rollout — the rule
|
|
448
|
+
# usually DOES carry it, the corpus just was not readable at this
|
|
449
|
+
# merge-base. Telling an agent its correct claim is wrong sends it to
|
|
450
|
+
# guess another id, which the help text explicitly warns against.
|
|
451
|
+
if ($ok | length) == 0 then "exemption inactive here (no qualifying rules at this merge-base)"
|
|
452
|
+
elif ($c | length) == 0 then "no Mandated-By: claim"
|
|
453
|
+
elif ($c | length) > 1 then "multiple Mandated-By: claims"
|
|
454
|
+
elif ($ok | index($c[0])) == null then "claim names a rule that does not carry [mandates-filing]: " + $c[0]
|
|
455
|
+
elif ($i.state // "") != "OPEN" then "issue is not OPEN: " + ($i.state // "<absent>")
|
|
456
|
+
# The companion asks one question: does the PR body positively name
|
|
457
|
+
# this issue? A `Filed:` declaration answers it — it is a STRONGER,
|
|
458
|
+
# whole-line assertion of the same relationship. Before this was
|
|
459
|
+
# widened, `Filed: #N` (the exact line /ship Phase 6 instructs) was the
|
|
460
|
+
# one shape that admitted a mandated filing to FILED while denying it
|
|
461
|
+
# the exemption, and the rejection printed "PR body has no Tracks/Refs
|
|
462
|
+
# #N companion" over a body that declared #N verbatim. The remediation
|
|
463
|
+
# then looped: the help text says to add `Tracks #N`, and an agent that
|
|
464
|
+
# wrote `Tracks: #N` got the identical message. Measured (#7896 review).
|
|
465
|
+
elif ((($i.number | IN($declared[]))
|
|
466
|
+
or ($pb | test("(^|[^A-Za-z])(Tracks|Refs)[ \t]+#" + ($i.number | tostring) + "([^0-9]|$)"))) | not)
|
|
467
|
+
then "PR body neither declares `Filed: #" + ($i.number | tostring) + "` nor carries a `Tracks #" + ($i.number | tostring) + "` companion"
|
|
468
|
+
else "" end
|
|
469
|
+
) as $why
|
|
470
|
+
# FOUR fields now. `attribution` goes BEFORE `detail` because detail is
|
|
471
|
+
# FREE TEXT and must stay last — a rejected row detail contains spaces,
|
|
472
|
+
# and @tsv + `read` would otherwise shift every later field.
|
|
473
|
+
| [ ($i.number | tostring),
|
|
474
|
+
(if $why == "" then "exempt" else "rejected" end),
|
|
475
|
+
(if (($declared | index($i.number)) != null)
|
|
476
|
+
and (((.body // "") | test("(^|[^0-9A-Za-z])#" + $pr + "([^0-9]|$)")) | not)
|
|
477
|
+
then "pr-body" else "issue-body" end),
|
|
478
|
+
(if $why == "" then $c[0] else $why end) ]
|
|
479
|
+
| @tsv
|
|
480
|
+
)
|
|
481
|
+
| . as $rows
|
|
482
|
+
| ($rows | map(split("\t")[0] | tonumber)) as $counted
|
|
483
|
+
| $rows[],
|
|
484
|
+
# Two trailing sentinel rows. Both are report-only, both are consumed by the
|
|
485
|
+
# loop below and never counted, and `_num` is non-numeric on each so neither
|
|
486
|
+
# can be mistaken for an issue row.
|
|
487
|
+
#
|
|
488
|
+
# SENTINEL every field, never emit one empty. Tab is IFS-WHITESPACE, so
|
|
489
|
+
# `read -r a b c d` COLLAPSES an empty middle field and every later field
|
|
490
|
+
# shifts left.
|
|
491
|
+
#
|
|
492
|
+
# UNDELIVERED replaces an earlier "possible unattributed filings" set that was
|
|
493
|
+
# computed from the PR body ALONE — it never joined the issue array, so it had
|
|
494
|
+
# no recency filter, no existence check, and no exclusion of rows the gate had
|
|
495
|
+
# already counted. Live on merged PR #7702 it printed five numbers of which
|
|
496
|
+
# FOUR were simultaneously in `Filing: 4`, alongside a prose cross-reference to
|
|
497
|
+
# an unrelated merged PR; the parenthetical "not counted" was false for most of
|
|
498
|
+
# the line. It reported every issue number a body happened to mention, so the
|
|
499
|
+
# drift metric built on it sat at ceiling and could not rise informatively.
|
|
500
|
+
#
|
|
501
|
+
# What replaces it is true by construction: numbers the PR DECLARED that did
|
|
502
|
+
# not become a counted row. Those are the declarations the gate could not
|
|
503
|
+
# honour — the issue predates the PR, is outside the fetched window, is in
|
|
504
|
+
# another repo, or does not exist — and every one of them was previously
|
|
505
|
+
# dropped from FILED *and* suppressed from the residual, i.e. silent.
|
|
506
|
+
("__UNDELIVERED__\t"
|
|
507
|
+
+ (($declared | map(select(IN($counted[]) | not)))
|
|
508
|
+
| if length == 0 then "NONE" else (map(tostring) | join(" ")) end)
|
|
509
|
+
+ "\t-\t-"),
|
|
510
|
+
("__CONTRADICTORY__\t"
|
|
511
|
+
+ ($contradictory
|
|
512
|
+
| if length == 0 then "NONE" else (map(tostring) | join(" ")) end)
|
|
513
|
+
+ "\t-\t-")' 2>/dev/null)" \
|
|
514
|
+
|| _fail_open "could not parse issue list"
|
|
515
|
+
|
|
516
|
+
FILED=0
|
|
517
|
+
EXEMPT=0
|
|
518
|
+
FILED_NUMS=""
|
|
519
|
+
EXEMPT_DETAIL=""
|
|
520
|
+
REJECTED_DETAIL=""
|
|
521
|
+
ATTRIBUTED_NUMS=""
|
|
522
|
+
UNDELIVERED_NUMS=""
|
|
523
|
+
CONTRADICTORY_NUMS=""
|
|
524
|
+
while IFS=$'\t' read -r _num _verdict _attr _detail; do
|
|
525
|
+
[[ -z "$_num" ]] && continue
|
|
526
|
+
# The report-only sentinel. Non-numeric by construction so it can never be
|
|
527
|
+
# mistaken for an issue row, and handled BEFORE the FILED increment so it
|
|
528
|
+
# cannot touch the count.
|
|
529
|
+
if [[ "$_num" == "__UNDELIVERED__" ]]; then
|
|
530
|
+
[[ "$_verdict" == "NONE" ]] || UNDELIVERED_NUMS="$_verdict"
|
|
531
|
+
continue
|
|
532
|
+
fi
|
|
533
|
+
if [[ "$_num" == "__CONTRADICTORY__" ]]; then
|
|
534
|
+
[[ "$_verdict" == "NONE" ]] || CONTRADICTORY_NUMS="$_verdict"
|
|
535
|
+
continue
|
|
536
|
+
fi
|
|
537
|
+
FILED=$((FILED + 1))
|
|
538
|
+
FILED_NUMS+="$_num "
|
|
539
|
+
# Derived from the rows ACTUALLY COUNTED, never from $declared — otherwise the
|
|
540
|
+
# report and the count can desynchronise and the line becomes a second claim
|
|
541
|
+
# rather than a view of the first.
|
|
542
|
+
[[ "$_attr" == "pr-body" ]] && ATTRIBUTED_NUMS+="$_num "
|
|
543
|
+
if [[ "$_verdict" == "exempt" ]]; then
|
|
544
|
+
EXEMPT=$((EXEMPT + 1))
|
|
545
|
+
EXEMPT_DETAIL+="#${_num} via ${_detail}; "
|
|
546
|
+
# Per-rule attribution lives in the STRUCTURED rule_id, not the free-text
|
|
547
|
+
# prefix field, so `summary.gate_exemptions` can group by rule without
|
|
548
|
+
# parsing prose. Still covered by the aggregator's net-issue-flow* prefix.
|
|
549
|
+
# Safe to interpolate: on the exempt path $_detail is a corpus-derived id,
|
|
550
|
+
# constrained to [a-z0-9-] by parse_bodies. Rejected rows carry free text and
|
|
551
|
+
# never reach here.
|
|
552
|
+
_emit_as "net-issue-flow-mandated-filing--${_detail}" bypass "exempt pr=${PR_NUMBER} issue=${_num}"
|
|
553
|
+
else
|
|
554
|
+
REJECTED_DETAIL+="#${_num} (${_detail}); "
|
|
555
|
+
fi
|
|
556
|
+
done <<< "$GATE_ROWS"
|
|
557
|
+
|
|
558
|
+
NET=$(( FILED - EXEMPT - CLOSING ))
|
|
559
|
+
|
|
560
|
+
# --- Display: always emitted, enumerating the actual numbers -----------------
|
|
561
|
+
# `Filing:` keeps its TRUE count. An exemption that silently reduces it is worse
|
|
562
|
+
# than the blanket override, which at least leaves a marker in the PR body.
|
|
563
|
+
_fmt() { if [[ -z "$1" ]]; then printf 'none'; else printf '%s' "$(printf '#%s ' $1 | sed 's/ $//')"; fi; }
|
|
564
|
+
_fmt_pairs() { if [[ -z "$1" ]]; then printf 'none'; else printf '%s' "${1%%'; '}"; fi; }
|
|
565
|
+
printf '\n'
|
|
566
|
+
printf 'PR #%s net-issue-flow:\n' "$PR_NUMBER"
|
|
567
|
+
if [[ -n "$CORPUS_NOTE" ]]; then
|
|
568
|
+
printf ' Mandating rules: 0 (%s)\n' "$CORPUS_NOTE"
|
|
569
|
+
else
|
|
570
|
+
printf ' Mandating rules: %s (%s, merge-base %s)\n' \
|
|
571
|
+
"$(printf '%s\n' "$MANDATING_IDS" | grep -c . || true)" \
|
|
572
|
+
"$(printf '%s' "$MANDATING_IDS" | tr '\n' ' ' | sed 's/ $//')" \
|
|
573
|
+
"${MB:0:12}"
|
|
574
|
+
fi
|
|
575
|
+
printf ' Closing: %s (%s)\n' "$CLOSING" "$(_fmt "$CLOSING_NUMS")"
|
|
576
|
+
printf ' Filing: %s (%s)\n' "$FILED" "$(_fmt "$FILED_NUMS")"
|
|
577
|
+
printf ' Exempt: %s (%s)\n' "$EXEMPT" "$(_fmt_pairs "$EXEMPT_DETAIL")"
|
|
578
|
+
# #7759: attribution provenance, and the report-only residual.
|
|
579
|
+
if [[ -n "$ATTRIBUTED_NUMS" ]]; then
|
|
580
|
+
printf ' Attributed: %s (subset of Filing:, admitted via the PR declared filing line)\n' \
|
|
581
|
+
"$(_fmt "$ATTRIBUTED_NUMS")"
|
|
582
|
+
_emit_as net-issue-flow-body-attributed applied \
|
|
583
|
+
"declared-arm fired pr=${PR_NUMBER} issues=$(printf '%s' "$ATTRIBUTED_NUMS" | tr -s ' ' ',' | sed 's/,$//')"
|
|
584
|
+
fi
|
|
585
|
+
if [[ -n "$UNDELIVERED_NUMS" ]]; then
|
|
586
|
+
printf ' Undelivered declarations: %s (declared on the Filed: line, no matching issue in range)\n' \
|
|
587
|
+
"$(_fmt "$UNDELIVERED_NUMS")"
|
|
588
|
+
_emit_as net-issue-flow-undelivered-declaration warn \
|
|
589
|
+
"declared but not counted pr=${PR_NUMBER} issues=$(printf '%s' "$UNDELIVERED_NUMS" | tr -s ' ' ',' | sed 's/,$//')"
|
|
590
|
+
fi
|
|
591
|
+
if [[ -n "$CONTRADICTORY_NUMS" ]]; then
|
|
592
|
+
printf ' Contradictory: %s (on BOTH the Filed: line and a close keyword — counted in both terms)\n' \
|
|
593
|
+
"$(_fmt "$CONTRADICTORY_NUMS")"
|
|
594
|
+
_emit_as net-issue-flow-contradictory-declaration warn \
|
|
595
|
+
"declared filed and closed pr=${PR_NUMBER} issues=$(printf '%s' "$CONTRADICTORY_NUMS" | tr -s ' ' ',' | sed 's/,$//')"
|
|
596
|
+
fi
|
|
597
|
+
if [[ -n "$REJECTED_DETAIL" ]]; then
|
|
598
|
+
printf ' Rejected: %s\n' "$(_fmt_pairs "$REJECTED_DETAIL")"
|
|
599
|
+
fi
|
|
600
|
+
printf ' Net: %+d (positive = backlog growth)\n' "$NET"
|
|
601
|
+
|
|
602
|
+
if [[ "$NET" -le 0 ]]; then
|
|
603
|
+
printf '\nnet-issue-flow: PASS (net <= 0).\n'
|
|
604
|
+
_emit applied "net-issue-flow pass net=${NET} pr=${PR_NUMBER}"
|
|
605
|
+
exit 0
|
|
606
|
+
fi
|
|
607
|
+
|
|
608
|
+
# --- NET > 0: override or block ---------------------------------------------
|
|
609
|
+
# $PR_BODY_SCAN is the fence-stripped body, computed once near the top. The
|
|
610
|
+
# BLOCKED message below PRINTS the literal marker, so an agent that pastes a
|
|
611
|
+
# gate failure into the PR description as context would otherwise smuggle in its
|
|
612
|
+
# own override — reported as OVERRIDDEN with a bypass event, while nothing in
|
|
613
|
+
# the body reads as a deliberate decision. Same self-override class the hook
|
|
614
|
+
# header guards against for spec files, via a different corpus path.
|
|
615
|
+
if grep -qF -- "$MARKER" < <(printf '%s' "$PR_BODY_SCAN"); then
|
|
616
|
+
printf '\nnet-issue-flow: OVERRIDDEN via the gate-override marker in the PR body.\n'
|
|
617
|
+
printf ' Net is +%d; the override is recorded as a deliberate decision.\n' "$NET"
|
|
618
|
+
_emit bypass "net-issue-flow overridden net=${NET} pr=${PR_NUMBER}"
|
|
619
|
+
exit 0
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
printf '\n'
|
|
623
|
+
printf 'net-issue-flow: BLOCKED — this PR is net-positive (+%d) on the issue queue.\n' "$NET"
|
|
624
|
+
printf '\n'
|
|
625
|
+
printf 'Every PR must close at least as many issues as it files. Filing is free;\n'
|
|
626
|
+
printf 'closing is expensive, and the queue grows by roughly the difference.\n'
|
|
627
|
+
printf '\n'
|
|
628
|
+
printf 'Resolve via one of:\n'
|
|
629
|
+
printf ' (a) Fix inline — fold the filed work into THIS PR. The cost-of-filing\n'
|
|
630
|
+
printf ' auto-flip (<=100 lines AND <=4 files) already covers most of it.\n'
|
|
631
|
+
printf ' NOTE this is a SIZE test. If the blocker is AUTHORITY (an\n'
|
|
632
|
+
printf ' operator-only credential or production decision), (a) does not\n'
|
|
633
|
+
printf ' apply no matter how small the diff would be — see (d).\n'
|
|
634
|
+
printf ' (b) Close something — if a filed issue supersedes an open one, close it\n'
|
|
635
|
+
printf ' AND add the `Closes #N` keyword to the PR body. CLOSING is parsed\n'
|
|
636
|
+
printf ' from the body only; closing on GitHub alone changes nothing here.\n'
|
|
637
|
+
printf ' (c) Override — add to the PR body:\n'
|
|
638
|
+
printf ' %s\n' "$MARKER"
|
|
639
|
+
printf ' plus a one-line justification per filed issue, or run with\n'
|
|
640
|
+
printf ' SOLEUR_SKIP_NET_ISSUE_FLOW_GATE=1.\n'
|
|
641
|
+
printf ' This is the general escape hatch, NOT an architectural-pivot-only\n'
|
|
642
|
+
printf ' one. Real uses include architectural pivots, but also filings from\n'
|
|
643
|
+
printf ' SKILL.md phase mandates that carry no rule id to cite in (d), and\n'
|
|
644
|
+
printf ' discovered defects in another subsystem that must stay separate.\n'
|
|
645
|
+
printf ' Describe the actual reason; do not reach for a criterion that does\n'
|
|
646
|
+
printf ' not fit. A hatch that has to be mis-described gets taken reflexively.\n'
|
|
647
|
+
printf ' (d) Mandated filing — the issue was REQUIRED by a rule that carries\n'
|
|
648
|
+
printf ' [mandates-filing] in AGENTS.rules.md. Put on its own line in the\n'
|
|
649
|
+
printf ' ISSUE body:\n'
|
|
650
|
+
printf ' Mandated-By: <rule-id>\n'
|
|
651
|
+
printf ' and a `Tracks #<issue>` / `Refs #<issue>` companion in the PR body,\n'
|
|
652
|
+
printf ' and keep the issue OPEN. The exemption is shown on its own line in\n'
|
|
653
|
+
printf ' the report above; it never reduces the Filing: count.\n'
|
|
654
|
+
if [[ -n "$MANDATING_IDS" ]]; then
|
|
655
|
+
printf ' Rules that currently qualify:\n'
|
|
656
|
+
printf '%s\n' "$MANDATING_IDS" | sed 's/^/ /'
|
|
657
|
+
printf ' If the rule that forced your filing is NOT in that list, (d) is\n'
|
|
658
|
+
printf ' unavailable to you — do not guess another id, it will be rejected.\n'
|
|
659
|
+
printf ' Use (c) and say so, or propose tagging the rule in its own PR.\n'
|
|
660
|
+
else
|
|
661
|
+
printf ' No rules currently qualify (%s),\n' "${CORPUS_NOTE:-none tagged}"
|
|
662
|
+
printf ' so (d) is unavailable on this run — use (c).\n'
|
|
663
|
+
fi
|
|
664
|
+
_emit deny "net-issue-flow blocked net=${NET} pr=${PR_NUMBER}"
|
|
665
|
+
exit 1
|