@event4u/agent-config 16.0.0 → 16.2.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 (382) 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 +289 -140
  5. package/CONTRIBUTING.md +9 -0
  6. package/README.md +2 -2
  7. package/dist/agent-src/commands/analyze/inbox.md +26 -0
  8. package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
  9. package/dist/agent-src/commands/challenge-me/closure.md +178 -0
  10. package/dist/agent-src/commands/challenge-me.md +9 -2
  11. package/dist/agent-src/commands/council/default.md +25 -5
  12. package/dist/agent-src/commands/feature/plan.md +28 -1
  13. package/dist/agent-src/commands/feature/roadmap.md +28 -1
  14. package/dist/agent-src/commands/implement-ticket.md +27 -0
  15. package/dist/agent-src/commands/jira-ticket.md +27 -0
  16. package/dist/agent-src/commands/pr/create.md +29 -0
  17. package/dist/agent-src/commands/pr/merge.md +60 -9
  18. package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
  19. package/dist/agent-src/commands/roadmap/create.md +74 -27
  20. package/dist/agent-src/commands/roadmap/materialize.md +27 -0
  21. package/dist/agent-src/commands/roadmap/process-full.md +123 -18
  22. package/dist/agent-src/commands/team.md +22 -0
  23. package/dist/agent-src/commands/work.md +4 -0
  24. package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
  25. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
  26. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
  27. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
  28. package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
  29. package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
  30. package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
  31. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
  32. package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  33. package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  34. package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
  35. package/dist/agent-src/rules/autonomous-execution.md +15 -8
  36. package/dist/agent-src/rules/delegation-policy.md +23 -45
  37. package/dist/agent-src/rules/design-fidelity.md +32 -37
  38. package/dist/agent-src/rules/evaluator-independence.md +43 -49
  39. package/dist/agent-src/rules/test-first.md +41 -0
  40. package/dist/agent-src/rules/ui-audit-gate.md +1 -2
  41. package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
  42. package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
  43. package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
  44. package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
  45. package/dist/agent-src/skills/database/data/manifest.json +4 -4
  46. package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
  47. package/dist/agent-src/skills/design-review/SKILL.md +80 -4
  48. package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
  49. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
  50. package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
  51. package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
  52. package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
  53. package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
  54. package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
  55. package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
  56. package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
  57. package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
  58. package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
  59. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
  60. package/dist/agent-src/templates/agent-settings.md +1 -1
  61. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  62. package/dist/agent-src/templates/roadmaps.md +218 -0
  63. package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  64. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  65. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  66. package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  67. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  68. package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  69. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  70. package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
  71. package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  72. package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  73. package/dist/cli/python/knowledge_ingest.js +1 -1
  74. package/dist/cli/python/workspace_drive.js +1 -1
  75. package/dist/cli-delegate/check_update_banner.js +40 -2
  76. package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
  77. package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
  78. package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
  79. package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
  80. package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
  81. package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
  82. package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
  83. package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
  84. package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
  85. package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
  86. package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
  87. package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
  88. package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
  89. package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
  90. package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
  91. package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
  92. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  93. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  94. package/dist/cli-delegate/cmd_conformance.js +985 -831
  95. package/dist/cli-delegate/cmd_doctor.js +7 -5
  96. package/dist/cli-delegate/cmd_explain.js +2 -2
  97. package/dist/cli-delegate/cmd_export.js +9 -7
  98. package/dist/cli-delegate/cmd_fleet.js +4 -2
  99. package/dist/cli-delegate/cmd_handoff.js +7 -7
  100. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  101. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  102. package/dist/cli-delegate/cmd_migrate.js +2 -2
  103. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  104. package/dist/cli-delegate/cmd_preflight.js +4 -2
  105. package/dist/cli-delegate/cmd_prune.js +10 -8
  106. package/dist/cli-delegate/cmd_route_audit.js +3 -3
  107. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  108. package/dist/cli-delegate/cmd_session_recycle.js +4 -4
  109. package/dist/cli-delegate/cmd_settings_get.js +28 -2
  110. package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
  111. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  112. package/dist/cli-delegate/cmd_sync.js +10 -8
  113. package/dist/cli-delegate/cmd_uninstall.js +16 -13
  114. package/dist/cli-delegate/cmd_update.js +1 -1
  115. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  116. package/dist/cli-delegate/cmd_validate.js +12 -10
  117. package/dist/cli-delegate/cmd_versions.js +1 -1
  118. package/dist/discovery/deprecation-report.md +1 -1
  119. package/dist/discovery/discovery-manifest.json +90 -43
  120. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  121. package/dist/discovery/discovery-manifest.summary.md +4 -4
  122. package/dist/discovery/dormancy-report.md +1 -1
  123. package/dist/discovery/orphan-report.md +1 -1
  124. package/dist/discovery/packs.json +10 -8
  125. package/dist/discovery/trust-report.md +3 -3
  126. package/dist/discovery/workspaces.json +8 -6
  127. package/dist/hooks/dispatch.js +7682 -2772
  128. package/dist/hooks/dispatch.sha256 +1 -0
  129. package/dist/install/claudeRuleRewrite.js +14 -3
  130. package/dist/install/conflict.js +60 -10
  131. package/dist/install/conflictTracker.js +218 -0
  132. package/dist/install/fsPrimitives.js +54 -0
  133. package/dist/install/install.mjs +2200 -1559
  134. package/dist/install/manifestFiles.js +259 -0
  135. package/dist/install/preflight.js +14 -2
  136. package/dist/install/preserve.js +157 -0
  137. package/dist/install/recordedOwnership.js +132 -0
  138. package/dist/install/txlog.js +11 -2
  139. package/dist/mcp/registry-manifest.json +3 -3
  140. package/dist/mcp/server.json +2 -2
  141. package/dist/mcp/server.mjs +3066 -1470
  142. package/dist/router.json +1 -1
  143. package/dist/scripts/_lib/agent_settings.js +3 -2
  144. package/dist/scripts/_lib/fs_atomic.js +167 -0
  145. package/dist/scripts/_lib/installed_tools.js +460 -0
  146. package/dist/scripts/_lib/lean_projection_mode.js +3 -2
  147. package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
  148. package/dist/server/routes/install.js +8 -0
  149. package/dist/server/routes/wizard.js +9 -3
  150. package/dist/server/schemas/settings.js +40 -5
  151. package/dist/shared/missionExecution.js +97 -0
  152. package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
  153. package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
  154. package/dist/ui/index.html +2 -2
  155. package/docs/MIGRATION.md +103 -0
  156. package/docs/contracts/settings-classes.md +28 -7
  157. package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
  158. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
  159. package/docs/guidelines/design-fidelity-routing.md +29 -0
  160. package/package.json +4 -4
  161. package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
  162. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
  163. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
  164. package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
  165. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
  166. package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
  167. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
  168. package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
  169. package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
  170. package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
  171. package/src/config/agent-settings.template.yml +168 -27
  172. package/src/config/auto-rule-budget.json +16 -0
  173. package/src/config/ci-local-parity.yml +36 -5
  174. package/src/config/continuity-surface.json +23 -2
  175. package/src/config/gate-coverage.yml +377 -13
  176. package/src/config/gate-violation-baselines.json +10 -6
  177. package/src/config/hook-latency-budget.json +21 -0
  178. package/src/config/hook-token-budget.json +4 -2
  179. package/src/config/loop-surfaces.yaml +215 -0
  180. package/src/config/metric-registry.yml +114 -0
  181. package/src/config/placeholder-drift-budget.json +10 -0
  182. package/src/config/platform-anchor.json +3 -2
  183. package/src/config/release-gate-locality.yml +11 -0
  184. package/src/config/routing-coverage-seed.json +33 -5
  185. package/src/config/rule-enforcement-baseline.json +0 -1
  186. package/src/install/claudeRuleRewrite.ts +16 -2
  187. package/src/install/conflict.ts +65 -9
  188. package/src/install/conflictTracker.ts +299 -0
  189. package/src/install/fsPrimitives.ts +54 -0
  190. package/src/install/manifestFiles.ts +312 -0
  191. package/src/install/preflight.ts +16 -2
  192. package/src/install/preserve.ts +205 -0
  193. package/src/install/recordedOwnership.ts +149 -0
  194. package/src/install/txlog.ts +11 -2
  195. package/src/install/types.ts +19 -4
  196. package/src/scripts/_cli/cmd_conformance.ts +88 -15
  197. package/src/scripts/_cli/cmd_doctor.ts +7 -20
  198. package/src/scripts/_cli/cmd_route_explain.ts +9 -2
  199. package/src/scripts/_cli/cmd_settings_get.ts +44 -0
  200. package/src/scripts/_cli/doctor_execution.ts +173 -0
  201. package/src/scripts/_cli/doctor_network_posture.ts +226 -0
  202. package/src/scripts/_lib/activation_payload.ts +247 -0
  203. package/src/scripts/_lib/agent_settings.ts +4 -2
  204. package/src/scripts/_lib/authority_path.ts +102 -0
  205. package/src/scripts/_lib/body_portable.ts +102 -0
  206. package/src/scripts/_lib/cascade_base.ts +94 -0
  207. package/src/scripts/_lib/cc_transcript.ts +132 -1
  208. package/src/scripts/_lib/changelog_eras.ts +7 -2
  209. package/src/scripts/_lib/concern_sla_window.ts +200 -0
  210. package/src/scripts/_lib/continuation_ladder.ts +149 -2
  211. package/src/scripts/_lib/council_transport.ts +129 -0
  212. package/src/scripts/_lib/delivery_ready.ts +112 -0
  213. package/src/scripts/_lib/forge_protection.ts +381 -0
  214. package/src/scripts/_lib/forge_reader.ts +558 -0
  215. package/src/scripts/_lib/hook_platform_keys.ts +27 -0
  216. package/src/scripts/_lib/host_env_write.ts +111 -0
  217. package/src/scripts/_lib/json_python_parity.ts +127 -0
  218. package/src/scripts/_lib/lean_projection_mode.ts +14 -3
  219. package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
  220. package/src/scripts/_lib/loop_surfaces.ts +382 -0
  221. package/src/scripts/_lib/mission_record.ts +190 -0
  222. package/src/scripts/_lib/obligation_frequency.ts +217 -0
  223. package/src/scripts/_lib/obligations.ts +513 -0
  224. package/src/scripts/_lib/orchestration_record.ts +11 -20
  225. package/src/scripts/_lib/probe_inputs.ts +271 -0
  226. package/src/scripts/_lib/read_surface_scan.ts +322 -0
  227. package/src/scripts/_lib/release_highlights.ts +201 -11
  228. package/src/scripts/_lib/release_holds.ts +493 -0
  229. package/src/scripts/_lib/release_migration_gate.ts +184 -0
  230. package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
  231. package/src/scripts/_lib/rides_along.ts +102 -0
  232. package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
  233. package/src/scripts/_lib/shell_write_shapes.ts +108 -0
  234. package/src/scripts/_lib/structural_hiding.ts +622 -0
  235. package/src/scripts/_lib/test_delta.ts +166 -0
  236. package/src/scripts/_lib/test_provenance.ts +171 -0
  237. package/src/scripts/_lib/turn_end_refusals.ts +525 -4
  238. package/src/scripts/_lib/turn_end_transcript.ts +216 -0
  239. package/src/scripts/_lib/typed_op_grant.ts +246 -0
  240. package/src/scripts/_lib/typed_op_watch.ts +185 -0
  241. package/src/scripts/_lib/verification_command.ts +324 -0
  242. package/src/scripts/_lib/verification_evidence.ts +647 -0
  243. package/src/scripts/_lib/verify_clause.ts +280 -0
  244. package/src/scripts/ai_council/config.ts +8 -11
  245. package/src/scripts/ai_council/fallback_config.ts +5 -3
  246. package/src/scripts/ai_council/ownership_classes.ts +118 -0
  247. package/src/scripts/ai_team/team_dispatch.ts +4 -0
  248. package/src/scripts/ask_block_census.ts +285 -15
  249. package/src/scripts/before_complete_hook.ts +340 -23
  250. package/src/scripts/bench_hook_latency.ts +412 -5
  251. package/src/scripts/cache_realization_report.ts +61 -1
  252. package/src/scripts/check_argument_hint.ts +287 -0
  253. package/src/scripts/check_claims.ts +125 -1
  254. package/src/scripts/check_enforcement_coverage.ts +38 -9
  255. package/src/scripts/check_enforcement_matrix.ts +866 -0
  256. package/src/scripts/check_finding_dispositions.ts +22 -3
  257. package/src/scripts/check_gate_reachability.ts +173 -10
  258. package/src/scripts/check_generator_sync.ts +1025 -0
  259. package/src/scripts/check_host_docs_digest.ts +786 -0
  260. package/src/scripts/check_host_format_column.ts +559 -0
  261. package/src/scripts/check_input_references.ts +228 -0
  262. package/src/scripts/check_inputs_parity.ts +235 -0
  263. package/src/scripts/check_kernel_edit_ratified.ts +47 -5
  264. package/src/scripts/check_kill_switch_table.ts +294 -0
  265. package/src/scripts/check_placeholder_drift.ts +221 -0
  266. package/src/scripts/check_preamble_payload_budget.ts +75 -2
  267. package/src/scripts/check_read_surface_coverage.ts +515 -0
  268. package/src/scripts/check_release_highlights.ts +33 -1
  269. package/src/scripts/check_release_holds.ts +568 -0
  270. package/src/scripts/check_routing_coverage.ts +378 -11
  271. package/src/scripts/check_rule_projection_integrity.ts +22 -12
  272. package/src/scripts/check_static_parity.ts +17 -0
  273. package/src/scripts/check_test_delta.ts +294 -0
  274. package/src/scripts/check_test_weakening.ts +299 -0
  275. package/src/scripts/ci_settle.ts +145 -19
  276. package/src/scripts/closure_scan.ts +447 -0
  277. package/src/scripts/compile_hook_manifest.ts +102 -18
  278. package/src/scripts/council_record_shape.ts +209 -0
  279. package/src/scripts/external_sources_denylist.json +29 -4
  280. package/src/scripts/hook_manifest.json +1 -1
  281. package/src/scripts/hook_manifest.yaml +127 -11
  282. package/src/scripts/hooks/block_config_weakening.ts +229 -4
  283. package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
  284. package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
  285. package/src/scripts/hooks/bundle_integrity.ts +291 -0
  286. package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
  287. package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
  288. package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
  289. package/src/scripts/hooks/concern_registry.ts +6 -0
  290. package/src/scripts/hooks/concern_timings.ts +94 -0
  291. package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
  292. package/src/scripts/hooks/design_pass_hook.ts +221 -5
  293. package/src/scripts/hooks/design_slop_hook.ts +1 -2
  294. package/src/scripts/hooks/dispatch_hook.ts +25 -19
  295. package/src/scripts/hooks/edit_shape_hook.ts +1 -2
  296. package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
  297. package/src/scripts/hooks/evidence_independence.ts +97 -2
  298. package/src/scripts/hooks/exit_codes.ts +128 -0
  299. package/src/scripts/hooks/host_lowering.json +1 -0
  300. package/src/scripts/hooks/host_lowering.ts +128 -3
  301. package/src/scripts/hooks/host_lowering.yaml +262 -46
  302. package/src/scripts/hooks/host_semantics.ts +30 -2
  303. package/src/scripts/hooks/injection_budget.ts +9 -2
  304. package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
  305. package/src/scripts/hooks/journal_record_hook.ts +1 -2
  306. package/src/scripts/hooks/memory_recall_hook.ts +539 -0
  307. package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
  308. package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
  309. package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
  310. package/src/scripts/hooks/permission_gate.ts +1 -2
  311. package/src/scripts/hooks/push_settle_hook.ts +1 -3
  312. package/src/scripts/hooks/replay_hook.ts +2 -1
  313. package/src/scripts/hooks/reread_guard_hook.ts +1 -2
  314. package/src/scripts/hooks/review_baseline_hook.ts +1 -2
  315. package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
  316. package/src/scripts/hooks/rule_inject_hook.ts +55 -4
  317. package/src/scripts/hooks/run_continuation_hook.ts +30 -35
  318. package/src/scripts/hooks/skill_route_hook.ts +1 -2
  319. package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
  320. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
  321. package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
  322. package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
  323. package/src/scripts/hooks/table_fingerprint.ts +49 -0
  324. package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
  325. package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
  326. package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
  327. package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
  328. package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
  329. package/src/scripts/hooks_doctor.ts +85 -0
  330. package/src/scripts/hooks_status.ts +19 -0
  331. package/src/scripts/install.ts +109 -210
  332. package/src/scripts/janitor.ts +7 -0
  333. package/src/scripts/lint_decision_classes.ts +464 -0
  334. package/src/scripts/lint_deny_text.ts +419 -0
  335. package/src/scripts/lint_exit_codes.ts +262 -0
  336. package/src/scripts/lint_hook_manifest.ts +66 -0
  337. package/src/scripts/lint_major_migration_sections.ts +450 -0
  338. package/src/scripts/lint_plan_risk_register.ts +47 -3
  339. package/src/scripts/lint_roadmap_blockers.ts +139 -12
  340. package/src/scripts/lint_roadmap_producers.ts +202 -0
  341. package/src/scripts/lint_skill_descriptions.ts +1 -1
  342. package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
  343. package/src/scripts/mcp_server/prompts.ts +142 -1
  344. package/src/scripts/measure_rule_budget.ts +101 -5
  345. package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
  346. package/src/scripts/measure_turn_end_gate.ts +193 -0
  347. package/src/scripts/memory_lookup.ts +1 -1
  348. package/src/scripts/new_roadmap.ts +37 -0
  349. package/src/scripts/orchestration_record.ts +0 -5
  350. package/src/scripts/prepack-check.mjs +53 -0
  351. package/src/scripts/release.ts +14 -98
  352. package/src/scripts/release_drill.ts +267 -1
  353. package/src/scripts/release_publication.ts +64 -1
  354. package/src/scripts/report_enforcement_drift.ts +566 -0
  355. package/src/scripts/report_host_injection_effect.ts +3 -11
  356. package/src/scripts/report_invocation_surface.ts +383 -0
  357. package/src/scripts/report_obligation_carriers.ts +699 -0
  358. package/src/scripts/report_obligation_writer_split.ts +236 -0
  359. package/src/scripts/report_skill_menu_census.ts +208 -0
  360. package/src/scripts/report_standing_payload_by_host.ts +26 -152
  361. package/src/scripts/roadmap_verify_share.ts +213 -0
  362. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  363. package/src/scripts/schemas/command.schema.json +48 -0
  364. package/src/scripts/schemas/review-findings.schema.json +4 -0
  365. package/src/scripts/schemas/skill.schema.json +44 -0
  366. package/src/scripts/scope_growth.ts +219 -0
  367. package/src/scripts/self_review_gate.ts +359 -10
  368. package/src/scripts/semantic_parity_census.ts +314 -0
  369. package/src/scripts/session_eol_report.ts +54 -1
  370. package/src/scripts/sweep_dead_scan_roots.ts +1 -1
  371. package/src/scripts/trigger_eval_grandfather.json +0 -3
  372. package/src/scripts/trigger_eval_rotation.ts +122 -27
  373. package/src/scripts/ui_conformance_probe.ts +774 -0
  374. package/src/scripts/update_prices.ts +85 -3
  375. package/src/scripts/write_bundle_digest.mjs +74 -0
  376. package/src/server/schemas/settings.ts +62 -7
  377. package/src/shared/missionExecution.ts +126 -0
  378. package/src/templates/consumer-settings/ONBOARDING.md +1 -0
  379. package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
  380. package/dist/ui/assets/index-D0jaCRPM.js +0 -29
  381. package/dist/ui/assets/index-jSfMW8_t.css +0 -1
  382. /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
