@event4u/agent-config 16.2.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 (332) 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 +89 -237
  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 +4 -4
  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/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
  23. package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
  24. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
  25. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
  26. package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
  27. package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
  28. package/dist/agent-src/rules/active-remediation.md +1 -1
  29. package/dist/agent-src/rules/autonomous-execution.md +1 -0
  30. package/dist/agent-src/rules/broken-access-control.md +1 -0
  31. package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
  32. package/dist/agent-src/rules/context-hygiene.md +1 -1
  33. package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
  34. package/dist/agent-src/rules/design-fidelity.md +13 -16
  35. package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
  36. package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
  37. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  38. package/dist/agent-src/rules/domain-safety-retention.md +1 -0
  39. package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
  40. package/dist/agent-src/rules/evaluator-independence.md +3 -2
  41. package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
  42. package/dist/agent-src/rules/finance-safety-floor.md +1 -0
  43. package/dist/agent-src/rules/git-history-discipline.md +1 -0
  44. package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
  45. package/dist/agent-src/rules/legal-safety-floor.md +3 -2
  46. package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
  47. package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
  48. package/dist/agent-src/rules/media-governance-routing.md +14 -17
  49. package/dist/agent-src/rules/persona-governance.md +2 -2
  50. package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
  51. package/dist/agent-src/rules/question-not-instruction.md +1 -0
  52. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  53. package/dist/agent-src/rules/runtime-safety.md +1 -0
  54. package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
  55. package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
  56. package/dist/agent-src/rules/session-canary.md +8 -62
  57. package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
  58. package/dist/agent-src/rules/source-confidentiality.md +1 -0
  59. package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
  60. package/dist/agent-src/rules/tool-safety.md +1 -0
  61. package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
  62. package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
  63. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  64. package/dist/agent-src/scripts/deferral_carry.ts +339 -0
  65. package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
  66. package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
  67. package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
  68. package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
  69. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
  70. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
  71. package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
  72. package/dist/agent-src/skills/iconography/SKILL.md +1 -1
  73. package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
  74. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
  75. package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
  76. package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
  77. package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
  78. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  79. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
  80. package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
  81. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
  82. package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
  83. package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
  84. package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
  85. package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
  86. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  87. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  88. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  89. package/dist/cli/registry.js +1 -1
  90. package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
  91. package/dist/cli-delegate/check_update_banner.js +12 -1
  92. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  93. package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
  94. package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
  95. package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
  96. package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
  97. package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
  98. package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
  99. package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
  100. package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-JQVYKIZA.js} +10 -0
  101. package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
  102. package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
  103. package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
  104. package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
  105. package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-NQ7WT43P.js} +1186 -244
  106. package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
  107. package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
  108. package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
  109. package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
  110. package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
  111. package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-RQNNW7IF.js} +1314 -560
  112. package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-S4UMZNES.js} +1 -1
  113. package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
  114. package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-TRI6DQZ6.js} +2781 -340
  115. package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
  116. package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
  117. package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
  118. package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
  119. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  120. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  121. package/dist/cli-delegate/cmd_conformance.js +1167 -234
  122. package/dist/cli-delegate/cmd_doctor.js +13 -6
  123. package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
  124. package/dist/cli-delegate/cmd_explain.js +5 -5
  125. package/dist/cli-delegate/cmd_export.js +11 -8
  126. package/dist/cli-delegate/cmd_fleet.js +3 -3
  127. package/dist/cli-delegate/cmd_handoff.js +10 -7
  128. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  129. package/dist/cli-delegate/cmd_memory_get.js +3 -1
  130. package/dist/cli-delegate/cmd_migrate.js +2 -2
  131. package/dist/cli-delegate/cmd_packs_active.js +3 -3
  132. package/dist/cli-delegate/cmd_preflight.js +6 -6
  133. package/dist/cli-delegate/cmd_prune.js +11 -8
  134. package/dist/cli-delegate/cmd_refresh.js +2 -1
  135. package/dist/cli-delegate/cmd_route_audit.js +8 -6
  136. package/dist/cli-delegate/cmd_route_explain.js +4 -4
  137. package/dist/cli-delegate/cmd_session_recycle.js +4 -3
  138. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  139. package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
  140. package/dist/cli-delegate/cmd_settings_set.js +4 -4
  141. package/dist/cli-delegate/cmd_sync.js +11 -8
  142. package/dist/cli-delegate/cmd_uninstall.js +11 -8
  143. package/dist/cli-delegate/cmd_update.js +1 -1
  144. package/dist/cli-delegate/cmd_upgrade.js +3 -2
  145. package/dist/cli-delegate/cmd_validate.js +11 -8
  146. package/dist/cli-delegate/cmd_versions.js +1 -1
  147. package/dist/cli-delegate/deferral_carry.js +16 -0
  148. package/dist/cli-delegate/gate_execute.js +2 -2
  149. package/dist/cli-delegate/roadmap_gates.js +5 -5
  150. package/dist/cli-delegate/update_roadmap_progress.js +1 -1
  151. package/dist/discovery/deprecation-report.md +1 -1
  152. package/dist/discovery/discovery-manifest.json +127 -75
  153. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  154. package/dist/discovery/discovery-manifest.summary.md +3 -3
  155. package/dist/discovery/dormancy-report.md +1 -1
  156. package/dist/discovery/orphan-report.md +1 -1
  157. package/dist/discovery/packs.json +7 -5
  158. package/dist/discovery/trust-report.md +2 -2
  159. package/dist/discovery/workspaces.json +6 -4
  160. package/dist/hooks/dispatch.js +5357 -6656
  161. package/dist/hooks/dispatch.sha256 +1 -1
  162. package/dist/install/claudePathsPlan.js +47 -6
  163. package/dist/install/claudeRuleRewrite.js +7 -0
  164. package/dist/install/emit_host_rules_cli.js +39 -36
  165. package/dist/install/globalRuleScope.js +87 -0
  166. package/dist/install/host_rule_ownership.js +160 -0
  167. package/dist/install/install.mjs +2080 -1024
  168. package/dist/install/installThinLayer.js +261 -0
  169. package/dist/install/installedRuleLinks.js +176 -0
  170. package/dist/install/wizard-plan.js +24 -0
  171. package/dist/mcp/registry-manifest.json +3 -3
  172. package/dist/mcp/server.json +2 -2
  173. package/dist/mcp/server.mjs +31965 -27154
  174. package/dist/scripts/_lib/agent_settings.js +11 -0
  175. package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
  176. package/dist/scripts/_lib/installed_layer.js +378 -0
  177. package/dist/scripts/_lib/lean_projection_mode.js +218 -15
  178. package/dist/scripts/_lib/rule_consequence_class.js +45 -0
  179. package/dist/scripts/_lib/rule_injection.js +67 -10
  180. package/dist/scripts/_lib/rule_law_section.js +122 -0
  181. package/dist/scripts/_lib/thin_rules.js +458 -0
  182. package/dist/scripts/_lib/windsurf_render.js +60 -0
  183. package/dist/scripts/condense.js +14 -26
  184. package/dist/scripts/project_thin_rules.js +14 -294
  185. package/dist/server/schemas/settings.js +6 -0
  186. package/dist/shared/skillRanking.js +74 -13
  187. package/docs/contracts/settings-classes.md +7 -5
  188. package/package.json +1 -1
  189. package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
  190. package/src/agent-src/scripts/deferral_carry.ts +339 -0
  191. package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
  192. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
  193. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
  194. package/src/config/agent-settings.template.yml +62 -22
  195. package/src/config/continuity-surface.json +7 -0
  196. package/src/config/evaluator-budgets.json +4 -4
  197. package/src/config/gate-coverage.yml +96 -3
  198. package/src/config/gate-violation-baselines.json +17 -9
  199. package/src/config/hook-bundle-budget.json +25 -0
  200. package/src/config/hook-token-budget.json +6 -1
  201. package/src/config/host-instruction-limits.json +33 -0
  202. package/src/config/routing-coverage-seed.json +24 -3
  203. package/src/config/rule-activation-census.json +7 -3
  204. package/src/config/rule-consequence-class.json +161 -0
  205. package/src/config/rule-law-ceilings.json +99 -0
  206. package/src/install/claudePathsPlan.ts +46 -4
  207. package/src/install/claudeRuleRewrite.ts +10 -1
  208. package/src/install/emit_host_rules_cli.ts +43 -34
  209. package/src/install/globalRuleScope.ts +109 -0
  210. package/src/install/host_rule_ownership.ts +170 -0
  211. package/src/install/installThinLayer.ts +312 -0
  212. package/src/install/installedRuleLinks.ts +220 -0
  213. package/src/install/wizard-plan.ts +24 -0
  214. package/src/scripts/_cli/cmd_doctor.ts +23 -71
  215. package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
  216. package/src/scripts/_dispatch.bash +16 -2
  217. package/src/scripts/_lib/agent_settings.ts +11 -0
  218. package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
  219. package/src/scripts/_lib/estate_offsets.ts +241 -0
  220. package/src/scripts/_lib/exemption_shape.ts +143 -0
  221. package/src/scripts/_lib/fnv.ts +23 -0
  222. package/src/scripts/_lib/foreign_scan.ts +106 -0
  223. package/src/scripts/_lib/gitignore_block.ts +314 -0
  224. package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
  225. package/src/scripts/_lib/graph_feeder_record.ts +214 -0
  226. package/src/scripts/_lib/hook_settings.ts +73 -0
  227. package/src/scripts/_lib/host_capability.ts +40 -19
  228. package/src/scripts/_lib/host_hook_merge.ts +165 -0
  229. package/src/scripts/_lib/installed_layer.ts +538 -0
  230. package/src/scripts/_lib/json_merge.ts +82 -0
  231. package/src/scripts/_lib/json_pointers.ts +93 -11
  232. package/src/scripts/_lib/lean_projection_mode.ts +275 -15
  233. package/src/scripts/_lib/machine_wake.ts +16 -3
  234. package/src/scripts/_lib/neighbour_census.ts +760 -0
  235. package/src/scripts/_lib/neighbour_scan.ts +122 -0
  236. package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
  237. package/src/scripts/_lib/prompt_shape.ts +46 -0
  238. package/src/scripts/_lib/release_migration_gate.ts +19 -3
  239. package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
  240. package/src/scripts/_lib/review_coverage.ts +134 -0
  241. package/src/scripts/_lib/rule_consequence_class.ts +71 -0
  242. package/src/scripts/_lib/rule_injection.ts +130 -10
  243. package/src/scripts/_lib/rule_law_section.ts +147 -0
  244. package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
  245. package/src/scripts/_lib/skill_origin.ts +254 -0
  246. package/src/scripts/_lib/structured_ask.ts +64 -21
  247. package/src/scripts/_lib/thin_rules.ts +540 -0
  248. package/src/scripts/_lib/touched_file_quality.ts +434 -0
  249. package/src/scripts/_lib/turn_end_refusals.ts +77 -2
  250. package/src/scripts/_lib/windsurf_render.ts +63 -0
  251. package/src/scripts/ai_council/cli_call_budget.ts +13 -5
  252. package/src/scripts/ai_council/clients.ts +26 -57
  253. package/src/scripts/ai_council/config.ts +1 -1
  254. package/src/scripts/ai_council/vendor_defaults.ts +45 -0
  255. package/src/scripts/audit_skill_overlap.ts +261 -0
  256. package/src/scripts/before_complete_hook.ts +243 -12
  257. package/src/scripts/bench_touched_file_quality.ts +238 -0
  258. package/src/scripts/check_enforcement_matrix.ts +377 -20
  259. package/src/scripts/check_estate_count.ts +130 -153
  260. package/src/scripts/check_finding_dispositions.ts +116 -4
  261. package/src/scripts/check_hook_bundle_composition.ts +370 -0
  262. package/src/scripts/check_kernel_edit_ratified.ts +49 -11
  263. package/src/scripts/check_memory.ts +7 -1
  264. package/src/scripts/check_release_highlights.ts +10 -0
  265. package/src/scripts/check_release_pr_shape.ts +38 -15
  266. package/src/scripts/check_standing_rule_delivery.ts +33 -13
  267. package/src/scripts/check_verify_expectation_delta.ts +521 -0
  268. package/src/scripts/code_graph/detect.ts +127 -7
  269. package/src/scripts/code_graph/foreign.ts +194 -0
  270. package/src/scripts/code_graph/query.ts +217 -4
  271. package/src/scripts/code_graph/types.ts +26 -2
  272. package/src/scripts/code_graph/validate.ts +3 -1
  273. package/src/scripts/code_graph/verbs.ts +103 -0
  274. package/src/scripts/compile_hook_manifest.ts +33 -1
  275. package/src/scripts/condense.ts +14 -27
  276. package/src/scripts/estate_exemption_shape.ts +202 -0
  277. package/src/scripts/generate_host_cost_table.ts +1 -1
  278. package/src/scripts/hook_manifest.json +1 -1
  279. package/src/scripts/hook_manifest.yaml +150 -9
  280. package/src/scripts/hooks/block_config_weakening.ts +67 -2
  281. package/src/scripts/hooks/block_plumbing_writes.ts +29 -1
  282. package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
  283. package/src/scripts/hooks/concern_failure_policy.ts +280 -0
  284. package/src/scripts/hooks/dispatch_hook.ts +52 -52
  285. package/src/scripts/hooks/exit_codes.ts +2 -1
  286. package/src/scripts/hooks/host_lowering.json +1 -1
  287. package/src/scripts/hooks/host_lowering.ts +19 -1
  288. package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
  289. package/src/scripts/hooks/payload_stub.ts +10 -1
  290. package/src/scripts/hooks/rule_inject_hook.ts +757 -63
  291. package/src/scripts/hooks/table_fingerprint.ts +21 -0
  292. package/src/scripts/hooks/telemetry_usage_hook.ts +64 -8
  293. package/src/scripts/hooks/turn_end_gate_hook.ts +84 -0
  294. package/src/scripts/hooks_status.ts +112 -0
  295. package/src/scripts/injection_scan_hook.ts +7 -0
  296. package/src/scripts/install.ts +72 -185
  297. package/src/scripts/installed_layer_report.ts +103 -0
  298. package/src/scripts/language_mirror_hook.ts +15 -6
  299. package/src/scripts/learning_sidecar.ts +119 -8
  300. package/src/scripts/lint_hook_manifest.ts +53 -0
  301. package/src/scripts/lint_major_migration_sections.ts +156 -26
  302. package/src/scripts/lint_pack_boundaries.ts +63 -5
  303. package/src/scripts/lint_rule_law_section.ts +517 -0
  304. package/src/scripts/lint_workflow_security.ts +161 -5
  305. package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
  306. package/src/scripts/mcp_server/graph_tools.ts +72 -3
  307. package/src/scripts/measure_routing_signal.ts +54 -16
  308. package/src/scripts/measure_skill_ranker_baseline.ts +216 -8
  309. package/src/scripts/memory_learn_hook.ts +91 -6
  310. package/src/scripts/model_rule_injection.ts +52 -5
  311. package/src/scripts/project_thin_rules.ts +34 -327
  312. package/src/scripts/report_evidence_temperature.ts +657 -0
  313. package/src/scripts/report_installed_rule_links.ts +157 -0
  314. package/src/scripts/report_label_agreement.ts +233 -0
  315. package/src/scripts/report_obligation_mechanism.ts +230 -0
  316. package/src/scripts/report_single_token_triggers.ts +277 -0
  317. package/src/scripts/report_skill_ranker_confusion.ts +442 -0
  318. package/src/scripts/report_standing_payload_by_host.ts +188 -15
  319. package/src/scripts/report_verification_record_coverage.ts +306 -0
  320. package/src/scripts/rule_activation_census.ts +5 -2
  321. package/src/scripts/rule_trigger_eval.ts +9 -9
  322. package/src/scripts/schemas/review-findings.schema.json +3 -2
  323. package/src/scripts/schemas/rule.schema.json +5 -0
  324. package/src/scripts/self_review_gate.ts +62 -7
  325. package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
  326. package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
  327. package/src/scripts/sync_gitignore.ts +29 -235
  328. package/src/scripts/trigger_eval_grandfather.json +0 -5
  329. package/src/server/schemas/settings.ts +10 -0
  330. package/src/shared/skillRanking.ts +117 -11
  331. package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
  332. package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
