@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
@@ -55,6 +55,8 @@
55
55
  import * as fs from 'node:fs';
56
56
  import * as os from 'node:os';
57
57
  import * as path from 'node:path';
58
+
59
+ import * as yaml from 'js-yaml';
58
60
  import process from 'node:process';
59
61
  import { fileURLToPath, pathToFileURL } from 'node:url';
60
62
 
@@ -75,6 +77,19 @@ export const LOWERING_REL = path.join('src', 'scripts', 'hooks', 'host_lowering.
75
77
  export const BEGIN_MARKER = '<!-- BEGIN GENERATED: enforcement-configured-by-slot -->';
76
78
  export const END_MARKER = '<!-- END GENERATED: enforcement-configured-by-slot -->';
77
79
 
80
+ /**
81
+ * The second generated region: every `blocking` concern, with the enforcement
82
+ * the host it is bound on can actually carry.
83
+ *
84
+ * road-to-blocking-severities 1.2. Its own marker pair rather than an extra
85
+ * column in the first region, because the two tables have different units — the
86
+ * first is one row per host-SLOT, this is one row per host-slot-CONCERN — and
87
+ * merging them would make every host-slot row repeat once per concern bound to
88
+ * it.
89
+ */
90
+ export const SEVERITY_BEGIN_MARKER = '<!-- BEGIN GENERATED: blocking-severity-by-binding -->';
91
+ export const SEVERITY_END_MARKER = '<!-- END GENERATED: blocking-severity-by-binding -->';
92
+
78
93
  /** The command a drift failure tells the reader to run. Printed, never guessed at. */
79
94
  export const REGEN_COMMAND = `./scripts-run src/scripts/${GATE} --write`;
80
95
 
@@ -88,12 +103,28 @@ export const CONFIGURED_OUTCOMES = ['refusal', 'halt-by-state', 'warning', 'unen
88
103
 
89
104
  export type ConfiguredOutcome = (typeof CONFIGURED_OUTCOMES)[number];
90
105
 
106
+ /**
107
+ * What the `Answered` column prints for a pair the configuration never dated.
108
+ *
109
+ * A word rather than an empty cell, because an empty cell in a rendered table
110
+ * is indistinguishable from a rendering bug, and this one is the finding.
111
+ * `lint_hook_manifest._check_slot_answers` is what actually refuses it; this
112
+ * gate's job is to make sure the published document cannot hide it.
113
+ */
114
+ export const UNDATED = '`undated`';
115
+
91
116
  export interface MatrixRow {
92
117
  host: string;
93
118
  slot: string;
94
119
  outcome: ConfiguredOutcome;
95
120
  /** The configuration values the outcome was read off, quoted for the table. */
96
121
  backing: string;
122
+ /**
123
+ * `answered_at` for this pair, or `undated` when the configuration carries
124
+ * none. Printed LAST so the outcome stays in column three and
125
+ * {@link committedCells} keeps reading the same index it always did.
126
+ */
127
+ answered: string;
97
128
  }
98
129
 
99
130
  export interface BuildResult {
@@ -192,6 +223,7 @@ export function buildRows(lowering: HostLowering): BuildResult {
192
223
  slot: reading.slot,
193
224
  outcome,
194
225
  backing: backingFor(reading, surface.fail_policy),
226
+ answered: surface.slots.get(reading.slot)?.answered_at ?? UNDATED,
195
227
  });
196
228
  }
197
229
  }
