Skip to content

feat(desktop): personas-as-events foundation with config pinned at create#939

Draft
wpfleger96 wants to merge 14 commits into
mainfrom
wpfleger/persona-events
Draft

feat(desktop): personas-as-events foundation with config pinned at create#939
wpfleger96 wants to merge 14 commits into
mainfrom
wpfleger/persona-events

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the personas-as-events architecture: defines a new Nostr event kind for persona definitions, adds client-side serialization, a local SQLite retention store for offline boot, migrates existing personas.json entries to events, and pins each agent's spawn config onto its record at create time so editing a template never silently mutates running agents.

Changes

Relay side

  • crates/buzz-core/src/kind.rs: Register KIND_PERSONA = 30175 as NIP-33 parameterized replaceable, keyed by (pubkey, kind, d_tag) where d_tag is the plaintext persona slug. Compile-time assertion verifies range membership.
  • crates/buzz-relay/src/handlers/ingest.rs: Allowlist kind:30175 under Scope::UsersWrite, mark as global-only (never channel-scoped). New tests verify scope and global-only classification.

Desktop client side

  • desktop/src-tauri/src/managed_agents/persona_events.rs (new): Serialize PersonaRecord ↔ kind:30175 event. JSON content body contains display_name, system_prompt, avatar_url, runtime, model, provider, name_pool, env_vars. Publish/fetch functions via relay HTTP API.
  • desktop/src-tauri/src/managed_agents/retention.rs (new): SQLite retention store with INSERT OR REPLACE on (kind, pubkey, d_tag) for NIP-33 latest-wins semantics. Pending-sync queue for deferred relay publish. Enables offline boot when relay is unreachable.
  • desktop/src-tauri/src/managed_agents/personas.rs: Add load_from_retention helper alongside the existing personas.json read path.
  • desktop/src-tauri/src/migration.rs: migrate_personas_to_events runs after identity resolution, signs every retained event with the owner's real keys (no placeholder events). Idempotent via retention-row-exists check (the data is the sentinel). Core logic extracted into migrate_personas_in_dir for unit testing.
  • desktop/src-tauri/src/lib.rs: Migration call moved after resolve_persisted_identity so keys are always available. Owner keys cloned from AppState and passed to migration.
  • desktop/src-tauri/Cargo.toml: Add rusqlite dependency with bundled feature.

Spawn config pinned to the agent record

  • At persona-agent create time, snapshot the persona's system_prompt/model/provider/env_vars plus a content hash onto the ManagedAgentRecord (persona_snapshot in persona_events.rs). New record fields provider and persona_source_version, both #[serde(default)].
  • Spawn and deploy read that pinned snapshot off the record, never the live persona (runtime.rs::spawn_agent_child, agents.rs::build_deploy_payload) — a running agent stays on the config it was created with across restarts.
  • A persona edit reaches an agent only via delete+respawn, which rewrites the snapshot; an "Out of date"/"Orphaned" drift badge shows in the meantime (build_managed_agent_summary compares the pinned hash vs the persona's current hash).
  • Pre-existing records are backfilled once at launch (restore.rs::backfill_persona_snapshots); #[serde(default)] means old managed-agents.json files deserialize cleanly, no migration needed.
  • Removes the dead mem/persona engram and the fleet_update.rs self-referential loop.

Key design decisions

  1. d-tag: plaintext slug — personas are designed for discovery/sharing, not privacy. HMAC blinding is the wrong threat model (that is for private engrams).
  2. Retention-first write strategy — write to local SQLite first (durable copy), then publish to relay opportunistically. Migration succeeds on local write, not relay ack.
  3. Duplicate safetyINSERT OR REPLACE keyed on (kind, pubkey, d_tag) in SQLite. NIP-33 replaceable semantics handle relay-side idempotency.
  4. Record is authoritative for spawn config — once an agent is created, its ManagedAgentRecord snapshot is the source of truth for spawn/deploy, so template edits never silently mutate running agents.
  5. No placeholder events — migration requires resolved keys (always available after identity resolution on desktop). Every retention row is a real signed event that downstream code can parse and verify.
  6. Data-is-the-sentinel — idempotency keyed on whether the retention store already holds rows for the owner pubkey. No separate sentinel file needed.

@wpfleger96 wpfleger96 requested a review from a team as a code owner June 10, 2026 04:19
@wpfleger96 wpfleger96 marked this pull request as draft June 10, 2026 13:59
wpfleger96 added a commit that referenced this pull request Jun 10, 2026
Ties the secrets-exclusion rule to the env_vars field removed in #939,
making the doc-to-code mapping unambiguous.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 11, 2026
…Body

PersonaEventContent is the public struct serialized in kind:30175 events.
PR #939 removed env_vars from it (secrets must not travel in plaintext
events). This commit removes the re-addition from #945 and instead
carries env_vars in PersonaEngramBody, which is NIP-44 encrypted inside
the agent's mem/persona engram.

