@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
@@ -45,14 +45,56 @@ import * as fs from 'node:fs';
45
45
  import * as path from 'node:path';
46
46
  import { fileURLToPath, pathToFileURL } from 'node:url';
47
47
 
48
+ // `yaml`, not `js-yaml`, and the choice is about the BUNDLE rather than about
49
+ // this file. Every concern is inlined into one `dist/hooks/dispatch.js` that
50
+ // every dispatch loads, and this import was the only `js-yaml` one left on that
51
+ // path while six other modules — `dispatch_hook.ts` and `host_lowering.ts`
52
+ // among them — already pulled `yaml`. Measured on 2026-10-01 from the esbuild
53
+ // metafile: `js-yaml` contributed 95,846 bytes (6.13 %) of a 1,564,211-byte
54
+ // bundle, paid on every hook event, for one call site. The behavioural delta is
55
+ // normalised in `parseSettingsDoc` below — three constraints, each MEASURED
56
+ // against the old reader rather than read off documentation — and pinned both by
57
+ // the characterization tests written before the swap and by a differential test
58
+ // that runs BOTH readers over one corpus and asserts the same refusal set.
59
+ import { parseDocument as parseYamlDocument } from 'yaml';
60
+
61
+ import {
62
+ buildSettingsClassIndex,
63
+ classOfPath,
64
+ parseSettingsClassRows,
65
+ type SettingsClass,
66
+ } from '../../shared/settingsClasses.js';
48
67
  import { EDIT_TOOLS } from '../minimal_safe_diff_hook.js';
49
68
  import { readHookStdin } from './hook_stdin.js';
69
+ import { EXIT_ALLOW, EXIT_BLOCK, EXIT_WARN } from './exit_codes.js';
50
70
 
51
71
  const _HERE = fileURLToPath(import.meta.url);
52
72
 
53
- const EXIT_ALLOW = 0;
54
- const EXIT_BLOCK = 1;
55
- const EXIT_WARN = 2;
73
+ /**
74
+ * This package's own root — where the shipped class contract lives.
75
+ *
76
+ * RESOLVED BY SEARCH, not by counting `..` segments. This file runs from three
77
+ * layouts: the source tree, an installed `node_modules/@event4u/agent-config/`,
78
+ * and inlined into `dist/hooks/dispatch.js`, where every module shares the
79
+ * BUNDLE's `import.meta.url` and a fixed hop count lands somewhere else
80
+ * entirely. A review flagged the fixed-hop version as resolving a different
81
+ * root from the one the package occupies; walking up to the nearest directory
82
+ * that actually carries the contract answers the question the guard is asking
83
+ * ("where is the contract") instead of a proxy for it.
84
+ *
85
+ * `null` when no ancestor carries it — which is the fail-closed input to
86
+ * `readClassIndex`, not a silent default root.
87
+ */
88
+ export function findPackageRoot(from: string): string | null {
89
+ let dir = path.dirname(from);
90
+ for (let i = 0; i < 12; i++) {
91
+ if (fs.existsSync(path.join(dir, SETTINGS_CLASSES_RELATIVE))) return dir;
92
+ const up = path.dirname(dir);
93
+ if (up === dir) break;
94
+ dir = up;
95
+ }
96
+ return null;
97
+ }
56
98
 
57
99
  /**
58
100
  * The stated cap from `autonomous-execution` § Antipattern. Crossing it in one
@@ -81,7 +123,18 @@ const _PATH_KEYS: readonly string[] = ['file_path', 'path', 'target_file', 'file
81
123
  * where "weakening" needs context a tool call does not carry (a baseline count
82
124
  * may legitimately reset after a refactor); those warn and never block.
83
125
  */
84
- export type ConfigKind = 'allowlist' | 'advisory' | null;
126
+ export type ConfigKind = 'allowlist' | 'advisory' | 'class-c' | null;
127
+
128
+ /**
129
+ * Project settings files whose keys this guard fences. User-global files are
130
+ * never in reach — the hook only sees a path a tool call named, and the writes
131
+ * that reach `~/.event4u/` go through `settings:set`, which applies its own
132
+ * per-key refusal.
133
+ */
134
+ const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', 'settings.json'];
135
+
136
+ /** Where the class contract lives, relative to the package root. */
137
+ export const SETTINGS_CLASSES_RELATIVE = 'docs/contracts/settings-classes.md';
85
138
 
86
139
  /** Classify a target path into a config surface, or null for everything else. */
