@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,314 @@
1
+ /**
2
+ * The `.gitignore` managed-block logic, with no CLI entry.
3
+ *
4
+ * WHY IT IS NOT IN `sync_gitignore.ts` ANY MORE. That file ends in a
5
+ * `process.exit(main(...))` behind a CLI-entry guard, and `check_installer_import_purity`
6
+ * refuses any module-level exit reachable from `install.ts`: esbuild inlines the
7
+ * whole closure into `dist/install/install.mjs`, where the guard comparing
8
+ * `import.meta.url` to `process.argv[1]` evaluates TRUE because the bundle IS
9
+ * argv[1]. A consumer install would then exit after writing its payload.
10
+ *
11
+ * So the pure half lives here, with no entry and no exit, and `sync_gitignore.ts`
12
+ * re-exports every name it used to own — the CLI contract and the ported
13
+ * pytest suite's snake_case call sites are unchanged.
14
+ *
15
+ * Everything below is byte-for-byte the behaviour it had in that file,
16
+ * including the historical quirks downstream consumers pin.
17
+ */
18
+ import * as fs from 'node:fs';
19
+ import * as path from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+
22
+ export const SECTION_HEADER = '# event4u/agent-config';
23
+ export const SECTION_FOOTER = '# event4u/agent-config — END';
24
+ export const DEFAULT_GITIGNORE = '.gitignore';
25
+
26
+ const _HERE = fileURLToPath(import.meta.url);
27
+ // _HERE === <repo>/src/scripts/_lib/gitignore_block.ts, so the repo root is
28
+ // three directories up. The retired Python implementation derived
29
+ // DEFAULT_TEMPLATE = <file>.parent.parent.parent / "src" / "config" /
30
+ // "gitignore-block.txt" from one level higher; the extra `..` here is that
31
+ // same root reached from one level deeper.
32
+ export const DEFAULT_TEMPLATE = path.join(
33
+ path.dirname(_HERE),
34
+ '..',
35
+ '..',
36
+ '..',
37
+ 'src',
38
+ 'config',
39
+ 'gitignore-block.txt',
40
+ );
41
+
42
+ // Legacy patterns that lived in older versions of src/config/gitignore-block.txt
43
+ // before runtime artefacts moved under /agents/runtime/ (May 2026). They get
44
+ // stripped wherever they appear in the consumer's .gitignore — inside the
45
+ // managed block or outside (older installers / hand-edits). Current canonical
46
+ // equivalents (e.g. /agents/runtime/.agent-prices.md) come from the template
47
+ // and are NOT affected. Leading-slash variants are matched defensively.
48
+ //
49
+ // Authoritative source: src/config/agents-paths.yml entries with `legacy: true`.
50
+ // Manual sync until the generation step from the manifest is implemented
51
+ // (road-to-agents-dir-and-gitignore-hygiene Phase 3.2 follow-on).
52
+ export const LEGACY_PATTERNS: readonly string[] = [
53
+ '.agent-chat-history',
54
+ '.agent-chat-history.bak',
55
+ '.agent-chat-history.*.bak',
56
+ '.agent-prices.md',
57
+ '.council-tmp/',
58
+ // 2.x intermediate: prices cache lived directly under agents/ before
59
+ // consolidating under agents/runtime/.
60
+ 'agents/.agent-prices.md',
61
+ // Budget history JSONLs — previously listed explicitly at project
62
+ // root or under agents/; now covered by the /agents/runtime/
63
+ // catch-all in the managed block.
64
+ '.augment-budget-history.jsonl',
65
+ '.rule-budget-history.jsonl',
66
+ 'agents/.augment-budget-history.jsonl',
67
+ 'agents/.rule-budget-history.jsonl',
68
+ ];
69
+
70
+ export function _strip(ln: string): string {
71
+ // Python: ln.rstrip("\n").rstrip() — strip a trailing newline, then strip
72
+ // all trailing whitespace.
73
+ return ln.replace(/\n+$/, '').replace(/\s+$/, '');
74
+ }
75
+
76
+ export function _is_entry(ln: string): boolean {
77
+ // Non-empty, non-comment line = a path/pattern entry.
78
+ const s = _strip(ln).replace(/^\s+/, '');
79
+ return Boolean(s) && !s.startsWith('#');
80
+ }
81
+
82
+ /** Error raised when the template file is missing (→ exit 2 in main). */
83
+ export class TemplateNotFoundError extends Error {
84
+ constructor(p: string) {
85
+ super(`template not found: ${p}`);
86
+ this.name = 'TemplateNotFoundError';
87
+ }
88
+ }
89
+
90
+ export function _isFile(p: string): boolean {
91
+ try {
92
+ return fs.statSync(p).isFile();
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+
98
+ /** Python str.splitlines() — split on universal newlines, no trailing empty. */
99
+ export function _splitlines(text: string): string[] {
100
+ if (text === '') return [];
101
+ // str.splitlines() splits on \n, \r, \r\n (and more) and does NOT keep a
102
+ // trailing empty element. The targets here only ever contain \n / \r\n.
103
+ const parts = text.split(/\r\n|\r|\n/);
104
+ if (parts.length > 0 && parts[parts.length - 1] === '') parts.pop();
105
+ return parts;
106
+ }
107
+
108
+ export function load_template(p: string): string[] {
109
+ if (!_isFile(p)) {
110
+ throw new TemplateNotFoundError(p);
111
+ }
112
+ const text = fs.readFileSync(p, 'utf-8');
113
+ // Keep trailing newlines stripped; we splice explicit newlines.
114
+ return _splitlines(text).map(_strip);
115
+ }
116
+
117
+ /**
118
+ * Locate the managed block; return [start_idx, end_idx_exclusive] or null.
119
+ *
120
+ * `start_idx` points at the SECTION_HEADER line.
121
+ * `end_idx_exclusive` points one past the last line of the block.
122
+ * Honors explicit SECTION_FOOTER when present; otherwise treats the
123
+ * block as extending to EOF or to the next non-managed section.
124
+ */
125
+ export function find_block(lines: readonly string[]): [number, number] | null {
126
+ for (let i = 0; i < lines.length; i += 1) {
127
+ if (_strip(lines[i] as string) === SECTION_HEADER) {
128
+ const start = i;
129
+ // Explicit footer?
130
+ for (let j = i + 1; j < lines.length; j += 1) {
131
+ if (_strip(lines[j] as string) === SECTION_FOOTER) {
132
+ return [start, j + 1];
133
+ }
134
+ }
135
+ // Legacy: extend to EOF or next non-managed section break.
136
+ let end = lines.length;
137
+ for (let j = i + 1; j < lines.length; j += 1) {
138
+ const s = _strip(lines[j] as string).replace(/^\s+/, '');
139
+ if (
140
+ s.startsWith('#') &&
141
+ !s.startsWith('# Agent config') &&
142
+ s !== SECTION_HEADER
143
+ ) {
144
+ end = j;
145
+ while (end > i + 1 && _strip(lines[end - 1] as string) === '') {
146
+ end -= 1;
147
+ }
148
+ break;
149
+ }
150
+ }
151
+ return [start, end];
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+
157
+ /** Return entries (paths/patterns) present in the given block. */
158
+ export function block_entries(block_lines: readonly string[]): string[] {
159
+ return block_lines
160
+ .filter((ln) => _is_entry(ln))
161
+ .map((ln) => _strip(ln).replace(/^\s+/, ''));
162
+ }
163
+
164
+ export function template_entries(template_lines: readonly string[]): string[] {
165
+ return template_lines.filter((ln) => _is_entry(ln)).map((ln) => ln.replace(/^\s+/, ''));
166
+ }
167
+
168
+ /** Return a fresh, fully-managed block with START + body + END. */
169
+ export function build_fresh_block(template_lines: readonly string[]): string[] {
170
+ return [SECTION_HEADER, ...template_lines, SECTION_FOOTER];
171
+ }
172
+
173
+ /**
174
+ * Return [new_lines, added_entries].
175
+ *
176
+ * - If block missing: append fresh block (preceded by a blank line if the
177
+ * file's last line is not already empty).
178
+ * - If block present and replace=true: rewrite block in full.
179
+ * - If block present and replace=false: append any missing managed entries
180
+ * before the END marker (adding END if absent). User-added lines inside the
181
+ * block are preserved.
182
+ */
183
+ export function sync_block(
184
+ existing_lines: readonly string[],
185
+ template_lines: readonly string[],
186
+ options: { replace?: boolean } = {},
187
+ ): [string[], string[]] {
188
+ const replace = options.replace ?? false;
189
+ const loc = find_block(existing_lines);
190
+ const fresh = build_fresh_block(template_lines);
191
+
192
+ // Missing block → append with leading blank if needed.
193
+ if (loc === null) {
194
+ const newLines = [...existing_lines];
195
+ if (newLines.length > 0 && _strip(newLines[newLines.length - 1] as string) !== '') {
196
+ newLines.push('');
197
+ }
198
+ newLines.push(...fresh);
199
+ return [newLines, template_entries(template_lines)];
200
+ }
201
+
202
+ const [start, end] = loc;
203
+ const head = existing_lines.slice(0, start);
204
+ let block = existing_lines.slice(start, end);
205
+ const tail = existing_lines.slice(end);
206
+
207
+ if (replace) {
208
+ const existing = new Set(block_entries(block));
209
+ const added = template_entries(template_lines).filter((e) => !existing.has(e));
210
+ return [[...head, ...fresh, ...tail], added];
211
+ }
212
+
213
+ // Append-only mode.
214
+ const existing_entries = new Set(block_entries(block));
215
+ const missing = template_entries(template_lines).filter((e) => !existing_entries.has(e));
216
+ if (missing.length === 0) {
217
+ return [[...existing_lines], []];
218
+ }
219
+
220
+ // Ensure block ends with SECTION_FOOTER; insert missing entries right
221
+ // before it.
222
+ let insert_at: number;
223
+ if (block.length > 0 && _strip(block[block.length - 1] as string) === SECTION_FOOTER) {
224
+ insert_at = block.length - 1;
225
+ } else {
226
+ block = [...block, SECTION_FOOTER];
227
+ insert_at = block.length - 1;
228
+ }
229
+ const new_block = [...block.slice(0, insert_at), ...missing, ...block.slice(insert_at)];
230
+ return [[...head, ...new_block, ...tail], missing];
231
+ }
232
+
233
+ /**
234
+ * Strip legacy entries from anywhere in the file.
235
+ *
236
+ * A line is legacy when its stripped, leading-whitespace-trimmed content
237
+ * matches a `LEGACY_PATTERNS` entry — with or without a leading slash.
238
+ * Comments and blank lines are untouched; current managed entries (e.g.
239
+ * `/agents/.agent-chat-history`) are not in the legacy set and survive.
240
+ */
241
+ export function cleanup_legacy(lines: readonly string[]): [string[], string[]] {
242
+ const legacy = new Set(LEGACY_PATTERNS);
243
+ const kept: string[] = [];
244
+ const removed: string[] = [];
245
+ for (const ln of lines) {
246
+ const s = _strip(ln).replace(/^\s+/, '');
247
+ if (legacy.has(s) || (s.startsWith('/') && legacy.has(s.slice(1)))) {
248
+ removed.push(s);
249
+ continue;
250
+ }
251
+ kept.push(ln);
252
+ }
253
+ return [kept, removed];
254
+ }
255
+
256
+ /** Join lines with newlines and enforce exactly one trailing newline. */
257
+ export function format_file(lines: readonly string[]): string {
258
+ const text = lines.join('\n');
259
+ return text.replace(/\n+$/, '') + '\n';
260
+ }
261
+
262
+ /**
263
+ * Write the managed block into a project that has no wizard to do it.
264
+ *
265
+ * WHY IT EXISTS. The block is normally installed by `refresh --project` /
266
+ * `sync:gitignore`, which the browser wizard reaches. A `--no-ui` install
267
+ * reaches neither, so a headless consumer ended up with a package that writes
268
+ * dispatcher state under `agents/` and a `.gitignore` that says nothing about
269
+ * it: `git status` fills with untracked runtime files the developer did not
270
+ * create and cannot place.
271
+ *
272
+ * APPEND-ONLY, NEVER `replace`. A line a developer put inside the block is
273
+ * theirs; this adds the managed entries that are missing and touches nothing
274
+ * else.
275
+ *
276
+ * BEST-EFFORT THROUGHOUT. No `.gitignore` and no git repository, an unreadable
277
+ * template, an unwritable file — each returns 0 quietly. An installer that
278
+ * failed because it could not tidy a listing would be worse than the listing.
279
+ *
280
+ * Returns the number of entries added (0 when nothing changed), and writes
281
+ * nothing when `dry_run`. Reporting is the caller's: this module owns no
282
+ * output convention.
283
+ */
284
+ export function write_managed_block(project_root: string, dry_run: boolean): number {
285
+ const target = path.join(project_root, DEFAULT_GITIGNORE);
286
+ let template: string[];
287
+ try {
288
+ template = load_template(DEFAULT_TEMPLATE);
289
+ } catch {
290
+ return 0;
291
+ }
292
+ let existing: string[] = [];
293
+ if (_isFile(target)) {
294
+ existing = _splitlines(fs.readFileSync(target, 'utf-8'));
295
+ } else if (!_isFile(path.join(project_root, '.git')) && !_isDir(path.join(project_root, '.git'))) {
296
+ return 0;
297
+ }
298
+ const [lines, added] = sync_block(existing, template);
299
+ if (added.length === 0 || dry_run) return added.length;
300
+ try {
301
+ fs.writeFileSync(target, format_file(lines), 'utf-8');
302
+ } catch {
303
+ return 0;
304
+ }
305
+ return added.length;
306
+ }
307
+
308
+ function _isDir(p: string): boolean {
309
+ try {
310
+ return fs.statSync(p).isDirectory();
311
+ } catch {
312
+ return false;
313
+ }
314
+ }
@@ -658,6 +658,38 @@ function difference(prev: unknown[], current: Set<string>): unknown[] {
658
658
  return out;
659
659
  }
660
660
 
661
+ /**
662
+ * The anchor-relative paths a previous deploy of `tool_id` recorded.
663
+ *
664
+ * Empty whenever the inventory has no entry, the recorded anchor differs from
665
+ * `anchor`, or the entry is malformed — the same "not provably ours anymore"
666
+ * discipline `reap_stale` applies before it deletes anything. Callers use it
667
+ * to answer "did this package write that file" for a file the CURRENT run did
668
+ * not write.
669
+ */
670
+ export function recorded_rel_files(
671
+ tool_id: string,
672
+ anchor: string,
673
+ inventory?: Inventory | null,
674
+ ): ReadonlySet<string> {
675
+ const inv = inventory ?? load_inventory();
676
+ const tools = (inv["tools"] as Record<string, unknown> | undefined) ?? {};
677
+ const entry = tools[tool_id];
678
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
679
+ return new Set<string>();
680
+ }
681
+ const e = entry as Record<string, unknown>;
682
+ const recorded_anchor = e["anchor"];
683
+ const files = e["files"];
684
+ if (typeof recorded_anchor !== "string" || !Array.isArray(files)) {
685
+ return new Set<string>();
686
+ }
687
+ if (resolve_path(expanduser(recorded_anchor)) !== resolve_path(expanduser(anchor))) {
688
+ return new Set<string>();
689
+ }
690
+ return new Set(files.filter((f): f is string => typeof f === "string"));
691
+ }
692
+
661
693
  /** Sort string members ascending by codepoint (non-strings filtered out). */
662
694
  function sorted_strings(items: unknown[]): string[] {
663
695
  return items
@@ -0,0 +1,214 @@
1
+ /**
2
+ * The graph feeder record — detector F's verdict and the code graph's, side by
3
+ * side, on every stop where a graph existed (`road-to-a-graph-that-feeds-the-gate`
4
+ * step 3.2).
5
+ *
6
+ * WHY A SEPARATE RECORD AND NOT `ShadowRecord` (D3). `ShadowRecord` is written
7
+ * only on the `stop_hook_active` and `refused_turn` layers and is the Q1
8
+ * instrument of `road-to-a-stop-that-holds` — a live measurement window with its
9
+ * own denominator. Folding a second signal into it would contaminate a reading
10
+ * somebody else is taking, which is a worse failure than a second file.
11
+ *
12
+ * WHY IT CHANGES NO VERDICT, AND WHAT THAT CLAIM DOES NOT COVER. Every function
13
+ * here is wrapped, every failure path returns silently, and no caller branches on
14
+ * the result, so no in-process path can move an exit code. It is NOT a claim
15
+ * about latency, and an earlier version of this paragraph said "costs the turn
16
+ * nothing", which a review correctly read as one: a stop in a repository with a
17
+ * graph pays a `git status` spawn, a graph open and an `untested` walk. Nothing
18
+ * measures that, and a host that timed a stop hook out would drop the gate's
19
+ * refusal — a failure the comparative exit-code test cannot see, because both of
20
+ * its runs complete. Named rather than implied away. Step 3.4 — promoting the
21
+ * graph verdict into F — is DEFERRED behind step 3.3's recall measurement and
22
+ * behind an owner amendment to ADR-277, and nothing here anticipates it.
23
+ *
24
+ * WHAT A ROW MAY CONTAIN. The fields are a closed set of enums, counts and
25
+ * repo-relative source paths. There is no field able to hold a prompt, a file
26
+ * body, a reply or a command — the same PII-exclusion-by-construction discipline
27
+ * `domain-safety-pii` § Surface 2 asks of a log line, and the same shape
28
+ * `ToolCall` already has. Paths are in the set because they are the evidence a
29
+ * human labeller needs in step 3.3 and because the gate's own refusal text
30
+ * already quotes them; they are capped so one turn cannot write an unbounded row.
31
+ */
32
+ import * as fs from 'node:fs';
33
+ import * as path from 'node:path';
34
+
35
+ import { detectSources, type GraphState, NATIVE_CACHE_REL, pickSource } from '../code_graph/detect.js';
36
+ import { loadGraph } from '../code_graph/query.js';
37
+ import { untested } from '../code_graph/verbs.js';
38
+
39
+ /** At most this many edit paths per row. */
40
+ const MAX_PATHS = 5;
41
+
42
+ /**
43
+ * At most this many rows per session file.
44
+ *
45
+ * `agents/state/` is local and gitignored, but "local" is not "free": a long
46
+ * session appends on every turn end, and an instrument that grows without a
47
+ * bound is the failure the gate's own refusal-state history already records.
48
+ * Past the cap the writer stops rather than rotating — a truncated tail is a
49
+ * readable sample, and a rotation would silently drop the earliest rows that a
50
+ * recall reading most wants.
51
+ */
52
+ export const MAX_ROWS_PER_SESSION = 200;
53
+
54
+ /** The graph's half of the verdict. `null` means the graph did not contribute. */
55
+ export type GraphVerdict = 'untested' | 'tested' | 'no-seeds' | null;
56
+
57
+ /** One stop, as the feeder records it. */
58
+ /**
59
+ * The stop layer a row came from.
60
+ *
61
+ * A closed union rather than `string`. The header below calls these fields "a
62
+ * closed set of enums", and a review pointed out the types did not enforce it —
63
+ * the sets lived only in the call sites, so a later caller could widen either
64
+ * with no type error and the prose would quietly become false.
65
+ */
66
+ export type FeederLayer = 'live' | 'stop_hook_active' | 'refused_turn';
67
+
68
+ /** Detector F's evidence class, from the same closed set the detector uses. */
69
+ export type FeederMode = 'transcript' | 'record';
70
+
71
+ export interface GraphFeederRow {
72
+ at: string;
73
+ /** The turn's ordinal — how many genuine user prompts preceded it. */
74
+ turn: number;
75
+ /** Which stop layer produced this row: the live verdict, or a retry. */
76
+ layer: FeederLayer;
77
+ graph_state: GraphState;
78
+ /** Did detector F fire on this turn? */
79
+ f: boolean;
80
+ /** F's evidence class when it fired — `transcript` or `record`. */
81
+ f_mode: FeederMode | null;
82
+ graph: GraphVerdict;
83
+ /** How many changed symbols the graph found no test edge for. */
84
+ graph_untested: number;
85
+ /** How many it did find one for — so the ratio is readable. */
86
+ graph_tested: number;
87
+ /** The turn's edit paths, capped at {@link MAX_PATHS}. */
88
+ paths: string[];
89
+ /** How many edit paths there were in total, when the list is capped. */
90
+ path_count: number;
91
+ }
92
+
93
+ export function feederFile(workspaceRoot: string, sessionKey: string): string {
94
+ return path.join(workspaceRoot, 'agents', 'state', 'graph-feeder', `${sessionKey}.jsonl`);
95
+ }
96
+
97
+ /**
98
+ * Run `untested` over the turn's edit paths.
99
+ *
100
+ * Returns `null` — the graph contributed nothing — for every reason there is:
101
+ * no usable source, a SCIP index with no owned reader, no edit paths, or any
102
+ * throw at all. A verdict this module cannot stand behind is not a verdict.
103
+ */
104
+ export function graphUntestedVerdict(
105
+ root: string,
106
+ state: GraphState,
107
+ paths: readonly string[],
108
+ ): { verdict: GraphVerdict; untested: number; tested: number } {
109
+ const none = { verdict: null, untested: 0, tested: 0 } as const;
110
+ // `behind:N` and `absent` contribute nothing, by the step's own terms: an
111
+ // answer from an index that predates the change is not evidence about the
112
+ // change.
113
+ if (state !== 'fresh' && state !== 'edited') return none;
114
+ if (paths.length === 0) return none;
115
+ let picked;
116
+ try {
117
+ picked = pickSource(detectSources(root, path.join(root, NATIVE_CACHE_REL)));
118
+ } catch {
119
+ return none;
120
+ }
121
+ if (!picked || picked.kind === 'scip') return none;
122
+ let g;
123
+ try {
124
+ g = loadGraph(picked.path, `${picked.kind}:${path.relative(root, picked.path)}`);
125
+ } catch {
126
+ return none;
127
+ }
128
+ try {
129
+ const r = untested(g, paths, state);
130
+ if (r.seeds.length === 0) return { verdict: 'no-seeds', untested: 0, tested: 0 };
131
+ return {
132
+ verdict: r.untested.length > 0 ? 'untested' : 'tested',
133
+ untested: r.untested.length,
134
+ tested: r.tested.length,
135
+ };
136
+ } catch {
137
+ return none;
138
+ } finally {
139
+ // The handle owns an open database; a stop hook that leaked one per turn
140
+ // would hold a descriptor for the life of the session.
141
+ try {
142
+ g.close();
143
+ } catch {
144
+ /* nothing to do about a failed close */
145
+ }
146
+ }
147
+ }
148
+
149
+ /** Append one row, or do nothing at all. Never throws, never returns a verdict. */
150
+ export function appendFeederRow(workspaceRoot: string, sessionKey: string, row: GraphFeederRow): void {
151
+ try {
152
+ const file = feederFile(workspaceRoot, sessionKey);
153
+ let existing = 0;
154
+ try {
155
+ existing = fs
156
+ .readFileSync(file, 'utf-8')
157
+ .split('\n')
158
+ .filter((l) => l.trim() !== '').length;
159
+ } catch {
160
+ /* absent is zero */
161
+ }
162
+ if (existing >= MAX_ROWS_PER_SESSION) return;
163
+ fs.mkdirSync(path.dirname(file), { recursive: true });
164
+ fs.appendFileSync(file, `${JSON.stringify(row)}\n`);
165
+ } catch {
166
+ // An instrument is never a reason to change what the gate does.
167
+ }
168
+ }
169
+
170
+ /** Read a session's rows back, skipping anything unparseable. */
171
+ export function readFeederRows(workspaceRoot: string, sessionKey: string): GraphFeederRow[] {
172
+ try {
173
+ return fs
174
+ .readFileSync(feederFile(workspaceRoot, sessionKey), 'utf-8')
175
+ .split('\n')
176
+ .filter((l) => l.trim() !== '')
177
+ .map((l) => {
178
+ try {
179
+ return JSON.parse(l) as GraphFeederRow;
180
+ } catch {
181
+ return null;
182
+ }
183
+ })
184
+ .filter((r): r is GraphFeederRow => r !== null);
185
+ } catch {
186
+ return [];
187
+ }
188
+ }
189
+
190
+ /** Build the row. Pure, so the shape can be asserted without a filesystem. */
191
+ export function buildFeederRow(input: {
192
+ turn: number;
193
+ layer: FeederLayer;
194
+ state: GraphState;
195
+ fFired: boolean;
196
+ fMode: FeederMode | null;
197
+ paths: readonly string[];
198
+ graph: { verdict: GraphVerdict; untested: number; tested: number };
199
+ at?: string;
200
+ }): GraphFeederRow {
201
+ return {
202
+ at: input.at ?? new Date().toISOString(),
203
+ turn: input.turn,
204
+ layer: input.layer,
205
+ graph_state: input.state,
206
+ f: input.fFired,
207
+ f_mode: input.fMode,
208
+ graph: input.graph.verdict,
209
+ graph_untested: input.graph.untested,
210
+ graph_tested: input.graph.tested,
211
+ paths: input.paths.slice(0, MAX_PATHS).map((p) => p),
212
+ path_count: input.paths.length,
213
+ };
214
+ }
@@ -78,6 +78,79 @@ export function hookSectionEnabled(root: string, section: string): boolean {
78
78
  return false;
79
79
  }
80
80
 
81
+ /**
82
+ * Raw scalar at `hooks.<section>.<key>` in `.agent-settings.yml`, or `''`.
83
+ *
84
+ * The `hookSectionEnabled` sibling above answers one boolean question and is
85
+ * the right shape for a default-OFF switch. This one exists because
86
+ * `hooks.verify_before_complete.touched_file_quality` is a three-valued mode
87
+ * (`off | shadow | warn`), and collapsing it into a boolean would lose exactly
88
+ * the distinction the roadmap's Phase 2 turns on — `shadow` records and says
89
+ * nothing, `warn` records and emits one line.
90
+ *
91
+ * It reuses the section scoping of `hookSectionEnabled` verbatim, including the
92
+ * ACTUAL-indent close that the 4-space-file defect produced: a reader that
93
+ * closed the section on a fixed depth would read a sibling section's value under
94
+ * this key's name, which for a mode string is worse than for a flag — the wrong
95
+ * value is a legal one.
96
+ *
97
+ * Interpretation is NOT done here. `''` means "nothing was written", and the
98
+ * caller decides what that defaults to — the same producer/consumer split
99
+ * `leanProjectionModeRaw` makes below, and for the same reason.
100
+ */
101
+ export function hookSectionValue(root: string, section: string, key: string): string {
102
+ const file = path.join(root, SETTINGS_FILE);
103
+ let text: string;
104
+ try {
105
+ if (!fs.statSync(file).isFile()) return '';
106
+ text = fs.readFileSync(file, 'utf-8');
107
+ } catch {
108
+ return '';
109
+ }
110
+
111
+ const esc = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
112
+ const sectionPattern = new RegExp(`^(\\s+)${esc(section)}\\s*:\\s*$`);
113
+ const keyPattern = new RegExp(`^\\s+${esc(key)}\\s*:\\s*(.+)$`);
114
+ let inHooks = false;
115
+ let sectionIndent: number | null = null;
116
+
117
+ const indentOf = (line: string): number => line.length - line.replace(/^\s+/, '').length;
118
+
119
+ for (const raw of text.split(/\r\n|\r|\n/)) {
120
+ const line = raw.replace(/\s+$/, '');
121
+ if (!line || line.replace(/^\s+/, '').startsWith('#')) continue;
122
+
123
+ if (!(line.startsWith(' ') || line.startsWith('\t'))) {
124
+ inHooks = /^hooks\s*:\s*$/.test(line);
125
+ sectionIndent = null;
126
+ continue;
127
+ }
128
+
129
+ if (inHooks) {
130
+ const opened = sectionPattern.exec(line);
131
+ if (opened) {
132
+ sectionIndent = opened[1]!.length;
133
+ continue;
134
+ }
135
+ if (sectionIndent !== null && indentOf(line) <= sectionIndent) {
136
+ sectionIndent = null;
137
+ }
138
+ }
139
+
140
+ if (sectionIndent !== null) {
141
+ const m = keyPattern.exec(line);
142
+ if (m) {
143
+ return (m[1] ?? '')
144
+ .replace(/\s+#.*$/, '')
145
+ .trim()
146
+ .replace(/^["']|["']$/g, '');
147
+ }
148
+ }
149
+ }
150
+
151
+ return '';
152
+ }
153
+
81
154
  /**
82
155
  * Raw `lean_projection.mode` string out of `.agent-settings.yml`, or `''`.
83
156
  *