@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
@@ -154,6 +154,8 @@ import { resolveBaseRef } from './_lib/ratchet_base_ref.js';
154
154
  import { materialiseSubtree } from './_lib/base_tree.js';
155
155
  import { SKILLS_POSIX, measureSkillEstate } from './_lib/skill_estate.js';
156
156
  import { CONCERN_MANIFEST_POSIX, countConcerns } from './_lib/concern_estate.js';
157
+ import { exemptionFindings, type ExemptionFinding } from './_lib/exemption_shape.js';
158
+ import { classifyDiff, growthClaims, type GrowthClaim, type OffsetLedger } from './_lib/estate_offsets.js';
157
159
  import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
158
160
 
159
161
  const GATE = 'check_estate_count';
@@ -301,32 +303,7 @@ export interface GrowthFinding {
301
303
  allowance: number;
302
304
  }
303
305
 
304
- /** How a change touched the active roadmap tree, as git reports it. */
305
- export interface OffsetLedger {
306
- /** Files that entered the active top level: new files, and un-parked ones. */
307
- added: string[];
308
- /** Files that left it: deleted, archived, parked or merged away. */
309
- offsets: string[];
310
- /** Added files carrying an `estate_offset_exempt:` reason, with the reason. */
311
- exempt: Array<{ file: string; reason: string }>;
312
- /**
313
- * The subset of `offsets` that went to `later/`.
314
- *
315
- * Tracked separately because parking is the one offset that RAISES another
316
- * gated count: active falls by one and later rises by one, which under an
317
- * exact floor is growth in `later_roadmaps` unless this allowance exists.
318
- * Kept out of a general "any offset raises any allowance" rule on purpose —
319
- * an archived roadmap must not buy a new `later/` file.
320
- */
321
- parked: string[];
322
- }
323
306
 
324
- /** An `estate_growth_exempt:` reason ADDED to a roadmap in this change. */
325
- export interface GrowthClaim {
326
- /** The roadmap the claim was added to, as git reports the path. */
327
- file: string;
328
- reason: string;
329
- }
330
307
 
