@event4u/agent-config 15.0.0 → 16.0.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 (78) 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 +142 -348
  5. package/dist/agent-src/commands/roadmap/create.md +3 -2
  6. package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +50 -0
  7. package/dist/agent-src/rules/user-interaction.md +31 -30
  8. package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
  9. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  10. package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-H32DGUJH.js} +14 -89
  11. package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
  12. package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
  13. package/dist/cli-delegate/{chunk-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
  14. package/dist/cli-delegate/cmd_conformance.js +831 -272
  15. package/dist/cli-delegate/cmd_explain.js +4 -2
  16. package/dist/cli-delegate/cmd_handoff.js +2 -2
  17. package/dist/cli-delegate/cmd_packs_active.js +2 -1
  18. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  19. package/dist/cli-delegate/cmd_settings_set.js +13 -0
  20. package/dist/discovery/deprecation-report.md +1 -1
  21. package/dist/discovery/discovery-manifest.json +5 -5
  22. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  23. package/dist/discovery/discovery-manifest.summary.md +1 -1
  24. package/dist/discovery/dormancy-report.md +1 -1
  25. package/dist/discovery/orphan-report.md +1 -1
  26. package/dist/discovery/packs.json +2 -2
  27. package/dist/discovery/trust-report.md +1 -1
  28. package/dist/discovery/workspaces.json +2 -2
  29. package/dist/hooks/dispatch.js +1274 -571
  30. package/dist/install/install.mjs +1 -0
  31. package/dist/mcp/registry-manifest.json +2 -2
  32. package/dist/mcp/server.json +2 -2
  33. package/dist/mcp/server.mjs +1218 -632
  34. package/dist/scripts/_lib/installed_lock.js +1 -1
  35. package/dist/server/io/yamlIO.js +50 -0
  36. package/package.json +1 -1
  37. package/src/config/continuity-surface.json +7 -0
  38. package/src/config/gate-violation-baselines.json +3 -3
  39. package/src/config/host-injection-effect.json +1 -1
  40. package/src/config/preamble-payload-budget.json +12 -7
  41. package/src/config/preamble-payload-exceptions.json +28 -0
  42. package/src/scripts/_dispatch.bash +3 -1
  43. package/src/scripts/_lib/base_ref_payload.ts +315 -0
  44. package/src/scripts/_lib/dropped_decision.ts +142 -0
  45. package/src/scripts/_lib/installed_lock.ts +1 -1
  46. package/src/scripts/_lib/measured_payload_ceiling.ts +424 -0
  47. package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
  48. package/src/scripts/_lib/release_findings_ingest.ts +324 -0
  49. package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
  50. package/src/scripts/_lib/review_baseline.ts +254 -0
  51. package/src/scripts/_lib/review_skipped_record.ts +61 -1
  52. package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
  53. package/src/scripts/_lib/turn_end_refusals.ts +51 -4
  54. package/src/scripts/check_beta_review_markers.ts +35 -4
  55. package/src/scripts/check_finding_dispositions.ts +167 -16
  56. package/src/scripts/check_held_object_arrivals.ts +381 -0
  57. package/src/scripts/check_preamble_payload_budget.ts +399 -122
  58. package/src/scripts/check_release_published.ts +1 -1
  59. package/src/scripts/check_reply_consistency.ts +86 -17
  60. package/src/scripts/hook_manifest.json +1 -1
  61. package/src/scripts/hook_manifest.yaml +39 -3
  62. package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
  63. package/src/scripts/hooks/concern_registry.ts +2 -0
  64. package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
  65. package/src/scripts/hooks/review_baseline_hook.ts +130 -0
  66. package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
  67. package/src/scripts/hooks_doctor.ts +4 -1
  68. package/src/scripts/install.ts +1 -1
  69. package/src/scripts/interruption_report.ts +8 -2
  70. package/src/scripts/janitor.ts +8 -1
  71. package/src/scripts/measure_turn_end_gate.ts +302 -5
  72. package/src/scripts/release.ts +52 -64
  73. package/src/scripts/release_drill.ts +193 -14
  74. package/src/scripts/release_env.ts +1 -1
  75. package/src/scripts/release_publication.ts +1 -1
  76. package/src/scripts/report_held_object_arrivals.ts +456 -0
  77. package/src/scripts/sync_agent_settings.ts +140 -18
  78. package/src/server/io/yamlIO.ts +48 -0
@@ -57,7 +57,7 @@ function _package_name(): string {
57
57
  *
58
58
  * This probe used to answer local-OR-remote, and that made the gate reply
59
59
  * "tagged, therefore published" to the exact state it exists to detect. A tag
60
- * created by `release.ts` step 8 whose push then failed sits in the local
60
+ * created by `release.ts` step 9 whose push then failed sits in the local
61
61
  * repository while nothing has shipped: `publish-npm.yml` triggers on
62
62
  * `push: tags:`, so a tag that never reached the remote published nothing.
63
63
  * Measured 2026-08-20 on 14.6.0 — main carried `package.json` 14.6.0, the tag
@@ -82,6 +82,10 @@ function _strip_codespans(line: string): string {
82
82
  }
83
83
 
84
84
  /** Split on \n / \r\n / \r, dropping a single trailing empty element. */
85
+ export function splitAndMask(text: string): string[] {
86
+ return mask_fences(_splitlines(text));
87
+ }
88
+
85
89
  function _splitlines(text: string): string[] {
86
90
  if (text === '') return [];
87
91
  const parts = text.split(/\r\n|\r|\n/);
@@ -143,6 +147,64 @@ export function find_option_blocks(text: string): OptionBlock[] {
143
147
  return blocks;
144
148
  }
145
149
 
150
+ /** A recommendation line found in a block's adjacency window. */
151
+ export interface RecommendationLine {
152
+ /** 1-based line of the recommendation. */
153
+ line: number;
154
+ /** The number it names. Not necessarily one of the block's options. */
155
+ num: number;
156
+ }
157
+
158
+ /**
159
+ * The recommendation line(s) sitting in ONE block's adjacency window.
160
+ *
161
+ * Extracted from `check_reply` rather than copied, because it has a second
162
+ * caller: `turn_end_gate_hook`'s `pending-decision` detector needs the same
163
+ * question this answers — is this numbered block an ASK, or is it a narrative
164
+ * list? Iron Law 1 says the recommendation line IS the ask, so a block without
165
+ * one was never a decision put to the user.
166
+ *
167
+ * That distinction is not decoration. Replayed over this repository's own 592
168
+ * assistant turns, a detector keyed on the block alone fired 29 times and 8 of
169
+ * those blocks carried no recommendation line anywhere near them — plans,
170
+ * findings lists, ordinary enumerations. Requiring the line removes that whole
171
+ * class, and it removes it by applying the rule the gate already cites rather
172
+ * than by adding a heuristic beside it.
173
+ *
174
+ * Returns every recommendation in the window, not the first: `check_reply`
175
+ * treats two as its own finding, and a caller that only wanted existence can
176
+ * read `.length > 0`.
177
+ */
178
+ export function recommendationsUnder(
179
+ text: string,
180
+ block: OptionBlock,
181
+ maskedLines?: readonly string[],
182
+ ): RecommendationLine[] {
183
+ // `maskedLines` lets a caller with several blocks over one text mask once
184
+ // instead of once per block. It is the SAME array `check_reply` and
185
+ // `find_option_blocks` compute; passing anything else silently changes the
186
+ // line numbers this returns, which is why it is an optimisation parameter
187
+ // and not part of the contract.
188
+ const lines = maskedLines ?? mask_fences(_splitlines(text));
189
+ const recs: RecommendationLine[] = [];
190
+ let inspected = 0;
191
+ for (let idx = block.endLine; idx < lines.length && inspected < REC_ADJACENCY_WINDOW; idx++) {
192
+ const line = lines[idx]!;
193
+ if (line.trim() === '') {
194
+ continue; // blank lines do not consume the adjacency window
195
+ }
196
+ if (HEADING_RE.test(line) || OPTION_LINE_RE.test(line)) {
197
+ break; // a new heading or a new options block ends this block's window
198
+ }
199
+ inspected += 1;
200
+ const m = REC_LINE_RE.exec(line);
201
+ if (m) {
202
+ recs.push({ line: idx + 1, num: parseInt(m[1]!, 10) });
203
+ }
204
+ }
205
+ return recs;
206
+ }
207
+
146
208
  /**
147
209
  * Core check — the exported function tests drive. Returns all findings for
148
210
  * one reply draft; empty array = the draft is consistent.
@@ -166,22 +228,7 @@ export function check_reply(text: string): Finding[] {
166
228
  // Per-block: exactly one adjacent recommendation line, number in range.
167
229
  const blocks = find_option_blocks(text);
168
230
  for (const block of blocks) {
169
- const recs: Array<{ line: number; num: number }> = [];
170
- let inspected = 0;
171
- for (let idx = block.endLine; idx < lines.length && inspected < REC_ADJACENCY_WINDOW; idx++) {
172
- const line = lines[idx]!;
173
- if (line.trim() === '') {
174
- continue; // blank lines do not consume the adjacency window
175
- }
176
- if (HEADING_RE.test(line) || OPTION_LINE_RE.test(line)) {
177
- break; // a new heading or a new options block ends this block's window
178
- }
179
- inspected += 1;
180
- const m = REC_LINE_RE.exec(line);
181
- if (m) {
182
- recs.push({ line: idx + 1, num: parseInt(m[1]!, 10) });
183
- }
184
- }
231
+ const recs = recommendationsUnder(text, block);
185
232
  if (recs.length === 0) {
186
233
  findings.push({
187
234
  line: block.startLine,
@@ -421,7 +468,24 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
421
468
  return 2;
422
469
  }
423
470
 
471
+ // Bundle-safety: never auto-run when inlined into an esbuild bundle, where
472
+ // every module shares the bundle's `import.meta.url`.
473
+ //
474
+ // This module is not a hook, and until `turn_end_gate_hook` began importing
475
+ // `find_option_blocks` / `recommendationsUnder` from it, it was never inside
476
+ // one. It is now pulled into `dist/hooks/dispatch.js`, where without this guard
477
+ // the `import.meta.url === argvUrl` comparison below is TRUE for every bundled
478
+ // module — so importing this file would call `process.exit(main())` and take the
479
+ // whole dispatch down at import time. The build banner's `.__direct__` argv
480
+ // rewrite also prevents it, and a comment in the gate says so; a guard the other
481
+ // bundled entries all carry is the load-bearing half, and depending on the
482
+ // banner alone is depending on something no test pins.
483
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
484
+
424
485
  function _isCliEntry(): boolean {
486
+ if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) {
487
+ return false;
488
+ }
425
489
  if (process.argv[1] === undefined) {
426
490
  return false;
427
491
  }
@@ -442,7 +506,12 @@ function _isCliEntry(): boolean {
442
506
  }
443
507
  }
444
508
 
445
- if (_isCliEntry() || process.argv[1] === _HERE) {
509
+ // The `|| process.argv[1] === _HERE` arm is the reason the bundle check lives in
510
+ // BOTH places: inside a bundle `_HERE` resolves to the bundle's own path and so
511
+ // does `process.argv[1]`, so this arm would be true and would route straight past
512
+ // a guard that only sat in `_isCliEntry`.
513
+ const _IN_BUNDLE = typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__;
514
+ if (!_IN_BUNDLE && (_isCliEntry() || process.argv[1] === _HERE)) {
446
515
  process.exit(main());
447
516
  }
448
517
 
@@ -1 +1 @@
1
- {"fingerprint":"33e5cccd:90644","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","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","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","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","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","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","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","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","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":"98bf3b44:92876","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},"review-baseline":{"script":"src/scripts/hooks/review_baseline_hook.ts","args":[],"fail_closed":false,"severity":"advisory"},"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","review-baseline","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","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","review-baseline","journal-record"],"session_end":["chat-history","memory-learn","session-register","roadmap-progress","telemetry-flush","journal-record"],"stop":["chat-history","verify-before-complete","team-review-gate","end-review-nudge","turn-end-gate","self-repair","session-register","session-eol","interruption-ledger","roadmap-progress","design-pass-stop","suggestion-capture","journal-record","run-continuation"],"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","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","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","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","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","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","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"}}}}
@@ -919,13 +919,49 @@ concerns:
919
919
  # about it. The saving is the module load rather than a large read, which is
920
920
  # the smaller of the two flags below it and is stated as such.
921
921
  skip_on_refusal_retry: true
922
+ # road-to-a-question-that-survives-the-turn Phase 2 — the session baseline
923
+ # `end-review-nudge` subtracts. CAPTURE ONLY: one small JSON file per session
924
+ # under agents/runtime/state/review-baseline/ (gitignored), holding HEAD and
925
+ # the count of non-doc lines already dirty when the session began. It emits no
926
+ # context, never warns, and always exits 0.
927
+ #
928
+ # WHY. `end_review_nudge_hook`'s own header records that a dirty tree
929
+ # predating the session is charged to the session, and names the reason it was
930
+ # left: "no such baseline exists anywhere a `stop` concern can read it". That
931
+ # was true of a one-concern phase and stale by the time it mattered —
932
+ # session_start is bound on seven hosts here. Measured cost of leaving it: on
933
+ # an accumulator branch the nudge read 1,771 lines of two crashed sessions'
934
+ # uncommitted work and fired every turn.
935
+ #
936
+ # The counting function is IMPORTED from the nudge, so the baseline and the
937
+ # measurement cannot drift on what a non-doc mutated line is.
938
+ #
939
+ # BOUND ON `claude` ALONE, and narrowly on purpose. Seven hosts carry
940
+ # session_start, but `end-review-nudge` — the only reader — is bound on
941
+ # claude's stop slot and nowhere else (cowork refuses it explicitly). Binding
942
+ # the writer everywhere would spend ~91 ms of git per session start on six
943
+ # hosts to produce a file nothing opens. It is dropped for the `worker` role
944
+ # alongside its reader, for the same reason. Where the baseline is absent the
945
+ # nudge reads `absent` and degrades to exactly its previous behaviour,
946
+ # recorded in its telemetry row rather than inferred. fail_closed: false.
947
+ review-baseline:
948
+ script: src/scripts/hooks/review_baseline_hook.ts
949
+ args: []
950
+ fail_closed: false
951
+ severity: advisory
922
952
  # road-to-conformance-round5 Phase 3 — the FIRST concern that can refuse a
923
953
  # turn-END. Round 5 measured the reason: both blocking carriers reached zero
924
954
  # violations, neither advisory carrier did, and 19 language violations
925
955
  # survived a pin that had fired 26-35 seconds earlier. Refusal-capable
926
956
  # intercepts enforce; context injection requests.
927
957
  #
928
- # Two detectors on one guard — promissory closing (20 measured) and language
958
+ # FIVE detectors on one guard — promissory closing, language mismatch,
959
+ # unverified edit, unsettled completion claim, and a decision the turn asked
960
+ # and then dropped. This said "Two" and named the first pair, which was true
961
+ # of the concern on the day it was registered and of nothing since; the
962
+ # authority is `DetectorId` in the hook, and the demotion contract carries the
963
+ # per-detector bars. Original note, kept because its reason still holds:
964
+ # promissory closing (20 measured) and language
929
965
  # mismatch (19 measured) — because building the unsafe part twice is how a
930
966
  # second detector becomes a second outage.
931
967
  #
@@ -1272,7 +1308,7 @@ roles:
1272
1308
  # contacts, so a worker turn that ends in a question to its orchestrator
1273
1309
  # would be recorded as a contact that never reached a human, inflating the
1274
1310
  # exact baseline Phase 0 pre-registers.
1275
- drop: [delegation-nudge, skill-route, end-review-nudge, council-availability, team-review-gate, self-repair, session-eol, interruption-ledger]
1311
+ drop: [delegation-nudge, skill-route, end-review-nudge, review-baseline, council-availability, team-review-gate, self-repair, session-eol, interruption-ledger]
1276
1312
 
1277
1313
  platforms:
1278
1314
  augment:
@@ -1283,7 +1319,7 @@ platforms:
1283
1319
  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]
1284
1320
 
1285
1321
  claude:
1286
- 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]
1322
+ session_start: [chat-history, hot-context, handoff-context, session-canary, council-availability, telemetry-disclosure, first-run-gate, onboarding-gate, verify-before-complete, minimal-safe-diff, profile-staleness, wrapper-freshness, surface-probe, session-register, review-baseline, journal-record]
1287
1323
  session_end: [chat-history, memory-learn, session-register, roadmap-progress, telemetry-flush, journal-record]
1288
1324
  stop: [chat-history, verify-before-complete, team-review-gate, end-review-nudge, turn-end-gate, self-repair, session-register, session-eol, interruption-ledger, roadmap-progress, design-pass-stop, suggestion-capture, journal-record, run-continuation]
1289
1325
  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]
@@ -26,8 +26,18 @@
26
26
  * exported pure functions, and reports via the same stderr-block-message +
27
27
  * exit-code shape (0 allow · 1 block). `fail_closed: true` in the manifest —
28
28
  * ADR-127 permits more than one blocking hook; this is the second (after
29
- * block-no-verify). No agent-accessible override: the sole legitimate bypass
30
- * is the human-owned exception registry the deny message points to.
29
+ * block-no-verify).
30
+ *
31
+ * Coverage is limited to edit-tool envelopes whose explicit target is a
32
+ * kernel rule, and to the literal shell-argument write shapes recognized by
33
+ * `check_envelope`. Indirect or dynamically resolved writes may not be
34
+ * detected; this guard is not a sandbox and does not provide complete write
35
+ * mediation. The human-owned exception registry named in the denial message
36
+ * is the only LEGITIMATE bypass; it is not the only reachable one.
37
+ *
38
+ * The boundary is stated as an abstraction, without an inventory of concrete
39
+ * evasion families. `check_envelope` is the authoritative inspection point
40
+ * for exact behaviour.
31
41
  *
32
42
  * Exit codes (docs/contracts/hook-architecture-v1.md):
33
43
  * 0 — allow
@@ -69,6 +69,7 @@ import { main as delegationNudgeMain } from './delegation_nudge_hook.js';
69
69
  import { main as skillRouteMain } from './skill_route_hook.js';
70
70
  import { main as ruleInjectMain } from './rule_inject_hook.js';
71
71
  import { main as endReviewNudgeMain } from './end_review_nudge_hook.js';
72
+ import { main as reviewBaselineMain } from './review_baseline_hook.js';
72
73
  import { main as turnEndGateMain } from './turn_end_gate_hook.js';
73
74
  import { main as editShapeMain } from './edit_shape_hook.js';
74
75
  import { main as commentDisciplineMain } from './comment_discipline_hook.js';
@@ -132,6 +133,7 @@ export const CONCERN_REGISTRY: Readonly<Record<string, ConcernMain>> = {
132
133
  'src/scripts/hooks/skill_route_hook.ts': skillRouteMain as ConcernMain,
133
134
  'src/scripts/hooks/rule_inject_hook.ts': ruleInjectMain as ConcernMain,
134
135
  'src/scripts/hooks/end_review_nudge_hook.ts': endReviewNudgeMain as ConcernMain,
136
+ 'src/scripts/hooks/review_baseline_hook.ts': reviewBaselineMain as ConcernMain,
135
137
  'src/scripts/hooks/turn_end_gate_hook.ts': turnEndGateMain as ConcernMain,
136
138
  'src/scripts/hooks/edit_shape_hook.ts': editShapeMain as ConcernMain,
137
139
  'src/scripts/hooks/comment_discipline_hook.ts': commentDisciplineMain as ConcernMain,
@@ -28,17 +28,26 @@
28
28
  * (`git ls-files --others --exclude-standard`, each one READ from the
29
29
  * filesystem rather than diffed in a subprocess — see "Untracked files" below;
30
30
  * fixes a review finding, F6, that a brand-new file was previously invisible
31
- * to this count entirely). Chosen over a session-start SHA/timestamp
32
- * baseline because no such baseline exists anywhere a `stop` concern can
33
- * read it (recording one would be new session-wide state, out of this
34
- * roadmap phase's one-concern scope). Two consequences, stated rather than
35
- * hidden:
36
- * - Uncommitted changes that PREDATE this session (a dirty working tree
37
- * the user already had) are counted as if they were this session's
38
- * mutation. Per `commit-policy`, an agent almost never commits without
39
- * explicit permission, so "uncommitted since HEAD" is usually a good
40
- * proxy for "mutated this session" in practice — but it is a proxy, not
41
- * an exact measure.
31
+ * to this count entirely).
32
+ *
33
+ * SINCE 2026-09-11 THIS READING IS BASELINE-CORRECTED ON `claude`, and this
34
+ * block used to say the opposite — that a session-start baseline was rejected
35
+ * "because no such baseline exists anywhere a `stop` concern can read it". That
36
+ * was true of a one-concern roadmap phase and stale by the time it mattered:
37
+ * `session_start` is bound on seven hosts, and `review-baseline` now records
38
+ * HEAD plus the already-dirty non-doc line count there. `_lib/review_baseline.ts`
39
+ * cites this paragraph as the defect it closes, so leaving the paragraph
40
+ * standing would have made the two files contradict each other.
41
+ *
42
+ * What the correction does and does not cover:
43
+ * - Uncommitted changes that PREDATE the session are SUBTRACTED where a
44
+ * baseline is readable and its HEAD and measure still match. Absent,
45
+ * unreadable, head-moved or mixed-measure → the unsubtracted count, with
46
+ * the reason carried in the `review_skipped` row.
47
+ * - The baseline is written once per session and never refreshed, so from a
48
+ * session's first commit onward `head-moved` holds and the pre-existing
49
+ * tree is charged again. Over-reporting is the safe direction; the limit is
50
+ * real and is stated in `review_baseline.ts` rather than implied away.
42
51
  * - A session that DID commit mid-turn (one of `commit-policy`'s four
43
52
  * exceptions) moves HEAD, so mutations already committed are invisible
44
53
  * to this diff. Declared, not silently assumed away.
@@ -207,11 +216,20 @@
207
216
  * exit-2 warn onto an unverified propagation path is exactly the
208
217
  * speculative mapping `host_semantics.ts` exists to avoid.
209
218
  *
210
- * CONTRACT: never blocks THE ACTUAL TURN. The DISPATCHER-INTERNAL exit is 2
211
- * on a fire (never 1/BLOCK, never >=3), `fail_closed: false`; the HOST-FACING
212
- * exit on `claude` is 0 either way, per the proof above. Doc-only
213
- * diffs, a reviewer having run, or any unreadable/malformed input all
214
- * resolve to silence (dispatcher-internal exit 0), not a crash.
219
+ * CONTRACT: never REFUSES a turn. The DISPATCHER-INTERNAL exit is 2 on a fire
220
+ * (never 1/BLOCK, never >=3), `fail_closed: false`; the HOST-FACING exit on
221
+ * `claude` is 0 either way, per the proof above. Doc-only diffs, a reviewer
222
+ * having run, or any unreadable/malformed input all resolve to silence
223
+ * (dispatcher-internal exit 0), not a crash.
224
+ *
225
+ * WHAT THE PROOF ABOVE DOES NOT COVER. Every term of it is an exit code, so it
226
+ * settles refusal and nothing else. Whether the `additionalContext` payload
227
+ * this concern emits at `stop` causes the host to run the agent AGAIN is a
228
+ * separate question, and this tree does not answer it — see
229
+ * `docs/contracts/hook-architecture-v1.md` under the exit-code table, which
230
+ * names the measurement that would. So do not read the proof above as
231
+ * establishing that this concern leaves the turn alone; it establishes only
232
+ * that the concern never refuses it.
215
233
  */
216
234
  import { spawnSync } from 'node:child_process';
217
235
  import * as crypto from 'node:crypto';
@@ -220,7 +238,18 @@ import * as os from 'node:os';
220
238
  import * as path from 'node:path';
221
239
  import { fileURLToPath, pathToFileURL } from 'node:url';
222
240
 
223
- import { buildReviewSkippedLine, type MutationMeasure } from '../_lib/review_skipped_record.js';
241
+ import {
242
+ applyBaseline,
243
+ currentHeadSha,
244
+ readBaseline,
245
+ type BaselineFallback,
246
+ type BaselineOutcome,
247
+ } from '../_lib/review_baseline.js';
248
+ import {
249
+ buildReviewSkippedLine,
250
+ type BaselineApplication,
251
+ type MutationMeasure,
252
+ } from '../_lib/review_skipped_record.js';
224
253
  import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
225
254
  import { readHookStdin } from './hook_stdin.js';
226
255
  import { atomic_write_json, is_replay_mode } from './state_io.js';
@@ -606,6 +635,7 @@ export function appendReviewSkippedTelemetry(
606
635
  workspaceRoot: string,
607
636
  diffLines: number,
608
637
  mutationMeasure: MutationMeasure,
638
+ baseline?: BaselineApplication,
609
639
  ): void {
610
640
  if (is_replay_mode()) return;
611
641
  const ts = new Date().toISOString();
@@ -614,6 +644,7 @@ export function appendReviewSkippedTelemetry(
614
644
  mutation_measure: mutationMeasure,
615
645
  ts,
616
646
  id: crypto.randomUUID(),
647
+ baseline,
617
648
  });
618
649
  if (errors.length || line === null) return; // refused input — never write a broken line
619
650
  try {
@@ -694,6 +725,23 @@ function _read_stdin(): string {
694
725
  return readHookStdin();
695
726
  }
696
727
 
728
+ /**
729
+ * The one place the two spellings meet.
730
+ *
731
+ * `BaselineFallback` is kebab-cased because it is a TypeScript union read only
732
+ * in this process; `BaselineApplication` is snake_cased because it is written
733
+ * into an audit line and every other enum in that stream is. A hand-written
734
+ * ternary translated them until a review pointed out that `head-moved` and
735
+ * `head_moved` differ by one character in a conditional nobody would re-read —
736
+ * an exhaustive record makes a new fallback a compile error instead.
737
+ */
738
+ const FALLBACK_TO_TELEMETRY: Readonly<Record<BaselineFallback, BaselineApplication>> = {
739
+ absent: 'absent',
740
+ unreadable: 'unreadable',
741
+ 'head-moved': 'head_moved',
742
+ 'mixed-measure': 'mixed_measure',
743
+ };
744
+
697
745
  export function main(): number {
698
746
  const [envelope, payload] = unwrap(_read_stdin(), 'claude');
699
747
 
@@ -705,15 +753,43 @@ export function main(): number {
705
753
  const workspace_root =
706
754
  String(envelope['workspace_root'] ?? '').trim() || process.cwd();
707
755
 
708
- let diffLines: number;
756
+ let measuredLines: number;
709
757
  let mutationMeasure: MutationMeasure;
710
758
  try {
711
759
  const measured = totalNonDocMutatedLinesWithMeasure(workspace_root);
712
- diffLines = measured.lines;
760
+ measuredLines = measured.lines;
713
761
  mutationMeasure = measured.measure;
714
762
  } catch {
715
763
  return 0; // fail-open — never block the agent loop
716
764
  }
765
+
766
+ // The session key is derived HERE rather than after the threshold test,
767
+ // because the threshold is now taken on the SUBTRACTED count and the
768
+ // baseline is keyed on the session. That is a real reordering: one extra
769
+ // `sha256` of a string and one `readFileSync` of a ~100-byte JSON file now
770
+ // run on every stop, including the majority that end below threshold. The
771
+ // alternative — testing the unsubtracted count first as a cheap
772
+ // pre-filter — is wrong in the one direction that matters: on an
773
+ // accumulator branch EVERY turn clears the unsubtracted threshold, so the
774
+ // pre-filter would pass exactly when the subtraction is needed and save
775
+ // nothing.
776
+ const sessionKey = deriveSessionKey(envelope, payload);
777
+ const baselineOutcome: BaselineOutcome = applyBaseline(
778
+ measuredLines,
779
+ readBaseline(workspace_root, sessionKey),
780
+ // A THUNK, not a value: on the `absent` and `unreadable` branches the
781
+ // result is discarded, and those two are the steady state on every host
782
+ // without this nudge and on every session before its first baseline
783
+ // write. Passing the value eagerly spent an 11-14 ms `git rev-parse` on
784
+ // each of those stops for nothing.
785
+ () => currentHeadSha(workspace_root),
786
+ mutationMeasure,
787
+ );
788
+ const diffLines = baselineOutcome.lines;
789
+ const baselineApplication: BaselineApplication = baselineOutcome.applied
790
+ ? 'applied'
791
+ : FALLBACK_TO_TELEMETRY[baselineOutcome.fallback];
792
+
717
793
  if (diffLines <= MUTATION_LINE_THRESHOLD) {
718
794
  return 0; // below threshold, or a doc-only diff — nothing to nudge
719
795
  }
@@ -723,7 +799,6 @@ export function main(): number {
723
799
  // JSON parse below. A session that already fired stops here, at a
724
800
  // single `fs.existsSync` call, without re-reading a (potentially
725
801
  // large) transcript file it already knows the answer for.
726
- const sessionKey = deriveSessionKey(envelope, payload);
727
802
  if (hasFiredThisSession(workspace_root, sessionKey)) {
728
803
  return 0;
729
804
  }
@@ -756,7 +831,12 @@ export function main(): number {
756
831
  markFiredThisSession(workspace_root, sessionKey, new Date().toISOString());
757
832
 
758
833
  try {
759
- appendReviewSkippedTelemetry(workspace_root, diffLines, mutationMeasure);
834
+ appendReviewSkippedTelemetry(
835
+ workspace_root,
836
+ diffLines,
837
+ mutationMeasure,
838
+ baselineApplication,
839
+ );
760
840
  } catch {
761
841
  // never let a telemetry failure block or fail the turn
762
842
  }