const STARTUP_INSTRUCTIONS_MANAGED_MARKER: &str = "<!-- agentmux:managed-startup-instructions -->";Expand description
Materialize generated_content (the composed Soul+AgentMD+Bundle+Skills
CLAUDE.md body build_config_files already produced) against whatever
is already on disk at base_path/CLAUDE.md, WITHOUT ever overwriting a
foreign file’s content:
- No file yet, or the existing file starts with
CLAUDE_MD_MANAGED_MARKER: AgentMux owns it — writeCLAUDE.mddirectly (marker +generated_content), exactly as every call site did unconditionally before this function existed. This is the one path that still hard-fails on an I/O error (propagated via?, same as every call site’s pre-existing behavior) — AgentMux’s own config file failing to write is a real launch-blocking problem. - Existing file present WITHOUT the marker, or present but
unreadable (permission denied, non-UTF-8 content, transient I/O
error — a
read_to_stringerror here must never collapse into “no file yet,” or an unreadable foreign file gets silently clobbered, defeating this whole function’s purpose; reagent P0 + codex P1 on PR #2747): treated identically as foreign.CLAUDE.mditself is never written to again.generated_contentinstead goes toAGENTMUX_MEMORY_FILENAME(always safe to regenerate — it’s 100% AgentMux’s own content), pulled in via a single@importline appended to the realCLAUDE.md— offered at most once per working directory (seeCLAUDE_MD_OWNERSHIP_MARKER_PATH’s doc comment). If the file is unreadable specifically, the@importoffer is skipped for this launch (nothing to safely append to/check idempotency against) — retried on the next launch.
Everything in the foreign-file branch is best-effort, in the same
spirit as write_managed_skill_file_manifest and unlike the
AgentMux-owned branch above: an I/O failure writing the side file,
appending the import line, or writing the ownership marker is logged
and this function still returns Ok(()) — losing one of these only
means the import line isn’t offered (or the side file isn’t
refreshed) on this one launch, not a correctness issue serious enough
to fail the whole agent launch (codex P1 on PR #2747: the original
version used ? throughout this branch, contradicting this doc
comment and propagating a side-file write failure into a hard launch
failure).
The @import append is a true O_APPEND write
(OpenOptions::append(true)), never a read-modify-write of the whole
file — if the foreign file changes between this function’s read and
the append (edited by the user or another process mid-launch), a
full-file rewrite would silently discard that intervening edit; a
true append can only ever add bytes at whatever the end happens to be
(codex P2 on PR #2747).
Every new path this function writes (AGENTMUX_MEMORY_FILENAME,
CLAUDE_MD_OWNERSHIP_MARKER_PATH) is resolved through
crate::backend::base::safe_join_within_base and verified against
crate::backend::base::verify_no_symlink_escape before any write —
both constants are fixed, not user-controllable, but .claude/ itself
could exist as a symlink escaping the working directory, and without
this check a write would silently follow it outside the selected
project (codex P1 on PR #2747, same defense-in-depth the other
config-file write loops already apply to their own paths).
First line AgentMux writes on any non-CLAUDE.md startup-instructions
file it fully owns (freshly created, or one it wrote on a prior
launch) — mirrors CLAUDE_MD_MANAGED_MARKER’s role for CLAUDE.md,
generic across filenames since AGENTS.md/GEMINI.md/QWEN.md/pi’s
APPEND_SYSTEM.md don’t each need distinct marker text. An HTML
comment renders invisibly in every markdown viewer and every one of
these providers reads its instructions file as plain text fed into a
prompt, so a leading comment line is universally harmless regardless
of provider — no per-provider syntax needed for the marker itself
(only the @import-equivalent side-file fallback CLAUDE.md also gets
would need that, and this function deliberately doesn’t attempt it —
see the doc comment below).