@@ -295,10 +327,12 @@ export function renderRegion(lowering: HostLowering): string {
295
327
  out.push('');
296
328
  out.push(summaryLine(lowering, rows));
297
329
  out.push('');
298
- out.push('| Host | Slot | Configured outcome | Backing |');
299
- out.push('|---|---|---|---|');
330
+ out.push('| Host | Slot | Configured outcome | Backing | Answered |');
331
+ out.push('|---|---|---|---|---|');
300
332
  for (const r of rows) {
301
- out.push(`| \`${r.host}\` | \`${r.slot}\` | \`${r.outcome}\` | ${r.backing} |`);
333
+ out.push(
334
+ `| \`${r.host}\` | \`${r.slot}\` | \`${r.outcome}\` | ${r.backing} | ${r.answered} |`,
335
+ );
302
336
  }
303
337
  out.push('');
304
338
  if (slotlessHosts.length === 0) {
@@ -314,31 +348,200 @@ export function renderRegion(lowering: HostLowering): string {
314
348
  return out.join('\n');
315
349
  }
316
350
 
317
- /** Replace the block between the markers. Idempotent by construction. */
318
- export function spliceDoc(text: string, region: string): string {
319
- const b = text.indexOf(BEGIN_MARKER);
320
- const e = text.indexOf(END_MARKER);
351
+ /**
352
+ * How each {@link VerifiedEnforcement} is worded in the published table.
353
+ *
354
+ * `unverified` and `warning-only` are worded so a reader cannot slide between
355
+ * them. Only `warning-only` says "cannot"; `unverified` says what is missing.
356
+ */
357
+ export const ENFORCEMENT_TEXT: Readonly<Record<VerifiedEnforcement, string>> = {
358
+ refusal: 'the slot denies — the concern does what it declares',
359
+ 'proof-expired': 'the slot can deny; this package’s `verified` citation has lapsed',
360
+ 'warning-only': 'the slot is bound and `block_exit` is null — it runs and warns, it cannot refuse',
361
+ unverified: 'no lowering row for this slot — nothing is bound natively, and nothing is established',
362
+ };
363
+
364
+ /**
365
+ * The 1.2 region body: one row per `blocking` binding, two independent columns.
366
+ *
367
+ * Grouped by host and ordered as `concernSlotAudit` sorted it, so a
368
+ * regeneration is a no-op whenever nothing moved.
369
+ */
370
+ export function renderSeverityRegion(audit: readonly HostConcernAudit[]): string {
371
+ const all = audit.flatMap((h) => h.blocking.map((b) => b));
372
+ const out: string[] = [];
373
+ out.push(
374
+ `Projected from \`${MANIFEST_REL}\` (declared severity) and \`${LOWERING_REL}\` ` +
375
+ '(verified enforcement). **Two independent facts, never folded into one.**',
376
+ );
377
+ out.push('');
378
+ out.push(
379
+ 'The manifest declares what a concern is *meant* to do; the lowering table records ' +
380
+ 'what the host slot it is bound on *can* do. A `blocking` concern on a slot that ' +
381
+ 'cannot refuse still runs and still warns — it is not silent, and it is not a ' +
382
+ 'refusal either. These columns say which.',
383
+ );
384
+ out.push('');
385
+ const byEnforcement = (e: VerifiedEnforcement): number =>
386
+ all.filter((b) => b.enforcement === e).length;
387
+ out.push(
388
+ `**${String(all.length)} binding(s) declare \`blocking\`: ` +
389
+ `${String(byEnforcement('refusal'))} refuse, ` +
390
+ `${String(byEnforcement('warning-only'))} run and warn on a slot verified unable to ` +
391
+ `refuse, ${String(byEnforcement('unverified'))} sit on a slot with no lowering row, ` +
392
+ `${String(byEnforcement('proof-expired'))} sit on a slot whose proof has lapsed.**`,
393
+ );
394
+ out.push('');
395
+ out.push('| Host | Slot | Concern | Declared severity | Verified enforcement | What that means |');
396
+ out.push('|---|---|---|---|---|---|');
397
+ for (const b of all) {
398
+ out.push(
399
+ `| \`${b.host}\` | \`${b.slot}\` | \`${b.concern}\` | \`${b.declared}\` | ` +
400
+ `\`${b.enforcement}\` | ${ENFORCEMENT_TEXT[b.enforcement]} |`,
401
+ );
402
+ }
403
+ if (all.length === 0) {
404
+ out.push('');
405
+ out.push('No concern in the manifest declares `blocking`.');
406
+ }
407
+ return out.join('\n');
408
+ }
409
+
410
+ /** Replace the block between one marker pair. Idempotent by construction. */
411
+ function _splice(text: string, region: string, begin: string, end: string): string {
412
+ const b = text.indexOf(begin);
413
+ const e = text.indexOf(end);
321
414
  if (b === -1 || e === -1 || e < b) {
322
415
  throw new Error(
323
416
  `${GATE}: ${DOC_REL} is missing the generated-region markers ` +
324
- `(${BEGIN_MARKER} … ${END_MARKER}). Restore them rather than hand-writing the ` +
417
+ `(${begin} … ${end}). Restore them rather than hand-writing the ` +
325
418
  'table — the markers are what makes the region regenerable.',
326
419
  );
327
420
  }
328
- return `${text.slice(0, b + BEGIN_MARKER.length)}\n${region}\n${text.slice(e)}`;
421
+ return `${text.slice(0, b + begin.length)}\n${region}\n${text.slice(e)}`;
329
422
  }
330
423
 
331
- /** The region body a committed document carries, or null when the markers are gone. */
332
- export function extractRegion(text: string): string | null {
333
- const b = text.indexOf(BEGIN_MARKER);
334
- const e = text.indexOf(END_MARKER);
424
+ /** Replace the block between the markers. Idempotent by construction. */
425
+ export function spliceDoc(text: string, region: string): string {
426
+ return _splice(text, region, BEGIN_MARKER, END_MARKER);
427
+ }
428
+
429
+ /** Replace the blocking-severity region. Same contract as {@link spliceDoc}. */
430
+ export function spliceSeverityDoc(text: string, region: string): string {
431
+ return _splice(text, region, SEVERITY_BEGIN_MARKER, SEVERITY_END_MARKER);
432
+ }
433
+
434
+ function _extract(text: string, begin: string, end: string): string | null {
435
+ const b = text.indexOf(begin);
436
+ const e = text.indexOf(end);
335
437
  if (b === -1 || e === -1 || e < b) return null;
336
438
  return text
337
- .slice(b + BEGIN_MARKER.length, e)
439
+ .slice(b + begin.length, e)
338
440
  .replace(/^\n/, '')
339
441
  .replace(/\n$/, '');
340
442
  }
341
443
 
444
+ /** The region body a committed document carries, or null when the markers are gone. */
445
+ export function extractRegion(text: string): string | null {
446
+ return _extract(text, BEGIN_MARKER, END_MARKER);
447
+ }
448
+
449
+ /** The blocking-severity region a committed document carries, or null. */
450
+ export function extractSeverityRegion(text: string): string | null {
451
+ return _extract(text, SEVERITY_BEGIN_MARKER, SEVERITY_END_MARKER);
452
+ }
453
+
454
+ /** One published blocking-severity row, read off the committed region. */
455
+ export interface CommittedSeverityCell {
456
+ readonly host: string;
457
+ readonly slot: string;
458
+ readonly concern: string;
459
+ readonly declared: string;
460
+ readonly enforcement: string;
461
+ }
462
+
463
+ /** Blocking-severity rows a committed region carries, in document order. */
464
+ export function committedSeverityCells(region: string): CommittedSeverityCell[] {
465
+ const out: CommittedSeverityCell[] = [];
466
+ for (const line of region.split('\n')) {
467
+ if (!line.trim().startsWith('|')) continue;
468
+ const cells = splitMarkdownRow(line);
469
+ if (cells.length < 5) continue;
470
+ if (cells[0] === 'Host' || /^-+$/.test((cells[0] ?? '').trim())) continue;
471
+ const strip = (s: string): string => s.trim().replace(/^`|`$/g, '');
472
+ out.push({
473
+ host: strip(cells[0] ?? ''),
474
+ slot: strip(cells[1] ?? ''),
475
+ concern: strip(cells[2] ?? ''),
476
+ declared: strip(cells[3] ?? ''),
477
+ enforcement: strip(cells[4] ?? ''),
478
+ });
479
+ }
480
+ return out;
481
+ }
482
+
483
+ /**
484
+ * The 1.2 HARD gate: published enforcement claims the configuration refutes.
485
+ *
486
+ * This is the half the council asked to fail rather than report, and the
487
+ * distinction from the audit above is the reason it may. `concernSlotAudit`
488
+ * counts BINDINGS, a population with a legacy backlog this package did not
489
+ * create in one diff — failing on it would red the tree on history. These
490
+ * findings compare a GENERATED DOCUMENT against the configuration it is
491
+ * generated from, which has no backlog by construction: every row was written
492
+ * by the generator one command ago, so a disagreement is a hand edit or a
493
+ * generator bug and neither should ship.
494
+ *
495
+ * Both seats named the same two dishonesty classes, and both directions are
496
+ * checked because they fail in opposite ways:
497
+ *
498
+ * - A row claiming `refusal` where the slot's `block_exit` is null, or where
499
+ * there is no row at all, OVERSTATES the guarantee. A reader takes it as a
500
+ * deterministic block and gets a warning.
501
+ * - A row claiming `warning-only` where the lowering table has NO row for the
502
+ * slot UNDERSTATES what is known: it asserts the host cannot refuse, which
503
+ * is a host fact this package has not established and which
504
+ * `host_lowering.yaml` explicitly disclaims. `unverified` is the honest
505
+ * value there, and collapsing the two is how an absence of measurement
506
+ * becomes a measurement.
507
+ *
508
+ * A value outside {@link VERIFIED_ENFORCEMENTS} is its own finding, for the
509
+ * same reason `unbackedOutcomes` rejects one: an invented category in a
510
+ * generated table looks exactly as authoritative as a real one.
511
+ */
512
+ export function severityFindings(region: string, lowering: HostLowering): string[] {
513
+ const findings: string[] = [];
514
+ for (const cell of committedSeverityCells(region)) {
515
+ const label = `${cell.host}/${cell.slot}/${cell.concern}`;
516
+ if (!(VERIFIED_ENFORCEMENTS as readonly string[]).includes(cell.enforcement)) {
517
+ findings.push(
518
+ `${label}: \`${cell.enforcement}\` is not in the closed enforcement vocabulary ` +
519
+ `(${VERIFIED_ENFORCEMENTS.map((e) => `\`${e}\``).join(' · ')}).`,
520
+ );
521
+ continue;
522
+ }
523
+ const row = readSlots(lowering, cell.host).slots.find(
524
+ (s: SlotReading) => s.slot === cell.slot,
525
+ );
526
+ if (cell.enforcement === 'refusal' && (row === undefined || row.literal === null)) {
527
+ findings.push(
528
+ `${label}: published \`refusal\`, but ${LOWERING_REL} carries ` +
529
+ `${row === undefined ? 'no row for this slot' : '`block_exit: null`'} — a ` +
530
+ 'refusal has nowhere to go, so the published row overstates the guarantee.',
531
+ );
532
+ continue;
533
+ }
534
+ if (cell.enforcement === 'warning-only' && row === undefined) {
535
+ findings.push(
536
+ `${label}: published \`warning-only\`, which asserts this slot CANNOT refuse, ` +
537
+ `but ${LOWERING_REL} carries no row for it at all. An absent row is ` +
538
+ '`unverified`, never a host fact — the table says so in its own header.',
539
+ );
540
+ }
541
+ }
542
+ return findings;
543
+ }
544
+
342
545
  /** Outcome cells a committed region carries, with their row label. */
343
546
  export function committedCells(region: string): { label: string; value: string }[] {
344
547
  const out: { label: string; value: string }[] = [];
@@ -394,6 +597,317 @@ function _read(root: string, rel: string): string | null {
394
597
  /** Flags this gate understands. `--root` is the only one taking a value. */
395
598
  const KNOWN_FLAGS: ReadonlySet<string> = new Set(['--self-test', '--root', '--write', '--quiet']);
396
599
 
600
+ /** Where the concern→slot bindings and per-concern severities live. */
601
+ export const MANIFEST_REL = path.join('src', 'scripts', 'hook_manifest.yaml');
602
+
603
+ /**
604
+ * Why a bound concern's verdict cannot leave its slot.
605
+ *
606
+ * Three states, not two, and the third is the one an earlier version of this
607
+ * audit got wrong by skipping it. They are ordered worst-first:
608
+ *
609
+ * - `unlowerable` — the lowering table carries NO row for this slot on this
610
+ * host, so the installer writes no native binding at all.
611
+ * `cowork` is the worked case: `slots: {}` against sixty-odd
612
+ * declared bindings. Skipping it printed `0/0 · 0 blocking`,
613
+ * byte-identical to a host that binds nothing — a false
614
+ * green on the host with the largest gap.
615
+ * - `null-block` — a row exists and its literal `block_exit` is null. The
616
+ * slot is wired and a refusal has nowhere to go.
617
+ * - `stale-proof` — the literal `block_exit` CAN deny, but the row's
618
+ * `verified` block has expired, so `effective` reads null.
619
+ * This is a statement about this package's provenance, NOT
620
+ * about the host: `host_lowering.yaml` says in its own
621
+ * header that an absent `verified` "does NOT mean the host
622
+ * cannot enforce". Printing "cannot deny" here would
623
+ * assert exactly the host fact that file disclaims, so it
624
+ * is reported as its own class and worded differently.
625
+ */
626
+ export type NoDenyReason = 'unlowerable' | 'null-block' | 'stale-proof';
627
+
628
+ /** One concern bound where its verdict cannot produce a refusal. */
629
+ export interface NullBlockBinding {
630
+ readonly host: string;
631
+ readonly slot: string;
632
+ readonly concern: string;
633
+ /** The concern's declared `severity`, or `(undeclared)`. */
634
+ readonly severity: string;
635
+ readonly reason: NoDenyReason;
636
+ }
637
+
638
+ /** What {@link concernSlotAudit} establishes for one host. */
639
+ export interface HostConcernAudit {
640
+ readonly host: string;
641
+ /** Lowerable slots whose literal `block_exit` can deny. */
642
+ readonly denySlots: number;
643
+ /** Lowerable slots in total. */
644
+ readonly lowerableSlots: number;
645
+ /** Distinct slots this host binds a concern to, whether lowerable or not. */
646
+ readonly boundSlots: number;
647
+ /**
648
+ * Bindings whose verdict cannot become a refusal, worst reason first and
649
+ * `blocking` severity ahead of the rest within a reason.
650
+ *
651
+ * An `advisory` concern here is CONSISTENT, not a defect — most concerns are
652
+ * advisory and belong on a reporting slot. The row that matters is a
653
+ * `blocking` severity: a concern declared to refuse, bound where a refusal
654
+ * has nowhere to go.
655
+ */
656
+ readonly noDeny: readonly NullBlockBinding[];
657
+ /**
658
+ * EVERY binding on this host whose declared severity is `blocking`, with
659
+ * the enforcement its slot carries — including the ones that CAN refuse,
660
+ * which `noDeny` by construction never holds.
661
+ *
662
+ * Separate from `noDeny` rather than a filter over it, because the
663
+ * published table needs the complete picture: a reader who sees only the
664
+ * gaps cannot tell a host that refuses nowhere from a host with no blocking
665
+ * concerns at all, and those are the two readings the 1.2 table exists to
666
+ * keep apart.
667
+ */
668
+ readonly blocking: readonly BlockingBinding[];
669
+ }
670
+
671
+ /** Slots that carry a dispatcher verdict. `ask` is a UI affordance, not one. */
672
+ const NON_VERDICT_SLOTS: ReadonlySet<string> = new Set(['ask']);
673
+
674
+ /**
675
+ * Cross-reference the manifest's concern bindings against the lowering table's
676
+ * per-slot `block_exit`.
677
+ *
678
+ * road-to-a-content-scanner-on-a-slot-that-can-refuse 2.1. The gate above
679
+ * compares published host-SLOT rows against the configuration and is green
680
+ * whenever those agree — it never asks whether a CONCERN is bound to a slot
681
+ * that can carry its severity. That question was answered once, by an external
682
+ * reader comparing two files by hand, which is why it drifted back: a hand
683
+ * comparison leaves no number behind. This makes it a printed count.
684
+ *
685
+ * DELIBERATELY NOT A FAILURE. It prints; the exit code is unchanged. Failing
686
+ * would red the tree on bindings that predate this check — the same reason the
687
+ * continuity and estate ratchets report distance-to-target instead of gating on
688
+ * it. What a reader gets is a number that moves when a binding or a slot moves.
689
+ *
690
+ * NOTHING BOUND IS SILENTLY DROPPED. Every bound slot lands in exactly one of
691
+ * {can deny, one of the three {@link NoDenyReason} classes, non-verdict}, and
692
+ * `boundSlots` publishes the denominator so a reader can check the arithmetic
693
+ * rather than trust it. That is the correction the first version needed: it
694
+ * skipped any slot missing from the lowering table, which is precisely the
695
+ * largest gap in the tree.
696
+ */
697
+ export function concernSlotAudit(root: string, lowering: HostLowering): HostConcernAudit[] {
698
+ const manifestText = _read(root, MANIFEST_REL);
699
+ if (manifestText === null) return [];
700
+ let manifest: unknown;
701
+ try {
702
+ manifest = yaml.load(manifestText);
703
+ } catch {
704
+ return [];
705
+ }
706
+ if (manifest === null || typeof manifest !== 'object') return [];
707
+ const m = manifest as Record<string, unknown>;
708
+
709
+ const severities = new Map<string, string>();
710
+ const concerns = m['concerns'];
711
+ if (concerns !== null && typeof concerns === 'object') {
712
+ for (const [name, spec] of Object.entries(concerns as Record<string, unknown>)) {
713
+ const sev =
714
+ spec !== null && typeof spec === 'object'
715
+ ? (spec as Record<string, unknown>)['severity']
716
+ : undefined;
717
+ severities.set(name, typeof sev === 'string' ? sev : '(undeclared)');
718
+ }
719
+ }
720
+
721
+ const platforms = m['platforms'];
722
+ if (platforms === null || typeof platforms !== 'object') return [];
723
+
724
+ const out: HostConcernAudit[] = [];
725
+ for (const [host, blockRaw] of Object.entries(platforms as Record<string, unknown>)) {
726
+ const reading = readSlots(lowering, host);
727
+ const rows = new Map<string, SlotReading>(reading.slots.map((s: SlotReading) => [s.slot, s]));
728
+ const denySlots = reading.slots.filter((s: SlotReading) => s.literal !== null).length;
729
+
730
+ const noDeny: NullBlockBinding[] = [];
731
+ const blocking: BlockingBinding[] = [];
732
+ let boundSlots = 0;
733
+ if (blockRaw !== null && typeof blockRaw === 'object') {
734
+ for (const [slot, value] of Object.entries(blockRaw as Record<string, unknown>)) {
735
+ if (!Array.isArray(value)) continue;
736
+ if (NON_VERDICT_SLOTS.has(slot)) continue;
737
+ boundSlots += 1;
738
+ const row = rows.get(slot);
739
+ // The literal decides whether a refusal can leave the slot; the
740
+ // `verified` currency decides only whether this package has
741
+ // cited its proof. Reading `effective` here would turn an
742
+ // expired citation into a claim that the host cannot enforce.
743
+ const reason: NoDenyReason | null =
744
+ row === undefined
745
+ ? 'unlowerable'
746
+ : row.literal === null
747
+ ? 'null-block'
748
+ : row.effective === null
749
+ ? 'stale-proof'
750
+ : null;
751
+ // The 1.2 roster runs BEFORE the `reason === null` return, so a
752
+ // binding on a slot that CAN refuse is recorded here and
753
+ // nowhere else.
754
+ for (const concern of value) {
755
+ if (typeof concern !== 'string') continue;
756
+ const declared = severities.get(concern) ?? '(unknown concern)';
757
+ if (declared !== 'blocking') continue;
758
+ blocking.push({
759
+ host,
760
+ slot,
761
+ concern,
762
+ declared,
763
+ enforcement: enforcementFor(reason),
764
+ });
765
+ }
766
+ if (reason === null) continue;
767
+ for (const concern of value) {
768
+ if (typeof concern !== 'string') continue;
769
+ noDeny.push({
770
+ host,
771
+ slot,
772
+ concern,
773
+ severity: severities.get(concern) ?? '(unknown concern)',
774
+ reason,
775
+ });
776
+ }
777
+ }
778
+ }
779
+ blocking.sort(
780
+ (a, b) => a.slot.localeCompare(b.slot) || a.concern.localeCompare(b.concern),
781
+ );
782
+ const reasonRank = (r: NoDenyReason): number =>
783
+ r === 'unlowerable' ? 0 : r === 'null-block' ? 1 : 2;
784
+ const sevRank = (s: string): number => (s === 'blocking' ? 0 : s === 'advisory' ? 2 : 1);
785
+ noDeny.sort(
786
+ (a, b) =>
787
+ reasonRank(a.reason) - reasonRank(b.reason) ||
788
+ sevRank(a.severity) - sevRank(b.severity) ||
789
+ a.slot.localeCompare(b.slot) ||
790
+ a.concern.localeCompare(b.concern),
791
+ );
792
+ out.push({
793
+ host,
794
+ denySlots,
795
+ lowerableSlots: reading.slots.length,
796
+ boundSlots,
797
+ noDeny,
798
+ blocking,
799
+ });
800
+ }
801
+ return out;
802
+ }
803
+
804
+ /**
805
+ * What a binding's host slot can actually carry, as a closed vocabulary.
806
+ *
807
+ * road-to-blocking-severities 1.2, option (a) as the council revised it on
808
+ * 2026-10-01 (2/2, both seats). The revision is in the NAMES and it is the
809
+ * whole point: the first draft called this the "effective severity", and the
810
+ * second seat refused that term — severity has not changed, enforcement
811
+ * strength has, and a generated document that silently redefines the
812
+ * manifest's own word is worse than one that says nothing. So a binding
813
+ * carries two independent facts, published side by side and never folded into
814
+ * one: the DECLARED severity, which is the concern's policy intent and belongs
815
+ * to `hook_manifest.yaml`, and the VERIFIED ENFORCEMENT below, which is the
816
+ * host slot's measured capability and belongs to `host_lowering.yaml`.
817
+ *
818
+ * - `refusal` — the slot's `block_exit` can deny and its `verified`
819
+ * proof is current. A `blocking` concern here does what
820
+ * it says.
821
+ * - `warning-only` — a lowering row exists and its `block_exit` is null. The
822
+ * slot is wired, the concern runs, and its verdict cannot
823
+ * become a refusal. This is the only value that asserts
824
+ * the host CANNOT refuse, and it is backed by a row.
825
+ * - `unverified` — no lowering row for this slot on this host. Nothing is
826
+ * bound natively and nothing has been established.
827
+ * Deliberately NOT `warning-only`: `host_lowering.yaml`
828
+ * says in its own header that an absence "does NOT mean
829
+ * the host cannot enforce", and both council seats named
830
+ * collapsing the two the defect to avoid. The gate below
831
+ * fails a published row that collapses them.
832
+ * - `proof-expired` — the literal `block_exit` can deny but this package's
833
+ * `verified` citation has lapsed. A statement about our
834
+ * provenance, never about the host.
835
+ *
836
+ * Order is strongest-first, matching {@link CONFIGURED_OUTCOMES}'s convention.
837
+ */
838
+ export const VERIFIED_ENFORCEMENTS = [
839
+ 'refusal',
840
+ 'proof-expired',
841
+ 'warning-only',
842
+ 'unverified',
843
+ ] as const;
844
+
845
+ export type VerifiedEnforcement = (typeof VERIFIED_ENFORCEMENTS)[number];
846
+
847
+ /**
848
+ * The enforcement a {@link NoDenyReason} corresponds to; `null` means the slot
849
+ * can deny, which is the one case `concernSlotAudit` does not record a reason
850
+ * for.
851
+ */
852
+ export function enforcementFor(reason: NoDenyReason | null): VerifiedEnforcement {
853
+ if (reason === null) return 'refusal';
854
+ if (reason === 'unlowerable') return 'unverified';
855
+ if (reason === 'null-block') return 'warning-only';
856
+ return 'proof-expired';
857
+ }
858
+
859
+ /** One concern declared `blocking`, with the enforcement its host slot carries. */
860
+ export interface BlockingBinding {
861
+ readonly host: string;
862
+ readonly slot: string;
863
+ readonly concern: string;
864
+ /** Always `blocking` here — kept explicit so the published row carries both facts. */
865
+ readonly declared: string;
866
+ readonly enforcement: VerifiedEnforcement;
867
+ }
868
+
869
+ /** How each {@link NoDenyReason} is worded. `stale-proof` never says "cannot". */
870
+ const REASON_TEXT: Readonly<Record<NoDenyReason, string>> = {
871
+ unlowerable: 'no lowering row for this slot — nothing is bound natively at all',
872
+ 'null-block': 'block_exit is null — a refusal has nowhere to go',
873
+ 'stale-proof': 'block_exit can deny, but this row’s `verified` proof has expired',
874
+ };
875
+
876
+ /** Render the audit as the lines the reporter prints. */
877
+ export function renderConcernAudit(audit: readonly HostConcernAudit[]): string[] {
878
+ const lines: string[] = [];
879
+ lines.push('');
880
+ lines.push(`${GATE} · concerns bound where a verdict cannot become a refusal`);
881
+ lines.push(
882
+ ' A blocking severity here is a concern declared to refuse, bound where a refusal',
883
+ );
884
+ lines.push(' cannot happen. An advisory one is consistent and is counted, not flagged.');
885
+ lines.push(' `stale-proof` is a statement about this package’s citation, never about the host.');
886
+ for (const h of audit) {
887
+ const blocking = h.noDeny.filter((b) => b.severity === 'blocking');
888
+ const others = h.noDeny.length - blocking.length;
889
+ lines.push(
890
+ ` ${h.host.padEnd(10)} ${String(h.denySlots)}/${String(h.lowerableSlots)} lowerable slot(s) can deny · ` +
891
+ `${String(h.boundSlots)} verdict-bearing slot(s) bound · ` +
892
+ `${String(blocking.length)} blocking + ${String(others)} non-blocking binding(s) cannot refuse`,
893
+ );
894
+ // One line per (reason, slot) rather than per concern: a host with an
895
+ // empty `slots:` map has sixty-odd bindings and the reason is the fact,
896
+ // not the roster.
897
+ const seen = new Set<string>();
898
+ for (const b of h.noDeny) {
899
+ if (b.severity !== 'blocking') continue;
900
+ const key = `${b.reason}/${b.slot}`;
901
+ if (!seen.has(key)) {
902
+ seen.add(key);
903
+ lines.push(` ⚠️ ${b.slot} — ${REASON_TEXT[b.reason]}`);
904
+ }
905
+ lines.push(` ${b.concern} (${b.severity})`);
906
+ }
907
+ }
908
+ return lines;
909
+ }
910
+
397
911
  export function main(argv?: readonly string[]): number {
398
912
  const args = argv ?? process.argv.slice(2);
399
913
  // An unrecognised flag is refused rather than ignored. Both ratification
@@ -437,19 +951,27 @@ export function main(argv?: readonly string[]): number {
437
951
  const ledger = new GateLedger(GATE);
438
952
  ledger.plan(built.rows.map((r) => `${r.host}/${r.slot}`));
439
953
  const region = renderRegion(lowering);
954
+ const audit = concernSlotAudit(root, lowering);
955
+ const severityRegion = renderSeverityRegion(audit);
440
956
 
441
957
  if (write) {
442
958
  let next: string;
443
959
  try {
444
- next = spliceDoc(docText, region);
960
+ next = spliceSeverityDoc(spliceDoc(docText, region), severityRegion);
445
961
  } catch (exc) {
446
962
  process.stderr.write(`❌ ${String(exc)}\n`);
447
963
  return 2;
448
964
  }
449
965
  // The backing check runs BEFORE the write, so a generator bug cannot
450
966
  // put a phantom value into the document and then be caught by the
451
- // check that same generator's output feeds.
452
- const phantom = unbackedOutcomes(region, backedOutcomes(lowering));
967
+ // check that same generator's output feeds. The 1.2 severity check
968
+ // rides in the same position and for the same reason: a generator that
969
+ // could write `refusal` onto a null `block_exit` must be stopped at the
970
+ // write, not discovered by the next read-only run.
971
+ const phantom = [
972
+ ...unbackedOutcomes(region, backedOutcomes(lowering)),
973
+ ...severityFindings(severityRegion, lowering),
974
+ ];
453
975
  if (phantom.length > 0) {
454
976
  for (const f of phantom) process.stderr.write(`❌ ${GATE}: ${f}\n`);
455
977
  process.stderr.write(
@@ -483,14 +1005,31 @@ export function main(argv?: readonly string[]): number {
483
1005
  return 2;
484
1006
  }
485
1007
 
1008
+ const committedSeverity = extractSeverityRegion(docText);
1009
+ if (committedSeverity === null) {
1010
+ process.stderr.write(
1011
+ `❌ ${GATE}: ${DOC_REL} has no blocking-severity region. The markers ` +
1012
+ `${SEVERITY_BEGIN_MARKER} … ${SEVERITY_END_MARKER} are what make that table ` +
1013
+ `regenerable; restore them and run \`${REGEN_COMMAND}\`.\n`,
1014
+ );
1015
+ return 2;
1016
+ }
1017
+
486
1018
  const findings: string[] = [];
487
1019
  findings.push(...unbackedOutcomes(committed, backedOutcomes(lowering)));
488
1020
  findings.push(...built.contradictions);
1021
+ findings.push(...severityFindings(committedSeverity, lowering));
489
1022
  if (committed !== region) {
490
1023
  findings.push(
491
1024
  `${DOC_REL} § the generated region differs from what \`${LOWERING_REL}\` produces.`,
492
1025
  );
493
1026
  }
1027
+ if (committedSeverity !== severityRegion) {
1028
+ findings.push(
1029
+ `${DOC_REL} § the blocking-severity region differs from what \`${MANIFEST_REL}\` ` +
1030
+ `and \`${LOWERING_REL}\` produce.`,
1031
+ );
1032
+ }
494
1033
 
495
1034
  // A pair is `fail`ed when the committed cell for it is absent or carries a
496
1035
  // different outcome, and `complete`d otherwise. The denominator is the
@@ -517,6 +1056,13 @@ export function main(argv?: readonly string[]): number {
517
1056
  });
518
1057
  ledger.report();
519
1058
 
1059
+ // The concern-vs-slot audit prints on every read-only run, before the
1060
+ // verdict, so it is visible whether the row comparison passed or failed.
1061
+ // It never changes the exit code — see `concernSlotAudit`.
1062
+ for (const line of renderConcernAudit(audit)) {
1063
+ process.stdout.write(`${line}\n`);
1064
+ }
1065
+
520
1066
  if (findings.length > 0) {
521
1067
  for (const f of findings) process.stdout.write(`❌ ${GATE}: ${f}\n`);
522
1068
  process.stdout.write(
@@ -546,15 +1092,33 @@ export function main(argv?: readonly string[]): number {
546
1092
  function selfTest(): number {
547
1093
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'enforcement-matrix-'));
548
1094
  const lowering = _read(ROOT, LOWERING_REL) ?? '';
549
- const plant = (name: string, mutate: (region: string) => string): string => {
1095
+ const manifest = _read(ROOT, MANIFEST_REL) ?? '';
1096
+ /**
1097
+ * Plant a fixture tree carrying BOTH generated regions.
1098
+ *
1099
+ * The manifest is copied in alongside the lowering table because the
1100
+ * severity region is derived from the two together — without it
1101
+ * `concernSlotAudit` reads nothing and every severity case would exercise
1102
+ * an empty table, which is a fixture that cannot fail.
1103
+ */
1104
+ const plant = (
1105
+ name: string,
1106
+ mutate: (region: string) => string,
1107
+ mutateSeverity: (region: string) => string = (r) => r,
1108
+ ): string => {
550
1109
  const dir = path.join(tmp, name);
551
1110
  fs.mkdirSync(path.join(dir, path.dirname(LOWERING_REL)), { recursive: true });
1111
+ fs.mkdirSync(path.join(dir, path.dirname(MANIFEST_REL)), { recursive: true });
552
1112
  fs.mkdirSync(path.join(dir, path.dirname(DOC_REL)), { recursive: true });
553
1113
  fs.writeFileSync(path.join(dir, LOWERING_REL), lowering, 'utf-8');
554
- const region = renderRegion(parseHostLowering(lowering));
1114
+ fs.writeFileSync(path.join(dir, MANIFEST_REL), manifest, 'utf-8');
1115
+ const table = parseHostLowering(lowering);
1116
+ const region = renderRegion(table);
1117
+ const severity = renderSeverityRegion(concernSlotAudit(dir, table));
555
1118
  fs.writeFileSync(
556
1119
  path.join(dir, DOC_REL),
557
- `# fixture\n\n${BEGIN_MARKER}\n${mutate(region)}\n${END_MARKER}\n`,
1120
+ `# fixture\n\n${BEGIN_MARKER}\n${mutate(region)}\n${END_MARKER}\n\n` +
1121
+ `${SEVERITY_BEGIN_MARKER}\n${mutateSeverity(severity)}\n${SEVERITY_END_MARKER}\n`,
558
1122
  'utf-8',
559
1123
  );
560
1124
  return dir;
@@ -565,9 +1129,44 @@ function selfTest(): number {
565
1129
  try {
566
1130
  return runSelfTest({
567
1131
  gate: GATE,
568
- minCases: 5,
569
- minRejectCases: 4,
1132
+ minCases: 8,
1133
+ minRejectCases: 7,
570
1134
  cases: [
1135
+ // road-to-blocking-severities 1.2 — the two dishonesty classes
1136
+ // the council asked to FAIL rather than report. Both are planted
1137
+ // by hand, because a generator that could emit either is the bug
1138
+ // these cases exist to catch, so a generated fixture could not
1139
+ // demonstrate one.
1140
+ {
1141
+ name: 'a published `refusal` on a slot whose block_exit is null is rejected',
1142
+ expect: 'reject',
1143
+ run: () =>
1144
+ run(
1145
+ plant('overstate', (r) => r, (s) =>
1146
+ s.replace('| `warning-only` |', '| `refusal` |'),
1147
+ ),
1148
+ ),
1149
+ },
1150
+ {
1151
+ name: 'a published `warning-only` on a slot with no lowering row is rejected',
1152
+ expect: 'reject',
1153
+ run: () =>
1154
+ run(
1155
+ plant('understate', (r) => r, (s) =>
1156
+ s.replace('| `unverified` |', '| `warning-only` |'),
1157
+ ),
1158
+ ),
1159
+ },
1160
+ {
1161
+ name: 'an enforcement value outside the closed vocabulary is rejected',
1162
+ expect: 'reject',
1163
+ run: () =>
1164
+ run(
1165
+ plant('enf-vocab', (r) => r, (s) =>
1166
+ s.replace('| `unverified` |', '| `advisory` |'),
1167
+ ),
1168
+ ),
1169
+ },
571
1170
  {
572
1171
  name: 'a cell claiming `halt-by-state`, which nothing emits, is rejected',
573
1172
  expect: 'reject',