@@ -77,6 +77,19 @@ export const LOWERING_REL = path.join('src', 'scripts', 'hooks', 'host_lowering.
77
77
  export const BEGIN_MARKER = '<!-- BEGIN GENERATED: enforcement-configured-by-slot -->';
78
78
  export const END_MARKER = '<!-- END GENERATED: enforcement-configured-by-slot -->';
79
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
+
80
93
  /** The command a drift failure tells the reader to run. Printed, never guessed at. */
81
94
  export const REGEN_COMMAND = `./scripts-run src/scripts/${GATE} --write`;
82
95
 
@@ -335,31 +348,200 @@ export function renderRegion(lowering: HostLowering): string {
335
348
  return out.join('\n');
336
349
  }
337
350
 
338
- /** Replace the block between the markers. Idempotent by construction. */
339
- export function spliceDoc(text: string, region: string): string {
340
- const b = text.indexOf(BEGIN_MARKER);
341
- 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);
342
414
  if (b === -1 || e === -1 || e < b) {
343
415
  throw new Error(
344
416
  `${GATE}: ${DOC_REL} is missing the generated-region markers ` +
345
- `(${BEGIN_MARKER} … ${END_MARKER}). Restore them rather than hand-writing the ` +
417
+ `(${begin} … ${end}). Restore them rather than hand-writing the ` +
346
418
  'table — the markers are what makes the region regenerable.',
347
419
  );
348
420
  }
349
- 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)}`;
350
422
  }
351
423
 
352
- /** The region body a committed document carries, or null when the markers are gone. */
353
- export function extractRegion(text: string): string | null {
354
- const b = text.indexOf(BEGIN_MARKER);
355
- 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);
356
437
  if (b === -1 || e === -1 || e < b) return null;
357
438
  return text
358
- .slice(b + BEGIN_MARKER.length, e)
439
+ .slice(b + begin.length, e)
359
440
  .replace(/^\n/, '')
360
441
  .replace(/\n$/, '');
361
442
  }
362
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
+
363
545
  /** Outcome cells a committed region carries, with their row label. */
364
546
  export function committedCells(region: string): { label: string; value: string }[] {
365
547
  const out: { label: string; value: string }[] = [];
@@ -472,6 +654,18 @@ export interface HostConcernAudit {
472
654
  * has nowhere to go.
473
655
  */
474
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[];
475
669
  }
476
670
 
477
671
  /** Slots that carry a dispatcher verdict. `ask` is a UI affordance, not one. */
@@ -534,6 +728,7 @@ export function concernSlotAudit(root: string, lowering: HostLowering): HostConc
534
728
  const denySlots = reading.slots.filter((s: SlotReading) => s.literal !== null).length;
535
729
 
536
730
  const noDeny: NullBlockBinding[] = [];
731
+ const blocking: BlockingBinding[] = [];
537
732
  let boundSlots = 0;
538
733
  if (blockRaw !== null && typeof blockRaw === 'object') {
539
734
  for (const [slot, value] of Object.entries(blockRaw as Record<string, unknown>)) {
@@ -553,6 +748,21 @@ export function concernSlotAudit(root: string, lowering: HostLowering): HostConc
553
748
  : row.effective === null
554
749
  ? 'stale-proof'
555
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
+ }
556
766
  if (reason === null) continue;
557
767
  for (const concern of value) {
558
768
  if (typeof concern !== 'string') continue;
@@ -566,6 +776,9 @@ export function concernSlotAudit(root: string, lowering: HostLowering): HostConc
566
776
  }
567
777
  }
568
778
  }
779
+ blocking.sort(
780
+ (a, b) => a.slot.localeCompare(b.slot) || a.concern.localeCompare(b.concern),
781
+ );
569
782
  const reasonRank = (r: NoDenyReason): number =>
570
783
  r === 'unlowerable' ? 0 : r === 'null-block' ? 1 : 2;
571
784
  const sevRank = (s: string): number => (s === 'blocking' ? 0 : s === 'advisory' ? 2 : 1);
@@ -582,11 +795,77 @@ export function concernSlotAudit(root: string, lowering: HostLowering): HostConc
582
795
  lowerableSlots: reading.slots.length,
583
796
  boundSlots,
584
797
  noDeny,
798
+ blocking,
585
799
  });
586
800
  }
587
801
  return out;
588
802
  }
589
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
+
590
869
  /** How each {@link NoDenyReason} is worded. `stale-proof` never says "cannot". */
591
870
  const REASON_TEXT: Readonly<Record<NoDenyReason, string>> = {
592
871
  unlowerable: 'no lowering row for this slot — nothing is bound natively at all',
@@ -672,19 +951,27 @@ export function main(argv?: readonly string[]): number {
672
951
  const ledger = new GateLedger(GATE);
673
952
  ledger.plan(built.rows.map((r) => `${r.host}/${r.slot}`));
674
953
  const region = renderRegion(lowering);
954
+ const audit = concernSlotAudit(root, lowering);
955
+ const severityRegion = renderSeverityRegion(audit);
675
956
 
676
957
  if (write) {
677
958
  let next: string;
678
959
  try {
679
- next = spliceDoc(docText, region);
960
+ next = spliceSeverityDoc(spliceDoc(docText, region), severityRegion);
680
961
  } catch (exc) {
681
962
  process.stderr.write(`❌ ${String(exc)}\n`);
682
963
  return 2;
683
964
  }
684
965
  // The backing check runs BEFORE the write, so a generator bug cannot
685
966
  // put a phantom value into the document and then be caught by the
686
- // check that same generator's output feeds.
687
- 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
+ ];
688
975
  if (phantom.length > 0) {
689
976
  for (const f of phantom) process.stderr.write(`❌ ${GATE}: ${f}\n`);
690
977
  process.stderr.write(
@@ -718,14 +1005,31 @@ export function main(argv?: readonly string[]): number {
718
1005
  return 2;
719
1006
  }
720
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
+
721
1018
  const findings: string[] = [];
722
1019
  findings.push(...unbackedOutcomes(committed, backedOutcomes(lowering)));
723
1020
  findings.push(...built.contradictions);
1021
+ findings.push(...severityFindings(committedSeverity, lowering));
724
1022
  if (committed !== region) {
725
1023
  findings.push(
726
1024
  `${DOC_REL} § the generated region differs from what \`${LOWERING_REL}\` produces.`,
