@event4u/agent-config 16.0.0 → 16.2.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 (382) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -43,6 +43,7 @@
43
43
  *
44
44
  * Exit codes: 0 allow · 2 block (stderr carries the reason).
45
45
  */
46
+ import { spawnSync } from "node:child_process";
46
47
  import crypto from "node:crypto";
47
48
  import fs from "node:fs";
48
49
  import path from "node:path";
@@ -50,8 +51,9 @@ import { fileURLToPath, pathToFileURL } from "node:url";
50
51
 
51
52
  import { atomic_write_json } from "./state_io.js";
52
53
  import { readHookStdin } from "./hook_stdin.js";
54
+ import { isCodePath, isTestPath } from "../_lib/test_delta.js";
55
+ import { EXIT_ALLOW, EXIT_BLOCK } from './exit_codes.js';
53
56
 
54
- const EXIT_ALLOW = 0;
55
57
  // MUST equal dispatch_hook.EXIT_BLOCK. The dispatcher's internal ladder is
56
58
  // 0 allow / 1 block / 2 warn — NOT the 2-means-block shape a PreToolUse guard
57
59
  // reads naturally from Claude's own native contract. This constant was 2 when
@@ -59,7 +61,6 @@ const EXIT_ALLOW = 0;
59
61
  // and the gate emitted advisory context while the operation went through.
60
62
  // Pinned against the dispatcher's export by
61
63
  // tests/hooks/concern_block_exit_parity.test.ts.
62
- const EXIT_BLOCK = 1;
63
64
 
64
65
  export const STATE_FILE = path.join("agents", "state", "evidence-dispatch.json");
65
66
 
@@ -324,6 +325,79 @@ export function decide(
324
325
  };
325
326
  }
326
327
 
