@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,412 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Hardened redaction engine (#5987). Adapted from gstack `redact-engine` (clean-room;
|
|
3
|
+
# see plugins/soleur/NOTICE). Contract preserved 1:1 with the former bash engine so the
|
|
4
|
+
# three in-repo consumers (incident dry-run.sh, code-to-prd Layer 2, legal-generate gate)
|
|
5
|
+
# need no change:
|
|
6
|
+
# argv[1] = path to scan.
|
|
7
|
+
# exit 0 = clean / 1 = redaction needed (incl. synthetic HIGH) / 2 = cannot-evaluate.
|
|
8
|
+
# stdout finding lines: "at offset <normOffset>: <prefix>***<suffix> matched pattern <class>"
|
|
9
|
+
#
|
|
10
|
+
# Pipeline: cap -> strip -> NFKC(whole-string) -> strip -> confusable-fold -> match -> meta-redact.
|
|
11
|
+
# Detection matches over ONE whole-string NFKC(strip(text)): per-codepoint normalization is NOT
|
|
12
|
+
# equal to whole-string NFKC (a decomposed/combining sequence folds to an ASCII secret char only
|
|
13
|
+
# whole-string), so per-codepoint would be a fail-OPEN. The sentinel halts; it never rewrites in
|
|
14
|
+
# place and no consumer parses the offset, so there is no offset-map back to the original.
|
|
15
|
+
import base64
|
|
16
|
+
import binascii
|
|
17
|
+
import contextlib
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import sys
|
|
21
|
+
import unicodedata
|
|
22
|
+
import urllib.parse
|
|
23
|
+
|
|
24
|
+
# 1 MiB default cap. Overridable via env for tests/tuning.
|
|
25
|
+
MAX = int(os.environ.get("REDACT_MAX_INPUT_BYTES", str(1024 * 1024)))
|
|
26
|
+
|
|
27
|
+
# Invisible / break-rendering / bidi / format characters that NFKC does NOT remove and that splice
|
|
28
|
+
# tokens invisibly. Built by Unicode CATEGORY, not a hand-picked codepoint list — an enumeration
|
|
29
|
+
# silently misses whole families (C0 controls, DEL, variation selectors, the Tags block U+E0000-E007F,
|
|
30
|
+
# combining grapheme joiner), each the SAME evasion class this engine defeats. Strip:
|
|
31
|
+
# - categories Cc (control), Cf (format), Cs (surrogate) — EXCEPT the meaningful ASCII whitespace
|
|
32
|
+
# controls (tab/LF/VT/FF/CR), which are kept (whitespace token-splitting is a documented non-goal);
|
|
33
|
+
# - variation selectors (U+FE00-FE0F, U+E0100-E01EF), combining grapheme joiner (U+034F), and the
|
|
34
|
+
# Mongolian free variation selectors (U+180B-180D) — invisible marks NFKC leaves intact.
|
|
35
|
+
# Plus an explicit tail for the invisibles that are NOT in Cc/Cf/Cs: line/para separators (Zl/Zp),
|
|
36
|
+
# the replacement char (So, from an invalid-byte splice), and the Hangul/Khmer fillers that render as
|
|
37
|
+
# nothing (Lo/Mn). Keys are ordinals throughout (escapes-only per `cq-regex-unicode-separators-escape-only`).
|
|
38
|
+
_KEEP_WHITESPACE = {0x09, 0x0A, 0x0B, 0x0C, 0x0D}
|
|
39
|
+
_EXPLICIT_STRIP = (
|
|
40
|
+
0x2028, 0x2029, # line / paragraph separators (Zl / Zp)
|
|
41
|
+
0xFFFD, # decode-replacement char (So — invalid-byte splice)
|
|
42
|
+
0x115F, 0x1160, 0x3164, 0xFFA0, # Hangul fillers (Lo) — incl. halfwidth U+FFA0 (NFKC->U+1160)
|
|
43
|
+
0x17B4, 0x17B5, # Khmer inherent vowels (Mn) — render as nothing
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _strippable(o):
|
|
48
|
+
if o in _KEEP_WHITESPACE:
|
|
49
|
+
return False
|
|
50
|
+
if unicodedata.category(chr(o)) in ("Cc", "Cf", "Cs"):
|
|
51
|
+
return True
|
|
52
|
+
return 0xFE00 <= o <= 0xFE0F or 0xE0100 <= o <= 0xE01EF or o == 0x034F or 0x180B <= o <= 0x180D
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Built once at import over the bounded ranges that hold every Cc/Cf/Cs + invisible-mark codepoint
|
|
56
|
+
# (all below U+30000, plus the Tags/variation-selector-supplement block U+E0000-E01FF). ~0.1s, ~2.5k keys.
|
|
57
|
+
STRIP = {o: None for o in _EXPLICIT_STRIP}
|
|
58
|
+
STRIP.update({o: None for o in range(0x30000) if _strippable(o)})
|
|
59
|
+
STRIP.update({o: None for o in range(0xE0000, 0xE0200) if _strippable(o)})
|
|
60
|
+
|
|
61
|
+
# Targeted ASCII-lookalike fold for the strong Cyrillic/Greek homoglyphs that appear in secret
|
|
62
|
+
# prefixes/bodies. Cheap partial cross-script coverage so AC1 is HONEST; the full TR39 skeleton is a
|
|
63
|
+
# named non-goal and the residual gap is version-controlled by redact-sentinel.test.sh Test 12.
|
|
64
|
+
# Deliberately NO ->t mapping: no Cyrillic/Greek 't' lookalike is folded (Test 12b pins this gap).
|
|
65
|
+
_CONFUSABLE_PAIRS = {
|
|
66
|
+
# Cyrillic -> Latin
|
|
67
|
+
"а": "a", "е": "e", "о": "o", "р": "p", "с": "c",
|
|
68
|
+
"ѕ": "s", "х": "x", "у": "y", "і": "i", "ј": "j",
|
|
69
|
+
"к": "k", "ԛ": "q", "ԁ": "d", "ѵ": "v", "ԝ": "w",
|
|
70
|
+
"һ": "h", "ɡ": "g",
|
|
71
|
+
# Greek -> Latin
|
|
72
|
+
"ο": "o", "ρ": "p", "ν": "v", "α": "a", "ε": "e",
|
|
73
|
+
"κ": "k", "ι": "i", "υ": "u", "χ": "x",
|
|
74
|
+
}
|
|
75
|
+
CONFUSABLE_MAP = {ord(k): v for k, v in _CONFUSABLE_PAIRS.items()}
|
|
76
|
+
|
|
77
|
+
# All classes compiled with re.ASCII so \b/\w/\s keep grep's C-locale (ASCII) semantics. WITHOUT
|
|
78
|
+
# re.ASCII, Python's Unicode \b makes "Юsk-ant-..." a MISS the bash version caught (a NEW evasion).
|
|
79
|
+
# POSIX [^[:space:]] is hand-translated to \S (a literal [^[:space:]] in Python re is the char set
|
|
80
|
+
# {[ : s p a c e ]} — a silent narrowing).
|
|
81
|
+
F = re.ASCII
|
|
82
|
+
PATTERNS = [
|
|
83
|
+
("github_token", re.compile(r"\b(ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9._~+/=-]{30,}|\bgithub_pat_[A-Za-z0-9._~+/=-]{20,}", F)),
|
|
84
|
+
("JWT", re.compile(r"eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}", F)),
|
|
85
|
+
# Local-part/domain quantifiers are BOUNDED (RFC 5321: local <=64, domain <=255) — an unbounded
|
|
86
|
+
# `+` backtracks O(n^2) on a long email-class run with no '@', hanging the fail-closed gate on a
|
|
87
|
+
# crafted ~1 MiB input (#6045 security review). Bounding keeps it linear without narrowing real emails.
|
|
88
|
+
("email", re.compile(r"\b[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{1,255}\.[A-Za-z]{2,}\b", F)),
|
|
89
|
+
# UUID broadened to [0-9A-Fa-f] (uppercase — a latent lowercase-only gap in the bash baseline).
|
|
90
|
+
("UUID", re.compile(r"\b[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\b", F)),
|
|
91
|
+
("stripe_key", re.compile(r"\b(sk|pk|rk)_(live|test)_[A-Za-z0-9]{16,}\b", F)),
|
|
92
|
+
("stripe_whsec", re.compile(r"\bwhsec_[A-Za-z0-9]{16,}\b", F)),
|
|
93
|
+
("stripe_acct", re.compile(r"\bacct_[A-Za-z0-9]{16,}\b", F)),
|
|
94
|
+
("stripe_cust_pi_seti_sub_in", re.compile(r"\b(cus|pi|seti|sub|in)_[A-Za-z0-9]{14,}\b", F)),
|
|
95
|
+
("IPv4", re.compile(r"\b(([0-9]{1,3})\.){3}[0-9]{1,3}\b", F)),
|
|
96
|
+
# env_var vendor list broadened (+HETZNER|FLAGSMITH|RESEND|TAILSCALE); \S+ (NOT [^[:space:]]+).
|
|
97
|
+
("env_var", re.compile(
|
|
98
|
+
r"\b(DOPPLER|SENTRY|STRIPE|SUPABASE|OPENAI|ANTHROPIC|GITHUB|VERCEL|CLOUDFLARE"
|
|
99
|
+
r"|HETZNER|FLAGSMITH|RESEND|TAILSCALE)_[A-Z_]+=\S+", F)),
|
|
100
|
+
("anthropic_key", re.compile(r"\bsk-ant-[A-Za-z0-9_-]{32,}\b", F)),
|
|
101
|
+
("openai_key", re.compile(r"\bsk-(proj-)?[A-Za-z0-9_-]{20,}\b", F)),
|
|
102
|
+
("supabase_pat", re.compile(r"\bsbp_[a-z0-9]{20,}\b|\b(sb_secret|sb_publishable)_[A-Za-z0-9]{20,}\b", F)),
|
|
103
|
+
# PEM header broadened to catch ENCRYPTED / SSH2 / any [A-Z0-9 ]* qualifier.
|
|
104
|
+
("pem_private_key", re.compile(r"-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----", F)),
|
|
105
|
+
# New Soleur crown-jewel classes (distinctive prefixes, low false-positive). Doppler token kinds:
|
|
106
|
+
# service (st), personal (pt), CLI (ct), service-account (sa), scim, audit. Slack token kinds:
|
|
107
|
+
# bot/user/app/refresh/legacy (baprs) + config (c) + config-refresh (e).
|
|
108
|
+
("doppler_token", re.compile(r"\bdp\.(st|pt|ct|sa|scim|audit)\.[A-Za-z0-9._-]{16,}", F)),
|
|
109
|
+
("slack_token", re.compile(r"\bxox[baprsce]-[A-Za-z0-9-]{10,}", F)),
|
|
110
|
+
# Cloudflare API tokens are exactly 40 chars of [A-Za-z0-9_-] with NO vendor prefix (#6045 item 6).
|
|
111
|
+
# A 40-char lowercase-hex git SHA collides on length (incident PIRs cite SHAs constantly), so the
|
|
112
|
+
# class carries an anti-SHA PREDICATE (below) requiring BOTH an uppercase letter AND a digit. Explicit
|
|
113
|
+
# non-class boundaries (lookaround), NOT \b — '-' is a non-word char and \b mis-anchors at token ends.
|
|
114
|
+
# The boundary ALSO rejects `+` / `/` / `=` adjacency (base64 specials) so a 40-char SEGMENT of a
|
|
115
|
+
# standard-base64 blob (split by +//) does not fire — that FP fail-closes a legitimate embedded asset
|
|
116
|
+
# (#6045 review F4). Real tokens sit on whitespace / quote / colon boundaries; the `=`-prefixed env-var
|
|
117
|
+
# case (`CLOUDFLARE_*_TOKEN=…`) is already caught by the env_var class.
|
|
118
|
+
("cloudflare_token", re.compile(r"(?<![A-Za-z0-9_+/=-])[A-Za-z0-9_-]{40}(?![A-Za-z0-9_+/=-])", F)),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Per-class post-match predicates: a match is kept only if the predicate returns truthy. The bare-length
|
|
122
|
+
# Cloudflare class must be gated or it fires on a 40-char lowercase-hex git SHA and 40-char kebab prose.
|
|
123
|
+
# A real CF API token is high-entropy base64url, so require BOTH an uppercase letter AND a digit — hex
|
|
124
|
+
# SHAs have no uppercase; kebab prose has neither. Deliberate ~0.1% miss (a real token lacking a digit or
|
|
125
|
+
# uppercase) to preserve the engine's low-false-positive posture. No entry = always kept. Shared by the
|
|
126
|
+
# base pass and the decoded-content pass so the gate cannot be bypassed via an encoded blob.
|
|
127
|
+
PREDICATES = {
|
|
128
|
+
"cloudflare_token": lambda t: bool(re.search(r"[A-Z]", t)) and bool(re.search(r"[0-9]", t)),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
PAT_BY_NAME = dict(PATTERNS)
|
|
132
|
+
|
|
133
|
+
# #6045 item 1 — whitespace / newline token-splitting re-scan. strip() KEEPS meaningful whitespace (a
|
|
134
|
+
# documented non-goal of the base pass), so a secret reflowed across a line break by a markdown/PDF
|
|
135
|
+
# renderer survives normalization. For each DISTINCTIVE-prefix class, locate the prefix at a real word
|
|
136
|
+
# boundary and rejoin the token across a BOUNDED number of whitespace runs (a reflowed token is split a
|
|
137
|
+
# few times by a renderer; prose has a whitespace run every few chars, so the bound admits reflow and
|
|
138
|
+
# rejects prose). Excludes the short/ambiguous openai `sk-` prefix and the prefixless Cloudflare class.
|
|
139
|
+
# REFLOW_PREFIXES keys MUST be PATTERNS names (asserted at import — no silent drift).
|
|
140
|
+
REFLOW_WINDOW = 512 # ~2.5x the longest anchored body; a 4096 window would be wasted work.
|
|
141
|
+
_MAX_REFLOW_SPLITS = 4 # rejoin across at most 4 whitespace runs — a reflowed token wrapped a
|
|
142
|
+
# few times is caught; prose (a run every few chars) never reaches a
|
|
143
|
+
# class's min length within 4 joins, so it is rejected (no manufactured FP).
|
|
144
|
+
_MAX_REFLOW_CANDIDATES = 4000 # cap prefix occurrences per class so a prefix-flooded 1 MiB input stays
|
|
145
|
+
# linear (code-to-prd scans attacker-controlled rendered text). Safe only
|
|
146
|
+
# while the 1 MiB MAX input cap dominates; re-evaluate if MAX is raised.
|
|
147
|
+
REFLOW_PREFIXES = {
|
|
148
|
+
"JWT": re.compile(r"eyJ", F),
|
|
149
|
+
"stripe_key": re.compile(r"(sk|pk|rk)_(live|test)_", F),
|
|
150
|
+
"stripe_whsec": re.compile(r"whsec_", F),
|
|
151
|
+
"stripe_acct": re.compile(r"acct_", F),
|
|
152
|
+
"github_token": re.compile(r"(gh[pousr]_|github_pat_)", F),
|
|
153
|
+
"anthropic_key": re.compile(r"sk-ant-", F),
|
|
154
|
+
"supabase_pat": re.compile(r"(sbp_|sb_secret_|sb_publishable_)", F),
|
|
155
|
+
"doppler_token": re.compile(r"dp\.(st|pt|ct|sa|scim|audit)\.", F),
|
|
156
|
+
"slack_token": re.compile(r"xox[baprsce]-", F),
|
|
157
|
+
}
|
|
158
|
+
assert set(REFLOW_PREFIXES) <= {n for n, _ in PATTERNS}, "REFLOW_PREFIXES has an unknown class name"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _is_word(ch):
|
|
162
|
+
# ASCII word char (matches re.ASCII \w): [A-Za-z0-9_]. Confirms a real boundary before a prefix.
|
|
163
|
+
return ch == "_" or (ch.isascii() and ch.isalnum())
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _meta_redact(t):
|
|
167
|
+
# Never emit a full token. Cap the revealed entropy — finding lines hit the transcript.
|
|
168
|
+
if len(t) > 24:
|
|
169
|
+
return f"{t[:4]}***{t[-4:]}"
|
|
170
|
+
if len(t) > 12:
|
|
171
|
+
return f"{t[:4]}***"
|
|
172
|
+
return "***"
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _emit(name, value, offset, tag, seen):
|
|
176
|
+
# Dedup on (class, raw value) so a token found by the base pass is not re-reported by the reflow /
|
|
177
|
+
# decode passes. Offsets are NOT part of the key — base-pass offsets are norm-space, derived-pass
|
|
178
|
+
# offsets are decoded-space, so they are not comparable; dedup affects finding-line noise only, never
|
|
179
|
+
# the exit code. The tag note ALWAYS starts with " (" so the parity grep 'matched pattern [A-Za-z_]+'
|
|
180
|
+
# (which stops at the space) never absorbs it. Returns 1 on a new emit, 0 on a dedup hit.
|
|
181
|
+
key = (name, value)
|
|
182
|
+
if key in seen:
|
|
183
|
+
return 0
|
|
184
|
+
seen.add(key)
|
|
185
|
+
note = f" ({tag})" if tag else ""
|
|
186
|
+
print(f"at offset {offset}: {_meta_redact(value)} matched pattern {name}{note}")
|
|
187
|
+
return 1
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _scan_reflow(norm, seen):
|
|
191
|
+
hits = 0
|
|
192
|
+
for name, prefix_rx in REFLOW_PREFIXES.items():
|
|
193
|
+
full_rx = PAT_BY_NAME[name]
|
|
194
|
+
count = 0
|
|
195
|
+
for pm in prefix_rx.finditer(norm):
|
|
196
|
+
count += 1
|
|
197
|
+
if count > _MAX_REFLOW_CANDIDATES:
|
|
198
|
+
break # bound total prefix occurrences EXAMINED per class (incremented before the skips)
|
|
199
|
+
p = pm.start()
|
|
200
|
+
if p > 0 and _is_word(norm[p - 1]):
|
|
201
|
+
continue # interior substring, not a real boundary — would fabricate a match
|
|
202
|
+
window = norm[p : p + REFLOW_WINDOW]
|
|
203
|
+
parts = re.split(r"\s+", window, maxsplit=_MAX_REFLOW_SPLITS)
|
|
204
|
+
if len(parts) < 2:
|
|
205
|
+
continue # no whitespace in window — the base pass already saw this exact run
|
|
206
|
+
# If the split cap was hit (len == _MAX_REFLOW_SPLITS+1), the last element is an unbounded
|
|
207
|
+
# prose remainder — drop it so prose cannot be glued into a spurious token. A token split at
|
|
208
|
+
# more than _MAX_REFLOW_SPLITS points is missed — a recorded residual (ADR-095). Joining
|
|
209
|
+
# removes the whitespace separators, so the candidate is always shorter than the window.
|
|
210
|
+
joinable = parts[:-1] if len(parts) == _MAX_REFLOW_SPLITS + 1 else parts
|
|
211
|
+
m = full_rx.match("".join(joinable))
|
|
212
|
+
if m:
|
|
213
|
+
hits += _emit(name, m.group(0), p, "whitespace-rejoin", seen)
|
|
214
|
+
return hits
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
# #6045 items 2 & 3 — reversibly-encoded secrets + headerless-PEM private-key body.
|
|
218
|
+
# Classes EXCLUDED from the decoded-content re-scan: weakly-anchored classes manufacture false positives
|
|
219
|
+
# on decoded high-entropy bytes (an IPv4/UUID/email surfacing INSIDE decoded bytes is far likelier a false
|
|
220
|
+
# positive than a real leak — over-redaction fail-closes a legitimate write). cloudflare_token (bare 40-char
|
|
221
|
+
# + predicate) is likewise excluded — a base64 blob is routinely 40 chars.
|
|
222
|
+
_DECODE_SKIP = {"IPv4", "UUID", "email", "cloudflare_token"}
|
|
223
|
+
# Bounds on the decode fan-out: the 1 MiB MAX input cap already bounds total input; these keep adversarial
|
|
224
|
+
# input (many candidate blobs) linear. Safe ONLY while MAX dominates — re-evaluate if MAX is raised.
|
|
225
|
+
_MAX_ENCODED_CANDIDATES = 4000
|
|
226
|
+
_MAX_CANDIDATE_LEN = 200000
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _b64_decode(s):
|
|
230
|
+
# Try standard then url-safe base64; tolerate missing padding. None on failure (never raises).
|
|
231
|
+
for alt in (s, s.replace("-", "+").replace("_", "/")):
|
|
232
|
+
body = alt.rstrip("=")
|
|
233
|
+
try:
|
|
234
|
+
decoded = base64.b64decode(body + "=" * ((-len(body)) % 4), validate=True)
|
|
235
|
+
except (binascii.Error, ValueError):
|
|
236
|
+
decoded = None # a per-candidate decode probe: most candidates are not base64 (ADR-095, 2026-07-06 amendment item 2: decode errors are swallowed, never bubble to exit 2)
|
|
237
|
+
if decoded is not None:
|
|
238
|
+
return decoded
|
|
239
|
+
return None
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _is_der_private_key(raw):
|
|
243
|
+
# #6045 item 3. True iff `raw` looks like an UNENCRYPTED private-key body: an outer DER SEQUENCE whose
|
|
244
|
+
# FIRST inner element is an INTEGER (the version field of PKCS#1 / SEC1 / PKCS#8). A cert / SPKI /
|
|
245
|
+
# EncryptedPrivateKeyInfo opens with an inner SEQUENCE and is REJECTED — those are public/encrypted
|
|
246
|
+
# material and flagging them would fail-close a legitimate write (a public cert in a legal draft is fine).
|
|
247
|
+
# Handles BOTH short-form (EC ~118B, Ed25519 ~48-85B) and long-form (RSA >=128B) length encodings; a
|
|
248
|
+
# long-form-only check would silently miss EC/Ed25519. Residual gap: EncryptedPrivateKeyInfo (inner
|
|
249
|
+
# SEQUENCE) is not caught — a named non-goal recorded in ADR-095. Floor guards tiny innocent DER.
|
|
250
|
+
if len(raw) < 48 or raw[0] != 0x30:
|
|
251
|
+
return False
|
|
252
|
+
lb = raw[1]
|
|
253
|
+
if lb < 0x80:
|
|
254
|
+
content_start = 2
|
|
255
|
+
elif lb in (0x81, 0x82, 0x83, 0x84):
|
|
256
|
+
content_start = 2 + (lb - 0x80)
|
|
257
|
+
else:
|
|
258
|
+
return False
|
|
259
|
+
return content_start < len(raw) and raw[content_start] == 0x02
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _scan_text(text, offset, tag, seen):
|
|
263
|
+
# Re-run only the vendor-prefix/format-ANCHORED secret classes over a derived string (a decoded blob).
|
|
264
|
+
# _DECODE_SKIP excludes the weakly-anchored classes that FP on decoded high-entropy bytes.
|
|
265
|
+
hits = 0
|
|
266
|
+
for name, rx in PATTERNS:
|
|
267
|
+
if name in _DECODE_SKIP:
|
|
268
|
+
continue
|
|
269
|
+
pred = PREDICATES.get(name)
|
|
270
|
+
for m in rx.finditer(text):
|
|
271
|
+
val = m.group(0)
|
|
272
|
+
if pred and not pred(val):
|
|
273
|
+
continue
|
|
274
|
+
hits += _emit(name, val, offset, tag, seen)
|
|
275
|
+
return hits
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _mostly_text(raw):
|
|
279
|
+
# Only re-scan decoded content that is plausibly TEXT. A real base64/hex-encoded secret decodes to
|
|
280
|
+
# printable text; a legitimate embedded BINARY asset (font/wasm/image) decodes to bytes that can carry
|
|
281
|
+
# an INCIDENTAL short-anchored run (e.g. `sk-…`, `whsec_…`) and manufacture a false positive that
|
|
282
|
+
# fail-closes the write (#6045 review F4). A private-key body is binary but is handled by the DER check
|
|
283
|
+
# BEFORE this gate. Empty → not text.
|
|
284
|
+
if not raw:
|
|
285
|
+
return False
|
|
286
|
+
printable = sum(1 for b in raw if 0x20 <= b < 0x7F or b in (0x09, 0x0A, 0x0D))
|
|
287
|
+
return printable / len(raw) >= 0.85
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _scan_encoded(norm, seen):
|
|
291
|
+
hits = 0
|
|
292
|
+
candidates = [] # (blob, offset)
|
|
293
|
+
# (1) single-line / inline base64 runs (inline blobs).
|
|
294
|
+
for m in re.finditer(r"[A-Za-z0-9+/_-]{24,}={0,2}", norm):
|
|
295
|
+
candidates.append((m.group(0), m.start()))
|
|
296
|
+
# (2) multi-line base64 BLOCKS: consecutive whole-line base64 joined into one blob — real PEM/DER
|
|
297
|
+
# bodies are 64-char line-wrapped, and k8s Secret / Helm `data:` values are INDENTED, so `.strip()`
|
|
298
|
+
# each line (not just rstrip) before the fullmatch or an indented wrapped body never assembles.
|
|
299
|
+
block_lines = []
|
|
300
|
+
block_off = None
|
|
301
|
+
pos = 0
|
|
302
|
+
for line in norm.splitlines(keepends=True):
|
|
303
|
+
stripped = line.strip()
|
|
304
|
+
if re.fullmatch(r"[A-Za-z0-9+/_-]{16,}={0,2}", stripped):
|
|
305
|
+
if not block_lines:
|
|
306
|
+
block_off = pos
|
|
307
|
+
block_lines.append(stripped)
|
|
308
|
+
else:
|
|
309
|
+
if len(block_lines) >= 2:
|
|
310
|
+
candidates.append(("".join(block_lines), block_off))
|
|
311
|
+
block_lines = []
|
|
312
|
+
pos += len(line)
|
|
313
|
+
if len(block_lines) >= 2:
|
|
314
|
+
candidates.append(("".join(block_lines), block_off))
|
|
315
|
+
|
|
316
|
+
b64_count = 0
|
|
317
|
+
for blob, off in candidates:
|
|
318
|
+
if b64_count >= _MAX_ENCODED_CANDIDATES:
|
|
319
|
+
break
|
|
320
|
+
if len(blob) > _MAX_CANDIDATE_LEN:
|
|
321
|
+
continue
|
|
322
|
+
b64_count += 1
|
|
323
|
+
raw = _b64_decode(blob) # never raises — returns None on failure (no exit-2 bubble)
|
|
324
|
+
if raw is None:
|
|
325
|
+
continue
|
|
326
|
+
if _is_der_private_key(raw): # binary key body — DER-checked BEFORE the text gate
|
|
327
|
+
hits += _emit("pem_key_body", blob, off, "headerless-PEM-DER", seen)
|
|
328
|
+
if _mostly_text(raw):
|
|
329
|
+
hits += _scan_text(raw.decode("utf-8", "replace"), off, "decoded base64", seen)
|
|
330
|
+
# hex runs (even length only — an odd run cannot be whole bytes).
|
|
331
|
+
hex_count = 0
|
|
332
|
+
for m in re.finditer(r"\b[0-9A-Fa-f]{32,}\b", norm):
|
|
333
|
+
if hex_count >= _MAX_ENCODED_CANDIDATES:
|
|
334
|
+
break
|
|
335
|
+
blob = m.group(0)
|
|
336
|
+
if len(blob) % 2 or len(blob) > _MAX_CANDIDATE_LEN:
|
|
337
|
+
continue
|
|
338
|
+
hex_count += 1
|
|
339
|
+
try:
|
|
340
|
+
raw = bytes.fromhex(blob)
|
|
341
|
+
except ValueError:
|
|
342
|
+
raw = None # defensive: the regex and the parity filter above leave only valid even-length hex, so this does not fire today (ADR-095, 2026-07-06 amendment item 2: decode errors never bubble to exit 2)
|
|
343
|
+
if raw is None:
|
|
344
|
+
continue
|
|
345
|
+
if _mostly_text(raw):
|
|
346
|
+
hits += _scan_text(raw.decode("utf-8", "replace"), m.start(), "decoded hex", seen)
|
|
347
|
+
# percent-encoding: unquote once over the normalized text (bounded like the base64/hex paths; a
|
|
348
|
+
# >_MAX_CANDIDATE_LEN document skips the decoded layer — its raw form is still scanned by the base pass).
|
|
349
|
+
if re.search(r"%[0-9A-Fa-f]{2}", norm) and len(norm) <= _MAX_CANDIDATE_LEN:
|
|
350
|
+
decoded = urllib.parse.unquote(norm)
|
|
351
|
+
if decoded != norm:
|
|
352
|
+
hits += _scan_text(decoded, 0, "decoded percent", seen)
|
|
353
|
+
return hits
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def scan(path):
|
|
357
|
+
if not os.path.isfile(path) or not os.access(path, os.R_OK):
|
|
358
|
+
sys.stderr.write(f"redact-engine: file not readable: {path}\n")
|
|
359
|
+
return 2
|
|
360
|
+
with open(path, "rb") as fh:
|
|
361
|
+
raw = fh.read()
|
|
362
|
+
if len(raw) > MAX:
|
|
363
|
+
print(f"SYNTHETIC HIGH: input exceeds {MAX} bytes ({len(raw)}) — fail closed")
|
|
364
|
+
return 1
|
|
365
|
+
stripped = raw.decode("utf-8", "replace").translate(STRIP)
|
|
366
|
+
# strip -> NFKC -> strip again: NFKC can FOLD a compatibility character into a strippable one that
|
|
367
|
+
# was not present in the raw input — e.g. U+FFA0 (halfwidth Hangul filler) -> U+1160 (Hangul filler,
|
|
368
|
+
# in STRIP). The second strip closes that re-opened splitter; it is idempotent on already-clean text.
|
|
369
|
+
# Then the targeted confusable fold.
|
|
370
|
+
norm = unicodedata.normalize("NFKC", stripped).translate(STRIP).translate(CONFUSABLE_MAP)
|
|
371
|
+
if len(norm.encode("utf-8")) > MAX: # post-NFKC re-check (NFKC can expand 1 cp -> up to 18)
|
|
372
|
+
print(f"SYNTHETIC HIGH: normalized input exceeds {MAX} bytes — fail closed")
|
|
373
|
+
return 1
|
|
374
|
+
seen = set()
|
|
375
|
+
hits = 0
|
|
376
|
+
# Base pass over the whole normalized string (real offsets). PREDICATES gate the bare-length classes.
|
|
377
|
+
for name, rx in PATTERNS:
|
|
378
|
+
pred = PREDICATES.get(name)
|
|
379
|
+
for m in rx.finditer(norm):
|
|
380
|
+
val = m.group(0)
|
|
381
|
+
if pred and not pred(val):
|
|
382
|
+
continue
|
|
383
|
+
hits += _emit(name, val, m.start(), "", seen)
|
|
384
|
+
hits += _scan_reflow(norm, seen) # #6045 item 1 — whitespace/newline token-splitting re-scan
|
|
385
|
+
hits += _scan_encoded(norm, seen) # #6045 items 2 & 3 — reversibly-encoded + headerless-PEM DER body
|
|
386
|
+
return 1 if hits else 0
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def main():
|
|
390
|
+
if len(sys.argv) != 2:
|
|
391
|
+
sys.stderr.write("usage: redact-engine.py <path>\n")
|
|
392
|
+
return 2
|
|
393
|
+
try:
|
|
394
|
+
return scan(sys.argv[1])
|
|
395
|
+
except Exception as e: # any engine crash = cannot-evaluate, fail closed
|
|
396
|
+
tb, own, last = e.__traceback__, None, None
|
|
397
|
+
while tb is not None:
|
|
398
|
+
last = tb
|
|
399
|
+
if tb.tb_frame.f_code.co_filename == __file__:
|
|
400
|
+
own = tb
|
|
401
|
+
tb = tb.tb_next
|
|
402
|
+
tb = own or last
|
|
403
|
+
where = "" if tb is None else " at %s:%s" % (tb.tb_frame.f_code.co_name, tb.tb_lineno)
|
|
404
|
+
# class name and innermost frame in this file only: exception text can carry scanned content (mirrors founder-check _locator)
|
|
405
|
+
# a closed stderr (None when fd 2 is closed) must not turn the fail-closed rc 2 into an uncaught crash (rc 1 reads as "redaction needed")
|
|
406
|
+
with contextlib.suppress(OSError, ValueError, AttributeError):
|
|
407
|
+
sys.stderr.write(f"redact-engine: internal error: {type(e).__name__}{where}\n")
|
|
408
|
+
return 2
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
if __name__ == "__main__":
|
|
412
|
+
sys.exit(main())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Pre-write redaction sentinel for /soleur:incident, code-to-prd Layer 2, and the
|
|
3
|
+
# legal-generate gate. Thin shim (#5987) — the engine is now redact-engine.py.
|
|
4
|
+
#
|
|
5
|
+
# Contract preserved 1:1 (argv, exit codes, output shape) so all consumers are unchanged:
|
|
6
|
+
# 0 = clean — no matches found
|
|
7
|
+
# 1 = redaction needed — at least one match found (incl. synthetic HIGH on oversize input)
|
|
8
|
+
# 2 = cannot-evaluate — missing/unreadable file, no python3, engine crash, or any non-{0,1,2}
|
|
9
|
+
#
|
|
10
|
+
# Output format (per FR3, now capped by the engine's meta-redaction — never a full token):
|
|
11
|
+
# at offset N: <=4-prefix>***<=4-suffix> matched pattern <class>
|
|
12
|
+
#
|
|
13
|
+
# Exit is NORMALIZED to {0,1,2}: any engine-cannot-run/unexpected code -> 2 (cannot-evaluate),
|
|
14
|
+
# which every consumer already treats as fail-closed (code-to-prd Layer 2, incident Phase 6,
|
|
15
|
+
# dry-run any-nonzero). python3 absent -> 2 (NOT 1: exit 1 means "matches found", which would give
|
|
16
|
+
# a false "secrets found" message and trap the incident operator in an unsatisfiable redact loop).
|
|
17
|
+
#
|
|
18
|
+
# The hardened engine defeats compatibility-char / invisible / bidi / control / invalid-byte /
|
|
19
|
+
# soft-hyphen / prefix-homoglyph evasion (matching runs over strip -> NFKC -> strip -> confusable-fold;
|
|
20
|
+
# invisibles are stripped by Unicode category, not a hand-picked list), whitespace / newline
|
|
21
|
+
# token-splitting (#6045 item 1 — bounded-rejoin re-scan), the bare Cloudflare 40-char token class
|
|
22
|
+
# (#6045 item 6), reversibly-encoded secrets (base64/hex/percent decode + block assembly, #6045 item 2),
|
|
23
|
+
# and a headerless-PEM private-key DER body (#6045 item 3). It does NOT defeat the full cross-script
|
|
24
|
+
# homoglyph space (TR39 skeleton), an EncryptedPrivateKeyInfo body, MULTIPLY-encoded secrets (decode is
|
|
25
|
+
# single-level), a token reflowed across >4 whitespace runs, or unprefixed/high-entropy tokens — each a
|
|
26
|
+
# named non-goal / recorded residual (see ADR-095).
|
|
27
|
+
set -uo pipefail
|
|
28
|
+
|
|
29
|
+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
30
|
+
|
|
31
|
+
command -v python3 >/dev/null 2>&1 || {
|
|
32
|
+
echo "redact-sentinel: python3 not found — failing closed (exit 2)." >&2
|
|
33
|
+
exit 2
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
python3 "${DIR}/redact-engine.py" "$@"
|
|
37
|
+
rc=$?
|
|
38
|
+
case "${rc}" in
|
|
39
|
+
0 | 1 | 2) exit "${rc}" ;;
|
|
40
|
+
*)
|
|
41
|
+
echo "redact-sentinel: engine exit ${rc} normalized to 2 (cannot-evaluate)." >&2
|
|
42
|
+
exit 2
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "{{TITLE}}"
|
|
3
|
+
date: {{DATE}}
|
|
4
|
+
incident_pr: {{INCIDENT_PR}}
|
|
5
|
+
incident_window: "{{INCIDENT_WINDOW}}"
|
|
6
|
+
recovery_at: "{{RECOVERY_AT}}"
|
|
7
|
+
suspected_change: "{{SUSPECTED_CHANGE}}"
|
|
8
|
+
brand_survival_threshold: {{BRAND_SURVIVAL_THRESHOLD}}
|
|
9
|
+
status: {{STATUS}}
|
|
10
|
+
triggers:
|
|
11
|
+
{{TRIGGERS_LIST}}
|
|
12
|
+
art_33_triggered: {{ART_33_TRIGGERED}}
|
|
13
|
+
art_34_triggered: {{ART_34_TRIGGERED}}
|
|
14
|
+
art_33_deadline: "{{ART_33_DEADLINE}}"
|
|
15
|
+
{{CLASSIFICATION_OVERRIDE_BLOCK}}
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Actor key
|
|
19
|
+
|
|
20
|
+
- `agent` — Claude Code did this autonomously (no operator ack required).
|
|
21
|
+
- `agent-with-ack` — Claude Code did this AFTER operator confirmed via menu option per `hr-menu-option-ack-not-prod-write-auth`.
|
|
22
|
+
- `human` — Operator did this directly.
|
|
23
|
+
|
|
24
|
+
{{SECRET_LEAK_PREAMBLE}}
|
|
25
|
+
|
|
26
|
+
# Incident Overview
|
|
27
|
+
|
|
28
|
+
{{INCIDENT_OVERVIEW}}
|
|
29
|
+
|
|
30
|
+
## Status
|
|
31
|
+
|
|
32
|
+
{{STATUS}} — one of `resolved` / `unresolved but ended` / `ongoing`. Mirrors the `status:` frontmatter above; do not introduce a second source of truth.
|
|
33
|
+
|
|
34
|
+
## Symptom
|
|
35
|
+
|
|
36
|
+
{{SYMPTOM}}
|
|
37
|
+
|
|
38
|
+
## Incident Timeline
|
|
39
|
+
|
|
40
|
+
- **Start time (detected):** {{DETECTED_AT}}
|
|
41
|
+
- **End time (recovered):** {{RECOVERY_AT}}
|
|
42
|
+
- **Duration (MTTR):** {{MTTR}}
|
|
43
|
+
|
|
44
|
+
Order of events (load-bearing: the redaction sentinel scans this table; the Actor key feeds the Actor column):
|
|
45
|
+
|
|
46
|
+
| Actor | Time (UTC) | Action |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| human | {{DETECTED_AT}} | Incident detected. |
|
|
49
|
+
|
|
50
|
+
## Participants and Systems Involved
|
|
51
|
+
|
|
52
|
+
{{PARTICIPANTS}}
|
|
53
|
+
|
|
54
|
+
## Detection (+ MTTD)
|
|
55
|
+
|
|
56
|
+
- **How detected:** {{DETECTION_METHOD}} — monitoring system vs. external/manual report.
|
|
57
|
+
- **MTTD (mean time to detect):** {{MTTD}}
|
|
58
|
+
|
|
59
|
+
## Triggered by
|
|
60
|
+
|
|
61
|
+
{{TRIGGERED_BY}} — one of user / system / market movement / provider.
|
|
62
|
+
|
|
63
|
+
## Root-cause hypothesis (triage)
|
|
64
|
+
|
|
65
|
+
Triage-time competing hypotheses; the post-resolution final root cause lives in the 5-Whys section below.
|
|
66
|
+
|
|
67
|
+
| Hypothesis | Supporting evidence | Disconfirming evidence | Status |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| {{ROOT_CAUSE_HYPOTHESIS}} | TBD | TBD | TBD |
|
|
70
|
+
|
|
71
|
+
## Resolution
|
|
72
|
+
|
|
73
|
+
{{RESOLUTION}}
|
|
74
|
+
|
|
75
|
+
## Recovery verification
|
|
76
|
+
|
|
77
|
+
TBD — describe the artifact or probe proving recovery. Cite a green workflow run, dashboard screenshot, or query result.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
# Incident Post-Mortem Analysis
|
|
82
|
+
|
|
83
|
+
## Root Cause(s) — 5-Whys
|
|
84
|
+
|
|
85
|
+
{{ROOT_CAUSE_5WHYS}}
|
|
86
|
+
|
|
87
|
+
## Versions of Components
|
|
88
|
+
|
|
89
|
+
- **Version(s) that triggered the outage:** {{VERSION_TRIGGERED}}
|
|
90
|
+
- **Version(s) that restored the service:** {{VERSION_RESTORED}}
|
|
91
|
+
|
|
92
|
+
## Impact details
|
|
93
|
+
|
|
94
|
+
### Services Impacted
|
|
95
|
+
|
|
96
|
+
{{SERVICES_IMPACTED}}
|
|
97
|
+
|
|
98
|
+
### Customer Impact (by role)
|
|
99
|
+
|
|
100
|
+
Per learning `2026-05-06-user-impact-section-by-role-not-surface.md` — enumerate by USER ROLE, not by surface. This is the canonical "Customer Impact"; do NOT add a second free-text Customer Impact block.
|
|
101
|
+
|
|
102
|
+
- Prospect: TBD
|
|
103
|
+
- Authenticated app user: TBD
|
|
104
|
+
- Legal-document signer: TBD
|
|
105
|
+
- Admin via Access: TBD
|
|
106
|
+
- Billing customer: TBD
|
|
107
|
+
- OAuth installation owner: TBD
|
|
108
|
+
|
|
109
|
+
### Revenue Impact
|
|
110
|
+
|
|
111
|
+
{{REVENUE_IMPACT}}
|
|
112
|
+
|
|
113
|
+
### Team Impact
|
|
114
|
+
|
|
115
|
+
{{TEAM_IMPACT}}
|
|
116
|
+
|
|
117
|
+
## Lessons Learned
|
|
118
|
+
|
|
119
|
+
### Where we got lucky
|
|
120
|
+
|
|
121
|
+
{{LUCKY}}
|
|
122
|
+
|
|
123
|
+
### What went well
|
|
124
|
+
|
|
125
|
+
{{WENT_WELL}}
|
|
126
|
+
|
|
127
|
+
### What went wrong
|
|
128
|
+
|
|
129
|
+
{{WENT_WRONG}}
|
|
130
|
+
|
|
131
|
+
## Action Items & Follow-ups
|
|
132
|
+
|
|
133
|
+
Every action item and follow-up so this incident cannot recur (save logs, add tests, set up alerts, automation, documentation, code sweeps, PRs).
|
|
134
|
+
|
|
135
|
+
**Each row MUST cite a filed GitHub issue number.** A bare bullet or a `TBD`/`(none)` placeholder is not allowed — file the issue first (`gh issue create`, cross-referencing the source PR in the body), then record its number here. An item with no `#NNNN` is shelf-ware that rots the moment the session ends; the `/ship` Incident-PIR gate blocks merge on any item that lacks an issue reference. If there are genuinely zero follow-ups, write exactly `No action items — incident fully resolved in the source PR with no residual work.` as a line of its own, with no emphasis — the permitted no-item form is plain so that the markdown linter (MD049, emphasis-style) cannot restyle it; the `/ship` gate accepts the plain line (and the underscore/asterisk-wrapped spellings shipped before this template changed).
|
|
136
|
+
|
|
137
|
+
| Issue | Action | Status |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| #{{ACTION_ITEM_ISSUE}} | {{ACTION_ITEM_DESC}} | open |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Synthetic dashboard outage for dry-run",
|
|
3
|
+
"detected_at": "2026-05-14T10:00:00Z",
|
|
4
|
+
"recovery_at": "2026-05-14T10:20:00Z",
|
|
5
|
+
"monitoring_detected_at": "2026-05-14T10:05:00Z",
|
|
6
|
+
"detection_method": "monitoring",
|
|
7
|
+
"triggered_by": "system",
|
|
8
|
+
"symptom": "Synthetic incident: dashboard error.tsx renders blank for authenticated app users after deploy",
|
|
9
|
+
"incident_overview": "Synthetic: authenticated dashboard rendered the error boundary after a deploy; recovered by a follow-up build.",
|
|
10
|
+
"suspected_change": "PR #9999 (synthetic)",
|
|
11
|
+
"resolution": "Synthetic: triggered a new release build that re-inlined the corrected bundle; canary swap verified.",
|
|
12
|
+
"version_triggered": "soleur-web-platform synthetic v0.0.1 (PR #9999)",
|
|
13
|
+
"version_restored": "soleur-web-platform synthetic v0.0.2",
|
|
14
|
+
"participants": "Operator (single founder); systems: synthetic web-platform, synthetic canary harness",
|
|
15
|
+
"services_impacted": "Synthetic authenticated dashboard surface",
|
|
16
|
+
"revenue_impact": "Unknown / N/A (synthetic fixture)",
|
|
17
|
+
"team_impact": "Unknown / N/A (synthetic fixture)",
|
|
18
|
+
"affected_user_count": 1,
|
|
19
|
+
"brand_survival_threshold": "single-user incident",
|
|
20
|
+
"art_33": {
|
|
21
|
+
"data_categories_breached": [],
|
|
22
|
+
"risk_to_subjects": "none"
|
|
23
|
+
},
|
|
24
|
+
"triggers": ["dashboard error boundary", "module-load throw"],
|
|
25
|
+
"status": "open"
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Synthetic api-key leak for dry-run",
|
|
3
|
+
"detected_at": "2026-05-14T10:00:00Z",
|
|
4
|
+
"recovery_at": "",
|
|
5
|
+
"monitoring_detected_at": "",
|
|
6
|
+
"detection_method": "external",
|
|
7
|
+
"triggered_by": "system",
|
|
8
|
+
"symptom": "Synthetic incident: STRIPE_SECRET_KEY committed to public repo via debug log",
|
|
9
|
+
"incident_overview": "Synthetic: a Stripe secret was committed to a public repo via a debug log line; revoke-first path exercised.",
|
|
10
|
+
"suspected_change": "PR #9998 (synthetic)",
|
|
11
|
+
"resolution": "Synthetic: revoked the leaked credential at the issuer, then rotated and re-deployed.",
|
|
12
|
+
"version_triggered": "soleur-web-platform synthetic v0.0.1 (PR #9998)",
|
|
13
|
+
"version_restored": "N/A — not yet restored",
|
|
14
|
+
"participants": "Operator (single founder); systems: synthetic web-platform, Stripe",
|
|
15
|
+
"services_impacted": "Synthetic billing credential surface",
|
|
16
|
+
"revenue_impact": "Unknown / N/A (synthetic fixture)",
|
|
17
|
+
"team_impact": "Unknown / N/A (synthetic fixture)",
|
|
18
|
+
"affected_user_count": 0,
|
|
19
|
+
"brand_survival_threshold": "single-user incident",
|
|
20
|
+
"art_33": {
|
|
21
|
+
"data_categories_breached": ["email", "userId"],
|
|
22
|
+
"risk_to_subjects": "high"
|
|
23
|
+
},
|
|
24
|
+
"triggers": ["api_key_leaked"],
|
|
25
|
+
"status": "open"
|
|
26
|
+
}
|