@@ -150,6 +150,21 @@ concerns:
150
150
  # Warn-in-context (exit 2), never blocks. Default-OFF: no-ops unless
151
151
  # hooks.injection_scan.enabled is true in .agent-settings.yml. The runtime
152
152
  # backstop layered on the always-on untrusted-input-defense rule.
153
+ #
154
+ # THE INPUT IT READS, AND WHY IT CANNOT MOVE TO A BLOCKING SLOT
155
+ # (road-to-a-content-scanner-on-a-slot-that-can-refuse 1.1). It scans the
156
+ # tool RESULT -- fetched pages, file reads, MCP/tool responses -- which is
157
+ # why it declares `result` below. `pre_tool_use` is the only slot on this
158
+ # host with a non-null `block_exit`, and a pre-call payload has no tool
159
+ # response in it: the `result` body class resolves to `tool_response` /
160
+ # `toolResponse` / `tool_result` / `toolUseResult` (hooks/payload_stub.ts),
161
+ # none of which exist before the call runs. So rebinding for the deny would
162
+ # leave the scanner reading the arguments it was never written to inspect --
163
+ # more enforcing-looking and strictly less detecting. It stays on
164
+ # `post_tool_use` and the published claims say `detected, not blocked`;
165
+ # `docs/enforcement-by-host.md` § "The slot rows are not a statement about
166
+ # any concern" carries the reasoning, and `check_enforcement_matrix` prints
167
+ # the per-host binding audit that keeps it from drifting back.
153
168
  injection-scan:
