@event4u/agent-config 14.22.0 → 14.23.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 (111) 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 +159 -380
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/pr/merge.md +47 -38
  7. package/dist/agent-src/commands/roadmap/next.md +8 -0
  8. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
  9. package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
  10. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
  11. package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -1
  12. package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
  13. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
  14. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  15. package/dist/cli/registry.js +1 -1
  16. package/dist/cli-delegate/archive_completed_roadmaps.js +52 -4
  17. package/dist/cli-delegate/{chunk-TGIPZBCW.js → chunk-2KKXJW25.js} +22 -8
  18. package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
  19. package/dist/cli-delegate/{chunk-BIXEDQPZ.js → chunk-KCNTUSPI.js} +1 -1
  20. package/dist/cli-delegate/cmd_export.js +2 -2
  21. package/dist/cli-delegate/cmd_handoff.js +20 -12
  22. package/dist/cli-delegate/cmd_prune.js +2 -2
  23. package/dist/cli-delegate/cmd_session_recycle.js +3 -13
  24. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  25. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  26. package/dist/cli-delegate/cmd_sync.js +2 -2
  27. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  28. package/dist/cli-delegate/cmd_validate.js +2 -2
  29. package/dist/discovery/deprecation-report.md +1 -1
  30. package/dist/discovery/discovery-manifest.json +8 -8
  31. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  32. package/dist/discovery/discovery-manifest.summary.md +1 -1
  33. package/dist/discovery/dormancy-report.md +1 -1
  34. package/dist/discovery/orphan-report.md +1 -1
  35. package/dist/discovery/packs.json +2 -2
  36. package/dist/discovery/trust-report.md +1 -1
  37. package/dist/discovery/workspaces.json +2 -2
  38. package/dist/hooks/dispatch.js +880 -289
  39. package/dist/install/install.mjs +15 -3
  40. package/dist/mcp/registry-manifest.json +2 -2
  41. package/dist/mcp/server.json +2 -2
  42. package/dist/mcp/server.mjs +5458 -2453
  43. package/dist/scripts/_lib/lean_projection_mode.js +108 -0
  44. package/dist/scripts/_lib/router_match.js +243 -0
  45. package/dist/scripts/_lib/rule_injection.js +216 -0
  46. package/dist/scripts/condense.js +20 -20
  47. package/dist/scripts/project_thin_rules.js +139 -19
  48. package/dist/server/schemas/settings.js +16 -3
  49. package/docs/MIGRATION.md +56 -0
  50. package/docs/contracts/settings-classes.md +39 -7
  51. package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
  52. package/llms.txt +1 -1
  53. package/package.json +1 -1
  54. package/src/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
  55. package/src/config/agent-settings.template.yml +47 -1
  56. package/src/config/continuity-surface.json +7 -0
  57. package/src/config/evaluator-budgets.json +4 -4
  58. package/src/config/gate-violation-baselines.json +2 -2
  59. package/src/config/hook-token-budget.json +7 -6
  60. package/src/config/packed-binary-manifest.json +1 -1
  61. package/src/config/routing-coverage-seed.json +9 -2
  62. package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
  63. package/src/scripts/_cli/handoff_sessions.ts +14 -44
  64. package/src/scripts/_lib/continuity_slot.ts +295 -0
  65. package/src/scripts/_lib/continuity_writer.ts +253 -0
  66. package/src/scripts/_lib/hook_settings.ts +85 -0
  67. package/src/scripts/_lib/lean_projection_mode.ts +127 -0
  68. package/src/scripts/_lib/packed_binary_predicate.ts +37 -0
  69. package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
  70. package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
  71. package/src/scripts/_lib/session_eol.ts +44 -0
  72. package/src/scripts/_lib/session_index_trust.ts +346 -0
  73. package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
  74. package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
  75. package/src/scripts/audit_mcp_tools.ts +69 -25
  76. package/src/scripts/build_mcp_catalog.ts +24 -1
  77. package/src/scripts/check_host_tree_parity.ts +291 -0
  78. package/src/scripts/check_preamble_payload_budget.ts +136 -7
  79. package/src/scripts/check_rule_projection_integrity.ts +102 -2
  80. package/src/scripts/ci_settle.ts +205 -5
  81. package/src/scripts/code_graph/build.ts +79 -0
  82. package/src/scripts/code_graph/cli.ts +258 -7
  83. package/src/scripts/code_graph/detect.ts +38 -0
  84. package/src/scripts/code_graph/query.ts +25 -2
  85. package/src/scripts/code_graph/sqlite_store.ts +16 -1
  86. package/src/scripts/code_graph/types.ts +52 -3
  87. package/src/scripts/code_graph/validate.ts +3 -1
  88. package/src/scripts/code_graph/verbs.ts +590 -0
  89. package/src/scripts/condense.ts +20 -20
  90. package/src/scripts/handoff_context_hook.ts +39 -1
  91. package/src/scripts/hook_manifest.json +1 -1
  92. package/src/scripts/hook_manifest.yaml +24 -1
  93. package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
  94. package/src/scripts/hooks/code_graph_context_hook.ts +7 -18
  95. package/src/scripts/hooks/push_settle_hook.ts +3 -2
  96. package/src/scripts/hooks/rule_inject_hook.ts +107 -20
  97. package/src/scripts/hooks/session_eol_hook.ts +120 -2
  98. package/src/scripts/hot_context_hook.ts +62 -7
  99. package/src/scripts/lint_roadmap_blockers.ts +164 -1
  100. package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
  101. package/src/scripts/mcp_server/graph_tools.ts +294 -0
  102. package/src/scripts/mcp_server/path_util.ts +31 -0
  103. package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
  104. package/src/scripts/mcp_server/tools.ts +3 -13
  105. package/src/scripts/model_rule_injection.ts +61 -4
  106. package/src/scripts/project_thin_rules.ts +149 -16
  107. package/src/scripts/report_standing_payload_by_host.ts +650 -0
  108. package/src/scripts/schemas/agent-settings.schema.json +7 -1
  109. package/src/scripts/session_memory_index.ts +81 -8
  110. package/src/server/schemas/settings.ts +23 -4
  111. package/src/vendor/grammars/README.md +27 -2
