@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
@@ -29,6 +29,37 @@
29
29
  * `not_applicable` with a reason and carries `findings: null` — never 0, which
30
30
  * would read as "ran and found nothing".
31
31
  *
32
+ * TWO CAUSES OF `not_applicable`, NOT ONE. The clause above was written for a
33
+ * host with no browser and then violated by the other empty input:
34
+ * `evaluate()` used to stamp all five dimensions `exercised` with `findings: 0`
35
+ * and return `structure_gate: 'passed'` for a reference that collected ZERO
36
+ * nodes — which is what every design handover in this tree produces, since none
37
+ * carries a `data-probe-id`. A comparison that never happened reported as a
38
+ * clean one. Since `road-to-a-probe-that-cannot-report-a-false-green` an empty
39
+ * reference takes the not-applicable path too, and its reason names the
40
+ * HANDOVER rather than the host, so the two causes stay tellable apart in the
41
+ * one field a reader looks at.
42
+ *
43
+ * THE ARTEFACT CARRIES ITS DENOMINATOR. `reference_nodes` and `target_nodes`
44
+ * are how many handled nodes each side actually held — `null` where nothing was
45
+ * observed at all, on the same never-0 contract as `findings`. Without them a
46
+ * zero-node run and a fully-matched run were indistinguishable in the JSON, so
47
+ * the cause above was readable only from prose.
48
+ *
49
+ * STRUCTURE IS SYMMETRIC. A handle present only in the target is a finding
50
+ * (`expected: 'absent'`, `observed: 'present'`), mirroring the reference-side
51
+ * one. `design-fidelity`'s Iron Law is "never omit or add an element" and the
52
+ * add half was unobservable while the loop read `reference.nodes` alone; a
53
+ * rename consequently reported one finding where it now reports two. The
54
+ * finding keys on the handle, never on the DOM node, so the wrapper and portal
55
+ * nodes a legitimate framework port inserts produce nothing.
56
+ *
57
+ * WHAT THIS DOES NOT FIX, said plainly because the fix is easy to over-read:
58
+ * no design handover in this tree carries a `data-probe-id` at all, so every
59
+ * real run still lands in the not-applicable branch. What changed is that the
60
+ * run now SAYS so. The correspondence problem — getting handles into a handover
61
+ * — is untouched, and nothing here promotes the probe out of shadow.
62
+ *
32
63
  * THE NOISE CONTROL. Computed-style comparison over every property fires on
33
64
  * font fallbacks and token indirection nobody cares about, so the compared set
34
65
  * is curated (`COMPARED_PROPERTIES`) to the properties a design token actually
@@ -36,6 +67,15 @@
36
67
  * `conformance.declared.json` and suppressed with its reason retained, so an
37
68
  * approved change is not re-litigated on every run.
38
69
  *
70
+ * THE ARTEFACT KNOWS WHAT IT READ. `generated_at` alone made a run before the
71
+ * last UI edit indistinguishable from one after it, so every artefact carries an
72
+ * `inputs` block: a content digest of the target surface, of the reference, and
73
+ * of the declarations file, plus the probe build that wrote them. The design-pass
74
+ * reader recomputes those digests and reports `stale` when they moved — a file
75
+ * comparison, no browser. The vocabulary is shared rather than duplicated
76
+ * (`_lib/probe_inputs.ts`), because a producer and a consumer with separate
77
+ * copies of a hash rule agree only until one of them is edited.
78
+ *
39
79
  * Exit codes: 0 written (with or without findings) · 1 usage error.
40
80
  */
41
81
  import fs from 'node:fs';
@@ -43,6 +83,8 @@ import { createRequire } from 'node:module';
43
83
  import path from 'node:path';
44
84
  import { fileURLToPath, pathToFileURL } from 'node:url';
45
85
 
86
+ import { collectInputs, declarationsPathFor, type ProbeInputs } from './_lib/probe_inputs.js';
87
+
46
88
  // Type-only, and that is load-bearing rather than stylistic: these are erased
47
89
  // at compile time, so they add no second runtime path to a module that refuses
48
90
  // to be loaded twice (see `loadPlaywright`).
