@event4u/agent-config 14.6.0 → 14.7.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 (188) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +260 -7
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/inbox.md +264 -10
  5. package/dist/agent-src/commands/analyze.md +1 -1
  6. package/dist/agent-src/commands/roadmap/process-full.md +116 -30
  7. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +31 -0
  8. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +135 -104
  9. package/dist/agent-src/rules/context-hygiene.md +2 -0
  10. package/dist/agent-src/rules/decision-revisit-gate.md +3 -0
  11. package/dist/agent-src/rules/fix-what-you-see.md +108 -0
  12. package/dist/agent-src/rules/no-roadmap-references.md +0 -5
  13. package/dist/agent-src/rules/recurring-criticism.md +110 -0
  14. package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
  15. package/dist/agent-src/rules/rule-type-governance.md +0 -2
  16. package/dist/agent-src/rules/self-repair-loop.md +17 -0
  17. package/dist/agent-src/rules/skill-quality.md +0 -2
  18. package/dist/agent-src/rules/source-confidentiality.md +37 -7
  19. package/dist/agent-src/scripts/archival_sweep.ts +117 -0
  20. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
  21. package/dist/agent-src/scripts/gate_budget.ts +282 -0
  22. package/dist/agent-src/scripts/gate_execute.ts +72 -31
  23. package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
  24. package/dist/agent-src/scripts/update_roadmap_progress.ts +251 -7
  25. package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
  26. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
  27. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
  28. package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
  29. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
  30. package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
  31. package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
  32. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
  33. package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
  34. package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
  35. package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
  36. package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
  37. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
  38. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
  39. package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
  40. package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
  41. package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
  42. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
  43. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  44. package/dist/agent-src/templates/roadmaps.md +45 -2
  45. package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  46. package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  47. package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  48. package/dist/cli/registry.js +1 -1
  49. package/dist/cli/registry.js.map +1 -1
  50. package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
  51. package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-4VR4ORQ6.js} +10 -7
  52. package/dist/cli-delegate/chunk-6EOCNQAM.js +402 -0
  53. package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-74E3TKWV.js} +381 -280
  54. package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-7F3VYSM2.js} +4 -4
  55. package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
  56. package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
  57. package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-GJ5BVHXM.js} +72 -165
  58. package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-JZ4NR2TW.js} +338 -0
  59. package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
  60. package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
  61. package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
  62. package/dist/cli-delegate/cmd_conformance.js +459 -709
  63. package/dist/cli-delegate/cmd_doctor.js +6 -6
  64. package/dist/cli-delegate/cmd_explain.js +1 -1
  65. package/dist/cli-delegate/cmd_export.js +7 -6
  66. package/dist/cli-delegate/cmd_handoff.js +6 -6
  67. package/dist/cli-delegate/cmd_packs_active.js +1 -1
  68. package/dist/cli-delegate/cmd_preflight.js +2 -2
  69. package/dist/cli-delegate/cmd_prune.js +7 -6
  70. package/dist/cli-delegate/cmd_session_recycle.js +840 -30
  71. package/dist/cli-delegate/cmd_settings_migrate.js +7 -6
  72. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  73. package/dist/cli-delegate/cmd_sync.js +7 -6
  74. package/dist/cli-delegate/cmd_uninstall.js +9 -7
  75. package/dist/cli-delegate/cmd_update.js +1 -1
  76. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  77. package/dist/cli-delegate/cmd_validate.js +7 -6
  78. package/dist/discovery/deprecation-report.md +1 -1
  79. package/dist/discovery/discovery-manifest.json +68 -25
  80. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  81. package/dist/discovery/discovery-manifest.summary.md +4 -4
  82. package/dist/discovery/dormancy-report.md +1 -1
  83. package/dist/discovery/orphan-report.md +1 -1
  84. package/dist/discovery/packs.json +10 -8
  85. package/dist/discovery/trust-report.md +3 -3
  86. package/dist/discovery/workspaces.json +8 -6
  87. package/dist/hooks/dispatch.js +2901 -2053
  88. package/dist/install/claudePathsPlan.js +187 -0
  89. package/dist/install/claudePathsPlan.js.map +1 -0
  90. package/dist/install/hostLayerFingerprint.js +136 -0
  91. package/dist/install/hostLayerFingerprint.js.map +1 -0
  92. package/dist/install/install.mjs +553 -425
  93. package/dist/install/partitionEligibility.js +276 -0
  94. package/dist/install/partitionEligibility.js.map +1 -0
  95. package/dist/mcp/registry-manifest.json +3 -3
  96. package/dist/mcp/server.json +2 -2
  97. package/dist/mcp/server.mjs +1744 -1191
  98. package/dist/router.json +1 -1
  99. package/dist/scripts/_lib/agent_src.js +41 -0
  100. package/dist/scripts/_lib/agent_src.js.map +1 -1
  101. package/dist/scripts/_lib/install_layout.js +83 -0
  102. package/dist/scripts/_lib/install_layout.js.map +1 -0
  103. package/dist/scripts/_lib/installed_lock.js +444 -0
  104. package/dist/scripts/_lib/installed_lock.js.map +1 -0
  105. package/dist/scripts/condense.js +13 -164
  106. package/dist/scripts/condense.js.map +1 -1
  107. package/dist/server/schemas/settings.js +4 -0
  108. package/dist/server/schemas/settings.js.map +1 -1
  109. package/docs/contracts/settings-classes.md +12 -6
  110. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
  111. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
  112. package/docs/guidelines/agent-infra/memory-access.md +17 -0
  113. package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  114. package/package.json +1 -1
  115. package/src/agent-src/scripts/archival_sweep.ts +117 -0
  116. package/src/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
  117. package/src/agent-src/scripts/gate_budget.ts +282 -0
  118. package/src/agent-src/scripts/gate_execute.ts +72 -31
  119. package/src/agent-src/scripts/roadmap_gates.ts +74 -3
  120. package/src/agent-src/scripts/update_roadmap_progress.ts +251 -7
  121. package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  122. package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  123. package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  124. package/src/config/agent-settings.template.yml +27 -2
  125. package/src/config/ci-local-parity.yml +18 -0
  126. package/src/config/estate-count-budget.json +339 -3
  127. package/src/config/gate-violation-baselines.json +41 -41
  128. package/src/config/rule-activation-census.json +14 -6
  129. package/src/config/rule-stub-ceilings.json +19 -6
  130. package/src/install/claudePathsPlan.ts +196 -0
  131. package/src/install/hostLayerFingerprint.ts +145 -0
  132. package/src/install/partitionEligibility.ts +325 -0
  133. package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
  134. package/src/scripts/_dispatch.bash +6 -1
  135. package/src/scripts/_lib/ac_heading.ts +30 -0
  136. package/src/scripts/_lib/agent_src.ts +42 -0
  137. package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
  138. package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
  139. package/src/scripts/_lib/envelope_grounding.ts +38 -8
  140. package/src/scripts/_lib/installed_lock.ts +50 -5
  141. package/src/scripts/_lib/md_section.ts +129 -0
  142. package/src/scripts/_lib/scope_guard.sh +26 -3
  143. package/src/scripts/_lib/self_repair.ts +19 -0
  144. package/src/scripts/_lib/self_repair_class_b.ts +171 -0
  145. package/src/scripts/ai_council/cli_help.ts +55 -0
  146. package/src/scripts/before_complete_hook.ts +153 -28
  147. package/src/scripts/bench_ab_v2_complexity.ts +27 -7
  148. package/src/scripts/check_references.ts +60 -0
  149. package/src/scripts/check_release_published.ts +32 -11
  150. package/src/scripts/check_single_delivery.ts +59 -15
  151. package/src/scripts/ci_settle.ts +170 -0
  152. package/src/scripts/condense.ts +20 -170
  153. package/src/scripts/council_cli.ts +3 -3
  154. package/src/scripts/dispatch_r2_reviewer.ts +6 -14
  155. package/src/scripts/hook_manifest.json +1 -1
  156. package/src/scripts/hook_manifest.yaml +77 -14
  157. package/src/scripts/hooks/block_no_verify.ts +61 -10
  158. package/src/scripts/hooks/block_unauthorized_git.ts +1 -1
  159. package/src/scripts/hooks/concern_registry.ts +4 -0
  160. package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
  161. package/src/scripts/hooks/run_continuation_hook.ts +8 -5
  162. package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
  163. package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
  164. package/src/scripts/hooks/state_io.ts +782 -20
  165. package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
  166. package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
  167. package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
  168. package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
  169. package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
  170. package/src/scripts/install.ts +16 -11
  171. package/src/scripts/language_mirror_hook.ts +561 -25
  172. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  173. package/src/scripts/lint_plan_risk_register.ts +4 -3
  174. package/src/scripts/lint_skill_router_head.ts +4 -4
  175. package/src/scripts/mcp_server/tools.ts +2 -2
  176. package/src/scripts/new_roadmap.ts +136 -0
  177. package/src/scripts/pack_mcp_content.ts +20 -5
  178. package/src/scripts/release.ts +37 -38
  179. package/src/scripts/report_layer_overlap.ts +6 -0
  180. package/src/scripts/self_repair_hook.ts +37 -5
  181. package/src/scripts/session_register_hook.ts +11 -0
  182. package/src/scripts/skill_usage_collect.ts +11 -1
  183. package/src/scripts/skill_usage_report.ts +278 -8
  184. package/src/scripts/sync_pr_branch.ts +7 -0
  185. package/src/server/schemas/settings.ts +8 -0
  186. package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
  187. package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
  188. /package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
