@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
@@ -238,7 +238,11 @@ Tier 2 — maintenance / internal (hooks, MCP, memory, telemetry):
238
238
  roadmap:set-step Flip ONE checkbox glyph safely (lock + line anchor +
239
239
  live invariant + survival check)
240
240
  roadmap:archive Archive completed roadmaps (branch-touched by default;
241
- --all for every complete one; --dry-run to preview)
241
+ --all for every complete one; --dry-run to preview;
242
+ bare [~] steps are carried to a follow-up first,
243
+ --no-carry to leave them blocking; owner-blocked
244
+ steps ask first; --owner-decision later --only <x>
245
+ parks that one roadmap)
242
246
  stubs:due Read-only: roadmap stubs past their `review_by:`
243
247
  date, plus the decisions routed to the owner.
244
248
  Writes nothing. Flags: --json, --counts,
@@ -342,7 +346,9 @@ Tier 2 — maintenance / internal (hooks, MCP, memory, telemetry):
342
346
  memory:check-proposal Run the admission gate on a memory proposal
343
347
  memory:learn Aggregate memory intake signals into the local learning
344
348
  sidecar (read-only; --write to emit). Usage: memory:learn [--intake-dir DIR]
345
- [--out-dir DIR] [--now ISO] [--write] [--format text|json]
349
+ [--out-dir DIR] [--now ISO] [--write] [--format text|json|status]
350
+ --format status prints the one-screen human view (verdict counts,
351
+ top preferred lessons, the hand-off); it refuses --write.
346
352
  analytics Local-only workspace analytics (emit|show|prune|migrate).
347
353
  Usage: analytics show [--window 30d|7d|24h] · analytics prune