331
308
  export interface EstateVerdict {
332
309
  counts: EstateCounts;
@@ -362,6 +339,23 @@ export interface EstateVerdict {
362
339
  offsets: OffsetLedger | null;
363
340
  offsetSkipReason: string | null;
364
341
  unpaid: number;
342
+ /**
343
+ * Added exemptions whose reason names no rejected alternative, or repeats
344
+ * another added file's. Empty on every change that adds no exemption.
345
+ */
346
+ exemptionFindings: ExemptionFinding[];
347
+ /**
348
+ * Roadmaps carrying an unscheduled status — REPORTED, never gated.
349
+ *
350
+ * `collect()` excludes them by design, so a draft is invisible to every
351
+ * count above and adding one costs no slot. That is the right policy: a
352
+ * draft is not active work, and charging for one would make every parked
353
+ * idea cost a slot. What was wrong was that the number was invisible too, so
354
+ * a reader of `active_roadmaps 26` could not tell an estate of 26 from an
355
+ * estate of 26 with 14 drafts behind it. It contributes nothing to
356
+ * `withinBudget` — there is no floor for it and no claim path to it.
357
+ */
358
+ draftRoadmaps: number;
365
359
  /**
366
360
  * The provisional-promotion path's state, reported and never acted on.
367
361
  *
@@ -513,143 +507,51 @@ export function countEstate(repoRoot: string): EstateCounts {
513
507
  };
514
508
  }
515
509
 
516
- /** Did the tokeniser resolve under `root`? A ratchet must not mix the two modes. */
517
- export function skillTokensExact(root: string): boolean {
518
- return measureSkillEstate(root).skill_description_tokens !== null;
519
- }
520
-
521
- /** `agents/roadmaps/<name>.md` — the active top level. code-comment-allow provenance-comment -- operand, not provenance */
522
- function isActiveTopLevel(rel: string): boolean {
523
- const norm = rel.split(path.sep).join('/');
524
- if (!norm.startsWith('agents/roadmaps/') || !norm.endsWith('.md')) {
525
- return false;
526
- }
527
- const tail = norm.slice('agents/roadmaps/'.length);
528
- return !tail.includes('/') && isRoadmapCandidate(norm);
529
- }
530
-
531
510
  /**
532
- * A disposition directory — where an offset sends a roadmap.
511
+ * Top-level roadmaps `collect()` drops for an unscheduled status.
533
512
  *
534
- * `stubs/` is in the set, and it was missing from the first version. Un-stubbing
535
- * is the documented promotion path, so a stub moved to the top level is an
536
- * ADDITION that T3 must charge, and a roadmap demoted to a stub is an offset.
537
- * With `stubs/` unrecognised, a promotion was classified as neither and the lint
538
- * could never charge it — the one hole that let an active roadmap arrive for free.
539
- */
540
- function isDisposed(rel: string): boolean {
541
- const norm = rel.split(path.sep).join('/');
542
- return /^agents\/roadmaps\/(archive|later|skipped|stubs)\//.test(norm);
543
- }
544
-
545
- /** A roadmap parked for later — the one disposition that grows another count. */
546
- function isParked(rel: string): boolean {
547
- return rel.split(path.sep).join('/').startsWith('agents/roadmaps/later/');
548
- }
549
-
550
- /**
551
- * Read the exemption reason a newly added roadmap declares, if any.
513
+ * Reported beside the count, never added to it. The exclusion in `collect()` is
514
+ * deliberate and stays: a draft is not active work, and counting one would make
515
+ * every parked idea cost a slot under one-in-one-out. What the exclusion also
516
+ * did, silently, was make the drafts unreadable — the estate report showed
517
+ * `active_roadmaps 26` whether 0 or 14 drafts sat beside them. This closes the
518
+ * reporting half and nothing else, so it has no floor, no allowance and no
519
+ * claim path: it cannot fail.
552
520
  *
553
- * The key lives in the file's own frontmatter rather than in a config or a
554
- * commit trailer, for the reason `RATCHET_RESET_KEY` gives for living inside the
555
- * baseline JSON: the claim then shows up in the diff of the change that makes
556
- * it, and a reviewer sees it without being told to look.
521
+ * `isUnscheduled` is the same predicate `collect()` filters on, so this count
522
+ * and that exclusion cannot disagree — a second status list here is how a
523
+ * reported number starts describing a different set from the one it explains.
557
524
  */
558
- export function exemptionReason(text: string): string | null {
559
- const fm = parseFrontmatter(text);
560
- const raw = (fm as Record<string, unknown>)['estate_offset_exempt'];
561
- if (typeof raw !== 'string') {
562
- return null;
525
+ export function countDrafts(repoRoot: string): number {
526
+ const dir = path.join(repoRoot, ROADMAPS_REL);
527
+ let names: string[];
528
+ try {
529
+ names = fs.readdirSync(dir);
530
+ } catch {
531
+ return 0;
563
532
  }
564
- const reason = raw.trim().replace(/^["']|["']$/g, '').trim();
565
- return reason === '' ? null : reason;
566
- }
567
-
568
- /**
569
- * Classify the change's effect on the active roadmap tree.
570
- *
571
- * Renames carry information a name-only diff loses: `road-to-x.md` →
572
- * `archive/road-to-x.md` is the wanted direction and counts as an offset, while
573
- * `later/road-to-x.md` → `road-to-x.md` is an un-parking and counts as an
574
- * addition. A top-level-to-top-level rename is neither.
575
- */
576
- export function classifyDiff(
577
- nameStatus: string,
578
- readFile: (rel: string) => string | null,
579
- ): OffsetLedger {
580
- const added: string[] = [];
581
- const offsets: string[] = [];
582
- const exempt: Array<{ file: string; reason: string }> = [];
583
- const parked: string[] = [];
584
- for (const line of nameStatus.split('\n')) {
585
- if (line.trim() === '') continue;
586
- const cols = line.split('\t');
587
- const status = (cols[0] ?? '').trim();
588
- if (status.startsWith('R') || status.startsWith('C')) {
589
- const from = cols[1] ?? '';
590
- const to = cols[2] ?? '';
591
- if (isActiveTopLevel(from) && isDisposed(to)) {
592
- offsets.push(from);
593
- if (isParked(to)) parked.push(to);
594
- } else if (isDisposed(from) && isActiveTopLevel(to)) {
595
- added.push(to);
596
- }
533
+ let n = 0;
534
+ for (const name of names) {
535
+ if (!name.endsWith('.md') || !isRoadmapCandidate(name)) continue;
536
+ const abs = path.join(dir, name);
537
+ let text: string;
538
+ try {
539
+ if (!fs.statSync(abs).isFile()) continue;
540
+ text = fs.readFileSync(abs, 'utf-8');
541
+ } catch {
597
542
  continue;
598
543
  }
599
- const file = cols[1] ?? '';
600
- if (!isActiveTopLevel(file)) continue;
601
- if (status === 'A') {
602
- added.push(file);
603
- } else if (status === 'D') {
604
- offsets.push(file);
605
- }
606
- }
607
- for (const file of added) {
608
- const text = readFile(file);
609
- if (text === null) continue;
610
- const reason = exemptionReason(text);
611
- if (reason !== null) {
612
- exempt.push({ file, reason });
613
- }
544
+ if (isUnscheduled(parseFrontmatter(text))) n += 1;
614
545
  }
615
- return { added, offsets, exempt, parked };
546
+ return n;
616
547
  }
617
548
 
618
- /**
619
- * The `estate_growth_exempt:` reasons this change ADDS, read from the patch.
620
- *
621
- * Read from the diff rather than from the file, and that is the point: a claim
622
- * sitting in a roadmap authorises nothing on a later change, so an exemption
623
- * cannot be banked the way surplus in a stored baseline could. It also means a
624
- * newly added roadmap and an edited one need no separate handling — in a
625
- * `base...HEAD` patch both arrive as `+` lines.
626
- *
627
- * `--unified=0` keeps context lines out, so a claim that merely sits NEAR an
628
- * edited line is not read as added. Deliberately tolerant of leading whitespace
629
- * and of quoted values, matching `exemptionReason`; deliberately NOT tolerant of
630
- * an empty reason, because an exemption whose reason is blank is the silent
631
- * exception the key exists to replace.
632
- */
633
- export function growthClaims(patch: string): GrowthClaim[] {
634
- const out: GrowthClaim[] = [];
635
- let file = '';
636
- for (const line of patch.split('\n')) {
637
- // `+++ b/<path>` names the file the following `+` lines belong to. The
638
- // `/dev/null` form is a deletion, which cannot carry a claim.
639
- const head = /^\+\+\+ b\/(.+)$/.exec(line);
640
- if (head !== null) {
641
- file = (head[1] ?? '').trim();
642
- continue;
643
- }
644
- if (!line.startsWith('+') || line.startsWith('+++')) continue;
645
- const m = /^\+\s*estate_growth_exempt:\s*(.+?)\s*$/.exec(line);
646
- if (m === null) continue;
647
- const reason = (m[1] ?? '').trim().replace(/^["']|["']$/g, '').trim();
648
- if (reason !== '') out.push({ file, reason });
649
- }
650
- return out;
549
+ /** Did the tokeniser resolve under `root`? A ratchet must not mix the two modes. */
550
+ export function skillTokensExact(root: string): boolean {
551
+ return measureSkillEstate(root).skill_description_tokens !== null;
651
552
  }
652
553
 
554
+
653
555
  export function evaluate(
654
556
  repoRoot: string,
655
557
  opts: { baseRef?: string | undefined } = {},
@@ -816,6 +718,13 @@ export function evaluate(
816
718
  unpaid = Math.max(0, chargeable.length - offsets.offsets.length);
817
719
  }
818
720
 
721
+ // The SHAPE half, and it is deliberately not gated on `lintApplies`. The
722
+ // threshold decides whether an unoffset addition is charged; it says nothing
723
+ // about whether a claim an author chose to write has to mean something. An
724
+ // exemption below the threshold buys nothing anyway, so a shapeless one
725
+ // below it is a line that will be read as a reason later and is not one.
726
+ const findings = offsets === null ? [] : exemptionFindings(offsets.exempt);
727
+
819
728
  // The allowances, one metric at a time and never a blanket rule. An exempt
820
729
  // addition and a parked roadmap are the two increases the existing policy
821
730
  // already sanctions, and under an exact floor each needs its own headroom or
@@ -883,13 +792,15 @@ export function evaluate(
883
792
  offsets,
884
793
  offsetSkipReason,
885
794
  unpaid,
795
+ exemptionFindings: findings,
796
+ draftRoadmaps: countDrafts(repoRoot),
886
797
  provisionalPromotion,
887
798
  // No floor is a FAILURE, not a skip, and it is the one place this gate
888
799
  // convicts on a missing input. The other halves can report "unproven" and
889
800
  // still leave a meaningful verdict behind; the count half IS the verdict,
890
801
  // so with no floor a green here would assert something nothing measured.
891
802
  // A shrink-only gate whose floor is absent passes every possible tree.
892
- withinBudget: floorSkipReason === null && growth.length === 0 && unpaid === 0,
803
+ withinBudget: floorSkipReason === null && growth.length === 0 && unpaid === 0 && findings.length === 0,
893
804
  };
894
805
  }
895
806
 
@@ -1104,10 +1015,44 @@ function selfTest(): number {
1104
1015
  write(
1105
1016
  dir,
1106
1017
  'agents/roadmaps/road-to-new.md',
1107
- '---\nestate_offset_exempt: fixture — the addition that cannot be offset\n---\n\n' + roadmap('N'),
1018
+ '---\nestate_offset_exempt: fixture — archiving road-to-0 was rejected, it is mid-flight\n---\n\n' +
1019
+ roadmap('N'),
1108
1020
  ),
1109
1021
  }),
1110
1022
  },
1023
+ {
1024
+ // The shape half. The old reason here was "the addition that cannot
1025
+ // be offset", which names no alternative and is exactly what this
1026
+ // case now refuses — so the pair above and below are the same
1027
+ // addition differing only in what its reason says.
1028
+ name: 'exemption naming no rejected alternative → reject',
1029
+ expect: 'reject',
1030
+ run: () =>
1031
+ fixture({
1032
+ roadmaps: 3,
1033
+ base: 'main',
1034
+ after: (dir) =>
1035
+ write(
1036
+ dir,
1037
+ 'agents/roadmaps/road-to-new.md',
1038
+ '---\nestate_offset_exempt: fixture — lane 5 of road-to-parent\n---\n\n' + roadmap('N'),
1039
+ ),
1040
+ }),
1041
+ },
1042
+ {
1043
+ name: 'one exemption reason pasted into two added files → reject',
1044
+ expect: 'reject',
1045
+ run: () =>
1046
+ fixture({
1047
+ roadmaps: 3,
1048
+ base: 'main',
1049
+ after: (dir) => {
1050
+ const fm = '---\nestate_offset_exempt: fixture — archiving road-to-0 was rejected, it is mid-flight\n---\n\n';
1051
+ write(dir, 'agents/roadmaps/road-to-n1.md', fm + roadmap('N1'));
1052
+ write(dir, 'agents/roadmaps/road-to-n2.md', fm + roadmap('N2'));
1053
+ },
1054
+ }),
1055
+ },
1111
1056
  {
1112
1057
  name: 'addition paid for by an archive move → accept',
1113
1058
  expect: 'accept',
@@ -1319,7 +1264,7 @@ export function main(argv: string[] = process.argv.slice(2)): number {
1319
1264
  }
1320
1265
 
1321
1266
  const ledger = new GateLedger(GATE);
1322
- ledger.plan([...METRICS, 'floor', 'one_in_one_out']);
1267
+ ledger.plan([...METRICS, 'floor', 'one_in_one_out', 'exemption_shape']);
1323
1268
  for (const metric of METRICS) {
1324
1269
  // Round 2 finding 3, kept: a metric is recorded ONCE here, for whichever
1325
1270
  // way it actually failed, so the ledger cannot show a green completeness
@@ -1335,6 +1280,10 @@ export function main(argv: string[] = process.argv.slice(2)): number {
1335
1280
  if (verdict.offsets === null) ledger.skip('one_in_one_out', 'precondition_unmet');
1336
1281
  else if (verdict.unpaid > 0) ledger.fail('one_in_one_out', `${String(verdict.unpaid)} unpaid addition(s)`);
1337
1282
  else ledger.complete('one_in_one_out');
1283
+ if (verdict.offsets === null) ledger.skip('exemption_shape', 'precondition_unmet');
1284
+ else if (verdict.exemptionFindings.length > 0)
1285
+ ledger.fail('exemption_shape', `${String(verdict.exemptionFindings.length)} unshaped exemption(s)`);
1286
+ else ledger.complete('exemption_shape');
1338
1287
 
1339
1288
  if (json) {
1340
1289
  // The ledger goes to stderr here so stdout stays parseable after the
@@ -1358,6 +1307,12 @@ export function main(argv: string[] = process.argv.slice(2)): number {
1358
1307
  ` ${metric.padEnd(18)} ${String(live).padStart(5)} (floor ${String(base)} at ${verdict.floorRef ?? '?'}, ${sign}${String(delta)})\n`,
1359
1308
  );
1360
1309
  }
1310
+ // Beside the gated counts, never among them: no floor line, no delta, and
1311
+ // the label says what it is. A reader comparing two runs can see the drafts
1312
+ // move without reading this as a ratchet that moved.
1313
+ process.stdout.write(
1314
+ ` ${'draft_roadmaps'.padEnd(18)} ${String(verdict.draftRoadmaps).padStart(5)} (reported, not gated — excluded from active_roadmaps)\n`,
1315
+ );
1361
1316
  if (verdict.offsets !== null) {
1362
1317
  const o = verdict.offsets;
1363
1318
  process.stdout.write(
@@ -1435,6 +1390,28 @@ export function main(argv: string[] = process.argv.slice(2)): number {
1435
1390
  ' that costs one reviewable line instead of a silent exception.\n',
1436
1391
  );
1437
1392
  }
1393
+
1394
+ for (const f of verdict.exemptionFindings) {
1395
+ if (f.kind === 'shapeless') {
1396
+ process.stderr.write(
1397
+ `❌ ${f.file}: estate_offset_exempt names no rejected alternative.\n` +
1398
+ ` reason: ${f.reason.slice(0, 160)}\n` +
1399
+ ' The exemption is the escape hatch from one-in-one-out, so its reason has to\n' +
1400
+ ' say what you considered INSTEAD of adding a file — archiving something,\n' +
1401
+ ' parking it in `later/`, merging into an existing roadmap — and why that was\n' +
1402
+ ' not available. Name the disposition you rejected. Measured over the existing\n' +
1403
+ ' 221 exemptions, 43 name none; this check applies to ADDED files only, so\n' +
1404
+ ' nothing already in the tree is re-read.\n',
1405
+ );
1406
+ } else {
1407
+ process.stderr.write(
1408
+ `❌ ${f.file}: estate_offset_exempt repeats ${f.twin ?? '(another added file)'} verbatim.\n` +
1409
+ ` reason: ${f.reason.slice(0, 160)}\n` +
1410
+ ' One sentence pasted into every file a round adds is one claim, not N. Write\n' +
1411
+ ' the reason this FILE could not be offset, or offset the ones that could.\n',
1412
+ );
1413
+ }
1414
+ }
1438
1415
  ledger.report();
1439
1416
  if (verdict.withinBudget) {
1440
1417
  process.stdout.write(`✅ ${GATE}: estate within its ratchet.\n`);
@@ -75,6 +75,7 @@ import * as path from 'node:path';
75
75
  import process from 'node:process';
76
76
  import { fileURLToPath } from 'node:url';
77
77
 
78
+ import { coverageLabel } from './_lib/review_coverage.js';
78
79
  import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
79
80
 
80
81
  const _HERE = fileURLToPath(import.meta.url);
@@ -84,6 +85,27 @@ export const LEDGER_DIR = 'agents/evidence/release-findings';
84
85
  export const DISPOSITION_STATUSES = ['fixed', 'false_positive', 'accepted_risk'] as const;
85
86
  export type DispositionStatus = (typeof DISPOSITION_STATUSES)[number];
86
87
 
88
+ /**
89
+ * Every status that means a human re-read the finding and decided.
90
+ *
91
+ * `still_open` is the fourth, and it exists so that "give every row a terminal
92
+ * status" does not read as "close every finding". A row re-checked against the
93
+ * merged head and found to be a real, unfixed issue is adjudicated; writing
94
+ * `accepted_risk` over it to make a count reach zero would be the cheapest way
95
+ * to satisfy the requirement and the one that destroys it.
96
+ *
97
+ * It is deliberately NOT in {@link DISPOSITION_STATUSES}: a BLOCKING row may
98
+ * never pass on it. `missing_dispositions` reads only the three, so a blocking
99
+ * finding marked `still_open` reports as an unknown status and the gate stays
100
+ * red — which is the behaviour a blocking finding that is still open should
101
+ * have.
102
+ *
103
+ * `open` from the schema is absent on purpose. The schema documents it as the
104
+ * INITIAL state, so counting it would make the tally reachable without anyone
105
+ * re-reading a finding.
106
+ */
107
+ export const TERMINAL_STATUSES = [...DISPOSITION_STATUSES, 'still_open'] as const;
108
+
87
109
  export interface LedgerFinding {
88
110
  finding_id: string;
89
111
  severity: 'critical' | 'high' | 'medium' | 'low';
@@ -95,6 +117,12 @@ export interface LedgerFinding {
95
117
  rationale?: string;
96
118
  verified_by?: string;
97
119
  date?: string;
120
+ /**
121
+ * The tree's mechanical disproof of the finding's premise, written by
122
+ * `self_review_gate.annotateContradicted`. A finding carrying one is not
123
+ * blocking — see `isBlocking`.
124
+ */
125
+ contradicted?: string;
98
126
  }
99
127
 
100
128
  export interface Ledger {
@@ -114,6 +142,12 @@ export interface Ledger {
114
142
  assurance?: string;
115
143
  reviewers?: string[];
116
144
  coverage?: unknown;
145
+ /**
146
+ * Per-run count of findings quoting a supplied fact, written by
147
+ * `self_review_gate` under `--findings-out`. Declared here because ingest
148
+ * now carries it; see {@link INTEGRITY_FIELDS}.
149
+ */
150
+ fact_claims?: unknown;
117
151
  }
118
152
 
119
153
  /**
@@ -133,7 +167,7 @@ export interface Ledger {
133
167
  * consumer inferring it from the findings would be inventing the integrity
134
168
  * claim the fields exist to record.
135
169
  */
136
- export const INTEGRITY_FIELDS = [
170
+ const SCHEMA_INTEGRITY_FIELDS = [
137
171
  'review_independence',
138
172
  'context_relation',
139
173
  'acceptance_status',
@@ -142,6 +176,24 @@ export const INTEGRITY_FIELDS = [
142
176
  'coverage',
143
177
  ] as const;
144
178
 
179
+ /**
180
+ * The carried set: the six above plus `fact_claims`.
181
+ *
182
+ * `fact_claims` is written by `self_review_gate.ts` at `--findings-out` time and
183
+ * was dropped by every ingest, because this list predates it. The field is the
184
+ * falsifier for the supplied-facts change — a count over the next cut — and a
185
+ * falsifier that never reaches the record is not one. It is carried rather than
186
+ * recomputed for the same reason the other six are: only the producing run knows
187
+ * what it quoted.
188
+ *
189
+ * It is NOT in {@link SCHEMA_INTEGRITY_FIELDS}, which is the set the ingest
190
+ * warning names. That warning tells a writer that `check_review_schema` derives
191
+ * `acceptance_status` and `assurance` from `review_independence` and will red on
192
+ * a ledger declaring neither — a sentence that is simply untrue of `fact_claims`,
193
+ * and naming it there would make the warning wrong to buy one shorter list.
194
+ */
195
+ export const INTEGRITY_FIELDS = [...SCHEMA_INTEGRITY_FIELDS, 'fact_claims'] as const;
196
+
145
197
  /**
146
198
  * Merge a self-review artifact into a ledger: new findings by id, the integrity
147
199
  * fields on first write, and — when the review found nothing — the reason that
@@ -220,8 +272,21 @@ function _clean_review_reason(artifact: Record<string, unknown>): string {
220
272
  );
221
273
  }
222
274
 
223
- /** Mirrors self_review_gate.classifyBlocking — security/claim × critical/high. */
224
- export function isBlocking(f: Pick<LedgerFinding, 'kind' | 'severity'>): boolean {
275
+ /**
276
+ * Mirrors self_review_gate.classifyBlocking — security/claim × critical/high,
277
+ * MINUS anything the tree disproved.
278
+ *
279
+ * The `contradicted` clause is not a second policy; it is the same one, and
280
+ * the mirror has to carry it or the two gates disagree: the review would stop
281
+ * counting a disproved deletion claim as blocking while the release kept
282
+ * demanding a disposition for it. A `contradicted` value is written only by
283
+ * `annotateContradicted`, which checks the claim against
284
+ * `git diff --name-status base...HEAD` AND the working tree.
285
+ */
286
+ export function isBlocking(f: Pick<LedgerFinding, 'kind' | 'severity' | 'contradicted'>): boolean {
287
+ if ((f.contradicted ?? '').trim() !== '') {
288
+ return false;
289
+ }
225
290
  return (
226
291
  (f.kind === 'security' || f.kind === 'claim') &&
227
292
  (f.severity === 'critical' || f.severity === 'high')
@@ -263,6 +328,66 @@ export function missing_dispositions(findings: readonly LedgerFinding[]): string
263
328
  return problems;
264
329
  }
265
330
 
331
+ /** What the success line reports, counted over the two populations apart. */
332
+ export interface DispositionTally {
333
+ /** Rows `isBlocking` admits. */
334
+ readonly blockingTotal: number;
335
+ /** Of those, the ones `missing_dispositions` finds nothing wrong with. */
336
+ readonly blockingDispositioned: number;
337
+ /** Every other row. The gate cannot fail on these; it can still count them. */
338
+ readonly nonBlockingTotal: number;
339
+ /** Of those, the ones carrying a {@link TERMINAL_STATUSES} value. */
340
+ readonly nonBlockingTerminal: number;
341
+ }
342
+
343
+ /**
344
+ * Count what the gate checked, split by what it can actually require.
345
+ *
346
+ * The recorded failure this exists for: `--release 16.2.0` printed "all 20
347
+ * recorded finding(s) for 16.2.0 dispositioned (blocking ones completely)" over
348
+ * a ledger carrying a `status` on 2 of 20 rows. Both halves of that sentence are
349
+ * individually defensible — the gate did require the blocking ones, and it did
350
+ * complete — and together they say the opposite of the truth, because nobody
351
+ * reads a parenthetical as a retraction of the word in front of it. Eighteen
352
+ * findings had never been adjudicated and the record said they had.
353
+ *
354
+ * The fix is a count, not a new refusal. Exit semantics are untouched: a
355
+ * non-blocking row still cannot fail the gate. What changes is that the line
356
+ * stops claiming the rows it never looked at.
357
+ *
358
+ * An unrecognised status counts as NOT terminal, which is the conservative
359
+ * direction: a typo surfaces as a row the line says is unadjudicated, rather
360
+ * than disappearing into a count that reads as finished.
361
+ */
362
+ export function disposition_tally(findings: readonly LedgerFinding[]): DispositionTally {
363
+ let blockingTotal = 0;
364
+ let blockingDispositioned = 0;
365
+ let nonBlockingTotal = 0;
366
+ let nonBlockingTerminal = 0;
367
+ for (const f of findings) {
368
+ if (isBlocking(f)) {
369
+ blockingTotal++;
370
+ if (missing_dispositions([f]).length === 0) {
371
+ blockingDispositioned++;
372
+ }
373
+ continue;
374
+ }
375
+ nonBlockingTotal++;
376
+ if ((TERMINAL_STATUSES as readonly string[]).includes((f.status ?? '').trim())) {
377
+ nonBlockingTerminal++;
378
+ }
379
+ }
380
+ return { blockingTotal, blockingDispositioned, nonBlockingTotal, nonBlockingTerminal };
381
+ }
382
+
383
+ /** The tally as the success line renders it. Never the word "all". */
384
+ export function disposition_summary(t: DispositionTally): string {
385
+ return (
386
+ `blocking ${String(t.blockingDispositioned)}/${String(t.blockingTotal)} dispositioned · ` +
387
+ `non-blocking ${String(t.nonBlockingTerminal)}/${String(t.nonBlockingTotal)} carry a terminal status`
388
+ );
389
+ }
390
+
266
391
  /**
267
392
  * An empty finding set must say why it is empty.
268
393
  *
@@ -413,7 +538,7 @@ export function resolve_release_status(release: string): ReleaseStatus {
413
538
 
414
539
  /** Findings the gate comment reported but the ledger never ingested. */
415
540
  export function unrecorded_findings(
416
- reported: ReadonlyArray<Pick<LedgerFinding, 'finding_id' | 'severity' | 'kind' | 'title'>>,
541
+ reported: ReadonlyArray<Pick<LedgerFinding, 'finding_id' | 'severity' | 'kind' | 'title' | 'contradicted'>>,
417
542
  ledger: readonly LedgerFinding[],
418
543
  ): string[] {
419
544
  const known = new Set(ledger.map((f) => f.finding_id));
@@ -558,7 +683,7 @@ function main(argv: readonly string[]): number {
558
683
  'ledger is a result rather than an absence\n',
559
684
  );
560
685
  }
561
- const absent = INTEGRITY_FIELDS.filter((k) => ledger[k] === undefined);
686
+ const absent = SCHEMA_INTEGRITY_FIELDS.filter((k) => ledger[k] === undefined);
562
687
  if (absent.length > 0) {
563
688
  process.stdout.write(
564
689
  `⚠️ artifact carried no ${absent.join(', ')} — ` +
@@ -656,10 +781,16 @@ function main(argv: readonly string[]): number {
656
781
 
657
782
  if (problems.length === 0) {
658
783
  const n = ledger.findings.length;
784
+ // The label is appended to BOTH lines, including the zero-findings one:
785
+ // a review that read 65 of 678 files and reported nothing is the case
786
+ // where "no findings" is least informative on its own.
787
+ const partial = coverageLabel(ledger.coverage);
788
+ const suffix = partial === null ? '' : ` — ${partial}`;
659
789
  process.stdout.write(
660
790
  n === 0
661
- ? `✅ no recorded findings for ${release} (ledger ${fs.existsSync(ledgerPath) ? 'empty' : 'absent'})\n`
662
- : `✅ all ${n} recorded finding(s) for ${release} dispositioned (blocking ones completely)\n`,
791
+ ? `✅ no recorded findings for ${release} (ledger ${fs.existsSync(ledgerPath) ? 'empty' : 'absent'})${suffix}\n`
792
+ : `✅ ${String(n)} recorded finding(s) for ${release} — ` +
793
+ `${disposition_summary(disposition_tally(ledger.findings))}${suffix}\n`,
663
794
  );
664
795
  return 0;
665
796
  }