Collected low-severity leftovers from the same audit: "one daemon owns this agent for its lifetime" assumptions that are wrong on the pool but do not break a user-visible flow on their own. Filed together so they are recorded without a separate issue each. Related class: #955.
1. cp_routing is one install-wide row rewritten from each member's memory
local-store.ts:3120 upserts on the literal id = 1 (CHECK (id = 1), :831), driven by router/cp-routing-layer.ts:62, which serializes the process's whole assignments map on every upsertAssign / applyUpdate / converge. Each member's write erases every other member's. CpRoutingLayer's constructor (:27) then hydrates whichever member wrote last, so a member boots holding a foreign routing map and a foreign routingEpoch — and applyUpdate's stale guard (:41, if (u.routingEpoch < this.routingEpoch) return) will then discard legitimate global-rule updates until the real epoch catches up. Bounded because converge() on register/ok reasserts from the CP snapshot, so the exposure is boot → register/ok plus continuous cross-member write amplification on one hot row. Fix: key the row by member, or stop persisting it (the CP re-sends the full snapshot on every connect, which is what makes the corruption survivable today).
2. findThreadOwner excludes every pool agent
packages/control-plane/src/persistence/repositories/session.repo.ts:1673:
where: { channel, thread, agent: { daemonId: { not: null }, integrations: { some: { botId, status: 'active' } } } }
The predicate requires a non-null daemonId, so the pull-on-miss fallback that routes un-mentioned thread follow-ups (channel-root / case-2a sessions) never fires for a pool agent. Above it, orchestrator/httpBot.ts:417 (lookupThread) returns SharedThreadAgent.daemonId verbatim — an FK-less column (prisma/schema.prisma:2018) that names a reaped member after a rollout. Note orchestrator/agentRouting.ts:15 explicitly defers thread affinity to a later milestone, so treat the lookupThread half as known-deferred; the findThreadOwner filter is a separate, independently fixable blind spot (resolve through the placement resolver instead of requiring the column).
3. channel/agents ownership bind uses the ingress projection for an authority question
packages/control-plane/src/ws/handlers/channel-agents.ts:131:
const requester = orgRoster.find((a) => a.agentId === requesterAgentId)
if (requester?.daemonId !== conn.daemonId) return [] // null/undefined never equals a daemonId
resolveDirectory deliberately uses routableDaemon (confirmed holders only). Between a duty grant and the member's first confirming digest, a member that genuinely holds the agent is refused and the agent is told it has no peers. Fix: bind with the resolver's may-act predicate (holders, not confirmed holders) while keeping resolveDirectory for the wake targets.
4. The organization-environment compatibility gate fails open for pool agents
packages/control-plane/src/http/routes/organization-environment.ts:139:
if (daemonId === null) return true // "unplaced is always fine"
called at :158 and :200 with agent.daemonId. A pool agent is placed but names no machine, so it reads as "unplaced" and the AGENT_CONFIG_REVISION_FEATURE precondition is skipped entirely. This is the inverse of the other findings in this class — a silent pass rather than a silent refusal — and worth fixing in the same pass so the two directions do not drift apart.
5. sessions.usage accumulation is a read-modify-write
local-store.ts:2029 / :2043 / :2059 / :2070: getUsage → merge in JS → writeUsage. addTokenUsage and addCost are genuinely additive and will lose increments under concurrency. Blast radius is one session and a session has one owner outside a handover, so this is token-accounting drift rather than a correctness break — but it is the same pattern, and a single relative UPDATE would close it.
6. Boot inbox replay logs one warn per foreign row
daemon.ts:20530: a member boots holding nothing, so replayInbox walks the whole install-wide inbox and logs durable inbox: skipping replay of <id> — unknown agent "<agentId>" for every pending row of every organization. Accurate when the store was one daemon's SQLite file; on the shared store it is per-Pod-start noise proportional to fleet backlog. Drop to debug, or scope the boot read.
7. The org-fenced data-plane transcript table is off the write path
PostgresDataPlane.transcripts — the agentconnect_data_plane.transcript table that carries org_id in every index (store/postgres-migrations.ts:28) — is constructed and then never read or written by the daemon (grep '\.transcripts\b' matches only its own constructor and flush). All transcript traffic goes to the LocalStore mirror in agentconnect_cloud_store, whose transcript / transcript_recipient tables have no org_id column at all (local-store.ts:798). Not a live bug — reads are keyed by (channel, thread) and go through agent-scoped callers — but the tenant fence that was written for this data is not the one in force, which is worth either wiring up or deleting so the schema stops implying a guarantee it does not provide.
Collected low-severity leftovers from the same audit: "one daemon owns this agent for its lifetime" assumptions that are wrong on the pool but do not break a user-visible flow on their own. Filed together so they are recorded without a separate issue each. Related class: #955.
1.
cp_routingis one install-wide row rewritten from each member's memorylocal-store.ts:3120upserts on the literalid = 1(CHECK (id = 1),:831), driven byrouter/cp-routing-layer.ts:62, which serializes the process's wholeassignmentsmap on everyupsertAssign/applyUpdate/converge. Each member's write erases every other member's.CpRoutingLayer's constructor (:27) then hydrates whichever member wrote last, so a member boots holding a foreign routing map and a foreignroutingEpoch— andapplyUpdate's stale guard (:41,if (u.routingEpoch < this.routingEpoch) return) will then discard legitimate global-rule updates until the real epoch catches up. Bounded becauseconverge()onregister/okreasserts from the CP snapshot, so the exposure is boot → register/ok plus continuous cross-member write amplification on one hot row. Fix: key the row by member, or stop persisting it (the CP re-sends the full snapshot on every connect, which is what makes the corruption survivable today).2.
findThreadOwnerexcludes every pool agentpackages/control-plane/src/persistence/repositories/session.repo.ts:1673:The predicate requires a non-null
daemonId, so the pull-on-miss fallback that routes un-mentioned thread follow-ups (channel-root / case-2a sessions) never fires for a pool agent. Above it,orchestrator/httpBot.ts:417(lookupThread) returnsSharedThreadAgent.daemonIdverbatim — an FK-less column (prisma/schema.prisma:2018) that names a reaped member after a rollout. Noteorchestrator/agentRouting.ts:15explicitly defers thread affinity to a later milestone, so treat thelookupThreadhalf as known-deferred; thefindThreadOwnerfilter is a separate, independently fixable blind spot (resolve through the placement resolver instead of requiring the column).3.
channel/agentsownership bind uses the ingress projection for an authority questionpackages/control-plane/src/ws/handlers/channel-agents.ts:131:resolveDirectorydeliberately usesroutableDaemon(confirmed holders only). Between a duty grant and the member's first confirming digest, a member that genuinely holds the agent is refused and the agent is told it has no peers. Fix: bind with the resolver's may-act predicate (holders, not confirmed holders) while keepingresolveDirectoryfor the wake targets.4. The organization-environment compatibility gate fails open for pool agents
packages/control-plane/src/http/routes/organization-environment.ts:139:called at
:158and:200withagent.daemonId. A pool agent is placed but names no machine, so it reads as "unplaced" and theAGENT_CONFIG_REVISION_FEATUREprecondition is skipped entirely. This is the inverse of the other findings in this class — a silent pass rather than a silent refusal — and worth fixing in the same pass so the two directions do not drift apart.5.
sessions.usageaccumulation is a read-modify-writelocal-store.ts:2029/:2043/:2059/:2070:getUsage→ merge in JS →writeUsage.addTokenUsageandaddCostare genuinely additive and will lose increments under concurrency. Blast radius is one session and a session has one owner outside a handover, so this is token-accounting drift rather than a correctness break — but it is the same pattern, and a single relativeUPDATEwould close it.6. Boot inbox replay logs one warn per foreign row
daemon.ts:20530: a member boots holding nothing, soreplayInboxwalks the whole install-wide inbox and logsdurable inbox: skipping replay of <id> — unknown agent "<agentId>"for every pending row of every organization. Accurate when the store was one daemon's SQLite file; on the shared store it is per-Pod-start noise proportional to fleet backlog. Drop to debug, or scope the boot read.7. The org-fenced data-plane transcript table is off the write path
PostgresDataPlane.transcripts— theagentconnect_data_plane.transcripttable that carriesorg_idin every index (store/postgres-migrations.ts:28) — is constructed and then never read or written by the daemon (grep '\.transcripts\b'matches only its own constructor andflush). All transcript traffic goes to theLocalStoremirror inagentconnect_cloud_store, whosetranscript/transcript_recipienttables have noorg_idcolumn at all (local-store.ts:798). Not a live bug — reads are keyed by(channel, thread)and go through agent-scoped callers — but the tenant fence that was written for this data is not the one in force, which is worth either wiring up or deleting so the schema stops implying a guarantee it does not provide.