@event4u/agent-config 16.1.0 → 16.3.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 (467) 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 +98 -257
  5. package/README.md +14 -12
  6. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  7. package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
  8. package/dist/agent-src/commands/bug/fix.md +17 -2
  9. package/dist/agent-src/commands/feature/plan.md +1 -1
  10. package/dist/agent-src/commands/feature/roadmap.md +1 -1
  11. package/dist/agent-src/commands/fix/ci.md +1 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/jira-ticket.md +1 -1
  14. package/dist/agent-src/commands/roadmap/create.md +1 -1
  15. package/dist/agent-src/commands/roadmap/materialize.md +1 -1
  16. package/dist/agent-src/commands/roadmap/process-full.md +5 -5
  17. package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
  18. package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
  19. package/dist/agent-src/commands/roadmap.md +18 -4
  20. package/dist/agent-src/commands/tests/create.md +17 -3
  21. package/dist/agent-src/commands/tests/execute.md +22 -5
  22. package/dist/agent-src/commands/work.md +4 -0
  23. package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  24. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  25. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  26. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
  27. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  28. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  29. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  30. package/dist/agent-src/rules/active-remediation.md +1 -1
  31. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  32. package/dist/agent-src/rules/broken-access-control.md +1 -0
  33. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  34. package/dist/agent-src/rules/context-hygiene.md +1 -1
  35. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  36. package/dist/agent-src/rules/design-fidelity.md +13 -16
  37. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  39. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  40. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  41. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  42. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  43. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  44. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  45. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  46. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  47. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  48. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  49. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  50. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  51. package/dist/agent-src/rules/persona-governance.md +2 -2
  52. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  53. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  54. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  55. package/dist/agent-src/rules/runtime-safety.md +1 -0
  56. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  57. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  58. package/dist/agent-src/rules/session-canary.md +8 -62
  59. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  60. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  61. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  62. package/dist/agent-src/rules/tool-safety.md +1 -0
  63. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  64. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  65. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  66. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  67. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  68. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  69. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  70. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
  71. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  72. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  74. package/dist/agent-src/skills/design-review/SKILL.md +30 -4
  75. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  76. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
  78. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  79. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  80. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  81. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  82. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  83. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  84. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  85. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  86. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  87. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  88. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  89. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  90. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  91. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  92. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  93. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  94. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  95. package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
  96. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  97. package/dist/agent-src/templates/roadmaps.md +30 -0
  98. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  99. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  100. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  101. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  102. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  103. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  104. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  105. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  106. package/dist/cli/python/knowledge_ingest.js +1 -1
  107. package/dist/cli/python/workspace_drive.js +1 -1
  108. package/dist/cli/registry.js +1 -1
  109. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  110. package/dist/cli-delegate/check_update_banner.js +12 -1
  111. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  112. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  113. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  114. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  115. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  116. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  117. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  118. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  119. package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
  120. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  121. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  122. package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
  123. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  124. package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
  125. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  126. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  127. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  128. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  129. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  130. package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
  131. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
  132. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  133. package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
  134. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  135. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  136. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  137. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  138. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  139. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  140. package/dist/cli-delegate/cmd_conformance.js +1862 -809
  141. package/dist/cli-delegate/cmd_doctor.js +13 -6
  142. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  143. package/dist/cli-delegate/cmd_explain.js +5 -5
  144. package/dist/cli-delegate/cmd_export.js +11 -8
  145. package/dist/cli-delegate/cmd_fleet.js +3 -3
  146. package/dist/cli-delegate/cmd_handoff.js +10 -7
  147. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  148. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  149. package/dist/cli-delegate/cmd_migrate.js +2 -2
  150. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  151. package/dist/cli-delegate/cmd_preflight.js +6 -6
  152. package/dist/cli-delegate/cmd_prune.js +11 -8
  153. package/dist/cli-delegate/cmd_refresh.js +2 -1
  154. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  155. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  156. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  157. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  158. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  159. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  160. package/dist/cli-delegate/cmd_sync.js +11 -8
  161. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  162. package/dist/cli-delegate/cmd_update.js +1 -1
  163. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  164. package/dist/cli-delegate/cmd_validate.js +11 -8
  165. package/dist/cli-delegate/cmd_versions.js +1 -1
  166. package/dist/cli-delegate/deferral_carry.js +16 -0
  167. package/dist/cli-delegate/gate_execute.js +2 -2
  168. package/dist/cli-delegate/roadmap_gates.js +5 -5
  169. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  170. package/dist/discovery/deprecation-report.md +1 -1
  171. package/dist/discovery/discovery-manifest.json +132 -80
  172. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  173. package/dist/discovery/discovery-manifest.summary.md +3 -3
  174. package/dist/discovery/dormancy-report.md +1 -1
  175. package/dist/discovery/orphan-report.md +1 -1
  176. package/dist/discovery/packs.json +7 -5
  177. package/dist/discovery/trust-report.md +2 -2
  178. package/dist/discovery/workspaces.json +6 -4
  179. package/dist/hooks/dispatch.js +7972 -5186
  180. package/dist/hooks/dispatch.sha256 +1 -0
  181. package/dist/install/claudePathsPlan.js +47 -6
  182. package/dist/install/claudeRuleRewrite.js +7 -0
  183. package/dist/install/emit_host_rules_cli.js +39 -36
  184. package/dist/install/globalRuleScope.js +87 -0
  185. package/dist/install/host_rule_ownership.js +160 -0
  186. package/dist/install/install.mjs +2226 -1055
  187. package/dist/install/installThinLayer.js +261 -0
  188. package/dist/install/installedRuleLinks.js +176 -0
  189. package/dist/install/wizard-plan.js +24 -0
  190. package/dist/mcp/registry-manifest.json +3 -3
  191. package/dist/mcp/server.json +2 -2
  192. package/dist/mcp/server.mjs +40913 -34865
  193. package/dist/scripts/_lib/agent_settings.js +11 -0
  194. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  195. package/dist/scripts/_lib/installed_layer.js +378 -0
  196. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  197. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  198. package/dist/scripts/_lib/rule_injection.js +67 -10
  199. package/dist/scripts/_lib/rule_law_section.js +122 -0
  200. package/dist/scripts/_lib/thin_rules.js +458 -0
  201. package/dist/scripts/_lib/windsurf_render.js +60 -0
  202. package/dist/scripts/condense.js +14 -26
  203. package/dist/scripts/project_thin_rules.js +14 -294
  204. package/dist/server/schemas/settings.js +29 -0
  205. package/dist/shared/skillRanking.js +74 -13
  206. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  207. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  208. package/dist/ui/index.html +2 -2
  209. package/docs/contracts/settings-classes.md +10 -6
  210. package/package.json +4 -4
  211. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  212. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  213. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  214. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  215. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
  216. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  217. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  218. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  219. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  220. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  221. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  222. package/src/config/agent-settings.template.yml +91 -22
  223. package/src/config/ci-local-parity.yml +18 -0
  224. package/src/config/continuity-surface.json +9 -2
  225. package/src/config/evaluator-budgets.json +4 -4
  226. package/src/config/gate-coverage.yml +327 -3
  227. package/src/config/gate-violation-baselines.json +21 -9
  228. package/src/config/hook-bundle-budget.json +25 -0
  229. package/src/config/hook-latency-budget.json +21 -0
  230. package/src/config/hook-token-budget.json +8 -3
  231. package/src/config/host-instruction-limits.json +33 -0
  232. package/src/config/metric-registry.yml +114 -0
  233. package/src/config/placeholder-drift-budget.json +10 -0
  234. package/src/config/routing-coverage-seed.json +38 -3
  235. package/src/config/rule-activation-census.json +7 -3
  236. package/src/config/rule-consequence-class.json +161 -0
  237. package/src/config/rule-law-ceilings.json +99 -0
  238. package/src/install/claudePathsPlan.ts +46 -4
  239. package/src/install/claudeRuleRewrite.ts +10 -1
  240. package/src/install/emit_host_rules_cli.ts +43 -34
  241. package/src/install/globalRuleScope.ts +109 -0
  242. package/src/install/host_rule_ownership.ts +170 -0
  243. package/src/install/installThinLayer.ts +312 -0
  244. package/src/install/installedRuleLinks.ts +220 -0
  245. package/src/install/wizard-plan.ts +24 -0
  246. package/src/scripts/_cli/cmd_doctor.ts +28 -90
  247. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  248. package/src/scripts/_cli/doctor_execution.ts +100 -17
  249. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  250. package/src/scripts/_dispatch.bash +16 -2
  251. package/src/scripts/_lib/agent_settings.ts +11 -0
  252. package/src/scripts/_lib/body_portable.ts +102 -0
  253. package/src/scripts/_lib/cc_transcript.ts +132 -1
  254. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  255. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  256. package/src/scripts/_lib/estate_offsets.ts +241 -0
  257. package/src/scripts/_lib/exemption_shape.ts +143 -0
  258. package/src/scripts/_lib/fnv.ts +23 -0
  259. package/src/scripts/_lib/foreign_scan.ts +106 -0
  260. package/src/scripts/_lib/forge_protection.ts +114 -0
  261. package/src/scripts/_lib/forge_reader.ts +558 -0
  262. package/src/scripts/_lib/gitignore_block.ts +314 -0
  263. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  264. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  265. package/src/scripts/_lib/hook_settings.ts +73 -0
  266. package/src/scripts/_lib/host_capability.ts +40 -19
  267. package/src/scripts/_lib/host_env_write.ts +111 -0
  268. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  269. package/src/scripts/_lib/installed_layer.ts +538 -0
  270. package/src/scripts/_lib/json_merge.ts +82 -0
  271. package/src/scripts/_lib/json_pointers.ts +93 -11
  272. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  273. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  274. package/src/scripts/_lib/machine_wake.ts +16 -3
  275. package/src/scripts/_lib/neighbour_census.ts +760 -0
  276. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  277. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  278. package/src/scripts/_lib/obligations.ts +119 -8
  279. package/src/scripts/_lib/probe_inputs.ts +271 -0
  280. package/src/scripts/_lib/prompt_shape.ts +46 -0
  281. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  282. package/src/scripts/_lib/release_highlights.ts +2 -1
  283. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  284. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  285. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  286. package/src/scripts/_lib/review_coverage.ts +134 -0
  287. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  288. package/src/scripts/_lib/rule_injection.ts +130 -10
  289. package/src/scripts/_lib/rule_law_section.ts +147 -0
  290. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  291. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  292. package/src/scripts/_lib/skill_origin.ts +254 -0
  293. package/src/scripts/_lib/structural_hiding.ts +622 -0
  294. package/src/scripts/_lib/structured_ask.ts +64 -21
  295. package/src/scripts/_lib/thin_rules.ts +540 -0
  296. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  297. package/src/scripts/_lib/turn_end_refusals.ts +600 -4
  298. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  299. package/src/scripts/_lib/verification_command.ts +324 -0
  300. package/src/scripts/_lib/verification_evidence.ts +647 -0
  301. package/src/scripts/_lib/verify_clause.ts +280 -0
  302. package/src/scripts/_lib/windsurf_render.ts +63 -0
  303. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  304. package/src/scripts/ai_council/clients.ts +26 -57
  305. package/src/scripts/ai_council/config.ts +1 -1
  306. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  307. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  308. package/src/scripts/audit_skill_overlap.ts +261 -0
  309. package/src/scripts/before_complete_hook.ts +573 -25
  310. package/src/scripts/bench_hook_latency.ts +412 -5
  311. package/src/scripts/bench_touched_file_quality.ts +238 -0
  312. package/src/scripts/cache_realization_report.ts +61 -1
  313. package/src/scripts/check_argument_hint.ts +287 -0
  314. package/src/scripts/check_claims.ts +125 -1
  315. package/src/scripts/check_enforcement_coverage.ts +38 -9
  316. package/src/scripts/check_enforcement_matrix.ts +621 -22
  317. package/src/scripts/check_estate_count.ts +130 -153
  318. package/src/scripts/check_finding_dispositions.ts +138 -7
  319. package/src/scripts/check_generator_sync.ts +71 -0
  320. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  321. package/src/scripts/check_host_docs_digest.ts +786 -0
  322. package/src/scripts/check_host_format_column.ts +559 -0
  323. package/src/scripts/check_input_references.ts +228 -0
  324. package/src/scripts/check_inputs_parity.ts +235 -0
  325. package/src/scripts/check_kernel_edit_ratified.ts +89 -9
  326. package/src/scripts/check_kill_switch_table.ts +294 -0
  327. package/src/scripts/check_memory.ts +7 -1
  328. package/src/scripts/check_placeholder_drift.ts +221 -0
  329. package/src/scripts/check_read_surface_coverage.ts +515 -0
  330. package/src/scripts/check_release_highlights.ts +10 -0
  331. package/src/scripts/check_release_holds.ts +2 -1
  332. package/src/scripts/check_release_pr_shape.ts +38 -15
  333. package/src/scripts/check_routing_coverage.ts +378 -11
  334. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  335. package/src/scripts/check_static_parity.ts +17 -0
  336. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  337. package/src/scripts/ci_settle.ts +145 -19
  338. package/src/scripts/closure_scan.ts +115 -1
  339. package/src/scripts/code_graph/detect.ts +127 -7
  340. package/src/scripts/code_graph/foreign.ts +194 -0
  341. package/src/scripts/code_graph/query.ts +217 -4
  342. package/src/scripts/code_graph/types.ts +26 -2
  343. package/src/scripts/code_graph/validate.ts +3 -1
  344. package/src/scripts/code_graph/verbs.ts +103 -0
  345. package/src/scripts/compile_hook_manifest.ts +90 -14
  346. package/src/scripts/condense.ts +14 -27
  347. package/src/scripts/estate_exemption_shape.ts +202 -0
  348. package/src/scripts/external_sources_denylist.json +29 -4
  349. package/src/scripts/generate_host_cost_table.ts +1 -1
  350. package/src/scripts/hook_manifest.json +1 -1
  351. package/src/scripts/hook_manifest.yaml +192 -14
  352. package/src/scripts/hooks/block_config_weakening.ts +294 -4
  353. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  354. package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
  355. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  356. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  357. package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
  358. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  359. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  360. package/src/scripts/hooks/concern_registry.ts +2 -0
  361. package/src/scripts/hooks/concern_timings.ts +94 -0
  362. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  363. package/src/scripts/hooks/design_pass_hook.ts +100 -9
  364. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  365. package/src/scripts/hooks/dispatch_hook.ts +77 -71
  366. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  367. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  368. package/src/scripts/hooks/evidence_independence.ts +1 -2
  369. package/src/scripts/hooks/exit_codes.ts +129 -0
  370. package/src/scripts/hooks/host_lowering.json +1 -0
  371. package/src/scripts/hooks/host_lowering.ts +146 -3
  372. package/src/scripts/hooks/host_lowering.yaml +262 -46
  373. package/src/scripts/hooks/host_semantics.ts +30 -2
  374. package/src/scripts/hooks/injection_budget.ts +9 -2
  375. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  376. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  377. package/src/scripts/hooks/memory_recall_hook.ts +1 -2
  378. package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
  379. package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
  380. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  381. package/src/scripts/hooks/payload_stub.ts +10 -1
  382. package/src/scripts/hooks/permission_gate.ts +1 -2
  383. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  384. package/src/scripts/hooks/replay_hook.ts +2 -1
  385. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  386. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  387. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  388. package/src/scripts/hooks/rule_inject_hook.ts +765 -68
  389. package/src/scripts/hooks/run_continuation_hook.ts +13 -15
  390. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  391. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  392. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  393. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  394. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  395. package/src/scripts/hooks/table_fingerprint.ts +70 -0
  396. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  397. package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
  398. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  399. package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
  400. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  401. package/src/scripts/hooks_status.ts +112 -0
  402. package/src/scripts/injection_scan_hook.ts +7 -0
  403. package/src/scripts/install.ts +75 -189
  404. package/src/scripts/installed_layer_report.ts +103 -0
  405. package/src/scripts/language_mirror_hook.ts +15 -6
  406. package/src/scripts/learning_sidecar.ts +119 -8
  407. package/src/scripts/lint_deny_text.ts +419 -0
  408. package/src/scripts/lint_exit_codes.ts +262 -0
  409. package/src/scripts/lint_hook_manifest.ts +108 -0
  410. package/src/scripts/lint_major_migration_sections.ts +156 -26
  411. package/src/scripts/lint_pack_boundaries.ts +63 -5
  412. package/src/scripts/lint_roadmap_blockers.ts +78 -12
  413. package/src/scripts/lint_rule_law_section.ts +517 -0
  414. package/src/scripts/lint_skill_descriptions.ts +1 -1
  415. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  416. package/src/scripts/lint_workflow_security.ts +161 -5
  417. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  418. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  419. package/src/scripts/mcp_server/prompts.ts +142 -1
  420. package/src/scripts/measure_routing_signal.ts +54 -16
  421. package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
  422. package/src/scripts/measure_turn_end_gate.ts +193 -0
  423. package/src/scripts/memory_learn_hook.ts +91 -6
  424. package/src/scripts/memory_lookup.ts +1 -1
  425. package/src/scripts/model_rule_injection.ts +52 -5
  426. package/src/scripts/prepack-check.mjs +53 -0
  427. package/src/scripts/project_thin_rules.ts +34 -327
  428. package/src/scripts/release_publication.ts +13 -1
  429. package/src/scripts/report_evidence_temperature.ts +657 -0
  430. package/src/scripts/report_installed_rule_links.ts +157 -0
  431. package/src/scripts/report_invocation_surface.ts +383 -0
  432. package/src/scripts/report_label_agreement.ts +233 -0
  433. package/src/scripts/report_obligation_carriers.ts +699 -0
  434. package/src/scripts/report_obligation_mechanism.ts +230 -0
  435. package/src/scripts/report_obligation_writer_split.ts +236 -0
  436. package/src/scripts/report_single_token_triggers.ts +277 -0
  437. package/src/scripts/report_skill_menu_census.ts +208 -0
  438. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  439. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  440. package/src/scripts/report_verification_record_coverage.ts +306 -0
  441. package/src/scripts/roadmap_verify_share.ts +213 -0
  442. package/src/scripts/rule_activation_census.ts +5 -2
  443. package/src/scripts/rule_trigger_eval.ts +9 -9
  444. package/src/scripts/schemas/command.schema.json +44 -0
  445. package/src/scripts/schemas/review-findings.schema.json +7 -2
  446. package/src/scripts/schemas/rule.schema.json +5 -0
  447. package/src/scripts/schemas/skill.schema.json +44 -0
  448. package/src/scripts/self_review_gate.ts +421 -17
  449. package/src/scripts/semantic_parity_census.ts +314 -0
  450. package/src/scripts/session_eol_report.ts +54 -1
  451. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  452. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  453. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  454. package/src/scripts/sync_gitignore.ts +29 -235
  455. package/src/scripts/trigger_eval_grandfather.json +0 -7
  456. package/src/scripts/trigger_eval_rotation.ts +122 -27
  457. package/src/scripts/ui_conformance_probe.ts +156 -4
  458. package/src/scripts/update_prices.ts +85 -3
  459. package/src/scripts/write_bundle_digest.mjs +74 -0
  460. package/src/server/schemas/settings.ts +41 -0
  461. package/src/shared/skillRanking.ts +117 -11
  462. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  463. package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
  464. package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
  465. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
  466. package/dist/ui/assets/index-DH1xc62v.js +0 -29
  467. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
