1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Combine the project-tier hook sources [`super::settings::write_project_hooks`]
//! writes into one merge-ready `{"hooks": {...}}` value (issue #2003).
//!
//! Why: the daemon's managed-launch path spawns `claude --setting-sources
//! project,local` (see [`crate::core::model_inject::SETTING_SOURCES_FLAG`]),
//! which excludes the `user` tier where
//! [`crate::core::standalone::hooks::ensure_managed_hooks`] provisions the
//! trusty-mpm lifecycle triad (circuit breaker / audit log / dashboard
//! observability, via [`crate::core::standalone::hooks::mpm_hook_additions_with_exe`]).
//! Managed sessions therefore never fired those six events at all — only the
//! `trusty-memory` + PM-guard entries the OLD `write_project_hooks` wrote
//! (and it wrote them by REPLACING the entire `hooks` key, clobbering any
//! pre-existing foreign hooks too). This module folds the SAME triad
//! definition the user-tier writer uses into the project-tier write, so a
//! single source of truth backs both writers and they cannot drift apart
//! again, and exposes the matching identity predicate so the caller's
//! replace-by-identity strip (entry-level, per issue #2948) recognises every
//! source this module combines.
//! What: [`project_managed_hook_additions`] deep-merges the `trusty-memory`
//! block, the PM-enforcement guard, and the lifecycle triad into one value;
//! [`is_project_managed_hook_command`] recognises a command from ANY of those
//! three sources.
//! Test: `project_hooks_tests.rs`.
use ;
use crate;
/// Build the full set of trusty-mpm-owned hook additions for the project tier.
///
/// Why: see the module doc — this is the single place the three sources
/// combine, so [`super::settings::write_project_hooks`] stays a thin
/// read-strip-merge-write shell.
/// What: starts from [`TRUSTY_MEMORY_HOOKS`] (parsed; `UserPromptSubmit` +
/// `SessionStart`), inserts the [`pm_guard_hook_value`] `PreToolUse` block,
/// then appends every group from
/// [`mpm_hook_additions_with_exe`]`(None)`'s six-event lifecycle triad onto
/// the corresponding event array (deduping by deep equality, mirroring
/// [`trusty_common::claude_config::merge_hook_entries`]'s own semantics).
/// `PreToolUse` and `SessionStart` — sources for more than one of the three —
/// end up with multiple handler groups (PM-guard + tm-hook, or trusty-memory +
/// tm-hook) rather than one clobbering the other.
/// Test: `project_managed_hook_additions_combines_all_three_sources`,
/// `project_managed_hook_additions_is_stable_across_calls`.
pub
/// Recognise a hook command belonging to ANY of the three project-tier
/// sources [`project_managed_hook_additions`] combines.
///
/// Why: [`super::settings::write_project_hooks`]'s replace-by-identity strip
/// (via [`crate::core::standalone::hooks::strip_hook_entries_matching_for_events`])
/// needs a predicate broader than
/// [`crate::core::standalone::hooks::is_mpm_hook_command`] alone — that
/// predicate only recognises the lifecycle-triad `<exe> hook` shape, not the
/// `trusty-memory` or PM-guard commands this module also writes. Without the
/// broader predicate, re-running `write_project_hooks` would duplicate the
/// `trusty-memory`/PM-guard groups on every launch instead of replacing them.
/// What: returns `true` for a lifecycle-triad command
/// ([`is_mpm_hook_command`]), a `trusty-memory ` command, or a PM-guard
/// command (ends with ` hook --pm-guard`).
/// Test: `is_project_managed_hook_command_recognises_all_three_sources`.
pub