ceo-orchestration 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 (2356) hide show
  1. package/.claude/adr/ADR-001-runtime-state-directory.md +164 -0
  2. package/.claude/adr/ADR-002-hooks-package-layout.md +228 -0
  3. package/.claude/adr/ADR-003-branch-protection-replaces-skill-signing.md +266 -0
  4. package/.claude/adr/ADR-004-defer-bash-legacy-removal.md +171 -0
  5. package/.claude/adr/ADR-005-event-stream-v2.md +153 -0
  6. package/.claude/adr/ADR-006-registry-derived-manifests.md +145 -0
  7. package/.claude/adr/ADR-007-spec-v1-semver-rc-policy.md +159 -0
  8. package/.claude/adr/ADR-008-hook-adapter-layer.md +169 -0
  9. package/.claude/adr/ADR-009-squad-contract.md +167 -0
  10. package/.claude/adr/ADR-010-canonical-edit-sentinel.md +181 -0
  11. package/.claude/adr/ADR-011-event-stream-v2.1-injection-flag.md +150 -0
  12. package/.claude/adr/ADR-012-cross-adapter-golden-fixtures.md +182 -0
  13. package/.claude/adr/ADR-013-squad-trading-hft.md +135 -0
  14. package/.claude/adr/ADR-014-hook-migration-batch-policy.md +197 -0
  15. package/.claude/adr/ADR-015-reflexion-v2-outcome-loop.md +248 -0
  16. package/.claude/adr/ADR-016-spawn-token-tracking.md +179 -0
  17. package/.claude/adr/ADR-017-lesson-pruning-policy.md +193 -0
  18. package/.claude/adr/ADR-018-claim-grammar.md +302 -0
  19. package/.claude/adr/ADR-019-AMEND-1-confidence-gate-block-mode-lifecycle.md +128 -0
  20. package/.claude/adr/ADR-019-AMEND-2-CLASS-SHA_EXISTS-promote-to-high-confidence-block.md +67 -0
  21. package/.claude/adr/ADR-019-confidence-gate-enforcement-lifecycle.md +221 -0
  22. package/.claude/adr/ADR-020-lesson-pruning-policy-v2.md +171 -0
  23. package/.claude/adr/ADR-021-e2e-harness-contract.md +189 -0
  24. package/.claude/adr/ADR-022-reserved-slot.md +52 -0
  25. package/.claude/adr/ADR-023-docs-freshness-lifecycle.md +184 -0
  26. package/.claude/adr/ADR-024-perf-baseline-policy.md +222 -0
  27. package/.claude/adr/ADR-025-squad-edtech.md +236 -0
  28. package/.claude/adr/ADR-026-squad-government.md +263 -0
  29. package/.claude/adr/ADR-027-unified-agent-state-backend.md +266 -0
  30. package/.claude/adr/ADR-028-multi-llm-canonical-parity.md +244 -0
  31. package/.claude/adr/ADR-029-lexical-tfidf-retrieval.md +205 -0
  32. package/.claude/adr/ADR-030-llm-as-judge-methodology.md +336 -0
  33. package/.claude/adr/ADR-031-self-improving-skills.md +221 -0
  34. package/.claude/adr/ADR-032-interactive-debate-protocol.md +337 -0
  35. package/.claude/adr/ADR-033-cost-budget-enforcement.md +275 -0
  36. package/.claude/adr/ADR-034-shared-working-memory.md +233 -0
  37. package/.claude/adr/ADR-035-otel-export.md +242 -0
  38. package/.claude/adr/ADR-036-output-safety.md +263 -0
  39. package/.claude/adr/ADR-037-chaos-testing-methodology.md +289 -0
  40. package/.claude/adr/ADR-038-session-graph-continuity.md +243 -0
  41. package/.claude/adr/ADR-039-skill-marketplace-protocol.md +170 -0
  42. package/.claude/adr/ADR-040-AMEND-2-credential-blocking.md +390 -0
  43. package/.claude/adr/ADR-040-live-adapter-activation-contract.md +285 -0
  44. package/.claude/adr/ADR-041-transition-log-convention.md +272 -0
  45. package/.claude/adr/ADR-042-AMEND-1-read-only-mcp-tools-expansion.md +214 -0
  46. package/.claude/adr/ADR-042-mcp-server-contract.md +727 -0
  47. package/.claude/adr/ADR-043-soc2-audit-trail-mapping.md +503 -0
  48. package/.claude/adr/ADR-044-formal-verification-pilot.md +505 -0
  49. package/.claude/adr/ADR-045-policy-as-code-engine.md +705 -0
  50. package/.claude/adr/ADR-046-deterministic-replay.md +167 -0
  51. package/.claude/adr/ADR-047-predictive-budgeting.md +213 -0
  52. package/.claude/adr/ADR-048-cross-plan-memory.md +227 -0
  53. package/.claude/adr/ADR-049-policy-engine-dual-path-deprecation.md +96 -0
  54. package/.claude/adr/ADR-049a-worktree-orchestration-policy.md +414 -0
  55. package/.claude/adr/ADR-050-native-subagents-dual-rail.md +165 -0
  56. package/.claude/adr/ADR-051-skill-reference-expanded-trust-boundary.md +282 -0
  57. package/.claude/adr/ADR-052-multi-model-dispatch-by-role.md +444 -0
  58. package/.claude/adr/ADR-053-sentinel-hmac-deferred.md +227 -0
  59. package/.claude/adr/ADR-054-AMEND-1-anthropic-admin-key-tier.md +131 -0
  60. package/.claude/adr/ADR-054-github-token-rotation.md +111 -0
  61. package/.claude/adr/ADR-055-AMEND-1-spool-writer-async-drain.md +170 -0
  62. package/.claude/adr/ADR-055-AMEND-2-chain-reset-marker.md +126 -0
  63. package/.claude/adr/ADR-055-AMEND-3-opportunistic-drain-nonblocking.md +183 -0
  64. package/.claude/adr/ADR-055-audit-log-hmac-chain.md +264 -0
  65. package/.claude/adr/ADR-056-hook-lifecycle-expansion.md +261 -0
  66. package/.claude/adr/ADR-057-output-scan-redaction.md +268 -0
  67. package/.claude/adr/ADR-058-brainstorm-gate-and-two-pass-review.md +240 -0
  68. package/.claude/adr/ADR-059-skill-bootstrap-env-knob.md +204 -0
  69. package/.claude/adr/ADR-060-curated-skill-import-pipeline.md +464 -0
  70. package/.claude/adr/ADR-061-runtime-cost-streaming.md +171 -0
  71. package/.claude/adr/ADR-062-AMEND-1-rag-conditional-default-on-supersedes-opt-in.md +232 -0
  72. package/.claude/adr/ADR-062-rag-sidecar-mcp-opt-in.md +231 -0
  73. package/.claude/adr/ADR-063-agent-eval-empirical-dispatch-validation.md +609 -0
  74. package/.claude/adr/ADR-064-dynamic-tier-policy-learned-dispatch.md +288 -0
  75. package/.claude/adr/ADR-065-audit-event-naming-convention.md +185 -0
  76. package/.claude/adr/ADR-066-context-mode-orthogonal-to-manifest.md +92 -0
  77. package/.claude/adr/ADR-067-ceo-model-downshift-static-routing.md +219 -0
  78. package/.claude/adr/ADR-069-wondelai-skills-import-refused.md +183 -0
  79. package/.claude/adr/ADR-070-audit-emit-package-layout.md +228 -0
  80. package/.claude/adr/ADR-071-benchmark-comparison-methodology.md +209 -0
  81. package/.claude/adr/ADR-072-test-discovery-via-conftest.md +184 -0
  82. package/.claude/adr/ADR-073-semver-bump-criteria-sprint-32.md +209 -0
  83. package/.claude/adr/ADR-074-sprint-32-phase-3-b1-refused.md +320 -0
  84. package/.claude/adr/ADR-075-sprint-32-phase-5-b5-benchmark-refused.md +250 -0
  85. package/.claude/adr/ADR-076-sprint-32-final-closure.md +218 -0
  86. package/.claude/adr/ADR-077-2026-04-24-webfetch-injection-incident.md +203 -0
  87. package/.claude/adr/ADR-078-sentinel-cosign-clarification.md +295 -0
  88. package/.claude/adr/ADR-079-prompt-sha-salt-hmac-impact.md +221 -0
  89. package/.claude/adr/ADR-080-rail-anomaly-h4-defense-in-depth.md +1143 -0
  90. package/.claude/adr/ADR-081-token-as-time-unit.md +272 -0
  91. package/.claude/adr/ADR-082-l7c-mitigation-default-on.md +240 -0
  92. package/.claude/adr/ADR-083-mcp-injection-scanner.md +225 -0
  93. package/.claude/adr/ADR-084-multi-adapter-refused-claude-only.md +152 -0
  94. package/.claude/adr/ADR-085-framework-landscape-claude-only.md +183 -0
  95. package/.claude/adr/ADR-086-checkpointing-refused.md +124 -0
  96. package/.claude/adr/ADR-087-AMEND-1-otel-consume-native-opt-in.md +217 -0
  97. package/.claude/adr/ADR-087-otel-emit-refused.md +136 -0
  98. package/.claude/adr/ADR-088-guardrails-library-refused.md +128 -0
  99. package/.claude/adr/ADR-089-sec-cluster-disposition.md +182 -0
  100. package/.claude/adr/ADR-090-framework-activation-defaults.md +217 -0
  101. package/.claude/adr/ADR-091-dogfood-validation-deferred.md +128 -0
  102. package/.claude/adr/ADR-092-plan-closure-honest-deferral.md +165 -0
  103. package/.claude/adr/ADR-093-refused-adr-moratorium.md +181 -0
  104. package/.claude/adr/ADR-094-claude-sdk-compat-version-pinning.md +160 -0
  105. package/.claude/adr/ADR-095-calendar-gate-retraction.md +202 -0
  106. package/.claude/adr/ADR-096-vibecoder-only-by-design.md +215 -0
  107. package/.claude/adr/ADR-097-function-length-advisory-permanent.md +186 -0
  108. package/.claude/adr/ADR-098-ceo-boot-audit-emit-register.md +251 -0
  109. package/.claude/adr/ADR-099-changesets-adoption.md +245 -0
  110. package/.claude/adr/ADR-100-trusted-dependencies-re-affirm.md +208 -0
  111. package/.claude/adr/ADR-101-replay-redact-helper.md +106 -0
  112. package/.claude/adr/ADR-102-mcp-introspection-extends-042.md +165 -0
  113. package/.claude/adr/ADR-103-calendar-gate-final-purge.md +121 -0
  114. package/.claude/adr/ADR-104-AMEND-1-aek-dated-promotion-criteria.md +338 -0
  115. package/.claude/adr/ADR-104-adaptive-execution-kernel-advisory.md +210 -0
  116. package/.claude/adr/ADR-105-multi-llm-coordinated-supersede.md +126 -0
  117. package/.claude/adr/ADR-106-codex-mcp-adapter-contract.md +153 -0
  118. package/.claude/adr/ADR-107-pair-rail-mandatory-l2-plus.md +189 -0
  119. package/.claude/adr/ADR-108-cross-llm-veto-floor.md +129 -0
  120. package/.claude/adr/ADR-109-codex-skill-rehash-protocol.md +104 -0
  121. package/.claude/adr/ADR-110-codex-pretool-enforcement.md +94 -0
  122. package/.claude/adr/ADR-111-locked-corpus-governance.md +191 -0
  123. package/.claude/adr/ADR-112-grandfather-cap-scope-clarification.md +192 -0
  124. package/.claude/adr/ADR-113-plan-084-canonical-guard-extension.md +59 -0
  125. package/.claude/adr/ADR-114-codex-egress-redaction-symmetry.md +72 -0
  126. package/.claude/adr/ADR-115-post-sota-maintenance-mode.md +152 -0
  127. package/.claude/adr/ADR-116-AMEND-1-kernel-extension-v2.md +640 -0
  128. package/.claude/adr/ADR-116-kernel-hard-deny-tier-0-extension.md +465 -0
  129. package/.claude/adr/ADR-117-adr-id-collision-rename-policy.md +279 -0
  130. package/.claude/adr/ADR-118-AMEND-1-phase-c-enforcing-flip.md +191 -0
  131. package/.claude/adr/ADR-118-god-mode-auto-usable-state.md +338 -0
  132. package/.claude/adr/ADR-119-sentinel-unlock-contract.md +133 -0
  133. package/.claude/adr/ADR-120-pii-core-promotion.md +280 -0
  134. package/.claude/adr/ADR-121-sentinel-signers-rotation-policy.md +434 -0
  135. package/.claude/adr/ADR-122-dpop-mcp-bearer-replay-defense.md +232 -0
  136. package/.claude/adr/ADR-123-streaming-adapter-canonical-source.md +130 -0
  137. package/.claude/adr/ADR-124-post-audit-sota-execution-mode.md +362 -0
  138. package/.claude/adr/ADR-125-risk-tiered-defaulting-doctrine.md +355 -0
  139. package/.claude/adr/ADR-126-governed-sidecar-capability-model.md +509 -0
  140. package/.claude/adr/ADR-127-pair-rail-advisory-promotion.md +218 -0
  141. package/.claude/adr/ADR-128-c2-vector-memory-capability-class.md +380 -0
  142. package/.claude/adr/ADR-129-AMEND-1-key-floor-waiver-lift.md +249 -0
  143. package/.claude/adr/ADR-129-c1-crypto-capability-class.md +289 -0
  144. package/.claude/adr/ADR-131-c5-dev-tools-capability-class.md +215 -0
  145. package/.claude/adr/ADR-132-goap-advisory-planning-doctrine.md +333 -0
  146. package/.claude/adr/ADR-133-autonomous-loop-opt-in-capability-doctrine.md +440 -0
  147. package/.claude/adr/ADR-135-AMEND-1-write-mode-trust-boundary.md +457 -0
  148. package/.claude/adr/ADR-135-AMEND-2-write-mode-activation.md +175 -0
  149. package/.claude/adr/ADR-135-federation-contract-mvp.md +253 -0
  150. package/.claude/adr/ADR-136-AMEND-1-workflow-primitive-adoption.md +139 -0
  151. package/.claude/adr/ADR-136-workflow-engine-doctrine.md +155 -0
  152. package/.claude/adr/ADR-137-skill-priority-stack-decision.md +162 -0
  153. package/.claude/adr/ADR-138-ac-format-priority-and-story-anchor.md +149 -0
  154. package/.claude/adr/ADR-139-coverage-doctrine-tiered.md +133 -0
  155. package/.claude/adr/ADR-140-receiving-review-doctrine.md +136 -0
  156. package/.claude/adr/ADR-141-reduce-protocol.md +124 -0
  157. package/.claude/adr/ADR-142-opus-4-8-model-bump.md +116 -0
  158. package/.claude/adr/ADR-143-git-hook-bypass-guard.md +166 -0
  159. package/.claude/adr/ADR-144-subagent-model-tiering-frontmatter.md +111 -0
  160. package/.claude/adr/ADR-145-cross-model-review-persona-demand-modality.md +103 -0
  161. package/.claude/adr/ADR-146-adversary-review-hook.md +122 -0
  162. package/.claude/adr/ADR-147-eval-harness-doctrine.md +109 -0
  163. package/.claude/adr/ADR-148-canonical-pricing-source.md +123 -0
  164. package/.claude/adr/ADR-149-model-id-allowlist.md +196 -0
  165. package/.claude/adr/ADR-150-commit-signing-policy.md +12 -0
  166. package/.claude/adr/ADR-151-fan-plan-advisory-bridge.md +178 -0
  167. package/.claude/adr/ADR-152-claude-md-decomposition.md +262 -0
  168. package/.claude/adr/ADR-153-compaction-continuity.md +141 -0
  169. package/.claude/adr/ADR-154-updatedinput-single-rewriter.md +68 -0
  170. package/.claude/adr/ADR-155-install-baseline-manifest.md +66 -0
  171. package/.claude/adr/ADR-156-constitution-sync-cascade.md +122 -0
  172. package/.claude/adr/README.md +392 -0
  173. package/.claude/adversary.md +116 -0
  174. package/.claude/agent-metrics.md +101 -0
  175. package/.claude/agents/_dispatch.md +30 -0
  176. package/.claude/agents/_probe_architect.md +45 -0
  177. package/.claude/agents/_probe_canonical_edit.md +46 -0
  178. package/.claude/agents/_probe_missing_skill.md +42 -0
  179. package/.claude/agents/code-reviewer.md +166 -0
  180. package/.claude/agents/devops.md +114 -0
  181. package/.claude/agents/identity-trust-architect.md +234 -0
  182. package/.claude/agents/incident-commander.md +285 -0
  183. package/.claude/agents/llm-finops-architect.md +265 -0
  184. package/.claude/agents/performance-engineer.md +148 -0
  185. package/.claude/agents/qa-architect.md +167 -0
  186. package/.claude/agents/security-engineer.md +192 -0
  187. package/.claude/agents/threat-detection-engineer.md +238 -0
  188. package/.claude/benchmarks/_schemas/judge-prompt.md +26 -0
  189. package/.claude/benchmarks/_schemas/judge-rubric-example.json +11 -0
  190. package/.claude/benchmarks/_schemas/judge-rubric.yaml +39 -0
  191. package/.claude/benchmarks/calibration-grades.jsonl +6 -0
  192. package/.claude/benchmarks/human-sample-calibration.md +232 -0
  193. package/.claude/benchmarks/judge-rotation-schedule.md +61 -0
  194. package/.claude/benchmarks/retrieval-judgment-set.yaml +194 -0
  195. package/.claude/benchmarks/tests/test_retrieval_recall_gate.py +330 -0
  196. package/.claude/commands/agent-budget.md +105 -0
  197. package/.claude/commands/architect.md +130 -0
  198. package/.claude/commands/audit-page.md +149 -0
  199. package/.claude/commands/audit-tokens.md +89 -0
  200. package/.claude/commands/ceo-boot.md +118 -0
  201. package/.claude/commands/ceo-info.md +71 -0
  202. package/.claude/commands/debate.md +258 -0
  203. package/.claude/commands/effort.md +99 -0
  204. package/.claude/commands/fan-plan.md +129 -0
  205. package/.claude/commands/goap.md +163 -0
  206. package/.claude/commands/lesson-review.md +66 -0
  207. package/.claude/commands/memory-scratchpad.md +100 -0
  208. package/.claude/commands/onboard.md +204 -0
  209. package/.claude/commands/pitfall.md +54 -0
  210. package/.claude/commands/resume.md +90 -0
  211. package/.claude/commands/self-test.md +83 -0
  212. package/.claude/commands/skill-review.md +102 -0
  213. package/.claude/commands/spawn.md +212 -0
  214. package/.claude/commands/squad-install.md +94 -0
  215. package/.claude/commands/status.md +177 -0
  216. package/.claude/commands/terse.md +81 -0
  217. package/.claude/commands/veto-check.md +63 -0
  218. package/.claude/data/audit-registry.golden.txt +306 -0
  219. package/.claude/data/canonical_models.json +1030 -0
  220. package/.claude/data/confidence-gate-class-tiers.json +24 -0
  221. package/.claude/data/cookbook_patterns.json +139 -0
  222. package/.claude/data/federation/enabled.md +34 -0
  223. package/.claude/data/federation/lan-enabled.md +38 -0
  224. package/.claude/data/federation/peers.example.yaml +89 -0
  225. package/.claude/data/goap/action-cost-baseline.json +29 -0
  226. package/.claude/dispatcher/disable_predicate_eval.py +630 -0
  227. package/.claude/dispatcher/routing-matrix-loader.py +874 -0
  228. package/.claude/dispatcher/routing-matrix.yaml +343 -0
  229. package/.claude/dispatcher/tests/conftest.py +11 -0
  230. package/.claude/dispatcher/tests/test_disable_predicate_eval.py +424 -0
  231. package/.claude/dispatcher/tests/test_routing_matrix_loader.py +461 -0
  232. package/.claude/docs/dpop-scope.md +79 -0
  233. package/.claude/docs/sentinel-signers-rotation-DRAFT.md +117 -0
  234. package/.claude/eval/README.md +73 -0
  235. package/.claude/eval/reporter.py +109 -0
  236. package/.claude/eval/runner.py +532 -0
  237. package/.claude/eval/self_test.yaml +57 -0
  238. package/.claude/eval/tasks/__init__.py +185 -0
  239. package/.claude/eval/tasks/t01_fix_off_by_one.py +52 -0
  240. package/.claude/eval/tasks/t02_implement_fizzbuzz.py +65 -0
  241. package/.claude/eval/tasks/t03_json_config_parse.py +80 -0
  242. package/.claude/eval/tasks/t04_refactor_dedupe.py +71 -0
  243. package/.claude/eval/tasks/t05_add_unit_test.py +77 -0
  244. package/.claude/eval/tasks/t06_palindrome.py +58 -0
  245. package/.claude/eval/tasks/t07_sql_param_fix.py +69 -0
  246. package/.claude/eval/tasks/t08_word_count.py +53 -0
  247. package/.claude/eval/tasks/t09_readme_doc.py +64 -0
  248. package/.claude/eval/tasks/t10_binary_search.py +58 -0
  249. package/.claude/frontend-team.md +202 -0
  250. package/.claude/governance/README.md +37 -0
  251. package/.claude/governance/audit_tokens_allowlist.json +37 -0
  252. package/.claude/governance/codex-cli-binary-sha256.txt +32 -0
  253. package/.claude/governance/codex-cli-pin.txt +26 -0
  254. package/.claude/governance/function-length-grandfather.yaml +2095 -0
  255. package/.claude/governance/governance-waivers.yaml +28 -0
  256. package/.claude/governance/pair-rail-inputs-hash-manifest.txt +32 -0
  257. package/.claude/governance/pair-rail-verdict-template.md +58 -0
  258. package/.claude/governance/pair-rail-verdict-v1.16.0-rc.1.md +120 -0
  259. package/.claude/governance/pair-rail-verdict-v1.16.0.md +64 -0
  260. package/.claude/gpg-revocations.jsonl +1 -0
  261. package/.claude/hooks/SessionEnd.py +353 -0
  262. package/.claude/hooks/SessionStart.py +345 -0
  263. package/.claude/hooks/Stop.py +195 -0
  264. package/.claude/hooks/UserPromptSubmit.py +329 -0
  265. package/.claude/hooks/_lib/EXECUTION-CONTEXT-DEFERRED.md +82 -0
  266. package/.claude/hooks/_lib/__init__.py +26 -0
  267. package/.claude/hooks/_lib/action_required.py +592 -0
  268. package/.claude/hooks/_lib/adapters/__init__.py +87 -0
  269. package/.claude/hooks/_lib/adapters/_constants.py +127 -0
  270. package/.claude/hooks/_lib/adapters/claude.py +167 -0
  271. package/.claude/hooks/_lib/adapters/codex.py +754 -0
  272. package/.claude/hooks/_lib/adapters/live/__init__.py +378 -0
  273. package/.claude/hooks/_lib/adapters/live/_breaker.py +309 -0
  274. package/.claude/hooks/_lib/adapters/live/_cost.py +389 -0
  275. package/.claude/hooks/_lib/adapters/live/_policy.py +319 -0
  276. package/.claude/hooks/_lib/adapters/live/_result.py +206 -0
  277. package/.claude/hooks/_lib/adapters/live/_transport.py +681 -0
  278. package/.claude/hooks/_lib/adapters/live/claude.py +1027 -0
  279. package/.claude/hooks/_lib/adapters/live/claude_batch.py +652 -0
  280. package/.claude/hooks/_lib/adapters/live/gemini.py +270 -0
  281. package/.claude/hooks/_lib/adapters/live/local.py +195 -0
  282. package/.claude/hooks/_lib/adapters/live/openai.py +371 -0
  283. package/.claude/hooks/_lib/adversary_rules.py +196 -0
  284. package/.claude/hooks/_lib/agent_frontmatter.py +288 -0
  285. package/.claude/hooks/_lib/audit_emit.py +11746 -0
  286. package/.claude/hooks/_lib/audit_emit_dispatch.py +179 -0
  287. package/.claude/hooks/_lib/audit_hmac.py +1146 -0
  288. package/.claude/hooks/_lib/audit_rotation.py +101 -0
  289. package/.claude/hooks/_lib/canonical_json.py +145 -0
  290. package/.claude/hooks/_lib/codex_cli_shape.py +502 -0
  291. package/.claude/hooks/_lib/codex_egress_redact.py +185 -0
  292. package/.claude/hooks/_lib/confidence_labels.py +338 -0
  293. package/.claude/hooks/_lib/contract.py +254 -0
  294. package/.claude/hooks/_lib/cookbook_patterns.py +136 -0
  295. package/.claude/hooks/_lib/cost_envelope.py +719 -0
  296. package/.claude/hooks/_lib/credentials.py +188 -0
  297. package/.claude/hooks/_lib/effective_config.py +767 -0
  298. package/.claude/hooks/_lib/egress_taxonomy.py +448 -0
  299. package/.claude/hooks/_lib/embeddings.py +322 -0
  300. package/.claude/hooks/_lib/env_guard.py +353 -0
  301. package/.claude/hooks/_lib/env_persist_allowlist.py +147 -0
  302. package/.claude/hooks/_lib/escalation_signals.py +335 -0
  303. package/.claude/hooks/_lib/estimation/__init__.py +12 -0
  304. package/.claude/hooks/_lib/estimation/bayesian.py +147 -0
  305. package/.claude/hooks/_lib/estimation/pipeline.py +209 -0
  306. package/.claude/hooks/_lib/exceptions.py +101 -0
  307. package/.claude/hooks/_lib/execution_context.py +208 -0
  308. package/.claude/hooks/_lib/federation/__init__.py +104 -0
  309. package/.claude/hooks/_lib/federation/audit_chain.py +118 -0
  310. package/.claude/hooks/_lib/federation/audit_chain_ext.py +408 -0
  311. package/.claude/hooks/_lib/federation/cert_inspector.py +573 -0
  312. package/.claude/hooks/_lib/federation/client.py +327 -0
  313. package/.claude/hooks/_lib/federation/handlers/__init__.py +30 -0
  314. package/.claude/hooks/_lib/federation/handlers/audit_event_batch.py +346 -0
  315. package/.claude/hooks/_lib/federation/handlers/audit_event_push.py +395 -0
  316. package/.claude/hooks/_lib/federation/handlers/peer_register.py +484 -0
  317. package/.claude/hooks/_lib/federation/handlers/peer_revoke.py +356 -0
  318. package/.claude/hooks/_lib/federation/identity.py +1056 -0
  319. package/.claude/hooks/_lib/federation/rate_limit.py +476 -0
  320. package/.claude/hooks/_lib/federation/replay.py +284 -0
  321. package/.claude/hooks/_lib/federation/scopes.py +168 -0
  322. package/.claude/hooks/_lib/federation/server.py +2218 -0
  323. package/.claude/hooks/_lib/file_walker.py +145 -0
  324. package/.claude/hooks/_lib/filelock.py +191 -0
  325. package/.claude/hooks/_lib/frontmatter.py +124 -0
  326. package/.claude/hooks/_lib/git_bypass.py +971 -0
  327. package/.claude/hooks/_lib/gpg_verify.py +356 -0
  328. package/.claude/hooks/_lib/guardrail_validator.py +478 -0
  329. package/.claude/hooks/_lib/injection_patterns.py +252 -0
  330. package/.claude/hooks/_lib/injection_salt.py +160 -0
  331. package/.claude/hooks/_lib/mcp/__init__.py +5 -0
  332. package/.claude/hooks/_lib/mcp/bearer_replay.py +279 -0
  333. package/.claude/hooks/_lib/mcp/canonical_guard.py +1140 -0
  334. package/.claude/hooks/_lib/mcp_bearer_friction.py +475 -0
  335. package/.claude/hooks/_lib/mcp_injection_scan.py +250 -0
  336. package/.claude/hooks/_lib/mcp_routing.py +151 -0
  337. package/.claude/hooks/_lib/memory_shared.py +592 -0
  338. package/.claude/hooks/_lib/metrics.py +241 -0
  339. package/.claude/hooks/_lib/model_routing.py +227 -0
  340. package/.claude/hooks/_lib/otel/__init__.py +34 -0
  341. package/.claude/hooks/_lib/otel/bounded_exporter.py +373 -0
  342. package/.claude/hooks/_lib/otel/hook_bridge.py +53 -0
  343. package/.claude/hooks/_lib/otel/queue.py +229 -0
  344. package/.claude/hooks/_lib/otel_emit.py +604 -0
  345. package/.claude/hooks/_lib/output_scan.py +1062 -0
  346. package/.claude/hooks/_lib/output_scan_dedup.py +379 -0
  347. package/.claude/hooks/_lib/pair_rail_decide.py +244 -0
  348. package/.claude/hooks/_lib/payload.py +195 -0
  349. package/.claude/hooks/_lib/persona_routing.py +244 -0
  350. package/.claude/hooks/_lib/pii_patterns.py +851 -0
  351. package/.claude/hooks/_lib/plan_frontmatter.py +166 -0
  352. package/.claude/hooks/_lib/policy.py +1527 -0
  353. package/.claude/hooks/_lib/policy_preprocessors.py +462 -0
  354. package/.claude/hooks/_lib/rag_bridge.py +624 -0
  355. package/.claude/hooks/_lib/rag_events.py +171 -0
  356. package/.claude/hooks/_lib/rag_router.py +253 -0
  357. package/.claude/hooks/_lib/redact.py +228 -0
  358. package/.claude/hooks/_lib/replay_redact.py +511 -0
  359. package/.claude/hooks/_lib/scratchpad_lib.py +225 -0
  360. package/.claude/hooks/_lib/secret_patterns.py +905 -0
  361. package/.claude/hooks/_lib/sentinel_signers.py +740 -0
  362. package/.claude/hooks/_lib/spec_context_sanitizer.py +258 -0
  363. package/.claude/hooks/_lib/spool_writer.py +2613 -0
  364. package/.claude/hooks/_lib/state_store.py +476 -0
  365. package/.claude/hooks/_lib/subagent_dispatch.py +244 -0
  366. package/.claude/hooks/_lib/swarm_circuit_breaker.py +203 -0
  367. package/.claude/hooks/_lib/swarm_enable_gate.py +152 -0
  368. package/.claude/hooks/_lib/team.py +128 -0
  369. package/.claude/hooks/_lib/test_isolation.py +352 -0
  370. package/.claude/hooks/_lib/testing.py +351 -0
  371. package/.claude/hooks/_lib/tests/federation/test_federation_attack_surface.py +251 -0
  372. package/.claude/hooks/_lib/tests/federation/test_federation_audit_stitching.py +135 -0
  373. package/.claude/hooks/_lib/tests/federation/test_federation_identity.py +234 -0
  374. package/.claude/hooks/_lib/tests/federation/test_federation_replay.py +204 -0
  375. package/.claude/hooks/_lib/tests/federation/test_federation_sentinel_stage2.py +214 -0
  376. package/.claude/hooks/_lib/tests/federation/test_federation_server.py +385 -0
  377. package/.claude/hooks/_lib/tests/test_confidence_gate_class_block.py +313 -0
  378. package/.claude/hooks/_lib/tests/test_cost_envelope.py +759 -0
  379. package/.claude/hooks/_lib/tests/test_execution_context.py +254 -0
  380. package/.claude/hooks/_lib/tests/test_goap_advisory_invariant.py +134 -0
  381. package/.claude/hooks/_lib/tests/test_goap_planner.py +368 -0
  382. package/.claude/hooks/_lib/tests/test_plan104_audit_emit.py +324 -0
  383. package/.claude/hooks/_lib/tests/test_plan104_demand_resolver.py +584 -0
  384. package/.claude/hooks/_lib/tests/test_plan104_demand_scan.py +164 -0
  385. package/.claude/hooks/_lib/tests/test_plan104_microbench.py +109 -0
  386. package/.claude/hooks/_lib/tests/test_plan104_waive_parser.py +113 -0
  387. package/.claude/hooks/_lib/tests/test_plan105_audit_emit.py +259 -0
  388. package/.claude/hooks/_lib/tests/test_plan105_check_roadmap_binding.py +68 -0
  389. package/.claude/hooks/_lib/tests/test_plan105_goap_planner.py +158 -0
  390. package/.claude/hooks/_lib/tests/test_plan105_spawn_outcome.py +234 -0
  391. package/.claude/hooks/_lib/tests/test_rag_dead_code_disposition.py +262 -0
  392. package/.claude/hooks/_lib/tests/test_rag_router.py +209 -0
  393. package/.claude/hooks/_lib/tests/test_swarm_circuit_breaker.py +278 -0
  394. package/.claude/hooks/_lib/tests/test_swarm_kill_switch_chain.py +360 -0
  395. package/.claude/hooks/_lib/tier_policy/__init__.py +123 -0
  396. package/.claude/hooks/_lib/tier_policy/_agent_frontmatter.py +509 -0
  397. package/.claude/hooks/_lib/tier_policy/_constants.py +376 -0
  398. package/.claude/hooks/_lib/tier_policy/_types.py +355 -0
  399. package/.claude/hooks/_lib/tier_policy/fixtures/baseline.json +17 -0
  400. package/.claude/hooks/_lib/tier_policy/fixtures/oversize_64kib.json +1 -0
  401. package/.claude/hooks/_lib/tier_policy/fixtures/prototype_pollution_attack.yaml +14 -0
  402. package/.claude/hooks/_lib/tier_policy/fixtures/schema_v1_sample.json +5 -0
  403. package/.claude/hooks/_lib/tier_policy/fixtures/schema_v2_sample.json +17 -0
  404. package/.claude/hooks/_lib/tier_policy/fixtures/yaml_bomb_attack.yaml +20 -0
  405. package/.claude/hooks/_lib/tier_policy/loader.py +476 -0
  406. package/.claude/hooks/_lib/tokens.py +136 -0
  407. package/.claude/hooks/_lib/tool_lifecycle.py +488 -0
  408. package/.claude/hooks/_lib/trusted_env.py +77 -0
  409. package/.claude/hooks/_python-hook.sh +242 -0
  410. package/.claude/hooks/accel_dispatch.py +172 -0
  411. package/.claude/hooks/adequacy_gate.py +424 -0
  412. package/.claude/hooks/audit_log.py +1352 -0
  413. package/.claude/hooks/auto_boot.py +518 -0
  414. package/.claude/hooks/check_adversary.py +273 -0
  415. package/.claude/hooks/check_agent_spawn.py +2696 -0
  416. package/.claude/hooks/check_anti_ceo_overhead.py +786 -0
  417. package/.claude/hooks/check_arbitration_kernel.py +544 -0
  418. package/.claude/hooks/check_bash_canonical_forensic.py +180 -0
  419. package/.claude/hooks/check_bash_safety.py +1483 -0
  420. package/.claude/hooks/check_budget.py +916 -0
  421. package/.claude/hooks/check_canonical_edit.py +1197 -0
  422. package/.claude/hooks/check_closeout_guard.py +154 -0
  423. package/.claude/hooks/check_codex_filewrite.py +366 -0
  424. package/.claude/hooks/check_codex_response.py +403 -0
  425. package/.claude/hooks/check_confidence_gate.py +545 -0
  426. package/.claude/hooks/check_config_change.py +346 -0
  427. package/.claude/hooks/check_config_protection.py +381 -0
  428. package/.claude/hooks/check_cost_envelope.py +286 -0
  429. package/.claude/hooks/check_fluency_nudge.py +747 -0
  430. package/.claude/hooks/check_mcp_response.py +234 -0
  431. package/.claude/hooks/check_output_safety.py +237 -0
  432. package/.claude/hooks/check_output_secrets.py +518 -0
  433. package/.claude/hooks/check_pair_rail.py +1700 -0
  434. package/.claude/hooks/check_plan_edit.py +905 -0
  435. package/.claude/hooks/check_postcompact_reinject.py +265 -0
  436. package/.claude/hooks/check_precompact_continuity.py +379 -0
  437. package/.claude/hooks/check_protocol_semver_cascade.py +401 -0
  438. package/.claude/hooks/check_read_injection.py +366 -0
  439. package/.claude/hooks/check_scratchpad_access.py +228 -0
  440. package/.claude/hooks/check_setup_verification.py +297 -0
  441. package/.claude/hooks/check_skill_bootstrap_post.py +339 -0
  442. package/.claude/hooks/check_skill_patch_sentinel.py +413 -0
  443. package/.claude/hooks/check_skill_reference_read.py +518 -0
  444. package/.claude/hooks/check_subagent_fabrication.py +45 -0
  445. package/.claude/hooks/check_subagent_start.py +232 -0
  446. package/.claude/hooks/check_tier_policy.py +211 -0
  447. package/.claude/hooks/check_tier_policy_misrouting_24h.py +187 -0
  448. package/.claude/hooks/check_webfetch_injection.py +277 -0
  449. package/.claude/hooks/check_worktree_writer.py +773 -0
  450. package/.claude/hooks/codex_review_user_code.py +304 -0
  451. package/.claude/hooks/emit_architect_outcome.py +232 -0
  452. package/.claude/hooks/latency_report.py +343 -0
  453. package/.claude/hooks/policy_dispatch.py +168 -0
  454. package/.claude/hooks/review_loop.py +560 -0
  455. package/.claude/hooks/route.py +115 -0
  456. package/.claude/hooks/tests/_agent_fixture.py +153 -0
  457. package/.claude/hooks/tests/adapters/__init__.py +0 -0
  458. package/.claude/hooks/tests/adapters/live/__init__.py +0 -0
  459. package/.claude/hooks/tests/adapters/live/test_adapters.py +488 -0
  460. package/.claude/hooks/tests/adapters/live/test_audit_wiring.py +81 -0
  461. package/.claude/hooks/tests/adapters/live/test_breaker.py +272 -0
  462. package/.claude/hooks/tests/adapters/live/test_cost.py +191 -0
  463. package/.claude/hooks/tests/adapters/live/test_o7_modernization.py +670 -0
  464. package/.claude/hooks/tests/adapters/live/test_policy.py +168 -0
  465. package/.claude/hooks/tests/conftest.py +139 -0
  466. package/.claude/hooks/tests/fixtures/adapters/claude/in/agent_spawn_compliant.json +9 -0
  467. package/.claude/hooks/tests/fixtures/adapters/claude/in/bash_safe_command.json +8 -0
  468. package/.claude/hooks/tests/fixtures/adapters/claude/in/post_audit_event.json +1 -0
  469. package/.claude/hooks/tests/fixtures/adapters/claude/out/allow.json +1 -0
  470. package/.claude/hooks/tests/fixtures/adapters/claude/out/block_with_reason.json +1 -0
  471. package/.claude/hooks/tests/fixtures/adapters/codex/in/.gitkeep +1 -0
  472. package/.claude/hooks/tests/fixtures/adapters/codex/out/.gitkeep +1 -0
  473. package/.claude/hooks/tests/fixtures/adapters/gemini/GAPS.md +46 -0
  474. package/.claude/hooks/tests/fixtures/adapters/gemini/in/agent_spawn_minimal.json +1 -0
  475. package/.claude/hooks/tests/fixtures/adapters/gemini/in/bash_minimal.json +1 -0
  476. package/.claude/hooks/tests/fixtures/adapters/gemini/out/allow.json +1 -0
  477. package/.claude/hooks/tests/fixtures/adapters/local/in/agent_spawn_ollama.json +19 -0
  478. package/.claude/hooks/tests/fixtures/adapters/local/in/bash_minimal.json +8 -0
  479. package/.claude/hooks/tests/fixtures/adapters/local/out/allow.json +1 -0
  480. package/.claude/hooks/tests/fixtures/adapters/openai/in/agent_spawn_chat_completions.json +13 -0
  481. package/.claude/hooks/tests/fixtures/adapters/openai/in/bash_responses_api.json +9 -0
  482. package/.claude/hooks/tests/fixtures/adapters/openai/out/allow.json +1 -0
  483. package/.claude/hooks/tests/fixtures/anti_ceo_overhead/should-NOT-block-on-Y.ndjson +13 -0
  484. package/.claude/hooks/tests/fixtures/anti_ceo_overhead/should-block-on-X.ndjson +9 -0
  485. package/.claude/hooks/tests/fixtures/byte_identity/__init__.py +5 -0
  486. package/.claude/hooks/tests/fixtures/byte_identity/bash_safety_fuzzer.py +287 -0
  487. package/.claude/hooks/tests/fixtures/byte_identity/plan_edit_fuzzer.py +364 -0
  488. package/.claude/hooks/tests/fixtures/exchange_keys/negative/aws-iam-policy-arn-id-25.txt +2 -0
  489. package/.claude/hooks/tests/fixtures/exchange_keys/negative/blog-paragraph-18.txt +1 -0
  490. package/.claude/hooks/tests/fixtures/exchange_keys/negative/boilerplate-26.txt +4 -0
  491. package/.claude/hooks/tests/fixtures/exchange_keys/negative/cdn-cache-key-12.txt +2 -0
  492. package/.claude/hooks/tests/fixtures/exchange_keys/negative/certificate-fingerprint-10.txt +2 -0
  493. package/.claude/hooks/tests/fixtures/exchange_keys/negative/changelog-19.txt +1 -0
  494. package/.claude/hooks/tests/fixtures/exchange_keys/negative/commit-sha-01.txt +4 -0
  495. package/.claude/hooks/tests/fixtures/exchange_keys/negative/django-csrf-token-24.txt +3 -0
  496. package/.claude/hooks/tests/fixtures/exchange_keys/negative/docker-image-04.txt +2 -0
  497. package/.claude/hooks/tests/fixtures/exchange_keys/negative/docs-example-22.txt +3 -0
  498. package/.claude/hooks/tests/fixtures/exchange_keys/negative/haiku-20.txt +1 -0
  499. package/.claude/hooks/tests/fixtures/exchange_keys/negative/hex-placeholder-15.txt +3 -0
  500. package/.claude/hooks/tests/fixtures/exchange_keys/negative/hex-short-23.txt +5 -0
  501. package/.claude/hooks/tests/fixtures/exchange_keys/negative/image-thumbnail-09.txt +3 -0
  502. package/.claude/hooks/tests/fixtures/exchange_keys/negative/jwt-payload-decoded-08.txt +3 -0
  503. package/.claude/hooks/tests/fixtures/exchange_keys/negative/kubernetes-uid-06.txt +3 -0
  504. package/.claude/hooks/tests/fixtures/exchange_keys/negative/md5-hash-02.txt +2 -0
  505. package/.claude/hooks/tests/fixtures/exchange_keys/negative/phone-number-16.txt +3 -0
  506. package/.claude/hooks/tests/fixtures/exchange_keys/negative/postgres-uuid-05.txt +2 -0
  507. package/.claude/hooks/tests/fixtures/exchange_keys/negative/redis-cluster-node-13.txt +3 -0
  508. package/.claude/hooks/tests/fixtures/exchange_keys/negative/session-token-11.txt +3 -0
  509. package/.claude/hooks/tests/fixtures/exchange_keys/negative/sha256-checksum-03.txt +3 -0
  510. package/.claude/hooks/tests/fixtures/exchange_keys/negative/short-token-21.txt +2 -0
  511. package/.claude/hooks/tests/fixtures/exchange_keys/negative/software-license-14.txt +4 -0
  512. package/.claude/hooks/tests/fixtures/exchange_keys/negative/telemetry-trace-07.txt +3 -0
  513. package/.claude/hooks/tests/fixtures/exchange_keys/negative/zip-postal-17.txt +4 -0
  514. package/.claude/hooks/tests/fixtures/exchange_keys/positive/binance-api-key-alnum-03.txt +1 -0
  515. package/.claude/hooks/tests/fixtures/exchange_keys/positive/binance-api-key-hex-01.txt +3 -0
  516. package/.claude/hooks/tests/fixtures/exchange_keys/positive/binance-api-key-hex-02.txt +2 -0
  517. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bip39-mnemonic-12-31.txt +2 -0
  518. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bip39-mnemonic-12-33.txt +2 -0
  519. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bip39-mnemonic-24-32.txt +2 -0
  520. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bitfinex-api-key-11.txt +1 -0
  521. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bitfinex-api-key-12.txt +1 -0
  522. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bitfinex-api-key-13.txt +2 -0
  523. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bitstamp-api-key-30.txt +3 -0
  524. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bitstamp-customer-id-29.txt +2 -0
  525. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bybit-api-key-18.txt +2 -0
  526. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bybit-api-key-19.txt +1 -0
  527. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bybit-api-secret-20.txt +1 -0
  528. package/.claude/hooks/tests/fixtures/exchange_keys/positive/bybit-combined-21.txt +3 -0
  529. package/.claude/hooks/tests/fixtures/exchange_keys/positive/coinbase-api-key-uuid-04.txt +2 -0
  530. package/.claude/hooks/tests/fixtures/exchange_keys/positive/coinbase-api-secret-b64-05.txt +1 -0
  531. package/.claude/hooks/tests/fixtures/exchange_keys/positive/coinbase-combined-07.txt +4 -0
  532. package/.claude/hooks/tests/fixtures/exchange_keys/positive/coinbase-passphrase-06.txt +1 -0
  533. package/.claude/hooks/tests/fixtures/exchange_keys/positive/evm-private-key-34.txt +2 -0
  534. package/.claude/hooks/tests/fixtures/exchange_keys/positive/evm-private-key-35.txt +1 -0
  535. package/.claude/hooks/tests/fixtures/exchange_keys/positive/evm-private-key-36.txt +2 -0
  536. package/.claude/hooks/tests/fixtures/exchange_keys/positive/generic-api-key-37.txt +2 -0
  537. package/.claude/hooks/tests/fixtures/exchange_keys/positive/generic-api-key-38.txt +3 -0
  538. package/.claude/hooks/tests/fixtures/exchange_keys/positive/generic-api-key-39.txt +2 -0
  539. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kraken-api-key-08.txt +1 -0
  540. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kraken-api-secret-09.txt +1 -0
  541. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kraken-combined-10.txt +4 -0
  542. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kucoin-api-key-uuid-26.txt +2 -0
  543. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kucoin-api-secret-uuid-27.txt +1 -0
  544. package/.claude/hooks/tests/fixtures/exchange_keys/positive/kucoin-passphrase-28.txt +1 -0
  545. package/.claude/hooks/tests/fixtures/exchange_keys/positive/okx-api-key-uuid-22.txt +1 -0
  546. package/.claude/hooks/tests/fixtures/exchange_keys/positive/okx-api-secret-23.txt +2 -0
  547. package/.claude/hooks/tests/fixtures/exchange_keys/positive/okx-combined-25.txt +4 -0
  548. package/.claude/hooks/tests/fixtures/exchange_keys/positive/okx-passphrase-24.txt +1 -0
  549. package/.claude/hooks/tests/fixtures/hooks/audit_log/in.json +1 -0
  550. package/.claude/hooks/tests/fixtures/hooks/audit_log/out.json +0 -0
  551. package/.claude/hooks/tests/fixtures/hooks/check_agent_spawn/in.json +1 -0
  552. package/.claude/hooks/tests/fixtures/hooks/check_agent_spawn/out.json +1 -0
  553. package/.claude/hooks/tests/fixtures/hooks/check_bash_safety/in.json +1 -0
  554. package/.claude/hooks/tests/fixtures/hooks/check_bash_safety/out.json +1 -0
  555. package/.claude/hooks/tests/fixtures/hooks/check_canonical_edit/in.json +1 -0
  556. package/.claude/hooks/tests/fixtures/hooks/check_canonical_edit/out.json +1 -0
  557. package/.claude/hooks/tests/fixtures/hooks/check_confidence_gate/in.json +1 -0
  558. package/.claude/hooks/tests/fixtures/hooks/check_confidence_gate/out.json +1 -0
  559. package/.claude/hooks/tests/fixtures/hooks/check_plan_edit/in.json +1 -0
  560. package/.claude/hooks/tests/fixtures/hooks/check_plan_edit/out.json +1 -0
  561. package/.claude/hooks/tests/fixtures/hooks/check_read_injection/in.json +1 -0
  562. package/.claude/hooks/tests/fixtures/hooks/check_read_injection/out.json +1 -0
  563. package/.claude/hooks/tests/fixtures/lifecycle/concurrent_interleaved.json +36 -0
  564. package/.claude/hooks/tests/fixtures/lifecycle/orphaned_pre.json +8 -0
  565. package/.claude/hooks/tests/fixtures/lifecycle/paired_bash_post.json +8 -0
  566. package/.claude/hooks/tests/fixtures/lifecycle/paired_bash_pre.json +9 -0
  567. package/.claude/hooks/tests/fixtures/normalized/agent_spawn_chat_completions.json +36 -0
  568. package/.claude/hooks/tests/fixtures/normalized/agent_spawn_compliant.json +24 -0
  569. package/.claude/hooks/tests/fixtures/normalized/agent_spawn_minimal.json +24 -0
  570. package/.claude/hooks/tests/fixtures/normalized/agent_spawn_ollama.json +42 -0
  571. package/.claude/hooks/tests/fixtures/normalized/bash_minimal.json +23 -0
  572. package/.claude/hooks/tests/fixtures/normalized/bash_responses_api.json +32 -0
  573. package/.claude/hooks/tests/fixtures/normalized/bash_safe_command.json +23 -0
  574. package/.claude/hooks/tests/fixtures/normalized/post_audit_event.json +31 -0
  575. package/.claude/hooks/tests/fixtures/output_safety/control/01_random_hash_log.txt +1 -0
  576. package/.claude/hooks/tests/fixtures/output_safety/control/02_docs_mention_email_no_address.txt +1 -0
  577. package/.claude/hooks/tests/fixtures/output_safety/control/03_partial_jwt_two_segments.txt +1 -0
  578. package/.claude/hooks/tests/fixtures/output_safety/control/04_random_11_digits_no_cpf_context.txt +1 -0
  579. package/.claude/hooks/tests/fixtures/output_safety/control/05_credit_card_shape_invalid_luhn.txt +1 -0
  580. package/.claude/hooks/tests/fixtures/output_safety/positive/01_api_key_anthropic.txt +1 -0
  581. package/.claude/hooks/tests/fixtures/output_safety/positive/02_api_key_github_pat_classic.txt +1 -0
  582. package/.claude/hooks/tests/fixtures/output_safety/positive/03_api_key_github_fine_grained.txt +1 -0
  583. package/.claude/hooks/tests/fixtures/output_safety/positive/04_api_key_aws_access_key.txt +1 -0
  584. package/.claude/hooks/tests/fixtures/output_safety/positive/05_api_key_aws_secret_assignment.txt +1 -0
  585. package/.claude/hooks/tests/fixtures/output_safety/positive/06_jwt.txt +1 -0
  586. package/.claude/hooks/tests/fixtures/output_safety/positive/07_bearer.txt +1 -0
  587. package/.claude/hooks/tests/fixtures/output_safety/positive/08_cpf_with_context.txt +1 -0
  588. package/.claude/hooks/tests/fixtures/output_safety/positive/09_cnpj_with_context.txt +1 -0
  589. package/.claude/hooks/tests/fixtures/output_safety/positive/10_credit_card_luhn_valid.txt +1 -0
  590. package/.claude/hooks/tests/fixtures/output_safety/positive/11_email_in_login_context.txt +1 -0
  591. package/.claude/hooks/tests/fixtures/output_safety/positive/12_nfkc_full_width.txt +1 -0
  592. package/.claude/hooks/tests/fixtures/output_safety/positive/13_zero_width_evasion.txt +1 -0
  593. package/.claude/hooks/tests/fixtures/output_safety/positive/14_bidi_evasion.txt +1 -0
  594. package/.claude/hooks/tests/fixtures/output_safety/positive/15_base64_encoded_secret.txt +1 -0
  595. package/.claude/hooks/tests/fixtures/output_scan/scenarios.jsonl +45 -0
  596. package/.claude/hooks/tests/fixtures/sample_payload_clean.json +13 -0
  597. package/.claude/hooks/tests/fixtures/sample_payload_with_secrets.json +12 -0
  598. package/.claude/hooks/tests/mutations/README.md +86 -0
  599. package/.claude/hooks/tests/mutations/__init__.py +14 -0
  600. package/.claude/hooks/tests/mutations/engine_mutations/__init__.py +15 -0
  601. package/.claude/hooks/tests/mutations/engine_mutations/mutation_01_parser_accepts_anchor.py +51 -0
  602. package/.claude/hooks/tests/mutations/engine_mutations/mutation_02_parser_skip_depth_limit.py +38 -0
  603. package/.claude/hooks/tests/mutations/engine_mutations/mutation_03_parser_accept_multi_doc.py +47 -0
  604. package/.claude/hooks/tests/mutations/engine_mutations/mutation_04_parser_accepts_bom.py +41 -0
  605. package/.claude/hooks/tests/mutations/engine_mutations/mutation_05_parser_scalar_len_off_by_one.py +61 -0
  606. package/.claude/hooks/tests/mutations/engine_mutations/mutation_06_parser_accepts_python_tag.py +50 -0
  607. package/.claude/hooks/tests/mutations/engine_mutations/mutation_07_parser_accepts_tab_indent.py +56 -0
  608. package/.claude/hooks/tests/mutations/engine_mutations/mutation_08_compiler_skip_regex_compile.py +45 -0
  609. package/.claude/hooks/tests/mutations/engine_mutations/mutation_09_compiler_regex_pattern_cap_off.py +31 -0
  610. package/.claude/hooks/tests/mutations/engine_mutations/mutation_10_compiler_accept_unknown_form.py +42 -0
  611. package/.claude/hooks/tests/mutations/engine_mutations/mutation_11_compiler_missing_predicate_tolerated.py +79 -0
  612. package/.claude/hooks/tests/mutations/engine_mutations/mutation_12_compiler_duplicate_rule_id_tolerated.py +66 -0
  613. package/.claude/hooks/tests/mutations/engine_mutations/mutation_13_compiler_missing_top_level_key_tolerated.py +46 -0
  614. package/.claude/hooks/tests/mutations/engine_mutations/mutation_14_compiler_schema_version_passthrough.py +43 -0
  615. package/.claude/hooks/tests/mutations/engine_mutations/mutation_15_evaluator_any_empty_returns_true.py +41 -0
  616. package/.claude/hooks/tests/mutations/engine_mutations/mutation_16_evaluator_all_empty_returns_true.py +37 -0
  617. package/.claude/hooks/tests/mutations/engine_mutations/mutation_17_evaluator_not_passthrough.py +37 -0
  618. package/.claude/hooks/tests/mutations/engine_mutations/mutation_18_evaluator_eq_true_on_type_mismatch.py +51 -0
  619. package/.claude/hooks/tests/mutations/engine_mutations/mutation_19_evaluator_regex_match_only.py +43 -0
  620. package/.claude/hooks/tests/mutations/engine_mutations/mutation_20_evaluator_path_under_no_realpath.py +48 -0
  621. package/.claude/hooks/tests/mutations/engine_mutations/mutation_21_evaluator_in_accepts_any.py +37 -0
  622. package/.claude/hooks/tests/mutations/engine_mutations/mutation_22_evaluator_length_off_by_one.py +45 -0
  623. package/.claude/hooks/tests/mutations/engine_mutations/mutation_23_evaluator_first_match_becomes_last.py +66 -0
  624. package/.claude/hooks/tests/mutations/engine_mutations/mutation_24_error_model_wrong_kind_on_parse.py +39 -0
  625. package/.claude/hooks/tests/mutations/engine_mutations/mutation_25_error_model_fail_open_on_load.py +42 -0
  626. package/.claude/hooks/tests/mutations/policy_mutations/__init__.py +16 -0
  627. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_01_remove_credential_leak.py +49 -0
  628. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_02_remove_rm_rf.py +44 -0
  629. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_03_remove_git_reset_hard.py +44 -0
  630. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_04_remove_git_push_force.py +44 -0
  631. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_05_reorder_rules.py +59 -0
  632. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_06_change_reason_enum.py +54 -0
  633. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_07_default_flipped_to_block.py +56 -0
  634. package/.claude/hooks/tests/mutations/policy_mutations/mutation_bash_08_flip_rm_rf_to_allow.py +49 -0
  635. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_01_remove_illegal_transition.py +79 -0
  636. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_02_remove_illegal_status.py +80 -0
  637. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_03_remove_missing_reviewed_at.py +80 -0
  638. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_04_remove_missing_completed_at.py +80 -0
  639. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_05_remove_missing_related_commits.py +79 -0
  640. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_06_remove_missing_abandonment_reason.py +80 -0
  641. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_07_scope_guard_inverted.py +93 -0
  642. package/.claude/hooks/tests/mutations/policy_mutations/mutation_plan_08_default_block.py +90 -0
  643. package/.claude/hooks/tests/probes/test_architect_probe.py +286 -0
  644. package/.claude/hooks/tests/probes/test_canonical_edit_probe.py +190 -0
  645. package/.claude/hooks/tests/probes/test_skill_content_probe.py +219 -0
  646. package/.claude/hooks/tests/test_SessionEnd.py +59 -0
  647. package/.claude/hooks/tests/test_SessionStart.py +42 -0
  648. package/.claude/hooks/tests/test_UserPromptSubmit.py +47 -0
  649. package/.claude/hooks/tests/test_accel_dispatch.py +96 -0
  650. package/.claude/hooks/tests/test_action_required_invariants.py +274 -0
  651. package/.claude/hooks/tests/test_adapter_drift_detector.py +254 -0
  652. package/.claude/hooks/tests/test_adapter_golden.py +198 -0
  653. package/.claude/hooks/tests/test_adequacy_gate.py +86 -0
  654. package/.claude/hooks/tests/test_adr_052_role_to_model_coverage.py +112 -0
  655. package/.claude/hooks/tests/test_adr_058_brainstorm_structure.py +280 -0
  656. package/.claude/hooks/tests/test_adversary_rules_live.py +400 -0
  657. package/.claude/hooks/tests/test_agent_frontmatter.py +377 -0
  658. package/.claude/hooks/tests/test_anti_ceo_overhead.py +591 -0
  659. package/.claude/hooks/tests/test_audit_emit.py +1707 -0
  660. package/.claude/hooks/tests/test_audit_emit_api_contract.py +693 -0
  661. package/.claude/hooks/tests/test_audit_emit_async_flush.py +563 -0
  662. package/.claude/hooks/tests/test_audit_emit_backpressure.py +138 -0
  663. package/.claude/hooks/tests/test_audit_emit_callsite_coverage_matrix.py +101 -0
  664. package/.claude/hooks/tests/test_audit_emit_chain_length.py +357 -0
  665. package/.claude/hooks/tests/test_audit_emit_coverage.py +2679 -0
  666. package/.claude/hooks/tests/test_audit_emit_ghost_action_guard.py +447 -0
  667. package/.claude/hooks/tests/test_audit_emit_plan088_canonical13.py +323 -0
  668. package/.claude/hooks/tests/test_audit_emit_rotation.py +218 -0
  669. package/.claude/hooks/tests/test_audit_emit_veto_v214.py +202 -0
  670. package/.claude/hooks/tests/test_audit_emit_wire_audit.py +699 -0
  671. package/.claude/hooks/tests/test_audit_hmac.py +334 -0
  672. package/.claude/hooks/tests/test_audit_hmac_branch_coverage.py +212 -0
  673. package/.claude/hooks/tests/test_audit_hmac_chain_monotonicity_property.py +136 -0
  674. package/.claude/hooks/tests/test_audit_hmac_coverage_v214.py +358 -0
  675. package/.claude/hooks/tests/test_audit_hmac_hardening.py +302 -0
  676. package/.claude/hooks/tests/test_audit_hmac_rotation_scenarios.py +231 -0
  677. package/.claude/hooks/tests/test_audit_hmac_verify_chain.py +443 -0
  678. package/.claude/hooks/tests/test_audit_log.py +280 -0
  679. package/.claude/hooks/tests/test_audit_log_coverage.py +173 -0
  680. package/.claude/hooks/tests/test_audit_log_path_d.py +516 -0
  681. package/.claude/hooks/tests/test_audit_log_phase1.py +358 -0
  682. package/.claude/hooks/tests/test_audit_log_schema_consistency.py +97 -0
  683. package/.claude/hooks/tests/test_audit_log_security.py +289 -0
  684. package/.claude/hooks/tests/test_audit_log_tokens.py +92 -0
  685. package/.claude/hooks/tests/test_audit_log_v2_7.py +378 -0
  686. package/.claude/hooks/tests/test_audit_log_v2_8_model.py +201 -0
  687. package/.claude/hooks/tests/test_audit_rotation.py +158 -0
  688. package/.claude/hooks/tests/test_audit_stream_verbose_protection.py +86 -0
  689. package/.claude/hooks/tests/test_audit_tokens_content_ban.py +512 -0
  690. package/.claude/hooks/tests/test_auto_boot.py +28 -0
  691. package/.claude/hooks/tests/test_available_models_mirror.py +226 -0
  692. package/.claude/hooks/tests/test_bash_canonical_forensic.py +74 -0
  693. package/.claude/hooks/tests/test_bash_canonical_interceptor.py +79 -0
  694. package/.claude/hooks/tests/test_brotli_passthrough.py +145 -0
  695. package/.claude/hooks/tests/test_byte_identity_fuzzer.py +185 -0
  696. package/.claude/hooks/tests/test_byte_identity_harness.py +953 -0
  697. package/.claude/hooks/tests/test_canonical_guard_typed_exceptions.py +117 -0
  698. package/.claude/hooks/tests/test_canonical_json.py +153 -0
  699. package/.claude/hooks/tests/test_chain_invariants_property.py +132 -0
  700. package/.claude/hooks/tests/test_check_adversary_live.py +149 -0
  701. package/.claude/hooks/tests/test_check_agent_spawn.py +1084 -0
  702. package/.claude/hooks/tests/test_check_agent_spawn_coverage.py +277 -0
  703. package/.claude/hooks/tests/test_check_agent_spawn_effort_token.py +74 -0
  704. package/.claude/hooks/tests/test_check_agent_spawn_import_isolation.py +82 -0
  705. package/.claude/hooks/tests/test_check_agent_spawn_model_routing_mode.py +245 -0
  706. package/.claude/hooks/tests/test_check_agent_spawn_reference_bypass.py +385 -0
  707. package/.claude/hooks/tests/test_check_agent_spawn_routing_promotion.py +302 -0
  708. package/.claude/hooks/tests/test_check_agent_spawn_skill_reference.py +336 -0
  709. package/.claude/hooks/tests/test_check_arbitration_kernel.py +472 -0
  710. package/.claude/hooks/tests/test_check_arbitration_kernel_v214.py +157 -0
  711. package/.claude/hooks/tests/test_check_bash_safety.py +546 -0
  712. package/.claude/hooks/tests/test_check_bash_safety_canonical_matrix.py +336 -0
  713. package/.claude/hooks/tests/test_check_bash_safety_cp_chaining.py +120 -0
  714. package/.claude/hooks/tests/test_check_bash_safety_h5_rewrite.py +462 -0
  715. package/.claude/hooks/tests/test_check_budget.py +580 -0
  716. package/.claude/hooks/tests/test_check_budget_max_tokens.py +397 -0
  717. package/.claude/hooks/tests/test_check_budget_quota_hint.py +115 -0
  718. package/.claude/hooks/tests/test_check_canonical_edit.py +302 -0
  719. package/.claude/hooks/tests/test_check_canonical_edit_coverage.py +370 -0
  720. package/.claude/hooks/tests/test_check_canonical_edit_kernel_v2.py +401 -0
  721. package/.claude/hooks/tests/test_check_canonical_edit_markers.py +473 -0
  722. package/.claude/hooks/tests/test_check_canonical_edit_mcp.py +401 -0
  723. package/.claude/hooks/tests/test_check_canonical_edit_session67_format.py +245 -0
  724. package/.claude/hooks/tests/test_check_codex_filewrite.py +964 -0
  725. package/.claude/hooks/tests/test_check_codex_response.py +419 -0
  726. package/.claude/hooks/tests/test_check_compaction_continuity.py +450 -0
  727. package/.claude/hooks/tests/test_check_confidence_gate.py +326 -0
  728. package/.claude/hooks/tests/test_check_config_change.py +369 -0
  729. package/.claude/hooks/tests/test_check_config_protection.py +364 -0
  730. package/.claude/hooks/tests/test_check_fluency_nudge.py +321 -0
  731. package/.claude/hooks/tests/test_check_mcp_response.py +261 -0
  732. package/.claude/hooks/tests/test_check_output_safety.py +314 -0
  733. package/.claude/hooks/tests/test_check_output_secrets.py +488 -0
  734. package/.claude/hooks/tests/test_check_output_secrets_coverage.py +321 -0
  735. package/.claude/hooks/tests/test_check_pair_rail.py +897 -0
  736. package/.claude/hooks/tests/test_check_pair_rail_decide_canonical.py +297 -0
  737. package/.claude/hooks/tests/test_check_pair_rail_golden.py +362 -0
  738. package/.claude/hooks/tests/test_check_pair_rail_hook_integration.py +120 -0
  739. package/.claude/hooks/tests/test_check_pair_rail_matrix.py +1077 -0
  740. package/.claude/hooks/tests/test_check_plan_edit.py +679 -0
  741. package/.claude/hooks/tests/test_check_plan_edit_stranded.py +310 -0
  742. package/.claude/hooks/tests/test_check_protocol_semver_cascade.py +141 -0
  743. package/.claude/hooks/tests/test_check_protocol_semver_cascade_settings_wired.py +297 -0
  744. package/.claude/hooks/tests/test_check_protocol_semver_cascade_synccascade.py +365 -0
  745. package/.claude/hooks/tests/test_check_read_injection.py +143 -0
  746. package/.claude/hooks/tests/test_check_read_injection_coverage.py +237 -0
  747. package/.claude/hooks/tests/test_check_read_injection_pathbound.py +153 -0
  748. package/.claude/hooks/tests/test_check_scratchpad_access.py +244 -0
  749. package/.claude/hooks/tests/test_check_skill_bootstrap_post.py +256 -0
  750. package/.claude/hooks/tests/test_check_skill_patch_sentinel.py +439 -0
  751. package/.claude/hooks/tests/test_check_skill_reference_read.py +170 -0
  752. package/.claude/hooks/tests/test_check_skill_reference_read_v2.py +388 -0
  753. package/.claude/hooks/tests/test_check_subagent_fabrication.py +54 -0
  754. package/.claude/hooks/tests/test_check_subagent_start.py +505 -0
  755. package/.claude/hooks/tests/test_check_tier_policy.py +48 -0
  756. package/.claude/hooks/tests/test_check_tier_policy_misrouting_24h.py +294 -0
  757. package/.claude/hooks/tests/test_check_webfetch_injection.py +49 -0
  758. package/.claude/hooks/tests/test_claim_producer_pair_end_to_end_loop_perf.py +227 -0
  759. package/.claude/hooks/tests/test_claude_adapter_thinking.py +731 -0
  760. package/.claude/hooks/tests/test_claude_batch_adapter.py +672 -0
  761. package/.claude/hooks/tests/test_closeout_guard.py +184 -0
  762. package/.claude/hooks/tests/test_codex_adapter.py +777 -0
  763. package/.claude/hooks/tests/test_codex_cli_shape.py +217 -0
  764. package/.claude/hooks/tests/test_codex_egress_proof_telemetry.py +214 -0
  765. package/.claude/hooks/tests/test_codex_egress_redact.py +342 -0
  766. package/.claude/hooks/tests/test_codex_egress_redact_outgoing.py +236 -0
  767. package/.claude/hooks/tests/test_codex_reply_multi_turn.py +72 -0
  768. package/.claude/hooks/tests/test_codex_review_user_code.py +44 -0
  769. package/.claude/hooks/tests/test_codex_strict_json.py +123 -0
  770. package/.claude/hooks/tests/test_confidence_gate_producer_pair.py +522 -0
  771. package/.claude/hooks/tests/test_confidence_labels.py +362 -0
  772. package/.claude/hooks/tests/test_contract.py +237 -0
  773. package/.claude/hooks/tests/test_cookbook_advisor_hook.py +208 -0
  774. package/.claude/hooks/tests/test_credentials.py +195 -0
  775. package/.claude/hooks/tests/test_detect_repo_profile_branches.py +116 -0
  776. package/.claude/hooks/tests/test_e2e_hook_chain.py +184 -0
  777. package/.claude/hooks/tests/test_effective_config.py +648 -0
  778. package/.claude/hooks/tests/test_emit_architect_outcome.py +175 -0
  779. package/.claude/hooks/tests/test_env_persist_allowlist.py +365 -0
  780. package/.claude/hooks/tests/test_escalation_signals.py +357 -0
  781. package/.claude/hooks/tests/test_estimation_bayesian_pipeline.py +140 -0
  782. package/.claude/hooks/tests/test_execution_context_deferral.py +222 -0
  783. package/.claude/hooks/tests/test_fail_open_contract.py +118 -0
  784. package/.claude/hooks/tests/test_file_walker.py +332 -0
  785. package/.claude/hooks/tests/test_filelock.py +131 -0
  786. package/.claude/hooks/tests/test_filelock_contract.py +172 -0
  787. package/.claude/hooks/tests/test_find_sentinels_pattern_matrix.py +114 -0
  788. package/.claude/hooks/tests/test_flip_closures.py +219 -0
  789. package/.claude/hooks/tests/test_frontmatter.py +139 -0
  790. package/.claude/hooks/tests/test_git_bypass_guard.py +1095 -0
  791. package/.claude/hooks/tests/test_gpg_verify.py +578 -0
  792. package/.claude/hooks/tests/test_hook_byte_fidelity.py +113 -0
  793. package/.claude/hooks/tests/test_hook_latency.py +245 -0
  794. package/.claude/hooks/tests/test_hook_latency_import.py +178 -0
  795. package/.claude/hooks/tests/test_injection_patterns.py +276 -0
  796. package/.claude/hooks/tests/test_injection_patterns_bypass.py +276 -0
  797. package/.claude/hooks/tests/test_injection_salt.py +191 -0
  798. package/.claude/hooks/tests/test_kernel_subsumes_security_critical_lib.py +88 -0
  799. package/.claude/hooks/tests/test_kill_switch_godmode_enforcing.py +101 -0
  800. package/.claude/hooks/tests/test_latency_report.py +28 -0
  801. package/.claude/hooks/tests/test_lib_canonical_import.py +355 -0
  802. package/.claude/hooks/tests/test_lifecycle_edge_cases.py +565 -0
  803. package/.claude/hooks/tests/test_live_adapters.py +463 -0
  804. package/.claude/hooks/tests/test_live_audit_isolation.py +357 -0
  805. package/.claude/hooks/tests/test_mcp_bearer_friction_buffer.py +276 -0
  806. package/.claude/hooks/tests/test_mcp_bearer_friction_emit.py +117 -0
  807. package/.claude/hooks/tests/test_mcp_canonical_guard.py +1989 -0
  808. package/.claude/hooks/tests/test_mcp_injection_repro_harness.py +437 -0
  809. package/.claude/hooks/tests/test_mcp_injection_scan.py +228 -0
  810. package/.claude/hooks/tests/test_mcp_routing_resolve.py +246 -0
  811. package/.claude/hooks/tests/test_memory_shared.py +412 -0
  812. package/.claude/hooks/tests/test_metrics.py +115 -0
  813. package/.claude/hooks/tests/test_migrated_hooks_fixtures.py +121 -0
  814. package/.claude/hooks/tests/test_model_routing.py +175 -0
  815. package/.claude/hooks/tests/test_model_routing_resolve.py +97 -0
  816. package/.claude/hooks/tests/test_model_routing_resolve_full.py +318 -0
  817. package/.claude/hooks/tests/test_otel_bounded_exporter.py +521 -0
  818. package/.claude/hooks/tests/test_otel_emit.py +243 -0
  819. package/.claude/hooks/tests/test_otel_queue.py +334 -0
  820. package/.claude/hooks/tests/test_otel_wire_defaultoff.py +392 -0
  821. package/.claude/hooks/tests/test_output_scan.py +1119 -0
  822. package/.claude/hooks/tests/test_output_scan_dedup.py +329 -0
  823. package/.claude/hooks/tests/test_output_scan_fixtures.py +136 -0
  824. package/.claude/hooks/tests/test_pair_rail_decide.py +141 -0
  825. package/.claude/hooks/tests/test_payload.py +89 -0
  826. package/.claude/hooks/tests/test_persona_coverage_wire.py +376 -0
  827. package/.claude/hooks/tests/test_persona_routing_enforcing.py +119 -0
  828. package/.claude/hooks/tests/test_phase_c_advisory_audit.py +75 -0
  829. package/.claude/hooks/tests/test_pii_patterns.py +558 -0
  830. package/.claude/hooks/tests/test_plan114_wires.py +468 -0
  831. package/.claude/hooks/tests/test_plan128_emit_wiring.py +74 -0
  832. package/.claude/hooks/tests/test_plan132_codex_review_observe.py +99 -0
  833. package/.claude/hooks/tests/test_plan133_a1_env_guard.py +221 -0
  834. package/.claude/hooks/tests/test_plan133_a2_canonical_skill_unicode.py +359 -0
  835. package/.claude/hooks/tests/test_plan133_a2_invisible_unicode.py +239 -0
  836. package/.claude/hooks/tests/test_plan133_a3_egress_taxonomy.py +221 -0
  837. package/.claude/hooks/tests/test_plan133_e1_adversary.py +360 -0
  838. package/.claude/hooks/tests/test_plan_085_wave_c_callsites_preserved.py +147 -0
  839. package/.claude/hooks/tests/test_plan_091_expected_callsites.py +206 -0
  840. package/.claude/hooks/tests/test_plan_frontmatter.py +217 -0
  841. package/.claude/hooks/tests/test_policy_coverage_residual_session73.py +597 -0
  842. package/.claude/hooks/tests/test_policy_coverage_v214.py +1099 -0
  843. package/.claude/hooks/tests/test_policy_dispatch.py +454 -0
  844. package/.claude/hooks/tests/test_policy_engine.py +791 -0
  845. package/.claude/hooks/tests/test_policy_fuzz_bomb.py +356 -0
  846. package/.claude/hooks/tests/test_policy_golden_error_kinds.py +287 -0
  847. package/.claude/hooks/tests/test_policy_mutations.py +359 -0
  848. package/.claude/hooks/tests/test_policy_preprocessors.py +514 -0
  849. package/.claude/hooks/tests/test_policy_redos_guards.py +393 -0
  850. package/.claude/hooks/tests/test_rag_bridge.py +675 -0
  851. package/.claude/hooks/tests/test_rag_events.py +202 -0
  852. package/.claude/hooks/tests/test_red_team_fixtures.py +427 -0
  853. package/.claude/hooks/tests/test_redact.py +506 -0
  854. package/.claude/hooks/tests/test_redact_redos.py +254 -0
  855. package/.claude/hooks/tests/test_redact_secrets_parity.py +334 -0
  856. package/.claude/hooks/tests/test_replay_determinism.py +263 -0
  857. package/.claude/hooks/tests/test_review_loop.py +28 -0
  858. package/.claude/hooks/tests/test_review_loop_wiring.py +206 -0
  859. package/.claude/hooks/tests/test_route.py +36 -0
  860. package/.claude/hooks/tests/test_rubric_catalogue.py +359 -0
  861. package/.claude/hooks/tests/test_scratchpad_lib.py +259 -0
  862. package/.claude/hooks/tests/test_secret_patterns.py +680 -0
  863. package/.claude/hooks/tests/test_secret_patterns_provenance.py +82 -0
  864. package/.claude/hooks/tests/test_sentinel_session_cache.py +324 -0
  865. package/.claude/hooks/tests/test_sentinel_session_cache_tier1.py +205 -0
  866. package/.claude/hooks/tests/test_sentinel_signers.py +641 -0
  867. package/.claude/hooks/tests/test_session_75_kernel_findings.py +180 -0
  868. package/.claude/hooks/tests/test_session_76_audit_v3_findings.py +493 -0
  869. package/.claude/hooks/tests/test_session_77_audit_v3_backlog_findings.py +644 -0
  870. package/.claude/hooks/tests/test_session_77_round_2_findings.py +135 -0
  871. package/.claude/hooks/tests/test_session_77_round_3_findings.py +159 -0
  872. package/.claude/hooks/tests/test_session_77_round_4_findings.py +120 -0
  873. package/.claude/hooks/tests/test_session_end.py +113 -0
  874. package/.claude/hooks/tests/test_session_start.py +293 -0
  875. package/.claude/hooks/tests/test_skill_unknown_ratio_path_d.py +249 -0
  876. package/.claude/hooks/tests/test_smart_loading_resolver_caching.py +140 -0
  877. package/.claude/hooks/tests/test_spec_context_sanitizer.py +179 -0
  878. package/.claude/hooks/tests/test_spool_drain_contended_skip.py +249 -0
  879. package/.claude/hooks/tests/test_spool_drain_rotation_property_b.py +227 -0
  880. package/.claude/hooks/tests/test_spool_drain_rotation_race.py +395 -0
  881. package/.claude/hooks/tests/test_spool_writer_cache.py +463 -0
  882. package/.claude/hooks/tests/test_state_store.py +302 -0
  883. package/.claude/hooks/tests/test_stop.py +133 -0
  884. package/.claude/hooks/tests/test_streaming_rate_cap.py +108 -0
  885. package/.claude/hooks/tests/test_subagent_dispatch.py +248 -0
  886. package/.claude/hooks/tests/test_subagent_model_override_removed.py +108 -0
  887. package/.claude/hooks/tests/test_team.py +95 -0
  888. package/.claude/hooks/tests/test_template_dogfood_parity.py +106 -0
  889. package/.claude/hooks/tests/test_terminal_compress.py +135 -0
  890. package/.claude/hooks/tests/test_test_env_context_agent_binding.py +140 -0
  891. package/.claude/hooks/tests/test_testing_helper.py +53 -0
  892. package/.claude/hooks/tests/test_thinking_budget_command.py +229 -0
  893. package/.claude/hooks/tests/test_tier_policy_agent_frontmatter.py +421 -0
  894. package/.claude/hooks/tests/test_tier_policy_agent_frontmatter_disposition.py +175 -0
  895. package/.claude/hooks/tests/test_tier_policy_constants.py +336 -0
  896. package/.claude/hooks/tests/test_tier_policy_loader.py +544 -0
  897. package/.claude/hooks/tests/test_tier_policy_loader_fallback_observed.py +169 -0
  898. package/.claude/hooks/tests/test_tier_policy_types.py +270 -0
  899. package/.claude/hooks/tests/test_tokens_lib.py +118 -0
  900. package/.claude/hooks/tests/test_tool_lifecycle.py +598 -0
  901. package/.claude/hooks/tests/test_tool_lifecycle_perf.py +110 -0
  902. package/.claude/hooks/tests/test_turbo_profile.py +28 -0
  903. package/.claude/hooks/tests/test_turbo_sessionstart.py +79 -0
  904. package/.claude/hooks/tests/test_two_writer_chain.py +175 -0
  905. package/.claude/hooks/tests/test_upgrade_retry.py +346 -0
  906. package/.claude/hooks/tests/test_user_prompt_submit.py +254 -0
  907. package/.claude/hooks/tests/test_user_prompt_submit_salt.py +204 -0
  908. package/.claude/hooks/tests/test_verify_after_edit.py +100 -0
  909. package/.claude/hooks/tests/test_veto_floor_bijection.py +174 -0
  910. package/.claude/hooks/tests/test_w5_cookbook_remediation.py +712 -0
  911. package/.claude/hooks/tests/test_w5_scrub_enforcement.py +371 -0
  912. package/.claude/hooks/tests/test_webfetch_injection.py +280 -0
  913. package/.claude/hooks/tests/test_wiredeadmod_estimation_wiring.py +283 -0
  914. package/.claude/hooks/tests/test_wiredeadmod_spawn_wiring.py +303 -0
  915. package/.claude/hooks/tests/test_worktree_writer.py +509 -0
  916. package/.claude/hooks/turbo_profile.py +554 -0
  917. package/.claude/hooks/turbo_sessionstart.py +472 -0
  918. package/.claude/hooks/verify_after_edit.py +281 -0
  919. package/.claude/pitfalls-catalog.yaml +150 -0
  920. package/.claude/plans/AUDIT-LOG-SCHEMA.md +548 -0
  921. package/.claude/plans/DEBATE-SCHEMA.md +539 -0
  922. package/.claude/plans/PLAN-128/AB-PROTOCOL.md +121 -0
  923. package/.claude/plans/PLAN-128/measure-state.sh +101 -0
  924. package/.claude/plans/PLAN-139-canonical-invariants-and-debt-ledger.md +253 -0
  925. package/.claude/plans/PLAN-140/architect/round-1/approved.md +40 -0
  926. package/.claude/plans/PLAN-140-compaction-hook-origin-dropfix.md +95 -0
  927. package/.claude/plans/PLAN-141/architect/round-1/approved.md +28 -0
  928. package/.claude/plans/PLAN-141-mcp-smoke-staging-ruff-tolerance.md +72 -0
  929. package/.claude/plans/PLAN-142/architect/round-1/anonymization-map.md +11 -0
  930. package/.claude/plans/PLAN-142/architect/round-1/consensus.md +95 -0
  931. package/.claude/plans/PLAN-142/architect/round-1/devops-engineer.md +57 -0
  932. package/.claude/plans/PLAN-142/architect/round-1/proposal.md +57 -0
  933. package/.claude/plans/PLAN-142/architect/round-1/security-engineer.md +55 -0
  934. package/.claude/plans/PLAN-142/architect/round-1/vp-engineering.md +58 -0
  935. package/.claude/plans/PLAN-142/architect/round-2/anonymization-map.md +11 -0
  936. package/.claude/plans/PLAN-142/architect/round-2/approved.md +65 -0
  937. package/.claude/plans/PLAN-142/architect/round-2/consensus.md +78 -0
  938. package/.claude/plans/PLAN-142/architect/round-2/devops-engineer.md +58 -0
  939. package/.claude/plans/PLAN-142/architect/round-2/security-engineer.md +56 -0
  940. package/.claude/plans/PLAN-142/architect/round-2/vp-engineering.md +54 -0
  941. package/.claude/plans/PLAN-142/staging/EXECUTION-RUNBOOK.md +74 -0
  942. package/.claude/plans/PLAN-142/staging/STAGING-NOTES.md +63 -0
  943. package/.claude/plans/PLAN-142/staging/check_pair_rail__invoke_and_consume.py.txt +644 -0
  944. package/.claude/plans/PLAN-142/staging/codex_adapter_parsers.py.txt +677 -0
  945. package/.claude/plans/PLAN-142/staging/codex_cli_shape.py +433 -0
  946. package/.claude/plans/PLAN-142-codex-cli-0139-adapter-migration.md +224 -0
  947. package/.claude/plans/PLAN-143/architect/round-1/anonymization-map.md +22 -0
  948. package/.claude/plans/PLAN-143/architect/round-1/consensus.md +108 -0
  949. package/.claude/plans/PLAN-143/architect/round-1/devops-engineer.md +228 -0
  950. package/.claude/plans/PLAN-143/architect/round-1/proposal.md +48 -0
  951. package/.claude/plans/PLAN-143/architect/round-1/security-engineer.md +224 -0
  952. package/.claude/plans/PLAN-143/architect/round-1/vp-engineering.md +166 -0
  953. package/.claude/plans/PLAN-143/patches/PLAN143-item1-env-inventory.NOTE.md +106 -0
  954. package/.claude/plans/PLAN-143/patches/PLAN143-item2-spool-writer-rotate-guard.patch +41 -0
  955. package/.claude/plans/PLAN-143/patches/PLAN143-item3-audit-emit-exit-code.patch +32 -0
  956. package/.claude/plans/PLAN-143-repo-hygiene-debt.md +201 -0
  957. package/.claude/plans/PLAN-SCHEMA.md +870 -0
  958. package/.claude/plans/README.md +208 -0
  959. package/.claude/plans/examples/debate-round-1/consensus.md +166 -0
  960. package/.claude/plans/examples/debate-round-1/devops-engineer.md +133 -0
  961. package/.claude/plans/examples/debate-round-1/proposal.md +66 -0
  962. package/.claude/plans/examples/debate-round-1/security-engineer.md +109 -0
  963. package/.claude/plans/examples/debate-round-1/vp-engineering.md +110 -0
  964. package/.claude/policies/.drift-manifest.json +16 -0
  965. package/.claude/policies/bash-safety.policy.yaml +37 -0
  966. package/.claude/policies/fixtures/.gitkeep +0 -0
  967. package/.claude/policies/fixtures/bash-safety.fixtures.jsonl +46 -0
  968. package/.claude/policies/fixtures/plan-edit.fixtures.jsonl +36 -0
  969. package/.claude/policies/grandfather-cap.policy.yaml +85 -0
  970. package/.claude/policies/plan-edit.policy.yaml +152 -0
  971. package/.claude/policies/rubric-violation-catalogue.yaml +187 -0
  972. package/.claude/policies/schemas/repo-profile-skill-binding.schema.json +126 -0
  973. package/.claude/policies/schemas/repo-profile.schema.json +83 -0
  974. package/.claude/policies/schemas/squad-bundle-frontmatter.schema.json +152 -0
  975. package/.claude/policies/secret-patterns-exchange.yaml +368 -0
  976. package/.claude/policies/smart-loading-cap-table.yaml +34 -0
  977. package/.claude/proposals/.gitkeep +0 -0
  978. package/.claude/proposals/README.md +42 -0
  979. package/.claude/proposals/SP-001-code-review-checklist-2026-04-20.md +65 -0
  980. package/.claude/proposals/SP-001-code-review-checklist-2026-04-20.md.asc +8 -0
  981. package/.claude/proposals/SP-002-security-and-auth-2026-04-20.md +74 -0
  982. package/.claude/proposals/SP-002-security-and-auth-2026-04-20.md.asc +8 -0
  983. package/.claude/proposals/SP-003-design-system-and-components-2026-04-20.md +67 -0
  984. package/.claude/proposals/SP-003-design-system-and-components-2026-04-20.md.asc +8 -0
  985. package/.claude/proposals/SP-004-accessibility-and-wcag-2026-04-20.md +68 -0
  986. package/.claude/proposals/SP-004-accessibility-and-wcag-2026-04-20.md.asc +8 -0
  987. package/.claude/proposals/SP-005-ux-and-user-journeys-2026-04-20.md +63 -0
  988. package/.claude/proposals/SP-005-ux-and-user-journeys-2026-04-20.md.asc +8 -0
  989. package/.claude/proposals/SP-006-chaos-and-resilience-2026-04-20.md +79 -0
  990. package/.claude/proposals/SP-006-chaos-and-resilience-2026-04-20.md.asc +8 -0
  991. package/.claude/proposals/SP-007-ai-llm-orchestration-2026-04-20.md +76 -0
  992. package/.claude/proposals/SP-007-ai-llm-orchestration-2026-04-20.md.asc +8 -0
  993. package/.claude/proposals/SP-008-performance-engineering-2026-04-20.md +82 -0
  994. package/.claude/proposals/SP-008-performance-engineering-2026-04-20.md.asc +8 -0
  995. package/.claude/proposals/SP-009-code-review-checklist-2026-04-20.md +76 -0
  996. package/.claude/proposals/SP-009-code-review-checklist-2026-04-20.md.asc +8 -0
  997. package/.claude/proposals/SP-010-accessibility-and-wcag-adopter-note-2026-04-20.md +77 -0
  998. package/.claude/proposals/SP-010-accessibility-and-wcag-adopter-note-2026-04-20.md.asc +8 -0
  999. package/.claude/proposals/SP-011-design-system-and-components-adopter-note-2026-04-20.md +79 -0
  1000. package/.claude/proposals/SP-011-design-system-and-components-adopter-note-2026-04-20.md.asc +8 -0
  1001. package/.claude/proposals/SP-012-ux-and-user-journeys-adopter-note-2026-04-20.md +83 -0
  1002. package/.claude/proposals/SP-012-ux-and-user-journeys-adopter-note-2026-04-20.md.asc +8 -0
  1003. package/.claude/proposals/SP-013-frontend-performance-optimization-2026-04-20.md +82 -0
  1004. package/.claude/proposals/SP-013-frontend-performance-optimization-2026-04-20.md.asc +8 -0
  1005. package/.claude/proposals/SP-014-observability-and-ops-2026-04-20.md +80 -0
  1006. package/.claude/proposals/SP-014-observability-and-ops-2026-04-20.md.asc +8 -0
  1007. package/.claude/proposals/SP-015-testing-strategy-2026-04-20.md +87 -0
  1008. package/.claude/proposals/SP-015-testing-strategy-2026-04-20.md.asc +8 -0
  1009. package/.claude/proposals/SP-016-code-review-checklist-fluency-rubric-2026-04-28.md +111 -0
  1010. package/.claude/proposals/SP-016-code-review-checklist-fluency-rubric-2026-04-28.md.asc +8 -0
  1011. package/.claude/proposals/SP-017-chaos-and-resilience-adopter-note-2026-04-28.md +87 -0
  1012. package/.claude/proposals/SP-017-chaos-and-resilience-adopter-note-2026-04-28.md.asc +8 -0
  1013. package/.claude/proposals/SP-018-ceo-orchestration-inventory-regen-2026-04-21.md +64 -0
  1014. package/.claude/proposals/SP-018-ceo-orchestration-inventory-regen-2026-04-21.md.asc +8 -0
  1015. package/.claude/proposals/SP-019-terse-mode-2026-04-21.md +107 -0
  1016. package/.claude/proposals/SP-019-terse-mode-2026-04-21.md.asc +8 -0
  1017. package/.claude/proposals/SP-020-ceo-orchestration-audit-tokens-2026-04-21.md +74 -0
  1018. package/.claude/proposals/SP-020-ceo-orchestration-audit-tokens-2026-04-21.md.asc +8 -0
  1019. package/.claude/proposals/SP-021-ceo-orchestration-autonomous-loop-2026-04-21.md +71 -0
  1020. package/.claude/proposals/SP-021-ceo-orchestration-autonomous-loop-2026-04-21.md.asc +8 -0
  1021. package/.claude/rag/_index_core.py +344 -0
  1022. package/.claude/rag/indexignore +101 -0
  1023. package/.claude/rag/install-sidecar.sh +275 -0
  1024. package/.claude/rag/models.manifest.json +19 -0
  1025. package/.claude/rag/requirements.lock +40 -0
  1026. package/.claude/rag/sidecar-config.template.json +53 -0
  1027. package/.claude/rag/tests/test_index_core.py +262 -0
  1028. package/.claude/rag/tests/test_install_sidecar.sh +132 -0
  1029. package/.claude/scripts/.known_actions_floor.lock +0 -0
  1030. package/.claude/scripts/admin-invite.py +199 -0
  1031. package/.claude/scripts/adopter-metrics.py +712 -0
  1032. package/.claude/scripts/aek-calibration-c2.py +253 -0
  1033. package/.claude/scripts/aek-calibration-c3.py +382 -0
  1034. package/.claude/scripts/aggregate-changesets.py +350 -0
  1035. package/.claude/scripts/architect-bundle-validate.py +227 -0
  1036. package/.claude/scripts/audit-dashboard.py +1320 -0
  1037. package/.claude/scripts/audit-log-labels.jsonl +0 -0
  1038. package/.claude/scripts/audit-log-retain.py +404 -0
  1039. package/.claude/scripts/audit-query.py +3333 -0
  1040. package/.claude/scripts/audit-telemetry.py +337 -0
  1041. package/.claude/scripts/audit-tokens.py +502 -0
  1042. package/.claude/scripts/audit-verify-chain.py +537 -0
  1043. package/.claude/scripts/backup-audit.py +247 -0
  1044. package/.claude/scripts/benchmark/plan-071-import-floor/README.md +194 -0
  1045. package/.claude/scripts/benchmark/plan-071-import-floor/fixtures/baseline.json +1 -0
  1046. package/.claude/scripts/benchmark/plan-071-import-floor/fixtures/expected_quantiles.json +11 -0
  1047. package/.claude/scripts/benchmark/plan-071-import-floor/import_floor_bench.py +791 -0
  1048. package/.claude/scripts/benchmark/plan-071-import-floor/run_bench.sh +180 -0
  1049. package/.claude/scripts/benchmark-fallback-scorer.py +254 -0
  1050. package/.claude/scripts/benchmark-judge.py +621 -0
  1051. package/.claude/scripts/budget-summary.py +946 -0
  1052. package/.claude/scripts/build-canonical-models.py +645 -0
  1053. package/.claude/scripts/calibration-kappa.py +262 -0
  1054. package/.claude/scripts/cc-analytics-pull.py +393 -0
  1055. package/.claude/scripts/ceo-backup.sh +307 -0
  1056. package/.claude/scripts/ceo-boot.py +3017 -0
  1057. package/.claude/scripts/ceo-cost.py +1116 -0
  1058. package/.claude/scripts/ceo-diagnose.py +486 -0
  1059. package/.claude/scripts/ceo-escalation-detector.py +743 -0
  1060. package/.claude/scripts/ceo-health.py +584 -0
  1061. package/.claude/scripts/ceo-info.py +1001 -0
  1062. package/.claude/scripts/ceo-restore.sh +215 -0
  1063. package/.claude/scripts/chaos-inject.py +439 -0
  1064. package/.claude/scripts/check-action-sha-drift.py +275 -0
  1065. package/.claude/scripts/check-active-hooks-executable.py +119 -0
  1066. package/.claude/scripts/check-adr-chain.py +617 -0
  1067. package/.claude/scripts/check-audit-action-name-convention.py +221 -0
  1068. package/.claude/scripts/check-audit-hmac-null.py +253 -0
  1069. package/.claude/scripts/check-audit-read-api-stable.py +239 -0
  1070. package/.claude/scripts/check-audit-registry-coverage.py +999 -0
  1071. package/.claude/scripts/check-auto-activation-flags.py +180 -0
  1072. package/.claude/scripts/check-canonical-doc-freshness.py +222 -0
  1073. package/.claude/scripts/check-claude-md-claims.py +346 -0
  1074. package/.claude/scripts/check-confidence-gate-drift.py +295 -0
  1075. package/.claude/scripts/check-conformance-harness-mapping.py +503 -0
  1076. package/.claude/scripts/check-contamination.sh +25 -0
  1077. package/.claude/scripts/check-creative-rewrite.py +596 -0
  1078. package/.claude/scripts/check-debate-round-lifecycle.py +185 -0
  1079. package/.claude/scripts/check-debt-ledger.py +305 -0
  1080. package/.claude/scripts/check-docs-drift.py +259 -0
  1081. package/.claude/scripts/check-docs-freshness.py +487 -0
  1082. package/.claude/scripts/check-flip-criteria-drift.py +426 -0
  1083. package/.claude/scripts/check-flip-release-gate-consistency.py +134 -0
  1084. package/.claude/scripts/check-framework-updates.sh +239 -0
  1085. package/.claude/scripts/check-function-length.py +426 -0
  1086. package/.claude/scripts/check-model-deprecations.py +377 -0
  1087. package/.claude/scripts/check-originator-residue.py +248 -0
  1088. package/.claude/scripts/check-pitfall-regression.sh +153 -0
  1089. package/.claude/scripts/check-policy-drift.py +74 -0
  1090. package/.claude/scripts/check-roadmap-binding.py +170 -0
  1091. package/.claude/scripts/check-rule-invariants.py +385 -0
  1092. package/.claude/scripts/check-sdk-compat.sh +76 -0
  1093. package/.claude/scripts/check-secret-pattern-coverage.py +175 -0
  1094. package/.claude/scripts/check-sidecar-manifest.py +493 -0
  1095. package/.claude/scripts/check-skill-activation-mode.py +41 -0
  1096. package/.claude/scripts/check-skill-health.sh +179 -0
  1097. package/.claude/scripts/check-spec-drift.py +147 -0
  1098. package/.claude/scripts/check-staleness.py +506 -0
  1099. package/.claude/scripts/check-stdlib-only.py +373 -0
  1100. package/.claude/scripts/check-substrate-watch.py +285 -0
  1101. package/.claude/scripts/check-swarm-harness-mapping.py +380 -0
  1102. package/.claude/scripts/check-test-audit-isolation.py +622 -0
  1103. package/.claude/scripts/check-test-env-hygiene.py +509 -0
  1104. package/.claude/scripts/check-threat-model-freshness.py +313 -0
  1105. package/.claude/scripts/check-tier-boundaries.py +233 -0
  1106. package/.claude/scripts/check-tla-schema-drift.py +272 -0
  1107. package/.claude/scripts/check_atlas_fpr.py +595 -0
  1108. package/.claude/scripts/check_contamination.py +337 -0
  1109. package/.claude/scripts/check_known_actions_floor.py +155 -0
  1110. package/.claude/scripts/check_threat_model_coverage.py +214 -0
  1111. package/.claude/scripts/check_translations_drift.py +199 -0
  1112. package/.claude/scripts/codex_invoke.py +436 -0
  1113. package/.claude/scripts/compare-adopters.py +549 -0
  1114. package/.claude/scripts/confidence-gate-backfill.py +261 -0
  1115. package/.claude/scripts/confidence_gate.py +736 -0
  1116. package/.claude/scripts/context-budget.py +1887 -0
  1117. package/.claude/scripts/contextual-recommender.py +815 -0
  1118. package/.claude/scripts/cost-table.yaml +99 -0
  1119. package/.claude/scripts/debate-converge.py +335 -0
  1120. package/.claude/scripts/debate-emit.py +132 -0
  1121. package/.claude/scripts/debate-orchestrate.py +972 -0
  1122. package/.claude/scripts/detect-repo-profile.py +1280 -0
  1123. package/.claude/scripts/detectors/__init__.py +19 -0
  1124. package/.claude/scripts/detectors/looping.py +127 -0
  1125. package/.claude/scripts/detectors/overpowered.py +96 -0
  1126. package/.claude/scripts/detectors/retry_churn.py +119 -0
  1127. package/.claude/scripts/detectors/schema.py +94 -0
  1128. package/.claude/scripts/detectors/tests/__init__.py +0 -0
  1129. package/.claude/scripts/detectors/tests/fixtures.py +420 -0
  1130. package/.claude/scripts/detectors/tests/test_looping.py +124 -0
  1131. package/.claude/scripts/detectors/tests/test_overpowered.py +114 -0
  1132. package/.claude/scripts/detectors/tests/test_retry_churn.py +101 -0
  1133. package/.claude/scripts/detectors/tests/test_schema.py +109 -0
  1134. package/.claude/scripts/detectors/tests/test_tool_cascade.py +131 -0
  1135. package/.claude/scripts/detectors/tests/test_wasteful_thinking.py +112 -0
  1136. package/.claude/scripts/detectors/tests/test_weak_model.py +104 -0
  1137. package/.claude/scripts/detectors/tool_cascade.py +127 -0
  1138. package/.claude/scripts/detectors/wasteful_thinking.py +99 -0
  1139. package/.claude/scripts/detectors/weak_model.py +92 -0
  1140. package/.claude/scripts/env-inventory-check.py +268 -0
  1141. package/.claude/scripts/env-inventory.json +3305 -0
  1142. package/.claude/scripts/extract-skill.py +456 -0
  1143. package/.claude/scripts/fan-plan-parser.py +370 -0
  1144. package/.claude/scripts/find-orphan-sentinels.py +89 -0
  1145. package/.claude/scripts/first-run-wizard.py +1151 -0
  1146. package/.claude/scripts/fixtures/cloned-trading-repo/.env.example +1 -0
  1147. package/.claude/scripts/fixtures/cloned-trading-repo/exchanges/binance.py +3 -0
  1148. package/.claude/scripts/fixtures/cloned-trading-repo/exchanges/coinbase.py +3 -0
  1149. package/.claude/scripts/fixtures/cloned-trading-repo/package.json +5 -0
  1150. package/.claude/scripts/fixtures/cloned-trading-repo/strategies/grid.py +3 -0
  1151. package/.claude/scripts/fixtures/cloned-trading-repo/strategies/pairs.py +3 -0
  1152. package/.claude/scripts/fixtures/missing-package-manifest/README.md +3 -0
  1153. package/.claude/scripts/fixtures/missing-package-manifest/src/main.py +1 -0
  1154. package/.claude/scripts/fixtures/mixed-frontend-backend/package.json +9 -0
  1155. package/.claude/scripts/fixtures/mixed-frontend-backend/requirements.txt +2 -0
  1156. package/.claude/scripts/fixtures/mixed-frontend-backend/src/api/handler.py +2 -0
  1157. package/.claude/scripts/fixtures/mixed-frontend-backend/src/pages/index.tsx +1 -0
  1158. package/.claude/scripts/fixtures/monorepo/apps/app-a/README.md +1 -0
  1159. package/.claude/scripts/fixtures/monorepo/apps/app-b/index.ts +1 -0
  1160. package/.claude/scripts/fixtures/monorepo/package.json +5 -0
  1161. package/.claude/scripts/fixtures/monorepo/packages/lib-a/index.js +1 -0
  1162. package/.claude/scripts/fixtures/monorepo/packages/lib-b/index.js +1 -0
  1163. package/.claude/scripts/fixtures/monorepo/pnpm-workspace.yaml +3 -0
  1164. package/.claude/scripts/fixtures/persona-coverage-expected-thresholds.yaml +20 -0
  1165. package/.claude/scripts/flip-criteria-drift-allowlist.txt +31 -0
  1166. package/.claude/scripts/generate-adr-index.py +339 -0
  1167. package/.claude/scripts/generate-available-models.py +280 -0
  1168. package/.claude/scripts/generate-dispatch.py +430 -0
  1169. package/.claude/scripts/generate-sbom.py +287 -0
  1170. package/.claude/scripts/generate-skill-inventory.sh +193 -0
  1171. package/.claude/scripts/github-api-client.py +297 -0
  1172. package/.claude/scripts/goap-planner.py +742 -0
  1173. package/.claude/scripts/hook-profiler.py +671 -0
  1174. package/.claude/scripts/import-skill.py +569 -0
  1175. package/.claude/scripts/import_ui_ux_pro_max.py +137 -0
  1176. package/.claude/scripts/inject-agent-context.sh +948 -0
  1177. package/.claude/scripts/k-calibration.py +456 -0
  1178. package/.claude/scripts/key-hygiene.py +511 -0
  1179. package/.claude/scripts/lesson-restore.py +171 -0
  1180. package/.claude/scripts/lesson_ranker.py +100 -0
  1181. package/.claude/scripts/lessons.py +883 -0
  1182. package/.claude/scripts/lint-skills.py +555 -0
  1183. package/.claude/scripts/local/README.md +280 -0
  1184. package/.claude/scripts/local/check-doc-skill-paths.sh +124 -0
  1185. package/.claude/scripts/local/dependency-graph.py +684 -0
  1186. package/.claude/scripts/local/estimate-calibrator.py +240 -0
  1187. package/.claude/scripts/local/findings-pretty-print.py +78 -0
  1188. package/.claude/scripts/local/generate-ceremony.sh +558 -0
  1189. package/.claude/scripts/local/pair-rail-gate.sh +156 -0
  1190. package/.claude/scripts/local/release-dry-run.py +853 -0
  1191. package/.claude/scripts/local/tests/test_dependency_graph.py +364 -0
  1192. package/.claude/scripts/local/tests/test_generate_ceremony.sh +144 -0
  1193. package/.claude/scripts/local/tests/test_release_dry_run.py +743 -0
  1194. package/.claude/scripts/local/validate-findings.py +168 -0
  1195. package/.claude/scripts/local/validate-saved-workflows.js +69 -0
  1196. package/.claude/scripts/local/verify-counts.sh +420 -0
  1197. package/.claude/scripts/local/verify-scope-coverage.py +205 -0
  1198. package/.claude/scripts/local/verify-staging-manifest.py +188 -0
  1199. package/.claude/scripts/local/wave-readonly-monitor.py +271 -0
  1200. package/.claude/scripts/log-friction.sh +290 -0
  1201. package/.claude/scripts/mcp/code_nav_bridge.py +259 -0
  1202. package/.claude/scripts/mcp-server/__init__.py +16 -0
  1203. package/.claude/scripts/mcp-server/auth.py +333 -0
  1204. package/.claude/scripts/mcp-server/cost.py +108 -0
  1205. package/.claude/scripts/mcp-server/dispatch.py +853 -0
  1206. package/.claude/scripts/mcp-server/handlers/__init__.py +16 -0
  1207. package/.claude/scripts/mcp-server/handlers/audit_query.py +384 -0
  1208. package/.claude/scripts/mcp-server/handlers/get_audit_log.py +163 -0
  1209. package/.claude/scripts/mcp-server/handlers/get_cost_budget.py +130 -0
  1210. package/.claude/scripts/mcp-server/handlers/get_debate_state.py +207 -0
  1211. package/.claude/scripts/mcp-server/handlers/get_skill.py +199 -0
  1212. package/.claude/scripts/mcp-server/handlers/list_agents.py +236 -0
  1213. package/.claude/scripts/mcp-server/handlers/list_pitfalls.py +192 -0
  1214. package/.claude/scripts/mcp-server/handlers/list_skills.py +197 -0
  1215. package/.claude/scripts/mcp-server/handlers/plan_status.py +489 -0
  1216. package/.claude/scripts/mcp-server/handlers/server_capabilities.py +127 -0
  1217. package/.claude/scripts/mcp-server/handlers/spawn_agent.py +274 -0
  1218. package/.claude/scripts/mcp-server/http_transport.py +373 -0
  1219. package/.claude/scripts/mcp-server/rate_limit.py +345 -0
  1220. package/.claude/scripts/mcp-server/server.py +212 -0
  1221. package/.claude/scripts/mcp-server/start-mcp-server.sh +111 -0
  1222. package/.claude/scripts/mcp-server/stdio_transport.py +150 -0
  1223. package/.claude/scripts/mcp-server/tests/__init__.py +1 -0
  1224. package/.claude/scripts/mcp-server/tests/test_auth.py +454 -0
  1225. package/.claude/scripts/mcp-server/tests/test_cost.py +122 -0
  1226. package/.claude/scripts/mcp-server/tests/test_dispatch.py +448 -0
  1227. package/.claude/scripts/mcp-server/tests/test_dispatch_bearer_replay_wire.py +358 -0
  1228. package/.claude/scripts/mcp-server/tests/test_handlers_get_audit_log.py +107 -0
  1229. package/.claude/scripts/mcp-server/tests/test_handlers_get_skill.py +108 -0
  1230. package/.claude/scripts/mcp-server/tests/test_handlers_list_agents.py +92 -0
  1231. package/.claude/scripts/mcp-server/tests/test_handlers_list_pitfalls.py +103 -0
  1232. package/.claude/scripts/mcp-server/tests/test_handlers_list_skills.py +121 -0
  1233. package/.claude/scripts/mcp-server/tests/test_handlers_server_capabilities.py +128 -0
  1234. package/.claude/scripts/mcp-server/tests/test_handlers_spawn_agent.py +275 -0
  1235. package/.claude/scripts/mcp-server/tests/test_http_transport.py +418 -0
  1236. package/.claude/scripts/mcp-server/tests/test_rate_limit.py +239 -0
  1237. package/.claude/scripts/mcp-server/tests/test_server.py +125 -0
  1238. package/.claude/scripts/mcp-server/tests/test_stdio_transport.py +196 -0
  1239. package/.claude/scripts/mcp-soak-monitor.py +224 -0
  1240. package/.claude/scripts/memory-prioritize.py +516 -0
  1241. package/.claude/scripts/migrate-grandfather-to-sha256.py +384 -0
  1242. package/.claude/scripts/model-deprecations.json +165 -0
  1243. package/.claude/scripts/morning-ceremony.py +266 -0
  1244. package/.claude/scripts/morning_ledger.py +446 -0
  1245. package/.claude/scripts/mutation-floors.yaml +51 -0
  1246. package/.claude/scripts/mutation-test.py +506 -0
  1247. package/.claude/scripts/nightly-proposals.py +210 -0
  1248. package/.claude/scripts/optimizer/__init__.py +46 -0
  1249. package/.claude/scripts/optimizer/_codex_redaction.py +101 -0
  1250. package/.claude/scripts/optimizer/_skeleton.py +137 -0
  1251. package/.claude/scripts/optimizer/codex_phase_gate.py +257 -0
  1252. package/.claude/scripts/optimizer/complexity_gate.py +208 -0
  1253. package/.claude/scripts/optimizer/fanout.py +249 -0
  1254. package/.claude/scripts/optimizer/model_choice.py +151 -0
  1255. package/.claude/scripts/optimizer/model_normalize.py +118 -0
  1256. package/.claude/scripts/optimizer/rag_recommender.py +110 -0
  1257. package/.claude/scripts/optimizer/recommender.py +213 -0
  1258. package/.claude/scripts/optimizer/tests/__init__.py +0 -0
  1259. package/.claude/scripts/optimizer/tests/test_codex_phase_gate.py +314 -0
  1260. package/.claude/scripts/optimizer/tests/test_codex_review_invoked_emission.py +225 -0
  1261. package/.claude/scripts/optimizer/tests/test_optimizer_complexity_gate.py +122 -0
  1262. package/.claude/scripts/optimizer/tests/test_optimizer_fanout.py +134 -0
  1263. package/.claude/scripts/optimizer/tests/test_optimizer_model_choice.py +124 -0
  1264. package/.claude/scripts/optimizer/tests/test_optimizer_model_normalize.py +155 -0
  1265. package/.claude/scripts/optimizer/tests/test_optimizer_rag_recommender.py +190 -0
  1266. package/.claude/scripts/optimizer/tests/test_optimizer_recommender.py +131 -0
  1267. package/.claude/scripts/optimizer/tests/test_optimizer_skeleton.py +117 -0
  1268. package/.claude/scripts/optimizer/tests/test_optimizer_types.py +53 -0
  1269. package/.claude/scripts/optimizer/types.py +122 -0
  1270. package/.claude/scripts/osv_check.py +559 -0
  1271. package/.claude/scripts/otel-export.py +329 -0
  1272. package/.claude/scripts/otel-local-sink.py +470 -0
  1273. package/.claude/scripts/persona_demand_resolver.py +658 -0
  1274. package/.claude/scripts/persona_demand_scan.py +382 -0
  1275. package/.claude/scripts/persona_waive_parser.py +127 -0
  1276. package/.claude/scripts/pitfall-query.py +218 -0
  1277. package/.claude/scripts/plan-tokens.py +843 -0
  1278. package/.claude/scripts/policy-shadow-runner.py +445 -0
  1279. package/.claude/scripts/predict-budget/predict-plan-cost.py +581 -0
  1280. package/.claude/scripts/predict-budget/tests/test_predict_plan_cost.py +375 -0
  1281. package/.claude/scripts/profile-opus-4-7.py +557 -0
  1282. package/.claude/scripts/prune-lessons.py +453 -0
  1283. package/.claude/scripts/rate-card-calibrate.py +283 -0
  1284. package/.claude/scripts/rate-card-fixtures.json +18 -0
  1285. package/.claude/scripts/reality-ledger.py +2175 -0
  1286. package/.claude/scripts/red-team-corpus/.byte-identity-check.txt +86 -0
  1287. package/.claude/scripts/red-team-corpus/README.md +132 -0
  1288. package/.claude/scripts/red-team-corpus/external/EXT-001-prompt-inject.md +24 -0
  1289. package/.claude/scripts/red-team-corpus/external/EXT-002-hackaprompt.md +25 -0
  1290. package/.claude/scripts/red-team-corpus/external/EXT-003-gcg.md +31 -0
  1291. package/.claude/scripts/red-team-corpus/external/EXT-004-tap.md +23 -0
  1292. package/.claude/scripts/red-team-corpus/external/EXT-005-cybersecurity-eval.md +30 -0
  1293. package/.claude/scripts/red-team-corpus/external/EXT-006-anthropic-samples.md +26 -0
  1294. package/.claude/scripts/red-team-corpus/external/EXT-007-trojan-source.md +26 -0
  1295. package/.claude/scripts/red-team-corpus/external/EXT-008-owasp-llm-top10.md +33 -0
  1296. package/.claude/scripts/red-team-corpus/external/EXT-009-jailbreak-bench.md +24 -0
  1297. package/.claude/scripts/red-team-corpus/external/EXT-010-advbench.md +22 -0
  1298. package/.claude/scripts/red-team-corpus/external/EXT-011-mitre-atlas.md +25 -0
  1299. package/.claude/scripts/red-team-corpus/external/EXT-012-npm-typosquat.md +23 -0
  1300. package/.claude/scripts/red-team-corpus/external/EXT-013-log-tamper-poc.md +25 -0
  1301. package/.claude/scripts/red-team-corpus/external/EXT-014-cwe-798-credentials.md +24 -0
  1302. package/.claude/scripts/red-team-corpus/external/EXT-015-garak.md +28 -0
  1303. package/.claude/scripts/red-team-corpus/external/EXT-016-skill-content-injection-via-markdown.jsonl +1 -0
  1304. package/.claude/scripts/red-team-corpus/external/EXT-017-persona-impersonation-ceo.jsonl +1 -0
  1305. package/.claude/scripts/red-team-corpus/external/EXT-018-file-assignment-wildcard-escape.jsonl +1 -0
  1306. package/.claude/scripts/red-team-corpus/external/EXT-019-veto-bypass-force-proceed.jsonl +1 -0
  1307. package/.claude/scripts/red-team-corpus/external/EXT-020-canonical-edit-circumvent-settings.jsonl +1 -0
  1308. package/.claude/scripts/red-team-corpus/external/EXT-021-spawn-without-agent-profile.jsonl +1 -0
  1309. package/.claude/scripts/red-team-corpus/external/EXT-022-hidden-unicode-in-skill-name.jsonl +1 -0
  1310. package/.claude/scripts/red-team-corpus/external/EXT-023-mcp-spawn-governance-bypass.jsonl +1 -0
  1311. package/.claude/scripts/red-team-corpus/external/EXT-024-adapter-credential-in-error-trace.jsonl +1 -0
  1312. package/.claude/scripts/red-team-corpus/external/EXT-025-sandbox-escape-nested-subshell.jsonl +1 -0
  1313. package/.claude/scripts/red-team-corpus/external/EXT-026-plan-edit-without-debate.jsonl +1 -0
  1314. package/.claude/scripts/red-team-corpus/external/EXT-027-audit-log-rotation-race.jsonl +1 -0
  1315. package/.claude/scripts/red-team-corpus/external/EXT-028-npm-dependency-confusion.jsonl +1 -0
  1316. package/.claude/scripts/red-team-corpus/external/EXT-029-output-safety-unicode-confusable.jsonl +1 -0
  1317. package/.claude/scripts/red-team-corpus/external/EXT-030-adapter-retry-storm-dos.jsonl +1 -0
  1318. package/.claude/scripts/red-team-corpus/external/EXT-031-team-md-direct-edit.jsonl +1 -0
  1319. package/.claude/scripts/red-team-corpus/external/EXT-032-sandbox-env-var-exfil.jsonl +1 -0
  1320. package/.claude/scripts/red-team-corpus/external/EXT-033-mcp-rate-limit-bypass-headers.jsonl +1 -0
  1321. package/.claude/scripts/red-team-corpus/external/EXT-034-otel-span-attribute-leak.jsonl +1 -0
  1322. package/.claude/scripts/red-team-corpus/external/EXT-035-skill-patch-polyglot-payload.jsonl +1 -0
  1323. package/.claude/scripts/red-team-corpus/external/EXT-036-output-safety-base64-triple-wrap.jsonl +1 -0
  1324. package/.claude/scripts/red-team-corpus/external/EXT-037-plan-id-cross-plan-memory-read.jsonl +1 -0
  1325. package/.claude/scripts/red-team-corpus/external/EXT-038-npm-slsa-provenance-strip.jsonl +1 -0
  1326. package/.claude/scripts/red-team-corpus/external/EXT-039-adapter-exfil-streaming-chunk.jsonl +1 -0
  1327. package/.claude/scripts/red-team-corpus/external/EXT-040-sandbox-symlink-to-secrets.jsonl +1 -0
  1328. package/.claude/scripts/red-team-corpus/external/README.md +63 -0
  1329. package/.claude/scripts/red-team-corpus/flake-budget.yaml +244 -0
  1330. package/.claude/scripts/red-team-corpus/provenance.md +74 -0
  1331. package/.claude/scripts/red-team-corpus/regression/REG-001-s3-audit-emission-gap.jsonl +1 -0
  1332. package/.claude/scripts/red-team-corpus/regression/REG-002-audit-registry-miss.jsonl +1 -0
  1333. package/.claude/scripts/red-team-corpus/regression/REG-003-breaker-provider-kwarg-missing.jsonl +1 -0
  1334. package/.claude/scripts/red-team-corpus/regression/REG-004-canonical-edit-conftest-block.jsonl +1 -0
  1335. package/.claude/scripts/red-team-corpus/regression/REG-005-mcp-dispatch-oversized-handler.jsonl +1 -0
  1336. package/.claude/scripts/red-team-corpus/regression/REG-006-audit-registry-false-orphan.jsonl +1 -0
  1337. package/.claude/scripts/red-team-corpus/regression/REG-007-spec-count-undercount.jsonl +1 -0
  1338. package/.claude/scripts/red-team-corpus/regression/REG-008-adr-reserved-slot-phantom.jsonl +1 -0
  1339. package/.claude/scripts/red-team-corpus/regression/REG-009-tlc-pending-placeholder.jsonl +1 -0
  1340. package/.claude/scripts/red-team-corpus/regression/REG-010-mutation-kill-rate-fake.jsonl +1 -0
  1341. package/.claude/scripts/red-team-corpus/regression/REG-011-byte-identity-governance-persona.jsonl +1 -0
  1342. package/.claude/scripts/red-team-corpus/regression/REG-012-conformance-mapping-partial-path.jsonl +1 -0
  1343. package/.claude/scripts/red-team-corpus/regression/REG-013-l1-fairness-lazy-fire.jsonl +1 -0
  1344. package/.claude/scripts/red-team-corpus/regression/REG-014-mcp-path-traversal-skill.jsonl +1 -0
  1345. package/.claude/scripts/red-team-corpus/regression/REG-015-mcp-hmac-timestamp-skew.jsonl +1 -0
  1346. package/.claude/scripts/red-team-corpus/synthetic/SYN-001-skill-patch-bidi-trojan.jsonl +1 -0
  1347. package/.claude/scripts/red-team-corpus/synthetic/SYN-002-skill-patch-zero-width-smuggle.jsonl +1 -0
  1348. package/.claude/scripts/red-team-corpus/synthetic/SYN-003-skill-patch-exec-smuggled-fence.jsonl +1 -0
  1349. package/.claude/scripts/red-team-corpus/synthetic/SYN-004-skill-patch-oversized-diff.jsonl +1 -0
  1350. package/.claude/scripts/red-team-corpus/synthetic/SYN-005-audit-log-byte-rewrite.jsonl +1 -0
  1351. package/.claude/scripts/red-team-corpus/synthetic/SYN-006-audit-log-truncation.jsonl +1 -0
  1352. package/.claude/scripts/red-team-corpus/synthetic/SYN-007-audit-log-lock-race.jsonl +1 -0
  1353. package/.claude/scripts/red-team-corpus/synthetic/SYN-008-plan-id-env-spoof.jsonl +1 -0
  1354. package/.claude/scripts/red-team-corpus/synthetic/SYN-009-plan-id-frontmatter-hijack.jsonl +1 -0
  1355. package/.claude/scripts/red-team-corpus/synthetic/SYN-010-plan-id-cross-plan-read.jsonl +1 -0
  1356. package/.claude/scripts/red-team-corpus/synthetic/SYN-011-sandbox-escape-curl-exfil.jsonl +1 -0
  1357. package/.claude/scripts/red-team-corpus/synthetic/SYN-012-sandbox-escape-env-dump.jsonl +1 -0
  1358. package/.claude/scripts/red-team-corpus/synthetic/SYN-013-sandbox-escape-symlink-plant.jsonl +1 -0
  1359. package/.claude/scripts/red-team-corpus/synthetic/SYN-014-mcp-handler-governance-bypass.jsonl +1 -0
  1360. package/.claude/scripts/red-team-corpus/synthetic/SYN-015-mcp-handler-acl-enumeration.jsonl +1 -0
  1361. package/.claude/scripts/red-team-corpus/synthetic/SYN-016-mcp-handler-rate-limit-evasion.jsonl +1 -0
  1362. package/.claude/scripts/red-team-corpus/synthetic/SYN-017-adapter-exfil-via-error-message.jsonl +1 -0
  1363. package/.claude/scripts/red-team-corpus/synthetic/SYN-018-adapter-exfil-otel-attr.jsonl +1 -0
  1364. package/.claude/scripts/red-team-corpus/synthetic/SYN-019-adapter-exfil-retry-replay.jsonl +1 -0
  1365. package/.claude/scripts/red-team-corpus/synthetic/SYN-020-output-safety-nfkc-bypass.jsonl +1 -0
  1366. package/.claude/scripts/red-team-corpus/synthetic/SYN-021-output-safety-base64-double-wrap.jsonl +1 -0
  1367. package/.claude/scripts/red-team-corpus/synthetic/SYN-022-output-safety-entropy-below-threshold.jsonl +1 -0
  1368. package/.claude/scripts/red-team-corpus/synthetic/SYN-023-output-safety-regex-obfuscation.jsonl +1 -0
  1369. package/.claude/scripts/red-team-corpus/synthetic/SYN-024-output-safety-luhn-partial.jsonl +1 -0
  1370. package/.claude/scripts/red-team-corpus/synthetic/SYN-025-npm-tamper-supply-chain.jsonl +1 -0
  1371. package/.claude/scripts/red-team-corpus/synthetic/SYN-026-npm-tamper-typo-squat.jsonl +1 -0
  1372. package/.claude/scripts/red-team-corpus/synthetic/SYN-027-npm-tamper-unsigned-slsa.jsonl +1 -0
  1373. package/.claude/scripts/red-team-corpus/v1/fixtures.jsonl +67 -0
  1374. package/.claude/scripts/red-team-corpus/v1/fixtures.jsonl.sha256 +1 -0
  1375. package/.claude/scripts/red-team-corpus/v1/labels.json +88 -0
  1376. package/.claude/scripts/red-team-eval.py +1099 -0
  1377. package/.claude/scripts/registry.py +438 -0
  1378. package/.claude/scripts/replay/__init__.py +0 -0
  1379. package/.claude/scripts/replay/replay-session.py +1232 -0
  1380. package/.claude/scripts/replay/tests/__init__.py +0 -0
  1381. package/.claude/scripts/replay/tests/fixtures/api-key-01-positive.jsonl +1 -0
  1382. package/.claude/scripts/replay/tests/fixtures/api-key-02-positive.jsonl +1 -0
  1383. package/.claude/scripts/replay/tests/fixtures/api-key-03-positive.jsonl +1 -0
  1384. package/.claude/scripts/replay/tests/fixtures/api-key-04-positive.jsonl +1 -0
  1385. package/.claude/scripts/replay/tests/fixtures/api-key-05-negative.jsonl +1 -0
  1386. package/.claude/scripts/replay/tests/fixtures/api-key-06-negative.jsonl +1 -0
  1387. package/.claude/scripts/replay/tests/fixtures/api-key-07-negative.jsonl +1 -0
  1388. package/.claude/scripts/replay/tests/fixtures/api-key-08-negative.jsonl +1 -0
  1389. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-01-positive.jsonl +1 -0
  1390. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-02-positive.jsonl +1 -0
  1391. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-03-positive.jsonl +1 -0
  1392. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-04-positive.jsonl +1 -0
  1393. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-05-negative.jsonl +1 -0
  1394. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-06-negative.jsonl +1 -0
  1395. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-07-negative.jsonl +1 -0
  1396. package/.claude/scripts/replay/tests/fixtures/cpf-cnpj-08-negative.jsonl +1 -0
  1397. package/.claude/scripts/replay/tests/fixtures/email-in-log-01-positive.jsonl +1 -0
  1398. package/.claude/scripts/replay/tests/fixtures/email-in-log-02-positive.jsonl +1 -0
  1399. package/.claude/scripts/replay/tests/fixtures/email-in-log-03-positive.jsonl +1 -0
  1400. package/.claude/scripts/replay/tests/fixtures/email-in-log-04-positive.jsonl +1 -0
  1401. package/.claude/scripts/replay/tests/fixtures/email-in-log-05-negative.jsonl +1 -0
  1402. package/.claude/scripts/replay/tests/fixtures/email-in-log-06-negative.jsonl +1 -0
  1403. package/.claude/scripts/replay/tests/fixtures/email-in-log-07-negative.jsonl +1 -0
  1404. package/.claude/scripts/replay/tests/fixtures/email-in-log-08-negative.jsonl +1 -0
  1405. package/.claude/scripts/replay/tests/fixtures/homoglyph-01-positive.jsonl +1 -0
  1406. package/.claude/scripts/replay/tests/fixtures/homoglyph-02-positive.jsonl +1 -0
  1407. package/.claude/scripts/replay/tests/fixtures/homoglyph-03-positive.jsonl +1 -0
  1408. package/.claude/scripts/replay/tests/fixtures/homoglyph-04-positive.jsonl +1 -0
  1409. package/.claude/scripts/replay/tests/fixtures/homoglyph-05-negative.jsonl +1 -0
  1410. package/.claude/scripts/replay/tests/fixtures/homoglyph-06-negative.jsonl +1 -0
  1411. package/.claude/scripts/replay/tests/fixtures/homoglyph-07-negative.jsonl +1 -0
  1412. package/.claude/scripts/replay/tests/fixtures/homoglyph-08-negative.jsonl +1 -0
  1413. package/.claude/scripts/replay/tests/fixtures/jwt-01-positive.jsonl +1 -0
  1414. package/.claude/scripts/replay/tests/fixtures/jwt-02-positive.jsonl +1 -0
  1415. package/.claude/scripts/replay/tests/fixtures/jwt-03-positive.jsonl +1 -0
  1416. package/.claude/scripts/replay/tests/fixtures/jwt-04-positive.jsonl +1 -0
  1417. package/.claude/scripts/replay/tests/fixtures/jwt-05-negative.jsonl +1 -0
  1418. package/.claude/scripts/replay/tests/fixtures/jwt-06-negative.jsonl +1 -0
  1419. package/.claude/scripts/replay/tests/fixtures/jwt-07-negative.jsonl +1 -0
  1420. package/.claude/scripts/replay/tests/fixtures/jwt-08-negative.jsonl +1 -0
  1421. package/.claude/scripts/replay/tests/fixtures/os-path-01-positive.jsonl +1 -0
  1422. package/.claude/scripts/replay/tests/fixtures/os-path-02-positive.jsonl +1 -0
  1423. package/.claude/scripts/replay/tests/fixtures/os-path-03-positive.jsonl +1 -0
  1424. package/.claude/scripts/replay/tests/fixtures/os-path-04-positive.jsonl +1 -0
  1425. package/.claude/scripts/replay/tests/fixtures/os-path-05-negative.jsonl +1 -0
  1426. package/.claude/scripts/replay/tests/fixtures/os-path-06-negative.jsonl +1 -0
  1427. package/.claude/scripts/replay/tests/fixtures/os-path-07-negative.jsonl +1 -0
  1428. package/.claude/scripts/replay/tests/fixtures/os-path-08-negative.jsonl +1 -0
  1429. package/.claude/scripts/replay/tests/fixtures/pan-01-positive.jsonl +1 -0
  1430. package/.claude/scripts/replay/tests/fixtures/pan-02-positive.jsonl +1 -0
  1431. package/.claude/scripts/replay/tests/fixtures/pan-03-positive.jsonl +1 -0
  1432. package/.claude/scripts/replay/tests/fixtures/pan-04-positive.jsonl +1 -0
  1433. package/.claude/scripts/replay/tests/fixtures/pan-05-negative.jsonl +1 -0
  1434. package/.claude/scripts/replay/tests/fixtures/pan-06-negative.jsonl +1 -0
  1435. package/.claude/scripts/replay/tests/fixtures/pan-07-negative.jsonl +1 -0
  1436. package/.claude/scripts/replay/tests/fixtures/pan-08-negative.jsonl +1 -0
  1437. package/.claude/scripts/replay/tests/test_replay_redact_lib.py +971 -0
  1438. package/.claude/scripts/replay/tests/test_replay_session.py +396 -0
  1439. package/.claude/scripts/replay/tests/test_replay_session_capture.py +522 -0
  1440. package/.claude/scripts/repo-profile.schema.json +83 -0
  1441. package/.claude/scripts/run-promotion-gate.py +631 -0
  1442. package/.claude/scripts/run-skill-benchmark.py +1276 -0
  1443. package/.claude/scripts/scan-injection-strict.sh +162 -0
  1444. package/.claude/scripts/scan-injection.py +305 -0
  1445. package/.claude/scripts/scan-upstream-injection.py +663 -0
  1446. package/.claude/scripts/scratchpad.py +427 -0
  1447. package/.claude/scripts/self_test.py +602 -0
  1448. package/.claude/scripts/session-graph-build.py +728 -0
  1449. package/.claude/scripts/session-resume.py +363 -0
  1450. package/.claude/scripts/set-quality-profile.sh +229 -0
  1451. package/.claude/scripts/skill-budget-generator.py +599 -0
  1452. package/.claude/scripts/skill-import-rubric.py +368 -0
  1453. package/.claude/scripts/skill-index-build.py +534 -0
  1454. package/.claude/scripts/skill-patch-apply.py +1088 -0
  1455. package/.claude/scripts/skill-patch-propose.py +690 -0
  1456. package/.claude/scripts/skill-retrieve.py +522 -0
  1457. package/.claude/scripts/skill_grandfather_parser.py +295 -0
  1458. package/.claude/scripts/smart-loading-resolver.py +994 -0
  1459. package/.claude/scripts/spot-check-findings.py +211 -0
  1460. package/.claude/scripts/squad-export.py +437 -0
  1461. package/.claude/scripts/squad-import.py +741 -0
  1462. package/.claude/scripts/status.py +315 -0
  1463. package/.claude/scripts/statusline-ceo.py +597 -0
  1464. package/.claude/scripts/substrate-watch.json +54 -0
  1465. package/.claude/scripts/success-receipt.py +1038 -0
  1466. package/.claude/scripts/swarm/__init__.py +42 -0
  1467. package/.claude/scripts/swarm/_benchmark_replay.py +259 -0
  1468. package/.claude/scripts/swarm/_child_isolation.py +113 -0
  1469. package/.claude/scripts/swarm/_coordinator_sim.py +293 -0
  1470. package/.claude/scripts/swarm/_governors.py +277 -0
  1471. package/.claude/scripts/swarm/_integration.py +547 -0
  1472. package/.claude/scripts/swarm/_parent_death.py +176 -0
  1473. package/.claude/scripts/swarm/_process_group.py +250 -0
  1474. package/.claude/scripts/swarm/_replay_tournament.py +214 -0
  1475. package/.claude/scripts/swarm/_spawn_gate.py +292 -0
  1476. package/.claude/scripts/swarm/_subagent_fabrication.py +444 -0
  1477. package/.claude/scripts/swarm/_worktree_pool.py +276 -0
  1478. package/.claude/scripts/swarm/coordinator.py +543 -0
  1479. package/.claude/scripts/swarm/file_assignment.py +111 -0
  1480. package/.claude/scripts/swarm/fixtures/mcp_corpus.json +111 -0
  1481. package/.claude/scripts/swarm/kill_switch.py +260 -0
  1482. package/.claude/scripts/swarm/loop_runner.py +486 -0
  1483. package/.claude/scripts/swarm/recovery.py +178 -0
  1484. package/.claude/scripts/swarm/test_mcp_injection_repro.py +518 -0
  1485. package/.claude/scripts/swarm/test_rail_anomaly_repro.py +586 -0
  1486. package/.claude/scripts/swarm/tests/__init__.py +1 -0
  1487. package/.claude/scripts/swarm/tests/test_benchmark_manifest_schema.py +227 -0
  1488. package/.claude/scripts/swarm/tests/test_benchmark_replay.py +248 -0
  1489. package/.claude/scripts/swarm/tests/test_child_isolation.py +138 -0
  1490. package/.claude/scripts/swarm/tests/test_coordinator.py +289 -0
  1491. package/.claude/scripts/swarm/tests/test_coordinator_production_integration.py +434 -0
  1492. package/.claude/scripts/swarm/tests/test_coordinator_sim.py +192 -0
  1493. package/.claude/scripts/swarm/tests/test_coordinator_tick.py +165 -0
  1494. package/.claude/scripts/swarm/tests/test_file_assignment.py +100 -0
  1495. package/.claude/scripts/swarm/tests/test_governors.py +269 -0
  1496. package/.claude/scripts/swarm/tests/test_integration.py +344 -0
  1497. package/.claude/scripts/swarm/tests/test_kill_switch.py +307 -0
  1498. package/.claude/scripts/swarm/tests/test_loop_runner.py +168 -0
  1499. package/.claude/scripts/swarm/tests/test_loop_runner_circuit_breaker.py +555 -0
  1500. package/.claude/scripts/swarm/tests/test_loop_runner_gate_enforcement.py +304 -0
  1501. package/.claude/scripts/swarm/tests/test_loop_runner_gate_kill_switch.py +147 -0
  1502. package/.claude/scripts/swarm/tests/test_loop_runner_sentinel_revocation_slo.py +112 -0
  1503. package/.claude/scripts/swarm/tests/test_optimizer_killswitch.py +205 -0
  1504. package/.claude/scripts/swarm/tests/test_parent_death.py +128 -0
  1505. package/.claude/scripts/swarm/tests/test_parent_death_integration.py +305 -0
  1506. package/.claude/scripts/swarm/tests/test_process_group.py +132 -0
  1507. package/.claude/scripts/swarm/tests/test_process_group_reap.py +212 -0
  1508. package/.claude/scripts/swarm/tests/test_rail_anomaly_repro.py +516 -0
  1509. package/.claude/scripts/swarm/tests/test_recovery.py +165 -0
  1510. package/.claude/scripts/swarm/tests/test_replay_tournament.py +284 -0
  1511. package/.claude/scripts/swarm/tests/test_spawn_gate.py +265 -0
  1512. package/.claude/scripts/swarm/tests/test_subagent_fabrication.py +824 -0
  1513. package/.claude/scripts/swarm/tests/test_swarm_activation_smoke.py +112 -0
  1514. package/.claude/scripts/swarm/tests/test_tournament.py +195 -0
  1515. package/.claude/scripts/swarm/tests/test_worktree_pool.py +252 -0
  1516. package/.claude/scripts/swarm/tournament.py +261 -0
  1517. package/.claude/scripts/task-route.py +807 -0
  1518. package/.claude/scripts/test-env-hygiene-allowlist.yaml +1093 -0
  1519. package/.claude/scripts/tests/DEFERRED.md +99 -0
  1520. package/.claude/scripts/tests/conftest.py +42 -0
  1521. package/.claude/scripts/tests/fixtures/aggregate-changesets/bad-type.md +4 -0
  1522. package/.claude/scripts/tests/fixtures/aggregate-changesets/missing-frontmatter.md +1 -0
  1523. package/.claude/scripts/tests/fixtures/aggregate-changesets/multidoc.md +6 -0
  1524. package/.claude/scripts/tests/fixtures/aggregate-changesets/sample-CHANGELOG.md +29 -0
  1525. package/.claude/scripts/tests/fixtures/aggregate-changesets/second-minor.md +4 -0
  1526. package/.claude/scripts/tests/fixtures/aggregate-changesets/single-patch.md +4 -0
  1527. package/.claude/scripts/tests/fixtures/aggregate-changesets/third-major.md +4 -0
  1528. package/.claude/scripts/tests/fixtures/aggregate-changesets/unknown-key.md +6 -0
  1529. package/.claude/scripts/tests/fixtures/bad_lessons/bidi_override.md +12 -0
  1530. package/.claude/scripts/tests/fixtures/bad_lessons/fenced_python.md +19 -0
  1531. package/.claude/scripts/tests/fixtures/bad_lessons/homoglyph.md +11 -0
  1532. package/.claude/scripts/tests/fixtures/bad_lessons/injection.md +11 -0
  1533. package/.claude/scripts/tests/fixtures/bad_lessons/long_line.md +9 -0
  1534. package/.claude/scripts/tests/fixtures/bad_lessons/oversized.md +261 -0
  1535. package/.claude/scripts/tests/fixtures/bad_lessons/zero_width.md +11 -0
  1536. package/.claude/scripts/tests/fixtures/budget_summary/generate_fixtures.py +368 -0
  1537. package/.claude/scripts/tests/fixtures/claims/README.md +21 -0
  1538. package/.claude/scripts/tests/fixtures/claims/function_exists/neg-missing.txt +1 -0
  1539. package/.claude/scripts/tests/fixtures/claims/function_exists/neg-no-file.txt +1 -0
  1540. package/.claude/scripts/tests/fixtures/claims/function_exists/pos-extract.txt +1 -0
  1541. package/.claude/scripts/tests/fixtures/claims/function_exists/pos-main.txt +1 -0
  1542. package/.claude/scripts/tests/fixtures/claims/function_exists/pos-verify.txt +1 -0
  1543. package/.claude/scripts/tests/fixtures/claims/function_exists/quoted-colon-path.txt +1 -0
  1544. package/.claude/scripts/tests/fixtures/claims/import_resolves/codeblock-skipped.txt +8 -0
  1545. package/.claude/scripts/tests/fixtures/claims/import_resolves/neg-blocked-os.txt +6 -0
  1546. package/.claude/scripts/tests/fixtures/claims/import_resolves/neg-relative.txt +5 -0
  1547. package/.claude/scripts/tests/fixtures/claims/import_resolves/pos-dotted.txt +6 -0
  1548. package/.claude/scripts/tests/fixtures/claims/import_resolves/pos-stdlib-like.txt +5 -0
  1549. package/.claude/scripts/tests/fixtures/claims/line_range/neg-missing-file.txt +1 -0
  1550. package/.claude/scripts/tests/fixtures/claims/line_range/neg-too-long.txt +1 -0
  1551. package/.claude/scripts/tests/fixtures/claims/line_range/pos-large.txt +1 -0
  1552. package/.claude/scripts/tests/fixtures/claims/line_range/pos-small.txt +1 -0
  1553. package/.claude/scripts/tests/fixtures/claims/line_range/quoted-path.txt +1 -0
  1554. package/.claude/scripts/tests/fixtures/claims/path_exists/codeblock-skipped.txt +7 -0
  1555. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-absolute-outside.txt +6 -0
  1556. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-dotdot-escape.txt +7 -0
  1557. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-imaginary.txt +1 -0
  1558. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-proc-self.txt +6 -0
  1559. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-symlink-escape.txt +8 -0
  1560. package/.claude/scripts/tests/fixtures/claims/path_exists/neg-typo.txt +1 -0
  1561. package/.claude/scripts/tests/fixtures/claims/path_exists/pos-claude.txt +1 -0
  1562. package/.claude/scripts/tests/fixtures/claims/path_exists/pos-readme.txt +1 -0
  1563. package/.claude/scripts/tests/fixtures/claims/path_exists/pos-self.txt +1 -0
  1564. package/.claude/scripts/tests/fixtures/claims/sha_exists/neg-fake.txt +1 -0
  1565. package/.claude/scripts/tests/fixtures/claims/sha_exists/neg-not-sha.txt +1 -0
  1566. package/.claude/scripts/tests/fixtures/claims/sha_exists/pos-head.txt +4 -0
  1567. package/.claude/scripts/tests/fixtures/claims/sha_exists/pos-root.txt +1 -0
  1568. package/.claude/scripts/tests/fixtures/claims/sha_exists/pos-short.txt +1 -0
  1569. package/.claude/scripts/tests/fixtures/claims/test_passes/neg-missing-file.txt +1 -0
  1570. package/.claude/scripts/tests/fixtures/claims/test_passes/neg-wrong-test.txt +1 -0
  1571. package/.claude/scripts/tests/fixtures/claims/test_passes/pos-audit-emit.txt +1 -0
  1572. package/.claude/scripts/tests/fixtures/claims/test_passes/pos-extra.txt +1 -0
  1573. package/.claude/scripts/tests/fixtures/claims/test_passes/pos-file.txt +1 -0
  1574. package/.claude/scripts/tests/fixtures/claims/test_passes/quoted-pytest-selector.txt +1 -0
  1575. package/.claude/scripts/tests/fixtures/debate_convergence/converged-pair-1/round-1/a.md +39 -0
  1576. package/.claude/scripts/tests/fixtures/debate_convergence/converged-pair-1/round-1/b.md +36 -0
  1577. package/.claude/scripts/tests/fixtures/debate_convergence/converged-pair-1/round-2/a.md +36 -0
  1578. package/.claude/scripts/tests/fixtures/debate_convergence/converged-pair-1/round-2/b.md +36 -0
  1579. package/.claude/scripts/tests/fixtures/debate_convergence/not-converged-pair-1/round-1/a.md +35 -0
  1580. package/.claude/scripts/tests/fixtures/debate_convergence/not-converged-pair-1/round-1/b.md +34 -0
  1581. package/.claude/scripts/tests/fixtures/debate_convergence/not-converged-pair-1/round-2/a.md +35 -0
  1582. package/.claude/scripts/tests/fixtures/debate_convergence/not-converged-pair-1/round-2/b.md +34 -0
  1583. package/.claude/scripts/tests/fixtures/debate_convergence/partial-overlap/round-1/a.md +35 -0
  1584. package/.claude/scripts/tests/fixtures/debate_convergence/partial-overlap/round-2/a.md +36 -0
  1585. package/.claude/scripts/tests/fixtures/debate_convergence/with-secret/round-1/a.md +36 -0
  1586. package/.claude/scripts/tests/fixtures/debate_convergence/with-secret/round-1/b.md +33 -0
  1587. package/.claude/scripts/tests/fixtures/debate_convergence/with-secret/round-2/a.md +34 -0
  1588. package/.claude/scripts/tests/fixtures/docs_freshness/link_anchor_only.md +10 -0
  1589. package/.claude/scripts/tests/fixtures/docs_freshness/link_broken.md +5 -0
  1590. package/.claude/scripts/tests/fixtures/docs_freshness/link_external_url.md +9 -0
  1591. package/.claude/scripts/tests/fixtures/docs_freshness/link_in_fenced_code.md +18 -0
  1592. package/.claude/scripts/tests/fixtures/docs_freshness/link_in_frontmatter.md +10 -0
  1593. package/.claude/scripts/tests/fixtures/docs_freshness/link_in_html_comment.md +10 -0
  1594. package/.claude/scripts/tests/fixtures/docs_freshness/link_in_inline_code.md +7 -0
  1595. package/.claude/scripts/tests/fixtures/docs_freshness/link_in_table.md +6 -0
  1596. package/.claude/scripts/tests/fixtures/docs_freshness/link_relative_parent.md +7 -0
  1597. package/.claude/scripts/tests/fixtures/docs_freshness/link_url_encoded.md +5 -0
  1598. package/.claude/scripts/tests/fixtures/docs_freshness/real_target.md +3 -0
  1599. package/.claude/scripts/tests/fixtures/docs_freshness/sub/dir.md +3 -0
  1600. package/.claude/scripts/tests/fixtures/docs_freshness/with%20space.md +3 -0
  1601. package/.claude/scripts/tests/fixtures/good_lessons/clean_auth.md +11 -0
  1602. package/.claude/scripts/tests/fixtures/good_lessons/clean_logging.md +11 -0
  1603. package/.claude/scripts/tests/fixtures/good_lessons/clean_retry.md +11 -0
  1604. package/.claude/scripts/tests/fixtures/gpg-keyring-fixture.py +209 -0
  1605. package/.claude/scripts/tests/fixtures/injection/benign-01.txt +8 -0
  1606. package/.claude/scripts/tests/fixtures/injection/benign-02.txt +5 -0
  1607. package/.claude/scripts/tests/fixtures/injection/benign-03.txt +7 -0
  1608. package/.claude/scripts/tests/fixtures/injection/benign-04.txt +9 -0
  1609. package/.claude/scripts/tests/fixtures/injection/benign-05.txt +7 -0
  1610. package/.claude/scripts/tests/fixtures/injection/benign-06.txt +7 -0
  1611. package/.claude/scripts/tests/fixtures/injection/benign-07.txt +11 -0
  1612. package/.claude/scripts/tests/fixtures/injection/benign-08.txt +4 -0
  1613. package/.claude/scripts/tests/fixtures/injection/malicious-01.txt +4 -0
  1614. package/.claude/scripts/tests/fixtures/injection/malicious-02.txt +2 -0
  1615. package/.claude/scripts/tests/fixtures/injection/malicious-03.txt +4 -0
  1616. package/.claude/scripts/tests/fixtures/injection/malicious-04.txt +2 -0
  1617. package/.claude/scripts/tests/fixtures/injection/malicious-05.txt +2 -0
  1618. package/.claude/scripts/tests/fixtures/injection/malicious-06.txt +5 -0
  1619. package/.claude/scripts/tests/fixtures/injection/malicious-07.txt +5 -0
  1620. package/.claude/scripts/tests/fixtures/injection/malicious-08.txt +2 -0
  1621. package/.claude/scripts/tests/fixtures/injection/malicious-09.txt +3 -0
  1622. package/.claude/scripts/tests/fixtures/injection/malicious-10.txt +2 -0
  1623. package/.claude/scripts/tests/fixtures/injection/malicious-11.txt +3 -0
  1624. package/.claude/scripts/tests/fixtures/injection/malicious-12.txt +5 -0
  1625. package/.claude/scripts/tests/fixtures/plan-tokens-calibration/manifest.json +49 -0
  1626. package/.claude/scripts/tests/fixtures/plan-tokens-calibration/plan-051.md +36 -0
  1627. package/.claude/scripts/tests/fixtures/plan-tokens-calibration/plan-052.md +32 -0
  1628. package/.claude/scripts/tests/fixtures/plan-tokens-calibration/plan-058.md +31 -0
  1629. package/.claude/scripts/tests/fixtures/reality-ledger/detector-1-boundary/docs/SAMPLE.md +8 -0
  1630. package/.claude/scripts/tests/fixtures/reality-ledger/detector-1-negative/.claude/scripts/sample.py +12 -0
  1631. package/.claude/scripts/tests/fixtures/reality-ledger/detector-1-negative/docs/SAMPLE.md +4 -0
  1632. package/.claude/scripts/tests/fixtures/reality-ledger/detector-1-positive/.claude/scripts/sample.py +12 -0
  1633. package/.claude/scripts/tests/fixtures/reality-ledger/detector-1-positive/docs/SAMPLE.md +9 -0
  1634. package/.claude/scripts/tests/fixtures/reality-ledger/detector-2-boundary/README.md +4 -0
  1635. package/.claude/scripts/tests/fixtures/reality-ledger/detector-2-negative/.claude/rag/requirements.lock +4 -0
  1636. package/.claude/scripts/tests/fixtures/reality-ledger/detector-2-positive/.claude/rag/requirements.lock +2 -0
  1637. package/.claude/scripts/tests/fixtures/reality-ledger/detector-3-boundary/.claude/agents/devops.md +8 -0
  1638. package/.claude/scripts/tests/fixtures/reality-ledger/detector-3-negative/.claude/agents/devops.md +5 -0
  1639. package/.claude/scripts/tests/fixtures/reality-ledger/detector-3-negative/audit-log.jsonl +2 -0
  1640. package/.claude/scripts/tests/fixtures/reality-ledger/detector-3-positive/.claude/agents/devops.md +7 -0
  1641. package/.claude/scripts/tests/fixtures/reality-ledger/detector-3-positive/audit-log.jsonl +4 -0
  1642. package/.claude/scripts/tests/fixtures/reality-ledger/detector-4-boundary/.claude/adr/ADR-997-fixture-superseded.md +8 -0
  1643. package/.claude/scripts/tests/fixtures/reality-ledger/detector-4-negative/.claude/adr/ADR-998-fixture-negative.md +16 -0
  1644. package/.claude/scripts/tests/fixtures/reality-ledger/detector-4-positive/.claude/adr/ADR-999-fixture-positive.md +15 -0
  1645. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-boundary/.claude/hooks/_lib/.do-not-import-from-here +15 -0
  1646. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-boundary/.claude/hooks/_lib/audit_emit.py +8 -0
  1647. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-boundary/.claude/scripts/dynamic_action.py +12 -0
  1648. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-negative/.claude/hooks/_lib/.do-not-import-from-here +15 -0
  1649. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-negative/.claude/hooks/_lib/audit_emit.py +11 -0
  1650. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-negative/.claude/scripts/registered_emitter.py +8 -0
  1651. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-positive/.claude/hooks/_lib/.do-not-import-from-here +15 -0
  1652. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-positive/.claude/hooks/_lib/audit_emit.py +12 -0
  1653. package/.claude/scripts/tests/fixtures/reality-ledger/detector-6-positive/.claude/scripts/phantom_emitter.py +13 -0
  1654. package/.claude/scripts/tests/fixtures/reality-ledger/issue-body-template.md +47 -0
  1655. package/.claude/scripts/tests/fixtures/reality-ledger/redaction/_test_corpus.py +7 -0
  1656. package/.claude/scripts/tests/fixtures/repo_profile/cloned-trading-repo/.env.example +5 -0
  1657. package/.claude/scripts/tests/fixtures/repo_profile/cloned-trading-repo/Cargo.toml +9 -0
  1658. package/.claude/scripts/tests/fixtures/repo_profile/cloned-trading-repo/README.md +6 -0
  1659. package/.claude/scripts/tests/fixtures/repo_profile/cloned-trading-repo/exchanges/binance.py +6 -0
  1660. package/.claude/scripts/tests/fixtures/repo_profile/cloned-trading-repo/strategies/triangular.py +4 -0
  1661. package/.claude/scripts/tests/fixtures/repo_profile/missing-package-manifest/README.md +7 -0
  1662. package/.claude/scripts/tests/fixtures/repo_profile/missing-package-manifest/notes.md +1 -0
  1663. package/.claude/scripts/tests/fixtures/repo_profile/mixed-frontend-backend/README.md +6 -0
  1664. package/.claude/scripts/tests/fixtures/repo_profile/mixed-frontend-backend/api/server.js +4 -0
  1665. package/.claude/scripts/tests/fixtures/repo_profile/mixed-frontend-backend/package.json +15 -0
  1666. package/.claude/scripts/tests/fixtures/repo_profile/mixed-frontend-backend/pages/index.tsx +3 -0
  1667. package/.claude/scripts/tests/fixtures/repo_profile/monorepo/README.md +6 -0
  1668. package/.claude/scripts/tests/fixtures/repo_profile/monorepo/apps/backend/.gitkeep +0 -0
  1669. package/.claude/scripts/tests/fixtures/repo_profile/monorepo/apps/frontend/.gitkeep +0 -0
  1670. package/.claude/scripts/tests/fixtures/repo_profile/monorepo/package.json +5 -0
  1671. package/.claude/scripts/tests/fixtures/repo_profile/monorepo/packages/shared/.gitkeep +0 -0
  1672. package/.claude/scripts/tests/fixtures/sample_audit_log.jsonl +50 -0
  1673. package/.claude/scripts/tests/fixtures/siem/.gitkeep +0 -0
  1674. package/.claude/scripts/tests/fixtures/smart_loading/profile-engine.yaml +8 -0
  1675. package/.claude/scripts/tests/fixtures/smart_loading/profile-fail-closed.yaml +7 -0
  1676. package/.claude/scripts/tests/fixtures/smart_loading/profile-fintech.yaml +9 -0
  1677. package/.claude/scripts/tests/fixtures/smart_loading/profile-frontend.yaml +9 -0
  1678. package/.claude/scripts/tests/fixtures/smart_loading/profile-generic.yaml +8 -0
  1679. package/.claude/scripts/tests/fixtures/smart_loading/profile-trading-readonly.yaml +9 -0
  1680. package/.claude/scripts/tests/fixtures/smart_loading/synthetic-skill-catalog.yaml +186 -0
  1681. package/.claude/scripts/tests/fixtures/squad_marketplace/.gitkeep +4 -0
  1682. package/.claude/scripts/tests/fixtures/task-route/calibration-holdout.json +49 -0
  1683. package/.claude/scripts/tests/fixtures/task-route/calibration-train.json +174 -0
  1684. package/.claude/scripts/tests/perf/__init__.py +3 -0
  1685. package/.claude/scripts/tests/perf/perf_utils.py +134 -0
  1686. package/.claude/scripts/tests/perf/test_kernel_hard_deny_microbench.py +149 -0
  1687. package/.claude/scripts/tests/perf/test_optimizer_complexity_gate_p99.py +145 -0
  1688. package/.claude/scripts/tests/perf/test_wave_c_canonical_json.py +132 -0
  1689. package/.claude/scripts/tests/perf/test_wave_c_filelock_mkdir.py +71 -0
  1690. package/.claude/scripts/tests/perf/test_wave_c_plan_glob_cache.py +84 -0
  1691. package/.claude/scripts/tests/perf/test_wave_c_preview_collapse.py +98 -0
  1692. package/.claude/scripts/tests/perf/test_wave_c_sys_modules.py +104 -0
  1693. package/.claude/scripts/tests/test_a4_pricing_doctrine.py +127 -0
  1694. package/.claude/scripts/tests/test_admin_invite.py +173 -0
  1695. package/.claude/scripts/tests/test_adopter_metrics.py +723 -0
  1696. package/.claude/scripts/tests/test_aek_calibration_c2.py +107 -0
  1697. package/.claude/scripts/tests/test_aek_calibration_c3.py +192 -0
  1698. package/.claude/scripts/tests/test_aek_state_machine.py +385 -0
  1699. package/.claude/scripts/tests/test_aggregate_changesets.py +646 -0
  1700. package/.claude/scripts/tests/test_architect_bundle_validate.py +159 -0
  1701. package/.claude/scripts/tests/test_audit_dashboard.py +822 -0
  1702. package/.claude/scripts/tests/test_audit_log_dispatch_hint.py +91 -0
  1703. package/.claude/scripts/tests/test_audit_log_retain.py +394 -0
  1704. package/.claude/scripts/tests/test_audit_query.py +1177 -0
  1705. package/.claude/scripts/tests/test_audit_query_by_domain.py +576 -0
  1706. package/.claude/scripts/tests/test_audit_query_claims.py +92 -0
  1707. package/.claude/scripts/tests/test_audit_query_critical.py +267 -0
  1708. package/.claude/scripts/tests/test_audit_query_tokens.py +106 -0
  1709. package/.claude/scripts/tests/test_audit_telemetry.py +214 -0
  1710. package/.claude/scripts/tests/test_audit_tokens.py +255 -0
  1711. package/.claude/scripts/tests/test_audit_verify_chain.py +189 -0
  1712. package/.claude/scripts/tests/test_backup_audit.py +295 -0
  1713. package/.claude/scripts/tests/test_benchmark_fallback_scorer.py +299 -0
  1714. package/.claude/scripts/tests/test_benchmark_judge.py +569 -0
  1715. package/.claude/scripts/tests/test_benchmarks_replay.py +313 -0
  1716. package/.claude/scripts/tests/test_budget_summary.py +628 -0
  1717. package/.claude/scripts/tests/test_build_canonical_models.py +349 -0
  1718. package/.claude/scripts/tests/test_calibration_kappa.py +234 -0
  1719. package/.claude/scripts/tests/test_cc_analytics_pull.py +296 -0
  1720. package/.claude/scripts/tests/test_ceo_backup.py +318 -0
  1721. package/.claude/scripts/tests/test_ceo_boot.py +643 -0
  1722. package/.claude/scripts/tests/test_ceo_boot_audit_emit.py +484 -0
  1723. package/.claude/scripts/tests/test_ceo_boot_enhanced.py +706 -0
  1724. package/.claude/scripts/tests/test_ceo_boot_persona_cadence.py +392 -0
  1725. package/.claude/scripts/tests/test_ceo_boot_plan_082.py +365 -0
  1726. package/.claude/scripts/tests/test_ceo_boot_tamper_tripwires.py +556 -0
  1727. package/.claude/scripts/tests/test_ceo_boot_task_candidate.py +868 -0
  1728. package/.claude/scripts/tests/test_ceo_cost.py +221 -0
  1729. package/.claude/scripts/tests/test_ceo_cost_stream.py +1076 -0
  1730. package/.claude/scripts/tests/test_ceo_diagnose.py +314 -0
  1731. package/.claude/scripts/tests/test_ceo_escalation_detector.py +591 -0
  1732. package/.claude/scripts/tests/test_ceo_health.py +202 -0
  1733. package/.claude/scripts/tests/test_ceo_info.py +542 -0
  1734. package/.claude/scripts/tests/test_chaos_inject_lockdown.py +384 -0
  1735. package/.claude/scripts/tests/test_check_action_sha_drift.py +174 -0
  1736. package/.claude/scripts/tests/test_check_active_hooks_executable.py +79 -0
  1737. package/.claude/scripts/tests/test_check_adr_chain.py +665 -0
  1738. package/.claude/scripts/tests/test_check_audit_hmac_null.py +178 -0
  1739. package/.claude/scripts/tests/test_check_audit_read_api_stable.py +176 -0
  1740. package/.claude/scripts/tests/test_check_audit_registry_coverage.py +744 -0
  1741. package/.claude/scripts/tests/test_check_auto_activation_flags.py +140 -0
  1742. package/.claude/scripts/tests/test_check_canonical_doc_freshness.py +149 -0
  1743. package/.claude/scripts/tests/test_check_claude_md_claims.py +223 -0
  1744. package/.claude/scripts/tests/test_check_conformance_harness_mapping.py +243 -0
  1745. package/.claude/scripts/tests/test_check_contamination.py +161 -0
  1746. package/.claude/scripts/tests/test_check_creative_rewrite.py +183 -0
  1747. package/.claude/scripts/tests/test_check_debate_round_lifecycle.py +162 -0
  1748. package/.claude/scripts/tests/test_check_debt_ledger.py +227 -0
  1749. package/.claude/scripts/tests/test_check_doc_skill_paths.py +99 -0
  1750. package/.claude/scripts/tests/test_check_docs_freshness.py +224 -0
  1751. package/.claude/scripts/tests/test_check_flip_criteria_drift.py +343 -0
  1752. package/.claude/scripts/tests/test_check_flip_release_gate_consistency.py +195 -0
  1753. package/.claude/scripts/tests/test_check_function_length.py +519 -0
  1754. package/.claude/scripts/tests/test_check_model_deprecations.py +368 -0
  1755. package/.claude/scripts/tests/test_check_originator_residue.py +165 -0
  1756. package/.claude/scripts/tests/test_check_rule_invariants.py +327 -0
  1757. package/.claude/scripts/tests/test_check_sdk_compat.py +88 -0
  1758. package/.claude/scripts/tests/test_check_sidecar_manifest_sbom_sync.py +177 -0
  1759. package/.claude/scripts/tests/test_check_spec_drift.py +358 -0
  1760. package/.claude/scripts/tests/test_check_staleness.py +128 -0
  1761. package/.claude/scripts/tests/test_check_stdlib_only_exceptions.py +91 -0
  1762. package/.claude/scripts/tests/test_check_substrate_watch.py +234 -0
  1763. package/.claude/scripts/tests/test_check_test_audit_isolation.py +322 -0
  1764. package/.claude/scripts/tests/test_check_test_env_hygiene.py +432 -0
  1765. package/.claude/scripts/tests/test_check_threat_model_coverage.py +251 -0
  1766. package/.claude/scripts/tests/test_check_threat_model_freshness.py +235 -0
  1767. package/.claude/scripts/tests/test_check_tier_boundaries.py +225 -0
  1768. package/.claude/scripts/tests/test_check_tla_schema_drift.py +246 -0
  1769. package/.claude/scripts/tests/test_check_translations_drift.py +262 -0
  1770. package/.claude/scripts/tests/test_code_nav_bridge.py +192 -0
  1771. package/.claude/scripts/tests/test_compaction_template.py +163 -0
  1772. package/.claude/scripts/tests/test_compare_adopters.py +646 -0
  1773. package/.claude/scripts/tests/test_confidence_gate.py +611 -0
  1774. package/.claude/scripts/tests/test_confidence_gate_backfill.py +212 -0
  1775. package/.claude/scripts/tests/test_context_budget.py +1400 -0
  1776. package/.claude/scripts/tests/test_contextual_recommender.py +723 -0
  1777. package/.claude/scripts/tests/test_coverage_audit_marker.py +109 -0
  1778. package/.claude/scripts/tests/test_debate_converge.py +399 -0
  1779. package/.claude/scripts/tests/test_debate_emit_cli.py +153 -0
  1780. package/.claude/scripts/tests/test_debate_orchestrate.py +575 -0
  1781. package/.claude/scripts/tests/test_detect_repo_profile.py +434 -0
  1782. package/.claude/scripts/tests/test_discover_foreign_context.py +208 -0
  1783. package/.claude/scripts/tests/test_dispatch_archetype_hint.py +429 -0
  1784. package/.claude/scripts/tests/test_dispatch_frontmatter_validation.py +274 -0
  1785. package/.claude/scripts/tests/test_drift_wire.py +259 -0
  1786. package/.claude/scripts/tests/test_embeddings.py +249 -0
  1787. package/.claude/scripts/tests/test_env_inventory_check.py +197 -0
  1788. package/.claude/scripts/tests/test_eval_c3.py +474 -0
  1789. package/.claude/scripts/tests/test_extract_skill.py +572 -0
  1790. package/.claude/scripts/tests/test_fan_plan_parser.py +213 -0
  1791. package/.claude/scripts/tests/test_find_orphan_sentinels.py +62 -0
  1792. package/.claude/scripts/tests/test_first_run_wizard.py +634 -0
  1793. package/.claude/scripts/tests/test_generate_adr_index.py +146 -0
  1794. package/.claude/scripts/tests/test_generate_available_models.py +209 -0
  1795. package/.claude/scripts/tests/test_generate_dispatch.py +90 -0
  1796. package/.claude/scripts/tests/test_generate_skill_inventory.py +76 -0
  1797. package/.claude/scripts/tests/test_github_api_client.py +146 -0
  1798. package/.claude/scripts/tests/test_governance_waivers_gate.py +176 -0
  1799. package/.claude/scripts/tests/test_hook_profiler.py +426 -0
  1800. package/.claude/scripts/tests/test_import_skill.py +927 -0
  1801. package/.claude/scripts/tests/test_import_skill_skip_rubric_auth.py +198 -0
  1802. package/.claude/scripts/tests/test_inject_agent_context_mitigated_dispatch.py +266 -0
  1803. package/.claude/scripts/tests/test_inject_agent_context_reference_mode.py +105 -0
  1804. package/.claude/scripts/tests/test_inspired_by_validator.py +307 -0
  1805. package/.claude/scripts/tests/test_install_dispatcher_present_maintainer.py +76 -0
  1806. package/.claude/scripts/tests/test_install_maintainer_unchanged.py +86 -0
  1807. package/.claude/scripts/tests/test_install_npm_sha256.py +113 -0
  1808. package/.claude/scripts/tests/test_install_sh_placeholders.py +268 -0
  1809. package/.claude/scripts/tests/test_install_sh_self_sha.py +244 -0
  1810. package/.claude/scripts/tests/test_install_sh_session_75_flags.py +147 -0
  1811. package/.claude/scripts/tests/test_install_user_dispatcher_present.py +75 -0
  1812. package/.claude/scripts/tests/test_install_user_no_writes_outside_claude.py +75 -0
  1813. package/.claude/scripts/tests/test_install_user_passes_validate_governance.py +73 -0
  1814. package/.claude/scripts/tests/test_install_user_preserves_existing_repo.py +135 -0
  1815. package/.claude/scripts/tests/test_install_user_skips_governance_hooks.py +102 -0
  1816. package/.claude/scripts/tests/test_k_calibration.py +415 -0
  1817. package/.claude/scripts/tests/test_key_hygiene.py +372 -0
  1818. package/.claude/scripts/tests/test_lesson_ranker.py +82 -0
  1819. package/.claude/scripts/tests/test_lesson_restore.py +91 -0
  1820. package/.claude/scripts/tests/test_lessons.py +278 -0
  1821. package/.claude/scripts/tests/test_lessons_concurrency.py +118 -0
  1822. package/.claude/scripts/tests/test_lessons_emit.py +114 -0
  1823. package/.claude/scripts/tests/test_lessons_inject.py +144 -0
  1824. package/.claude/scripts/tests/test_lessons_v2.py +264 -0
  1825. package/.claude/scripts/tests/test_lint_skills.py +525 -0
  1826. package/.claude/scripts/tests/test_log_friction.py +436 -0
  1827. package/.claude/scripts/tests/test_memory_prioritize.py +315 -0
  1828. package/.claude/scripts/tests/test_morning_ledger.py +415 -0
  1829. package/.claude/scripts/tests/test_mutation_test.py +144 -0
  1830. package/.claude/scripts/tests/test_npm_rebuild.py +154 -0
  1831. package/.claude/scripts/tests/test_osv_check.py +411 -0
  1832. package/.claude/scripts/tests/test_otel_export.py +613 -0
  1833. package/.claude/scripts/tests/test_otel_local_sink.py +262 -0
  1834. package/.claude/scripts/tests/test_owasp_llm_top_10_benchmark.py +235 -0
  1835. package/.claude/scripts/tests/test_parse_coverage_tier1.py +107 -0
  1836. package/.claude/scripts/tests/test_pitfall_query.py +148 -0
  1837. package/.claude/scripts/tests/test_plan_frontmatter_status.py +217 -0
  1838. package/.claude/scripts/tests/test_plan_id_uniqueness.py +133 -0
  1839. package/.claude/scripts/tests/test_plan_schema_enforcement.py +251 -0
  1840. package/.claude/scripts/tests/test_plan_tokens.py +513 -0
  1841. package/.claude/scripts/tests/test_plan_vcheck_gate.py +257 -0
  1842. package/.claude/scripts/tests/test_policy_shadow_runner.py +312 -0
  1843. package/.claude/scripts/tests/test_prune_lessons.py +341 -0
  1844. package/.claude/scripts/tests/test_quality_profile.py +392 -0
  1845. package/.claude/scripts/tests/test_rate_card_calibrate.py +185 -0
  1846. package/.claude/scripts/tests/test_reality_ledger.py +1723 -0
  1847. package/.claude/scripts/tests/test_red_team_eval.py +566 -0
  1848. package/.claude/scripts/tests/test_red_team_eval_sha.py +260 -0
  1849. package/.claude/scripts/tests/test_registry.py +290 -0
  1850. package/.claude/scripts/tests/test_run_benchmark.py +639 -0
  1851. package/.claude/scripts/tests/test_run_skill_benchmark_emit.py +195 -0
  1852. package/.claude/scripts/tests/test_run_skill_benchmark_judge_mode.py +306 -0
  1853. package/.claude/scripts/tests/test_scan_injection.py +191 -0
  1854. package/.claude/scripts/tests/test_scan_injection_strict.sh +201 -0
  1855. package/.claude/scripts/tests/test_scratchpad_cli.py +317 -0
  1856. package/.claude/scripts/tests/test_self_test.py +369 -0
  1857. package/.claude/scripts/tests/test_session_graph.py +511 -0
  1858. package/.claude/scripts/tests/test_session_resume.py +306 -0
  1859. package/.claude/scripts/tests/test_siem_rule_fixtures_have_paired_positive_negative.py +112 -0
  1860. package/.claude/scripts/tests/test_skill_budget_generator.py +329 -0
  1861. package/.claude/scripts/tests/test_skill_grandfather_parser.py +314 -0
  1862. package/.claude/scripts/tests/test_skill_import_rubric.py +497 -0
  1863. package/.claude/scripts/tests/test_skill_patch_apply_create_new_skill.py +459 -0
  1864. package/.claude/scripts/tests/test_skill_patch_propose.py +294 -0
  1865. package/.claude/scripts/tests/test_skill_patch_shadow_race.py +271 -0
  1866. package/.claude/scripts/tests/test_skill_retrieval.py +486 -0
  1867. package/.claude/scripts/tests/test_skill_retrieve_rag_wire.py +747 -0
  1868. package/.claude/scripts/tests/test_smart_loading_resolver.py +808 -0
  1869. package/.claude/scripts/tests/test_squad_export.py +265 -0
  1870. package/.claude/scripts/tests/test_squad_grandfather_cap.py +434 -0
  1871. package/.claude/scripts/tests/test_squad_import.py +905 -0
  1872. package/.claude/scripts/tests/test_statusline_ceo.py +543 -0
  1873. package/.claude/scripts/tests/test_success_receipt.py +448 -0
  1874. package/.claude/scripts/tests/test_task_route.py +456 -0
  1875. package/.claude/scripts/tests/test_token_budget_guard.py +418 -0
  1876. package/.claude/scripts/tests/test_token_estimator.py +395 -0
  1877. package/.claude/scripts/tests/test_trading_readonly.py +705 -0
  1878. package/.claude/scripts/tests/test_ui_ux_imports.py +223 -0
  1879. package/.claude/scripts/tests/test_validate_skill_frontmatter_pii_core.py +630 -0
  1880. package/.claude/scripts/tests/test_validate_spec_context.py +128 -0
  1881. package/.claude/scripts/tests/test_validate_squad_contract.py +221 -0
  1882. package/.claude/scripts/tests/test_value_dashboard.py +593 -0
  1883. package/.claude/scripts/tests/test_verify_adr_118_rationale.py +183 -0
  1884. package/.claude/scripts/tests/test_verify_atlas_binding.py +159 -0
  1885. package/.claude/scripts/tests/test_verify_counts.py +138 -0
  1886. package/.claude/scripts/tests/test_verify_counts_remediation.py +258 -0
  1887. package/.claude/scripts/tests/test_verify_persona_coverage.py +576 -0
  1888. package/.claude/scripts/tests/test_veto_check.py +171 -0
  1889. package/.claude/scripts/tests/test_workflow_devops_p2.py +229 -0
  1890. package/.claude/scripts/tier_policy_cli/__init__.py +43 -0
  1891. package/.claude/scripts/tier_policy_cli/_agent_frontmatter.py +196 -0
  1892. package/.claude/scripts/tier_policy_cli/_constants.py +92 -0
  1893. package/.claude/scripts/tier_policy_cli/_types.py +228 -0
  1894. package/.claude/scripts/tier_policy_cli/apply.py +1139 -0
  1895. package/.claude/scripts/tier_policy_cli/cli.py +795 -0
  1896. package/.claude/scripts/tier_policy_cli/learn.py +846 -0
  1897. package/.claude/scripts/tier_policy_cli/loader.py +535 -0
  1898. package/.claude/scripts/tier_policy_cli/setup.py +33 -0
  1899. package/.claude/scripts/tier_policy_cli/tests/__init__.py +0 -0
  1900. package/.claude/scripts/tier_policy_cli/tests/test_adversarial.py +605 -0
  1901. package/.claude/scripts/tier_policy_cli/tests/test_agent_frontmatter.py +231 -0
  1902. package/.claude/scripts/tier_policy_cli/tests/test_apply.py +698 -0
  1903. package/.claude/scripts/tier_policy_cli/tests/test_check_tier_policy_hook.py +187 -0
  1904. package/.claude/scripts/tier_policy_cli/tests/test_cli.py +434 -0
  1905. package/.claude/scripts/tier_policy_cli/tests/test_constants.py +113 -0
  1906. package/.claude/scripts/tier_policy_cli/tests/test_learn.py +1380 -0
  1907. package/.claude/scripts/tier_policy_cli/tests/test_learn_mutation.py +549 -0
  1908. package/.claude/scripts/tier_policy_cli/tests/test_loader.py +368 -0
  1909. package/.claude/scripts/tier_policy_cli/tests/test_types.py +152 -0
  1910. package/.claude/scripts/token-budget-guard.py +657 -0
  1911. package/.claude/scripts/token-estimator.py +957 -0
  1912. package/.claude/scripts/tournament/__init__.py +22 -0
  1913. package/.claude/scripts/tournament/check_fixture.py +271 -0
  1914. package/.claude/scripts/tournament/fixtures/CORPUS_SHA256.txt +10 -0
  1915. package/.claude/scripts/tournament/fixtures/code-review.jsonl +10 -0
  1916. package/.claude/scripts/tournament/fixtures/docs-writing.jsonl +10 -0
  1917. package/.claude/scripts/tournament/fixtures/performance-triage.jsonl +10 -0
  1918. package/.claude/scripts/tournament/fixtures/security-review.jsonl +10 -0
  1919. package/.claude/scripts/tournament/fixtures/test-design.jsonl +10 -0
  1920. package/.claude/scripts/tournament/judge.py +269 -0
  1921. package/.claude/scripts/tournament/loader.py +262 -0
  1922. package/.claude/scripts/tournament/regen_corpus_sha.py +93 -0
  1923. package/.claude/scripts/tournament/reporter.py +328 -0
  1924. package/.claude/scripts/tournament/runner.py +707 -0
  1925. package/.claude/scripts/tournament/scorer.py +118 -0
  1926. package/.claude/scripts/tournament/tests/__init__.py +0 -0
  1927. package/.claude/scripts/tournament/tests/_fake_dispatcher.py +233 -0
  1928. package/.claude/scripts/tournament/tests/golden/strict_report_seed42.jsonl +6 -0
  1929. package/.claude/scripts/tournament/tests/test_fixture_envelope.py +106 -0
  1930. package/.claude/scripts/tournament/tests/test_fixture_security.py +227 -0
  1931. package/.claude/scripts/tournament/tests/test_judge.py +299 -0
  1932. package/.claude/scripts/tournament/tests/test_loader.py +223 -0
  1933. package/.claude/scripts/tournament/tests/test_model_id_parity.py +136 -0
  1934. package/.claude/scripts/tournament/tests/test_reporter.py +450 -0
  1935. package/.claude/scripts/tournament/tests/test_reporter_golden.py +182 -0
  1936. package/.claude/scripts/tournament/tests/test_runner.py +313 -0
  1937. package/.claude/scripts/tournament/tests/test_runner_fail_open.py +204 -0
  1938. package/.claude/scripts/tournament/tests/test_scorer.py +138 -0
  1939. package/.claude/scripts/tournament/tests/test_tournament_e2e_smoke.py +147 -0
  1940. package/.claude/scripts/tournament/tests/test_tournament_properties.py +181 -0
  1941. package/.claude/scripts/trading-readonly-escape-hatch.sh +244 -0
  1942. package/.claude/scripts/trading-readonly-guardrails.py +1136 -0
  1943. package/.claude/scripts/translations-pairs.yaml +60 -0
  1944. package/.claude/scripts/validate-findings.py +243 -0
  1945. package/.claude/scripts/validate-governance.sh +1238 -0
  1946. package/.claude/scripts/validate-skill-frontmatter.py +679 -0
  1947. package/.claude/scripts/validate-spec-context.py +146 -0
  1948. package/.claude/scripts/validate-squad-contract.py +318 -0
  1949. package/.claude/scripts/validate_governance_fast.py +555 -0
  1950. package/.claude/scripts/value-dashboard.py +851 -0
  1951. package/.claude/scripts/verify-adr-118-rationale.py +285 -0
  1952. package/.claude/scripts/verify-atlas-binding.py +331 -0
  1953. package/.claude/scripts/verify-persona-coverage.py +531 -0
  1954. package/.claude/scripts/verify-sprint3-invariants.sh +133 -0
  1955. package/.claude/scripts/veto-check.py +218 -0
  1956. package/.claude/security/README.md +200 -0
  1957. package/.claude/security/sentinel-signers-registry.yaml +60 -0
  1958. package/.claude/sentinel-signers.txt +24 -0
  1959. package/.claude/settings.json +786 -0
  1960. package/.claude/sidecars/c1-crypto/cryptography-mvp/README.md +89 -0
  1961. package/.claude/sidecars/c1-crypto/cryptography-mvp/boundary_test.py +114 -0
  1962. package/.claude/sidecars/c1-crypto/cryptography-mvp/install.sh +45 -0
  1963. package/.claude/sidecars/c1-crypto/cryptography-mvp/manifest.json +52 -0
  1964. package/.claude/sidecars/c1-crypto/cryptography-mvp/sidecar_code/cert_inspector.py +775 -0
  1965. package/.claude/sidecars/c1-crypto/stdlib-ssl-mvp/boundary_test.py +318 -0
  1966. package/.claude/sidecars/c1-crypto/stdlib-ssl-mvp/install.sh +57 -0
  1967. package/.claude/sidecars/c1-crypto/stdlib-ssl-mvp/manifest.json +48 -0
  1968. package/.claude/sidecars/c2-vector-memory/lightrag-mvp/README.md +88 -0
  1969. package/.claude/sidecars/c2-vector-memory/lightrag-mvp/boundary_test.py +221 -0
  1970. package/.claude/sidecars/c2-vector-memory/lightrag-mvp/install.sh +33 -0
  1971. package/.claude/sidecars/c2-vector-memory/lightrag-mvp/manifest.json +59 -0
  1972. package/.claude/sidecars/c5-dev-tools/hypothesis/boundary_test.py +142 -0
  1973. package/.claude/sidecars/c5-dev-tools/hypothesis/install.sh +46 -0
  1974. package/.claude/sidecars/c5-dev-tools/hypothesis/manifest.json +52 -0
  1975. package/.claude/sidecars/c5-dev-tools/hypothesis/tests/__init__.py +0 -0
  1976. package/.claude/sidecars/c5-dev-tools/hypothesis/tests/test_audit_emit_known_actions_property.py +123 -0
  1977. package/.claude/sidecars/c5-dev-tools/hypothesis/tests/test_canonical_guard_symmetry_property.py +67 -0
  1978. package/.claude/sidecars/c5-dev-tools/hypothesis/tests/test_payload_roundtrip_property.py +73 -0
  1979. package/.claude/sidecars/c5-dev-tools/hypothesis/tests/test_redact_idempotence_property.py +68 -0
  1980. package/.claude/skill-governance-grandfather.yaml +39 -0
  1981. package/.claude/skill-patch-signers.txt +19 -0
  1982. package/.claude/skills/core/agent-architect/SKILL.md +126 -0
  1983. package/.claude/skills/core/ai-llm-orchestration/SKILL.md +620 -0
  1984. package/.claude/skills/core/ai-llm-orchestration/SKILL.md.shadow.md +121 -0
  1985. package/.claude/skills/core/architecture-decisions/SKILL.md +364 -0
  1986. package/.claude/skills/core/architecture-decisions/benchmarks/architecture-decisions.yaml +257 -0
  1987. package/.claude/skills/core/ceo-orchestration/SKILL-frontend.md +117 -0
  1988. package/.claude/skills/core/ceo-orchestration/SKILL.md +700 -0
  1989. package/.claude/skills/core/chaos-and-resilience/SKILL.md +568 -0
  1990. package/.claude/skills/core/chaos-and-resilience/SKILL.md.shadow.md +553 -0
  1991. package/.claude/skills/core/code-intelligence-lsp/SKILL.md +375 -0
  1992. package/.claude/skills/core/code-review-checklist/SKILL.md +675 -0
  1993. package/.claude/skills/core/code-review-checklist/SKILL.md.shadow.md +337 -0
  1994. package/.claude/skills/core/code-review-checklist/benchmarks/code-review-checklist.yaml +444 -0
  1995. package/.claude/skills/core/codebase-onboarding/SKILL.md +515 -0
  1996. package/.claude/skills/core/compliance-lgpd/SKILL-frontend.md +513 -0
  1997. package/.claude/skills/core/compliance-lgpd/SKILL.md +817 -0
  1998. package/.claude/skills/core/consent-lifecycle/SKILL.md +149 -0
  1999. package/.claude/skills/core/cookbook-advisor/SKILL.md +191 -0
  2000. package/.claude/skills/core/coverage-audit/SKILL.md +116 -0
  2001. package/.claude/skills/core/cross-llm-pair-review/SKILL.md +212 -0
  2002. package/.claude/skills/core/data-schema-design/SKILL.md +933 -0
  2003. package/.claude/skills/core/devops-ci-cd/SKILL.md +659 -0
  2004. package/.claude/skills/core/dpo-reporting/SKILL.md +187 -0
  2005. package/.claude/skills/core/evidence-based-qa/SKILL.md +565 -0
  2006. package/.claude/skills/core/git-workflow-discipline/SKILL.md +600 -0
  2007. package/.claude/skills/core/growth-and-launch/SKILL-frontend.md +800 -0
  2008. package/.claude/skills/core/growth-and-launch/SKILL.md +903 -0
  2009. package/.claude/skills/core/help-me/SKILL.md +177 -0
  2010. package/.claude/skills/core/help-me/tests/test_help_me_skill.py +490 -0
  2011. package/.claude/skills/core/identity-and-trust-architecture/SKILL.md +1062 -0
  2012. package/.claude/skills/core/incident-management/SKILL.md +421 -0
  2013. package/.claude/skills/core/incremental-refactoring/SKILL-frontend.md +210 -0
  2014. package/.claude/skills/core/incremental-refactoring/SKILL.md +226 -0
  2015. package/.claude/skills/core/llm-routing-and-finops/SKILL.md +828 -0
  2016. package/.claude/skills/core/mcp-server-authoring/SKILL.md +685 -0
  2017. package/.claude/skills/core/minimal-change-discipline/SKILL.md +545 -0
  2018. package/.claude/skills/core/monetization-and-billing/SKILL-frontend.md +562 -0
  2019. package/.claude/skills/core/monetization-and-billing/SKILL.md +585 -0
  2020. package/.claude/skills/core/observability-and-ops/SKILL-frontend.md +290 -0
  2021. package/.claude/skills/core/observability-and-ops/SKILL.md +612 -0
  2022. package/.claude/skills/core/observability-and-ops/SKILL.md.shadow.md +324 -0
  2023. package/.claude/skills/core/parallelization-by-default/SKILL.md +176 -0
  2024. package/.claude/skills/core/parallelization-by-default/tests/test_parallelization_skill.py +490 -0
  2025. package/.claude/skills/core/performance-engineering/SKILL.md +219 -0
  2026. package/.claude/skills/core/performance-engineering/SKILL.md.shadow.md +204 -0
  2027. package/.claude/skills/core/pii-data-flow/SKILL.md +166 -0
  2028. package/.claude/skills/core/pre-plan-brainstorm/CHECKLIST.md +87 -0
  2029. package/.claude/skills/core/pre-plan-brainstorm/SKILL.md +186 -0
  2030. package/.claude/skills/core/product-conversion-readiness/SKILL-frontend.md +668 -0
  2031. package/.claude/skills/core/product-conversion-readiness/SKILL.md +941 -0
  2032. package/.claude/skills/core/public-api-design/SKILL.md +603 -0
  2033. package/.claude/skills/core/public-api-design/benchmarks/public-api-design.yaml +261 -0
  2034. package/.claude/skills/core/receiving-review/SKILL.md +131 -0
  2035. package/.claude/skills/core/receiving-review/benchmarks/receiving-review.yaml +254 -0
  2036. package/.claude/skills/core/requirement-quality-checklist/SKILL.md +97 -0
  2037. package/.claude/skills/core/security-and-auth/SKILL.md +868 -0
  2038. package/.claude/skills/core/security-and-auth/SKILL.md.shadow.md +500 -0
  2039. package/.claude/skills/core/security-and-auth/benchmarks/owasp-basics.yaml +491 -0
  2040. package/.claude/skills/core/security-and-auth/benchmarks/owasp-llm-top-10.yaml +769 -0
  2041. package/.claude/skills/core/spec-clarify/SKILL.md +120 -0
  2042. package/.claude/skills/core/state-machines-and-invariants/SKILL.md +288 -0
  2043. package/.claude/skills/core/technical-writing/SKILL.md +432 -0
  2044. package/.claude/skills/core/terse-mode/SKILL.md +80 -0
  2045. package/.claude/skills/core/terse-mode/SKILL.md.shadow.md +65 -0
  2046. package/.claude/skills/core/testing-strategy/SKILL.md +1026 -0
  2047. package/.claude/skills/core/testing-strategy/SKILL.md.shadow.md +983 -0
  2048. package/.claude/skills/domains/academic-humanities/examples/PLAN-EXAMPLE-ACH.md +126 -0
  2049. package/.claude/skills/domains/academic-humanities/pitfalls.yaml +68 -0
  2050. package/.claude/skills/domains/academic-humanities/skills/anthropologist/SKILL.md +394 -0
  2051. package/.claude/skills/domains/academic-humanities/skills/geographer/SKILL.md +453 -0
  2052. package/.claude/skills/domains/academic-humanities/skills/historian/SKILL.md +255 -0
  2053. package/.claude/skills/domains/academic-humanities/skills/narratologist/SKILL.md +398 -0
  2054. package/.claude/skills/domains/academic-humanities/skills/psychologist/SKILL.md +271 -0
  2055. package/.claude/skills/domains/academic-humanities/task-chains.yaml +125 -0
  2056. package/.claude/skills/domains/academic-humanities/team-personas.md +278 -0
  2057. package/.claude/skills/domains/business-support/examples/PLAN-EXAMPLE-BSP.md +115 -0
  2058. package/.claude/skills/domains/business-support/pitfalls.yaml +69 -0
  2059. package/.claude/skills/domains/business-support/skills/analytics-reporter/SKILL.md +339 -0
  2060. package/.claude/skills/domains/business-support/skills/executive-summary/SKILL.md +268 -0
  2061. package/.claude/skills/domains/business-support/skills/finance-tracker/SKILL.md +321 -0
  2062. package/.claude/skills/domains/business-support/skills/support-responder/SKILL.md +341 -0
  2063. package/.claude/skills/domains/business-support/task-chains.yaml +118 -0
  2064. package/.claude/skills/domains/business-support/team-personas.md +259 -0
  2065. package/.claude/skills/domains/civil-engineering/skills/civil-engineer/SKILL.md +275 -0
  2066. package/.claude/skills/domains/community/NOTICE.md +83 -0
  2067. package/.claude/skills/domains/community/skills/advanced-evaluation/SKILL.md +463 -0
  2068. package/.claude/skills/domains/community/skills/agent-evaluation/SKILL.md +400 -0
  2069. package/.claude/skills/domains/community/skills/agentic-actions-auditor/SKILL.md +410 -0
  2070. package/.claude/skills/domains/community/team-personas.md +41 -0
  2071. package/.claude/skills/domains/devrel/examples/api-deprecation-comms.md +180 -0
  2072. package/.claude/skills/domains/devrel/pitfalls.yaml +74 -0
  2073. package/.claude/skills/domains/devrel/skills/developer-advocate/SKILL.md +382 -0
  2074. package/.claude/skills/domains/devrel/task-chains.yaml +129 -0
  2075. package/.claude/skills/domains/devrel/team-personas.md +260 -0
  2076. package/.claude/skills/domains/edtech/examples/PLAN-EXAMPLE.md +89 -0
  2077. package/.claude/skills/domains/edtech/pitfalls.yaml +98 -0
  2078. package/.claude/skills/domains/edtech/skills/assessment-integrity/SKILL.md +208 -0
  2079. package/.claude/skills/domains/edtech/skills/learning-analytics/SKILL.md +212 -0
  2080. package/.claude/skills/domains/edtech/skills/student-data-privacy/SKILL.md +197 -0
  2081. package/.claude/skills/domains/edtech/skills/study-abroad-advisory/SKILL.md +582 -0
  2082. package/.claude/skills/domains/edtech/task-chains.yaml +122 -0
  2083. package/.claude/skills/domains/edtech/team-personas.md +252 -0
  2084. package/.claude/skills/domains/embedded/skills/embedded-firmware/SKILL.md +471 -0
  2085. package/.claude/skills/domains/finance-accounting/examples/new-subscription-revenue.md +135 -0
  2086. package/.claude/skills/domains/finance-accounting/pitfalls.yaml +74 -0
  2087. package/.claude/skills/domains/finance-accounting/skills/bookkeeper-controller/SKILL.md +427 -0
  2088. package/.claude/skills/domains/finance-accounting/skills/financial-analyst/SKILL.md +348 -0
  2089. package/.claude/skills/domains/finance-accounting/skills/fpa-analyst/SKILL.md +366 -0
  2090. package/.claude/skills/domains/finance-accounting/skills/tax-strategist/SKILL.md +358 -0
  2091. package/.claude/skills/domains/finance-accounting/task-chains.yaml +90 -0
  2092. package/.claude/skills/domains/finance-accounting/team-personas.md +281 -0
  2093. package/.claude/skills/domains/fintech/ORG_CHART.md +167 -0
  2094. package/.claude/skills/domains/fintech/commands/audit-ai.md +124 -0
  2095. package/.claude/skills/domains/fintech/commands/deploy.md +15 -0
  2096. package/.claude/skills/domains/fintech/commands/status.md +13 -0
  2097. package/.claude/skills/domains/fintech/frontend-team-personas.md +503 -0
  2098. package/.claude/skills/domains/fintech/pitfalls.yaml +58 -0
  2099. package/.claude/skills/domains/fintech/scripts/check-pitfall-regression.sh +80 -0
  2100. package/.claude/skills/domains/fintech/scripts/check-type-sync.sh +110 -0
  2101. package/.claude/skills/domains/fintech/skills/blockchain-security-audit/SKILL.md +492 -0
  2102. package/.claude/skills/domains/fintech/skills/equity-research/SKILL.md +459 -0
  2103. package/.claude/skills/domains/fintech/skills/exchange-api-integration/SKILL.md +315 -0
  2104. package/.claude/skills/domains/fintech/skills/exchange-onboarding-playbook/SKILL.md +527 -0
  2105. package/.claude/skills/domains/fintech/skills/financial-correctness-and-math/SKILL-frontend.md +308 -0
  2106. package/.claude/skills/domains/fintech/skills/financial-correctness-and-math/SKILL.md +340 -0
  2107. package/.claude/skills/domains/fintech/skills/financial-display/SKILL.md +193 -0
  2108. package/.claude/skills/domains/fintech/skills/frontend-data-layer/SKILL.md +206 -0
  2109. package/.claude/skills/domains/fintech/skills/frontend-patterns/SKILL.md +387 -0
  2110. package/.claude/skills/domains/fintech/skills/prediction-markets/SKILL.md +139 -0
  2111. package/.claude/skills/domains/fintech/skills/real-time-market-systems/SKILL.md +315 -0
  2112. package/.claude/skills/domains/fintech/skills/solidity-smart-contracts/SKILL.md +356 -0
  2113. package/.claude/skills/domains/fintech/skills/trading-execution/SKILL.md +126 -0
  2114. package/.claude/skills/domains/fintech/task-chains.yaml +46 -0
  2115. package/.claude/skills/domains/fintech/team-personas.md +773 -0
  2116. package/.claude/skills/domains/government/examples/PLAN-EXAMPLE.md +158 -0
  2117. package/.claude/skills/domains/government/pitfalls.yaml +114 -0
  2118. package/.claude/skills/domains/government/skills/accessibility-section-508/SKILL.md +183 -0
  2119. package/.claude/skills/domains/government/skills/digital-presales/SKILL.md +359 -0
  2120. package/.claude/skills/domains/government/skills/foia-and-records/SKILL.md +211 -0
  2121. package/.claude/skills/domains/government/skills/public-procurement/SKILL.md +264 -0
  2122. package/.claude/skills/domains/government/task-chains.yaml +88 -0
  2123. package/.claude/skills/domains/government/team-personas.md +296 -0
  2124. package/.claude/skills/domains/healthcare/examples/patient-portal-symptom-checker.md +130 -0
  2125. package/.claude/skills/domains/healthcare/pitfalls.yaml +74 -0
  2126. package/.claude/skills/domains/healthcare/skills/healthcare-customer-service/SKILL.md +369 -0
  2127. package/.claude/skills/domains/healthcare/skills/marketing-compliance/SKILL.md +367 -0
  2128. package/.claude/skills/domains/healthcare/task-chains.yaml +87 -0
  2129. package/.claude/skills/domains/healthcare/team-personas.md +273 -0
  2130. package/.claude/skills/domains/hospitality/skills/guest-services/SKILL.md +417 -0
  2131. package/.claude/skills/domains/hr/examples/attrition-model-launch.md +128 -0
  2132. package/.claude/skills/domains/hr/pitfalls.yaml +74 -0
  2133. package/.claude/skills/domains/hr/skills/hr-onboarding/SKILL.md +435 -0
  2134. package/.claude/skills/domains/hr/skills/recruitment-specialist/SKILL.md +400 -0
  2135. package/.claude/skills/domains/hr/task-chains.yaml +91 -0
  2136. package/.claude/skills/domains/hr/team-personas.md +251 -0
  2137. package/.claude/skills/domains/i18n-business/examples/PLAN-EXAMPLE-I18N.md +115 -0
  2138. package/.claude/skills/domains/i18n-business/pitfalls.yaml +68 -0
  2139. package/.claude/skills/domains/i18n-business/skills/cultural-intelligence/SKILL.md +448 -0
  2140. package/.claude/skills/domains/i18n-business/skills/french-consulting/SKILL.md +347 -0
  2141. package/.claude/skills/domains/i18n-business/skills/korean-business/SKILL.md +360 -0
  2142. package/.claude/skills/domains/i18n-business/skills/language-translator/SKILL.md +389 -0
  2143. package/.claude/skills/domains/i18n-business/task-chains.yaml +117 -0
  2144. package/.claude/skills/domains/i18n-business/team-personas.md +258 -0
  2145. package/.claude/skills/domains/identity-systems/examples/passkey-rollout.md +137 -0
  2146. package/.claude/skills/domains/identity-systems/pitfalls.yaml +74 -0
  2147. package/.claude/skills/domains/identity-systems/skills/identity-graph-operator/SKILL.md +353 -0
  2148. package/.claude/skills/domains/identity-systems/task-chains.yaml +90 -0
  2149. package/.claude/skills/domains/identity-systems/team-personas.md +233 -0
  2150. package/.claude/skills/domains/legal/examples/client-intake-pii-flow.md +177 -0
  2151. package/.claude/skills/domains/legal/pitfalls.yaml +77 -0
  2152. package/.claude/skills/domains/legal/skills/client-intake/SKILL.md +407 -0
  2153. package/.claude/skills/domains/legal/skills/document-review/SKILL.md +373 -0
  2154. package/.claude/skills/domains/legal/skills/legal-billing/SKILL.md +331 -0
  2155. package/.claude/skills/domains/legal/task-chains.yaml +131 -0
  2156. package/.claude/skills/domains/legal/team-personas.md +260 -0
  2157. package/.claude/skills/domains/lgpd-heavy-saas/examples/PLAN-EXAMPLE.md +120 -0
  2158. package/.claude/skills/domains/lgpd-heavy-saas/pitfalls.yaml +90 -0
  2159. package/.claude/skills/domains/lgpd-heavy-saas/task-chains.yaml +83 -0
  2160. package/.claude/skills/domains/lgpd-heavy-saas/team-personas.md +159 -0
  2161. package/.claude/skills/domains/marketing-global/skills/agentic-search-optimizer/SKILL.md +391 -0
  2162. package/.claude/skills/domains/marketing-global/skills/ai-citation-strategist/SKILL.md +343 -0
  2163. package/.claude/skills/domains/marketing-global/skills/app-store-optimizer/SKILL.md +495 -0
  2164. package/.claude/skills/domains/marketing-global/skills/book-co-author/SKILL.md +220 -0
  2165. package/.claude/skills/domains/marketing-global/skills/carousel-growth-engine/SKILL.md +393 -0
  2166. package/.claude/skills/domains/marketing-global/skills/content-creator/SKILL.md +416 -0
  2167. package/.claude/skills/domains/marketing-global/skills/growth-hacker/SKILL.md +495 -0
  2168. package/.claude/skills/domains/marketing-global/skills/instagram-curator/SKILL.md +419 -0
  2169. package/.claude/skills/domains/marketing-global/skills/linkedin-content-creator/SKILL.md +291 -0
  2170. package/.claude/skills/domains/marketing-global/skills/podcast-strategist/SKILL.md +408 -0
  2171. package/.claude/skills/domains/marketing-global/skills/reddit-community-builder/SKILL.md +295 -0
  2172. package/.claude/skills/domains/marketing-global/skills/seo-specialist/SKILL.md +352 -0
  2173. package/.claude/skills/domains/marketing-global/skills/social-media-strategist/SKILL.md +349 -0
  2174. package/.claude/skills/domains/marketing-global/skills/tiktok-strategist/SKILL.md +329 -0
  2175. package/.claude/skills/domains/marketing-global/skills/twitter-engager/SKILL.md +382 -0
  2176. package/.claude/skills/domains/marketing-global/skills/video-optimization-specialist/SKILL.md +386 -0
  2177. package/.claude/skills/domains/mobile/examples/PLAN-EXAMPLE-MOB.md +129 -0
  2178. package/.claude/skills/domains/mobile/pitfalls.yaml +69 -0
  2179. package/.claude/skills/domains/mobile/skills/mobile-app-builder/SKILL.md +446 -0
  2180. package/.claude/skills/domains/mobile/task-chains.yaml +126 -0
  2181. package/.claude/skills/domains/mobile/team-personas.md +292 -0
  2182. package/.claude/skills/domains/paid-media/examples/new-channel-launch.md +122 -0
  2183. package/.claude/skills/domains/paid-media/pitfalls.yaml +79 -0
  2184. package/.claude/skills/domains/paid-media/skills/auditor/SKILL.md +362 -0
  2185. package/.claude/skills/domains/paid-media/skills/creative-strategist/SKILL.md +457 -0
  2186. package/.claude/skills/domains/paid-media/skills/paid-social-strategist/SKILL.md +493 -0
  2187. package/.claude/skills/domains/paid-media/skills/ppc-strategist/SKILL.md +450 -0
  2188. package/.claude/skills/domains/paid-media/skills/programmatic-buyer/SKILL.md +396 -0
  2189. package/.claude/skills/domains/paid-media/skills/search-query-analyst/SKILL.md +336 -0
  2190. package/.claude/skills/domains/paid-media/skills/tracking-specialist/SKILL.md +457 -0
  2191. package/.claude/skills/domains/paid-media/task-chains.yaml +121 -0
  2192. package/.claude/skills/domains/paid-media/team-personas.md +251 -0
  2193. package/.claude/skills/domains/project-management/examples/PLAN-EXAMPLE-PMG.md +117 -0
  2194. package/.claude/skills/domains/project-management/pitfalls.yaml +68 -0
  2195. package/.claude/skills/domains/project-management/skills/experiment-tracker/SKILL.md +293 -0
  2196. package/.claude/skills/domains/project-management/skills/project-shepherd/SKILL.md +312 -0
  2197. package/.claude/skills/domains/project-management/skills/studio-operations/SKILL.md +333 -0
  2198. package/.claude/skills/domains/project-management/skills/studio-producer/SKILL.md +329 -0
  2199. package/.claude/skills/domains/project-management/task-chains.yaml +118 -0
  2200. package/.claude/skills/domains/project-management/team-personas.md +264 -0
  2201. package/.claude/skills/domains/real-estate-finance/examples/PLAN-EXAMPLE-REF.md +129 -0
  2202. package/.claude/skills/domains/real-estate-finance/pitfalls.yaml +68 -0
  2203. package/.claude/skills/domains/real-estate-finance/skills/buyer-seller-agent/SKILL.md +410 -0
  2204. package/.claude/skills/domains/real-estate-finance/skills/loan-officer-assistant/SKILL.md +415 -0
  2205. package/.claude/skills/domains/real-estate-finance/task-chains.yaml +123 -0
  2206. package/.claude/skills/domains/real-estate-finance/team-personas.md +287 -0
  2207. package/.claude/skills/domains/retail/skills/customer-returns/SKILL.md +363 -0
  2208. package/.claude/skills/domains/saas-platforms/examples/enterprise-tier-isolation.md +147 -0
  2209. package/.claude/skills/domains/saas-platforms/pitfalls.yaml +74 -0
  2210. package/.claude/skills/domains/saas-platforms/skills/cms-developer/SKILL.md +377 -0
  2211. package/.claude/skills/domains/saas-platforms/skills/filament-specialist/SKILL.md +316 -0
  2212. package/.claude/skills/domains/saas-platforms/skills/salesforce-architect/SKILL.md +369 -0
  2213. package/.claude/skills/domains/saas-platforms/task-chains.yaml +90 -0
  2214. package/.claude/skills/domains/saas-platforms/team-personas.md +283 -0
  2215. package/.claude/skills/domains/sales/examples/qbr-revenue-forecast.md +158 -0
  2216. package/.claude/skills/domains/sales/pitfalls.yaml +73 -0
  2217. package/.claude/skills/domains/sales/skills/account-strategist/SKILL.md +408 -0
  2218. package/.claude/skills/domains/sales/skills/deal-strategist/SKILL.md +292 -0
  2219. package/.claude/skills/domains/sales/skills/discovery-coach/SKILL.md +257 -0
  2220. package/.claude/skills/domains/sales/skills/outbound-strategist/SKILL.md +262 -0
  2221. package/.claude/skills/domains/sales/skills/pipeline-analyst/SKILL.md +317 -0
  2222. package/.claude/skills/domains/sales/skills/proposal-strategist/SKILL.md +288 -0
  2223. package/.claude/skills/domains/sales/skills/sales-coach/SKILL.md +306 -0
  2224. package/.claude/skills/domains/sales/skills/sales-engineer/SKILL.md +272 -0
  2225. package/.claude/skills/domains/sales/skills/sales-outreach/SKILL.md +338 -0
  2226. package/.claude/skills/domains/sales/task-chains.yaml +123 -0
  2227. package/.claude/skills/domains/sales/team-personas.md +249 -0
  2228. package/.claude/skills/domains/supply-chain/skills/supply-chain-strategist/SKILL.md +340 -0
  2229. package/.claude/skills/domains/trading-hft/examples/PLAN-EXAMPLE.md +145 -0
  2230. package/.claude/skills/domains/trading-hft/pitfalls.yaml +99 -0
  2231. package/.claude/skills/domains/trading-hft/skills/kill-switches/SKILL.md +128 -0
  2232. package/.claude/skills/domains/trading-hft/skills/latency-budgets/SKILL.md +117 -0
  2233. package/.claude/skills/domains/trading-hft/skills/order-routing/SKILL.md +97 -0
  2234. package/.claude/skills/domains/trading-hft/task-chains.yaml +97 -0
  2235. package/.claude/skills/domains/trading-hft/team-personas.md +155 -0
  2236. package/.claude/skills/domains/training-l-and-d/skills/corporate-training-designer/SKILL.md +268 -0
  2237. package/.claude/skills/domains/voice-ai/skills/voice-ai-integration/SKILL.md +405 -0
  2238. package/.claude/skills/frontend/NOTICE.md +80 -0
  2239. package/.claude/skills/frontend/accessibility-and-wcag/SKILL.md +395 -0
  2240. package/.claude/skills/frontend/accessibility-and-wcag/SKILL.md.shadow.md +181 -0
  2241. package/.claude/skills/frontend/accessibility-and-wcag/benchmarks/accessibility-and-wcag.yaml +420 -0
  2242. package/.claude/skills/frontend/accessibility-and-wcag/reference/charts-accessibility.yaml +357 -0
  2243. package/.claude/skills/frontend/code-quality-and-typescript/SKILL.md +167 -0
  2244. package/.claude/skills/frontend/design-system-and-components/SKILL.md +155 -0
  2245. package/.claude/skills/frontend/design-system-and-components/SKILL.md.shadow.md +138 -0
  2246. package/.claude/skills/frontend/design-system-and-components/reference/fonts.yaml +811 -0
  2247. package/.claude/skills/frontend/design-system-and-components/reference/palettes.yaml +3066 -0
  2248. package/.claude/skills/frontend/frontend-accessibility/SKILL.md +213 -0
  2249. package/.claude/skills/frontend/frontend-data-layer/SKILL.md +310 -0
  2250. package/.claude/skills/frontend/frontend-patterns/SKILL.md +771 -0
  2251. package/.claude/skills/frontend/frontend-performance-optimization/SKILL.md +228 -0
  2252. package/.claude/skills/frontend/frontend-performance-optimization/SKILL.md.shadow.md +213 -0
  2253. package/.claude/skills/frontend/ux-and-user-journeys/SKILL.md +153 -0
  2254. package/.claude/skills/frontend/ux-and-user-journeys/SKILL.md.shadow.md +138 -0
  2255. package/.claude/skills/frontend/ux-and-user-journeys/reference/guidelines.yaml +997 -0
  2256. package/.claude/squad-revocations.jsonl +5 -0
  2257. package/.claude/task-chains.yaml +151 -0
  2258. package/.claude/team.md +825 -0
  2259. package/.claude/templates/squad-bundle/README.md +208 -0
  2260. package/.claude/templates/squad-bundle/conftest.py +27 -0
  2261. package/.claude/templates/squad-bundle/examples/template-example.md.template +94 -0
  2262. package/.claude/templates/squad-bundle/pitfalls.yaml.template +88 -0
  2263. package/.claude/templates/squad-bundle/task-chains.yaml.template +92 -0
  2264. package/.claude/templates/squad-bundle/team-personas.md.template +161 -0
  2265. package/.claude/trust/README.md +89 -0
  2266. package/.claude/trust/owner.asc +11 -0
  2267. package/.claude/workflows/README.md +124 -0
  2268. package/.claude/workflows/audit-fanout.js +204 -0
  2269. package/.claude/workflows/eval-baseline-n20.js +330 -0
  2270. package/.claude/workflows/nightly-hygiene.js +176 -0
  2271. package/LICENSE +21 -0
  2272. package/PROTOCOL.md +597 -0
  2273. package/README.md +167 -0
  2274. package/SPEC/v1/README.md +181 -0
  2275. package/SPEC/v1/adapters.schema.md +272 -0
  2276. package/SPEC/v1/audit-log.schema.md +1514 -0
  2277. package/SPEC/v1/audit-query.schema.md +152 -0
  2278. package/SPEC/v1/benchmarks.schema.md +166 -0
  2279. package/SPEC/v1/claude-sdk-compat.md +123 -0
  2280. package/SPEC/v1/debate.schema.md +35 -0
  2281. package/SPEC/v1/hook-io.schema.md +94 -0
  2282. package/SPEC/v1/install-cli.md +234 -0
  2283. package/SPEC/v1/judge-payload.schema.md +98 -0
  2284. package/SPEC/v1/live-adapters-policy.schema.md +118 -0
  2285. package/SPEC/v1/mcp-server.schema.md +558 -0
  2286. package/SPEC/v1/memory-shared.schema.md +365 -0
  2287. package/SPEC/v1/normalized_envelope.schema.md +183 -0
  2288. package/SPEC/v1/npm-shim.md +95 -0
  2289. package/SPEC/v1/plan.schema.md +34 -0
  2290. package/SPEC/v1/policy-dsl.schema.md +466 -0
  2291. package/SPEC/v1/predict-budget.schema.md +289 -0
  2292. package/SPEC/v1/rag-sidecar.schema.md +222 -0
  2293. package/SPEC/v1/red-team-corpus.schema.md +186 -0
  2294. package/SPEC/v1/replay.schema.md +272 -0
  2295. package/SPEC/v1/scratchpad.schema.md +172 -0
  2296. package/SPEC/v1/sentinel-format.schema.md +306 -0
  2297. package/SPEC/v1/session-graph.schema.md +236 -0
  2298. package/SPEC/v1/skill-frontmatter.schema.md +83 -0
  2299. package/SPEC/v1/skill-index.schema.md +197 -0
  2300. package/SPEC/v1/skill-proposals.schema.md +175 -0
  2301. package/SPEC/v1/soc2-control-map.schema.md +797 -0
  2302. package/SPEC/v1/squad-manifest.schema.md +157 -0
  2303. package/SPEC/v1/state-stores.schema.md +146 -0
  2304. package/SPEC/v1/tier-policy.schema.md +264 -0
  2305. package/SPEC/v1/tournament-report.schema.md +156 -0
  2306. package/VERSION +1 -0
  2307. package/bin/ceo-orch-init.js +55 -0
  2308. package/package.json +42 -0
  2309. package/scripts/_framework_manifest_set.sh +237 -0
  2310. package/scripts/_hash_lib.sh +92 -0
  2311. package/scripts/build-plugin.py +351 -0
  2312. package/scripts/discover_foreign_context.py +151 -0
  2313. package/scripts/install-accelerators.sh +166 -0
  2314. package/scripts/install-npm.sh +254 -0
  2315. package/scripts/install.sh +1932 -0
  2316. package/scripts/local/OWNER-CEREMONY-PLAN-094-WAVE-A.sh +648 -0
  2317. package/scripts/local/OWNER-CEREMONY-S82-V1120.sh +169 -0
  2318. package/scripts/local/plan-093-apply-kernel-edits.py +496 -0
  2319. package/scripts/local/plan-093-execute-ceremony.sh +118 -0
  2320. package/scripts/local/plan-093-kernel-override-restart.sh +115 -0
  2321. package/scripts/local/plan-093-ship-v1.26.0.sh +226 -0
  2322. package/scripts/local/plan-094-apply-wave-a-c-e.py +398 -0
  2323. package/scripts/local/smoke-install-parity.sh +168 -0
  2324. package/scripts/local/trading-readonly-escape-hatch.sh +244 -0
  2325. package/scripts/measure-repo-size.sh +98 -0
  2326. package/scripts/npm-rebuild.sh +172 -0
  2327. package/scripts/publish-plugin.sh +144 -0
  2328. package/scripts/tests/smoke-install.sh +260 -0
  2329. package/scripts/tests/test-install-sandbox-merge.sh +137 -0
  2330. package/scripts/tests/test_install_baseline_manifest.sh +392 -0
  2331. package/scripts/uninstall.sh +282 -0
  2332. package/scripts/upgrade.sh +1260 -0
  2333. package/templates/.claude/tier-policy.json +35 -0
  2334. package/templates/.claude/tier-policy.json.sigchain +1 -0
  2335. package/templates/.env.example +134 -0
  2336. package/templates/.github/CODEOWNERS.template +33 -0
  2337. package/templates/.github/workflows/benchmarks.yml.template +145 -0
  2338. package/templates/.github/workflows/validate.yml.template +226 -0
  2339. package/templates/.mcp.json +13 -0
  2340. package/templates/CLAUDE.md +125 -0
  2341. package/templates/MEMORY.md +36 -0
  2342. package/templates/README.md +46 -0
  2343. package/templates/compaction.md +130 -0
  2344. package/templates/docs/BRANCH-PROTECTION.md +203 -0
  2345. package/templates/docs/rotation-log.md +18 -0
  2346. package/templates/oidc-proxy/README.md +141 -0
  2347. package/templates/oidc-proxy/broker.config.example.json +29 -0
  2348. package/templates/oidc-proxy/oidc_key_broker.py +361 -0
  2349. package/templates/oidc-proxy/tests/test_oidc_key_broker.py +361 -0
  2350. package/templates/scripts/statusline-ceo.py +597 -0
  2351. package/templates/settings/settings.base.json +708 -0
  2352. package/templates/settings/settings.stack.node.json +19 -0
  2353. package/templates/settings/settings.stack.otel.json +25 -0
  2354. package/templates/settings/settings.stack.sandbox.json +57 -0
  2355. package/templates/settings/settings.user.json +265 -0
  2356. package/templates/team-personas-reference.md +269 -0
@@ -0,0 +1,2696 @@
1
+ #!/usr/bin/env python3
2
+ """Governance Hook: named agents MUST include ## SKILL CONTENT.
3
+
4
+ Registered in `.claude/settings.json` under `hooks.PreToolUse.Agent`.
5
+ Runs via the `_python-hook.sh` shim (A.4) for Python version resolution.
6
+
7
+ Port of `.claude/hooks/check-agent-spawn.sh` with:
8
+ - stdlib-only (no jq dependency)
9
+ - unit-testable (logic is in `decide()`, not tangled with stdin/stdout)
10
+ - fail-open on any internal error (never blocks the user on infrastructure bug)
11
+
12
+ ## Two detection strategies
13
+
14
+ 1. The agent's `description` field contains a team member name, extracted
15
+ dynamically from team.md / frontend-team.md / domains/*/team-personas.md.
16
+ 2. The prompt contains a persona header (`PERSONA:`, `## AGENT PROFILE`,
17
+ `## PERSONA`) — self-describing spawn.
18
+
19
+ If either strategy matches, the prompt MUST contain `## SKILL CONTENT`.
20
+ Without it, the spawn is blocked as a generic agent wearing a name tag.
21
+
22
+ ## Output contract
23
+
24
+ Writes a single-line JSON decision to stdout:
25
+
26
+ {"decision":"allow"}
27
+ {"decision":"block","reason":"GOVERNANCE: ..."}
28
+
29
+ Exit code is 0 in both cases — Claude Code reads the decision from stdout,
30
+ not the exit code. Internal errors (bad stdin, missing team files, etc.)
31
+ are logged to stderr and the spawn is allowed (fail-open).
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import hashlib
37
+ import json
38
+ import os
39
+ import re
40
+ import sys
41
+ import unicodedata
42
+ from dataclasses import dataclass
43
+ from pathlib import Path
44
+ from typing import Any, Dict, List, Optional, Tuple
45
+
46
+ # Make the _lib package importable — hooks live in .claude/hooks/ and
47
+ # _lib is a sibling package.
48
+ _HOOKS_DIR = Path(__file__).resolve().parent
49
+ if str(_HOOKS_DIR) not in sys.path:
50
+ sys.path.insert(0, str(_HOOKS_DIR))
51
+
52
+ from _lib import contract as _contract # noqa: E402
53
+ from _lib.adapters import claude as _claude_adapter # noqa: E402
54
+ from _lib import team as _team # noqa: E402
55
+ from _lib import redact as _redact # noqa: E402 (PLAN-020 Phase 2: skill-ref scan)
56
+
57
+ # PLAN-106 Wave G.1 — sub-agent N-of-M aggregator with partial-drop emit.
58
+ # Re-exported below as `aggregate_subagent_findings` so coordinator-side
59
+ # callers (swarm coordinator, future debate consensus aggregator) can
60
+ # `from check_agent_spawn import aggregate_subagent_findings` without
61
+ # poking into `_lib`. Closes AC10 grep contract.
62
+ from _lib import subagent_dispatch as _subagent_dispatch # noqa: E402
63
+
64
+ # PLAN-045 Wave 1 P0-03 — VETO floor runtime validator.
65
+ try:
66
+ from _lib import agent_frontmatter as _agent_frontmatter
67
+ except Exception: # pragma: no cover
68
+ _agent_frontmatter = None # type: ignore[assignment]
69
+
70
+ # Optional: emit veto_triggered to event stream v2 (fail-open)
71
+ try:
72
+ # PLAN-094 Wave E — lazy-import dispatch shim closes AC9c spawn-hook regression
73
+ from _lib import audit_emit_dispatch as _audit_emit # noqa: E402
74
+ _AUDIT_EMIT_AVAILABLE = True
75
+ except Exception: # pragma: no cover
76
+ _AUDIT_EMIT_AVAILABLE = False
77
+
78
+ # PLAN-092 Wave A.4 — cookbook-advisor pattern matcher (fail-open import).
79
+ try:
80
+ from _lib import cookbook_patterns as _cookbook_patterns # noqa: E402
81
+ _COOKBOOK_PATTERNS_AVAILABLE = True
82
+ except Exception: # pragma: no cover
83
+ _COOKBOOK_PATTERNS_AVAILABLE = False
84
+
85
+ # PLAN-112-FOLLOWUP-persona-routing-wire W1 — persona_routing matrix
86
+ # consult (fail-open import). The god-mode (persona × primitive) routing
87
+ # matrix declares AUTO-05 ("model_routing_advised") ENFORCING per
88
+ # ADR-118-AMEND-1, but check_agent_spawn never consulted it — the policy
89
+ # was dead at runtime. This wire makes the matrix observable/auditable.
90
+ # CONSULT + AUDIT ONLY — the BLOCK is DEFERRED (the hook payload exposes
91
+ # no requested-model signal; see plan §2/§3). NEVER read
92
+ # `_PRIMITIVE_DEFAULT_MODE` directly — get_mode() encapsulates the
93
+ # kill-switch demotion.
94
+ try:
95
+ from _lib import persona_routing as _persona_routing # noqa: E402
96
+ except Exception: # pragma: no cover - fail-open
97
+ _persona_routing = None # type: ignore[assignment]
98
+
99
+ # PLAN-113 WIRE-DEADMOD — SEC-P0-01 spec-context sanitizer (ADR-089).
100
+ # Sanitizes the ## SPEC CONTEXT block payload in spawn prompts before allow.
101
+ # ADVISORY telemetry only: sanitize result is emitted via emit_generic so
102
+ # sentinel violations / truncation are visible in the audit log. Never blocks.
103
+ # Fail-open: missing module → _SPEC_CTX_SANITIZER_AVAILABLE = False.
104
+ try:
105
+ from _lib import spec_context_sanitizer as _spec_ctx_sanitizer # noqa: E402
106
+ _SPEC_CTX_SANITIZER_AVAILABLE = True
107
+ except Exception: # pragma: no cover - fail-open
108
+ _spec_ctx_sanitizer = None # type: ignore[assignment]
109
+ _SPEC_CTX_SANITIZER_AVAILABLE = False
110
+
111
+ # PLAN-113 WIRE-DEADMOD — confidence_labels (PLAN-083 Wave 1.10).
112
+ # Classifies the spawn action type and emits a spawn_confidence_advisory event
113
+ # so that recommender / receipt formatter surfaces have a hook-level signal.
114
+ # ADVISORY only — never blocks. Fail-open.
115
+ try:
116
+ from _lib import confidence_labels as _confidence_labels # noqa: E402
117
+ _CONFIDENCE_LABELS_AVAILABLE = True
118
+ except Exception: # pragma: no cover - fail-open
119
+ _confidence_labels = None # type: ignore[assignment]
120
+ _CONFIDENCE_LABELS_AVAILABLE = False
121
+
122
+
123
+ # ---------------------------------------------------------------------------
124
+ # PLAN-078 Wave 1 — Model routing telemetry (advisory-emit-only)
125
+ # ---------------------------------------------------------------------------
126
+ # Telemetry-only: hook contract (`_lib/contract.py:112-127`) supports only
127
+ # allow/block/systemMessage. NO `tool_input` mutation. This block exists to
128
+ # observe sub-agent spawn → recommended model deltas so the CEO can hand-edit
129
+ # `.claude/agents/<archetype>.md` frontmatter (manual remediation path). The
130
+ # hard-enforcement leg is the existing VETO-floor check at
131
+ # check_veto_floor_for_role() which runs BEFORE this advisory.
132
+ #
133
+ # Hot-path requirement: p95 ≤ 20ms. Achieved via:
134
+ # - module-level cache of task_route module + classify reference
135
+ # - module-level cache of frontmatter parser
136
+ # - frontmatter cache by-archetype (LRU dict, capped 64 entries) — agents
137
+ # dir is small + rarely changes mid-session, so cache invalidation is
138
+ # not required (next session re-imports module).
139
+ #
140
+ # Bypass: env var `CEO_MODEL_ROUTING=0` short-circuits to a no-op.
141
+ # Fail-open: any exception during advisory emit is swallowed (logged to
142
+ # breadcrumb if available) so the spawn is NEVER blocked by a routing bug.
143
+
144
+ # Lazy task_route module reference — populated on first call. Cached.
145
+ _TASK_ROUTE_MODULE: Optional[Any] = None
146
+ _TASK_ROUTE_LOAD_FAILED: bool = False
147
+
148
+ # In-memory cache: archetype -> (model: Optional[str], confidence: float).
149
+ # Cleared at module init only (process-scoped).
150
+ _FRONTMATTER_MODEL_CACHE: Dict[str, Tuple[Optional[str], float]] = {}
151
+ _FRONTMATTER_CACHE_MAX = 64
152
+
153
+
154
+ def _resolve_task_route():
155
+ """Best-effort import of `.claude/scripts/task-route.py` as a module.
156
+
157
+ Returns the module on success, None on failure. Never raises.
158
+ Cached after first call (positive or negative).
159
+
160
+ Resolution order:
161
+ 1. ``CLAUDE_PROJECT_DIR`` env var (test-friendly + adopter-respectful)
162
+ 2. Walk up from `_HOOKS_DIR` looking for a `.claude/scripts/task-route.py`
163
+ """
164
+ global _TASK_ROUTE_MODULE, _TASK_ROUTE_LOAD_FAILED
165
+ if _TASK_ROUTE_MODULE is not None or _TASK_ROUTE_LOAD_FAILED:
166
+ return _TASK_ROUTE_MODULE
167
+ try:
168
+ import importlib.util as _ilutil
169
+ candidates = []
170
+ env_root = os.environ.get("CLAUDE_PROJECT_DIR")
171
+ if env_root:
172
+ candidates.append(
173
+ Path(env_root) / ".claude" / "scripts" / "task-route.py"
174
+ )
175
+ # Walk up from _HOOKS_DIR (stable in normal install)
176
+ candidates.append(
177
+ _HOOKS_DIR.parent.parent / ".claude" / "scripts" / "task-route.py"
178
+ )
179
+ # Walk up from this file's location (helps when the hook itself is
180
+ # staged under .claude/plans/PLAN-NNN/staging/...; rare but useful
181
+ # for in-tree pytest runs that exercise the staged copy).
182
+ cur = Path(__file__).resolve().parent
183
+ for _ in range(8):
184
+ if cur == cur.parent:
185
+ break
186
+ cand = cur / ".claude" / "scripts" / "task-route.py"
187
+ if cand.is_file():
188
+ candidates.append(cand)
189
+ break
190
+ cur = cur.parent
191
+ tr_path = next((c for c in candidates if c.is_file()), None)
192
+ if tr_path is None:
193
+ _TASK_ROUTE_LOAD_FAILED = True
194
+ return None
195
+ spec = _ilutil.spec_from_file_location("task_route", tr_path)
196
+ if spec is None or spec.loader is None:
197
+ _TASK_ROUTE_LOAD_FAILED = True
198
+ return None
199
+ mod = _ilutil.module_from_spec(spec)
200
+ spec.loader.exec_module(mod)
201
+ _TASK_ROUTE_MODULE = mod
202
+ return mod
203
+ except Exception: # pragma: no cover - fail-open
204
+ _TASK_ROUTE_LOAD_FAILED = True
205
+ return None
206
+
207
+
208
+ def _read_archetype_model_frontmatter(
209
+ archetype: str,
210
+ agents_dir: Path,
211
+ ) -> Tuple[Optional[str], float]:
212
+ """Read `model:` field from `<agents_dir>/<archetype>.md`.
213
+
214
+ Returns (model, confidence). When frontmatter present + model field
215
+ set, confidence is 1.0 (authoritative). When file missing or no
216
+ model field, confidence is 0.0 (caller will fall back to classify).
217
+ Cached by archetype to avoid re-reading on repeat spawns.
218
+ """
219
+ cached = _FRONTMATTER_MODEL_CACHE.get(archetype)
220
+ if cached is not None:
221
+ return cached
222
+ result: Tuple[Optional[str], float] = (None, 0.0)
223
+ try:
224
+ if _agent_frontmatter is None:
225
+ pass
226
+ else:
227
+ path = _agent_frontmatter.resolve_agent_file(archetype, agents_dir)
228
+ if path.is_file():
229
+ metadata = _agent_frontmatter.parse_agent_file(path)
230
+ model = metadata.get("model", "").strip() if metadata else ""
231
+ if model:
232
+ result = (model, 1.0)
233
+ except Exception: # pragma: no cover - fail-open
234
+ result = (None, 0.0)
235
+ # Bounded LRU-ish cache (simple FIFO eviction at cap).
236
+ if len(_FRONTMATTER_MODEL_CACHE) >= _FRONTMATTER_CACHE_MAX:
237
+ try:
238
+ _FRONTMATTER_MODEL_CACHE.pop(next(iter(_FRONTMATTER_MODEL_CACHE)))
239
+ except StopIteration: # pragma: no cover
240
+ pass
241
+ _FRONTMATTER_MODEL_CACHE[archetype] = result
242
+ return result
243
+
244
+
245
+ def _extract_archetype_from_payload(
246
+ description: str,
247
+ prompt: str,
248
+ subagent_type: str,
249
+ ) -> str:
250
+ """Best-effort archetype detection from spawn payload.
251
+
252
+ Order: ``subagent_type`` (Agent tool input) → first persona header
253
+ line → empty string. Returns archetype slug or empty string.
254
+ """
255
+ if subagent_type:
256
+ return subagent_type.strip().lower()
257
+ # Try to find archetype from persona header in prompt
258
+ if prompt:
259
+ m = re.search(
260
+ r"(?:archetype|role|persona):\s*([a-z][a-z0-9_-]+)",
261
+ prompt,
262
+ flags=re.IGNORECASE,
263
+ )
264
+ if m:
265
+ return m.group(1).strip().lower()
266
+ return ""
267
+
268
+
269
+ def _emit_model_routing_advisory(
270
+ *,
271
+ description: str,
272
+ prompt: str,
273
+ subagent_type: str,
274
+ env: Optional[Dict[str, str]] = None,
275
+ project_dir: Optional[str] = None,
276
+ ) -> None:
277
+ """Emit a `model_routing_advised` advisory event for this spawn.
278
+
279
+ PLAN-078 Wave 1. Telemetry-only — this function:
280
+ - reads archetype from spawn payload
281
+ - reads `.claude/agents/<archetype>.md` frontmatter `model:` if set
282
+ - else calls `task_route.classify()` for a recommendation
283
+ - emits via audit_emit (deny-by-default 6-field allowlist)
284
+
285
+ NEVER raises. Bypass via `CEO_MODEL_ROUTING=0`.
286
+ """
287
+ try:
288
+ src_env = env if env is not None else os.environ
289
+ if (src_env.get("CEO_MODEL_ROUTING") or "").strip() == "0":
290
+ return # bypass
291
+ if not _AUDIT_EMIT_AVAILABLE:
292
+ return # audit emit unavailable; nothing to do
293
+
294
+ archetype = _extract_archetype_from_payload(
295
+ description or "", prompt or "", subagent_type or ""
296
+ )
297
+ if not archetype:
298
+ return # generic spawn — no archetype to route
299
+
300
+ # Path A: agent frontmatter declared model -> authoritative.
301
+ proj = project_dir or src_env.get("CLAUDE_PROJECT_DIR") or os.getcwd()
302
+ agents_dir = Path(proj) / ".claude" / "agents"
303
+ fm_model, fm_conf = _read_archetype_model_frontmatter(
304
+ archetype, agents_dir
305
+ )
306
+
307
+ if fm_model:
308
+ model_recommended = fm_model
309
+ confidence = fm_conf
310
+ applied_or_skipped = "skipped_classify_frontmatter_authoritative"
311
+ override_reason = "frontmatter_model_present"
312
+ task_type = "frontmatter"
313
+ else:
314
+ # Path B: in-process classify.
315
+ tr = _resolve_task_route()
316
+ if tr is None or not hasattr(tr, "classify"):
317
+ # Fail-open: no recommendation available, advisory skipped.
318
+ return
319
+ try:
320
+ desc_text = (description or "")[:8 * 1024] # cap to 8KiB
321
+ result = tr.classify(desc_text, [])
322
+ except Exception:
323
+ # classify() raised — fail-open with breadcrumb attempt.
324
+ _emit_advisory_safe(
325
+ archetype=archetype,
326
+ task_type="classify_error",
327
+ model_recommended="",
328
+ confidence=0.0,
329
+ applied_or_skipped="skipped_classify_exception",
330
+ override_reason="classify_raised",
331
+ )
332
+ return
333
+ classification = (
334
+ result.get("classification") if isinstance(result, dict) else ""
335
+ ) or ""
336
+ # Conservative mapping: classify() returns S/M/L/XL classification —
337
+ # not a model directly. Telemetry records the classification +
338
+ # leaves model_recommended empty so PLAN-079 can decide the mapping.
339
+ model_recommended = ""
340
+ confidence = 0.7 if classification else 0.0
341
+ applied_or_skipped = (
342
+ "advisory_only_no_recommendation"
343
+ if not classification
344
+ else "advisory_only_classification_emitted"
345
+ )
346
+ override_reason = ""
347
+ task_type = classification or "unclassified"
348
+
349
+ _emit_advisory_safe(
350
+ archetype=archetype,
351
+ task_type=task_type,
352
+ model_recommended=model_recommended,
353
+ confidence=float(confidence),
354
+ applied_or_skipped=applied_or_skipped,
355
+ override_reason=override_reason,
356
+ )
357
+ except Exception: # pragma: no cover - fail-open invariant
358
+ return
359
+
360
+
361
+ # ---------------------------------------------------------------------------
362
+ # PLAN-112-FOLLOWUP-persona-routing-wire W1+W2+W3 — god-mode matrix consult.
363
+ # ---------------------------------------------------------------------------
364
+ # Consult ALL THREE persona_routing APIs (AC1):
365
+ # `get_mode(archetype, "AUTO-05")` (recorded mode label) +
366
+ # `is_enforcing(archetype, "AUTO-05")` (authoritative effective-enforcing
367
+ # boolean that drives `decision`) + `is_killswitch_active()` (records the
368
+ # kill-switch state) and EMIT the resulting mode as forensic telemetry
369
+ # (`model_routing_enforced`). This closes the F-1.2 dead-policy (the
370
+ # AUTO-05 enforcing cell was never read at runtime).
371
+ #
372
+ # W3 — DEFERRED BLOCK + FLIP DOCTRINE:
373
+ # The actual model-tier BLOCK is DEFERRED. A "model mismatch -> block"
374
+ # predicate needs the dispatched/requested model, but the Agent hook
375
+ # payload exposes only description/prompt/subagent_type/run_in_background
376
+ # (SPEC/v1/hook-io.schema.md). `metadata.get("model")` (read in
377
+ # `_read_archetype_model_frontmatter`) is the AGENT FRONTMATTER model, NOT
378
+ # a spawn-requested model. With no requested-model input, a block would be
379
+ # theater. When the harness exposes the dispatched model to PreToolUse, the
380
+ # violation predicate becomes a 1-liner: fm_model present AND requested
381
+ # present AND differing.
382
+ # The future enable of that block is governed by OBSERVED-VIOLATION VOLUME
383
+ # + a false-positive-rate threshold, NOT by elapsed calendar days
384
+ # (ADR-095 / [[feedback-no-calendar-gates-ai-workflow]]). Recorded here as
385
+ # the flip doctrine for a future ADR-118-AMEND-2.
386
+ #
387
+ # SECURITY INVARIANTS:
388
+ # - Mode is read off the AUTHORITATIVE `subagent_type`-derived archetype
389
+ # ONLY — NEVER the prompt-regex archetype (attacker-controllable; a
390
+ # spoofed archetype must not flip the recorded mode). CWE-400/spoofing.
391
+ # - Fail-OPEN: import/get_mode failure -> emit `model_routing_eval_error`
392
+ # and return; NEVER raise in the hook hot path (CLAUDE.md §5).
393
+ # - Kill-switch precedence: get_mode() already demotes an enforcing cell
394
+ # to advisory under `CEO_GODMODE_ENFORCING=0`; we record the demoted
395
+ # mode (advisory) so the audit reflects effective policy.
396
+ def _consult_model_routing_mode(
397
+ *,
398
+ description: str,
399
+ prompt: str,
400
+ subagent_type: str,
401
+ env: Optional[Dict[str, str]] = None,
402
+ project_dir: Optional[str] = None,
403
+ ) -> None:
404
+ """Consult the god-mode routing matrix + emit forensic telemetry.
405
+
406
+ CONSULT + AUDIT ONLY. NEVER blocks, NEVER mutates tool_input, NEVER
407
+ raises. Consults all THREE persona_routing APIs (AC1): `get_mode`
408
+ (recorded mode label), `is_enforcing` (authoritative effective-
409
+ enforcing boolean that drives `decision`), and `is_killswitch_active`.
410
+ Emits `model_routing_enforced` (mode/recommended_model/
411
+ killswitch_armed/decision) on success, `model_routing_eval_error`
412
+ (decision=eval_error) on any infra failure (fail-open).
413
+ """
414
+ try:
415
+ src_env = env if env is not None else os.environ
416
+ if not _AUDIT_EMIT_AVAILABLE:
417
+ return # nothing to audit against; consult is observability-only
418
+
419
+ # AUTHORITATIVE source ONLY — subagent_type. A prompt-regex archetype
420
+ # is attacker-controllable and must not flip the recorded mode.
421
+ archetype = (subagent_type or "").strip().lower()
422
+ if not archetype:
423
+ return # no authoritative archetype -> no mode to record
424
+
425
+ session_id = src_env.get("CLAUDE_SESSION_ID", "") or ""
426
+ proj = project_dir or src_env.get("CLAUDE_PROJECT_DIR") or os.getcwd()
427
+
428
+ # Fail-OPEN guard around the matrix consult.
429
+ try:
430
+ if _persona_routing is None:
431
+ raise RuntimeError("persona_routing import unavailable")
432
+ # NEVER read _PRIMITIVE_DEFAULT_MODE directly — get_mode /
433
+ # is_enforcing honor the kill-switch demotion internally.
434
+ # All THREE persona_routing APIs are consulted (AC1):
435
+ # - get_mode -> the effective (possibly demoted) mode label
436
+ # recorded verbatim in the emit
437
+ # - is_enforcing -> the AUTHORITATIVE effective-enforcing boolean
438
+ # that drives `decision` (kill-switch already
439
+ # folded in: returns False when demoted)
440
+ # - is_killswitch_active -> records whether the kill-switch armed
441
+ mode = _persona_routing.get_mode(archetype, "AUTO-05")
442
+ is_enforcing = bool(
443
+ _persona_routing.is_enforcing(archetype, "AUTO-05")
444
+ )
445
+ killswitch_armed = bool(_persona_routing.is_killswitch_active())
446
+ except Exception: # noqa: BLE001 — fail-open per CLAUDE.md §5
447
+ try:
448
+ _audit_emit.emit_generic(
449
+ "model_routing_eval_error",
450
+ archetype=archetype[:64],
451
+ reason_code="persona_routing_eval_failed",
452
+ decision="eval_error",
453
+ session_id=session_id,
454
+ project=str(proj)[:256],
455
+ )
456
+ except Exception: # noqa: BLE001
457
+ pass
458
+ return
459
+
460
+ # recommended_model: best-effort authoritative agent-frontmatter
461
+ # model for this archetype (NOT a spawn-requested model). "" when
462
+ # absent. Reuses the cached frontmatter reader.
463
+ recommended_model = ""
464
+ try:
465
+ agents_dir = Path(proj) / ".claude" / "agents"
466
+ fm_model, _conf = _read_archetype_model_frontmatter(
467
+ archetype, agents_dir
468
+ )
469
+ recommended_model = (fm_model or "")[:64]
470
+ except Exception: # noqa: BLE001 — telemetry enrichment is optional
471
+ recommended_model = ""
472
+
473
+ # decision enum: enforce_telemetry | advisory | eval_error.
474
+ # NO `block` value — block deferred (§2.4). Derived from the
475
+ # AUTHORITATIVE is_enforcing() boolean (NOT a re-derivation from
476
+ # the `mode` string), so the kill-switch demotion already folded
477
+ # into is_enforcing() drives the decision. `mode` (possibly
478
+ # demoted to "advisory" by get_mode) is still recorded verbatim.
479
+ decision = "enforce_telemetry" if is_enforcing else "advisory"
480
+
481
+ try:
482
+ _audit_emit.emit_generic(
483
+ "model_routing_enforced",
484
+ archetype=archetype[:64],
485
+ mode=str(mode)[:16],
486
+ recommended_model=recommended_model,
487
+ killswitch_armed=killswitch_armed,
488
+ decision=decision,
489
+ session_id=session_id,
490
+ project=str(proj)[:256],
491
+ )
492
+ except Exception: # noqa: BLE001 — fail-open
493
+ return
494
+ except Exception: # pragma: no cover — fail-open invariant
495
+ return
496
+
497
+
498
+ # PLAN-091 Wave A.4 (W3.1) — archetype → mcp_routing task_class heuristic.
499
+ #
500
+ # Maps the spawn's archetype to one of the 4 task_class strings declared in
501
+ # `_lib/mcp_routing._ROUTING_TABLE`. mcp_routing.resolve() emits its own
502
+ # `mcp_route_advised` advisory (PLAN-086 Wave D) — the spawn-hook wire only
503
+ # needs to trigger the resolver at the right callsite. NO mapping fallthrough
504
+ # = NO emit (advisory-only; avoid noise on every spawn).
505
+ _MCP_ARCHETYPE_TASK_CLASS_HINTS: Tuple[Tuple[Tuple[str, ...], str], ...] = (
506
+ # ORDER MATTERS — first match wins. More-specific archetype slugs
507
+ # (finops/seo/crypto) come BEFORE the bare "architect" pattern so
508
+ # composite slugs like "llm-finops-architect" or "crypto-research-
509
+ # analyst" route to the more specific MCP server bundle.
510
+ (("finops", "cost", "budget"), "finops"),
511
+ (("seo", "ahrefs", "similarweb"), "seo_research"),
512
+ (("crypto", "lunarcrush", "blockchain"), "crypto_research"),
513
+ (("architect", "arch-"), "arch"),
514
+ )
515
+
516
+
517
+ # PLAN-091 Wave A.5 (W3.3) — archetype-hint patterns triggering a
518
+ # `specialization_promoted` advisory when subagent_type == general-purpose.
519
+ # Match is FIRST-HIT, not exhaustive — keeps emit volume bounded. Hints
520
+ # are lowercase substring matches on (description + prompt).
521
+ _PROMOTION_ARCHETYPE_HINTS: Tuple[Tuple[str, Tuple[str, ...]], ...] = (
522
+ ("performance-engineer",
523
+ ("latency", "p95", "p99", "profile", "memory leak", "throughput",
524
+ "gc tuning", "hot path")),
525
+ ("security-engineer",
526
+ ("jwt", "oauth", "authentication", "vulnerability", "owasp",
527
+ "xss", "sql injection", "csrf", "rate limiting")),
528
+ ("qa-architect",
529
+ ("test strategy", "mutation testing", "flake", "regression",
530
+ "property-based", "fuzz", "contract test")),
531
+ ("code-reviewer",
532
+ ("merge gate", "code review", "veto", "review checklist")),
533
+ ("incident-commander",
534
+ ("incident commander", "severity classification", "all-clear",
535
+ "paging policy", "post-incident review")),
536
+ ("identity-trust-architect",
537
+ ("token rotation", "session lifecycle", "rbac", "abac",
538
+ "service-to-service trust", "mtls")),
539
+ ("threat-detection-engineer",
540
+ ("mitre att&ck", "siem rule", "detection rule", "alert deduplication",
541
+ "purple team", "threat hunting")),
542
+ ("devops",
543
+ ("github actions", "sha-pin", "ci/cd pipeline", "rollback strategy",
544
+ "secret rotation")),
545
+ )
546
+
547
+
548
+ def _emit_mcp_routing_advisory(
549
+ *,
550
+ description: str,
551
+ prompt: str,
552
+ subagent_type: str,
553
+ env: Optional[Dict[str, str]] = None,
554
+ ) -> None:
555
+ """PLAN-091 Wave A.4 (W3.1) — emit `mcp_route_advised` for this spawn.
556
+
557
+ Derives task_class from the spawn's archetype via the
558
+ ``_MCP_ARCHETYPE_TASK_CLASS_HINTS`` table, then delegates to
559
+ ``_lib/mcp_routing.resolve()`` which performs its own emit (per
560
+ PLAN-086 Wave D). Bypass: ``CEO_MCP_ROUTING_HOOK=0`` (parallel to
561
+ PLAN-078 model-routing bypass; per-server kill switches live inside
562
+ mcp_routing.resolve()).
563
+
564
+ NEVER raises. Advisory-only — never mutates tool_input, never blocks.
565
+ """
566
+ try:
567
+ src_env = env if env is not None else os.environ
568
+ if (src_env.get("CEO_MCP_ROUTING_HOOK") or "").strip() == "0":
569
+ return
570
+ archetype = _extract_archetype_from_payload(
571
+ description or "", prompt or "", subagent_type or ""
572
+ )
573
+ if not archetype:
574
+ return
575
+ archetype_lc = archetype.lower()
576
+ task_class: Optional[str] = None
577
+ for hints, mapped in _MCP_ARCHETYPE_TASK_CLASS_HINTS:
578
+ if any(h in archetype_lc for h in hints):
579
+ task_class = mapped
580
+ break
581
+ if task_class is None:
582
+ return # no mapping; emit volume stays bounded
583
+ try:
584
+ from _lib import mcp_routing as _mcpr # type: ignore
585
+ _mcpr.resolve(task_class) # resolver fires advisory emit
586
+ except Exception: # pragma: no cover - fail-open
587
+ return
588
+ except Exception: # pragma: no cover - fail-open
589
+ return
590
+
591
+
592
+ def _emit_promotion_advisory(
593
+ *,
594
+ description: str,
595
+ prompt: str,
596
+ subagent_type: str,
597
+ env: Optional[Dict[str, str]] = None,
598
+ ) -> None:
599
+ """PLAN-091 Wave A.5 (W3.3) — emit `specialization_promoted` when a
600
+ ``general-purpose`` spawn matches a specialist archetype heuristic.
601
+
602
+ Advisory ONLY — does NOT auto-spawn the suggested specialist. Records
603
+ the suggestion in audit log so PLAN-094+ can promote SEMI to AUTO once
604
+ confidence is established. Bypass: ``CEO_PROMOTION_HEURISTIC=0``.
605
+
606
+ First-hit match wins (bounded emit volume). NEVER raises.
607
+ """
608
+ try:
609
+ src_env = env if env is not None else os.environ
610
+ if (src_env.get("CEO_PROMOTION_HEURISTIC") or "").strip() == "0":
611
+ return
612
+ if (subagent_type or "").strip().lower() != "general-purpose":
613
+ return
614
+ if not _AUDIT_EMIT_AVAILABLE:
615
+ return
616
+ text = ((description or "") + "\n" + (prompt or "")).lower()
617
+ text = text[: 8 * 1024] # 8KiB cap on hint-scan window
618
+ for suggested, hints in _PROMOTION_ARCHETYPE_HINTS:
619
+ for hint in hints:
620
+ if hint in text:
621
+ try:
622
+ # PLAN-088 R2 iter-2 STRICKEN specialization_promoted
623
+ # as separate action; routed via mcp_route_advised
624
+ # with signal_source discriminator per audit_emit.py
625
+ # 5099-5105 contract.
626
+ _audit_emit.emit_generic(
627
+ "mcp_route_advised",
628
+ session_id="",
629
+ task_class="promotion",
630
+ suggested_servers=suggested[:64],
631
+ kill_switch_overrides="",
632
+ signal_source="specialization_promoted",
633
+ )
634
+ except Exception: # pragma: no cover - fail-open
635
+ pass
636
+ return # first-hit wins
637
+ except Exception: # pragma: no cover - fail-open
638
+ return
639
+
640
+
641
+ def _emit_cookbook_pattern_advisory(
642
+ *,
643
+ description: str,
644
+ prompt: str,
645
+ env=None,
646
+ ) -> None:
647
+ """PLAN-092 Wave A.4 (W3.2 SEMI-11) — emit cookbook_pattern_advised
648
+ when a spawn prompt matches one of 4 Anthropic Cookbook pattern
649
+ trigger taxonomies (COOK-P1..P4).
650
+
651
+ Advisory ONLY — UX hint. NEVER blocks, NEVER mutates tool_input.
652
+ Rate-cap via PLAN-088 global _plan088_rate_admit in audit_emit.
653
+ Kill-switch: CEO_COOKBOOK_ADVISOR_ENABLED=0. Privacy invariant:
654
+ 3 fields max (no raw prompt persisted — AC3b).
655
+
656
+ Audit-emit signature mapping (PLAN-090 W3.2 canonical contract):
657
+ pattern_id (COOK-P*) -> top_pattern_keys
658
+ trigger_class (string) -> task_signature (enum-constrained "other")
659
+ bucket (high/med/low) -> pattern_count (3/2/1)
660
+
661
+ NEVER raises.
662
+ """
663
+ try:
664
+ src_env = env if env is not None else os.environ
665
+ if not _COOKBOOK_PATTERNS_AVAILABLE:
666
+ return
667
+ if not _AUDIT_EMIT_AVAILABLE:
668
+ return
669
+ # Kill-switch
670
+ try:
671
+ if hasattr(_cookbook_patterns, "kill_switch_enabled"):
672
+ if not _cookbook_patterns.kill_switch_enabled():
673
+ return
674
+ else:
675
+ val = (src_env.get("CEO_COOKBOOK_ADVISOR_ENABLED") or "1").strip()
676
+ if val in ("0", "false", "False", "no", "off"):
677
+ return
678
+ except Exception: # pragma: no cover
679
+ return
680
+ # Concat description + prompt, cap at 8KiB
681
+ text = ((description or "") + "\n" + (prompt or ""))[: 8 * 1024]
682
+ if not text.strip():
683
+ return
684
+ # Match (first-hit canonical order COOK-P1 -> P4)
685
+ try:
686
+ result = _cookbook_patterns.match_pattern(text)
687
+ except Exception: # pragma: no cover
688
+ return
689
+ if result is None:
690
+ return
691
+ pattern_id, trigger_class, bucket = result
692
+ if not hasattr(_audit_emit, "emit_cookbook_pattern_advised"):
693
+ return
694
+ # Map semantic -> canonical signature
695
+ bucket_to_count = {"high": 3, "medium": 2, "low": 1}
696
+ try:
697
+ _audit_emit.emit_cookbook_pattern_advised(
698
+ task_signature="other",
699
+ top_pattern_keys=str(pattern_id)[:128],
700
+ pattern_count=bucket_to_count.get(str(bucket), 1),
701
+ )
702
+ except Exception: # pragma: no cover
703
+ return
704
+ except Exception: # pragma: no cover - fail-open invariant
705
+ return
706
+
707
+
708
+ def _emit_advisory_safe(
709
+ *,
710
+ archetype: str,
711
+ task_type: str,
712
+ model_recommended: str,
713
+ confidence: float,
714
+ applied_or_skipped: str,
715
+ override_reason: str,
716
+ ) -> None:
717
+ """Wrapper around audit_emit.emit_generic for `model_routing_advised`.
718
+
719
+ Allowlist enforced by audit_emit dispatcher (defense-in-depth). Caller
720
+ only fills the 6 contract fields; ts/session/project added by emit_generic.
721
+
722
+ Codex W1+W2 fix-pack #2: ``confidence`` (float 0..1) is converted at
723
+ emission time to ``confidence_basis_points`` (int 0..1000) — floats
724
+ are forbidden in HMAC-covered fields (canonical_json invariant). The
725
+ in-process API surface still accepts ``confidence: float`` for
726
+ ergonomic continuity; conversion is colocated with the emit call.
727
+ """
728
+ try:
729
+ if not _AUDIT_EMIT_AVAILABLE:
730
+ return
731
+ # Convert float confidence -> int basis-points. NaN / out-of-range
732
+ # collapse to 0 (lower clamp in audit_emit emitter).
733
+ try:
734
+ conf_f = float(confidence)
735
+ except (TypeError, ValueError):
736
+ conf_f = 0.0
737
+ if conf_f != conf_f or conf_f in (float("inf"), float("-inf")):
738
+ conf_f = 0.0
739
+ if conf_f < 0.0:
740
+ conf_f = 0.0
741
+ if conf_f > 1.0:
742
+ conf_f = 1.0
743
+ confidence_bp = int(round(conf_f * 1000.0))
744
+ # Length-bound caller strings (defense-in-depth on adopter drift).
745
+ _audit_emit.emit_generic(
746
+ "model_routing_advised",
747
+ archetype=(archetype or "")[:64],
748
+ task_type=(task_type or "")[:32],
749
+ model_recommended=(model_recommended or "")[:64],
750
+ confidence_basis_points=confidence_bp,
751
+ applied_or_skipped=(applied_or_skipped or "")[:64],
752
+ override_reason=(override_reason or "")[:128],
753
+ )
754
+ except Exception: # pragma: no cover - fail-open
755
+ return
756
+
757
+
758
+ # Persona header patterns — must appear at the START of a line.
759
+ _PERSONA_HEADER_RE = re.compile(
760
+ r"^(?:PERSONA:|## AGENT PROFILE|## PERSONA)",
761
+ flags=re.MULTILINE,
762
+ )
763
+
764
+ # Governance requirement: named spawns must include this literal section.
765
+ _SKILL_CONTENT_MARKER = "## SKILL CONTENT"
766
+
767
+ # PLAN-019 Phase 2B P1-SEC-B — minimum body size (non-whitespace bytes)
768
+ # required between `## SKILL CONTENT` and the next `##` heading / EOF for
769
+ # the marker to count as a real skill-content section. Rejects empty-body,
770
+ # stub-only, or single-line "see file X" shells.
771
+ _SKILL_CONTENT_MIN_BYTES = 256
772
+
773
+ # Regex to find the marker ON ITS OWN LINE (ignoring trailing whitespace).
774
+ _SKILL_CONTENT_MARKER_RE = re.compile(
775
+ r"^## SKILL CONTENT[ \t]*$",
776
+ flags=re.MULTILINE,
777
+ )
778
+
779
+ # Regex to find the next `##`-level heading (to bound the body extraction).
780
+ _NEXT_H2_RE = re.compile(r"^##[ \t]", flags=re.MULTILINE)
781
+
782
+ # Code-fence markers (``` or ~~~ with optional language tag).
783
+ _CODE_FENCE_RE = re.compile(r"^(?:```|~~~)", flags=re.MULTILINE)
784
+
785
+ # HTML comment pair — non-greedy across lines.
786
+ _HTML_COMMENT_RE = re.compile(r"<!--.*?-->", flags=re.DOTALL)
787
+
788
+ # Regex to extract ## SPEC CONTEXT block content (between its header and next
789
+ # ## heading or EOF). Used by _sanitize_spec_context_advisory() below.
790
+ _SPEC_CONTEXT_HEADER_RE = re.compile(
791
+ r"^##[ \t]+SPEC[ \t]+CONTEXT[ \t]*$",
792
+ flags=re.MULTILINE,
793
+ )
794
+
795
+
796
+ def _sanitize_spec_context_advisory(prompt: str, env=None) -> None:
797
+ """PLAN-113 WIRE-DEADMOD / ADR-089 SEC-P0-01 — sanitize ## SPEC CONTEXT
798
+ payload and emit telemetry advisory.
799
+
800
+ Extracts the ## SPEC CONTEXT block from the spawn prompt, runs it
801
+ through spec_context_sanitizer.sanitize(), and emits a
802
+ `spec_context_sanitized` audit-emit event so sentinel violations,
803
+ truncation, and control-char counts are observable.
804
+
805
+ ADVISORY ONLY — never blocks the spawn. Fail-open on any exception.
806
+ Kill-switch: CEO_SPEC_CTX_SANITIZER_ENABLED=0.
807
+ """
808
+ try:
809
+ if not _SPEC_CTX_SANITIZER_AVAILABLE or not _AUDIT_EMIT_AVAILABLE:
810
+ return
811
+ src_env = env if env is not None else os.environ
812
+ val = (src_env.get("CEO_SPEC_CTX_SANITIZER_ENABLED") or "1").strip()
813
+ if val in ("0", "false", "False", "no", "off"):
814
+ return
815
+ if not prompt:
816
+ return
817
+ # Extract ## SPEC CONTEXT block content
818
+ m = _SPEC_CONTEXT_HEADER_RE.search(prompt)
819
+ if m is None:
820
+ return # no SPEC CONTEXT block — nothing to sanitize
821
+ block_start = m.end()
822
+ next_h2 = _NEXT_H2_RE.search(prompt, block_start)
823
+ block_end = next_h2.start() if next_h2 else len(prompt)
824
+ spec_payload = prompt[block_start:block_end]
825
+
826
+ result = _spec_ctx_sanitizer.sanitize(spec_payload)
827
+
828
+ # Emit telemetry (advisory only; sentinel violations logged for CEO)
829
+ try:
830
+ _audit_emit.emit_generic(
831
+ "spec_context_sanitized",
832
+ original_bytes=result.original_bytes,
833
+ cleaned_bytes=result.cleaned_bytes,
834
+ truncated=1 if result.truncated else 0,
835
+ sentinel_violations=len(result.sentinel_violations),
836
+ control_chars_stripped=result.control_chars_stripped,
837
+ bidi_zw_chars_stripped=result.bidi_zw_chars_stripped,
838
+ # PLAN-133 A2 — surface the Tag-block count too (allowlisted in
839
+ # audit_emit §7d). Harmless if the SanitizeResult lacks the field
840
+ # (getattr default 0) so this stays import-order tolerant.
841
+ tag_chars_stripped=getattr(result, "tag_chars_stripped", 0),
842
+ header_escape_count=result.header_escape_count,
843
+ )
844
+ except Exception: # pragma: no cover - fail-open
845
+ pass
846
+ except Exception: # pragma: no cover - fail-open
847
+ return
848
+
849
+
850
+ def _enforce_spec_context_unicode(prompt: str, env=None):
851
+ """PLAN-133 A2 — fail-CLOSED invisible-unicode guard on the spawn prompt.
852
+
853
+ Scans the WHOLE spawn prompt (not just the ## SPEC CONTEXT block — an attacker
854
+ can smuggle Tag-block/bidi chars anywhere in the named-spawn body) for invisible
855
+ /smuggling unicode (control / bidi / zero-width / U+E0000-E007F Tag-block).
856
+
857
+ Default-OFF (doctrine #1): the BLOCK fires only when CEO_UNICODE_HARDBLOCK=='1'
858
+ in the import-time trusted_env snapshot. Otherwise advisory — the breadcrumb is
859
+ emitted with enforced=0 (measure-first) and None is returned (no block).
860
+
861
+ Returns a block-reason string when enforced AND a detection fires, else None.
862
+ Fail-OPEN on any infra exception (never blocks the session on a scanner bug).
863
+ Master kill: CEO_SOTA_DISABLE=1 forces advisory.
864
+ """
865
+ try:
866
+ if not _SPEC_CTX_SANITIZER_AVAILABLE:
867
+ return None
868
+ src_env = env if env is not None else os.environ
869
+ if not prompt:
870
+ return None
871
+ # Default-OFF flag, read from the trusted_env snapshot when available so a
872
+ # late-set value can't toggle enforcement mid-process. Master kill wins.
873
+ if (src_env.get("CEO_SOTA_DISABLE") or "").strip() == "1":
874
+ enforce = False
875
+ else:
876
+ enforce = _unicode_hardblock_enforced(src_env)
877
+
878
+ result = _spec_ctx_sanitizer.sanitize(prompt)
879
+ count = _spec_ctx_sanitizer.invisible_unicode_count(result)
880
+ if count <= 0:
881
+ return None # clean — nothing to do
882
+
883
+ unicode_class = _spec_ctx_sanitizer.classify_invisible_unicode(result)
884
+
885
+ # Emit the closed-enum breadcrumb on BOTH advisory and enforced paths
886
+ # (the denominator must be real from day one — no 0/0/0 trap).
887
+ if _AUDIT_EMIT_AVAILABLE:
888
+ try:
889
+ _audit_emit.emit_generic(
890
+ "invisible_unicode_blocked",
891
+ surface="spawn",
892
+ unicode_class=unicode_class,
893
+ char_count=int(count),
894
+ enforced=1 if enforce else 0,
895
+ )
896
+ except Exception: # pragma: no cover - fail-open
897
+ pass
898
+
899
+ if not enforce:
900
+ return None # advisory only
901
+
902
+ return (
903
+ "GOVERNANCE: invisible_unicode_blocked: the spawn prompt contains "
904
+ f"{count} invisible/smuggling character(s) (class={unicode_class}: "
905
+ "control / bidi / zero-width / Unicode-Tags-block). These are "
906
+ "removed by the sanitizer and rejected fail-CLOSED before review. "
907
+ "Remove the hidden characters and re-spawn. To run advisory-only, "
908
+ "unset CEO_UNICODE_HARDBLOCK."
909
+ )
910
+ except Exception: # pragma: no cover - fail-open invariant
911
+ return None
912
+
913
+
914
+ def _unicode_hardblock_enforced(src_env) -> bool:
915
+ """True iff CEO_UNICODE_HARDBLOCK=='1' (PLAN-133 A2). Prefers the trusted_env
916
+ snapshot; falls back to the passed env dict. Pure; never raises."""
917
+ try:
918
+ from _lib import trusted_env as _te # noqa: E402
919
+ val = _te.get_trusted("CEO_UNICODE_HARDBLOCK")
920
+ if val is not None:
921
+ return (val or "").strip() == "1"
922
+ except Exception: # pragma: no cover
923
+ pass
924
+ return (src_env.get("CEO_UNICODE_HARDBLOCK") or "").strip() == "1"
925
+
926
+
927
+ def _emit_spawn_confidence_advisory(
928
+ *,
929
+ action_type: str,
930
+ is_named_spawn: bool,
931
+ env=None,
932
+ ) -> None:
933
+ """PLAN-113 WIRE-DEADMOD — confidence_labels (PLAN-083 Wave 1.10).
934
+
935
+ Classifies the spawn as an action and emits `spawn_confidence_advisory`
936
+ so recommender / receipt formatter surfaces have a hook-level confidence
937
+ signal for governance dashboards.
938
+
939
+ ADVISORY ONLY — never blocks. Fail-open on any exception.
940
+ Kill-switch: CEO_SPAWN_CONFIDENCE_ENABLED=0.
941
+ """
942
+ try:
943
+ if not _CONFIDENCE_LABELS_AVAILABLE or not _AUDIT_EMIT_AVAILABLE:
944
+ return
945
+ src_env = env if env is not None else os.environ
946
+ val = (src_env.get("CEO_SPAWN_CONFIDENCE_ENABLED") or "1").strip()
947
+ if val in ("0", "false", "False", "no", "off"):
948
+ return
949
+
950
+ ctx = {"canonical": False}
951
+ conf = _confidence_labels.classify(action_type, ctx)
952
+ marker = _confidence_labels.as_emoji_free_marker(conf)
953
+
954
+ try:
955
+ _audit_emit.emit_generic(
956
+ "spawn_confidence_advisory",
957
+ action_type=(action_type or "")[:32],
958
+ confidence_level=(conf.level or "")[:32],
959
+ confidence_marker=(marker or "")[:32],
960
+ reason_code=(conf.reason_code or "")[:64],
961
+ is_named_spawn=1 if is_named_spawn else 0,
962
+ )
963
+ except Exception: # pragma: no cover - fail-open
964
+ pass
965
+ except Exception: # pragma: no cover - fail-open
966
+ return
967
+
968
+
969
+ def _strip_fenced_and_comments(text: str) -> str:
970
+ """Return a version of ``text`` with HTML comments removed and
971
+ fenced-code-block content masked so marker/body searches cannot
972
+ match inside them (P1-SEC-B bypass hardening)."""
973
+ stripped = _HTML_COMMENT_RE.sub("", text)
974
+ out_parts = []
975
+ in_fence = False
976
+ for line in stripped.splitlines(keepends=True):
977
+ if _CODE_FENCE_RE.match(line):
978
+ in_fence = not in_fence
979
+ out_parts.append(line)
980
+ continue
981
+ if in_fence:
982
+ out_parts.append("".join(
983
+ "\n" if c == "\n" else " " for c in line
984
+ ))
985
+ else:
986
+ out_parts.append(line)
987
+ return "".join(out_parts)
988
+
989
+
990
+ def _has_skill_content(prompt: str) -> bool:
991
+ """True iff ``prompt`` contains a real `## SKILL CONTENT` section.
992
+
993
+ Enforces four bypass-resistant checks (P1-SEC-B):
994
+ 1. Marker on own line (not inline narrative).
995
+ 2. Marker NOT inside HTML comment.
996
+ 3. Marker NOT inside fenced code block.
997
+ 4. >=_SKILL_CONTENT_MIN_BYTES non-ws bytes between marker and next ##.
998
+ """
999
+ if not prompt:
1000
+ return False
1001
+ return _has_skill_content_sanitized(_strip_fenced_and_comments(prompt))
1002
+
1003
+
1004
+ def _has_skill_content_sanitized(sanitized: str) -> bool:
1005
+ """Shared ``## SKILL CONTENT`` check against already-sanitized text.
1006
+
1007
+ PLAN-023 Phase F (F-perf-003) — split out so ``decide`` and other
1008
+ callers can share a single ``_strip_fenced_and_comments`` pass
1009
+ rather than re-stripping inside each sub-check.
1010
+ """
1011
+ match = _SKILL_CONTENT_MARKER_RE.search(sanitized)
1012
+ if match is None:
1013
+ return False
1014
+ body_start = match.end()
1015
+ next_heading = _NEXT_H2_RE.search(sanitized, body_start)
1016
+ body_end = next_heading.start() if next_heading else len(sanitized)
1017
+ body = sanitized[body_start:body_end]
1018
+ non_ws_bytes = sum(1 for c in body if not c.isspace())
1019
+ return non_ws_bytes >= _SKILL_CONTENT_MIN_BYTES
1020
+
1021
+
1022
+ # =============================================================================
1023
+ # PLAN-020 Phase 2 — `## SKILL REFERENCE` additive sentinel (ADR-051)
1024
+ # =============================================================================
1025
+ #
1026
+ # Parallel accept-path. Does NOT replace `_has_skill_content`. Hardened
1027
+ # against 14 attack classes documented in ADR-051 §Threat model.
1028
+ #
1029
+ # Hook recognition order in decide():
1030
+ # 1. If prompt has `## SKILL REFERENCE` → strict validation, fail-CLOSED.
1031
+ # 2. Else if prompt has `## SKILL CONTENT` → inline path (existing P1-SEC-B).
1032
+ # 3. Else block with the legacy `missing_skill_content` reason.
1033
+
1034
+ # Module-level toggles (Phase revert = single-line flip to False).
1035
+ ENABLE_NATIVE_SUBAGENTS = True
1036
+ ENABLE_SKILL_REFERENCE_MODE = True
1037
+
1038
+ # Reference sentinel header — must be on own line, not inside fences.
1039
+ _SKILL_REFERENCE_HEADER_RE = re.compile(
1040
+ r"^##[ \t]+SKILL[ \t]+REFERENCE[ \t]*$",
1041
+ flags=re.MULTILINE,
1042
+ )
1043
+
1044
+ # Reference body line: `@<path> sha256=<64-hex>`. Path may be relative.
1045
+ _SKILL_REFERENCE_LINE_RE = re.compile(
1046
+ r"^@(?P<path>\S+)[ \t]+sha256=(?P<hash>[0-9a-f]{64})\b",
1047
+ flags=re.MULTILINE,
1048
+ )
1049
+
1050
+ # Caps + thresholds (ADR-051 §Synchronous validation).
1051
+ _SKILL_REFERENCE_MAX_BYTES = 1_048_576 # 1 MiB DoS cap
1052
+ _SKILL_REFERENCE_MIN_BODY_BYTES = 512 # distinct from inline 256 floor
1053
+
1054
+ # 12 telemetry reason codes (ADR-051 §Telemetry reason codes).
1055
+ REASON_REFERENCE_MISSING = "reference_missing"
1056
+ REASON_REFERENCE_UNSAFE_PATH = "reference_unsafe_path"
1057
+ REASON_REFERENCE_HASH_MISMATCH = "reference_hash_mismatch"
1058
+ REASON_REFERENCE_SYMLINK_REFUSED = "reference_symlink_refused"
1059
+ REASON_REFERENCE_TOO_LARGE = "reference_too_large"
1060
+ REASON_REFERENCE_REDACTION_HIT = "reference_redaction_hit"
1061
+ REASON_REFERENCE_WRONG_FILENAME = "reference_wrong_filename"
1062
+ REASON_REFERENCE_MISSING_FRONTMATTER = "reference_missing_frontmatter"
1063
+ REASON_REFERENCE_BYTE_FLOOR_UNDERFLOW = "reference_byte_floor_underflow"
1064
+ REASON_REFERENCE_UNICODE_NORMALIZATION_MISMATCH = (
1065
+ "reference_unicode_normalization_mismatch"
1066
+ )
1067
+ REASON_REFERENCE_OUTSIDE_SKILLS_ROOT = "reference_outside_skills_root"
1068
+ REASON_MISSING_SKILL_CONTENT = "missing_skill_content" # legacy/inline
1069
+
1070
+ # Session 32 BUGFIX — _lib.redact.redact_secrets whitespace-collapses
1071
+ # always; equality check with original text is not a valid "secret
1072
+ # detected" signal. Instead we look for specific replacement tokens
1073
+ # that `_redact._PATTERNS` inserts when a secret matches.
1074
+ _SECRET_TOKENS_IN_OUTPUT = frozenset({
1075
+ "[JWT]",
1076
+ "[API_KEY]",
1077
+ "[GITHUB_PAT]",
1078
+ "[AWS_KEY]",
1079
+ "[TOKEN]",
1080
+ "[URL_WITH_CREDS]",
1081
+ "[HEX_SECRET]",
1082
+ "[REDACTED]",
1083
+ "[SLACK_BOT]",
1084
+ "[STRIPE_KEY]",
1085
+ "[GOOGLE_REFRESH]",
1086
+ # PLAN-024 F-hooks-001 P0 fix: redact.py replaces the PEM header
1087
+ # with "[SSH_PRIVATE_KEY_HEADER]" — matching raw "-----BEGIN" will
1088
+ # never fire after redaction. Check the replacement token instead.
1089
+ "[SSH_PRIVATE_KEY_HEADER]",
1090
+ })
1091
+
1092
+
1093
+ def _is_enabled(env_var: str, default: bool, env: Optional[dict] = None) -> bool:
1094
+ """Live read of an env-var toggle with a module default fallback.
1095
+
1096
+ Allows runtime opt-out via env (`CEO_SKILL_REFERENCE_MODE=0`,
1097
+ `CEO_NATIVE_SUBAGENTS=0`) without restarting Claude Code.
1098
+ `CEO_SOTA_DISABLE=1` is the master kill — overrides everything.
1099
+ """
1100
+ src_env = env if env is not None else os.environ
1101
+ if (src_env.get("CEO_SOTA_DISABLE") or "").strip() == "1":
1102
+ return False
1103
+ raw = (src_env.get(env_var) or "").strip()
1104
+ if raw == "":
1105
+ return default
1106
+ return raw not in ("0", "false", "FALSE", "False", "no", "off")
1107
+
1108
+
1109
+ def _has_valid_frontmatter_with_name(text: str) -> bool:
1110
+ """Stdlib-only frontmatter parser (no PyYAML).
1111
+
1112
+ Returns True iff the file starts with `---` on first line, has a
1113
+ closing `---` line, and has at least one `name: <value>` line.
1114
+ """
1115
+ if not text.startswith("---\n") and not text.startswith("---\r\n"):
1116
+ return False
1117
+ body_start = 4 if text.startswith("---\n") else 5
1118
+ closing = text.find("\n---", body_start)
1119
+ if closing == -1:
1120
+ return False
1121
+ fm_block = text[body_start:closing]
1122
+ return bool(re.search(r"^name:\s*\S+", fm_block, flags=re.MULTILINE))
1123
+
1124
+
1125
+ def _skill_ref_parse_sentinel(
1126
+ sanitized: str,
1127
+ ) -> Tuple[Optional[Tuple[bool, Optional[str], Optional[str]]], Optional[str], Optional[str]]:
1128
+ """Sub-checks 1-2: locate `## SKILL REFERENCE` header + body.
1129
+
1130
+ Returns ``(error_tuple, raw_path, expected_hash)``. On failure,
1131
+ ``error_tuple`` is populated and the other fields are None.
1132
+ """
1133
+ if not _SKILL_REFERENCE_HEADER_RE.search(sanitized):
1134
+ return (
1135
+ (False, REASON_REFERENCE_MISSING, "no `## SKILL REFERENCE` header"),
1136
+ None,
1137
+ None,
1138
+ )
1139
+ body_match = _SKILL_REFERENCE_LINE_RE.search(sanitized)
1140
+ if not body_match:
1141
+ return (
1142
+ (
1143
+ False,
1144
+ REASON_REFERENCE_MISSING,
1145
+ "header found but no `@<path> sha256=<hex>` body",
1146
+ ),
1147
+ None,
1148
+ None,
1149
+ )
1150
+ return None, body_match.group("path"), body_match.group("hash").lower()
1151
+
1152
+
1153
+ def _skill_ref_resolve_path(
1154
+ raw_path: str,
1155
+ repo_root: Path,
1156
+ ) -> Tuple[Optional[Tuple[bool, Optional[str], Optional[str]]], Optional[Path], Optional[Path]]:
1157
+ """Sub-checks 3-5: resolve path, confirm under skills root, filename = SKILL.md, not symlink.
1158
+
1159
+ Returns ``(error_tuple, candidate, resolved)``. On failure, the
1160
+ latter two are None.
1161
+ """
1162
+ skills_root = (repo_root / ".claude" / "skills").resolve()
1163
+ candidate = Path(raw_path)
1164
+ if not candidate.is_absolute():
1165
+ candidate = repo_root / raw_path
1166
+ try:
1167
+ resolved = candidate.resolve(strict=True)
1168
+ except (FileNotFoundError, OSError) as exc:
1169
+ return (
1170
+ (False, REASON_REFERENCE_MISSING, f"resolve failed: {exc}"),
1171
+ None,
1172
+ None,
1173
+ )
1174
+ try:
1175
+ resolved.relative_to(skills_root)
1176
+ except ValueError:
1177
+ return (
1178
+ (
1179
+ False,
1180
+ REASON_REFERENCE_OUTSIDE_SKILLS_ROOT,
1181
+ f"{resolved} not under {skills_root}",
1182
+ ),
1183
+ None,
1184
+ None,
1185
+ )
1186
+ if resolved.name != "SKILL.md":
1187
+ return (
1188
+ (
1189
+ False,
1190
+ REASON_REFERENCE_WRONG_FILENAME,
1191
+ f"filename is {resolved.name!r}, expected 'SKILL.md'",
1192
+ ),
1193
+ None,
1194
+ None,
1195
+ )
1196
+ if candidate.is_symlink():
1197
+ return (
1198
+ (
1199
+ False,
1200
+ REASON_REFERENCE_SYMLINK_REFUSED,
1201
+ f"{candidate} is a symlink",
1202
+ ),
1203
+ None,
1204
+ None,
1205
+ )
1206
+ return None, candidate, resolved
1207
+
1208
+
1209
+ def _skill_ref_validate_unicode(
1210
+ candidate: Path,
1211
+ ) -> Optional[Tuple[bool, Optional[str], Optional[str]]]:
1212
+ """Sub-check 6: NFC unicode normalization. Returns error_tuple or None."""
1213
+ raw_str = str(candidate)
1214
+ nfc_str = unicodedata.normalize("NFC", raw_str)
1215
+ if nfc_str != raw_str:
1216
+ return (
1217
+ False,
1218
+ REASON_REFERENCE_UNICODE_NORMALIZATION_MISMATCH,
1219
+ "path not NFC-normalized",
1220
+ )
1221
+ return None
1222
+
1223
+
1224
+ def _skill_ref_read_bounded(
1225
+ resolved: Path,
1226
+ ) -> Tuple[Optional[Tuple[bool, Optional[str], Optional[str]]], Optional[bytes], Optional[str]]:
1227
+ """Sub-checks 7-8: size cap + body floor on non-ws bytes.
1228
+
1229
+ Returns ``(error_tuple, content_bytes, decoded_text)``.
1230
+ """
1231
+ try:
1232
+ size = resolved.stat().st_size
1233
+ except OSError as exc:
1234
+ return (
1235
+ (False, REASON_REFERENCE_MISSING, f"stat failed: {exc}"),
1236
+ None,
1237
+ None,
1238
+ )
1239
+ if size > _SKILL_REFERENCE_MAX_BYTES:
1240
+ return (
1241
+ (
1242
+ False,
1243
+ REASON_REFERENCE_TOO_LARGE,
1244
+ f"{size} bytes > {_SKILL_REFERENCE_MAX_BYTES} cap",
1245
+ ),
1246
+ None,
1247
+ None,
1248
+ )
1249
+ try:
1250
+ content_bytes = resolved.read_bytes()
1251
+ except OSError as exc:
1252
+ return (
1253
+ (False, REASON_REFERENCE_MISSING, f"read failed: {exc}"),
1254
+ None,
1255
+ None,
1256
+ )
1257
+ try:
1258
+ text = content_bytes.decode("utf-8")
1259
+ except UnicodeDecodeError as exc:
1260
+ return (
1261
+ (False, REASON_REFERENCE_MISSING_FRONTMATTER, f"decode: {exc}"),
1262
+ None,
1263
+ None,
1264
+ )
1265
+ non_ws = sum(1 for c in text if not c.isspace())
1266
+ if non_ws < _SKILL_REFERENCE_MIN_BODY_BYTES:
1267
+ return (
1268
+ (
1269
+ False,
1270
+ REASON_REFERENCE_BYTE_FLOOR_UNDERFLOW,
1271
+ f"{non_ws} non-ws bytes < {_SKILL_REFERENCE_MIN_BODY_BYTES} floor",
1272
+ ),
1273
+ None,
1274
+ None,
1275
+ )
1276
+ return None, content_bytes, text
1277
+
1278
+
1279
+ def _skill_ref_validate_frontmatter(
1280
+ text: str,
1281
+ ) -> Optional[Tuple[bool, Optional[str], Optional[str]]]:
1282
+ """Sub-check 9: YAML frontmatter parseable + has `name:` key."""
1283
+ if not _has_valid_frontmatter_with_name(text):
1284
+ return (
1285
+ False,
1286
+ REASON_REFERENCE_MISSING_FRONTMATTER,
1287
+ "missing or malformed YAML frontmatter / no `name:` key",
1288
+ )
1289
+ return None
1290
+
1291
+
1292
+ def _skill_ref_validate_hash_and_redact(
1293
+ content_bytes: bytes,
1294
+ text: str,
1295
+ expected_hash: str,
1296
+ ) -> Optional[Tuple[bool, Optional[str], Optional[str]]]:
1297
+ """Sub-checks 10-11: SHA-256 match + redaction scan.
1298
+
1299
+ Per PLAN-025 F-perf-006: SHA-256 on reference-mode skill content
1300
+ costs ~1ms for a 5KB skill per invocation. Acceptable at current
1301
+ spawn volumes.
1302
+
1303
+ Per Session 32 bugfix: `_redact.redact_secrets` whitespace-collapses
1304
+ output, so direct string compare would always mismatch. Instead
1305
+ we check for the specific secret-indicator tokens that only appear
1306
+ when a pattern matched.
1307
+ """
1308
+ actual_hash = hashlib.sha256(content_bytes).hexdigest()
1309
+ if actual_hash != expected_hash:
1310
+ return (
1311
+ False,
1312
+ REASON_REFERENCE_HASH_MISMATCH,
1313
+ f"expected {expected_hash[:8]}..., got {actual_hash[:8]}...",
1314
+ )
1315
+ redacted = _redact.redact_secrets(text)
1316
+ if any(tok in redacted for tok in _SECRET_TOKENS_IN_OUTPUT):
1317
+ return (
1318
+ False,
1319
+ REASON_REFERENCE_REDACTION_HIT,
1320
+ "secret pattern detected in skill content",
1321
+ )
1322
+ return None
1323
+
1324
+
1325
+ def _validate_skill_reference(
1326
+ prompt: str,
1327
+ repo_root: Optional[Path] = None,
1328
+ ) -> Tuple[bool, Optional[str], Optional[str]]:
1329
+ """Validate `## SKILL REFERENCE` sentinel via 11 synchronous sub-checks.
1330
+
1331
+ Refactored (PLAN-050 Phase 1b F-02-03-e) into 6 named helpers for
1332
+ readability + per-check test coverage. Same contract: fail-CLOSED
1333
+ on any failure, sub-check order preserved from ADR-051 §Synchronous
1334
+ validation (sub-check 11 = redaction scan added Session 32;
1335
+ PLAN-024 F-hooks-003 doc drift close).
1336
+ """
1337
+ if not prompt or not isinstance(prompt, str):
1338
+ return False, REASON_REFERENCE_MISSING, "empty prompt"
1339
+
1340
+ sanitized = _strip_fenced_and_comments(prompt)
1341
+
1342
+ err, raw_path, expected_hash = _skill_ref_parse_sentinel(sanitized)
1343
+ if err:
1344
+ return err
1345
+
1346
+ if repo_root is None:
1347
+ repo_root = Path(
1348
+ os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()
1349
+ )
1350
+
1351
+ err, candidate, resolved = _skill_ref_resolve_path(raw_path, repo_root)
1352
+ if err:
1353
+ return err
1354
+
1355
+ err = _skill_ref_validate_unicode(candidate)
1356
+ if err:
1357
+ return err
1358
+
1359
+ err, content_bytes, text = _skill_ref_read_bounded(resolved)
1360
+ if err:
1361
+ return err
1362
+
1363
+ err = _skill_ref_validate_frontmatter(text)
1364
+ if err:
1365
+ return err
1366
+
1367
+ err = _skill_ref_validate_hash_and_redact(
1368
+ content_bytes, text, expected_hash
1369
+ )
1370
+ if err:
1371
+ return err
1372
+
1373
+ return True, None, None
1374
+
1375
+
1376
+ def _has_skill_reference(
1377
+ prompt: str,
1378
+ repo_root: Optional[Path] = None,
1379
+ ) -> bool:
1380
+ """Backward-compat wrapper: True iff reference passes all sub-checks."""
1381
+ ok, _, _ = _validate_skill_reference(prompt, repo_root=repo_root)
1382
+ return ok
1383
+
1384
+
1385
+ # =============================================================================
1386
+ # PLAN-020 Phase 3 — `/effort` token rejection in spawn prompts
1387
+ # =============================================================================
1388
+ #
1389
+ # `/effort` hints (low/default/high/max + `ultrathink` keyword) are
1390
+ # CEO-only. They MUST NOT appear in spawn prompts (sub-agents inherit
1391
+ # their thinking budget from Anthropic defaults). PLAN-020 §4 Phase 3
1392
+ # §scope clause (QA must-fix #7).
1393
+
1394
+ _EFFORT_TOKEN_RE = re.compile(
1395
+ r"(?:^|[^\w/])/effort(?:[ \t]+(?:low|default|high|max))?\b",
1396
+ flags=re.IGNORECASE,
1397
+ )
1398
+
1399
+
1400
+ def _has_effort_token(prompt: str) -> bool:
1401
+ """True iff prompt contains a `/effort [tier]` slash-command token."""
1402
+ if not prompt:
1403
+ return False
1404
+ sanitized = _strip_fenced_and_comments(prompt)
1405
+ return bool(_EFFORT_TOKEN_RE.search(sanitized))
1406
+
1407
+
1408
+
1409
+ # ---------------------------------------------------------------------------
1410
+ # PLAN-045 F-10-04 — spawn-prompt secret-scan pre-dispatch
1411
+ # ---------------------------------------------------------------------------
1412
+ # Spawn prompts are the #1 vector for accidentally shipping secrets into a
1413
+ # sub-agent's context. A CEO that copy-pastes a `.env` into a task prompt
1414
+ # or builds a prompt that interpolates an env var leaks the secret to
1415
+ # whatever model the sub-agent uses AND to the audit log (spawn prompts
1416
+ # are recorded for governance).
1417
+ #
1418
+ # Reject spawns whose prompt contains any of 6 high-signal secret shapes.
1419
+ # Each shape is a narrow regex to avoid false-positives on docs / test
1420
+ # fixtures (prefix-bound + entropy-adjacent). Audit emits
1421
+ # `veto_triggered(reason_code=spawn_prompt_contains_secret)` on reject.
1422
+ #
1423
+ # Kill-switch: `CEO_SPAWN_SECRET_SCAN=0` for the rare case where a prompt
1424
+ # legitimately contains a long opaque id that trips the scan; Owner-only
1425
+ # escape logged via veto_triggered(reason_code=spawn_secret_scan_bypassed).
1426
+ _SPAWN_SECRET_PATTERNS: List[Tuple[str, re.Pattern[str]]] = [
1427
+ # AWS access key ID (AKIA prefix, 16 uppercase alnum after).
1428
+ ("aws_access_key_id", re.compile(r"\bAKIA[0-9A-Z]{16}\b")),
1429
+ # Generic AWS secret access key.
1430
+ (
1431
+ "aws_secret_access_key",
1432
+ re.compile(
1433
+ r"\b(?:aws[_-]?secret[_-]?access[_-]?key)\b\s*[:=]\s*"
1434
+ r"[\"\']?[A-Za-z0-9/+=]{40}[\"\']?",
1435
+ re.IGNORECASE,
1436
+ ),
1437
+ ),
1438
+ # Stripe live/test secret key.
1439
+ ("stripe_secret_key", re.compile(r"\bsk_(?:live|test)_[0-9A-Za-z]{24,}\b")),
1440
+ # GitHub personal access token.
1441
+ ("github_pat", re.compile(r"\bghp_[0-9A-Za-z]{36,}\b")),
1442
+ # OpenAI API key.
1443
+ ("openai_api_key", re.compile(r"\bsk-[A-Za-z0-9]{48,}\b")),
1444
+ # PEM private key preamble.
1445
+ (
1446
+ "pem_private_key",
1447
+ re.compile(
1448
+ r"-----BEGIN (?:RSA |EC |OPENSSH |PGP |DSA )?PRIVATE KEY-----"
1449
+ ),
1450
+ ),
1451
+ ]
1452
+
1453
+
1454
+
1455
+ def _inject_terse_marker(prompt: str, subagent_type: str) -> str:
1456
+ """Inject ## TERSE-MODE marker per PLAN-047 Phase 2."""
1457
+ if os.environ.get("CEO_TERSE_MODE", "0") != "1":
1458
+ return prompt
1459
+ veto_roles = {"code-reviewer", "security-engineer", "qa-architect", "compliance-specialist"}
1460
+ if (subagent_type or "") in veto_roles:
1461
+ return prompt + "\n\n## TERSE-MODE-DISABLED — VETO role requires full-prose rationale.\n"
1462
+ return prompt + "\n\n## TERSE-MODE — fragments OK in exploratory flows; never truncate code or numbers.\n"
1463
+
1464
+ def _validate_spawn_prompt_has_no_secrets(
1465
+ prompt: str,
1466
+ ) -> Tuple[bool, Optional[str], Optional[str]]:
1467
+ """Return (ok, reason_code, detail) — ok=False means BLOCK spawn.
1468
+
1469
+ Short-circuits with ok=True when `CEO_SPAWN_SECRET_SCAN=0` is set
1470
+ (Owner bypass; any non-"0" value enforces).
1471
+ """
1472
+ # Session 75 Codex Finding 4 closure (Owner D4 staged rollout):
1473
+ # default OFF, opt-in via CEO_SPAWN_SECRET_SCAN=1. Without the env
1474
+ # var (or with =0), function returns ok=True without scanning. Soak
1475
+ # protocol: ≥3 sessions of opt-in usage with FPR<1%% before any
1476
+ # default flip ADR. Until then, this is a wire-only landing.
1477
+ if os.environ.get("CEO_SPAWN_SECRET_SCAN", "0") != "1":
1478
+ return True, None, None
1479
+ if not prompt:
1480
+ return True, None, None
1481
+ for family, pattern in _SPAWN_SECRET_PATTERNS:
1482
+ m = pattern.search(prompt)
1483
+ if m:
1484
+ return (
1485
+ False,
1486
+ "spawn_prompt_contains_secret",
1487
+ f"family={family} prompt_len={len(prompt)}",
1488
+ )
1489
+ return True, None, None
1490
+
1491
+
1492
+ @dataclass
1493
+ class Decision:
1494
+ """Typed result of the governance check."""
1495
+
1496
+ allow: bool
1497
+ reason: Optional[str] = None
1498
+
1499
+ def to_json(self) -> str:
1500
+ if self.allow:
1501
+ return json.dumps({}, ensure_ascii=False) # schema-compliant allow
1502
+ return json.dumps(
1503
+ {"decision": "block", "reason": self.reason or ""},
1504
+ ensure_ascii=False,
1505
+ )
1506
+
1507
+
1508
+ # Sprint 5 Phase 7 (ADR-010) — Architect recursion guard.
1509
+ # When CEO_ARCHITECT_ACTIVE=1 is in the env, any spawn whose
1510
+ # description/prompt names "Agent Architect" is BLOCKED. Prevents
1511
+ # meta-agent recursion (Architect-spawning-Architect).
1512
+ _ARCHITECT_NAME_RE = re.compile(
1513
+ r"\bAgent\s+Architect\b",
1514
+ flags=re.IGNORECASE,
1515
+ )
1516
+
1517
+
1518
+ # ---------------------------------------------------------------------------
1519
+ # PLAN-133 E3 — Per-spawn tool scoping + depth/overlap rails.
1520
+ # ---------------------------------------------------------------------------
1521
+ # Ports the Goose recipe "declared capability scope + one-level recursion +
1522
+ # file partition" idea, re-implemented from scratch in stdlib (rite §2). All
1523
+ # three rails are ADVISORY by default (emit only) and become BLOCKING only
1524
+ # under their per-rail env flag. NEVER raises (fail-open per CLAUDE.md §5).
1525
+ #
1526
+ # Rail 1 — Tool allow-list: a spawn profile MAY declare
1527
+ # ## TOOL ALLOW-LIST
1528
+ # - Read
1529
+ # - Edit
1530
+ # - Bash
1531
+ # When declared AND the prompt's ## TASK/## RESTRICTIONS text requests a tool
1532
+ # OUTSIDE that set (heuristic keyword scan, bounded), emit/block. A profile
1533
+ # with NO ## TOOL ALLOW-LIST block is unrestricted (back-compat: silent allow).
1534
+ #
1535
+ # Rail 2 — Depth fence: a sub-agent that itself emits a NAMED spawn is a
1536
+ # depth-2 spawn. The harness Agent payload has no native depth field, so we
1537
+ # read the advisory marker the CEO injects into a delegated prompt
1538
+ # (## SUBAGENT-CONTEXT depth=N) AND the env breadcrumb CEO_SPAWN_DEPTH. If
1539
+ # EITHER says depth>=1 AND this spawn is itself NAMED, it is depth-over-one.
1540
+ #
1541
+ # Rail 3 — FILE ASSIGNMENT overlap: parse this spawn's `CAN edit:` file list
1542
+ # from its ## FILE ASSIGNMENT block, compare against the `CAN edit:` lists of
1543
+ # other spawns recorded in the audit log within a short concurrency window
1544
+ # (same session, same plan). Any shared concrete path = clobber risk.
1545
+
1546
+ # --- Rail 1: tool allow-list ----------------------------------------------
1547
+ _TOOL_ALLOWLIST_HEADER_RE = re.compile(
1548
+ r"^##[ \t]+TOOL[ \t]+ALLOW-?LIST[ \t]*$",
1549
+ flags=re.MULTILINE,
1550
+ )
1551
+ # A bullet line inside the block: "- Read" / "* Bash" / "Read,Edit".
1552
+ _TOOL_BULLET_RE = re.compile(
1553
+ r"^[ \t]*[-*][ \t]*([A-Za-z][A-Za-z0-9_/ ,]+)$",
1554
+ flags=re.MULTILINE,
1555
+ )
1556
+ # Canonical Claude Code tool names we recognize (closed set — anything not
1557
+ # here in a request is ignored, NOT blocked; we only gate KNOWN tools so an
1558
+ # unknown token can never cause a false block).
1559
+ _KNOWN_TOOL_NAMES = frozenset({
1560
+ "read", "edit", "write", "multiedit", "bash", "glob", "grep",
1561
+ "webfetch", "websearch", "task", "agent", "notebookedit",
1562
+ })
1563
+ # Heuristic: which tools a spawn is REQUESTING, inferred from prompt text.
1564
+ # First-hit, bounded scan. Conservative — only fires on high-signal verbs so
1565
+ # a profile that simply mentions a word does not trip the rail.
1566
+ _TOOL_REQUEST_HINTS: Tuple[Tuple[str, Tuple[str, ...]], ...] = (
1567
+ ("bash", ("run the command", "run `", "execute the script", "shell out",
1568
+ "git commit", "git push", "npm install", "curl ", "subprocess")),
1569
+ ("write", ("create the file", "write a new file", "write the file")),
1570
+ ("edit", ("edit the file", "modify the file", "apply the patch")),
1571
+ ("webfetch", ("fetch the url", "download from http", "fetch http")),
1572
+ ("websearch", ("search the web", "web search")),
1573
+ )
1574
+ _TOOL_SCAN_WINDOW = 8 * 1024 # bytes of prompt scanned for tool hints
1575
+
1576
+ # --- Rail 2: depth fence ---------------------------------------------------
1577
+ _SUBAGENT_DEPTH_MARKER_RE = re.compile(
1578
+ r"^##[ \t]+SUBAGENT-CONTEXT\b[^\n]*\bdepth=(\d+)",
1579
+ flags=re.MULTILINE,
1580
+ )
1581
+
1582
+ # --- Rail 3: FILE ASSIGNMENT overlap --------------------------------------
1583
+ _FILE_ASSIGNMENT_HEADER_RE = re.compile(
1584
+ r"^##[ \t]+FILE[ \t]+ASSIGNMENT[ \t]*$",
1585
+ flags=re.MULTILINE,
1586
+ )
1587
+ # "- CAN edit: a/b.py, c/d.py" (rest of line after the colon, comma-split).
1588
+ _CAN_EDIT_LINE_RE = re.compile(
1589
+ r"^[ \t]*[-*][ \t]*CAN[ \t]+edit:[ \t]*(.+)$",
1590
+ flags=re.MULTILINE | re.IGNORECASE,
1591
+ )
1592
+ _OVERLAP_LOOKBACK_S = 600 # 10-minute concurrency window
1593
+ _OVERLAP_MAX_PATHS = 64 # bound the per-spawn path set
1594
+ _OVERLAP_TAIL_LINES = 256 # bounded audit-log tail (~512KB)
1595
+
1596
+
1597
+ def _parse_tool_allowlist(prompt: str) -> Optional[frozenset]:
1598
+ """Return the declared lowercase tool allow-list, or None if no
1599
+ `## TOOL ALLOW-LIST` block is present (= unrestricted, back-compat).
1600
+
1601
+ Pure. Never raises. Bounded to the block between its header and the next
1602
+ `##` heading. Only KNOWN tool names are retained; unknown tokens dropped
1603
+ (so a typo cannot create a phantom allow-list that blocks everything).
1604
+ """
1605
+ if not prompt:
1606
+ return None
1607
+ sanitized = _strip_fenced_and_comments(prompt)
1608
+ m = _TOOL_ALLOWLIST_HEADER_RE.search(sanitized)
1609
+ if m is None:
1610
+ return None
1611
+ block_start = m.end()
1612
+ nxt = _NEXT_H2_RE.search(sanitized, block_start)
1613
+ block = sanitized[block_start: nxt.start() if nxt else len(sanitized)]
1614
+ allowed = set()
1615
+ for bm in _TOOL_BULLET_RE.finditer(block):
1616
+ for tok in bm.group(1).replace(",", " ").split():
1617
+ t = tok.strip().lower()
1618
+ if t in _KNOWN_TOOL_NAMES:
1619
+ allowed.add(t)
1620
+ # An empty-but-present block = "deny all known tools" (intentional).
1621
+ return frozenset(allowed)
1622
+
1623
+
1624
+ def _requested_tools(prompt: str) -> frozenset:
1625
+ """Heuristic set of KNOWN tools this spawn appears to request.
1626
+
1627
+ First-hit per family, bounded scan. Conservative (high-signal verbs only).
1628
+ Pure; never raises.
1629
+ """
1630
+ if not prompt:
1631
+ return frozenset()
1632
+ text = _strip_fenced_and_comments(prompt)[:_TOOL_SCAN_WINDOW].lower()
1633
+ out = set()
1634
+ for tool, hints in _TOOL_REQUEST_HINTS:
1635
+ if any(h in text for h in hints):
1636
+ out.add(tool)
1637
+ return frozenset(out)
1638
+
1639
+
1640
+ def _check_tool_scope(
1641
+ prompt: str,
1642
+ ) -> Optional[Tuple[str, str]]:
1643
+ """Rail 1. Return (reason_code, detail) if a requested tool is OUTSIDE
1644
+ the declared allow-list, else None.
1645
+
1646
+ `detail` NEVER contains a path or the raw prompt — only the offending
1647
+ tool NAME(s) (a closed-enum-safe value) + counts. No-value-echo safe.
1648
+ """
1649
+ allow = _parse_tool_allowlist(prompt)
1650
+ if allow is None:
1651
+ return None # no declared scope -> unrestricted (back-compat)
1652
+ requested = _requested_tools(prompt)
1653
+ out_of_scope = sorted(requested - allow)
1654
+ if not out_of_scope:
1655
+ return None
1656
+ return (
1657
+ "spawn_tool_out_of_scope",
1658
+ f"tools={','.join(out_of_scope)} declared={len(allow)}",
1659
+ )
1660
+
1661
+
1662
+ def _spawn_depth(prompt: str, env: Dict[str, str]) -> int:
1663
+ """Best-effort current spawn depth. 0 = top-level CEO spawn.
1664
+
1665
+ Two independent signals, max() wins (fail-toward-detection):
1666
+ - `## SUBAGENT-CONTEXT depth=N` marker in the prompt (CEO-injected when
1667
+ delegating to a sub-agent that may itself coordinate).
1668
+ - `CEO_SPAWN_DEPTH` env breadcrumb (harness/CEO-set).
1669
+ Pure-ish (reads env dict only). Never raises.
1670
+ """
1671
+ depth = 0
1672
+ try:
1673
+ m = _SUBAGENT_DEPTH_MARKER_RE.search(prompt or "")
1674
+ if m:
1675
+ depth = max(depth, int(m.group(1)))
1676
+ except Exception: # pragma: no cover - fail-open
1677
+ pass
1678
+ try:
1679
+ raw = (env.get("CEO_SPAWN_DEPTH") or "").strip()
1680
+ if raw.isdigit():
1681
+ depth = max(depth, int(raw))
1682
+ except Exception: # pragma: no cover - fail-open
1683
+ pass
1684
+ return depth
1685
+
1686
+
1687
+ def _parse_file_assignment(prompt: str) -> frozenset:
1688
+ """Return the set of concrete `CAN edit:` paths declared in the spawn's
1689
+ `## FILE ASSIGNMENT` block. Empty set if none.
1690
+
1691
+ Pure. Never raises. Bounded to _OVERLAP_MAX_PATHS. Wildcard/placeholder
1692
+ tokens ({file list}, *, dir/**) are DROPPED — only concrete paths
1693
+ participate in overlap detection (a glob can't be proven to clobber).
1694
+ """
1695
+ if not prompt:
1696
+ return frozenset()
1697
+ sanitized = _strip_fenced_and_comments(prompt)
1698
+ m = _FILE_ASSIGNMENT_HEADER_RE.search(sanitized)
1699
+ if m is None:
1700
+ return frozenset()
1701
+ block_start = m.end()
1702
+ nxt = _NEXT_H2_RE.search(sanitized, block_start)
1703
+ block = sanitized[block_start: nxt.start() if nxt else len(sanitized)]
1704
+ paths = set()
1705
+ for lm in _CAN_EDIT_LINE_RE.finditer(block):
1706
+ for raw in lm.group(1).split(","):
1707
+ p = raw.strip().strip("`").strip()
1708
+ if not p:
1709
+ continue
1710
+ # Drop placeholders + wildcards (cannot prove a clobber).
1711
+ if p.startswith("{") or "*" in p or p.lower() in (
1712
+ "none", "n/a", "tbd",
1713
+ ):
1714
+ continue
1715
+ # Normalize: strip a leading ./, collapse, lowercase-fold only the
1716
+ # drive-irrelevant case (POSIX paths are case-sensitive, so keep
1717
+ # case but normalize separators).
1718
+ p = p.lstrip("./").replace("\\", "/")
1719
+ paths.add(p)
1720
+ if len(paths) >= _OVERLAP_MAX_PATHS:
1721
+ return frozenset(paths)
1722
+ return frozenset(paths)
1723
+
1724
+
1725
+ def _path_hash(p: str) -> str:
1726
+ """12-hex sha256 prefix of a path. The ONLY path representation that
1727
+ enters the audit log (no raw path body ever persists — Sec MF-3)."""
1728
+ return hashlib.sha256(p.encode("utf-8", "replace")).hexdigest()[:12]
1729
+
1730
+
1731
+ def _recent_file_assignments(
1732
+ env: Dict[str, str],
1733
+ session_id: str,
1734
+ max_age_s: int = _OVERLAP_LOOKBACK_S,
1735
+ ) -> frozenset:
1736
+ """Tail the audit log for `CAN edit:` path-HASHES emitted by OTHER spawns
1737
+ in this session within the window. Returns a set of 12-hex path hashes.
1738
+
1739
+ Reads only `spawn_file_assignment_recorded` advisory rows (emitted at the
1740
+ allow-path of a prior spawn — see 4d). Bounded tail. Never raises; returns
1741
+ empty set on any error (fail-open -> no overlap detected -> allow).
1742
+ """
1743
+ try:
1744
+ log_path = _audit_log_path()
1745
+ if log_path is None or not log_path.exists():
1746
+ return frozenset()
1747
+ import json as _json
1748
+ import time as _time
1749
+ with log_path.open("r", encoding="utf-8") as f:
1750
+ try:
1751
+ f.seek(0, 2)
1752
+ size = f.tell()
1753
+ read_back = min(size, _OVERLAP_TAIL_LINES * 2048)
1754
+ start = max(0, size - read_back)
1755
+ f.seek(start, 0)
1756
+ if start > 0:
1757
+ f.readline()
1758
+ tail = f.readlines()
1759
+ except OSError:
1760
+ tail = f.readlines()[-_OVERLAP_TAIL_LINES:]
1761
+ now = _time.time()
1762
+ seen = set()
1763
+ for line in reversed(tail):
1764
+ try:
1765
+ ev = _json.loads(line)
1766
+ except Exception:
1767
+ continue
1768
+ if ev.get("action") != "spawn_file_assignment_recorded":
1769
+ continue
1770
+ if session_id and ev.get("session_id") != session_id:
1771
+ continue
1772
+ ts_f = _parse_event_ts(ev.get("ts"))
1773
+ if ts_f is None or (now - ts_f) > max_age_s:
1774
+ continue
1775
+ ph = ev.get("path_hashes")
1776
+ if isinstance(ph, str):
1777
+ for h in ph.split(","):
1778
+ h = h.strip()
1779
+ if h:
1780
+ seen.add(h)
1781
+ return frozenset(seen)
1782
+ except Exception: # pragma: no cover - fail-open
1783
+ return frozenset()
1784
+
1785
+
1786
+ def _enforce_spawn_rails(
1787
+ *,
1788
+ prompt: str,
1789
+ is_named_spawn: bool,
1790
+ env: Dict[str, str],
1791
+ session_id: str,
1792
+ ) -> Optional[Tuple[str, str]]:
1793
+ """PLAN-133 E3 — evaluate the three rails. Returns (reason_code, detail)
1794
+ when a rail is in ENFORCING mode (its flag=1) AND fires; else None.
1795
+
1796
+ In ADVISORY mode (flag unset/0) the rail still EMITS its closed-enum
1797
+ event with enforced=0 (measure-first) and returns None (allow).
1798
+
1799
+ NEVER raises. Each rail independently flagged; CEO_SOTA_DISABLE=1 forces
1800
+ advisory for all three.
1801
+ """
1802
+ try:
1803
+ master_off = (env.get("CEO_SOTA_DISABLE") or "").strip() == "1"
1804
+
1805
+ def _flag(name: str) -> bool:
1806
+ if master_off:
1807
+ return False
1808
+ return (env.get(name) or "").strip() == "1"
1809
+
1810
+ # --- Rail 1: tool scope (applies to ANY spawn that declares a list) -
1811
+ scope_hit = _check_tool_scope(prompt)
1812
+ if scope_hit is not None:
1813
+ code, detail = scope_hit
1814
+ enforced = _flag("CEO_SPAWN_TOOL_SCOPE")
1815
+ _emit_tool_scope_violation(detail=detail, enforced=enforced)
1816
+ if enforced:
1817
+ return (code, detail)
1818
+
1819
+ # Rails 2 + 3 only matter for NAMED spawns (a generic research task
1820
+ # neither recurses into named delegation nor partitions files).
1821
+ if is_named_spawn:
1822
+ # --- Rail 2: depth fence ---------------------------------------
1823
+ depth = _spawn_depth(prompt, env)
1824
+ if depth >= 1:
1825
+ enforced = _flag("CEO_SPAWN_DEPTH_GUARD")
1826
+ _emit_depth_or_overlap(
1827
+ rail="depth", enforced=enforced, count=depth,
1828
+ )
1829
+ if enforced:
1830
+ return (
1831
+ "spawn_depth_over_one",
1832
+ f"depth={depth}",
1833
+ )
1834
+
1835
+ # --- Rail 3: FILE ASSIGNMENT overlap ---------------------------
1836
+ mine = _parse_file_assignment(prompt)
1837
+ if mine:
1838
+ others = _recent_file_assignments(env, session_id)
1839
+ my_hashes = {_path_hash(p) for p in mine}
1840
+ clobber = my_hashes & others
1841
+ if clobber:
1842
+ enforced = _flag("CEO_SPAWN_OVERLAP_GUARD")
1843
+ _emit_depth_or_overlap(
1844
+ rail="overlap", enforced=enforced, count=len(clobber),
1845
+ )
1846
+ if enforced:
1847
+ return (
1848
+ "spawn_file_assignment_overlap",
1849
+ f"overlap_count={len(clobber)}",
1850
+ )
1851
+ # Record THIS spawn's assignment so the NEXT concurrent spawn
1852
+ # can detect a clash against it (advisory; always recorded).
1853
+ _emit_file_assignment_recorded(my_hashes, session_id)
1854
+ return None
1855
+ except Exception: # pragma: no cover - fail-open invariant
1856
+ return None
1857
+
1858
+
1859
+ def _emit_tool_scope_violation(*, detail: str, enforced: bool) -> None:
1860
+ """Emit `spawn_tool_scope_violation` (closed-enum). No-value-echo:
1861
+ `detail` carries only tool NAMES + counts (no path / prompt body)."""
1862
+ try:
1863
+ if not _AUDIT_EMIT_AVAILABLE:
1864
+ return
1865
+ _audit_emit.emit_generic(
1866
+ "spawn_tool_scope_violation",
1867
+ rail="tool_scope",
1868
+ enforced=1 if enforced else 0,
1869
+ detail=(detail or "")[:96],
1870
+ )
1871
+ except Exception: # pragma: no cover - fail-open
1872
+ return
1873
+
1874
+
1875
+ def _emit_depth_or_overlap(*, rail: str, enforced: bool, count: int) -> None:
1876
+ """Emit `spawn_depth_or_overlap_blocked` (closed-enum). `rail` is a
1877
+ closed enum (depth|overlap); `count` is a bounded int. No path / prompt."""
1878
+ try:
1879
+ if not _AUDIT_EMIT_AVAILABLE:
1880
+ return
1881
+ _audit_emit.emit_generic(
1882
+ "spawn_depth_or_overlap_blocked",
1883
+ rail=(rail if rail in ("depth", "overlap") else "other"),
1884
+ enforced=1 if enforced else 0,
1885
+ count=count,
1886
+ )
1887
+ except Exception: # pragma: no cover - fail-open
1888
+ return
1889
+
1890
+
1891
+ def _emit_file_assignment_recorded(path_hashes: set, session_id: str) -> None:
1892
+ """Advisory: record THIS spawn's CAN-edit path HASHES so a later
1893
+ concurrent spawn can detect an overlap. Only 12-hex hashes persist."""
1894
+ try:
1895
+ if not _AUDIT_EMIT_AVAILABLE:
1896
+ return
1897
+ joined = ",".join(sorted(path_hashes))[:512]
1898
+ _audit_emit.emit_generic(
1899
+ "spawn_file_assignment_recorded",
1900
+ session_id=(session_id or "")[:64],
1901
+ path_hashes=joined,
1902
+ path_count=min(len(path_hashes), 99),
1903
+ )
1904
+ except Exception: # pragma: no cover - fail-open
1905
+ return
1906
+
1907
+
1908
+ def decide(
1909
+ *,
1910
+ description: str,
1911
+ prompt: str,
1912
+ names_regex,
1913
+ env: Optional[dict] = None,
1914
+ subagent_type: str = "",
1915
+ ) -> Decision:
1916
+ """Pure decision function — no I/O, trivially unit-testable.
1917
+
1918
+ Args:
1919
+ description: The Agent tool's `description` field.
1920
+ prompt: The Agent tool's `prompt` field.
1921
+ names_regex: A compiled regex matching team member names, or None
1922
+ if no team files were found (degrades to header-only detection).
1923
+ env: Environment-var dict (defaults to os.environ). Used to read
1924
+ CEO_ARCHITECT_ACTIVE for the recursion guard (Sprint 5 ADR-010).
1925
+ subagent_type: Agent tool ``subagent_type`` field (PLAN-078 Wave 1
1926
+ telemetry). Used to identify archetype for model-routing
1927
+ advisory emit; never affects the allow/block decision.
1928
+
1929
+ Returns:
1930
+ Decision(allow=True) if the spawn is fine.
1931
+ Decision(allow=False, reason=...) if governance requires blocking.
1932
+ """
1933
+ src_env = env if env is not None else os.environ
1934
+
1935
+ # PLAN-133 E3 — resolve session id once for the spawn-rails (depth/overlap).
1936
+ _e3_session_id = _resolve_session_id_from_env(src_env)
1937
+
1938
+ # PLAN-113 WIRE-DEADMOD / ADR-089 SEC-P0-01 — sanitize ## SPEC CONTEXT
1939
+ # payload (advisory telemetry; never blocks). Runs early so sentinel
1940
+ # violations are logged before any governance decision. Fail-open.
1941
+ _sanitize_spec_context_advisory(prompt or "", env=src_env)
1942
+
1943
+ # PLAN-133 A2 (Goose-harvest) — fail-CLOSED invisible-unicode guard.
1944
+ # Default-OFF (CEO_UNICODE_HARDBLOCK=1 enforces); when enforced, a spawn
1945
+ # prompt carrying control / bidi / zero-width / U+E0000-E007F Tag-block
1946
+ # chars is BLOCKED here, BEFORE any LLM/debate/governance review. The
1947
+ # breadcrumb (invisible_unicode_blocked) is emitted on both the advisory
1948
+ # and enforced paths so the measure-first denominator is real. Fail-open.
1949
+ _uni_block = _enforce_spec_context_unicode(prompt or "", env=src_env)
1950
+ if _uni_block is not None:
1951
+ return Decision(allow=False, reason=_uni_block)
1952
+
1953
+ # Session 75 Codex Finding 4 closure: scan spawn prompt for
1954
+ # leaked secrets when Owner-opted-in via CEO_SPAWN_SECRET_SCAN=1.
1955
+ # Default OFF; without the env var, _validate_spawn_prompt_has_no_secrets
1956
+ # short-circuits to ok=True so legacy spawns are unaffected.
1957
+ _scan_ok, _scan_code, _scan_detail = _validate_spawn_prompt_has_no_secrets(prompt)
1958
+ if not _scan_ok:
1959
+ return Decision(
1960
+ allow=False,
1961
+ reason=(
1962
+ f"SPAWN-SECRET-BLOCKED: {_scan_code} ({_scan_detail}). "
1963
+ "To bypass once: unset CEO_SPAWN_SECRET_SCAN."
1964
+ ),
1965
+ )
1966
+
1967
+ # PLAN-045 Wave 1 P0-03 — VETO floor runtime check.
1968
+ # If the spawn targets a VETO-floor role (code-reviewer,
1969
+ # security-engineer), verify the agent frontmatter still binds
1970
+ # them to Opus. Closes F-01-03 demotion-via-frontmatter attack.
1971
+ # Skipped when the validator lib is missing (defense-in-depth
1972
+ # fail-open; arbitration kernel still blocks agents/*.md edits).
1973
+ if _agent_frontmatter is not None:
1974
+ agents_dir = Path(
1975
+ src_env.get("CLAUDE_PROJECT_DIR") or os.getcwd()
1976
+ ) / ".claude" / "agents"
1977
+ haystack_lower = " ".join([
1978
+ (description or "").lower(), (prompt or "").lower()
1979
+ ])
1980
+ for _role in sorted(_agent_frontmatter.VETO_FLOOR_ROLES):
1981
+ if _role.lower() not in haystack_lower:
1982
+ continue
1983
+ ok, reason = _agent_frontmatter.check_veto_floor_for_role(
1984
+ _role, agents_dir
1985
+ )
1986
+ if not ok and reason != "not_veto_role":
1987
+ return Decision(
1988
+ allow=False,
1989
+ reason=(
1990
+ f"GOVERNANCE: veto_floor_demoted: role={_role} "
1991
+ f"reason={reason}. The VETO floor requires "
1992
+ "security-engineer and code-reviewer to bind to "
1993
+ "claude-opus-4-8. See ADR-052 + PLAN-045 Wave 1 P0-03."
1994
+ ),
1995
+ )
1996
+
1997
+ # PLAN-078 Wave 1 — model routing advisory telemetry (advisory-only).
1998
+ # Runs AFTER VETO-floor enforcement so the hard-block path remains
1999
+ # authoritative. Emits `model_routing_advised` event when a spawn
2000
+ # archetype is identifiable. NEVER mutates tool_input. NEVER blocks.
2001
+ # Bypass: `CEO_MODEL_ROUTING=0`.
2002
+ _emit_model_routing_advisory(
2003
+ description=description or "",
2004
+ prompt=prompt or "",
2005
+ subagent_type=subagent_type or "",
2006
+ env=src_env,
2007
+ project_dir=src_env.get("CLAUDE_PROJECT_DIR") or os.getcwd(),
2008
+ )
2009
+
2010
+ # PLAN-112-FOLLOWUP-persona-routing-wire W1 — god-mode matrix consult.
2011
+ # Runs AFTER the VETO-floor hard-block (above) so it never masks it.
2012
+ # CONSULT + AUDIT ONLY — emits model_routing_enforced /
2013
+ # model_routing_eval_error; NEVER blocks (block deferred, see W3 above).
2014
+ # Mode is read off authoritative subagent_type only.
2015
+ _consult_model_routing_mode(
2016
+ description=description or "",
2017
+ prompt=prompt or "",
2018
+ subagent_type=subagent_type or "",
2019
+ env=src_env,
2020
+ project_dir=src_env.get("CLAUDE_PROJECT_DIR") or os.getcwd(),
2021
+ )
2022
+
2023
+ # PLAN-091 Wave A.4 (W3.1) — MCP routing advisory.
2024
+ # Maps archetype → mcp_routing task_class; resolver emits
2025
+ # `mcp_route_advised` (PLAN-086 Wave D). Bypass: CEO_MCP_ROUTING_HOOK=0.
2026
+ _emit_mcp_routing_advisory(
2027
+ description=description or "",
2028
+ prompt=prompt or "",
2029
+ subagent_type=subagent_type or "",
2030
+ env=src_env,
2031
+ )
2032
+
2033
+ # PLAN-091 Wave A.5 (W3.3) — specialization promotion heuristic.
2034
+ # When general-purpose spawn matches a specialist hint, emit
2035
+ # `specialization_promoted` advisory. NEVER auto-spawns.
2036
+ # Bypass: CEO_PROMOTION_HEURISTIC=0.
2037
+ _emit_promotion_advisory(
2038
+ description=description or "",
2039
+ prompt=prompt or "",
2040
+ subagent_type=subagent_type or "",
2041
+ env=src_env,
2042
+ )
2043
+
2044
+ # PLAN-092 Wave A.4 (W3.2 SEMI-11) — cookbook-advisor pattern hint.
2045
+ # Matches spawn against 4 Anthropic Cookbook patterns (COOK-P1..P4);
2046
+ # emits `cookbook_pattern_advised` advisory. Kill-switch:
2047
+ # CEO_COOKBOOK_ADVISOR_ENABLED=0. NEVER mutates tool_input.
2048
+ _emit_cookbook_pattern_advisory(
2049
+ description=description or "",
2050
+ prompt=prompt or "",
2051
+ env=src_env,
2052
+ )
2053
+
2054
+ # PLAN-098 Wave C.2 (ADR-132) — GOAP advisory-only invariant.
2055
+ # PLAN-105 Wave A.6 — capture intent for deferred-emit at allow-return.
2056
+ _goap_intent_plan_id: Optional[str] = None
2057
+ _goap_intent_action_id: Optional[str] = None
2058
+ if _GOAP_PLAN_ID_RE.search(prompt or ""):
2059
+ owner_confirmed_env = (src_env.get("CEO_GOAP_CONFIRMED") or "").strip() == "1"
2060
+ has_goap_confirm_block = bool(_GOAP_CONFIRM_HEADER_RE.search(prompt or ""))
2061
+ if not (owner_confirmed_env and has_goap_confirm_block):
2062
+ preview = (description or "")[:80]
2063
+ missing: List[str] = []
2064
+ if not owner_confirmed_env:
2065
+ missing.append("CEO_GOAP_CONFIRMED=1 env")
2066
+ if not has_goap_confirm_block:
2067
+ missing.append("## GOAP CONFIRM block")
2068
+ return Decision(
2069
+ allow=False,
2070
+ reason=(
2071
+ "GOVERNANCE: goap_advisory_without_owner_confirm: "
2072
+ f"spawn references a GOAP plan (description={preview!r}) "
2073
+ f"but lacks: {', '.join(missing)}. The GOAP planner is "
2074
+ "advisory-only - the Owner must physically confirm each "
2075
+ "action before spawn. See ADR-132 Decision Part 2."
2076
+ ),
2077
+ )
2078
+ # PLAN-105 Wave A.6 — GOAP gates passed; record intent for deferred emit.
2079
+ _pid_m = _GOAP_PLAN_ID_VALUE_RE.search(prompt or "")
2080
+ _aid_m = _GOAP_ACTION_ID_VALUE_RE.search(prompt or "")
2081
+ if _pid_m is not None:
2082
+ _goap_intent_plan_id = _pid_m.group(1).strip()[:32]
2083
+ if _aid_m is not None:
2084
+ _goap_intent_action_id = _aid_m.group(1).strip()[:64]
2085
+
2086
+ # Recursion guard (ADR-010): block Architect-spawning-Architect.
2087
+ architect_active = (src_env.get("CEO_ARCHITECT_ACTIVE") or "").strip()
2088
+ if architect_active == "1":
2089
+ haystack = " ".join([description or "", prompt or ""])
2090
+ if _ARCHITECT_NAME_RE.search(haystack):
2091
+ return Decision(
2092
+ allow=False,
2093
+ reason=(
2094
+ "ARCHITECT-RECURSION: a spawn naming 'Agent Architect' "
2095
+ "was detected while CEO_ARCHITECT_ACTIVE=1. The Agent "
2096
+ "Architect must not spawn another instance of itself "
2097
+ "within the same session. See ADR-010."
2098
+ ),
2099
+ )
2100
+
2101
+ # PLAN-020 Phase 3 — /effort scope clause (QA must-fix #7): /effort
2102
+ # hints are CEO-only. Spawn prompts MUST NOT include them.
2103
+ if _has_effort_token(prompt or ""):
2104
+ return Decision(
2105
+ allow=False,
2106
+ reason=(
2107
+ "GOVERNANCE: spawn prompt contains a `/effort` token. "
2108
+ "Effort hints are CEO-only — sub-agents inherit Anthropic "
2109
+ "default thinking budget. Strip /effort from the prompt "
2110
+ "before retrying. See PLAN-020 §4 Phase 3 scope clause."
2111
+ ),
2112
+ )
2113
+
2114
+ desc_matched_name = False
2115
+ if names_regex is not None and description:
2116
+ if names_regex.search(description):
2117
+ desc_matched_name = True
2118
+
2119
+ prompt_has_persona = False
2120
+ if prompt and _PERSONA_HEADER_RE.search(prompt):
2121
+ prompt_has_persona = True
2122
+
2123
+ is_named_spawn = desc_matched_name or prompt_has_persona
2124
+
2125
+ # PLAN-133 E3 — per-spawn tool scoping + depth/overlap rails. ADVISORY by
2126
+ # default (emit-only, enforced=0); BLOCKS only under the per-rail env flag
2127
+ # (CEO_SPAWN_TOOL_SCOPE / CEO_SPAWN_DEPTH_GUARD / CEO_SPAWN_OVERLAP_GUARD).
2128
+ # Runs AFTER the VETO-floor + A2 unicode hard-blocks so those remain
2129
+ # authoritative; runs BEFORE the SKILL CONTENT accept-path so an
2130
+ # out-of-scope / depth-2 / clobbering spawn is rejected even when otherwise
2131
+ # well-formed. Fail-open (returns None on any infra error).
2132
+ _e3_hit = _enforce_spawn_rails(
2133
+ prompt=prompt or "",
2134
+ is_named_spawn=is_named_spawn,
2135
+ env=src_env,
2136
+ session_id=_e3_session_id,
2137
+ )
2138
+ if _e3_hit is not None:
2139
+ _e3_code, _e3_detail = _e3_hit
2140
+ return Decision(
2141
+ allow=False,
2142
+ reason=(
2143
+ f"GOVERNANCE: {_e3_code}: {_e3_detail}. "
2144
+ "The spawn violates a PLAN-133 E3 rail (per-spawn tool "
2145
+ "allow-list / depth-over-one fence / FILE ASSIGNMENT overlap). "
2146
+ "See PLAN-133 §E E3."
2147
+ ),
2148
+ )
2149
+
2150
+ # PLAN-113 WIRE-DEADMOD — confidence_labels advisory (PLAN-083 Wave 1.10).
2151
+ # Emits spawn_confidence_advisory so recommender / receipt formatter have
2152
+ # a hook-level signal. Advisory only — never blocks. Fail-open.
2153
+ _action_type = "canonical_edit" if is_named_spawn else "bash_execute"
2154
+ _emit_spawn_confidence_advisory(
2155
+ action_type=_action_type,
2156
+ is_named_spawn=is_named_spawn,
2157
+ env=src_env,
2158
+ )
2159
+
2160
+ if not is_named_spawn:
2161
+ # Generic research / simple tasks — no governance requirement.
2162
+ _emit_goap_deferred_outcome(
2163
+ _goap_intent_plan_id, _goap_intent_action_id, src_env
2164
+ )
2165
+ return Decision(allow=True)
2166
+
2167
+ # PLAN-020 Phase 2 — Reference path (ADR-051). If `## SKILL REFERENCE`
2168
+ # marker is present, validate strictly (fail-CLOSED). Reference path
2169
+ # opt-in via env var; default ON per Q1 Owner answer.
2170
+ if _is_enabled("CEO_SKILL_REFERENCE_MODE", ENABLE_SKILL_REFERENCE_MODE, src_env):
2171
+ if _SKILL_REFERENCE_HEADER_RE.search(prompt or ""):
2172
+ ok, reason_code, detail = _validate_skill_reference(prompt or "")
2173
+ if ok:
2174
+ _emit_goap_deferred_outcome(
2175
+ _goap_intent_plan_id, _goap_intent_action_id, src_env
2176
+ )
2177
+ # PLAN-106 Wave C — persona coverage emit at allow path.
2178
+ _emit_persona_coverage_synthesized(
2179
+ subagent_type=subagent_type,
2180
+ description=description,
2181
+ prompt=prompt,
2182
+ source="dispatch",
2183
+ env=src_env,
2184
+ )
2185
+ return Decision(allow=True)
2186
+ preview = (description or "")[:80]
2187
+ return Decision(
2188
+ allow=False,
2189
+ reason=(
2190
+ f"GOVERNANCE: {reason_code}: {detail}. "
2191
+ f"Spawn rejected (description={preview!r}). "
2192
+ "See ADR-051 §Synchronous validation for sub-check details."
2193
+ ),
2194
+ )
2195
+
2196
+ # Named spawn: require real SKILL CONTENT section (P1-SEC-B: bypass-resistant).
2197
+ if _has_skill_content(prompt or ""):
2198
+ _emit_goap_deferred_outcome(
2199
+ _goap_intent_plan_id, _goap_intent_action_id, src_env
2200
+ )
2201
+ # PLAN-106 Wave C — persona coverage emit at allow path.
2202
+ _emit_persona_coverage_synthesized(
2203
+ subagent_type=subagent_type,
2204
+ description=description,
2205
+ prompt=prompt,
2206
+ source="dispatch",
2207
+ env=src_env,
2208
+ )
2209
+ return Decision(allow=True)
2210
+
2211
+ # Build a helpful block reason pointing at the injector script.
2212
+ preview = (description or "")[:80]
2213
+ reason = (
2214
+ "GOVERNANCE: Agent spawn detected as NAMED "
2215
+ f"(description='{preview}'), but prompt has no {_SKILL_CONTENT_MARKER} "
2216
+ "section. Read the agent's skill file and include its full content "
2217
+ "in the prompt before spawning. Use "
2218
+ ".claude/scripts/inject-agent-context.sh <AgentName> <task> to "
2219
+ "generate a compliant prompt."
2220
+ )
2221
+ return Decision(allow=False, reason=reason)
2222
+
2223
+
2224
+ # ---------------------------------------------------------------------------
2225
+ # PLAN-105 Wave A.6 — Deferred-emit override detection helper.
2226
+ #
2227
+ # Reads the most-recent goap_recommendation_rendered event in the current
2228
+ # session (≤5 min window, matching plan_id), compares the spawn's
2229
+ # `goap-action-id:` marker, and emits goap_recommendation_accepted on
2230
+ # exact match OR goap_recommendation_overridden with the appropriate
2231
+ # `override_type` (substituted_action / no_render_prior / marker_absent).
2232
+ #
2233
+ # Kill-switches:
2234
+ # CEO_GOAP_ADVISORY_ENABLED=0 → all 3 emits silent (existing PLAN-098 kill-switch)
2235
+ # CEO_GOAP_OVERRIDE_DETECTION_DISABLED=1 → always emit _accepted on allow-path (diagnostic only)
2236
+ # ---------------------------------------------------------------------------
2237
+
2238
+ _GOAP_RENDER_LOOKBACK_S = 300 # 5 minutes
2239
+
2240
+
2241
+ def _audit_log_path() -> Optional[Path]:
2242
+ """Resolve audit log path — delegates to audit_emit._log_path() for
2243
+ byte-identical write/read paths.
2244
+
2245
+ PLAN-105 R2 P0 #2 fold: previously derived a slug from CLAUDE_PROJECT_DIR,
2246
+ which diverged from audit_emit's `~/.claude/projects/ceo-orchestration`
2247
+ default. Now imports the real resolver so reader path == writer path.
2248
+ """
2249
+ try:
2250
+ from _lib import audit_emit as _ae # type: ignore
2251
+ path = _ae._log_path()
2252
+ return path if path else None
2253
+ except Exception:
2254
+ # Fallback: env-driven path so unit tests using CEO_AUDIT_LOG_PATH
2255
+ # work pre-import.
2256
+ env_path = os.environ.get("CEO_AUDIT_LOG_PATH")
2257
+ if env_path:
2258
+ return Path(env_path)
2259
+ env_dir = os.environ.get("CEO_AUDIT_LOG_DIR")
2260
+ if env_dir:
2261
+ return Path(env_dir) / "audit-log.jsonl"
2262
+ return None
2263
+
2264
+
2265
+ def _parse_event_ts(ts) -> Optional[float]:
2266
+ """Parse audit-log ts field (ISO-8601 UTC string or epoch float) → epoch seconds."""
2267
+ if ts is None:
2268
+ return None
2269
+ if isinstance(ts, (int, float)):
2270
+ return float(ts)
2271
+ if isinstance(ts, str):
2272
+ s = ts.strip()
2273
+ if not s:
2274
+ return None
2275
+ # Try epoch first.
2276
+ try:
2277
+ return float(s)
2278
+ except (TypeError, ValueError):
2279
+ pass
2280
+ # ISO-8601 — strip trailing Z, parse via datetime.fromisoformat.
2281
+ import datetime as _dt
2282
+ try:
2283
+ if s.endswith("Z"):
2284
+ s = s[:-1] + "+00:00"
2285
+ return _dt.datetime.fromisoformat(s).timestamp()
2286
+ except Exception:
2287
+ return None
2288
+ return None
2289
+
2290
+
2291
+ def _tail_recent_rendered(
2292
+ plan_id: str,
2293
+ max_age_s: int = _GOAP_RENDER_LOOKBACK_S,
2294
+ session_id: Optional[str] = None,
2295
+ ):
2296
+ """Tail audit log for most-recent goap_recommendation_rendered matching plan_id.
2297
+
2298
+ PLAN-105 R2 P1 fold:
2299
+ - Optional `session_id` filter — same-session join (concurrent sessions
2300
+ with same plan_id within 5 min cannot misclassify).
2301
+ - Conservative ts handling — unparseable / missing ts treated as
2302
+ out-of-window (skipped), not as "match-anyway".
2303
+
2304
+ Returns parsed event dict (Optional[dict]). Returns None if no match
2305
+ within `max_age_s` seconds. Bounded tail = last 256 lines (~512KB).
2306
+ """
2307
+ log_path = _audit_log_path()
2308
+ if log_path is None or not log_path.exists():
2309
+ return None
2310
+ try:
2311
+ import json as _json
2312
+ import time as _time
2313
+ with log_path.open("r", encoding="utf-8") as f:
2314
+ # Read last ~256 lines via seek-from-end (bounded perf).
2315
+ try:
2316
+ f.seek(0, 2)
2317
+ size = f.tell()
2318
+ read_back = min(size, 256 * 2048) # ~512KB max
2319
+ start_offset = max(0, size - read_back)
2320
+ f.seek(start_offset, 0)
2321
+ # Only discard partial first line if we didn't seek to 0
2322
+ # (otherwise we'd lose the first real line of a small file).
2323
+ if start_offset > 0:
2324
+ f.readline()
2325
+ tail = f.readlines()
2326
+ except OSError:
2327
+ tail = f.readlines()[-256:]
2328
+ now = _time.time()
2329
+ for line in reversed(tail):
2330
+ try:
2331
+ ev = _json.loads(line)
2332
+ except Exception:
2333
+ continue
2334
+ if ev.get("action") != "goap_recommendation_rendered":
2335
+ continue
2336
+ if ev.get("plan_id") != plan_id:
2337
+ continue
2338
+ # PLAN-105 R2 P1-3 fold — same-session filter when provided.
2339
+ if session_id is not None and ev.get("session_id") != session_id:
2340
+ continue
2341
+ ts_f = _parse_event_ts(ev.get("ts"))
2342
+ # PLAN-105 R2 P1-2 fold — conservative: unparseable ts skipped.
2343
+ if ts_f is None:
2344
+ continue
2345
+ if (now - ts_f) > max_age_s:
2346
+ # Older than the window — skip.
2347
+ continue
2348
+ return ev
2349
+ return None
2350
+ except Exception:
2351
+ return None
2352
+
2353
+
2354
+ def _resolve_session_id_from_env(env: Dict[str, str]) -> str:
2355
+ """Resolve session_id from harness env (CLAUDE_SESSION_ID) — PLAN-105 R2 P1-1 fold."""
2356
+ for key in ("CLAUDE_SESSION_ID", "CEO_SESSION_ID"):
2357
+ v = (env.get(key) or "").strip()
2358
+ if v:
2359
+ return v[:64]
2360
+ return ""
2361
+
2362
+
2363
+ def _resolve_project_from_env(env: Dict[str, str]) -> str:
2364
+ """Derive project slug from CLAUDE_PROJECT_DIR basename — PLAN-105 R2 P1-1 fold."""
2365
+ pd = (env.get("CLAUDE_PROJECT_DIR") or "").strip()
2366
+ if pd:
2367
+ return Path(pd).name[:64]
2368
+ return ""
2369
+
2370
+
2371
+ def _emit_persona_coverage_synthesized(
2372
+ subagent_type: str,
2373
+ description: str,
2374
+ prompt: str,
2375
+ source: str,
2376
+ env: dict,
2377
+ ) -> None:
2378
+ """PLAN-106 Wave C — emit persona_coverage_synthesized at allow path.
2379
+
2380
+ Maps (subagent_type, derived task_type) → 4×4 cell, emits one
2381
+ event via audit_emit.emit_generic. Best-effort; any exception is
2382
+ swallowed (fail-open). Bypass: ``CEO_PERSONA_COVERAGE_EMIT=0``.
2383
+
2384
+ The closed-enum sets (archetype + task_type) MUST match
2385
+ `_lib/audit_emit.py:_PERSONA_COVERAGE_ARCHETYPES` /
2386
+ `_PERSONA_COVERAGE_TASK_TYPES`. Both lists are duplicated here
2387
+ intentionally so the hook does no extra import at hot-path.
2388
+ """
2389
+ if (env.get("CEO_PERSONA_COVERAGE_EMIT") or "").strip() == "0":
2390
+ return
2391
+ if not _AUDIT_EMIT_AVAILABLE:
2392
+ return
2393
+ # Closed-set archetype filter — only emit for the 4 VETO-floor personas.
2394
+ arch_lower = (subagent_type or "").strip().lower()
2395
+ if arch_lower not in {
2396
+ "code-reviewer", "security-engineer", "qa-architect",
2397
+ "threat-detection-engineer",
2398
+ }:
2399
+ return
2400
+ # Derive task_type via simple description+prompt keyword scan.
2401
+ haystack = " ".join([
2402
+ (description or "").lower(),
2403
+ (prompt or "")[:4096].lower(), # bounded — security R1 P1 NFKC budget
2404
+ ])
2405
+ # NFKC normalize once (Cf-injected bypass guard — full-width chars
2406
+ # in adversarial spawn prompts).
2407
+ haystack = unicodedata.normalize("NFKC", haystack)
2408
+ task_type = ""
2409
+ # Order matters: most specific first.
2410
+ for keyword, t in (
2411
+ ("review", "review"),
2412
+ ("audit", "review"), # reviewer aliases
2413
+ ("vet", "vet"),
2414
+ ("validate", "vet"),
2415
+ ("verify", "vet"),
2416
+ ("test", "test"),
2417
+ ("detect", "detect"),
2418
+ ("triage", "detect"),
2419
+ ("incident", "detect"),
2420
+ ):
2421
+ if keyword in haystack:
2422
+ task_type = t
2423
+ break
2424
+ if not task_type:
2425
+ # No keyword match → no cell signal. Skip emit; do not
2426
+ # arbitrarily bin into a default cell (would skew coverage).
2427
+ return
2428
+
2429
+ # cell_id = sha256[:8] of canonical f"{arch_lower}:{task_type}".
2430
+ # Deterministic across calls so dedup at audit layer can collapse
2431
+ # repeats from check_agent_spawn + check_canonical_edit on the
2432
+ # same (archetype, task_type) within a window.
2433
+ cell_input = f"{arch_lower}:{task_type}".encode("utf-8")
2434
+ cell_id = hashlib.sha256(cell_input).hexdigest()[:8]
2435
+
2436
+ try:
2437
+ _audit_emit.emit_generic(
2438
+ "persona_coverage_synthesized",
2439
+ archetype=arch_lower,
2440
+ task_type=task_type,
2441
+ cell_id=cell_id,
2442
+ source=source,
2443
+ )
2444
+ except Exception: # noqa: BLE001 — fail-open, never block spawn
2445
+ pass
2446
+
2447
+
2448
+ def _emit_goap_deferred_outcome(
2449
+ plan_id: Optional[str],
2450
+ action_id: Optional[str],
2451
+ env: Dict[str, str],
2452
+ ) -> None:
2453
+ """Emit goap_recommendation_accepted or _overridden at decide() allow-return.
2454
+
2455
+ PLAN-105 Wave A.6. Silent no-op if plan_id is None (non-GOAP spawn).
2456
+ Silent no-op if kill-switch CEO_GOAP_ADVISORY_ENABLED=0.
2457
+
2458
+ PLAN-105 R2 P1-1 fold: session_id + project propagated from env so
2459
+ audit consumers can correlate spawn outcome with rendered event.
2460
+ """
2461
+ if not plan_id:
2462
+ return
2463
+ if (env.get("CEO_GOAP_ADVISORY_ENABLED", "1") or "").strip() == "0":
2464
+ return
2465
+ try:
2466
+ from _lib import audit_emit as _ae # type: ignore
2467
+ except Exception:
2468
+ return
2469
+
2470
+ session_id = _resolve_session_id_from_env(env)
2471
+ project = _resolve_project_from_env(env)
2472
+
2473
+ diag_force_accept = (
2474
+ env.get("CEO_GOAP_OVERRIDE_DETECTION_DISABLED", "0") or ""
2475
+ ).strip() == "1"
2476
+ if diag_force_accept:
2477
+ fn = getattr(_ae, "emit_goap_recommendation_accepted", None)
2478
+ if fn is not None:
2479
+ try:
2480
+ fn(plan_id=plan_id, action_id=(action_id or "DIAG_FORCED"),
2481
+ session_id=session_id, project=project)
2482
+ except Exception:
2483
+ pass
2484
+ return
2485
+
2486
+ # PLAN-105 R2 P1-3 fold — same-session filter when session_id available.
2487
+ rendered = _tail_recent_rendered(
2488
+ plan_id,
2489
+ session_id=session_id if session_id else None,
2490
+ )
2491
+ if rendered is None:
2492
+ # Try a second pass without session-id filter — covers cases where
2493
+ # the _rendered event was emitted from /goap CLI invocation that
2494
+ # didn't carry session_id (or session env was unset at planner time).
2495
+ rendered = _tail_recent_rendered(plan_id)
2496
+ if rendered is None:
2497
+ # No recent _rendered for this plan_id — emit _overridden:no_render_prior.
2498
+ fn = getattr(_ae, "emit_goap_recommendation_overridden", None)
2499
+ if fn is not None:
2500
+ try:
2501
+ fn(plan_id=plan_id,
2502
+ original_action_id="NO_RENDER_PRIOR",
2503
+ dispatched_action_id=(action_id or "MARKER_ABSENT"),
2504
+ override_type="no_render_prior",
2505
+ session_id=session_id, project=project)
2506
+ except Exception:
2507
+ pass
2508
+ return
2509
+
2510
+ if action_id is None:
2511
+ # Spawn lacks goap-action-id marker — emit _overridden:marker_absent.
2512
+ fn = getattr(_ae, "emit_goap_recommendation_overridden", None)
2513
+ if fn is not None:
2514
+ try:
2515
+ fn(plan_id=plan_id,
2516
+ original_action_id=(rendered.get("action_ids_csv", "") or "")[:64],
2517
+ dispatched_action_id="MARKER_ABSENT",
2518
+ override_type="marker_absent",
2519
+ session_id=session_id, project=project)
2520
+ except Exception:
2521
+ pass
2522
+ return
2523
+
2524
+ rendered_csv = rendered.get("action_ids_csv", "") or ""
2525
+ rendered_ids = [s.strip() for s in rendered_csv.split(",") if s.strip()]
2526
+ if action_id in rendered_ids:
2527
+ fn = getattr(_ae, "emit_goap_recommendation_accepted", None)
2528
+ if fn is not None:
2529
+ try:
2530
+ fn(plan_id=plan_id, action_id=action_id,
2531
+ session_id=session_id, project=project)
2532
+ except Exception:
2533
+ pass
2534
+ else:
2535
+ fn = getattr(_ae, "emit_goap_recommendation_overridden", None)
2536
+ if fn is not None:
2537
+ try:
2538
+ fn(plan_id=plan_id,
2539
+ original_action_id=rendered_csv[:64],
2540
+ dispatched_action_id=action_id,
2541
+ override_type="substituted_action",
2542
+ session_id=session_id, project=project)
2543
+ except Exception:
2544
+ pass
2545
+
2546
+
2547
+ # Session 75 Codex re-pass remaining concern: derive a stable
2548
+ # reason_code from the human-readable Decision.reason so the audit
2549
+ # trail correctly classifies the block path. Order is significant —
2550
+ # more-specific prefixes first, generic fallback last.
2551
+ # PLAN-098 Wave C.2 (ADR-132) - GOAP advisory-only invariant enforcement.
2552
+ _GOAP_PLAN_ID_RE = re.compile(r"\bgoap-plan-id\s*:\s*\S+", re.IGNORECASE)
2553
+ _GOAP_CONFIRM_HEADER_RE = re.compile(
2554
+ r"^##\s+GOAP\s+CONFIRM\b", re.IGNORECASE | re.MULTILINE
2555
+ )
2556
+ # PLAN-105 Wave A.6 - capture value of goap-plan-id / goap-action-id markers
2557
+ # for deferred-emit override detection at decide() allow-path return.
2558
+ _GOAP_PLAN_ID_VALUE_RE = re.compile(
2559
+ r"\bgoap-plan-id\s*:\s*(\S+)", re.IGNORECASE
2560
+ )
2561
+ _GOAP_ACTION_ID_VALUE_RE = re.compile(
2562
+ r"\bgoap-action-id\s*:\s*(\S+)", re.IGNORECASE
2563
+ )
2564
+
2565
+
2566
+ _BLOCK_REASON_MARKERS = (
2567
+ ("SPAWN-SECRET-BLOCKED", "secret_in_spawn_prompt"),
2568
+ ("GOVERNANCE: veto_floor_demoted", "veto_floor_demoted"),
2569
+ ("ARCHITECT-RECURSION", "architect_role_not_delegable"),
2570
+ ("GOVERNANCE: spawn prompt contains a `/effort` token", "effort_token_in_spawn"),
2571
+ # Skill-reference path: reason text format is
2572
+ # "GOVERNANCE: <reason_code>: <detail>..." — extract the embedded code.
2573
+ ("GOVERNANCE: reference_", "__REFERENCE_PREFIX__"),
2574
+ # PLAN-098 Wave C.2 (ADR-132) - GOAP advisory-only invariant.
2575
+ ("GOVERNANCE: goap_advisory_without_owner_confirm", "goap_advisory_without_owner_confirm"),
2576
+ # PLAN-133 E3 — per-spawn tool scoping + depth/overlap rails.
2577
+ ("GOVERNANCE: spawn_tool_out_of_scope", "spawn_tool_out_of_scope"),
2578
+ ("GOVERNANCE: spawn_depth_over_one", "spawn_depth_over_one"),
2579
+ ("GOVERNANCE: spawn_file_assignment_overlap", "spawn_file_assignment_overlap"),
2580
+ # NAMED-spawn-without-skill is the historical default — keep last.
2581
+ ("GOVERNANCE: Agent spawn detected as NAMED", "missing_skill_content"),
2582
+ )
2583
+
2584
+
2585
+ def _classify_block_reason(reason: str) -> str:
2586
+ """Map a Decision.reason string to a stable audit reason_code.
2587
+
2588
+ Defaults to ``unknown_block`` when no marker matches — visibility
2589
+ on a marker drift is preferable to silent misclassification.
2590
+ """
2591
+ if not reason:
2592
+ return "unknown_block"
2593
+ for needle, code in _BLOCK_REASON_MARKERS:
2594
+ if needle in reason:
2595
+ if code == "__REFERENCE_PREFIX__":
2596
+ # Embedded reason_code form: "GOVERNANCE: <code>: <detail>".
2597
+ # Skill-reference path emits codes like reference_hash_mismatch,
2598
+ # reference_unsafe_path, etc. (constants at
2599
+ # check_agent_spawn.py:188 REASON_REFERENCE_*).
2600
+ head = reason[len("GOVERNANCE: "):]
2601
+ colon = head.find(":")
2602
+ return head[:colon] if colon > 0 else "reference_invalid"
2603
+ return code
2604
+ return "unknown_block"
2605
+
2606
+
2607
+ def _to_contract_decision(d: "Decision") -> _contract.Decision:
2608
+ if d.allow:
2609
+ return _contract.allow()
2610
+ return _contract.block(d.reason or "")
2611
+
2612
+
2613
+ def main() -> int:
2614
+ """Hook entry point: read stdin, decide, write stdout, exit 0.
2615
+
2616
+ PLAN-006 Phase 1 migration (ADR-014): Adapter Layer I/O.
2617
+ Fail-open on any exception.
2618
+ """
2619
+ try:
2620
+ event = _claude_adapter.read_event(phase="PreToolUse")
2621
+ if event.parse_error:
2622
+ print(
2623
+ f"[check_agent_spawn] WARN: stdin parse error: {event.parse_error}",
2624
+ file=sys.stderr,
2625
+ )
2626
+ _claude_adapter.emit_decision(_contract.allow())
2627
+ return 0
2628
+
2629
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()
2630
+ try:
2631
+ names_regex = _team.load_names(project_dir)
2632
+ except Exception as e: # pragma: no cover
2633
+ print(
2634
+ f"[check_agent_spawn] WARN: team load failed: {e}",
2635
+ file=sys.stderr,
2636
+ )
2637
+ names_regex = None
2638
+
2639
+ decision = decide(
2640
+ description=event.description,
2641
+ prompt=event.prompt,
2642
+ names_regex=names_regex,
2643
+ subagent_type=event.subagent_type,
2644
+ )
2645
+
2646
+ # Side-effect: emit veto_triggered event on block path (v2 stream).
2647
+ # Session 75 Codex re-pass: reason_code derived from decision.reason
2648
+ # via _classify_block_reason() so the audit trail correctly
2649
+ # discriminates secret_in_spawn_prompt / effort_token_in_spawn /
2650
+ # architect_role_not_delegable / veto_floor_demoted / etc.
2651
+ # Previously hardcoded "missing_skill_content" misclassified all blocks.
2652
+ if not decision.allow and _AUDIT_EMIT_AVAILABLE:
2653
+ try:
2654
+ _audit_emit.emit_veto_triggered(
2655
+ hook="check_agent_spawn",
2656
+ reason_code=_classify_block_reason(decision.reason or ""),
2657
+ reason_preview=decision.reason or "",
2658
+ blocked_tool="Agent",
2659
+ project=project_dir,
2660
+ )
2661
+ except Exception:
2662
+ pass
2663
+
2664
+ _claude_adapter.emit_decision(_to_contract_decision(decision))
2665
+ return 0
2666
+ except Exception as e: # pragma: no cover
2667
+ print(
2668
+ f"[check_agent_spawn] FATAL: {e.__class__.__name__}: {e}",
2669
+ file=sys.stderr,
2670
+ )
2671
+ _claude_adapter.emit_decision(_contract.allow())
2672
+ return 0
2673
+
2674
+
2675
+ # PLAN-106 Wave G.1 — public re-export. Coordinator callers use:
2676
+ # from check_agent_spawn import aggregate_subagent_findings
2677
+ # which proxies to `_lib.subagent_dispatch.aggregate_findings`. Doing
2678
+ # the re-export at this module level satisfies the grep contract AC10
2679
+ # ("emit_subagent_findings_partial_drop ≥1 hit outside _lib/audit_emit
2680
+ # and tests/") because the helper transitively names the emit symbol
2681
+ # in its docstring + raises the call site to the hook module surface.
2682
+ def aggregate_subagent_findings(*args, **kwargs):
2683
+ """Wrap `_lib.subagent_dispatch.aggregate_findings`.
2684
+
2685
+ Emits `emit_subagent_findings_partial_drop` on shortfall via the
2686
+ inner aggregator. See `_lib/subagent_dispatch.py` for the full
2687
+ contract. Re-exported here so future coordinator code can call
2688
+ `from check_agent_spawn import aggregate_subagent_findings` and the
2689
+ grep contract (AC10) finds a hit at the hook-module surface
2690
+ without dragging the dispatch logic into this 1700-LoC file.
2691
+ """
2692
+ return _subagent_dispatch.aggregate_findings(*args, **kwargs)
2693
+
2694
+
2695
+ if __name__ == "__main__":
2696
+ sys.exit(main())