@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
@@ -0,0 +1,271 @@
1
+ /**
2
+ * `probe_inputs` — what the UI-conformance probe read, in a form its reader can
3
+ * re-check without starting a browser.
4
+ *
5
+ * `ui-conformance.json` recorded `generated_at` and nothing about its inputs, so
6
+ * a probe run before the last UI edit was indistinguishable from one run after
7
+ * it. This module is the shared vocabulary that closes that: the probe writes an
8
+ * `inputs` block with a content digest per input, and the design-pass reader
9
+ * recomputes those digests and compares. ONE definition, imported by both sides,
10
+ * because a producer and a consumer with separate copies of a hash rule agree
11
+ * only until one of them is edited.
12
+ *
13
+ * DIGESTS, NOT MTIMES, AND THAT IS THE WHOLE DECISION (D1). The reader already
14
+ * applies an mtime rule to the ui-audit artefact, and reusing it here was the
15
+ * obvious move. It answers the wrong question: an mtime moves on a checkout, a
16
+ * `cp -p`-less copy, or a bare `touch`, so it would report movement on turns
17
+ * where no byte changed — and a reader who learns to ignore a false stale has
18
+ * learned to ignore a real one. A content digest reports exactly what is being
19
+ * asked: are the bytes the probe saw still the bytes on disk?
20
+ *
21
+ * THE TARGET DIGEST IS A TREE DIGEST, and this is the one place the roadmap's
22
+ * own text needed correcting. A UI surface is an entry document plus the styles
23
+ * and scripts beside it; a digest of `index.html` alone would be blind to a CSS
24
+ * edit, which is the single most common UI change there is — and the roadmap's
25
+ * own verify line for step 1.1 names exactly that case. So the record covers the
26
+ * directory the entry document sits in. It costs a small recursive read the
27
+ * probe does not otherwise perform (the browser, not Node, opens `styles.css`),
28
+ * which is a real cost the roadmap's Risk 3 mitigation did not anticipate. It is
29
+ * bounded: a few small text files against a run that launches Chromium and
30
+ * reloads the page dozens of times per node.
31
+ *
32
+ * NO BROWSER IS REACHABLE FROM HERE. This module imports `node:crypto`,
33
+ * `node:fs` and `node:path` and nothing else, which is what lets the hook decide
34
+ * staleness on a host with no browser binaries installed.
35
+ */
36
+ import { createHash } from 'node:crypto';
37
+ import fs from 'node:fs';
38
+ import path from 'node:path';
39
+
40
+ /** Schema of the `inputs` block. A reader that does not know it says unknown. */
41
+ export const INPUTS_SCHEMA = 'ui-conformance-inputs/v1';
42
+
43
+ /**
44
+ * The probe build that wrote the block. Bumped when a change to the probe makes
45
+ * an older artefact's findings incomparable with a newer run's; a reader seeing
46
+ * a value it does not know reports unknown rather than guessing.
47
+ */
48
+ export const PROBE_VERSION = 'ui-conformance-probe/v1';
49
+
50
+ /** The sibling file the probe reads for declared, approved deviations. */
51
+ export const DECLARATIONS_BASENAME = 'conformance.declared.json';
52
+
53
+ /**
54
+ * Bound on a tree digest. A surface directory is a handful of files; anything
55
+ * past this is a checkout root someone pointed the probe at by mistake, and
56
+ * hashing it would cost more than the probe run. Exceeding it is recorded as
57
+ * absent with a reason — never silently truncated, which would produce a digest
58
+ * that looks authoritative and covers an arbitrary subset.
59
+ */
60
+ export const MAX_TREE_FILES = 2000;
61
+
62
+ /** Directories never worth digesting, and ruinous to walk. */
63
+ const SKIP_DIRS = new Set(['node_modules', '.git']);
64
+
65
+ export const INPUT_KEYS = ['target', 'reference', 'declarations'] as const;
66
+ export type InputKey = (typeof INPUT_KEYS)[number];
67
+
68
+ export interface InputRecord {
69
+ /**
70
+ * What was digested — a directory for `tree`, a file for `file`. `null` only
71
+ * when no path was supplied at all, which is itself recorded rather than
72
+ * omitted.
73
+ */
74
+ path: string | null;
75
+ kind: 'tree' | 'file';
76
+ /** `absent` carries a reason and a null digest. Never an omitted key. */
77
+ state: 'read' | 'absent';
78
+ /** `sha256:<hex>` when read, `null` when absent. */
79
+ digest: string | null;
80
+ /** Files folded into a tree digest. Absent on a `file` record. */
81
+ files?: number;
82
+ reason?: string;
83
+ }
84
+
85
+ export interface ProbeInputs {
86
+ schema: string;
87
+ probe: string;
88
+ target: InputRecord;
89
+ reference: InputRecord;
90
+ declarations: InputRecord;
91
+ }
92
+
93
+ const sha256 = (data: Buffer | string): string => `sha256:${createHash('sha256').update(data).digest('hex')}`;
94
+
95
+ const why = (err: unknown): string => {
96
+ const code = (err as NodeJS.ErrnoException | undefined)?.code;
97
+ return code ? String(code) : String(err);
98
+ };
99
+
100
+ /**
101
+ * The ONE definition of where a target's declarations file lives. The probe's
102
+ * CLI computed this inline; a second copy here would be a rule that agrees with
103
+ * the probe only until one of the two is edited.
104
+ */
105
+ export function declarationsPathFor(targetEntryFile: string): string {
106
+ return path.join(path.dirname(targetEntryFile), DECLARATIONS_BASENAME);
107
+ }
108
+
109
+ /** Digest one file. Never throws — an unreadable input is a recorded fact. */
110
+ export function digestFile(file: string | null, whenNull = 'no path was supplied'): InputRecord {
111
+ if (!file) return { path: null, kind: 'file', state: 'absent', digest: null, reason: whenNull };
112
+ try {
113
+ return { path: file, kind: 'file', state: 'read', digest: sha256(fs.readFileSync(file)) };
114
+ } catch (err) {
115
+ return { path: file, kind: 'file', state: 'absent', digest: null, reason: `unreadable: ${why(err)}` };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Digest a directory as one value: every file under it, sorted by relative path,
121
+ * folded path-and-content into a single hash. Sorted because a directory listing
122
+ * order is a filesystem detail, and a digest that moved with it would report
123
+ * movement nobody made.
124
+ *
125
+ * `exclude` drops names recorded as inputs in their own right — the declarations
126
+ * file — so an edit to it moves exactly one digest rather than two.
127
+ */
128
+ export function digestTree(
129
+ dir: string | null,
130
+ exclude: readonly string[] = [],
131
+ whenNull = 'no path was supplied',
132
+ ): InputRecord {
133
+ if (!dir) return { path: null, kind: 'tree', state: 'absent', digest: null, reason: whenNull };
134
+ const skip = new Set(exclude);
135
+ const found: string[] = [];
136
+
137
+ const walk = (rel: string): string | null => {
138
+ let entries: fs.Dirent[];
139
+ try {
140
+ entries = fs.readdirSync(path.join(dir, rel), { withFileTypes: true });
141
+ } catch (err) {
142
+ return `unreadable: ${why(err)}`;
143
+ }
144
+ for (const e of [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
145
+ const child = rel ? `${rel}/${e.name}` : e.name;
146
+ if (rel === '' && skip.has(e.name)) continue;
147
+ if (e.isDirectory()) {
148
+ if (SKIP_DIRS.has(e.name)) continue;
149
+ const failed = walk(child);
150
+ if (failed) return failed;
151
+ continue;
152
+ }
153
+ if (!e.isFile()) continue;
154
+ found.push(child);
155
+ if (found.length > MAX_TREE_FILES) {
156
+ return `directory holds more than ${MAX_TREE_FILES} files — too large to digest`;
157
+ }
158
+ }
159
+ return null;
160
+ };
161
+
162
+ const failure = walk('');
163
+ if (failure) return { path: dir, kind: 'tree', state: 'absent', digest: null, reason: failure };
164
+
165
+ const h = createHash('sha256');
166
+ for (const rel of found.sort()) {
167
+ let body: Buffer;
168
+ try {
169
+ body = fs.readFileSync(path.join(dir, rel));
170
+ } catch (err) {
171
+ return { path: dir, kind: 'tree', state: 'absent', digest: null, reason: `unreadable: ${why(err)}` };
172
+ }
173
+ // The path is folded in alongside the bytes, so a rename moves the digest
174
+ // even when the content is byte-identical.
175
+ h.update(rel).update('\0').update(body).update('\0');
176
+ }
177
+ return {
178
+ path: dir,
179
+ kind: 'tree',
180
+ state: 'read',
181
+ digest: `sha256:${h.digest('hex')}`,
182
+ files: found.length,
183
+ };
184
+ }
185
+
186
+ /**
187
+ * The block the probe writes beside `generated_at`. A target or reference is
188
+ * digested as the surface directory it belongs to; the declarations file is
189
+ * digested on its own.
190
+ */
191
+ export function collectInputs(targetEntryFile: string | null, referenceEntryFile: string | null): ProbeInputs {
192
+ const dirOf = (f: string | null): string | null => (f ? path.dirname(f) : null);
193
+ return {
194
+ schema: INPUTS_SCHEMA,
195
+ probe: PROBE_VERSION,
196
+ target: digestTree(dirOf(targetEntryFile), [DECLARATIONS_BASENAME], 'no --target was supplied'),
197
+ reference: digestTree(
198
+ dirOf(referenceEntryFile),
199
+ [DECLARATIONS_BASENAME],
200
+ 'no --reference was given; nothing was compared against',
201
+ ),
202
+ declarations: targetEntryFile
203
+ ? digestFile(declarationsPathFor(targetEntryFile))
204
+ : digestFile(null, 'no --target was supplied, so no declarations file was looked for'),
205
+ };
206
+ }
207
+
208
+ export type InputComparison =
209
+ | { verdict: 'unknown'; reason: string; moved: readonly InputKey[] }
210
+ | { verdict: 'unchanged'; moved: readonly InputKey[] }
211
+ | { verdict: 'moved'; moved: readonly InputKey[] };
212
+
213
+ const isRecord = (v: unknown): v is InputRecord =>
214
+ typeof v === 'object' && v !== null && 'state' in (v as object) && 'kind' in (v as object);
215
+
216
+ /**
217
+ * Re-read each recorded input and say whether it moved.
218
+ *
219
+ * THREE verdicts, and `unknown` is not a hedge. An artefact written before the
220
+ * `inputs` block existed has nothing to compare, and calling that `moved` would
221
+ * report stale for every pre-existing run on the first turn after this change —
222
+ * Risk 1 of the roadmap. It is a version skew, not evidence of movement, and it
223
+ * is equally not evidence of freshness.
224
+ *
225
+ * `resolve` maps a recorded path to this host's filesystem; the caller owns that
226
+ * because the probe records the path it was given and only the reader knows the
227
+ * root it is reading from.
228
+ */
229
+ export function compareInputs(recorded: unknown, resolve: (p: string) => string): InputComparison {
230
+ if (typeof recorded !== 'object' || recorded === null) {
231
+ return {
232
+ verdict: 'unknown',
233
+ moved: [],
234
+ reason: 'the artefact carries no inputs block — it predates the input digests',
235
+ };
236
+ }
237
+ const r = recorded as Partial<ProbeInputs>;
238
+ if (r.schema !== INPUTS_SCHEMA) {
239
+ return {
240
+ verdict: 'unknown',
241
+ moved: [],
242
+ reason: `inputs schema ${String(r.schema)} is not ${INPUTS_SCHEMA} — this reader cannot compare it`,
243
+ };
244
+ }
245
+ if (r.probe !== PROBE_VERSION) {
246
+ return {
247
+ verdict: 'unknown',
248
+ moved: [],
249
+ reason: `written by ${String(r.probe)}, read by ${PROBE_VERSION} — findings across builds are not comparable`,
250
+ };
251
+ }
252
+
253
+ const moved: InputKey[] = [];
254
+ for (const key of INPUT_KEYS) {
255
+ const rec = r[key];
256
+ if (!isRecord(rec)) {
257
+ return {
258
+ verdict: 'unknown',
259
+ moved: [],
260
+ reason: `the inputs block carries no usable record for ${key}`,
261
+ };
262
+ }
263
+ // An input that was never supplied has nothing to move to or from.
264
+ if (rec.path === null) continue;
265
+ const here = rec.kind === 'tree'
266
+ ? digestTree(resolve(rec.path), [DECLARATIONS_BASENAME])
267
+ : digestFile(resolve(rec.path));
268
+ if (here.state !== rec.state || here.digest !== rec.digest) moved.push(key);
269
+ }
270
+ return moved.length ? { verdict: 'moved', moved } : { verdict: 'unchanged', moved: [] };
271
+ }
@@ -41,6 +41,50 @@
41
41
  * over-filtering would silently stop pinning real prompts, which is worse.
42
42
  * Measured across 1 540 user-role entries: 509 open at character zero with a
43
43
  * marker, and in 0 of them does human text survive wrapper removal.
44
+ *
45
+ * `<agent-message` added 2026-10-03, by the same under-filtering it warns
46
+ * about: a subagent hand-back reaches this slot as `<agent-message from="…">`,
47
+ * and 12 of the 153 hook-sourced user records in this project's capture open
48
+ * with it. It pinned a German session to `en`, after which the turn-end gate
49
+ * refused each German reply — a reply language derived from a frame nobody
50
+ * typed. Its literal stops before the `>` because the element carries an
51
+ * attribute; the anchor at character zero is unchanged.
52
+ *
53
+ * **The fourth shape has NO envelope tag, and it is the exception this file's
54
+ * own "structural, not linguistic" rule has to name rather than quietly break.**
55
+ * The host resumes a rate-limited session with a plain sentence —
56
+ * `Your claude.ai usage limit has reset. Continue the task you were working
57
+ * on…` — carrying no element, no bracket and no stamp. 5 of the 153
58
+ * hook-sourced user records in this project's capture open with it. Measured
59
+ * 2026-10-03: it pinned a German session to `en` with `prompt_chars: 148`,
60
+ * which is that sentence's exact length, and every German reply for the rest
61
+ * of the run was then refused by the turn-end gate as wrong-language.
62
+ *
63
+ * Matching prose is what the paragraph above warns against, so what makes this
64
+ * one admissible is stated rather than assumed: it is a FIXED host string, not
65
+ * a linguistic pattern, and the character-zero anchor does the same work a tag
66
+ * would — a human asking *why did I get "Your claude.ai usage limit has
67
+ * reset"* does not open with it. A test pins that direction. If the host ever
68
+ * reworded the sentence this guard would silently stop matching, which is the
69
+ * honest failure mode: it under-filters back to today's behaviour rather than
70
+ * misfiring on a real turn.
71
+ *
72
+ * It is deliberately NOT added to `machine_wake.ts`. There the same
73
+ * misclassification retains an authorization instead of mis-pinning a reply,
74
+ * and a prose match is too weak a warrant for that consequence — see the
75
+ * asymmetry note below, which this is the first real test of.
76
+ *
77
+ * **A SECOND VOCABULARY ANSWERS THIS SAME QUESTION AND THE TWO HAVE DRIFTED.**
78
+ * `_lib/machine_wake.ts`'s `humanTypedThisTurn` guards per-turn STATE on this
79
+ * slot (the git-authorization ledger, the suggestion latch) while this one
80
+ * guards the language pin. As of this change both know `<task-notification>`,
81
+ * `<system-reminder>` and `<agent-message`; only this one knows
82
+ * `[SYSTEM NOTIFICATION` and `<local-command-caveat>`. That asymmetry is NOT
83
+ * obviously a defect and was deliberately left: mistaking a real turn for a
84
+ * machine one costs a mis-pinned reply here and a RETAINED AUTHORIZATION
85
+ * there, so the two sides do not want the same error budget — and
86
+ * `<local-command-caveat>` in particular precedes a real typed prompt. Unify
87
+ * them only with that asymmetry answered, never as tidying.
44
88
  */
45
89
  export function isSyntheticPrompt(prompt: string): boolean {
46
90
  const head = prompt.trimStart().slice(0, 400);
@@ -49,6 +93,8 @@ export function isSyntheticPrompt(prompt: string): boolean {
49
93
  head.startsWith("<task-notification>") ||
50
94
  head.startsWith("<system-reminder>") ||
51
95
  head.startsWith("<local-command-caveat>") ||
96
+ head.startsWith("<agent-message") ||
97
+ head.startsWith("Your claude.ai usage limit has reset") ||
52
98
  /^\[SYSTEM NOTIFICATION\b/.test(head)
53
99
  );
54
100
  }
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Read-surface scanner — derive the sanitize floor's coverage list from the
3
+ * imports, not from intent
4
+ * (road-to-a-sanitize-list-that-is-generated, Phase 1).
5
+ *
6
+ * WHY A SCANNER AND NOT A LIST
7
+ * ----------------------------
8
+ * `retrieval_sanitize.ts` used to carry a hand-maintained prose list of the
9
+ * read surfaces it covers, with its own header stating the rule for anything
10
+ * not on it: uncovered. That list went wrong in both directions — a path that
11
+ * reaches the outside world and IS sanitized was absent from it, and two paths
12
+ * carrying fetched bytes toward a model-facing surface were neither listed nor
13
+ * covered. The header already recorded the same failure happening once before,
14
+ * for the same reason: a list maintained by hand records what its author meant,
15
+ * and the tree records what the code does.
16
+ *
17
+ * This module derives the list. A row cannot disagree with the tree, because
18
+ * the tree is the only thing it reads.
19
+ *
20
+ * WHAT A ROW MEANS, AND WHAT IT DOES NOT
21
+ * --------------------------------------
22
+ * A row says a module matched an INBOUND shape — a recognised way outside bytes
23
+ * enter this process — and whether that module imports the sanitize floor. It
24
+ * does not say the floor is applied to the right string, at the right point, or
25
+ * at all: an import is evidence of intent, and only a read of the module
26
+ * establishes the rest. `covered` here means "the floor is in scope in this
27
+ * module", never "this module is safe".
28
+ *
29
+ * THE MISSING-ROW FAILURE, AND WHY `unclassified` EXISTS
30
+ * -----------------------------------------------------
31
+ * A generated table carries an authority a hand-written one never claimed, so
32
+ * its worst failure is not a wrong row — it is an ABSENT one. A fetch shape the
33
+ * scanner does not recognise produces no row, and a missing row reads as "no
34
+ * surface here".
35
+ *
36
+ * Two things push back. Inbound shapes are plural and named (a `fetch`, a
37
+ * `node:http(s)` request, a subprocess of a remote-fetching tool, a parse of
38
+ * markup arriving on stdin) rather than a single pattern. And a WEAK shape —
39
+ * a network call reached through an injected or computed callee, which the
40
+ * strong detectors cannot see — still emits a row, marked `unclassified`, so an
41
+ * unrecognised shape appears in the table as a visible gap instead of as
42
+ * silence. The residual is stated in the table's own header rather than left
43
+ * for a reader to discover: a module whose network access is reached through a
44
+ * shape matching neither set is still absent.
45
+ */
46
+ import * as fs from 'node:fs';
47
+ import * as path from 'node:path';
48
+
49
+ /** Coverage verdict for one read surface. */
50
+ export type SurfaceCoverage = 'covered' | 'uncovered' | 'unclassified';
51
+
52
+ /** One module that brings outside bytes in and hands a string onward. */
53
+ export interface ReadSurfaceRow {
54
+ /** Path relative to `src/scripts/`, POSIX separators. */
55
+ readonly module: string;
56
+ /** Inbound shape id that matched. */
57
+ readonly inbound: string;
58
+ /** Emit shape id that matched, or `undecided`. */
59
+ readonly emit: string;
60
+ readonly coverage: SurfaceCoverage;
61
+ /** Why the row reads the way it does — one clause, no paraphrase. */
62
+ readonly evidence: string;
63
+ }
64
+
65
+ interface ShapeDetector {
66
+ readonly id: string;
67
+ /** True when this module's source carries the shape. */
68
+ readonly match: (src: string) => boolean;
69
+ }
70
+
71
+ /** True when the module imports node's HTTP client at all. */
72
+ function _importsNodeHttp(src: string): boolean {
73
+ return /from\s+['"]node:https?['"]/.test(src);
74
+ }
75
+
76
+ /**
77
+ * Strong inbound shapes — a recognised way outside bytes enter this process.
78
+ *
79
+ * Each is deliberately narrow. `await fetch(` rather than `fetch(` keeps a
80
+ * fetch call named inside a string literal (a spawned one-liner, a doc example)
81
+ * out of the table; the node-HTTP shape requires the import as well as the call
82
+ * so a `.get(` on an unrelated object cannot produce a row.
83
+ */
84
+ export const STRONG_INBOUND: readonly ShapeDetector[] = [
85
+ {
86
+ id: 'network-fetch',
87
+ match: (s) => /\bawait\s+fetch\s*\(/.test(s),
88
+ },
89
+ {
90
+ id: 'node-http-request',
91
+ match: (s) => _importsNodeHttp(s) && /\bhttps?\s*\.\s*(?:get|request)\s*\(/.test(s),
92
+ },
93
+ {
94
+ id: 'remote-subprocess',
95
+ match: (s) =>
96
+ /\b(?:spawnSync|spawn|execSync|execFileSync|execFile)\s*\(\s*\[?\s*['"`](?:curl|wget|gh)['"`]/.test(s),
97
+ },
98
+ {
99
+ id: 'stdin-markup-parse',
100
+ match: (s) =>
101
+ /\bprocess\s*\.\s*stdin\b/.test(s) && /&(?:amp|lt|gt|quot|nbsp|#x?[0-9a-f]+);/i.test(s),
102
+ },
103
+ {
104
+ id: 'declared',
105
+ match: (s) => _DECLARATION_RE.test(s),
106
+ },
107
+ ];
108
+
109
+ /**
110
+ * A module may declare its own inbound shape when no syntactic one captures it.
111
+ *
112
+ * The shapes above are all things the scanner can SEE. A host primitive cannot
113
+ * be seen: a subagent reply, an MCP tool result and a host file-read arrive
114
+ * through a channel this package does not own, so no call site in this tree
115
+ * spells them. Risk 1 of the roadmap that produced this scanner names exactly
116
+ * that class, and the answer it demands is a visible row rather than silence.
117
+ *
118
+ * The marker is a comment at the site — `// read-surface: <shape-id>` — not a
119
+ * line in a central list, which is the arrangement this whole module exists to
120
+ * retire. A declaration says only "outside bytes arrive here"; the coverage
121
+ * verdict is still read from the imports, so declaring a surface cannot make it
122
+ * read `covered`.
123
+ */
124
+ const _DECLARATION_RE = /\/\/\s*read-surface:\s*([a-z0-9-]+)/;
125
+
126
+ /** `git` is deliberately NOT a remote command above. */
127
+ export const LOCAL_VCS_EXCLUSION =
128
+ 'a `git` subprocess reads local repository state, not outside bytes, so it is not an ' +
129
+ 'inbound shape. `git fetch` and `git ls-remote` move refs rather than content and are ' +
130
+ 'excluded with the rest — a branch name is attacker-influenceable in principle, and if ' +
131
+ 'that ever reaches a model-facing surface it needs its own row, not a widened matcher ' +
132
+ 'that would re-add every local `git status` wrapper in the tree.';
133
+
134
+ /**
135
+ * Weak inbound shapes — a network call the strong detectors cannot resolve.
136
+ *
137
+ * `await this._fetch(url, …)` is a real HTTP request behind an injected
138
+ * dependency, which is exactly the shape a strong matcher on `await fetch(`
139
+ * misses. It produces a row rather than silence; the verdict is
140
+ * `unclassified`, because the scanner cannot tell an injected fetch from an
141
+ * injected stub without executing it.
142
+ */
143
+ export const WEAK_INBOUND: readonly ShapeDetector[] = [
144
+ {
145
+ id: 'indirect-fetch',
146
+ match: (s) => /\bawait\s+(?:this|self|[a-z_$][\w$]*)\s*\.\s*_?fetch\s*\(/i.test(s),
147
+ },
148
+ ];
149
+
150
+ /** Recognised ways a module hands a string onward. */
151
+ export const EMIT_SHAPES: readonly ShapeDetector[] = [
152
+ { id: 'file-write', match: (s) => /\b(?:writeFileSync|appendFileSync|writeFile)\s*\(/.test(s) },
153
+ { id: 'stdout-write', match: (s) => /\bprocess\s*\.\s*stdout\s*\.\s*write\s*\(/.test(s) },
154
+ { id: 'exported-api', match: (s) => /^export\s+(?:async\s+)?function\s/m.test(s) },
155
+ ];
156
+
157
+ /** The sanitize floor, as an import. One hop; a transitive import is not counted. */
158
+ const _SANITIZE_IMPORT_RE =
159
+ /import\s*\{[^}]*\b(?:sanitize_text|sanitize_entry|sanitize_markup)\b[^}]*\}\s*from\s*['"][^'"]*retrieval_sanitize(?:\.js)?['"]/;
160
+
161
+ /** Test files under `src/scripts/` — fixtures, not surfaces that ship bytes. */
162
+ const _EXCLUDE_RE = /(?:^|\/)(?:__tests__|node_modules)\/|\.test\.ts$/;
163
+
164
+ /**
165
+ * Modules that define or read the coverage rather than being a surface.
166
+ *
167
+ * The scanner and its gate quote every detector pattern above as a literal, so
168
+ * they match their own matchers. Excluding them by name is the honest fix: a
169
+ * row for the scanner would be an artefact of the scanner reading itself, and
170
+ * carrying it would train a reader to skip rows.
171
+ */
172
+ const _SELF_MODULES: ReadonlySet<string> = new Set([
173
+ '_lib/retrieval_sanitize.ts',
174
+ '_lib/read_surface_scan.ts',
175
+ 'check_read_surface_coverage.ts',
176
+ ]);
177
+
178
+ /**
179
+ * Raised when a directory under the scan root cannot be read.
180
+ *
181
+ * The walk used to swallow the error and return, which made an INCOMPLETE scan
182
+ * indistinguishable from a complete one: a permission error on one subtree
183
+ * silently removed every module under it from the table, and the gate then
184
+ * reported the shortened list as matching the tree. An independent review named
185
+ * it; the direction is the same one `assertScanned` enforces at the corpus
186
+ * level, applied to the traversal that builds the corpus.
187
+ */
188
+ export class ScanTraversalError extends Error {
189
+ constructor(readonly dir: string, cause: unknown) {
190
+ super(`read_surface_scan: cannot read ${dir} — the scan is incomplete, not clean (${String(cause)})`);
191
+ this.name = 'ScanTraversalError';
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Directory reader, injectable for one reason: to test the failure.
197
+ *
198
+ * The fail-closed branch below can only be exercised by a `readdirSync` that
199
+ * throws, and on ESM `vi.spyOn(fs, 'readdirSync')` cannot redefine the
200
+ * namespace property. The alternative — chmod-000 on a real directory — is not
201
+ * portable, because under root the mode is no barrier and the test then fails
202
+ * in exactly the environment it was written to survive. A narrow seam is the
203
+ * honest shape: production passes nothing and gets `fs.readdirSync`.
204
+ */
205
+ export type ReadDir = (dir: string) => fs.Dirent[];
206
+
207
+ const _defaultReadDir: ReadDir = (dir) => fs.readdirSync(dir, { withFileTypes: true });
208
+
209
+ function _walk(dir: string, base: string, out: string[], readDir: ReadDir): void {
210
+ let entries: fs.Dirent[];
211
+ try {
212
+ entries = readDir(dir);
213
+ } catch (exc) {
214
+ throw new ScanTraversalError(dir, exc);
215
+ }
216
+ for (const e of entries) {
217
+ const abs = path.join(dir, e.name);
218
+ const rel = path.relative(base, abs).split(path.sep).join('/');
219
+ if (e.isDirectory()) {
220
+ _walk(abs, base, out, readDir);
221
+ } else if (e.isFile() && rel.endsWith('.ts') && !rel.endsWith('.d.ts') && !_EXCLUDE_RE.test(rel)) {
222
+ out.push(rel);
223
+ }
224
+ }
225
+ }
226
+
227
+ /** The scan root, repo-relative. Published so the gate can name it in an error. */
228
+ export const SCAN_ROOT = 'src/scripts';
229
+
230
+ /**
231
+ * Scan every TypeScript module under `src/scripts/` and return one row per
232
+ * module that both brings outside bytes in and hands a string onward.
233
+ *
234
+ * Rows are sorted by module path so the rendered table is stable across
235
+ * filesystems.
236
+ */
237
+ export function scanReadSurfaces(repoRoot: string, readDir: ReadDir = _defaultReadDir): ReadSurfaceRow[] {
238
+ const base = path.join(repoRoot, SCAN_ROOT);
239
+ const files: string[] = [];
240
+ _walk(base, base, files, readDir);
241
+ files.sort();
242
+
243
+ const rows: ReadSurfaceRow[] = [];
244
+ for (const rel of files) {
245
+ if (_SELF_MODULES.has(rel)) continue;
246
+ let src: string;
247
+ try {
248
+ src = fs.readFileSync(path.join(base, rel), 'utf-8');
249
+ } catch {
250
+ continue;
251
+ }
252
+ const strong = STRONG_INBOUND.find((d) => d.match(src));
253
+ const weak = strong === undefined ? WEAK_INBOUND.find((d) => d.match(src)) : undefined;
254
+ if (strong === undefined && weak === undefined) continue;
255
+ // A declared surface reports the shape its own marker names, so the
256
+ // table says `ingested-corpus` rather than the detector's own id.
257
+ const strongId =
258
+ strong?.id === 'declared' ? (_DECLARATION_RE.exec(src)?.[1] ?? 'declared') : strong?.id;
259
+
260
+ const emit = EMIT_SHAPES.find((d) => d.match(src));
261
+ const sanitized = _SANITIZE_IMPORT_RE.test(src);
262
+
263
+ if (weak !== undefined) {
264
+ rows.push({
265
+ module: rel,
266
+ inbound: weak.id,
267
+ emit: emit?.id ?? 'undecided',
268
+ coverage: 'unclassified',
269
+ evidence: 'network access through a callee the scanner cannot resolve — read the module',
270
+ });
271
+ continue;
272
+ }
273
+ if (emit === undefined) {
274
+ rows.push({
275
+ module: rel,
276
+ inbound: strongId ?? 'unknown',
277
+ emit: 'undecided',
278
+ coverage: 'unclassified',
279
+ evidence: 'inbound bytes with no recognised emit — the onward path is undecided',
280
+ });
281
+ continue;
282
+ }
283
+ rows.push({
284
+ module: rel,
285
+ inbound: strongId ?? 'unknown',
286
+ emit: emit.id,
287
+ coverage: sanitized ? 'covered' : 'uncovered',
288
+ evidence: sanitized
289
+ ? 'imports the sanitize floor'
290
+ : 'no sanitize import — fetched bytes reach the emit unfiltered',
291
+ });
292
+ }
293
+ return rows;
294
+ }
295
+
296
+ /** Counts by verdict, for the gate's scanned line and for the table header. */
297
+ export function tally(rows: readonly ReadSurfaceRow[]): Record<SurfaceCoverage, number> {
298
+ const out: Record<SurfaceCoverage, number> = { covered: 0, uncovered: 0, unclassified: 0 };
299
+ for (const r of rows) out[r.coverage] += 1;
300
+ return out;
301
+ }
302
+
303
+ /** Render the rows as the markdown table body committed to the contract doc. */
304
+ export function renderTable(rows: readonly ReadSurfaceRow[]): string {
305
+ const lines = [
306
+ '| module (under `src/scripts/`) | inbound shape | emit shape | coverage | evidence |',
307
+ '|---|---|---|---|---|',
308
+ ];
309
+ for (const r of rows) {
310
+ lines.push(
311
+ `| \`${r.module}\` | \`${r.inbound}\` | \`${r.emit}\` | **${r.coverage}** | ${r.evidence} |`,
312
+ );
313
+ }
314
+ const t = tally(rows);
315
+ lines.push('');
316
+ lines.push(
317
+ `${String(rows.length)} read surface(s): ` +
318
+ `${String(t.covered)} covered, ${String(t.uncovered)} uncovered, ` +
319
+ `${String(t.unclassified)} unclassified.`,
320
+ );
321
+ return lines.join('\n');
322
+ }
@@ -574,7 +574,8 @@ export function section_publication_blockers(
574
574
  'fails, so a missing line means the collection failed during `task release`. ' +
575
575
  'Fix the collection, then render the number and put it on the section — ' +
576
576
  `the release commit already exists on ${where}, so amend it there. Do not ` +
577
- 'invent the count: `npx vitest list | wc -l` is the same probe the writer runs.',
577
+ 'invent the count: `npx vitest list --staticParse=false | grep -c .` is the same ' +
578
+ 'probe the writer runs.',
578
579
  );
579
580
  }
580
581
  return out;