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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Callables the package manifest declares the runtime enters (harn#6272).
//!
//! `@host_entry` exists because a host's registration lives in the host's own
//! source, where no static pass can see it (#6193). A manifest hook or trigger
//! is the same contract — the runtime invokes the handler at the arity the
//! declaration fixes — except that the registration is not invisible at all. It
//! is written down, in `harn.toml`, in a block this crate already parses:
//!
//! ```toml
//! [[hooks]]
//! event = "PreToolUse"
//! handler = "burin_code::enforce_stage_tool_gate"
//! ```
//!
//! Asking an author to also write `@host_entry` above that function would make
//! the manifest and the attribute two surfaces stating one fact, and the one
//! that drifts is the one nothing checks. So the fixer reads the manifest.
//!
//! # What the runtime actually supplies
//!
//! Not a fixed arity — the two dispatchers differ. A manifest tool hook goes
//! through `invoke_vm_hook_handler`, which calls the closure with
//! `[harness, event]`; a persona step hook goes through `call_lifecycle_hook`,
//! which passes the payload alone. What they share is the part that matters
//! here: when a capability argument is supplied at all it is the **root**
//! `Harness`. So the migration's carrier ladder is the problem, exactly as in
//! #6193 — it rewrote `enforce_stage_tool_gate(event)` to take
//! `{agent: HarnessAgent, runtime: HarnessRuntime}`, and no dispatcher builds a
//! record.
//!
//! # Why freeze rather than pin to root
//!
//! For a hook entered with `[harness, event]`, taking root `Harness` first is a
//! legal shape, so freezing refuses one rewrite that would have been correct in
//! order to refuse the several that are not. Choosing the conservative side
//! keeps this identical to what `@host_entry` already does for an entry point
//! with no capability parameter, and the refusal is reported as a frozen
//! callable rather than applied silently. Promoting a registered handler to
//! root `Harness` is a real improvement, but it is a migration with its own
//! correctness argument — per hook event — and not something to infer from a
//! body.
use ;
use ;
use crate;
/// The manifest-declared entry points, indexed by the file that declares them.
///
/// Indexed by file rather than held as one name set because the manifest
/// resolves a handler to an exact module — `pkg::fn` to the package's
/// `lib.harn`, an export key to its mapped path. Freezing `run` everywhere
/// because some trigger handler is named `run` would block migrations the
/// manifest says nothing about.
pub
/// Every `module::function` a manifest block names as a runtime entry point.
///
/// Hooks and triggers are one class here: both resolve a handler through
/// [`manifest_module_source_path`] and both are invoked by the runtime at the
/// arity the declaration fixes. A block that gains a handler field later joins
/// this list; nothing else has to change.
/// Compare paths by their resolved form.
///
/// The fixer's file list and the manifest's resolved handler path reach the
/// same file by different routes — a relative target walked from the invocation
/// directory versus a join onto the manifest directory. Falling back to the
/// original path keeps a non-existent file comparable to itself.