154
169
  script: src/scripts/injection_scan_hook.ts
155
170
  args: []
@@ -186,6 +201,27 @@ concerns:
186
201
  fail_closed: true
187
202
  severity: blocking
188
203
  needs_payload_bodies: [input]
204
+ # road-to-a-kernel-that-guards-its-plumbing 1.2 — the build-output half of
205
+ # "one mechanism per file class". PreToolUse guard: DENIES Write/Edit/
206
+ # NotebookEdit and the shell write shapes whose target is
207
+ # `dist/hooks/dispatch.js` (the esbuild bundle) or `hooks/hooks.json` (the
208
+ # host binding file). Both are GENERATED — by `npm run build:hooks` and by
209
+ # condense.ts via `task sync` — so a hand edit is never legitimate, and a
210
+ # hand-edited bundle reaches every dispatch while being invisible in a source
211
+ # review. The plumbing SOURCES beside them (hook_manifest.yaml,
212
+ # host_lowering.yaml, the *-dispatcher.sh trampolines, both budget files) are
213
+ # edited on purpose all the time and carry the ADR-268 § 4 ratification
214
+ # RECORD instead — check_kernel_edit_ratified, path set extended by step 1.1.
215
+ # The legitimate builds pass: they reach these files through a build tool,
216
+ # not through a redirect / in-place sed / tee / mv shape. fail_closed: true —
217
+ # the guarantee is about a DETECTED write; a malformed envelope with no
218
+ # target allows. No agent-accessible override; the fix is to rebuild.
219
+ block-plumbing-writes:
220
+ script: src/scripts/hooks/block_plumbing_writes.ts
221
+ args: []
222
+ fail_closed: true
223
+ severity: blocking
224
+ needs_payload_bodies: [input]
189
225
  # road-to-asked-not-parked Phase 5.1 — one question per structured-ask call.