@@ -53,7 +53,33 @@ import * as fs from 'node:fs';
53
53
  import * as os from 'node:os';
54
54
  import * as path from 'node:path';
55
55
  import { fileURLToPath } from 'node:url';
56
+ import { parse as parseYaml } from 'yaml';
57
+
56
58
  import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
59
+ // The per-concern SLA surface moved to its own module when this file crossed
60
+ // the 1,500-line source ceiling (`check_source_size_budget`). Re-exported here
61
+ // so every caller's import path is unchanged and the move is invisible outside
62
+ // these two files.
63
+ import {
64
+ ASK_PROBE,
65
+ mergeProbeSamples,
66
+ PROBE_TARGET,
67
+ slaOverruns,
68
+ type ToolShape,
69
+ windowState,
70
+ } from './_lib/concern_sla_window.js';
71
+
72
+ export {
73
+ ASK_PROBE,
74
+ mergeProbeSamples,
75
+ PROBE_TARGET,
76
+ slaOverruns,
77
+ windowState,
78
+ type BoundedConcern,
79
+ type SlaOverrun,
80
+ type ToolShape,
81
+ type WindowState,
82
+ } from './_lib/concern_sla_window.js';
57
83
 
58
84
  const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
59
85
  const BUNDLE = path.join(REPO_ROOT, 'dist', 'hooks', 'dispatch.js');
