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,3333 @@
1
+ #!/usr/bin/env python3
2
+ """audit-query — query the ceo-orchestration agent spawn audit log.
3
+
4
+ Stdlib-only CLI that reads `audit-log.jsonl` (+ rotated siblings) and
5
+ answers common operator questions:
6
+
7
+ audit-query.py summary
8
+ audit-query.py by-skill [--top N]
9
+ audit-query.py compliance
10
+ audit-query.py by-day [--days N]
11
+ audit-query.py search <regex>
12
+ audit-query.py since <ISO-date>
13
+ audit-query.py errors
14
+ audit-query.py stats
15
+ audit-query.py export [--format csv|json|tsv]
16
+ audit-query.py by-domain [--window=30d|--start=YYYY-MM-DD --end=YYYY-MM-DD]
17
+ [--check-reopen]
18
+
19
+ Default input path:
20
+ ${CEO_AUDIT_LOG_PATH:-$HOME/.claude/projects/ceo-orchestration/audit-log.jsonl}
21
+
22
+ Pass `--log <path>` to override or `--include-rotated` to aggregate
23
+ across all `audit-log*.jsonl` in the audit directory.
24
+
25
+ Output formats:
26
+ - Default: human-readable tab-separated table
27
+ - `--json` flag: machine-readable JSON
28
+ - `--csv` flag: CSV
29
+
30
+ Exit codes:
31
+ 0 — success (including empty result set)
32
+ 1 — log file missing OR bad query
33
+ 2 — unreadable log file (permissions, etc.)
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import argparse
39
+ import csv
40
+ import io
41
+ import json
42
+ import os
43
+ import re
44
+ import sys
45
+ from collections import Counter, defaultdict
46
+ from datetime import datetime, timedelta, timezone
47
+ from pathlib import Path
48
+ from typing import Any, Dict, Iterable, Iterator, List, Optional, Tuple
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Path resolution (mirrors audit_log.py conventions)
53
+ # ---------------------------------------------------------------------------
54
+
55
+
56
+ def default_log_path() -> Path:
57
+ """Return the conventional audit log path from env vars / defaults."""
58
+ home = Path(os.environ.get("HOME") or str(Path.home()))
59
+ default_dir = home / ".claude" / "projects" / "ceo-orchestration"
60
+ return Path(
61
+ os.environ.get("CEO_AUDIT_LOG_PATH") or str(default_dir / "audit-log.jsonl")
62
+ )
63
+
64
+
65
+ def default_errors_path() -> Path:
66
+ home = Path(os.environ.get("HOME") or str(Path.home()))
67
+ default_dir = home / ".claude" / "projects" / "ceo-orchestration"
68
+ return Path(
69
+ os.environ.get("CEO_AUDIT_LOG_ERR") or str(default_dir / "audit-log.errors")
70
+ )
71
+
72
+
73
+ def discover_logs(primary: Path, include_rotated: bool) -> List[Path]:
74
+ """Return the list of log files to read, sorted by modification time."""
75
+ if not include_rotated:
76
+ return [primary] if primary.is_file() else []
77
+ if not primary.parent.is_dir():
78
+ return []
79
+ stem = primary.stem # "audit-log"
80
+ siblings = []
81
+ for candidate in primary.parent.glob(f"{stem}*.jsonl"):
82
+ if candidate.is_file():
83
+ siblings.append(candidate)
84
+ # Sort: primary file last so its entries are newest in the stream
85
+ siblings.sort(key=lambda p: p.stat().st_mtime)
86
+ return siblings
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Streaming reader — tolerates malformed lines with a stderr breadcrumb
91
+ # ---------------------------------------------------------------------------
92
+
93
+
94
+ # ---------------------------------------------------------------------------
95
+ # Perf-P1-002 — large-log materialization warning threshold.
96
+ #
97
+ # Subcommands that need FULL context (median, percentile, debate grouping,
98
+ # weekly-summary) still materialize the list. At this size threshold we
99
+ # emit a stderr hint so operators learn the log grew past the streaming
100
+ # regime and consider `--include-rotated=false` or log rotation.
101
+ # Value = 100_000 entries ≈ ~40-80 MB typical ≈ ~500-900ms parse on a
102
+ # modern laptop. Under this bar the materialization is unnoticeable.
103
+ _MATERIALIZATION_WARN_THRESHOLD = 100_000
104
+
105
+
106
+ def read_entries(
107
+ paths: Iterable[Path],
108
+ *,
109
+ warn_stream=None,
110
+ ) -> Iterator[Dict[str, Any]]:
111
+ """Yield parsed JSON entries from the given log paths, streaming.
112
+
113
+ Malformed lines are skipped with a breadcrumb to warn_stream.
114
+ Large logs are read line-by-line so memory stays constant.
115
+
116
+ Note: warn_stream defaults to None → resolved to sys.stderr at call
117
+ time, not definition time, so test harness stderr redirection works.
118
+ """
119
+ if warn_stream is None:
120
+ warn_stream = sys.stderr
121
+ for path in paths:
122
+ try:
123
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
124
+ for lineno, line in enumerate(f, start=1):
125
+ line = line.strip()
126
+ if not line:
127
+ continue
128
+ try:
129
+ entry = json.loads(line)
130
+ except json.JSONDecodeError as e:
131
+ print(
132
+ f"[audit-query] WARN: {path}:{lineno}: "
133
+ f"skipping malformed JSONL ({e.msg})",
134
+ file=warn_stream,
135
+ )
136
+ continue
137
+ if not isinstance(entry, dict):
138
+ continue
139
+ yield entry
140
+ except OSError as e:
141
+ print(
142
+ f"[audit-query] WARN: cannot read {path}: {e}",
143
+ file=warn_stream,
144
+ )
145
+
146
+
147
+ # ---------------------------------------------------------------------------
148
+ # Sub-command implementations
149
+ # ---------------------------------------------------------------------------
150
+
151
+
152
+ def cmd_summary(entries, args) -> Dict[str, Any]:
153
+ """Summary aggregates — single-pass streaming (Perf-P1-002).
154
+
155
+ Accepts any iterable of entries. Computes total / first_ts / last_ts /
156
+ top 5 skills / compliant count in one pass so a 100k-row log does not
157
+ need to be fully materialized.
158
+ """
159
+ total = 0
160
+ first_ts = ""
161
+ last_ts = ""
162
+ skill_counts: Counter = Counter()
163
+ compliant = 0
164
+ for e in entries:
165
+ total += 1
166
+ ts = e.get("ts", "")
167
+ if ts:
168
+ if not first_ts or ts < first_ts:
169
+ first_ts = ts
170
+ if ts > last_ts:
171
+ last_ts = ts
172
+ skill_counts[e.get("skill", "unknown")] += 1
173
+ if (
174
+ e.get("has_profile")
175
+ and e.get("has_file_assignment")
176
+ and e.get("skill") != "unknown"
177
+ ):
178
+ compliant += 1
179
+
180
+ if total == 0:
181
+ return {
182
+ "total_spawns": 0,
183
+ "date_range": None,
184
+ "top_skills": [],
185
+ "compliance_rate": None,
186
+ }
187
+
188
+ top_skills = skill_counts.most_common(5)
189
+ compliance_rate = compliant / total
190
+ return {
191
+ "total_spawns": total,
192
+ "date_range": {"from": first_ts, "to": last_ts},
193
+ "top_skills": [{"skill": s, "count": c} for s, c in top_skills],
194
+ "compliance_rate": round(compliance_rate, 3),
195
+ "compliant_spawns": compliant,
196
+ }
197
+
198
+
199
+ def cmd_by_skill(entries, args) -> List[Dict[str, Any]]:
200
+ """Streamable — single-pass Counter aggregation (Perf-P1-002)."""
201
+ counts: Counter = Counter()
202
+ for e in entries:
203
+ counts[e.get("skill", "unknown")] += 1
204
+ top = counts.most_common(args.top)
205
+ return [{"skill": s, "count": c} for s, c in top]
206
+
207
+
208
+ def cmd_compliance(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
209
+ """Handle the `audit-query compliance` sub-command — SOC2/LGPD evidence pack."""
210
+ total = len(entries)
211
+ if total == 0:
212
+ return {
213
+ "total": 0,
214
+ "has_profile_rate": None,
215
+ "has_file_assignment_rate": None,
216
+ "known_skill_rate": None,
217
+ }
218
+
219
+ with_profile = sum(1 for e in entries if e.get("has_profile"))
220
+ with_fa = sum(1 for e in entries if e.get("has_file_assignment"))
221
+ with_skill = sum(1 for e in entries if e.get("skill") != "unknown")
222
+
223
+ non_compliant = [
224
+ {
225
+ "ts": e.get("ts"),
226
+ "skill": e.get("skill", "unknown"),
227
+ "has_profile": e.get("has_profile", False),
228
+ "has_file_assignment": e.get("has_file_assignment", False),
229
+ "desc_preview": e.get("desc_preview", ""),
230
+ }
231
+ for e in entries
232
+ if not (
233
+ e.get("has_profile")
234
+ and e.get("has_file_assignment")
235
+ and e.get("skill") != "unknown"
236
+ )
237
+ ]
238
+
239
+ return {
240
+ "total": total,
241
+ "has_profile": with_profile,
242
+ "has_profile_rate": round(with_profile / total, 3),
243
+ "has_file_assignment": with_fa,
244
+ "has_file_assignment_rate": round(with_fa / total, 3),
245
+ "known_skill": with_skill,
246
+ "known_skill_rate": round(with_skill / total, 3),
247
+ "non_compliant_count": len(non_compliant),
248
+ "non_compliant": non_compliant[:20], # cap preview
249
+ }
250
+
251
+
252
+ def cmd_by_day(entries, args) -> List[Dict[str, Any]]:
253
+ """Streamable — single-pass histogram (Perf-P1-002).
254
+
255
+ Fast-path: cutoff is compared as a fixed YYYY-MM-DD string (since our
256
+ `ts` format is lexicographically sortable). We only fall back to
257
+ ``datetime.strptime`` when the raw timestamp doesn't match the
258
+ expected 20-char ISO-Z shape — a rare case (parse errors / rotated
259
+ schemas) that does not dominate 100k-entry runs.
260
+ """
261
+ days = args.days
262
+ now = datetime.now(timezone.utc)
263
+ cutoff = now - timedelta(days=days)
264
+ cutoff_day = cutoff.strftime("%Y-%m-%d")
265
+ histogram: Dict[str, int] = defaultdict(int)
266
+ for e in entries:
267
+ ts = e.get("ts", "")
268
+ if not ts:
269
+ continue
270
+ # Fast path: YYYY-MM-DDTHH:MM:SSZ is 20 chars with day at 0..10
271
+ if len(ts) >= 20 and ts.endswith("Z") and ts[10:11] == "T":
272
+ day_key = ts[:10]
273
+ # Lexicographic compare works because YYYY-MM-DD is fixed width.
274
+ if day_key < cutoff_day:
275
+ continue
276
+ else:
277
+ try:
278
+ when = datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(
279
+ tzinfo=timezone.utc
280
+ )
281
+ except ValueError:
282
+ continue
283
+ if when < cutoff:
284
+ continue
285
+ day_key = when.strftime("%Y-%m-%d")
286
+ histogram[day_key] += 1
287
+
288
+ result = [
289
+ {"date": day, "count": count}
290
+ for day, count in sorted(histogram.items())
291
+ ]
292
+ return result
293
+
294
+
295
+ def cmd_search(entries, args) -> List[Dict[str, Any]]:
296
+ """Streamable — filter via regex (Perf-P1-002)."""
297
+ try:
298
+ pattern = re.compile(args.regex, flags=re.IGNORECASE)
299
+ except re.error as e:
300
+ print(f"[audit-query] ERROR: bad regex: {e}", file=sys.stderr)
301
+ sys.exit(1)
302
+ matches = []
303
+ for e in entries:
304
+ preview = e.get("desc_preview", "")
305
+ if pattern.search(preview):
306
+ matches.append(
307
+ {
308
+ "ts": e.get("ts"),
309
+ "skill": e.get("skill"),
310
+ "desc_preview": preview,
311
+ "desc_hash": e.get("desc_hash"),
312
+ }
313
+ )
314
+ return matches
315
+
316
+
317
+ def cmd_since(entries, args) -> List[Dict[str, Any]]:
318
+ """Streamable — filter by date cutoff (Perf-P1-002)."""
319
+ # Parse the input date (supports YYYY-MM-DD, YYYY-MM-DDTHH:MM:SSZ)
320
+ raw = args.iso_date
321
+ parsed = None
322
+ for fmt in ("%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%d"):
323
+ try:
324
+ parsed = datetime.strptime(raw, fmt).replace(tzinfo=timezone.utc)
325
+ break
326
+ except ValueError:
327
+ continue
328
+ if parsed is None:
329
+ print(
330
+ f"[audit-query] ERROR: cannot parse date {raw!r} "
331
+ "(use YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ)",
332
+ file=sys.stderr,
333
+ )
334
+ sys.exit(1)
335
+
336
+ # Fast path: both parsed cutoff and entry ts are ISO-Z → string compare
337
+ # is equivalent to datetime compare (fixed-width YYYY-MM-DDTHH:MM:SSZ).
338
+ # Falls back to strptime for non-canonical ts shapes.
339
+ cutoff_str = parsed.strftime("%Y-%m-%dT%H:%M:%SZ")
340
+ out = []
341
+ for e in entries:
342
+ ts = e.get("ts", "")
343
+ if len(ts) >= 20 and ts.endswith("Z") and ts[10:11] == "T":
344
+ if ts >= cutoff_str:
345
+ out.append(e)
346
+ continue
347
+ try:
348
+ when = datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ").replace(
349
+ tzinfo=timezone.utc
350
+ )
351
+ except ValueError:
352
+ continue
353
+ if when >= parsed:
354
+ out.append(e)
355
+ return out
356
+
357
+
358
+ def cmd_stats(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
359
+ """Handle the `audit-query stats` sub-command — aggregate counts by action."""
360
+ # PLAN-125 WS-1 — per-tool-call lifecycle latency view (--tool-latency).
361
+ if getattr(args, "tool_latency", False):
362
+ return _tool_latency_stats(entries)
363
+
364
+ total = len(entries)
365
+ if total == 0:
366
+ return {"total": 0}
367
+
368
+ bucket_counts = Counter(
369
+ e.get("prompt_len_bucket", "unknown") for e in entries
370
+ )
371
+ response_kinds = Counter(e.get("response_kind", "absent") for e in entries)
372
+
373
+ # hook_duration_ms stats (tolerates missing — older entries don't have it)
374
+ durations = [
375
+ e.get("hook_duration_ms")
376
+ for e in entries
377
+ if isinstance(e.get("hook_duration_ms"), (int, float))
378
+ ]
379
+ duration_summary: Dict[str, Any]
380
+ if durations:
381
+ durations_sorted = sorted(durations)
382
+ duration_summary = {
383
+ "count": len(durations_sorted),
384
+ "min_ms": durations_sorted[0],
385
+ "max_ms": durations_sorted[-1],
386
+ "mean_ms": round(sum(durations_sorted) / len(durations_sorted), 1),
387
+ "p50_ms": _percentile(durations_sorted, 50),
388
+ "p95_ms": _percentile(durations_sorted, 95),
389
+ "p99_ms": _percentile(durations_sorted, 99),
390
+ }
391
+ else:
392
+ duration_summary = {"count": 0}
393
+
394
+ return {
395
+ "total": total,
396
+ "prompt_len_buckets": dict(bucket_counts),
397
+ "response_kinds": dict(response_kinds),
398
+ "hook_duration_ms": duration_summary,
399
+ }
400
+
401
+
402
+ def _tool_latency_stats(entries: List[Dict[str, Any]]) -> Dict[str, Any]:
403
+ """PLAN-125 WS-1 — per-tool lifecycle bucket histogram.
404
+
405
+ Reads ``tool_call_lifecycle_recorded`` rows and rolls up, per
406
+ ``tool_name_enum``, a Counter of ``duration_bucket`` plus success / orphan
407
+ tallies. Bucket-counts ONLY — the action never records a raw
408
+ ``duration_ms`` (MF-SEC-3), so there are no percentiles to compute.
409
+ """
410
+ rows = [
411
+ e for e in entries
412
+ if e.get("action") == "tool_call_lifecycle_recorded"
413
+ ]
414
+ per_tool: Dict[str, Dict[str, Any]] = {}
415
+ for e in rows:
416
+ tool = e.get("tool_name_enum", "other")
417
+ if not isinstance(tool, str):
418
+ tool = "other"
419
+ slot = per_tool.setdefault(
420
+ tool,
421
+ {"count": 0, "duration_buckets": Counter(),
422
+ "success": 0, "failure": 0, "orphan": 0},
423
+ )
424
+ slot["count"] += 1
425
+ bucket = e.get("duration_bucket", "unknown")
426
+ if not isinstance(bucket, str):
427
+ bucket = "unknown"
428
+ slot["duration_buckets"][bucket] += 1
429
+ if e.get("orphan") is True:
430
+ slot["orphan"] += 1
431
+ if e.get("success") is True:
432
+ slot["success"] += 1
433
+ elif e.get("success") is False:
434
+ slot["failure"] += 1
435
+
436
+ # Materialize Counters to plain dicts for JSON / display.
437
+ tool_latency = {
438
+ tool: {
439
+ "count": slot["count"],
440
+ "duration_buckets": dict(slot["duration_buckets"]),
441
+ "success": slot["success"],
442
+ "failure": slot["failure"],
443
+ "orphan": slot["orphan"],
444
+ }
445
+ for tool, slot in sorted(per_tool.items())
446
+ }
447
+ return {
448
+ "total_lifecycle_rows": len(rows),
449
+ "tool_latency": tool_latency,
450
+ }
451
+
452
+
453
+ def _percentile(sorted_values: List[float], p: float) -> float:
454
+ if not sorted_values:
455
+ return 0.0
456
+ k = (len(sorted_values) - 1) * (p / 100.0)
457
+ f = int(k)
458
+ c = min(f + 1, len(sorted_values) - 1)
459
+ if f == c:
460
+ return round(float(sorted_values[f]), 1)
461
+ return round(
462
+ sorted_values[f] + (sorted_values[c] - sorted_values[f]) * (k - f),
463
+ 1,
464
+ )
465
+
466
+
467
+ def cmd_errors(args) -> Dict[str, Any]:
468
+ err_path = Path(args.errors_path) if args.errors_path else default_errors_path()
469
+ if not err_path.is_file():
470
+ return {"errors_path": str(err_path), "count": 0, "lines": []}
471
+ try:
472
+ text = err_path.read_text(encoding="utf-8", errors="replace")
473
+ except OSError as e:
474
+ print(f"[audit-query] ERROR: cannot read {err_path}: {e}", file=sys.stderr)
475
+ sys.exit(2)
476
+ lines = [ln for ln in text.split("\n") if ln.strip()]
477
+ return {
478
+ "errors_path": str(err_path),
479
+ "count": len(lines),
480
+ "lines": lines[-50:], # tail
481
+ }
482
+
483
+
484
+ def cmd_debate(entries: List[Dict[str, Any]], args) -> List[Dict[str, Any]]:
485
+ """Group debate_event rows by (plan_id, round). Sprint 5 A.2."""
486
+ groups: Dict[Tuple[str, int], Dict[str, Any]] = {}
487
+ for e in entries:
488
+ if e.get("action") != "debate_event":
489
+ continue
490
+ key = (e.get("plan_id", "?"), int(e.get("round") or 0))
491
+ g = groups.setdefault(
492
+ key,
493
+ {
494
+ "plan_id": key[0],
495
+ "round": key[1],
496
+ "start_ts": None,
497
+ "consensus_ts": None,
498
+ "agents": [],
499
+ "consensus_adjustments": None,
500
+ },
501
+ )
502
+ phase = e.get("phase")
503
+ ts = e.get("ts")
504
+ if phase == "start":
505
+ g["start_ts"] = ts
506
+ elif phase == "agent-done":
507
+ agent = e.get("agent")
508
+ if agent and agent not in g["agents"]:
509
+ g["agents"].append(agent)
510
+ elif phase == "consensus":
511
+ g["consensus_ts"] = ts
512
+ if e.get("consensus_adjustments_count") is not None:
513
+ g["consensus_adjustments"] = e["consensus_adjustments_count"]
514
+ # Sort by (plan_id, round)
515
+ return [groups[k] for k in sorted(groups.keys())]
516
+
517
+
518
+ def cmd_plans(entries: List[Dict[str, Any]], args) -> List[Dict[str, Any]]:
519
+ """Show plan status transitions over time. Sprint 5 A.2."""
520
+ by_plan: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
521
+ for e in entries:
522
+ if e.get("action") != "plan_transition":
523
+ continue
524
+ by_plan[e.get("plan_id", "?")].append(
525
+ {
526
+ "ts": e.get("ts"),
527
+ "from": e.get("from_status"),
528
+ "to": e.get("to_status"),
529
+ "editor": e.get("editor_tool"),
530
+ }
531
+ )
532
+ # Sort transitions by ts, then emit one row per plan with a chain summary
533
+ out = []
534
+ for plan_id in sorted(by_plan.keys()):
535
+ trans = sorted(by_plan[plan_id], key=lambda t: t.get("ts") or "")
536
+ chain = "→".join(
537
+ [t["from"] for t in trans] + [trans[-1]["to"]] if trans else []
538
+ )
539
+ out.append(
540
+ {
541
+ "plan_id": plan_id,
542
+ "transitions": len(trans),
543
+ "chain": chain,
544
+ "first_ts": trans[0]["ts"] if trans else None,
545
+ "last_ts": trans[-1]["ts"] if trans else None,
546
+ "current_status": trans[-1]["to"] if trans else None,
547
+ }
548
+ )
549
+ return out
550
+
551
+
552
+ def cmd_vetoes(entries: List[Dict[str, Any]], args) -> List[Dict[str, Any]]:
553
+ """Aggregate veto_triggered rows by (hook, reason_code). Sprint 5 A.2."""
554
+ counts: Dict[Tuple[str, str], Dict[str, Any]] = {}
555
+ for e in entries:
556
+ if e.get("action") != "veto_triggered":
557
+ continue
558
+ key = (e.get("hook", "?"), e.get("reason_code", "?"))
559
+ g = counts.setdefault(
560
+ key,
561
+ {
562
+ "hook": key[0],
563
+ "reason_code": key[1],
564
+ "count": 0,
565
+ "first_ts": None,
566
+ "last_ts": None,
567
+ "sample_preview": "",
568
+ },
569
+ )
570
+ g["count"] += 1
571
+ ts = e.get("ts") or ""
572
+ if g["first_ts"] is None or ts < g["first_ts"]:
573
+ g["first_ts"] = ts
574
+ if g["last_ts"] is None or ts > g["last_ts"]:
575
+ g["last_ts"] = ts
576
+ if not g["sample_preview"] and e.get("reason_preview"):
577
+ g["sample_preview"] = e["reason_preview"]
578
+ return sorted(
579
+ counts.values(),
580
+ key=lambda g: (-g["count"], g["hook"], g["reason_code"]),
581
+ )
582
+
583
+
584
+ def _bench_cost_usd(r: Dict[str, Any]) -> float:
585
+ """Per-run benchmark cost in USD. Prefers the int-encoded
586
+ ``cost_usd_cents`` (÷100); falls back to a legacy float ``cost_usd``.
587
+ Returns 0.0 when neither is present (pre-cost-instrumented runs)."""
588
+ cents = r.get("cost_usd_cents")
589
+ if cents is not None:
590
+ try:
591
+ return int(cents) / 100.0
592
+ except (TypeError, ValueError):
593
+ return 0.0
594
+ try:
595
+ return float(r.get("cost_usd") or 0.0)
596
+ except (TypeError, ValueError):
597
+ return 0.0
598
+
599
+
600
+ def _bench_duration_s(r: Dict[str, Any]) -> float:
601
+ """Per-run wall-clock (the harbor 'compute' column) in seconds.
602
+ Prefers int-encoded ``duration_ms`` (÷1000); falls back to legacy
603
+ float ``duration_s``."""
604
+ ms = r.get("duration_ms")
605
+ if ms is not None:
606
+ try:
607
+ return int(ms) / 1000.0
608
+ except (TypeError, ValueError):
609
+ return 0.0
610
+ try:
611
+ return float(r.get("duration_s") or 0.0)
612
+ except (TypeError, ValueError):
613
+ return 0.0
614
+
615
+
616
+ def _bench_turns(r: Dict[str, Any]) -> int:
617
+ """Per-run scenario count (the harbor 'turns' column). Each scenario
618
+ is one model interaction unit; pass+fail covers every scored
619
+ scenario. Tolerates missing counts."""
620
+ try:
621
+ return int(r.get("pass_count") or 0) + int(r.get("fail_count") or 0)
622
+ except (TypeError, ValueError):
623
+ return 0
624
+
625
+
626
+ def cmd_benchmarks(
627
+ entries: List[Dict[str, Any]], args
628
+ ) -> List[Dict[str, Any]]:
629
+ """Aggregate benchmark_run rows by skill. Sprint 5 A.2; PLAN-133 C4.
630
+
631
+ Reports run count, latest pass_rate, median across runs, cumulative
632
+ lessons_written per skill.
633
+
634
+ PLAN-133 C4 (harbor-style row): co-reports **cost + compute + turns
635
+ alongside pass-rate** so a benchmark is never read as a bare scalar.
636
+ The added columns are strictly additive (existing keys unchanged) and
637
+ derive only from fields already on the ``benchmark_run`` event
638
+ (``cost_usd_cents``, ``duration_ms``, ``pass_count``/``fail_count``) —
639
+ no SPEC change is required, so this reader stays $0 and canonical-free.
640
+ """
641
+ by_skill: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
642
+ for e in entries:
643
+ if e.get("action") != "benchmark_run":
644
+ continue
645
+ by_skill[e.get("skill", "?")].append(e)
646
+ out = []
647
+ for skill in sorted(by_skill.keys()):
648
+ runs = sorted(by_skill[skill], key=lambda r: r.get("ts") or "")
649
+ # Prefer new int-encoded fields (bps ÷ 1000 → float); fall back to
650
+ # the legacy float fields for logs written before the migration.
651
+ def _pass_rate(r: Dict[str, Any]) -> float:
652
+ bps = r.get("pass_rate_bps")
653
+ if bps is not None:
654
+ return int(bps) / 1000.0
655
+ return float(r.get("pass_rate") or 0.0)
656
+
657
+ def _floor_rate(r: Dict[str, Any]) -> float:
658
+ bps = r.get("floor_bps")
659
+ if bps is not None:
660
+ return int(bps) / 1000.0
661
+ return float(r.get("floor") or 0.0)
662
+
663
+ rates = [_pass_rate(r) for r in runs]
664
+ if rates:
665
+ med = _percentile(sorted(rates), 50)
666
+ else:
667
+ med = 0.0
668
+ latest = runs[-1]
669
+ # PLAN-133 C4 — harbor-style compute/cost/turns co-report.
670
+ # Cumulative across all runs for the skill + the latest single run,
671
+ # so an operator sees both the trend cost and the marginal cost.
672
+ total_cost = sum(_bench_cost_usd(r) for r in runs)
673
+ total_compute_s = sum(_bench_duration_s(r) for r in runs)
674
+ total_turns = sum(_bench_turns(r) for r in runs)
675
+ out.append(
676
+ {
677
+ "skill": skill,
678
+ "runs": len(runs),
679
+ "latest_pass_rate": round(_pass_rate(latest), 3),
680
+ "median_pass_rate": med,
681
+ "last_floor": _floor_rate(latest),
682
+ "latest_ts": latest.get("ts"),
683
+ "total_lessons_written": sum(
684
+ int(r.get("lessons_written") or 0) for r in runs
685
+ ),
686
+ # --- PLAN-133 C4 harbor-style row (additive) ---
687
+ "latest_cost_usd": round(_bench_cost_usd(latest), 6),
688
+ "total_cost_usd": round(total_cost, 6),
689
+ "latest_compute_s": round(_bench_duration_s(latest), 3),
690
+ "total_compute_s": round(total_compute_s, 3),
691
+ "latest_turns": _bench_turns(latest),
692
+ "total_turns": total_turns,
693
+ }
694
+ )
695
+ return out
696
+
697
+
698
+ def cmd_lessons(entries: List[Dict[str, Any]], args) -> List[Dict[str, Any]]:
699
+ """Aggregate lesson_write rows by archetype. Sprint 5 A.2."""
700
+ by_arch: Dict[str, Dict[str, Any]] = {}
701
+ triggers = Counter()
702
+ for e in entries:
703
+ if e.get("action") != "lesson_write":
704
+ continue
705
+ arch = e.get("archetype", "?")
706
+ g = by_arch.setdefault(
707
+ arch,
708
+ {
709
+ "archetype": arch,
710
+ "count": 0,
711
+ "triggers": Counter(),
712
+ "last_ts": None,
713
+ },
714
+ )
715
+ g["count"] += 1
716
+ g["triggers"][e.get("trigger", "unknown")] += 1
717
+ ts = e.get("ts") or ""
718
+ if g["last_ts"] is None or ts > g["last_ts"]:
719
+ g["last_ts"] = ts
720
+ triggers[e.get("trigger", "unknown")] += 1
721
+
722
+ out = []
723
+ for arch in sorted(by_arch.keys(), key=lambda a: (-by_arch[a]["count"], a)):
724
+ g = by_arch[arch]
725
+ # Collapse the Counter to a dict for JSON-friendly rendering
726
+ g["triggers"] = dict(g["triggers"])
727
+ out.append(g)
728
+ return out
729
+
730
+
731
+ def _parse_since_arg(raw: str) -> Optional[datetime]:
732
+ """Parse `--since` value: ``24h`` / ``7d`` / ``30m`` / ISO 8601 / ``all``.
733
+
734
+ Returns a timezone-aware UTC datetime cutoff, or None for "all time".
735
+ PLAN-009 A18 (shared across new sub-commands).
736
+ """
737
+ if not raw or raw == "all":
738
+ return None
739
+ raw = raw.strip().lower()
740
+ # Shorthand: <int><unit> (m=minutes, h=hours, d=days)
741
+ if re.fullmatch(r"\d+[mhd]", raw):
742
+ n = int(raw[:-1])
743
+ unit = raw[-1]
744
+ delta = {"m": timedelta(minutes=n), "h": timedelta(hours=n), "d": timedelta(days=n)}[unit]
745
+ return datetime.now(timezone.utc) - delta
746
+ # Try ISO 8601 parse
747
+ try:
748
+ if raw.endswith("z"):
749
+ raw = raw[:-1] + "+00:00"
750
+ dt = datetime.fromisoformat(raw)
751
+ if dt.tzinfo is None:
752
+ dt = dt.replace(tzinfo=timezone.utc)
753
+ return dt
754
+ except ValueError:
755
+ return None
756
+
757
+
758
+ def cmd_lessons_effectiveness(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
759
+ """`audit-query lessons-effectiveness` — per-lesson effectiveness ranking.
760
+
761
+ PLAN-009 Phase 5 P5.1. Aggregates `lesson_outcome` + `lesson_read`
762
+ events per `lesson_id`; computes effectiveness + injection count +
763
+ recency; sorts by operator-chosen axis.
764
+
765
+ Output envelope (SPEC/v1/audit-query.schema.md):
766
+ {"query": "lessons-effectiveness", "version": "1",
767
+ "data": {"lessons": [...]}}
768
+ """
769
+ since = _parse_since_arg(getattr(args, "since", "24h"))
770
+ include_window_only = getattr(args, "include_window_only", False)
771
+ sort_by = getattr(args, "by", "effectiveness")
772
+ top_n = int(getattr(args, "top", 0) or 0)
773
+ bottom_n = int(getattr(args, "bottom", 0) or 0)
774
+
775
+ def _in_window(e: Dict[str, Any]) -> bool:
776
+ if since is None:
777
+ return True
778
+ ts = _parse_since_arg(e.get("ts", ""))
779
+ return ts is None or ts >= since
780
+
781
+ from collections import defaultdict
782
+ agg: Dict[str, Dict[str, Any]] = defaultdict(
783
+ lambda: {"hit": 0, "miss": 0, "injections": 0,
784
+ "last": "", "modes": {}}
785
+ )
786
+
787
+ for e in entries:
788
+ action = e.get("action")
789
+ if action == "lesson_outcome":
790
+ mode = e.get("inference_mode", "") or "unspecified"
791
+ if not include_window_only and mode == "window-only":
792
+ continue
793
+ if not _in_window(e):
794
+ continue
795
+ lid = e.get("lesson_id", "")
796
+ if not lid:
797
+ continue
798
+ # When lesson_id is comma-separated (emit_architect_outcome
799
+ # aggregate emit), split into component ids
800
+ for lid_part in lid.split(","):
801
+ lid_part = lid_part.strip()
802
+ if not lid_part:
803
+ continue
804
+ a = agg[lid_part]
805
+ if e.get("hit"):
806
+ a["hit"] += 1
807
+ else:
808
+ a["miss"] += 1
809
+ a["modes"][mode] = a["modes"].get(mode, 0) + 1
810
+ ts = e.get("ts", "")
811
+ if ts > a["last"]:
812
+ a["last"] = ts
813
+ elif action == "lesson_read":
814
+ if not _in_window(e):
815
+ continue
816
+ for lid in e.get("lesson_ids", []) or []:
817
+ agg[lid]["injections"] += 1
818
+
819
+ # Compute effectiveness + days-since-last
820
+ from datetime import datetime, timezone
821
+ now = datetime.now(timezone.utc)
822
+ lessons = []
823
+ for lid, a in agg.items():
824
+ total = a["hit"] + a["miss"]
825
+ eff = (a["hit"] / total) if total > 0 else None
826
+ days_since = None
827
+ last_dt = _parse_since_arg(a["last"]) if a["last"] else None
828
+ if last_dt is not None:
829
+ days_since = (now - last_dt).total_seconds() / 86400.0
830
+ lessons.append({
831
+ "lesson_id": lid,
832
+ "hit_count": a["hit"],
833
+ "miss_count": a["miss"],
834
+ "effectiveness": eff,
835
+ "days_since_last_outcome": days_since,
836
+ "injection_count": a["injections"],
837
+ "inference_mode_breakdown": a["modes"],
838
+ })
839
+
840
+ # Sorting — null effectiveness sorts AFTER non-null (A5/A9)
841
+ warning = None
842
+ if sort_by == "effectiveness":
843
+ lessons.sort(key=lambda x: (
844
+ x["effectiveness"] is None,
845
+ -(x["effectiveness"] or 0.0),
846
+ ))
847
+ elif sort_by == "recency":
848
+ lessons.sort(key=lambda x: (
849
+ x["days_since_last_outcome"] is None,
850
+ x["days_since_last_outcome"] or float("inf"),
851
+ ))
852
+ elif sort_by == "injections":
853
+ # VP unseen #4 — warn about gameable axis
854
+ warning = "sorting by 'injections' is a gameable axis; use with caution"
855
+ lessons.sort(key=lambda x: -x["injection_count"])
856
+
857
+ if top_n > 0:
858
+ lessons = lessons[:top_n]
859
+ elif bottom_n > 0:
860
+ lessons = lessons[-bottom_n:][::-1]
861
+
862
+ data = {
863
+ "sort_by": sort_by,
864
+ "include_window_only": include_window_only,
865
+ "since": since.isoformat() if since else "all",
866
+ "lesson_count": len(lessons),
867
+ "lessons": lessons,
868
+ }
869
+ if warning:
870
+ data["warning"] = warning
871
+ return {
872
+ "query": "lessons-effectiveness",
873
+ "version": "1",
874
+ "data": data,
875
+ }
876
+
877
+
878
+ def cmd_spawn_stats(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
879
+ """PLAN-025 Batch D F-obs-001 — spawn distribution by model/skill.
880
+
881
+ Reads audit-log entries, filters to `action == "agent_spawn"`, and
882
+ aggregates counts by `model` (ADR-052 v2.8 field), `skill`, or both.
883
+
884
+ Respects `--since` with either ISO-8601 timestamps or relative
885
+ offsets ("7d", "30d", "1h"). Renders a human-friendly table (or JSON
886
+ via `--json`).
887
+ """
888
+ import re as _re
889
+ from datetime import datetime as _dt, timedelta as _td, timezone as _tz
890
+
891
+ # Resolve --since
892
+ since_dt: Optional[_dt] = None
893
+ if getattr(args, "since", None):
894
+ raw = str(args.since).strip()
895
+ m = _re.match(r"^(\d+)([hdwm])$", raw)
896
+ if m:
897
+ n, unit = int(m.group(1)), m.group(2)
898
+ delta = {"h": _td(hours=n), "d": _td(days=n),
899
+ "w": _td(weeks=n), "m": _td(days=30 * n)}[unit]
900
+ since_dt = _dt.now(tz=_tz.utc) - delta
901
+ else:
902
+ try:
903
+ since_dt = _dt.fromisoformat(raw.replace("Z", "+00:00"))
904
+ if since_dt.tzinfo is None:
905
+ since_dt = since_dt.replace(tzinfo=_tz.utc)
906
+ except ValueError:
907
+ return {"error": f"unparseable --since: {raw!r}"}
908
+
909
+ by_model: Dict[str, int] = {}
910
+ by_skill: Dict[str, int] = {}
911
+ by_pair: Dict[str, int] = {} # "model|skill"
912
+ total = 0
913
+
914
+ for ev in entries:
915
+ if ev.get("action") != "agent_spawn":
916
+ continue
917
+ if since_dt is not None:
918
+ ts_s = ev.get("ts") or ev.get("timestamp") or ""
919
+ try:
920
+ ev_ts = _dt.fromisoformat(str(ts_s).replace("Z", "+00:00"))
921
+ if ev_ts.tzinfo is None:
922
+ ev_ts = ev_ts.replace(tzinfo=_tz.utc)
923
+ except (ValueError, TypeError):
924
+ continue
925
+ if ev_ts < since_dt:
926
+ continue
927
+
928
+ total += 1
929
+ model = ev.get("model") or "unknown_model"
930
+ skill = ev.get("skill") or "unknown_skill"
931
+ by_model[model] = by_model.get(model, 0) + 1
932
+ by_skill[skill] = by_skill.get(skill, 0) + 1
933
+ by_pair[f"{model}|{skill}"] = by_pair.get(f"{model}|{skill}", 0) + 1
934
+
935
+ out: Dict[str, Any] = {
936
+ "since": str(since_dt) if since_dt else "all-time",
937
+ "total_spawns": total,
938
+ }
939
+ by = getattr(args, "by", "model")
940
+ if by in ("model", "both"):
941
+ out["by_model"] = dict(sorted(by_model.items(), key=lambda kv: -kv[1]))
942
+ if by in ("skill", "both"):
943
+ out["by_skill"] = dict(sorted(by_skill.items(), key=lambda kv: -kv[1]))
944
+ if by == "both":
945
+ out["by_model_and_skill"] = dict(sorted(by_pair.items(), key=lambda kv: -kv[1]))
946
+ return out
947
+
948
+
949
+ # ---------------------------------------------------------------------------
950
+ # weekly-summary helpers (PLAN-023 Phase E decomposition)
951
+ # ---------------------------------------------------------------------------
952
+
953
+
954
+ def _weekly_parse_ts(e: Dict[str, Any]) -> Optional[datetime]:
955
+ """Parse ISO-8601 ``ts`` from an audit entry (None on missing/malformed)."""
956
+ raw = e.get("ts")
957
+ if not isinstance(raw, str):
958
+ return None
959
+ try:
960
+ s = raw[:-1] + "+00:00" if raw.endswith("Z") else raw
961
+ dt = datetime.fromisoformat(s)
962
+ return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)
963
+ except ValueError:
964
+ return None
965
+
966
+
967
+ def _weekly_parse_window(window_raw: str) -> "Optional[timedelta]":
968
+ """Translate ``7d``/``14d``/``30m``/``all`` into a timedelta (or None)."""
969
+ if window_raw == "all":
970
+ return None
971
+ m = re.fullmatch(r"(\d+)([mhd])", window_raw.strip().lower())
972
+ if not m:
973
+ print(
974
+ "[audit-query] ERROR: weekly-summary: bad --window {0!r}; "
975
+ "expected 7d/14d/30m/all".format(window_raw),
976
+ file=sys.stderr,
977
+ )
978
+ sys.exit(1)
979
+ n, unit = int(m.group(1)), m.group(2)
980
+ return {"m": timedelta(minutes=n), "h": timedelta(hours=n),
981
+ "d": timedelta(days=n)}[unit]
982
+
983
+
984
+ def _weekly_empty_bucket() -> Dict[str, Any]:
985
+ return {
986
+ "spawns": 0,
987
+ "vetoes": 0,
988
+ "plan_transitions": 0,
989
+ "confidence_total": 0,
990
+ "confidence_failed": 0,
991
+ "veto_reasons": Counter(),
992
+ }
993
+
994
+
995
+ def _weekly_collect_buckets(
996
+ entries: List[Dict[str, Any]],
997
+ current_start: Optional[datetime],
998
+ prior_start: Optional[datetime],
999
+ prior_end: Optional[datetime],
1000
+ now: datetime,
1001
+ ) -> "Tuple[Dict[str, Any], Dict[str, Any]]":
1002
+ """Single-pass accumulation of audit events into current/prior buckets."""
1003
+ current = _weekly_empty_bucket()
1004
+ prior = _weekly_empty_bucket()
1005
+ for e in entries:
1006
+ dt = _weekly_parse_ts(e)
1007
+ if dt is None:
1008
+ continue
1009
+ if current_start is None:
1010
+ bucket_name = "current" # --window all
1011
+ elif current_start <= dt <= now:
1012
+ bucket_name = "current"
1013
+ elif (
1014
+ prior_start is not None
1015
+ and prior_end is not None
1016
+ and prior_start <= dt < prior_end
1017
+ ):
1018
+ bucket_name = "prior"
1019
+ else:
1020
+ continue
1021
+ bucket = current if bucket_name == "current" else prior
1022
+ action = e.get("action")
1023
+ if action == "agent_spawn":
1024
+ bucket["spawns"] += 1
1025
+ elif action == "veto_triggered":
1026
+ bucket["vetoes"] += 1
1027
+ reason = e.get("reason_code") or "unknown"
1028
+ bucket["veto_reasons"][reason] += 1
1029
+ elif action == "plan_transition":
1030
+ bucket["plan_transitions"] += 1
1031
+ elif action == "confidence_gate":
1032
+ bucket["confidence_total"] += 1
1033
+ if e.get("outcome") == "fail" or e.get("failed_claim_count"):
1034
+ bucket["confidence_failed"] += 1
1035
+ return current, prior
1036
+
1037
+
1038
+ def _weekly_render_bucket(bucket: Dict[str, Any]) -> Dict[str, Any]:
1039
+ """Compute derived rates for one accumulator bucket."""
1040
+ denom = bucket["spawns"] + bucket["vetoes"]
1041
+ veto_rate = bucket["vetoes"] / denom if denom else None
1042
+ conf_total = bucket["confidence_total"]
1043
+ conf_fail_rate = bucket["confidence_failed"] / conf_total if conf_total else None
1044
+ return {
1045
+ "spawns": bucket["spawns"],
1046
+ "vetoes": bucket["vetoes"],
1047
+ "veto_rate": round(veto_rate, 3) if veto_rate is not None else None,
1048
+ "plan_transitions": bucket["plan_transitions"],
1049
+ "confidence_total": bucket["confidence_total"],
1050
+ "confidence_failed": bucket["confidence_failed"],
1051
+ "confidence_fail_rate": round(conf_fail_rate, 3) if conf_fail_rate is not None else None,
1052
+ }
1053
+
1054
+
1055
+ def _weekly_delta(
1056
+ a: Optional[float], b: Optional[float], *, pp: bool = False,
1057
+ ) -> "Optional[float]":
1058
+ """Signed delta between two optional floats (pp=True → ×100 rounded 1dp)."""
1059
+ if a is None or b is None:
1060
+ return None
1061
+ d = a - b
1062
+ if pp:
1063
+ return round(d * 100.0, 1)
1064
+ return round(d, 3) if isinstance(d, float) else int(d)
1065
+
1066
+
1067
+ def cmd_weekly_summary(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1068
+ """`audit-query weekly-summary` — adopter-side weekly triage metrics.
1069
+
1070
+ PLAN-015 Phase 0.5. Compares a current window against the prior window
1071
+ of the same length and reports signed deltas so the Owner can see
1072
+ week-over-week trends (spawn growth, veto-rate shifts, plan velocity,
1073
+ confidence-gate health, top vetoed reasons).
1074
+
1075
+ Window sizing: ``--window`` accepts the shared `_parse_since_arg`
1076
+ shorthand (``7d`` default, ``14d``, ``30d``) or ``all`` (disables
1077
+ the prior-window comparison — current-window only).
1078
+ ``--now ISO`` overrides "now" for deterministic tests.
1079
+
1080
+ PLAN-023 Phase E decomposition: delegates window parsing, bucket
1081
+ accumulation, rendering, and delta computation to module-level
1082
+ helpers (``_weekly_parse_window``, ``_weekly_collect_buckets``,
1083
+ ``_weekly_render_bucket``, ``_weekly_delta``). Behavior byte-
1084
+ identical to the pre-decomposition 151-LoC monolith.
1085
+ """
1086
+ window_raw = str(getattr(args, "window", "7d"))
1087
+ now_raw = getattr(args, "now", None)
1088
+ now = _parse_since_arg(now_raw) if now_raw else datetime.now(timezone.utc)
1089
+ if now is None:
1090
+ now = datetime.now(timezone.utc)
1091
+
1092
+ window_delta = _weekly_parse_window(window_raw)
1093
+
1094
+ if window_delta is None:
1095
+ current_start: Optional[datetime] = None
1096
+ prior_start: Optional[datetime] = None
1097
+ prior_end: Optional[datetime] = None
1098
+ else:
1099
+ current_start = now - window_delta
1100
+ prior_start = now - (2 * window_delta)
1101
+ prior_end = current_start
1102
+
1103
+ current, prior = _weekly_collect_buckets(
1104
+ entries, current_start, prior_start, prior_end, now,
1105
+ )
1106
+ cur = _weekly_render_bucket(current)
1107
+ pri = _weekly_render_bucket(prior)
1108
+
1109
+ trend = {
1110
+ "spawn_delta": cur["spawns"] - pri["spawns"],
1111
+ "veto_rate_delta_pp": _weekly_delta(cur["veto_rate"], pri["veto_rate"], pp=True),
1112
+ "plan_transition_delta": cur["plan_transitions"] - pri["plan_transitions"],
1113
+ "confidence_fail_rate_delta_pp": _weekly_delta(
1114
+ cur["confidence_fail_rate"], pri["confidence_fail_rate"], pp=True
1115
+ ),
1116
+ }
1117
+
1118
+ top_vetoed = [
1119
+ {"reason_code": code, "count": count}
1120
+ for code, count in current["veto_reasons"].most_common(3)
1121
+ ]
1122
+
1123
+ return {
1124
+ "query": "weekly-summary",
1125
+ "version": "1",
1126
+ "window": window_raw,
1127
+ "now": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
1128
+ "current_window": cur,
1129
+ "previous_window": pri,
1130
+ "trend": trend,
1131
+ "top_vetoed_reasons_current": top_vetoed,
1132
+ }
1133
+
1134
+
1135
+ def cmd_architect_outcomes(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1136
+ """`audit-query architect-outcomes` — per-lesson hit/miss from Architect spawns.
1137
+
1138
+ PLAN-009 P3.4. Aggregates `lesson_outcome` events filtered by
1139
+ `consumer="architect"` (default) and `inference_mode` (default
1140
+ "session-correlated" — dirty window-only data excluded unless
1141
+ `--include-window-only` is passed).
1142
+
1143
+ Output envelope (SPEC/v1/audit-query.schema.md):
1144
+ {"query": "architect-outcomes", "version": "1",
1145
+ "data": {"lessons": [{lesson_id, hit_count, miss_count,
1146
+ effectiveness, last_outcome_at,
1147
+ inference_modes: {...}}, ...]}}
1148
+ """
1149
+ since = _parse_since_arg(getattr(args, "since", "24h"))
1150
+ include_window_only = getattr(args, "include_window_only", False)
1151
+ consumer_filter = getattr(args, "consumer", "architect")
1152
+
1153
+ def _in_window(e: Dict[str, Any]) -> bool:
1154
+ if since is None:
1155
+ return True
1156
+ ts = _parse_since_arg(e.get("ts", ""))
1157
+ return ts is None or ts >= since
1158
+
1159
+ # Aggregate per lesson_id
1160
+ from collections import defaultdict
1161
+ per_lesson: Dict[str, Dict[str, Any]] = defaultdict(
1162
+ lambda: {"hit_count": 0, "miss_count": 0,
1163
+ "last_outcome_at": "",
1164
+ "inference_modes": {}, "archetype": ""}
1165
+ )
1166
+
1167
+ for e in entries:
1168
+ if e.get("action") != "lesson_outcome":
1169
+ continue
1170
+ if e.get("consumer", "benchmark") != consumer_filter:
1171
+ continue
1172
+ mode = e.get("inference_mode", "")
1173
+ if not include_window_only and mode == "window-only":
1174
+ continue
1175
+ if not _in_window(e):
1176
+ continue
1177
+ lid = e.get("lesson_id", "")
1178
+ if not lid:
1179
+ continue
1180
+ agg = per_lesson[lid]
1181
+ if e.get("hit"):
1182
+ agg["hit_count"] += 1
1183
+ else:
1184
+ agg["miss_count"] += 1
1185
+ agg["inference_modes"][mode or "unspecified"] = (
1186
+ agg["inference_modes"].get(mode or "unspecified", 0) + 1
1187
+ )
1188
+ ts = e.get("ts", "")
1189
+ if ts > agg["last_outcome_at"]:
1190
+ agg["last_outcome_at"] = ts
1191
+ if not agg["archetype"]:
1192
+ agg["archetype"] = e.get("archetype", "")
1193
+
1194
+ lessons = []
1195
+ for lid, agg in per_lesson.items():
1196
+ total = agg["hit_count"] + agg["miss_count"]
1197
+ effectiveness = (agg["hit_count"] / total) if total > 0 else None
1198
+ lessons.append({
1199
+ "lesson_id": lid,
1200
+ "archetype": agg["archetype"],
1201
+ "hit_count": agg["hit_count"],
1202
+ "miss_count": agg["miss_count"],
1203
+ "effectiveness": effectiveness,
1204
+ "last_outcome_at": agg["last_outcome_at"],
1205
+ "inference_modes": agg["inference_modes"],
1206
+ })
1207
+ lessons.sort(key=lambda x: (x["effectiveness"] is None, -(x["effectiveness"] or 0)))
1208
+
1209
+ data = {
1210
+ "consumer": consumer_filter,
1211
+ "include_window_only": include_window_only,
1212
+ "since": since.isoformat() if since else "all",
1213
+ "lesson_count": len(lessons),
1214
+ "lessons": lessons,
1215
+ }
1216
+ return {
1217
+ "query": "architect-outcomes",
1218
+ "version": "1",
1219
+ "data": data,
1220
+ }
1221
+
1222
+
1223
+ def cmd_prune_restore_ratio(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1224
+ """`audit-query prune-restore-ratio` — ADR-020 measurement hook.
1225
+
1226
+ Reads `lesson_archived` + `lesson_restored` events; computes
1227
+ restored/archived. Default window 24h (PLAN-009 A18). Dedupes
1228
+ by lesson_id; warns on >1 restore event per lesson (C12/A13).
1229
+
1230
+ Output shape (SPEC/v1/audit-query.schema.md envelope):
1231
+ {"query": "prune-restore-ratio", "version": "1",
1232
+ "data": { ... }}
1233
+ """
1234
+ since = _parse_since_arg(getattr(args, "since", "24h"))
1235
+ until = _parse_since_arg(getattr(args, "until", None)) if getattr(args, "until", None) else None
1236
+
1237
+ def _in_window(e: Dict[str, Any]) -> bool:
1238
+ ts = e.get("ts", "")
1239
+ if not ts:
1240
+ return True
1241
+ dt = _parse_since_arg(ts)
1242
+ if dt is None:
1243
+ return True
1244
+ if since and dt < since:
1245
+ return False
1246
+ if until and dt > until:
1247
+ return False
1248
+ return True
1249
+
1250
+ archived = [e for e in entries if e.get("action") == "lesson_archived" and _in_window(e)]
1251
+ restored = [e for e in entries if e.get("action") == "lesson_restored" and _in_window(e)]
1252
+
1253
+ # Dedupe restored by lesson_id + count multi-restore warnings
1254
+ restored_counts: Dict[str, int] = {}
1255
+ for e in restored:
1256
+ lid = e.get("lesson_id", "")
1257
+ if lid:
1258
+ restored_counts[lid] = restored_counts.get(lid, 0) + 1
1259
+ unique_restored = len(restored_counts)
1260
+ multi_restored = {lid: n for lid, n in restored_counts.items() if n > 1}
1261
+
1262
+ archived_count = len(archived)
1263
+ if archived_count == 0:
1264
+ ratio: Optional[float] = None
1265
+ else:
1266
+ ratio = unique_restored / archived_count
1267
+
1268
+ data = {
1269
+ "archived_count": archived_count,
1270
+ "restored_count": len(restored),
1271
+ "unique_restored_lesson_ids": unique_restored,
1272
+ "restore_ratio": ratio,
1273
+ "since": since.isoformat() if since else "all",
1274
+ "until": until.isoformat() if until else "now",
1275
+ "multi_restore_warnings": multi_restored,
1276
+ }
1277
+ return {
1278
+ "query": "prune-restore-ratio",
1279
+ "version": "1",
1280
+ "data": data,
1281
+ }
1282
+
1283
+
1284
+ def cmd_claims(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1285
+ """Confidence-gate events aggregated (Sprint 8 Phase 2, ADR-018).
1286
+
1287
+ Returns totals + per-kind counts + pass/fail breakdown + per-agent
1288
+ aggregates. Filters via --kind / --agent / --failed-only.
1289
+ """
1290
+ kind_filter = getattr(args, "kind", None)
1291
+ agent_filter = getattr(args, "agent", None)
1292
+ failed_only = getattr(args, "failed_only", False)
1293
+
1294
+ total = 0
1295
+ claim_count = 0
1296
+ pass_count = 0
1297
+ fail_count = 0
1298
+ per_kind: Dict[str, Dict[str, int]] = {}
1299
+ per_agent: Dict[str, Dict[str, int]] = {}
1300
+
1301
+ for e in entries:
1302
+ if e.get("action") != "confidence_gate":
1303
+ continue
1304
+ if failed_only and int(e.get("fail_count", 0)) == 0:
1305
+ continue
1306
+ if agent_filter and e.get("agent_name", "") != agent_filter:
1307
+ continue
1308
+
1309
+ kind_counts = e.get("verifier_kind_counts", {}) or {}
1310
+ if kind_filter:
1311
+ if kind_filter not in kind_counts:
1312
+ continue
1313
+
1314
+ total += 1
1315
+ claim_count += int(e.get("claim_count", 0))
1316
+ pass_count += int(e.get("pass_count", 0))
1317
+ fail_count += int(e.get("fail_count", 0))
1318
+
1319
+ for kind, cnt in kind_counts.items():
1320
+ g = per_kind.setdefault(kind, {"total": 0, "events": 0})
1321
+ g["total"] += int(cnt)
1322
+ g["events"] += 1
1323
+
1324
+ agent = e.get("agent_name", "") or "(unnamed)"
1325
+ a = per_agent.setdefault(agent, {"events": 0, "pass": 0, "fail": 0, "claims": 0})
1326
+ a["events"] += 1
1327
+ a["pass"] += int(e.get("pass_count", 0))
1328
+ a["fail"] += int(e.get("fail_count", 0))
1329
+ a["claims"] += int(e.get("claim_count", 0))
1330
+
1331
+ fpr = (fail_count / claim_count) if claim_count else None
1332
+
1333
+ return {
1334
+ "event_count": total,
1335
+ "claim_count": claim_count,
1336
+ "pass_count": pass_count,
1337
+ "fail_count": fail_count,
1338
+ "failure_rate": fpr,
1339
+ "per_kind": per_kind,
1340
+ "per_agent": per_agent,
1341
+ }
1342
+
1343
+
1344
+ def cmd_metrics(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1345
+ """Cross-cutting derived metrics. Sprint 5 A.2.
1346
+
1347
+ Action-type distribution, total veto rate, average duration per
1348
+ benchmark_run, debate completion rate (rounds that reached consensus).
1349
+ """
1350
+ action_counts = Counter(e.get("action", "unknown") for e in entries)
1351
+
1352
+ # Veto rate: vetoes / (spawns + vetoes)
1353
+ spawns = action_counts.get("agent_spawn", 0)
1354
+ vetoes = action_counts.get("veto_triggered", 0)
1355
+ denom = spawns + vetoes
1356
+ veto_rate = (vetoes / denom) if denom else None
1357
+
1358
+ # Debate completion: rounds that reached consensus / rounds started
1359
+ rounds_started: set = set()
1360
+ rounds_concluded: set = set()
1361
+ for e in entries:
1362
+ if e.get("action") != "debate_event":
1363
+ continue
1364
+ key = (e.get("plan_id"), e.get("round"))
1365
+ if e.get("phase") == "start":
1366
+ rounds_started.add(key)
1367
+ elif e.get("phase") == "consensus":
1368
+ rounds_concluded.add(key)
1369
+ debate_completion = (
1370
+ len(rounds_concluded) / len(rounds_started) if rounds_started else None
1371
+ )
1372
+
1373
+ # Benchmark duration avg — prefer new duration_ms (÷1000 → s), fall back
1374
+ # to legacy duration_s float for logs written before the migration.
1375
+ durations = []
1376
+ for e in entries:
1377
+ if e.get("action") != "benchmark_run":
1378
+ continue
1379
+ dur_ms = e.get("duration_ms")
1380
+ if dur_ms is not None:
1381
+ durations.append(int(dur_ms) / 1000.0)
1382
+ elif e.get("duration_s") is not None:
1383
+ durations.append(float(e.get("duration_s")))
1384
+ avg_bench_duration = (
1385
+ round(sum(durations) / len(durations), 3) if durations else None
1386
+ )
1387
+
1388
+ return {
1389
+ "action_counts": dict(action_counts),
1390
+ "veto_rate": round(veto_rate, 3) if veto_rate is not None else None,
1391
+ "debate_rounds_started": len(rounds_started),
1392
+ "debate_rounds_concluded": len(rounds_concluded),
1393
+ "debate_completion_rate": (
1394
+ round(debate_completion, 3) if debate_completion is not None else None
1395
+ ),
1396
+ "benchmark_run_count": action_counts.get("benchmark_run", 0),
1397
+ "benchmark_avg_duration_s": avg_bench_duration,
1398
+ "lesson_write_count": action_counts.get("lesson_write", 0),
1399
+ }
1400
+
1401
+
1402
+ def cmd_tokens(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1403
+ """Spawn token aggregates — PLAN-006 Phase 5a (ADR-016).
1404
+
1405
+ Groups `agent_spawn` events by archetype / skill / day and sums
1406
+ `tokens_in` / `tokens_out`. Null fields are counted under
1407
+ `records_without_tokens` so the operator knows adapter coverage.
1408
+ """
1409
+ per_skill: Dict[str, Dict[str, int]] = {}
1410
+ per_subagent: Dict[str, Dict[str, int]] = {}
1411
+ per_day: Dict[str, Dict[str, int]] = {}
1412
+ total_in = 0
1413
+ total_out = 0
1414
+ with_tokens = 0
1415
+ without_tokens = 0
1416
+
1417
+ for e in entries:
1418
+ if e.get("action") != "agent_spawn":
1419
+ continue
1420
+ tin = e.get("tokens_in")
1421
+ tout = e.get("tokens_out")
1422
+ tin_n = tin if isinstance(tin, int) else 0
1423
+ tout_n = tout if isinstance(tout, int) else 0
1424
+ has_any = isinstance(tin, int) or isinstance(tout, int)
1425
+ if has_any:
1426
+ with_tokens += 1
1427
+ total_in += tin_n
1428
+ total_out += tout_n
1429
+ else:
1430
+ without_tokens += 1
1431
+
1432
+ skill = str(e.get("skill") or "unknown")
1433
+ sk = per_skill.setdefault(skill, {"tokens_in": 0, "tokens_out": 0, "spawns": 0})
1434
+ sk["tokens_in"] += tin_n
1435
+ sk["tokens_out"] += tout_n
1436
+ sk["spawns"] += 1
1437
+
1438
+ sub = str(e.get("subagent_type") or "unknown")
1439
+ su = per_subagent.setdefault(sub, {"tokens_in": 0, "tokens_out": 0, "spawns": 0})
1440
+ su["tokens_in"] += tin_n
1441
+ su["tokens_out"] += tout_n
1442
+ su["spawns"] += 1
1443
+
1444
+ ts = str(e.get("ts") or "")
1445
+ day = ts[:10] if len(ts) >= 10 else "unknown"
1446
+ dy = per_day.setdefault(day, {"tokens_in": 0, "tokens_out": 0, "spawns": 0})
1447
+ dy["tokens_in"] += tin_n
1448
+ dy["tokens_out"] += tout_n
1449
+ dy["spawns"] += 1
1450
+
1451
+ return {
1452
+ "totals": {
1453
+ "tokens_in": total_in,
1454
+ "tokens_out": total_out,
1455
+ "tokens_total": total_in + total_out,
1456
+ "spawns_with_tokens": with_tokens,
1457
+ "spawns_without_tokens": without_tokens,
1458
+ },
1459
+ "per_skill": dict(sorted(per_skill.items())),
1460
+ "per_subagent_type": dict(sorted(per_subagent.items())),
1461
+ "per_day": dict(sorted(per_day.items())),
1462
+ }
1463
+
1464
+
1465
+ def cmd_health(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1466
+ """High-level framework health. Sprint 5 A.2.
1467
+
1468
+ Rolls up several gates into an overall PASS/WARN/FAIL verdict so
1469
+ `audit-dashboard.py` and ops tooling have one call to make.
1470
+ """
1471
+ total = len(entries)
1472
+ if total == 0:
1473
+ return {"verdict": "NO_DATA", "total_events": 0, "gates": {}}
1474
+
1475
+ compliance = cmd_compliance(entries, args)
1476
+ metrics = cmd_metrics(entries, args)
1477
+
1478
+ gates: Dict[str, str] = {}
1479
+
1480
+ # Gate 1: compliance rate >= 0.95
1481
+ known_rate = compliance.get("known_skill_rate")
1482
+ if known_rate is None:
1483
+ gates["compliance"] = "NO_DATA"
1484
+ elif known_rate >= 0.95:
1485
+ gates["compliance"] = "PASS"
1486
+ elif known_rate >= 0.8:
1487
+ gates["compliance"] = "WARN"
1488
+ else:
1489
+ gates["compliance"] = "FAIL"
1490
+
1491
+ # Gate 2: veto rate < 0.15
1492
+ veto_rate = metrics.get("veto_rate")
1493
+ if veto_rate is None:
1494
+ gates["vetoes"] = "NO_DATA"
1495
+ elif veto_rate < 0.05:
1496
+ gates["vetoes"] = "PASS"
1497
+ elif veto_rate < 0.15:
1498
+ gates["vetoes"] = "WARN"
1499
+ else:
1500
+ gates["vetoes"] = "FAIL"
1501
+
1502
+ # Gate 3: debate completion rate >= 0.8 (if any rounds started)
1503
+ dc = metrics.get("debate_completion_rate")
1504
+ if dc is None:
1505
+ gates["debate_completion"] = "NO_DATA"
1506
+ elif dc >= 0.8:
1507
+ gates["debate_completion"] = "PASS"
1508
+ elif dc >= 0.5:
1509
+ gates["debate_completion"] = "WARN"
1510
+ else:
1511
+ gates["debate_completion"] = "FAIL"
1512
+
1513
+ # Verdict reduction
1514
+ if any(v == "FAIL" for v in gates.values()):
1515
+ verdict = "FAIL"
1516
+ elif any(v == "WARN" for v in gates.values()):
1517
+ verdict = "WARN"
1518
+ elif all(v in ("PASS", "NO_DATA") for v in gates.values()):
1519
+ verdict = "PASS"
1520
+ else:
1521
+ verdict = "WARN"
1522
+
1523
+ return {
1524
+ "verdict": verdict,
1525
+ "total_events": total,
1526
+ "gates": gates,
1527
+ "known_skill_rate": known_rate,
1528
+ "veto_rate": veto_rate,
1529
+ "debate_completion_rate": dc,
1530
+ "action_counts": metrics.get("action_counts", {}),
1531
+ }
1532
+
1533
+
1534
+ def cmd_export(entries: List[Dict[str, Any]], args) -> Any:
1535
+ """Handle the `audit-query export` sub-command — emit filtered audit-log slices."""
1536
+ fmt = args.export_format
1537
+ if fmt == "json":
1538
+ return entries
1539
+ if fmt == "csv":
1540
+ buf = io.StringIO()
1541
+ if not entries:
1542
+ return ""
1543
+ fieldnames = sorted({k for e in entries for k in e.keys()})
1544
+ writer = csv.DictWriter(buf, fieldnames=fieldnames, extrasaction="ignore")
1545
+ writer.writeheader()
1546
+ for e in entries:
1547
+ writer.writerow(e)
1548
+ return buf.getvalue()
1549
+ if fmt == "tsv":
1550
+ buf = io.StringIO()
1551
+ if not entries:
1552
+ return ""
1553
+ fieldnames = sorted({k for e in entries for k in e.keys()})
1554
+ writer = csv.DictWriter(
1555
+ buf, fieldnames=fieldnames, extrasaction="ignore", delimiter="\t"
1556
+ )
1557
+ writer.writeheader()
1558
+ for e in entries:
1559
+ writer.writerow(e)
1560
+ return buf.getvalue()
1561
+ raise ValueError(f"unsupported export format: {fmt}")
1562
+
1563
+
1564
+ # ---------------------------------------------------------------------------
1565
+ # PLAN-080 Phase 1 — by-domain sub-command
1566
+ # ---------------------------------------------------------------------------
1567
+
1568
+ # Default window for by-domain (calendar days, trailing from UTC midnight)
1569
+ _BY_DOMAIN_DEFAULT_WINDOW_DAYS = 30
1570
+
1571
+ # Sentinel used when no dispatch_archetype_hint is available
1572
+ _UNKNOWN_BUCKET = "UNKNOWN"
1573
+
1574
+ # Default policy file location (relative to repo root, resolved at runtime)
1575
+ _GRANDFATHER_POLICY_RELPATH = ".claude/policies/grandfather-cap.policy.yaml"
1576
+
1577
+
1578
+ def _parse_domain_date(raw: str) -> Optional[datetime]:
1579
+ """Parse YYYY-MM-DD into a UTC-aware datetime (midnight UTC).
1580
+
1581
+ Returns None on failure (not sys.exit — caller handles the error).
1582
+ """
1583
+ try:
1584
+ dt = datetime.strptime(raw.strip(), "%Y-%m-%d")
1585
+ return dt.replace(tzinfo=timezone.utc)
1586
+ except ValueError:
1587
+ return None
1588
+
1589
+
1590
+ def _resolve_window_bounds(
1591
+ args,
1592
+ ) -> Tuple[Optional[datetime], Optional[datetime], str]:
1593
+ """Resolve --window / --start / --end into (start_dt, end_dt, window_label).
1594
+
1595
+ Returns (start_dt, end_dt, label) where:
1596
+ - start_dt: inclusive lower bound (UTC midnight); None means no lower bound
1597
+ - end_dt: inclusive upper bound (UTC now for default windows)
1598
+ - label: human-readable description of the window
1599
+
1600
+ Validates start <= end; prints error + sys.exit(1) on invalid input.
1601
+ """
1602
+ now = datetime.now(timezone.utc)
1603
+ # --start / --end take precedence over --window
1604
+ start_raw = getattr(args, "start", None)
1605
+ end_raw = getattr(args, "end", None)
1606
+ window_raw = getattr(args, "window", None) or f"{_BY_DOMAIN_DEFAULT_WINDOW_DAYS}d"
1607
+
1608
+ if start_raw or end_raw:
1609
+ if not start_raw or not end_raw:
1610
+ print(
1611
+ "[audit-query] ERROR: by-domain: --start and --end must both be provided",
1612
+ file=sys.stderr,
1613
+ )
1614
+ sys.exit(1)
1615
+ start_dt = _parse_domain_date(start_raw)
1616
+ end_dt = _parse_domain_date(end_raw)
1617
+ if start_dt is None:
1618
+ print(
1619
+ f"[audit-query] ERROR: by-domain: invalid --start date {start_raw!r} "
1620
+ "(use YYYY-MM-DD)",
1621
+ file=sys.stderr,
1622
+ )
1623
+ sys.exit(1)
1624
+ if end_dt is None:
1625
+ print(
1626
+ f"[audit-query] ERROR: by-domain: invalid --end date {end_raw!r} "
1627
+ "(use YYYY-MM-DD)",
1628
+ file=sys.stderr,
1629
+ )
1630
+ sys.exit(1)
1631
+ # Make end_dt the end of that calendar day (23:59:59 UTC)
1632
+ end_dt = end_dt.replace(hour=23, minute=59, second=59)
1633
+ if start_dt > end_dt:
1634
+ print(
1635
+ f"[audit-query] ERROR: by-domain: --start ({start_raw}) is after "
1636
+ f"--end ({end_raw})",
1637
+ file=sys.stderr,
1638
+ )
1639
+ sys.exit(1)
1640
+ label = f"{start_raw} to {end_raw}"
1641
+ return start_dt, end_dt, label
1642
+
1643
+ # Parse --window (e.g. "30d", "7d")
1644
+ m = re.fullmatch(r"(\d+)([dhm])", window_raw.strip().lower())
1645
+ if not m:
1646
+ print(
1647
+ f"[audit-query] ERROR: by-domain: invalid --window {window_raw!r}; "
1648
+ "expected e.g. 30d, 7d, 14d",
1649
+ file=sys.stderr,
1650
+ )
1651
+ sys.exit(1)
1652
+ n, unit = int(m.group(1)), m.group(2)
1653
+ delta = {"d": timedelta(days=n), "h": timedelta(hours=n), "m": timedelta(minutes=n)}[unit]
1654
+ start_dt = now - delta
1655
+ label = f"trailing {window_raw}"
1656
+ return start_dt, now, label
1657
+
1658
+
1659
+ def _load_sunset_domains(args) -> Optional[List[str]]:
1660
+ """Load sunset domain list for --check-reopen.
1661
+
1662
+ Sources (in priority order):
1663
+ 1. CEO_GRANDFATHER_POLICY_PATH env var
1664
+ 2. <repo_root>/.claude/policies/grandfather-cap.policy.yaml (if exists)
1665
+ 3. stdin JSON list (if CEO_BY_DOMAIN_SUNSET_STDIN=1)
1666
+
1667
+ Returns a list of domain slugs, or None if the policy file is not found
1668
+ and stdin mode is not active. An empty list [] means "policy found but
1669
+ no sunset members".
1670
+ """
1671
+ # Check env override
1672
+ policy_path_env = os.environ.get("CEO_GRANDFATHER_POLICY_PATH")
1673
+ if policy_path_env:
1674
+ p = Path(policy_path_env)
1675
+ if p.is_file():
1676
+ return _parse_sunset_from_policy(p)
1677
+ print(
1678
+ f"[audit-query] WARN: CEO_GRANDFATHER_POLICY_PATH={policy_path_env!r} "
1679
+ "not found; no sunset list loaded",
1680
+ file=sys.stderr,
1681
+ )
1682
+ return []
1683
+
1684
+ # Try conventional project-relative path via CLAUDE_PROJECT_DIR or cwd
1685
+ for base_env in ("CLAUDE_PROJECT_DIR",):
1686
+ base = os.environ.get(base_env)
1687
+ if base:
1688
+ p = Path(base) / _GRANDFATHER_POLICY_RELPATH
1689
+ if p.is_file():
1690
+ return _parse_sunset_from_policy(p)
1691
+
1692
+ # Try cwd-relative (developer convenience)
1693
+ p = Path(_GRANDFATHER_POLICY_RELPATH)
1694
+ if p.is_file():
1695
+ return _parse_sunset_from_policy(p)
1696
+
1697
+ # Stdin JSON fallback (CEO_BY_DOMAIN_SUNSET_STDIN=1 for testing)
1698
+ if os.environ.get("CEO_BY_DOMAIN_SUNSET_STDIN") == "1":
1699
+ try:
1700
+ raw = sys.stdin.read()
1701
+ data = json.loads(raw)
1702
+ if isinstance(data, list):
1703
+ return [str(x) for x in data if x]
1704
+ except (json.JSONDecodeError, OSError):
1705
+ pass
1706
+ return []
1707
+
1708
+ return None # policy not found — caller will skip reopen check
1709
+
1710
+
1711
+ def _parse_sunset_from_policy(path: Path) -> List[str]:
1712
+ """Extract sunset domain members from grandfather-cap.policy.yaml.
1713
+
1714
+ Parses the YAML manually using stdlib (no PyYAML dependency).
1715
+ Looks for `domain_bundles.members:` list block.
1716
+
1717
+ Returns list of domain slug strings (may be empty).
1718
+ """
1719
+ try:
1720
+ text = path.read_text(encoding="utf-8", errors="replace")
1721
+ except OSError:
1722
+ return []
1723
+
1724
+ # Simple line-based YAML parser for the members list.
1725
+ # We scan for "members:" under "domain_bundles:" section.
1726
+ in_domain_bundles = False
1727
+ in_members = False
1728
+ members: List[str] = []
1729
+
1730
+ for line in text.splitlines():
1731
+ stripped = line.strip()
1732
+ if stripped.startswith("#"):
1733
+ continue
1734
+ if stripped == "domain_bundles:":
1735
+ in_domain_bundles = True
1736
+ in_members = False
1737
+ continue
1738
+ if in_domain_bundles:
1739
+ # Another top-level key → leave domain_bundles section
1740
+ if stripped and not line.startswith(" ") and not line.startswith("\t"):
1741
+ in_domain_bundles = False
1742
+ in_members = False
1743
+ continue
1744
+ if stripped == "members:":
1745
+ in_members = True
1746
+ continue
1747
+ if in_members:
1748
+ if stripped.startswith("- "):
1749
+ member = stripped[2:].strip()
1750
+ if member:
1751
+ members.append(member)
1752
+ elif stripped and not stripped.startswith("-"):
1753
+ # End of list
1754
+ in_members = False
1755
+
1756
+ return members
1757
+
1758
+
1759
+ def _load_sunset_reopen_options(args) -> Optional[Dict[str, bool]]:
1760
+ """M2-CDX-4 + M2-CDX-7 (Codex Phase 1 iter 1) — load reopen filter flags.
1761
+
1762
+ Returns dict with `requires_hint_match` and `unknown_excluded` boolean
1763
+ flags read from the same grandfather-cap.policy.yaml as
1764
+ `_load_sunset_domains`. Defaults to {True, True} if either flag is
1765
+ missing/unparseable. Returns None if the policy file is not found.
1766
+ """
1767
+ # Reuse the same path resolution as _load_sunset_domains for consistency
1768
+ policy_path_env = os.environ.get("CEO_GRANDFATHER_POLICY_PATH")
1769
+ candidates: List[Path] = []
1770
+ if policy_path_env:
1771
+ candidates.append(Path(policy_path_env))
1772
+ base = os.environ.get("CLAUDE_PROJECT_DIR")
1773
+ if base:
1774
+ candidates.append(Path(base) / _GRANDFATHER_POLICY_RELPATH)
1775
+ candidates.append(Path(_GRANDFATHER_POLICY_RELPATH))
1776
+
1777
+ for p in candidates:
1778
+ if not p.is_file():
1779
+ continue
1780
+ try:
1781
+ text = p.read_text(encoding="utf-8", errors="replace")
1782
+ except OSError:
1783
+ continue
1784
+ return _parse_sunset_reopen_flags(text)
1785
+ return None
1786
+
1787
+
1788
+ def _parse_sunset_reopen_flags(text: str) -> Dict[str, bool]:
1789
+ """Parse `sunset_reopen_requires_hint_match` and `sunset_reopen_unknown_excluded`.
1790
+
1791
+ Both flags default to True if missing (defensive — secure-by-default).
1792
+ """
1793
+ requires_hint_match = True
1794
+ unknown_excluded = True
1795
+ for line in text.splitlines():
1796
+ s = line.strip()
1797
+ if s.startswith("#"):
1798
+ continue
1799
+ if s.startswith("sunset_reopen_requires_hint_match:"):
1800
+ v = s.split(":", 1)[1].strip().lower()
1801
+ requires_hint_match = (v == "true" or v.startswith("true"))
1802
+ elif s.startswith("sunset_reopen_unknown_excluded:"):
1803
+ v = s.split(":", 1)[1].strip().lower()
1804
+ unknown_excluded = (v == "true" or v.startswith("true"))
1805
+ return {
1806
+ "requires_hint_match": requires_hint_match,
1807
+ "unknown_excluded": unknown_excluded,
1808
+ }
1809
+
1810
+
1811
+ def _entry_ts_to_dt(ts: str) -> Optional[datetime]:
1812
+ """Parse a log entry `ts` field into a UTC datetime."""
1813
+ if not ts:
1814
+ return None
1815
+ try:
1816
+ s = ts[:-1] + "+00:00" if ts.endswith("Z") else ts
1817
+ dt = datetime.fromisoformat(s)
1818
+ return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)
1819
+ except ValueError:
1820
+ return None
1821
+
1822
+
1823
+ def cmd_by_domain(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
1824
+ """`audit-query by-domain` — spawn activity grouped by dispatch_archetype_hint.
1825
+
1826
+ PLAN-080 Phase 1 — domain-level observability for squad-bundle governance.
1827
+
1828
+ Groups `agent_spawn` events by `dispatch_archetype_hint` field if present;
1829
+ falls back to `archetype` field, then to the "UNKNOWN" bucket. Entries
1830
+ outside the requested time window are excluded.
1831
+
1832
+ Output columns:
1833
+ Domain | Spawns | First seen | Last seen | hint_coverage_pct
1834
+
1835
+ Sorted deterministically by domain name (alphabetic, UNKNOWN last).
1836
+
1837
+ With --check-reopen: additionally filters to domains present in the sunset
1838
+ list from grandfather-cap.policy.yaml. Reports domains with >= 1 spawn
1839
+ where hint matches a sunset domain (UNKNOWN excluded per M2-CDX-7).
1840
+ """
1841
+ start_dt, end_dt, window_label = _resolve_window_bounds(args)
1842
+
1843
+ # Load sunset list + reopen options if --check-reopen requested
1844
+ check_reopen = getattr(args, "check_reopen", False)
1845
+ sunset_domains: Optional[List[str]] = None
1846
+ # M2-CDX-4 + M2-CDX-7 (Codex Phase 1 iter 1): honor policy flags.
1847
+ # Defaults match policy semantics when flags missing/unparseable.
1848
+ requires_hint_match = True # sunset_reopen_requires_hint_match (default true per §8)
1849
+ unknown_excluded = True # sunset_reopen_unknown_excluded (M2-CDX-7 default true)
1850
+ if check_reopen:
1851
+ sunset_domains = _load_sunset_domains(args)
1852
+ sunset_options = _load_sunset_reopen_options(args)
1853
+ if sunset_options is not None:
1854
+ requires_hint_match = sunset_options.get("requires_hint_match", True)
1855
+ unknown_excluded = sunset_options.get("unknown_excluded", True)
1856
+ if sunset_domains is None:
1857
+ print(
1858
+ "[audit-query] WARN: by-domain --check-reopen: no sunset policy file "
1859
+ "found; skipping reopen filter. Set CEO_GRANDFATHER_POLICY_PATH or "
1860
+ "place grandfather-cap.policy.yaml at "
1861
+ f"{_GRANDFATHER_POLICY_RELPATH}",
1862
+ file=sys.stderr,
1863
+ )
1864
+
1865
+ # Aggregation state per domain bucket
1866
+ # domain → {spawns, first_ts, last_ts, with_hint, total}
1867
+ domain_agg: Dict[str, Dict[str, Any]] = {}
1868
+
1869
+ total_in_window = 0
1870
+ total_with_hint = 0
1871
+
1872
+ for e in entries:
1873
+ if e.get("action") != "agent_spawn":
1874
+ continue
1875
+
1876
+ ts_raw = e.get("ts", "")
1877
+ entry_dt = _entry_ts_to_dt(ts_raw)
1878
+
1879
+ # Apply window filter
1880
+ if start_dt is not None and entry_dt is not None:
1881
+ if entry_dt < start_dt:
1882
+ continue
1883
+ if end_dt is not None and entry_dt is not None:
1884
+ if entry_dt > end_dt:
1885
+ continue
1886
+
1887
+ total_in_window += 1
1888
+
1889
+ # Determine domain bucket
1890
+ hint = e.get("dispatch_archetype_hint")
1891
+ if hint and isinstance(hint, str) and hint.strip():
1892
+ domain = hint.strip()
1893
+ has_hint = True
1894
+ total_with_hint += 1
1895
+ else:
1896
+ # Fallback: archetype field
1897
+ arch = e.get("archetype")
1898
+ if arch and isinstance(arch, str) and arch.strip():
1899
+ domain = arch.strip()
1900
+ else:
1901
+ domain = _UNKNOWN_BUCKET
1902
+ has_hint = False
1903
+
1904
+ bucket = domain_agg.setdefault(
1905
+ domain,
1906
+ {
1907
+ "spawns": 0,
1908
+ "first_ts": ts_raw or "",
1909
+ "last_ts": ts_raw or "",
1910
+ "with_hint": 0,
1911
+ "total": 0,
1912
+ },
1913
+ )
1914
+ bucket["spawns"] += 1
1915
+ bucket["total"] += 1
1916
+ if has_hint:
1917
+ bucket["with_hint"] += 1
1918
+ if ts_raw:
1919
+ if not bucket["first_ts"] or ts_raw < bucket["first_ts"]:
1920
+ bucket["first_ts"] = ts_raw
1921
+ if ts_raw > bucket["last_ts"]:
1922
+ bucket["last_ts"] = ts_raw
1923
+
1924
+ # Build output rows
1925
+ rows: List[Dict[str, Any]] = []
1926
+ for domain in sorted(domain_agg.keys(), key=lambda d: ("" if d != _UNKNOWN_BUCKET else "\xff") + d):
1927
+ b = domain_agg[domain]
1928
+ hint_pct = round(b["with_hint"] / b["total"] * 100, 1) if b["total"] > 0 else 0.0
1929
+ row: Dict[str, Any] = {
1930
+ "domain": domain,
1931
+ "spawns": b["spawns"],
1932
+ "first_seen": b["first_ts"][:10] if b["first_ts"] else "",
1933
+ "last_seen": b["last_ts"][:10] if b["last_ts"] else "",
1934
+ "hint_coverage_pct": hint_pct,
1935
+ }
1936
+ rows.append(row)
1937
+
1938
+ # Apply --check-reopen filter
1939
+ # M2-CDX-7: UNKNOWN excluded when sunset_reopen_unknown_excluded=true (default)
1940
+ # M2-CDX-4: When sunset_reopen_requires_hint_match=true (default), only
1941
+ # spawns whose ORIGINAL audit row carried `dispatch_archetype_hint`
1942
+ # (has_hint=True) qualify for reopen. archetype-fallback rows do not
1943
+ # trigger reopen (per PLAN-080 §8: "spawn carries dispatch_archetype_hint
1944
+ # matching the sunset domain's archetype set").
1945
+ if check_reopen and sunset_domains is not None:
1946
+ sunset_set = set(sunset_domains)
1947
+ reopen_rows = []
1948
+ for r in rows:
1949
+ if unknown_excluded and r["domain"] == _UNKNOWN_BUCKET:
1950
+ continue
1951
+ if r["domain"] not in sunset_set:
1952
+ continue
1953
+ if r["spawns"] < 1:
1954
+ continue
1955
+ if requires_hint_match:
1956
+ # Reopen requires at least one hint-source spawn matching this
1957
+ # sunset domain. Use the per-bucket `with_hint` count from the
1958
+ # aggregation — bucket["with_hint"] tracks how many spawns in
1959
+ # this domain bucket arrived with dispatch_archetype_hint set.
1960
+ bucket = domain_agg.get(r["domain"], {})
1961
+ if int(bucket.get("with_hint", 0)) < 1:
1962
+ continue
1963
+ reopen_rows.append(r)
1964
+ else:
1965
+ reopen_rows = []
1966
+
1967
+ # Build markdown table
1968
+ def _md_table(table_rows: List[Dict[str, Any]]) -> str:
1969
+ if not table_rows:
1970
+ return "| Domain | Spawns | First seen | Last seen | hint_coverage_pct |\n" \
1971
+ "| ------ | ------ | ---------- | --------- | ----------------- |\n" \
1972
+ "| (no results) | - | - | - | - |\n"
1973
+ lines = [
1974
+ "| Domain | Spawns | First seen | Last seen | hint_coverage_pct |",
1975
+ "| ------ | ------ | ---------- | --------- | ----------------- |",
1976
+ ]
1977
+ for r in table_rows:
1978
+ lines.append(
1979
+ f"| {r['domain']} | {r['spawns']} | {r['first_seen']} "
1980
+ f"| {r['last_seen']} | {r['hint_coverage_pct']}% |"
1981
+ )
1982
+ return "\n".join(lines) + "\n"
1983
+
1984
+ output: Dict[str, Any] = {
1985
+ "query": "by-domain",
1986
+ "version": "1",
1987
+ "window": window_label,
1988
+ "total_spawns_in_window": total_in_window,
1989
+ "overall_hint_coverage_pct": (
1990
+ round(total_with_hint / total_in_window * 100, 1)
1991
+ if total_in_window > 0
1992
+ else 0.0
1993
+ ),
1994
+ "domain_count": len(rows),
1995
+ "domains": rows,
1996
+ "markdown_table": _md_table(rows),
1997
+ }
1998
+
1999
+ if check_reopen:
2000
+ output["check_reopen"] = {
2001
+ "sunset_domains_loaded": len(sunset_domains) if sunset_domains is not None else 0,
2002
+ "reopen_candidates": reopen_rows,
2003
+ "reopen_count": len(reopen_rows),
2004
+ "note": (
2005
+ "Domains from sunset list with >= 1 spawn in window. "
2006
+ "UNKNOWN bucket excluded per M2-CDX-7."
2007
+ ),
2008
+ }
2009
+
2010
+ return output
2011
+
2012
+
2013
+ # ---------------------------------------------------------------------------
2014
+ # Output formatting
2015
+ # ---------------------------------------------------------------------------
2016
+
2017
+
2018
+ def render(result: Any, *, as_json: bool, as_csv: bool) -> str:
2019
+ """Render query results into the requested output format (text / json / markdown)."""
2020
+ if as_json:
2021
+ return json.dumps(result, indent=2, ensure_ascii=True)
2022
+ if as_csv and isinstance(result, list) and result and isinstance(result[0], dict):
2023
+ buf = io.StringIO()
2024
+ fieldnames = sorted({k for e in result for k in e.keys()})
2025
+ writer = csv.DictWriter(buf, fieldnames=fieldnames, extrasaction="ignore")
2026
+ writer.writeheader()
2027
+ for e in result:
2028
+ writer.writerow(e)
2029
+ return buf.getvalue()
2030
+
2031
+ # Default: pretty-print
2032
+ if isinstance(result, dict):
2033
+ # by-domain: prefer the embedded markdown table for human output
2034
+ if result.get("query") == "by-domain" and "markdown_table" in result:
2035
+ lines = [
2036
+ f"window: {result.get('window', '')}",
2037
+ f"total_spawns: {result.get('total_spawns_in_window', 0)}",
2038
+ f"overall_hint_coverage: {result.get('overall_hint_coverage_pct', 0)}%",
2039
+ f"domain_count: {result.get('domain_count', 0)}",
2040
+ "",
2041
+ result["markdown_table"],
2042
+ ]
2043
+ if "check_reopen" in result:
2044
+ cr = result["check_reopen"]
2045
+ lines += [
2046
+ "--- Reopen check ---",
2047
+ f"sunset_domains_loaded: {cr.get('sunset_domains_loaded', 0)}",
2048
+ f"reopen_candidates: {cr.get('reopen_count', 0)}",
2049
+ ]
2050
+ for row in cr.get("reopen_candidates", []):
2051
+ lines.append(f" * {row['domain']} ({row['spawns']} spawns)")
2052
+ return "\n".join(lines)
2053
+ return _format_dict(result)
2054
+ if isinstance(result, list):
2055
+ if not result:
2056
+ return "(no results)"
2057
+ if isinstance(result[0], dict):
2058
+ return _format_table(result)
2059
+ return "\n".join(str(x) for x in result)
2060
+ return str(result)
2061
+
2062
+
2063
+ def _format_dict(d: Dict[str, Any]) -> str:
2064
+ lines = []
2065
+ for key, value in d.items():
2066
+ if isinstance(value, (list, dict)):
2067
+ lines.append(f"{key}:")
2068
+ sub = _format_dict(value) if isinstance(value, dict) else _format_list_under(value)
2069
+ for ln in sub.split("\n"):
2070
+ lines.append(f" {ln}")
2071
+ else:
2072
+ lines.append(f"{key}: {value}")
2073
+ return "\n".join(lines)
2074
+
2075
+
2076
+ def _format_list_under(items: List[Any]) -> str:
2077
+ if not items:
2078
+ return "(empty)"
2079
+ if isinstance(items[0], dict):
2080
+ return _format_table(items)
2081
+ return "\n".join(f"- {x}" for x in items)
2082
+
2083
+
2084
+ def _format_table(rows: List[Dict[str, Any]]) -> str:
2085
+ if not rows:
2086
+ return "(no rows)"
2087
+ fieldnames = list(rows[0].keys())
2088
+ # Compute column widths
2089
+ widths = {f: len(f) for f in fieldnames}
2090
+ str_rows = []
2091
+ for r in rows:
2092
+ str_row = {}
2093
+ for f in fieldnames:
2094
+ s = str(r.get(f, ""))
2095
+ str_row[f] = s
2096
+ if len(s) > widths[f]:
2097
+ widths[f] = min(len(s), 80)
2098
+ str_rows.append(str_row)
2099
+ header = " | ".join(f.ljust(widths[f]) for f in fieldnames)
2100
+ sep = "-+-".join("-" * widths[f] for f in fieldnames)
2101
+ body = "\n".join(
2102
+ " | ".join(str(r[f])[: widths[f]].ljust(widths[f]) for f in fieldnames)
2103
+ for r in str_rows
2104
+ )
2105
+ return f"{header}\n{sep}\n{body}"
2106
+
2107
+
2108
+ # ---------------------------------------------------------------------------
2109
+ # Argument parsing
2110
+ # ---------------------------------------------------------------------------
2111
+
2112
+
2113
+ # ---------------------------------------------------------------------------
2114
+ # PLAN-081 Phase 6-bis — Pair-Rail label store + fp-rate aggregator + Codex
2115
+ # writeguard summary (R1 S-TDE-3 + S-TDE Q2). Owner labels Case B verdicts
2116
+ # post-hoc; fp-rate computes lower/upper bounds; codex-writeguard-summary
2117
+ # aggregates deny-list hits.
2118
+ # ---------------------------------------------------------------------------
2119
+
2120
+ _LABEL_STORE_PATH = (
2121
+ Path(__file__).resolve().parent / "audit-log-labels.jsonl"
2122
+ )
2123
+ # ADR-108 §Operational labeling protocol: Owner labels Case-B verdicts with
2124
+ # fp (false-positive — Codex was wrong; close as advisory), tp (true-positive
2125
+ # — block stands), or triage_pending (extends grace by 24h; max 1 extension
2126
+ # before mechanical close-as-advisory). retracted is added for explicit Owner
2127
+ # revocation of a prior label (creates a new chain entry that supersedes).
2128
+ _LABEL_VALID_CASES = frozenset(["A", "B", "C", "D", "E", "F"])
2129
+ _LABEL_VALID_LABELS = frozenset(["fp", "tp", "triage_pending", "retracted"])
2130
+
2131
+
2132
+ def _label_store_path() -> Path:
2133
+ """Return the labels jsonl path; env override for tests."""
2134
+ override = os.environ.get("CEO_PAIR_RAIL_LABEL_STORE_PATH")
2135
+ return Path(override) if override else _LABEL_STORE_PATH
2136
+
2137
+
2138
+ def _canonical_json_for_chain(record: Dict[str, Any]) -> str:
2139
+ """Canonical JSON for HMAC chain — sort keys + no whitespace + UTF-8."""
2140
+ return json.dumps(record, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
2141
+
2142
+
2143
+ def _compute_label_hmac(prev_hmac_hex: str, record: Dict[str, Any]) -> str:
2144
+ """Compute HMAC-SHA256 over (prev_hmac || canonical_record) using
2145
+ the same key as `audit_hmac.get_or_create_key()`. Each record's
2146
+ `hmac` field links to the previous record's hmac forming a chain.
2147
+
2148
+ Hexlified output for jsonl-friendly storage.
2149
+ """
2150
+ import hmac as _hmac
2151
+ import hashlib as _hashlib
2152
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "hooks" / "_lib"))
2153
+ try:
2154
+ import audit_hmac # type: ignore
2155
+ key = audit_hmac.get_or_create_key()
2156
+ except Exception:
2157
+ # Fallback: deterministic per-project SHA-256 chain when key infra
2158
+ # unavailable (e.g. fresh adopter clone). This still provides
2159
+ # tamper-evidence (mutating an old record breaks subsequent chain
2160
+ # verification) but is not authenticated against an external key.
2161
+ key = b"ceo-pair-rail-label-chain-fallback-key-v1"
2162
+ msg = (prev_hmac_hex + _canonical_json_for_chain(record)).encode("utf-8")
2163
+ return _hmac.new(key, msg, _hashlib.sha256).hexdigest()
2164
+
2165
+
2166
+ def _load_label_records() -> List[Dict[str, Any]]:
2167
+ """Read label jsonl + verify HMAC chain. Returns list of records or [].
2168
+
2169
+ On HMAC mismatch, raises ValueError with the offending record index.
2170
+ """
2171
+ p = _label_store_path()
2172
+ if not p.exists():
2173
+ return []
2174
+ records: List[Dict[str, Any]] = []
2175
+ prev_hmac = "" # genesis
2176
+ with p.open("r", encoding="utf-8") as fh:
2177
+ for idx, line in enumerate(fh):
2178
+ line = line.strip()
2179
+ if not line:
2180
+ continue
2181
+ try:
2182
+ rec = json.loads(line)
2183
+ except json.JSONDecodeError as e:
2184
+ raise ValueError(
2185
+ f"audit-log-labels.jsonl record {idx}: invalid JSON: {e}"
2186
+ ) from e
2187
+ stored_hmac = rec.pop("hmac", "")
2188
+ record_for_compute = dict(rec)
2189
+ expected_hmac = _compute_label_hmac(prev_hmac, record_for_compute)
2190
+ if stored_hmac != expected_hmac:
2191
+ raise ValueError(
2192
+ f"audit-log-labels.jsonl record {idx}: HMAC chain broken "
2193
+ f"(stored={stored_hmac[:16]}, computed={expected_hmac[:16]})"
2194
+ )
2195
+ rec["hmac"] = stored_hmac
2196
+ records.append(rec)
2197
+ prev_hmac = stored_hmac
2198
+ return records
2199
+
2200
+
2201
+ def _append_label_record(record: Dict[str, Any]) -> str:
2202
+ """Append a record to label store with HMAC chain link. Returns the new HMAC."""
2203
+ p = _label_store_path()
2204
+ p.parent.mkdir(parents=True, exist_ok=True)
2205
+ records = _load_label_records()
2206
+ prev_hmac = records[-1]["hmac"] if records else ""
2207
+ new_hmac = _compute_label_hmac(prev_hmac, record)
2208
+ full_record = dict(record)
2209
+ full_record["hmac"] = new_hmac
2210
+ with p.open("a", encoding="utf-8") as fh:
2211
+ fh.write(_canonical_json_for_chain(full_record) + "\n")
2212
+ return new_hmac
2213
+
2214
+
2215
+ def cmd_label(entries, args) -> Dict[str, Any]:
2216
+ """Owner labels a pair_rail_case event post-hoc. PLAN-081 Phase 6-bis.
2217
+
2218
+ Append-only via HMAC chain to `.claude/scripts/audit-log-labels.jsonl`.
2219
+ The Owner labels Case B (Claude PASS + Codex BLOCK) verdicts per
2220
+ ADR-108 §Owner labeling protocol with one of:
2221
+
2222
+ - fp (false-positive — Codex was wrong; close as advisory)
2223
+ - tp (true-positive — block stands)
2224
+ - triage_pending (extends grace by 24h; max 1 extension before
2225
+ mechanical close-as-advisory)
2226
+ - retracted (creates new chain entry that supersedes the latest
2227
+ prior label for the same run_id; if retracted is the most-recent
2228
+ entry, the event reverts to unlabeled status in fp-rate computations)
2229
+
2230
+ fp-rate aggregator (cmd_fp_rate) denominator behavior: case_b_total
2231
+ ALWAYS includes every Case-B event in the window. The numerator
2232
+ counts only `fp` labels for the lower bound; unlabeled +
2233
+ triage_pending count as worst-case FP for the upper bound. See
2234
+ cmd_fp_rate docstring for the Wilson 95% bounds details.
2235
+ """
2236
+ run_id = getattr(args, "run_id", None)
2237
+ case = getattr(args, "case", None)
2238
+ label = getattr(args, "label", None)
2239
+ note = getattr(args, "note", "") or ""
2240
+
2241
+ if not run_id:
2242
+ return {"verdict": "ERROR", "reason": "--run-id required"}
2243
+ if case not in _LABEL_VALID_CASES:
2244
+ return {
2245
+ "verdict": "ERROR",
2246
+ "reason": f"--case must be one of {sorted(_LABEL_VALID_CASES)}, got {case!r}",
2247
+ }
2248
+ if label not in _LABEL_VALID_LABELS:
2249
+ return {
2250
+ "verdict": "ERROR",
2251
+ "reason": f"--label must be one of {sorted(_LABEL_VALID_LABELS)}, got {label!r}",
2252
+ }
2253
+
2254
+ ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
2255
+ record = {
2256
+ "ts": ts,
2257
+ "run_id": run_id,
2258
+ "case": case,
2259
+ "label": label,
2260
+ "note_bucket": "empty" if not note else (
2261
+ "short" if len(note) <= 50 else "medium" if len(note) <= 200 else "long"
2262
+ ),
2263
+ }
2264
+ new_hmac = _append_label_record(record)
2265
+ return {
2266
+ "verdict": "OK",
2267
+ "run_id": run_id,
2268
+ "case": case,
2269
+ "label": label,
2270
+ "ts": ts,
2271
+ "hmac_prefix": new_hmac[:16],
2272
+ }
2273
+
2274
+
2275
+ def _wilson_bounds(successes: int, n: int, z: float = 1.96) -> Tuple[float, float]:
2276
+ """95% Wilson score interval for binomial proportion (ADR-108 §FP-rate).
2277
+
2278
+ More accurate than naive (p ± z·sqrt(p(1-p)/n)) at small n or
2279
+ extreme p. Returns (lower, upper) ∈ [0, 1].
2280
+ """
2281
+ if n <= 0:
2282
+ return 0.0, 0.0
2283
+ p = successes / n
2284
+ z2 = z * z
2285
+ denom = 1.0 + z2 / n
2286
+ center = (p + z2 / (2.0 * n)) / denom
2287
+ half_width = (z * ((p * (1.0 - p) + z2 / (4.0 * n)) / n) ** 0.5) / denom
2288
+ lo = max(0.0, center - half_width)
2289
+ hi = min(1.0, center + half_width)
2290
+ return lo, hi
2291
+
2292
+
2293
+ def cmd_fp_rate(entries, args) -> Dict[str, Any]:
2294
+ """PLAN-081 Phase 6-bis (R1 S-TDE-3 + ADR-108 §FP-rate): Case-B
2295
+ false-positive rate aggregator with 95% Wilson score interval.
2296
+
2297
+ The Wilson interval provides robust lower/upper bounds at small n or
2298
+ extreme proportions, which a simple Laplace bound underestimates. Per
2299
+ ADR-108 §FP-rate, the reopen criterion fires when fp_rate_30d > 30%
2300
+ via `disable_predicate_eval.py` `fp_rate_30d_above_30pct` predicate.
2301
+
2302
+ Labels per ADR-108 §Owner labeling protocol:
2303
+ - `fp` = labeled false-positive — counted in numerator + denominator
2304
+ - `tp` = labeled true-positive — counted in denominator (not in fp num)
2305
+ - `triage_pending` = grace extended — counted in denominator (still
2306
+ provisional; treated as labeled-pending for Wilson computation)
2307
+ - `retracted` = supersedes prior label — most-recent non-retracted
2308
+ label wins; if retracted is the latest entry for a run_id, the
2309
+ event reverts to unlabeled
2310
+ The denominator (case_b_total) ALWAYS includes every Case-B event in
2311
+ the window — labeled or not. Wilson upper bound treats unlabeled +
2312
+ triage_pending as worst-case FP.
2313
+
2314
+ Window selection via --window-days (default 30).
2315
+ """
2316
+ window_days = getattr(args, "window_days", None)
2317
+ if window_days is None:
2318
+ window_days = 30 # default 30-day window per ADR-108 §FP-rate
2319
+
2320
+ now = datetime.now(timezone.utc)
2321
+ cutoff = now - timedelta(days=window_days)
2322
+ cutoff_iso = cutoff.strftime("%Y-%m-%dT%H:%M:%SZ")
2323
+
2324
+ # Filter pair_rail_case events in window — only Case B (asymmetric case)
2325
+ case_b_total = 0
2326
+ case_b_run_ids: List[str] = []
2327
+ for e in entries:
2328
+ if e.get("action") != "pair_rail_case":
2329
+ continue
2330
+ if str(e.get("ts", "")) < cutoff_iso:
2331
+ continue
2332
+ if e.get("case") != "B":
2333
+ continue
2334
+ case_b_total += 1
2335
+ rid = str(e.get("run_id") or e.get("pair_rail_run_id") or "")
2336
+ if rid:
2337
+ case_b_run_ids.append(rid)
2338
+
2339
+ # Load labels + match by run_id (latest label wins per run_id; retracted
2340
+ # entries supersede prior labels)
2341
+ try:
2342
+ labels = _load_label_records()
2343
+ except ValueError as exc:
2344
+ return {"verdict": "ERROR", "reason": str(exc)}
2345
+
2346
+ label_index: Dict[str, str] = {} # run_id → most-recent non-retracted label
2347
+ for r in labels:
2348
+ if r.get("case") != "B":
2349
+ continue
2350
+ rid = str(r.get("run_id", ""))
2351
+ lab = str(r.get("label", ""))
2352
+ if lab == "retracted":
2353
+ label_index.pop(rid, None)
2354
+ else:
2355
+ label_index[rid] = lab
2356
+
2357
+ labeled_fp = 0
2358
+ labeled_tp = 0
2359
+ labeled_triage = 0
2360
+ unlabeled = 0
2361
+ for rid in case_b_run_ids:
2362
+ lab = label_index.get(rid)
2363
+ if lab == "fp":
2364
+ labeled_fp += 1
2365
+ elif lab == "tp":
2366
+ labeled_tp += 1
2367
+ elif lab == "triage_pending":
2368
+ labeled_triage += 1
2369
+ else:
2370
+ unlabeled += 1
2371
+
2372
+ # Wilson 95% bounds: numerator = labeled_fp, denominator = case_b_total
2373
+ # Upper bound assumes both unlabeled AND triage_pending are worst-case
2374
+ # FP per ADR-108 §FP-rate + cmd_label/cmd_fp_rate docstring contract
2375
+ # (Codex iter-6 P2 fix — implementation now matches docstring).
2376
+ fp_lo, _ = _wilson_bounds(labeled_fp, case_b_total)
2377
+ worst_case_fp = labeled_fp + unlabeled + labeled_triage
2378
+ _, fp_hi_via_worst = _wilson_bounds(worst_case_fp, case_b_total)
2379
+ fp_hi = fp_hi_via_worst
2380
+
2381
+ # Reopen threshold per ADR-108 §FP-rate (default 0.30 — the 30% bar that
2382
+ # triggers `fp_rate_30d_above_30pct` predicate in disable_predicate_eval.py)
2383
+ threshold = float(getattr(args, "reopen_threshold", None) or 0.30)
2384
+ trigger_reopen = fp_lo > threshold
2385
+
2386
+ return {
2387
+ "window_days": window_days,
2388
+ "window_start": cutoff_iso,
2389
+ "window_end": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
2390
+ "case_b_total": case_b_total,
2391
+ "case_b_labeled_fp": labeled_fp,
2392
+ "case_b_labeled_tp": labeled_tp,
2393
+ "case_b_labeled_triage": labeled_triage,
2394
+ "case_b_unlabeled": unlabeled,
2395
+ "fp_rate_lower_bound": round(fp_lo, 4),
2396
+ "fp_rate_upper_bound": round(fp_hi, 4),
2397
+ "wilson_z": 1.96,
2398
+ "reopen_threshold": threshold,
2399
+ "trigger_reopen": trigger_reopen,
2400
+ "predicate_ref": "fp_rate_30d_above_30pct",
2401
+ }
2402
+
2403
+
2404
+ def cmd_case_summary(entries, args) -> Dict[str, Any]:
2405
+ """PLAN-081 Phase 6-bis (ADR-108 §Operational): Cases A-F distribution.
2406
+
2407
+ Aggregates `pair_rail_case` audit events over the window and returns
2408
+ counts + percentages for each Case (A=both PASS, B=Claude PASS+Codex
2409
+ BLOCK, C=Claude BLOCK+Codex PASS, D=both BLOCK, E=Jaccard divergence,
2410
+ F=Codex outage). Operators use this to monitor healthy distribution
2411
+ per ADR-108 §Operational (Case A typically 70-85%; Case F < 2%).
2412
+ """
2413
+ window_days = getattr(args, "window_days", None)
2414
+ if window_days is None:
2415
+ window_days = 7 # default 7-day window (faster operational signal)
2416
+
2417
+ now = datetime.now(timezone.utc)
2418
+ cutoff = now - timedelta(days=window_days)
2419
+ cutoff_iso = cutoff.strftime("%Y-%m-%dT%H:%M:%SZ")
2420
+
2421
+ per_case: Dict[str, int] = {"A": 0, "B": 0, "C": 0, "D": 0, "E": 0, "F": 0}
2422
+ unknown_case = 0
2423
+ precondition_met_count = 0
2424
+ precondition_not_met_count = 0 # Case B'
2425
+ for e in entries:
2426
+ if e.get("action") != "pair_rail_case":
2427
+ continue
2428
+ if str(e.get("ts", "")) < cutoff_iso:
2429
+ continue
2430
+ case = str(e.get("case") or "")
2431
+ if case in per_case:
2432
+ per_case[case] += 1
2433
+ if case == "B":
2434
+ pm = e.get("precondition_met")
2435
+ if pm is True or pm == "true":
2436
+ precondition_met_count += 1
2437
+ elif pm is False or pm == "false":
2438
+ precondition_not_met_count += 1
2439
+ else:
2440
+ unknown_case += 1
2441
+
2442
+ total = sum(per_case.values()) + unknown_case
2443
+ per_case_pct = {
2444
+ k: round(v / total * 100.0, 2) if total else 0.0
2445
+ for k, v in per_case.items()
2446
+ }
2447
+
2448
+ # Healthy distribution sentinels per ADR-108 §Operational
2449
+ healthy_ranges = {
2450
+ "A": (70.0, 85.0),
2451
+ "B": (1.0, 8.0),
2452
+ "C": (2.0, 10.0),
2453
+ "D": (1.0, 5.0),
2454
+ "E": (1.0, 5.0),
2455
+ "F": (0.0, 2.0),
2456
+ }
2457
+ health = {}
2458
+ for k, pct in per_case_pct.items():
2459
+ lo, hi = healthy_ranges[k]
2460
+ if total == 0:
2461
+ health[k] = "NO_DATA"
2462
+ elif lo <= pct <= hi:
2463
+ health[k] = "OK"
2464
+ elif pct < lo:
2465
+ health[k] = "LOW"
2466
+ else:
2467
+ health[k] = "HIGH"
2468
+
2469
+ return {
2470
+ "window_days": window_days,
2471
+ "window_start": cutoff_iso,
2472
+ "window_end": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
2473
+ "total_pair_rail_case_events": total,
2474
+ "case_counts": per_case,
2475
+ "case_percentages": per_case_pct,
2476
+ "unknown_case_count": unknown_case,
2477
+ "case_b_precondition_met": precondition_met_count,
2478
+ "case_b_precondition_not_met": precondition_not_met_count,
2479
+ "health_per_case": health,
2480
+ }
2481
+
2482
+
2483
+ def cmd_codex_writeguard_summary(entries, args) -> Dict[str, Any]:
2484
+ """PLAN-081 Phase 6-bis (R1 S-TDE Q2): Codex codando deny-list hit summary.
2485
+
2486
+ Aggregates `pair_rail_codex_denylist_hit` audit events by target_path
2487
+ bucket. Surfaces top-attempted forbidden paths so operators can detect
2488
+ deny-list coverage gaps.
2489
+ """
2490
+ window_days = getattr(args, "window_days", None)
2491
+ if window_days is None:
2492
+ window_days = 30
2493
+
2494
+ now = datetime.now(timezone.utc)
2495
+ cutoff = now - timedelta(days=window_days)
2496
+ cutoff_iso = cutoff.strftime("%Y-%m-%dT%H:%M:%SZ")
2497
+
2498
+ per_path: Dict[str, int] = {}
2499
+ total_hits = 0
2500
+ for e in entries:
2501
+ if e.get("action") != "pair_rail_codex_denylist_hit":
2502
+ continue
2503
+ if str(e.get("ts", "")) < cutoff_iso:
2504
+ continue
2505
+ path = str(e.get("target_path_bucket") or e.get("target_path") or "unknown")
2506
+ per_path[path] = per_path.get(path, 0) + 1
2507
+ total_hits += 1
2508
+
2509
+ top_n = int(getattr(args, "top", None) or 10)
2510
+ top_paths = sorted(per_path.items(), key=lambda kv: (-kv[1], kv[0]))[:top_n]
2511
+
2512
+ return {
2513
+ "window_days": window_days,
2514
+ "window_start": cutoff_iso,
2515
+ "window_end": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
2516
+ "total_hits": total_hits,
2517
+ "unique_paths": len(per_path),
2518
+ "top_paths": [{"path_bucket": p, "hits": c} for p, c in top_paths],
2519
+ }
2520
+
2521
+
2522
+ # ---------------------------------------------------------------------------
2523
+ # PLAN-113 Phase B, Wave W1 — critical-security-action reader (audit-reader
2524
+ # coverage). These 38 actions are emitted by hooks but had NO reader handler
2525
+ # in this CLI, so an operator could not surface them (PLAN-112 finding class
2526
+ # "Critical action has no reader handler"). cmd_critical reads the registry
2527
+ # below and surfaces, per action, count + first/last timestamp + a compact
2528
+ # set of SAFE summary fields ALREADY PRESENT on the event (never invented).
2529
+ # Actions with ZERO occurrences are still listed (count=0) so a
2530
+ # missing-but-expected critical event is visible — surfacing ABSENCE is the
2531
+ # whole point.
2532
+ # ---------------------------------------------------------------------------
2533
+
2534
+ _CRITICAL_SECURITY_ACTIONS: Tuple[str, ...] = (
2535
+ # --- federation (cross-machine peer trust / write-mode) ---
2536
+ "federation_autonomous_call_blocked",
2537
+ "federation_cert_revoked",
2538
+ "federation_event_action_blocked",
2539
+ "federation_hmac_secret_rotated",
2540
+ "federation_key_floor_rejected",
2541
+ "federation_lan_bind_denied",
2542
+ "federation_peer_revoked_remote",
2543
+ "federation_scope_denied",
2544
+ "federation_spki_fingerprint_mismatch",
2545
+ "federation_tamper_detected",
2546
+ "federation_write_attempt_blocked",
2547
+ "federation_write_endpoint_denied",
2548
+ # --- mcp (bearer-token / tenant isolation) ---
2549
+ "mcp_bearer_replay_rejected",
2550
+ "mcp_cross_tenant_denied",
2551
+ "mcp_non_loopback_rejected",
2552
+ # --- sentinel-signer (GPG quorum / rotation / revocation) ---
2553
+ "gpg_signed",
2554
+ "gpg_verified",
2555
+ "sentinel_signer_expiry_warned",
2556
+ "sentinel_signer_quorum_attempted",
2557
+ "sentinel_signer_quorum_failed",
2558
+ "sentinel_signer_revoked",
2559
+ "sentinel_signer_rotated",
2560
+ # --- trading (kill-switch / write-override) ---
2561
+ "trading_kill_switch_disabled",
2562
+ "trading_kill_switch_invoked",
2563
+ "trading_write_override_used",
2564
+ # --- credential (age / emergency override) ---
2565
+ "credential_blocked_due_to_age",
2566
+ "credential_emergency_override_used",
2567
+ # --- audit-spool (tamper detection) ---
2568
+ "audit_spool_tamper_detected",
2569
+ # --- governance (kernel / kill-switch / overrides / pair-rail / swarm) ---
2570
+ "anti_ceo_overhead_override_used",
2571
+ "bash_canonical_bypass_invoked",
2572
+ "confidence_gate_blocked",
2573
+ "kernel_extension_landed",
2574
+ "kill_switch_invoked",
2575
+ "live_adapter_blocked",
2576
+ "pair_rail_codex_injection_detected",
2577
+ "pair_rail_outgoing_redaction_applied",
2578
+ "phase_c_enforcing_flipped",
2579
+ "swarm_layer_3_4_blocked",
2580
+ )
2581
+
2582
+ # Map each critical action to its domain group (for the table/json output).
2583
+ # Domains mirror the comment blocks in _CRITICAL_SECURITY_ACTIONS above.
2584
+ _CRITICAL_ACTION_DOMAIN: Dict[str, str] = {
2585
+ # federation
2586
+ "federation_autonomous_call_blocked": "federation",
2587
+ "federation_cert_revoked": "federation",
2588
+ "federation_event_action_blocked": "federation",
2589
+ "federation_hmac_secret_rotated": "federation",
2590
+ "federation_key_floor_rejected": "federation",
2591
+ "federation_lan_bind_denied": "federation",
2592
+ "federation_peer_revoked_remote": "federation",
2593
+ "federation_scope_denied": "federation",
2594
+ "federation_spki_fingerprint_mismatch": "federation",
2595
+ "federation_tamper_detected": "federation",
2596
+ "federation_write_attempt_blocked": "federation",
2597
+ "federation_write_endpoint_denied": "federation",
2598
+ # mcp
2599
+ "mcp_bearer_replay_rejected": "mcp",
2600
+ "mcp_cross_tenant_denied": "mcp",
2601
+ "mcp_non_loopback_rejected": "mcp",
2602
+ # sentinel-signer
2603
+ "gpg_signed": "sentinel-signer",
2604
+ "gpg_verified": "sentinel-signer",
2605
+ "sentinel_signer_expiry_warned": "sentinel-signer",
2606
+ "sentinel_signer_quorum_attempted": "sentinel-signer",
2607
+ "sentinel_signer_quorum_failed": "sentinel-signer",
2608
+ "sentinel_signer_revoked": "sentinel-signer",
2609
+ "sentinel_signer_rotated": "sentinel-signer",
2610
+ # trading
2611
+ "trading_kill_switch_disabled": "trading",
2612
+ "trading_kill_switch_invoked": "trading",
2613
+ "trading_write_override_used": "trading",
2614
+ # credential
2615
+ "credential_blocked_due_to_age": "credential",
2616
+ "credential_emergency_override_used": "credential",
2617
+ # audit-spool
2618
+ "audit_spool_tamper_detected": "audit-spool",
2619
+ # governance
2620
+ "anti_ceo_overhead_override_used": "governance",
2621
+ "bash_canonical_bypass_invoked": "governance",
2622
+ "confidence_gate_blocked": "governance",
2623
+ "kernel_extension_landed": "governance",
2624
+ "kill_switch_invoked": "governance",
2625
+ "live_adapter_blocked": "governance",
2626
+ "pair_rail_codex_injection_detected": "governance",
2627
+ "pair_rail_outgoing_redaction_applied": "governance",
2628
+ "phase_c_enforcing_flipped": "governance",
2629
+ "swarm_layer_3_4_blocked": "governance",
2630
+ }
2631
+
2632
+ # Allowlist of SAFE, schema-stable scalar summary keys to echo when present
2633
+ # on a critical event. We never echo arbitrary fields (avoids surfacing
2634
+ # large/unexpected payloads) and never INVENT a field — only keys in this set
2635
+ # that actually exist on the event dict are surfaced, and only scalar values
2636
+ # (str/int/float/bool). These keys are all part of audit_emit's per-action
2637
+ # allowlists (already scrubbed at emit time) so echoing them is safe.
2638
+ _CRITICAL_SAFE_SUMMARY_KEYS: Tuple[str, ...] = (
2639
+ "session_id",
2640
+ "project",
2641
+ "reason",
2642
+ "reason_code",
2643
+ "scope",
2644
+ "phase",
2645
+ "migration_phase",
2646
+ "env_value",
2647
+ "mode",
2648
+ "outcome",
2649
+ "decision",
2650
+ "peer_id",
2651
+ "endpoint",
2652
+ "tenant",
2653
+ "signer",
2654
+ "key_id",
2655
+ "fingerprint",
2656
+ "loop_id",
2657
+ "redaction_count",
2658
+ "field_count",
2659
+ )
2660
+
2661
+
2662
+ def cmd_critical(entries: List[Dict[str, Any]], args) -> Dict[str, Any]:
2663
+ """`audit-query critical` — surface the 38 critical-security actions.
2664
+
2665
+ PLAN-113 Phase B, Wave W1 (audit-reader-coverage). Each action in
2666
+ ``_CRITICAL_SECURITY_ACTIONS`` is emitted by a hook but had no reader
2667
+ handler, so an operator could not query it. This command aggregates,
2668
+ per critical action that appears in the log: ``count``, first/last ISO
2669
+ timestamp, and a compact set of SAFE summary fields drawn from the
2670
+ LATEST matching event (only keys in ``_CRITICAL_SAFE_SUMMARY_KEYS`` that
2671
+ are actually present + scalar — no invented fields).
2672
+
2673
+ Actions with ZERO occurrences are STILL emitted with ``count=0`` so a
2674
+ missing-but-expected critical event is visible (surfacing absence is the
2675
+ whole point of the reader-coverage gap PLAN-112 found).
2676
+
2677
+ ``--action <name>`` drills into one action; the name must be in the
2678
+ registry (else error). Output is a table (default) or JSON (``--json``).
2679
+
2680
+ Output envelope (mirrors the other v-era commands):
2681
+ {"query": "critical", "version": "1",
2682
+ "data": {"action_filter": ..., "total_critical_events": N,
2683
+ "present_action_count": M, "registry_size": 38,
2684
+ "actions": [{action, domain, count, first_ts, last_ts,
2685
+ last_summary: {...}}, ...]}}
2686
+ """
2687
+ action_filter = getattr(args, "action", None)
2688
+ if action_filter is not None:
2689
+ if action_filter not in _CRITICAL_SECURITY_ACTION_SET:
2690
+ print(
2691
+ f"[audit-query] ERROR: critical: unknown --action "
2692
+ f"{action_filter!r}; must be one of the "
2693
+ f"{len(_CRITICAL_SECURITY_ACTIONS)} registry actions",
2694
+ file=sys.stderr,
2695
+ )
2696
+ sys.exit(1)
2697
+ registry: Tuple[str, ...] = (action_filter,)
2698
+ else:
2699
+ registry = _CRITICAL_SECURITY_ACTIONS
2700
+
2701
+ # Single pass: accumulate count / first_ts / last_ts / latest-event
2702
+ # safe-summary per critical action. We only track actions in `registry`.
2703
+ registry_set = set(registry)
2704
+ agg: Dict[str, Dict[str, Any]] = {
2705
+ a: {"count": 0, "first_ts": "", "last_ts": "", "last_summary": {}}
2706
+ for a in registry
2707
+ }
2708
+ total_critical_events = 0
2709
+
2710
+ for e in entries:
2711
+ action = e.get("action")
2712
+ if action not in registry_set:
2713
+ continue
2714
+ a = agg[action]
2715
+ a["count"] += 1
2716
+ total_critical_events += 1
2717
+ ts = str(e.get("ts") or "")
2718
+ if ts:
2719
+ if not a["first_ts"] or ts < a["first_ts"]:
2720
+ a["first_ts"] = ts
2721
+ if ts >= a["last_ts"]:
2722
+ a["last_ts"] = ts
2723
+ a["last_summary"] = _critical_safe_summary(e)
2724
+
2725
+ actions: List[Dict[str, Any]] = []
2726
+ present_action_count = 0
2727
+ # Stable order: by domain, then action name (registry is the universe).
2728
+ for action in sorted(
2729
+ registry,
2730
+ key=lambda x: (_CRITICAL_ACTION_DOMAIN.get(x, "zzz"), x),
2731
+ ):
2732
+ a = agg[action]
2733
+ if a["count"] > 0:
2734
+ present_action_count += 1
2735
+ actions.append(
2736
+ {
2737
+ "action": action,
2738
+ "domain": _CRITICAL_ACTION_DOMAIN.get(action, "unknown"),
2739
+ "count": a["count"],
2740
+ "first_ts": a["first_ts"],
2741
+ "last_ts": a["last_ts"],
2742
+ "last_summary": a["last_summary"],
2743
+ }
2744
+ )
2745
+
2746
+ data = {
2747
+ "action_filter": action_filter,
2748
+ "registry_size": len(_CRITICAL_SECURITY_ACTIONS),
2749
+ "total_critical_events": total_critical_events,
2750
+ "present_action_count": present_action_count,
2751
+ "absent_action_count": len(registry) - present_action_count,
2752
+ "actions": actions,
2753
+ }
2754
+ return {
2755
+ "query": "critical",
2756
+ "version": "1",
2757
+ "data": data,
2758
+ }
2759
+
2760
+
2761
+ # Set form for O(1) membership tests (validation + per-entry filter).
2762
+ _CRITICAL_SECURITY_ACTION_SET = frozenset(_CRITICAL_SECURITY_ACTIONS)
2763
+
2764
+
2765
+ def _critical_safe_summary(event: Dict[str, Any]) -> Dict[str, Any]:
2766
+ """Echo only the SAFE, scalar summary keys present on a critical event.
2767
+
2768
+ Never invents a field: iterates ``_CRITICAL_SAFE_SUMMARY_KEYS`` and copies
2769
+ a key only when it exists on the event AND its value is a scalar
2770
+ (str/int/float/bool). This keeps the per-action summary compact and avoids
2771
+ surfacing large/unexpected payloads.
2772
+ """
2773
+ out: Dict[str, Any] = {}
2774
+ for key in _CRITICAL_SAFE_SUMMARY_KEYS:
2775
+ if key not in event:
2776
+ continue
2777
+ val = event[key]
2778
+ if isinstance(val, bool) or isinstance(val, (str, int, float)):
2779
+ out[key] = val
2780
+ return out
2781
+
2782
+
2783
+ def _build_shared_parser() -> argparse.ArgumentParser:
2784
+ """Return the parent parser with the shared flags.
2785
+
2786
+ Parent parser carrying the shared flags. These are added to each
2787
+ sub-command via ``parents=[]``, so users can write either::
2788
+
2789
+ audit-query.py summary --json
2790
+ audit-query.py --json summary # also works
2791
+ """
2792
+ shared = argparse.ArgumentParser(add_help=False)
2793
+ shared.add_argument(
2794
+ "--log",
2795
+ default=None,
2796
+ help="Path to audit-log.jsonl (default: CEO_AUDIT_LOG_PATH or ~)",
2797
+ )
2798
+ shared.add_argument(
2799
+ "--include-rotated",
2800
+ action="store_true",
2801
+ help="Also read audit-log-YYYY-MM*.jsonl siblings",
2802
+ )
2803
+ shared.add_argument("--json", dest="as_json", action="store_true")
2804
+ shared.add_argument("--csv", dest="as_csv", action="store_true")
2805
+ shared.add_argument(
2806
+ "--errors-path",
2807
+ default=None,
2808
+ help="Override path for the `errors` sub-command",
2809
+ )
2810
+ return shared
2811
+
2812
+
2813
+ def _add_v1_subparsers(sub: "argparse._SubParsersAction",
2814
+ shared: argparse.ArgumentParser) -> None:
2815
+ """Register the original v1 sub-commands (summary/by-skill/... export)."""
2816
+ sub.add_parser(
2817
+ "summary",
2818
+ parents=[shared],
2819
+ help="Overview: count, range, top skills, compliance",
2820
+ )
2821
+
2822
+ byskill = sub.add_parser("by-skill", parents=[shared], help="Rank skills by usage count")
2823
+ byskill.add_argument("--top", type=int, default=10)
2824
+
2825
+ sub.add_parser("compliance", parents=[shared], help="Governance compliance breakdown")
2826
+
2827
+ byday = sub.add_parser("by-day", parents=[shared], help="Spawns-per-day histogram")
2828
+ byday.add_argument("--days", type=int, default=14)
2829
+
2830
+ search = sub.add_parser("search", parents=[shared], help="Regex-match against desc_preview")
2831
+ search.add_argument("regex")
2832
+
2833
+ since = sub.add_parser("since", parents=[shared], help="Entries on or after a date")
2834
+ since.add_argument("iso_date", help="YYYY-MM-DD or full ISO 8601")
2835
+
2836
+ sub.add_parser("errors", parents=[shared], help="Tail the audit-log.errors breadcrumb file")
2837
+
2838
+ stats_p = sub.add_parser(
2839
+ "stats",
2840
+ parents=[shared],
2841
+ help="Prompt-length + response_kind distributions + latency; "
2842
+ "--tool-latency for per-tool lifecycle buckets",
2843
+ )
2844
+ # PLAN-125 WS-1 — per-tool-call lifecycle latency view. Bucket-counts ONLY
2845
+ # (the tool_call_lifecycle_recorded action carries NO raw duration_ms per
2846
+ # MF-SEC-3), so this is a histogram, NOT percentiles.
2847
+ stats_p.add_argument(
2848
+ "--tool-latency",
2849
+ dest="tool_latency",
2850
+ action="store_true",
2851
+ help="Per-tool_name_enum duration_bucket histogram (+ success / orphan "
2852
+ "rollups) from tool_call_lifecycle_recorded rows. Bucket-counts "
2853
+ "only — no percentiles (raw duration_ms is never recorded).",
2854
+ )
2855
+
2856
+ export = sub.add_parser("export", parents=[shared], help="Dump all entries in csv/json/tsv")
2857
+ export.add_argument(
2858
+ "--format", dest="export_format", choices=["csv", "json", "tsv"], default="json"
2859
+ )
2860
+
2861
+
2862
+ def _add_v2_subparsers(sub: "argparse._SubParsersAction",
2863
+ shared: argparse.ArgumentParser) -> None:
2864
+ """Register Sprint 5 A.2 v2 event-stream sub-commands."""
2865
+ sub.add_parser(
2866
+ "debate",
2867
+ parents=[shared],
2868
+ help="Debate rounds: grouped by (plan_id, round) with agents + consensus",
2869
+ )
2870
+ sub.add_parser(
2871
+ "plans",
2872
+ parents=[shared],
2873
+ help="Plan status transitions per plan_id",
2874
+ )
2875
+ sub.add_parser(
2876
+ "vetoes",
2877
+ parents=[shared],
2878
+ help="Veto events aggregated by (hook, reason_code)",
2879
+ )
2880
+ sub.add_parser(
2881
+ "benchmarks",
2882
+ parents=[shared],
2883
+ help=(
2884
+ "Benchmark runs aggregated by skill — harbor-style row: "
2885
+ "pass_rate + cost + compute + turns (PLAN-133 C4)"
2886
+ ),
2887
+ )
2888
+ sub.add_parser(
2889
+ "lessons",
2890
+ parents=[shared],
2891
+ help="Lesson-write events grouped by archetype + trigger",
2892
+ )
2893
+ sub.add_parser(
2894
+ "metrics",
2895
+ parents=[shared],
2896
+ help="Cross-cutting derived metrics (veto rate, debate completion)",
2897
+ )
2898
+ sub.add_parser(
2899
+ "health",
2900
+ parents=[shared],
2901
+ help="Framework health verdict (PASS / WARN / FAIL / NO_DATA)",
2902
+ )
2903
+ sub.add_parser(
2904
+ "tokens",
2905
+ parents=[shared],
2906
+ help="Spawn token aggregates (PLAN-006 Phase 5a / ADR-016)",
2907
+ )
2908
+
2909
+
2910
+ def _add_sprint8_9_subparsers(sub: "argparse._SubParsersAction",
2911
+ shared: argparse.ArgumentParser) -> None:
2912
+ """Register confidence-gate + ADR-020 + lesson-effectiveness commands."""
2913
+ # Sprint 8 Phase 2 — confidence_gate aggregates (ADR-018)
2914
+ claims = sub.add_parser(
2915
+ "claims",
2916
+ parents=[shared],
2917
+ help="Confidence-gate verification aggregates (pass/fail by kind + agent)",
2918
+ )
2919
+ claims.add_argument("--kind", help="Filter by claim kind (e.g. path_exists)")
2920
+ claims.add_argument("--agent", help="Filter by agent name")
2921
+ claims.add_argument(
2922
+ "--failed-only",
2923
+ action="store_true",
2924
+ help="Only include events with ≥1 failed claim",
2925
+ )
2926
+
2927
+ # Sprint 9 Phase 2 (ADR-020) — prune-restore-ratio measurement
2928
+ prr = sub.add_parser(
2929
+ "prune-restore-ratio",
2930
+ parents=[shared],
2931
+ help="Ratio of restored/archived lessons over a time window (ADR-020)",
2932
+ )
2933
+ prr.add_argument(
2934
+ "--since", default="24h",
2935
+ help="Window start: 24h / 7d / 30m / ISO 8601 / 'all' (default 24h)",
2936
+ )
2937
+ prr.add_argument(
2938
+ "--until", default=None,
2939
+ help="Window end: ISO 8601 (default: now)",
2940
+ )
2941
+
2942
+ # Sprint 9 Phase 3 (PLAN-009 P3.4) — Architect outcome tracking
2943
+ aout = sub.add_parser(
2944
+ "architect-outcomes",
2945
+ parents=[shared],
2946
+ help="Per-lesson hit/miss from Architect spawns (ADR-015 amended)",
2947
+ )
2948
+ aout.add_argument(
2949
+ "--since", default="24h",
2950
+ help="Window start: 24h / 7d / 30m / ISO 8601 / 'all' (default 24h)",
2951
+ )
2952
+ aout.add_argument(
2953
+ "--consumer", default="architect",
2954
+ choices=["architect", "benchmark"],
2955
+ help="Filter by consumer (default: architect)",
2956
+ )
2957
+ aout.add_argument(
2958
+ "--include-window-only", action="store_true",
2959
+ help="Include pre-Sprint-9 window-only events (dirty signal)",
2960
+ )
2961
+
2962
+ # Sprint 9 Phase 5 (PLAN-009 P5.1) — lessons effectiveness ranking
2963
+ leff = sub.add_parser(
2964
+ "lessons-effectiveness",
2965
+ parents=[shared],
2966
+ help="Per-lesson effectiveness ranking (PLAN-009 Phase 5)",
2967
+ )
2968
+ leff.add_argument("--since", default="24h",
2969
+ help="Window start (default 24h; 'all' for no filter)")
2970
+ leff.add_argument("--include-window-only", action="store_true")
2971
+ leff.add_argument("--by", default="effectiveness",
2972
+ choices=["effectiveness", "recency", "injections"],
2973
+ help="Sort axis (default: effectiveness)")
2974
+ leff.add_argument("--top", type=int, default=0, help="Return top N")
2975
+ leff.add_argument("--bottom", type=int, default=0, help="Return bottom N")
2976
+
2977
+
2978
+ def _add_plan015_subparsers(sub: "argparse._SubParsersAction",
2979
+ shared: argparse.ArgumentParser) -> None:
2980
+ """Register PLAN-015 adopter-triage sub-commands."""
2981
+ # PLAN-015 Phase 0.5 — adopter-side weekly triage
2982
+ wks = sub.add_parser(
2983
+ "weekly-summary",
2984
+ parents=[shared],
2985
+ help="Week-over-week spawn/veto/plan trend for adopter triage (PLAN-015)",
2986
+ )
2987
+ wks.add_argument("--window", default="7d",
2988
+ help="Window length: 7d (default), 14d, 30d, or 'all'")
2989
+ wks.add_argument("--now", default=None,
2990
+ help="Override 'now' with an ISO-8601 timestamp (testing)")
2991
+
2992
+ # PLAN-025 Batch D F-obs-001 — ADR-052 multi-model spawn distribution.
2993
+ # Referenced from SLO-SLA.md + DAY-1-CHECKLIST; was missing pre-Batch-D.
2994
+ spawn_stats = sub.add_parser(
2995
+ "spawn-stats",
2996
+ parents=[shared],
2997
+ help="Spawn distribution by skill + model (PLAN-025 F-obs-001; ADR-052)",
2998
+ )
2999
+ spawn_stats.add_argument(
3000
+ "--since",
3001
+ default=None,
3002
+ help="Filter to entries at or after this ISO-8601 timestamp or relative (e.g. '7d')",
3003
+ )
3004
+ spawn_stats.add_argument(
3005
+ "--by",
3006
+ default="model",
3007
+ choices=("model", "skill", "both"),
3008
+ help="Group-by dimension (default: model)",
3009
+ )
3010
+
3011
+
3012
+ def _add_plan080_subparsers(sub: "argparse._SubParsersAction",
3013
+ shared: argparse.ArgumentParser) -> None:
3014
+ """Register PLAN-080 Phase 1 sub-commands.
3015
+
3016
+ by-domain: group spawn events by dispatch_archetype_hint for squad-bundle
3017
+ governance observability (PLAN-080 Phase 1 / ADR-112).
3018
+ """
3019
+ by_domain = sub.add_parser(
3020
+ "by-domain",
3021
+ parents=[shared],
3022
+ help=(
3023
+ "Group agent_spawn events by dispatch_archetype_hint domain "
3024
+ "(PLAN-080 Phase 1 / ADR-112)"
3025
+ ),
3026
+ )
3027
+ # Window options — mutually exclusive groups handled in cmd_by_domain
3028
+ by_domain.add_argument(
3029
+ "--window",
3030
+ default=f"{_BY_DOMAIN_DEFAULT_WINDOW_DAYS}d",
3031
+ metavar="WINDOW",
3032
+ help=(
3033
+ "Trailing window (e.g. 30d, 7d, 14d). "
3034
+ f"Default: {_BY_DOMAIN_DEFAULT_WINDOW_DAYS}d. "
3035
+ "Ignored when --start/--end are provided."
3036
+ ),
3037
+ )
3038
+ by_domain.add_argument(
3039
+ "--start",
3040
+ default=None,
3041
+ metavar="YYYY-MM-DD",
3042
+ help="Start date (inclusive). Must be paired with --end.",
3043
+ )
3044
+ by_domain.add_argument(
3045
+ "--end",
3046
+ default=None,
3047
+ metavar="YYYY-MM-DD",
3048
+ help="End date (inclusive). Must be paired with --start.",
3049
+ )
3050
+ by_domain.add_argument(
3051
+ "--check-reopen",
3052
+ action="store_true",
3053
+ dest="check_reopen",
3054
+ help=(
3055
+ "Filter output to sunset domains (from grandfather-cap.policy.yaml) "
3056
+ "with >= 1 spawn. UNKNOWN bucket excluded per M2-CDX-7."
3057
+ ),
3058
+ )
3059
+
3060
+
3061
+ def _add_plan081_subparsers(sub: "argparse._SubParsersAction",
3062
+ shared: argparse.ArgumentParser) -> None:
3063
+ """Register PLAN-081 Phase 6-bis sub-commands.
3064
+
3065
+ label: Owner labels a pair_rail_case event post-hoc (R1 S-TDE-3).
3066
+ fp-rate: false-positive rate aggregator with reopen-trigger threshold.
3067
+ codex-writeguard-summary: aggregates pair_rail_codex_denylist_hit events
3068
+ by target_path bucket (R1 S-TDE Q2).
3069
+ """
3070
+ # --- label ---
3071
+ label_p = sub.add_parser(
3072
+ "label",
3073
+ parents=[shared],
3074
+ help="Append a Case-B verdict label to audit-log-labels.jsonl (PLAN-081 Phase 6-bis)",
3075
+ )
3076
+ label_p.add_argument(
3077
+ "--run-id", required=True, dest="run_id",
3078
+ help="pair_rail_promotion_run_id (UUID hex) of the event being labeled",
3079
+ )
3080
+ label_p.add_argument(
3081
+ "--case", required=True, choices=sorted(_LABEL_VALID_CASES),
3082
+ help="Case letter A-F to label",
3083
+ )
3084
+ label_p.add_argument(
3085
+ "--label", required=True, choices=sorted(_LABEL_VALID_LABELS),
3086
+ help="Verdict label: fp | tp | triage_pending | retracted (per ADR-108 §Owner labeling protocol)",
3087
+ )
3088
+ label_p.add_argument(
3089
+ "--note", default="",
3090
+ help="Optional free-form note (length is bucketed in audit emit; content NOT stored)",
3091
+ )
3092
+
3093
+ # --- fp-rate ---
3094
+ fp_p = sub.add_parser(
3095
+ "fp-rate",
3096
+ parents=[shared],
3097
+ help="Case-B false-positive rate aggregator (PLAN-081 Phase 6-bis R1 S-TDE-3)",
3098
+ )
3099
+ fp_p.add_argument(
3100
+ "--window-days", type=int, default=30, dest="window_days",
3101
+ help="Trailing window in days (default 30)",
3102
+ )
3103
+ fp_p.add_argument(
3104
+ "--reopen-threshold", type=float, default=0.30, dest="reopen_threshold",
3105
+ help="trigger_reopen=true when fp_rate_lower_bound > threshold (default 0.30 per ADR-108 §FP-rate)",
3106
+ )
3107
+
3108
+ # --- case-summary ---
3109
+ cs_p = sub.add_parser(
3110
+ "case-summary",
3111
+ parents=[shared],
3112
+ help="Cases A-F distribution rollup over window (PLAN-081 Phase 6-bis ADR-108 §Operational)",
3113
+ )
3114
+ cs_p.add_argument(
3115
+ "--window-days", type=int, default=7, dest="window_days",
3116
+ help="Trailing window in days (default 7 — faster operational signal than 30d)",
3117
+ )
3118
+
3119
+ # --- codex-writeguard-summary ---
3120
+ cw_p = sub.add_parser(
3121
+ "codex-writeguard-summary",
3122
+ parents=[shared],
3123
+ help="Codex codando deny-list hit summary by path bucket (PLAN-081 Phase 6-bis R1 S-TDE Q2)",
3124
+ )
3125
+ cw_p.add_argument(
3126
+ "--window-days", type=int, default=30, dest="window_days",
3127
+ help="Trailing window in days (default 30)",
3128
+ )
3129
+ cw_p.add_argument(
3130
+ "--top", type=int, default=10,
3131
+ help="Top N most-attempted forbidden paths (default 10)",
3132
+ )
3133
+
3134
+
3135
+ def _add_plan113_subparsers(sub: "argparse._SubParsersAction",
3136
+ shared: argparse.ArgumentParser) -> None:
3137
+ """Register PLAN-113 Phase B Wave W1 sub-command.
3138
+
3139
+ critical: surface the 38 critical-security actions (count + first/last
3140
+ ts + safe summary), listing zero-occurrence actions so missing-but-
3141
+ expected critical events are visible (audit-reader-coverage gap).
3142
+ """
3143
+ crit = sub.add_parser(
3144
+ "critical",
3145
+ parents=[shared],
3146
+ help=(
3147
+ "Surface critical-security actions (count + last-seen + safe "
3148
+ "summary); absent actions listed with count=0 (PLAN-113 W1)"
3149
+ ),
3150
+ )
3151
+ crit.add_argument(
3152
+ "--action",
3153
+ default=None,
3154
+ metavar="ACTION",
3155
+ help=(
3156
+ "Drill into a single critical action (must be one of the "
3157
+ "registry actions; errors otherwise)"
3158
+ ),
3159
+ )
3160
+
3161
+
3162
+ def build_parser() -> argparse.ArgumentParser:
3163
+ """Construct the full audit-query argparse tree.
3164
+
3165
+ Delegates each era of sub-commands to a private helper
3166
+ (``_add_v1_subparsers`` / ``_add_v2_subparsers`` /
3167
+ ``_add_sprint8_9_subparsers`` / ``_add_plan015_subparsers`` /
3168
+ ``_add_plan080_subparsers`` / ``_add_plan081_subparsers``). The
3169
+ public contract of this function is unchanged — callers get back
3170
+ an ``argparse.ArgumentParser`` with ``cmd`` as the required
3171
+ subcommand.
3172
+ """
3173
+ shared = _build_shared_parser()
3174
+
3175
+ parser = argparse.ArgumentParser(
3176
+ prog="audit-query.py",
3177
+ parents=[shared],
3178
+ description="Query the ceo-orchestration agent spawn audit log",
3179
+ formatter_class=argparse.RawDescriptionHelpFormatter,
3180
+ epilog=(
3181
+ "Examples:\n"
3182
+ " audit-query.py summary\n"
3183
+ " audit-query.py by-skill --top 10\n"
3184
+ " audit-query.py compliance --json\n"
3185
+ " audit-query.py search 'security' --include-rotated\n"
3186
+ " audit-query.py by-day --days 7\n"
3187
+ " audit-query.py since 2026-04-01\n"
3188
+ " audit-query.py export --format csv > spawns.csv\n"
3189
+ " audit-query.py stats --json\n"
3190
+ " audit-query.py errors\n"
3191
+ " audit-query.py by-domain --window 30d\n"
3192
+ " audit-query.py by-domain --start 2026-04-01 --end 2026-05-01\n"
3193
+ " audit-query.py by-domain --window 30d --check-reopen\n"
3194
+ " audit-query.py critical\n"
3195
+ " audit-query.py critical --action kill_switch_invoked --json\n"
3196
+ ),
3197
+ )
3198
+
3199
+ sub = parser.add_subparsers(dest="cmd", required=True)
3200
+ _add_v1_subparsers(sub, shared)
3201
+ _add_v2_subparsers(sub, shared)
3202
+ _add_sprint8_9_subparsers(sub, shared)
3203
+ _add_plan015_subparsers(sub, shared)
3204
+ _add_plan080_subparsers(sub, shared)
3205
+ _add_plan081_subparsers(sub, shared)
3206
+ _add_plan113_subparsers(sub, shared)
3207
+
3208
+ return parser
3209
+
3210
+
3211
+ # ---------------------------------------------------------------------------
3212
+ # main
3213
+ # ---------------------------------------------------------------------------
3214
+
3215
+
3216
+ def main(argv: Optional[List[str]] = None) -> int:
3217
+ """CLI entrypoint — dispatch audit-query sub-commands."""
3218
+ parser = build_parser()
3219
+ args = parser.parse_args(argv)
3220
+
3221
+ # `errors` subcommand reads a different file, doesn't need the jsonl
3222
+ if args.cmd == "errors":
3223
+ result = cmd_errors(args)
3224
+ print(render(result, as_json=args.as_json, as_csv=args.as_csv))
3225
+ return 0
3226
+
3227
+ log_path = Path(args.log) if args.log else default_log_path()
3228
+ log_files = discover_logs(log_path, args.include_rotated)
3229
+
3230
+ # Perf-P1-002 — streamable subcommands consume the iterator directly so
3231
+ # a 100k-row log does not sit in RAM as a Python list. Non-streamable
3232
+ # subcommands (median / percentile / cross-reference) still materialize
3233
+ # but emit a stderr hint when the log crosses the warn threshold.
3234
+ _STREAMABLE_CMDS = frozenset({
3235
+ "summary", "by-skill", "by-day", "search", "since",
3236
+ })
3237
+
3238
+ if not log_files:
3239
+ entries_iter: Iterable[Dict[str, Any]] = iter(())
3240
+ entries: List[Dict[str, Any]] = []
3241
+ elif args.cmd in _STREAMABLE_CMDS:
3242
+ entries_iter = read_entries(log_files)
3243
+ entries = [] # sentinel — not used on streaming path
3244
+ else:
3245
+ entries = list(read_entries(log_files))
3246
+ entries_iter = entries
3247
+ if len(entries) >= _MATERIALIZATION_WARN_THRESHOLD:
3248
+ print(
3249
+ "[audit-query] NOTE: loaded {n} entries into RAM for "
3250
+ "subcommand {cmd!r} (threshold {thr}). Consider rotating "
3251
+ "older logs or running a streamable subcommand.".format(
3252
+ n=len(entries),
3253
+ cmd=args.cmd,
3254
+ thr=_MATERIALIZATION_WARN_THRESHOLD,
3255
+ ),
3256
+ file=sys.stderr,
3257
+ )
3258
+
3259
+ if args.cmd == "summary":
3260
+ result = cmd_summary(entries_iter, args)
3261
+ elif args.cmd == "by-skill":
3262
+ result = cmd_by_skill(entries_iter, args)
3263
+ elif args.cmd == "compliance":
3264
+ result = cmd_compliance(entries, args)
3265
+ elif args.cmd == "by-day":
3266
+ result = cmd_by_day(entries_iter, args)
3267
+ elif args.cmd == "search":
3268
+ result = cmd_search(entries_iter, args)
3269
+ elif args.cmd == "since":
3270
+ result = cmd_since(entries_iter, args)
3271
+ elif args.cmd == "stats":
3272
+ result = cmd_stats(entries, args)
3273
+ elif args.cmd == "export":
3274
+ result = cmd_export(entries, args)
3275
+ # Export handles its own formatting
3276
+ if args.export_format in ("csv", "tsv"):
3277
+ sys.stdout.write(result)
3278
+ return 0
3279
+ print(json.dumps(result, indent=2, ensure_ascii=True))
3280
+ return 0
3281
+ # Sprint 5 A.2 — 7 new sub-commands
3282
+ elif args.cmd == "debate":
3283
+ result = cmd_debate(entries, args)
3284
+ elif args.cmd == "plans":
3285
+ result = cmd_plans(entries, args)
3286
+ elif args.cmd == "vetoes":
3287
+ result = cmd_vetoes(entries, args)
3288
+ elif args.cmd == "benchmarks":
3289
+ result = cmd_benchmarks(entries, args)
3290
+ elif args.cmd == "lessons":
3291
+ result = cmd_lessons(entries, args)
3292
+ elif args.cmd == "metrics":
3293
+ result = cmd_metrics(entries, args)
3294
+ elif args.cmd == "health":
3295
+ result = cmd_health(entries, args)
3296
+ elif args.cmd == "tokens":
3297
+ result = cmd_tokens(entries, args)
3298
+ elif args.cmd == "claims":
3299
+ result = cmd_claims(entries, args)
3300
+ elif args.cmd == "prune-restore-ratio":
3301
+ result = cmd_prune_restore_ratio(entries, args)
3302
+ elif args.cmd == "architect-outcomes":
3303
+ result = cmd_architect_outcomes(entries, args)
3304
+ elif args.cmd == "lessons-effectiveness":
3305
+ result = cmd_lessons_effectiveness(entries, args)
3306
+ elif args.cmd == "weekly-summary":
3307
+ result = cmd_weekly_summary(entries, args)
3308
+ elif args.cmd == "spawn-stats":
3309
+ result = cmd_spawn_stats(entries, args)
3310
+ # PLAN-080 Phase 1
3311
+ elif args.cmd == "by-domain":
3312
+ result = cmd_by_domain(entries, args)
3313
+ # PLAN-081 Phase 6-bis
3314
+ elif args.cmd == "label":
3315
+ result = cmd_label(entries, args)
3316
+ elif args.cmd == "fp-rate":
3317
+ result = cmd_fp_rate(entries, args)
3318
+ elif args.cmd == "case-summary":
3319
+ result = cmd_case_summary(entries, args)
3320
+ elif args.cmd == "codex-writeguard-summary":
3321
+ result = cmd_codex_writeguard_summary(entries, args)
3322
+ # PLAN-113 Phase B Wave W1 — critical-security-action reader
3323
+ elif args.cmd == "critical":
3324
+ result = cmd_critical(entries, args)
3325
+ else: # pragma: no cover — argparse prevents this
3326
+ parser.error(f"unknown cmd: {args.cmd}")
3327
+
3328
+ print(render(result, as_json=args.as_json, as_csv=args.as_csv))
3329
+ return 0
3330
+
3331
+
3332
+ if __name__ == "__main__":
3333
+ sys.exit(main())