190
226
  # PreToolUse guard: DENIES a call to a host's native question picker whose
191
227
  # payload carries more than one question. `ask-when-uncertain`'s Iron Law is
@@ -462,8 +498,9 @@ concerns:
462
498
  # `name === 'Grep' || name === 'Glob'` and `name === 'Read'`
463
499
  # (code_graph_context_hook.ts), and a test pins the manifest list against it.
464
500
  #
465
- # Deliberately NOT declared on the three blocking PreToolUse guards
466
- # (block-no-verify, block-kernel-rule-writes, block-config-weakening): their
501
+ # Deliberately NOT declared on the four blocking PreToolUse guards
502
+ # (block-no-verify, block-kernel-rule-writes, block-plumbing-writes,
503
+ # block-config-weakening): their
467
504
  # tool sets span host-specific naming variants (`Bash` / `BashTool` /
468
505
  # `launch-process` / `launch_process`, `str-replace-editor` /
469
506
  # `str_replace_editor` / `save-file` / …), so a list that misses one variant
@@ -867,11 +904,18 @@ concerns:
867
904
  # pointer-shaped rule delivery is the 36.2 %-against-48 % run at
868
905
  # docs/CLAIMS.md:188-189.
869
906
  #
870
- # DEFAULT OFF, and off means ZERO bytes: the concern returns before reading
871
- # `dist/router.json` unless `lean_projection.mode: delivery`. Under every
872
- # shipped default it emits nothing, so its `hook-token-budget.json` row is
873
- # registered against the derived per-prompt cap rather than a measured
874
- # emission — there is no measured emission yet, and saying so is the point.
907
+ # SHIPPED ON FOR CLAUDE CODE SINCE ADR-267 — corrected 2026-09-12, because
908
+ # this comment still read "DEFAULT OFF … under every shipped default it emits
909
+ # nothing … there is no measured emission yet" after its twin in
910
+ # `hooks/rule_inject_hook.ts` had already been corrected. The shipped template
911
+ # carries `lean_projection.mode: delivery` with `hosts: [claude-code]`, and
912
+ # the emission IS measured: p50 6,674 B, p90 16,188 B, max 20,406 B over 318
913
+ # gate-open fires on the frozen corpus, which is the distribution the
914
+ # `user_prompt_submit` raise in `hook-token-budget.json` is derived from.
915
+ #
916
+ # OFF STILL MEANS ZERO BYTES: the concern returns before reading
917
+ # `dist/router.json` unless the resolved (mode, hosts) pair thins THIS host —
918
+ # so on any other host, and on a `mode: eager-all` rollback, it emits nothing.
875
919
  #