Co-authored-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
wpfleger96 added a commit that referenced this pull request Jun 11, 2026
…Body

PersonaEventContent is the public struct serialized in kind:30175 events.
PR #939 removed env_vars from it (secrets must not travel in plaintext
events). This commit removes the re-addition from #945 and instead
carries env_vars in PersonaEngramBody, which is NIP-44 encrypted inside
the agent's mem/persona engram.

Co-authored-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
@wpfleger96 wpfleger96 force-pushed the wpfleger/persona-events branch 2 times, most recently from 9b8924a to 6c2e650 Compare June 12, 2026 18:45
wpfleger96 added a commit that referenced this pull request Jun 16, 2026
The Relay E2E job (new in #939) is the first to run these previously-skipped
tests. Two asserted against `/channels/.../threads` and `/channels/.../messages`
REST routes the relay never served (permanent 404); a third raced a live
kind:44100 fan-out that a sibling subscription's drain silently discarded.

Rewrite the thread read-backs against POST /query — the depth_limit + #e
extension routes to get_thread_replies, the relay's real thread surface — and
reorder the DM test to subscribe after create_dm so the persisted membership
and discovery events are served deterministically from history.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
The Relay E2E job (new in #939) is the first to run these previously-skipped
tests. Two asserted against `/channels/.../threads` and `/channels/.../messages`
REST routes the relay never served (permanent 404); a third raced a live
kind:44100 fan-out that a sibling subscription's drain silently discarded.

Rewrite the thread read-backs against POST /query — the depth_limit + #e
extension routes to get_thread_replies, the relay's real thread surface — and
reorder the DM test to subscribe after create_dm so the persisted membership
and discovery events are served deterministically from history.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the wpfleger/persona-events branch from 93a291e to 289bfc2 Compare June 17, 2026 20:03
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
The Relay E2E job (new in #939) is the first to run these previously-skipped
tests. Two asserted against `/channels/.../threads` and `/channels/.../messages`
REST routes the relay never served (permanent 404); a third raced a live
kind:44100 fan-out that a sibling subscription's drain silently discarded.

Rewrite the thread read-backs against POST /query — the depth_limit + #e
extension routes to get_thread_replies, the relay's real thread surface — and
reorder the DM test to subscribe after create_dm so the persisted membership
and discovery events are served deterministically from history.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the wpfleger/persona-events branch from 289bfc2 to 2fb370f Compare June 17, 2026 20:28
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 17, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 and others added 11 commits June 17, 2026 20:54
Squashed for rebase — original commits:
- feat(desktop): add persona event kind with client publish/read/retain
- fix(desktop): sign every migrated persona event and drop the sentinel file
- fix(relay): validate kind:30175 persona d-tag slug grammar on ingest
- fix(desktop): drop env_vars from public PersonaEventContent
- docs: add NIP-AP spec for kind:30175 persona events
- feat(ci): add relay E2E testing job and persona event tests
Missed during rebase: sprout_core→buzz_core_pkg imports in
persona_events.rs and migration.rs, sprout-desktop→buzz-desktop
in log messages, and cargo fmt on e2e_persona.rs.
cargo fmt requires buzz_core_pkg imports before nostr (alphabetical).
Relay E2E git tests need git-credential-nostr built alongside the relay.
…ay behavior

The relay now has a generic file upload path that accepts PDFs, random
bytes, and unrecognised formats as application/octet-stream downloads.
SVG with XML declaration is not detected by `infer` and also routes
through the generic path. Updated four content validation tests from
expecting rejection (400/415) to expecting acceptance (200).

Additionally, three WebSocket imeta tests hit /api/events but the
relay route is at /events (no /api prefix). Fixed the URL in all three.

Co-authored-by: Will Pfleger <wpfleger@squareup.com>
Signed-off-by: Will Pfleger <wpfleger@squareup.com>
infer detects XML-based SVG as text/xml (not image/svg+xml), which is
not in the blocked list, so the relay accepts it through the generic
file path with that MIME type.

Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
Signed-off-by: Will Pfleger <wpfleger96@gmail.com>
Same class of issues as e2e_media_extended — tests assumed image-only
policy and wrong API path. The relay ignores Content-Type headers (uses
magic bytes) and the route is /events not /api/events.

Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
Signed-off-by: Will Pfleger <wpfleger96@gmail.com>
The live_split_model_completes test is a manual runbook test requiring
multiple serve nodes. Replace panic!() with println+return so it skips
gracefully when CI runs --ignored tests.

Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
Signed-off-by: Will Pfleger <wpfleger96@gmail.com>
The Relay E2E job (new in #939) is the first to run these previously-skipped
tests. Two asserted against `/channels/.../threads` and `/channels/.../messages`
REST routes the relay never served (permanent 404); a third raced a live
kind:44100 fan-out that a sibling subscription's drain silently discarded.

Rewrite the thread read-backs against POST /query — the depth_limit + #e
extension routes to get_thread_replies, the relay's real thread surface — and
reorder the DM test to subscribe after create_dm so the persisted membership
and discovery events are served deterministically from history.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
test_nip10_thread_reply_not_in_top_level asserted only that a reply
threads under its root — a correlate, not the relay's actual top-level
rule. get_channel_messages_top_level surfaces a depth-1 reply iff
broadcast = true, so a broadcast=1 depth-1 reply satisfied every old
assertion yet IS surfaced at top level: the test greened by data
accident. The relay exposes no top-level-queryable surface over
POST /query (feed_types routes to feed queries that never read
thread_metadata.depth/broadcast), so the rule is pinned via its two
test-observable inputs — recorded depth and the broadcast tag — in both
directions: a non-broadcast depth-1 reply is excluded, a broadcast=1
depth-1 reply is surfaced.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
cargo fmt --all --check failed Rust Lint on the assertions added in the
prior commit (lines 925, 938). Whitespace/reflow only; no assertion or
logic change.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The relay's clock-skew guard rejects events with created_at too far from
server time. Replace hardcoded Nov 2023 timestamps (1_700_000_000) with
Timestamp::now()-relative values that stay within the skew window while
preserving the older-vs-newer replacement semantics.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 and others added 2 commits June 17, 2026 20:54
…2e_relay tests

Tests assumed HTTP REST endpoints (/api/events, /api/users/{pubkey}/profile,
/api/users/me/channel-add-policy) that the relay does not serve. The relay
router only exposes /events, /query, /count, and a few other paths.

Three fix patterns applied:
- POST /api/events → POST /events (the relay's actual HTTP bridge)
- GET /api/users/{pubkey}/profile → POST /query with kind:0 filter
- PUT /api/users/me/channel-add-policy → submit kind:10100 event via POST /events

Also fixed self-add test that hit nostr crate's default p-tag stripping
(EventBuilder removes p tags matching the signer unless allow_self_tagging
is called), and added a 1s sleep in kind0_nip05_sync to ensure the
replacement event gets a strictly newer created_at timestamp.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
The relay-e2e job (introduced on this branch, absent on main) ran the
full `--test '*'` glob across all buzz-test-client e2e suites. That
swept in three suites targeting a REST `/api/*` surface that no longer
exists in the relay binary — the surface was migrated to the Nostr HTTP
bridge and torn out, leaving e2e_rest_api (37), e2e_tokens (19), and
e2e_workflows (7) as 63 tests that 404 against a route the relay does
not mount.

This job was added to exercise the persona/interop work, so scope it to
the two suites it covers: e2e_persona and e2e_nostr_interop. Reimplementing
the REST surface is separate, non-gating work and should not block this
merge base from going green.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the wpfleger/persona-events branch from e6a44d7 to 7c98207 Compare June 18, 2026 00:56
wpfleger96 added a commit that referenced this pull request Jun 18, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Jun 18, 2026
Persona-created agents previously re-read the live persona catalog at
every spawn, so a template edit silently changed running agents on
restart. Snapshot the persona's system_prompt/model/provider/env_vars
plus its content hash onto ManagedAgentRecord at create, and switch the
spawn and provider-deploy call sites to read that snapshot. Restart
reuses the record so it stays pinned; delete+respawn re-runs create and
rewrites the snapshot. env_vars are pinned too — without that, persona
credential edits would leak into a running agent on restart.

The shared resolve_effective_prompt_model_provider body is left intact:
its summary and ModelPicker callers intentionally read the live persona,
so only the spawn/deploy call sites move to the snapshot.

The Agents menu gets a drift indicator: build_managed_agent_summary
compares the record's pinned source_version against the persona's
current hash. A deleted persona is orphaned (never out-of-date, since
there is nothing to respawn into). The summary now displays the pinned
snapshot — what the agent actually runs — so the badge never contradicts
the fields beside it.

The mem/persona engram and fleet-update subsystem are removed: with the
record authoritative, fleet-update became a self-referential write loop
nothing read at spawn. Deletes fleet_update.rs, its launch and
persona-save triggers, the engram-at-creation write, and the
engram-only helpers in persona_events. The public persona catalog
primitives (#939) are untouched — they remain the source the record
snapshots from. Pre-existing agents are backfilled from the live
persona once at launch, logging loudly if the linked persona is gone.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
@wpfleger96 wpfleger96 changed the title feat(desktop): add persona event kind with client publish/read/retain feat(desktop): personas-as-events foundation with config pinned at create Jun 18, 2026
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.

1 participant