727
1025
  );
728
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
+ }
729
1033
 
730
1034
  // A pair is `fail`ed when the committed cell for it is absent or carries a
731
1035
  // different outcome, and `complete`d otherwise. The denominator is the
@@ -755,7 +1059,7 @@ export function main(argv?: readonly string[]): number {
755
1059
  // The concern-vs-slot audit prints on every read-only run, before the
756
1060
  // verdict, so it is visible whether the row comparison passed or failed.
757
1061
  // It never changes the exit code — see `concernSlotAudit`.
758
- for (const line of renderConcernAudit(concernSlotAudit(root, lowering))) {
1062
+ for (const line of renderConcernAudit(audit)) {
759
1063
  process.stdout.write(`${line}\n`);
760
1064
  }
761
1065
 
@@ -788,15 +1092,33 @@ export function main(argv?: readonly string[]): number {
788
1092
  function selfTest(): number {
789
1093
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'enforcement-matrix-'));
790
1094
  const lowering = _read(ROOT, LOWERING_REL) ?? '';
791
- 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 => {
792
1109
  const dir = path.join(tmp, name);
793
1110
  fs.mkdirSync(path.join(dir, path.dirname(LOWERING_REL)), { recursive: true });
1111
+ fs.mkdirSync(path.join(dir, path.dirname(MANIFEST_REL)), { recursive: true });
794
1112
  fs.mkdirSync(path.join(dir, path.dirname(DOC_REL)), { recursive: true });
795
1113
  fs.writeFileSync(path.join(dir, LOWERING_REL), lowering, 'utf-8');
796
- 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));
797
1118
  fs.writeFileSync(
798
1119
  path.join(dir, DOC_REL),
799
- `# 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`,
800
1122
  'utf-8',
801
1123
  );
802
1124
  return dir;
@@ -807,9 +1129,44 @@ function selfTest(): number {
807
1129
  try {
808
1130
  return runSelfTest({
809
1131
  gate: GATE,
810
- minCases: 5,
811
- minRejectCases: 4,
1132
+ minCases: 8,
1133
+ minRejectCases: 7,
812
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
+ },
813
1170
  {
814
1171
  name: 'a cell claiming `halt-by-state`, which nothing emits, is rejected',
815
1172
  expect: 'reject',