Skip to content

fix: re-read persona system_prompt at spawn time for non-pack agents#729

Open
tellaho wants to merge 2 commits into
mainfrom
fix/persona-reread-on-spawn
Open

fix: re-read persona system_prompt at spawn time for non-pack agents#729
tellaho wants to merge 2 commits into
mainfrom
fix/persona-reread-on-spawn

Conversation

@tellaho
Copy link
Copy Markdown
Collaborator

@tellaho tellaho commented May 22, 2026

What changed for users

Before: Editing a persona's system prompt or model had no effect on existing agents — even after stopping and restarting them. The only workaround was to delete the agent and recreate it from scratch.

After: Persona edits take effect on the very next agent spawn. Stop an agent, tweak its persona's instructions, start it again — it picks up the new prompt immediately. This makes agent iteration and improvement loops dramatically faster.

Pack-based agents already had this behavior. This fix brings non-pack persona-linked agents to parity.

Technical changes

  • Extracted resolve_effective_prompt_and_model() — a pure function that resolves the effective system prompt and model given a persona_id, the persona store, and the record's stale snapshot values. No AppHandle dependency, trivially testable.
  • spawn_agent_child() now re-reads the persona store at spawn time via load_personas() then delegates to the extracted helper. Falls back to record.system_prompt / record.model when no persona is linked or the persona can't be found (graceful degradation).
  • Extracted tests to runtime/tests.rs following the env_vars/tests.rs pattern, bringing runtime.rs under the file-size linter limit.
  • Lowered file-size override from 1110 → 880 in check-file-sizes.mjs.

Testing

  • 5 new unit tests covering every branch of resolve_effective_prompt_and_model:
    1. persona_id matches → reads fresh prompt + model from persona store
    2. No persona_id → falls back to record snapshot
    3. persona_id not found (deleted) → graceful fallback to record snapshot
    4. No persona and no record prompt → returns None/None
    5. Persona with no model → returns None (doesn't inherit stale record model)
  • cargo test --manifest-path desktop/src-tauri/Cargo.toml --lib354/354 pass
  • rustfmt --check
  • No schema migrations, no new deps, easy single-commit rollback
image

👆 Set the agent's instructions from only respond with 5 words, starting with the letter P, sent a message, stopped it, set the instructions to only respond with 5 words, starting with the letter B, then respawned it.

Note for the team

The desktop Tauri crate has 354 unit tests (including 5 new ones from this PR), but just test-unit and just ci don't run them — only cargo test --manifest-path desktop/src-tauri/Cargo.toml --lib exercises them locally. Consider wiring this into the CI gate separately.

@tellaho tellaho requested a review from a team as a code owner May 22, 2026 23:56
@tellaho tellaho marked this pull request as draft May 23, 2026 00:09
@tellaho tellaho force-pushed the fix/persona-reread-on-spawn branch from e04dcb9 to 99ed533 Compare May 26, 2026 21:25
@tellaho tellaho marked this pull request as ready for review May 26, 2026 21:32
@tellaho tellaho force-pushed the fix/persona-reread-on-spawn branch 3 times, most recently from f402311 to 02d5850 Compare May 26, 2026 21:44
Extract resolve_effective_prompt_and_model() as a pure, testable function
that resolves the effective system prompt and model for a managed agent.
When persona_id is set, looks up the persona in the store and returns its
current values. Falls back to the record snapshot when no persona is linked
or the persona can't be found.

Add 5 unit tests covering:
- persona_id matches → reads fresh prompt + model
- no persona_id → falls back to record snapshot
- persona_id not found in store → falls back to record snapshot
- no persona and no record prompt → returns None/None
- persona has no model → returns None (doesn't inherit record model)
@tellaho tellaho force-pushed the fix/persona-reread-on-spawn branch from 044e21e to a652632 Compare May 26, 2026 22:02
@wesbillman
Copy link
Copy Markdown
Collaborator

@codex please review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3778e9d47

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +525 to +527
match from_persona {
Some((prompt, model)) => (Some(prompt), model),
None => (
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve per-agent model overrides for persona-linked agents

When a persona_id is present, this branch always returns the persona's model (including None) and never uses record_model, so a saved per-agent model choice is ignored at spawn time for non-pack persona agents. In practice, if a persona has no model set and the user selects one via update_managed_agent/ModelPicker, the value is persisted on the record but SPROUT_ACP_MODEL is removed on restart, causing the runtime default model to be used instead of the selected one.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

@wesbillman wesbillman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Review notes from my pass:

I think the user-facing goal is worth doing: persona edits should take effect after an agent restart. The current implementation looks too broad, though, because it changes the precedence semantics for all persona-linked local agents and leaves provider deploy on the old behavior.

Main things to address:

  • Preserve or intentionally remove per-agent prompt/model overrides for persona-linked agents. Right now the UI still saves them, but local spawn ignores them when persona_id resolves.
  • Apply the same effective prompt/model resolution to provider deploys, or document that this fix is local-only.
  • Tighten the missing-persona test/description so it does not imply full spawn succeeds with a missing persona; resolve_persona_env() still fails closed later in spawn_agent_child().

I could not complete the local targeted Tauri unit run because the build failed before tests on a missing sidecar resource: desktop/src-tauri/binaries/sprout-aarch64-apple-darwin.

});

match from_persona {
Some((prompt, model)) => (Some(prompt), model),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This makes the persona prompt/model always win whenever persona_id resolves, but the existing app still treats ManagedAgentRecord.system_prompt and .model as mutable per-agent settings. EditAgentDialog persists systemPrompt, and ModelPicker persists model; after this change those saved values become no-ops for persona-linked agents. In particular, if a persona has model: None, selecting a model on that agent will still save/display but spawn will remove SPROUT_ACP_MODEL. Can we either preserve explicit agent override precedence, or update the UI/data model so persona-backed agents cannot appear to have per-agent prompt/model overrides?

Some((prompt, model)) => (Some(prompt), model),
None => (record.system_prompt.clone(), record.model.clone()),
};
// Resolve system prompt and model via the extracted helper. Re-reads
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This re-read is only wired into local spawn_agent_child(). Provider-backed starts/redeploys still build their payload from record.system_prompt and record.model in build_deploy_payload(), so persona edits will not propagate there. If provider agents are in scope, this effective prompt/model resolver should be shared with the deploy payload path too; if not, the PR description should call out that the fix is local-only.

/// returns its current `system_prompt` and `model`. Falls back to the
/// agent record's stale snapshot (`record_prompt`, `record_model`) when:
/// - `persona_id` is `None`
/// - the persona can't be found in the store (e.g. deleted)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 This fallback is true for the pure helper, but full spawn still calls resolve_persona_env(app, record.persona_id.as_deref())? later, which fails closed when persona_id is set and missing. The test/description around the missing-persona case may overstate runtime behavior unless the env resolution path is also adjusted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants