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,3017 @@
1
+ #!/usr/bin/env python3
2
+ """ceo-boot.py — PLAN-065 Phase 3 production session-boot autopilot.
3
+
4
+ Single command at session start that consolidates governance reads + state
5
+ digest + recommendations. Per PLAN-065 §4.3 acceptance:
6
+
7
+ - 15 Tier-S checks dispatched parallel via ThreadPoolExecutor (stdlib)
8
+ - Per-check timeout 500 ms; aggregate wall-clock budget 5 s
9
+ - ``--short`` defaults to cached mode (≤2 s budget; cache-hit ≤200 ms)
10
+ - ``--json`` emits machine-readable digest
11
+ - Idempotent (back-to-back identical mod timestamps + transient failures)
12
+ - Recommendations engine (rule-based; ≤5 items)
13
+ - Audit emit hasattr-guarded — works pre + post canonical ceremony
14
+
15
+ Stdlib only. Python 3.9+.
16
+
17
+ Run from repo root:
18
+
19
+ python3 .claude/scripts/ceo-boot.py # full digest
20
+ python3 .claude/scripts/ceo-boot.py --short # cached top-line
21
+ python3 .claude/scripts/ceo-boot.py --json # machine output
22
+ python3 .claude/scripts/ceo-boot.py --bench # bench harness
23
+
24
+ Slash command: ``/ceo-boot`` (see ``.claude/commands/ceo-boot.md``).
25
+ """
26
+ from __future__ import annotations
27
+
28
+ import argparse
29
+ import hashlib
30
+ import json
31
+ import os
32
+ import re
33
+ import resource
34
+ import subprocess
35
+ import sys
36
+ import time
37
+ import tracemalloc
38
+ import unicodedata
39
+ from concurrent.futures import (
40
+ ThreadPoolExecutor,
41
+ TimeoutError as FuturesTimeout,
42
+ as_completed,
43
+ )
44
+ from pathlib import Path
45
+ from typing import Any, Callable, Dict, List, Optional, Tuple
46
+
47
+ REPO_ROOT = Path(__file__).resolve().parents[2]
48
+
49
+
50
+ # PLAN-087 Wave C.4 — module-level plan-glob cache.
51
+ # Populated lazily on first call to _get_plan_paths(); subsequent calls
52
+ # within the same /ceo-boot subprocess return the cached sorted list.
53
+ # Process-scoped (no TTL); each /ceo-boot invocation is a fresh subprocess
54
+ # so the cache cannot go stale within a single invocation.
55
+ _PLAN_GLOB_CACHE: Optional[List[Path]] = None
56
+
57
+
58
+ def _get_plan_paths() -> List[Path]:
59
+ """Return sorted PLAN-*.md paths, using a module-level cache."""
60
+ global _PLAN_GLOB_CACHE
61
+ if _PLAN_GLOB_CACHE is None:
62
+ _PLAN_GLOB_CACHE = sorted(
63
+ (REPO_ROOT / ".claude" / "plans").glob("PLAN-*.md")
64
+ )
65
+ return _PLAN_GLOB_CACHE
66
+
67
+
68
+ def _reset_plan_glob_cache() -> None:
69
+ """Test helper: clear the cache so a subsequent _get_plan_paths re-globs."""
70
+ global _PLAN_GLOB_CACHE
71
+ _PLAN_GLOB_CACHE = None
72
+ AUDIT_LOG_DEFAULT = (
73
+ Path.home() / ".claude" / "projects" / "ceo-orchestration" / "audit-log.jsonl"
74
+ )
75
+ # Legacy single-file cache (kept for backward compat with S82 MVP).
76
+ CACHE_FILE_DEFAULT = (
77
+ Path.home() / ".claude" / "projects" / "ceo-orchestration" / "cache" / "ceo-boot-digest.json"
78
+ )
79
+ # PLAN-065 §4.3.2 real cache directory — keyed by (HEAD + audit-log mtime + size).
80
+ # Default lives under project state dir so it is excluded from git via
81
+ # ~/.claude/projects layout (parity with audit-log.jsonl). Override via env
82
+ # CEO_BOOT_CACHE_DIR for tests.
83
+ CACHE_DIR_DEFAULT = (
84
+ Path.home() / ".claude" / "projects" / "ceo-orchestration" / "state" / "ceo-boot-cache"
85
+ )
86
+ CACHE_TTL_S = 3600.0 # 1 hour
87
+ CACHE_FILE_SIZE_CAP_BYTES = 100 * 1024 # 100 KB per cache file
88
+ CACHE_DIR_SIZE_CAP_BYTES = 10 * 1024 * 1024 # 10 MB total → LRU eviction
89
+ CACHE_HIT_BUDGET_MS = 200.0 # ≤200 ms wall-clock budget for cache hit
90
+
91
+
92
+ def _cache_dir() -> Path:
93
+ """Resolve cache dir at call time so test env overrides are honored."""
94
+ override = os.environ.get("CEO_BOOT_CACHE_DIR")
95
+ if override:
96
+ return Path(override)
97
+ return CACHE_DIR_DEFAULT
98
+
99
+ # ---- Per-check + aggregate budgets ------------------------------------------
100
+ # Default per-check budget. Most checks are file-walks completing in <300ms;
101
+ # subprocess-bound checks need longer (overrides below). Aggregate is the hard
102
+ # cap for the whole boot.
103
+ PER_CHECK_TIMEOUT_S = 1.0
104
+ AGGREGATE_TIMEOUT_S = 5.0
105
+ MAX_WORKERS = 8
106
+
107
+ # Per-check overrides — PLAN-082 Codex Item A: governance_validate now
108
+ # dispatches `validate-governance.sh --fast --json` (~40 ms typical).
109
+ # Previous full-walk path required 2.5 s ceiling; fast profile fits the
110
+ # default 1.0 s easily, but we keep a small explicit ceiling for cold-start
111
+ # bash + python3 spawn variance on adopter machines.
112
+ PER_CHECK_TIMEOUT_OVERRIDES_S: Dict[str, float] = {
113
+ "governance_validate": 2.0, # fast --json profile (~40-200 ms warm)
114
+ "plans_executing": 1.5, # full plan tree walk
115
+ "plans_reviewed_pending": 1.5,
116
+ "plans_stranded_executing": 2.0, # plan walk + git log subprocess
117
+ "plans_draft": 1.5,
118
+ "audit_v3_backlog": 1.5,
119
+ "dispatch_count_24h": 1.5, # streaming audit-log read
120
+ "skill_unknown_ratio": 1.5, # streaming audit-log read
121
+ "cost_24h_usd": 1.5,
122
+ "sentinels_pending_gpg": 1.0,
123
+ # PLAN-106 Wave F.3 — EXPLICIT 200 ms override per perf R1 P1 fold.
124
+ # Empirical scan time on 2.3 MB log is ~9-54 ms (200-300k json.loads/sec
125
+ # at ~11.5k events); 200 ms gives ~4-9× headroom. Without explicit
126
+ # override the check would inherit the 1.0 s default and a future
127
+ # log-growth regression wouldn't trip an alarm until 1000 ms.
128
+ "confidence_gate_drift_7d": 0.2,
129
+ }
130
+
131
+ # ---- Sentinel mtime cutoff (Codex S82 P2 fix) ------------------------------
132
+ # Sentinels signed before this date are pre-enforcement-era legacy and don't
133
+ # require GPG sign now. Without cutoff, scanning all historical produces
134
+ # eternal noise (30+ pending every boot).
135
+ # 2026-04-22 = first ceremony with mandatory GPG enforcement (S81 ceremony-generator).
136
+ SENTINEL_CUTOFF_EPOCH = 1776816000 # 2026-04-22 00:00:00 UTC (Codex S82 P2 fix: was 1776297600 = 2026-04-16, off by 6d)
137
+
138
+ # ---- Sanitization for recommendations engine inputs (Sec MF-4) ------------
139
+ # audit_emit telemetry (`ceo_boot_emitted` / `ceo_boot_check_skipped` actions)
140
+ # was DEFERRED to PLAN-065 Phase 7.A v1.12.0 ceremony pre-S82. Phase 2 wire
141
+ # (this file): we now CALL the typed wrappers but guard with hasattr() so
142
+ # the script keeps working pre-canonical-merge. After ceremony lands the
143
+ # kernel ceremony for `_KNOWN_ACTIONS` add + 2 emit functions, this guard
144
+ # becomes a no-op false-branch. Field allowlist (Sec MF-3) is enforced
145
+ # ON THE EMIT SIDE in `_lib/audit_emit.py` — this caller passes only the
146
+ # allowlisted fields and never raises on emit failure.
147
+ _HOOKS_DIR = REPO_ROOT / ".claude" / "hooks"
148
+ if str(_HOOKS_DIR) not in sys.path:
149
+ sys.path.insert(0, str(_HOOKS_DIR))
150
+ try:
151
+ from _lib import injection_patterns as _injection_patterns # type: ignore
152
+ except Exception: # noqa: BLE001
153
+ _injection_patterns = None
154
+
155
+ # Fail-soft import: pre-canonical-ceremony, audit_emit module loads but
156
+ # the new symbols may not exist yet. Use hasattr() at call site.
157
+ try:
158
+ from _lib import audit_emit as _audit_emit # type: ignore
159
+ except Exception: # noqa: BLE001
160
+ _audit_emit = None # type: ignore[assignment]
161
+
162
+ # PLAN-135 W1 S3 — settings/env tamper tripwires. The shared resolver
163
+ # `_lib/effective_config.py` (built ONCE for the three consumers S3 / W2 H2
164
+ # / W5 O11 per the debate round-1 shared-module rule) captures its trusted
165
+ # env surface (`IMPORT_TIME_ENV_SNAPSHOT`: ANTHROPIC_* + *DANGEROUSLY*
166
+ # keys) at ITS import time. Importing it HERE — at the top of ceo-boot,
167
+ # before any check dispatch — anchors that snapshot as early as the
168
+ # `trusted_env` import-time pattern allows for this script
169
+ # (check_bash_safety.py precedent: a late-set value injected by a
170
+ # sub-agent/subprocess after this anchor cannot dodge the scan).
171
+ # Fail-soft: a missing module (pre-W1 ceremony / partial install) degrades
172
+ # the `settings_tamper_tripwires` Tier-S check to yellow, never crashes boot.
173
+ try:
174
+ from _lib import effective_config as _effective_config # type: ignore
175
+ except Exception: # noqa: BLE001
176
+ _effective_config = None # type: ignore[assignment]
177
+
178
+ # Frozen copy of the import-time env snapshot (defense-in-depth: a later
179
+ # mutation of the module attribute cannot alter what the check scans).
180
+ try:
181
+ _TAMPER_ENV_SNAPSHOT: Dict[str, str] = (
182
+ dict(_effective_config.IMPORT_TIME_ENV_SNAPSHOT)
183
+ if _effective_config is not None
184
+ else {}
185
+ )
186
+ except Exception: # noqa: BLE001
187
+ _TAMPER_ENV_SNAPSHOT = {}
188
+
189
+
190
+ def _sanitize_for_recs(s: str) -> str:
191
+ """Sanitize a disk-sourced string before recommendation rendering (Sec MF-4).
192
+
193
+ Pipeline (deterministic, applied in order):
194
+
195
+ 1. Coerce non-str → str.
196
+ 2. Strip NUL bytes (defense vs. accidental binary in audit-log).
197
+ 3. NFKC normalize (PLAN-065 Sec MF-4 — collapse homoglyph escapes:
198
+ fullwidth, ligatures, mathematical alphanumerics).
199
+ 4. Length-bound to 200 chars (post-NFKC; NFKC may expand a few code
200
+ points but bound applies to final rendered string).
201
+ 5. injection_patterns scan; substitute [REDACTED-INJECTION-PATTERN] on hit.
202
+ 6. Strip HTML angle brackets + markdown link URL + backticks (defensive
203
+ belt-and-suspenders if patterns library missed a variant).
204
+ """
205
+ if not isinstance(s, str):
206
+ s = str(s)
207
+ # NUL strip pre-NFKC (NFKC preserves NUL otherwise)
208
+ s = s.replace("\x00", "")
209
+ # NFKC homoglyph collapse — must run BEFORE length bound + scan so that
210
+ # fullwidth/ligature variants are normalized to their ASCII canonicals
211
+ # before the pattern scan (otherwise scanner misses them).
212
+ try:
213
+ s = unicodedata.normalize("NFKC", s)
214
+ except (TypeError, ValueError):
215
+ pass
216
+ s = s[:200]
217
+ if _injection_patterns is not None:
218
+ try:
219
+ # Codex S82 P0 #3 (post-patch v2): scan_harness_mimicry returns
220
+ # ScanResult dataclass (.matched bool), NOT iterable. Previous
221
+ # `if hits:` was always truthy → over-redaction of clean strings.
222
+ # Now check .matched attr; fall back to scan_text alias signature.
223
+ scan_fn = (
224
+ getattr(_injection_patterns, "scan_harness_mimicry", None)
225
+ or getattr(_injection_patterns, "scan_text", None)
226
+ )
227
+ if callable(scan_fn):
228
+ result = scan_fn(s)
229
+ # ScanResult has .matched (bool); legacy iterable returns truthy non-empty
230
+ matched = getattr(result, "matched", None)
231
+ if matched is None:
232
+ matched = bool(result) # legacy iterable contract
233
+ if matched:
234
+ return "[REDACTED-INJECTION-PATTERN]"
235
+ except Exception: # noqa: BLE001
236
+ pass
237
+ # Strip HTML angle brackets + markdown link syntax + backticks (defensive)
238
+ s = re.sub(r"[<>`]", "", s)
239
+ s = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", s)
240
+ return s
241
+
242
+
243
+ # ---- Result dataclass-lite -------------------------------------------------
244
+ class CheckResult:
245
+ __slots__ = ("name", "status", "summary", "duration_ms", "detail")
246
+
247
+ def __init__(self, name: str, status: str, summary: str, duration_ms: float, detail: Any = None):
248
+ self.name = name
249
+ self.status = status # green/yellow/red/timeout/error
250
+ self.summary = summary
251
+ self.duration_ms = duration_ms
252
+ self.detail = detail
253
+
254
+
255
+ # ---- 15 Tier-S checks (PoC implementations) -------------------------------
256
+
257
+ def check_plans_executing() -> Tuple[str, str, Any]:
258
+ plans = _get_plan_paths()
259
+ executing: List[str] = []
260
+ for p in plans:
261
+ try:
262
+ text = p.read_text(encoding="utf-8", errors="replace")
263
+ except OSError:
264
+ continue
265
+ m = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
266
+ if not m:
267
+ continue
268
+ if re.search(r"^status:\s*executing\s*$", m.group(1), re.MULTILINE):
269
+ executing.append(p.stem)
270
+ status = "yellow" if executing else "green"
271
+ return status, f"{len(executing)} executing", executing
272
+
273
+
274
+ def check_plans_reviewed_pending() -> Tuple[str, str, Any]:
275
+ plans = _get_plan_paths()
276
+ reviewed: List[str] = []
277
+ for p in plans:
278
+ try:
279
+ text = p.read_text(encoding="utf-8", errors="replace")
280
+ except OSError:
281
+ continue
282
+ m = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
283
+ if not m:
284
+ continue
285
+ if re.search(r"^status:\s*reviewed\s*$", m.group(1), re.MULTILINE):
286
+ reviewed.append(p.stem)
287
+ return ("yellow" if reviewed else "green", f"{len(reviewed)} reviewed", reviewed)
288
+
289
+
290
+ def check_plans_stranded_executing() -> Tuple[str, str, Any]:
291
+ # Subprocess: git log --since=24h --name-only
292
+ try:
293
+ proc = subprocess.run(
294
+ ["git", "-C", str(REPO_ROOT), "log", "--since=24 hours ago", "--name-only", "--pretty=format:"],
295
+ capture_output=True, text=True, timeout=2.0,
296
+ )
297
+ touched = {line.strip() for line in proc.stdout.splitlines() if line.strip()}
298
+ except (subprocess.TimeoutExpired, FileNotFoundError):
299
+ return "yellow", "git unavailable", None
300
+ # Cross-ref against executing plans
301
+ executing_status, _, executing_list = check_plans_executing()
302
+ stranded = [
303
+ plan for plan in executing_list
304
+ if not any(plan in t for t in touched)
305
+ ]
306
+ return ("red" if stranded else "green", f"{len(stranded)} stranded", stranded)
307
+
308
+
309
+ def check_plans_draft() -> Tuple[str, str, Any]:
310
+ plans = _get_plan_paths()
311
+ draft: List[str] = []
312
+ for p in plans:
313
+ try:
314
+ text = p.read_text(encoding="utf-8", errors="replace")
315
+ except OSError:
316
+ continue
317
+ m = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
318
+ if not m:
319
+ continue
320
+ if re.search(r"^status:\s*draft\s*$", m.group(1), re.MULTILINE):
321
+ draft.append(p.stem)
322
+ return "green", f"{len(draft)} draft", draft
323
+
324
+
325
+ def check_audit_log_freshness() -> Tuple[str, str, Any]:
326
+ """Check audit-log freshness + surface errors sidecar signals.
327
+
328
+ F-6.3 (PLAN-113 W7-OPS): also inspects audit-log.errors sidecar so
329
+ that spool_writer FAIL-CLOSED floods become visible at boot time.
330
+ The errors sidecar is resolved via CEO_AUDIT_LOG_ERR env var if set,
331
+ otherwise defaults to audit-log.errors sibling of AUDIT_LOG_DEFAULT.
332
+ Fail-open on any OSError — never blocks boot.
333
+ """
334
+ try:
335
+ st = AUDIT_LOG_DEFAULT.stat()
336
+ except OSError:
337
+ return "yellow", "audit-log missing", None
338
+ age_s = time.time() - st.st_mtime
339
+ age_h = age_s / 3600.0
340
+ size_mb = st.st_size / (1024 * 1024)
341
+
342
+ # F-6.3: inspect audit-log.errors sidecar for write failures.
343
+ errors_path_raw = os.environ.get("CEO_AUDIT_LOG_ERR", "")
344
+ if errors_path_raw:
345
+ errors_path = Path(errors_path_raw)
346
+ else:
347
+ errors_path = AUDIT_LOG_DEFAULT.parent / "audit-log.errors"
348
+
349
+ errors_present = False
350
+ errors_line_count = 0
351
+ try:
352
+ if errors_path.is_file():
353
+ errors_st = errors_path.stat()
354
+ if errors_st.st_size > 0:
355
+ errors_present = True
356
+ # Count lines without reading the full file into memory.
357
+ with errors_path.open("rb") as ef:
358
+ errors_line_count = sum(1 for _ in ef)
359
+ except OSError:
360
+ pass # fail-open
361
+
362
+ detail: Dict[str, Any] = {
363
+ "age_hours": age_h,
364
+ "size_mb": size_mb,
365
+ "errors_present": errors_present,
366
+ "errors_line_count": errors_line_count,
367
+ }
368
+ if errors_present:
369
+ status = "yellow"
370
+ summary = (
371
+ f"{age_h:.1f}h old, {size_mb:.1f} MB "
372
+ f"[audit-log.errors: {errors_line_count} lines]"
373
+ )
374
+ else:
375
+ status = "green" if age_h < 24 else "yellow"
376
+ summary = f"{age_h:.1f}h old, {size_mb:.1f} MB"
377
+ return status, summary, detail
378
+
379
+
380
+ def _iter_audit_events_since(hours: float = 24.0):
381
+ """PoC streaming iterator — single-pass discipline."""
382
+ if not AUDIT_LOG_DEFAULT.exists():
383
+ return
384
+ cutoff = time.time() - hours * 3600
385
+ with AUDIT_LOG_DEFAULT.open("r", encoding="utf-8", errors="replace") as f:
386
+ for line in f:
387
+ line = line.strip()
388
+ if not line:
389
+ continue
390
+ try:
391
+ ev = json.loads(line)
392
+ except json.JSONDecodeError:
393
+ continue
394
+ ts = ev.get("ts") or ev.get("timestamp")
395
+ if not ts:
396
+ continue
397
+ # Best-effort epoch parse
398
+ try:
399
+ from datetime import datetime
400
+ dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
401
+ if dt.timestamp() < cutoff:
402
+ continue
403
+ except (ValueError, TypeError):
404
+ continue
405
+ yield ev
406
+
407
+
408
+ def check_dispatch_count_24h() -> Tuple[str, str, Any]:
409
+ n = sum(
410
+ 1 for ev in _iter_audit_events_since(24)
411
+ if ev.get("action") == "agent_spawn" and not _is_test_pollution_event(ev)
412
+ )
413
+ return "green", f"{n} dispatches/24h", n
414
+
415
+
416
+ # SHA256 of empty string — fingerprint for harness ghost-events that fire
417
+ # PostToolUse on Agent calls with no real payload (ToolSearch probes, canceled
418
+ # spawns, harness-internal invocations). S86 follow-up: these polluted the
419
+ # skill_unknown_ratio detector by inflating denominator with non-dispatches.
420
+ _EMPTY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
421
+
422
+ # S127 follow-up: PLAN-094 / PLAN-094-FOLLOWUP perf benchmarks + drain warmup
423
+ # fixtures emit synthetic `action=agent_spawn` events into the canonical
424
+ # audit-log via `audit_emit_dispatch.emit_generic` (e.g. wave-d-compound-
425
+ # benchmark-full.py:73). They carry a literal `test` discriminant — filter
426
+ # them from spawn-attribution detectors so the ratio reflects real CEO
427
+ # dispatches. Patch B (test redirection via TestEnvContext) is the proper
428
+ # fix; this hygiene patch stops the detector from mis-classifying.
429
+ _TEST_DISCRIMINANTS = ("bench", "warmup", "probe")
430
+
431
+ # S239: governance self-test probes — the `_probe_*` archetypes in the agent
432
+ # registry (`_probe_missing_skill`, `_probe_canonical_edit`, `_probe_architect`)
433
+ # are synthetic spawns whose entire purpose is to exercise the spawn / canonical
434
+ # hooks. They are skill-less by design and do zero real LLM work, yet they emit a
435
+ # genuine `action=agent_spawn` row that carries NO `test` discriminant — so the
436
+ # `test in _TEST_DISCRIMINANTS` line below misses them. Counting such a probe as a
437
+ # governance gap (skill=unknown) or a cache-coverage failure (cache_coverage_bps=0)
438
+ # is the exact false-positive class these filters exist to prevent: a single S237
439
+ # A3 hook-parity probe pinned BOTH skill_unknown_ratio and cache_discipline_alerted
440
+ # to red on an otherwise-idle window. A CLOSED SET of the three registered probe
441
+ # archetypes — not a `_probe_` PREFIX match — keeps this advisory detector from
442
+ # being side-stepped by a real skill-less dispatch that merely names itself
443
+ # `_probe_*` (the prefix would have excluded `_probe_anything`). These are the
444
+ # only `_probe_*` archetypes in the agent registry (Codex S239 review, P2).
445
+ _PROBE_ARCHETYPES = frozenset({
446
+ "_probe_missing_skill",
447
+ "_probe_canonical_edit",
448
+ "_probe_architect",
449
+ })
450
+
451
+
452
+ def _is_test_pollution_event(ev: Dict[str, Any]) -> bool:
453
+ if ev.get("test") in _TEST_DISCRIMINANTS:
454
+ return True
455
+ for key in ("archetype", "subagent_type"):
456
+ if ev.get(key) in _PROBE_ARCHETYPES:
457
+ return True
458
+ return False
459
+
460
+
461
+ def _is_ghost_spawn_event(ev: Dict[str, Any]) -> bool:
462
+ """True iff the agent_spawn event is a harness ghost-event (no real payload).
463
+
464
+ All four conditions must hold simultaneously to avoid false-positives on
465
+ legitimate near-empty dispatches: empty desc, no rail attribution, no
466
+ profile marker, and SHA-of-empty-string desc_hash.
467
+ """
468
+ return (
469
+ ev.get("desc_preview") == ""
470
+ and ev.get("rail") is None
471
+ and ev.get("has_profile") is False
472
+ and ev.get("desc_hash") == _EMPTY_SHA256
473
+ )
474
+
475
+
476
+ def check_skill_unknown_ratio() -> Tuple[str, str, Any]:
477
+ """Detect spawns that should have SKILL injection but didn't.
478
+
479
+ S94 follow-up: excludes intentionally skill-less archetypes —
480
+ `general-purpose` subagent dispatches via the mitigated rail
481
+ (ADR-082) by design have no SKILL.md anchor. They broker cross-
482
+ LLM gate calls and similar utility work; counting them as FPs
483
+ inflates the ratio to 100% during healthy Codex MCP sessions
484
+ and trains the operator to ignore the channel.
485
+
486
+ A spawn counts as "skill missing" ONLY when its `archetype` is
487
+ a custom (non-general-purpose) one AND `skill` is unknown/empty.
488
+ That is the original PLAN-020 ADR-051 governance gap the
489
+ detector was built for.
490
+ """
491
+ total = 0
492
+ unknown = 0
493
+ ghosts_skipped = 0
494
+ skill_less_by_design = 0
495
+ test_pollution_skipped = 0
496
+ for ev in _iter_audit_events_since(24):
497
+ if ev.get("action") != "agent_spawn":
498
+ continue
499
+ if _is_test_pollution_event(ev):
500
+ test_pollution_skipped += 1
501
+ continue
502
+ if _is_ghost_spawn_event(ev):
503
+ ghosts_skipped += 1
504
+ continue
505
+ # Skill-less by design: general-purpose archetype dispatches
506
+ # (mitigated rail per ADR-082) AND built-in subagent types like
507
+ # Explore/Plan/claude-code-guide that have no .claude/agents/<name>.md
508
+ # and so cannot carry a `Loads <skill> skill via reference` phrase
509
+ # (drift-detector contract per S143 lesson). Adding them to
510
+ # _ARCHETYPE_TO_SKILL would violate the contract — exclude here instead.
511
+ # S200: claude/claude-code-guide/statusline-setup are first-party
512
+ # Claude Code built-ins (no .claude/agents anchor); counting them as a
513
+ # governance gap is a false positive — exactly the FP class this filter
514
+ # exists to prevent (else healthy claude-code-guide use trains the
515
+ # operator to ignore the channel).
516
+ _SKILL_LESS_BUILTINS = {
517
+ "general-purpose", "Explore", "Plan",
518
+ "claude", "claude-code-guide", "statusline-setup",
519
+ }
520
+ if (
521
+ ev.get("subagent_type") in _SKILL_LESS_BUILTINS
522
+ and ev.get("archetype") in _SKILL_LESS_BUILTINS
523
+ ):
524
+ skill_less_by_design += 1
525
+ continue
526
+ total += 1
527
+ if ev.get("skill") in (None, "unknown", ""):
528
+ unknown += 1
529
+ if total == 0:
530
+ msg = (
531
+ "no custom-archetype spawns "
532
+ "({s} general-purpose, {g} ghosts, {t} test-pollution)".format(
533
+ s=skill_less_by_design, g=ghosts_skipped, t=test_pollution_skipped,
534
+ )
535
+ )
536
+ return "green", msg, {
537
+ "unknown": 0, "total": 0,
538
+ "ghosts_skipped": ghosts_skipped,
539
+ "skill_less_by_design": skill_less_by_design,
540
+ "test_pollution_skipped": test_pollution_skipped,
541
+ }
542
+ ratio = unknown / total
543
+ status = "red" if ratio > 0.10 else "yellow" if ratio > 0 else "green"
544
+ return status, f"{unknown}/{total} = {ratio:.0%}", {
545
+ "unknown": unknown, "total": total,
546
+ "ghosts_skipped": ghosts_skipped,
547
+ "skill_less_by_design": skill_less_by_design,
548
+ "test_pollution_skipped": test_pollution_skipped,
549
+ }
550
+
551
+
552
+ def check_governance_validate() -> Tuple[str, str, Any]:
553
+ """PLAN-082 Codex Item A: dispatch fast-profile validator.
554
+
555
+ Calls `validate-governance.sh --fast --json` (delegates to
556
+ `validate_governance_fast.py`) with a 1.8 s timeout. Parses JSON
557
+ output; `rc != 0` is the red truth signal (NOT `stdout.count("ERROR")`
558
+ — Codex 6th-option catch: the full validator emits `FAIL:` in some
559
+ sections without printing literal "ERROR", which underclassified
560
+ failures as yellow).
561
+ """
562
+ script = REPO_ROOT / ".claude" / "scripts" / "validate-governance.sh"
563
+ if not script.exists():
564
+ return "yellow", "validate-governance missing", None
565
+ try:
566
+ proc = subprocess.run(
567
+ ["bash", str(script), "--fast", "--json"],
568
+ capture_output=True, text=True, timeout=1.8, cwd=str(REPO_ROOT),
569
+ )
570
+ except subprocess.TimeoutExpired:
571
+ return "red", "validate timeout (fast)", None
572
+ rc = proc.returncode
573
+ try:
574
+ payload = json.loads(proc.stdout) if proc.stdout else {}
575
+ except (json.JSONDecodeError, ValueError):
576
+ payload = {}
577
+ errors = payload.get("errors", []) if isinstance(payload, dict) else []
578
+ warnings = payload.get("warnings", []) if isinstance(payload, dict) else []
579
+ n_err = len(errors) if isinstance(errors, list) else 0
580
+ n_warn = len(warnings) if isinstance(warnings, list) else 0
581
+ # rc != 0 is the red truth (Codex 6th-option catch).
582
+ if rc != 0:
583
+ status = "red"
584
+ summary = f"fast fail: {n_err} error(s)"
585
+ elif n_warn:
586
+ status = "yellow"
587
+ summary = f"fast pass, {n_warn} warn(s)"
588
+ else:
589
+ status = "green"
590
+ summary = "fast pass"
591
+ return status, summary, {"rc": rc, "errors": n_err, "warnings": n_warn, "profile": "fast"}
592
+
593
+
594
+ def check_hook_live_smoke() -> Tuple[str, str, Any]:
595
+ """PLAN-082 Codex Item D replacement for hook_test_baseline.
596
+
597
+ Drops the broken `.claude/cache/hook-tests.json` baseline (never
598
+ populated; required pytest 12 s — not boot-budget feasible). Per
599
+ Codex 6th-option: replace with a cheap live hook smoke — parse
600
+ settings.json + verify referenced hook files exist + executable +
601
+ `py_compile` cleanly. Stdlib only, no pytest.
602
+
603
+ Test provenance (last full pytest pass) moves to Tier-A
604
+ (`tier_a_hook_test_baseline_age` — separate check).
605
+ """
606
+ import py_compile
607
+
608
+ settings = REPO_ROOT / ".claude" / "settings.json"
609
+ if not settings.exists():
610
+ return "yellow", "settings.json missing", None
611
+ try:
612
+ data = json.loads(settings.read_text(encoding="utf-8"))
613
+ except (OSError, json.JSONDecodeError) as exc:
614
+ return "red", f"settings.json parse: {exc.__class__.__name__}", None
615
+
616
+ hooks_table = data.get("hooks") if isinstance(data, dict) else None
617
+ if not isinstance(hooks_table, dict):
618
+ return "yellow", "no hooks table", None
619
+
620
+ shim_re = re.compile(r"_python-hook\.sh[\"']?\s+[\"']?([A-Za-z0-9_./-]+\.py)")
621
+ direct_re = re.compile(r"\.claude/hooks/[A-Za-z0-9_./-]+\.py")
622
+ seen: List[str] = []
623
+ seen_set: set = set()
624
+ for hook_list in hooks_table.values():
625
+ if not isinstance(hook_list, list):
626
+ continue
627
+ for entry in hook_list:
628
+ if not isinstance(entry, dict):
629
+ continue
630
+ inner = entry.get("hooks") if isinstance(entry.get("hooks"), list) else []
631
+ for cmd_obj in inner:
632
+ if not isinstance(cmd_obj, dict):
633
+ continue
634
+ cmd_str = cmd_obj.get("command", "")
635
+ if not isinstance(cmd_str, str):
636
+ continue
637
+ refs: List[str] = []
638
+ refs.extend(direct_re.findall(cmd_str))
639
+ refs.extend(shim_re.findall(cmd_str))
640
+ for raw in refs:
641
+ s = re.sub(r"^\$\{?CLAUDE_PROJECT_DIR\}?/", "", raw)
642
+ if "/" not in s:
643
+ s = f".claude/hooks/{s}"
644
+ if s not in seen_set:
645
+ seen_set.add(s)
646
+ seen.append(s)
647
+
648
+ failures: List[str] = []
649
+ checked = 0
650
+ for rel in seen:
651
+ path = REPO_ROOT / rel
652
+ if not path.is_file():
653
+ failures.append(f"missing:{rel}")
654
+ continue
655
+ try:
656
+ py_compile.compile(str(path), doraise=True)
657
+ except (py_compile.PyCompileError, OSError):
658
+ failures.append(f"compile_fail:{rel}")
659
+ continue
660
+ checked += 1
661
+
662
+ if failures:
663
+ return "red", f"{len(failures)}/{len(seen)} hook(s) broken", {
664
+ "failures": failures[:10], # bound for sanitization
665
+ "checked": checked,
666
+ "total": len(seen),
667
+ }
668
+ if checked == 0:
669
+ return "yellow", "no hooks discovered", {"checked": 0, "total": 0}
670
+ return "green", f"{checked} hook(s) smoke-pass", {"checked": checked, "total": len(seen)}
671
+
672
+
673
+ # Backward-compat alias — some external callers / tests may still import the
674
+ # old name. Live smoke is a strict improvement (no cache dependency, faster).
675
+ check_hook_test_baseline = check_hook_live_smoke
676
+
677
+
678
+ def check_audit_v3_backlog() -> Tuple[str, str, Any]:
679
+ # PoC: count plans with audit_v3_* tags still open
680
+ plans = _get_plan_paths()
681
+ backlog: List[str] = []
682
+ for p in plans:
683
+ try:
684
+ text = p.read_text(encoding="utf-8", errors="replace")
685
+ except OSError:
686
+ continue
687
+ m = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
688
+ if not m:
689
+ continue
690
+ front = m.group(1)
691
+ if "audit_v3" in front and not re.search(r"^status:\s*done\s*$", front, re.MULTILINE):
692
+ backlog.append(p.stem)
693
+ return ("yellow" if backlog else "green", f"{len(backlog)} open", backlog)
694
+
695
+
696
+ def check_sentinels_pending_gpg() -> Tuple[str, str, Any]:
697
+ """Count GPG-pending sentinels post-cutoff.
698
+
699
+ Codex S82 P2 fix: previous impl scanned ALL historical sentinels with no
700
+ date cutoff → 30+ pending entries every boot from PLAN-030/031/039 round-1
701
+ pre-enforcement era. Now applies SENTINEL_CUTOFF_EPOCH (2026-04-22) to
702
+ skip legacy. Also Codex S82 P2 sorted glob for CR-N7 stable ordering.
703
+ """
704
+ pending: List[str] = []
705
+ plans_dir = REPO_ROOT / ".claude" / "plans"
706
+ for approved in sorted(plans_dir.glob("PLAN-*/architect/round-*/approved.md")):
707
+ try:
708
+ mtime = approved.stat().st_mtime
709
+ except OSError:
710
+ continue
711
+ if mtime < SENTINEL_CUTOFF_EPOCH:
712
+ continue # pre-enforcement legacy
713
+ if not (approved.parent / "approved.md.asc").exists():
714
+ pending.append(str(approved.relative_to(REPO_ROOT)))
715
+ return ("yellow" if pending else "green", f"{len(pending)} pending", pending)
716
+
717
+
718
+ def check_rc_hold_aged() -> Tuple[str, str, Any]:
719
+ release_md = REPO_ROOT / "RELEASE.md"
720
+ rc_hold = REPO_ROOT / "RC-HOLD.md"
721
+ target = release_md if release_md.exists() else rc_hold if rc_hold.exists() else None
722
+ if target is None:
723
+ return "green", "no rc-hold doc", None
724
+ text = target.read_text(encoding="utf-8", errors="replace")
725
+ # PoC: count rc-hold-waiver entries (real impl would parse dates)
726
+ n = len(re.findall(r"rc-hold-waiver", text))
727
+ return ("yellow" if n else "green", f"{n} rc-hold-waiver entries", n)
728
+
729
+
730
+ # E7-F5 (PLAN-120-FOLLOWUP): real daily-USD burn-rate thresholds.
731
+ # Previously this check returned "green" unconditionally (Potemkin stub) — it
732
+ # tallied cost_usd over 24h but never compared it against a budget, so a
733
+ # runaway burn never surfaced at boot. We now apply a yellow/red ceiling.
734
+ # Both bounds are env-overridable (adopter sessions differ wildly in cost);
735
+ # defaults are calibrated to a typical heavy CEO session. Fail-OPEN: any
736
+ # parse/lookup error degrades to the default ceiling, never raises, and an
737
+ # empty/zero-cost log stays green (the steady-state).
738
+ _COST_YELLOW_USD_DEFAULT = 50.0
739
+ _COST_RED_USD_DEFAULT = 150.0
740
+
741
+
742
+ def _cost_threshold(env_var: str, default: float) -> float:
743
+ """Read a positive float ceiling from env; fall back to default fail-open."""
744
+ raw = os.environ.get(env_var, "")
745
+ if not raw:
746
+ return default
747
+ try:
748
+ val = float(raw.strip())
749
+ except (TypeError, ValueError):
750
+ return default
751
+ # Reject non-finite / non-positive overrides (would defeat the gate).
752
+ if val != val or val == float("inf") or val <= 0:
753
+ return default
754
+ return val
755
+
756
+
757
+ def check_cost_24h_usd() -> Tuple[str, str, Any]:
758
+ """Sum cost_usd over 24h and gate against env-overridable USD ceilings.
759
+
760
+ yellow at CEO_BOOT_COST_YELLOW_USD (default $50/24h); red at
761
+ CEO_BOOT_COST_RED_USD (default $150/24h). Fail-open: no cost datapoints
762
+ or unreadable log => green. Thresholds are advisory burn-rate alerts
763
+ (ADR-064 50/80/95% doctrine), never a hard block.
764
+ """
765
+ total = 0.0
766
+ samples = 0
767
+ for ev in _iter_audit_events_since(24):
768
+ c = ev.get("cost_usd")
769
+ if isinstance(c, (int, float)) and not isinstance(c, bool):
770
+ total += float(c)
771
+ samples += 1
772
+ yellow = _cost_threshold("CEO_BOOT_COST_YELLOW_USD", _COST_YELLOW_USD_DEFAULT)
773
+ red = _cost_threshold("CEO_BOOT_COST_RED_USD", _COST_RED_USD_DEFAULT)
774
+ # Guard against an inverted override (yellow >= red): keep red as the
775
+ # higher bound so the status ladder stays monotonic.
776
+ if yellow >= red:
777
+ yellow = min(yellow, red)
778
+ if samples == 0:
779
+ status = "green"
780
+ elif total >= red:
781
+ status = "red"
782
+ elif total >= yellow:
783
+ status = "yellow"
784
+ else:
785
+ status = "green"
786
+ return status, f"${total:.2f}/24h", {
787
+ "total_usd": round(total, 4),
788
+ "samples": samples,
789
+ "yellow_usd": yellow,
790
+ "red_usd": red,
791
+ }
792
+
793
+
794
+ def check_active_plan_burn_ratio() -> Tuple[str, str, Any]:
795
+ # PoC: find first executing plan, parse budget_tokens, sum tokens from log
796
+ _, _, executing = check_plans_executing()
797
+ if not executing:
798
+ return "green", "no active plan", None
799
+ plan_id = executing[0].split("-")[0] + "-" + executing[0].split("-")[1] if executing else None
800
+ if not plan_id:
801
+ return "green", "no plan id", None
802
+ plan_path = REPO_ROOT / ".claude" / "plans" / f"{executing[0]}.md"
803
+ try:
804
+ text = plan_path.read_text(encoding="utf-8", errors="replace")
805
+ except OSError:
806
+ return "yellow", "plan unreadable", None
807
+ m = re.search(r"^budget_tokens:\s*(.+?)$", text, re.MULTILINE)
808
+ if not m:
809
+ return "yellow", "no budget_tokens", None
810
+ return "green", f"budget {m.group(1).strip()}", {"plan": plan_id, "budget_raw": m.group(1).strip()}
811
+
812
+
813
+ def check_adrs_stale_proposed() -> Tuple[str, str, Any]:
814
+ adrs = sorted((REPO_ROOT / ".claude" / "adr").glob("ADR-*.md"))
815
+ proposed_old: List[str] = []
816
+ now = time.time()
817
+ for a in adrs:
818
+ try:
819
+ text = a.read_text(encoding="utf-8", errors="replace")
820
+ mtime = a.stat().st_mtime
821
+ except OSError:
822
+ continue
823
+ m = re.match(r"^---\s*\n(.*?)\n---\s*\n", text, re.DOTALL)
824
+ if not m:
825
+ continue
826
+ if re.search(r"^status:\s*proposed\s*$", m.group(1), re.MULTILINE):
827
+ age_d = (now - mtime) / 86400
828
+ if age_d > 30:
829
+ proposed_old.append(a.stem)
830
+ return ("yellow" if proposed_old else "green", f"{len(proposed_old)} proposed >30d", proposed_old)
831
+
832
+
833
+ # ---- 10 Tier-A checks (--verbose mode; PLAN-065 §4.3.3) ------------------
834
+ # Selection rationale (each picked for high signal-to-cost ratio + non-overlap
835
+ # with Tier-S; see PLAN-065 §4.3.3):
836
+ # tier_a_debate_transcripts — debate hygiene (Round-1 archetype output)
837
+ # tier_a_lessons_30d — lesson velocity (memory growth pulse)
838
+ # tier_a_spec_version_drift — VERSION ↔ SPEC/v*/VERSION mismatch
839
+ # tier_a_npm_version_match — package.json vs VERSION mismatch
840
+ # tier_a_waivers_count — waivers/*.md aggregate (rc-hold + cosmetic)
841
+ # tier_a_adrs_recent_status — ADR-098..104 reservation slots tracking
842
+ # tier_a_cache_hit_rate_24h — ceo-boot-emitted cache_hit ratio (self-loop)
843
+ # tier_a_hook_test_baseline_age — last-cached hook-test baseline age (S81 cache)
844
+ # tier_a_sentinel_signers_tracked — .claude/state/sentinel-signers.txt git-tracked?
845
+ # tier_a_gitignore_state_excluded — .gitignore covers state/ dir?
846
+
847
+
848
+ def check_tier_a_debate_transcripts() -> Tuple[str, str, Any]:
849
+ """Count debate transcripts produced in last 24h (forensic hygiene)."""
850
+ debate_root = REPO_ROOT / ".claude" / "plans"
851
+ n = 0
852
+ cutoff = time.time() - 86400
853
+ for transcript in debate_root.rglob("debate/*/round-*.md"):
854
+ try:
855
+ if transcript.stat().st_mtime >= cutoff:
856
+ n += 1
857
+ except OSError:
858
+ continue
859
+ # Status green regardless — informational. Yellow if zero AND there is an
860
+ # executing plan (suggests work without debate trail).
861
+ _, _, executing = check_plans_executing()
862
+ if n == 0 and executing:
863
+ return "yellow", f"0 transcripts/24h ({len(executing)} executing)", n
864
+ return "green", f"{n} transcripts/24h", n
865
+
866
+
867
+ def check_tier_a_lessons_30d() -> Tuple[str, str, Any]:
868
+ """Count lessons added in memory dir over 30d (informational)."""
869
+ # Derive the Claude Code project slug from the project dir (absolute path
870
+ # with "/" -> "-") instead of hard-coding the meta-repo's slug, so this
871
+ # resolves correctly in any install. (Previously hard-coded the Owner's
872
+ # absolute home path, which broke for every other install and tripped the
873
+ # contamination guard.)
874
+ project_dir = os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd()
875
+ slug = str(Path(project_dir).resolve()).replace("/", "-")
876
+ mem_dir = Path.home() / ".claude" / "projects" / slug / "memory"
877
+ if not mem_dir.exists():
878
+ return "green", "memory dir absent", 0
879
+ cutoff = time.time() - 30 * 86400
880
+ n = 0
881
+ for f in mem_dir.glob("*.md"):
882
+ try:
883
+ if f.stat().st_mtime >= cutoff:
884
+ n += 1
885
+ except OSError:
886
+ continue
887
+ return "green", f"{n} lessons/30d", n
888
+
889
+
890
+ def check_tier_a_spec_version_drift() -> Tuple[str, str, Any]:
891
+ """VERSION file vs latest SPEC/v*/VERSION agreement (informational)."""
892
+ version_file = REPO_ROOT / "VERSION"
893
+ if not version_file.exists():
894
+ return "yellow", "VERSION missing", None
895
+ try:
896
+ repo_v = version_file.read_text(encoding="utf-8", errors="replace").strip()
897
+ except OSError:
898
+ return "yellow", "VERSION unreadable", None
899
+ spec_root = REPO_ROOT / "SPEC"
900
+ if not spec_root.exists():
901
+ return "green", f"repo {repo_v}, no SPEC dir", repo_v
902
+ spec_versions = sorted(p.name for p in spec_root.iterdir() if p.is_dir() and p.name.startswith("v"))
903
+ if not spec_versions:
904
+ return "green", f"repo {repo_v}, no SPEC versions", repo_v
905
+ return "green", f"repo {repo_v}, spec {','.join(spec_versions[-2:])}", {
906
+ "repo_version": repo_v, "spec_versions": spec_versions,
907
+ }
908
+
909
+
910
+ def check_tier_a_npm_version_match() -> Tuple[str, str, Any]:
911
+ """package.json version vs VERSION file (Codex S79 P1 finding)."""
912
+ version_file = REPO_ROOT / "VERSION"
913
+ pkg_file = REPO_ROOT / "package.json"
914
+ if not version_file.exists() or not pkg_file.exists():
915
+ return "green", "no npm artifacts", None
916
+ try:
917
+ repo_v = version_file.read_text(encoding="utf-8", errors="replace").strip()
918
+ pkg = json.loads(pkg_file.read_text(encoding="utf-8", errors="replace"))
919
+ except (OSError, json.JSONDecodeError):
920
+ return "yellow", "parse error", None
921
+ pkg_v = pkg.get("version", "")
922
+ if pkg_v == repo_v:
923
+ return "green", f"match {repo_v}", {"version": repo_v}
924
+ return "red", f"drift: VERSION={repo_v} package.json={pkg_v}", {
925
+ "repo_version": repo_v, "pkg_version": pkg_v,
926
+ }
927
+
928
+
929
+ def check_tier_a_waivers_count() -> Tuple[str, str, Any]:
930
+ """Aggregate waivers/*.md count (rc-hold + cosmetic + audit)."""
931
+ waivers_dir = REPO_ROOT / "waivers"
932
+ if not waivers_dir.exists():
933
+ return "green", "no waivers dir", 0
934
+ waivers = list(waivers_dir.glob("*.md"))
935
+ n = len(waivers)
936
+ status = "yellow" if n > 5 else "green"
937
+ return status, f"{n} waivers", n
938
+
939
+
940
+ def check_tier_a_adrs_recent_status() -> Tuple[str, str, Any]:
941
+ """ADR-098..104 status tracker (PLAN-065 reserved slots — drift detector)."""
942
+ adr_dir = REPO_ROOT / ".claude" / "adr"
943
+ statuses: Dict[str, str] = {}
944
+ for adr_num in range(98, 105):
945
+ matches = list(adr_dir.glob(f"ADR-{adr_num:03d}-*.md"))
946
+ if not matches:
947
+ statuses[f"ADR-{adr_num:03d}"] = "missing"
948
+ continue
949
+ try:
950
+ text = matches[0].read_text(encoding="utf-8", errors="replace")
951
+ except OSError:
952
+ statuses[f"ADR-{adr_num:03d}"] = "unreadable"
953
+ continue
954
+ m = re.search(r"^status:\s*([a-zA-Z\-]+)\s*$", text, re.MULTILINE)
955
+ statuses[f"ADR-{adr_num:03d}"] = m.group(1).lower() if m else "unknown"
956
+ accepted = sum(1 for v in statuses.values() if v == "accepted")
957
+ return "green", f"{accepted}/{len(statuses)} accepted", statuses
958
+
959
+
960
+ def check_tier_a_cache_hit_rate_24h() -> Tuple[str, str, Any]:
961
+ """ceo_boot_emitted cache_hit ratio over 24h (self-observation)."""
962
+ total = 0
963
+ hits = 0
964
+ for ev in _iter_audit_events_since(24):
965
+ if ev.get("action") != "ceo_boot_emitted":
966
+ continue
967
+ total += 1
968
+ if ev.get("cache_hit"):
969
+ hits += 1
970
+ if total == 0:
971
+ return "green", "no boots/24h", 0
972
+ ratio = hits / total
973
+ return "green", f"{hits}/{total} = {ratio:.0%} cache-hit", {
974
+ "hits": hits, "total": total, "ratio": ratio,
975
+ }
976
+
977
+
978
+ def check_tier_a_hook_test_baseline_age() -> Tuple[str, str, Any]:
979
+ """Age of cached hook-test baseline file (S81 cache convention)."""
980
+ cache = REPO_ROOT / ".claude" / "cache" / "hook-tests.json"
981
+ if not cache.exists():
982
+ return "yellow", "no cached baseline", None
983
+ try:
984
+ st = cache.stat()
985
+ except OSError:
986
+ return "yellow", "stat failed", None
987
+ age_h = (time.time() - st.st_mtime) / 3600.0
988
+ status = "green" if age_h < 168 else "yellow" # 7d window
989
+ return status, f"{age_h:.1f}h old", {"age_hours": age_h}
990
+
991
+
992
+ def check_tier_a_sentinel_signers_tracked() -> Tuple[str, str, Any]:
993
+ """sentinel-signers.txt presence + git-tracked status."""
994
+ candidates = [
995
+ REPO_ROOT / ".claude" / "state" / "sentinel-signers.txt",
996
+ REPO_ROOT / "sentinel-signers.txt",
997
+ ]
998
+ found: Optional[Path] = None
999
+ for c in candidates:
1000
+ if c.exists():
1001
+ found = c
1002
+ break
1003
+ if found is None:
1004
+ return "yellow", "sentinel-signers.txt missing", None
1005
+ try:
1006
+ proc = subprocess.run(
1007
+ ["git", "-C", str(REPO_ROOT), "ls-files", "--error-unmatch", str(found.relative_to(REPO_ROOT))],
1008
+ capture_output=True, text=True, timeout=1.5,
1009
+ )
1010
+ tracked = (proc.returncode == 0)
1011
+ except (subprocess.TimeoutExpired, FileNotFoundError, ValueError):
1012
+ return "yellow", "git unavailable", None
1013
+ return ("green" if tracked else "yellow",
1014
+ f"present, tracked={tracked}",
1015
+ {"path": str(found.relative_to(REPO_ROOT)), "tracked": tracked})
1016
+
1017
+
1018
+ def check_tier_a_gitignore_state_excluded() -> Tuple[str, str, Any]:
1019
+ """.gitignore covers state/ dir (LRU cache + sentinels safety)."""
1020
+ gi = REPO_ROOT / ".gitignore"
1021
+ if not gi.exists():
1022
+ return "yellow", ".gitignore missing", None
1023
+ try:
1024
+ text = gi.read_text(encoding="utf-8", errors="replace")
1025
+ except OSError:
1026
+ return "yellow", ".gitignore unreadable", None
1027
+ # Match leading patterns: state/, .claude/state/, /state, etc.
1028
+ has_state = bool(re.search(r"(?m)^\s*\.?/?(?:\.claude/)?state/", text))
1029
+ return ("green" if has_state else "yellow",
1030
+ f"state/ excluded={has_state}",
1031
+ {"covered": has_state})
1032
+
1033
+
1034
+ # ---- Registry --------------------------------------------------------------
1035
+
1036
+
1037
+ def check_tier_policy_misrouting_24h() -> Tuple[str, str, Any]:
1038
+ """16th Tier-S check — delegates to standalone hook module (PLAN-091 W2.1).
1039
+
1040
+ Lazy-imports ``.claude/hooks/check_tier_policy_misrouting_24h.py`` so
1041
+ that the standalone module can also be invoked as a CLI smoke-test
1042
+ (``python3 .claude/hooks/check_tier_policy_misrouting_24h.py``).
1043
+ Any import-time failure surfaces as a `yellow` status (fail-soft
1044
+ Tier-S contract); the dispatcher's outer try/except still wraps the
1045
+ inner call for additional defense-in-depth.
1046
+ """
1047
+ try:
1048
+ hooks_dir = REPO_ROOT / ".claude" / "hooks"
1049
+ if str(hooks_dir) not in sys.path:
1050
+ sys.path.insert(0, str(hooks_dir))
1051
+ from check_tier_policy_misrouting_24h import ( # type: ignore
1052
+ check_tier_policy_misrouting_24h as _impl,
1053
+ )
1054
+ return _impl()
1055
+ except Exception as exc: # noqa: BLE001 (Tier-S fail-soft floor)
1056
+ return "yellow", f"tier_policy_misrouting import error: {exc}", None
1057
+
1058
+
1059
+ def check_cache_discipline_alerted() -> Tuple[str, str, Any]:
1060
+ """17th Tier-S check — prompt-cache coverage detection (PLAN-093 Wave C.2).
1061
+
1062
+ Surfaces ``cache_discipline_alerted`` when prompt-cache coverage falls
1063
+ below 0.7 over the last 24h of audit-log events. Detection is heuristic:
1064
+ any audit row (``agent_spawn`` action) carrying a ``cache_coverage_bps``
1065
+ numeric field is averaged; rows without the field are ignored. Absent
1066
+ any datapoints, status is green with a "no data" summary (fail-soft
1067
+ Tier-S contract).
1068
+
1069
+ Field alignment (F-5-5.1-0624274e fix): audit_log.py emits the
1070
+ cache-coverage metric derived from usage_metadata cache_read /
1071
+ (cache_read + cache_creation + uncached); older code read the
1072
+ non-existent ``cache_hit_rate`` field, causing the gate to always
1073
+ return green/"no data". This fix aligns the reader to the emitted field.
1074
+ PLAN-118 WS-E (S181): the emitted field is now ``cache_coverage_bps``
1075
+ (integer basis-points); this reader reads it (÷10000) and falls back to
1076
+ the legacy ``cache_coverage`` float for events emitted before the fix.
1077
+
1078
+ Emits ``cache_discipline_alerted`` via ``emit_generic`` on yellow/red
1079
+ so downstream analytics can correlate with /ceo-boot runs. Action
1080
+ name is registered at ``audit_emit.py:440`` (PLAN-088 canonical).
1081
+ """
1082
+ threshold = 0.70
1083
+ try:
1084
+ rates: List[float] = []
1085
+ for ev in _iter_audit_events_since(hours=24.0):
1086
+ # S239: skip synthetic governance-probe / benchmark spawns — they
1087
+ # carry cache_coverage_bps=0 by construction (no real cached LLM
1088
+ # call), so a probe-only 24h window would pin this gate to red (FP).
1089
+ if _is_test_pollution_event(ev):
1090
+ continue
1091
+ # PLAN-118 WS-E (S181): primary field is now ``cache_coverage_bps``
1092
+ # (integer basis-points, ratio × 10000) — the legacy float
1093
+ # ``cache_coverage`` was dropped because it broke the HMAC chain.
1094
+ # Read bps first, fall back to the legacy float for events emitted
1095
+ # before the fix (the 24h window straddles the transition; without
1096
+ # the fallback this Tier-S gate would go silently dead again —
1097
+ # the exact F-5-5.1-0624274e failure mode this check exists to avoid).
1098
+ v_bps = ev.get("cache_coverage_bps")
1099
+ if isinstance(v_bps, int) and not isinstance(v_bps, bool) and 0 <= v_bps <= 10000:
1100
+ rates.append(v_bps / 10000.0)
1101
+ continue
1102
+ # F-5-5.1-0624274e: legacy float field (pre-PLAN-118 events).
1103
+ v = ev.get("cache_coverage")
1104
+ if isinstance(v, (int, float)) and not isinstance(v, bool) and 0.0 <= float(v) <= 1.0:
1105
+ rates.append(float(v))
1106
+ if not rates:
1107
+ return "green", "no cache_coverage datapoints", {"samples": 0}
1108
+ avg = sum(rates) / len(rates)
1109
+ if avg < threshold:
1110
+ try:
1111
+ if _audit_emit is not None and hasattr(
1112
+ _audit_emit, "emit_cache_discipline_alerted"
1113
+ ):
1114
+ _audit_emit.emit_cache_discipline_alerted(
1115
+ hit_rate_basis_points=max(
1116
+ 0, min(1000, int(round(avg * 1000)))
1117
+ ),
1118
+ floor_basis_points=max(
1119
+ 0, min(1000, int(round(threshold * 1000)))
1120
+ ),
1121
+ session_count_24h=len(rates),
1122
+ below_floor=True,
1123
+ opted_out=False,
1124
+ )
1125
+ except Exception: # noqa: BLE001 (Tier-S fail-soft)
1126
+ pass
1127
+ return "red", f"cache_coverage {avg:.2f} < {threshold}", {
1128
+ "avg_rate": avg,
1129
+ "samples": len(rates),
1130
+ }
1131
+ return "green", f"cache_coverage {avg:.2f} ok", {
1132
+ "avg_rate": avg,
1133
+ "samples": len(rates),
1134
+ }
1135
+ except Exception as exc: # noqa: BLE001 (Tier-S fail-soft)
1136
+ return "yellow", f"cache_discipline_alerted error: {exc}", None
1137
+
1138
+
1139
+ # PLAN-093 Wave C.5/C.6 canonical persona × task matrix.
1140
+ #
1141
+ # S127 cadence-amendment (Codex R2 thread `019e33a3` AMEND verdict
1142
+ # `PHASE-1+2-WITH-(c)`): the 4×4 matrix is demoted from gate-eligible to
1143
+ # permanent observability. RED authority moves to a future event-driven
1144
+ # demand ledger (`PLAN-104-persona-demand-ledger`); see
1145
+ # `PLAN-093-FOLLOWUP-cadence-amendment.md` for the full doctrine record.
1146
+ _VETO_FLOOR_PERSONAS = (
1147
+ "code-reviewer", "security-engineer", "qa-architect",
1148
+ "threat-detection-engineer",
1149
+ )
1150
+ _PERSONA_TASK_TYPES = ("review", "vet", "test", "detect")
1151
+ _VETO_FLOOR_PERSONAS_LOWER = frozenset(p.lower() for p in _VETO_FLOOR_PERSONAS)
1152
+
1153
+ # PLAN-112-FOLLOWUP-persona-routing-wire W4 — F-5.4-tasktype-pollution.
1154
+ # `_score_persona_coverage` previously counted task_type from ANY audit
1155
+ # event whose archetype matched a VETO-floor persona. Unrelated emitters
1156
+ # (notably `model_routing_advised`, which carries archetype + a bogus
1157
+ # task_type like `frontmatter`/`M`) inflated the denominator + skewed cells.
1158
+ # Restrict contributing events to the GENUINE persona-dispatch actions:
1159
+ # - `persona_coverage_synthesized` (SPEC/v1/audit-log.schema.md:319 —
1160
+ # carries `archetype` + `task_type`, the only fields the scorer reads)
1161
+ # - `persona_demand_*` (the persona-demand ledger family; PLAN-104)
1162
+ # NOTE: `persona_dispatch` does NOT exist and must not be referenced.
1163
+ _PERSONA_DISPATCH_ACTION_PREFIXES = ("persona_demand_",)
1164
+ _PERSONA_DISPATCH_ACTIONS = frozenset({"persona_coverage_synthesized"})
1165
+
1166
+
1167
+ def _is_persona_dispatch_event(ev: Dict[str, Any]) -> bool:
1168
+ """True iff `ev` is a genuine persona-dispatch event (F-5.4 filter).
1169
+
1170
+ Defensive: a missing/non-str `action` -> False (excluded).
1171
+ """
1172
+ action = ev.get("action")
1173
+ if not isinstance(action, str):
1174
+ return False
1175
+ if action in _PERSONA_DISPATCH_ACTIONS:
1176
+ return True
1177
+ return any(action.startswith(p) for p in _PERSONA_DISPATCH_ACTION_PREFIXES)
1178
+
1179
+
1180
+ def _normalize_persona_role(ev: Dict[str, Any]) -> str:
1181
+ """Case-folded canonical role extracted from any audit_log emission surface.
1182
+
1183
+ PLAN-093 Wave C.5 originally read only ``archetype`` / ``persona``. Codex
1184
+ R2 thread `019e33a3` AMEND #4: audit-log events emitted from
1185
+ ``audit_log.py`` carry the role on ``subagent_type`` (canonical) and
1186
+ ``dispatch_archetype_hint`` (resolved-from-prompt) — narrow read missed
1187
+ those surfaces. First non-empty match wins, case-folded.
1188
+ """
1189
+ for field in ("archetype", "persona", "subagent_type",
1190
+ "dispatch_archetype_hint"):
1191
+ val = ev.get(field)
1192
+ if isinstance(val, str) and val.strip():
1193
+ return val.strip().lower()
1194
+ return ""
1195
+
1196
+
1197
+ def _score_persona_coverage(hours: float) -> Dict[str, int]:
1198
+ """Compute 4×4 persona × task coverage over a rolling audit-log window.
1199
+
1200
+ Returns canonical metrics dict suitable for both the 24h session-smoke
1201
+ check and the 7d trend check. Phase 1 (S127): `eligible_demand_events`
1202
+ is unconditionally 0 — that signal is produced by the demand ledger
1203
+ scheduled for Phase 2 (`PLAN-104-persona-demand-ledger`).
1204
+ """
1205
+ canonical_by_lower = {p.lower(): p for p in _VETO_FLOOR_PERSONAS}
1206
+ seen: Dict[str, set] = {p: set() for p in _VETO_FLOOR_PERSONAS}
1207
+ events_with_target = 0
1208
+ for ev in _iter_audit_events_since(hours=hours):
1209
+ # PLAN-112-FOLLOWUP-persona-routing-wire W4 — F-5.4 task_type
1210
+ # pollution fix. Only genuine persona-dispatch events contribute to
1211
+ # coverage; unrelated emitters carrying a VETO-floor archetype +
1212
+ # a bogus task_type are excluded entirely.
1213
+ if not _is_persona_dispatch_event(ev):
1214
+ continue
1215
+ role = _normalize_persona_role(ev)
1216
+ if role not in _VETO_FLOOR_PERSONAS_LOWER:
1217
+ continue
1218
+ events_with_target += 1
1219
+ task_type = ev.get("task_type") or ev.get("phase") or ""
1220
+ if not isinstance(task_type, str) or not task_type:
1221
+ continue
1222
+ task_lower = task_type.lower()
1223
+ for t in _PERSONA_TASK_TYPES:
1224
+ if t in task_lower:
1225
+ seen[canonical_by_lower[role]].add(t)
1226
+ break
1227
+ cells_covered = sum(len(v) for v in seen.values())
1228
+ total_cells = len(_VETO_FLOOR_PERSONAS) * len(_PERSONA_TASK_TYPES)
1229
+ score_pct = (cells_covered / total_cells * 100.0) if total_cells else 0.0
1230
+ return {
1231
+ "cells_covered": cells_covered,
1232
+ "total_cells": total_cells,
1233
+ "events_with_target_archetype": events_with_target,
1234
+ "score_x100": int(round(score_pct * 100)),
1235
+ }
1236
+
1237
+
1238
+ def _persona_coverage_status(
1239
+ metrics: Dict[str, int],
1240
+ *,
1241
+ window_hours: int,
1242
+ ) -> Tuple[str, str]:
1243
+ """Decide status + summary for a persona-coverage check window.
1244
+
1245
+ Phase 1 (S127) semantic — pure observability, never red:
1246
+
1247
+ ``events_with_target_archetype == 0`` → green "no VETO-floor
1248
+ dispatches in <h>h" (mirrors demand-driven empty-green pattern of
1249
+ the other 17 Tier-S checks — Codex R2 AMEND #1).
1250
+
1251
+ ``events_with_target_archetype > 0`` → yellow "M/16 cells covered
1252
+ in <h>h" (matrix demoted to max-yellow forever per Codex R2 AMEND
1253
+ #2 — RED authority reserved for Phase 2 demand-driven gate).
1254
+ """
1255
+ events_target = metrics["events_with_target_archetype"]
1256
+ cells = metrics["cells_covered"]
1257
+ total = metrics["total_cells"]
1258
+ if events_target == 0:
1259
+ return "green", f"no VETO-floor dispatches in {window_hours}h"
1260
+ return "yellow", f"{cells}/{total} cells covered in {window_hours}h"
1261
+
1262
+
1263
+ def _emit_persona_coverage(
1264
+ metrics: Dict[str, int],
1265
+ *,
1266
+ window_hours: int,
1267
+ ) -> None:
1268
+ """Emit ``ceo_boot_persona_coverage_score`` audit event (shared by 24h+7d).
1269
+
1270
+ S127 Phase 1 scope-(b) — emits only the 3 fields already in the kernel
1271
+ allowlist (`score_x100`, `cells_covered`, `total_cells`). The new fields
1272
+ (`window_hours`, `events_with_target_archetype`, `eligible_demand_events`)
1273
+ surface in the /ceo-boot result dict + summary text but are NOT persisted
1274
+ in the audit-log under Phase 1. Deferring the
1275
+ ``_CEO_BOOT_PERSONA_COVERAGE_ALLOWLIST`` kernel amendment to an Owner
1276
+ ceremony — bundle with the Phase 2 demand-ledger ship (which needs more
1277
+ kernel surface anyway), avoiding two separate kernel-override events.
1278
+
1279
+ The `window_hours` value is consumed by `_persona_coverage_status` for
1280
+ the summary string; downstream audit-log consumers reconstructing
1281
+ cadence from emitted events would need to infer it (or wait for
1282
+ Phase 2 ship).
1283
+ """
1284
+ del window_hours # See docstring; not emitted under Phase 1 scope-(b).
1285
+ try:
1286
+ if _audit_emit is not None and hasattr(_audit_emit, "emit_generic"):
1287
+ # score_x100 is integer basis-points (0-10000); floats break
1288
+ # canonical JSON HMAC chain — Codex S123 iter-2 P1.
1289
+ _audit_emit.emit_generic(
1290
+ "ceo_boot_persona_coverage_score",
1291
+ score_x100=metrics["score_x100"],
1292
+ cells_covered=metrics["cells_covered"],
1293
+ total_cells=metrics["total_cells"],
1294
+ )
1295
+ except Exception: # noqa: BLE001 (Tier-S fail-soft)
1296
+ pass
1297
+
1298
+
1299
+ def check_ceo_boot_persona_coverage_score() -> Tuple[str, str, Any]:
1300
+ """18th Tier-S check — persona × task coverage at 24h cadence (session-smoke).
1301
+
1302
+ Originally PLAN-093 Wave C.5/C.6 AC10 — sourced 24h of audit-log and
1303
+ scored a 4×4 matrix with `<50% red` thresholds. S127 cadence-amendment
1304
+ (Codex R2 thread `019e33a3` AMEND `PHASE-1+2-WITH-(c)`): demoted to
1305
+ permanent observability. Never red, never gate-failing. RED authority
1306
+ moves to `PLAN-104-persona-demand-ledger` (Phase 2 event-driven gate).
1307
+
1308
+ Companion: `check_persona_atrophy_7d` at 168h cadence for trend signal.
1309
+
1310
+ Emits ``ceo_boot_persona_coverage_score`` with `window_hours=24` and
1311
+ `eligible_demand_events=0` (PLAN-104 demand ledger live; observability-only here per AC4).
1312
+ """
1313
+ try:
1314
+ metrics = _score_persona_coverage(hours=24.0)
1315
+ except Exception as exc: # noqa: BLE001 (Tier-S fail-soft)
1316
+ return "yellow", f"persona_coverage error: {exc}", None
1317
+ status, summary = _persona_coverage_status(metrics, window_hours=24)
1318
+ _emit_persona_coverage(metrics, window_hours=24)
1319
+ return status, summary, {
1320
+ **metrics,
1321
+ "window_hours": 24,
1322
+ "eligible_demand_events": 0,
1323
+ }
1324
+
1325
+
1326
+ def check_persona_atrophy_7d() -> Tuple[str, str, Any]:
1327
+ """19th Tier-S check — demand-normalized persona-atrophy at 168h.
1328
+
1329
+ PLAN-104 Wave D activated the demand-driven RED branch (S134 Codex R2
1330
+ thread `019e37e3` ACCEPT). Set-algebra in
1331
+ `persona_demand_resolver.atrophy_7d_status` (Codex iter-1 P0 #2 +
1332
+ iter-2 P1 #1 folds — adds defense-in-depth effective_unmet
1333
+ computation inline):
1334
+
1335
+ satisfied = opened & matched
1336
+ unmet_recorded = (opened & unmet) - matched - waived
1337
+ effective_unmet = opened where opened_ts + 24h < now AND
1338
+ no terminal AND no in-window dispatch-match
1339
+ unmet_total = unmet_recorded | (effective_unmet - waived)
1340
+ waived = (opened & waived) - matched
1341
+ still_open = opened where window NOT expired and no terminal
1342
+ eligible_settled = satisfied | unmet_total | waived
1343
+
1344
+ not opened -> green "no eligible persona demand in 168h"
1345
+ not eligible_settled -> green "<N> demand(s) still inside window"
1346
+ not unmet_total -> green "<S>/<E> demands matched (<W> waived)"
1347
+ else -> red "<U> persona demand(s) unmet in 168h ..."
1348
+
1349
+ Side-effect: this check runs scan + waive-emit + resolve before
1350
+ computing status (Codex iter-1 P0 #1). Ordering per Codex iter-2
1351
+ P1 #2: scan -> emit_opened -> emit_waives_for_scanned -> resolve
1352
+ -> emit_resolutions (waive precedes unmet emit).
1353
+
1354
+ Kill-switch CEO_PERSONA_DEMAND_LEDGER_DISABLED=1 reverts to
1355
+ pre-PLAN-104 observability-only semantic (max-yellow). The 18th check
1356
+ `check_ceo_boot_persona_coverage_score` stays observability-only
1357
+ forever per S127 AMEND option-(c).
1358
+ """
1359
+ if os.environ.get("CEO_PERSONA_DEMAND_LEDGER_DISABLED") == "1":
1360
+ try:
1361
+ metrics = _score_persona_coverage(hours=168.0)
1362
+ except Exception as exc: # noqa: BLE001
1363
+ return "yellow", f"persona_atrophy_7d error: {exc}", None
1364
+ status, summary = _persona_coverage_status(metrics, window_hours=168)
1365
+ _emit_persona_coverage(metrics, window_hours=168)
1366
+ return status, summary, {
1367
+ **metrics,
1368
+ "window_hours": 168,
1369
+ "eligible_demand_events": 0,
1370
+ }
1371
+
1372
+ try:
1373
+ import importlib.util
1374
+ scripts_dir = Path(__file__).resolve().parent
1375
+ spec_path = scripts_dir / "persona_demand_resolver.py"
1376
+ spec = importlib.util.spec_from_file_location(
1377
+ "persona_demand_resolver", spec_path,
1378
+ )
1379
+ if spec is None or spec.loader is None:
1380
+ raise ImportError("resolver spec load failed")
1381
+ resolver = importlib.util.module_from_spec(spec)
1382
+ spec.loader.exec_module(resolver)
1383
+ scan_spec = importlib.util.spec_from_file_location(
1384
+ "persona_demand_scan", scripts_dir / "persona_demand_scan.py",
1385
+ )
1386
+ if scan_spec is None or scan_spec.loader is None:
1387
+ raise ImportError("scan spec load failed")
1388
+ scanner = importlib.util.module_from_spec(scan_spec)
1389
+ scan_spec.loader.exec_module(scanner)
1390
+ except Exception as exc: # noqa: BLE001
1391
+ return "yellow", f"persona_atrophy_7d module import error: {exc}", None
1392
+
1393
+ # Codex iter-1 P0 #1 fold: actually run scan + waive-emit + resolve
1394
+ # before computing status. Without this the 19th check only reads
1395
+ # ledger state that nothing ever populates -> never reaches RED in
1396
+ # real use. Each step is best-effort; any IO error is swallowed and
1397
+ # the status path's defense-in-depth expiry computation still works.
1398
+ #
1399
+ # Codex iter-2 P1 #2 fold: waives MUST fire BEFORE emit_resolutions.
1400
+ # Codex iter-4 P1 #1 fold: detect_all() is called ONCE here and
1401
+ # both scoped operations re-use it (avoids git-subprocess duplication).
1402
+ # Order:
1403
+ # 1. detect_all() -> full candidate set with target_ref cleartext
1404
+ # 2. emit_opened() dedups via audit-log
1405
+ # 3. emit_waives_for_scanned(all_candidates) scoped to commits
1406
+ # 4. resolve() / emit_resolutions() catches non-waived expired demands
1407
+ repo_root = Path(__file__).resolve().parents[2]
1408
+ all_candidates: List = []
1409
+ try:
1410
+ all_candidates = scanner.detect_all(repo_root)
1411
+ # Local dedup against audit-log (avoids 2nd git subprocess pass).
1412
+ already = scanner._existing_demand_ids(
1413
+ AUDIT_LOG_DEFAULT, scanner.SCAN_HORIZON_HOURS,
1414
+ )
1415
+ new_only = [ev for ev in all_candidates if ev.demand_id not in already]
1416
+ scanner.emit_opened(new_only)
1417
+ except Exception: # noqa: BLE001 (Tier-S fail-soft)
1418
+ pass
1419
+ try:
1420
+ resolver.emit_waives_for_scanned(all_candidates, AUDIT_LOG_DEFAULT, repo_root)
1421
+ except Exception: # noqa: BLE001
1422
+ pass
1423
+ try:
1424
+ summary_resolve = resolver.resolve(AUDIT_LOG_DEFAULT)
1425
+ resolver.emit_resolutions(summary_resolve)
1426
+ except Exception: # noqa: BLE001
1427
+ pass
1428
+
1429
+ try:
1430
+ status, summary, demand_metrics = resolver.atrophy_7d_status(AUDIT_LOG_DEFAULT)
1431
+ except Exception as exc: # noqa: BLE001
1432
+ return "yellow", f"persona_atrophy_7d resolver error: {exc}", None
1433
+
1434
+ try:
1435
+ score_metrics = _score_persona_coverage(hours=168.0)
1436
+ _emit_persona_coverage(score_metrics, window_hours=168)
1437
+ except Exception: # noqa: BLE001
1438
+ score_metrics = {"score_x100": 0, "cells_covered": 0, "total_cells": 16}
1439
+
1440
+ return status, summary, {
1441
+ **score_metrics,
1442
+ **demand_metrics,
1443
+ "window_hours": 168,
1444
+ }
1445
+
1446
+
1447
+ def check_confidence_gate_drift_7d() -> Tuple[str, str, Any]:
1448
+ """PLAN-106 Wave F.2 — 20th Tier-S check.
1449
+
1450
+ Detects HIGH_CONFIDENCE_BLOCK classes whose 7d FPR > 2% per
1451
+ ADR-019-AMEND-1 §6. Wraps the side-effect-free `detect_drift_7d`
1452
+ function from `.claude/scripts/check-confidence-gate-drift.py`
1453
+ (refactored per Wave F.1).
1454
+
1455
+ Status mapping:
1456
+ - drift NOT detected, valid config + log → green
1457
+ - drift NOT detected, missing config/log → green (fail-OPEN
1458
+ per ADR-095 doctrine — no calendar gates, but also no
1459
+ spurious RED on fresh installs)
1460
+ - drift detected → yellow (advisory; auto-demote is the
1461
+ underlying script's responsibility, not the ceo-boot check)
1462
+ - exception → yellow with error message
1463
+ """
1464
+ try:
1465
+ import importlib.util
1466
+ scripts_dir = Path(__file__).resolve().parent
1467
+ spec = importlib.util.spec_from_file_location(
1468
+ "check_confidence_gate_drift",
1469
+ scripts_dir / "check-confidence-gate-drift.py",
1470
+ )
1471
+ if spec is None or spec.loader is None:
1472
+ return "yellow", "drift detector module spec load failed", None
1473
+ mod = importlib.util.module_from_spec(spec)
1474
+ spec.loader.exec_module(mod)
1475
+ except Exception as exc: # noqa: BLE001 (Tier-S fail-soft)
1476
+ return "yellow", f"drift detector import error: {exc}", None
1477
+
1478
+ try:
1479
+ drift, summary, detail = mod.detect_drift_7d()
1480
+ except Exception as exc: # noqa: BLE001
1481
+ return "yellow", f"detect_drift_7d error: {exc}", None
1482
+
1483
+ if not drift:
1484
+ return "green", summary, detail
1485
+ return "yellow", summary, detail
1486
+
1487
+
1488
+ def _emit_settings_tamper_detected_safe(findings: List[Dict[str, str]]) -> None:
1489
+ """Emit ONE closed-enum ``settings_tamper_detected`` event per class.
1490
+
1491
+ PLAN-135 W1 S3. Field contract (Sec MF-3, enforced emit-side by
1492
+ ``_SETTINGS_TAMPER_DETECTED_ALLOWLIST`` in ``_lib/audit_emit.py``):
1493
+
1494
+ tamper_class — closed enum (the 5 ``effective_config.TAMPER_*``
1495
+ members; off-enum values are COERCED emit-side)
1496
+ layer — closed enum (user/project/local/managed/env/disk;
1497
+ first layer seen for the class)
1498
+ finding_count — int, clamped 0..99
1499
+
1500
+ The finding DETAIL string is NEVER emitted — it can carry endpoint
1501
+ URLs, model ids, helper paths or flag values (mcp_routing.py
1502
+ breadcrumb precedent +
1503
+ [[feedback-closed-enum-breadcrumb-must-not-echo-rejected-value]]).
1504
+
1505
+ Fail-open contract: pre-ceremony (action not yet in ``_KNOWN_ACTIONS``)
1506
+ writes a stderr breadcrumb instead of emitting; any emit failure is
1507
+ swallowed. NEVER raises, NEVER blocks boot.
1508
+ """
1509
+ if _audit_emit is None or not findings:
1510
+ return
1511
+ try:
1512
+ emit_fn = getattr(_audit_emit, "emit_generic", None)
1513
+ if not callable(emit_fn):
1514
+ return
1515
+ known = getattr(_audit_emit, "_KNOWN_ACTIONS", None)
1516
+ if known is not None and "settings_tamper_detected" not in known:
1517
+ sys.stderr.write(
1518
+ "[ceo-boot] 'settings_tamper_detected' not in _KNOWN_ACTIONS; "
1519
+ "emit dropped until the PLAN-135 W1 kernel ceremony lands.\n"
1520
+ )
1521
+ return
1522
+ by_class: Dict[str, Dict[str, Any]] = {}
1523
+ for f in findings:
1524
+ if not isinstance(f, dict):
1525
+ continue
1526
+ klass = str(f.get("class", ""))[:64]
1527
+ if not klass:
1528
+ continue
1529
+ slot = by_class.setdefault(
1530
+ klass, {"layer": str(f.get("layer", ""))[:16], "count": 0}
1531
+ )
1532
+ slot["count"] += 1
1533
+ for klass in sorted(by_class): # deterministic order (CR-N7)
1534
+ slot = by_class[klass]
1535
+ emit_fn(
1536
+ "settings_tamper_detected",
1537
+ session_id=_ceo_boot_session_id(),
1538
+ tamper_class=klass,
1539
+ layer=slot["layer"],
1540
+ finding_count=max(0, min(99, int(slot["count"]))),
1541
+ )
1542
+ except Exception: # noqa: BLE001 — advisory fail-open
1543
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
1544
+ import traceback
1545
+ traceback.print_exc(file=sys.stderr)
1546
+
1547
+
1548
+ def check_settings_tamper_tripwires() -> Tuple[str, str, Any]:
1549
+ """PLAN-135 W1 S3 — 21st Tier-S check: settings/env tamper tripwires.
1550
+
1551
+ Scans the RESOLVED multi-layer settings (user / project / local /
1552
+ managed — including the gitignored, sentinel-blind
1553
+ ``settings.local.json``) plus the import-time env snapshot for the
1554
+ five tamper classes of THREAT-MODEL-WORKSHEET.md §2, via the shared
1555
+ ``_lib/effective_config`` module:
1556
+
1557
+ (a) ``disableAllHooks`` truthy in ANY settings layer
1558
+ (b) ``ANTHROPIC_MODEL`` / ``ANTHROPIC_DEFAULT_*`` /
1559
+ ``ANTHROPIC_SMALL_FAST_MODEL`` remap outside the ADR-149
1560
+ allowlist (skipped fail-open when the allowlist is unreadable)
1561
+ (c) ``ANTHROPIC_BASE_URL`` / ``ANTHROPIC_AUTH_TOKEN`` /
1562
+ ``apiKeyHelper`` endpoint remap (model substitution AND
1563
+ transcript egress outside check (b) entirely)
1564
+ (d) ``permissions.defaultMode: bypassPermissions`` or
1565
+ dangerously-skip flags in any layer (nullifies the S2 floor)
1566
+ (e) effective hook count == registered count (registered-but-
1567
+ missing-on-disk census; a missing hook fails open = allow)
1568
+
1569
+ Env reads come EXCLUSIVELY from the module-import-time snapshot
1570
+ (``_TAMPER_ENV_SNAPSHOT``, trusted_env pattern) — never live
1571
+ ``os.environ``. Settings reads come from the RESOLVED multi-layer
1572
+ config (``effective_config.resolve_settings``).
1573
+
1574
+ Status mapping (ADVISORY — /ceo-boot never blocks the session):
1575
+
1576
+ findings present → red (rail integrity is suspect)
1577
+ no findings, layer errors → yellow (a corrupt PRESENT layer is
1578
+ itself an anomaly worth eyes)
1579
+ no findings, clean → green
1580
+ module missing / internal → yellow (advisory fail-open +
1581
+ stderr breadcrumb, never crash)
1582
+
1583
+ Side-effect: one closed-enum ``settings_tamper_detected`` audit emit
1584
+ per detected class via ``_emit_settings_tamper_detected_safe``
1585
+ (``_KNOWN_ACTIONS``-guarded pre-ceremony).
1586
+ """
1587
+ if _effective_config is None:
1588
+ sys.stderr.write(
1589
+ "[ceo-boot] effective_config unavailable — settings tamper "
1590
+ "tripwires inactive (fail-open).\n"
1591
+ )
1592
+ return (
1593
+ "yellow",
1594
+ "effective_config unavailable — tamper tripwires inactive",
1595
+ None,
1596
+ )
1597
+ try:
1598
+ resolved = _effective_config.resolve_settings(REPO_ROOT)
1599
+ findings = _effective_config.classify_tampering(
1600
+ resolved, _TAMPER_ENV_SNAPSHOT
1601
+ )
1602
+ _emit_settings_tamper_detected_safe(findings)
1603
+ if findings:
1604
+ classes = sorted({
1605
+ str(f.get("class", ""))
1606
+ for f in findings
1607
+ if isinstance(f, dict) and f.get("class")
1608
+ })
1609
+ # Summary carries ONLY closed-enum class names (never the
1610
+ # finding detail — it can embed env values / endpoints).
1611
+ return (
1612
+ "red",
1613
+ f"{len(findings)} tamper finding(s): "
1614
+ f"{','.join(classes)[:160]}",
1615
+ findings,
1616
+ )
1617
+ errors = resolved.get("errors") or []
1618
+ if errors:
1619
+ return (
1620
+ "yellow",
1621
+ f"no tamper indicators; {len(errors)} unparseable "
1622
+ f"settings layer(s)",
1623
+ {"errors": [str(e)[:160] for e in errors[:4]]},
1624
+ )
1625
+ registered: set = set()
1626
+ for layer in resolved.get("layers", []):
1627
+ if isinstance(layer, dict) and layer.get("name") in (
1628
+ "project", "local",
1629
+ ):
1630
+ registered.update(
1631
+ _effective_config.registered_hook_basenames(
1632
+ layer.get("data") or {}
1633
+ )
1634
+ )
1635
+ effective = _effective_config.count_effective_hooks(REPO_ROOT)
1636
+ return (
1637
+ "green",
1638
+ f"no tamper indicators ({effective}/{len(registered)} "
1639
+ f"registered hooks effective)",
1640
+ {"registered": len(registered), "effective_on_disk": effective},
1641
+ )
1642
+ except Exception as exc: # noqa: BLE001 (Tier-S fail-soft floor)
1643
+ return "yellow", f"tamper tripwires error: {type(exc).__name__}", None
1644
+
1645
+
1646
+ TIER_S_CHECKS: List[Tuple[str, Callable[[], Tuple[str, str, Any]]]] = [
1647
+ ("plans_executing", check_plans_executing),
1648
+ ("plans_reviewed_pending", check_plans_reviewed_pending),
1649
+ ("plans_stranded_executing", check_plans_stranded_executing),
1650
+ ("plans_draft", check_plans_draft),
1651
+ ("audit_log_freshness", check_audit_log_freshness),
1652
+ ("dispatch_count_24h", check_dispatch_count_24h),
1653
+ ("skill_unknown_ratio", check_skill_unknown_ratio),
1654
+ ("governance_validate", check_governance_validate),
1655
+ # PLAN-082 Codex Item D: `hook_test_baseline` renamed to `hook_live_smoke`
1656
+ # — the check now performs a live hook smoke (settings.json parse + file
1657
+ # existence + py_compile) rather than reading a pytest-baseline cache that
1658
+ # was never populated. Old function symbol preserved as alias for tests.
1659
+ ("hook_live_smoke", check_hook_live_smoke),
1660
+ ("audit_v3_backlog", check_audit_v3_backlog),
1661
+ ("sentinels_pending_gpg", check_sentinels_pending_gpg),
1662
+ ("rc_hold_aged", check_rc_hold_aged),
1663
+ ("cost_24h_usd", check_cost_24h_usd),
1664
+ ("active_plan_burn_ratio", check_active_plan_burn_ratio),
1665
+ ("adrs_stale_proposed", check_adrs_stale_proposed),
1666
+ # PLAN-091 Wave A.1 — 16th Tier-S check. Delegates to standalone hook
1667
+ # module `.claude/hooks/check_tier_policy_misrouting_24h.py` per the
1668
+ # PLAN-088 §AC11 18-check target.
1669
+ ("tier_policy_misrouting_24h", check_tier_policy_misrouting_24h),
1670
+ # PLAN-093 Wave C.2 — 17th Tier-S check: prompt-cache hit-rate
1671
+ # detection emitting `cache_discipline_alerted` on threshold breach.
1672
+ ("cache_discipline_alerted", check_cache_discipline_alerted),
1673
+ # PLAN-093 Wave C.5/C.6 — 18th Tier-S check: 4-persona × 4-task coverage
1674
+ # matrix at 24h cadence (session-smoke). S127 cadence-amendment (Codex R2
1675
+ # `019e33a3` AMEND): demoted to permanent observability, never red.
1676
+ ("ceo_boot_persona_coverage_score", check_ceo_boot_persona_coverage_score),
1677
+ # S127 cadence-amendment — 19th Tier-S check: same matrix at 168h cadence
1678
+ # (trend / chronic-atrophy signal). Phase 1: observability-only, never red.
1679
+ # Phase 2 (PLAN-104-persona-demand-ledger): RED authority activated once
1680
+ # `eligible_demand_events` is populated from the demand ledger.
1681
+ ("persona_atrophy_7d", check_persona_atrophy_7d),
1682
+ # PLAN-106 Wave F.2 — 20th Tier-S check. Wires the standalone
1683
+ # `.claude/scripts/check-confidence-gate-drift.py` module's
1684
+ # `detect_drift_7d()` importable into the parallel registry per
1685
+ # ADR-019-AMEND-1 §6 (7d rolling FPR > 2% advisory). Read-only;
1686
+ # the underlying script's `--emit` flag remains the canonical
1687
+ # emission surface for `confidence_gate_fp_drift_detected`.
1688
+ ("confidence_gate_drift_7d", check_confidence_gate_drift_7d),
1689
+ # PLAN-135 W1 S3 — 21st Tier-S check: settings/env tamper tripwires
1690
+ # over the RESOLVED multi-layer settings (shared _lib/effective_config;
1691
+ # user/project/local/managed incl. the sentinel-blind
1692
+ # settings.local.json) + the import-time env snapshot (trusted_env
1693
+ # pattern). Classes (a)-(e) per THREAT-MODEL-WORKSHEET.md §2; closed-
1694
+ # enum `settings_tamper_detected` emit per class. ADVISORY fail-open:
1695
+ # infra error → yellow + stderr breadcrumb, never crashes, never blocks.
1696
+ ("settings_tamper_tripwires", check_settings_tamper_tripwires),
1697
+ ]
1698
+
1699
+ assert len(TIER_S_CHECKS) == 21, f"Expected 21 Tier-S checks, got {len(TIER_S_CHECKS)}"
1700
+
1701
+
1702
+ TIER_A_CHECKS: List[Tuple[str, Callable[[], Tuple[str, str, Any]]]] = [
1703
+ ("tier_a_debate_transcripts", check_tier_a_debate_transcripts),
1704
+ ("tier_a_lessons_30d", check_tier_a_lessons_30d),
1705
+ ("tier_a_spec_version_drift", check_tier_a_spec_version_drift),
1706
+ ("tier_a_npm_version_match", check_tier_a_npm_version_match),
1707
+ ("tier_a_waivers_count", check_tier_a_waivers_count),
1708
+ ("tier_a_adrs_recent_status", check_tier_a_adrs_recent_status),
1709
+ ("tier_a_cache_hit_rate_24h", check_tier_a_cache_hit_rate_24h),
1710
+ ("tier_a_hook_test_baseline_age", check_tier_a_hook_test_baseline_age),
1711
+ ("tier_a_sentinel_signers_tracked", check_tier_a_sentinel_signers_tracked),
1712
+ ("tier_a_gitignore_state_excluded", check_tier_a_gitignore_state_excluded),
1713
+ ]
1714
+
1715
+ assert len(TIER_A_CHECKS) == 10, f"Expected 10 Tier-A checks, got {len(TIER_A_CHECKS)}"
1716
+
1717
+
1718
+ # Verbose-mode aggregate budget: extends Tier-S 5s window to 10s when
1719
+ # Tier-A is dispatched alongside (PLAN-065 §4.3.3).
1720
+ AGGREGATE_TIMEOUT_VERBOSE_S = 10.0
1721
+
1722
+
1723
+ # ---- Dispatcher ------------------------------------------------------------
1724
+
1725
+ def _wrap_check(name: str, fn: Callable[[], Tuple[str, str, Any]]) -> CheckResult:
1726
+ t0 = time.perf_counter()
1727
+ try:
1728
+ status, summary, detail = fn()
1729
+ dur = (time.perf_counter() - t0) * 1000
1730
+ return CheckResult(name, status, summary, dur, detail)
1731
+ except Exception as e: # noqa: BLE001 (PoC fail-soft)
1732
+ dur = (time.perf_counter() - t0) * 1000
1733
+ return CheckResult(name, "error", f"{type(e).__name__}: {e}", dur, None)
1734
+
1735
+
1736
+ def dispatch_parallel(
1737
+ *,
1738
+ include_tier_a: bool = False,
1739
+ aggregate_timeout_s: Optional[float] = None,
1740
+ ) -> List[CheckResult]:
1741
+ """Dispatch Tier-S (and optionally Tier-A) checks in parallel via as_completed.
1742
+
1743
+ Codex S82 P0 #2 fix: previous impl iterated future_to_name.items() and
1744
+ called fut.result(timeout=PER_CHECK_TIMEOUT_S) sequentially — so the
1745
+ 500ms started counting when each future was *observed*, not when it
1746
+ started running. Timeouts cascaded and the per-check budget was
1747
+ fictional under load. New impl uses as_completed() with the AGGREGATE
1748
+ budget; per-check budget becomes a soft annotation (subprocess timeouts
1749
+ inside each check enforce real CPU/IO ceilings, e.g. governance_validate
1750
+ has subprocess timeout=4.0).
1751
+
1752
+ Tier-A extension (PLAN-065 §4.3.3): when ``include_tier_a=True``,
1753
+ dispatcher also enqueues TIER_A_CHECKS and the aggregate budget
1754
+ defaults to AGGREGATE_TIMEOUT_VERBOSE_S (10s).
1755
+
1756
+ Pool lifecycle (per PLAN-087 A.6 / `F-A-CR-D0012` P2): the
1757
+ ``ThreadPoolExecutor`` is NOT used as a context manager because
1758
+ ``with`` exit calls ``shutdown(wait=True)`` which blocks on
1759
+ long-running futures past the aggregate timeout. The explicit
1760
+ ``shutdown(wait=False, cancel_futures=True)`` in the ``finally``
1761
+ block releases the pool immediately and cancels any futures that
1762
+ have not yet started; in-flight futures continue to run on their
1763
+ daemon threads but their results are dropped (the aggregate
1764
+ timeout has already produced their `AGG_TIMEOUT` rows). Python
1765
+ 3.9+ ``cancel_futures`` parameter required; the project min
1766
+ Python is 3.9 per ADR-002.
1767
+ """
1768
+ registry: List[Tuple[str, Callable[[], Tuple[str, str, Any]]]] = list(TIER_S_CHECKS)
1769
+ if include_tier_a:
1770
+ registry = registry + list(TIER_A_CHECKS)
1771
+ if aggregate_timeout_s is None:
1772
+ aggregate_timeout_s = (
1773
+ AGGREGATE_TIMEOUT_VERBOSE_S if include_tier_a else AGGREGATE_TIMEOUT_S
1774
+ )
1775
+
1776
+ results_by_name: Dict[str, CheckResult] = {}
1777
+ pool = ThreadPoolExecutor(max_workers=MAX_WORKERS)
1778
+ try:
1779
+ future_to_name = {
1780
+ pool.submit(_wrap_check, name, fn): name for name, fn in registry
1781
+ }
1782
+ try:
1783
+ for fut in as_completed(future_to_name, timeout=aggregate_timeout_s):
1784
+ name = future_to_name[fut]
1785
+ try:
1786
+ res = fut.result() # already done, instant
1787
+ except Exception as e: # noqa: BLE001
1788
+ res = CheckResult(name, "error", f"{type(e).__name__}: {e}", 0.0, None)
1789
+ # Soft per-check ceiling: annotate slow but green checks.
1790
+ budget_s = PER_CHECK_TIMEOUT_OVERRIDES_S.get(name, PER_CHECK_TIMEOUT_S)
1791
+ if res.duration_ms > budget_s * 1000 and res.status == "green":
1792
+ res.summary = (
1793
+ f"{res.summary} (slow {res.duration_ms:.0f}ms > "
1794
+ f"budget {int(budget_s * 1000)}ms)"
1795
+ )
1796
+ results_by_name[name] = res
1797
+ except FuturesTimeout:
1798
+ pass # aggregate exceeded — handled below
1799
+
1800
+ # Mark non-completed as aggregate-timeout (Codex P0 #2: explicit, not silent)
1801
+ for fut, name in future_to_name.items():
1802
+ if name not in results_by_name:
1803
+ ms = int(aggregate_timeout_s * 1000)
1804
+ results_by_name[name] = CheckResult(
1805
+ name, "timeout",
1806
+ f"AGG_TIMEOUT (>{ms}ms aggregate)",
1807
+ aggregate_timeout_s * 1000, None,
1808
+ )
1809
+ _emit_ceo_boot_check_skipped_safe(
1810
+ check_name=name,
1811
+ timeout_ms=ms,
1812
+ )
1813
+ finally:
1814
+ # See docstring "Pool lifecycle" — non-blocking shutdown is
1815
+ # required to honor the aggregate timeout.
1816
+ pool.shutdown(wait=False, cancel_futures=True)
1817
+
1818
+ # Codex S82 post-patch fix: emit results in registry order for CR-N7
1819
+ # stability across runs (was completion-order, non-deterministic).
1820
+ return [results_by_name[name] for name, _ in registry if name in results_by_name]
1821
+
1822
+
1823
+ # ---- Cached path (PLAN-065 §4.3.2 real per-key cache) -------------------
1824
+
1825
+ def _cache_key_raw() -> str:
1826
+ """Compose raw cache key string from (HEAD + audit-log mtime + size).
1827
+
1828
+ Per Codex S82 P1 #5 the sub-second precision is NOT required; we use
1829
+ integer seconds + size-in-bytes which together provide collision-safe
1830
+ invalidation when the audit-log is appended.
1831
+ """
1832
+ try:
1833
+ proc = subprocess.run(
1834
+ ["git", "-C", str(REPO_ROOT), "rev-parse", "HEAD"],
1835
+ capture_output=True, text=True, timeout=1.0,
1836
+ )
1837
+ head = proc.stdout.strip() or "nogit"
1838
+ except (subprocess.TimeoutExpired, FileNotFoundError):
1839
+ head = "nogit"
1840
+ try:
1841
+ st = AUDIT_LOG_DEFAULT.stat()
1842
+ mtime = int(st.st_mtime)
1843
+ size = int(st.st_size)
1844
+ except OSError:
1845
+ mtime, size = 0, 0
1846
+ return f"{head}:{mtime}:{size}"
1847
+
1848
+
1849
+ def _cache_key() -> str:
1850
+ """SHA-256 short-hash of raw cache key (filename-safe + bounded length)."""
1851
+ raw = _cache_key_raw()
1852
+ return hashlib.sha256(raw.encode("utf-8", errors="replace")).hexdigest()[:32]
1853
+
1854
+
1855
+ def _cache_path_for_key(key: str) -> Path:
1856
+ """Resolve cache file path for a given key under the active cache dir."""
1857
+ return _cache_dir() / f"{key}.json"
1858
+
1859
+
1860
+ def cache_lru_evict() -> None:
1861
+ """LRU-evict oldest cache files when dir size exceeds CACHE_DIR_SIZE_CAP_BYTES.
1862
+
1863
+ Fail-open: any OSError silently breadcrumbs to stderr and returns.
1864
+ Atime-aware where supported; mtime fallback (atime is updated by reads
1865
+ on most filesystems but POSIX `relatime` may suppress it).
1866
+ """
1867
+ cdir = _cache_dir()
1868
+ if not cdir.exists():
1869
+ return
1870
+ try:
1871
+ entries: List[Tuple[float, int, Path]] = []
1872
+ total = 0
1873
+ for f in cdir.glob("*.json"):
1874
+ try:
1875
+ st = f.stat()
1876
+ except OSError:
1877
+ continue
1878
+ entries.append((st.st_atime, st.st_size, f))
1879
+ total += st.st_size
1880
+ if total <= CACHE_DIR_SIZE_CAP_BYTES:
1881
+ return
1882
+ # Evict oldest-first until under cap.
1883
+ entries.sort(key=lambda e: e[0])
1884
+ for atime, size, path in entries:
1885
+ if total <= CACHE_DIR_SIZE_CAP_BYTES:
1886
+ break
1887
+ try:
1888
+ path.unlink()
1889
+ total -= size
1890
+ except OSError:
1891
+ continue
1892
+ except OSError as e:
1893
+ sys.stderr.write(f"# ceo-boot cache LRU evict failed: {type(e).__name__}\n")
1894
+
1895
+
1896
+ def cached_load() -> Tuple[bool, Any]:
1897
+ """Per-key cache load. Returns (hit, payload).
1898
+
1899
+ Hit semantics: cache file exists for current key, mtime within TTL,
1900
+ file size within cap, JSON parses cleanly. Otherwise miss (fail-open).
1901
+ Atime is touched on hit (LRU signal).
1902
+ """
1903
+ key = _cache_key()
1904
+ path = _cache_path_for_key(key)
1905
+ if not path.exists():
1906
+ return False, None
1907
+ try:
1908
+ st = path.stat()
1909
+ if st.st_size > CACHE_FILE_SIZE_CAP_BYTES:
1910
+ return False, None # corrupt / oversized — treat as miss
1911
+ if (time.time() - st.st_mtime) > CACHE_TTL_S:
1912
+ return False, None
1913
+ data = json.loads(path.read_text(encoding="utf-8"))
1914
+ except (OSError, json.JSONDecodeError):
1915
+ return False, None
1916
+ # Defense-in-depth: validate cache_key matches (mtime alone could
1917
+ # collide if filesystem is restored from backup).
1918
+ if data.get("cache_key") != key:
1919
+ return False, None
1920
+ # Touch atime for LRU signal (best-effort; ignore filesystem refusal).
1921
+ try:
1922
+ os.utime(path, None)
1923
+ except OSError:
1924
+ pass
1925
+ return True, data
1926
+
1927
+
1928
+ def cached_store(results: List[CheckResult]) -> None:
1929
+ """Write digest to per-key cache. Atomic (temp + rename); fail-open.
1930
+
1931
+ Codex S82 P1 fix: previous impl had unguarded mkdir + write_text;
1932
+ permission/lock/filesystem errors aborted boot post-checks pre-output.
1933
+ Now wraps all I/O in try/except; on failure emits stderr breadcrumb
1934
+ and returns silently (cache miss next boot, main path unaffected).
1935
+
1936
+ Schema parity: payload includes gate_pass / checks_total / checks_failed
1937
+ / recommendations / results — identical shape to the live --json output
1938
+ so adopters get the same payload from cache-hit and fresh dispatch.
1939
+ """
1940
+ cdir = _cache_dir()
1941
+ key = _cache_key()
1942
+ target = _cache_path_for_key(key)
1943
+ try:
1944
+ cdir.mkdir(parents=True, exist_ok=True)
1945
+ failed = sum(1 for r in results if r.status in ("red", "error", "timeout"))
1946
+ gate_pass = (failed == 0)
1947
+ payload = {
1948
+ "cache_key": key,
1949
+ "ts": time.time(),
1950
+ "gate_pass": gate_pass,
1951
+ "checks_total": len(results),
1952
+ "checks_failed": failed,
1953
+ "recommendations": _make_recommendations(results),
1954
+ "results": [
1955
+ {"name": r.name, "status": r.status, "summary": r.summary, "duration_ms": r.duration_ms}
1956
+ for r in results
1957
+ ],
1958
+ }
1959
+ body = json.dumps(payload)
1960
+ if len(body.encode("utf-8")) > CACHE_FILE_SIZE_CAP_BYTES:
1961
+ # Drop the recommendations + heavy detail to fit the cap.
1962
+ payload["recommendations"] = []
1963
+ body = json.dumps(payload)
1964
+ # Atomic write: temp file + rename.
1965
+ tmp = target.with_suffix(target.suffix + ".tmp")
1966
+ tmp.write_text(body, encoding="utf-8")
1967
+ os.replace(tmp, target)
1968
+ # Best-effort LRU eviction (post-write, never blocks).
1969
+ cache_lru_evict()
1970
+ except (OSError, PermissionError, json.JSONDecodeError) as e:
1971
+ sys.stderr.write(f"# ceo-boot cache-store failed (fail-open): {type(e).__name__}\n")
1972
+
1973
+
1974
+ # ---- Recommendations engine (PLAN-065 §4.3 Phase 3-D) ---------------------
1975
+
1976
+ def _make_recommendations(results: List[CheckResult]) -> List[str]:
1977
+ """Rule-based prioritizer ≤5 actionable items (Sec MF-4 sanitized).
1978
+
1979
+ Deterministic ordering (CR-N7): lex-sort by category prefix so ``--json``
1980
+ is stable across runs.
1981
+ """
1982
+ recs: List[Tuple[str, str]] = [] # (sort_key, formatted)
1983
+ by_name: Dict[str, CheckResult] = {r.name: r for r in results}
1984
+
1985
+ # Codex S82 P1 fix: recs engine ignored timeout/error checks. Since those
1986
+ # flip gate_pass=False, they MUST surface as top-priority recommendations.
1987
+ #
1988
+ # Codex CDX-W5-iter3-P1 closure: the original `_NAMED_RULES` skip
1989
+ # was over-engineered defense against duplicate emit — but the named
1990
+ # rule branches below only fire on `status in {yellow, red}`. A named
1991
+ # check that times out (status "timeout"/"error") therefore matched
1992
+ # NEITHER branch, producing a silent gap where gate_pass=False but
1993
+ # zero recommendation surfaced. We now emit the 00-* row for every
1994
+ # failing check; the named branches can never co-fire (their `status`
1995
+ # gate is incompatible with timeout/error), so dedup is moot.
1996
+ failing = sorted(
1997
+ (r for r in results if r.status in ("timeout", "error")),
1998
+ key=lambda r: r.name,
1999
+ )
2000
+ for r in failing[:3]: # cap at 3 to leave room for named rules
2001
+ recs.append((
2002
+ f"00-{r.name}-{r.status}", # sort BEFORE 01-owner-sentinels
2003
+ f"Check '{r.name}' {r.status}: {_sanitize_for_recs(r.summary)} "
2004
+ f"(blocks gate_pass)",
2005
+ ))
2006
+
2007
+ # PLAN-135 W1 S3 — settings/env tamper tripwires (rail integrity).
2008
+ # Sort key "005-*" lands AFTER the 00-* gate-blockers and BEFORE
2009
+ # 01-owner-sentinels (lexicographic: "00-" < "005" < "01-"): a fired
2010
+ # tripwire means every other signal on this digest may already be
2011
+ # produced by a disarmed/redirected rail. Only closed-enum class
2012
+ # names reach the rendered text (finding detail can embed env values).
2013
+ tamper = by_name.get("settings_tamper_tripwires")
2014
+ if tamper and tamper.status == "red" and tamper.detail:
2015
+ items = tamper.detail if isinstance(tamper.detail, list) else []
2016
+ classes = sorted({
2017
+ str(f.get("class", ""))
2018
+ for f in items
2019
+ if isinstance(f, dict) and f.get("class")
2020
+ })
2021
+ if classes:
2022
+ preview = _sanitize_for_recs(", ".join(classes[:3]))
2023
+ recs.append((
2024
+ "005-settings-tamper",
2025
+ f"Settings/env tamper tripwire(s) fired ({len(items)}): "
2026
+ f"{preview}{'...' if len(classes) > 3 else ''} — inspect "
2027
+ f"settings layers + env before trusting this session",
2028
+ ))
2029
+
2030
+ # Owner-pending GPG sentinels — highest priority (HARD blocker for ceremony)
2031
+ sent = by_name.get("sentinels_pending_gpg")
2032
+ if sent and sent.status == "yellow" and sent.detail:
2033
+ items = sent.detail if isinstance(sent.detail, list) else []
2034
+ if items:
2035
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2036
+ recs.append((
2037
+ "01-owner-sentinels",
2038
+ f"Owner GPG sign pending: {len(items)} sentinels ({preview}{'...' if len(items) > 3 else ''})",
2039
+ ))
2040
+
2041
+ # Stranded executing plans (no commits in 24h)
2042
+ stranded = by_name.get("plans_stranded_executing")
2043
+ if stranded and stranded.status == "red" and stranded.detail:
2044
+ items = stranded.detail if isinstance(stranded.detail, list) else []
2045
+ if items:
2046
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2047
+ recs.append((
2048
+ "02-stranded-plans",
2049
+ f"Stranded executing plans (>24h no commits): {preview}",
2050
+ ))
2051
+
2052
+ # Skill-unknown ratio > threshold
2053
+ skill = by_name.get("skill_unknown_ratio")
2054
+ if skill and skill.status == "red":
2055
+ recs.append((
2056
+ "03-skill-unknown",
2057
+ f"Spawn dispatch skill=unknown ratio elevated: {_sanitize_for_recs(skill.summary)}",
2058
+ ))
2059
+
2060
+ # Audit-v3 backlog open
2061
+ av3 = by_name.get("audit_v3_backlog")
2062
+ if av3 and av3.status == "yellow" and av3.detail:
2063
+ items = av3.detail if isinstance(av3.detail, list) else []
2064
+ if items:
2065
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2066
+ recs.append((
2067
+ "04-audit-v3-backlog",
2068
+ f"Audit-v3 backlog open ({len(items)}): {preview}",
2069
+ ))
2070
+
2071
+ # ADRs stale-proposed >30d
2072
+ adrs = by_name.get("adrs_stale_proposed")
2073
+ if adrs and adrs.status == "yellow" and adrs.detail:
2074
+ items = adrs.detail if isinstance(adrs.detail, list) else []
2075
+ if items:
2076
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2077
+ recs.append((
2078
+ "05-adrs-stale",
2079
+ f"ADRs PROPOSED >30d ({len(items)}): {preview} — promote or retract",
2080
+ ))
2081
+
2082
+ # Sort by deterministic key (CR-N7) and cap at 5
2083
+ recs.sort(key=lambda x: x[0])
2084
+ return [text for _, text in recs[:5]]
2085
+
2086
+
2087
+ # PLAN-078 Wave 5 — severity-aware view of the recommendations engine.
2088
+ # Mirrors `_make_recommendations` ordering exactly (same sort key + ≤5 cap)
2089
+ # but exposes the (sort_key, text, severity) triple so the marker emitter
2090
+ # can filter by severity≥medium without re-classifying. Severity buckets
2091
+ # track the rule rank assigned in `_make_recommendations`:
2092
+ #
2093
+ # 00-* (timeout/error gate-blockers) → high
2094
+ # 005-settings-tamper → high (PLAN-135 W1 S3 rail integrity)
2095
+ # 01-owner-sentinels → high
2096
+ # 02-stranded-plans → high
2097
+ # 03-skill-unknown → medium
2098
+ # 04-audit-v3-backlog → medium
2099
+ # 05-adrs-stale → low
2100
+ #
2101
+ # Anything else (future rules) defaults to "low" — caller policy is to
2102
+ # only emit markers for medium/high, so unknown future rules are silent
2103
+ # until the mapping is updated. Codex CDX-P1-04 closure: this helper is
2104
+ # deterministic + side-effect-free; the marker emitter consumes the
2105
+ # triple and never mutates `_make_recommendations` output.
2106
+ def _recommendations_with_severity(
2107
+ results: List[CheckResult],
2108
+ ) -> List[Tuple[str, str, str]]:
2109
+ """Return (sort_key, text, severity) triples mirroring _make_recommendations.
2110
+
2111
+ Re-runs the rule pipeline (cheap — already O(N) over results) so this
2112
+ helper is safe to call after `_make_recommendations` without ordering
2113
+ drift. Severity is derived from the sort_key prefix (deterministic).
2114
+ """
2115
+ recs: List[Tuple[str, str]] = []
2116
+ by_name: Dict[str, CheckResult] = {r.name: r for r in results}
2117
+
2118
+ # Mirror `_make_recommendations` exactly (Codex CDX-W5-iter3-P1):
2119
+ # named-rule skip removed because timeout/error never overlaps with
2120
+ # the yellow/red gates of the named branches.
2121
+ failing = sorted(
2122
+ (r for r in results if r.status in ("timeout", "error")),
2123
+ key=lambda r: r.name,
2124
+ )
2125
+ for r in failing[:3]:
2126
+ recs.append((
2127
+ f"00-{r.name}-{r.status}",
2128
+ f"Check '{r.name}' {r.status}: {_sanitize_for_recs(r.summary)} "
2129
+ f"(blocks gate_pass)",
2130
+ ))
2131
+
2132
+ # PLAN-135 W1 S3 — mirror of the _make_recommendations tamper rule
2133
+ # (same sort key + same text so the two pipelines never drift).
2134
+ tamper = by_name.get("settings_tamper_tripwires")
2135
+ if tamper and tamper.status == "red" and tamper.detail:
2136
+ items = tamper.detail if isinstance(tamper.detail, list) else []
2137
+ classes = sorted({
2138
+ str(f.get("class", ""))
2139
+ for f in items
2140
+ if isinstance(f, dict) and f.get("class")
2141
+ })
2142
+ if classes:
2143
+ preview = _sanitize_for_recs(", ".join(classes[:3]))
2144
+ recs.append((
2145
+ "005-settings-tamper",
2146
+ f"Settings/env tamper tripwire(s) fired ({len(items)}): "
2147
+ f"{preview}{'...' if len(classes) > 3 else ''} — inspect "
2148
+ f"settings layers + env before trusting this session",
2149
+ ))
2150
+
2151
+ sent = by_name.get("sentinels_pending_gpg")
2152
+ if sent and sent.status == "yellow" and sent.detail:
2153
+ items = sent.detail if isinstance(sent.detail, list) else []
2154
+ if items:
2155
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2156
+ recs.append((
2157
+ "01-owner-sentinels",
2158
+ f"Owner GPG sign pending: {len(items)} sentinels ({preview}{'...' if len(items) > 3 else ''})",
2159
+ ))
2160
+
2161
+ stranded = by_name.get("plans_stranded_executing")
2162
+ if stranded and stranded.status == "red" and stranded.detail:
2163
+ items = stranded.detail if isinstance(stranded.detail, list) else []
2164
+ if items:
2165
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2166
+ recs.append((
2167
+ "02-stranded-plans",
2168
+ f"Stranded executing plans (>24h no commits): {preview}",
2169
+ ))
2170
+
2171
+ skill = by_name.get("skill_unknown_ratio")
2172
+ if skill and skill.status == "red":
2173
+ recs.append((
2174
+ "03-skill-unknown",
2175
+ f"Spawn dispatch skill=unknown ratio elevated: {_sanitize_for_recs(skill.summary)}",
2176
+ ))
2177
+
2178
+ av3 = by_name.get("audit_v3_backlog")
2179
+ if av3 and av3.status == "yellow" and av3.detail:
2180
+ items = av3.detail if isinstance(av3.detail, list) else []
2181
+ if items:
2182
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2183
+ recs.append((
2184
+ "04-audit-v3-backlog",
2185
+ f"Audit-v3 backlog open ({len(items)}): {preview}",
2186
+ ))
2187
+
2188
+ adrs = by_name.get("adrs_stale_proposed")
2189
+ if adrs and adrs.status == "yellow" and adrs.detail:
2190
+ items = adrs.detail if isinstance(adrs.detail, list) else []
2191
+ if items:
2192
+ preview = _sanitize_for_recs(", ".join(items[:3]))
2193
+ recs.append((
2194
+ "05-adrs-stale",
2195
+ f"ADRs PROPOSED >30d ({len(items)}): {preview} — promote or retract",
2196
+ ))
2197
+
2198
+ recs.sort(key=lambda x: x[0])
2199
+ triples: List[Tuple[str, str, str]] = []
2200
+ for sort_key, text in recs[:5]:
2201
+ if sort_key.startswith("00-") or sort_key in (
2202
+ "005-settings-tamper", # PLAN-135 W1 S3 — rail-integrity = high
2203
+ "01-owner-sentinels", "02-stranded-plans"
2204
+ ):
2205
+ severity = "high"
2206
+ elif sort_key in ("03-skill-unknown", "04-audit-v3-backlog"):
2207
+ severity = "medium"
2208
+ elif sort_key == "05-adrs-stale":
2209
+ severity = "low"
2210
+ else: # pragma: no cover — defensive default for future rules
2211
+ severity = "low"
2212
+ triples.append((sort_key, text, severity))
2213
+ return triples
2214
+
2215
+
2216
+ # ---- Renderer ---------------------------------------------------------------
2217
+
2218
+ def render_digest(results: List[CheckResult], short: bool = False) -> str:
2219
+ lines = ["", "## /ceo-boot digest", ""]
2220
+ if short:
2221
+ red = sum(1 for r in results if r.status == "red")
2222
+ yellow = sum(1 for r in results if r.status == "yellow")
2223
+ timeout = sum(1 for r in results if r.status == "timeout")
2224
+ error = sum(1 for r in results if r.status == "error")
2225
+ green = sum(1 for r in results if r.status == "green")
2226
+ lines.append(
2227
+ f"- {green} green / {yellow} yellow / {red} red / "
2228
+ f"{timeout} timeout / {error} error"
2229
+ )
2230
+ # Surface non-green checks one-line for situational awareness
2231
+ for r in results:
2232
+ if r.status != "green":
2233
+ lines.append(f" - {r.name}: {r.status} — {r.summary}")
2234
+ else:
2235
+ lines.append("| Check | Status | Summary | Duration ms |")
2236
+ lines.append("|---|---|---|---|")
2237
+ for r in results:
2238
+ lines.append(f"| {r.name} | {r.status} | {r.summary} | {r.duration_ms:.0f} |")
2239
+
2240
+ # Recommendations engine output
2241
+ recs = _make_recommendations(results)
2242
+ if recs:
2243
+ lines.append("")
2244
+ lines.append("### Recommendations")
2245
+ for i, rec in enumerate(recs, 1):
2246
+ lines.append(f"{i}. {rec}")
2247
+
2248
+ lines.append("")
2249
+ return "\n".join(lines)
2250
+
2251
+
2252
+ # ---- Bench harness ---------------------------------------------------------
2253
+
2254
+ def _percentile(xs: List[float], p: float) -> float:
2255
+ """Stdlib percentile via sorted index. p in [0,100]. Empty → 0.0.
2256
+
2257
+ Spec (PLAN-065 §4.3 + S82 brief): use ``sorted(arr)[int(0.95 * len(arr))]``
2258
+ style indexing — NOT numpy. With small N the index can hit an off-by-one
2259
+ near the upper bound; we use ``int(round((len(s)-1) * p/100))`` which is
2260
+ monotonic-correct for both N=5 and N=10.
2261
+ """
2262
+ if not xs:
2263
+ return 0.0
2264
+ s = sorted(xs)
2265
+ k = int(round((len(s) - 1) * p / 100.0))
2266
+ return s[k]
2267
+
2268
+
2269
+ def _rss_kb_current() -> float:
2270
+ """Return current process RSS in KiB.
2271
+
2272
+ Codex S82 P1 fix: ``resource.getrusage(RUSAGE_SELF).ru_maxrss`` returns
2273
+ BYTES on macOS but KiB on Linux. We normalize to KiB by detecting the
2274
+ platform. This is high-water mark for the process; deltas across runs
2275
+ are still meaningful as long as the platform's unit is consistent.
2276
+ """
2277
+ rss = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
2278
+ if sys.platform == "darwin":
2279
+ # macOS: bytes → KiB
2280
+ return rss / 1024.0
2281
+ # Linux + most BSDs: already KiB
2282
+ return float(rss)
2283
+
2284
+
2285
+ def bench(n_runs: int = 5, *, include_tier_a: bool = False) -> Dict[str, Any]:
2286
+ """Run the dispatcher N times. Report p50/p95 wall-clock, per-iter RSS, deltas.
2287
+
2288
+ Output schema includes the legacy fields (``wall_clock_ms`` map, per-check
2289
+ p50/p95, tracemalloc current/peak) PLUS the PLAN-065 §4.3 spec fields
2290
+ (per-iter ``iterations`` list with ``iter``, ``duration_ms``, ``rss_kb``
2291
+ + summary dict with ``p50_ms``, ``p95_ms``, ``min_ms``, ``max_ms``,
2292
+ ``rss_delta_kb``).
2293
+ """
2294
+ wall_clocks: List[float] = []
2295
+ rss_per_iter: List[float] = []
2296
+ iterations: List[Dict[str, Any]] = []
2297
+ registry = list(TIER_S_CHECKS) + (list(TIER_A_CHECKS) if include_tier_a else [])
2298
+ per_check_durations: Dict[str, List[float]] = {name: [] for name, _ in registry}
2299
+
2300
+ rss_before = _rss_kb_current()
2301
+ tracemalloc.start()
2302
+ snap_before = tracemalloc.take_snapshot()
2303
+ for i in range(n_runs):
2304
+ t0 = time.perf_counter()
2305
+ results = dispatch_parallel(include_tier_a=include_tier_a)
2306
+ wc = (time.perf_counter() - t0) * 1000
2307
+ rss_now = _rss_kb_current()
2308
+ wall_clocks.append(wc)
2309
+ rss_per_iter.append(rss_now)
2310
+ iterations.append({
2311
+ "iter": i + 1,
2312
+ "duration_ms": round(wc, 2),
2313
+ "rss_kb": round(rss_now, 2),
2314
+ })
2315
+ for r in results:
2316
+ per_check_durations.setdefault(r.name, []).append(r.duration_ms)
2317
+ snap_after = tracemalloc.take_snapshot()
2318
+ current, peak = tracemalloc.get_traced_memory()
2319
+ tracemalloc.stop()
2320
+ rss_after = _rss_kb_current()
2321
+
2322
+ diff_stats = snap_after.compare_to(snap_before, "filename")
2323
+ py_delta_kb = sum(stat.size_diff for stat in diff_stats) / 1024.0
2324
+
2325
+ return {
2326
+ "n_runs": n_runs,
2327
+ "include_tier_a": include_tier_a,
2328
+ "iterations": iterations,
2329
+ "wall_clock_ms": {
2330
+ "p50": _percentile(wall_clocks, 50),
2331
+ "p95": _percentile(wall_clocks, 95),
2332
+ "min": min(wall_clocks) if wall_clocks else 0.0,
2333
+ "max": max(wall_clocks) if wall_clocks else 0.0,
2334
+ },
2335
+ "summary": {
2336
+ "p50_ms": round(_percentile(wall_clocks, 50), 2),
2337
+ "p95_ms": round(_percentile(wall_clocks, 95), 2),
2338
+ "min_ms": round(min(wall_clocks), 2) if wall_clocks else 0.0,
2339
+ "max_ms": round(max(wall_clocks), 2) if wall_clocks else 0.0,
2340
+ "rss_delta_kb": round(rss_after - rss_before, 2),
2341
+ },
2342
+ "per_check_p95_ms": {name: _percentile(durs, 95) for name, durs in per_check_durations.items()},
2343
+ "per_check_p50_ms": {name: _percentile(durs, 50) for name, durs in per_check_durations.items()},
2344
+ "memory_python_delta_kb": round(py_delta_kb, 2),
2345
+ "tracemalloc_peak_kb": round(peak / 1024.0, 2),
2346
+ "tracemalloc_current_kb": round(current / 1024.0, 2),
2347
+ }
2348
+
2349
+
2350
+ def render_bench_markdown(report: Dict[str, Any]) -> str:
2351
+ """Render bench report as a markdown table (PLAN-065 §4.3 spec).
2352
+
2353
+ Header columns: iter # | duration_ms | RSS_kb. Summary row appended
2354
+ with p50/p95/min/max/RSS_delta. Returns the rendered string (caller
2355
+ writes to stdout).
2356
+ """
2357
+ lines = ["", "## /ceo-boot --bench", ""]
2358
+ lines.append(f"N={report['n_runs']} include_tier_a={report.get('include_tier_a', False)}")
2359
+ lines.append("")
2360
+ lines.append("| iter | duration_ms | RSS_kb |")
2361
+ lines.append("|---|---|---|")
2362
+ for it in report.get("iterations", []):
2363
+ lines.append(f"| {it['iter']} | {it['duration_ms']:.1f} | {it['rss_kb']:.1f} |")
2364
+ s = report.get("summary", {})
2365
+ lines.append(
2366
+ f"| **summary** | p50={s.get('p50_ms', 0):.1f} / p95={s.get('p95_ms', 0):.1f}"
2367
+ f" / min={s.get('min_ms', 0):.1f} / max={s.get('max_ms', 0):.1f}"
2368
+ f" | rss_delta={s.get('rss_delta_kb', 0):.1f} |"
2369
+ )
2370
+ lines.append("")
2371
+ return "\n".join(lines)
2372
+
2373
+
2374
+ # === PLAN-065 Phase 2 audit_emit wire =====================================
2375
+ # Reality-Ledger fixture #4 closure (declared-but-not-wired). Pre-S82,
2376
+ # ceo-boot.py shipped emit comments only. Phase 2 wires the actual call.
2377
+ # Sec MF-3 field allowlist enforced ON THE EMIT SIDE (_lib/audit_emit.py).
2378
+ # Caller passes only allowlisted fields; never raises on emit failure.
2379
+ # Pre-canonical-ceremony the symbol is missing → hasattr() guard short-
2380
+ # circuits silently (advisory log to stderr only when CEO_BOOT_DEBUG=1).
2381
+
2382
+
2383
+ def _ceo_boot_session_id() -> str:
2384
+ """Derive session id from harness env or a stable fallback.
2385
+
2386
+ Defense-in-depth: never raises. The session_id is used as a forensic
2387
+ correlator across the 15 Tier-S checks; it does NOT need to be
2388
+ cryptographically unique.
2389
+ """
2390
+ sid = os.environ.get("CLAUDE_SESSION_ID") or os.environ.get("CEO_SESSION_ID")
2391
+ if sid:
2392
+ return sid[:64] # bound length defense-in-depth
2393
+ # Fallback: parent shell PID + start of audit-log mtime. Stable
2394
+ # within a session, advisory across sessions.
2395
+ try:
2396
+ return f"pid-{os.getppid()}-{int(AUDIT_LOG_DEFAULT.stat().st_mtime)}"
2397
+ except OSError:
2398
+ return f"pid-{os.getppid()}"
2399
+
2400
+
2401
+ def _emit_ceo_boot_emitted_safe(
2402
+ *,
2403
+ gate_pass: bool,
2404
+ duration_ms: int,
2405
+ checks_total: int,
2406
+ checks_failed: int,
2407
+ cache_hit: bool = False,
2408
+ ) -> None:
2409
+ """Wire-up to audit_emit.emit_ceo_boot_emitted. Fail-open contract.
2410
+
2411
+ Pre-canonical-ceremony: hasattr() returns False, function is a no-op.
2412
+ Post-ceremony: emits the telemetry event with Sec MF-3 field allowlist
2413
+ enforced on the emit side.
2414
+ """
2415
+ if _audit_emit is None:
2416
+ return
2417
+ fn = getattr(_audit_emit, "emit_ceo_boot_emitted", None)
2418
+ if not callable(fn):
2419
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2420
+ sys.stderr.write(
2421
+ "# ceo-boot: audit_emit.emit_ceo_boot_emitted not registered "
2422
+ "(canonical ceremony pending v1.12.0)\n"
2423
+ )
2424
+ return
2425
+ try:
2426
+ fn(
2427
+ session_id=_ceo_boot_session_id(),
2428
+ gate_pass=bool(gate_pass),
2429
+ duration_ms=int(duration_ms),
2430
+ checks_total=int(checks_total),
2431
+ checks_failed=int(checks_failed),
2432
+ cache_hit=bool(cache_hit),
2433
+ )
2434
+ except Exception: # noqa: BLE001 — fail-open per audit_emit contract
2435
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2436
+ import traceback
2437
+ traceback.print_exc(file=sys.stderr)
2438
+
2439
+
2440
+ def _emit_ceo_boot_check_skipped_safe(
2441
+ *,
2442
+ check_name: str,
2443
+ timeout_ms: int,
2444
+ ) -> None:
2445
+ """Wire-up to audit_emit.emit_ceo_boot_check_skipped. Fail-open contract."""
2446
+ if _audit_emit is None:
2447
+ return
2448
+ fn = getattr(_audit_emit, "emit_ceo_boot_check_skipped", None)
2449
+ if not callable(fn):
2450
+ return
2451
+ try:
2452
+ fn(
2453
+ session_id=_ceo_boot_session_id(),
2454
+ check_name=check_name,
2455
+ timeout_ms=int(timeout_ms),
2456
+ )
2457
+ except Exception: # noqa: BLE001 — fail-open
2458
+ pass
2459
+
2460
+
2461
+ # === END PLAN-065 Phase 2 audit_emit wire =================================
2462
+
2463
+
2464
+ # === PLAN-078 Wave 5 — TaskCreate-candidate marker emit + dedup ============
2465
+ # Layer A of the Wave 5 closure (per PLAN-078 §4 + Codex CDX-UNIQUE-02 +
2466
+ # CDX-P0-03 + CDX-P1-04 + Perf PERF-P1-03). Writes a structured stdout
2467
+ # marker block per top-3 high/medium recommendation when gate_pass=False,
2468
+ # dedup'd by 12-hex subject_hash via a 24h TTL state file under
2469
+ # `_lib/filelock`. The Claude orchestrator running /ceo-boot reads the
2470
+ # marker blocks and invokes TaskCreate; this script never touches the
2471
+ # TaskCreate harness primitive directly. Audit emit goes through
2472
+ # `audit_emit.emit_ceo_boot_task_candidate_emitted` (hasattr-guarded
2473
+ # pre-canonical-ceremony per the W5 staging→canonical model).
2474
+
2475
+ # Default state path lives under the same project state dir as the cache
2476
+ # (parity with audit-log.jsonl). Override `CEO_BOOT_TASK_STATE_PATH` for
2477
+ # tests. Format: {"entries": [{"subject_hash": "...", "ts": <epoch>}, ...]}
2478
+ # bounded to 256 entries (LRU evict on overflow).
2479
+ TASK_EMIT_STATE_PATH_DEFAULT = (
2480
+ Path.home()
2481
+ / ".claude" / "projects" / "ceo-orchestration"
2482
+ / "state" / "ceo-boot-tasks-emitted.json"
2483
+ )
2484
+ TASK_EMIT_TTL_S = 24 * 60 * 60 # 24h dedup window
2485
+ TASK_EMIT_TOP_N = 3 # emit at most 3 markers per boot
2486
+ TASK_EMIT_STATE_MAX_ENTRIES = 256 # bounded state size
2487
+
2488
+
2489
+ def _task_emit_state_path() -> Path:
2490
+ """Resolve dedup state-file path at call time (env override-aware)."""
2491
+ override = os.environ.get("CEO_BOOT_TASK_STATE_PATH")
2492
+ if override:
2493
+ return Path(override)
2494
+ return TASK_EMIT_STATE_PATH_DEFAULT
2495
+
2496
+
2497
+ def _subject_hash(subject: str) -> str:
2498
+ """Return a 12-hex-char prefix of sha256(subject) for dedup bookkeeping.
2499
+
2500
+ The full subject text is NEVER persisted (Sec MF-3); the hash is the
2501
+ only stable identifier shared between the audit event and the state
2502
+ file. NFKC-normalize first so homoglyph variants collapse to the
2503
+ same dedup key (parity with `_sanitize_for_recs`).
2504
+ """
2505
+ safe = subject if isinstance(subject, str) else str(subject)
2506
+ try:
2507
+ safe = unicodedata.normalize("NFKC", safe)
2508
+ except (TypeError, ValueError): # pragma: no cover — defensive
2509
+ pass
2510
+ digest = hashlib.sha256(safe.encode("utf-8", errors="replace")).hexdigest()
2511
+ return digest[:12]
2512
+
2513
+
2514
+ def _load_task_emit_state(path: Path) -> Dict[str, Any]:
2515
+ """Load dedup state, prune entries older than TASK_EMIT_TTL_S.
2516
+
2517
+ Fail-open: corrupt JSON / unreadable file → returns empty state. The
2518
+ caller persists the pruned state on next write so corruption is
2519
+ self-healing across boots.
2520
+
2521
+ Codex CDX-W5-P1-04 closure: drop entries with non-finite timestamps
2522
+ (NaN / inf) and entries with timestamps in the future (NTP jump
2523
+ backward, deliberate clock skew). The TTL window is `[0, TTL)` —
2524
+ age must be a finite non-negative number strictly less than the
2525
+ TTL bound.
2526
+ """
2527
+ if not path.exists():
2528
+ return {"entries": []}
2529
+ try:
2530
+ raw = path.read_text(encoding="utf-8")
2531
+ data = json.loads(raw)
2532
+ except (OSError, json.JSONDecodeError):
2533
+ return {"entries": []}
2534
+ entries = data.get("entries") if isinstance(data, dict) else None
2535
+ if not isinstance(entries, list):
2536
+ return {"entries": []}
2537
+ now = time.time()
2538
+ pruned: List[Dict[str, Any]] = []
2539
+ for entry in entries:
2540
+ if not isinstance(entry, dict):
2541
+ continue
2542
+ ts = entry.get("ts")
2543
+ sh = entry.get("subject_hash")
2544
+ if not isinstance(sh, str) or not isinstance(ts, (int, float)):
2545
+ continue
2546
+ # Reject NaN / inf — float comparison NaN!=NaN always; inf age
2547
+ # would otherwise be retained as TTL-current.
2548
+ ts_f = float(ts)
2549
+ if ts_f != ts_f or ts_f in (float("inf"), float("-inf")):
2550
+ continue
2551
+ age = now - ts_f
2552
+ if 0 <= age < TASK_EMIT_TTL_S:
2553
+ pruned.append({"subject_hash": sh[:12], "ts": ts_f})
2554
+ # Bound state size — LRU keep most-recent.
2555
+ if len(pruned) > TASK_EMIT_STATE_MAX_ENTRIES:
2556
+ pruned.sort(key=lambda e: e["ts"], reverse=True)
2557
+ pruned = pruned[:TASK_EMIT_STATE_MAX_ENTRIES]
2558
+ return {"entries": pruned}
2559
+
2560
+
2561
+ def _save_task_emit_state(path: Path, state: Dict[str, Any]) -> None:
2562
+ """Persist state atomically via temp-file + rename. Fail-open.
2563
+
2564
+ Codex CDX-W5-P1-03 closure: `os.replace` is atomic but not
2565
+ crash-durable on macOS — if the box loses power between the rename
2566
+ and the buffer flush, the dedup record is lost. We `fsync(tmp_fd)`
2567
+ before the rename and best-effort `fsync` the parent directory after.
2568
+ Both fsyncs are wrapped — fsync failure must NOT block the user
2569
+ session (the dedup is advisory; over-emitting once on crash is
2570
+ acceptable, lost-update is not).
2571
+ """
2572
+ try:
2573
+ path.parent.mkdir(parents=True, exist_ok=True)
2574
+ except OSError:
2575
+ return
2576
+ tmp = path.with_suffix(path.suffix + ".tmp")
2577
+ payload = json.dumps(state, separators=(",", ":")).encode("utf-8")
2578
+ try:
2579
+ # Write + fsync the data file before atomic rename.
2580
+ fd = os.open(str(tmp), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)
2581
+ try:
2582
+ os.write(fd, payload)
2583
+ try:
2584
+ os.fsync(fd)
2585
+ except OSError: # pragma: no cover — fsync best-effort
2586
+ pass
2587
+ finally:
2588
+ os.close(fd)
2589
+ os.replace(str(tmp), str(path))
2590
+ # Best-effort fsync of the parent directory so the rename
2591
+ # itself is durable. POSIX-only; NotImplementedError on win.
2592
+ try:
2593
+ dir_fd = os.open(str(path.parent), os.O_RDONLY)
2594
+ try:
2595
+ os.fsync(dir_fd)
2596
+ except OSError: # pragma: no cover
2597
+ pass
2598
+ finally:
2599
+ os.close(dir_fd)
2600
+ except OSError: # pragma: no cover — directory fsync optional
2601
+ pass
2602
+ except OSError:
2603
+ # Best-effort cleanup of the tmp file
2604
+ try:
2605
+ if tmp.exists():
2606
+ tmp.unlink()
2607
+ except OSError: # pragma: no cover
2608
+ pass
2609
+
2610
+
2611
+ def _is_subject_recent(state: Dict[str, Any], subject_hash: str) -> bool:
2612
+ """Return True if `subject_hash` is in state within TTL (already pruned by load)."""
2613
+ for entry in state.get("entries", []):
2614
+ if isinstance(entry, dict) and entry.get("subject_hash") == subject_hash:
2615
+ return True
2616
+ return False
2617
+
2618
+
2619
+ # Codex CDX-W5-P1-05 closure: collapse interior whitespace in a marker
2620
+ # subject so the `Subject:` line stays single-line. `_sanitize_for_recs`
2621
+ # strips angle brackets + backticks but preserves `\n` / `\t` / multi-
2622
+ # space, which can ambiguate the `<!-- /TASKCREATE-CANDIDATE -->`
2623
+ # closing marker if a recommendation summary contains a literal newline.
2624
+ # Bound to 200 chars (parity with `_sanitize_for_recs` length cap).
2625
+ def _collapse_marker_subject(text: str) -> str:
2626
+ """Single-line, length-bounded subject for `Subject:` marker line.
2627
+
2628
+ Python `re.sub(r"\\s+", " ", ...)` on a `str` matches Unicode
2629
+ whitespace (NBSP / em-space / narrow NBSP / line-tab / vertical-tab /
2630
+ form-feed in addition to ASCII), so the collapse is locale-safe.
2631
+ """
2632
+ if not isinstance(text, str):
2633
+ text = str(text)
2634
+ # Replace ALL whitespace runs (Unicode-aware) with a single space.
2635
+ text = re.sub(r"\s+", " ", text).strip()
2636
+ return text[:200]
2637
+
2638
+
2639
+ def _emit_task_candidate_safe(
2640
+ *,
2641
+ rank: int,
2642
+ severity: str,
2643
+ subject_hash: str,
2644
+ awaiting_confirm: bool = False,
2645
+ ) -> None:
2646
+ """Wire-up to audit_emit.emit_ceo_boot_task_candidate_emitted. Fail-open.
2647
+
2648
+ Pre-canonical-ceremony: hasattr() returns False, function is a no-op.
2649
+ Post-ceremony: emits the telemetry event with Sec MF-3 field allowlist
2650
+ enforced on the emit side (subject text NEVER leaves this script).
2651
+ """
2652
+ if _audit_emit is None:
2653
+ return
2654
+ fn = getattr(_audit_emit, "emit_ceo_boot_task_candidate_emitted", None)
2655
+ if not callable(fn):
2656
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2657
+ sys.stderr.write(
2658
+ "# ceo-boot: audit_emit.emit_ceo_boot_task_candidate_emitted "
2659
+ "not registered (canonical ceremony pending)\n"
2660
+ )
2661
+ return
2662
+ try:
2663
+ fn(
2664
+ session_id=_ceo_boot_session_id(),
2665
+ rank=int(rank),
2666
+ severity=str(severity),
2667
+ subject_hash=str(subject_hash),
2668
+ awaiting_confirm=bool(awaiting_confirm),
2669
+ )
2670
+ except Exception: # noqa: BLE001 — fail-open per audit_emit contract
2671
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2672
+ import traceback
2673
+ traceback.print_exc(file=sys.stderr)
2674
+
2675
+
2676
+ def _emit_task_candidate_markers(
2677
+ results: List[CheckResult],
2678
+ *,
2679
+ gate_pass: bool,
2680
+ short: bool,
2681
+ cached: bool,
2682
+ ) -> List[Dict[str, Any]]:
2683
+ """Write `<!-- TASKCREATE-CANDIDATE -->` blocks to stdout for top-3 recs.
2684
+
2685
+ Layer A of PLAN-078 Wave 5. Bypass paths (return [] without emit):
2686
+ * `gate_pass` is True (no actionable failure)
2687
+ * `short` mode (≤2s budget — skip per Perf table)
2688
+ * `cached` mode (handled by uncached path on next non-cached boot)
2689
+ * Env `CEO_BOOT_AUTO_TASK=0` (operator opt-out)
2690
+ * No medium/high recommendations after dedup
2691
+
2692
+ Returns the list of marker payloads emitted (used by tests + future
2693
+ JSON renderer). Each payload carries `rank`, `severity`,
2694
+ `subject_hash`, `subject` (not persisted — only stdout), and
2695
+ `awaiting_confirm`.
2696
+
2697
+ Sec MF-3 closure: `subject` text passes through `_sanitize_for_recs`
2698
+ (already applied by `_recommendations_with_severity` callee) before
2699
+ rendering; only the 12-hex `subject_hash` is persisted to the
2700
+ audit-log + dedup state. Raw stderr / check detail NEVER appears in
2701
+ the marker block.
2702
+ """
2703
+ if gate_pass:
2704
+ return []
2705
+ if short or cached:
2706
+ return []
2707
+ if os.environ.get("CEO_BOOT_AUTO_TASK") == "0":
2708
+ return []
2709
+
2710
+ triples = _recommendations_with_severity(results)
2711
+ # Codex CDX-W5-P1-01 closure: do NOT pre-slice to TASK_EMIT_TOP_N
2712
+ # before dedup. Iterate the full medium+/high actionable list and
2713
+ # break only after we've emitted TOP_N markers — otherwise three
2714
+ # already-deduped subjects at the head of the list would silently
2715
+ # block any 4th candidate from ever surfacing.
2716
+ actionable = [(t, s) for (_, t, s) in triples if s in ("medium", "high")]
2717
+ if not actionable:
2718
+ return []
2719
+
2720
+ state_path = _task_emit_state_path()
2721
+ lock_path = state_path.with_suffix(state_path.suffix + ".lock")
2722
+
2723
+ # Acquire filelock for read-modify-write of dedup state. Codex
2724
+ # CDX-W5-P1-02 closure: on FileLockTimeout we still emit markers
2725
+ # (fail-open — better to over-task once than silently drop) but we
2726
+ # do NOT persist the new state. Persisting unlocked state can
2727
+ # clobber a sibling process that just acquired the lock and wrote
2728
+ # different entries (lost-update). Operator pays the price of one
2729
+ # duplicate marker on the next boot in exchange for not corrupting
2730
+ # the audit-bookkeeping channel.
2731
+ #
2732
+ # Codex CDX-W5-iter3-P1 closure: any exception during lock acquisition
2733
+ # (OSError on bad path, PermissionError, NotImplementedError on
2734
+ # non-POSIX, etc.) used to fall through the OUTER except and silently
2735
+ # suppress every marker. We now narrow the lock-acquire try/except to
2736
+ # just lock setup; the marker-emit loop runs unconditionally with
2737
+ # `lock_acquired = False` if anything went wrong.
2738
+ emitted: List[Dict[str, Any]] = []
2739
+ rank = 0
2740
+ lock_acquired = False
2741
+ state: Dict[str, Any] = {"entries": []}
2742
+ lock_ctx = None
2743
+
2744
+ # --- Phase 1: try to acquire the lock + load state ---
2745
+ try:
2746
+ try:
2747
+ from _lib.filelock import FileLock, FileLockTimeout
2748
+ except Exception: # noqa: BLE001 — pre-canonical or import-broken
2749
+ FileLock = None # type: ignore[assignment]
2750
+ FileLockTimeout = Exception # type: ignore[assignment]
2751
+
2752
+ if FileLock is None:
2753
+ # No filelock available (pre-canonical / non-POSIX). Read
2754
+ # state opportunistically; allow persistence (best-effort).
2755
+ state = _load_task_emit_state(state_path)
2756
+ lock_acquired = True # treat as "owned" for save semantics
2757
+ else:
2758
+ try:
2759
+ lock_ctx = FileLock(str(lock_path), timeout=2.5)
2760
+ lock_ctx.__enter__()
2761
+ state = _load_task_emit_state(state_path)
2762
+ lock_acquired = True
2763
+ except FileLockTimeout:
2764
+ # Lock contended — emit unlocked, skip persist.
2765
+ state = _load_task_emit_state(state_path)
2766
+ lock_ctx = None
2767
+ lock_acquired = False
2768
+ except Exception: # noqa: BLE001 — invalid path, perm err, etc.
2769
+ # Any other error during lock acquisition — emit
2770
+ # unlocked, skip persist. Empty state means we may
2771
+ # over-emit (no dedup), but that's better than silent
2772
+ # suppression.
2773
+ state = {"entries": []}
2774
+ lock_ctx = None
2775
+ lock_acquired = False
2776
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2777
+ import traceback
2778
+ traceback.print_exc(file=sys.stderr)
2779
+ except Exception: # noqa: BLE001 — never let phase-1 abort markers
2780
+ state = {"entries": []}
2781
+ lock_ctx = None
2782
+ lock_acquired = False
2783
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2784
+ import traceback
2785
+ traceback.print_exc(file=sys.stderr)
2786
+
2787
+ # --- Phase 2: emit markers + persist (always runs, even after
2788
+ # phase-1 failure). Wrapped in its own try/except so any state-file
2789
+ # bug NEVER blocks the user session. ---
2790
+ try:
2791
+ now = time.time()
2792
+ for text, severity in actionable:
2793
+ # Codex CDX-W5-P1-05 closure: collapse interior whitespace
2794
+ # so the `Subject:` line stays single-line — newlines in a
2795
+ # recommendation summary would otherwise ambiguate the
2796
+ # closing marker for the orchestrator parser.
2797
+ safe_subject = _collapse_marker_subject(text)
2798
+ # Codex CDX-W5-iter2-P1 closure: hash the COLLAPSED subject
2799
+ # (the bytes the orchestrator actually parses + re-hashes
2800
+ # for dedup against the live task list). Hashing the raw
2801
+ # pre-collapse text would break the contract documented in
2802
+ # `commands/ceo-boot.md:Step 4.5` where the orchestrator
2803
+ # computes `sha256(NFKC(visible Subject))[:12]`.
2804
+ sh = _subject_hash(safe_subject)
2805
+ if _is_subject_recent(state, sh):
2806
+ continue
2807
+ rank += 1
2808
+ payload = {
2809
+ "rank": rank,
2810
+ "severity": severity,
2811
+ "subject_hash": sh,
2812
+ "subject": safe_subject,
2813
+ "awaiting_confirm": False,
2814
+ }
2815
+ sys.stdout.write(
2816
+ f"\n<!-- TASKCREATE-CANDIDATE rank={rank} "
2817
+ f"severity={severity} awaiting_confirm=false -->\n"
2818
+ )
2819
+ sys.stdout.write(f"Subject: {safe_subject}\n")
2820
+ sys.stdout.write("<!-- /TASKCREATE-CANDIDATE -->\n")
2821
+ state["entries"].append({"subject_hash": sh, "ts": now})
2822
+ emitted.append(payload)
2823
+ _emit_task_candidate_safe(
2824
+ rank=rank,
2825
+ severity=severity,
2826
+ subject_hash=sh,
2827
+ awaiting_confirm=False,
2828
+ )
2829
+ if rank >= TASK_EMIT_TOP_N:
2830
+ break
2831
+ # Codex CDX-W5-iter3 P2: keep state size bound after the post-load
2832
+ # append (load trims to MAX_ENTRIES, but we just added up to TOP_N
2833
+ # entries on top — re-cap before save so persisted state never
2834
+ # exceeds the documented MAX). LRU keep most-recent.
2835
+ entries = state.get("entries", [])
2836
+ if isinstance(entries, list) and len(entries) > TASK_EMIT_STATE_MAX_ENTRIES:
2837
+ entries.sort(key=lambda e: e.get("ts", 0), reverse=True)
2838
+ state["entries"] = entries[:TASK_EMIT_STATE_MAX_ENTRIES]
2839
+ if emitted and lock_acquired:
2840
+ _save_task_emit_state(state_path, state)
2841
+ except Exception: # noqa: BLE001 — fail-open: a state-file bug must
2842
+ # NEVER block the user session
2843
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2844
+ import traceback
2845
+ traceback.print_exc(file=sys.stderr)
2846
+ finally:
2847
+ if lock_ctx is not None:
2848
+ try:
2849
+ lock_ctx.__exit__(None, None, None)
2850
+ except Exception: # pragma: no cover — fail-open
2851
+ pass
2852
+ return emitted
2853
+
2854
+
2855
+ # === END PLAN-078 Wave 5 marker emit + dedup ===============================
2856
+
2857
+
2858
+ # ---- Main ------------------------------------------------------------------
2859
+
2860
+ # === PLAN-134 W4 — Morning Ledger renderer ================================
2861
+ # Renders the proposal-queue ledger (sign / don't sign / why, founder
2862
+ # language) as an extra default-mode section — same pattern as the Wave 5
2863
+ # TASKCREATE markers: NOT a Tier-S check (the registry is pinned at 20),
2864
+ # never affects gate_pass, fail-open on any error. Fast mode only
2865
+ # (manifest-level Merkle re-derivation); byte-level verification belongs to
2866
+ # morning-ceremony.py. Kill switch: CEO_BOOT_LEDGER=0.
2867
+ def _render_morning_ledger_safe() -> str:
2868
+ if os.environ.get("CEO_BOOT_LEDGER", "1") == "0":
2869
+ return ""
2870
+ try:
2871
+ import importlib.util as _ilu
2872
+ _ml_path = Path(__file__).resolve().parent / "morning_ledger.py"
2873
+ if not _ml_path.is_file():
2874
+ return ""
2875
+ _ml = sys.modules.get("morning_ledger")
2876
+ if _ml is None:
2877
+ _spec = _ilu.spec_from_file_location("morning_ledger", _ml_path)
2878
+ _ml = _ilu.module_from_spec(_spec)
2879
+ # py3.9 dataclasses + `from __future__ import annotations`
2880
+ # resolve field types via sys.modules[cls.__module__] — the
2881
+ # module MUST be registered before exec_module.
2882
+ sys.modules["morning_ledger"] = _ml
2883
+ _spec.loader.exec_module(_ml) # type: ignore[union-attr]
2884
+ if not _ml.pending_bundles():
2885
+ return ""
2886
+ rendered = _ml.render_ledger(deep=False)
2887
+ # Defense-in-depth: ledger text is disk-sourced — pass each line
2888
+ # through the same sanitizer the recommendations use (Sec MF-4).
2889
+ safe_lines = [_sanitize_for_recs(ln) if ln.strip() else ln for ln in rendered.splitlines()]
2890
+ return "\n" + "\n".join(safe_lines) + "\n"
2891
+ except Exception: # noqa: BLE001 — advisory section, never block boot
2892
+ if os.environ.get("CEO_BOOT_DEBUG") == "1":
2893
+ import traceback
2894
+ traceback.print_exc(file=sys.stderr)
2895
+ return ""
2896
+
2897
+
2898
+ def main(argv: List[str]) -> int:
2899
+ parser = argparse.ArgumentParser(description="ceo-boot session-boot autopilot")
2900
+ parser.add_argument("--short", action="store_true", help="terse output (≤15 lines target)")
2901
+ parser.add_argument("--cached", action="store_true", help="prefer cache-hit (≤200ms budget)")
2902
+ parser.add_argument("--bench", action="store_true", help="run N=5 bench harness (markdown table)")
2903
+ parser.add_argument("--bench-n", type=int, default=5, help="bench N runs (default 5)")
2904
+ parser.add_argument("--bench-json", action="store_true", help="emit bench report as JSON instead of markdown")
2905
+ parser.add_argument("--json", action="store_true", help="emit machine-readable JSON digest")
2906
+ parser.add_argument("--verbose", action="store_true", help="include 10 Tier-A checks (~10s budget)")
2907
+ args = parser.parse_args(argv)
2908
+
2909
+ # Codex S82 P1 fix: --short defaults to cached path per spec
2910
+ # (.claude/commands/ceo-boot.md:12 "--short defaults cached mode").
2911
+ # Was running full dispatch ignoring cache.
2912
+ if args.short and not args.cached:
2913
+ args.cached = True
2914
+
2915
+ if args.bench:
2916
+ report = bench(args.bench_n, include_tier_a=args.verbose)
2917
+ if args.bench_json or args.json:
2918
+ sys.stdout.write(json.dumps(report, indent=2))
2919
+ sys.stdout.write("\n")
2920
+ else:
2921
+ sys.stdout.write(render_bench_markdown(report))
2922
+ return 0
2923
+
2924
+ t0 = time.perf_counter()
2925
+
2926
+ if args.cached:
2927
+ hit, payload = cached_load()
2928
+ elapsed = (time.perf_counter() - t0) * 1000
2929
+ if hit:
2930
+ if args.json:
2931
+ sys.stdout.write(json.dumps(payload, indent=2))
2932
+ else:
2933
+ sys.stdout.write(f"\n## /ceo-boot --cached HIT ({elapsed:.0f} ms)\n")
2934
+ for r in payload["results"]:
2935
+ sys.stdout.write(f"- {r['name']}: {r['status']} — {r['summary']}\n")
2936
+ # PLAN-065 Phase 2 wire — cache-hit path. Replay the cached
2937
+ # gate_pass/checks_total summary so adopter telemetry counts
2938
+ # cached invocations (Reality-Ledger fixture #4 closure).
2939
+ cached_failed = sum(
2940
+ 1 for r in payload.get("results", [])
2941
+ if r.get("status") in ("red", "error", "timeout")
2942
+ )
2943
+ cached_total = len(payload.get("results", []))
2944
+ _emit_ceo_boot_emitted_safe(
2945
+ gate_pass=(cached_failed == 0),
2946
+ duration_ms=int(elapsed),
2947
+ checks_total=cached_total,
2948
+ checks_failed=cached_failed,
2949
+ cache_hit=True,
2950
+ )
2951
+ return 0
2952
+ else:
2953
+ sys.stderr.write(f"# cache-miss ({elapsed:.0f} ms) — falling back to full digest\n")
2954
+
2955
+ results = dispatch_parallel(include_tier_a=args.verbose)
2956
+ elapsed_ms = (time.perf_counter() - t0) * 1000
2957
+
2958
+ cached_store(results)
2959
+
2960
+ # Aggregate gate semantics: gate_pass = no red/error/timeout
2961
+ failed = sum(1 for r in results if r.status in ("red", "error", "timeout"))
2962
+ gate_pass = (failed == 0)
2963
+
2964
+ if args.json:
2965
+ out = {
2966
+ "elapsed_ms": elapsed_ms,
2967
+ "gate_pass": gate_pass,
2968
+ "checks_total": len(results),
2969
+ "checks_failed": failed,
2970
+ "recommendations": _make_recommendations(results),
2971
+ "results": [
2972
+ {"name": r.name, "status": r.status, "summary": r.summary, "duration_ms": r.duration_ms}
2973
+ for r in results
2974
+ ],
2975
+ }
2976
+ sys.stdout.write(json.dumps(out, indent=2))
2977
+ sys.stdout.write("\n")
2978
+ else:
2979
+ sys.stdout.write(render_digest(results, short=args.short))
2980
+ sys.stdout.write(f"\nWall-clock: {elapsed_ms:.0f} ms (gate_pass={gate_pass}, failed={failed}/{len(results)})\n")
2981
+ # PLAN-134 W4 — Morning Ledger section (default full mode only;
2982
+ # --short keeps its 5-line budget). Empty string when queue is
2983
+ # empty, module missing, or CEO_BOOT_LEDGER=0.
2984
+ if not args.short:
2985
+ sys.stdout.write(_render_morning_ledger_safe())
2986
+
2987
+ # PLAN-078 Wave 5 — TaskCreate-candidate markers. Bypass paths handled
2988
+ # inside `_emit_task_candidate_markers`: gate_pass=True, --short,
2989
+ # --cached, env CEO_BOOT_AUTO_TASK=0, no medium+/high recs after
2990
+ # 24h-TTL dedup. Markers go to stdout (parsed by Claude orchestrator
2991
+ # running /ceo-boot per `commands/ceo-boot.md` workflow). Audit emit
2992
+ # of `ceo_boot_task_candidate_emitted` is invoked per-marker via
2993
+ # `_emit_task_candidate_safe` (hasattr-guarded pre-canonical-ceremony).
2994
+ # JSON mode skips marker emit so machine consumers see only the JSON
2995
+ # payload; switch to default markdown mode to surface markers.
2996
+ if not args.json:
2997
+ _emit_task_candidate_markers(
2998
+ results,
2999
+ gate_pass=gate_pass,
3000
+ short=args.short,
3001
+ cached=args.cached,
3002
+ )
3003
+
3004
+ # PLAN-065 Phase 2 wire — uncached path. Emits gate_pass + counts
3005
+ # only (Sec MF-3 field allowlist denies tokens/cost/paths/prompt/SKILL/env).
3006
+ _emit_ceo_boot_emitted_safe(
3007
+ gate_pass=gate_pass,
3008
+ duration_ms=int(elapsed_ms),
3009
+ checks_total=len(results),
3010
+ checks_failed=failed,
3011
+ cache_hit=False,
3012
+ )
3013
+ return 0
3014
+
3015
+
3016
+ if __name__ == "__main__":
3017
+ sys.exit(main(sys.argv[1:]))