@@ -1 +1 @@
1
- {"fingerprint":"4834904a:65187","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"]},"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"]},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"code-graph-nudge":{"script":"src/scripts/hooks/code_graph_nudge_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"]},"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"},"block-unauthorized-git":{"script":"src/scripts/hooks/block_unauthorized_git.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"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"},"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},"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"},"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"]}},"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress"],"pre_tool_use":["block-no-verify","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress"],"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","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"],"pre_tool_use":["block-no-verify","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape","language-mirror"],"pre_compact":["language-mirror","hot-context"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress"],"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","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-nudge","ship-diff-volume","reread-guard","spawn-guard-shadow"],"post_tool_use":["chat-history","roadmap-progress","context-hygiene","verify-before-complete","minimal-safe-diff","injection-scan","pr-url-reminder","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"],"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"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"],"stop":["chat-history","hot-context","verify-before-complete","self-repair","session-register","roadmap-progress"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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":"f4a2905d:68966","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"]},"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"]},"ui-route-nudge":{"script":"src/scripts/hooks/ui_route_nudge_hook.ts","args":[],"fail_closed":false,"severity":"advisory","needs_payload_bodies":["input"]},"code-graph-nudge":{"script":"src/scripts/hooks/code_graph_nudge_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"]},"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"},"block-unauthorized-git":{"script":"src/scripts/hooks/block_unauthorized_git.ts","args":[],"fail_closed":true,"severity":"blocking","needs_payload_bodies":["input"]},"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},"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"},"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"]}},"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"],"pre_tool_use":["block-no-verify","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush"],"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","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"],"pre_tool_use":["block-no-verify","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape","language-mirror"],"pre_compact":["language-mirror","hot-context"],"subagent_start":["subagent-ledger"],"subagent_stop":["subagent-ledger"]},"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"],"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","block-unauthorized-git","evidence-independence","block-kernel-rule-writes","block-config-weakening","rtk-wrap","design-slop","ui-route-nudge","code-graph-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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"],"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"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"],"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"],"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","orchestration-record","telemetry-usage","tool-result-bytes","edit-shape"]},"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"}}}}
@@ -80,12 +80,18 @@ concerns:
80
80
  severity: advisory