328
+ /**
329
+ * The one-commit test-authorship flag —
330
+ * `road-to-adversarial-verification-and-long-runs` 2.4, third clause.
331
+ *
332
+ * A commit staging BOTH a test and code it could cover is the shape of L0 on the
333
+ * independence scale — the implementer wrote the evaluator — and it is the one
334
+ * case nobody sees, because the diff looks exactly like a well-tested change.
335
+ *
336
+ * **It reads the STAGED SET and the command, and nothing else.** It does NOT
337
+ * read a session id, an author or a timestamp, so it cannot distinguish an
338
+ * implementer who wrote both from one committing a test another session authored
339
+ * — an independent review of this function caught an earlier docstring here
340
+ * claiming a session boundary the code never reads, which is exactly the
341
+ * over-claim `evaluator-independence` exists over. The honest reading is: this
342
+ * is the shape L0 takes in a diff, surfaced so the level is stated rather than
343
+ * inferred.
344
+ *
345
+ * **WARN, never block.** L0 is explicitly permitted as a fallback, so refusing it
346
+ * would forbid a legal state; and a signal this coarse may surface, never refuse.
347
+ *
348
+ * Returns `null` when nothing is worth saying — i.e. this is not a commit.
349
+ */
350
+ export function sameSessionTestFlag(command: string | null): string | null {
351
+ if (command === null || !/\bgit\b[^|;&]*\bcommit\b/.test(command)) return null;
352
+ return SAME_SESSION_TEXT;
353
+ }
354
+
355
+ const SAME_SESSION_TEXT =
356
+ "This commit stages both a test and code it could cover — the shape L0 takes " +
357
+ "in a diff, where the implementer authored the evaluator. This check reads the " +
358
+ "staged set only: it cannot tell who wrote the test, so it is a prompt to STATE " +
359
+ "the level, not a finding that you are at L0. L0 is a permitted FALLBACK, not " +
360
+ "the default: where a second session, a second model or a second provider was " +
361
+ "available, the test should have come from one of them. Say which in the commit " +
362
+ "or the PR body — a diff looks identical either way. See evaluator-independence " +
363
+ "§ Tests are evaluators.";
364
+
365
+ /** Both a test path and a production path in one changed set. */
366
+ export function touchesTestAndCode(paths: readonly string[]): boolean {
367
+ return paths.some(isTestPath) && paths.some(isCodePath);
368
+ }
369
+
370
+ /** The shell command this envelope is about, across the key names hosts use. */
371
+ function _commandOf(envelope: JsonObject): string | null {
372
+ const payload = _isObject(envelope["payload"]) ? envelope["payload"] : envelope;
373
+ const input = _isObject(payload["tool_input"]) ? payload["tool_input"] : payload;
374
+ for (const key of ["command", "cmd", "shell_command"]) {
375
+ const v = input[key];
376
+ if (typeof v === "string" && v !== "") return v;
377
+ }
378
+ return null;
379
+ }
380
+
381
+ /**
382
+ * Staged paths, or `null` when git cannot answer.
383
+ *
384
+ * `null` is not "nothing staged" — it is "no reading", and the caller must not
385
+ * turn an unreadable index into a silent clean bill. A hook that cannot measure
386
+ * says nothing rather than implying a measurement.
387
+ */
388
+ function _stagedPaths(consumer_root: string): string[] | null {
389
+ const r = spawnSync("git", ["diff", "--cached", "--name-only"], {
390
+ cwd: consumer_root,
391
+ encoding: "utf8",
392
+ });
393
+ if (r.status !== 0) return null;
394
+ const out = r.stdout
395
+ .split("\n")
396
+ .map((s) => s.trim())
397
+ .filter((s) => s !== "");
398
+ return out.length === 0 ? null : out;
399
+ }
400
+
327
401
  export function run(stdin_text: string, options: { consumer_root: string }): number {
328
402
  let envelope: JsonObject = {};
329
403
  if (stdin_text.trim()) {
@@ -355,6 +429,27 @@ export function run(stdin_text: string, options: { consumer_root: string }): num
355
429
  }
356
430
 
357
431
  const [tool, prompt] = extractDispatch(envelope);
432
+
433
+ // 2.4 — the one-commit test-authorship flag, checked BEFORE the dispatch
434
+ // branch and returning early. A commit envelope carries no evaluation prompt,
435
+ // so `decide` would classify it as not-an-evaluation and allow it silently;
436
+ // running both would mean the commit path fell through to a counter that is
437
+ // about evaluation dispatches and would be wrong to advance here.
438
+ //
439
+ // CHEAP TEST FIRST, and that ordering is the finding rather than a style
440
+ // preference: `_stagedPaths` spawns `git diff --cached`, and this concern is
441
+ // bound on `pre_tool_use`, so reading the index before the regex made every
442
+ // Read, Edit, Grep and Task dispatch in every session pay a subprocess. The
443
+ // regex answers "is this even a commit" for free.
444
+ const flag = sameSessionTestFlag(_commandOf(envelope));
445
+ if (flag !== null) {
446
+ const staged = _stagedPaths(options.consumer_root);
447
+ if (staged !== null && touchesTestAndCode(staged)) {
448
+ process.stdout.write(`${JSON.stringify({ decision: "warn", reason: flag })}\n`);
449
+ return EXIT_ALLOW;
450
+ }
451
+ }
452
+
358
453
  const decision = decide(tool, prompt, state.evaluations.length);
359
454
 
360
455
  if (decision.evaluations > state.evaluations.length) {
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The hook exit-code table — one frozen definition, imported everywhere.
3
+ *
4
+ * road-to-a-kernel-that-guards-its-plumbing 4.1.
5
+ *
6
+ * WHAT WAS WRONG. Thirty-three files under `src/scripts/hooks/` each declared
7
+ * their own `const EXIT_ALLOW = 0` / `EXIT_BLOCK = 1` / `EXIT_WARN = 2`, two of
8
+ * them under a third name (`EXIT_OK`). Thirty-three copies of a contract is
9
+ * thirty-three places it can be changed in one and not the others, and the
10
+ * change that matters here is not a typo: exit 1 and exit 2 mean the OPPOSITE
11
+ * things on Claude Code from what they mean in this tree's internal language
12
+ * (`host_semantics.ts` exists entirely to lower one onto the other). A number
13
+ * that means "advisory" here and "hard deny" there is not a constant anyone
14
+ * should be re-deriving per file.
15
+ *
16
+ * WHAT THE TABLE ADDS BEYOND THE NUMBERS. `owner` and `authorizedBy` — who
17
+ * decides that a concern emits this code, and what authorises the dispatcher to
18
+ * act on it. A bare `1` in a concern says nothing about whether that concern was
19
+ * ever allowed to refuse; the manifest's `severity` is what authorises it, and
20
+ * the dispatcher enforces that ceiling (`_is_advisory`). Recording it beside the
21
+ * number is what makes the table a contract rather than an enum.
22
+ *
23
+ * WHAT IS NOT IN SCOPE. The `*-dispatcher.sh` trampolines carry inline
24
+ * `process.exit(0)` in a `node -e` one-liner that reads a workspace root off
25
+ * stdin. They are shell, they cannot import, and their exit code is the
26
+ * one-liner's, never a verdict. `lint_exit_codes` scans `.ts` only.
27
+ */
28
+
29
+ /** Allow — the concern has no objection. The overwhelmingly common verdict. */
30
+ export const EXIT_ALLOW = 0;
31
+
32
+ /** Block — the concern refuses. Only a `severity: blocking` concern may. */
33
+ export const EXIT_BLOCK = 1;
34
+
35
+ /** Warn — advisory. The dispatcher exits 0 and surfaces the reason. */
36
+ export const EXIT_WARN = 2;
37
+
38
+ /**
39
+ * Error — the concern crashed or could not decide.
40
+ *
41
+ * `3` is the floor, not the only value: the dispatcher treats every rc ≥ 3 the
42
+ * same way, and what that way IS depends on the concern's declared severity.
43
+ */
44
+ export const EXIT_ERROR = 3;
45
+
46
+ /**
47
+ * Usage error on the process's OWN argv — not a verdict, and not a row.
48
+ *
49
+ * `dispatch_hook` and `replay_hook` are also CLIs: run with `--platform`
50
+ * missing they exit before any concern is consulted. The number is 2 by the
51
+ * POSIX convention for a usage error, which COLLIDES with `EXIT_WARN` by
52
+ * coincidence and not by meaning — nothing advisory has happened, and no
53
+ * concern has spoken.
54
+ *
55
+ * Named rather than left as a bare `2`, and kept OUT of `EXIT_CODES`, because
56
+ * both readings are wrong: importing `EXIT_WARN` here would assert a verdict
57
+ * that was never reached, and adding a fifth row would put a non-verdict in a
58
+ * table whose whole subject is what a concern may say. `lint_exit_codes`
59
+ * refuses the bare literal, so the next writer has to pick one of the two
60
+ * deliberately.
61
+ */
62
+ export const EXIT_USAGE = 2;
63
+
64
+ export interface ExitCodeRow {
65
+ /** The numeric code, or its floor for the open-ended error band. */
66
+ readonly code: number;
67
+ readonly name: 'allow' | 'block' | 'warn' | 'error';
68
+ readonly meaning: string;
69
+ /** Who decides a concern emits this code. */
70
+ readonly owner: string;
71
+ /** What authorises the dispatcher to act on it. */
72
+ readonly authorizedBy: string;
73
+ }
74
+
75
+ /**
76
+ * The frozen table. Ordered by code; the last row is a band, not a value.
77
+ *
78
+ * `as const` plus `Object.freeze` rather than a plain array: this is the kind of
79
+ * structure a later convenience ("just push a row for the new verdict") turns
80
+ * into a fourth dialect, and the whole point of the file is that there is one.
81
+ */
82
+ export const EXIT_CODES: readonly ExitCodeRow[] = Object.freeze([
83
+ Object.freeze({
84
+ code: EXIT_ALLOW,
85
+ name: 'allow',
86
+ meaning: 'no objection; the dispatcher passes the call through',
87
+ owner: 'every concern — the default verdict',
88
+ authorizedBy: 'nothing; allowing needs no authority',
89
+ }),
90
+ Object.freeze({
91
+ code: EXIT_BLOCK,
92
+ name: 'block',
93
+ meaning: 'refuse the call; the dispatcher lowers this onto the host deny channel',
94
+ owner: 'a concern declared `severity: blocking` in hook_manifest.yaml',
95
+ authorizedBy:
96
+ 'the manifest entry plus its row in the BLOCKING_ALLOWLIST of ' +
97
+ 'tests/hooks/concern_severity.test.ts — an advisory concern emitting this is ' +
98
+ 'downgraded to warn by dispatch_hook._is_advisory',
99
+ }),
100
+ Object.freeze({
101
+ code: EXIT_WARN,
102
+ name: 'warn',
103
+ meaning: 'advisory; the dispatcher exits 0 and surfaces the reason',
104
+ owner: 'any concern',
105
+ authorizedBy:
106
+ 'nothing; warning is the ceiling for an advisory concern and is always available ' +
107
+ 'to a blocking one',
108
+ }),
109
+ Object.freeze({
110
+ code: EXIT_ERROR,
111
+ name: 'error',
112
+ meaning:
113
+ 'the concern crashed, timed out, or could not decide — any rc >= 3. The dispatcher ' +
114
+ 'resolves it by the concern`s declared severity, never by the number itself',
115
+ owner: 'no concern emits this on purpose; the runtime does',
116
+ authorizedBy:
117
+ 'hook_manifest.yaml `fail_closed` and `severity` together — see ' +
118
+ 'docs/contracts/hook-architecture-v1.md § Exit-code semantics',
119
+ }),
120
+ ] as const);
121
+
122
+ /** The row a raw exit code resolves to, or `null` for a negative code. */
123
+ export function rowFor(code: number): ExitCodeRow | null {
124
+ if (code >= EXIT_ERROR) {
125
+ return EXIT_CODES[3] ?? null;
126
+ }
127
+ return EXIT_CODES.find((r) => r.code === code) ?? null;
128
+ }
@@ -0,0 +1 @@
1
+ {"fingerprint":"08575a71d81fd8a5ef9e4b400f4a38cb66c26e1dd701d98f3cdc0bdc9e4758b4:22235","table":{"schema_version":1,"hosts":{"claude":{"surfaces":{"any":{"entry_shape":"claude-settings-hooks","json_shape":"hook-specific-output","fail_policy":"propagate","timeout_unit":"seconds","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://code.claude.com/docs/en/hooks.md","docs_digest":"9dd68316c1111c118e3ddb82861ac6271a8cd164cee0d371b26165efff35fe88","probe_at":"2026-09-06","host_version":"2.1.263","expires":"2027-09-06"},"slots":{"session_start":{"native":"SessionStart","block_exit":null,"answered_at":"2026-09-29"},"session_end":{"native":"SessionEnd","block_exit":null,"answered_at":"2026-09-29"},"stop":{"native":"Stop","block_exit":2,"answered_at":"2026-09-29"},"user_prompt_submit":{"native":"UserPromptSubmit","block_exit":2,"answered_at":"2026-09-29"},"pre_tool_use":{"native":"PreToolUse","block_exit":2,"answered_at":"2026-09-29"},"post_tool_use":{"native":"PostToolUse","block_exit":null,"answered_at":"2026-09-29"},"pre_compact":{"native":"PreCompact","block_exit":null,"answered_at":"2026-09-29"},"subagent_start":{"native":"SubagentStart","block_exit":null,"answered_at":"2026-09-29"},"subagent_stop":{"native":"SubagentStop","block_exit":null,"answered_at":"2026-09-29"}}}}},"augment":{"surfaces":{"any":{"entry_shape":"augment-settings-hooks","json_shape":"none","fail_policy":"discard","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://docs.augmentcode.com/cli/hooks.md","docs_digest":"e500823be8303717020c1823f670e06d100108f6e3ac4b8d12a46d91fd1898fa","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{"session_start":{"native":"SessionStart","block_exit":null,"answered_at":"2026-09-29"},"session_end":{"native":"SessionEnd","block_exit":null,"answered_at":"2026-09-29"},"stop":{"native":"Stop","block_exit":null,"answered_at":"2026-09-29"},"pre_tool_use":{"native":"PreToolUse","block_exit":null,"answered_at":"2026-09-29"},"post_tool_use":{"native":"PostToolUse","block_exit":null,"answered_at":"2026-09-29"}}}}},"cursor":{"surfaces":{"any":{"entry_shape":"cursor-hooks-json","json_shape":"none","fail_policy":"propagate","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://cursor.com/docs/hooks.md","docs_digest":"7294a2d63b19d79a5283c948afe174cba8b06a7585563d34a2c41b90f5df0d39","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{"session_start":{"native":"sessionStart","block_exit":null,"answered_at":"2026-09-29"},"session_end":{"native":"sessionEnd","block_exit":null,"answered_at":"2026-09-29"},"stop":{"native":"stop","block_exit":null,"answered_at":"2026-09-29"},"user_prompt_submit":{"native":"beforeSubmitPrompt","block_exit":null,"answered_at":"2026-09-29"},"post_tool_use":{"native":"postToolUse","block_exit":null,"answered_at":"2026-09-29"}}}}},"cline":{"surfaces":{"any":{"entry_shape":"cline-per-event-script","json_shape":"none","fail_policy":"discard","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://docs.cline.bot/customization/hooks.md","docs_digest":"ef87561721f4cb80fe6d682b6866d638a4952dd416d36cd5ab398426ce64084b","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{"session_start":{"native":["TaskStart","TaskResume"],"block_exit":null,"answered_at":"2026-09-29"},"session_end":{"native":"TaskComplete","block_exit":null,"answered_at":"2026-09-29"},"stop":{"native":"TaskCancel","block_exit":null,"answered_at":"2026-09-29"},"user_prompt_submit":{"native":"UserPromptSubmit","block_exit":null,"answered_at":"2026-09-29"},"post_tool_use":{"native":"PostToolUse","block_exit":null,"answered_at":"2026-09-29"}}}}},"windsurf":{"surfaces":{"any":{"entry_shape":"windsurf-hooks-json","json_shape":"none","fail_policy":"propagate","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://docs.devin.ai/desktop/cascade/hooks.md","docs_digest":"460ffe7aa91998804920f6722c0153b9948eadd5edafa19582298475a19835a2","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{"session_start":{"native":"post_setup_worktree","block_exit":null,"answered_at":"2026-09-29"},"user_prompt_submit":{"native":"pre_user_prompt","block_exit":null,"answered_at":"2026-09-29"},"stop":{"native":"post_cascade_response","block_exit":null,"answered_at":"2026-09-29"}}}}},"gemini":{"surfaces":{"any":{"entry_shape":"gemini-settings-hooks","json_shape":"none","fail_policy":"propagate","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://geminicli.com/docs/hooks/reference.md","docs_digest":"06d37f2f66812ee78bb46c37a112203376f0bae4d3b8b44ac79d0bda4b86a118","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{"session_start":{"native":"SessionStart","block_exit":null,"answered_at":"2026-09-29","matcher":""},"session_end":{"native":"SessionEnd","block_exit":null,"answered_at":"2026-09-29","matcher":""},"stop":{"native":"AfterAgent","block_exit":null,"answered_at":"2026-09-29","matcher":""},"user_prompt_submit":{"native":"BeforeAgent","block_exit":null,"answered_at":"2026-09-29","matcher":""},"post_tool_use":{"native":"AfterTool","block_exit":null,"answered_at":"2026-09-29","matcher":".*"}}}}},"cowork":{"surfaces":{"any":{"entry_shape":"none","json_shape":"none","fail_policy":"discard","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":null,"docs_digest":null,"probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{}}}},"copilot":{"surfaces":{"any":{"entry_shape":"none","json_shape":"none","fail_policy":"discard","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://docs.github.com/en/copilot/concepts/agents/hooks.md","docs_digest":"bfd40aa93ad62133efc17a50f189034f01594dc89a5d31a13a0c541d691acede","probe_at":null,"host_version":null,"expires":"2027-09-29"},"slots":{}}}},"codex":{"surfaces":{"any":{"entry_shape":"none","json_shape":"none","fail_policy":"discard","timeout_unit":"unknown","timeout_default":null,"verified":{"docs_at":"2026-09-29","docs_url":"https://developers.openai.com/codex/hooks.md","docs_digest":"724881d81f1a9fdb35ea42d4851a11b1f6606bbc6e9d2d05c1e8c466fc25c630","probe_at":"2026-10-01","host_version":"0.148.0","expires":"2027-09-29"},"slots":{}}}}}}}
@@ -21,6 +21,8 @@ import { fileURLToPath } from 'node:url';
21
21
 
22
22
  import { parse as parseYaml } from 'yaml';
23
23
 
24
+ import { tableFingerprint } from './table_fingerprint.js';
25
+
24
26
  declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
25
27
  const _IN_BUNDLE = typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__;
26
28
  const _REPO_ROOT = path.resolve(
@@ -30,10 +32,59 @@ const _REPO_ROOT = path.resolve(
30
32
 
31
33
  export const HOST_LOWERING_PATH = path.join(_REPO_ROOT, 'src', 'scripts', 'hooks', 'host_lowering.yaml');
32
34
 
35
+ /**
36
+ * Compiled sibling of the table above — the same data with comments stripped.
37
+ *
38
+ * WHY IT EXISTS, measured rather than assumed. The dispatcher consults this
39
+ * table on EVERY dispatch (`emitFor` needs `blockExitFor`), and since
40
+ * `hook_manifest.json` landed it is the ONLY remaining YAML reader on that
41
+ * path — so it pays both its own parse and the cold load of the `yaml` module
42
+ * nothing else pulls in any more. Measured on one developer machine: the
43
+ * 16.8 kB source parses in 3.1-7.0 ms and the `yaml` module costs a further
44
+ * 8.1 ms to load, against `JSON.parse` of the 5.9 kB compiled form at 0.03 ms.
45
+ *
46
+ * Exactly the shape `compile_hook_manifest` already established for the
47
+ * manifest, including the content fingerprint — see `table_fingerprint` for
48
+ * why freshness is decided by content and never by mtime.
49
+ */
50
+ export const HOST_LOWERING_JSON_PATH = path.join(_REPO_ROOT, 'src', 'scripts', 'hooks', 'host_lowering.json');
51
+
33
52
  /** Provenance of a row. Every field may be null; `expires` may not. */
34
53
  export interface VerifiedBlock {
35
54
  docs_at: string | null;
36
55
  docs_url: string | null;
56
+ /**
57
+ * sha256 of the `docs_url` body as last ESTABLISHED for this row.
58
+ *
59
+ * "Established" means recorded by a human-run fill, not merely seen. The
60
+ * watcher re-hashes the page on every run and writes the new value only
61
+ * when the field was empty; on a DRIFT it deliberately leaves the recorded
62
+ * digest alone, so the field keeps pointing at the body someone actually
63
+ * read while `expires` carries the alarm.
64
+ *
65
+ * Three earlier wordings were wrong and all three are named rather than
66
+ * quietly replaced, because each was corrected by evidence the next one
67
+ * then contradicted. "As fetched on `docs_at`" was refuted by the very
68
+ * first fill (digests taken 2026-10-01 against rows whose `docs_at` is
69
+ * 2026-09-29). "As fetched by the LAST digest run" was refuted by the
70
+ * writer (after a drift the last run computed a different hash than the
71
+ * field holds). "`docs_at` + `docs_digest` are ONE pair" was refuted by
72
+ * the same committed data as the first — stating it was a relapse, caught
73
+ * in review.
74
+ *
75
+ * The two fields are simply INDEPENDENT. `docs_at` dates a human reading
76
+ * the page for the row's ANSWERS; `docs_digest` names the body the watcher
77
+ * compares against. Nothing guarantees they were the same fetch, and no
78
+ * wording that implies they were can survive this table's own rows.
79
+ *
80
+ * Null where there is nothing to hash — a row whose `docs_url` is null —
81
+ * and null is also the pre-fill state `check_host_docs_digest --fetch
82
+ * --write` resolves. It is deliberately NOT part of the expiry test: a
83
+ * digest is evidence about the page, `expires` is this package's review-by
84
+ * date, and collapsing the two would make a vendor's CSS rebuild look like
85
+ * a lapsed citation.
86
+ */
87
+ docs_digest: string | null;
37
88
  probe_at: string | null;
38
89
  host_version: string | null;
39
90
  expires: string;
@@ -44,6 +95,21 @@ export interface SlotRow {
44
95
  native: string[];
45
96
  /** Exit code the host honours as a refusal here, or null if unestablished. */
46
97
  block_exit: number | null;
98
+ /**
99
+ * The date this pair was last answered, or null when it never was.
100
+ *
101
+ * `block_exit: null` alone cannot distinguish "looked, found none" from
102
+ * "nobody looked", and the whole point of this table is that a zero is a
103
+ * fact with a date on it. `lint_hook_manifest` requires the date; the
104
+ * parser keeps it nullable so a malformed table still parses into
105
+ * something the linter can report on rather than throwing first.
106
+ */
107
+ answered_at: string | null;
108
+ /**
109
+ * Citation for this pair, when it differs from the row's
110
+ * `verified.docs_url`. Absent means the row's URL is the citation.
111
+ */
112
+ docs_url: string | null;
47
113
  /** Entry-shape-specific extra (gemini's per-binding matcher). */
48
114
  matcher?: string;
49
115
  }
@@ -72,7 +138,18 @@ function _asString(v: unknown): string | null {
72
138
  }
73
139
 
74
140
  function _parse(text: string): HostLowering {
75
- const raw = parseYaml(text) as Record<string, unknown> | null;
141
+ return _structure(parseYaml(text) as Record<string, unknown> | null);
142
+ }
143
+
144
+ /**
145
+ * Build the table from an ALREADY-PARSED document.
146
+ *
147
+ * Split out of `_parse` so the compiled-JSON fast path below reaches exactly
148
+ * the same validation on exactly the same shape — the YAML step is the only
149
+ * thing it skips. A second structuring function would be a second thing to
150
+ * drift; there is one, and both paths call it.
151
+ */
152
+ function _structure(raw: Record<string, unknown> | null): HostLowering {
76
153
  const hostsRaw = raw?.['hosts'];
77
154
  if (typeof hostsRaw !== 'object' || hostsRaw === null) {
78
155
  throw new Error('host_lowering.yaml: missing `hosts:` map');
@@ -96,6 +173,7 @@ function _parse(text: string): HostLowering {
96
173
  verified = {
97
174
  docs_at: _asString(verifiedRaw['docs_at']),
98
175
  docs_url: _asString(verifiedRaw['docs_url']),
176
+ docs_digest: _asString(verifiedRaw['docs_digest']),
99
177
  probe_at: _asString(verifiedRaw['probe_at']),
100
178
  host_version: _asString(verifiedRaw['host_version']),
101
179
  expires,
@@ -110,6 +188,8 @@ function _parse(text: string): HostLowering {
110
188
  const row: SlotRow = {
111
189
  native,
112
190
  block_exit: typeof be === 'number' ? be : null,
191
+ answered_at: _asString(rv['answered_at']),
192
+ docs_url: _asString(rv['docs_url']),
113
193
  };
114
194
  if (typeof rv['matcher'] === 'string') row.matcher = rv['matcher'];
115
195
  slots.set(slot, row);
@@ -131,12 +211,57 @@ function _parse(text: string): HostLowering {
131
211
 
132
212
  let _cache: HostLowering | null = null;
133
213
 
134
- /** Parse the table once per process. Throws on a missing or malformed file. */
214
+ /**
215
+ * Parse the table once per process. Throws on a missing or malformed file.
216
+ *
217
+ * Tries the compiled sibling first and falls through to the YAML source on
218
+ * anything unexpected — missing, unreadable, malformed, or a fingerprint that
219
+ * does not match the live source. A stale or absent compiled file is therefore
220
+ * SLOW and never WRONG, which is the property that lets the optimisation ship
221
+ * without a second correctness argument.
222
+ */
135
223
  export function loadHostLowering(): HostLowering {
136
- if (_cache === null) _cache = _parse(fs.readFileSync(HOST_LOWERING_PATH, 'utf-8'));
224
+ if (_cache !== null) return _cache;
225
+ // Reading the YAML unconditionally costs ~0.03 ms — it is the PARSE that is
226
+ // expensive — so the fast path still skips the whole 11-15 ms.
227
+ const text = fs.readFileSync(HOST_LOWERING_PATH, 'utf-8');
228
+ let compiled: string | null;
229
+ try {
230
+ compiled = fs.readFileSync(HOST_LOWERING_JSON_PATH, 'utf-8');
231
+ } catch {
232
+ compiled = null;
233
+ }
234
+ _cache = resolveTable(text, compiled);
137
235
  return _cache;
138
236
  }
139
237
 
238
+ /**
239
+ * Pick the path and build the table — the whole decision, with no I/O.
240
+ *
241
+ * Split from `loadHostLowering` so the FALLBACK semantics are testable. They
242
+ * are the half that carries the risk: the fast path is easy to verify and a
243
+ * mis-shaped fallback would be a wrong table rather than a slow one. A test
244
+ * that could only reach the happy path would be watching the safe half.
245
+ */
246
+ export function resolveTable(yamlText: string, compiledText: string | null): HostLowering {
247
+ if (compiledText !== null) {
248
+ try {
249
+ const raw = JSON.parse(compiledText) as Record<string, unknown>;
250
+ const table = raw['table'];
251
+ if (
252
+ raw['fingerprint'] === tableFingerprint(yamlText) &&
253
+ typeof table === 'object' &&
254
+ table !== null
255
+ ) {
256
+ return _structure(table as Record<string, unknown>);
257
+ }
258
+ } catch {
259
+ // Malformed JSON → the source below. Never fail on the optimisation.
260
+ }
261
+ }
262
+ return _parse(yamlText);
263
+ }
264
+
140
265
  /** Test seam — drop the memoized table so a fixture can be loaded instead. */
141
266
  export function _resetHostLoweringCache(table?: HostLowering): void {
142
267
  _cache = table ?? null;