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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! The PM system-prompt file every spawn path carries, and the compiled-prompt
//! refresh that rides with it (#2125, #4752, #7568).
//!
//! Why: `runtime::claude_code` is at the 500-SLOC production cap, and #7568 adds
//! a named-root seam beside the ambient entry point. The pair is cohesive — one
//! composes the prompt and records the fold, the other only resolves the
//! operator's framework root for it — so they move out together.
//! What: [`build_prompt_file`] (ambient framework root, the production spawn
//! path) and [`build_prompt_file_in`] (named root, what the tests drive).
//! Test: `build_prompt_file_writes_resolved_prompt_for_project`,
//! `build_prompt_file_refreshes_the_compiled_prompt`,
//! `build_prompt_file_compiled_write_failure_does_not_block_the_spawn`,
//! `build_prompt_file_records_under_the_named_framework_root`,
//! `an_ambient_build_prompt_file_records_under_whatever_home_it_inherits` in
//! `runtime::claude_code::tests`.
use ;
use RuntimeError;
use crateSessionProfile;
/// Build and write the PM system-prompt file for `project_dir`, for injection
/// into the daemon managed-spawn command via `--append-system-prompt-file`
/// (issue #2125 item 3 — the daemon-adapter carrier).
///
/// Why: `spawn` never actually passed `--append-system-prompt-file`
/// at all, so the one thing that turns a bare `claude` process into a
/// trusty-mpm PM never reached the daemon's default managed-spawn path,
/// leaving every bare-`tm` session running vanilla Claude Code (#2125). This
/// reuses the exact same seam
/// ([`crate::core::session_launch::build_system_prompt_for_with_style_and_native`])
/// the CLI `tm launch` / client `/connect` paths already use, so all three
/// drivers build byte-identical prompts for the same project.
/// What: resolves live native-output-style support (fail-safe to injection via
/// [`crate::core::output_style::claude_supports_native_output_style`]), builds
/// the override-resolved + style-injected prompt for `project_dir`, and writes
/// it to a fresh file under `prompt_dir` via
/// [`crate::core::model_inject::write_prompt_file_in`].
/// #8286: a write failure is `Err` ([`RuntimeError::Spawn`] naming the file and
/// the I/O cause), and every caller refuses the launch on it. Each caller
/// launches a PM or supervisor session, whose role IS this prompt, and there
/// is no other carrier: #2173 forbids falling back to the project's CLAUDE.md.
///
/// #4752/#4832: this is also where the session's compiled prompt
/// (`<harness-root>/.trusty-mpm/sessions/<id>/INSTRUCTIONS-COMPILED.md`) is
/// refreshed from the exact bytes handed to `--append-system-prompt-file`.
/// `session_id` selects that directory: `spawn`/`spawn_resume` pass the managed
/// id they already hold, and the in-place relaunch passes `None` so
/// [`crate::core::harness_root::session_scope`] falls back to the pane's
/// `TM_MANAGED_SESSION_ID`. Passing `None` where an id IS known would refresh a
/// different file from the one preparation wrote.
///
/// WHY THIS ONE IS BEST-EFFORT while the same write is FATAL in the
/// provisioning paths — the asymmetry is deliberate: those are PROVISIONING
/// steps, whose job is to establish the session's on-disk state before anything
/// spawns; refusing there is coherent and is what the owner ruled. This call
/// sits INSIDE the spawn, past that gate.
///
/// That holds only because ALL THREE entry points that reach this function have
/// already had a fatal compiled write succeed:
/// * fresh start → `session_launch::prepare_session_inner`
/// * daemon resume → `managed_routes::lifecycle::resume_managed`
/// * bare-`tm` in-place relaunch → `guided_inplace::run_inplace_relaunch`,
/// via `instruction_pipeline::refresh_compiled_prompt`
///
/// The third was missing until round 4 of #4752, which made the earlier version
/// of this comment false: `run_inplace_relaunch` calls neither of the other two,
/// so this best-effort write was its ONLY compiled write. Adding a call here
/// without that fatal upstream write would resurrect the same hole — check the
/// list above before adding a fourth spawn path.
///
/// The prompt file below is the write the launch depends on, and since #8286
/// it is the fatal one; the compiled copy is only for inspection.
/// Test: `build_prompt_file_writes_resolved_prompt_for_project`,
/// `build_prompt_file_refreshes_the_compiled_prompt`,
/// `build_prompt_file_compiled_write_failure_does_not_block_the_spawn`.
// #8545: the last production caller now names its root; only tests use this.
pub
/// [`build_prompt_file`] against a caller-named framework root.
///
/// Why (#7568): the entry point above resolves its ledger from the process home
/// directory, and this is the LAST savings producer that did. Every in-process
/// test that drives a spawn therefore had to redirect `$HOME` — a
/// process-global write, `#[serial]`-guarded, of the class #5544 bans outright
/// in the `tm` binary — just to keep a fixture's row and its `no-fold-warned`
/// marker out of the operator's own `~/.trusty-mpm/usage/`. Naming the root is
/// what lets a test assert the destination instead of guarding the process.
/// The ambient form stays the production path: a real spawn's ledger IS the
/// operator's.
/// What: the body of [`build_prompt_file`] — composes the project's prompt,
/// refreshes the compiled copy under `framework_root` (best-effort; see the
/// entry point for why this write is not fatal here), and writes the
/// `--append-system-prompt-file` payload to a file under `prompt_dir`
/// (production: the process temp dir; #8286 tests name an unwritable one).
/// Test: `build_prompt_file_records_under_the_named_framework_root`,
/// `an_ambient_build_prompt_file_records_under_whatever_home_it_inherits`,
/// `spawn_refuses_when_the_prompt_file_cannot_be_written`.
pub