81
81
  # Phase 5 — Tier-1 hook for the verify-before-complete rule. Records
82
82
  # observable evidence of verification commands (tests, quality tools,
83
- # builds) into agents/state/verify-before-complete.json — the path the code
84
- # actually writes (`before_complete_hook.STATE_FILE`); this comment said
85
- # `agents/runtime/state/` until round 7, which is a directory it never used.
86
- # The rule body cites that file as the source of truth for "verified this
87
- # turn?". Since round 7 it also carries `ci_last`, the session-scoped CI-settle
88
- # negative that `turn-end-gate` reads to refuse a premature completion claim.
83
+ # builds) into agents/state/verify-before-complete/<digest>.json — ONE FILE
84
+ # PER SESSION since 2026-08-20, addressed through
85
+ # `before_complete_hook.statePathFor`. Two earlier corrections are worth
86
+ # keeping visible, because this comment was wrong about the path twice: it
87
+ # said `agents/runtime/state/` until round 7 (a directory the code never
88
+ # used), and it named the single `STATE_FILE` until the split, which is the
89
+ # constant `prune_legacy_state_file` now DELETES. The rule body cites that
90
+ # state as the source of truth for "verified this turn?". Since round 7 it
91
+ # also carries `ci_last`, the session-scoped CI-settle negative that
92
+ # `turn-end-gate` reads to refuse a premature completion claim — and
93
+ # "session-scoped" is now a property of the layout rather than of a reset
94
+ # that fired when a foreign id showed up in a shared file.
89
95
  verify-before-complete:
90
96
  script: src/scripts/before_complete_hook.ts
91
97
  args: []
@@ -423,6 +429,34 @@ concerns:
423
429
  args: []
424
430
  fail_closed: false
425
431
  severity: advisory
432
+ # road-to-org-telemetry Phase 2 (step 2.1) — session-end Class-A flush.
433
+ # Checks whether the outbound spool has bytes and, if so, spawns a DETACHED
434
+ # sender and returns. It makes no network call in the session's own process,
435
+ # ever, and that shape is measured rather than preferred: Phase 0's second
436
+ # spike pre-registered "at or below one second at p95" for an inline flush
437
+ # and measured 1002 ms against a blackhole sink (a socket that accepts and
438
+ # never answers — the shape a wedged sink takes), against 20.5 ms for the
439
+ # detached spool. The step's own text defers to that result.
440
+ #
441
+ # DEFAULT-OFF and then some, exactly as telemetry-usage: `active` needs
442
+ # `enabled` AND an endpoint AND an org id AND a salt, none of which this
443
+ # public repository ships a value for, so a clone cannot reach the spawn
444
+ # path. `flush: never` short-circuits before the spool is stat-ed. Inactive
445
+ # ⇒ zero telemetry file operations and zero network calls.
446
+ #
447
+ # Bound on the six platforms that carry `session_end`; windsurf has the slot
448
+ # nowhere in this manifest, so an org running it keeps its records local
449
+ # until a flush slot exists there — a readable absence, not a silent one.
450
+ #
451
+ # Silent on every path and exit 0 always, including a failed spawn: a
452
+ # telemetry concern has nothing to say to the model, least of all at session
453
+ # end where the line would arrive after the last thing the user reads.
454
+ # Carries no hook-token-budget row — it emits nothing. fail_closed: false.
455
+ telemetry-flush:
456
+ script: src/scripts/hooks/telemetry_flush_hook.ts
457
+ args: []
458
+ fail_closed: false
459
+ severity: advisory
426
460
  # Self-repair intake (road-to-self-repair Phase 1). Two slots, one queue:
427
461
  # user_prompt_submit opens a record when the user states the agent worked
428
462
  # wrongly and injects the open-queue line; stop runs the deterministic
@@ -832,6 +866,35 @@ concerns:
832
866
  fail_closed: false
833
867
  severity: advisory
834
868
  tools: [Agent, Task]
869
+ # road-to-source-first-frontend Phase 3 Steps 1-2 — source-first gate, SHADOW
870
+ # ONLY, per docs/contracts/concern-activation-policy.md. On every
871
+ # capture-shaped tool call it appends one `source_first_gate_shadow` line
872
+ # recording what the gate WOULD have warned under four candidate gating
873
+ # rules. It emits nothing to the model and has no code path that can warn or
874
+ # deny.
875
+ #
876
+ # Phase 3 Step 1 was drafted as `exit 2 warn` and Step 2 left the posture an
877
+ # explicit maintainer call; the roadmap's own 2026-08-17 correction records
878
+ # that the activation policy contradicts the drafted default-ON warn. Shadow
879
+ # is the re-argued answer, and it dissolves both of that step's blocking
880
+ # dependencies rather than waiting on them: the verifier-exemption risk is a
881
+ # harm of EMISSION (a concern that emits nothing warns nobody, so the open
882
+ # SLI `agent_id` binding becomes a recorded field instead of a gate), and the
883
+ # unfinished multi-host matcher census becomes the measurement rather than a
884
+ # false-positive generator (`capture_kind` separates page-reaching capture
885
+ # from display capture on every record).
886
+ #
887
+ # No `tools:` filter on purpose: the concern must see ordinary reads to latch
888
+ # whether the handover source was read before the capture. The flip condition
889
+ # to advisory is pre-registered in the hook header.
890
+ #
891
+ # Never blocks, never warns: exit 0 on every path. fail_closed: false.
892
+ source-first-gate:
893
+ script: src/scripts/hooks/source_first_gate_hook.ts
894
+ args: []
895
+ fail_closed: false
896
+ severity: advisory
897
+ needs_payload_bodies: [input]
835
898
 
836
899
  # Role axis (road-to-token-economy-dispatch Phase 2). The dispatcher reads
837
900
  # AGENT_CONFIG_SESSION_ROLE (resolved via _lib/session_role.ts — set ONLY by
@@ -884,17 +947,17 @@ roles:
884
947
  platforms:
885
948
  augment:
886
949
  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]
887
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
950
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
888
951
  stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register, roadmap-progress]
889
952
  pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, ui-route-nudge, code-graph-nudge, ship-diff-volume, reread-guard]
890
953
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape]
891
954
 
892
955
  claude:
893
956
  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]
894
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
957
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
895
958
  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, run-continuation]
896
959
  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]
897
- pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, ui-route-nudge, code-graph-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow]
960
+ pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, ui-route-nudge, code-graph-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate]
898
961
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape, language-mirror]
899
962
  # `language-mirror` — round-5 § 6.1. `pre_compact` sets a pin-lost marker;
900
963
  # the `post_tool_use` entry above re-emits the pin exactly once while that
@@ -935,7 +998,7 @@ platforms:
935
998
  # agents/settings/contexts/chat-history-platform-hooks.md § Cowork.
936
999
  cowork:
937
1000
  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]
938
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
1001
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
939
1002
  # end-review-nudge and delegation-nudge are DELIBERATELY NOT bound on
940
1003
  # this platform (F10, review) — cowork-dispatcher.sh's own header states
941
1004
  # it "always exit[s] 0" and its dispatch line discards the dispatcher's
@@ -948,7 +1011,7 @@ platforms:
948
1011
  # still costs the dispatch, and its entire output is thrown away.
949
1012
  stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register, roadmap-progress]
950
1013
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
951
- pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, ui-route-nudge, code-graph-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow]
1014
+ pre_tool_use: [block-no-verify, block-unauthorized-git, evidence-independence, block-kernel-rule-writes, block-config-weakening, rtk-wrap, design-slop, ui-route-nudge, code-graph-nudge, ship-diff-volume, reread-guard, spawn-guard-shadow, source-first-gate]
952
1015
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape]
953
1016
  # subagent-ledger IS bound here, unlike the two nudges above, and the
954
1017
  # difference is the delivery channel rather than the platform: the nudges
@@ -969,7 +1032,7 @@ platforms:
969
1032
  # agents/settings/contexts/chat-history-platform-hooks.md.
970
1033
  cursor:
971
1034
  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]
972
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
1035
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
973
1036
  stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register, roadmap-progress]
974
1037
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
975
1038
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape]
@@ -984,7 +1047,7 @@ platforms:
984
1047
  # session is interrupted with partial state (mirrors Augment Stop).
