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,2218 @@
1
+ """PLAN-099 Wave A.1 + Wave B + PLAN-099-FOLLOWUP Wave D/E — stdlib federation server.
2
+
3
+ This module ships ``FederationServer``, a thin wrapper around
4
+ :class:`http.server.ThreadingHTTPServer` with:
5
+
6
+ - TLSv1.3 minimum (:class:`ssl.SSLContext` ``minimum_version``)
7
+ - mTLS mandatory (``CERT_REQUIRED``; server-side ``check_hostname=False``
8
+ by design — stdlib SSLContext does not validate incoming-client SNI
9
+ via this flag, so leaving it True would only add a confusing
10
+ no-op. Client-side context (``federation.client``) DOES enable
11
+ ``check_hostname=True`` for outbound peer-cert SAN validation.)
12
+ - Loopback-default bind; non-loopback bind gated by Owner-GPG LAN sentinel
13
+ - Mechanical HTTP method allowlist (GET always; POST ONLY when write-mode
14
+ is active per the Layer 0a env + Layer 0b sentinel default-OFF chain —
15
+ PLAN-112-FOLLOWUP-federation-wire-or-delete W1)
16
+ - 3 read-only endpoints: ``/federation/identity``, ``/federation/status``,
17
+ ``/federation/audit-summary`` (Wave B)
18
+ - 4 write endpoints behind the 11-gate dispatcher (ADR-135-AMEND-1 §2.2;
19
+ Wave D) — default-OFF.
20
+ - HMAC+nonce+timestamp replay protection (Wave A — AC13)
21
+ - Joint-key rate limit on /audit-summary (AC17) + per-route token-bucket +
22
+ circuit-breaker + backpressure on writes (Wave E)
23
+ - Peer-list reload-watcher so a ``peer_revoke`` propagates to the running
24
+ server in <60s without restart (PLAN-112-FOLLOWUP W3, P0-1).
25
+ - All emit paths gated through :mod:`audit_emit` with kernel-override-
26
+ registered ``federation_*`` actions; the ``_safe_emit`` shim now falls
27
+ back to ``emit_generic`` so the Wave-F.2 actions (which have NO named
28
+ ``emit_*`` wrapper) are actually written (PLAN-112-FOLLOWUP C-4 fix —
29
+ closes the R-TD-1 no-op trap).
30
+
31
+ Stdlib-only per ADR-126 §Part 6 (no ``cryptography`` package).
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import collections
37
+ import datetime as _dt
38
+ import hashlib
39
+ import http.server
40
+ import ipaddress
41
+ import json
42
+ import os
43
+ import re
44
+ import socket
45
+ import ssl
46
+ import sys
47
+ import threading
48
+ import time
49
+ from pathlib import Path
50
+ from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple
51
+
52
+
53
+ def _read_max_cert_validity_days_from_init(*, default: int) -> int:
54
+ """Single-source-of-truth read of ``MAX_CERT_VALIDITY_DAYS``.
55
+
56
+ PLAN-113 W5 (F-5.8-mac-cert-validity-duplicate). The package
57
+ ``__init__.py`` owns the literal value. The package-relative
58
+ ``from . import MAX_CERT_VALIDITY_DAYS`` (in the try-block below)
59
+ is the normal path; this helper covers ONLY the flat-import
60
+ (test / draft) fallback where there is no ``federation`` package
61
+ namespace to import from. Rather than re-declare the literal here
62
+ (which can drift), parse it out of the sibling ``__init__.py`` by
63
+ path so the number lives in exactly one place.
64
+
65
+ Fail-soft: any read/parse failure returns ``default`` (the historical
66
+ literal). server.py and __init__.py ship in the same directory, so
67
+ a miss is never expected in a real install.
68
+ """
69
+ try:
70
+ init_path = Path(__file__).resolve().parent / "__init__.py"
71
+ text = init_path.read_text(encoding="utf-8")
72
+ except (OSError, ValueError):
73
+ return default
74
+ # Match a top-level `MAX_CERT_VALIDITY_DAYS = <int>` assignment.
75
+ m = re.search(
76
+ r"^MAX_CERT_VALIDITY_DAYS\s*=\s*(\d+)\s*$",
77
+ text,
78
+ re.MULTILINE,
79
+ )
80
+ if m is None:
81
+ return default
82
+ try:
83
+ return int(m.group(1))
84
+ except ValueError: # pragma: no cover — regex guarantees digits
85
+ return default
86
+
87
+
88
+ # Package-internal helpers — imports resolve via the canonical package
89
+ # path .claude/hooks/_lib/federation/ once the patcher installs the
90
+ # files. During unit tests the draft layout in .claude/plans/PLAN-099/
91
+ # is loaded directly.
92
+ try:
93
+ from .identity import (
94
+ PeerRecord,
95
+ PeersFileError,
96
+ compute_cert_fingerprint,
97
+ compare_fingerprints,
98
+ load_peers,
99
+ lookup_peer_by_fingerprint,
100
+ verify_enable_sentinel_pair,
101
+ # Wave C (PLAN-099-FOLLOWUP) — SPKI dispatcher primitives.
102
+ compute_spki_fingerprint,
103
+ compute_der_fingerprint_from_pem,
104
+ select_pin_for_peer,
105
+ PinSelectionError,
106
+ # Wave C P0 F-003 — specific subclass for no-pin parse-time
107
+ # invariant violations (routed to the federation_peer_invalid_no_fingerprint
108
+ # emit in _load_peers_or_raise).
109
+ PeerHasNoFingerprintError,
110
+ )
111
+ from .replay import ReplayCache, ReplayDecision, parse_rfc3339_utc
112
+ from .audit_chain import (
113
+ CORRELATION_ID_HEADER,
114
+ stamp_local_with_correlation,
115
+ )
116
+ # Wave D (PLAN-099-FOLLOWUP) — RBAC matrix + write-endpoint handlers.
117
+ from . import scopes as _fed_scopes
118
+ from .handlers import (
119
+ peer_register as _h_peer_register,
120
+ audit_event_push as _h_audit_event_push,
121
+ audit_event_batch as _h_audit_event_batch,
122
+ peer_revoke as _h_peer_revoke,
123
+ )
124
+ from . import (
125
+ AUDIT_SUMMARY_RATE_PER_MIN,
126
+ ALLOWED_HTTP_METHODS,
127
+ CERT_EXPIRY_WARN_DAYS,
128
+ DEFAULT_BIND,
129
+ DEFAULT_PORT,
130
+ FEDERATION_KILL_SWITCH_ENV,
131
+ HANDSHAKE_TIMEOUT_SECONDS,
132
+ MAX_CERT_VALIDITY_DAYS,
133
+ MAX_CLOCK_SKEW_SECONDS,
134
+ OWNER_GPG_FPR,
135
+ FEDERATION_WRITE_KILL_SWITCH_ENV,
136
+ WRITE_ALLOWED_HTTP_METHODS,
137
+ PEER_RELOAD_MIN_INTERVAL_SECONDS,
138
+ )
139
+ except ImportError:
140
+ # Test / draft mode — flat-import fallback.
141
+ from identity import ( # type: ignore[no-redef]
142
+ PeerRecord,
143
+ PeersFileError,
144
+ compute_cert_fingerprint,
145
+ compare_fingerprints,
146
+ load_peers,
147
+ lookup_peer_by_fingerprint,
148
+ verify_enable_sentinel_pair,
149
+ # Wave C (PLAN-099-FOLLOWUP) — SPKI dispatcher primitives.
150
+ compute_spki_fingerprint,
151
+ compute_der_fingerprint_from_pem,
152
+ select_pin_for_peer,
153
+ PinSelectionError,
154
+ PeerHasNoFingerprintError,
155
+ )
156
+ from replay import ReplayCache, ReplayDecision, parse_rfc3339_utc # type: ignore[no-redef]
157
+ from audit_chain import ( # type: ignore[no-redef]
158
+ CORRELATION_ID_HEADER,
159
+ stamp_local_with_correlation,
160
+ )
161
+ try:
162
+ import scopes as _fed_scopes # type: ignore[no-redef]
163
+ from handlers import ( # type: ignore[no-redef]
164
+ peer_register as _h_peer_register,
165
+ audit_event_push as _h_audit_event_push,
166
+ audit_event_batch as _h_audit_event_batch,
167
+ peer_revoke as _h_peer_revoke,
168
+ )
169
+ except ImportError:
170
+ _fed_scopes = None # type: ignore[assignment]
171
+ _h_peer_register = None # type: ignore[assignment]
172
+ _h_audit_event_push = None # type: ignore[assignment]
173
+ _h_audit_event_batch = None # type: ignore[assignment]
174
+ _h_peer_revoke = None # type: ignore[assignment]
175
+ AUDIT_SUMMARY_RATE_PER_MIN = 10
176
+ ALLOWED_HTTP_METHODS = frozenset(("GET",))
177
+ CERT_EXPIRY_WARN_DAYS = 14
178
+ DEFAULT_BIND = "127.0.0.1"
179
+ DEFAULT_PORT = 8843
180
+ FEDERATION_KILL_SWITCH_ENV = "CEO_FEDERATION_ENABLED"
181
+ HANDSHAKE_TIMEOUT_SECONDS = 5.0
182
+ # PLAN-113 W5 — F-5.8-mac-cert-validity-duplicate. The package
183
+ # __init__.py is the SINGLE SOURCE OF TRUTH for MAX_CERT_VALIDITY_DAYS.
184
+ # In flat-import (test/draft) mode the `from . import ...` above raised
185
+ # ImportError, so we cannot use a package-relative import here. Instead
186
+ # of redefining the literal (drift risk), source the value out of the
187
+ # sibling __init__.py by path. The literal `90` then lives in exactly
188
+ # ONE place. Fall back to the historical literal ONLY if the sibling
189
+ # file is unreadable (never expected — server.py and __init__.py ship
190
+ # in the same package directory).
191
+ MAX_CERT_VALIDITY_DAYS = _read_max_cert_validity_days_from_init(default=90)
192
+ MAX_CLOCK_SKEW_SECONDS = 30
193
+ OWNER_GPG_FPR = "0000000000000000000000000000000000000000"
194
+ FEDERATION_WRITE_KILL_SWITCH_ENV = "CEO_FEDERATION_WRITE_ENABLED"
195
+ WRITE_ALLOWED_HTTP_METHODS = frozenset(("GET", "POST"))
196
+ PEER_RELOAD_MIN_INTERVAL_SECONDS = 1.0
197
+
198
+
199
+ # Wave E modules — lazy-loaded inside the dispatcher to keep module
200
+ # import cheap and tolerant of partial installs.
201
+ # (rate_limit + audit_chain_ext resolved via _load_rate_limit / _load_audit_chain_ext)
202
+
203
+
204
+ __all__ = [
205
+ "FederationConfig",
206
+ "FederationServer",
207
+ "FederationStartError",
208
+ "build_ssl_context",
209
+ "resolve_bind_is_loopback",
210
+ "write_mode_enabled_from_env",
211
+ ]
212
+
213
+
214
+ # ---------------------------------------------------------------------------
215
+ # Exceptions
216
+ # ---------------------------------------------------------------------------
217
+
218
+
219
+ class FederationStartError(RuntimeError):
220
+ """Raised by :meth:`FederationServer.serve_forever` when any of the
221
+ enable invariants fail. The audit-emit side-effect happens
222
+ BEFORE the raise; the exception is the operator-facing surface."""
223
+
224
+
225
+ # ---------------------------------------------------------------------------
226
+ # Audit emit shims (hasattr-guarded to work pre + post canonical ceremony)
227
+ # ---------------------------------------------------------------------------
228
+
229
+
230
+ def _safe_emit(action: str, **fields: Any) -> None:
231
+ """Call ``audit_emit.emit_<action>(...)`` if registered; else fall
232
+ back to ``audit_emit.emit_generic(action, ...)``; else no-op.
233
+
234
+ PLAN-112-FOLLOWUP-federation-wire-or-delete C-4 fix (closes R-TD-1):
235
+ the Wave-F.2 federation actions are present in ``_KNOWN_ACTIONS`` but
236
+ have NO named ``emit_<action>`` wrapper. The previous shim only tried
237
+ the named wrapper, so EVERY Wave-D/E federation emit silently no-oped
238
+ (dead detection that passes green). We now fall back to ``emit_generic``
239
+ which validates against ``_KNOWN_ACTIONS`` and writes through the same
240
+ filelock + HMAC chain. Unknown actions still no-op (emit_generic
241
+ breadcrumbs + returns), preserving the fail-open-on-infra contract.
242
+ """
243
+ try:
244
+ try:
245
+ from _lib import audit_emit # type: ignore[import]
246
+ except ImportError:
247
+ import importlib
248
+ audit_emit = importlib.import_module(".audit_emit", package="_lib")
249
+ except ImportError:
250
+ return
251
+ fn_name = "emit_{0}".format(action)
252
+ fn = getattr(audit_emit, fn_name, None)
253
+ try:
254
+ if fn is not None:
255
+ fn(**fields)
256
+ return
257
+ generic = getattr(audit_emit, "emit_generic", None)
258
+ if generic is not None:
259
+ generic(action, **fields)
260
+ except Exception:
261
+ # Audit-emit MUST NEVER block the server. Swallow + breadcrumb.
262
+ try:
263
+ sys.stderr.write(
264
+ "[federation.server] audit emit '{0}' raised; ignored\n".format(
265
+ action
266
+ )
267
+ )
268
+ except Exception:
269
+ pass
270
+
271
+
272
+ def write_mode_enabled_from_env() -> bool:
273
+ """Layer 0a — master write-mode env switch (fail-CLOSED default-OFF).
274
+
275
+ PLAN-112-FOLLOWUP W1 (AC1/AC14). The ONLY accepted truthy value is the
276
+ exact string ``"1"``. Unset / empty / ``"0"`` / ``"true"`` / any other
277
+ value → write-mode OFF. This is deliberately STRICTER than the read-mode
278
+ kill-switch (which accepts ``1/true/TRUE``) — write activation is a
279
+ higher-blast-radius operation and must not be flipped by an ambient
280
+ ``true`` left in a profile.
281
+
282
+ Returns False on any error (fail-CLOSED).
283
+ """
284
+ try:
285
+ v = os.environ.get(FEDERATION_WRITE_KILL_SWITCH_ENV, "")
286
+ return v.strip() == "1"
287
+ except Exception:
288
+ return False
289
+
290
+
291
+ # ---------------------------------------------------------------------------
292
+ # Lazy Wave-E loaders
293
+ # ---------------------------------------------------------------------------
294
+
295
+
296
+ def _load_rate_limit():
297
+ """Return the rate_limit module or None (partial-install tolerant)."""
298
+ try:
299
+ try:
300
+ from . import rate_limit as _rl # type: ignore
301
+ except ImportError:
302
+ import importlib
303
+ _rl = importlib.import_module(
304
+ "_lib.federation.rate_limit"
305
+ )
306
+ return _rl
307
+ except Exception:
308
+ return None
309
+
310
+
311
+ def _load_audit_chain_ext():
312
+ """Return the audit_chain_ext module or None."""
313
+ try:
314
+ try:
315
+ from . import audit_chain_ext as _ace # type: ignore
316
+ except ImportError:
317
+ import importlib
318
+ _ace = importlib.import_module(
319
+ "_lib.federation.audit_chain_ext"
320
+ )
321
+ return _ace
322
+ except Exception:
323
+ return None
324
+
325
+
326
+ # ---------------------------------------------------------------------------
327
+ # Configuration
328
+ # ---------------------------------------------------------------------------
329
+
330
+
331
+ class FederationConfig:
332
+ """Resolved server configuration (immutable after construction).
333
+
334
+ Fields
335
+ ------
336
+ bind_host
337
+ Host string (IP literal or hostname). Hostnames are resolved on
338
+ bind to check loopback.
339
+ bind_port
340
+ TCP port (1..65535).
341
+ cert_file
342
+ Server cert (PEM).
343
+ key_file
344
+ Server private key (PEM).
345
+ ca_file
346
+ Bundle of trusted client-CA certs (PEM).
347
+ peers_path
348
+ Path to ``peers.yaml`` (allowlist for client cert fingerprints).
349
+ enabled_sentinel
350
+ Path to ``.claude/data/federation/enabled.md`` (signed cleartext).
351
+ enabled_sentinel_asc
352
+ Path to ``enabled.md.asc`` (detached signature).
353
+ lan_enabled_sentinel
354
+ Path to ``lan-enabled.md`` (LAN-bind additional pair).
355
+ lan_enabled_sentinel_asc
356
+ Path to ``lan-enabled.md.asc``.
357
+ signer_registry_path
358
+ Path to ``.claude/security/sentinel-signers-registry.yaml`` for
359
+ Stage-2 signer expiry / revocation check.
360
+ write_enabled_sentinel
361
+ Path to ``.claude/data/federation/write-enabled.md`` (Layer 0b /
362
+ Gate #8 third sentinel pair — PLAN-112-FOLLOWUP). Optional; when
363
+ None, defaults are resolved at runtime.
364
+ write_enabled_sentinel_asc
365
+ Detached signature for the write-enable pair.
366
+ federation_sentinels_dir
367
+ Directory hosting per-request Owner-co-sign sentinels (Gate #10).
368
+ audit_log_path
369
+ Path to the local ``audit-log.jsonl`` that the audit-event write
370
+ handlers append to AND the post-handler T1565 tamper check
371
+ inspects (PLAN-112-FOLLOWUP P0 #1 — Codex BLOCK). MUST be the SAME
372
+ path for both or the tamper check is a no-op. Optional; when None,
373
+ the handlers' own ``CEO_AUDIT_LOG_PATH``/platform-default resolution
374
+ is used AND the dispatcher resolves the identical path so the
375
+ check inspects the log just appended.
376
+ """
377
+
378
+ __slots__ = (
379
+ "bind_host", "bind_port", "cert_file", "key_file", "ca_file",
380
+ "peers_path", "enabled_sentinel", "enabled_sentinel_asc",
381
+ "lan_enabled_sentinel", "lan_enabled_sentinel_asc",
382
+ "signer_registry_path",
383
+ # PLAN-112-FOLLOWUP W1/W2 — write-mode activation surfaces.
384
+ "write_enabled_sentinel", "write_enabled_sentinel_asc",
385
+ "federation_sentinels_dir",
386
+ # PLAN-112-FOLLOWUP P0 #1 — one canonical audit-log path.
387
+ "audit_log_path",
388
+ )
389
+
390
+ def __init__(
391
+ self,
392
+ bind_host: str,
393
+ bind_port: int,
394
+ cert_file: Path,
395
+ key_file: Path,
396
+ ca_file: Path,
397
+ peers_path: Path,
398
+ enabled_sentinel: Path,
399
+ enabled_sentinel_asc: Path,
400
+ lan_enabled_sentinel: Path,
401
+ lan_enabled_sentinel_asc: Path,
402
+ signer_registry_path: Optional[Path] = None,
403
+ write_enabled_sentinel: Optional[Path] = None,
404
+ write_enabled_sentinel_asc: Optional[Path] = None,
405
+ federation_sentinels_dir: Optional[Path] = None,
406
+ audit_log_path: Optional[Path] = None,
407
+ ) -> None:
408
+ self.bind_host = bind_host
409
+ self.bind_port = int(bind_port)
410
+ self.cert_file = cert_file
411
+ self.key_file = key_file
412
+ self.ca_file = ca_file
413
+ self.peers_path = peers_path
414
+ self.enabled_sentinel = enabled_sentinel
415
+ self.enabled_sentinel_asc = enabled_sentinel_asc
416
+ self.lan_enabled_sentinel = lan_enabled_sentinel
417
+ self.lan_enabled_sentinel_asc = lan_enabled_sentinel_asc
418
+ self.signer_registry_path = signer_registry_path
419
+ # Default-resolve write-mode sentinel paths relative to peers_path
420
+ # parent so a single data-dir hosts all three sentinel pairs.
421
+ data_dir = Path(peers_path).parent if peers_path else Path(
422
+ ".claude/data/federation"
423
+ )
424
+ self.write_enabled_sentinel = (
425
+ write_enabled_sentinel
426
+ if write_enabled_sentinel is not None
427
+ else data_dir / "write-enabled.md"
428
+ )
429
+ self.write_enabled_sentinel_asc = (
430
+ write_enabled_sentinel_asc
431
+ if write_enabled_sentinel_asc is not None
432
+ else Path(str(self.write_enabled_sentinel) + ".asc")
433
+ )
434
+ self.federation_sentinels_dir = (
435
+ federation_sentinels_dir
436
+ if federation_sentinels_dir is not None
437
+ else data_dir / "sentinels"
438
+ )
439
+ # PLAN-112-FOLLOWUP P0 #1 — one canonical audit-log path. None →
440
+ # the dispatcher resolves the SAME path the audit handlers use
441
+ # (CEO_AUDIT_LOG_PATH or platform default) at serve_forever time.
442
+ self.audit_log_path = (
443
+ Path(audit_log_path) if audit_log_path is not None else None
444
+ )
445
+
446
+
447
+ # ---------------------------------------------------------------------------
448
+ # Bind-loopback resolver (AC3 — covers ALL non-loopback shapes)
449
+ # ---------------------------------------------------------------------------
450
+
451
+
452
+ def resolve_bind_is_loopback(bind_host: str) -> Tuple[bool, str]:
453
+ """Return ``(is_loopback, resolved_ip_str)`` for a bind host.
454
+
455
+ Resolves hostnames via :func:`socket.gethostbyname_ex` and tests
456
+ EACH resolved address against :func:`ipaddress.ip_address.is_loopback`.
457
+ A host with any non-loopback address counts as non-loopback (the
458
+ server cannot bind to "loopback-only" without an explicit literal).
459
+ """
460
+ raw = (bind_host or "").strip()
461
+ if not raw:
462
+ return False, ""
463
+
464
+ # Try literal IP first (covers IPv4 + IPv6 + "::").
465
+ try:
466
+ addr = ipaddress.ip_address(raw)
467
+ # `unspecified` (0.0.0.0 / ::) is intentionally NOT loopback.
468
+ return bool(addr.is_loopback), str(addr)
469
+ except ValueError:
470
+ pass
471
+
472
+ # Hostname — resolve and check every A/AAAA.
473
+ try:
474
+ _, _, ips = socket.gethostbyname_ex(raw)
475
+ except (socket.gaierror, OSError):
476
+ return False, ""
477
+
478
+ all_loopback = True
479
+ first = ""
480
+ for ip in ips:
481
+ if not first:
482
+ first = ip
483
+ try:
484
+ addr = ipaddress.ip_address(ip)
485
+ except ValueError:
486
+ all_loopback = False
487
+ continue
488
+ if not addr.is_loopback:
489
+ all_loopback = False
490
+ return all_loopback, first
491
+
492
+
493
+ # ---------------------------------------------------------------------------
494
+ # IPv6-aware threaded HTTPS server (AC3)
495
+ # ---------------------------------------------------------------------------
496
+
497
+
498
+ class _ThreadingHTTPSServer(http.server.ThreadingHTTPServer):
499
+ """ThreadingHTTPServer with dual-stack support."""
500
+
501
+ address_family = socket.AF_INET # default; overridden at instantiate
502
+
503
+ def __init__(self, server_address, RequestHandlerClass, address_family=None):
504
+ if address_family is not None:
505
+ self.address_family = address_family
506
+ super().__init__(server_address, RequestHandlerClass)
507
+
508
+
509
+ # ---------------------------------------------------------------------------
510
+ # SSLContext builder (AC1 / AC2)
511
+ # ---------------------------------------------------------------------------
512
+
513
+
514
+ def build_ssl_context(
515
+ cert_file: Path,
516
+ key_file: Path,
517
+ ca_file: Path,
518
+ ) -> ssl.SSLContext:
519
+ """Construct a TLSv1.3-min mTLS SSLContext for the server side."""
520
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
521
+ ctx.minimum_version = ssl.TLSVersion.TLSv1_3
522
+ ctx.verify_mode = ssl.CERT_REQUIRED
523
+ ctx.check_hostname = False # Server side: client SNI not validated by us
524
+ ctx.load_cert_chain(certfile=str(cert_file), keyfile=str(key_file))
525
+ ctx.load_verify_locations(cafile=str(ca_file))
526
+ return ctx
527
+
528
+
529
+ # ---------------------------------------------------------------------------
530
+ # Rate limiter — joint key (peer_id_cert_fingerprint, ip) — for /audit-summary
531
+ # ---------------------------------------------------------------------------
532
+
533
+
534
+ class JointKeyRateLimiter:
535
+ """Bucket-by-minute joint-key rate limiter (AC17 — read path)."""
536
+
537
+ def __init__(self, per_minute: int) -> None:
538
+ self.per_minute = int(per_minute)
539
+ self._buckets: Dict[Tuple[str, str, int], int] = {}
540
+ self._lock = threading.Lock()
541
+
542
+ def allow(
543
+ self,
544
+ peer_fpr: str,
545
+ client_ip: str,
546
+ now_epoch: Optional[float] = None,
547
+ ) -> bool:
548
+ now = now_epoch if now_epoch is not None else time.time()
549
+ bucket = int(now // 60)
550
+ key = (peer_fpr.lower(), client_ip, bucket)
551
+ with self._lock:
552
+ self._prune_locked(bucket)
553
+ current = self._buckets.get(key, 0)
554
+ if current >= self.per_minute:
555
+ return False
556
+ self._buckets[key] = current + 1
557
+ return True
558
+
559
+ def _prune_locked(self, current_bucket: int) -> None:
560
+ cutoff = current_bucket - 2
561
+ stale = [k for k in self._buckets if k[2] < cutoff]
562
+ for k in stale:
563
+ del self._buckets[k]
564
+
565
+
566
+ # ---------------------------------------------------------------------------
567
+ # Peer-list reload-watcher (PLAN-112-FOLLOWUP W3 — P0-1 revocation <60s)
568
+ # ---------------------------------------------------------------------------
569
+
570
+
571
+ def _peers_file_signature(peers_path: Path) -> Tuple[float, int, str]:
572
+ """Return a cheap (mtime, size, sha256-hex) signature of peers.yaml.
573
+
574
+ Used by the reload-watcher to decide whether a reload is warranted
575
+ WITHOUT re-parsing on every request. The sha256 is only computed when
576
+ (mtime, size) changed — see :func:`_maybe_reload_peers`.
577
+ """
578
+ try:
579
+ st = peers_path.stat()
580
+ except OSError:
581
+ return (0.0, -1, "")
582
+ try:
583
+ data = peers_path.read_bytes()
584
+ digest = hashlib.sha256(data).hexdigest()
585
+ except OSError:
586
+ digest = ""
587
+ return (st.st_mtime, st.st_size, digest)
588
+
589
+
590
+ def _reload_peers_now(httpd: Any) -> bool:
591
+ """Re-parse peers.yaml + refresh ``httpd.federation_peers`` +
592
+ re-run cert-level revocation. Returns True on a successful refresh.
593
+
594
+ PLAN-112-FOLLOWUP W3 / R-IT-A: this re-evaluates cert-level revocation
595
+ (expiry → ``federation_cert_revoked``) on every reload, NOT just the
596
+ ``peer.revoked`` boolean — so a CERT revocation (expiry past) also
597
+ propagates without restart. R-IT-C: every reload emits
598
+ ``federation_peer_list_reloaded`` so the SLO is forensically
599
+ observable.
600
+
601
+ Fail-CLOSED note: on a parse error we DO NOT wipe the existing peer
602
+ set (that would open the federation to all-peers-denied DoS on a
603
+ transient bad write) — we keep the last-good set + emit a rejected
604
+ breadcrumb. A peer_revoke writes peers.yaml atomically (tmpfile +
605
+ rename) so a torn read is not expected; the keep-last-good policy is
606
+ purely defense-in-depth.
607
+ """
608
+ cfg = getattr(httpd, "federation_config", None)
609
+ peers_path = getattr(cfg, "peers_path", None) if cfg else None
610
+ if peers_path is None:
611
+ peers_path = getattr(httpd, "federation_peers_path", None)
612
+ if peers_path is None:
613
+ return False
614
+ peers_path = Path(peers_path)
615
+
616
+ now_dt = _dt.datetime.now(_dt.timezone.utc)
617
+ try:
618
+ peers = load_peers(peers_path)
619
+ except (FileNotFoundError, PeersFileError, PeerHasNoFingerprintError) as exc:
620
+ _safe_emit(
621
+ "federation_peer_list_reloaded",
622
+ peer_count=len(getattr(httpd, "federation_peers", {}) or {}),
623
+ reload_reason="parse_error_kept_last_good",
624
+ source_path=str(peers_path)[:128],
625
+ )
626
+ try:
627
+ sys.stderr.write(
628
+ "[federation.reload] peers.yaml reload failed ({0}); "
629
+ "kept last-good set\n".format(type(exc).__name__)
630
+ )
631
+ except Exception:
632
+ pass
633
+ return False
634
+
635
+ # R-IT-A — re-run cert-level revocation on reload (mirror
636
+ # _load_peers_or_raise's expiry handling).
637
+ for peer in peers.values():
638
+ try:
639
+ days_left = (peer.not_valid_after - now_dt).days
640
+ except Exception:
641
+ continue
642
+ if days_left < 0:
643
+ _safe_emit(
644
+ "federation_cert_revoked",
645
+ peer_id=peer.peer_id[:64],
646
+ reason="expired",
647
+ )
648
+
649
+ httpd.federation_peers = peers # type: ignore[attr-defined]
650
+ httpd.federation_peers_extra = { # type: ignore[attr-defined]
651
+ peer.peer_id: {
652
+ "peer_id_spki_fingerprint": peer.peer_id_spki_fingerprint,
653
+ "peer_id_cert_fingerprint": peer.peer_id_cert_fingerprint,
654
+ "scopes": list(getattr(peer, "scopes", []) or []),
655
+ "audit_event_push_allowlist": list(
656
+ getattr(peer, "audit_event_push_allowlist", []) or []
657
+ ),
658
+ "revoked": bool(peer.revoked),
659
+ }
660
+ for peer in peers.values()
661
+ }
662
+ _safe_emit(
663
+ "federation_peer_list_reloaded",
664
+ peer_count=len(peers),
665
+ reload_reason="content_changed",
666
+ source_path=str(peers_path)[:128],
667
+ )
668
+ return True
669
+
670
+
671
+ def _maybe_reload_peers(httpd: Any, *, now: Optional[float] = None) -> None:
672
+ """Reload peers.yaml IF it changed since the last check.
673
+
674
+ R-IT-C: kill-switch + peer-list are NOT boot-cached — this runs at the
675
+ TOP of every write dispatch (and could be wired into the read path)
676
+ so the <60s SLO is bounded by the request cadence, not a poll thread.
677
+ A poll thread is ALSO started in ``serve_forever`` (see
678
+ :meth:`FederationServer._start_reload_thread`) so the SLO holds even
679
+ with zero traffic.
680
+
681
+ Debounced by ``PEER_RELOAD_MIN_INTERVAL_SECONDS`` to avoid hashing the
682
+ file on every single request under load.
683
+ """
684
+ ts = time.time() if now is None else float(now)
685
+ lock = getattr(httpd, "federation_reload_lock", None)
686
+ if lock is None:
687
+ return
688
+ cfg = getattr(httpd, "federation_config", None)
689
+ peers_path = getattr(cfg, "peers_path", None) if cfg else None
690
+ if peers_path is None:
691
+ return
692
+ peers_path = Path(peers_path)
693
+ with lock:
694
+ last_check = getattr(httpd, "federation_peers_last_check", 0.0)
695
+ if (ts - last_check) < PEER_RELOAD_MIN_INTERVAL_SECONDS:
696
+ return
697
+ httpd.federation_peers_last_check = ts # type: ignore[attr-defined]
698
+ sig = _peers_file_signature(peers_path)
699
+ prev_sig = getattr(httpd, "federation_peers_signature", None)
700
+ if prev_sig is not None and sig == prev_sig:
701
+ return
702
+ httpd.federation_peers_signature = sig # type: ignore[attr-defined]
703
+ # Reload OUTSIDE the debounce lock (load_peers may do I/O); the reload
704
+ # itself mutates httpd attributes which are read-mostly + GIL-atomic
705
+ # dict reassignment.
706
+ _reload_peers_now(httpd)
707
+
708
+
709
+ def _reap_orphaned_inflight(
710
+ sentinels_dir,
711
+ *,
712
+ max_age_seconds=300.0,
713
+ now=None,
714
+ ):
715
+ """AC15 (PLAN-112-FOLLOWUP PHASE2) — housekeeping sweep for orphaned
716
+ ``*.inflight`` co-sign markers.
717
+
718
+ Gate #10's claim renames ``approval.md`` / ``approval.md.asc`` to
719
+ ``*.inflight`` BEFORE verify. Verify-failure self-reverts (Codex P2 #4)
720
+ and verify-success consumes to ``*.consumed-<sigref>``; so the ONLY way
721
+ an ``.inflight`` persists is a hard crash BETWEEN the two claim renames
722
+ or between claim and revert/consume. This sweep moves any ``*.inflight``
723
+ older than ``max_age_seconds`` to a TERMINAL ``*.orphaned-<mtime>``
724
+ marker.
725
+
726
+ **Quarantine, NOT revert (Codex AC18 P1).** An earlier draft renamed the
727
+ orphan back to ``approval.md`` to make the co-sign re-drivable. That is
728
+ unsafe: a crash in the post-verify / post-handler-mutation / pre-consume
729
+ window would let the reaper RESURRECT an already-admitted DESTRUCTIVE
730
+ co-sign — a single-use-violation / replay. Fail-CLOSED is the only safe
731
+ posture for a destructive admission: an orphaned co-sign is terminalized
732
+ and the Owner must issue a FRESH co-sign. (The verify-FAILURE revert in
733
+ ``_verify_owner_cosign_claim`` is still safe + kept — a co-sign that
734
+ failed verification was never admitted, so restoring it merely lets the
735
+ Owner retry; it cannot drive anything until it verifies.)
736
+
737
+ Best-effort + fail-open; returns the count quarantined. The 5-min default
738
+ leaves a legitimately in-flight claim (completes in ms) untouched.
739
+ """
740
+ if sentinels_dir is None:
741
+ return 0
742
+ base = Path(sentinels_dir)
743
+ if not base.is_dir():
744
+ return 0
745
+ ts = time.time() if now is None else float(now)
746
+ quarantined = 0
747
+ suffix = ".inflight"
748
+ try:
749
+ candidates = list(base.glob("*/*" + suffix))
750
+ except OSError:
751
+ return 0
752
+ for inflight in candidates:
753
+ try:
754
+ if not inflight.is_file():
755
+ continue
756
+ mtime = inflight.stat().st_mtime
757
+ if (ts - mtime) <= max_age_seconds:
758
+ continue
759
+ terminal = inflight.with_name(
760
+ inflight.name[: -len(suffix)]
761
+ + ".orphaned-{0}".format(int(mtime))
762
+ )
763
+ try:
764
+ os.rename(str(inflight), str(terminal))
765
+ quarantined += 1
766
+ except OSError:
767
+ # Terminal target already exists (re-reap) — drop the stray.
768
+ try:
769
+ inflight.unlink()
770
+ except OSError:
771
+ pass
772
+ except OSError:
773
+ continue
774
+ return quarantined
775
+
776
+
777
+ # ---------------------------------------------------------------------------
778
+ # Request handler — 3 read endpoints + 4 write endpoints + 11-gate chain
779
+ # ---------------------------------------------------------------------------
780
+
781
+
782
+ class _FederationHandler(http.server.BaseHTTPRequestHandler):
783
+ """Per-request handler. Wired by :class:`FederationServer`."""
784
+
785
+ server_version = "CEOFederation/1.0"
786
+ sys_version = "" # don't leak Python version
787
+
788
+ # AC16 — fail-fast timeout per request.
789
+ timeout = HANDSHAKE_TIMEOUT_SECONDS
790
+
791
+ def log_message(self, format: str, *args: Any) -> None: # noqa: A002
792
+ return # silent — audit-log is the canonical record
793
+
794
+ # AC15 mechanical method allowlist. PLAN-112-FOLLOWUP W1: POST is
795
+ # admitted ONLY when write-mode is active (Layer 0a env AND Layer 0b
796
+ # sentinel). When write-mode is OFF, the module constant
797
+ # ALLOWED_HTTP_METHODS = ("GET",) is the effective set and POST → 405.
798
+ def parse_request(self) -> bool:
799
+ ok = http.server.BaseHTTPRequestHandler.parse_request(self)
800
+ if not ok:
801
+ return ok
802
+ method = (self.command or "").upper()
803
+ if method in ALLOWED_HTTP_METHODS:
804
+ return True
805
+ # Non-GET. POST is conditionally allowed when write-mode active.
806
+ if method == "POST" and self._write_mode_active():
807
+ return True
808
+ self._emit_write_blocked()
809
+ self._send_405()
810
+ return False
811
+
812
+ # -- Write-mode activation gates (PLAN-112-FOLLOWUP W1) -------------
813
+
814
+ def _write_mode_active(self) -> bool:
815
+ """Layer 0a AND Layer 0b — write-mode is reachable.
816
+
817
+ Default-OFF (AC1/AC7): BOTH the env switch (Layer 0a) AND the
818
+ write-enable sentinel (Layer 0b / Gate #8) must pass. Either OFF
819
+ → False → POST stays 405. Fail-CLOSED on any error.
820
+ """
821
+ try:
822
+ if not write_mode_enabled_from_env():
823
+ return False
824
+ return self._write_enable_sentinel_valid()
825
+ except Exception:
826
+ return False
827
+
828
+ def _emit_write_blocked(self) -> None:
829
+ # AC15 — every non-allowed method-allowlist violation emits.
830
+ peer_fpr = self._lookup_peer_fpr()
831
+ correlation = self.headers.get(CORRELATION_ID_HEADER, "") or ""
832
+ _safe_emit(
833
+ "federation_write_attempt_blocked",
834
+ method=str(self.command)[:16] if self.command else "",
835
+ path=str(self.path)[:128] if self.path else "",
836
+ peer_id_cert_fingerprint=peer_fpr,
837
+ client_ip=self._client_ip(),
838
+ fed_correlation_id=correlation[:64],
839
+ )
840
+
841
+ def _send_405(self) -> None:
842
+ self.send_response(405)
843
+ # Advertise POST only when write-mode is active.
844
+ allow = "GET, POST" if self._write_mode_active() else "GET"
845
+ self.send_header("Allow", allow)
846
+ self.send_header("Content-Type", "application/json")
847
+ self.send_header("Connection", "close")
848
+ body = b'{"error":"method_not_allowed"}'
849
+ self.send_header("Content-Length", str(len(body)))
850
+ self.end_headers()
851
+ self.wfile.write(body)
852
+
853
+ def do_POST(self) -> None: # noqa: N802 — base-class convention
854
+ # PLAN-112-FOLLOWUP W1/W2 — when write-mode is OFF, behave exactly
855
+ # as before (emit blocked + 405). When ON, run the 11-gate
856
+ # dispatcher.
857
+ if not self._write_mode_active():
858
+ self._emit_write_blocked()
859
+ self._send_405()
860
+ return
861
+ self._dispatch_write(method="POST")
862
+
863
+ def do_PUT(self) -> None: # noqa: N802
864
+ self._emit_write_blocked()
865
+ self._send_405()
866
+
867
+ def do_PATCH(self) -> None: # noqa: N802
868
+ self._emit_write_blocked()
869
+ self._send_405()
870
+
871
+ def do_DELETE(self) -> None: # noqa: N802
872
+ self._emit_write_blocked()
873
+ self._send_405()
874
+
875
+ def do_OPTIONS(self) -> None: # noqa: N802
876
+ self._emit_write_blocked()
877
+ self._send_405()
878
+
879
+ def do_HEAD(self) -> None: # noqa: N802
880
+ self._emit_write_blocked()
881
+ self._send_405()
882
+
883
+ # -- Common helpers --------------------------------------------------
884
+
885
+ def _client_ip(self) -> str:
886
+ try:
887
+ return self.client_address[0] if self.client_address else ""
888
+ except (AttributeError, IndexError):
889
+ return ""
890
+
891
+ def _ip_prefix(self) -> str:
892
+ """Return a /24 (v4) or /48 (v6) prefix of the client IP.
893
+
894
+ Used as the rate-limit discriminator (LLM06/GDPR — never log the
895
+ full client IP). Best-effort; empty on any error.
896
+ """
897
+ ip = self._client_ip()
898
+ if not ip:
899
+ return ""
900
+ try:
901
+ addr = ipaddress.ip_address(ip)
902
+ except ValueError:
903
+ return ""
904
+ try:
905
+ if addr.version == 4:
906
+ net = ipaddress.ip_network(ip + "/24", strict=False)
907
+ else:
908
+ net = ipaddress.ip_network(ip + "/48", strict=False)
909
+ return str(net.network_address)
910
+ except ValueError:
911
+ return ""
912
+
913
+ def _lookup_peer_fpr(self) -> str:
914
+ try:
915
+ der = self.connection.getpeercert(True) # type: ignore[union-attr]
916
+ except (AttributeError, ssl.SSLError):
917
+ return ""
918
+ if not der:
919
+ return ""
920
+ return hashlib.sha256(der).hexdigest()
921
+
922
+ def _presented_cert_pem_bytes(self) -> bytes:
923
+ try:
924
+ der = self.connection.getpeercert(True) # type: ignore[union-attr]
925
+ except (AttributeError, ssl.SSLError):
926
+ return b""
927
+ if not der:
928
+ return b""
929
+ try:
930
+ return ssl.DER_cert_to_PEM_cert(der).encode("ascii")
931
+ except (TypeError, ValueError, UnicodeEncodeError):
932
+ return b""
933
+
934
+ def _lookup_peer_record(self) -> Optional[PeerRecord]:
935
+ # PLAN-112-FOLLOWUP W3/R-IT-B: refresh the peer list before SPKI
936
+ # match so a revocation propagates at the connection-accept layer
937
+ # too (not only Gate #7).
938
+ try:
939
+ _maybe_reload_peers(self.server)
940
+ except Exception:
941
+ pass # reload is best-effort; fail-open on infra only
942
+ peers = getattr(self.server, "federation_peers", {})
943
+ peers_extra = getattr(self.server, "federation_peers_extra", {}) or {}
944
+ if not peers:
945
+ return None
946
+
947
+ presented_der_fpr = self._lookup_peer_fpr()
948
+ if not presented_der_fpr:
949
+ return None
950
+
951
+ presented_pem = self._presented_cert_pem_bytes()
952
+ presented_spki_fpr = ""
953
+ spki_compute_attempted = False
954
+ spki_compute_error: Optional[str] = None
955
+
956
+ for peer in peers.values():
957
+ if peer.revoked:
958
+ continue
959
+ extra = peers_extra.get(peer.peer_id, {}) if peers_extra else {}
960
+ spki_from_record = peer.peer_id_spki_fingerprint
961
+ spki_from_extra = extra.get("peer_id_spki_fingerprint", "") if isinstance(extra, dict) else ""
962
+ effective_spki = spki_from_record or spki_from_extra or ""
963
+ peer_row = {
964
+ "peer_id": peer.peer_id,
965
+ "peer_id_spki_fingerprint": effective_spki,
966
+ "peer_id_cert_fingerprint": peer.peer_id_cert_fingerprint,
967
+ }
968
+ try:
969
+ pin_type, pin_value = select_pin_for_peer(peer_row)
970
+ except PinSelectionError:
971
+ self._emit_peer_invalid_no_fingerprint(peer.peer_id)
972
+ continue
973
+
974
+ if pin_type == "spki":
975
+ if not spki_compute_attempted:
976
+ spki_compute_attempted = True
977
+ if presented_pem:
978
+ try:
979
+ presented_spki_fpr = compute_spki_fingerprint(
980
+ presented_pem
981
+ )
982
+ except (ImportError, ValueError, TypeError) as exc:
983
+ spki_compute_error = (
984
+ "{0}:{1}".format(
985
+ type(exc).__name__, str(exc)[:64]
986
+ )
987
+ )
988
+ presented_spki_fpr = ""
989
+ if not presented_spki_fpr:
990
+ self._emit_spki_fingerprint_mismatch(
991
+ peer.peer_id,
992
+ reason="presented_spki_compute_failed:{0}".format(
993
+ spki_compute_error or "no_pem"
994
+ ),
995
+ )
996
+ continue
997
+ if compare_fingerprints(presented_spki_fpr, pin_value):
998
+ return peer
999
+ self._emit_spki_fingerprint_mismatch(
1000
+ peer.peer_id, reason="spki_mismatch"
1001
+ )
1002
+ continue
1003
+
1004
+ if compare_fingerprints(presented_der_fpr, pin_value):
1005
+ self._emit_pin_legacy_used(peer.peer_id)
1006
+ return peer
1007
+ continue
1008
+
1009
+ return None
1010
+
1011
+ def _resolve_peer(self) -> Optional[PeerRecord]:
1012
+ """Public alias for :meth:`_lookup_peer_record` (Wave D contract)."""
1013
+ return self._lookup_peer_record()
1014
+
1015
+ # Wave C audit emit wrappers.
1016
+ def _emit_spki_fingerprint_mismatch(
1017
+ self, peer_id: str, reason: str = "spki_mismatch",
1018
+ ) -> None:
1019
+ presented_fpr = self._lookup_peer_fpr() or ""
1020
+ _safe_emit(
1021
+ "federation_spki_fingerprint_mismatch",
1022
+ peer_id=peer_id[:64],
1023
+ expected_prefix="",
1024
+ presented_prefix=presented_fpr[:16],
1025
+ route=str(self.path or "")[:64],
1026
+ )
1027
+
1028
+ def _emit_pin_legacy_used(self, peer_id: str) -> None:
1029
+ der_fpr = self._lookup_peer_fpr() or ""
1030
+ _safe_emit(
1031
+ "federation_pin_legacy_used",
1032
+ peer_id=peer_id[:64],
1033
+ route=str(self.path or "")[:64],
1034
+ der_fingerprint_prefix=der_fpr[:16],
1035
+ )
1036
+
1037
+ def _emit_peer_invalid_no_fingerprint(self, peer_id: str) -> None:
1038
+ source_path = str(getattr(
1039
+ getattr(self, "server", None),
1040
+ "federation_peers_path",
1041
+ ".claude/data/federation/peers.yaml",
1042
+ ))
1043
+ _safe_emit(
1044
+ "federation_peer_invalid_no_fingerprint",
1045
+ peer_id=peer_id[:64],
1046
+ source_path=source_path[:128],
1047
+ )
1048
+
1049
+ def _emit_connection_rejected(self, reason: str) -> None:
1050
+ _safe_emit(
1051
+ "federation_connection_rejected",
1052
+ peer_id_cert_fingerprint=self._lookup_peer_fpr(),
1053
+ client_ip=self._client_ip(),
1054
+ reason=reason[:64],
1055
+ )
1056
+
1057
+ def _emit_connection_accepted(self, peer_id: str) -> None:
1058
+ correlation = self.headers.get(CORRELATION_ID_HEADER, "") or ""
1059
+ _safe_emit(
1060
+ "federation_connection_accepted",
1061
+ peer_id=peer_id[:64],
1062
+ client_ip=self._client_ip(),
1063
+ fed_correlation_id=correlation[:64],
1064
+ )
1065
+
1066
+ # -- Replay + signature verification (AC13) -------------------------
1067
+
1068
+ def _replay_freshness_preflight(
1069
+ self, peer: PeerRecord,
1070
+ ) -> Optional[str]:
1071
+ """R-SE-a Gate #3a — NON-mutating timestamp-window preflight.
1072
+
1073
+ Runs BEFORE the body read. Returns None if fresh, else a reason.
1074
+ Does NOT touch the nonce ring (no state mutation) so an unauth /
1075
+ replayed peer cannot poison the ring before HMAC verification.
1076
+ """
1077
+ nonce = self.headers.get("X-CEO-Federation-Nonce", "")
1078
+ ts = self.headers.get("X-CEO-Federation-Timestamp", "")
1079
+ sig = self.headers.get("X-CEO-Federation-Signature", "")
1080
+ if not (nonce and ts and sig):
1081
+ return "missing_replay_headers"
1082
+ try:
1083
+ parsed = parse_rfc3339_utc(ts)
1084
+ except ValueError:
1085
+ return "malformed_timestamp"
1086
+ skew = abs(parsed.timestamp() - time.time())
1087
+ if skew > MAX_CLOCK_SKEW_SECONDS:
1088
+ return "clock_skew"
1089
+ return None
1090
+
1091
+ def _verify_signature_only(
1092
+ self, peer: PeerRecord, body: bytes,
1093
+ ) -> Optional[str]:
1094
+ """R-SE-a Gate #3c — HMAC signature verify (needs body). No mutation."""
1095
+ nonce = self.headers.get("X-CEO-Federation-Nonce", "")
1096
+ ts = self.headers.get("X-CEO-Federation-Timestamp", "")
1097
+ sig = self.headers.get("X-CEO-Federation-Signature", "")
1098
+ try:
1099
+ from .replay import verify_signature # type: ignore[import]
1100
+ except ImportError:
1101
+ from replay import verify_signature # type: ignore[no-redef]
1102
+ ok = verify_signature(
1103
+ self.command or "", self.path or "", ts, nonce, body,
1104
+ peer.hmac_secret_hex, sig,
1105
+ )
1106
+ return None if ok else "signature_invalid"
1107
+
1108
+ def _commit_nonce(self, peer: PeerRecord) -> Optional[str]:
1109
+ """R-SE-a Gate #3d — MUTATING nonce commit. Runs ONLY after HMAC."""
1110
+ replay = getattr(self.server, "federation_replay_cache", None)
1111
+ if replay is None:
1112
+ return "replay_cache_missing"
1113
+ nonce = self.headers.get("X-CEO-Federation-Nonce", "")
1114
+ ts = self.headers.get("X-CEO-Federation-Timestamp", "")
1115
+ decision: ReplayDecision = replay.check_and_record(peer.peer_id, nonce, ts)
1116
+ return None if decision.accepted else decision.reason
1117
+
1118
+ def _verify_replay_and_signature(
1119
+ self, peer: PeerRecord, body: bytes,
1120
+ ) -> Optional[str]:
1121
+ """Legacy combined check (read path, GET). Kept for do_GET."""
1122
+ replay = getattr(self.server, "federation_replay_cache", None)
1123
+ if replay is None:
1124
+ return "replay_cache_missing"
1125
+ nonce = self.headers.get("X-CEO-Federation-Nonce", "")
1126
+ ts = self.headers.get("X-CEO-Federation-Timestamp", "")
1127
+ sig = self.headers.get("X-CEO-Federation-Signature", "")
1128
+ if not (nonce and ts and sig):
1129
+ return "missing_replay_headers"
1130
+ decision: ReplayDecision = replay.check_and_record(peer.peer_id, nonce, ts)
1131
+ if not decision.accepted:
1132
+ return decision.reason
1133
+ try:
1134
+ from .replay import verify_signature # type: ignore[import]
1135
+ except ImportError:
1136
+ from replay import verify_signature # type: ignore[no-redef]
1137
+ ok = verify_signature(
1138
+ self.command or "", self.path or "", ts, nonce, body,
1139
+ peer.hmac_secret_hex, sig,
1140
+ )
1141
+ if not ok:
1142
+ return "signature_invalid"
1143
+ return None
1144
+
1145
+ # ===================================================================
1146
+ # Wave D 11-gate write dispatcher (PLAN-112-FOLLOWUP W2/W3/W4/W6b)
1147
+ # ===================================================================
1148
+
1149
+ def _dispatch_write(self, method: str) -> None:
1150
+ """ADR-135-AMEND-1 §2.2 11-gate chain for write endpoints."""
1151
+ if _fed_scopes is None:
1152
+ # Partial install — scopes module absent. Fail-CLOSED.
1153
+ self._emit_write_blocked()
1154
+ self._send_405()
1155
+ return
1156
+
1157
+ raw_path = self.path or ""
1158
+ path = raw_path.split("?", 1)[0].split("#", 1)[0]
1159
+
1160
+ # Gate #2 — resolve peer (also refreshes peer list per R-IT-B).
1161
+ peer = self._resolve_peer()
1162
+ if peer is None:
1163
+ self._emit_connection_rejected("peer_unresolved")
1164
+ self._send_status(401, "unauthorized")
1165
+ return
1166
+
1167
+ peers_extra = getattr(self.server, "federation_peers_extra", {}) or {}
1168
+ extra = peers_extra.get(peer.peer_id, {}) if peers_extra else {}
1169
+ peer_row = {
1170
+ "peer_id": peer.peer_id,
1171
+ "peer_id_spki_fingerprint": extra.get(
1172
+ "peer_id_spki_fingerprint", ""
1173
+ ),
1174
+ "peer_id_cert_fingerprint": peer.peer_id_cert_fingerprint,
1175
+ "scopes": list(extra.get("scopes", [])),
1176
+ "audit_event_push_allowlist": list(
1177
+ extra.get("audit_event_push_allowlist", [])
1178
+ ),
1179
+ "revoked": bool(extra.get("revoked", peer.revoked)),
1180
+ }
1181
+
1182
+ # GATE #3a — non-mutating timestamp-window preflight (R-SE-a).
1183
+ pf_reason = self._replay_freshness_preflight(peer)
1184
+ if pf_reason is not None:
1185
+ _safe_emit(
1186
+ "federation_connection_replay_suspected",
1187
+ peer_id=peer.peer_id[:64],
1188
+ reason=pf_reason[:64],
1189
+ client_ip=self._client_ip(),
1190
+ )
1191
+ self._send_status(401, "hmac_or_replay")
1192
+ return
1193
+
1194
+ # GATE #3b — body read (capped).
1195
+ body = self._read_body_capped(max_bytes=1024 * 1024)
1196
+ if body is None:
1197
+ self._send_status(413, "payload_too_large")
1198
+ return
1199
+
1200
+ # GATE #3c — HMAC signature verify (needs body, no mutation).
1201
+ sig_reason = self._verify_signature_only(peer, body)
1202
+ if sig_reason is not None:
1203
+ _safe_emit(
1204
+ "federation_connection_replay_suspected",
1205
+ peer_id=peer.peer_id[:64],
1206
+ reason=sig_reason[:64],
1207
+ client_ip=self._client_ip(),
1208
+ )
1209
+ self._send_status(401, "hmac_or_replay")
1210
+ return
1211
+
1212
+ # GATE #3d — MUTATING nonce commit (ONLY after HMAC passes).
1213
+ nonce_reason = self._commit_nonce(peer)
1214
+ if nonce_reason is not None:
1215
+ _safe_emit(
1216
+ "federation_connection_replay_suspected",
1217
+ peer_id=peer.peer_id[:64],
1218
+ reason=nonce_reason[:64],
1219
+ client_ip=self._client_ip(),
1220
+ )
1221
+ self._send_status(401, "hmac_or_replay")
1222
+ return
1223
+
1224
+ # GATE #4 — method + path → scope.
1225
+ required_scope = _fed_scopes.route_required_scope(method, path)
1226
+ if required_scope is None:
1227
+ self._emit_write_blocked()
1228
+ self._send_405()
1229
+ return
1230
+
1231
+ # GATE #5 — X-CEO-Federation-Scope header presence + match.
1232
+ if not _fed_scopes.validate_scope_header(self.headers, required_scope):
1233
+ self._emit_scope_denied(peer.peer_id, path, required_scope, peer_row)
1234
+ self._send_status(400, "scope_header")
1235
+ return
1236
+
1237
+ # GATE #6 — peer's scopes list grants the required scope.
1238
+ if not _fed_scopes.peer_has_scope(peer_row, required_scope):
1239
+ self._emit_write_endpoint_denied(
1240
+ peer.peer_id, path, gate_failed=6, reason_code="write_unauthorized",
1241
+ )
1242
+ self._send_status(403, "scope")
1243
+ return
1244
+
1245
+ # GATE #7 — peer not revoked (reloaded list per P0-1).
1246
+ if peer_row.get("revoked") is True:
1247
+ self._emit_write_endpoint_denied(
1248
+ peer.peer_id, path, gate_failed=7, reason_code="peer_revoked",
1249
+ )
1250
+ self._send_status(403, "revoked")
1251
+ return
1252
+
1253
+ # GATE #8 — write-enable sentinel valid (also enforced at 0a).
1254
+ if not self._write_enable_sentinel_valid():
1255
+ self._emit_write_disabled_sentinel_invalid("gpg_verify_failed")
1256
+ self._send_status(503, "write_disabled")
1257
+ return
1258
+
1259
+ # GATE #9 — rate-limit (REAL — Wave E; default-DENY on exception).
1260
+ ok, rl_reason = self._rate_limit_check(method, path, self.headers, peer_row)
1261
+ if not ok:
1262
+ self._emit_write_endpoint_denied(
1263
+ peer.peer_id, path, gate_failed=9,
1264
+ reason_code=(rl_reason or "rate_limited")[:32],
1265
+ )
1266
+ self._send_status(429, "rate_limited")
1267
+ return
1268
+
1269
+ # GATE #10a — CLAIM + VERIFY (destructive routes; BEFORE handler).
1270
+ cosign_inflight_paths = None
1271
+ if _fed_scopes.is_destructive_route(method, path):
1272
+ ok, reason, cosign_inflight_paths = self._verify_owner_cosign_claim(
1273
+ method, path,
1274
+ )
1275
+ if not ok:
1276
+ self._emit_write_endpoint_denied(
1277
+ peer.peer_id, path, gate_failed=10,
1278
+ reason_code="destructive_unauthz:{0}".format(reason[:12]),
1279
+ )
1280
+ self._send_status(403, "owner_cosign")
1281
+ return
1282
+
1283
+ # GATE #11 — handler executes.
1284
+ # PLAN-112-FOLLOWUP P0 #1 + P1 #2 (Codex BLOCK): pass the
1285
+ # SERVER-CONFIGURED state paths route-specifically so a non-default
1286
+ # config mutates the RIGHT trust-root file (peers.yaml) and the
1287
+ # tamper check inspects the SAME audit-log the handler appends to.
1288
+ # Without this the handlers fall back to PEERS_FILE_DEFAULT /
1289
+ # their own audit-log resolution and the revocation-propagation +
1290
+ # T1565 claims become false.
1291
+ handler = self._route_handler(method, path)
1292
+ if handler is None:
1293
+ self._send_405()
1294
+ return
1295
+ handler_kwargs = self._handler_state_kwargs(path)
1296
+ try:
1297
+ status, reason, response_body = handler.handle(
1298
+ peer_row, self.headers, body, **handler_kwargs
1299
+ )
1300
+ except Exception as exc:
1301
+ sys.stderr.write(
1302
+ "[federation.dispatch] handler crashed: {0}: {1}\n".format(
1303
+ type(exc).__name__, str(exc)[:200]
1304
+ )
1305
+ )
1306
+ self._send_status(500, "handler_crash")
1307
+ return
1308
+
1309
+ # GATE #10b — CONSUME (AFTER handler success only).
1310
+ if cosign_inflight_paths is not None and status < 400:
1311
+ self._consume_owner_cosign_sentinel(cosign_inflight_paths)
1312
+
1313
+ # POST-handler T1565 — audit-chain tamper detection (W4). We walk
1314
+ # the SAME canonical audit-log the push handler just appended to
1315
+ # (handler_kwargs["audit_log_path"]) so the check is guaranteed to
1316
+ # inspect the freshly-written record — F-7.10 critical detection
1317
+ # must be REACHABLE, not merely plumbed.
1318
+ if status < 400 and path in (
1319
+ "/federation/audit-event", "/federation/audit-event/batch",
1320
+ ):
1321
+ self._maybe_check_audit_chain(
1322
+ peer.peer_id, path, handler_kwargs.get("audit_log_path"),
1323
+ )
1324
+
1325
+ self._send_response_bytes(status, response_body)
1326
+
1327
+ def _resolve_audit_log_path(self) -> Optional[Path]:
1328
+ """Return the ONE canonical audit-log path (P0 #1).
1329
+
1330
+ Precedence: server-config ``audit_log_path`` → the audit handler's
1331
+ own resolver (``CEO_AUDIT_LOG_PATH`` / platform default), so the
1332
+ write handlers + the T1565 tamper check ALWAYS agree on the path.
1333
+ """
1334
+ cfg = getattr(self.server, "federation_config", None)
1335
+ path = getattr(self.server, "federation_audit_log_path", None)
1336
+ if path is None and cfg is not None:
1337
+ path = getattr(cfg, "audit_log_path", None)
1338
+ if path is not None:
1339
+ return Path(path)
1340
+ # Mirror the handler's resolver so both sides land on the same file.
1341
+ try:
1342
+ try:
1343
+ from .handlers import audit_event_push as _aep # type: ignore
1344
+ except ImportError:
1345
+ import importlib
1346
+ _aep = importlib.import_module(
1347
+ "_lib.federation.handlers.audit_event_push"
1348
+ )
1349
+ resolver = getattr(_aep, "_resolve_audit_log_path", None)
1350
+ if resolver is not None:
1351
+ return Path(resolver())
1352
+ except Exception:
1353
+ pass
1354
+ return None
1355
+
1356
+ def _handler_state_kwargs(self, path: str) -> Dict[str, Any]:
1357
+ """Route-specific server-state kwargs for the gate-#11 handler.
1358
+
1359
+ P1 #2: peer_register/peer_revoke get the configured ``peers_path``
1360
+ (NOT PEERS_FILE_DEFAULT). P0 #1: audit-event push/batch get the ONE
1361
+ canonical ``audit_log_path``. Only paths that the target handler's
1362
+ ``handle(...)`` accepts are passed (kwargs are route-matched).
1363
+ """
1364
+ cfg = getattr(self.server, "federation_config", None)
1365
+ if path in ("/federation/peer-register", "/federation/peer-revoke"):
1366
+ peers_path = getattr(self.server, "federation_peers_path", None)
1367
+ if peers_path is None and cfg is not None:
1368
+ peers_path = getattr(cfg, "peers_path", None)
1369
+ if peers_path is not None:
1370
+ return {"peers_path": Path(peers_path)}
1371
+ return {}
1372
+ if path in (
1373
+ "/federation/audit-event", "/federation/audit-event/batch",
1374
+ ):
1375
+ log_path = self._resolve_audit_log_path()
1376
+ if log_path is not None:
1377
+ return {"audit_log_path": log_path}
1378
+ return {}
1379
+ return {}
1380
+
1381
+ def _route_handler(self, method: str, path: str):
1382
+ """Map (method, path) → handler module."""
1383
+ key = (method.upper(), path)
1384
+ if key == ("POST", "/federation/peer-register"):
1385
+ return _h_peer_register
1386
+ if key == ("POST", "/federation/audit-event"):
1387
+ return _h_audit_event_push
1388
+ if key == ("POST", "/federation/audit-event/batch"):
1389
+ return _h_audit_event_batch
1390
+ if key == ("POST", "/federation/peer-revoke"):
1391
+ return _h_peer_revoke
1392
+ return None
1393
+
1394
+ # -- Gate #8 — write-enable sentinel verify ------------------------
1395
+
1396
+ def _write_enable_sentinel_valid(self) -> bool:
1397
+ """Gate #8 / Layer 0b — verify the write-enable sentinel pair.
1398
+
1399
+ Fail-CLOSED on any error. No caching (ADR-121 §6 v1.x no-cache).
1400
+ """
1401
+ cfg = getattr(self.server, "federation_config", None)
1402
+ signed_path = getattr(self.server, "write_enabled_sentinel", None)
1403
+ if signed_path is None and cfg is not None:
1404
+ signed_path = getattr(cfg, "write_enabled_sentinel", None)
1405
+ if signed_path is None:
1406
+ signed_path = Path(".claude/data/federation/write-enabled.md")
1407
+ signed_path = Path(signed_path)
1408
+
1409
+ sig_path = getattr(self.server, "write_enabled_sentinel_asc", None)
1410
+ if sig_path is None and cfg is not None:
1411
+ sig_path = getattr(cfg, "write_enabled_sentinel_asc", None)
1412
+ if sig_path is None:
1413
+ sig_path = Path(str(signed_path) + ".asc")
1414
+ sig_path = Path(sig_path)
1415
+
1416
+ if not signed_path.exists() or not sig_path.exists():
1417
+ return False
1418
+
1419
+ try:
1420
+ owner_fpr = getattr(self.server, "owner_fpr", OWNER_GPG_FPR)
1421
+ registry_path = getattr(self.server, "signer_registry_path", None)
1422
+ if registry_path is None and cfg is not None:
1423
+ registry_path = getattr(cfg, "signer_registry_path", None)
1424
+ ok, _reason = verify_enable_sentinel_pair(
1425
+ signed_path,
1426
+ sig_path,
1427
+ [owner_fpr],
1428
+ signer_registry_path=registry_path,
1429
+ )
1430
+ except Exception:
1431
+ return False
1432
+ return bool(ok)
1433
+
1434
+ # -- Gate #10 — Owner co-sign claim / consume (split TOCTOU) --------
1435
+
1436
+ def _verify_owner_cosign_claim(
1437
+ self, method: str, path: str,
1438
+ ) -> Tuple[bool, str, Optional[Tuple[Path, Path, Path, Path, str]]]:
1439
+ """Gate #10a — CLAIM + VERIFY per-request Owner-co-sign sentinel."""
1440
+ sigref = ""
1441
+ for k, v in self.headers.items():
1442
+ if isinstance(k, str) and k.lower() == "x-ceo-owner-sigref":
1443
+ sigref = str(v) if isinstance(v, str) else ""
1444
+ break
1445
+ if not sigref:
1446
+ return False, "missing_header:x_ceo_owner_sigref", None
1447
+ if not re.match(r"^[A-Za-z0-9_-]{1,64}$", sigref):
1448
+ return False, "sigref_charset", None
1449
+
1450
+ cfg = getattr(self.server, "federation_config", None)
1451
+ base = getattr(self.server, "federation_sentinels_dir", None)
1452
+ if base is None and cfg is not None:
1453
+ base = getattr(cfg, "federation_sentinels_dir", None)
1454
+ if base is None:
1455
+ base = Path(".claude/data/federation/sentinels")
1456
+ base_dir = Path(base) / sigref
1457
+ md_path = base_dir / "approval.md"
1458
+ asc_path = base_dir / "approval.md.asc"
1459
+
1460
+ if not md_path.exists() or not asc_path.exists():
1461
+ return False, "sentinel_not_found", None
1462
+
1463
+ md_inflight = md_path.with_suffix(md_path.suffix + ".inflight")
1464
+ try:
1465
+ os.rename(str(md_path), str(md_inflight))
1466
+ except OSError:
1467
+ return False, "sentinel_inflight_collision", None
1468
+
1469
+ asc_inflight = asc_path.with_suffix(asc_path.suffix + ".inflight")
1470
+ try:
1471
+ os.rename(str(asc_path), str(asc_inflight))
1472
+ except OSError:
1473
+ try:
1474
+ os.rename(str(md_inflight), str(md_path))
1475
+ except OSError:
1476
+ pass
1477
+ return False, "sentinel_asc_claim_failed", None
1478
+
1479
+ # PLAN-112-FOLLOWUP P2 #4 (Codex BLOCK): every failure path AFTER
1480
+ # both files are renamed to .inflight (verify failure, read failure,
1481
+ # missing signed_at, TTL expiry, unexpected exception) must REVERT
1482
+ # the .inflight pair to its original names — best-effort — so the
1483
+ # sentinel is NOT left stuck. The sentinel was NOT consumed (verify
1484
+ # failed), so reverting is correct: it restores the recoverable
1485
+ # pre-claim state. The 5-min reaper (REMAINING) is the backstop for
1486
+ # the residual crash-between-rename window only.
1487
+ def _revert_inflight() -> None:
1488
+ try:
1489
+ if md_inflight.exists() and not md_path.exists():
1490
+ os.rename(str(md_inflight), str(md_path))
1491
+ except OSError:
1492
+ pass
1493
+ try:
1494
+ if asc_inflight.exists() and not asc_path.exists():
1495
+ os.rename(str(asc_inflight), str(asc_path))
1496
+ except OSError:
1497
+ pass
1498
+
1499
+ try:
1500
+ owner_fpr = getattr(self.server, "owner_fpr", OWNER_GPG_FPR)
1501
+ registry_path = getattr(self.server, "signer_registry_path", None)
1502
+ if registry_path is None and cfg is not None:
1503
+ registry_path = getattr(cfg, "signer_registry_path", None)
1504
+ ok, reason = verify_enable_sentinel_pair(
1505
+ md_inflight, asc_inflight, [owner_fpr],
1506
+ signer_registry_path=registry_path,
1507
+ )
1508
+ if not ok:
1509
+ _revert_inflight()
1510
+ return False, "verify_failed:{0}".format(reason[:64]), None
1511
+ try:
1512
+ md_text = md_inflight.read_text(encoding="utf-8")
1513
+ except OSError as exc:
1514
+ _revert_inflight()
1515
+ return False, "read_md_failed:{0}".format(
1516
+ exc.errno or "unknown",
1517
+ ), None
1518
+ signed_at = self._parse_signed_at(md_text)
1519
+ if signed_at is None:
1520
+ _revert_inflight()
1521
+ return False, "missing_signed_at", None
1522
+ age_seconds = time.time() - signed_at
1523
+ if age_seconds > 24 * 3600:
1524
+ _revert_inflight()
1525
+ return False, "ttl_expired:{0:.0f}s".format(age_seconds), None
1526
+ except Exception as exc:
1527
+ _revert_inflight()
1528
+ return False, "verify_call_failed:{0}".format(
1529
+ type(exc).__name__,
1530
+ ), None
1531
+
1532
+ return True, "verified", (
1533
+ md_path, asc_path, md_inflight, asc_inflight, sigref,
1534
+ )
1535
+
1536
+ def _consume_owner_cosign_sentinel(
1537
+ self,
1538
+ inflight_paths: Tuple[Path, Path, Path, Path, str],
1539
+ ) -> None:
1540
+ """Gate #10b — CONSUME the verified .inflight sentinel pair."""
1541
+ md_path, asc_path, md_inflight, asc_inflight, sigref = inflight_paths
1542
+ consumed_md = md_path.with_suffix(
1543
+ md_path.suffix + ".consumed-{0}".format(sigref)
1544
+ )
1545
+ consumed_asc = asc_path.with_suffix(
1546
+ asc_path.suffix + ".consumed-{0}".format(sigref)
1547
+ )
1548
+ try:
1549
+ os.rename(str(md_inflight), str(consumed_md))
1550
+ except OSError:
1551
+ return
1552
+ try:
1553
+ os.rename(str(asc_inflight), str(consumed_asc))
1554
+ except OSError:
1555
+ pass
1556
+
1557
+ def _parse_signed_at(self, md_text: str) -> Optional[float]:
1558
+ """Parse a ``signed_at: <iso-8601>`` line from cleartext .md."""
1559
+ for line in md_text.splitlines():
1560
+ line = line.strip()
1561
+ if not line.startswith("signed_at:"):
1562
+ continue
1563
+ ts_raw = line.split(":", 1)[1].strip().strip('"').strip("'")
1564
+ if ts_raw.endswith("Z"):
1565
+ ts_raw = ts_raw[:-1] + "+00:00"
1566
+ try:
1567
+ dt = _dt.datetime.fromisoformat(ts_raw)
1568
+ return dt.timestamp()
1569
+ except (ValueError, TypeError):
1570
+ return None
1571
+ return None
1572
+
1573
+ # -- Gate #9 — REAL rate-limit (Wave E; PLAN-112-FOLLOWUP W4) -------
1574
+
1575
+ def _rate_limit_check(
1576
+ self,
1577
+ method: str,
1578
+ path: str,
1579
+ headers,
1580
+ peer_row: dict,
1581
+ ) -> Tuple[bool, Optional[str]]:
1582
+ """Gate #9 — token-bucket + circuit-breaker + backpressure.
1583
+
1584
+ DEFAULT-DENY on any exception (R-SE-b: no gate fails OPEN). Bridges
1585
+ the dispatcher seam signature ``(method, path, headers, peer_row)``
1586
+ to ``rate_limit.py``'s ``(peer_id, route, ip_prefix, *, now=)``.
1587
+ Emits ``federation_message_storm_detected`` (breaker trip) +
1588
+ ``federation_audit_log_backpressure`` from inside rate_limit.py via
1589
+ the C-4-fixed _safe_emit fallback.
1590
+ """
1591
+ rl = _load_rate_limit()
1592
+ if rl is None:
1593
+ # Partial install — no limiter. Fail-CLOSED (deny).
1594
+ return False, "rate_limit:module_missing"
1595
+ try:
1596
+ peer_id = str(peer_row.get("peer_id", ""))
1597
+ route = path
1598
+ ip_prefix = self._ip_prefix()
1599
+ now_fn = getattr(self.server, "federation_clock", None)
1600
+ now = now_fn() if callable(now_fn) else None
1601
+
1602
+ # 1. Backpressure (T1499 latency overload).
1603
+ ok_bp, _info = rl.check_backpressure(now=now)
1604
+ if not ok_bp:
1605
+ return False, "audit_log_backpressure"
1606
+
1607
+ # 2. Circuit-breaker — already-tripped / trip-on-this-check.
1608
+ ok_cb, cb_reason = rl.check_circuit_breaker(peer_id, route, now=now)
1609
+ if not ok_cb:
1610
+ return False, (cb_reason or "circuit_breaker")[:32]
1611
+
1612
+ # 3. Token bucket.
1613
+ ok_rl, rl_reason = rl.check_rate_limit(
1614
+ peer_id, route, ip_prefix, now=now,
1615
+ )
1616
+ if not ok_rl:
1617
+ # Advance breaker window so a sustained storm trips it.
1618
+ rl.record_hit(peer_id, route, ip_prefix, now=now)
1619
+ return False, (rl_reason or "rate_limit")[:32]
1620
+ return True, None
1621
+ except Exception as exc:
1622
+ sys.stderr.write(
1623
+ "[federation.dispatch] rate_limit raised {0}; "
1624
+ "DEFAULT-DENY\n".format(type(exc).__name__)
1625
+ )
1626
+ return False, "rate_limit:exception"
1627
+
1628
+ def _maybe_check_audit_chain(
1629
+ self, peer_id: str, path: str,
1630
+ audit_log_path: Optional[Path] = None,
1631
+ ) -> None:
1632
+ """POST-handler T1565 — bounded audit-chain tamper walk (W4).
1633
+
1634
+ P0 #1 (Codex BLOCK): the path is the SAME canonical audit-log the
1635
+ push handler just appended to (passed from the dispatcher). We
1636
+ fall back to ``_resolve_audit_log_path()`` so the check still runs
1637
+ even if the caller didn't thread it through — it must NEVER be a
1638
+ no-op just because ``self.server.federation_audit_log_path`` is
1639
+ unset (the prior bug).
1640
+ """
1641
+ ace = _load_audit_chain_ext()
1642
+ if ace is None:
1643
+ return
1644
+ log_path = audit_log_path or self._resolve_audit_log_path()
1645
+ if log_path is None:
1646
+ return
1647
+ try:
1648
+ ok, _info = ace.check_chain(Path(log_path), max_events=500)
1649
+ # check_chain itself emits federation_tamper_detected on break
1650
+ # via its own _safe_emit (also C-4-fixed). Nothing to do here
1651
+ # on the happy path.
1652
+ _ = ok
1653
+ except Exception:
1654
+ pass
1655
+
1656
+ # -- Wave D emit shims (canonical F.2 vocabulary) ------------------
1657
+
1658
+ def _emit_scope_denied(
1659
+ self,
1660
+ peer_id: str,
1661
+ route: str,
1662
+ required_scope: str,
1663
+ peer_row: Mapping[str, Any],
1664
+ ) -> None:
1665
+ scopes_list = peer_row.get("scopes", []) if peer_row else []
1666
+ if not isinstance(scopes_list, (list, tuple)):
1667
+ scopes_list = []
1668
+ _safe_emit(
1669
+ "federation_scope_denied",
1670
+ peer_id=peer_id[:64],
1671
+ route=route[:64],
1672
+ required_scope=required_scope[:32],
1673
+ peer_scopes_count=int(len(scopes_list)),
1674
+ )
1675
+
1676
+ def _emit_write_endpoint_denied(
1677
+ self,
1678
+ peer_id: str,
1679
+ route: str,
1680
+ *,
1681
+ gate_failed: int,
1682
+ reason_code: str,
1683
+ ) -> None:
1684
+ _safe_emit(
1685
+ "federation_write_endpoint_denied",
1686
+ peer_id=peer_id[:64],
1687
+ route=route[:64],
1688
+ gate_failed=int(gate_failed),
1689
+ reason_code=reason_code[:32],
1690
+ )
1691
+
1692
+ def _emit_write_disabled_sentinel_invalid(
1693
+ self,
1694
+ reason_code: str = "gpg_verify_failed",
1695
+ ) -> None:
1696
+ sentinel_path = str(getattr(
1697
+ self.server,
1698
+ "write_enabled_sentinel",
1699
+ ".claude/data/federation/write-enabled.md",
1700
+ ))
1701
+ _safe_emit(
1702
+ "federation_write_disabled_sentinel_invalid",
1703
+ reason_code=reason_code[:32],
1704
+ sentinel_path=sentinel_path[:128],
1705
+ )
1706
+
1707
+ # -- Body / response helpers (Wave D) ------------------------------
1708
+
1709
+ def _read_body_capped(self, max_bytes: int) -> Optional[bytes]:
1710
+ """Read request body with a hard cap. Returns None on overflow."""
1711
+ cl_header = self.headers.get("Content-Length", "")
1712
+ try:
1713
+ content_length = int(cl_header) if cl_header else 0
1714
+ except (TypeError, ValueError):
1715
+ return None
1716
+ if content_length > max_bytes:
1717
+ return None
1718
+ if content_length <= 0:
1719
+ return b""
1720
+ try:
1721
+ return self.rfile.read(content_length)
1722
+ except (OSError, ValueError):
1723
+ return None
1724
+
1725
+ def _send_status(self, status: int, reason: str) -> None:
1726
+ body = json.dumps({"error": reason}).encode("utf-8")
1727
+ self.send_response(status)
1728
+ self.send_header("Content-Type", "application/json")
1729
+ self.send_header("Content-Length", str(len(body)))
1730
+ self.send_header("Connection", "close")
1731
+ self.end_headers()
1732
+ self.wfile.write(body)
1733
+
1734
+ def _send_response_bytes(self, status: int, body: bytes) -> None:
1735
+ self.send_response(status)
1736
+ self.send_header("Content-Type", "application/json")
1737
+ self.send_header("Content-Length", str(len(body)))
1738
+ self.send_header("Connection", "close")
1739
+ self.end_headers()
1740
+ if body:
1741
+ self.wfile.write(body)
1742
+
1743
+ # -- Dispatch (read path) -------------------------------------------
1744
+
1745
+ def do_GET(self) -> None: # noqa: N802
1746
+ if self.command not in ALLOWED_HTTP_METHODS:
1747
+ self._emit_write_blocked()
1748
+ self._send_405()
1749
+ return
1750
+
1751
+ peer = self._lookup_peer_record()
1752
+ if peer is None:
1753
+ self._emit_connection_rejected("peer_unknown_or_revoked")
1754
+ self._send_403()
1755
+ return
1756
+
1757
+ body = b""
1758
+ cl = self.headers.get("Content-Length")
1759
+ if cl and cl.isdigit():
1760
+ try:
1761
+ body = self.rfile.read(min(int(cl), 4096))
1762
+ except OSError:
1763
+ body = b""
1764
+
1765
+ replay_reason = self._verify_replay_and_signature(peer, body)
1766
+ if replay_reason is not None:
1767
+ _safe_emit(
1768
+ "federation_connection_replay_suspected",
1769
+ peer_id=peer.peer_id[:64],
1770
+ reason=replay_reason[:64],
1771
+ client_ip=self._client_ip(),
1772
+ )
1773
+ self._send_401(replay_reason)
1774
+ return
1775
+
1776
+ self._emit_connection_accepted(peer.peer_id)
1777
+
1778
+ if self.path == "/federation/identity":
1779
+ self._handle_identity()
1780
+ elif self.path == "/federation/status":
1781
+ self._handle_status()
1782
+ elif self.path.startswith("/federation/audit-summary"):
1783
+ self._handle_audit_summary(peer)
1784
+ else:
1785
+ self._send_404()
1786
+
1787
+ # -- Endpoints ------------------------------------------------------
1788
+
1789
+ def _handle_identity(self) -> None:
1790
+ server_fpr = getattr(self.server, "federation_server_fingerprint", "")
1791
+ payload = {"peer_id_cert_fingerprint": server_fpr}
1792
+ self._send_json(200, payload)
1793
+
1794
+ def _handle_status(self) -> None:
1795
+ peer = self._lookup_peer_record()
1796
+ peer_id = peer.peer_id if peer else ""
1797
+ started_at = getattr(self.server, "federation_started_at", 0.0)
1798
+ uptime_s = max(0, int(time.time() - started_at)) if started_at else 0
1799
+ last_events_digest = getattr(self.server, "federation_status_digest", "")
1800
+ last_events_count = getattr(self.server, "federation_status_count", 0)
1801
+ payload = {
1802
+ "peer_id": peer_id,
1803
+ "uptime_seconds": uptime_s,
1804
+ "last_event_opaque_sha256": last_events_digest,
1805
+ "last_event_count": int(last_events_count),
1806
+ }
1807
+ self._send_json(200, payload)
1808
+
1809
+ def _handle_audit_summary(self, peer: PeerRecord) -> None:
1810
+ rl = getattr(self.server, "federation_rate_limiter", None)
1811
+ if rl is not None and not rl.allow(
1812
+ peer.peer_id_cert_fingerprint, self._client_ip(),
1813
+ ):
1814
+ self._send_429()
1815
+ return
1816
+
1817
+ fetch: Optional[Callable[[Optional[str]], List[Dict[str, Any]]]] = (
1818
+ getattr(self.server, "federation_audit_fetch", None)
1819
+ )
1820
+ since = ""
1821
+ if "?" in self.path:
1822
+ qs = self.path.split("?", 1)[1]
1823
+ for kv in qs.split("&"):
1824
+ if kv.startswith("since="):
1825
+ since = kv.split("=", 1)[1][:64]
1826
+ break
1827
+
1828
+ events: List[Dict[str, Any]] = []
1829
+ if fetch is not None:
1830
+ try:
1831
+ events = fetch(since or None) or []
1832
+ except Exception:
1833
+ events = []
1834
+
1835
+ events = _apply_redaction_pipeline(events)
1836
+ payload = {"events": events, "count": len(events)}
1837
+ self._send_json(200, payload)
1838
+
1839
+ # -- Response helpers ------------------------------------------------
1840
+
1841
+ def _send_json(self, code: int, payload: Dict[str, Any]) -> None:
1842
+ body = json.dumps(payload, ensure_ascii=False, sort_keys=True).encode("utf-8")
1843
+ self.send_response(code)
1844
+ self.send_header("Content-Type", "application/json")
1845
+ self.send_header("Content-Length", str(len(body)))
1846
+ self.send_header("Connection", "close")
1847
+ self.end_headers()
1848
+ self.wfile.write(body)
1849
+
1850
+ def _send_403(self) -> None:
1851
+ body = b'{"error":"forbidden"}'
1852
+ self.send_response(403)
1853
+ self.send_header("Content-Type", "application/json")
1854
+ self.send_header("Content-Length", str(len(body)))
1855
+ self.end_headers()
1856
+ self.wfile.write(body)
1857
+
1858
+ def _send_401(self, reason: str) -> None:
1859
+ body = json.dumps(
1860
+ {"error": "unauthorized", "reason": reason[:64]},
1861
+ ensure_ascii=False,
1862
+ ).encode("utf-8")
1863
+ self.send_response(401)
1864
+ self.send_header("Content-Type", "application/json")
1865
+ self.send_header("Content-Length", str(len(body)))
1866
+ self.end_headers()
1867
+ self.wfile.write(body)
1868
+
1869
+ def _send_404(self) -> None:
1870
+ body = b'{"error":"not_found"}'
1871
+ self.send_response(404)
1872
+ self.send_header("Content-Type", "application/json")
1873
+ self.send_header("Content-Length", str(len(body)))
1874
+ self.end_headers()
1875
+ self.wfile.write(body)
1876
+
1877
+ def _send_429(self) -> None:
1878
+ body = b'{"error":"rate_limited"}'
1879
+ self.send_response(429)
1880
+ self.send_header("Content-Type", "application/json")
1881
+ self.send_header("Retry-After", "60")
1882
+ self.send_header("Content-Length", str(len(body)))
1883
+ self.end_headers()
1884
+ self.wfile.write(body)
1885
+
1886
+
1887
+ # AC6 fail-CLOSED contract.
1888
+ _REDACTION_FAIL_CLOSED_MARKER = object()
1889
+
1890
+
1891
+ def _apply_redaction_pipeline(
1892
+ events: List[Dict[str, Any]],
1893
+ ) -> List[Dict[str, Any]]:
1894
+ """Apply ``redact_secrets`` + ``pii_patterns.scan(...,mode="redact")``."""
1895
+ out: List[Dict[str, Any]] = []
1896
+ redact_secrets = None
1897
+ pii_scan = None
1898
+ load_error: Optional[str] = None
1899
+ try:
1900
+ try:
1901
+ from _lib import redact as _redact # type: ignore[import]
1902
+ from _lib import pii_patterns as _pii # type: ignore[import]
1903
+ except ImportError:
1904
+ import importlib
1905
+ _redact = importlib.import_module(".redact", package="_lib")
1906
+ _pii = importlib.import_module(".pii_patterns", package="_lib")
1907
+ redact_secrets = getattr(_redact, "redact_secrets", None)
1908
+ _pii_scan = getattr(_pii, "scan", None)
1909
+ if _pii_scan is not None:
1910
+ def _pii_redact_adapter(text: str) -> str:
1911
+ result = _pii_scan(text, mode="redact")
1912
+ return getattr(result, "redacted_text", text) or text
1913
+ pii_scan = _pii_redact_adapter
1914
+ except Exception as e: # noqa: BLE001 — defense-in-depth
1915
+ load_error = "{0}:{1}".format(type(e).__name__, str(e)[:80])
1916
+
1917
+ if redact_secrets is None or pii_scan is None:
1918
+ try:
1919
+ sys.stderr.write(
1920
+ "[federation.server] AC6 fail-CLOSED — redact_secrets={0!r} "
1921
+ "pii_scan={1!r} load_error={2!r}; refusing to serialise "
1922
+ "audit-summary events\n".format(
1923
+ redact_secrets is not None,
1924
+ pii_scan is not None,
1925
+ load_error,
1926
+ )
1927
+ )
1928
+ except Exception:
1929
+ pass
1930
+ return []
1931
+
1932
+ for ev in events:
1933
+ if not isinstance(ev, dict):
1934
+ continue
1935
+ try:
1936
+ blob = json.dumps(ev, ensure_ascii=False)
1937
+ except (TypeError, ValueError):
1938
+ continue
1939
+ try:
1940
+ blob = redact_secrets(blob)
1941
+ except Exception:
1942
+ continue
1943
+ try:
1944
+ blob = pii_scan(blob)
1945
+ except Exception:
1946
+ continue
1947
+ try:
1948
+ redacted = json.loads(blob)
1949
+ except (TypeError, ValueError):
1950
+ continue
1951
+ out.append(redacted)
1952
+ return out
1953
+
1954
+
1955
+ # ---------------------------------------------------------------------------
1956
+ # FederationServer — orchestrator
1957
+ # ---------------------------------------------------------------------------
1958
+
1959
+
1960
+ class FederationServer:
1961
+ """Top-level orchestrator for the federation server."""
1962
+
1963
+ def __init__(
1964
+ self,
1965
+ config: FederationConfig,
1966
+ audit_fetch: Optional[Callable[[Optional[str]], List[Dict[str, Any]]]] = None,
1967
+ now: Optional[_dt.datetime] = None,
1968
+ ) -> None:
1969
+ self.config = config
1970
+ self._audit_fetch = audit_fetch
1971
+ self._now = now or _dt.datetime.now(_dt.timezone.utc)
1972
+ self._httpd: Optional[http.server.HTTPServer] = None
1973
+ self._reload_thread: Optional[threading.Thread] = None
1974
+ self._reload_stop = threading.Event()
1975
+
1976
+ # -- Pre-start invariants ------------------------------------------
1977
+
1978
+ def _check_kill_switch(self) -> None:
1979
+ v = os.environ.get(FEDERATION_KILL_SWITCH_ENV, "0").strip()
1980
+ if v not in ("1", "true", "TRUE"):
1981
+ raise FederationStartError(
1982
+ "kill-switch {0} not set; refusing to start".format(
1983
+ FEDERATION_KILL_SWITCH_ENV
1984
+ )
1985
+ )
1986
+
1987
+ def _check_enable_sentinel(self) -> None:
1988
+ cfg = self.config
1989
+ ok, reason = verify_enable_sentinel_pair(
1990
+ cfg.enabled_sentinel,
1991
+ cfg.enabled_sentinel_asc,
1992
+ [OWNER_GPG_FPR],
1993
+ signer_registry_path=cfg.signer_registry_path,
1994
+ now=self._now,
1995
+ )
1996
+ if not ok:
1997
+ _safe_emit(
1998
+ "federation_enable_sentinel_invalid",
1999
+ sentinel_kind="enable",
2000
+ reason=reason[:96],
2001
+ )
2002
+ raise FederationStartError(
2003
+ "enable sentinel invalid: {0}".format(reason)
2004
+ )
2005
+
2006
+ def _check_lan_sentinel_if_required(self) -> None:
2007
+ cfg = self.config
2008
+ is_loopback, resolved = resolve_bind_is_loopback(cfg.bind_host)
2009
+ if is_loopback:
2010
+ return
2011
+ ok, reason = verify_enable_sentinel_pair(
2012
+ cfg.lan_enabled_sentinel,
2013
+ cfg.lan_enabled_sentinel_asc,
2014
+ [OWNER_GPG_FPR],
2015
+ signer_registry_path=cfg.signer_registry_path,
2016
+ now=self._now,
2017
+ )
2018
+ if not ok:
2019
+ _safe_emit(
2020
+ "federation_lan_bind_denied",
2021
+ bind_host=cfg.bind_host[:64],
2022
+ resolved_ip=resolved[:64],
2023
+ reason=reason[:96],
2024
+ )
2025
+ raise FederationStartError(
2026
+ "LAN bind denied: {0}".format(reason)
2027
+ )
2028
+
2029
+ def _load_peers_or_raise(self) -> Dict[str, PeerRecord]:
2030
+ try:
2031
+ peers = load_peers(self.config.peers_path)
2032
+ except FileNotFoundError:
2033
+ _safe_emit(
2034
+ "federation_connection_rejected",
2035
+ reason="peers_yaml_missing",
2036
+ peer_id_cert_fingerprint="",
2037
+ client_ip="",
2038
+ )
2039
+ raise FederationStartError(
2040
+ "peers.yaml missing at {0}".format(self.config.peers_path)
2041
+ )
2042
+ except PeerHasNoFingerprintError as e:
2043
+ _safe_emit(
2044
+ "federation_peer_invalid_no_fingerprint",
2045
+ peer_id=(getattr(e, "peer_id", "") or "")[:64],
2046
+ source_path=str(self.config.peers_path)[:128],
2047
+ )
2048
+ raise FederationStartError(
2049
+ "peers.yaml no-fingerprint invariant: {0}".format(e)
2050
+ )
2051
+ except PeersFileError as e:
2052
+ _safe_emit(
2053
+ "federation_connection_rejected",
2054
+ reason="peers_yaml_parse_error",
2055
+ peer_id_cert_fingerprint="",
2056
+ client_ip="",
2057
+ )
2058
+ raise FederationStartError(
2059
+ "peers.yaml parse error: {0}".format(e)
2060
+ )
2061
+
2062
+ now = self._now
2063
+ for peer in peers.values():
2064
+ days_left = (peer.not_valid_after - now).days
2065
+ if days_left <= CERT_EXPIRY_WARN_DAYS:
2066
+ _safe_emit(
2067
+ "federation_cert_expiry_warned",
2068
+ peer_id=peer.peer_id[:64],
2069
+ days_remaining=int(days_left),
2070
+ )
2071
+ if days_left < 0:
2072
+ _safe_emit(
2073
+ "federation_cert_revoked",
2074
+ peer_id=peer.peer_id[:64],
2075
+ reason="expired",
2076
+ )
2077
+ return peers
2078
+
2079
+ # -- Lifecycle -----------------------------------------------------
2080
+
2081
+ def _start_reload_thread(self, httpd: Any) -> None:
2082
+ """P0-1 — start a low-frequency poll thread so revocation
2083
+ propagates in <60s even with zero traffic."""
2084
+ def _poll() -> None:
2085
+ while not self._reload_stop.is_set():
2086
+ # Poll every 5s; debounce inside _maybe_reload_peers means
2087
+ # an unchanged file is a cheap stat+hash, not a re-parse.
2088
+ self._reload_stop.wait(5.0)
2089
+ if self._reload_stop.is_set():
2090
+ break
2091
+ try:
2092
+ _maybe_reload_peers(httpd)
2093
+ except Exception:
2094
+ pass
2095
+ try:
2096
+ _reap_orphaned_inflight(
2097
+ getattr(httpd, "federation_sentinels_dir", None)
2098
+ )
2099
+ except Exception:
2100
+ pass
2101
+ t = threading.Thread(
2102
+ target=_poll, name="federation-peer-reload", daemon=True,
2103
+ )
2104
+ self._reload_thread = t
2105
+ t.start()
2106
+
2107
+ def serve_forever(self) -> None:
2108
+ """Start the server and serve until ``shutdown()`` is called."""
2109
+ self._check_kill_switch()
2110
+ self._check_enable_sentinel()
2111
+ self._check_lan_sentinel_if_required()
2112
+ peers = self._load_peers_or_raise()
2113
+ cfg = self.config
2114
+
2115
+ ctx = build_ssl_context(cfg.cert_file, cfg.key_file, cfg.ca_file)
2116
+
2117
+ try:
2118
+ with open(cfg.cert_file, "r", encoding="utf-8") as fh:
2119
+ pem = fh.read()
2120
+ server_fpr = compute_cert_fingerprint(pem)
2121
+ except OSError:
2122
+ server_fpr = ""
2123
+
2124
+ bind_family = socket.AF_INET
2125
+ try:
2126
+ if ipaddress.ip_address(cfg.bind_host).version == 6:
2127
+ bind_family = socket.AF_INET6
2128
+ except ValueError:
2129
+ pass
2130
+
2131
+ httpd = _ThreadingHTTPSServer(
2132
+ (cfg.bind_host, cfg.bind_port),
2133
+ _FederationHandler,
2134
+ address_family=bind_family,
2135
+ )
2136
+ httpd.socket = ctx.wrap_socket(httpd.socket, server_side=True)
2137
+
2138
+ httpd.federation_config = cfg # type: ignore[attr-defined]
2139
+ httpd.federation_peers = peers # type: ignore[attr-defined]
2140
+ httpd.federation_peers_path = cfg.peers_path # type: ignore[attr-defined]
2141
+ httpd.federation_peers_extra = { # type: ignore[attr-defined]
2142
+ peer.peer_id: {
2143
+ "peer_id_spki_fingerprint": peer.peer_id_spki_fingerprint,
2144
+ "peer_id_cert_fingerprint": peer.peer_id_cert_fingerprint,
2145
+ "scopes": list(getattr(peer, "scopes", []) or []),
2146
+ "audit_event_push_allowlist": list(
2147
+ getattr(peer, "audit_event_push_allowlist", []) or []
2148
+ ),
2149
+ "revoked": bool(peer.revoked),
2150
+ }
2151
+ for peer in peers.values()
2152
+ }
2153
+ httpd.federation_replay_cache = ReplayCache( # type: ignore[attr-defined]
2154
+ max_skew_seconds=MAX_CLOCK_SKEW_SECONDS,
2155
+ )
2156
+ httpd.federation_rate_limiter = JointKeyRateLimiter( # type: ignore[attr-defined]
2157
+ AUDIT_SUMMARY_RATE_PER_MIN,
2158
+ )
2159
+ httpd.federation_started_at = time.time() # type: ignore[attr-defined]
2160
+ httpd.federation_server_fingerprint = server_fpr # type: ignore[attr-defined]
2161
+ httpd.federation_status_digest = "" # type: ignore[attr-defined]
2162
+ httpd.federation_status_count = 0 # type: ignore[attr-defined]
2163
+ httpd.federation_audit_fetch = self._audit_fetch # type: ignore[attr-defined]
2164
+ # PLAN-112-FOLLOWUP W1/W2/W3 — write-mode + reload-watcher state.
2165
+ httpd.write_enabled_sentinel = cfg.write_enabled_sentinel # type: ignore[attr-defined]
2166
+ httpd.write_enabled_sentinel_asc = cfg.write_enabled_sentinel_asc # type: ignore[attr-defined]
2167
+ httpd.federation_sentinels_dir = cfg.federation_sentinels_dir # type: ignore[attr-defined]
2168
+ httpd.signer_registry_path = cfg.signer_registry_path # type: ignore[attr-defined]
2169
+ httpd.owner_fpr = OWNER_GPG_FPR # type: ignore[attr-defined]
2170
+ # PLAN-112-FOLLOWUP P0 #1 — ONE canonical audit-log path shared by
2171
+ # the audit-event handlers (gate #11) AND the T1565 tamper walk.
2172
+ # If cfg.audit_log_path is None we resolve the SAME path the
2173
+ # handlers use so the check inspects the log just appended (never a
2174
+ # no-op). When an explicit env CEO_AUDIT_LOG_PATH is set the handler
2175
+ # honours it; we mirror it here for the tamper walk.
2176
+ _resolved_audit_log: Optional[Path] = (
2177
+ Path(cfg.audit_log_path) if cfg.audit_log_path is not None
2178
+ else None
2179
+ )
2180
+ if _resolved_audit_log is None:
2181
+ try:
2182
+ try:
2183
+ from .handlers import audit_event_push as _aep # type: ignore
2184
+ except ImportError:
2185
+ import importlib
2186
+ _aep = importlib.import_module(
2187
+ "_lib.federation.handlers.audit_event_push"
2188
+ )
2189
+ _r = getattr(_aep, "_resolve_audit_log_path", None)
2190
+ if _r is not None:
2191
+ _resolved_audit_log = Path(_r())
2192
+ except Exception:
2193
+ _resolved_audit_log = None
2194
+ httpd.federation_audit_log_path = _resolved_audit_log # type: ignore[attr-defined]
2195
+ httpd.federation_reload_lock = threading.Lock() # type: ignore[attr-defined]
2196
+ httpd.federation_peers_last_check = time.time() # type: ignore[attr-defined]
2197
+ httpd.federation_peers_signature = _peers_file_signature( # type: ignore[attr-defined]
2198
+ Path(cfg.peers_path)
2199
+ )
2200
+
2201
+ self._httpd = httpd
2202
+ self._start_reload_thread(httpd)
2203
+ try:
2204
+ httpd.serve_forever()
2205
+ finally:
2206
+ self._reload_stop.set()
2207
+ try:
2208
+ httpd.server_close()
2209
+ except OSError:
2210
+ pass
2211
+
2212
+ def shutdown(self) -> None:
2213
+ self._reload_stop.set()
2214
+ if self._httpd is not None:
2215
+ try:
2216
+ self._httpd.shutdown()
2217
+ except (OSError, RuntimeError):
2218
+ pass