876
920
  # DELIBERATELY NOT nudge-class: it carries no `nudge_rank`. A rule body is not
877
921
  # a hint the model may ignore, so the one-ranked-nudge-per-slot exclusivity
@@ -904,6 +948,19 @@ concerns:
904
948
  # future blocking decision has a measured distribution to cite. Never
905
949
  # blocks the turn — see the hook's file header for the `host_semantics.
906
950
  # emitFor` proof. fail_closed: false.
951
+ # The reader the delivered-obligation record never had. SHADOW ONLY: it
952
+ # computes the verdict an armed detector would reach, records one row per
953
+ # missing SET, and returns allow on every path — arming is gated on a bar
954
+ # pre-registered in docs/CLAIMS.md before any code able to refuse exists.
955
+ # advisory + fail_closed: false, and both are load-bearing rather than
956
+ # conventional: this estate already carries one BLOCKING turn-end concern,
957
+ # and the first-ranked risk against this lane is that a second one refuses a
958
+ # clean turn and an operator disables the carrier for good.
959
+ obligation-settle:
960
+ script: src/scripts/hooks/obligation_settle_hook.ts
961
+ args: []
962
+ fail_closed: false
963
+ severity: advisory
907
964
  end-review-nudge:
908
965
  script: src/scripts/hooks/end_review_nudge_hook.ts
909
966
  args: []
@@ -1043,6 +1100,41 @@ concerns:
1043
1100
  # Provable from the hook's own source: READ_TOOLS ∪ WRITE_TOOLS.
1044
1101
  tools: [Read, Edit, Write]
1045
1102
  needs_payload_bodies: [input]
1103
+ # Memory recall — delivers a memory the store ALREADY holds, at the moment
1104
+ # the tool call it is about would run. The finding: in one long autonomous
1105
+ # run the agent walked into four traps that each had a memory file written
1106
+ # weeks earlier, and the store's own index records "of eleven error classes
1107
+ # in one session, nine had a file here already and all nine were read AFTER
1108
+ # the failure". `memory_lookup` is a CLI nothing calls automatically, so
1109
+ # recall is model-carried and fails exactly when the agent is busy.
1110
+ #
1111
+ # A COMMITTED TRIGGER TABLE, never a similarity score: `RECALL_RULES` maps
1112
+ # one precisely-decidable payload condition to one memory slug, so the floor
1113
+ # is a predicate rather than a threshold and every row is replayed against a
1114
+ # real payload in tests. A fuzzy match over 467 memory files would fire on
1115
+ # most calls and be tuned out within a day.
1116
+ #
1117
+ # ADVISORY ONLY — binds on the blocking slot and never returns exit 1 on any
1118
+ # path; a stale trigger row must never deny work. Delivery mirrors
1119
+ # `reread-guard` (exit-2 warn, host-facing exit 0 on the verified `claude`
1120
+ # platform). ONE memory per fire, pointer + the memory's own description,
1121
+ # hard-capped at 768 bytes (registered BELOW the 1024 default in
1122
+ # hook-token-budget.json so a later change that injects memory BODIES fails
1123
+ # the build). Per-session per-slug latch under
1124
+ # agents/runtime/state/memory-recall/ — slug keys and timestamps only, no
1125
+ # field able to hold a path or a command. The silent path does zero fs work.
1126
+ # An unresolvable store emits the pointer anyway plus a stderr diagnostic:
1127
+ # an absent store and a store with no match are different answers.
1128
+ # fail_closed: false.
1129
+ memory-recall:
1130
+ script: src/scripts/hooks/memory_recall_hook.ts
1131
+ args: []
1132
+ fail_closed: false
1133
+ severity: advisory
1134
+ # Provable from the hook's own source: the union of the `tools` sets on
1135
+ # RECALL_RULES — COMMAND_TOOLS (rules 1 and 4) ∪ EDIT_TOOLS (rules 2, 3).
1136
+ tools: [Bash, Edit, Write, MultiEdit, str-replace-editor, save-file, launch-process]
1137
+ needs_payload_bodies: [input]
1046
1138
  # road-to-token-economy-recycling Phases 1.1 / 3.2 / 4.2 — session
1047
1139
  # end-of-life instrument + recycle advisory. Every Stop, incrementally
1048
1140
  # folds the transcript's newly appended lines (payload `transcript_path`,
@@ -1311,17 +1403,35 @@ roles:
1311
1403
  drop: [delegation-nudge, skill-route, end-review-nudge, review-baseline, council-availability, team-review-gate, self-repair, session-eol, interruption-ledger]
1312
1404
 
1313
1405
  platforms:
1406
+ # `ask:` — the SHAPE of this host's user-facing question primitive.
1407
+ #
1408
+ # native the host exposes a structured-ask tool the agent can call, and
1409
+ # an owner-owned question uses it, carrying the recommendation as
1410
+ # its default option.
1411
+ # text no such primitive; the numbered text block is the ask, and it
1412
+ # keeps its own Iron Law.
1413
+ #
1414
+ # DECLARED, NOT PROBED, and the difference matters: this row records what the
1415
+ # host's documented tool surface offers, and nothing here verifies it at
1416
+ # runtime. A host declaring `native` whose primitive turns out to be absent
1417
+ # degrades to the text block — the same output either way — so the risk of a
1418
+ # wrong row is a missed upgrade, never a broken ask.
1419
+ #
1420
+ # It exists so the shape is a row `hooks:status` prints rather than an
1421
+ # assumption a contract makes on every host's behalf.
1314
1422
  augment:
1423
+ ask: text
1315
1424
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
1316
1425
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1317
1426
  stop: [chat-history, verify-before-complete, self-repair, session-register, roadmap-progress, design-pass-stop]
1318
- pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-config-weakening, block-speaking-inbox-dir, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard]
1427
+ pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-plumbing-writes, block-config-weakening, block-speaking-inbox-dir, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard, memory-recall]
1319
1428
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, push-settle, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape, comment-discipline, design-pass]
1320
1429
 
1321
1430
  claude:
1431
+ ask: native
1322
1432
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register, review-baseline, journal-record]
1323
1433
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush, journal-record]
1324
- stop: [chat-history, verify-before-complete, team-review-gate, end-review-nudge, turn-end-gate, self-repair, session-register, session-eol, interruption-ledger, roadmap-progress, design-pass-stop, suggestion-capture, journal-record, run-continuation]
1434
+ stop: [chat-history, verify-before-complete, team-review-gate, end-review-nudge, obligation-settle, turn-end-gate, self-repair, session-register, session-eol, interruption-ledger, roadmap-progress, design-pass-stop, suggestion-capture, journal-record, run-continuation]
1325
1435
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, delegation-nudge, skill-route, git-authorization, session-canary, self-repair, session-register, rule-inject, suggestion-capture, journal-record]
1326
1436
  # `rule-inject` was bound here and is not any more
1327
1437
  # (road-to-delivery-for-every-host 3.2, owner ruling E2). E2's strategy is
@@ -1346,7 +1456,7 @@ platforms:
1346
1456
  # (+16,329 GPT tok, +69 % on the thin rule layer) are in
1347
1457
  # `project_thin_rules.path_only_ids`; the receiver is
1348
1458
  # `agents/roadmaps/stubs/road-to-a-path-route-under-delivery.md`.
1349
- pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-config-weakening, block-speaking-inbox-dir, one-question-per-ask, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate]
1459
+ pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-plumbing-writes, block-config-weakening, block-speaking-inbox-dir, one-question-per-ask, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate, memory-recall]
1350
1460
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, push-settle, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape, comment-discipline, language-mirror, design-pass, journal-record]
1351
1461
  # `language-mirror` — round-5 § 6.1. `pre_compact` sets a pin-lost marker;
1352
1462
  # the `post_tool_use` entry above re-emits the pin exactly once while that
@@ -1378,6 +1488,7 @@ platforms:
1378
1488
  # Decision matrix + upstream blockers tracked in
1379
1489
  # agents/settings/contexts/chat-history-platform-hooks.md § Cowork.
1380
1490
  cowork:
1491
+ ask: text
1381
1492
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
1382
1493
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1383
1494
  # end-review-nudge and delegation-nudge are DELIBERATELY NOT bound on
@@ -1392,7 +1503,7 @@ platforms:
1392
1503
  # still costs the dispatch, and its entire output is thrown away.
1393
1504
  stop: [chat-history, verify-before-complete, self-repair, session-register, roadmap-progress, design-pass-stop]
1394
1505
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
1395
- pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-config-weakening, block-speaking-inbox-dir, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate]
1506
+ pre_tool_use: [block-no-verify, evidence-independence, block-kernel-rule-writes, block-plumbing-writes, block-config-weakening, block-speaking-inbox-dir, rtk-wrap, design-slop, ui-route-nudge, code-graph-context, chain-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate, memory-recall]
1396
1507
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, push-settle, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape, comment-discipline, design-pass]
1397
1508
  # subagent-ledger IS bound here, unlike the two nudges above, and the
1398
1509
  # difference is the delivery channel rather than the platform: the nudges
@@ -1413,6 +1524,7 @@ platforms:
1413
1524
  # covers a session where it does not. See
1414
1525
  # agents/settings/contexts/chat-history-platform-hooks.md § Cursor.
1415
1526
  cursor:
1527
+ ask: text
1416
1528
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
1417
1529
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1418
1530
  stop: [chat-history, verify-before-complete, self-repair, session-register, roadmap-progress, design-pass-stop]
@@ -1428,6 +1540,7 @@ platforms:
1428
1540
  # both map to session_start. TaskCancel maps to stop because the
1429
1541
  # session is interrupted with partial state (mirrors Augment Stop).
1430
1542
  cline:
1543
+ ask: text
1431
1544
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
1432
1545
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1433
1546
  stop: [chat-history, verify-before-complete, self-repair, session-register, roadmap-progress, design-pass-stop]
@@ -1447,6 +1560,7 @@ platforms:
1447
1560
  # surface to record verification commands; documented limitation).
1448
1561
  # minimal-safe-diff is omitted entirely on Windsurf for the same reason.
1449
1562
  windsurf:
1563
+ ask: text
1450
1564
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, profile-staleness, wrapper-freshness, surface-probe, session-register]
1451
1565
  stop: [chat-history, verify-before-complete, self-repair, session-register, design-pass-stop]
1452
1566
  # language-mirror IS bound here: unlike minimal-safe-diff it needs only the
@@ -1464,6 +1578,7 @@ platforms:
1464
1578
  # turn-check semantics. AfterAgent fires when the agent loop ends
1465
1579
  # — this is our `stop` slot.
1466
1580
  gemini:
1581
+ ask: text
1467
1582
  session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register]
1468
1583
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1469
1584
  stop: [chat-history, verify-before-complete, self-repair, session-register, roadmap-progress, design-pass-stop]
@@ -1474,6 +1589,7 @@ platforms:
1474
1589
  # there. Concerns route through rule-only fallback; the dispatcher silently
1475
1590
  # no-ops on --platform copilot.
1476
1591
  copilot:
1592
+ ask: text
1477
1593
  fallback_only: true
1478
1594
 
1479
1595
  # Native-event → agent-config-event translation table. Used by per-platform
@@ -45,14 +45,45 @@ 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
+ import * as yaml from 'js-yaml';
49
+
50
+ import {
51
+ buildSettingsClassIndex,
52
+ classOfPath,
53
+ parseSettingsClassRows,
54
+ type SettingsClass,
55
+ } from '../../shared/settingsClasses.js';
48
56
  import { EDIT_TOOLS } from '../minimal_safe_diff_hook.js';
49
57
  import { readHookStdin } from './hook_stdin.js';
58
+ import { EXIT_ALLOW, EXIT_BLOCK, EXIT_WARN } from './exit_codes.js';
50
59
 