985
1048
  cline:
986
1049
  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]
987
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
1050
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
988
1051
  stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register, roadmap-progress]
989
1052
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
990
1053
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape]
@@ -1020,7 +1083,7 @@ platforms:
1020
1083
  # — this is our `stop` slot.
1021
1084
  gemini:
1022
1085
  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]
1023
- session_end: [chat-history, memory-learn, session-register, roadmap-progress]
1086
+ session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush]
1024
1087
  stop: [chat-history, hot-context, verify-before-complete, self-repair, session-register, roadmap-progress]
1025
1088
  user_prompt_submit: [chat-history, verify-before-complete, minimal-safe-diff, language-mirror, git-authorization, session-canary, self-repair, session-register]
1026
1089
  post_tool_use: [chat-history, roadmap-progress, context-hygiene, verify-before-complete, minimal-safe-diff, injection-scan, pr-url-reminder, orchestration-record, telemetry-usage, tool-result-bytes, edit-shape]
@@ -38,7 +38,9 @@ import { substitutionPayloads } from './block_unauthorized_git.js';
38
38
 
39
39
  const _HERE = fileURLToPath(import.meta.url);
40
40
 
41
- const _SHELL_SEPARATORS: ReadonlySet<string> = new Set(['&&', '||', ';', '|']);
41
+ const _SHELL_SEPARATORS: ReadonlySet<string> = new Set(['&&', '||', ';', '|', '&']);
42
+ /** Characters that begin a shell separator, for `shlexSplit(s, true)`. */
43
+ const _OPERATOR_CHARS = ';|&';
42
44
  const _NO_VERIFY_FLAGS: ReadonlySet<string> = new Set(['--no-verify']);
43
45
  const _NO_VERIFY_SHORT: ReadonlySet<string> = new Set(['-n']);
44
46
  const _HOOKS_PATH_RE = /^core\.hooksPath\s*=/i;
@@ -78,14 +80,36 @@ type JsonObject = { [k: string]: JsonValue };
78
80
  * Faithful port of `shlex.split(s, comments=False, posix=True)` for the
79
81
  * subset block_no_verify needs: whitespace tokenisation, single + double