@@ -136,7 +162,7 @@ let PAYLOAD_BYTES = 0;
136
162
  */
137
163
  const TOOL_EVENTS: ReadonlySet<string> = new Set(['pre_tool_use', 'post_tool_use']);
138
164
 
139
- function syntheticPayload(event: string, workspace: string): string {
165
+ function syntheticPayload(event: string, workspace: string, tool?: ToolShape): string {
140
166
  // Claude-shaped payload; concerns read tool_name/tool_input for
141
167
  // pre/post_tool_use. A plain Read is the common non-matching case —
142
168
  // the fast path consumers pay on every tool call.
@@ -144,8 +170,8 @@ function syntheticPayload(event: string, workspace: string): string {
144
170
  session_id: 'bench-hook-latency',
145
171
  cwd: workspace,
146
172
  hook_event_name: event,
147
- tool_name: 'Read',
148
- tool_input: { file_path: path.join(workspace, 'README.md') },
173
+ tool_name: tool?.tool_name ?? 'Read',
174
+ tool_input: tool?.tool_input ?? { file_path: path.join(workspace, 'README.md') },
149
175
  };
150
176
  if (PAYLOAD_BYTES > 0 && TOOL_EVENTS.has(event)) {
151
177
  // Filler with no JSON metacharacters, so the cost measured is
@@ -188,9 +214,12 @@ export function benchEvent(
188
214
  runs: number,
189
215
  workspace: string,
190
216
  via: InvocationPath = 'bundle',
217
+ timingsSink?: string,
218
+ replay = true,
219
+ tool?: ToolShape,
191
220
  ): EventResult {
192
221
  const durations: number[] = [];
193
- const payload = syntheticPayload(event, workspace);
222
+ const payload = syntheticPayload(event, workspace, tool);
194
223
  const command: [string, string[]] =
195
224
  via === 'cli'
196
225
  ? ['bash', ['-c', hooksJsonCommand(event)]]
@@ -211,7 +240,21 @@ export function benchEvent(
211
240
  const proc = spawnSync(command[0], command[1], {
212
241
  input: payload,
213
242
  encoding: 'utf-8',
214
- env: { ...process.env, AGENT_CONFIG_REPLAY: '1', CLAUDE_PROJECT_DIR: workspace },
243
+ env: {
244
+ ...process.env,
245
+ // Replay is the DEFAULT and the gated slot numbers depend on it
246
+ // -- the pre-registered budget was measured with it on. The
247
+ // per-concern SLA pass below turns it OFF deliberately; see
248
+ // `concernSlaPass`.
249
+ ...(replay ? { AGENT_CONFIG_REPLAY: '1' } : {}),
250
+ CLAUDE_PROJECT_DIR: workspace,
251
+ // Step 3.2: arm the dispatcher's per-concern timings sink for the
252
+ // duration of the bench. Unset on a real dispatch, so this costs one
253
+ // `process.env` lookup outside the harness.
254
+ ...(timingsSink === undefined
255
+ ? {}
256
+ : { AGENT_CONFIG_HOOK_TIMINGS: timingsSink }),
257
+ },
215
258
  timeout: 60000,
216
259
  });
217
260
  const elapsed = performance.now() - started;
@@ -517,6 +560,277 @@ export function capFor(budget: Budget, event: string): number {
517
560
  return capsFor(budget, event)[0]?.cap_ms ?? Number.POSITIVE_INFINITY;
518
561
  }
519
562
 
563
+ /**
564
+ * Per-concern latency (step 3.2 of `road-to-a-kernel-that-guards-its-plumbing`).
565
+ *
566
+ * This is deliberately not the slot number divided by the concern count.
567
+ * The slot rows above measure a whole dispatch: spawn + interpreter start +
568
+ * bundle load + the concern chain. Step 3.3 wants to give each BLOCKING
569
+ * concern its own timeout, and a timeout derived from a slot p95 would be a
570
+ * number about the runner, not about the concern — on a slow host every
571
+ * concern would inherit the same inflated bound, and on a fast one the
572
+ * slowest concern would be bounded by the average of its neighbours.
573
+ *
574
+ * So the dispatcher reports its own per-concern `duration_ms` into a JSONL
575
+ * sink (`_write_concern_timings`, armed by `AGENT_CONFIG_HOOK_TIMINGS`) and
576
+ * this reads the distribution back. The samples are the concern's own work,
577
+ * measured inside the process that ran it, with the spawn term already
578
+ * excluded by construction.
579
+ *
580
+ * The report says `not_measured`, never `0`.
581
+ * A blocking concern with no samples in the sink is reported `not_measured`.
582
+ * It is the whole point of the step's verify line and it is load-bearing in a
583
+ * way that is easy to lose: `0` is a legal p95 for a fast concern, so a
584
+ * zero-filled row for a concern that never ran is indistinguishable from a
585
+ * real measurement of the fastest possible one — and step 3.3 would then
586
+ * compute a timeout of `0 x 3` and refuse every call. The absence has to stay
587
+ * visible as an absence all the way to the report.
588
+ *
589
+ * A concern can be absent for ordinary reasons: it is bound to a slot this
590
+ * bench does not exercise, or its `tools:` filter skipped it on the synthetic
591
+ * payload. That is information, not a failure, which is why this reports and
592
+ * does not gate.
593
+ *
594
+ * ONE NUMBER PER CONCERN, AND IT IS THE MAX OVER EVENTS, NOT THE P95 OF THE
595
+ * UNION. A concern bound to several slots has a distribution per slot, and the
596
+ * first version of this took a p95 across all of them pooled — which is a
597
+ * number describing no slot in particular and is biased by whichever slot the
598
+ * bench happens to run most. Step 3.3 derives ONE timeout per concern
599
+ * (`sla_ms x 3`) and the dispatcher applies it wherever the concern runs, so
600
+ * the bound has to hold on the concern's WORST slot or it refuses there. The
601
+ * row therefore carries the max of the per-event p95s and names the event that
602
+ * produced it, so a reader can see which slot sets the bound.
603
+ */
604
+ export interface ConcernLatencyRow {
605
+ concern: string;
606
+ fail_closed: boolean;
607
+ /** Sample count found in the sink. */
608
+ n: number;
609
+ /**
610
+ * p95 of the concern's own durations in MICROseconds, or null when it was
611
+ * never timed.
612
+ *
613
+ * Microseconds, not milliseconds, because the unit was a defect. Every
614
+ * concern in the static registry runs in-process and finishes in well
615
+ * under a millisecond, so the dispatcher's floored `duration_ms` reported
616
+ * exactly `0` for all nine blocking concerns on the first run of this
617
+ * report. A `0 ms` p95 is not a missing measurement and would not be
618
+ * caught by the `not_measured` branch below — it is a real-looking
619
+ * number that step 3.3 would multiply by three to get a timeout of zero.
620
+ */
621
+ p95_us: number | null;
622
+ /** Which event produced `p95_us` — the slowest slot this concern runs on. */
623
+ p95_event: string | null;
624
+ /** The registered SLA in milliseconds, or null when none is registered yet. */
625
+ sla_ms: number | null;
626
+ /** The budget carried a value for this concern that is not a number. */
627
+ sla_malformed: boolean;
628
+ }
629
+
630
+ /** Placeholder printed for a number the run could not establish. */
631
+ export const NOT_MEASURED = 'not_measured';
632
+
633
+ /**
634
+ * Every `severity: blocking` concern in the manifest, in declaration order.
635
+ *
636
+ * Read from the manifest rather than hardcoded: the step was written against
637
+ * eight blocking concerns and `block-plumbing-writes` (step 1.2) made it nine
638
+ * in the same roadmap. A list in this file would have been stale before the
639
+ * phase closed.
640
+ */
641
+ export function blockingConcerns(manifestPath: string = MANIFEST_PATH): {
642
+ name: string;
643
+ fail_closed: boolean;
644
+ }[] {
645
+ const raw = parseYaml(fs.readFileSync(manifestPath, 'utf-8'), { version: '1.1' }) as
646
+ | { concerns?: Record<string, { severity?: string; fail_closed?: boolean }> }
647
+ | null;
648
+ const concerns = raw?.concerns ?? {};
649
+ return Object.entries(concerns)
650
+ .filter(([, def]) => String(def?.severity ?? '').trim().toLowerCase() === 'blocking')
651
+ .map(([name, def]) => ({ name, fail_closed: Boolean(def?.fail_closed) }));
652
+ }
653
+
654
+ /**
655
+ * Read the JSONL sink into per-concern sample arrays.
656
+ *
657
+ * A malformed or unreadable line is skipped rather than fatal — the sink is a
658
+ * measurement aid, and a bench that dies on one bad row would convert an
659
+ * observation into an outage. A skipped line lowers `n`, which the report
660
+ * prints, so the reduction is visible rather than silent.
661
+ */
662
+ export interface ConcernTimings {
663
+ /** concern -> event -> samples. Nested, never flattened; see below. */
664
+ byConcernEvent: Map<string, Map<string, number[]>>;
665
+ /** Lines present in the sink that could not be read as a sample. */
666
+ skipped: number;
667
+ }
668
+
669
+ export function readConcernTimings(sinkPath: string): ConcernTimings {
670
+ const byConcernEvent = new Map<string, Map<string, number[]>>();
671
+ let skipped = 0;
672
+ let text: string;
673
+ try {
674
+ text = fs.readFileSync(sinkPath, 'utf-8');
675
+ } catch {
676
+ return { byConcernEvent, skipped };
677
+ }
678
+ for (const line of text.split('\n')) {
679
+ const trimmed = line.trim();
680
+ if (trimmed === '') continue;
681
+ let row: { concern?: unknown; duration_us?: unknown; event?: unknown };
682
+ try {
683
+ row = JSON.parse(trimmed) as typeof row;
684
+ } catch {
685
+ skipped += 1;
686
+ continue;
687
+ }
688
+ const name = typeof row.concern === 'string' ? row.concern : '';
689
+ const us = row.duration_us;
690
+ if (name === '' || typeof us !== 'number' || !Number.isFinite(us)) {
691
+ skipped += 1;
692
+ continue;
693
+ }
694
+ const event = typeof row.event === 'string' && row.event !== '' ? row.event : 'unknown';
695
+ let perEvent = byConcernEvent.get(name);
696
+ if (perEvent === undefined) {
697
+ perEvent = new Map<string, number[]>();
698
+ byConcernEvent.set(name, perEvent);
699
+ }
700
+ const bucket = perEvent.get(event);
701
+ if (bucket === undefined) perEvent.set(event, [us]);
702
+ else bucket.push(us);
703
+ }
704
+ return { byConcernEvent, skipped };
705
+ }
706
+
707
+ /**
708
+ * One row per blocking concern — measured p95 beside the registered SLA.
709
+ *
710
+ * Both columns are independently nullable, and the two nulls mean different
711
+ * things: a null `p95_ms` is "this run did not time it", a null `sla_ms` is
712
+ * "no SLA is registered yet". Collapsing them would hide the state step 3.3
713
+ * needs to distinguish — a concern it may bound, versus one it may not.
714
+ */
715
+ export function perConcernRows(
716
+ blocking: readonly { name: string; fail_closed: boolean }[],
717
+ samples: ReadonlyMap<string, ReadonlyMap<string, number[]>>,
718
+ sla: Readonly<Record<string, unknown>> = {},
719
+ ): ConcernLatencyRow[] {
720
+ return blocking.map(({ name, fail_closed }) => {
721
+ const perEvent = samples.get(name);
722
+ let n = 0;
723
+ let worst: number | null = null;
724
+ let worstEvent: string | null = null;
725
+ for (const [event, xs] of perEvent ?? []) {
726
+ n += xs.length;
727
+ if (xs.length === 0) continue;
728
+ const sorted = [...xs].sort((a, b) => a - b);
729
+ const p = Math.round(percentile(sorted, 95));
730
+ if (worst === null || p > worst) {
731
+ worst = p;
732
+ worstEvent = event;
733
+ }
734
+ }
735
+ const registered = sla[name];
736
+ // A value that is present but not a number is NOT silently `null`. The
737
+ // two states print differently and mean different things: `null` is a
738
+ // registered absence, a malformed entry is a typo in the budget file
739
+ // that would otherwise read as a missing measurement.
740
+ const slaMalformed =
741
+ registered !== undefined &&
742
+ registered !== null &&
743
+ (typeof registered !== 'number' || !Number.isFinite(registered));
744
+ return {
745
+ concern: name,
746
+ fail_closed,
747
+ n,
748
+ p95_us: worst,
749
+ p95_event: worstEvent,
750
+ sla_ms: typeof registered === 'number' && Number.isFinite(registered) ? registered : null,
751
+ sla_malformed: slaMalformed,
752
+ };
753
+ });
754
+ }
755
+
756
+ /**
757
+ * Collect per-concern samples with replay mode OFF.
758
+ *
759
+ * MEASURED, and the reason this is a separate pass rather than a read of the
760
+ * gated run. `AGENT_CONFIG_REPLAY=1` suppresses state writes (`state_io.ts`),
761
+ * and several blocking concerns do most of their work through state — so the
762
+ * gated bench, which needs replay on because the pre-registered slot budget
763
+ * was measured with it, times those concerns at a fraction of their real cost.
764
+ *
765
+ * A/B over this tree, n=25 per cell, 2026-09-30:
766
+ *
767
+ * verify-before-complete live 1249 us replay 310 us x4.03
768
+ * run-continuation live 431 us replay 220 us x1.96
769
+ * journal-record live 175 us replay 141 us x1.24
770
+ * block-no-verify live 779 us replay 642 us x1.21
771
+ * (everything else within +/-10%, i.e. noise)
772
+ *
773
+ * Step 3.3 derives a timeout of `sla_ms x 3`. An SLA of 310 us for a concern
774
+ * whose real p95 is 1249 us yields a 930 us bound that the concern exceeds on
775
+ * an ORDINARY run — which is Risk 1 of the owning roadmap, reached through a
776
+ * measurement instead of through a guess. So the numbers step 3.3 will one day
777
+ * consume have to come from a pass that runs the concerns for real.
778
+ *
779
+ * It runs against a throwaway workspace, so the state writes it permits land in
780
+ * a temp dir and nothing in the repository is touched. It gates NOTHING and
781
+ * contributes to no budget row: it is an observation, and the slot rows above
782
+ * remain the only gated numbers.
783
+ */
784
+ export function concernSlaPass(
785
+ runs: number,
786
+ workspace: string,
787
+ sinkPath: string,
788
+ events: readonly string[] = EVENTS,
789
+ ): void {
790
+ for (const event of events) {
791
+ benchEvent(event, runs, workspace, 'bundle', sinkPath, false);
792
+ }
793
+ // One extra `pre_tool_use` pass under the ask-shaped probe, so the ninth
794
+ // blocking concern is measured rather than waived. See `ASK_PROBE`: every
795
+ // other blocking concern is reached by the default `Read` payload, and
796
+ // `one-question-per-ask`'s `tools:` filter means no number of `Read` runs
797
+ // will ever produce a sample for it.
798
+ //
799
+ // IT WRITES TO ITS OWN SINK, AND THAT IS THE WHOLE POINT OF THE SPLIT.
800
+ // The probe dispatches the whole `pre_tool_use` event, not one concern —
801
+ // only the manifest's `tools:` filter isolates the target, so every
802
+ // UNFILTERED concern on that slot runs under the ask payload too and emits
803
+ // a timing. Writing both passes to one sink pools them under the same
804
+ // `pre_tool_use` key, and the neighbours' p95 then describes a mixture of
805
+ // two payload shapes while the comment above claims their number comes
806
+ // from the default shape alone. Measured, not hypothesised: the sample
807
+ // count for every `pre_tool_use` concern went from n=20 to n=40 the moment
808
+ // the probe landed, which is the contamination showing up in the report's
809
+ // own output. Caught by an independent review, 2026-10-01.
810
+ //
811
+ // `mergeProbeSamples` then takes ONLY the target concern across. The
812
+ // neighbours' ask-shaped samples are measured and discarded, which is
813
+ // correct: they are a real cost of a payload shape the gated numbers do
814
+ // not model.
815
+ const probeSink = `${sinkPath}.probe`;
816
+ benchEvent('pre_tool_use', runs, workspace, 'bundle', probeSink, false, ASK_PROBE);
817
+ mergeProbeSamples(probeSink, sinkPath, PROBE_TARGET);
818
+ }
819
+
820
+ /** Render one row. `null` prints `not_measured` — never `0`. */
821
+ export function renderConcernRow(row: ConcernLatencyRow): string {
822
+ // Three decimals, because the numbers are microseconds and the whole point
823
+ // of the unit change is that rounding them to whole milliseconds produced
824
+ // nine zeroes.
825
+ const p95 = row.p95_us === null ? NOT_MEASURED : `${(row.p95_us / 1000).toFixed(3)} ms`;
826
+ const sla = row.sla_malformed ? 'MALFORMED' : row.sla_ms === null ? NOT_MEASURED : `${row.sla_ms} ms`;
827
+ const via = row.p95_event === null ? '' : `, worst on ${row.p95_event}`;
828
+ return (
829
+ ` ${row.concern.padEnd(26)} p95 ${p95.padStart(13)} · sla ${sla.padStart(13)} ` +
830
+ `(n=${row.n}${via}${row.fail_closed ? ', fail_closed' : ''})`
831
+ );
832
+ }
833
+
520
834
  function loadJson<T>(p: string): T | null {
521
835
  try {
522
836
  return JSON.parse(fs.readFileSync(p, 'utf-8')) as T;
@@ -531,6 +845,14 @@ export interface Budget {
531
845
  any_hook_event: { p95_ci: number; blocking?: boolean };
532
846
  };
533
847
  regression_gate: { max_regression_pct: number };
848
+ /**
849
+ * Per-concern SLA in milliseconds, keyed by concern name (step 3.2).
850
+ *
851
+ * A `null` value is a registered ABSENCE — the concern is known and its
852
+ * bound has deliberately not been set yet. Step 3.3 may only bound a
853
+ * concern whose value here is a number.
854
+ */
855
+ concern_sla_ms?: Record<string, number | null>;
534
856
  gate_remeasure?: { attempts?: number };
535
857
  normalized?: {
536
858
  observe_only?: boolean;
@@ -770,6 +1092,7 @@ export function main(argv: string[] = process.argv.slice(2)): number {
770
1092
  }
771
1093
  }
772
1094
  const budget = loadJson<Budget>(BUDGET_PATH);
1095
+ let concernRows: ConcernLatencyRow[] = [];
773
1096
  if (budget === null) {
774
1097
  process.stderr.write(`bench_hook_latency: budget file missing/invalid at ${BUDGET_PATH}\n`);
775
1098
  return 2;
@@ -796,6 +1119,10 @@ export function main(argv: string[] = process.argv.slice(2)): number {
796
1119
  }
797
1120
 
798
1121
  const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'ac-hook-bench-'));
1122
+ // Step 3.2's per-concern sink. Lives beside the workspace and is torn down
1123
+ // with it, so a bench run leaves nothing behind and a stale file from an
1124
+ // earlier run can never be read as this run's distribution.
1125
+ const timingsSink = path.join(workspace, 'concern-timings.jsonl');
799
1126
  const results: EventResult[] = [];
800
1127
  let control: EventResult | null = null;
801
1128
  try {
@@ -826,6 +1153,86 @@ export function main(argv: string[] = process.argv.slice(2)): number {
826
1153
  `${'control (node -e 0)'.padEnd(20)} p50 ${String(control.p50_ms).padStart(5)} ms · p95 ${String(control.p95_ms).padStart(5)} ms · max ${String(control.max_ms).padStart(5)} ms (n=${control.runs})\n`,
827
1154
  );
828
1155
 
1156
+ // Step 3.2 — per-concern p95 beside the registered SLA, for every
1157
+ // BLOCKING concern. Its own pass, with replay OFF: the gated slot runs
1158
+ // above need replay on, and replay times several concerns at a
1159
+ // fraction of their real cost. `concernSlaPass` carries the A/B.
1160
+ //
1161
+ // Printed on every run, gate or not: a row that appeared only under
1162
+ // `--gate` would make the number step 3.3 depends on unavailable
1163
+ // exactly where it is cheapest to read, which is the same mistake the
1164
+ // composite row above documents having made once. It reports and never
1165
+ // fails — the SLAs it feeds are not armed, and a measurement that can
1166
+ // red a build before its bar exists is the guessed-threshold failure
1167
+ // Risk 1 of the owning roadmap names.
1168
+ concernSlaPass(Math.min(runs, 20), workspace, timingsSink);
1169
+ const timings = readConcernTimings(timingsSink);
1170
+ concernRows = perConcernRows(
1171
+ blockingConcerns(),
1172
+ timings.byConcernEvent,
1173
+ budget?.concern_sla_ms ?? {},
1174
+ );
1175
+ if (timings.skipped > 0) {
1176
+ // The CAUSE of a lowered `n`, not only its effect. An earlier
1177
+ // version claimed the reduction was visible because `n` was
1178
+ // printed; `n` shows that fewer samples arrived, never that some
1179
+ // were unreadable.
1180
+ process.stdout.write(
1181
+ ` ⚠️ ${timings.skipped} unreadable line(s) in the timings sink — samples lost, n is lower than the run count\n`,
1182
+ );
1183
+ }
1184
+ process.stdout.write(
1185
+ `blocking concerns (${concernRows.length}) — per-concern p95 vs registered SLA:\n`,
1186
+ );
1187
+ for (const row of concernRows) {
1188
+ process.stdout.write(`${renderConcernRow(row)}\n`);
1189
+ }
1190
+ const unmeasured = concernRows.filter((r) => r.p95_us === null).length;
1191
+ if (unmeasured > 0) {
1192
+ process.stdout.write(
1193
+ ` ℹ️ ${unmeasured} of ${concernRows.length} not measured by this run — ` +
1194
+ 'a concern bound to a slot this bench does not exercise, or skipped by its ' +
1195
+ "`tools:` filter, has no sample. Reported as an absence, never as 0.\n",
1196
+ );
1197
+ }
1198
+ // The warn-only window step 3.3 names, read out loud. Observe-only: it
1199
+ // reports and never changes the exit code, because the bound it checks
1200
+ // is not armed anywhere and a measurement that can red a build before
1201
+ // its bar is validated is Risk 1 of the owning roadmap.
1202
+ const overruns = slaOverruns(concernRows);
1203
+ const w = windowState(concernRows);
1204
+ if (w.registered === 0) {
1205
+ process.stdout.write(
1206
+ ' ℹ️ no `concern_sla_ms` registered — the warn-only window has not started.\n',
1207
+ );
1208
+ } else if (overruns.length === 0 && w.unusable > 0) {
1209
+ // NOT a clean run. A registered concern this pass could not time is
1210
+ // an unknown, and `slaOverruns` skips it — so counting the SUCCESS
1211
+ // marker off `registered` alone prints "none over" for a window
1212
+ // that observed nothing about `unusable` of its bounded concerns.
1213
+ // The same unknown-is-not-fine invariant `slaOverruns` keeps per
1214
+ // row, kept here for the summary line. Caught by an independent
1215
+ // review, 2026-10-01.
1216
+ process.stdout.write(
1217
+ ` ⚠️ warn-only window INCOMPLETE: ${w.usable} of ${w.registered} bounded ` +
1218
+ `concerns measured this run, ${w.unusable} unmeasured or malformed — ` +
1219
+ 'no overrun seen, and this run does not count as clean.\n',
1220
+ );
1221
+ } else if (overruns.length === 0) {
1222
+ process.stdout.write(
1223
+ ` ✅ warn-only window: ${w.usable} of ${concernRows.length} bounded and ` +
1224
+ 'measured, none over `sla_ms × 3` on this run.\n',
1225
+ );
1226
+ } else {
1227
+ for (const o of overruns) {
1228
+ process.stdout.write(
1229
+ ` ⚠️ warn-only window: ${o.concern} p95 ${(o.p95_us / 1000).toFixed(3)} ms ` +
1230
+ `is over its \`sla_ms × 3\` bound of ${(o.bound_us / 1000).toFixed(3)} ms — ` +
1231
+ 'observe-only; step 3.3 must not flip to deny while this stands.\n',
1232
+ );
1233
+ }
1234
+ }
1235
+
829
1236
  // Breach re-measure (--gate only). A single p95 over the cap is not yet
830
1237
  // evidence of a regression: a load spike inside one 50-run sample moves
831
1238
  // p95 upward and never downward, so re-measuring and keeping the MINIMUM