@@ -149,10 +191,24 @@ export interface SuppressedDeviation {
149
191
  export interface ProbeArtefact {
150
192
  schema: 'ui-conformance/v1';
151
193
  generated_at: string;
194
+ /**
195
+ * What this run read, by content. Beside `generated_at` rather than instead
196
+ * of it: the timestamp says when, the digests say against what — and only
197
+ * the second question can be re-asked later.
198
+ */
199
+ inputs: ProbeInputs;
152
200
  host_class: 'A' | 'B' | 'C' | 'unknown';
153
201
  target: string;
154
202
  reference: string | null;
203
+ /**
204
+ * The denominator each side actually had. `null` where nothing was observed
205
+ * at all — the same contract `findings` carries, and for the same reason: a
206
+ * 0 would read as "looked and saw none", which the no-browser path did not.
207
+ */
208
+ reference_nodes: number | null;
209
+ target_nodes: number | null;
155
210
  structure_gate: 'passed' | 'stopped';
211
+ /** Reference-side only: the nodes whose STYLE comparison was skipped. */
156
212
  unmatched_nodes: string[];
157
213
  dimensions: DimensionRow[];
158
214
  findings: Finding[];
@@ -201,11 +257,61 @@ const STATE_BASELINE: Record<(typeof INTERACTION_STATES)[number], 'base' | 'hove
201
257
  keyboard: 'base',
202
258
  };
203
259
 
260
+ /**
261
+ * The second cause of `not_applicable`, and it is deliberately worded to be
262
+ * unmistakable for the first.
263
+ *
264
+ * A handover carrying no `data-probe-id` and a host carrying no browser both
265
+ * end in five not-applicable rows, so the reason string is the one field a
266
+ * reader can tell them apart by. An operator who reads "no browser" goes
267
+ * looking for a missing binary instead of fixing the handover — so this text
268
+ * names the handover and never the host, and `reference_nodes: 0` makes the
269
+ * same cause readable from the data without the prose being read at all.
270
+ */
271
+ const EMPTY_REFERENCE_REASON =
272
+ 'the reference observation carries no data-probe-id handles, so there was nothing to ' +
273
+ 'compare against. The capture ran and collected zero nodes: this is a property of the ' +
274
+ 'handover, not of this machine.';
275
+
204
276
  export function evaluate(
205
277
  reference: Observation,
206
278
  target: Observation,
207
279
  declarations: readonly Declaration[],
208
280
  ): ProbeArtefact {
281
+ // AN EMPTY COMPARISON IS NOT A CLEAN ONE.
282
+ // Keyed on the REFERENCE only, and that asymmetry is the whole point: a
283
+ // target that collected nothing is a real comparison in which every node is
284
+ // missing, and four real structure findings must not be collapsed into a
285
+ // shrug. A reference that collected nothing has no question to ask, so no
286
+ // dimension was entered and none may report a count.
287
+ if (reference.nodes.length === 0) {
288
+ return {
289
+ schema: 'ui-conformance/v1',
290
+ generated_at: new Date().toISOString(),
291
+ host_class: resolveHostClass(),
292
+ target: target.source,
293
+ reference: reference.source,
294
+ reference_nodes: 0,
295
+ target_nodes: target.nodes.length,
296
+ // The empty-reference path READ both sources — it found no handles
297
+ // in the reference, which is a property of the handover, not a
298
+ // failure to look. So it records the same digests the normal path
299
+ // does; omitting them would make a stopped run indistinguishable
300
+ // from one whose inputs were never seen.
301
+ inputs: collectInputs(target.source, reference.source),
302
+ structure_gate: 'stopped',
303
+ unmatched_nodes: [],
304
+ dimensions: DIMENSIONS.map((d) => ({
305
+ dimension: d,
306
+ status: 'not_applicable' as const,
307
+ findings: null,
308
+ reason: `${d} was never entered: ${EMPTY_REFERENCE_REASON}`,
309
+ })),
310
+ findings: [],
311
+ declared_suppressed: [],
312
+ };
313
+ }
314
+
209
315
  const targetById = new Map(target.nodes.map((n) => [n.probe_id, n]));
210
316
  const findings: Finding[] = [];
211
317
  const suppressed: SuppressedDeviation[] = [];
@@ -349,6 +455,37 @@ export function evaluate(
349
455
  }
350
456
  }
351
457
 
458
+ // --- structure, from the TARGET side. `design-fidelity`'s Iron Law is
459
+ // "never omit or add an element" and only the omit half was observable: the
460
+ // loop above reads `reference.nodes`, so a handle present only in the target
461
+ // reached nothing.
462
+ //
463
+ // KEYED ON THE HANDLE, NEVER ON THE DOM NODE, and that is what keeps this
464
+ // from reddening correct work. A faithful port into a component framework
465
+ // inserts wrapper and portal nodes the handover never had; none of them
466
+ // carries a `data-probe-id`, so none of them is observed here. Excluding the
467
+ // false-positive class by construction, rather than by a heuristic, is the
468
+ // difference between a dimension that gets acted on and one that gets muted.
469
+ const referenceIds = new Set(reference.nodes.map((n) => n.probe_id));
470
+ const addedIds = target.nodes
471
+ .map((n) => n.probe_id)
472
+ .filter((id) => !referenceIds.has(id))
473
+ .sort();
474
+ for (const id of addedIds) {
475
+ findings.push({
476
+ dimension: 'structure',
477
+ probe_id: id,
478
+ state: null,
479
+ property: null,
480
+ expected: 'absent',
481
+ observed: 'present',
482
+ detail:
483
+ `a node carries data-probe-id="${id}" in the target and no node carries it in ` +
484
+ 'the reference. There is nothing to compare its style against, so it is reported ' +
485
+ 'and then left alone — the mirror of the missing-node case above.',
486
+ });
487
+ }
488
+
352
489
  const dimensions: DimensionRow[] = DIMENSIONS.map((d) => ({
353
490
  dimension: d,
354
491
  status: 'exercised',
@@ -358,10 +495,17 @@ export function evaluate(
358
495
  return {
359
496
  schema: 'ui-conformance/v1',
360
497
  generated_at: new Date().toISOString(),
498
+ inputs: collectInputs(target.source, reference.source),
361
499
  host_class: resolveHostClass(),
362
500
  target: target.source,
363
501
  reference: reference.source,
364
- structure_gate: unmatched.length ? 'stopped' : 'passed',
502
+ reference_nodes: reference.nodes.length,
503
+ target_nodes: target.nodes.length,
504
+ // Both directions stop the gate, because both describe a node that was
505
+ // never style-compared. `unmatched_nodes` keeps its narrower meaning —
506
+ // the reference-side nodes whose comparison was SKIPPED — so a reader of
507
+ // that list still learns exactly what it always said.
508
+ structure_gate: unmatched.length || addedIds.length ? 'stopped' : 'passed',
365
509
  unmatched_nodes: unmatched,
366
510
  dimensions,
367
511
  findings,
@@ -373,13 +517,21 @@ export function evaluate(
373
517
  * The artefact a host that cannot run the probe must still produce. Emitting
374
518
  * nothing is indistinguishable from a clean run to everything downstream.
375
519
  */
376
- export function unavailableArtefact(target: string, reason: string): ProbeArtefact {
520
+ export function unavailableArtefact(target: string, reason: string, reference: string | null = null): ProbeArtefact {
377
521
  return {
378
522
  schema: 'ui-conformance/v1',
379
523
  generated_at: new Date().toISOString(),
524
+ // A degraded run still records what it WOULD have compared. An artefact
525
+ // with no inputs block reads as a version skew to the reader, which is a
526
+ // different and less useful statement than "this host has no browser".
527
+ inputs: collectInputs(target, reference),
380
528
  host_class: 'unknown',
381
529
  target,
382
530
  reference: null,
531
+ // Null, never 0 — nothing was observed on either side, and a 0 would
532
+ // read as "looked and saw none".
533
+ reference_nodes: null,
534
+ target_nodes: null,
383
535
  structure_gate: 'stopped',
384
536
  unmatched_nodes: [],
385
537
  dimensions: DIMENSIONS.map((d) => ({
@@ -596,14 +748,14 @@ async function main(): Promise<number> {
596
748
 
597
749
  let artefact: ProbeArtefact;
598
750
  if (!chromiumAvailable()) {
599
- artefact = unavailableArtefact(target, 'no Chromium binary resolved from @playwright/test');
751
+ artefact = unavailableArtefact(target, 'no Chromium binary resolved from @playwright/test', referencePath ?? null);
600
752
  } else if (!referencePath) {
601
753
  artefact = unavailableArtefact(target, 'no --reference given; a comparison needs an intended implementation');
602
754
  for (const row of artefact.dimensions) {
603
755
  row.reason = `${row.dimension} needs a --reference to compare against; none was given`;
604
756
  }
605
757
  } else {
606
- artefact = evaluate(await captureVariant(referencePath), await captureVariant(target), loadDeclarations(path.join(path.dirname(target), 'conformance.declared.json')));
758
+ artefact = evaluate(await captureVariant(referencePath), await captureVariant(target), loadDeclarations(declarationsPathFor(target)));
607
759
  }
608
760
 
609
761
  fs.mkdirSync(path.dirname(out), { recursive: true });
@@ -23,6 +23,7 @@ import * as path from 'node:path';
23
23
  import { fileURLToPath, pathToFileURL } from 'node:url';
24
24
 
25
25
  import { DEFAULT_PRICES, as_rows } from './ai_council/_default_prices.js';
26
+ import { sanitize_text } from './_lib/retrieval_sanitize.js';
26
27
  import { PRICES_FILE, _render_markdown, is_stale, load_prices } from './ai_council/pricing.js';
27
28
 
28
29
  const _HERE = fileURLToPath(import.meta.url);
@@ -39,6 +40,40 @@ type Json = any;
39
40
  // from the LiteLLM payload. set(DEFAULT_PRICES.keys()) → "provider model" keys.
40
41
  const ALLOW_LIST: ReadonlySet<string> = new Set(DEFAULT_PRICES.keys());
41
42
 
43
+ /**
44
+ * The only shape a remote identifier may have to be considered at all.
45
+ *
46
+ * Pure ASCII letters, digits and the punctuation real provider/model names use.
47
+ * Every allow-list entry is ASCII, so nothing outside this can be a member —
48
+ * and asserting it on the UNTOUCHED bytes is what makes the case fold below
49
+ * safe, because within ASCII folding is lossless. A Unicode character that
50
+ * folds onto an ASCII letter never reaches the comparison.
51
+ */
52
+ const _CANONICAL_ID = /^[A-Za-z0-9._-]+$/;
53
+
54
+ /** ASCII-only case fold — never `toLowerCase`, which folds Unicode too. */
55
+ function _asciiLower(s: string): string {
56
+ return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
57
+ }
58
+
59
+ /**
60
+ * Report a rejected identifier that WOULD have matched after transformation.
61
+ *
62
+ * A raw miss whose normalised form hits the allow list is a different event
63
+ * from an ordinary unlisted name, and printing both as the same silence is what
64
+ * would hide an admission attempt. Ordinary misses stay silent — the fetched
65
+ * catalogue carries hundreds of models nobody here lists.
66
+ */
67
+ function _reportCollision(rawProvider: string, rawModel: string): void {
68
+ const normalised = `${sanitize_text(rawProvider).toLowerCase()} ${sanitize_text(rawModel)}`;
69
+ if (!ALLOW_LIST.has(normalised)) return;
70
+ process.stderr.write(
71
+ 'update_prices: DROPPED a remote row whose raw identifier is not allow-listed ' +
72
+ `but normalises onto \`${normalised}\`. A fetched name that folds or sanitizes ` +
73
+ 'onto a listed one is an admission attempt, not a typo.\n',
74
+ );
75
+ }
76
+
42
77
  /**
43
78
  * Synchronous HTTPS GET so the CLI stays a straight-line script (Python uses
44
79
  * a blocking urlopen). Returns parsed JSON dict, or null on any failure
@@ -99,13 +134,60 @@ export function _toRowsFromLitellm(
99
134
  if (entry === null || typeof entry !== 'object' || Array.isArray(entry)) {
100
135
  continue;
101
136
  }
102
- const provider = String((entry.litellm_provider as Json) ?? '').toLowerCase();
137
+ // Sanitize BEFORE the allow-list compare, not after. These two strings
138
+ // come off the wire and end up in a tracked markdown file that agents
139
+ // read, so they are the model-facing copy this floor exists for. Doing
140
+ // it before the compare is also the only order that works: a vector
141
+ // inside a model name would otherwise fail the allow-list lookup and
142
+ // drop the row, turning an injection attempt into a silent data loss.
143
+ // The fetched payload itself is never written anywhere — only this
144
+ // rendering is transformed.
145
+ const rawProvider = String((entry.litellm_provider as Json) ?? '');
103
146
  // LiteLLM keys are sometimes "provider/model"; strip the prefix.
104
147
  const slash = key.indexOf('/');
105
- const model = slash !== -1 ? key.slice(slash + 1) : key;
106
- if (!ALLOW_LIST.has(`${provider} ${model}`)) {
148
+ const rawModel = slash !== -1 ? key.slice(slash + 1) : key;
149
+
150
+ // THE ADMISSION BOUNDARY: a canonical ASCII grammar first, then a
151
+ // comparison on values nothing has transformed.
152
+ //
153
+ // Two earlier orders were both wrong and the second is the instructive
154
+ // one. The first sanitized before comparing, so a poisoned name could
155
+ // normalise onto a listed one and be admitted as it. The fix compared
156
+ // "raw" values — but `.toLowerCase()` ran before the compare, and THAT
157
+ // IS ITSELF A LOSSY UNICODE TRANSFORM: the Kelvin sign and the Turkish
158
+ // dotted capital both fold onto ASCII letters, so the provider field
159
+ // still carried the collision the model field no longer did. An
160
+ // independent review named it and was right; closing one field and
161
+ // calling the class closed is the failure mode, not the typo.
162
+ //
163
+ // The reachable fold is ONE character: `U+212A KELVIN SIGN -> k`.
164
+ // Measured, not recalled — U+212B ANGSTROM folds to `å`, which is not
165
+ // ASCII, and an earlier note here claimed otherwise. No provider in the
166
+ // current list carries a `k`, so this boundary guards nothing reachable
167
+ // TODAY; it guards the day one is added, which is the only time a
168
+ // boundary of this shape can be added without a migration.
169
+ //
170
+ // So the grammar is asserted on the UNTOUCHED bytes. Pure ASCII, with
171
+ // the punctuation real identifiers use. Nothing outside it can be an
172
+ // allow-list member, because every entry is ASCII — and within ASCII,
173
+ // case folding is lossless, which is what makes the fold below safe to
174
+ // apply after the gate rather than before it.
175
+ if (!_CANONICAL_ID.test(rawProvider) || !_CANONICAL_ID.test(rawModel)) {
176
+ _reportCollision(rawProvider, rawModel);
107
177
  continue;
108
178
  }
179
+ const provider = _asciiLower(rawProvider);
180
+ if (!ALLOW_LIST.has(`${provider} ${rawModel}`)) {
181
+ _reportCollision(rawProvider, rawModel);
182
+ continue;
183
+ }
184
+
185
+ // Only now, and only for the rendering: the row reaches a tracked
186
+ // markdown file that agents read, so the model-facing copy carries the
187
+ // floor. The fetched payload itself is never written anywhere. Both
188
+ // values are already canonical ASCII here, so this is a no-op in
189
+ // practice and a belt on the rendering in principle.
190
+ const model = sanitize_text(rawModel);
109
191
  const inCost = entry.input_cost_per_token;
110
192
  const outCost = entry.output_cost_per_token;
111
193
  if (!_isNumber(inCost) || !_isNumber(outCost)) {
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * write_bundle_digest.mjs — the sidecar `dist/hooks/dispatch.sha256`.
4
+ *
5
+ * Step 3.1 of `road-to-a-kernel-that-guards-its-plumbing`. The dispatcher
6
+ * verifies at runtime that the bundle it is executing is the bundle the build
7
+ * produced, and it needs something to compare against. This writes that
8
+ * something, in the same `npm run build:hooks` invocation that writes the
9
+ * bundle, so the two cannot be produced separately and cannot disagree.
10
+ *
11
+ * Plain `.mjs` rather than a TS script under `scripts-run`: it is chained into
12
+ * `build:hooks`, which runs before anything is compiled and on machines where
13
+ * a `tsx` startup is pure cost. `prepack-check.mjs` sets the precedent.
14
+ *
15
+ * Format is one line, `<64 hex> <basename>`, i.e. what `shasum -a 256`
16
+ * emits — so an operator can verify the claim with a tool they already have
17
+ * instead of trusting this script:
18
+ *
19
+ * shasum -a 256 -c dist/hooks/dispatch.js.sha256
20
+ *
21
+ * This is NOT a signature and does not pretend to be. Anyone who can rewrite
22
+ * the bundle can rewrite the sidecar beside it. What it defends against is the
23
+ * hand edit — a bundle changed in place between builds, which until now
24
+ * survived until the next build, reached every dispatch meanwhile, and was
25
+ * invisible in a source review. `block_plumbing_writes` refuses that edit at
26
+ * tool-call time; this catches one that arrived by some other route.
27
+ */
28
+ import { createHash } from 'node:crypto';
29
+ import { readFileSync, writeFileSync } from 'node:fs';
30
+ import { basename, resolve } from 'node:path';
31
+ import { argv, exit, stderr, stdout } from 'node:process';
32
+
33
+ const bundle = resolve(argv[2] ?? 'dist/hooks/dispatch.js');
34
+ // `dispatch.sha256`, not `dispatch.js.sha256` — the name the roadmap step
35
+ // wrote. Either works with `shasum -c`, which reads the subject filename from
36
+ // INSIDE the file rather than from the sidecar's own name, so the choice is
37
+ // free and the step's spelling wins.
38
+ const sidecar = bundle.replace(/\.js$/u, '') + '.sha256';
39
+
40
+ let bytes;
41
+ try {
42
+ bytes = readFileSync(bundle);
43
+ } catch (err) {
44
+ stderr.write(`write_bundle_digest: cannot read ${bundle}: ${err.message}\n`);
45
+ // Exit non-zero: a build that produced no bundle must not report success,
46
+ // and a sidecar written from a missing file would be a digest of nothing.
47
+ exit(1);
48
+ }
49
+
50
+ const digest = createHash('sha256').update(bytes).digest('hex');
51
+ writeFileSync(sidecar, `${digest} ${basename(bundle)}\n`, 'utf-8');
52
+
53
+ // Read back what was just written and confirm it describes the bundle beside
54
+ // it. An independent review called a post-build verification the
55
+ // highest-leverage addition here, and the reason is the runtime posture: a
56
+ // MISSING or unreadable sidecar is `unverifiable` at dispatch time and ALLOWS,
57
+ // deliberately, so that a consumer whose package predates the sidecar is not
58
+ // wedged. That choice means a half-written or unreadable sidecar degrades the
59
+ // check silently instead of failing anything.
60
+ //
61
+ // `prepack-check.mjs` catches it at packaging time, which covers the published
62
+ // tarball and nothing else. This covers every path that produces a bundle at
63
+ // all — local build, CI build, packaging — because it is part of producing one.
64
+ // It costs one re-read of 78 bytes.
65
+ const readBack = readFileSync(sidecar, 'utf-8');
66
+ const claimed = /^([0-9a-f]{64})\s/u.exec(readBack.trim())?.[1] ?? null;
67
+ if (claimed !== digest) {
68
+ stderr.write(
69
+ `write_bundle_digest: wrote ${sidecar} but read back ${claimed ?? '(unparseable)'} ` +
70
+ `instead of ${digest} — the sidecar does not describe the bundle it was written for.\n`,
71
+ );
72
+ exit(1);
73
+ }
74
+ stdout.write(`write_bundle_digest: ${digest} ${basename(bundle)} (${String(bytes.length)} bytes, verified)\n`);
@@ -174,6 +174,37 @@ export const settingsSchema = z.object({
174
174
  'How a mission\'s work is shaped into pull requests. single (default) = one branch, one pull request. stacked = a dependent series. stacked is never chosen by the agent and never asked about at roadmap creation — the owner plans it or it does not happen.',
175
175
  ),
176
176
  }),
177
+ /**
178
+ * What this package may write into the HOST's own environment.
179
+ *
180
+ * THE GUARANTEE IS THE SHAPE, not a check. Only two keys exist here, and
181
+ * both are request-SIZE caps. Neither traffic variable is expressible —
182
+ * `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` and `DISABLE_AUTOUPDATER` both
183
+ * reach the host's update-disabled resolver (the blanket one as its third
184
+ * rung, measured on Claude Code 2.1.284), so a profile able to write either
185
+ * could disable a consumer's security updates. Owner-decided 2026-09-30:
186
+ * they are not writable through this package at all, and the strongest way
187
+ * to hold that is a schema that cannot say them rather than an exclusion
188
+ * list somebody has to maintain.
189
+ *
190
+ * `null` is the default on both, and it is the off switch: nothing is
191
+ * written. There is no separate enable flag, because a flag plus a value is
192
+ * two ways to say off and one of them would drift.
193
+ */
194
+ host_environment: z.object({
195
+ request_size_caps: z.object({
196
+ bash_max_output_length: z.number().int().positive().max(150_000).nullable().default(null).describe(
197
+ 'Character cap on Bash tool output the host places inline into a model request. null (default) = this package writes nothing and the host default of 30000 applies. A number is written into the host env block. Values above 150000 are capped by the host itself, so the schema refuses them here rather than writing a value the host silently changes. Bounds request SIZE, never request COUNT: lowering it suppresses no telemetry, no update check and no download.',
198
+ ),
199
+ max_mcp_output_tokens: z.number().int().positive().nullable().default(null).describe(
200
+ 'Token cap on a single MCP tool result the host places into a model request. null (default) = nothing written, host default 25000 applies. Same size-not-count caveat: it does not govern MCP connections, timeouts, or whether a tool runs.',
201
+ ),
202
+ }).default({}).describe(
203
+ 'Request-size caps this package may write into the host env block. Both null by default. No traffic or auto-updater variable is expressible here, by construction — see the block comment.',
204
+ ),
205
+ }).default({}).describe(
206
+ 'What this package may write into the host environment. ABSENT resolves to the all-null shape, which is the same thing as off — a settings file predating this section must not fail to parse, and "no section" and "both caps null" both mean write nothing.',
207
+ ),
177
208
  augment: z.object({
178
209
  rules_use_symlinks: z.boolean().default(false).describe(
179
210
  'When true, .augment/rules/*.md are symlinks into dist/agent-src/rules/ — edits flow back to source on save. When false (default), they are copies — safer on Windows and shared volumes, but rule edits in .augment/ are lost on the next `task sync`.',
@@ -292,6 +323,11 @@ export const settingsSchema = z.object({
292
323
  fidelity_mode: 'strict',
293
324
  approximation: { enabled: false, tolerance: { color: null, length: null } },
294
325
  }),
326
+ code_graph: z.object({
327
+ consumer_index_paths: z.array(z.string()).default([]).describe(
328
+ "Extra project-relative paths where THIS project keeps a code-graph index another tool wrote. The built-in list is deliberately vendor-neutral (graph.json, code-graph.json, .code-graph/graph.json) and names no tool, so a tool that writes its index elsewhere is named here rather than waited for. A graph found this way LOADS and answers query and explain with every edge tagged read-from-elsewhere; the gate verbs (impact, untested, dead) refuse a graph made only of such edges and say so, because an empty answer from a gate must not read as 'nothing found' when it means 'nothing trusted'. Absolute paths and paths escaping the project root are ignored.",
329
+ ),
330
+ }).default({}),
295
331
  consistency: z.object({
296
332
  cross_source: crossSourceMode.default('on').describe(
297
333
  'Consumed by the cross-source-consistency rule. When the agent works from multiple sources (ticket text, an attached image/mockup, the spec, the codebase) it checks them against each other and asks before proceeding on a discrepancy — instead of silently guessing. on (default) = surface every real cross-source contradiction / silent-scope-expansion as one question; auto = surface only high-confidence contradictions, state low-confidence as an assumption; off = no cross-source checking.',
@@ -559,6 +595,11 @@ export const settingsSchema = z.object({
559
595
  'When a hook exceeds hooks.concern_budget.max_per_event, fail the run (true) instead of warning and continuing (false, default). Turn on in CI when you want hook quality to gate merges.',
560
596
  ),
561
597
  }),
598
+ verify_before_complete: z.object({
599
+ touched_file_quality: z.enum(['off', 'shadow', 'warn']).default('off').describe(
600
+ "Stop-slot pass that runs the project's OWN quality commands — the ones resolve_toolchain() already reports — over the files this turn edited. Default off. `shadow` records a per-run result and emits nothing; `warn` adds ONE advisory line of at most 200 bytes. Never blocks and never counts as verification: a command recorded here never reaches _lib/verification_command.ts, so a clean eslint run cannot let a turn claim it verified anything. A writing command runs only in its check form (pint --test) or not at all, and a command with no per-file form is skipped rather than run project-wide at every stop.",
601
+ ),
602
+ }).default({}),
562
603
  injection_scan: z.object({
563
604
  enabled: z.boolean().default(false).describe(
564
605
  'PostToolUse prompt-injection scanner (road-to-security-pillar.md P3.2). Default off. When on, scans tool output (file reads, web fetches, MCP responses) for injection signatures and WARNS in context (never blocks). Runtime backstop on top of the always-on untrusted-input-defense rule; detection is probabilistic.',