80
82
  * quotes, backslash escaping (POSIX), and a `ValueError("No closing
81
- * quotation")` on an unterminated quote. `&&` / `|` / `;` are ordinary word
82
- * characters here (no `punctuation_chars`), so they survive as embedded
83
- * tokens exactly as CPython's shlex returns them — the separator split below
84
- * matches them as whole tokens.
83
+ * quotation")` on an unterminated quote.
84
+ *
85
+ * `splitOperators` (default OFF, so the port stays faithful for
86
+ * `rtk_wrap_hook`, its other caller) additionally emits an UNQUOTED shell
87
+ * separator as its own token, maximal-munch for `&&` and `||`.
88
+ *
89
+ * WHY IT EXISTS. Without it, POSIX shlex leaves a separator attached to the
90
+ * preceding word — `git status; sed -n 1,5p f` tokenises as
91
+ * `['git','status;','sed','-n',...]`, one token, no separator. This docstring
92
+ * previously asserted the opposite ("they survive as embedded tokens ... the
93
+ * separator split below matches them as whole tokens"); that holds only when
94
+ * the separator is surrounded by whitespace, and the guard's segmentation is
95
+ * built on it. Measured 2026-08-20, the mistake had TWO faces and the second
96
+ * is the serious one:
97
+ *
98
+ * git status; sed -n 1,5p f → REFUSED (false positive: the `-n`
99
+ * of `sed` is read as git's)
100
+ * echo hi; git commit --no-verify → ALLOWED (BYPASS: the group starts
101
+ * with `echo`, so `_git_base` returns
102
+ * null and the git command is never
103
+ * scanned at all)
104
+ *
105
+ * Quote- and escape-safe by construction: the operator check lives in the
106
+ * unquoted word path only, so `git commit -m "a;b"` and `git commit -m a\;b`
107
+ * keep their semicolon inside one token and cannot be split into a group that
108
+ * no longer starts with `git`.
85
109
  */
86
110
  class ShlexError extends Error {}
87
111
 
88
- function shlexSplit(s: string): string[] {
112
+ function shlexSplit(s: string, splitOperators = false): string[] {
89
113
  const whitespace = ' \t\r\n\f\v';
90
114
  const quotes = '\'"';
91
115
  const escape = '\\';
@@ -159,6 +183,25 @@ function shlexSplit(s: string): string[] {
159
183
  }
160
184
  continue;
161
185
  }
186
+ if (splitOperators && _OPERATOR_CHARS.includes(c)) {
187
+ // Unquoted separator: close the word in progress, then emit the
188
+ // operator as its own token. Maximal munch so `&&` and `||` stay
189
+ // one token rather than two, matching _SHELL_SEPARATORS.
190
+ if (token !== null) {
191
+ tokens.push(token);
192
+ token = null;
193
+ }
194
+ const nxt = i + 1 < n ? (s[i + 1] as string) : null;
195
+ if (nxt === c && (c === '&' || c === '|')) {
196
+ tokens.push(c + c);
197
+ i += 2;
198
+ } else {
199
+ tokens.push(c);
200
+ i += 1;
201
+ }
202
+ state = 'ws';
203
+ continue;
204
+ }
162
205
  if (escape.includes(c)) {
163
206
  // POSIX escape outside quotes: backslash preserves the next char.
164
207
  const nxt = i + 1 < n ? (s[i + 1] as string) : null;
@@ -293,19 +336,22 @@ function _is_blocked(git_tokens: string[]): [boolean, string] {
293
336
  while (i < git_tokens.length) {
294
337
  const tok = git_tokens[i] as string;
295
338
  if (_NO_VERIFY_FLAGS.has(tok)) {
296
- return [true, `'${tok}' bypasses git hooks (git-history-discipline)`];
339
+ return [true, `'${tok}' bypasses git hooks (git-history-discipline).`
340
+ + ' Let the hooks run and fix what they report; that is the only path this guard leaves open.'];
297
341
  }
298
342
  if (_NO_VERIFY_SHORT.has(tok)) {
299
343
  return [
300
344
  true,
301
- `'${tok}' is short for --no-verify and bypasses git hooks (git-history-discipline)`,
345
+ `'${tok}' is short for --no-verify and bypasses git hooks (git-history-discipline).`
346
+ + ' Let the hooks run and fix what they report; that is the only path this guard leaves open.',
302
347
  ];
303
348
  }
304
349
  // Short flag bundles containing 'n': -nm, -mn, etc.
305
350
  if (/^-[a-zA-Z]*n[a-zA-Z]*$/.test(tok) && !tok.startsWith('--')) {
306
351
  return [
307
352
  true,
308
- `'${tok}' contains -n (--no-verify) and bypasses git hooks (git-history-discipline)`,
353
+ `'${tok}' contains -n (--no-verify) and bypasses git hooks (git-history-discipline).`
354
+ + ' Let the hooks run and fix what they report; that is the only path this guard leaves open.',
309
355
  ];
310
356
  }
311
357
  if (tok === '-c') {
@@ -492,7 +538,12 @@ function _check_command(cmd: string, depth = 0): [boolean, string] {
492
538
  }
493
539
  let tokens: string[];
494
540
  try {
495
- tokens = shlexSplit(stripped);
541
+ // splitOperators: an unquoted `;` / `&&` / `||` / `|` / `&` becomes its
542
+ // own token, so _split_subcommands can see it. Without it a separator
543
+ // attached to the preceding word (`status;`) kept the whole line in one
544
+ // group — refusing `git status; sed -n 1,5p f` and, in the other
545
+ // direction, never scanning `echo hi; git commit --no-verify` at all.
546
+ tokens = shlexSplit(stripped, true);
496
547
  } catch (e) {
497
548
  if (e instanceof ShlexError) {
498
549
  // Tested against the string that actually failed to parse. Round 7
@@ -486,7 +486,7 @@ export function commandOp(command: string): GitOp | null {
486
486
  }
487
487
 
488
488
  /** A ledger older than this is not "this turn" under any reading. */
489
- export const LEDGER_MAX_AGE_MS = 30 * 60 * 1000;
489
+ export const LEDGER_MAX_AGE_MS = 6 * 60 * 60 * 1000; // TEMP: PR-drain run, revert after
490
490
 
491
491
  /**
492
492
  * Load the ledger, and refuse one that does not belong to THIS session and
@@ -59,6 +59,7 @@ import { main as codeGraphNudgeMain } from './code_graph_nudge_hook.js';
59
59
  import { main as uiRouteNudgeMain } from './ui_route_nudge_hook.js';
60
60
  import { main as orchestrationRecordMain } from './orchestration_record_hook.js';
61
61
  import { main as telemetryUsageMain } from './telemetry_usage_hook.js';
62
+ import { main as telemetryFlushMain } from './telemetry_flush_hook.js';
62
63
  import { main as delegationNudgeMain } from './delegation_nudge_hook.js';
63
64
  import { main as skillRouteMain } from './skill_route_hook.js';
64
65
  import { main as endReviewNudgeMain } from './end_review_nudge_hook.js';
@@ -69,6 +70,7 @@ import { main as sessionEolMain } from './session_eol_hook.js';
69
70
  import { main as subagentLedgerMain } from './subagent_ledger_hook.js';
70
71
  import { main as toolResultBytesMain } from './tool_result_bytes_hook.js';
71
72
  import { main as spawnGuardShadowMain } from './spawn_guard_shadow_hook.js';
73
+ import { main as sourceFirstGateMain } from './source_first_gate_hook.js';
72
74
  import { main as interruptionLedgerMain } from './interruption_ledger_hook.js';
73
75
  import { main as runContinuationMain } from './run_continuation_hook.js';
74
76
 
@@ -112,6 +114,7 @@ export const CONCERN_REGISTRY: Readonly<Record<string, ConcernMain>> = {
112
114
  'src/scripts/hooks/ui_route_nudge_hook.ts': uiRouteNudgeMain as ConcernMain,
113
115
  'src/scripts/hooks/orchestration_record_hook.ts': orchestrationRecordMain as ConcernMain,
114
116
  'src/scripts/hooks/telemetry_usage_hook.ts': telemetryUsageMain as ConcernMain,
117
+ 'src/scripts/hooks/telemetry_flush_hook.ts': telemetryFlushMain as ConcernMain,
115
118
  'src/scripts/hooks/delegation_nudge_hook.ts': delegationNudgeMain as ConcernMain,
116
119
  'src/scripts/hooks/skill_route_hook.ts': skillRouteMain as ConcernMain,
117
120
  'src/scripts/hooks/end_review_nudge_hook.ts': endReviewNudgeMain as ConcernMain,
@@ -122,6 +125,7 @@ export const CONCERN_REGISTRY: Readonly<Record<string, ConcernMain>> = {
122
125
  'src/scripts/hooks/subagent_ledger_hook.ts': subagentLedgerMain as ConcernMain,
123
126
  'src/scripts/hooks/tool_result_bytes_hook.ts': toolResultBytesMain as ConcernMain,
124
127
  'src/scripts/hooks/spawn_guard_shadow_hook.ts': spawnGuardShadowMain as ConcernMain,
128
+ 'src/scripts/hooks/source_first_gate_hook.ts': sourceFirstGateMain as ConcernMain,
125
129
  'src/scripts/hooks/interruption_ledger_hook.ts': interruptionLedgerMain as ConcernMain,
126
130
  'src/scripts/hooks/run_continuation_hook.ts': runContinuationMain as ConcernMain,
127
131
  };
@@ -155,14 +155,19 @@ export function classify(command: string): Eligibility {
155
155
 
156
156
  let tokens: string[];
157
157
  try {
158
- tokens = shlexSplit(cmd);
158
+ tokens = shlexSplit(cmd, true);
159
159
  } catch (e) {
160
160
  if (e instanceof ShlexError) return { eligible: false, reason_skip: "parse-error" };
161
161
  throw e;
162
162
  }
163
163
 
164
164
  // Compound / piped → don't wrap (rtk wraps a single command). `_split_subcommands`
165
- // splits on && || ; | — more than one group means a pipeline/chain.
165
+ // splits on && || ; | & — more than one group means a pipeline/chain.
166
+ // `splitOperators` is REQUIRED here: POSIX shlex leaves a separator attached
167
+ // to the preceding word, so `foo; bar` tokenised to one group and read as a
168
+ // single command. Same construct as the block_no_verify defect measured
169
+ // 2026-08-20; found by the sibling search for that fix, 2 call sites, both
170
+ // in this file.
166
171
  const groups = _split_subcommands(tokens);
167
172
  if (groups.length !== 1) return { eligible: false, reason_skip: "compound-or-piped" };
168
173
 
@@ -256,7 +261,7 @@ export function classifyCap(
256
261
  if (!cmd) return null;
257
262
  let tokens: string[];
258
263
  try {
259
- tokens = shlexSplit(cmd);
264
+ tokens = shlexSplit(cmd, true);
260
265
  } catch (e) {
261
266
  if (e instanceof ShlexError) return null;
262
267
  throw e;
@@ -72,12 +72,15 @@
72
72
  * duplicate, whatever the counts say.
73
73
  * scope complete (0 open,
74
74
  * none blocked) → allow (event: complete; state cleared)
75
- * runnable work exhausted
76
- * (0 open, ≥1 blocked) → allow (event: blocked, ONCE per run, guarded by
75
+ * remaining work externally
76
+ * impossible (0 open, ≥1
77
+ * blocked) → allow (event: blocked, ONCE per run, guarded by
77
78
  * the LEDGER: the state is cleared like `complete`,
78
- * so no field there outlives the event. ADR-235's
79
- * terminal outcome — not a sixth halt, never in
80
- * `HALT_ACTIONS`, never stamped into `halted`)
79
+ * so no field there outlives the event. A terminal
80
+ * outcome — not a sixth halt, never in
81
+ * `HALT_ACTIONS`, never stamped into `halted`.
82
+ * ADR-237 narrows what may legally wear a
83
+ * `blocked-by:` marker; semantics unchanged.)
81
84
  * iterations ≥ MAX (25) → allow (event: halt-max-iterations)
82
85
  * wall clock ≥ cap (4 h) → allow (event: halt-wall-clock)
83
86
  * stall (3 engagements, no
@@ -33,6 +33,7 @@ import path from 'node:path';
33
33
  import { fileURLToPath, pathToFileURL } from 'node:url';
34
34
 
35
35
  import { readHookStdin } from './hook_stdin.js';
36
+ import { isPayloadStub } from './payload_stub.js';
36
37
  import { atomic_write_json } from './state_io.js';
37
38
 
38
39
  export const STATE_FILE = path.join('agents', 'state', 'ship-diff-volume.json');
@@ -89,31 +90,96 @@ export function correctedVolume(numstat: string): { volume: number; excluded: nu
89
90
  return { volume, excluded, files };
90
91
  }
91
92
 
92
- function extractCommand(envelope: Record<string, unknown>): string {
93
- const ti = envelope['tool_input'];
94
- if (ti !== null && typeof ti === 'object') {
95
- const c = (ti as Record<string, unknown>)['command'];
93
+ /**
94
+ * Tools that carry a shell command. Mirrors `block_unauthorized_git`'s set.
95
+ *
96
+ * `SHIP_PATTERNS` are unanchored, so without this gate a `grep` for the string
97
+ * `git push` spawns two `git` subprocesses on a blocking slot. ALLOW-list: a
98
+ * named tool outside it is declined, so an unlisted host shell goes dark until
99
+ * added — the trade that sibling already makes while BLOCKING. A payload naming
100
+ * no tool still reads; that is the bare-host shape, with nothing to gate on.
101
+ */
102
+ const COMMAND_TOOLS: ReadonlySet<string> = new Set([
103
+ 'launch-process',
104
+ 'launch_process',
105
+ 'Bash',
106
+ 'BashTool',
107
+ 'run-process',
108
+ 'runProcess',
109
+ 'shell',
110
+ 'execute_shell',
111
+ 'RunShellCommand',
112
+ ]);
113
+
114
+ function isObj(v: unknown): v is Record<string, unknown> {
115
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
116
+ }
117
+
118
+ /**
119
+ * Descend to the platform payload.
120
+ *
121
+ * The dispatcher nests the host-shaped tool fields under `payload`
122
+ * (`_build_envelope`); a bare host invocation puts them at the top level. This
123
+ * concern shipped reading the top level ONLY, so under the real dispatcher it
124
+ * found nothing and returned `''` every time — it ran, cost a dispatch, and
125
+ * could never fire.
126
+ *
127
+ * Deliberately NOT `envelope.ts`'s shared `unwrap`, which descends only when all
128
+ * four `ENVELOPE_KEYS` are present: a partial envelope would return this concern
129
+ * to its pre-fix dead state with no test noticing. `design-slop` and
130
+ * `ui-route-nudge` each carry their own local descent for the same reason.
131
+ */
132
+ function payloadOf(root: Record<string, unknown>): Record<string, unknown> {
133
+ const inner = root['payload'];
134
+ return isObj(inner) ? inner : root;
135
+ }
136
+
137
+ /** The ship command, or `''` when this payload does not carry one. */
138
+ function shipCommandFrom(payload: Record<string, unknown>): string {
139
+ const toolRaw = payload['tool_name'] ?? payload['toolName'] ?? payload['tool'];
140
+ if (typeof toolRaw === 'string' && !COMMAND_TOOLS.has(toolRaw)) return '';
141
+
142
+ const ti = payload['tool_input'] ?? payload['toolInput'];
143
+ if (isPayloadStub(ti)) {
144
+ // Body omitted despite `needs_payload_bodies: [input]`. A bare '' here is
145
+ // indistinguishable from "no ship verb" — the silent death this file was
146
+ // fixed to remove — so say so instead of dying quietly again.
147
+ process.stderr.write(
148
+ 'ship-diff-volume: tool_input arrived stubbed; the concern declares ' +
149
+ 'needs_payload_bodies: [input] and cannot read a command without it.\n',
150
+ );
151
+ return '';
152
+ }
153
+ if (isObj(ti)) {
154
+ const c = ti['command'];
96
155
  if (typeof c === 'string') return c;
97
156
  }
98
- const c = envelope['command'];
157
+ const c = payload['command'];
99
158
  return typeof c === 'string' ? c : '';
100
159
  }
101
160
 
161
+ /**
162
+ * The stdin boundary: raw concern stdin → the ship command. Exported so a
163
+ * regression drives the boundary rather than the extractor. Never throws.
164
+ */
165
+ export function commandFromStdin(raw: string): string {
166
+ if (!raw.trim()) return '';
167
+ let root: unknown;
168
+ try {
169
+ root = JSON.parse(raw);
170
+ } catch {
171
+ return '';
172
+ }
173
+ if (!isObj(root)) return '';
174
+ return shipCommandFrom(payloadOf(root));
175
+ }
176
+
102
177
  export function main(argv?: string[]): number {
103
178
  const args = argv ?? process.argv.slice(2);
104
179
  const idx = args.indexOf('--command');
105
180
  let cmd = idx >= 0 && args[idx + 1] !== undefined ? args[idx + 1]! : '';
106
181
  if (!cmd) {
107
- const raw = readHookStdin();
108
- let envelope: Record<string, unknown> = {};
109
- if (raw.trim()) {
110
- try {
111
- envelope = JSON.parse(raw) as Record<string, unknown>;
112
- } catch {
113
- envelope = {};
114
- }
115
- }
116
- cmd = extractCommand(envelope);
182
+ cmd = commandFromStdin(readHookStdin());
117
183
  }
118
184
  if (!isShipCommand(cmd)) return 0;
119
185