What
Machine references are stored twice: as actual MACHINE pages/machine-* tables, and as denormalized MachineRef[] JSONB blobs on AI_CHAT pages (schema/core.ts:66 pages.machines) and on global_assistant_config (schema/integrations.ts:239).
The scrub that keeps the blobs consistent — createDbMachineRefScrub (apps/web/src/lib/machines/machine-settings-runtime.ts:218-295), which rewrites both blobs when a machine is deleted — is invoked from exactly one call site: api/machines/settings/route.ts:197.
Paths that bypass the scrub
- Generic page trash of a MACHINE page (
pageService.trashPage, bulk delete, folder cascade) — explicitly acknowledged as un-handled in machine-branches.ts:55-67.
- Drive deletion — FK cascade deletes the machine page rows outright.
- Trash-expiry purge.
Each leaves dangling {kind:'existing', machineId} refs in chat/agent configs pointing at machines that no longer exist. Migration 0195 already had to rewrite both blobs together once (machine-settings-runtime.ts:23) — proof a coordinated rewrite was needed before.
Proposed direction
Either hang the scrub off the single choke point every deletion path already goes through (page trash/purge service + drive deletion), or — better — stop denormalizing: store machine refs as ids only and resolve liveness at read time (a dangling id filtered on read is self-healing; a dangling JSONB blob entry is permanent). If the blob must stay for perf, a periodic sweep that drops refs to nonexistent machines would make the drift bounded instead of permanent.
Filed from the sources-of-truth audit.
What
Machine references are stored twice: as actual MACHINE pages/machine-* tables, and as denormalized
MachineRef[]JSONB blobs on AI_CHAT pages (schema/core.ts:66pages.machines) and onglobal_assistant_config(schema/integrations.ts:239).The scrub that keeps the blobs consistent —
createDbMachineRefScrub(apps/web/src/lib/machines/machine-settings-runtime.ts:218-295), which rewrites both blobs when a machine is deleted — is invoked from exactly one call site:api/machines/settings/route.ts:197.Paths that bypass the scrub
pageService.trashPage, bulk delete, folder cascade) — explicitly acknowledged as un-handled inmachine-branches.ts:55-67.Each leaves dangling
{kind:'existing', machineId}refs in chat/agent configs pointing at machines that no longer exist. Migration0195already had to rewrite both blobs together once (machine-settings-runtime.ts:23) — proof a coordinated rewrite was needed before.Proposed direction
Either hang the scrub off the single choke point every deletion path already goes through (page trash/purge service + drive deletion), or — better — stop denormalizing: store machine refs as ids only and resolve liveness at read time (a dangling id filtered on read is self-healing; a dangling JSONB blob entry is permanent). If the blob must stay for perf, a periodic sweep that drops refs to nonexistent machines would make the drift bounded instead of permanent.
Filed from the sources-of-truth audit.