51
60
  const _HERE = fileURLToPath(import.meta.url);
52
61
 
53
- const EXIT_ALLOW = 0;
54
- const EXIT_BLOCK = 1;
55
- const EXIT_WARN = 2;
62
+ /**
63
+ * This package's own root — where the shipped class contract lives.
64
+ *
65
+ * RESOLVED BY SEARCH, not by counting `..` segments. This file runs from three
66
+ * layouts: the source tree, an installed `node_modules/@event4u/agent-config/`,
67
+ * and inlined into `dist/hooks/dispatch.js`, where every module shares the
68
+ * BUNDLE's `import.meta.url` and a fixed hop count lands somewhere else
69
+ * entirely. A review flagged the fixed-hop version as resolving a different
70
+ * root from the one the package occupies; walking up to the nearest directory
71
+ * that actually carries the contract answers the question the guard is asking
72
+ * ("where is the contract") instead of a proxy for it.
73
+ *
74
+ * `null` when no ancestor carries it — which is the fail-closed input to
75
+ * `readClassIndex`, not a silent default root.
76
+ */
77
+ export function findPackageRoot(from: string): string | null {
78
+ let dir = path.dirname(from);
79
+ for (let i = 0; i < 12; i++) {
80
+ if (fs.existsSync(path.join(dir, SETTINGS_CLASSES_RELATIVE))) return dir;
81
+ const up = path.dirname(dir);
82
+ if (up === dir) break;
83
+ dir = up;
84
+ }
85
+ return null;
86
+ }
56
87
 
57
88
  /**
58
89
  * The stated cap from `autonomous-execution` § Antipattern. Crossing it in one
@@ -81,7 +112,18 @@ const _PATH_KEYS: readonly string[] = ['file_path', 'path', 'target_file', 'file
81
112
  * where "weakening" needs context a tool call does not carry (a baseline count
82
113
  * may legitimately reset after a refactor); those warn and never block.
83
114
  */
84
- export type ConfigKind = 'allowlist' | 'advisory' | null;
115
+ export type ConfigKind = 'allowlist' | 'advisory' | 'class-c' | null;
116
+
117
+ /**
118
+ * Project settings files whose keys this guard fences. User-global files are
119
+ * never in reach — the hook only sees a path a tool call named, and the writes
120
+ * that reach `~/.event4u/` go through `settings:set`, which applies its own
121
+ * per-key refusal.
122
+ */
123
+ const CLASS_C_BASENAMES: readonly string[] = ['.agent-settings.yml', '.agent-settings.yaml', 'settings.json'];
124
+
125
+ /** Where the class contract lives, relative to the package root. */
126
+ export const SETTINGS_CLASSES_RELATIVE = 'docs/contracts/settings-classes.md';
85
127
 
86
128
  /** Classify a target path into a config surface, or null for everything else. */
