fix(desktop): propagate agent definition edits to instances and remove dead config knobs#1715
Merged
Merged
Conversation
…ances The unified-agent materialization left definition-runtime edits inert once an instance carried a materialized runtime: the spawn re-pin and spawn_config_hash prospective re-snapshot copied model/provider but not runtime, so the stale instance value shadowed the definition via ladder step 2 with no drift badge and no auto-restart. The Inherit sentinel in the update command also left a stale materialized runtime that contradicted the just-expressed inherit intent until the next spawn. Adds runtime to PersonaSnapshot, applies it verbatim from the persona at all four re-snapshot sites (spawn, backfill, launch-restore, spawn_config_hash), and clears record.runtime when the Inherit sentinel clears agent_command_override.
The per-instance mcp_command field is write-only: spawn derives the MCP command exclusively from the runtime catalog (known_acp_runtime), and the stored record value is never read at spawn time. Edits made in the instance dialog were silently discarded. Stop persisting it on update (remove the patching arm) and on create (always derive from the catalog instead of honoring input.mcp_command). Keep the wire fields on both request structs for backward compatibility — existing frontends that still send the field will parse cleanly with no side effects. Remove the editable input from the instance dialog.
BUZZ_ACP_TURN_TIMEOUT is deprecated and ignored by the harness (marked hide=true in buzz-acp/config.rs; the desktop stopped emitting it deliberately). The per-record turn_timeout_seconds field is still stored and shown in the runtime summary, but editing it has no effect. Remove the editable input from the instance dialog and the patching arm from update_managed_agent. Stop sending it on create too, using the schema default instead. Keep the wire fields on both request structs so older clients that still send the field parse cleanly without side effects. Callers that need actual turn-length control should use idle_timeout_seconds or max_turn_duration_seconds instead.
…cords When a persona definition's display_name was renamed via `update_persona`, linked agent instances whose `name` field matched the old display_name were left stale. The config-drift badge would fire on next startup and only a delete+respawn cycle would heal the mismatch. Capture the old display_name before mutation, then propagate the rename to any linked instance still carrying that name (skip pool-named instances like "Birch" or "Compass" so individualized names are preserved). Both `record.name` and `record.display_name` are updated so the relay profile sync that follows publishes the correct name to the relay.
write_back_persona_md runs on every update_persona for pack-backed personas and swallowed every failure behind a bare eprintln!, so a failed write-back silently diverged the saved record from its source .persona.md with no signal to the user. Extract try_write_back_persona_md as a testable function that takes resolved teams rather than an AppHandle, and return failures as Option<String> from write_back_persona_md. update_persona forwards it as a non-fatal writeback_warning on UpdatePersonaResult so the frontend can surface the divergence; in-app edits still save even when the pack file is unwritable. Add regression tests for the legacy team shape (UUID team id, source_dir whose basename is the team_persona_key, symlinked pack dir, persona .md missing the runtime: key — confirmed working on current code) and for the read-only pack file case.
…agation The snapshot-to-record apply block was copy-pasted at four sites (spawn re-pin, launch backfill, restore re-snapshot, prospective hash re-snapshot); a PersonaSnapshot field addition had to touch all four -- exactly how the runtime propagation regression slipped in. All sites now share apply_persona_snapshot. Rename propagation similarly ran per record on one-element slices inside a loop that already filtered by persona; it now runs once over the records slice and reports renamed pubkeys for the relay profile sync.
A crash mid-write could truncate a pack .persona.md: writes now go through a sibling temp file + rename, behind an explicit writability probe (a rename only needs directory permission, which would silently defeat a deliberately read-only pack file) and a canonicalized containment check so a manifest cannot redirect the write outside the pack root. Symlinked pack dirs still pass -- both sides resolve first. The inherit sentinel's runtime clear is now guarded on a live persona link: for a definition-less record the materialized runtime is the only harness source left after the override clear, so a stray empty agentCommand from a non-dialog caller must not change what the agent runs. The edit-apply block is extracted as apply_agent_command_update so the sentinel behavior is directly unit-tested.
idle_timeout_seconds claimed to override the now-deprecated turn_timeout_seconds; describe its real BUZZ_ACP_IDLE_TIMEOUT semantics instead, and mark the inert record/summary fields so a reader of the struct does not need the PR history. Note on record.runtime that None means inherit, serialization omits the key, and boot materialization re-mirrors the live definition -- so a hand-edited "runtime": null is honored and the state converges rather than resurrecting staleness.
The guard tests claimed that re-adding an update arm would fail their assertions, but they never call update_managed_agent -- the asserted record was locally deserialized, so the claim was false confidence. They now assert only the wire shape: deprecated request fields keep parsing for older frontends.
…ize guard The dead-knob docs and sentinel helper pushed types.rs and discovery.rs past their file-size limits. Move the managed-agent create/update request DTOs into the existing types/requests.rs, and the agent_command_override decision family (divergent / create-time / update-time / apply) into discovery/overrides.rs -- both pure relocations behind re-exports. The discovery.rs guard entry ratchets 802 -> 685 to bank the headroom.
wpfleger96
added a commit
to wpfleger96/ai-agent-rules
that referenced
this pull request
Jul 10, 2026
…#197) Two related cleanups to the Sietch Tabr pack deployment. **Persona runtime declarations.** Adds `runtime: buzz-agent` to the frontmatter of `paul.persona.md` and `duncan.persona.md`. Thufir and Alia already declare their runtimes (`codex` / `claude`), but Paul and Duncan resolved to `buzz-agent` only through the Buzz desktop app's default-runtime fallthrough. Declaring it in the pack makes the intent explicit and durable across team re-imports and persona materialization. **Legacy Sprout symlink removal.** #154 removed the pre-rename `xyz.block.sprout.app` / `xyz.block.sprout.app.dev` bundles from `BuzzTool.symlinks`, but machines that installed earlier still carry pack symlinks at those locations — and stale app state (e.g. the Buzz `teams.json` `source_dir`) keeps resolving through them. `BuzzTool` now surfaces those two paths via the existing `get_deprecated_symlinks()` hook, so `install`/`apply`/`uninstall` actively remove them. Removal only ever unlinks actual symlinks — regular files or directories at those paths are left untouched. Related: [block/buzz#1715](block/buzz#1715), which fixed how runtime and agent-command edits propagate from persona definitions to agent instances.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Editing an agent definition (the former "persona" surface) silently failed to reach linked instances in several ways, and the instance edit dialog exposed two knobs that no longer do anything. Five fixes:
Runtime propagation (the core regression). Since runtime materialization landed (#1618), the spawn-time persona re-snapshot copied
system_prompt/model/providerfrom the definition but notruntime— so once an instance carried a materializedruntime, definition-runtime edits were inert: no drift badge, no auto-restart, and the stale instance value shadowed the definition's on every resolution. The persona snapshot now carriesruntime(copied verbatim from the definition, includingNone), and the four snapshot-apply sites (start_local_agent_with_preflight,backfill_persona_snapshots,restore_managed_agents_on_launch, and the prospective re-snapshot inspawn_config_hash— the latter is what makes the drift badge and auto-restart fire) now share a singleapply_persona_snapshot, so a future snapshot field addition cannot miss a site the way this regression did.agent_command_overridestill wins as the explicit per-instance pin, and the Inherit sentinel now also clears the materializedrecord.runtimeon definition-linked instances (extracted asapply_agent_command_updateand unit-tested) so inherit intent takes effect immediately instead of on the next spawn; definition-less records keep their materialized runtime, since it is the only harness source they have left. Backfilled standalone agents (#1667) round-trip as a no-op since their manufactured definitions copy the record's runtime verbatim.display_namepropagation.update_personarepublished the relay kind:0 profile under the new name but left linked records' localname/display_namefrozen. Renames now propagate in one pass to linked records that still carry the old definition display name; instances individualized vianame_poolkeep their pool names, and the rename pass reports the affected pubkeys so only those records get a relay profile re-publish.mcp_commandis derived, not editable. The per-recordmcp_commandwas writable from the instance dialog but never read — spawn, the runtime summary, and the spawn-config hash all derive the MCP command from the runtime catalog. The update arm no longer persists it, create derives it from the catalog, and the input is removed from the instance dialog. Stored values are deliberately not honored: stale records exist in real user data and honoring them would change agents' behavior on upgrade. The request fields stay for wire compatibility, doc-marked inert.turn_timeout_secondsis dead.BUZZ_ACP_TURN_TIMEOUTis deprecated and ignored by the harness, and the desktop deliberately stopped emitting it — the editable field was a no-op. The input and the update/create arms are removed; the record and summary fields stay serde-compatible and doc-marked deprecated.idle_timeout_secondsandmax_turn_duration_secondsare untouched (both work).Pack write-back failures were invisible.
write_back_persona_mdswallowed every failure with a bareeprintln!, so edits to pack-backed definitions could silently diverge from the source.persona.md. The inner logic is extracted astry_write_back_persona_md(testable without anAppHandle), failures now come back as a non-fatalwriteback_warningon theupdate_personaresult (UpdatePersonaResult, flattened so existing callers keep working), and the API layer logs it. Writes are atomic — a sibling temp file renamed over the target — behind an explicit writability probe (a rename only needs directory permission, which would silently defeat a deliberately read-only pack file) and a canonicalized containment check so a pack manifest cannot redirect the write outside the pack root; symlinked pack dirs still resolve correctly. In-app edits still save when the pack file is unwritable. New regression tests cover the legacy team shape — UUID team ids, sprout-erasource_dirwhose basename is the team key, symlinked pack dirs, and inserting a missingruntime:frontmatter key — confirming path resolution works on current code, so the observability gap was the defect.Context: #1618 (runtime materialization), #1667 (B5 standalone backfill).