@soleur/opencode 0.0.0-stage → 1.0.0

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.
Files changed (812) hide show
  1. package/.claude-plugin/agents.manifest.json +476 -0
  2. package/.claude-plugin/plugin.json +42 -0
  3. package/README.md +44 -2
  4. package/agents/community/.gitkeep +0 -0
  5. package/agents/engineering/cto.md +87 -0
  6. package/agents/engineering/design/ddd-architect.md +95 -0
  7. package/agents/engineering/discovery/agent-finder.md +214 -0
  8. package/agents/engineering/discovery/functional-discovery.md +182 -0
  9. package/agents/engineering/infra/infra-security.md +151 -0
  10. package/agents/engineering/infra/platform-strategist.md +109 -0
  11. package/agents/engineering/infra/terraform-architect.md +171 -0
  12. package/agents/engineering/prompt-engineer.md +37 -0
  13. package/agents/engineering/research/best-practices-researcher.md +115 -0
  14. package/agents/engineering/research/framework-docs-researcher.md +93 -0
  15. package/agents/engineering/research/git-history-analyzer.md +53 -0
  16. package/agents/engineering/research/learnings-researcher.md +285 -0
  17. package/agents/engineering/research/repo-research-analyst.md +118 -0
  18. package/agents/engineering/review/agent-native-reviewer.md +279 -0
  19. package/agents/engineering/review/architecture-strategist.md +61 -0
  20. package/agents/engineering/review/code-quality-analyst.md +92 -0
  21. package/agents/engineering/review/code-simplicity-reviewer.md +109 -0
  22. package/agents/engineering/review/data-integrity-guardian.md +82 -0
  23. package/agents/engineering/review/data-migration-expert.md +98 -0
  24. package/agents/engineering/review/deployment-verification-agent.md +183 -0
  25. package/agents/engineering/review/dhh-rails-reviewer.md +46 -0
  26. package/agents/engineering/review/kieran-rails-reviewer.md +87 -0
  27. package/agents/engineering/review/legacy-code-expert.md +55 -0
  28. package/agents/engineering/review/observability-coverage-reviewer.md +147 -0
  29. package/agents/engineering/review/pattern-recognition-specialist.md +59 -0
  30. package/agents/engineering/review/performance-oracle.md +130 -0
  31. package/agents/engineering/review/security-sentinel.md +167 -0
  32. package/agents/engineering/review/semgrep-sast.md +36 -0
  33. package/agents/engineering/review/test-design-reviewer.md +150 -0
  34. package/agents/engineering/review/user-impact-reviewer.md +70 -0
  35. package/agents/engineering/workflow/pr-comment-resolver.md +69 -0
  36. package/agents/finance/budget-analyst.md +29 -0
  37. package/agents/finance/cfo.md +44 -0
  38. package/agents/finance/financial-reporter.md +30 -0
  39. package/agents/finance/revenue-analyst.md +30 -0
  40. package/agents/legal/clo.md +115 -0
  41. package/agents/legal/legal-compliance-auditor.md +110 -0
  42. package/agents/legal/legal-document-generator.md +134 -0
  43. package/agents/marketing/analytics-analyst.md +25 -0
  44. package/agents/marketing/brand-architect.md +181 -0
  45. package/agents/marketing/cmo.md +76 -0
  46. package/agents/marketing/conversion-optimizer.md +19 -0
  47. package/agents/marketing/copywriter.md +20 -0
  48. package/agents/marketing/fact-checker.md +70 -0
  49. package/agents/marketing/growth-strategist.md +145 -0
  50. package/agents/marketing/paid-media-strategist.md +28 -0
  51. package/agents/marketing/pricing-strategist.md +17 -0
  52. package/agents/marketing/programmatic-seo-specialist.md +25 -0
  53. package/agents/marketing/retention-strategist.md +23 -0
  54. package/agents/marketing/seo-aeo-analyst.md +164 -0
  55. package/agents/operations/coo.md +50 -0
  56. package/agents/operations/ops-advisor.md +78 -0
  57. package/agents/operations/ops-provisioner.md +101 -0
  58. package/agents/operations/ops-research.md +52 -0
  59. package/agents/operations/service-automator.md +278 -0
  60. package/agents/product/business-validator.md +206 -0
  61. package/agents/product/competitive-intelligence.md +89 -0
  62. package/agents/product/cpo.md +63 -0
  63. package/agents/product/design/ux-design-lead.md +209 -0
  64. package/agents/product/spec-flow-analyzer.md +122 -0
  65. package/agents/sales/cro.md +44 -0
  66. package/agents/sales/deal-architect.md +29 -0
  67. package/agents/sales/outbound-strategist.md +29 -0
  68. package/agents/sales/pipeline-analyst.md +29 -0
  69. package/agents/support/cco.md +48 -0
  70. package/agents/support/community-manager.md +570 -0
  71. package/agents/support/ticket-triage.md +70 -0
  72. package/bin/soleur-opencode.js +29 -0
  73. package/commands/go.md +659 -0
  74. package/commands/help.md +273 -0
  75. package/commands/sync.md +977 -0
  76. package/commands/version.md +103 -0
  77. package/hooks/antigravity-hooks.json +84 -0
  78. package/hooks/antigravity-session-start.sh +15 -0
  79. package/hooks/bare-root-cwd-guard.sh +131 -0
  80. package/hooks/browser-snapshot-credential-guard.sh +200 -0
  81. package/hooks/codex-session-start.sh +9 -0
  82. package/hooks/compaction-state.sh +525 -0
  83. package/hooks/destructive-command-guard.sh +1387 -0
  84. package/hooks/devin-session-start.sh +90 -0
  85. package/hooks/ext-filter-detect.sh +171 -0
  86. package/hooks/hooks.json +133 -0
  87. package/hooks/lib/hook-tool-kind.sh +24 -0
  88. package/hooks/lib/log-rotation.sh +176 -0
  89. package/hooks/lib/shell-argv.pl +576 -0
  90. package/hooks/memory-backstop.sh +1114 -0
  91. package/hooks/operator-stage-approval.sh +391 -0
  92. package/hooks/stop-hook.sh +367 -0
  93. package/hooks/unkept-promise-hook.sh +383 -0
  94. package/hooks/version-banner.sh +127 -0
  95. package/hooks/welcome-hook.sh +50 -0
  96. package/install.js +108 -0
  97. package/lib/agent-registry.ts +154 -0
  98. package/lib/c4-canonical-cli.mjs +34 -0
  99. package/lib/c4-canonical.mjs +46 -0
  100. package/lib/c4-from-components.ts +554 -0
  101. package/lib/go-routing.ts +81 -0
  102. package/lib/grok-inspect-contract.ts +311 -0
  103. package/lib/harness-model-map.ts +101 -0
  104. package/lib/harness-parity.ts +726 -0
  105. package/lib/harness-tool-map.ts +180 -0
  106. package/lib/harness.ts +716 -0
  107. package/lib/heartbeat-live-reconcile.ts +1153 -0
  108. package/lib/heartbeat-manifest.ts +342 -0
  109. package/lib/kb-coverage.ts +252 -0
  110. package/lib/pr-merge-poll.ts +103 -0
  111. package/lib/workflow-fidelity.ts +388 -0
  112. package/opencode/INSTRUCTIONS.md +152 -0
  113. package/opencode/agents/engineering/cto.md +10 -0
  114. package/opencode/agents/engineering/design/ddd-architect.md +10 -0
  115. package/opencode/agents/engineering/discovery/agent-finder.md +10 -0
  116. package/opencode/agents/engineering/discovery/functional-discovery.md +10 -0
  117. package/opencode/agents/engineering/infra/infra-security.md +10 -0
  118. package/opencode/agents/engineering/infra/platform-strategist.md +10 -0
  119. package/opencode/agents/engineering/infra/terraform-architect.md +10 -0
  120. package/opencode/agents/engineering/prompt-engineer.md +10 -0
  121. package/opencode/agents/engineering/research/best-practices-researcher.md +10 -0
  122. package/opencode/agents/engineering/research/framework-docs-researcher.md +10 -0
  123. package/opencode/agents/engineering/research/git-history-analyzer.md +10 -0
  124. package/opencode/agents/engineering/research/learnings-researcher.md +10 -0
  125. package/opencode/agents/engineering/research/repo-research-analyst.md +10 -0
  126. package/opencode/agents/engineering/review/agent-native-reviewer.md +10 -0
  127. package/opencode/agents/engineering/review/architecture-strategist.md +10 -0
  128. package/opencode/agents/engineering/review/code-quality-analyst.md +10 -0
  129. package/opencode/agents/engineering/review/code-simplicity-reviewer.md +10 -0
  130. package/opencode/agents/engineering/review/data-integrity-guardian.md +10 -0
  131. package/opencode/agents/engineering/review/data-migration-expert.md +10 -0
  132. package/opencode/agents/engineering/review/deployment-verification-agent.md +10 -0
  133. package/opencode/agents/engineering/review/dhh-rails-reviewer.md +10 -0
  134. package/opencode/agents/engineering/review/kieran-rails-reviewer.md +10 -0
  135. package/opencode/agents/engineering/review/legacy-code-expert.md +10 -0
  136. package/opencode/agents/engineering/review/observability-coverage-reviewer.md +10 -0
  137. package/opencode/agents/engineering/review/pattern-recognition-specialist.md +10 -0
  138. package/opencode/agents/engineering/review/performance-oracle.md +10 -0
  139. package/opencode/agents/engineering/review/security-sentinel.md +10 -0
  140. package/opencode/agents/engineering/review/semgrep-sast.md +10 -0
  141. package/opencode/agents/engineering/review/test-design-reviewer.md +10 -0
  142. package/opencode/agents/engineering/review/user-impact-reviewer.md +10 -0
  143. package/opencode/agents/engineering/workflow/pr-comment-resolver.md +10 -0
  144. package/opencode/agents/finance/budget-analyst.md +10 -0
  145. package/opencode/agents/finance/cfo.md +10 -0
  146. package/opencode/agents/finance/financial-reporter.md +10 -0
  147. package/opencode/agents/finance/revenue-analyst.md +10 -0
  148. package/opencode/agents/legal/clo.md +10 -0
  149. package/opencode/agents/legal/legal-compliance-auditor.md +10 -0
  150. package/opencode/agents/legal/legal-document-generator.md +10 -0
  151. package/opencode/agents/marketing/analytics-analyst.md +10 -0
  152. package/opencode/agents/marketing/brand-architect.md +10 -0
  153. package/opencode/agents/marketing/cmo.md +10 -0
  154. package/opencode/agents/marketing/conversion-optimizer.md +10 -0
  155. package/opencode/agents/marketing/copywriter.md +10 -0
  156. package/opencode/agents/marketing/fact-checker.md +10 -0
  157. package/opencode/agents/marketing/growth-strategist.md +10 -0
  158. package/opencode/agents/marketing/paid-media-strategist.md +10 -0
  159. package/opencode/agents/marketing/pricing-strategist.md +10 -0
  160. package/opencode/agents/marketing/programmatic-seo-specialist.md +10 -0
  161. package/opencode/agents/marketing/retention-strategist.md +10 -0
  162. package/opencode/agents/marketing/seo-aeo-analyst.md +10 -0
  163. package/opencode/agents/operations/coo.md +10 -0
  164. package/opencode/agents/operations/ops-advisor.md +10 -0
  165. package/opencode/agents/operations/ops-provisioner.md +10 -0
  166. package/opencode/agents/operations/ops-research.md +10 -0
  167. package/opencode/agents/operations/service-automator.md +10 -0
  168. package/opencode/agents/product/business-validator.md +10 -0
  169. package/opencode/agents/product/competitive-intelligence.md +10 -0
  170. package/opencode/agents/product/cpo.md +10 -0
  171. package/opencode/agents/product/design/ux-design-lead.md +10 -0
  172. package/opencode/agents/product/spec-flow-analyzer.md +10 -0
  173. package/opencode/agents/sales/cro.md +10 -0
  174. package/opencode/agents/sales/deal-architect.md +10 -0
  175. package/opencode/agents/sales/outbound-strategist.md +10 -0
  176. package/opencode/agents/sales/pipeline-analyst.md +10 -0
  177. package/opencode/agents/support/cco.md +10 -0
  178. package/opencode/agents/support/community-manager.md +10 -0
  179. package/opencode/agents/support/ticket-triage.md +10 -0
  180. package/opencode/commands/go.md +10 -0
  181. package/opencode/commands/help.md +10 -0
  182. package/opencode/commands/sync.md +10 -0
  183. package/opencode/commands/version.md +11 -0
  184. package/opencode/opencode.json +8 -0
  185. package/opencode/plugin.ts +493 -0
  186. package/package.json +36 -4
  187. package/scripts/admin-merge-ready.sh +545 -0
  188. package/scripts/admin-merge-ready.test.sh +753 -0
  189. package/scripts/affected-tests.sh +908 -0
  190. package/scripts/affected-tests.test.sh +1797 -0
  191. package/scripts/alpha-metrics.sh +78 -0
  192. package/scripts/alpha-metrics.test.sh +115 -0
  193. package/scripts/ancestor-signal-helpers.baseline.txt +16 -0
  194. package/scripts/audit-flag-flip.sh +107 -0
  195. package/scripts/check-red-on-main.sh +316 -0
  196. package/scripts/ci-head-verdict.sh +363 -0
  197. package/scripts/cloud-detect.sh +193 -0
  198. package/scripts/delegation-verify.sh +657 -0
  199. package/scripts/delegation-verify.test.sh +1424 -0
  200. package/scripts/deploy-arm.sh +475 -0
  201. package/scripts/domain-model-drift.sh +419 -0
  202. package/scripts/emit-decision.sh +144 -0
  203. package/scripts/emit-decision.test.sh +144 -0
  204. package/scripts/ext-filter-divergence-corpus.txt +15 -0
  205. package/scripts/ext-filter-divergence.sh +189 -0
  206. package/scripts/generate-c4-from-components.ts +442 -0
  207. package/scripts/grok-fidelity-gate.sh +68 -0
  208. package/scripts/grok-pre-push-gate.sh +232 -0
  209. package/scripts/harness-discovery-smoke.ts +906 -0
  210. package/scripts/harness-parity-census.ts +92 -0
  211. package/scripts/inngest-luks-alert-armed-probe.ts +29 -0
  212. package/scripts/install-opencode-adapter.ts +520 -0
  213. package/scripts/lib/affected-tests/common.sh +365 -0
  214. package/scripts/lib/affected-tests/go.sh +157 -0
  215. package/scripts/lib/affected-tests/js.sh +188 -0
  216. package/scripts/lib/affected-tests/nx-turbo.sh +98 -0
  217. package/scripts/lib/affected-tests/python.sh +185 -0
  218. package/scripts/lib/context-sanitize.sh +49 -0
  219. package/scripts/lib/domain-model-lib.sh +180 -0
  220. package/scripts/lib/ext-filter-grammar.tsv +34 -0
  221. package/scripts/lib/operator-script.sh +1236 -0
  222. package/scripts/lib/proc.sh +580 -0
  223. package/scripts/lib/session-state.sh +748 -0
  224. package/scripts/lib/tmp-classify.sh +843 -0
  225. package/scripts/lib/wrapper-boundary.sh +47 -0
  226. package/scripts/lint-ratchet.py +1547 -0
  227. package/scripts/monitor-pr-checks.sh +441 -0
  228. package/scripts/pipeline-tally.sh +756 -0
  229. package/scripts/pipeline-tally.test.sh +740 -0
  230. package/scripts/plugin-version.sh +528 -0
  231. package/scripts/precommit-guard.sh +113 -0
  232. package/scripts/precommit-guard.test.sh +254 -0
  233. package/scripts/reconcile-live-heartbeats.ts +705 -0
  234. package/scripts/render-c4-model.sh +217 -0
  235. package/scripts/resolve-git-root.sh +55 -0
  236. package/scripts/resolve-regenerable-conflicts.sh +363 -0
  237. package/scripts/resolve-regenerable-conflicts.test.sh +780 -0
  238. package/scripts/ruff-requirements.txt +22 -0
  239. package/scripts/run-in-pid-namespace.sh +98 -0
  240. package/scripts/run-in-pid-namespace.test.sh +290 -0
  241. package/scripts/scan-ancestor-signal-helpers.py +556 -0
  242. package/scripts/scan-ancestor-signal-helpers.test.sh +670 -0
  243. package/scripts/setup-ralph-loop.sh +238 -0
  244. package/scripts/sync-cursor-name-map.ts +316 -0
  245. package/scripts/sync-grok-agent-compat.ts +188 -0
  246. package/scripts/sync-npm-package.ts +178 -0
  247. package/scripts/sync-opencode-adapter.ts +479 -0
  248. package/scripts/sync-pr-behind.sh +534 -0
  249. package/scripts/sync-pr-behind.test.sh +252 -0
  250. package/scripts/taste-profile-update.sh +180 -0
  251. package/scripts/taste-profile-update.test.sh +167 -0
  252. package/scripts/write-budget-marker.sh +23 -0
  253. package/scripts/write-kb-coverage.ts +113 -0
  254. package/skills/admin-ip-refresh/SKILL.md +81 -0
  255. package/skills/admin-ip-refresh/references/admin-ip-refresh-procedure.md +201 -0
  256. package/skills/agent-browser/SKILL.md +698 -0
  257. package/skills/agent-browser/scripts/playwright-mcp-profile-slot.sh +304 -0
  258. package/skills/agent-browser/scripts/playwright-mcp-redact-proxy.py +964 -0
  259. package/skills/agent-browser/scripts/redact-a11y-snapshot.py +443 -0
  260. package/skills/agent-browser/test/fixtures/capture-playwright-mcp-fixtures.py +217 -0
  261. package/skills/agent-browser/test/fixtures/fake-passthrough-proxy.py +24 -0
  262. package/skills/agent-browser/test/fixtures/fake-playwright-mcp.py +131 -0
  263. package/skills/agent-browser/test/fixtures/make-proxy-mutant.py +48 -0
  264. package/skills/agent-browser/test/fixtures/odd-shapes/dialog-link-injection.json +10 -0
  265. package/skills/agent-browser/test/fixtures/odd-shapes/empty-content.json +5 -0
  266. package/skills/agent-browser/test/fixtures/odd-shapes/error-and-result.json +4 -0
  267. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-bool.json +6 -0
  268. package/skills/agent-browser/test/fixtures/odd-shapes/error-code-string.json +6 -0
  269. package/skills/agent-browser/test/fixtures/odd-shapes/error-message-tree.json +6 -0
  270. package/skills/agent-browser/test/fixtures/odd-shapes/error-no-data.json +6 -0
  271. package/skills/agent-browser/test/fixtures/odd-shapes/error-string.json +4 -0
  272. package/skills/agent-browser/test/fixtures/odd-shapes/error-with-data.json +9 -0
  273. package/skills/agent-browser/test/fixtures/odd-shapes/evaluate-escaped-object.json +10 -0
  274. package/skills/agent-browser/test/fixtures/odd-shapes/invalid-utf8.json +4 -0
  275. package/skills/agent-browser/test/fixtures/odd-shapes/link-result.json +10 -0
  276. package/skills/agent-browser/test/fixtures/odd-shapes/list-two-results.json +6 -0
  277. package/skills/agent-browser/test/fixtures/odd-shapes/meta-in-result.json +13 -0
  278. package/skills/agent-browser/test/fixtures/odd-shapes/no-content.json +5 -0
  279. package/skills/agent-browser/test/fixtures/odd-shapes/quoted-key.json +10 -0
  280. package/skills/agent-browser/test/fixtures/odd-shapes/resource-block.json +18 -0
  281. package/skills/agent-browser/test/fixtures/odd-shapes/result-and-method.json +11 -0
  282. package/skills/agent-browser/test/fixtures/odd-shapes/result-string.json +3 -0
  283. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped-one-row.json +10 -0
  284. package/skills/agent-browser/test/fixtures/odd-shapes/run-code-escaped.json +10 -0
  285. package/skills/agent-browser/test/fixtures/odd-shapes/structured-content.json +13 -0
  286. package/skills/agent-browser/test/fixtures/odd-shapes/tools-list-no-tools.json +5 -0
  287. package/skills/agent-browser/test/fixtures/odd-shapes/tree-no-credential.json +10 -0
  288. package/skills/agent-browser/test/fixtures/odd-shapes/two-text-blocks.json +14 -0
  289. package/skills/agent-browser/test/fixtures/odd-shapes/unparsable-line.json +3 -0
  290. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-brace.json +10 -0
  291. package/skills/agent-browser/test/fixtures/odd-shapes/value-contains-link-substring.json +10 -0
  292. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/close.json +12 -0
  293. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/evaluate.json +12 -0
  294. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/find.json +12 -0
  295. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/framing.json +6 -0
  296. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/initialize.json +14 -0
  297. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate-link.json +12 -0
  298. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/navigate.json +12 -0
  299. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/roots-list-request.json +5 -0
  300. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/screenshot.json +17 -0
  301. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-filename.json +12 -0
  302. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-json.json +12 -0
  303. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot-meta-raw.json +12 -0
  304. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/snapshot.json +12 -0
  305. package/skills/agent-browser/test/fixtures/playwright-mcp-0.0.78/tools-list.json +827 -0
  306. package/skills/agent-browser/test/fixtures/proxy-session-driver.py +138 -0
  307. package/skills/agent-browser/test/fixtures/session-facts.py +102 -0
  308. package/skills/agent-browser/test/playwright-mcp-lifetime.test.sh +1620 -0
  309. package/skills/agent-browser/test/playwright-mcp-redact-proxy.test.sh +1599 -0
  310. package/skills/agent-browser/test/redact-a11y-snapshot.test.sh +527 -0
  311. package/skills/agent-native-architecture/SKILL.md +479 -0
  312. package/skills/agent-native-architecture/references/action-parity-discipline.md +430 -0
  313. package/skills/agent-native-architecture/references/agent-execution-patterns.md +483 -0
  314. package/skills/agent-native-architecture/references/agent-native-testing.md +605 -0
  315. package/skills/agent-native-architecture/references/architecture-patterns.md +493 -0
  316. package/skills/agent-native-architecture/references/dynamic-context-injection.md +358 -0
  317. package/skills/agent-native-architecture/references/files-universal-interface.md +314 -0
  318. package/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +381 -0
  319. package/skills/agent-native-architecture/references/mcp-tool-design.md +524 -0
  320. package/skills/agent-native-architecture/references/mobile-patterns.md +897 -0
  321. package/skills/agent-native-architecture/references/product-implications.md +478 -0
  322. package/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +342 -0
  323. package/skills/agent-native-architecture/references/self-modification.md +294 -0
  324. package/skills/agent-native-architecture/references/shared-workspace-architecture.md +700 -0
  325. package/skills/agent-native-architecture/references/system-prompt-design.md +263 -0
  326. package/skills/agent-native-audit/SKILL.md +300 -0
  327. package/skills/agent-native-audit/workflows/agent-native-audit.workflow.js +423 -0
  328. package/skills/andrew-kane-gem-writer/SKILL.md +189 -0
  329. package/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
  330. package/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
  331. package/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
  332. package/skills/andrew-kane-gem-writer/references/resources.md +126 -0
  333. package/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
  334. package/skills/architecture/SKILL.md +436 -0
  335. package/skills/architecture/references/adr-template.md +154 -0
  336. package/skills/architecture/references/likec4-reference.md +136 -0
  337. package/skills/architecture/references/nfr-reference.md +126 -0
  338. package/skills/archive-kb/SKILL.md +86 -0
  339. package/skills/archive-kb/scripts/archive-kb.sh +300 -0
  340. package/skills/archive-kb/test/archive-kb-partial-run.test.sh +276 -0
  341. package/skills/atdd-developer/SKILL.md +65 -0
  342. package/skills/brainstorm/SKILL.md +701 -0
  343. package/skills/brainstorm/references/brainstorm-brand-workshop.md +153 -0
  344. package/skills/brainstorm/references/brainstorm-domain-config.md +63 -0
  345. package/skills/brainstorm/references/brainstorm-validation-workshop.md +65 -0
  346. package/skills/brainstorm/references/ui-surface-terms.md +42 -0
  347. package/skills/brainstorm-techniques/SKILL.md +231 -0
  348. package/skills/brainstorm-techniques/references/decision-principles.md +150 -0
  349. package/skills/brainstorm-techniques/references/phase-boundaries.md +103 -0
  350. package/skills/brainstorm-techniques/references/standing-delegation.md +407 -0
  351. package/skills/brainstorm-techniques/references/technical-ruling-delegation.md +174 -0
  352. package/skills/campaign-calendar/SKILL.md +145 -0
  353. package/skills/cf-token-scope/SKILL.md +164 -0
  354. package/skills/cf-token-scope/references/widen-playbook.md +114 -0
  355. package/skills/cf-token-scope/scripts/cf-token-scope.sh +253 -0
  356. package/skills/changelog/SKILL.md +142 -0
  357. package/skills/code-to-prd/SKILL.md +66 -0
  358. package/skills/code-to-prd/references/banner-template.md +12 -0
  359. package/skills/code-to-prd/references/prd-template.md +41 -0
  360. package/skills/code-to-prd/scripts/code-to-prd.sh +634 -0
  361. package/skills/code-to-prd/test/code-to-prd.test.sh +447 -0
  362. package/skills/code-to-prd/test/fixture/.env.example +7 -0
  363. package/skills/code-to-prd/test/fixture/app/about/page.tsx +11 -0
  364. package/skills/code-to-prd/test/fixture/app/api/health/route.ts +10 -0
  365. package/skills/code-to-prd/test/fixture/app/page.tsx +14 -0
  366. package/skills/code-to-prd/test/fixture/next.config.mjs +3 -0
  367. package/skills/code-to-prd/test/fixture/package.json +6 -0
  368. package/skills/cohort-status/SKILL.md +62 -0
  369. package/skills/community/SKILL.md +210 -0
  370. package/skills/community/scripts/bsky-community.sh +644 -0
  371. package/skills/community/scripts/bsky-setup.sh +631 -0
  372. package/skills/community/scripts/community-router.sh +93 -0
  373. package/skills/community/scripts/discord-community.sh +487 -0
  374. package/skills/community/scripts/discord-setup.sh +784 -0
  375. package/skills/community/scripts/github-community.sh +706 -0
  376. package/skills/community/scripts/hn-community.sh +233 -0
  377. package/skills/community/scripts/lib/hmac-sha1-b64.sh +126 -0
  378. package/skills/community/scripts/linkedin-community.sh +750 -0
  379. package/skills/community/scripts/linkedin-setup.sh +758 -0
  380. package/skills/community/scripts/x-community.sh +890 -0
  381. package/skills/community/scripts/x-setup.sh +713 -0
  382. package/skills/community/test/community-argv.test.sh +2922 -0
  383. package/skills/community/test/github-community.test.sh +981 -0
  384. package/skills/competitive-analysis/SKILL.md +69 -0
  385. package/skills/competitive-analysis/references/peer-plugin-audit.md +218 -0
  386. package/skills/compound/SKILL.md +673 -0
  387. package/skills/compound/scripts/token-efficiency-report.sh +334 -0
  388. package/skills/compound/test/phase-16.test.sh +379 -0
  389. package/skills/compound-capture/SKILL.md +791 -0
  390. package/skills/compound-capture/assets/critical-pattern-template.md +37 -0
  391. package/skills/compound-capture/assets/resolution-template.md +116 -0
  392. package/skills/compound-capture/references/yaml-schema.md +67 -0
  393. package/skills/compound-capture/schema.yaml +181 -0
  394. package/skills/constraint-scaffold/SKILL.md +175 -0
  395. package/skills/constraint-scaffold/references/boundary-readme.template +35 -0
  396. package/skills/constraint-scaffold/references/constraint-gates-workflow.template +69 -0
  397. package/skills/constraint-scaffold/references/depcruise-config.template +230 -0
  398. package/skills/constraint-scaffold/references/fix-constraints-stage-a.template +240 -0
  399. package/skills/constraint-scaffold/references/fix-constraints-stage-b.template +313 -0
  400. package/skills/constraint-scaffold/references/shared-runner.template +135 -0
  401. package/skills/constraint-scaffold/scripts/constraint-scaffold.sh +589 -0
  402. package/skills/constraint-scaffold/test/bite-proof.test.sh +1338 -0
  403. package/skills/constraint-scaffold/test/boundary.test.sh +734 -0
  404. package/skills/constraint-scaffold/test/emit-fix-constraints.test.sh +193 -0
  405. package/skills/constraint-scaffold/test/generator.test.sh +93 -0
  406. package/skills/constraint-scaffold/test/parity.test.sh +285 -0
  407. package/skills/content-writer/SKILL.md +242 -0
  408. package/skills/content-writer/scripts/blog-jargon-scan.sh +42 -0
  409. package/skills/cron-delete/SKILL.md +56 -0
  410. package/skills/cron-list/SKILL.md +54 -0
  411. package/skills/deepen-plan/SKILL.md +937 -0
  412. package/skills/deepen-plan/workflows/deepen-plan.workflow.js +488 -0
  413. package/skills/deploy/SKILL.md +115 -0
  414. package/skills/deploy/references/hetzner-setup.md +65 -0
  415. package/skills/deploy/scripts/deploy.sh +37 -0
  416. package/skills/deploy-docs/SKILL.md +95 -0
  417. package/skills/dhh-rails-style/SKILL.md +203 -0
  418. package/skills/dhh-rails-style/references/architecture.md +717 -0
  419. package/skills/dhh-rails-style/references/controllers.md +323 -0
  420. package/skills/dhh-rails-style/references/frontend.md +556 -0
  421. package/skills/dhh-rails-style/references/gems.md +316 -0
  422. package/skills/dhh-rails-style/references/models.md +388 -0
  423. package/skills/dhh-rails-style/references/testing.md +354 -0
  424. package/skills/discord-content/SKILL.md +126 -0
  425. package/skills/docs-site/SKILL.md +214 -0
  426. package/skills/drain-labeled-backlog/SKILL.md +203 -0
  427. package/skills/drain-labeled-backlog/scripts/group-by-area.sh +208 -0
  428. package/skills/drain-labeled-backlog/workflows/drain-labeled-backlog.workflow.js +433 -0
  429. package/skills/drain-prs/SKILL.md +142 -0
  430. package/skills/drain-prs/scripts/triage-prs.sh +192 -0
  431. package/skills/drain-prs/test/argv-ceiling.test.sh +183 -0
  432. package/skills/dspy-ruby/SKILL.md +634 -0
  433. package/skills/dspy-ruby/assets/config-template.rb +359 -0
  434. package/skills/dspy-ruby/assets/module-template.rb +326 -0
  435. package/skills/dspy-ruby/assets/signature-template.rb +143 -0
  436. package/skills/dspy-ruby/references/core-concepts.md +268 -0
  437. package/skills/dspy-ruby/references/optimization.md +623 -0
  438. package/skills/dspy-ruby/references/providers.md +335 -0
  439. package/skills/eval-harness/README.md +194 -0
  440. package/skills/eval-harness/SKILL.md +157 -0
  441. package/skills/eval-harness/enums/go-routes.json +13 -0
  442. package/skills/eval-harness/enums/incident-threshold.json +5 -0
  443. package/skills/eval-harness/enums/lane.json +5 -0
  444. package/skills/eval-harness/enums/tool-selection.json +20 -0
  445. package/skills/eval-harness/enums/triage-levels.json +5 -0
  446. package/skills/eval-harness/gated-skills.json +34 -0
  447. package/skills/eval-harness/models.generated.json +5 -0
  448. package/skills/eval-harness/promptfooconfig-go-routing.yaml +35 -0
  449. package/skills/eval-harness/promptfooconfig-incident-threshold.yaml +32 -0
  450. package/skills/eval-harness/promptfooconfig-lane-inference.yaml +33 -0
  451. package/skills/eval-harness/promptfooconfig-ticket-triage.yaml +34 -0
  452. package/skills/eval-harness/promptfooconfig-tool-selection.yaml +33 -0
  453. package/skills/eval-harness/prompts/go-baseline.txt +6 -0
  454. package/skills/eval-harness/prompts/go-skill.txt +21 -0
  455. package/skills/eval-harness/prompts/incident-threshold-baseline.txt +6 -0
  456. package/skills/eval-harness/prompts/incident-threshold-skill.txt +12 -0
  457. package/skills/eval-harness/prompts/lane-inference-baseline.txt +6 -0
  458. package/skills/eval-harness/prompts/lane-inference-skill.txt +15 -0
  459. package/skills/eval-harness/prompts/tool-selection-baseline.txt +19 -0
  460. package/skills/eval-harness/prompts/tool-selection-skill.txt +22 -0
  461. package/skills/eval-harness/prompts/triage-baseline.txt +7 -0
  462. package/skills/eval-harness/prompts/triage-skill.txt +10 -0
  463. package/skills/eval-harness/scripts/eval-gate.cjs +318 -0
  464. package/skills/eval-harness/scripts/extract-block.cjs +87 -0
  465. package/skills/eval-harness/scripts/gate-classification.cjs +28 -0
  466. package/skills/eval-harness/scripts/gen-models.sh +41 -0
  467. package/skills/eval-harness/scripts/gen-skill-prompt.cjs +177 -0
  468. package/skills/eval-harness/scripts/measure-classification.cjs +30 -0
  469. package/skills/eval-harness/scripts/parse-label.cjs +93 -0
  470. package/skills/eval-harness/scripts/verdict.cjs +132 -0
  471. package/skills/eval-harness/tasks/go-routing.jsonl +8 -0
  472. package/skills/eval-harness/tasks/incident-threshold.jsonl +7 -0
  473. package/skills/eval-harness/tasks/lane-inference.jsonl +7 -0
  474. package/skills/eval-harness/tasks/ticket-triage.jsonl +6 -0
  475. package/skills/eval-harness/tasks/tool-selection.jsonl +15 -0
  476. package/skills/eval-harness/test/eval-gate.test.sh +132 -0
  477. package/skills/eval-harness/test/extract-block.test.sh +87 -0
  478. package/skills/eval-harness/test/gate-classification.test.sh +77 -0
  479. package/skills/eval-harness/test/gen-models.test.sh +75 -0
  480. package/skills/eval-harness/test/measure-classification.test.sh +74 -0
  481. package/skills/eval-harness/test/parse-label.test.sh +129 -0
  482. package/skills/eval-harness/test/registry-completeness.test.sh +109 -0
  483. package/skills/eval-harness/test/verdict.test.sh +123 -0
  484. package/skills/every-style-editor/SKILL.md +142 -0
  485. package/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
  486. package/skills/feature-tweet/SKILL.md +199 -0
  487. package/skills/feature-video/SKILL.md +412 -0
  488. package/skills/feature-video/scripts/check_deps.sh +281 -0
  489. package/skills/file-todos/SKILL.md +267 -0
  490. package/skills/file-todos/assets/todo-template.md +166 -0
  491. package/skills/fix-issue/SKILL.md +278 -0
  492. package/skills/fix-issue/references/agent-authored-exclusion.md +118 -0
  493. package/skills/fix-issue/references/exclude-label-jq-snippet.md +96 -0
  494. package/skills/flag-bootstrap/SETUP.md +142 -0
  495. package/skills/flag-create/SKILL.md +145 -0
  496. package/skills/flag-create/scripts/create.sh +273 -0
  497. package/skills/flag-delete/SKILL.md +167 -0
  498. package/skills/flag-delete/scripts/delete.sh +288 -0
  499. package/skills/flag-list/SKILL.md +99 -0
  500. package/skills/flag-list/scripts/list.sh +275 -0
  501. package/skills/flag-set-role/SKILL.md +229 -0
  502. package/skills/flag-set-role/scripts/flip.sh +774 -0
  503. package/skills/frontend-anti-slop/SKILL.md +106 -0
  504. package/skills/frontend-anti-slop/references/anti-patterns.md +199 -0
  505. package/skills/frontend-anti-slop/references/slop-rules.md +65 -0
  506. package/skills/frontend-anti-slop/scripts/tier1-scan.ts +525 -0
  507. package/skills/frontend-design/SKILL.md +77 -0
  508. package/skills/gdpr-gate/NOTICE +136 -0
  509. package/skills/gdpr-gate/SKILL.md +341 -0
  510. package/skills/gdpr-gate/references/fields.md +160 -0
  511. package/skills/gdpr-gate/references/layers/api-layer.md +355 -0
  512. package/skills/gdpr-gate/references/layers/auth-sessions.md +290 -0
  513. package/skills/gdpr-gate/references/layers/data-in-transit.md +331 -0
  514. package/skills/gdpr-gate/references/layers/data-lifecycle.md +341 -0
  515. package/skills/gdpr-gate/references/layers/frontend.md +249 -0
  516. package/skills/gdpr-gate/references/layers/testing-seeding.md +256 -0
  517. package/skills/gdpr-gate/references/leakage-vectors.md +248 -0
  518. package/skills/gdpr-gate/references/legacy/legal-consent-v1-prose.md +48 -0
  519. package/skills/gdpr-gate/references/legal-consent.md +140 -0
  520. package/skills/gdpr-gate/references/non-negotiables.md +60 -0
  521. package/skills/gdpr-gate/scripts/gdpr-gate.sh +271 -0
  522. package/skills/gdpr-gate/scripts/notice-frontmatter.sh +337 -0
  523. package/skills/gdpr-gate/scripts/path-denylist.txt +39 -0
  524. package/skills/gdpr-gate/scripts/repo-scan.sh +158 -0
  525. package/skills/gdpr-gate/scripts/vendor-drift-classify.sh +120 -0
  526. package/skills/gdpr-gate/scripts/vendor-pin-integrity.sh +237 -0
  527. package/skills/gemini-imagegen/SKILL.md +283 -0
  528. package/skills/gemini-imagegen/requirements.txt +5 -0
  529. package/skills/gemini-imagegen/scripts/_error_handling.py +146 -0
  530. package/skills/gemini-imagegen/scripts/compose_images.py +150 -0
  531. package/skills/gemini-imagegen/scripts/edit_image.py +137 -0
  532. package/skills/gemini-imagegen/scripts/gemini_images.py +256 -0
  533. package/skills/gemini-imagegen/scripts/generate_image.py +147 -0
  534. package/skills/gemini-imagegen/scripts/multi_turn_chat.py +223 -0
  535. package/skills/gemini-imagegen/scripts/test_error_handling.py +179 -0
  536. package/skills/git-worktree/SKILL.md +534 -0
  537. package/skills/git-worktree/scripts/git-repo-readiness-diag.sh +98 -0
  538. package/skills/git-worktree/scripts/worktree-manager.sh +4773 -0
  539. package/skills/git-worktree/test/create-from-origin-main.test.sh +116 -0
  540. package/skills/git-worktree/test/lease-protects-active.test.sh +1056 -0
  541. package/skills/git-worktree/test/no-repo-fail-loud.test.sh +88 -0
  542. package/skills/git-worktree/test/orphan-reaper-honest-count.test.sh +379 -0
  543. package/skills/git-worktree/test/reap-archive-persistence.test.sh +725 -0
  544. package/skills/git-worktree/test/stale-lock-sweep.test.sh +176 -0
  545. package/skills/go/SKILL.md +54 -0
  546. package/skills/growth/SKILL.md +164 -0
  547. package/skills/harvest-debt/SKILL.md +78 -0
  548. package/skills/harvest-debt/scripts/harvest-debt.sh +114 -0
  549. package/skills/heal-skill/SKILL.md +173 -0
  550. package/skills/help/SKILL.md +46 -0
  551. package/skills/incident/NOTICE +39 -0
  552. package/skills/incident/SKILL.md +395 -0
  553. package/skills/incident/scripts/dry-run.sh +448 -0
  554. package/skills/incident/scripts/redact-engine.py +412 -0
  555. package/skills/incident/scripts/redact-sentinel.sh +44 -0
  556. package/skills/incident/templates/pir.md +139 -0
  557. package/skills/incident/test/fixtures/dry-run-incident.json +26 -0
  558. package/skills/incident/test/fixtures/dry-run-secret-leak.json +26 -0
  559. package/skills/incident/test/fixtures/legacy-bash-scanner.sh +82 -0
  560. package/skills/incident/test/fixtures/positive-corpus.md +87 -0
  561. package/skills/incident/test/redact-sentinel.test.sh +1420 -0
  562. package/skills/invoice/SKILL.md +206 -0
  563. package/skills/kb-glossary/SKILL.md +71 -0
  564. package/skills/kb-glossary/references/glossary-format.md +111 -0
  565. package/skills/kb-glossary/references/rejected-request-register.md +115 -0
  566. package/skills/kb-search/SKILL.md +276 -0
  567. package/skills/kb-search/scripts/kb-search-cache.sh +123 -0
  568. package/skills/legal-audit/SKILL.md +161 -0
  569. package/skills/legal-generate/NOTICE +242 -0
  570. package/skills/legal-generate/SKILL.md +244 -0
  571. package/skills/legal-generate/references/templates/advisor-agreement/template.md +12 -0
  572. package/skills/legal-generate/references/templates/business-associate-agreement/template.md +112 -0
  573. package/skills/legal-generate/references/templates/cookie-notice/template.md +82 -0
  574. package/skills/legal-generate/references/templates/dpa-global/template.md +439 -0
  575. package/skills/legal-generate/references/templates/dpa-us/template.md +222 -0
  576. package/skills/legal-generate/references/templates/employee-offer-letter/template.md +75 -0
  577. package/skills/legal-generate/references/templates/master-services-agreement/template.md +252 -0
  578. package/skills/legal-generate/references/templates/mutual-nda/template.md +66 -0
  579. package/skills/legal-generate/references/templates/one-way-nda/template.md +38 -0
  580. package/skills/legal-generate/references/templates/privacy-policy-gdpr/template.md +427 -0
  581. package/skills/legal-generate/references/templates/privacy-policy-us/template.md +313 -0
  582. package/skills/legal-generate/references/templates/terms-of-use/template.md +75 -0
  583. package/skills/legal-generate/scripts/strip-vendor-credit.sh +96 -0
  584. package/skills/linear-fetch/SKILL.md +247 -0
  585. package/skills/linear-fetch/scripts/assert-no-linear-telemetry.sh +66 -0
  586. package/skills/linear-fetch/scripts/redact-linear-urls.sh +51 -0
  587. package/skills/linear-fetch/scripts/render-caller-template.sh +36 -0
  588. package/skills/linear-fetch/test/assert-no-linear-telemetry.test.sh +113 -0
  589. package/skills/linear-fetch/test/parity.test.sh +112 -0
  590. package/skills/linear-fetch/test/persist-safe-integration.test.sh +191 -0
  591. package/skills/linear-fetch/test/redact-linear-urls.test.sh +226 -0
  592. package/skills/merge-pr/SKILL.md +668 -0
  593. package/skills/model-launch-review/SKILL.md +255 -0
  594. package/skills/model-launch-review/scripts/audit-models.sh +501 -0
  595. package/skills/one-shot/SKILL.md +316 -0
  596. package/skills/one-shot/references/token-discipline.md +74 -0
  597. package/skills/operator-bootstrap/SKILL.md +412 -0
  598. package/skills/operator-bootstrap/template.sh +439 -0
  599. package/skills/operator-digest/SKILL.md +287 -0
  600. package/skills/operator-digest/assets/operator-digest.workflow.yml +175 -0
  601. package/skills/operator-digest/scripts/digest-scrub.sh +119 -0
  602. package/skills/operator-digest/scripts/provision-operator-digest-repo.sh +147 -0
  603. package/skills/operator-rephrase/SKILL.md +98 -0
  604. package/skills/pencil-setup/SKILL.md +201 -0
  605. package/skills/pencil-setup/scripts/check_deps.sh +556 -0
  606. package/skills/pencil-setup/scripts/copy_adapter.sh +80 -0
  607. package/skills/pencil-setup/scripts/package-lock.json +1172 -0
  608. package/skills/pencil-setup/scripts/package.json +14 -0
  609. package/skills/pencil-setup/scripts/pencil-error-enrichment.mjs +21 -0
  610. package/skills/pencil-setup/scripts/pencil-mcp-adapter.mjs +733 -0
  611. package/skills/pencil-setup/scripts/pencil-response-classify.mjs +38 -0
  612. package/skills/pencil-setup/scripts/pencil-save-gate.mjs +26 -0
  613. package/skills/pencil-setup/scripts/sanitize-filename.mjs +25 -0
  614. package/skills/plan/SKILL.md +1140 -0
  615. package/skills/plan/references/differential-migration-testing.md +185 -0
  616. package/skills/plan/references/plan-community-discovery.md +42 -0
  617. package/skills/plan/references/plan-founder-check.md +137 -0
  618. package/skills/plan/references/plan-functional-overlap.md +25 -0
  619. package/skills/plan/references/plan-host-fanout.md +51 -0
  620. package/skills/plan/references/plan-issue-templates.md +779 -0
  621. package/skills/plan/references/plan-network-outage-checklist.md +131 -0
  622. package/skills/plan/references/plan-scope-check.md +126 -0
  623. package/skills/plan/references/plan-sharp-edges.md +325 -0
  624. package/skills/plan/references/plan-signoff-lifecycle.md +12 -0
  625. package/skills/plan/references/webapp-cwv-observability.md +143 -0
  626. package/skills/plan-review/SKILL.md +91 -0
  627. package/skills/plan-review/lib/named-panel.mjs +58 -0
  628. package/skills/plan-review/workflows/plan-review.workflow.js +477 -0
  629. package/skills/postmerge/SKILL.md +642 -0
  630. package/skills/postmerge/references/deploy-status-debugging.md +138 -0
  631. package/skills/postmerge/references/graceful-degradation.md +20 -0
  632. package/skills/postmerge/references/sentry-error-count-delta.md +137 -0
  633. package/skills/preflight/SKILL.md +1553 -0
  634. package/skills/preflight/references/check-13-founder-check.md +286 -0
  635. package/skills/preflight/scripts/founder-check.py +1640 -0
  636. package/skills/preflight/scripts/parse-form-a.awk +107 -0
  637. package/skills/preflight/scripts/probe-verb-gate.sh +108 -0
  638. package/skills/product-roadmap/SKILL.md +357 -0
  639. package/skills/product-roadmap/scripts/roadmap-reconcile.sh +327 -0
  640. package/skills/provision-cloudflare/SKILL.md +67 -0
  641. package/skills/provision-cloudflare/scripts/provision-cloudflare.sh +271 -0
  642. package/skills/provision-doppler/SKILL.md +58 -0
  643. package/skills/provision-doppler/scripts/provision-doppler.sh +358 -0
  644. package/skills/provision-github/SKILL.md +59 -0
  645. package/skills/provision-github/scripts/provision-github.sh +300 -0
  646. package/skills/provision-hetzner/SKILL.md +76 -0
  647. package/skills/provision-hetzner/scripts/provision-hetzner.sh +262 -0
  648. package/skills/provision-hetzner/test/fixture/knowledge-base/legal/tenant-dpa-register.md +26 -0
  649. package/skills/provision-hetzner/test/fixture/provision-hetzner-dry-run.golden +34 -0
  650. package/skills/provision-hetzner/test/provision-hetzner-characterization.test.sh +227 -0
  651. package/skills/qa/SKILL.md +263 -0
  652. package/skills/qa/references/browser-preflight.md +22 -0
  653. package/skills/questionnaire-generate/SKILL.md +328 -0
  654. package/skills/questionnaire-generate/references/questionnaire.template +60 -0
  655. package/skills/rclone/SKILL.md +162 -0
  656. package/skills/rclone/scripts/check_setup.sh +63 -0
  657. package/skills/release-announce/SKILL.md +65 -0
  658. package/skills/release-docs/SKILL.md +92 -0
  659. package/skills/reproduce-bug/SKILL.md +358 -0
  660. package/skills/resolve-debt/SKILL.md +85 -0
  661. package/skills/resolve-debt/scripts/resolve-debt.py +584 -0
  662. package/skills/resolve-parallel/SKILL.md +45 -0
  663. package/skills/resolve-parallel/workflows/resolve-parallel.workflow.js +415 -0
  664. package/skills/resolve-pr-parallel/SKILL.md +68 -0
  665. package/skills/resolve-pr-parallel/workflows/resolve-pr-parallel.workflow.js +419 -0
  666. package/skills/resolve-todo-parallel/SKILL.md +57 -0
  667. package/skills/resolve-todo-parallel/workflows/resolve-todo-parallel.workflow.js +398 -0
  668. package/skills/review/SKILL.md +1621 -0
  669. package/skills/review/references/anti-slop-scanner-hook.md +40 -0
  670. package/skills/review/references/defect-classes-migrations.md +9 -0
  671. package/skills/review/references/generated-input-oracle-surfaces.md +15 -0
  672. package/skills/review/references/review-e2e-testing.md +116 -0
  673. package/skills/review/references/review-todo-structure.md +165 -0
  674. package/skills/review/references/risk-tier-and-fix-rounds.md +95 -0
  675. package/skills/review/references/semgrep-custom-rules.yaml +174 -0
  676. package/skills/review/references/ultrathink-phases.md +98 -0
  677. package/skills/review/references/wfs.md +6 -0
  678. package/skills/review/scripts/emit-review-trailer.sh +499 -0
  679. package/skills/review/scripts/ensure-semgrep.sh +99 -0
  680. package/skills/review/scripts/fix-round-seats.sh +197 -0
  681. package/skills/review/test/emit-review-trailer.test.sh +387 -0
  682. package/skills/review/test/ensure-semgrep.test.sh +111 -0
  683. package/skills/review/test/fix-round-seats.test.sh +237 -0
  684. package/skills/review/workflows/README.md +147 -0
  685. package/skills/review/workflows/review.workflow.js +667 -0
  686. package/skills/schedule/SKILL.md +796 -0
  687. package/skills/seo-aeo/SKILL.md +139 -0
  688. package/skills/seo-aeo/scripts/validate-csp.sh +233 -0
  689. package/skills/seo-aeo/scripts/validate-seo.sh +262 -0
  690. package/skills/ship/SKILL.md +3022 -0
  691. package/skills/ship/references/adr-ordinal-collision.md +47 -0
  692. package/skills/ship/references/affected-gate-contract.md +115 -0
  693. package/skills/ship/references/ci-run-diagnosis.md +29 -0
  694. package/skills/ship/references/ci-workflow-authoring.md +68 -0
  695. package/skills/ship/references/counsel-review-clo-attestation.md +38 -0
  696. package/skills/ship/references/cto-ruling-armed-push.md +5 -0
  697. package/skills/ship/references/decision-challenges-render.md +71 -0
  698. package/skills/ship/references/detached-battery-verdict.md +13 -0
  699. package/skills/ship/references/feature-tweet-draft.md +42 -0
  700. package/skills/ship/references/followthrough-issue-body-template.md +47 -0
  701. package/skills/ship/references/followthrough-stub-template.sh +92 -0
  702. package/skills/ship/references/merge-queue-dequeue.md +39 -0
  703. package/skills/ship/references/operator-holds.md +149 -0
  704. package/skills/ship/references/pipeline-tally-render.md +43 -0
  705. package/skills/ship/references/pr-overlap.md +153 -0
  706. package/skills/ship/references/queue-mode.md +7 -0
  707. package/skills/ship/references/ready-run-wait.md +36 -0
  708. package/skills/ship/references/red-on-main-quarantine.md +110 -0
  709. package/skills/ship/references/register-update-pr-pattern.md +88 -0
  710. package/skills/ship/references/settle-then-admin-merge.md +130 -0
  711. package/skills/ship/references/supabase-column-probe.md +13 -0
  712. package/skills/ship/scripts/auto-close-scan.sh +76 -0
  713. package/skills/ship/scripts/battery-owed.sh +403 -0
  714. package/skills/ship/scripts/hold-gate-eval.sh +410 -0
  715. package/skills/ship/scripts/net-issue-flow.sh +665 -0
  716. package/skills/ship/scripts/pr-overlap-preflight.sh +483 -0
  717. package/skills/ship/scripts/ship-pir-action-items-gate.sh +246 -0
  718. package/skills/ship/scripts/technical-ruling-gate.sh +444 -0
  719. package/skills/skill-creator/SKILL.md +299 -0
  720. package/skills/skill-creator/references/api-security.md +248 -0
  721. package/skills/skill-creator/references/authoring-levers.md +110 -0
  722. package/skills/skill-creator/references/be-clear-and-direct.md +587 -0
  723. package/skills/skill-creator/references/best-practices.md +431 -0
  724. package/skills/skill-creator/references/common-patterns.md +639 -0
  725. package/skills/skill-creator/references/core-principles.md +193 -0
  726. package/skills/skill-creator/references/executable-code.md +188 -0
  727. package/skills/skill-creator/references/iteration-and-testing.md +509 -0
  728. package/skills/skill-creator/references/official-spec.md +193 -0
  729. package/skills/skill-creator/references/recommended-structure.md +180 -0
  730. package/skills/skill-creator/references/skill-structure.md +396 -0
  731. package/skills/skill-creator/references/using-scripts.md +122 -0
  732. package/skills/skill-creator/references/using-templates.md +120 -0
  733. package/skills/skill-creator/references/workflows-and-validation.md +541 -0
  734. package/skills/skill-creator/scripts/init_skill.py +322 -0
  735. package/skills/skill-creator/scripts/package_skill.py +110 -0
  736. package/skills/skill-creator/scripts/quick_validate.py +64 -0
  737. package/skills/skill-creator/templates/router-skill.md +73 -0
  738. package/skills/skill-creator/templates/simple-skill.md +34 -0
  739. package/skills/skill-creator/workflows/add-reference.md +96 -0
  740. package/skills/skill-creator/workflows/add-script.md +104 -0
  741. package/skills/skill-creator/workflows/add-template.md +83 -0
  742. package/skills/skill-creator/workflows/add-workflow.md +128 -0
  743. package/skills/skill-creator/workflows/audit-skill.md +159 -0
  744. package/skills/skill-creator/workflows/create-domain-expertise-skill.md +623 -0
  745. package/skills/skill-creator/workflows/create-new-skill.md +207 -0
  746. package/skills/skill-creator/workflows/get-guidance.md +134 -0
  747. package/skills/skill-creator/workflows/upgrade-to-router.md +170 -0
  748. package/skills/skill-creator/workflows/verify-skill.md +226 -0
  749. package/skills/skill-security-scan/SKILL.md +145 -0
  750. package/skills/skill-security-scan/references/disclaimer.md +21 -0
  751. package/skills/skill-security-scan/references/first-party-allowlist.yaml +38 -0
  752. package/skills/skill-security-scan/references/override-artifact-schema.json +68 -0
  753. package/skills/skill-security-scan/references/override-mechanism.md +171 -0
  754. package/skills/skill-security-scan/references/redirect-domains.yaml +27 -0
  755. package/skills/skill-security-scan/references/regex-patterns.md +136 -0
  756. package/skills/skill-security-scan/references/rules/code-exec.yaml +70 -0
  757. package/skills/skill-security-scan/references/rules/filesystem-boundary.yaml +29 -0
  758. package/skills/skill-security-scan/references/rules/manifest.yaml +44 -0
  759. package/skills/skill-security-scan/references/rules/supply-chain.yaml +36 -0
  760. package/skills/skill-security-scan/references/scan-meta-schema.json +63 -0
  761. package/skills/skill-security-scan/references/test-fixtures/clean-soleur-style.skill.md +23 -0
  762. package/skills/skill-security-scan/references/test-fixtures/clean-third-party.skill.md +26 -0
  763. package/skills/skill-security-scan/references/test-fixtures/malicious-codeexec.skill.md +23 -0
  764. package/skills/skill-security-scan/references/test-fixtures/malicious-curl-pipe-bash.skill.md +69 -0
  765. package/skills/skill-security-scan/references/test-fixtures/malicious-prompt-injection.skill.md +17 -0
  766. package/skills/skill-security-scan/references/test-fixtures/malicious-telemetry-beacon.skill.md +23 -0
  767. package/skills/skill-security-scan/references/typosquat-targets.yaml +65 -0
  768. package/skills/skill-security-scan/scripts/check-codeexec.sh +41 -0
  769. package/skills/skill-security-scan/scripts/check-filesystem-boundary.sh +19 -0
  770. package/skills/skill-security-scan/scripts/check-prompt-injection.sh +95 -0
  771. package/skills/skill-security-scan/scripts/check-supply-chain.sh +219 -0
  772. package/skills/skill-security-scan/scripts/check-telemetry-surface.sh +126 -0
  773. package/skills/skill-security-scan/scripts/lib.sh +197 -0
  774. package/skills/skill-security-scan/scripts/parse-override.sh +231 -0
  775. package/skills/skill-security-scan/scripts/run-scan.sh +342 -0
  776. package/skills/skill-security-scan/scripts/run-self-test.sh +95 -0
  777. package/skills/skill-security-scan/test/argv-ceiling.test.sh +180 -0
  778. package/skills/skill-security-scan/test/meta-dir-reap.test.sh +411 -0
  779. package/skills/skill-security-scan/test/parse-override.test.sh +607 -0
  780. package/skills/skill-security-scan/test/redact-pii-ghs-shape.test.sh +251 -0
  781. package/skills/social-distribute/SKILL.md +485 -0
  782. package/skills/spec-templates/SKILL.md +274 -0
  783. package/skills/sync/SKILL.md +49 -0
  784. package/skills/test-browser/SKILL.md +464 -0
  785. package/skills/test-browser/references/feature-map.md +161 -0
  786. package/skills/test-fix-loop/SKILL.md +159 -0
  787. package/skills/test-fix-loop/references/determinism-checklist.md +154 -0
  788. package/skills/triage/SKILL.md +402 -0
  789. package/skills/trigger-cron/SKILL.md +136 -0
  790. package/skills/trigger-cron/scripts/trigger.sh +198 -0
  791. package/skills/user-set-role/SKILL.md +147 -0
  792. package/skills/user-set-role/scripts/set-role.sh +218 -0
  793. package/skills/user-story-writer/SKILL.md +77 -0
  794. package/skills/ux-audit/SKILL.md +251 -0
  795. package/skills/ux-audit/references/finding.schema.json +62 -0
  796. package/skills/ux-audit/references/route-list.yaml +95 -0
  797. package/skills/ux-audit/scripts/bot-fixture.ts +270 -0
  798. package/skills/ux-audit/scripts/bot-signin.ts +135 -0
  799. package/skills/ux-audit/scripts/dedup-hash.ts +46 -0
  800. package/skills/work/SKILL.md +1479 -0
  801. package/skills/work/references/end-of-work-resume.md +7 -0
  802. package/skills/work/references/hook-test-main-cwd.md +24 -0
  803. package/skills/work/references/key-principles.md +83 -0
  804. package/skills/work/references/property-testing.md +122 -0
  805. package/skills/work/references/sql-security-definer-rpc-scaffold.sql +78 -0
  806. package/skills/work/references/work-agent-teams.md +97 -0
  807. package/skills/work/references/work-lifecycle-parallel.md +147 -0
  808. package/skills/work/references/work-lockfile-bumps.md +97 -0
  809. package/skills/work/references/work-scratch-sandboxes.md +212 -0
  810. package/skills/work/references/work-subagent-fanout.md +61 -0
  811. package/skills/work/references/work-test-harness-sharp-edges.md +73 -0
  812. 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