Summary
The boot-time TOML drift loop at crates/librefang-kernel/src/kernel/mod.rs:~2860 overwrites multiple runtime-rendered prompt tails on every restart, not just the ## User Configuration block. PR #3142 fixes the settings-tail case; this issue tracks the rest.
Mechanism (recap)
At hand activation / agent spawn, the kernel APPENDS several markdown blocks to manifest.model.system_prompt:
| Block |
Source |
Rendered when |
## User Configuration |
librefang_hands::resolve_settings() from [[settings]] |
hand activation |
## Reference Knowledge |
skill prompt_context.md content from the agent's skills allowlist |
hand activation / agent spawn |
## Your Team |
peer agent registry info |
hand activation / agent spawn |
These are persisted to the agents DB.
On every subsequent restart, the boot-time drift detector compares the disk TOML manifest to the DB blob:
let changed = serde_json::to_value(&disk_manifest).ok()
!= serde_json::to_value(&entry.manifest).ok();
if changed {
entry.manifest = disk_manifest; // ← clobbers ALL rendered tails
kernel.memory.save_agent(&entry)?;
}
The disk TOML never carries any of those tails (they're runtime artifacts, never written back to the source file), so the comparison ALWAYS triggers on a hand-with-any-rendered-tail restart, and all the rendered context is silently dropped from the prompt.
What's already fixed (PR #3142)
PR #3142 introduces apply_settings_block_to_manifest(), makes it idempotent via a USER_CONFIG_TAIL_MARKER, and re-runs it inside the drift block before save_agent. The activation site is also refactored to use the same helper. Result: settings tail survives restarts.
What's still broken
After #3142 lands, restarts will still:
-
Strip ## Reference Knowledge. Agents lose access to their skill prompt_context.md content. The agent then falls back to skill_read_file({skill, path}) calls on demand if it knows what to ask for, but the discoverability summary is gone — affects which skills get considered on the first turn of each session, even when session_mode=new.
-
Strip ## Your Team. Agents lose the peer registry list, so agent_send to peers becomes "I don't know who's running" guesswork. Symptom: agent calls agent_list redundantly at the top of every session because the static peer info is no longer in the prompt.
Both are silent: no error, no warning, just degraded agent context. The bug is most visible on multi-hand setups with peer routing or skill-heavy hands.
Suggested follow-up shape
Two reasonable approaches:
A. Replicate the #3142 pattern for each rendered tail. Helpers
apply_skill_section_to_manifest() and
apply_team_section_to_manifest(), each idempotent via marker, each
called from both activation and drift sites. Pros: minimal, surgical.
Cons: multiplies the helper count; future runtime-rendered tails
(there will be more) need the same treatment.
B. Compare on a sanitized projection of the manifest that strips ALL known runtime-rendered tails before the diff. The drift block then only fires when the raw TOML truly differs from the raw DB form, ignoring rendered synthesis entirely. Pros: solves the whole class of bug at once; new rendered blocks are automatically tolerated as long as they have markers. Cons: requires defining a manifest_for_diff() projection and keeping the marker registry up to date.
I lean toward B for long-term hygiene, but A is the cheap incremental path if maintainer prefers landing fixes one at a time.
Repro (post-#3142)
A multi-agent hand with multiple skills:
# example-hand/HAND.toml
id = "example"
name = "Example"
skills = ["alpha", "beta"] # both have non-empty prompt_context.md
[agents.worker]
name = "example-worker"
skills = ["alpha", "beta"] # workaround for #3135
[agents.worker.model]
provider = "default"
model = "default"
system_prompt = """You are example-worker."""
librefang hand activate example
# Inspect example-worker's stored manifest — system_prompt has
# ## User Configuration (post-#3142: survives)
# ## Reference Knowledge (still gets dropped)
# ## Your Team (still gets dropped — if peers exist)
just restart
# Re-inspect — Reference Knowledge and Your Team blocks GONE.
Why one issue
These two follow-up cases share the exact same diagnosis as #3142 — same code path, same fix shape. Filing as one issue rather than two so reviewers can decide between (A) and (B) holistically.
Out of scope
Summary
The boot-time TOML drift loop at
crates/librefang-kernel/src/kernel/mod.rs:~2860overwrites multiple runtime-rendered prompt tails on every restart, not just the## User Configurationblock. PR #3142 fixes the settings-tail case; this issue tracks the rest.Mechanism (recap)
At hand activation / agent spawn, the kernel APPENDS several markdown blocks to
manifest.model.system_prompt:## User Configurationlibrefang_hands::resolve_settings()from[[settings]]## Reference Knowledgeprompt_context.mdcontent from the agent'sskillsallowlist## Your TeamThese are persisted to the agents DB.
On every subsequent restart, the boot-time drift detector compares the disk TOML manifest to the DB blob:
The disk TOML never carries any of those tails (they're runtime artifacts, never written back to the source file), so the comparison ALWAYS triggers on a hand-with-any-rendered-tail restart, and all the rendered context is silently dropped from the prompt.
What's already fixed (PR #3142)
PR #3142 introduces
apply_settings_block_to_manifest(), makes it idempotent via aUSER_CONFIG_TAIL_MARKER, and re-runs it inside the drift block beforesave_agent. The activation site is also refactored to use the same helper. Result: settings tail survives restarts.What's still broken
After #3142 lands, restarts will still:
Strip
## Reference Knowledge. Agents lose access to their skillprompt_context.mdcontent. The agent then falls back toskill_read_file({skill, path})calls on demand if it knows what to ask for, but the discoverability summary is gone — affects which skills get considered on the first turn of each session, even whensession_mode=new.Strip
## Your Team. Agents lose the peer registry list, soagent_sendto peers becomes "I don't know who's running" guesswork. Symptom: agent callsagent_listredundantly at the top of every session because the static peer info is no longer in the prompt.Both are silent: no error, no warning, just degraded agent context. The bug is most visible on multi-hand setups with peer routing or skill-heavy hands.
Suggested follow-up shape
Two reasonable approaches:
A. Replicate the #3142 pattern for each rendered tail. Helpers
apply_skill_section_to_manifest()andapply_team_section_to_manifest(), each idempotent via marker, eachcalled from both activation and drift sites. Pros: minimal, surgical.
Cons: multiplies the helper count; future runtime-rendered tails
(there will be more) need the same treatment.
B. Compare on a sanitized projection of the manifest that strips ALL known runtime-rendered tails before the diff. The drift block then only fires when the raw TOML truly differs from the raw DB form, ignoring rendered synthesis entirely. Pros: solves the whole class of bug at once; new rendered blocks are automatically tolerated as long as they have markers. Cons: requires defining a
manifest_for_diff()projection and keeping the marker registry up to date.I lean toward B for long-term hygiene, but A is the cheap incremental path if maintainer prefers landing fixes one at a time.
Repro (post-#3142)
A multi-agent hand with multiple skills:
Why one issue
These two follow-up cases share the exact same diagnosis as #3142 — same code path, same fix shape. Filing as one issue rather than two so reviewers can decide between (A) and (B) holistically.
Out of scope
prompt_builder.rsand is unrelated to drift.[[settings]]schema or theresolve_settingsrendering format (covered in fix(kernel): re-render hand [[settings]] tail after boot-time TOML drift #3142).