348
354
  knowledge Global knowledge-card store (list|show|trace|forget|promote|
@@ -1456,7 +1462,15 @@ cmd_prune() {
1456
1462
  # `agent-config doctor` — read-only drift report against the manifest.
1457
1463
  # Surfaces missing / modified / foreign files. Exit 0 clean, 1 drift,
1458
1464
  # 2 manifest-absent. See scripts/_cli/cmd_doctor.ts.
1465
+ # `doctor neighbours` routes to its own entry point rather than a branch inside
1466
+ # cmd_doctor.ts, which is 2,000 lines over the source-size ceiling and cannot
1467
+ # grow. Routed here because the dispatcher is where verbs are already resolved.
1459
1468
  cmd_doctor() {
1469
+ if [[ "${1:-}" == "neighbours" ]]; then
1470
+ shift
1471
+ exec_ts "$PACKAGE_ROOT/src/scripts/_cli/cmd_doctor_neighbours.ts" "$@"
1472
+ return
1473
+ fi
1460
1474
  exec_ts "$PACKAGE_ROOT/src/scripts/_cli/cmd_doctor.ts" "$@"
1461
1475
  }
1462
1476
 
@@ -305,6 +305,17 @@ export const MERGEABLE_KEYS: readonly string[] = [
305
305
  // GUI; whitelisting decides only whether the value SURVIVES the merge
306
306
  // (ADR-271, road-to-design-intent-conformance 2.3).
307
307
  'design.fidelity_mode',
308
+ // `lean_projection.*` is a per-MACHINE setting and the install it governs is
309
+ // a user-scope one: the layer being thinned is `~/.claude/rules`, which
310
+ // belongs to the machine and not to any checkout. Without these two rows a
311
+ // consumer who wrote the opt-in into the user-global file got no error, no
312
+ // warning and no effect — the same silent-filter defect the two entries
313
+ // above repair, reaching a third key, and unsatisfiable-by-construction on
314
+ // an ADR-020 global-only install, where no project layer exists to carry it.
315
+ // Whitelisting decides only whether a value SURVIVES the merge: it sets
316
+ // nothing, and `installerThinsHost` still refuses a template-only value.
317
+ 'lean_projection.mode',
318
+ 'lean_projection.hosts',
308
319
  ];
309
320
 
310
321
  /** The shipped template, relative to the package root. */
@@ -0,0 +1,102 @@
1
+ /**
2
+ * `body-portable` — does a skill's declared frontmatter survive the MCP-lite
3
+ * carrier whole?
4
+ *
5
+ * road-to-semantic-parity-before-off-menu, Phase 2.1.
6
+ *
7
+ * WHAT THIS DECIDES, AND WHAT IT DOES NOT
8
+ *
9
+ * It decides ELIGIBILITY only. It marks nothing, excludes nothing, and is not a
10
+ * verdict about which skills matter. A committed list of skills this returns
11
+ * false for is one step from a menu-exclusion discharged by citing it, which is
12
+ * precisely the laundering the parked menu-economy roadmap's Risk 1 names — a
13
+ * measurement of carrier coverage reused as a judgement about worth. Nothing in
14
+ * the tree consumes this as an exclusion trigger, and building the half that
15
+ * could is a separate decision that has not been taken.
16
+ *
17
+ * THE RULE
18
+ *
19
+ * A skill is body-portable when every key it declares is one the carrier
20
+ * transports whole. Three ways to fail, and they are deliberately not
21
+ * distinguished in the boolean:
22
+ *
23
+ * - a DROPPED key — the carrier never reads it, so the semantic stays on disk.
24
+ * - a PARTIAL key — the carrier reads it and transports part of the value.
25
+ * `triggers` is the only one: `keyword` and `phrase` travel, while
26
+ * `file_pattern`, `path_prefix`, `command` and `reason` do not. Counting it
27
+ * as portable would make a path-triggered skill look complete when the very
28
+ * routing that selects it never leaves the disk.
29
+ * - an UNKNOWN key — one the schema does not declare at all. It fails CLOSED.
30
+ * The alternative is to ignore what you cannot classify, and an unknown key
31
+ * is exactly the case where the carrier's behaviour is unestablished, so
32
+ * returning true there would assert portability from ignorance.
33
+ *
34
+ * SCOPE: one carrier, `ContentEntry` in `src/cli/mcp/content.ts`. A second
35
+ * delivery path drops a different set and needs its own predicate rather than a
36
+ * widened reading of this one.
37
+ */
38
+
39
+ import { INDEXED_TRIGGER_KEYS } from '../../shared/skillRanking.js';
40
+
41
+ /**
42
+ * Frontmatter keys `buildEntry` reads and transports whole
43
+ * (`src/cli/mcp/content.ts` — `fm.name`, `fm.description`, `fm.source`,
44
+ * `fm.personas`).
45
+ *
46
+ * Derived from the carrier source, never from the schema: a schema property the
47
+ * carrier does not read is dropped no matter what the schema says about it.
48
+ */
49
+ export const CARRIED_KEYS: readonly string[] = ['name', 'description', 'source', 'personas'];
50
+
51
+ /**
52
+ * Keys the carrier reads but truncates, with the sub-keys that actually travel.
53
+ *
54
+ * The sub-key list is IMPORTED from `INDEXED_TRIGGER_KEYS` rather than restated.
55
+ * Restating it was the first version of this file and it is a silent-lie shape:
56
+ * add a third indexed key and a hand-copied literal keeps reporting the old two
57
+ * while the carrier transports three, so the census `travels` column would be
58
+ * wrong with every test still green.
59
+ */
60
+ export const PARTIAL_KEYS: Readonly<Record<string, readonly string[]>> = {
61
+ triggers: INDEXED_TRIGGER_KEYS,
62
+ };
63
+
64
+ /** Why a skill is not body-portable. Empty when it is. */
65
+ export interface PortabilityReasons {
66
+ /** Declared keys the carrier transports only in part. */
67
+ readonly partial: readonly string[];
68
+ /** Declared keys the carrier does not read, including keys the schema does not declare. */
69
+ readonly dropped: readonly string[];
70
+ }
71
+
72
+ export interface PortabilityVerdict extends PortabilityReasons {
73
+ readonly portable: boolean;
74
+ }
75
+
76
+ /**
77
+ * Classify one skill's declared frontmatter keys.
78
+ *
79
+ * Pure: it reads nothing and depends only on its argument and the two constants
80
+ * above.
81
+ */
82
+ export function classifyPortability(frontmatter: Readonly<Record<string, unknown>>): PortabilityVerdict {
83
+ const partial: string[] = [];
84
+ const dropped: string[] = [];
85
+
86
+ for (const key of Object.keys(frontmatter).sort()) {
87
+ if (CARRIED_KEYS.includes(key)) continue;
88
+ if (Object.hasOwn(PARTIAL_KEYS, key)) {
89
+ partial.push(key);
90
+ continue;
91
+ }
92
+ // Dropped, and an unknown key lands here too — failing closed.
93
+ dropped.push(key);
94
+ }
95
+
96
+ return { portable: partial.length === 0 && dropped.length === 0, partial, dropped };
97
+ }
98
+
99
+ /** The predicate itself. `true` only when nothing the skill declares is lost. */
100
+ export function isBodyPortable(frontmatter: Readonly<Record<string, unknown>>): boolean {
101
+ return classifyPortability(frontmatter).portable;
102
+ }
@@ -82,6 +82,16 @@ export interface TranscriptRecord {
82
82
  /** Raw `timestamp` field from the record, or null when absent. */
83
83
  timestamp: string | null;
84
84
  usage: TokenCounts;
85
+ /**
86
+ * UTF-8 byte length of this record's own `message.content`, or `null` when
87
+ * the scan did not ask for it ({@link ScanOptions.measureBytes}) or the
88
+ * record carries no serialisable content.
89
+ *
90
+ * `null` is NOT zero. A record whose content was never measured and a
91
+ * record whose content is empty are different facts, and a ratio built
92
+ * over the first would divide by a denominator it never observed.
93
+ */
94
+ content_bytes: number | null;
85
95
  }
86
96
 
87
97
  export interface ScanOptions {
@@ -98,6 +108,17 @@ export interface ScanOptions {
98
108
  maxAgeDays?: number;
99
109
  /** Clock used to evaluate `maxAgeDays` — injectable for tests. */
100
110
  now?: Date;
111
+ /**
112
+ * Also measure each record's `message.content` byte length
113
+ * ({@link TranscriptRecord.content_bytes}).
114
+ *
115
+ * OPT-IN, because it costs a second serialisation of content the scan
116
+ * otherwise never touches, and the existing callers — `cost_summary`,
117
+ * `preamble_byte_census`, the bench harness — want token sums only. A
118
+ * caller that does not ask gets `content_bytes: null` on every record and
119
+ * pays nothing.
120
+ */
121
+ measureBytes?: boolean;
101
122
  }
102
123
 
103
124
  export interface ScanResult {
@@ -161,7 +182,7 @@ interface RawUsage {
161
182
 
162
183
  interface RawAssistantRecord {
163
184
  type?: unknown;
164
- message?: { id?: unknown; model?: unknown; usage?: RawUsage };
185
+ message?: { id?: unknown; model?: unknown; usage?: RawUsage; content?: unknown };
165
186
  requestId?: unknown;
166
187
  agentId?: unknown;
167
188
  isSidechain?: unknown;
@@ -180,6 +201,29 @@ function toTokenCounts(raw: RawUsage | undefined): TokenCounts {
180
201
  };
181
202
  }
182
203
 
204
+ /**
205
+ * UTF-8 byte length of one record's `message.content`, or `null` when there is
206
+ * nothing serialisable to measure.
207
+ *
208
+ * Bytes, never characters: a multibyte block costs what it costs, and
209
+ * `String.length` would under-report it. Content is what this reader can
210
+ * actually SEE — the request's system prompt, tool definitions and prior turns
211
+ * are not in the transcript at all (`preamble_byte_census` records the same
212
+ * limitation), so a ratio built from this is a ratio over assistant output and
213
+ * is labelled as such wherever it surfaces.
214
+ */
215
+ export function measureContentBytes(raw: RawAssistantRecord): number | null {
216
+ const content = raw.message?.content;
217
+ if (content === undefined || content === null) return null;
218
+ if (typeof content === 'string') return Buffer.byteLength(content, 'utf8');
219
+ try {
220
+ return Buffer.byteLength(JSON.stringify(content), 'utf8');
221
+ } catch {
222
+ // Circular or otherwise unserialisable — unmeasurable, not zero.
223
+ return null;
224
+ }
225
+ }
226
+
183
227
  /** Subagent iff a non-empty top-level `agentId` OR `isSidechain === true` — see module doc comment. */
184
228
  function classifyBucket(raw: RawAssistantRecord): TranscriptBucket {
185
229
  const hasAgentId = typeof raw.agentId === 'string' && raw.agentId.length > 0;
@@ -285,6 +329,7 @@ export function scanTranscripts(opts: ScanOptions = {}): ScanResult {
285
329
  model: typeof raw.message.model === 'string' ? raw.message.model : 'unknown',
286
330
  timestamp: typeof raw.timestamp === 'string' ? raw.timestamp : null,
287
331
  usage: toTokenCounts(raw.message.usage),
332
+ content_bytes: opts.measureBytes === true ? measureContentBytes(raw) : null,
288
333
  });
289
334
  }
290
335
  }
@@ -298,6 +343,92 @@ export function scanTranscripts(opts: ScanOptions = {}): ScanResult {
298
343
  };
299
344
  }
300
345
 
346
+ /* -------------------------------------------------------------------------- */
347
+ /* Bytes — measured on the corpus, never assumed */
348
+ /* -------------------------------------------------------------------------- */
349
+
350
+ /**
351
+ * A bytes-per-token ratio, together with the two sums it was divided from and
352
+ * the population it was measured over.
353
+ *
354
+ * The sums travel WITH the ratio on purpose. A bare ratio is the shape that
355
+ * becomes a constant: somebody copies the number, it outlives the corpus that
356
+ * produced it, and the next reader cannot tell what it measured. Carrying
357
+ * `content_bytes` and `output_tokens` means the division is re-derivable from
358
+ * the record itself.
359
+ */
360
+ export interface BytesPerToken {
361
+ /** Always `'measured'` — this value is a division of two counted sums. */
362
+ basis: 'measured';
363
+ /** Summed `content_bytes` over the records that carried one. */
364
+ content_bytes: number;
365
+ /** Summed `output_tokens` over those same records. */
366
+ output_tokens: number;
367
+ /** `content_bytes / output_tokens`. */
368
+ bytes_per_token: number;
369
+ /** Records that contributed — `content_bytes !== null` and `output_tokens > 0`. */
370
+ records: number;
371
+ }
372
+
373
+ /**
374
+ * Measure a bytes-per-token ratio ON THE GIVEN RECORDS. Returns `null` when
375
+ * nothing in the corpus can supply one.
376
+ *
377
+ * `null` is the honest outcome and callers must render it as such rather than
378
+ * substituting a default: a ratio invented where none was measured is exactly
379
+ * the constant this function exists to make unnecessary. There is deliberately
380
+ * no fallback value anywhere in this module — the factor is a property of the
381
+ * corpus, and a corpus that cannot supply one has not supplied one.
382
+ */
383
+ export function measureBytesPerToken(records: readonly TranscriptRecord[]): BytesPerToken | null {
384
+ let bytes = 0;
385
+ let tokens = 0;
386
+ let n = 0;
387
+ for (const rec of records) {
388
+ if (rec.content_bytes === null || rec.usage.output_tokens <= 0) continue;
389
+ bytes += rec.content_bytes;
390
+ tokens += rec.usage.output_tokens;
391
+ n += 1;
392
+ }
393
+ if (n === 0 || tokens <= 0) return null;
394
+ return {
395
+ basis: 'measured',
396
+ content_bytes: bytes,
397
+ output_tokens: tokens,
398
+ bytes_per_token: bytes / tokens,
399
+ records: n,
400
+ };
401
+ }
402
+
403
+ /** A byte figure obtained by multiplying a token count by a measured ratio. */
404
+ export interface DerivedBytes {
405
+ /** Always `'derived'`. Never `'measured'` — nothing counted these bytes. */
406
+ basis: 'derived';
407
+ /** The token count that was multiplied. */
408
+ tokens: number;
409
+ /** The ratio used, carried so the arithmetic sits beside its result. */
410
+ factor: BytesPerToken;
411
+ /** `tokens * factor.bytes_per_token`, rounded to a whole byte. */
412
+ bytes: number;
413
+ }
414
+
415
+ /**
416
+ * Multiply a token count by a ratio measured on the same corpus.
417
+ *
418
+ * The result carries `basis: 'derived'` and the factor that produced it, so a
419
+ * derived figure and a counted one can never be read as the same number — the
420
+ * failure this whole path is shaped to prevent. `null` factor in, `null` out.
421
+ */
422
+ export function derivedBytes(tokens: number, factor: BytesPerToken | null): DerivedBytes | null {
423
+ if (factor === null) return null;
424
+ return {
425
+ basis: 'derived',
426
+ tokens,
427
+ factor,
428
+ bytes: Math.round(tokens * factor.bytes_per_token),
429
+ };
430
+ }
431
+
301
432
  function emptyAggregate(bucket: TranscriptBucket): BucketAggregate {
302
433
  return {
303
434
  bucket,
@@ -98,16 +98,18 @@ export function build_claude_hook_matrix(manifest_path: string): ClaudeHookMatri
98
98
  const aliasesAll = (manifest['native_event_aliases'] ?? {}) as Record<string, unknown>;
99
99
  const aliases = ((aliasesAll['claude'] ?? {}) as Record<string, unknown>) || {};
100
100
 
101
- const ac_to_native: Record<string, string> = {};
102
- for (const [native, ac] of Object.entries(aliases)) {
103
- ac_to_native[String(ac)] = native;
104
- }
105
-
101
+ // Iterating the ALIAS map, not the event map, because the relation is
102
+ // many-natives-to-one-event and the inversion this replaced silently lost
103
+ // every native but the last. `PostToolUse` and `PostToolUseFailure` both
104
+ // lower to `post_tool_use`; building `ac_to_native` would have kept one of
105
+ // them and UNBOUND the other, turning a routing fix into a routing
106
+ // regression that no existing test could see. Pinned by
107
+ // `before_complete_failure_envelope.test.ts` ("both natives stay bound").
106
108
  const matrix: ClaudeHookMatrix = {};
107
- for (const [ac_event, concerns] of Object.entries(claude_events)) {
109
+ for (const [native, ac_raw] of Object.entries(aliases)) {
110
+ const ac_event = String(ac_raw);
111
+ const concerns = claude_events[ac_event];
108
112
  if (!concerns || (Array.isArray(concerns) && concerns.length === 0)) continue;
109
- const native = ac_to_native[ac_event];
110
- if (native === undefined) continue;
111
113
  // Bundle fast path (road-to-hook-latency-repair Phase 2): invoke the
112
114
  // precompiled dispatcher bundle directly — no bash shim, no CLI boot
113
115
  // (measured ~450-500 ms CLI vs ~110 ms bundle per event on a 1-vCPU
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Per-concern SLA measurement and the warn-only window's reporting.
3
+ *
4
+ * Extracted from `bench_hook_latency.ts` on 2026-10-01, not for tidiness: the
5
+ * additions that made `one-question-per-ask` measurable and gave the warn-only
6
+ * window a reader pushed that file from 1,361 lines to 1,583, i.e. 83 lines
7
+ * past the 1,500-line source ceiling, and `check_source_size_budget`'s ratchet
8
+ * asked for exactly this by name. Raising the baseline was the other option
9
+ * and is the one the ratchet exists to make visible rather than easy — a MOVE
10
+ * gives the budget back instead of spending it, which is the doctrine every
11
+ * entry in `gate-violation-baselines.json` records.
12
+ *
13
+ * It is also the better boundary on its own terms. Everything here answers one
14
+ * question — which blocking concern is bounded, by how much, and did this run
15
+ * cross it — and none of it is about measuring a hook SLOT, which is what the
16
+ * bench itself does.
17
+ *
18
+ * NOTHING HERE GATES. The bound these functions read is not armed anywhere:
19
+ * step 3.3 of `road-to-a-kernel-that-guards-its-plumbing` is its only consumer
20
+ * and is blocked on the window these functions report. A measurement that can
21
+ * red a build before its bar is validated is Risk 1 of that roadmap.
22
+ */
23
+ import * as fs from 'node:fs';
24
+
25
+ /**
26
+ * The subset of a per-concern row the window needs: a measurement and a bound.
27
+ *
28
+ * Declared here rather than imported so the dependency runs one way only —
29
+ * `bench_hook_latency.ts` imports this module and this module imports nothing
30
+ * back. `ConcernLatencyRow` satisfies it structurally, so the caller passes its
31
+ * own rows unchanged and no adapter exists to drift.
32
+ */
33
+ export interface BoundedConcern {
34
+ readonly concern: string;
35
+ /** p95 of the concern's own durations in microseconds, or null when untimed. */
36
+ readonly p95_us: number | null;
37
+ /** The registered SLA in milliseconds, or null when none is registered. */
38
+ readonly sla_ms: number | null;
39
+ /** The budget carried a value for this concern that is not a number. */
40
+ readonly sla_malformed: boolean;
41
+ }
42
+
43
+ /**
44
+ * An alternate tool shape for the synthetic payload.
45
+ *
46
+ * The default payload names `Read`, which is the right default for every gated
47
+ * number: it is the common non-matching case consumers pay on every tool call.
48
+ * But a concern filtered by the manifest's per-concern `tools:` key is SKIPPED
49
+ * in-process on a payload naming a tool it does not claim, so it never produces
50
+ * a sample and the per-concern report prints `not_measured` for it — correctly,
51
+ * and uselessly for a step that needs a bound.
52
+ *
53
+ * `one-question-per-ask` is the live instance: `severity: blocking`, filtered to
54
+ * `AskUserQuestion` and five aliases. It is the only blocking concern the
55
+ * default payload cannot reach, and the owning roadmap's Phase-3 blocker names
56
+ * the choice explicitly — "either a payload shaped to trigger it or an explicit
57
+ * decision that an unmeasured blocking concern keeps the current 30 s timeout".
58
+ * This is the first branch: measure it, so the decision is not forced by the
59
+ * harness.
60
+ *
61
+ * Measurement-only, like `--bundle` and `--payload-bytes`. It is reachable only
62
+ * from `concernSlaPass`, which gates nothing and writes no budget row — a probe
63
+ * payload must never produce a gated slot number, because the slot caps were
64
+ * derived against the `Read` shape and a different shape runs a different
65
+ * concern chain.
66
+ */
67
+ export interface ToolShape {
68
+ readonly tool_name: string;
69
+ readonly tool_input: Record<string, unknown>;
70
+ }
71
+
72
+ /**
73
+ * The probe that reaches `one-question-per-ask`.
74
+ *
75
+ * ONE question, not two: a single question is the ALLOW path, and the allow
76
+ * path is what an ordinary call costs. Two questions would measure the deny
77
+ * path — the rarer branch, and the one that builds a message — so a bound taken
78
+ * from it would describe the exception rather than the rule. Both run
79
+ * `countStructuredAskQuestions`, which is the concern's actual work.
80
+ */
81
+ export const ASK_PROBE: ToolShape = {
82
+ tool_name: 'AskUserQuestion',
83
+ tool_input: { questions: [{ question: 'bench probe' }] },
84
+ };
85
+
86
+ /**
87
+ * The one concern the ask probe exists to measure.
88
+ *
89
+ * Named rather than inferred from the `tools:` filter: a future concern added
90
+ * with an overlapping filter would be measured under a payload chosen for THIS
91
+ * one, and silently adopting it is the contamination above wearing a different
92
+ * shape.
93
+ */
94
+ export const PROBE_TARGET = 'one-question-per-ask';
95
+
96
+ /**
97
+ * Append only `target`'s samples from the probe sink onto the main sink.
98
+ *
99
+ * The probe sink is read and then left in place; it is a temp-dir file the
100
+ * caller owns. A read failure is not fatal — the probe is an observation, and
101
+ * a bench that dies because one auxiliary sink was unreadable converts a
102
+ * measurement into an outage, which is the posture `readConcernTimings`
103
+ * already takes one level down.
104
+ */
105
+ export function mergeProbeSamples(probeSink: string, sinkPath: string, target: string): void {
106
+ let text: string;
107
+ try {
108
+ text = fs.readFileSync(probeSink, 'utf-8');
109
+ } catch {
110
+ return;
111
+ }
112
+ const keep: string[] = [];
113
+ for (const line of text.split('\n')) {
114
+ const trimmed = line.trim();
115
+ if (trimmed === '') continue;
116
+ let row: { concern?: unknown };
117
+ try {
118
+ row = JSON.parse(trimmed) as typeof row;
119
+ } catch {
120
+ continue;
121
+ }
122
+ if (row.concern === target) keep.push(trimmed);
123
+ }
124
+ if (keep.length === 0) return;
125
+ try {
126
+ fs.appendFileSync(sinkPath, `${keep.join('\n')}\n`, 'utf-8');
127
+ } catch {
128
+ /* the sink is an observation aid; losing it must not fail the bench */
129
+ }
130
+ }
131
+
132
+ /**
133
+ * The warn-only window, made legible — observe-only, gating nothing.
134
+ *
135
+ * Step 3.3 derives a per-concern timeout of `sla_ms x 3` and its own text says
136
+ * "warn-only for the first measured window, then deny". A window nothing
137
+ * observes is not a window: with `sla_ms` registered and no reader, the flip
138
+ * would be taken on the absence of a complaint rather than on a reading.
139
+ *
140
+ * So this is the instrument. Every bench run — local or CI, gated or not —
141
+ * prints each blocking concern's measured p95 beside its registered bound and
142
+ * names any concern that crossed it. The window is then a span of runs a human
143
+ * can read, and the flip has an evidence base that exists before it is taken.
144
+ *
145
+ * It lives in the measurement harness and NOT in the dispatcher deliberately.
146
+ * A runtime warn would be a dispatcher change shipped as part of a step that is
147
+ * blocked on the very readings it would produce, and Risk 1 of the owning
148
+ * roadmap is precisely about acting on a bound before it is validated.
149
+ *
150
+ * A concern with no registered SLA, or no measurement, is NOT an overrun — it
151
+ * is an unknown, and the two must not collapse. Returning it as "fine" would
152
+ * let an unregistered concern read as a clean window.
153
+ */
154
+ export interface SlaOverrun {
155
+ concern: string;
156
+ p95_us: number;
157
+ /** `sla_ms x 3`, in microseconds — the bound step 3.3 would apply. */
158
+ bound_us: number;
159
+ }
160
+
161
+ /**
162
+ * What a run can actually say about the window — three counts, never one.
163
+ *
164
+ * `registered` is how many concerns carry a bound. `usable` is how many of
165
+ * those this run could compare against it. The gap is what an earlier version
166
+ * of the summary line silently dropped: it printed "none over" off `registered`,
167
+ * so a run that measured none of its bounded concerns read exactly like a run
168
+ * that measured all of them and found nothing. That is the same
169
+ * unknown-collapsed-into-fine failure `slaOverruns` refuses per row, one level
170
+ * up at the summary.
171
+ */
172
+ export interface WindowState {
173
+ registered: number;
174
+ usable: number;
175
+ /** Registered but not comparable this run — unmeasured or malformed. */
176
+ unusable: number;
177
+ }
178
+
179
+ export function windowState(rows: readonly BoundedConcern[]): WindowState {
180
+ let registered = 0;
181
+ let usable = 0;
182
+ for (const row of rows) {
183
+ if (row.sla_ms === null && !row.sla_malformed) continue;
184
+ registered += 1;
185
+ if (!row.sla_malformed && row.sla_ms !== null && row.p95_us !== null) usable += 1;
186
+ }
187
+ return { registered, usable, unusable: registered - usable };
188
+ }
189
+
190
+ export function slaOverruns(rows: readonly BoundedConcern[]): SlaOverrun[] {
191
+ const out: SlaOverrun[] = [];
192
+ for (const row of rows) {
193
+ if (row.sla_ms === null || row.sla_malformed || row.p95_us === null) continue;
194
+ const bound_us = row.sla_ms * 3 * 1000;
195
+ if (row.p95_us > bound_us) {
196
+ out.push({ concern: row.concern, p95_us: row.p95_us, bound_us });
197
+ }
198
+ }
199
+ return out;
200
+ }