87
140
  export function classify_target(p: string): ConfigKind {
@@ -96,9 +149,154 @@ export function classify_target(p: string): ConfigKind {
96
149
  if (/-budget(s)?\.(json|ya?ml)$/.test(base) || base === 'budgets.yml') {
97
150
  return 'advisory';
98
151
  }
152
+ // `settings.json` only under a host directory: the bare name is common
153
+ // enough elsewhere that classifying it on the basename alone would fence
154
+ // files that carry no settings key at all.
155
+ if (base === 'settings.json') {
156
+ return /(^|\/)\.(claude|cursor|augment|windsurf)\/settings\.json$/.test(posix) ? 'class-c' : null;
157
+ }
158
+ if (CLASS_C_BASENAMES.includes(base)) {
159
+ return 'class-c';
160
+ }
99
161
  return null;
100
162
  }
101
163
 
164
+ /**
165
+ * Leaf key paths of a parsed settings document, dotted.
166
+ *
167
+ * LEAVES ONLY, and the choice is load-bearing in both directions. A class-C key
168
+ * whose value is a MAP (`hooks`, say) has children that never appear as their
169
+ * own rows in the contract, so emitting only leaves and letting `classOfPath`
170
+ * walk up to the nearest classified ancestor is what makes `hooks.enabled`
171
+ * resolve to C. Emitting the interior nodes as well would double-count the same
172
+ * change and report a parent that nothing edited.
173
+ */
174
+ export function leafPaths(value: unknown, prefix = ''): Map<string, string> {
175
+ const out = new Map<string, string>();
176
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
177
+ if (prefix !== '') out.set(prefix, JSON.stringify(value ?? null));
178
+ return out;
179
+ }
180
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
181
+ const key = prefix === '' ? k : `${prefix}.${k}`;
182
+ for (const [ck, cv] of leafPaths(v, key)) out.set(ck, cv);
183
+ }
184
+ return out;
185
+ }
186
+
187
+ /**
188
+ * Parse a settings document, or `null` when it is not parseable.
189
+ *
190
+ * `null` is NOT "no keys changed" — the caller treats it as an unreadable
191
+ * corpus and refuses, because a guard that allows whatever it cannot parse is
192
+ * bypassed by making the file unparseable for one call.
193
+ *
194
+ * THE YAML READER IS CONSTRAINED, and all three constraints exist to keep the
195
+ * refusal set the same as `js-yaml`'s rather than to express a preference.
196
+ * Each was MEASURED against the old reader over a shared corpus, which the
197
+ * differential test re-runs; the equivalence is not argued from documentation.
198
+ *
199
+ * · `schema: 'core'` + `resolveKnownTags: false` — WITHOUT THIS, THE SWAP
200
+ * WOULD HAVE WEAKENED THE GUARD. `js-yaml` v5's default CORE schema does
201
+ * not carry the YAML 1.1 tags, so `!!timestamp`, `!!binary`, `!!set`,
202
+ * `!!omap`, `!!pairs` and any unknown `!custom` tag THREW — the document
203
+ * was unparseable and the guard refused. The `yaml` package resolves them
204
+ * by default, so the same document would have parsed and the guard would
205
+ * have gone on to allow an edit it previously refused. Found by an
206
+ * independent council review (openai/codex-default, 2026-10-01) and
207
+ * confirmed by running both readers: six tagged inputs, `js-yaml` throws on
208
+ * all six, default `yaml` returns a value for all six.
209
+ * · A WARNING IS A REFUSAL. `yaml` reports an unresolvable tag as a warning
210
+ * and keeps parsing where `js-yaml` throws, so treating warnings as success
211
+ * would re-open the same hole one tag at a time. `parseDocument` is used
212
+ * rather than `parse` precisely to see them.
213
+ * · NULLISH IS UNPARSEABLE. `js-yaml` threw on an empty document and returned
214
+ * `null` for a value-less one (`~`, `null`); both landed on refusal. `yaml`
215
+ * returns `null` for all three instead of throwing on the first, so without
216
+ * this collapse an EMPTIED settings file would arrive at the caller as a
217
+ * parsed document with no keys — the one shape that turns "I cannot read
218
+ * this" into "nothing changed".
219
+ *
220
+ * · `merge: false` — PINNED, NOT FIXING AN OBSERVED DIFFERENCE. The `yaml`
221
+ * package documents `merge` as defaulting to the document's YAML VERSION:
222
+ * true for 1.1, false for 1.2. A second review round (openai/codex-default)
223
+ * flagged that a `%YAML 1.1` directive could therefore re-enable merge
224
+ * resolution past the schema pin, which would expose keys the old reader
225
+ * left nested under a literal `<<`. Measured, and it does NOT reproduce
226
+ * here — under `schema: 'core'` a `%YAML 1.1` document reads
227
+ * `{"personal":{"<<":{…}}}` identically with and without this option, same
228
+ * as `js-yaml`. It is set anyway because the cost is a word and the finding
229
+ * named a real documented coupling between two options: a future default
230
+ * cannot flip it silently. An option set without an observed failure behind
231
+ * it is said so rather than implied.
232
+ *
233
+ * A merge key (`<<: *anchor`) was raised as a difference by the other seat and
234
+ * is NOT one: both readers leave `<<` literal at these versions, with and
235
+ * without a version directive. The same differential corpus pins both forms, so
236
+ * the question is settled by a reading rather than re-litigated from
237
+ * documentation next time.
238
+ */
239
+ export function parseSettingsDoc(text: string, rel: string): unknown | null {
240
+ if (rel.endsWith('.json')) {
241
+ try {
242
+ return (JSON.parse(text) as unknown) ?? null;
243
+ } catch {
244
+ return null;
245
+ }
246
+ }
247
+ try {
248
+ const doc = parseYamlDocument(text, { schema: 'core', resolveKnownTags: false, merge: false });
249
+ if (doc.errors.length > 0 || doc.warnings.length > 0) return null;
250
+ return (doc.toJS() as unknown) ?? null;
251
+ } catch {
252
+ return null;
253
+ }
254
+ }
255
+
256
+ /** Dotted paths whose value differs between two settings documents. */
257
+ export function changedKeys(before: unknown, after: unknown): string[] {
258
+ const a = leafPaths(before);
259
+ const b = leafPaths(after);
260
+ const out = new Set<string>();
261
+ for (const [k, v] of b) if (a.get(k) !== v) out.add(k);
262
+ for (const k of a.keys()) if (!b.has(k)) out.add(k);
263
+ return [...out].sort();
264
+ }
265
+
266
+ /**
267
+ * The class index, read from the shipped contract. `null` when it cannot be
268
+ * read AT ALL, which the caller treats as fail-closed.
269
+ *
270
+ * Not cached: the file is small, and a cache would keep a stale fence alive
271
+ * across an upgrade that reclassified a key — the same reasoning the server's
272
+ * own reader states for the same file.
273
+ */
274
+ export function readClassIndex(packageRoot: string): Map<string, SettingsClass> | null {
275
+ try {
276
+ const text = fs.readFileSync(path.join(packageRoot, SETTINGS_CLASSES_RELATIVE), 'utf-8');
277
+ const index = buildSettingsClassIndex(parseSettingsClassRows(text));
278
+ return index.size === 0 ? null : index;
279
+ } catch {
280
+ return null;
281
+ }
282
+ }
283
+
284
+ /**
285
+ * The guarded subset of a change list.
286
+ *
287
+ * A key is guarded when its nearest classified ancestor is C, and ALSO when
288
+ * nothing on its path is classified at all — the same per-key refusal
289
+ * `settings:set` and the GUI write route already apply, reached here through
290
+ * the same shared classifier rather than a second copy of the rule.
291
+ */
292
+ export function guardedKeys(index: ReadonlyMap<string, SettingsClass> | null, keys: readonly string[]): string[] {
293
+ if (index === null) return [...keys];
294
+ return keys.filter((k) => {
295
+ const cls = classOfPath(index, k);
296
+ return cls === 'C' || cls === undefined;
297
+ });
298
+ }
299
+
102
300
  /**
103
301
  * Count allowlist entries in a blob.
104
302
  *
@@ -167,6 +365,89 @@ export interface Decision {
167
365
  }
168
366
 
169
367
  /** Decide from the cumulative session total for one allowlist file. */