87
129
  export function classify_target(p: string): ConfigKind {
@@ -96,9 +138,100 @@ export function classify_target(p: string): ConfigKind {
96
138
  if (/-budget(s)?\.(json|ya?ml)$/.test(base) || base === 'budgets.yml') {
97
139
  return 'advisory';
98
140
  }
141
+ // `settings.json` only under a host directory: the bare name is common
142
+ // enough elsewhere that classifying it on the basename alone would fence
143
+ // files that carry no settings key at all.
144
+ if (base === 'settings.json') {
145
+ return /(^|\/)\.(claude|cursor|augment|windsurf)\/settings\.json$/.test(posix) ? 'class-c' : null;
146
+ }
147
+ if (CLASS_C_BASENAMES.includes(base)) {
148
+ return 'class-c';
149
+ }
99
150
  return null;
100
151
  }
101
152
 
153
+ /**
154
+ * Leaf key paths of a parsed settings document, dotted.
155
+ *
156
+ * LEAVES ONLY, and the choice is load-bearing in both directions. A class-C key
157
+ * whose value is a MAP (`hooks`, say) has children that never appear as their
158
+ * own rows in the contract, so emitting only leaves and letting `classOfPath`
159
+ * walk up to the nearest classified ancestor is what makes `hooks.enabled`
160
+ * resolve to C. Emitting the interior nodes as well would double-count the same
161
+ * change and report a parent that nothing edited.
162
+ */
163
+ export function leafPaths(value: unknown, prefix = ''): Map<string, string> {
164
+ const out = new Map<string, string>();
165
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
166
+ if (prefix !== '') out.set(prefix, JSON.stringify(value ?? null));
167
+ return out;
168
+ }
169
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
170
+ const key = prefix === '' ? k : `${prefix}.${k}`;
171
+ for (const [ck, cv] of leafPaths(v, key)) out.set(ck, cv);
172
+ }
173
+ return out;
174
+ }
175
+
176
+ /**
177
+ * Parse a settings document, or `null` when it is not parseable.
178
+ *
179
+ * `null` is NOT "no keys changed" — the caller treats it as an unreadable
180
+ * corpus and refuses, because a guard that allows whatever it cannot parse is
181
+ * bypassed by making the file unparseable for one call.
182
+ */
183
+ export function parseSettingsDoc(text: string, rel: string): unknown | null {
184
+ try {
185
+ return rel.endsWith('.json') ? (JSON.parse(text) as unknown) : yaml.load(text);
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+
191
+ /** Dotted paths whose value differs between two settings documents. */
192
+ export function changedKeys(before: unknown, after: unknown): string[] {
193
+ const a = leafPaths(before);
194
+ const b = leafPaths(after);
195
+ const out = new Set<string>();
196
+ for (const [k, v] of b) if (a.get(k) !== v) out.add(k);
197
+ for (const k of a.keys()) if (!b.has(k)) out.add(k);
198
+ return [...out].sort();
199
+ }
200
+
201
+ /**
202
+ * The class index, read from the shipped contract. `null` when it cannot be
203
+ * read AT ALL, which the caller treats as fail-closed.
204
+ *
205
+ * Not cached: the file is small, and a cache would keep a stale fence alive
206
+ * across an upgrade that reclassified a key — the same reasoning the server's
207
+ * own reader states for the same file.
208
+ */
209
+ export function readClassIndex(packageRoot: string): Map<string, SettingsClass> | null {
210
+ try {
211
+ const text = fs.readFileSync(path.join(packageRoot, SETTINGS_CLASSES_RELATIVE), 'utf-8');
212
+ const index = buildSettingsClassIndex(parseSettingsClassRows(text));
213
+ return index.size === 0 ? null : index;
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * The guarded subset of a change list.
221
+ *
222
+ * A key is guarded when its nearest classified ancestor is C, and ALSO when
223
+ * nothing on its path is classified at all — the same per-key refusal
224
+ * `settings:set` and the GUI write route already apply, reached here through
225
+ * the same shared classifier rather than a second copy of the rule.
226
+ */
227
+ export function guardedKeys(index: ReadonlyMap<string, SettingsClass> | null, keys: readonly string[]): string[] {
228
+ if (index === null) return [...keys];
229
+ return keys.filter((k) => {
230
+ const cls = classOfPath(index, k);
231
+ return cls === 'C' || cls === undefined;
232
+ });
233
+ }
234
+
102
235
  /**
103
236
  * Count allowlist entries in a blob.
104
237
  *
@@ -167,6 +300,89 @@ export interface Decision {
167
300
  }
168
301
 
169
302
  /** Decide from the cumulative session total for one allowlist file. */
303
+ /**
304
+ * The refusal reason for a class-C settings edit, or `null` to allow.
305
+ *
306
+ * WHAT THIS FENCES, and what it deliberately does not. The unit is the KEY, not
307
+ * the file: a Class A key stays agent-writable through this path exactly as it
308
+ * is through `settings:set`, and fencing the whole file would make an ordinary
309
+ * preference edit need a human. Only a key the contract classifies C — or one
310
+ * nothing on its path classifies at all, which is the same fail-closed default
311
+ * the CLI writer applies — is refused.
312
+ *
313
+ * THE UNPARSEABLE CASES REFUSE, both of them, and for the same reason. A
314
+ * contract this guard cannot read leaves it with no way to tell a C key from an
315
+ * A key; a resulting document it cannot parse leaves it with no key list at
316
+ * all. Allowing either would mean the fence is lifted by making one file
317
+ * unreadable for the length of one tool call, which is a bypass with no
318
+ * authorisation step in it.
319
+ *
320
+ * WHAT IT CANNOT SEE: an edit applied through a shell redirect rather than an
321
+ * edit tool — `EDIT_TOOLS` is the corpus, and the shell shapes are
322
+ * `block_plumbing_writes`' subject, not this one.
323
+ */
324
+ export function classCVerdict(
325
+ ti: JsonObject,
326
+ on_disk: string | null,
327
+ rel_path: string,
328
+ index: ReadonlyMap<string, SettingsClass> | null,
329
+ ): string | null {
330
+ const before = on_disk === null ? null : parseSettingsDoc(on_disk, rel_path);
331
+
332
+ const content = ti['content'];
333
+ const oldStr = ti['old_string'];
334
+ const newStr = ti['new_string'];
335
+ let afterText: string | null = null;
336
+ if (typeof content === 'string') {
337
+ afterText = content;
338
+ } else if (typeof oldStr === 'string' && typeof newStr === 'string' && on_disk !== null) {
339
+ if (!on_disk.includes(oldStr)) return null; // the edit will not apply
340
+ // `replace_all` is the host's own flag and it changes WHICH text the
341
+ // edit produces. Modelling only the first occurrence let a
342
+ // `replace_all` edit whose SECOND occurrence is the Class C one pass
343
+ // this guard while the real edit changed it — the guard evaluating a
344
+ // different edit from the one executed. Reported by an independent
345
+ // review before this landed.
346
+ const all = ti['replace_all'];
347
+ if (all !== undefined && typeof all !== 'boolean') {
348
+ return (
349
+ `${rel_path}: this edit carries a \`replace_all\` value the guard cannot ` +
350
+ 'interpret, so the text it would produce is unknown and no key can be ' +
351
+ 'cleared. Re-send it as a plain edit, or write the change through ' +
352
+ '`agent-config settings:set`.'
353
+ );
354
+ }
355
+ afterText = all === true ? on_disk.split(oldStr).join(newStr) : on_disk.replace(oldStr, newStr);
356
+ }
357
+ if (afterText === null) return null;
358
+
359
+ const after = parseSettingsDoc(afterText, rel_path);
360
+ if (after === null) {
361
+ return (
362
+ `${rel_path}: the result of this edit does not parse as settings, so no key ` +
363
+ 'list can be derived and no key can be cleared. Fix the syntax, or write the ' +
364
+ 'change through `agent-config settings:set`, which refuses a Class C key by name.'
365
+ );
366
+ }
367
+
368
+ const guarded = guardedKeys(index, changedKeys(before, after));
369
+ if (guarded.length === 0) return null;
370
+
371
+ const unreadable =
372
+ index === null
373
+ ? ' The class contract could not be read, so EVERY changed key is treated as ' +
374
+ 'guarded — that is the fail-closed direction, not a classification.'
375
+ : '';
376
+ return (
377
+ `${rel_path}: this edit changes ${String(guarded.length)} guarded settings key(s) — ` +
378
+ `${guarded.slice(0, 5).join(', ')}${guarded.length > 5 ? ', …' : ''}.` +
379
+ unreadable +
380
+ ' A Class C key is a policy dial (spend ceiling, allowlist, gate switch, hooks) and ' +
381
+ 'is a human edit by contract: docs/contracts/settings-classes.md. Every other key in ' +
382
+ 'this file stays writable.'
383
+ );
384
+ }
385
+
170
386
  export function decide(kind: ConfigKind, rel_path: string, session_total: number, added: number): Decision {
171
387
  if (kind === null || added <= 0) {
172
388
  return { action: 'allow', reason: '' };
@@ -292,6 +508,15 @@ export function main(): number {
292
508
  } catch {
293
509
  on_disk = null;
294
510
  }
511
+ if (kind === 'class-c') {
512
+ const rel_c = path.relative(root, abs) || p;
513
+ const pkg = findPackageRoot(_HERE);
514
+ const verdict = classCVerdict(ti, on_disk, rel_c, pkg === null ? null : readClassIndex(pkg));
515
+ if (verdict === null) continue;
516
+ process.stderr.write(`block-config-weakening: BLOCKED — ${verdict}\n`);
517
+ return EXIT_BLOCK;
518
+ }
519
+
295
520
  const added = added_entries(ti, on_disk);
296
521
  if (added <= 0) {
297
522
  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
  /**