@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,434 @@
1
+ /**
2
+ * touched_file_quality — run the project's OWN quality commands, in shadow,
3
+ * over the files a turn actually edited.
4
+ *
5
+ * loss_class: ephemeral-lossy
6
+ *
7
+ * The one place this module shortens content is `advisoryLine`, whose 200-byte
8
+ * cap (roadmap 2.2) trims a summary it synthesised itself from `QualityRun[]`.
9
+ * The trimmed tail is not stored anywhere and is not meant to be: the runs are
10
+ * in-memory for one invocation, the line goes to stderr, and the verdict it
11
+ * summarises is reproduced by re-running the same resolver command. A recovery
12
+ * locator would promise a retrieval nothing performs.
13
+ *
14
+ * `road-to-touched-files-that-pass-their-own-tools` Phase 1. The project already
15
+ * computes a per-stack quality list — `resolve_toolchain().quality` in
16
+ * `src/agent-src/templates/scripts/work_engine/stack/runner.ts` — and nothing at
17
+ * stop calls it. This module is the caller. It owns no list of its own: D2 of the
18
+ * roadmap pins the resolver as the single authority, because a second list is a
19
+ * second authority that drifts from the first in silence.
20
+ *
21
+ * WHAT THIS IS NOT. It is not verification. A command recorded here never reaches
22
+ * `_lib/verification_command.ts`, never raises `verifications_this_turn`, and
23
+ * never sets `verified_this_turn` — `eslint` exits 0 over a clean file set, and a
24
+ * zero exit that reached the completion gate would mean a turn could claim "I
25
+ * verified" by having edited nothing interesting. The producer writes
26
+ * `quality_runs[]`; the classifier reads `verification_runs[]`. Two arrays, two
27
+ * readers, no overlap — pinned by a negative fixture rather than by this comment.
28
+ *
29
+ * FOUR RULES, AND EACH ONE EXISTS BECAUSE THE NAIVE VERSION IS HARMFUL.
30
+ *
31
+ * 1. A command runs only if the resolver listed it. Nothing is synthesised.
32
+ * 2. A command that WRITES runs only in its check form, or not at all. The
33
+ * resolver emits `vendor/bin/pint`, which rewrites the consumer's source; a
34
+ * shadow pass that reformats the tree it is observing is not a shadow pass.
35
+ * Where a check flag is known (`--test`) it is used; where it is not, the row
36
+ * is `skipped: mutating` and the command is never spawned.
37
+ * 3. A command with no file-scoped form is `skipped: unscoped`. `tsc --noEmit`
38
+ * cannot take a file list without discarding `tsconfig.json`, and running it
39
+ * project-wide at every stop makes stop the slowest hook in the chain. The
40
+ * resolver carries no cost field, so nothing here guesses that an unknown
41
+ * command is cheap: an unrecognised command is `unscoped` and is not run.
42
+ * 4. ENOENT or exit 127 is `skipped: absent`, counted apart from verdicts.
43
+ * Detection upstream is by manifest (`typescript` in `devDependencies`), not
44
+ * by presence on PATH, so "the tool is not installed" must never read as a red.
45
+ *
46
+ * Output per command: the command, its exit code, and the first 20 lines of its
47
+ * combined output. Bounded on purpose — this lands in a per-session state file
48
+ * that another hook reads on every turn.
49
+ */
50
+ import { spawnSync } from 'node:child_process';
51
+ import * as fs from 'node:fs';
52
+ import * as path from 'node:path';
53
+
54
+ /** Lines of combined output retained per command. */
55
+ export const OUTPUT_HEAD_LINES = 20;
56
+
57
+ /** Default per-command wall-clock bound, milliseconds. */
58
+ export const DEFAULT_TIMEOUT_MS = 30_000;
59
+
60
+ /**
61
+ * Why a command was not executed.
62
+ *
63
+ * `no_files` is the fourth reason and is not in the roadmap's prose list, which
64
+ * named three. It is needed and it is honest: running `eslint` over a turn that
65
+ * touched only `.php` files produces a verdict about nothing. 2.1 counts
66
+ * `skipped:*` separately from verdicts, and the glob is what makes a fourth
67
+ * reason additive rather than a contract change.
68
+ */
69
+ export type SkipReason = 'mutating' | 'unscoped' | 'absent' | 'no_files';
70
+
71
+ /** One resolver command, classified into a runnable form or a skip reason. */
72
+ export interface QualityPlan {
73
+ /** The resolver's command string, verbatim — the provenance of this row. */
74
+ readonly source_command: string;
75
+ /** argv actually spawned, or `null` when the row is skipped. */
76
+ readonly argv: readonly string[] | null;
77
+ /** The touched files passed to it. */
78
+ readonly files: readonly string[];
79
+ readonly skipped: SkipReason | null;
80
+ }
81
+
82
+ /** One executed (or deliberately skipped) quality command. */
83
+ export interface QualityRun {
84
+ /** The command as run, or the resolver's command when skipped. */
85
+ readonly command: string;
86
+ readonly source_command: string;
87
+ /** `null` when the row was skipped — never `0`, which is a real verdict. */
88
+ readonly exit_code: number | null;
89
+ readonly output_head: readonly string[];
90
+ readonly skipped: SkipReason | null;
91
+ readonly files: readonly string[];
92
+ readonly at: string;
93
+ }
94
+
95
+ /**
96
+ * How one resolver command is turned into a file-scoped, non-writing form.
97
+ *
98
+ * Keyed on the exact string the resolver emits. Keying on the whole command
99
+ * rather than on a parsed head is deliberate: the key IS the resolver's output,
100
+ * so a resolver change that this table has not seen falls through to `unscoped`
101
+ * and is not run, instead of being half-recognised and run in a form nobody
102
+ * chose.
103
+ */
104
+ interface ToolSpec {
105
+ /** argv prefix; the touched files are appended. */
106
+ readonly argv: readonly string[];
107
+ /** Extensions this tool reads. Empty means "any file". */
108
+ readonly extensions: readonly string[];
109
+ /**
110
+ * The resolver's own command writes files, and `argv` above is its check
111
+ * form. Recorded so a reader can see that the executed command differs from
112
+ * the resolver's by exactly the flag that makes it read-only.
113
+ */
114
+ readonly check_form: boolean;
115
+ }
116
+
117
+ const JS_EXTENSIONS = ['.js', '.jsx', '.mjs', '.cjs', '.ts', '.tsx', '.mts', '.cts'] as const;
118
+
119
+ /**
120
+ * The scoped forms, one row per resolver command that HAS one.
121
+ *
122
+ * Everything the resolver can emit that is absent here — `npx tsc --noEmit`,
123
+ * `go vet ./...`, `cargo clippy` — is absent because it has no per-file form,
124
+ * and absence is what produces `skipped: unscoped`.
125
+ */
126
+ const SCOPED_FORMS: ReadonlyMap<string, ToolSpec> = new Map<string, ToolSpec>([
127
+ ['npx eslint .', { argv: ['npx', 'eslint'], extensions: JS_EXTENSIONS, check_form: false }],
128
+ [
129
+ 'vendor/bin/phpstan analyse',
130
+ { argv: ['vendor/bin/phpstan', 'analyse'], extensions: ['.php'], check_form: false },
131
+ ],
132
+ // `vendor/bin/pint` REWRITES source. `--test` is its check form: same tool,
133
+ // same rules, reports instead of writing. Risk 3 of the roadmap is that this
134
+ // module reformats the consumer's tree; this row plus the fixture that proves
135
+ // no file changed is the mitigation.
136
+ [
137
+ 'vendor/bin/pint',
138
+ { argv: ['vendor/bin/pint', '--test'], extensions: ['.php'], check_form: true },
139
+ ],
140
+ ['ruff check', { argv: ['ruff', 'check'], extensions: ['.py'], check_form: false }],
141
+ ['mypy .', { argv: ['mypy'], extensions: ['.py'], check_form: false }],
142
+ ]);
143
+
144
+ /**
145
+ * Resolver commands known to write files for which NO check form is known.
146
+ *
147
+ * Empty today — every mutating command the resolver emits (`vendor/bin/pint`)
148
+ * has one, and it is in `SCOPED_FORMS` above. The map exists so that the day a
149
+ * writing command without a `--check` lands, the answer is a row here
150
+ * (`skipped: mutating`, never spawned) rather than a judgement call at the call
151
+ * site. A mutating command that fell through to `unscoped` would be recorded
152
+ * under the wrong reason and counted in the wrong bucket by 2.1.
153
+ */
154
+ const MUTATING_WITHOUT_CHECK_FORM: ReadonlySet<string> = new Set<string>([]);
155
+
156
+ function extensionOf(file: string): string {
157
+ const base = path.basename(file);
158
+ const dot = base.lastIndexOf('.');
159
+ return dot <= 0 ? '' : base.slice(dot).toLowerCase();
160
+ }
161
+
162
+ /**
163
+ * Classify the resolver's list against the turn's touched files. PURE.
164
+ *
165
+ * Separated from execution so the four rules above are testable without
166
+ * spawning anything, and so a reader can see the whole policy in one return
167
+ * value before any process starts.
168
+ */
169
+ export function planQualityRuns(
170
+ commands: readonly string[],
171
+ files: readonly string[],
172
+ ): QualityPlan[] {
173
+ return commands.map((source_command): QualityPlan => {
174
+ if (MUTATING_WITHOUT_CHECK_FORM.has(source_command)) {
175
+ return { source_command, argv: null, files: [], skipped: 'mutating' };
176
+ }
177
+ const spec = SCOPED_FORMS.get(source_command);
178
+ if (spec === undefined) {
179
+ return { source_command, argv: null, files: [], skipped: 'unscoped' };
180
+ }
181
+ const matched =
182
+ spec.extensions.length === 0
183
+ ? [...files]
184
+ : files.filter((f) => spec.extensions.includes(extensionOf(f)));
185
+ if (matched.length === 0) {
186
+ return { source_command, argv: null, files: [], skipped: 'no_files' };
187
+ }
188
+ return { source_command, argv: [...spec.argv, ...matched], files: matched, skipped: null };
189
+ });
190
+ }
191
+
192
+ /** `argv` rendered for the record. Not re-parsed — display and audit only. */
193
+ export function renderCommand(argv: readonly string[]): string {
194
+ return argv.join(' ');
195
+ }
196
+
197
+ function headLines(text: string, limit: number): string[] {
198
+ if (text === '') return [];
199
+ return text
200
+ .split(/\r\n|\r|\n/)
201
+ .filter((line, i, all) => !(line === '' && i === all.length - 1))
202
+ .slice(0, limit);
203
+ }
204
+
205
+ /** ISO-8601 to seconds, matching the record's other timestamps. */
206
+ function now(): string {
207
+ return new Date().toISOString().replace(/\.\d{3}Z$/, '+00:00');
208
+ }
209
+
210
+ export interface RunOptions {
211
+ readonly root: string;
212
+ readonly commands: readonly string[];
213
+ readonly files: readonly string[];
214
+ readonly timeout_ms?: number;
215
+ /** Injected in tests so the table's behaviour is observable without a toolchain. */
216
+ readonly spawn?: (
217
+ argv: readonly string[],
218
+ cwd: string,
219
+ timeout_ms: number,
220
+ ) => { status: number | null; output: string; enoent: boolean };
221
+ }
222
+
223
+ function defaultSpawn(
224
+ argv: readonly string[],
225
+ cwd: string,
226
+ timeout_ms: number,
227
+ ): { status: number | null; output: string; enoent: boolean } {
228
+ const [head, ...rest] = argv;
229
+ if (head === undefined) return { status: null, output: '', enoent: true };
230
+ const res = spawnSync(head, rest, {
231
+ cwd,
232
+ timeout: timeout_ms,
233
+ encoding: 'utf-8',
234
+ // No shell. The argv comes from a committed table, and a shell would
235
+ // make a file path with a space into an injection surface.
236
+ shell: false,
237
+ maxBuffer: 4 * 1024 * 1024,
238
+ });
239
+ const enoent =
240
+ res.error !== undefined &&
241
+ (res.error as NodeJS.ErrnoException).code === 'ENOENT';
242
+ const output = `${res.stdout ?? ''}${res.stderr ?? ''}`;
243
+ return { status: res.status, output, enoent };
244
+ }
245
+
246
+ /**
247
+ * Execute the plan and return one record per resolver command.
248
+ *
249
+ * Never throws. A spawn that fails for any reason becomes a row, because a
250
+ * shadow pass that can take the stop hook down with it is worse than no shadow
251
+ * pass at all.
252
+ */
253
+ export function runTouchedFileQuality(opts: RunOptions): QualityRun[] {
254
+ const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
255
+ const spawn = opts.spawn ?? defaultSpawn;
256
+ const out: QualityRun[] = [];
257
+ for (const plan of planQualityRuns(opts.commands, opts.files)) {
258
+ if (plan.argv === null) {
259
+ out.push({
260
+ command: plan.source_command,
261
+ source_command: plan.source_command,
262
+ exit_code: null,
263
+ output_head: [],
264
+ skipped: plan.skipped,
265
+ files: [],
266
+ at: now(),
267
+ });
268
+ continue;
269
+ }
270
+ let status: number | null = null;
271
+ let output = '';
272
+ let enoent = false;
273
+ try {
274
+ const res = spawn(plan.argv, opts.root, timeout_ms);
275
+ status = res.status;
276
+ output = res.output;
277
+ enoent = res.enoent;
278
+ } catch {
279
+ enoent = true;
280
+ }
281
+ // Exit 127 is the shell's "command not found" and reaches us when the
282
+ // tool is a wrapper script whose own dependency is missing. Same meaning
283
+ // as ENOENT for this module's purposes, and the same bucket in 2.1.
284
+ const absent = enoent || status === 127;
285
+ out.push({
286
+ command: renderCommand(plan.argv),
287
+ source_command: plan.source_command,
288
+ exit_code: absent ? null : status,
289
+ output_head: absent ? [] : headLines(output, OUTPUT_HEAD_LINES),
290
+ skipped: absent ? 'absent' : null,
291
+ files: plan.files,
292
+ at: now(),
293
+ });
294
+ }
295
+ return out;
296
+ }
297
+
298
+ /**
299
+ * The single advisory line `warn` mode emits — command, first file, verdict.
300
+ *
301
+ * `null` when nothing failed. Capped at 200 bytes by the roadmap's 2.2, and the
302
+ * cap is enforced on the UTF-8 byte length rather than on `String.length`, which
303
+ * counts code units and would let a path with non-ASCII characters through at
304
+ * nearly twice the budget.
305
+ */
306
+ export const WARN_LINE_MAX_BYTES = 200;
307
+
308
+ export function advisoryLine(runs: readonly QualityRun[]): string | null {
309
+ const failed = runs.find((r) => r.skipped === null && r.exit_code !== null && r.exit_code !== 0);
310
+ if (failed === undefined) return null;
311
+ const file = failed.files[0] ?? '(no file)';
312
+ const more = failed.files.length > 1 ? ` (+${String(failed.files.length - 1)} more)` : '';
313
+ const line = `touched-file quality: ${failed.source_command} exited ${String(failed.exit_code)} on ${file}${more}`;
314
+ return truncateToBytes(line, WARN_LINE_MAX_BYTES);
315
+ }
316
+
317
+ /** Cut `text` so its UTF-8 encoding is at most `max` bytes, never mid-character. */
318
+ export function truncateToBytes(text: string, max: number): string {
319
+ const buf = Buffer.from(text, 'utf-8');
320
+ if (buf.length <= max) return text;
321
+ let end = max;
322
+ // Back off into the start of a character: continuation bytes are 10xxxxxx.
323
+ while (end > 0 && (buf[end] ?? 0) >= 0x80 && (buf[end] ?? 0) < 0xc0) end -= 1;
324
+ return buf.subarray(0, end).toString('utf-8');
325
+ }
326
+
327
+ // ---------------------------------------------------------------------------
328
+ // Which files the turn touched
329
+ // ---------------------------------------------------------------------------
330
+
331
+ /** Where the touched-path set came from, recorded beside the runs. */
332
+ export type FileSource = 'minimal-safe-diff∩worktree' | 'none';
333
+
334
+ export interface TouchedFiles {
335
+ readonly files: readonly string[];
336
+ readonly source: FileSource;
337
+ }
338
+
339
+ /**
340
+ * The turn's touched files, intersected with what the working tree says changed.
341
+ *
342
+ * RISK 4 OF THE ROADMAP, AND THE REASON THIS IS NOT JUST A READ.
343
+ * `agents/state/minimal-safe-diff.json` is ONE FILE PER PROJECT ROOT, not per
344
+ * session — and under this repository's worktree workflow `CLAUDE_PROJECT_DIR`
345
+ * resolves to the parent checkout, so concurrent runs share it. Reading it
346
+ * straight would let a neighbouring session's edits become this session's
347
+ * quality target.
348
+ *
349
+ * Two independent narrowings, and both are needed:
350
+ *
351
+ * - OWNERSHIP. A state file whose `session_id` is not this session's is
352
+ * refused outright, which is the same stance `owns_session_state` takes on
353
+ * the verification record.
354
+ * - THE WORKING TREE. A path is kept only if git still reports it as changed.
355
+ * A path the recorder saw but the tree has since reverted is not a quality
356
+ * target, and an edit from a different checkout is not in THIS tree's status.
357
+ *
358
+ * An absent, foreign or unreadable recorder state yields `none` and NO files,
359
+ * which makes the shadow pass a no-op. The alternative — falling back to the
360
+ * whole working tree — is exactly the cross-session leak this is guarding, so
361
+ * the degradation is toward measuring nothing rather than toward measuring
362
+ * somebody else's work.
363
+ */
364
+ export function touchedFilesAtStop(
365
+ root: string,
366
+ session_id: string,
367
+ gitStatus: (root: string) => readonly string[] = defaultGitStatus,
368
+ ): TouchedFiles {
369
+ let recorded: string[];
370
+ try {
371
+ const raw = fs.readFileSync(
372
+ path.join(root, 'agents', 'state', 'minimal-safe-diff.json'),
373
+ 'utf-8',
374
+ );
375
+ const decoded: unknown = JSON.parse(raw);
376
+ if (typeof decoded !== 'object' || decoded === null || Array.isArray(decoded)) {
377
+ return { files: [], source: 'none' };
378
+ }
379
+ const state = decoded as Record<string, unknown>;
380
+ if (state['session_id'] !== session_id || session_id === '') {
381
+ return { files: [], source: 'none' };
382
+ }
383
+ const touched = state['files_touched_this_turn'];
384
+ if (!Array.isArray(touched)) return { files: [], source: 'none' };
385
+ recorded = touched.filter((x): x is string => typeof x === 'string');
386
+ } catch {
387
+ return { files: [], source: 'none' };
388
+ }
389
+
390
+ const changed = new Set(gitStatus(root));
391
+ const files = recorded.filter((f) => changed.has(normalizePath(f)));
392
+ return { files, source: 'minimal-safe-diff∩worktree' };
393
+ }
394
+
395
+ /** Repo-relative, forward slashes, no leading `./` — the shape git prints. */
396
+ export function normalizePath(p: string): string {
397
+ return p.replace(/\\/g, '/').replace(/^\.\//, '');
398
+ }
399
+
400
+ function defaultGitStatus(root: string): string[] {
401
+ const res = spawnSync('git', ['status', '--porcelain', '-z'], {
402
+ cwd: root,
403
+ encoding: 'utf-8',
404
+ timeout: 10_000,
405
+ shell: false,
406
+ maxBuffer: 4 * 1024 * 1024,
407
+ });
408
+ if (res.status !== 0 || typeof res.stdout !== 'string') return [];
409
+ return parsePorcelainZ(res.stdout);
410
+ }
411
+
412
+ /**
413
+ * Paths out of `git status --porcelain -z`.
414
+ *
415
+ * `-z` rather than the line-oriented form: without it git QUOTES a path holding
416
+ * a space or a non-ASCII byte, and the quoted form never matches a recorded
417
+ * path, so the intersection would silently drop exactly the files most likely
418
+ * to be mis-handled downstream. A rename entry carries two NUL-separated paths
419
+ * (`R <new>\0<old>`); the new path is the one that exists to be checked.
420
+ */
421
+ export function parsePorcelainZ(stdout: string): string[] {
422
+ const out: string[] = [];
423
+ const parts = stdout.split('\0');
424
+ for (let i = 0; i < parts.length; i += 1) {
425
+ const entry = parts[i];
426
+ if (entry === undefined || entry.length < 4) continue;
427
+ const status = entry.slice(0, 2);
428
+ out.push(normalizePath(entry.slice(3)));
429
+ // Rename / copy: the NEXT NUL-separated field is the ORIGIN path, which
430
+ // is consumed here so it is not read as a status entry of its own.
431
+ if (status.startsWith('R') || status.startsWith('C')) i += 1;
432
+ }
433
+ return out;
434
+ }