368
+ /**
369
+ * The refusal reason for a class-C settings edit, or `null` to allow.
370
+ *
371
+ * WHAT THIS FENCES, and what it deliberately does not. The unit is the KEY, not
372
+ * the file: a Class A key stays agent-writable through this path exactly as it
373
+ * is through `settings:set`, and fencing the whole file would make an ordinary
374
+ * preference edit need a human. Only a key the contract classifies C — or one
375
+ * nothing on its path classifies at all, which is the same fail-closed default
376
+ * the CLI writer applies — is refused.
377
+ *
378
+ * THE UNPARSEABLE CASES REFUSE, both of them, and for the same reason. A
379
+ * contract this guard cannot read leaves it with no way to tell a C key from an
380
+ * A key; a resulting document it cannot parse leaves it with no key list at
381
+ * all. Allowing either would mean the fence is lifted by making one file
382
+ * unreadable for the length of one tool call, which is a bypass with no
383
+ * authorisation step in it.
384
+ *
385
+ * WHAT IT CANNOT SEE: an edit applied through a shell redirect rather than an
386
+ * edit tool — `EDIT_TOOLS` is the corpus, and the shell shapes are
387
+ * `block_plumbing_writes`' subject, not this one.
388
+ */
389
+ export function classCVerdict(
390
+ ti: JsonObject,
391
+ on_disk: string | null,
392
+ rel_path: string,
393
+ index: ReadonlyMap<string, SettingsClass> | null,
394
+ ): string | null {
395
+ const before = on_disk === null ? null : parseSettingsDoc(on_disk, rel_path);
396
+
397
+ const content = ti['content'];
398
+ const oldStr = ti['old_string'];
399
+ const newStr = ti['new_string'];
400
+ let afterText: string | null = null;
401
+ if (typeof content === 'string') {
402
+ afterText = content;
403
+ } else if (typeof oldStr === 'string' && typeof newStr === 'string' && on_disk !== null) {
404
+ if (!on_disk.includes(oldStr)) return null; // the edit will not apply
405
+ // `replace_all` is the host's own flag and it changes WHICH text the
406
+ // edit produces. Modelling only the first occurrence let a
407
+ // `replace_all` edit whose SECOND occurrence is the Class C one pass
408
+ // this guard while the real edit changed it — the guard evaluating a
409
+ // different edit from the one executed. Reported by an independent
410
+ // review before this landed.
411
+ const all = ti['replace_all'];
412
+ if (all !== undefined && typeof all !== 'boolean') {
413
+ return (
414
+ `${rel_path}: this edit carries a \`replace_all\` value the guard cannot ` +
415
+ 'interpret, so the text it would produce is unknown and no key can be ' +
416
+ 'cleared. Re-send it as a plain edit, or write the change through ' +
417
+ '`agent-config settings:set`.'
418
+ );
419
+ }
420
+ afterText = all === true ? on_disk.split(oldStr).join(newStr) : on_disk.replace(oldStr, newStr);
421
+ }
422
+ if (afterText === null) return null;
423
+
424
+ const after = parseSettingsDoc(afterText, rel_path);
425
+ if (after === null) {
426
+ return (
427
+ `${rel_path}: the result of this edit does not parse as settings, so no key ` +
428
+ 'list can be derived and no key can be cleared. Fix the syntax, or write the ' +
429
+ 'change through `agent-config settings:set`, which refuses a Class C key by name.'
430
+ );
431
+ }
432
+
433
+ const guarded = guardedKeys(index, changedKeys(before, after));
434
+ if (guarded.length === 0) return null;
435
+
436
+ const unreadable =
437
+ index === null
438
+ ? ' The class contract could not be read, so EVERY changed key is treated as ' +
439
+ 'guarded — that is the fail-closed direction, not a classification.'
440
+ : '';
441
+ return (
442
+ `${rel_path}: this edit changes ${String(guarded.length)} guarded settings key(s) — ` +
443
+ `${guarded.slice(0, 5).join(', ')}${guarded.length > 5 ? ', …' : ''}.` +
444
+ unreadable +
445
+ ' A Class C key is a policy dial (spend ceiling, allowlist, gate switch, hooks) and ' +
446
+ 'is a human edit by contract: docs/contracts/settings-classes.md. Every other key in ' +
447
+ 'this file stays writable.'
448
+ );
449
+ }
450
+
170
451
  export function decide(kind: ConfigKind, rel_path: string, session_total: number, added: number): Decision {
171
452
  if (kind === null || added <= 0) {
172
453
  return { action: 'allow', reason: '' };
@@ -292,6 +573,15 @@ export function main(): number {
292
573
  } catch {
293
574
  on_disk = null;
294
575
  }
576
+ if (kind === 'class-c') {
577
+ const rel_c = path.relative(root, abs) || p;
578
+ const pkg = findPackageRoot(_HERE);
579
+ const verdict = classCVerdict(ti, on_disk, rel_c, pkg === null ? null : readClassIndex(pkg));
580
+ if (verdict === null) continue;
581
+ process.stderr.write(`block-config-weakening: BLOCKED — ${verdict}\n`);
582
+ return EXIT_BLOCK;
583
+ }
584
+
295
585
  const added = added_entries(ti, on_disk);
296
586
  if (added <= 0) {
297
587
  continue;
@@ -49,6 +49,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
49
49
  import * as path from 'node:path';
50
50
 
51
51
  import { is_kernel_rule } from '../_lib/kernel_rules.js';
52
+ import { shellWriteTarget } from '../_lib/shell_write_shapes.js';
52
53
  import { EDIT_TOOLS } from '../minimal_safe_diff_hook.js';
53
54
  import { COMMAND_TOOLS } from '../before_complete_hook.js';
54
55
  import { readHookStdin } from './hook_stdin.js';
@@ -133,87 +134,17 @@ export function targets_kernel_rule(filePath: string): string | null {
133
134
  return basename.replace(/\.md$/, '');
134
135
  }
135
136
 
136
- /** In-place mutators that name their target as a plain argument. */
137
- const _MUTATOR_VERBS: ReadonlySet<string> = new Set([
138
- 'sed',
139
- 'tee',
140
- 'truncate',
141
- 'rm',
142
- 'shred',
143
- ]);
144
- /** Verbs whose LAST positional argument is the destination. */
145
- const _DEST_LAST_VERBS: ReadonlySet<string> = new Set(['mv', 'cp', 'install', 'rsync']);
146
-
147
137
  /**
148
- * Shell tokens that, in a command naming a kernel rule path, mean the path is
149
- * being WRITTEN rather than read. Reads (`cat`, `grep`, `head`, `diff`,
150
- * `git show`) carry none of these and stay allowed — a kernel rule is
151
- * immutable, not secret.
138
+ * The kernel rule a command WRITES, or null.
139
+ *
140
+ * The shape catalogue moved to `_lib/shell_write_shapes.ts` when
141
+ * `block_plumbing_writes.ts` needed the same shapes over a different file set
142
+ * (road-to-a-kernel-that-guards-its-plumbing 1.2). Behaviour here is unchanged:
143
+ * the narrowness, the reads-stay-allowed rule and the known-open interpreter
144
+ * residual are all properties of that shared module now, stated in its header.
152
145
  */
153
146
  function _bash_targets_kernel_rule(command: string): string | null {
154
- // Cheap reject first: no kernel-rule path in the string at all.
155
- const tokens = command.split(/\s+/).filter((t) => t.length > 0);
156
- const stripped = tokens.map((t) => t.replace(/^['"]|['"]$/g, ''));
157
- const ruleOf = (t: string): string | null => targets_kernel_rule(t);
158
- if (!stripped.some((t) => ruleOf(t) !== null)) {
159
- return null;
160
- }
161
-
162
- // 1. Redirection into the path: `> p`, `>> p`, `>p`, `>>p`.
163
- for (let i = 0; i < stripped.length; i += 1) {
164
- const tok = stripped[i] as string;
165
- const inline = /^>{1,2}(.+)$/.exec(tok);
166
- if (inline) {
167
- const r = ruleOf(inline[1] as string);
168
- if (r !== null) {
169
- return r;
170
- }
171
- }
172
- if (tok === '>' || tok === '>>') {
173
- const next = stripped[i + 1];
174
- if (typeof next === 'string') {
175
- const r = ruleOf(next);
176
- if (r !== null) {
177
- return r;
178
- }
179
- }
180
- }
181
- }
182
-
183
- // 2. A mutator verb anywhere in the pipeline, with the path as an argument.
184
- // `sed` only counts in its in-place form — `sed 's/x/y/' file` prints.
185
- const verbs = new Set<string>();
186
- let prevWasSeparator = true;
187
- for (const tok of stripped) {
188
- if (['&&', '||', ';', '|'].includes(tok)) {
189
- prevWasSeparator = true;
190
- continue;
191
- }
192
- if (prevWasSeparator && !tok.startsWith('-')) {
193
- verbs.add(path.basename(tok));
194
- prevWasSeparator = false;
195
- }
196
- }
197
- const sedInPlace = verbs.has('sed') && stripped.some((t) => /^-i/.test(t));
198
- for (const verb of verbs) {
199
- if (verb === 'sed' && !sedInPlace) {
200
- continue;
201
- }
202
- if (_MUTATOR_VERBS.has(verb)) {
203
- const hit = stripped.map(ruleOf).find((r) => r !== null);
204
- if (hit !== undefined && hit !== null) {
205
- return hit;
206
- }
207
- }
208
- if (_DEST_LAST_VERBS.has(verb)) {
209
- const last = stripped[stripped.length - 1] as string;
210
- const r = ruleOf(last);
211
- if (r !== null) {
212
- return r;
213
- }
214
- }
215
- }
216
- return null;
147
+ return shellWriteTarget(command, targets_kernel_rule);
217
148
  }
218
149
 
219
150
  /**