@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
@@ -34,7 +34,13 @@ import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs
34
34
  import path from 'node:path';
35
35
  import { fileURLToPath } from 'node:url';
36
36
 
37
- import { AnthropicClient, load_anthropic_key } from './ai_council/clients.js';
37
+ import { CLI_CONSUMER_SELF_REVIEW } from './ai_council/cli_call_budget.js';
38
+ import {
39
+ AnthropicClient,
40
+ AnthropicCliClient,
41
+ type ExternalAIClient,
42
+ load_anthropic_key,
43
+ } from './ai_council/clients.js';
38
44
  import { independenceFields } from './_lib/review_independence.js';
39
45
 
40
46
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -50,6 +56,15 @@ export interface Finding {
50
56
  title: string;
51
57
  detail: string;
52
58
  file?: string;
59
+ /**
60
+ * Set when the tree mechanically DISPROVES the finding's premise — today
61
+ * only for the deletion class: the finding says an artifact was removed,
62
+ * the range deletes nothing by that name, and the artifact is still in the
63
+ * tree. Carries the disproof, so the finding stays readable rather than
64
+ * disappearing. A finding carrying this is not merge-blocking; see
65
+ * `classifyBlocking`.
66
+ */
67
+ contradicted?: string;
53
68
  }
54
69
 
55
70
  /**
@@ -74,6 +89,13 @@ export function findingId(f: Pick<Finding, 'kind' | 'title' | 'file'>): string {
74
89
  * block ONLY on the narrow security/claim × {critical,high} intersection.
75
90
  */
76
91
  export function classifyBlocking(f: Finding): boolean {
92
+ // A premise the tree DISPROVES cannot block a merge. This is not leniency
93
+ // and not a heuristic: `contradicted` is set only where a deletion claim
94
+ // was checked against `git diff --name-status base...HEAD` AND against the
95
+ // working tree, and both said the named artifact is present and undeleted.
96
+ // See `contradictedByTree` for why this class needed a mechanical disproof
97
+ // rather than one more sentence in the prompt.
98
+ if ((f.contradicted ?? '').trim() !== '') return false;
77
99
  return (f.kind === 'security' || f.kind === 'claim') && (f.severity === 'critical' || f.severity === 'high');
78
100
  }
79
101
 
@@ -120,6 +142,271 @@ function changedFiles(baseRef: string, cwd: string = REPO_ROOT): string[] {
120
142
  .filter(isReviewablePath);
121
143
  }
122
144
 
