feat(kernel): persist canonical agent UUID across respawns (#4614)#4630
Merged
Conversation
Adds an `AgentIdentityRegistry` that stores `agent_name -> canonical_uuid` at `<home_dir>/agent_identities.toml`, written atomically (write to `.tmp.<pid>.<seq>.<nanos>`, fsync, rename). The registry is consulted on every top-level spawn — if a binding exists for the agent's name, that UUID is reused verbatim; otherwise the historical `AgentId::from_name` derivation is used and the result registered as the canonical UUID. Hand / child agents (parent.is_some()) keep their existing UUID-v5-from-hand-id semantics. The kernel grows a sibling `kill_agent_with_purge(id, purge: bool)`: the existing `kill_agent` is now a thin shim that preserves the binding (so internal lifecycle resets — panic restart, manifest reload, hot reload — never silently orphan history). The destructive purge path is reachable only through the explicit confirmation flow. API surface (refs #4614): - `DELETE /api/agents/{id}` now requires `?confirm=true`. Without it the request is rejected with `409 Conflict` and the data-loss warning text from the issue ("permanently remove its canonical UUID and all associated memories and sessions"). With confirm, the agent is killed AND the canonical UUID is purged. - `GET /api/agents/identities` lists the registry contents (RFC3339 `created_at` per row). - `POST /api/agents/identities/{name}/reset?confirm=true` drops the binding without killing a running agent (next spawn re-derives a fresh UUID). Same 409-without-confirm gate. CLI (refs #4614): - `librefang agent kill <id>` auto-sends `?confirm=true` (the verb is the user's confirmation). - `librefang agent delete <name> [--yes]` resolves name -> canonical UUID via the new endpoint, prompts `[y/N]` with the warning text, and issues the confirmed DELETE. - `librefang agent reset-uuid <name> [--yes]` purges the binding via the new reset endpoint without touching the running agent. - `librefang agent merge-history <name> --from <old-uuid>` is a stub that prints the deferral rationale and exits 2. Dashboard: `deleteAgent()` now sends `?confirm=true` so the modal flow continues to work end-to-end. Tests: - Unit (in `agent_identity_registry.rs`): round-trip read/write, first-register-wins, purge persists, malformed file leaves the on-disk file alone, in-memory persist is no-op, list ordering is deterministic. - Integration (`crates/librefang-api/tests/agent_identity_registry_test.rs`, 6 cases): canonical UUID is registered on spawn; bare DELETE returns 409 + warning + preserves identity; confirmed DELETE purges + respawn re-registers; malformed UUID still 400; list endpoint surfaces entries; reset endpoint gates on confirm + 404 on missing. - Existing `api_integration_test`, `load_test`, and `agents_routes_integration` callers updated to send `?confirm=true` (a `delete_confirmed` helper was added in the latter so future tests don't have to open-code the URL). Deferred to a follow-up (documented in CLI long_about): - `librefang agent merge-history` cross-table reassignment. Moving sessions / memories / events / kv_store / entities / relations / usage_events / canonical_sessions / prompt_experiments / audit_entries / approval_audit + the proactive memory store from one UUID to another under a single transaction with rollback is deep memory-substrate surgery and warrants its own PR with data-integrity testing. Closes #4614
houko
force-pushed
the
feat/4614-canonical-agent-uuid
branch
from
May 5, 2026 09:50
3e40f24 to
28d74fa
Compare
This was referenced May 5, 2026
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.
Summary
Adds a canonical agent UUID registry so respawns reuse the same
AgentIdinstead of orphaning history (refs #4614).AgentIdentityRegistrypersistsagent_name -> canonical_uuidat
<home_dir>/agent_identities.toml. Atomic writes(
.tmp.<pid>.<seq>.<nanos>+fsync+rename).AgentId::from_name(UUID v5) and registers as the canonicalbinding. Hand / child agents keep their existing UUID semantics.
kill_agentnow preserves the binding (so panic restarts / hotreloads no longer silently orphan history). Destructive purge is
reachable only through the explicit confirmation flow via the new
kill_agent_with_purge(id, true)kernel helper.API
DELETE /api/agents/{id}requires?confirm=true; without itreturns
409 Conflictwith the data-loss warning text from theissue (mentions canonical UUID + cannot be undone). With confirm
the agent is killed AND the canonical UUID is purged. Idempotent
shortcut for an already-gone agent (DELETE /api/agents/{id} not idempotent — second call returns 404, breaks Terraform-style retry #3509) still applies.
GET /api/agents/identitieslists the registry contents.POST /api/agents/identities/{name}/reset?confirm=truedrops thebinding without touching a running agent. Same 409-without-confirm
gate.
404on a missing name.CLI
librefang agent kill <id>now auto-sends?confirm=true.librefang agent delete <name> [--yes]— name resolution via theidentity endpoint,
[y/N]prompt with the warning text, then theconfirmed DELETE.
librefang agent reset-uuid <name> [--yes]— purges the bindingvia the reset endpoint.
librefang agent merge-history <name> --from <old-uuid>—documented stub (see Deferred).
Dashboard
deleteAgent()updated to send?confirm=true.Tests
agent_identity_registry::tests(round-trip,first-register-wins, purge persists, malformed file is left
intact, in-memory no-op, deterministic list ordering).
crates/librefang-api/tests/agent_identity_registry_test.rs:spawn_registers_canonical_uuiddelete_without_confirm_returns_409_and_preserves_identitydelete_with_confirm_purges_identity_and_respawn_recovers_uuiddelete_invalid_uuid_short_circuits_400_before_confirm_checklist_identities_returns_registered_entriesreset_identity_endpoint_gates_on_confirmapi_integration_test/load_test/agents_routes_integrationcallers updated to use?confirm=true; adelete_confirmedhelper added in the latterso future tests don't have to open-code the URL.
Deferred to follow-up
librefang agent merge-historycross-table reassignment. Movingsessions / memories / events / kv_store / entities / relations /
usage_events / canonical_sessions / prompt_experiments /
audit_entries / approval_audit + the proactive memory store from
one UUID to another under a single transaction with rollback is
deep memory-substrate surgery and warrants its own PR with
data-integrity tests.
Verification
cargo check --workspace --lib— cleancargo check -p librefang-cli— clean--workspace --all-targets -- -D warningscargo test -p librefang-kernel/-p librefang-apideferred to CI per coordinator instruction
Closes #4614