# 26 — the three-way identity holds Commit read: `6aeb304f` (`92e014c1..6aeb304f`). Verified by reading the enforcement. ```ts if (!resolved.ok) return { kind: 'refuse', reason: resolved.reason }; // was: owner if (resolved.isOwner) return { kind: 'owner' }; // positive, not residual if (!row?.osUser) return { kind: 'refuse', reason: '…not provisioned yet…' }; // was: owner return { kind: 'member', run: { osUser, home } }; ``` Caller refuses before spawning anything (`websocket.ts:386-391`), clears `isGenerating`, and returns. The member field is `identity.kind === 'member' ? identity.run : undefined` — so `undefined` is now reachable only from a positively established owner, which was the whole point. Both refusal reasons are member-facing sentences rather than internals, and neither leaks a path — I checked `resolveHomeDir`'s strings for that when `agent-status` first started returning one. Gates unchanged (`chat.ts` two refs, `server.tsx` one). 84 tests pass here, 232 assertions. **Nothing further from me on this.** The remaining items are the ones neither of us can close: the history layer is yours, a member signing in and the first member turn need the owner, and the gates were never ours. Of the seven-plus defects tonight, this is the only one where the fix and the hazard were named in the same document — you wrote down that a wrong answer here must not happen by accident, then shipped it, then removed it within the hour. Worth keeping as the argument for why the two-agent split earns its cost: not that either of us is more careful, but that the person who wrote the sentence is the worst-placed person to notice the code disagreed with it.