145
+ /**
146
+ * The paths the range actually DELETES, from `git diff --name-status`.
147
+ *
148
+ * Deliberately NOT filtered by `isReviewablePath`: the question this answers is
149
+ * "did the range remove this artifact", and a removal under `dist/agent-src/`
150
+ * is a removal even though the projection is never sent to the reviewer. A
151
+ * filter here would turn a real deletion into a disproof of itself.
152
+ *
153
+ * Renames are reported by `--name-status` as `R<score>\told\tnew`, and the old
154
+ * path IS gone, so it is counted. A roadmap moved into `archive/` shows up as a
155
+ * genuine deletion of its old path, which is exactly the reading a finding
156
+ * about that path deserves.
157
+ */
158
+ export function rangeDeletions(baseRef: string, cwd: string = REPO_ROOT): Set<string> {
159
+ return parseDeletions(nameStatusText(baseRef, cwd));
160
+ }
161
+
162
+ /**
163
+ * The paths the range MODIFIES, adds, or renames TO — everything it touched
164
+ * that still has a path afterwards.
165
+ *
166
+ * The complement of `rangeDeletions` over the same `--name-status` output, and
167
+ * the input `contradictedByTree` needs to tell "the range did not touch this"
168
+ * from "the range changed the inside of this". Unfiltered for the same reason
169
+ * `rangeDeletions` is: the question is what the RANGE did, not what the
170
+ * reviewer was sent.
171
+ */
172
+ export function rangeModifications(baseRef: string, cwd: string = REPO_ROOT): Set<string> {
173
+ return parseModifications(nameStatusText(baseRef, cwd));
174
+ }
175
+
176
+ /** One `--name-status` read, shared by both parses so the range is read once. */
177
+ function nameStatusText(baseRef: string, cwd: string = REPO_ROOT): string {
178
+ const r = spawnSync('git', ['-c', 'core.quotePath=false', 'diff', '--name-status', `${baseRef}...HEAD`], {
179
+ cwd,
180
+ encoding: 'utf8',
181
+ });
182
+ if (r.status !== 0) {
183
+ throw new Error(`git diff --name-status failed: ${(r.stderr ?? '').toString().slice(0, 300)}`);
184
+ }
185
+ return r.stdout ?? '';
186
+ }
187
+
188
+ /** Pure core of `rangeDeletions`, so the parse is testable without a repo. */
189
+ export function parseDeletions(nameStatus: string): Set<string> {
190
+ const out = new Set<string>();
191
+ for (const line of nameStatus.split('\n')) {
192
+ const cols = line.split('\t');
193
+ const status = (cols[0] ?? '').trim();
194
+ if (status === '') continue;
195
+ if (status === 'D' && cols[1]) out.add(cols[1].trim());
196
+ // A rename removes its OLD path; `R100\told\tnew` puts it in column 1.
197
+ if (status.startsWith('R') && cols[1]) out.add(cols[1].trim());
198
+ }
199
+ return out;
200
+ }
201
+
202
+ /**
203
+ * Pure core of `rangeModifications` — every path the range left standing.
204
+ *
205
+ * A `D` row contributes nothing. A rename contributes its NEW path (column 2),
206
+ * never its old one: the old path is a deletion and is parsed as such. Every
207
+ * other status (`M`, `A`, `C`, `T`, and the `U` of an unmerged path) has its
208
+ * path in column 1 and that path exists after the range.
209
+ */
210
+ export function parseModifications(nameStatus: string): Set<string> {
211
+ const out = new Set<string>();
212
+ for (const line of nameStatus.split('\n')) {
213
+ const cols = line.split('\t');
214
+ const status = (cols[0] ?? '').trim();
215
+ if (status === '' || status === 'D') continue;
216
+ // `R<score>\told\tnew` / `C<score>\tsrc\tdest` — the surviving path is column 2.
217
+ const col = status.startsWith('R') || status.startsWith('C') ? cols[2] : cols[1];
218
+ if (col) out.add(col.trim());
219
+ }
220
+ return out;
221
+ }
222
+
223
+ /**
224
+ * Names a finding points at: its `file`, plus every backticked token in the
225
+ * title and detail that could be a path or an artifact directory name.
226
+ *
227
+ * Backticks are the convention the review prompt asks for and the one every
228
+ * measured finding used, so this reads what the model actually writes rather
229
+ * than guessing at prose.
230
+ */
231
+ export function namedArtifacts(f: Pick<Finding, 'title' | 'detail' | 'file'>): string[] {
232
+ const out = new Set<string>();
233
+ if (f.file) out.add(f.file);
234
+ for (const m of `${f.title}\n${f.detail}`.matchAll(/`([^`\n]{2,120})`/g)) {
235
+ const tok = (m[1] ?? '').trim();
236
+ // A token with whitespace is prose in backticks, not a name.
237
+ if (tok !== '' && !/\s/.test(tok)) out.add(tok);
238
+ }
239
+ return [...out];
240
+ }
241
+
242
+ /** Does the finding's text assert that something was removed from the tree? */
243
+ export function assertsDeletion(f: Pick<Finding, 'title' | 'detail'>): boolean {
244
+ return /\b(deleted|removed|deletion|removal)\b/i.test(`${f.title}\n${f.detail}`);
245
+ }
246
+
247
+ /**
248
+ * The disproof, or null when there is none.
249
+ *
250
+ * WHY THIS IS MECHANICAL AND NOT A PROMPT LINE. The review runs over a
251
+ * per-file PARTITION of the span and, on a large release, over only part of it
252
+ * — 202 of 401 files at the 16.1.0 cut. A reviewer holding half the files
253
+ * cannot distinguish "this artifact is absent from my chunk" from "this
254
+ * artifact was deleted", and on that cut it reported two `critical security`
255
+ * deletions of skills the range never touched. Both blocked the release and
256
+ * cost a full adjudication cycle each.
257
+ *
258
+ * The prompt already carries a sentence for the neighbouring class ("Do NOT
259
+ * report a feature as not in the diff when it is present in the release
260
+ * range") and the same run violated it, which is the evidence that a sentence
261
+ * is not the control here.
262
+ *
263
+ * The test is conservative in the direction that matters: a disproof needs a
264
+ * named artifact that is BOTH absent from the range's deletion set AND present
265
+ * in the tree AND untouched by the range.
266
+ *
267
+ * THE THIRD HALF, AND WHY IT WAS MISSING. The first two were written against
268
+ * WHOLE-FILE deletion, where "a genuine deletion satisfies neither half" is
269
+ * true. It is false for a removal asserted INSIDE a file: "digest verification
270
+ * was removed in `install.ts`" names a path the range MODIFIED, so the path is
271
+ * absent from the deletion set and present in the tree, and the finding was
272
+ * de-blocked as though it had been checked. A path the range modified is
273
+ * evidence the range touched that artifact — the opposite of this disproof's
274
+ * premise — so it can no longer carry one.
275
+ *
276
+ * WHAT THIS GATE STILL CANNOT DECIDE, and says nothing about. The disproof is
277
+ * built entirely out of PATHS: the deletion set, the modification set, and the
278
+ * working tree. A claim about something that has no path of its own — a
279
+ * symbol, an export, a config key, a behaviour, a sentence inside a surviving
280
+ * file — cannot be resolved to a candidate, so no half of the test applies and
281
+ * the finding is returned unannotated. That is the intended reading: the gate
282
+ * cannot decide such a claim, it is not asserting the claim is true, and an
283
+ * operator adjudicating the block is reading an UNCHECKED finding rather than
284
+ * an unrefuted one. Widening the test to cover that class would mean deciding
285
+ * a claim from the same partition the reviewer had, which is the fabrication
286
+ * this mechanism exists to absorb.
287
+ */
288
+ export function contradictedByTree(
289
+ f: Pick<Finding, 'title' | 'detail' | 'file'>,
290
+ deleted: ReadonlySet<string>,
291
+ exists: (p: string) => boolean,
292
+ modified: ReadonlySet<string> = new Set<string>(),
293
+ ): string | null {
294
+ if (!assertsDeletion(f)) return null;
295
+ for (const name of namedArtifacts(f)) {
296
+ // Any deletion whose path mentions the token leaves the claim standing.
297
+ if ([...deleted].some((d) => d === name || d.includes(name))) continue;
298
+ const candidates = [name, `src/skills/${name}/SKILL.md`, `src/rules/${name}.md`, `src/scripts/${name}`];
299
+ // The range touched something this token resolves to — a whole-artifact
300
+ // disproof cannot be read off a path the range changed. Checked over
301
+ // EVERY candidate, not only the one `exists` happens to hit first: the
302
+ // conservative direction is fewer disproofs, never more.
303
+ if (candidates.some((c) => modified.has(c))) continue;
304
+ const hit = candidates.find((c) => exists(c));
305
+ if (hit !== undefined) {
306
+ return (
307
+ `the range deletes nothing matching \`${name}\`, and \`${hit}\` is present in the tree — ` +
308
+ 'checked against `git diff --name-status base...HEAD` and the working tree, not against the review chunk.'
309
+ );
310
+ }
311
+ }
312
+ return null;
313
+ }
314
+
315
+ /**
316
+ * The two counts road-to-a-fact-plane Step 2.3 pre-registers as its falsifier,
317
+ * written into the findings artifact so the next cut can be read rather than
318
+ * recomputed.
319
+ *
320
+ * `asserting_removal` is the population — findings that make a checkable
321
+ * factual claim of the deletion class. `disproved_by_tree` is how many of them
322
+ * the range and the tree refuted. The premise of the fact block is that
323
+ * supplying the range's facts lowers the first and, with it, the second; a
324
+ * `disproved_by_tree` that does not fall after the block ships is the honest
325
+ * null, and the roadmap records that result rather than widening the mechanism
326
+ * to chase it.
327
+ *
328
+ * Every recorded cut so far reports `disproved_by_tree` as 0 for a reason that
329
+ * is not a measurement: the annotation pass landed after the last of them, so
330
+ * the number was never produced. The baseline is "never observed", not
331
+ * "observed as zero", and the first cut after this change is the first reading.
332
+ */
333
+ export function factClaimCounts(findings: readonly Finding[]): {
334
+ asserting_removal: number;
335
+ disproved_by_tree: number;
336
+ } {
337
+ return {
338
+ asserting_removal: findings.filter((f) => assertsDeletion(f)).length,
339
+ disproved_by_tree: findings.filter((f) => (f.contradicted ?? '').trim() !== '').length,
340
+ };
341
+ }
342
+
343
+ /** Render one list of the fact block, or an explicit `(none)`. */
344
+ function factList(label: string, paths: readonly string[]): string {
345
+ const head = `${label} — ${String(paths.length)}:`;
346
+ return paths.length === 0 ? `${head}\n (none)` : `${head}\n${paths.map((p) => ` ${p}`).join('\n')}`;
347
+ }
348
+
349
+ /**
350
+ * The range's facts, serialised for the reviewer.
351
+ *
352
+ * WHY THE REVIEWER IS HANDED THESE RATHER THAN LEFT TO INFER THEM. The review
353
+ * runs over a per-file PARTITION of the span and, on a large release, over only
354
+ * part of it — 202 of 401 files at the 16.1.0 cut. From inside one chunk, "this
355
+ * path is not in front of me" and "this path was deleted" are the same
356
+ * observation, and on that cut the model resolved the ambiguity the wrong way
357
+ * twice, in `critical security` findings that each cost an adjudication cycle.
358
+ * `contradictedByTree` catches that class AFTER the spend; this block removes
359
+ * the inference that produces it.
360
+ *
361
+ * Exactly three lists, all of them already computed by this gate: the paths the
362
+ * range deleted, the paths it changed and left standing, and the working tree's
363
+ * presence check over both — reported by its EXCEPTIONS, because the normal
364
+ * case is empty and a third full copy of the same paths would spend budget on
365
+ * a line the reviewer can derive. Nothing else is added: the block is paths, so
366
+ * it scales with file count and not with change size, and the six-request
367
+ * ceiling stays a budget question this does not reopen.
368
+ */
369
+ export function factBlock(
370
+ deleted: ReadonlySet<string>,
371
+ modified: ReadonlySet<string>,
372
+ exists: (p: string) => boolean,
373
+ ): string {
374
+ const del = [...deleted].sort();
375
+ const mod = [...modified].sort();
376
+ return [
377
+ '=== RANGE FACTS (AUTHORITATIVE) ===',
378
+ 'Read from `git diff --name-status <base>...HEAD` and from the working tree,',
379
+ 'NOT from the diff below. The diff below is ONE PARTITION of this range: a',
380
+ 'path missing from it is a path outside your chunk, never a deleted path.',
381
+ 'A claim that contradicts these lists is a defect in the finding, not a',
382
+ 'finding about the tree — do not report it.',
383
+ '',
384
+ factList('DELETED BY THIS RANGE', del),
385
+ '',
386
+ factList('CHANGED BY THIS RANGE, AND THE TREE HOLDS THEM', mod),
387
+ '',
388
+ 'PRESENCE CHECK (exceptions to the two lists above):',
389
+ factList(' deleted, yet still present in the working tree', del.filter((p) => exists(p))),
390
+ factList(' changed, yet absent from the working tree', mod.filter((p) => !exists(p))),
391
+ '=== END RANGE FACTS ===',
392
+ '',
393
+ '',
394
+ ].join('\n');
395
+ }
396
+
397
+ /** Annotate every finding the tree disproves. Pure given `deleted` + `exists`. */
398
+ export function annotateContradicted(
399
+ findings: readonly Finding[],
400
+ deleted: ReadonlySet<string>,
401
+ exists: (p: string) => boolean,
402
+ modified: ReadonlySet<string> = new Set<string>(),
403
+ ): Finding[] {
404
+ return findings.map((f) => {
405
+ const why = contradictedByTree(f, deleted, exists, modified);
406
+ return why === null ? f : { ...f, contradicted: why };
407
+ });
408
+ }
409
+
123
410
  function diffText(baseRef: string, files: string[], cwd: string = REPO_ROOT): string {
124
411
  if (files.length === 0) return '';
125
412
  const r = spawnSync('git', ['diff', `${baseRef}...HEAD`, '--', ...files], {
@@ -459,6 +746,14 @@ export interface ReviewPlan {
459
746
  * it thrown away.
460
747
  */
461
748
  partition: DiffPartition;
749
+ /**
750
+ * The range facts prepended to EVERY request — see `factBlock`.
751
+ *
752
+ * Computed in the plan rather than at send time so `--dry-run` can print
753
+ * the request the live run would send, and so the partition budget can
754
+ * reserve the block's length instead of discovering it after the split.
755
+ */
756
+ facts: string;
462
757
  /** Model calls the live run will make — one per budgeted chunk. */
463
758
  requests: number;
464
759
  /** Files no request will carry, with the reason. */
@@ -499,22 +794,33 @@ export function buildPlan(baseRef: string, cwd: string = REPO_ROOT): ReviewPlan
499
794
  // REQUEST COUNT rather than only a token estimate. This gate spends money
500
795
  // per request, and a plan that reports "~N tokens" without saying how many
501
796
  // calls that becomes hides the number the maintainer is deciding about.
502
- const partition = partitionDiff(perFileDiffs(analysisBase, files, cwd));
797
+ // The facts ride on every request, so the per-request diff budget has to
798
+ // pay for them. Reserving here rather than at send time is what keeps the
799
+ // block from silently pushing a chunk over the provider's cap — the exact
800
+ // failure four consecutive releases already spent a review on.
801
+ const facts = factBlock(rangeDeletions(analysisBase, cwd), rangeModifications(analysisBase, cwd), (p) =>
802
+ existsSync(path.join(cwd, p)),
803
+ );
804
+ const partition = partitionDiff(
805
+ perFileDiffs(analysisBase, files, cwd),
806
+ Math.max(1, PROMPT_BUDGET_CHARS - facts.length),
807
+ );
503
808
  return {
504
809
  partition,
810
+ facts,
505
811
  requests: partition.chunks.length,
506
812
  unreviewed: partition.unreviewed,
507
813
  skills: [...REVIEW_SKILLS],
508
814
  files,
509
815
  analysisBase,
510
816
  ...(release ? { release } : {}),
511
- promptChars: systemPrompt.length + diff.length,
817
+ promptChars: systemPrompt.length + facts.length + diff.length,
512
818
  escalation,
513
819
  note:
514
820
  files.length === 0
515
821
  ? 'No reviewable (non-generated) files changed — the live review would no-op.'
516
822
  : `${files.length} reviewable file(s); live review would send ~${Math.ceil(
517
- (systemPrompt.length * Math.max(1, partition.chunks.length) + diff.length) /
823
+ ((systemPrompt.length + facts.length) * Math.max(1, partition.chunks.length) + diff.length) /
518
824
  BUDGET_CHARS_PER_TOKEN,
519
825
  )} input tokens across ${partition.chunks.length} request(s)` +
520
826
  (partition.unreviewed.length > 0
@@ -639,7 +945,11 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
639
945
  // read as inconsistent with the narrow `blocking.length` verdict count.
640
946
  const rows = findings
641
947
  .map((f) => {
642
- const marker = classifyBlocking(f) ? 'Blocking' : 'Advisory';
948
+ const marker = (f.contradicted ?? '') !== ''
949
+ ? 'Contradicted'
950
+ : classifyBlocking(f)
951
+ ? 'Blocking'
952
+ : 'Advisory';
643
953
  return `| ${findingId(f)} | ${f.severity} (${marker}) | ${f.kind} | ${f.file ?? '—'} | ${f.title} |`;
644
954
  })
645
955
  .join('\n');
@@ -653,6 +963,12 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
653
963
  // 12-hex id looks exactly like a short SHA, so the rendered comment now says
654
964
  // what it is and where its disposition lives. This is the only place a reader
655
965
  // of the comment can learn it without opening the source.
966
+ const contradicted = findings.filter((f) => (f.contradicted ?? '') !== '');
967
+ const contradictedNote = contradicted.length === 0
968
+ ? ''
969
+ : '\n\n**Contradicted by the tree** — these assert a removal the range does not contain, '
970
+ + 'so they are reported and NOT counted as blocking:\n'
971
+ + contradicted.map((f) => `- \`${findingId(f)}\` ${f.title} — ${f.contradicted ?? ''}`).join('\n');
656
972
  const idNote =
657
973
  '\n\n`id` above is a FINDING id — the first 12 hex of sha256(kind|title|file), '
658
974
  + '**not a commit SHA**. Searching the git log for it finds nothing, by design. '
@@ -662,9 +978,20 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
662
978
  // demands committed dispositions. The comment is transport, never the
663
979
  // record — the record is `agents/evidence/release-findings/<version>.json`.
664
980
  const machine = `\n\n<!-- release-findings-json: ${JSON.stringify(
665
- findings.map((f) => ({ finding_id: findingId(f), severity: f.severity, kind: f.kind, title: f.title, file: f.file ?? null })),
981
+ findings.map((f) => ({
982
+ finding_id: findingId(f),
983
+ severity: f.severity,
984
+ kind: f.kind,
985
+ title: f.title,
986
+ file: f.file ?? null,
987
+ // Carried into the trigger block, not only the rendered table: the
988
+ // PR-comment path of `check_finding_dispositions` reads THIS, and
989
+ // would otherwise demand a disposition for a claim the tree already
990
+ // disproved.
991
+ ...((f.contradicted ?? '') !== '' ? { contradicted: f.contradicted } : {}),
992
+ })),
666
993
  )} -->`;
667
- return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${idNote}${cov}${esc}${machine}`;
994
+ return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${contradictedNote}${idNote}${cov}${esc}${machine}`;
668
995
  }
669
996
 
670
997
  function postReview(body: string): void {
@@ -691,6 +1018,49 @@ function resolveKey(): string | null {
691
1018
  }
692
1019
  }
693
1020
 
1021
+ /** The transport a reviewer was reached on, for the run's own report. */
1022
+ interface Reviewer {
1023
+ client: ExternalAIClient;
1024
+ transport: 'cli' | 'api';
1025
+ how: string;
1026
+ }
1027
+
1028
+ /**
1029
+ * Resolve the review client CLI-FIRST — the subscription transport before the
1030
+ * metered one, which is the ordering the rest of this suite already uses for
1031
+ * coding work and which the council has used since it grew a `mode: cli`.
1032
+ *
1033
+ * The ordering is not a preference. An API key is a METERED resource with a
1034
+ * balance that runs out, and when it does the gate reports NEUTRAL — a release
1035
+ * then carries no review at all. That is not hypothetical: it is how 16.3.0
1036
+ * stalled, with six chunks returning `credit balance is too low` and the
1037
+ * release step refusing for want of a findings artefact. A vendor CLI backed by
1038
+ * a subscription has no balance to exhaust, so trying it first removes the one
1039
+ * failure mode that has actually produced an unreviewed release here.
1040
+ *
1041
+ * `AnthropicCliClient`'s constructor THROWS when `claude` is not on PATH, which
1042
+ * is the probe: a hosted GitHub runner has no vendor CLI and falls straight
1043
+ * through to the API transport, so this change costs CI nothing and buys every
1044
+ * local and self-hosted run a free review. The catch is deliberately broad —
1045
+ * anything that goes wrong reaching for the CLI resolves to the API path rather
1046
+ * than to a NEUTRAL, because a reviewed release beats a diagnosed one.
1047
+ *
1048
+ * Calls book against `CLI_CONSUMER_SELF_REVIEW` so the daily CLI counter can
1049
+ * tell a review apart from a council round; without the attribution the gate's
1050
+ * six chunks would read as unexplained council spend.
1051
+ */
1052
+ function resolveReviewer(): Reviewer | null {
1053
+ try {
1054
+ const client = new AnthropicCliClient({ consumer: CLI_CONSUMER_SELF_REVIEW });
1055
+ return { client, transport: 'cli', how: `vendor CLI (${client.binary}), subscription` };
1056
+ } catch {
1057
+ // No `claude` on PATH, or the client refused to construct. Fall through.
1058
+ }
1059
+ const key = resolveKey();
1060
+ if (!key) return null;
1061
+ return { client: new AnthropicClient({ api_key: key }), transport: 'api', how: 'ANTHROPIC_API_KEY' };
1062
+ }
1063
+
694
1064
  // ── CLI ───────────────────────────────────────────────────────────────
695
1065
  export function main(argv: string[]): 0 | 2 {
696
1066
  const dryRun = argv.includes('--dry-run');
@@ -700,6 +1070,16 @@ export function main(argv: string[]): 0 | 2 {
700
1070
 
701
1071
  const plan = buildPlan(baseRef);
702
1072
 
1073
+ // The request the live run would send, verbatim and unspent. A summary
1074
+ // would be this script's description of the request rather than the
1075
+ // request, and the whole point of the fact block is that what the reviewer
1076
+ // is handed can be read rather than inferred — including by a maintainer.
1077
+ if (argv.includes('--print-request')) {
1078
+ const first = plan.partition.chunks[0];
1079
+ process.stdout.write(plan.facts + (first?.text ?? '(no chunk — nothing to review in this range)\n'));
1080
+ return 0;
1081
+ }
1082
+
703
1083
  if (dryRun) {
704
1084
  process.stdout.write(
705
1085
  `self-review-gate (dry-run — no spend, advisory):\n` +
@@ -756,22 +1136,28 @@ export function main(argv: string[]): 0 | 2 {
756
1136
  return 0;
757
1137
  }
758
1138
 
759
- const key = resolveKey();
760
- if (!key) {
1139
+ const reviewer = resolveReviewer();
1140
+ if (!reviewer) {
761
1141
  // Explicit NEUTRAL state — never a bare green that reads as "reviewed".
1142
+ // BOTH transports are named, because naming only the API one is what
1143
+ // made the CLI path invisible: an operator reading this warning set the
1144
+ // secret and never learned a logged-in `claude` would also have served.
762
1145
  process.stdout.write(
763
- '::warning::self-review-gate NEUTRAL — no ANTHROPIC_API_KEY configured, NOTHING was reviewed. ' +
764
- 'Set the repo secret to enable the live dogfooded review.\n',
1146
+ '::warning::self-review-gate NEUTRAL — no reviewer reachable (no `claude` CLI on PATH, ' +
1147
+ 'no ANTHROPIC_API_KEY), NOTHING was reviewed. Log in to the vendor CLI or set the ' +
1148
+ 'repo secret to enable the live dogfooded review.\n',
765
1149
  );
766
1150
  const summary = process.env.GITHUB_STEP_SUMMARY;
767
1151
  if (summary) {
768
1152
  appendFileSync(
769
1153
  summary,
770
- '### Self-review gate: NEUTRAL\n\nNo `ANTHROPIC_API_KEY` secret — nothing was reviewed. This is not a pass.\n',
1154
+ '### Self-review gate: NEUTRAL\n\nNo reviewer reachable — neither the `claude` CLI ' +
1155
+ '(subscription) nor `ANTHROPIC_API_KEY` (metered). Nothing was reviewed. This is not a pass.\n',
771
1156
  );
772
1157
  }
773
1158
  return 0;
774
1159
  }
1160
+ process.stdout.write(`::notice::self-review-gate — transport: ${reviewer.transport} (${reviewer.how}).\n`);
775
1161
 
776
1162
  // Belt-and-suspenders: the WHOLE live path is wrapped so that ANYTHING going
777
1163
  // wrong — no API credit / balance (HTTP 402), rate-limit (429), network error,
@@ -780,7 +1166,7 @@ export function main(argv: string[]): 0 | 2 {
780
1166
  // gate is advisory by design. (Only an --enforce run with real blocking
781
1167
  // findings returns 2 — an error is not a finding.)
782
1168
  try {
783
- const client = new AnthropicClient({ api_key: key });
1169
+ const client = reviewer.client;
784
1170
  const systemPrompt = buildSystemPrompt(plan.release, baseRef);
785
1171
  const partition = plan.partition;
786
1172
 
@@ -812,7 +1198,10 @@ export function main(argv: string[]): 0 | 2 {
812
1198
  let reviewedChunks = 0;
813
1199
  let filesRead = 0;
814
1200
  for (const [i, chunk] of partition.chunks.entries()) {
815
- const resp = client.ask(systemPrompt, chunk.text, 4096);
1201
+ // Facts FIRST, then the partition. The reviewer reads what the
1202
+ // range did before it reads the fragment it was given, so "absent
1203
+ // from my chunk" can no longer be mistaken for "deleted".
1204
+ const resp = client.ask(systemPrompt, plan.facts + chunk.text, 4096);
816
1205
  if (resp.error) {
817
1206
  process.stdout.write(
818
1207
  `::warning::self-review-gate — chunk ${String(i + 1)}/` +
@@ -854,6 +1243,16 @@ export function main(argv: string[]): 0 | 2 {
854
1243
  filesTotal: plan.files.length,
855
1244
  unreviewed,
856
1245
  };
1246
+ // Ground-truth pass, ONCE, before anything reads the finding set. A
1247
+ // deletion claim the range and the tree both disprove is annotated
1248
+ // here and stops being merge-blocking; it is still written to the
1249
+ // artifact and still rendered, carrying its disproof.
1250
+ const reviewed = annotateContradicted(
1251
+ dedupeFindings(findings),
1252
+ rangeDeletions(baseRef),
1253
+ (p) => existsSync(path.join(REPO_ROOT, p)),
1254
+ rangeModifications(baseRef),
1255
+ );
857
1256
  const outIdx = argv.indexOf('--findings-out');
858
1257
  if (outIdx >= 0 && argv[outIdx + 1]) {
859
1258
  // Durable ingestion input for the disposition ledger (release-truth
@@ -869,7 +1268,12 @@ export function main(argv: string[]): 0 | 2 {
869
1268
  schema_version: 1,
870
1269
  ...independenceFields(['anthropic']),
871
1270
  coverage,
872
- findings: dedupeFindings(findings).map((f) => ({
1271
+ // Readable, not recomputable: the falsifier for the
1272
+ // supplied-facts change is a count over the NEXT cut,
1273
+ // and a number a reader has to re-derive from 50
1274
+ // findings is a number nobody checks.
1275
+ fact_claims: factClaimCounts(reviewed),
1276
+ findings: reviewed.map((f) => ({
873
1277
  finding_id: findingId(f),
874
1278
  ...f,
875
1279
  })),
@@ -879,10 +1283,10 @@ export function main(argv: string[]): 0 | 2 {
879
1283
  ) + '\n',
880
1284
  );
881
1285
  }
882
- const body = renderReview(dedupeFindings(findings), enforce, plan.escalation, coverage);
1286
+ const body = renderReview(reviewed, enforce, plan.escalation, coverage);
883
1287
  postReview(body);
884
1288
 
885
- const code = gateVerdict(dedupeFindings(findings), { enforce });
1289
+ const code = gateVerdict(reviewed, { enforce });
886
1290
  if (code === 2) {
887
1291
  process.stdout.write('::error::self-review-gate — merge-blocking findings under enforce mode.\n');
888
1292
  }