@@ -157,6 +157,12 @@ export function consume_handoff_context(root: string, now: Date = new Date()): C
157
157
  * - drift (Phase 3.2): the envelope's recorded repo identity + branch +
158
158
  * HEAD are compared against the tree it lands in, and any mismatch LEADS
159
159
  * the injected block. Never a silent stale resume;
160
+ * - the BRANCH GATE (2026-09-09): a branch mismatch is a refusal rather than
161
+ * an annotation, and the refusal is `absent` so the record is left for a
162
+ * session on the right branch instead of consumed. It is the half of the
163
+ * resolution rule that keeps an unrelated session in the same checkout from
164
+ * resuming an ended peer, now that the resolver admits any record that is
165
+ * not the reader's own. Unknown on either side is not a mismatch;
160
166
  * - focus (Phase 3.4): `AGENT_RESUME_FOCUS` narrows what the successor
161
167
  * attacks first — the consumer-side mirror of `next_task`.
162
168
  */
@@ -262,7 +268,39 @@ export function consume_recycle_envelope(
262
268
 
263
269
  // Drift first: a stale resume against the wrong tree is the failure the
264
270
  // reader must see before anything else in the block.
265
- const drift = describeDrift(envelope, collectRepoAnchor(root));
271
+ const anchor = collectRepoAnchor(root);
272
+
273
+ // The branch gate — an ADMISSION check, not an annotation, and the half of
274
+ // the 2026-09-09 resolution rule that lives outside the resolver.
275
+ //
276
+ // Why it is needed at all: the resolver now admits the unique record that is
277
+ // not the reader's own, so peer isolation no longer comes from the filename.
278
+ // Without this, an unrelated session in the same checkout would resume an
279
+ // ended peer's work simply by being the next one to start. AI council
280
+ // 2026-09-09 was explicit that (d) is acceptable WITH this gate and not
281
+ // without it.
282
+ //
283
+ // `absent`, deliberately, and this is the one place the invariant matters:
284
+ // every other outcome consumes the record (moved, not copied), and consuming
285
+ // here would destroy state the rightful successor could still use. `absent`
286
+ // is the only outcome that leaves the file alone, and the 48-hour staleness
287
+ // guard bounds how long it can sit unclaimed.
288
+ //
289
+ // Unknown on either side is NOT a mismatch. An envelope written before the
290
+ // field existed, or a tree whose branch cannot be read, must not be refused
291
+ // on a comparison neither side can make — that would turn a missing field
292
+ // into a broken resume. `describeDrift` still annotates those.
293
+ const envelopeBranch = String((envelope as Record<string, unknown>)['branch'] ?? '').trim();
294
+ if (envelopeBranch !== '' && anchor.branch !== null && envelopeBranch !== anchor.branch) {
295
+ return {
296
+ action: 'absent',
297
+ reason:
298
+ `recycle envelope is for branch "${envelopeBranch}" and this tree is on ` +
299
+ `"${anchor.branch}" — left unclaimed for a session on that branch rather than consumed`,
300
+ };
301
+ }
302
+
303
+ const drift = describeDrift(envelope, anchor);
266
304
  // Proposal fields carrying an imperative LEAD the block as a stop
267
305
  // notice — surfaced, never executed, never silently stripped.
268
306
  const warnings = scanEnvelopeDirectives(envelope);
@@ -1 +1 @@
1
- {"fingerprint":"5d55db9c:88593","manifest":{"schema_version":1,"concerns":{"chat-history":{"script":"src/scripts/chat_history.ts","args":["hook-dispatch"],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["result"]},"hot-context":{"script":"src/scripts/hot_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"handoff-context":{"script":"src/scripts/handoff_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"roadmap-progress":{"script":"src/scripts/roadmap_progress_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"onboarding-gate":{"script":"src/scripts/onboarding_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"context-hygiene":{"script":"src/scripts/context_hygiene_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"verify-before-complete":{"script":"src/scripts/before_complete_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"ship-diff-volume":{"script":"src/scripts/hooks/ship_diff_volume_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"minimal-safe-diff":{"script":"src/scripts/minimal_safe_diff_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"injection-scan":{"script":"src/scripts/injection_scan_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"block-no-verify":{"script":"src/scripts/hooks/block_no_verify.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"block-kernel-rule-writes":{"script":"src/scripts/hooks/block_kernel_rule_writes.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"one-question-per-ask":{"script":"src/scripts/hooks/one_question_per_ask_hook.ts","args":[],"fail_closed":false,"severity":"blocking","tools":["AskUserQuestion","AskQuestion","UserQuestion","ask_user_question","ask_question","user_question"],"needs_payload_bodies":["input"]},"block-speaking-inbox-dir":{"script":"src/scripts/hooks/block_speaking_inbox_dir.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"block-config-weakening":{"script":"src/scripts/hooks/block_config_weakening.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"rtk-wrap":{"script":"src/scripts/hooks/rtk_wrap_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-slop":{"script":"src/scripts/hooks/design_slop_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-pass":{"script":"src/scripts/hooks/design_pass_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-pass-stop":{"script":"src/scripts/hooks/design_pass_hook.ts","args":["--slot","stop"],"fail_closed":false,"severity":"advisory"},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"suggestion-capture":{"script":"src/scripts/hooks/suggestion_capture_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"chain-nudge":{"script":"src/scripts/hooks/chain_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"code-graph-context":{"script":"src/scripts/hooks/code_graph_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Grep","Glob","Read"],"needs_payload_bodies":["input"]},"memory-learn":{"script":"src/scripts/memory_learn_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"first-run-gate":{"script":"src/scripts/first_run_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"profile-staleness":{"script":"src/scripts/profile_staleness_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"wrapper-freshness":{"script":"src/scripts/wrapper_freshness_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"surface-probe":{"script":"src/scripts/surface_probe_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"team-review-gate":{"script":"src/scripts/team_review_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"pr-url-reminder":{"script":"src/scripts/pr_url_reminder_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"push-settle":{"script":"src/scripts/hooks/push_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"language-mirror":{"script":"src/scripts/language_mirror_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"git-authorization":{"script":"src/scripts/git_authorization_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"evidence-independence":{"script":"src/scripts/hooks/evidence_independence.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"session-canary":{"script":"src/scripts/session_canary_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"council-availability":{"script":"src/scripts/council_availability_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"telemetry-disclosure":{"script":"src/scripts/telemetry_disclosure_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"telemetry-flush":{"script":"src/scripts/hooks/telemetry_flush_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"self-repair":{"script":"src/scripts/self_repair_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"session-register":{"script":"src/scripts/session_register_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"orchestration-record":{"script":"src/scripts/hooks/orchestration_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Agent","Task"],"needs_payload_bodies":["input","result"]},"telemetry-usage":{"script":"src/scripts/hooks/telemetry_usage_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Skill"],"needs_payload_bodies":["input"]},"delegation-nudge":{"script":"src/scripts/hooks/delegation_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","nudge_rank":1},"skill-route":{"script":"src/scripts/hooks/skill_route_hook.ts","args":[],"fail_closed":false,"severity":"advisory","nudge_rank":2},"rule-inject":{"script":"src/scripts/hooks/rule_inject_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"end-review-nudge":{"script":"src/scripts/hooks/end_review_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","skip_on_refusal_retry":true},"turn-end-gate":{"script":"src/scripts/hooks/turn_end_gate_hook.ts","args":[],"fail_closed":false,"severity":"blocking"},"comment-discipline":{"script":"src/scripts/hooks/comment_discipline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Write","Edit"],"needs_payload_bodies":["input"]},"edit-shape":{"script":"src/scripts/hooks/edit_shape_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Write"],"needs_payload_bodies":["input"]},"reread-guard":{"script":"src/scripts/hooks/reread_guard_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Read","Edit","Write"],"needs_payload_bodies":["input"]},"session-eol":{"script":"src/scripts/hooks/session_eol_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"run-continuation":{"script":"src/scripts/hooks/run_continuation_hook.ts","args":[],"fail_closed":false,"severity":"blocking"},"interruption-ledger":{"script":"src/scripts/hooks/interruption_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","skip_on_refusal_retry":true},"subagent-ledger":{"script":"src/scripts/hooks/subagent_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"tool-result-bytes":{"script":"src/scripts/hooks/tool_result_bytes_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"spawn-guard-shadow":{"script":"src/scripts/hooks/spawn_guard_shadow_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Agent","Task"]},"source-first-gate":{"script":"src/scripts/hooks/source_first_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"journal-record":{"script":"src/scripts/hooks/journal_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory"}},"roles":{"worker":{"drop":["delegation-nudge","skill-route","end-review-nudge","council-availability","team-review-gate","self-repair","session-eol","interruption-ledger"]}},"platforms":{"augment":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"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"],"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"]},"claude":{"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","journal-record"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush","journal-record"],"stop":["chat-history","hot-context","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"],"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"],"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","rule-inject"],"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"],"pre_compact":["language-mirror","hot-context","rule-inject","journal-record"],"subagent_start":["subagent-ledger","journal-record"],"subagent_stop":["subagent-ledger","journal-record"]},"cowork":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"],"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"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"cursor":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"cline":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"windsurf":{"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","language-mirror","git-authorization","session-canary","self-repair","session-register"]},"gemini":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"copilot":{"fallback_only":true}},"native_event_aliases":{"augment":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use"},"claude":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cowork":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cursor":{"sessionStart":"session_start","sessionEnd":"session_end","stop":"stop","beforeSubmitPrompt":"user_prompt_submit","postToolUse":"post_tool_use","preToolUse":"pre_tool_use","preCompact":"pre_compact"},"cline":{"TaskStart":"session_start","TaskResume":"session_start","TaskComplete":"session_end","TaskCancel":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact"},"windsurf":{"pre_user_prompt":"user_prompt_submit","post_cascade_response":"stop","post_setup_worktree":"session_start"},"gemini":{"SessionStart":"session_start","SessionEnd":"session_end","BeforeAgent":"user_prompt_submit","AfterAgent":"stop","BeforeTool":"pre_tool_use","AfterTool":"post_tool_use"}}}}
1
+ {"fingerprint":"ebf09730:90278","manifest":{"schema_version":1,"concerns":{"chat-history":{"script":"src/scripts/chat_history.ts","args":["hook-dispatch"],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["result"]},"hot-context":{"script":"src/scripts/hot_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"handoff-context":{"script":"src/scripts/handoff_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"roadmap-progress":{"script":"src/scripts/roadmap_progress_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"onboarding-gate":{"script":"src/scripts/onboarding_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"context-hygiene":{"script":"src/scripts/context_hygiene_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"verify-before-complete":{"script":"src/scripts/before_complete_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"ship-diff-volume":{"script":"src/scripts/hooks/ship_diff_volume_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"minimal-safe-diff":{"script":"src/scripts/minimal_safe_diff_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"injection-scan":{"script":"src/scripts/injection_scan_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"block-no-verify":{"script":"src/scripts/hooks/block_no_verify.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"block-kernel-rule-writes":{"script":"src/scripts/hooks/block_kernel_rule_writes.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"one-question-per-ask":{"script":"src/scripts/hooks/one_question_per_ask_hook.ts","args":[],"fail_closed":false,"severity":"blocking","tools":["AskUserQuestion","AskQuestion","UserQuestion","ask_user_question","ask_question","user_question"],"needs_payload_bodies":["input"]},"block-speaking-inbox-dir":{"script":"src/scripts/hooks/block_speaking_inbox_dir.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"block-config-weakening":{"script":"src/scripts/hooks/block_config_weakening.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"rtk-wrap":{"script":"src/scripts/hooks/rtk_wrap_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-slop":{"script":"src/scripts/hooks/design_slop_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-pass":{"script":"src/scripts/hooks/design_pass_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"design-pass-stop":{"script":"src/scripts/hooks/design_pass_hook.ts","args":["--slot","stop"],"fail_closed":false,"severity":"advisory"},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"suggestion-capture":{"script":"src/scripts/hooks/suggestion_capture_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"chain-nudge":{"script":"src/scripts/hooks/chain_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"code-graph-context":{"script":"src/scripts/hooks/code_graph_context_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Grep","Glob","Read"],"needs_payload_bodies":["input"]},"memory-learn":{"script":"src/scripts/memory_learn_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"first-run-gate":{"script":"src/scripts/first_run_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"profile-staleness":{"script":"src/scripts/profile_staleness_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"wrapper-freshness":{"script":"src/scripts/wrapper_freshness_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"surface-probe":{"script":"src/scripts/surface_probe_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"team-review-gate":{"script":"src/scripts/team_review_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"pr-url-reminder":{"script":"src/scripts/pr_url_reminder_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"push-settle":{"script":"src/scripts/hooks/push_settle_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input","result"]},"language-mirror":{"script":"src/scripts/language_mirror_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"git-authorization":{"script":"src/scripts/git_authorization_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"evidence-independence":{"script":"src/scripts/hooks/evidence_independence.ts","args":[],"fail_closed":false,"severity":"blocking","needs_payload_bodies":["input"]},"session-canary":{"script":"src/scripts/session_canary_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"council-availability":{"script":"src/scripts/council_availability_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"telemetry-disclosure":{"script":"src/scripts/telemetry_disclosure_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"telemetry-flush":{"script":"src/scripts/hooks/telemetry_flush_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"self-repair":{"script":"src/scripts/self_repair_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"session-register":{"script":"src/scripts/session_register_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"orchestration-record":{"script":"src/scripts/hooks/orchestration_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Agent","Task"],"needs_payload_bodies":["input","result"]},"telemetry-usage":{"script":"src/scripts/hooks/telemetry_usage_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Skill"],"needs_payload_bodies":["input"]},"delegation-nudge":{"script":"src/scripts/hooks/delegation_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","nudge_rank":1},"skill-route":{"script":"src/scripts/hooks/skill_route_hook.ts","args":[],"fail_closed":false,"severity":"advisory","nudge_rank":2},"rule-inject":{"script":"src/scripts/hooks/rule_inject_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"end-review-nudge":{"script":"src/scripts/hooks/end_review_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","skip_on_refusal_retry":true},"turn-end-gate":{"script":"src/scripts/hooks/turn_end_gate_hook.ts","args":[],"fail_closed":false,"severity":"blocking"},"comment-discipline":{"script":"src/scripts/hooks/comment_discipline_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Write","Edit"],"needs_payload_bodies":["input"]},"edit-shape":{"script":"src/scripts/hooks/edit_shape_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Write"],"needs_payload_bodies":["input"]},"reread-guard":{"script":"src/scripts/hooks/reread_guard_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Read","Edit","Write"],"needs_payload_bodies":["input"]},"session-eol":{"script":"src/scripts/hooks/session_eol_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"run-continuation":{"script":"src/scripts/hooks/run_continuation_hook.ts","args":[],"fail_closed":false,"severity":"blocking"},"interruption-ledger":{"script":"src/scripts/hooks/interruption_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory","skip_on_refusal_retry":true},"subagent-ledger":{"script":"src/scripts/hooks/subagent_ledger_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"tool-result-bytes":{"script":"src/scripts/hooks/tool_result_bytes_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"spawn-guard-shadow":{"script":"src/scripts/hooks/spawn_guard_shadow_hook.ts","args":[],"fail_closed":false,"severity":"advisory","tools":["Agent","Task"]},"source-first-gate":{"script":"src/scripts/hooks/source_first_gate_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"journal-record":{"script":"src/scripts/hooks/journal_record_hook.ts","args":[],"fail_closed":false,"severity":"advisory"}},"roles":{"worker":{"drop":["delegation-nudge","skill-route","end-review-nudge","council-availability","team-review-gate","self-repair","session-eol","interruption-ledger"]}},"platforms":{"augment":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"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"],"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"]},"claude":{"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","journal-record"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush","journal-record"],"stop":["chat-history","hot-context","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"],"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"],"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"],"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"],"pre_compact":["language-mirror","hot-context","rule-inject","journal-record"],"subagent_start":["subagent-ledger","journal-record"],"subagent_stop":["subagent-ledger","journal-record"]},"cowork":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"],"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"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"cursor":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"cline":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"windsurf":{"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","language-mirror","git-authorization","session-canary","self-repair","session-register"]},"gemini":{"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress","design-pass-stop"],"user_prompt_submit":["chat-history","verify-before-complete","minimal-safe-diff","language-mirror","git-authorization","session-canary","self-repair","session-register"],"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"]},"copilot":{"fallback_only":true}},"native_event_aliases":{"augment":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use"},"claude":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cowork":{"SessionStart":"session_start","SessionEnd":"session_end","Stop":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact","SubagentStart":"subagent_start","SubagentStop":"subagent_stop"},"cursor":{"sessionStart":"session_start","sessionEnd":"session_end","stop":"stop","beforeSubmitPrompt":"user_prompt_submit","postToolUse":"post_tool_use","preToolUse":"pre_tool_use","preCompact":"pre_compact"},"cline":{"TaskStart":"session_start","TaskResume":"session_start","TaskComplete":"session_end","TaskCancel":"stop","UserPromptSubmit":"user_prompt_submit","PostToolUse":"post_tool_use","PreToolUse":"pre_tool_use","PreCompact":"pre_compact"},"windsurf":{"pre_user_prompt":"user_prompt_submit","post_cascade_response":"stop","post_setup_worktree":"session_start"},"gemini":{"SessionStart":"session_start","SessionEnd":"session_end","BeforeAgent":"user_prompt_submit","AfterAgent":"stop","BeforeTool":"pre_tool_use","AfterTool":"post_tool_use"}}}}
@@ -1271,7 +1271,30 @@ platforms:
1271
1271
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush, journal-record]
1272
1272
  stop: [chat-history, hot-context, 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]
1273
1273
  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]
1274
- 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, rule-inject]
1274
+ # `rule-inject` was bound here and is not any more
1275
+ # (road-to-delivery-for-every-host 3.2, owner ruling E2). E2's strategy is
1276
+ # `user_prompt_submit` + `pre_compact`; this slot is the hottest one in the
1277
+ # manifest and its measured gate-open p90 for the concern is 19,649 B against
1278
+ # a 2,048-byte slot sum cap E2 keeps unchanged. The three rules that were
1279
+ # reachable ONLY from this slot — design-review-after-ui-write,
1280
+ # source-of-truth, ui-audit-gate — lose nothing: `project_thin_rules.path_only_ids`
1281
+ # keeps a path-only rule full-bodied, the same exemption a trigger-less rule
1282
+ # already had, so they are projected eagerly instead of delivered.
1283
+ #
1284
+ # WHAT "LOSE NOTHING" DOES NOT COVER, added 2026-09-08 after R2 finding 1 read
1285
+ # the sentence above as covering more than those three. It is accurate for the
1286
+ # three and silent about the class next to them: 18 rules carry BOTH path and
1287
+ # non-path triggers, `path_only_ids` uses `every`, so all 18 are THINNED and
1288
+ # their path-shaped half has no carrier once this binding is gone. They keep
1289
+ # every keyword / phrase / command route through the concern, and on this host
1290
+ # they never had a separate host-native path route — `_claude_paths_plan` emits
1291
+ # no `paths:` for a mixed-trigger rule on purpose — so what is lost is the
1292
+ # difference between loading unconditionally and being delivered on a prompt
1293
+ # match. The names, the measurement and the reason the exemption is not widened
1294
+ # (+16,329 GPT tok, +69 % on the thin rule layer) are in
1295
+ # `project_thin_rules.path_only_ids`; the receiver is
1296
+ # `agents/roadmaps/stubs/road-to-a-path-route-under-delivery.md`.
1297
+ 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]
1275
1298
  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]
1276
1299
  # `language-mirror` — round-5 § 6.1. `pre_compact` sets a pin-lost marker;
1277
1300
  # the `post_tool_use` entry above re-emits the pin exactly once while that
@@ -22,7 +22,13 @@
22
22
  *
23
23
  * Refuses: a Write/Edit/NotebookEdit (or cross-platform equivalent) whose
24
24
  * target path introduces a **new** first-level directory under `agents/tmp/`
25
- * or `agents/tmp.old/` whose name is not an opaque round identifier.
25
+ * or `agents/tmp.old/` whose name is not an opaque round identifier — and a
26
+ * shell command carrying its path. Two narrowings bring the code closer to
27
+ * that sentence without classifying commands: a read tool's `file_path` is
28
+ * filtered by `_READ_ONLY_TOOLS`, and a directory name that is a GLOB over an
29
+ * acceptable one is not a creatable literal
30
+ * ({@link isGlobOverAcceptableInboxDir}). Both were judged unconditionally
31
+ * before, which is what refused a read command in this guard's own workflow.
26
32
  *
27
33
  * Allowed, deliberately:
28
34
  *
@@ -62,6 +68,7 @@ import * as path from 'node:path';
62
68
  import { fileURLToPath, pathToFileURL } from 'node:url';
63
69
 
64
70
  import { isNonHarvestTmpDir, isOpaqueRoundId } from '../_lib/source_shape.js';
71
+ import { invokedSegments } from './git_command_classifier.js';
65
72
  import { readHookStdin } from './hook_stdin.js';
66
73
 
67
74
  const _HERE = fileURLToPath(import.meta.url);
@@ -76,10 +83,146 @@ const _PATH_KEYS: readonly string[] = [
76
83
  'file_path', 'path', 'target_file', 'filename', 'filePath', 'notebook_path',
77
84
  ];
78
85
 
86
+ /**
87
+ * Tools whose `file_path` names something to READ, never something to write.
88
+ *
89
+ * `_PATH_KEYS` covers `file_path` and `path`, which is exactly what a Read,
90
+ * Grep or Glob call carries — so a search under a speaking round that does not
91
+ * exist yet was refused by a guard whose own docstring scopes itself to
92
+ * `Write/Edit/NotebookEdit`. Filtering here rather than through the manifest's
93
+ * `tools:` key on purpose: that key is an exact match on the host's own tool
94
+ * name, so an allowlist written in Claude's vocabulary would silence this
95
+ * guard on every host that spells its write tools differently. A DENY-list of
96
+ * read tools fails the other way — an unrecognised tool is still judged.
97
+ *
98
+ * An envelope with no tool name is judged too, for the same reason
99
+ * `_concern_matches_tool` runs a concern when the field is absent: a key that
100
+ * cannot decide must not be what silences a blocking guard.
101
+ */
102
+ const _READ_ONLY_TOOLS: ReadonlySet<string> = new Set([
103
+ 'read', 'grep', 'glob', 'notebookread', 'ls', 'view', 'search',
104
+ 'codebase-retrieval', 'codebase_retrieval', 'file_search', 'grep_search',
105
+ 'read_file', 'list_dir', 'semantic_search',
106
+ ]);
107
+
79
108
  function _isObject(v: JsonValue | undefined): v is JsonObject {
80
109
  return v !== null && v !== undefined && typeof v === 'object' && !Array.isArray(v);
81
110
  }
82
111
 
112
+ /**
113
+ * A directory name that is a GLOB over an already-acceptable name.
114
+ *
115
+ * READING IS NOT CREATING, AND THE COMMAND SCAN DID NOT KNOW IT. Measured
116
+ * twice in one session while running the inbox flow over a real round:
117
+ * `ls -d agents/tmp/inbox-2026-09-*` — the command the naming rule tells an
118
+ * operator to run to find a free round identifier — was refused, because the
119
+ * scan in `main` offered every whitespace token to the same verdict that judges
120
+ * a Write target and `inbox-2026-09-*` is not an opaque identifier: the `*`
121
+ * fails the 1-3 char disambiguator. So the guard stood between the operator and
122
+ * the rule it exists to enforce.
123
+ *
124
+ * WHY THIS AXIS AND NOT THE VERB. The first two attempts classified the
125
+ * COMMAND — an allowlist of read-only verbs, then per-verb writing markers.
126
+ * Two rounds of neutral review took that apart from both ends: round 1 found
127
+ * `sed -i`, `find -exec` and `awk system()` allowed where the plain token scan
128
+ * had blocked them; round 2 found the siblings the resulting deny-list did not
129
+ * reach (`sed`'s `s///w`, `awk`'s output pipe, `sort -o`, `yq -i`), a false
130
+ * positive the awk marker reintroduced by running past the program into a shell
131
+ * redirect, mention-blocking on heredoc bodies, and a fail-OPEN when the
132
+ * segmenter returned nothing. Its handoff named the shape of the problem:
133
+ * *"3 and 5 pull in opposite directions, so patching them one probe at a time
134
+ * will keep oscillating."*
135
+ *
136
+ * It was the wrong axis, and the measured defect never needed it. What is
137
+ * actually decidable from the token alone is whether the name could be created
138
+ * AS WRITTEN, and a glob cannot: the shell expands it against what exists. The
139
+ * one thing that could — `mkdir 'inbox-2026-09-*'`, creating a literal
140
+ * directory with a `*` in it — produces a name this predicate accepts anyway,
141
+ * because its non-glob part is opaque and carries no source. So the carve-out
142
+ * is safe by construction rather than by enumeration, and every writing form
143
+ * above is judged again by the unchanged token scan.
144
+ *
145
+ * A pattern over a SPEAKING name (`my-source-*`) is not accepted: the glob
146
+ * characters are removed and the remainder must itself pass.
147
+ */
148
+ function isGlobOverAcceptableInboxDir(name: string): boolean {
149
+ if (!/[*?[\]]/.test(name)) {
150
+ return false;
151
+ }
152
+ const literal = name.replace(/[*?]|\[[^\]]*\]/g, '');
153
+ const trimmed = literal.replace(/-+$/, '');
154
+ return trimmed !== '' && isAcceptableInboxDir(trimmed);
155
+ }
156
+
157
+ /**
158
+ * The paths a redirect inside one segment would create.
159
+ *
160
+ * `splitOutsideQuotes` treats `;`, `|`, `&` and newline as separators and `>`
161
+ * as ordinary text, so a redirect target stays inside its segment. Collected
162
+ * separately from the token scan because the quotes matter: the token split
163
+ * removes them, this does not, and both inbox regexes need `agents` at a `/`
164
+ * or at the start — so `> "agents/tmp/<name>/x"` read as not-an-inbox-path
165
+ * until the strip below. Also run over the RAW command, because the heredoc
166
+ * regex in `invokedSegments` swallows a redirect that follows the delimiter.
167
+ */
168
+ function _redirectTargets(segment: string): string[] {
169
+ const out: string[] = [];
170
+ const re = /(?:^|[^0-9<>&])[0-9]*>>?\s*([^\s|&;<>]+)/g;
171
+ let m: RegExpExecArray | null = re.exec(segment);
172
+ while (m !== null) {
173
+ // The character class stops at whitespace and separators but NOT at a
174
+ // quote, while both inbox regexes require `agents` at a `/` or at the
175
+ // start of the string. So `> "agents/tmp/<name>/x"` came back as
176
+ // `"agents/tmp/…` and read as not-an-inbox-path — allowed, where the
177
+ // unquoted form blocked. A neutral review probed it; stripping the
178
+ // quotes is the whole fix.
179
+ const target = (m[1] ?? '').replace(/^['"`]+|['"`]+$/g, '');
180
+ if (target) {
181
+ out.push(target);
182
+ }
183
+ m = re.exec(segment);
184
+ }
185
+ return out;
186
+ }
187
+
188
+ /**
189
+ * The tokens of a shell command whose CREATION this guard should judge.
190
+ *
191
+ * Every whitespace token, exactly as before this change — the classification
192
+ * that tried to be cleverer than this is gone, and the docstring on
193
+ * {@link isGlobOverAcceptableInboxDir} records why. The two things kept are
194
+ * the ones no review disputed: the RAW command is scanned as well, because
195
+ * `invokedSegments` strips heredoc bodies as data and a `python3 - <<PY` body
196
+ * calling `makedirs` is precisely this guard's input; and redirect targets are
197
+ * collected separately with their quotes stripped, because `>` is not a
198
+ * segment separator and `> "path"` otherwise read as not-a-path.
199
+ *
200
+ * Scanning the raw command in addition to the segments is deliberately
201
+ * redundant rather than conditional: a heredoc predicate is one more thing to
202
+ * get wrong, the extra tokens can only ever matter if one of them names a
203
+ * speaking inbox directory, and the mention-blocking a review found came from
204
+ * the read-only SKIP being dropped for heredocs — not from the extra scan.
205
+ *
206
+ * Exported so the harness can pin the polarity pair on the tokens themselves,
207
+ * without an envelope.
208
+ */
209
+ export function creatableTokens(command: string): string[] {
210
+ const out: string[] = [];
211
+ const segments = invokedSegments(command);
212
+ for (const seg of segments) {
213
+ out.push(..._redirectTargets(seg));
214
+ }
215
+ out.push(..._redirectTargets(command));
216
+ for (const text of [...segments, command]) {
217
+ for (const tok of text.split(/[\s"'`(){};|&<>]+/)) {
218
+ if (tok) {
219
+ out.push(tok);
220
+ }
221
+ }
222
+ }
223
+ return out;
224
+ }
225
+
83
226
  /** `agents/tmp/<name>/…` or `agents/tmp.old/<name>/…`, after normalisation. */
84
227
  const INBOX_RE = /(?:^|\/)agents\/tmp(?:\.old)?\/([^/]+)\/./;
85
228
 
@@ -145,6 +288,9 @@ export function verdictFor(
145
288
  if (isAcceptableInboxDir(dir)) {
146
289
  return { block: false, dir, reason: 'opaque round identifier or named working set' };
147
290
  }
291
+ if (isGlobOverAcceptableInboxDir(dir)) {
292
+ return { block: false, dir, reason: 'glob over an acceptable name — not a creatable literal' };
293
+ }
148
294
  // Already created — a rename is the fix, and refusing every later write
149
295
  // would wedge the round without removing the name.
150
296
  //
@@ -153,17 +299,36 @@ export function verdictFor(
153
299
  // `agents/tmp-notes/` and `agents/tmpfiles/`, and taking the FIRST
154
300
  // occurrence meant a path carrying a decoy earlier segment probed the wrong
155
301
  // directory — in either direction. Found by the R2 review of this branch.
302
+ // THE CAPTURE CARRIES THE PATH'S OWN PREFIX, and it did not. Anchoring the
303
+ // group at `agents/` discarded everything before it, so a nested inbox —
304
+ // `app/Modules/<m>/agents/tmp/<round>/…`, the shape a module-per-package
305
+ // tree produces — probed `<repoRoot>/agents/tmp/<round>`, a directory that
306
+ // does not exist. The already-exists carve-out could therefore never fire
307
+ // there, and every write into a nested speaking round was refused forever
308
+ // instead of once.
309
+ //
310
+ // The prefix group is `??` — optional AND preferring ABSENT — and that is
311
+ // the half a neutral review had to point out. A greedy `(?:.*\/)?`
312
+ // resolves to the LAST inbox segment in the path while `inboxDirName`
313
+ // reads the FIRST, so a path carrying two of them judged one directory and
314
+ // probed another, reporting "already exists" for a name that does not.
315
+ // Making only `.*` lazy is NOT enough: `(...)?` still prefers one
316
+ // repetition, so the engine grows the prefix rather than trying the
317
+ // no-prefix branch, and the long match wins anyway — the test for this
318
+ // caught exactly that. `??` tries no-prefix first, so both reads agree.
319
+ // The decoy case below still resolves: `agents/tmp-notes/` fails the `/`
320
+ // after `tmp`, so the no-prefix branch is rejected and the match advances.
156
321
  const normalized = filePath.replace(/\\/g, '/').replace(/^(\.\/)+/, '');
157
322
  const m =
158
- /(?:^|\/)(agents\/tmp(?:\.old)?\/[^/]+)\//.exec(normalized) ??
159
- /(?:^|\/)(agents\/tmp(?:\.old)?\/[^/.]+)\/?$/.exec(normalized);
323
+ /^((?:.*?\/)??agents\/tmp(?:\.old)?\/[^/]+)\//.exec(normalized) ??
324
+ /^((?:.*?\/)??agents\/tmp(?:\.old)?\/[^/.]+)\/?$/.exec(normalized);
160
325
  if (m === null) {
161
326
  // `inboxDirName` matched but this did not — treat as unattributable and
162
327
  // BLOCK, since the name is speaking and we cannot prove it pre-exists.
163
328
  return { block: true, dir, reason: 'new inbox directory with a speaking name (path not re-anchorable)' };
164
329
  }
165
330
  const upto = m[1] as string;
166
- const probe = repoRoot === '' ? upto : path.join(repoRoot, upto);
331
+ const probe = repoRoot === '' || path.isAbsolute(upto) ? upto : path.join(repoRoot, upto);
167
332
  if (exists(probe)) {
168
333
  return { block: false, dir, reason: 'directory already exists — rename, do not re-refuse' };
169
334
  }
@@ -216,11 +381,15 @@ export function main(): number {
216
381
  return 0;
217
382
  }
218
383
  const repoRoot = typeof envelope['project_dir'] === 'string' ? envelope['project_dir'] : '';
384
+ const rawTool = payload['tool_name'] ?? envelope['tool_name'];
385
+ const toolName = typeof rawTool === 'string' ? rawTool.trim().toLowerCase() : '';
219
386
  const candidates: string[] = [];
220
- for (const key of _PATH_KEYS) {
221
- const v = ti[key];
222
- if (typeof v === 'string' && v) {
223
- candidates.push(v);
387
+ if (!_READ_ONLY_TOOLS.has(toolName)) {
388
+ for (const key of _PATH_KEYS) {
389
+ const v = ti[key];
390
+ if (typeof v === 'string' && v) {
391
+ candidates.push(v);
392
+ }
224
393
  }
225
394
  }
226
395
  // A shell command creates the directory just as effectively as a Write,
@@ -228,16 +397,13 @@ export function main(): number {
228
397
  // agents/tmp/<speaking-name>`, a `git mv` into one, or a redirect
229
398
  // bypassed it entirely. Both siblings on this slot (`block_no_verify`,
230
399
  // `block_kernel_rule_writes`) parse the command string; this one did
231
- // not, and the R2 review of this branch caught it. Every whitespace
232
- // token of the command is offered to the same pure verdict, so the
233
- // decision logic is shared rather than duplicated.
400
+ // not, and the R2 review of this branch caught it. The tokens that
401
+ // could CREATE something are offered to the same pure verdict, so the
402
+ // decision logic is shared rather than duplicated — see
403
+ // `creatableTokens` for why "every whitespace token" was too many.
234
404
  const cmd = ti['command'] ?? (_isObject(envelope['payload']) ? (envelope['payload'] as JsonObject)['command'] : undefined);
235
405
  if (typeof cmd === 'string' && cmd) {
236
- for (const tok of cmd.split(/[\s"'`(){};|&<>]+/)) {
237
- if (tok) {
238
- candidates.push(tok);
239
- }
240
- }
406
+ candidates.push(...creatableTokens(cmd));
241
407
  }
242
408
  for (const v of candidates) {
243
409
  const verdict = verdictFor(v, (p) => fs.existsSync(p), repoRoot);
@@ -39,7 +39,7 @@ import fs from 'node:fs';
39
39
  import path from 'node:path';
40
40
  import { fileURLToPath, pathToFileURL } from 'node:url';
41
41
 
42
- import { detectSources, pickSource } from '../code_graph/detect.js';
42
+ import { type GraphState, graphState } from '../code_graph/detect.js';
43
43
  import { readHookStdin } from './hook_stdin.js';
44
44
 
45
45
  const EXIT_ALLOW = 0;
@@ -114,26 +114,15 @@ function latch(root: string, session: string): void {
114
114
  }
115
115
  }
116
116
 
117
- const NATIVE_CACHE = path.join('agents', 'runtime', 'state', 'code-graph-v1.json');
118
-
119
- /** The three-state staleness token this hook reports. */
120
- export type GraphState = 'absent' | 'fresh' | `behind:${number}`;
121
-
122
117
  /**
123
- * Resolve the graph's state for `root`.
118
+ * The three-state staleness token this hook reports.
124
119
  *
125
- * `absent` means no source at all — the silent case. A picked source whose
126
- * staleness is UNKNOWN reads as `fresh`, mirroring `computeVerdict`'s own
127
- * `picked.stale ? STALE : FRESH`: unknown is not treated as stale, because
128
- * inventing a commit count would be worse than reporting none.
120
+ * Re-exported, not defined here: Phase 3's verbs must print the same token, and
121
+ * an engine module importing this hook to learn it would invert the dependency
122
+ * direction D9 measures. The definition lives in `code_graph/detect.ts`; this
123
+ * re-export keeps every existing importer of the hook working unchanged.
129
124
  */
130
- export function graphState(root: string): GraphState {
131
- const picked = pickSource(detectSources(root, path.join(root, NATIVE_CACHE)));
132
- if (!picked) return 'absent';
133
- if (picked.stale !== true) return 'fresh';
134
- const behind = picked.commits_behind;
135
- return `behind:${typeof behind === 'number' ? behind : 0}`;
136
- }
125
+ export { type GraphState, graphState };
137
126
 
138
127
  /**
139
128
  * The one line, ≤ ~45 tokens.
@@ -191,8 +191,9 @@ export function buildReminder(branch: string, pr: number | null): { reason: stri
191
191
  `own exit code.\n\n` +
192
192
  `Before this turn ends:\n` +
193
193
  ` 1. Settle it:\n ${settle}\n` +
194
- ` Exit 0 = settled green · 1 = settled, something failed · 2 = NOT a verdict ` +
195
- `(timed out or unreadable — never report 2 as green).\n` +
194
+ ` Exit 0 = settled green, and the line names the head it is about · 1 = settled, ` +
195
+ `something failed · 2 = NOT a verdict (timed out, unreadable, or the PR is merged / ` +
196
+ `closed / head-diverged — never report 2 as green).\n` +
196
197
  ` 2. Red? Read only the failing part:\n` +
197
198
  ` gh run view --job <id> --log-failed | grep -E '×|FAIL|Error'\n` +
198
199
  ` then fix it and push again (fix-what-you-see: the author is irrelevant).\n` +