# Data Custody Contract — Pointer and Consent Schema (D14)

Status: DRAFT — awaiting ratification (webui-audit S2, contract 7 of 9).
Authority: PRD D14/D6 (Part I §7) — sensitive profile categories live in
the **user's own brain ONLY**; PostgreSQL holds structural data, consent
records, and pointers — never the content; "user data does not leak" is
enforced by architecture, not policy. The Standalone split is a MAY with
a recommended-default (kept for conversion forward-compatibility, D3).
PRD D4 (Part I §6) — profile answers feed `USER.md` and/or the user's
data store subject to the custody rule; connectors carry granular
agentic-access consent. PRD D6 — estate brains hold operational records;
only product-relevant material migrates into repository docs.

Revision 2 (sol r1 findings F1–F6): the registry is now a full profile
classifier that can represent non-sensitive rows, with an exact row
shape, versioning, refusal semantics for unknown keys, and transition
rules (F1); the consent model gains a concrete grantee reference model,
an active-row uniqueness constraint, append-only re-grant semantics,
and explicit mutation authority (F2); the pointer schema names its
exact column set, binds the one-pointer rule to a database constraint,
defines the `brain_ref` grammar and owner-bound resolution, replaces
the unkeyed hash with a keyed construction, and bounds orphan repair
(F3); the witnesses import the hierarchy contract's §6.2 column
allowlist and §6.3-style closed route inventories, add negative
controls and a column-type allowlist, and cover every binding rule
(F4); the Standalone physical split, both valid layouts, the election
record, and v1 phase timing are defined consistently with the wizard
and mode-conversion contracts (F5); drafting additions are disclosed in
§8 and the ruling request is one sentence with one decision (F6).

Revision 3 (sol r2 findings F1–F9): reclassification gains a
custody-config write fence (shared/exclusive row lock) so no
concurrent writer can commit a stale-version route, a declared
supported-version refusal, and witnesses for both transition
directions (F1). The grantee reference becomes three per-type FK
columns with a generated discriminant, real referent tables for
`connector` and `feature` (contract-owned, empty at v1), the
enrolled-agent binding named to the rank-4 family's table, and
revoked-row immutability enforced by a database trigger (F2). The
content hash is domain-separated per (user, category, locator) with
canonical bytes, an embedded key id, and a rotation rule; the audit FK
referent and the non-sensitive profile table (`profile_answers`) are
named; tree-object resolution prohibits symlink escape (F3). The §7
route witnesses import hierarchy §6.3's full prong set, scan
`plugins/`, extend the no-content probe to every platform table, and
add the missing negatives (F4). `custody_config` is singleton by
constraint (F5). §8 discloses the previously omitted policies (F6).
Mutation authority is split into two disjoint actor predicates —
subject mutations and the precondition-checked system auto-revocation
(F7). Hash mismatch gets a representable pointer state with a defined
terminal outcome (F8). Managed deletion is an ordered git-then-database
protocol with fault-injection witnesses at both interruption points,
replacing the impossible cross-store transaction (F9).

Revision 4 (sol r3 findings F1/F2/F4/F6/F7/F9 residual, F10/F11 new):
the write fence pins `READ COMMITTED` isolation and lock-first
statement order for the reclassification migration, and the
supported-version declaration becomes a named source symbol checked
under the same lock as each decision (F1). The user foreign keys
adopt the identity store's actual key type — `users.id` is `text`
(BetterAuth identifiers), so every `user_id` column is `text` (F10).
The agent referent binds to the live platform `agents` table by
symbol, since `identity-lifecycle.md` defines no enrollment surface;
an amendment re-binds if one arrives (F2). The system auto-revocation
predicate is withdrawn entirely: grantee cessation is handled
structurally by `ON DELETE CASCADE` from the grantee referent (as
subject cessation already is from `users`), default-deny ends access,
and the audit event table keeps the durable history — which removes
the trigger/actor contradiction rather than patching it (F2, F7).
Sensitive-write routes gain a static write-set closure witness so a
derived representation cannot land in any generic platform table
(F4). Managed deletion step 2 becomes a compare-and-delete on the
hash removed in step 1, witnessed against a concurrent rewrite (F9).
Ordinary sensitive writes get an ordered content-first protocol with
deterministic locators, ack-after-pointer-commit, idempotent retry,
and fault-injection witnesses (F11). §8 discloses every rule above
plus the previously undisclosed §4.3 committed-tree/symlink rule
(F6).

Revision 5 (sol r4 findings F9/F11 residual, F12/F13 new): the
pointer row gains a monotonic `generation` counter bumped by every
upsert, and managed-deletion step 2 conditions on the generation
recorded in step 1 instead of the content hash, closing the
identical-content ABA in which a byte-identical rewrite reproduced
the old hash and a stale deletion unpointed fresh content (F9). The
ordered-write protocol gains a contract-defined locator scheme —
injective over (category, slot), with the slot set fixed to the
single value `answer` at version 1 — and a per-locator advisory lock
spanning both steps, so same-locator writers are serialized and a
completed write always leaves the pointer hash describing the
current blob; §7.16 adds the concurrent-writer,
crash-before-acknowledgment, stale-retry, and locator-injectivity
witnesses (F11). Cascaded consent deletion now emits a semantic
cessation event in the same transaction as the referent deletion,
written by a trigger on the consent table, so the audit trail
records when and why each active grant ended, not only that it began
(F12). The stale §3.6/§5.2 cross-references and the "first
predicate" remnant are corrected (F13).

Revision 6 (sol r5 residuals F9/F12 + F14): the deletion and repair
token becomes the PAIR (row `id`, `generation`) — the uuid primary
key is minted fresh on every insert and never reused, so it is the
row-incarnation identity the bare counter lacked; a stale
compare-and-delete can no longer match a row reincarnated at
generation 1 after an orphan-repair delete, and §7.10 adds the
delete/reinsert ABA control (F9). The §3.7 locator lock now covers
EVERY blob/pointer mutator for a locator, not only ordinary writers:
managed deletion (§4.7) and reconciliation repair (§4.5) acquire the
same advisory lock across their observe-and-mutate spans, so the
reverse interleaving — a deleter destroying a blob a locked writer
has committed but not yet pointed — is unschedulable while the
tokens keep the crash-window (lock released by session death)
harmless; §7.10 adds the reverse interleaving and its lock-removed
control (F14). The cessation-event snapshot gains the deleted
consent row's `id` and its `user_id`, so events from two subjects'
grants to one ceasing grantee are attributable to their exact rows
and subjects, witnessed with a two-subject case (F12).

Revision 7 (sol r6 F15): the reconciliation try-acquire no longer
discharges a locator for the cycle — a failed try-acquire QUEUES the
locator within the same cycle, and before the cycle completes the
sweep revisits every queued locator with a blocking acquire, running
the same locked observation and token-conditioned repair. A
triggered reconciliation cycle is complete only when every in-scope
pointer has been processed under its locator lock, so the §4.5
one-cycle orphan bound holds unconditionally on lock contention;
the retry terminates because every §3.7 hold is transaction- or
session-scoped and session end releases the lock automatically.
§7.10 adds the lock-held contention control (sweep pass skips
without blocking; cycle completion waits for the queued repair;
the orphan is deleted before the cycle reports completion), its
repeated-contention variant, and a third static control failing an
implementation that defers a skipped locator to a later cycle
(F15).

Revision 8 (sol r7 residual F15): the one-cycle bound gains its
missing termination proof. §3.7 adds two liveness rules for the
locator lock: a declared finite HOLD DEADLINE, enforced with
session-level timeout mechanisms on every lock-holding path — a
live holder that stalls past the deadline is terminated, session
end releases the lock, and the stalled-holder schedule collapses
into the already-harmless crash window — and an arrival-order
GRANT rule, so a blocked waiter's wait is bounded by the current
holder and the waiters already ahead, never by later arrivals.
§4.5's blocking retry therefore terminates within a wall-clock
bound fixed when it starts waiting, and the implementing PR
derives and declares a maximum cycle duration whose exceedance is
surfaced as an error, never a silent hang. §7.10 adds the
hold-deadline control (a non-cooperating stalled holder is
terminated at the deadline and the orphan is still deleted before
cycle completion), the grant-order control (a stream of later
contenders cannot starve the queued repair, and the declared
cycle-duration limit is exercised as a checked bound), and a
fourth static control failing any lock-holding path without the
declared deadline (F15).

Revision 9 (sol r8 residual F15 + new F16): the hold deadline
becomes independently enforceable and the external store gains a
fence. §3.7's deadline is now enforced by a deadline supervisor
whose liveness does not depend on the stalled holder — the
database's timeout mechanisms are defense in depth, not the
enforcement path, since step 1's brain commit is not a database
statement — and every brain mutation performed under a locator
lock is FENCED: applied conditionally on the locator's brain state
still equaling the value recorded at lock acquisition, so a
terminated holder's in-flight external operation, resuming after
the lock has passed on, is refused atomically and can never remove
or replace a later holder's committed content (F16). §4.5's
deferred repair now RESERVES its queue place at the moment its
try-acquire fails, fixing the predecessor set at skip time and
closing the window between skip and queue entry; the cycle maximum
becomes the computed formula `M = S + Σᵢ(D·(Qᵢ+1)+R)` over
snapshots taken at or before each reservation, fixed when the last
reservation is placed and never enlarged; and exceedance is an
explicit transition — the cycle reports failure, never completion,
withdraws its pending reservations, and persists a durable
carry-over set the next cycle processes first — so the orphan
bound binds every completing cycle and every non-completing path
is a surfaced error, and the grant-order rule must be bound to a
documented mechanism guarantee or an application FIFO queue, not
assumed (F15). §7.10 adds two external-step deadline controls (a
stalled managed deletion and a stalled ordinary-writer brain
commit, each terminated by the supervisor mid-external-operation,
resumed after a fresh holder's acknowledged write, and asserted
refused by the fence), the reservation control, the exceedance
control, and static controls binding the grant-order guarantee and
the fenced deletion path; §7.16 adds the static fenced-commit
control (F15, F16).

Revision 10 (sol r9 residual F16 + new F17): the brain fence becomes
ABA-resistant. §3.7's fence value is now a per-locator fence EPOCH
with two invariants: every successful brain mutation mints a fresh,
never-reused value — byte-identical recommits and transitions to
absence included, absence being a unique tombstone state rather than
a recurrable bare-missing-path value — and every holder performs a
FENCE-ADVANCE immediately after lock acquisition, minting a fresh
epoch before its first brain observation or mutation, so every
earlier holder's recorded fence is invalidated at handoff, before the
new holder relies on exclusivity. A stale external tail is therefore
refused unconditionally: neither a byte-identical recommit, nor a
return to absence, nor resumption before the new holder's first
mutation can re-present a consumed epoch (F16). The live holder's
fence-pass now follows from handoff invalidation rather than from
lock possession, and a defect-only refusal has a specified
abort-and-surface transition. §4.5 repair performs the advance before
the absence observation it relies on. The unqualified distinct-locator
no-contention claim is narrowed to locator locks: a shared atomic
update point may cost swap-loop retries across locators but never
invalidates a distinct locator's condition — a liveness cost, never a
correctness coupling (F17). §7.10 adds two ABA fence controls
(byte-identical stale deletion; absence→write→delete→stale-writer
resurrection), a handoff-invalidation control with a
repair-observation variant, and a static fence-epoch-invariant
control; §7.16's static fenced-commit control is restated over the
epoch (F16, F17).

Revision 11 (sol r10 residual F16 + new F18): the fence-advance
itself is fenced against an older acquisition. §3.7 adds a
per-locator ACQUISITION TOKEN minted by the lock authority at every
lock grant — fresh, strictly increasing, existing before the
holder's first brain operation and carried unchanged through every
brain operation and swap-loop retry of that acquisition — and the
brain store records the greatest accepted token atomically with the
locator's epoch and content state, refusing any brain mutation,
the fence-advance included, that carries an older token. The
advance's acceptance condition is exactly this comparison (an epoch
condition would be circular, an unconditional mint would let a
terminated holder's delayed advance supersede a later holder's); a
refused advance mints nothing and returns no usable fence, with a
mandatory abort-release-surface transition that is an EXPECTED
outcome after deadline supersession, not a defect. Revision 10's
"refused unconditionally" claim is precisified to an exact
boundary: after the newer holder's advance linearizes, every
older-acquisition operation — its delayed advance included — is
refused; a stale content mutation linearizing BEFORE that advance
serializes entirely before anything the new holder relies on (the
advance preserves the content state it finds and mints after it),
indistinguishable from completion before the previous holder's
lock release — the defined legal outcome. The pre-step-1
interruption predicates in §3.7, §4.7, and the §7.16 fault
injection are restated exactly — content presence, content bytes,
and database rows unchanged, the advance's epoch mint and token
recording expressly permitted — resolving their contradiction with
the mandatory advance (F18). §7.10 adds a stale-advance refusal
control (fresh-writer and repair-observation variants) and a
pre-advance linearization control, and extends the fence-invariant
static control with the token invariants; §7.16's static
fenced-commit control carries the token (F16, F18).

Revision 12 (sol r11 minors F18 residual, F19, F20): three
exactness corrections to the revision-11 mechanism, no behavioral
change. §3.7's token comparison is made exact at both ends:
strictly less-than refuses (an already-recorded token re-presented
by a swap-loop retry or a later mutation of the same acquisition is
accepted), and an absent recorded token compares lower than every
minted token, so a virgin locator's first advance is accepted and
atomically creates the token record — with a §7.10 virgin-locator
control and a delayed-first-grant race variant (F19). §7.10's
remaining managed-deletion fault predicate ("both stores
unchanged") is restated as the exact §4.7 predicate — content
presence, content bytes, and database rows unchanged, the
advance's epoch mint and token recording expressly permitted
(F18 closed). The pre-advance-linearization outcome no longer
assigns the (`id`, `generation`) pair to every stale operation:
the §3.7 boundary text and the §7.10 control now distinguish the
database halves — deletion's and repair's step 2 stays
pair-conditioned, while a terminated ordinary writer performs no
pointer upsert and can continue only through a fresh acquisition
under a new token (F20).

This contract binds the profile-category registry (§2), the custody
placement rule (§3), the pointer schema (§4), the consent schema and its
evaluation (§5), mode application (§6), witnesses (§7), and disclosed
drafting additions (§8). It defines schemas and placement; wizard step
flow stays with contract 3 (`onboarding-wizard.md`), mode and conversion
with contract 6 (`mode-conversion.md`), identity with
`identity-lifecycle.md`, tool mapping with `tool-gateway-mapping.md`.

## 1. Definitions

1. **User brain**: the git-tracked per-user data store. Its two valid
   Standalone layouts are defined in §6.2; in Enterprise it is the
   user's own brain repository.
2. **Sensitive content**: any profile answer or derived text in a §2
   category classified `sensitive`.
3. **Pointer**: a database record referencing sensitive content that
   lives in a user brain, carrying no content (§4).
4. **Grantee**: a non-subject principal that may be granted access to a
   user's sensitive content: a platform agent (a row of the `agents`
   table, §5.2), a connector, or a platform feature (§5.2).
5. **Consent record**: a database record granting one grantee access to
   one category of one user's data (§5).
6. **Registry version**: the monotonically increasing integer
   identifying the active state of the §2 registry.
7. **Physical split** (Standalone): sensitive user content living in a
   per-user brain repository separate from the estate mosaic-brain, as
   opposed to the unsplit layout where it lives in a dedicated
   user-files subtree of the single mosaic-brain (§6.2).
8. **Audit event table**: the single platform table into which the
   hierarchy contract (contract 1, `hierarchy-schema.md` §5.2) commits
   its semantic audit events. No sibling contract names the physical
   table, so this contract binds it by identity, not by name: every
   `audit_event_id` column in §3.5, §4.1, §5.1, and §6.3 is a foreign
   key to that table, the implementing PR that creates the audit store
   binds these FKs to it, and the §7.1 witness asserts that each such
   FK references the same physical table the contract-1 audit write
   path inserts into.

## 2. Profile-category registry

1. **Model.** The registry is the single classifier for every profile
   category, closed and versioned, in the platform database. Table
   `profile_category_registry`, columns exactly:

   | Column           | Type        | Constraints                                          |
   | ---------------- | ----------- | ---------------------------------------------------- |
   | `category_key`   | text        | primary key                                          |
   | `classification` | text        | NOT NULL, CHECK in (`sensitive`, `non-sensitive`)    |
   | `since_version`  | integer     | NOT NULL (registry version that introduced this row) |
   | `created_at`     | timestamptz | NOT NULL                                             |

   The current registry version is a single integer held in
   `custody_config` (§6.3). Rows are added or reclassified ONLY by
   amendment to this contract shipped as a migration that bumps the
   registry version; no runtime write path may insert, update, or
   delete registry rows.

2. **Initial registry (version 1).** Drawn from the D4 profile step and
   D14's "disabilities, family, communication style, and similar":

   | Category key              | Classification             | Covers                                                              |
   | ------------------------- | -------------------------- | ------------------------------------------------------------------- |
   | `disabilities`            | sensitive                  | disabilities including ADHD/autism/PDA/vision                       |
   | `family-social`           | sensitive                  | family, pets, friends                                               |
   | `communication-style`     | sensitive                  | desired agent communication style, voice-matching interview product |
   | `personal-interests`      | sensitive                  | hobbies, likes/dislikes                                             |
   | `connector-content`       | sensitive                  | email and drive content reached through user connectors             |
   | `professional-background` | non-sensitive (per ruling) | professional background summary used for agent configuration        |
   | `education`               | non-sensitive (per ruling) | education summary used for agent configuration                      |

   The `Covers` column is contract documentation, not a database
   column. Account identity fields (email, name, credentials) are
   identity-contract data, not profile custody data, and have no
   registry row.

3. **Unknown category — refusal.** A profile write naming a
   `category_key` with no registry row is REFUSED with an explicit
   error. Nothing is stored anywhere, no registry row is auto-added
   (§2.1), and no pointer is created (so the §4.1 foreign key is never
   asked to reference a missing row). Fail-closed means refusal, not
   silent routing.
4. **Reclassification transitions.** A reclassification ships as a
   contract amendment plus migration that bumps the registry version.
   Non-sensitive → sensitive: the same migration moves every existing
   relational value for that category into its owning user's brain,
   creates the pointers, and deletes the relational values, all before
   the new version activates. Sensitive → non-sensitive: existing
   brain content and pointers remain valid and are never automatically
   materialized into the database; only writes evaluated after the new
   version activates route relationally.
5. **Unreadable registry — refusal.** If the registry or its version
   cannot be read at decision time, every routing and consent decision
   that depends on it is refused. There is no cached-default or
   assume-sensitive fallback that performs a write.
6. **Write fence.** Every profile-write transaction reads the current
   registry version by taking a **shared lock** on the `custody_config`
   row (`SELECT ... FOR SHARE`) inside the same database transaction
   that performs the write, and routes by the version so read. A
   reclassification migration takes an **exclusive lock** on that row
   (`SELECT ... FOR UPDATE`) before its scan and holds it through the
   value moves and the version bump. Isolation and statement order
   are part of the fence, not implementation freedom: both the
   profile-write transaction and the migration transaction run at
   `READ COMMITTED`, and the migration's `SELECT ... FOR UPDATE` is
   the FIRST statement of its transaction — no read precedes it, so
   the migration establishes no snapshot before it holds the lock.
   Under `READ COMMITTED` each subsequent statement takes a fresh
   snapshot, so the migration's scan — which runs only after the lock
   is granted — sees every row committed by every `FOR SHARE` holder
   that finished before the lock was granted. Consequently a
   concurrent writer either commits before the lock is granted — its
   value is then visible to and swept by the scan — or blocks until
   the migration commits and then routes by the new version. No
   interleaving exists in which a value routed under the old version
   escapes the scan, including the writer-commits-while-migration-
   waits interleaving (witness §7.6g).
7. **Supported version.** Each release declares the exact registry
   version its code implements as an exported compile-time constant
   in the custody module of the shared schema package; the
   implementing PR names the symbol and §7.6(h) pins it by symbol
   identity, so the declaration is source, not configuration. Every
   profile routing decision and every consent decision reads
   `custody_config.registry_version` under a `SELECT ... FOR SHARE`
   on the `custody_config` row inside the same database transaction
   as the decision (write decisions share the §2.6 fence transaction;
   read-only consent evaluations take the shared lock in their own
   transaction), so the supported-version comparison and the decision
   it gates use one atomically read version. If the declared version
   does not equal the version so read, the decision is refused — a
   readable but unsupported registry is a refusal, never best-effort
   routing under either version (witness §7.6h).

## 3. Custody placement rule

1. Sensitive content is written to the owning user's brain ONLY.
   PostgreSQL tables MUST NOT store sensitive content in any column —
   not as text, not as excerpts or previews, not as encodings, and not
   as embeddings or other derived representations that reconstruct
   content.
2. The database MAY hold, about sensitive content: the pointer records
   of §4, the consent records of §5, the registry of §2, and the
   custody configuration of §6.3. Nothing else.
3. Every write path for profile answers routes by the registry:
   `sensitive` → brain write + pointer upsert; `non-sensitive` → the
   `profile_answers` table (§3.5); unknown → refusal (§2.3). The
   routing decision is made server-side from the registry at its
   current version, inside the §2.6 write fence; a client-supplied
   classification or routing override is ignored.
4. D6 boundary: operational records stay in estate brains and are
   linked, not migrated. This contract governs user-profile custody
   only and creates no new obligation on estate brains.
5. **Non-sensitive profile table.** Non-sensitive profile answers land
   in exactly one declared table, `profile_answers`, columns exactly:
   `id` (uuid, primary key), `user_id` (text, NOT NULL, FK → users(id)
   ON DELETE CASCADE), `category_key` (text, NOT NULL, FK →
   profile_category_registry(category_key)), `value` (text, NOT NULL),
   `created_at` and `updated_at` (timestamptz, NOT NULL), and
   `audit_event_id` (uuid, NOT NULL, FK → the §1.8 audit event table);
   plus UNIQUE (`user_id`, `category_key`). A value may sit in this
   table ONLY while its category's registry classification is
   `non-sensitive` (§2.4 migrates the rows on reclassification). No
   other platform table stores profile answer content.
6. **User-key type binding.** The identity store declares `users.id`
   as `text` (BetterAuth identifiers, `packages/db` schema); every
   `user_id` column in this contract (§3.5, §4.1, §5.1) is therefore
   `text`, matching the referenced key's declared type exactly. The
   identifiers are opaque strings, not guaranteed UUIDs; no custody
   column re-types, parses, or reformats them. If a sibling contract
   ever migrates the user key type, an amendment to this contract
   follows it (§7.1 asserts the FK types match the live referenced
   columns).
7. **Ordered sensitive-write protocol.** An ordinary sensitive write
   spans two stores and is ordered, content first.

   **Locator scheme.** The locator is a pure function
   `L(category_key, slot) → brain_ref` declared by the implementing
   PR, with three contract-bound properties: it is deterministic (a
   retry of the same write resolves to the same path), it is
   INJECTIVE — distinct (category_key, slot) pairs map to distinct
   `brain_ref` values, witnessed with a collision control (§7.16) —
   and it is stable across releases (a scheme change is a contract
   amendment with a migration). An **answer slot** is the stable
   identifier of one answer's position within a category; at
   version 1 every registry category is single-valued (mirroring
   §3.5's UNIQUE (`user_id`, `category_key`)), so the slot set is
   the single constant `answer`. An amendment introducing a
   multi-valued category must extend the slot set while preserving
   injectivity.

   **Serialization.** The entire write — both steps — runs under a
   per-locator mutual exclusion: before step 1 the writer acquires
   an exclusive advisory lock keyed on (`user_id`, `category_key`,
   slot) (e.g. a session-scoped `pg_advisory_lock` on a stable hash
   of the triple), held until step 2 commits or the writer's session
   ends (session end releases it automatically, so a crashed holder
   never wedges the locator). Two liveness rules bound this
   exclusion. **Hold deadline:** every session that acquires a
   locator lock — ordinary writer, managed deleter (§4.7),
   reconciliation repair (§4.5) — runs under a declared finite hold
   deadline: the implementing PR declares the value and enforces it
   through a **deadline supervisor** whose liveness is INDEPENDENT
   of the holder — a separate live component, not a timer inside
   the possibly-stalled holder process, that observes lock
   acquisition times and terminates the database session of any
   holder past its deadline. The database's session-level timeout
   mechanisms (statement timeout, idle-in-transaction timeout) are
   configured as defense in depth but are not the enforcement
   path: a statement timeout aborts a statement without ending the
   session, an idle-in-transaction timeout fires only on an idle
   open transaction, and step 1's brain commit is not a database
   statement at all — so a session stalled inside an external
   operation is terminated by the supervisor, which no
   database-side mechanism replaces. A holder that stalls past the
   deadline thus has its session TERMINATED — session end then
   releases the lock automatically per the rule above. The
   terminated holder's partial DATABASE protocol is exactly the
   crash window the tokens already make harmless (§4.5, §4.7); its
   possibly still in-flight EXTERNAL operation is made harmless by
   the brain fence below. No holder
   — live, stalled, or crashed — holds a locator lock longer than
   the declared deadline. **Grant order:** conflicting waiters on
   one locator lock are granted in arrival order — a requester that
   starts waiting after another never acquires before it. The
   implementing PR MUST bind this property to a mechanism, not
   assume it: either it cites the pinned advisory-lock mechanism's
   documented queue-order grant guarantee, or, absent such a
   documented guarantee, it routes every conflicting locator-lock
   acquisition through an application-level FIFO queue whose order
   invariant is itself part of the implementation; the binding is
   checked by a §7.10 static control and the property is exercised
   dynamically (§7.10) — so a blocked waiter's wait is bounded by the
   current holder's remaining deadline plus the deadline-bounded
   holds of the waiters already ahead of it, never by later
   arrivals. Writers to one locator are therefore
   totally ordered; writes to distinct locators do not contend FOR
   LOCATOR LOCKS. The brain store's atomic update point MAY be
   shared across locators (a single head compare-and-swap in a
   git-backed store serializes physically unrelated writes): losing
   that swap costs a retry of the swap loop, and the retry
   re-validates only the writer's own per-locator condition (Brain
   fence below), which an unrelated locator's mutation never
   changes — so cross-locator contention at a shared update point
   is a liveness cost, never a correctness coupling.
   Because no two same-locator writes interleave, a completed write
   always leaves the pointer's `content_hash` describing the blob
   currently committed at the locator — the divergent interleaving
   (writer A's pointer over writer B's content) is unschedulable,
   not merely unlikely.

   **Brain fence.** Deadline termination releases the lock while an
   external brain operation started by the terminated holder may
   still be in flight; lock release is therefore not, by itself, a
   fence for the external store. Two per-locator values fence it:
   an **acquisition token** ordering holders and a **fence epoch**
   ordering mutations. The acquisition token is minted by the lock
   authority at EVERY grant of the locator lock: a fresh, strictly
   increasing per-locator value handed to the holder with the
   grant — it therefore exists before the holder's first brain
   operation, independently of any brain state — and carried
   UNCHANGED through every brain operation of that acquisition,
   including every retry of a swap loop; a holder never re-mints
   or refreshes its token. The fence epoch is a per-locator value
   stored in the brain store, atomically readable
   and comparable together with the locator's content state, whose
   defining property is that every change to it MINTS a fresh value
   never before used at that locator. A per-locator monotonic
   counter satisfies this, as does a unique commit identity in a
   git-backed store; the locator's content BYTES do not — two
   byte-identical states at different times are distinct epochs —
   and ABSENCE is itself an epoch-bearing state, recorded as a
   unique tombstone, never a bare missing path whose "value" can
   recur. Three rules bind them. **Minting:** every successful
   brain mutation under a locator lock — a content commit, a
   byte-identical recommit, a content deletion (transition to
   absence) — advances the locator's epoch to a fresh never-reused
   value, atomically with the mutation. **Token recording:** the
   brain store records, per locator and atomically with the
   locator's epoch and content state, the greatest acquisition
   token it has accepted; EVERY brain mutation under a locator
   lock — the fence-advance below included — presents its holder's
   acquisition token, is REFUSED atomically when that token is
   less than the recorded value, and records it on acceptance.
   The comparison is exact at both ends: it is strictly
   less-than — re-presenting the already-recorded token, as a
   swap-loop retry or a later mutation of the same acquisition
   does, is accepted — and a locator with NO recorded token
   compares as lower than every minted token, so a virgin
   locator's first presented token is never refused by this
   comparison and its acceptance atomically CREATES the token
   record; an implementation whose absent-token comparison can
   refuse or fail to record the first holder is nonconforming
   (witness §7.10).
   **Fence-advance at
   acquisition:** immediately after acquiring the locator lock,
   before its first brain observation or mutation at the locator,
   the holder performs a fence-advance — an atomic epoch mint that
   leaves the locator's content presence and bytes unchanged — and
   records the resulting epoch as its **fence value**; the advance
   invalidates every fence recorded by any earlier holder before
   the new holder relies on anything it reads or on its
   exclusivity. The advance's OWN acceptance condition is exactly
   the token comparison — not the epoch (no fence exists for the
   acquisition until the advance returns, so an epoch condition
   would be circular) and not nothing (an unconditional mint would
   let a terminated holder's delayed advance supersede a later
   holder's): the token is minted at lock grant, so it exists
   before the advance, and grants for one locator are totally
   ordered, so the comparison encodes acquisition order. A refused
   advance returns NO usable fence: the holder MUST NOT perform
   any brain observation or mutation, MUST abort its protocol run
   before any later step, release the lock if it still holds it,
   and surface the error — and this refusal is an EXPECTED outcome
   for an acquisition whose lock was deadline-released and
   re-granted before its delayed advance applied, not a defect.
   Every other brain mutation performed under a locator
   lock — an ordinary write's step-1 content commit (below), a
   managed deletion's step-1 content deletion (§4.7) — is applied
   conditionally: the brain store applies it only if the locator's
   epoch at application time still equals the holder's recorded
   fence value AND the token-recording comparison accepts, with
   the checks and the application atomic with
   respect to other brain mutations (compare-and-swap semantics; an
   atomic ref update in a git-backed store satisfies this, with the
   per-locator conditions re-validated inside the swap loop
   when the swap is taken on a coarser head). A stale operation —
   one whose session was terminated and whose lock has passed to a
   newer holder — is refused at an exact boundary. Once the newer
   holder's fence-advance has linearized, EVERY operation of the
   older acquisition is refused: its epoch condition fails (the
   advance consumed its epoch, and no sequence of mutations can
   re-present a consumed one — whether the intervening history
   changed the bytes, recommitted byte-identical content, returned
   the locator to absence, or consists of the advance alone), its
   token comparison fails, and its own delayed fence-advance is
   refused by the token comparison, minting nothing — a superseded
   acquisition cannot manufacture a fresh fence. Before the newer
   advance linearizes, a stale content mutation whose conditions
   still hold MAY linearize first: the advance then finds the
   post-mutation state, expressly preserves the content presence
   and bytes it finds, mints its epoch after that mutation, and
   the new holder's first relied-on observation follows its own
   advance — so the stale effect serializes entirely before
   anything the new holder relies on, indistinguishable from the
   same operation completing before the previous holder's lock
   release, an outcome the protocol already defines as legal. What
   the stale operation's DATABASE half may then do depends on its
   protocol: a managed deletion's or repair's step 2 remains
   conditioned on the recorded (`id`, `generation`) pair (§4.5,
   §4.7) and cannot unpoint any later row incarnation; an ordinary
   write's terminated run performs NO pointer upsert — its
   database session is dead, the committed-but-unpointed content
   falls into the existing step-1-to-step-2 interruption window
   repaired by §4.5, and any retry is a NEW lock acquisition under
   a fresh token, never a continuation of the dead run. These are the only two outcomes: a stale
   operation either serializes wholly before the new holder's
   advance or is refused; it never applies after the advance, and
   it cannot remove or replace content committed by any later
   holder, whether it resumes microseconds
   or hours after the termination. A live holder's own fenced
   mutation cannot fail its conditions — not because it holds the
   lock, but because handoff invalidation plus minting mean the
   only epochs minted since its fence-advance are its own, and
   because lock grants are serialized by the lock itself, no
   greater acquisition token can be recorded while it holds the
   lock; a
   holder whose fenced mutation is nonetheless refused (reachable
   only under a defect breaching these invariants) MUST abort its
   protocol run, release the lock, and surface the error — it
   never retries the mutation under its stale fence. Every
   brain-mutation path carries the fence and the acquisition
   token, and every brain-RELYING
   observation path (reconciliation repair's absence observation,
   §4.5) performs its fence-advance before the observation it
   relies on; a path applying an unfenced or token-free brain
   mutation fails the
   §7.10 and §7.16 static controls.

   **Steps.** Step 1 commits the content to the owning user's brain
   at `L(category_key, slot)` as a fenced mutation (Brain fence
   above); step 2, only after step 1 has
   committed, upserts the pointer row (insert, or update of the
   existing row for that (user, category, brain_ref), incrementing
   `generation` per §4.1) in its own database transaction inside the
   §2.6 fence. The write is acknowledged to the caller only after
   step 2 commits. Interruption before step 1 leaves content
   presence, content bytes, and every database row unchanged — the
   post-acquisition fence-advance may already have minted an epoch
   and recorded an acquisition token, the only brain change the
   protocol permits before step 1. Interruption between the steps leaves committed brain
   content with no pointer: unpointed content is inert — it lives in
   the correct custody store, no read path serves it (reads resolve
   through pointers only), and nothing dangles in the database.
   Interruption after step 2 commits but before the acknowledgment
   reaches the caller leaves the converged state; the retry below
   observes it and returns success without corrupting it. The
   client's retry — the write was never acknowledged — re-acquires
   the locator lock, recommits the same locator, and completes the
   pointer upsert, converging to the pointed state; the retry is
   idempotent whichever interruption point preceded it. A stale
   retry (an earlier write retried after a later same-locator write
   completed) also executes under the lock and leaves a consistent
   pointed state — same-locator outcomes are last-completed-write
   wins, and no completion order can leave the hash describing a
   non-current blob. The reverse order (pointer before content) is
   forbidden: an ordinary write never creates a pointer whose
   content has not committed. Witness §7.16 fault-injects the
   interruption points, drives retry convergence, the same-locator
   concurrent interleaving, the stale retry, and the injectivity
   control.

## 4. Pointer schema

1. **Exact columns.** Table `profile_pointers`, columns exactly:

   | Column           | Type        | Constraints                                            |
   | ---------------- | ----------- | ------------------------------------------------------ |
   | `id`             | uuid        | primary key                                            |
   | `user_id`        | text        | NOT NULL, FK → users(id) ON DELETE CASCADE (§3.6)      |
   | `category_key`   | text        | NOT NULL, FK → profile_category_registry(category_key) |
   | `brain_ref`      | text        | NOT NULL, CHECK against the §4.3 grammar               |
   | `content_hash`   | text        | NOT NULL (§4.4 construction)                           |
   | `generation`     | bigint      | NOT NULL (1 on insert; every §3.7 upsert increments)   |
   | `created_at`     | timestamptz | NOT NULL                                               |
   | `updated_at`     | timestamptz | NOT NULL                                               |
   | `mismatch_at`    | timestamptz | NULL unless the pointer is in the §4.6 mismatch state  |
   | `audit_event_id` | uuid        | NOT NULL, FK → the §1.8 audit event table              |

   plus the database constraint UNIQUE (`user_id`, `category_key`,
   `brain_ref`) — the one-pointer rule is a constraint, not a
   convention.

2. **Opacity.** `brain_ref` and every other pointer column MUST NOT
   embed content or content-derived text (no titles, snippets, or
   free-text descriptions). A locator is structural, not descriptive.
3. **`brain_ref` grammar and owner binding.** `brain_ref` is a
   normalized repository-relative POSIX path: one or more segments
   matching `[A-Za-z0-9][A-Za-z0-9._-]*`, joined by `/`, with no
   leading `/`, no empty segment, and no `.` or `..` segment; the
   stored form matches
   `^[A-Za-z0-9][A-Za-z0-9._-]*(/[A-Za-z0-9][A-Za-z0-9._-]*)*$`.
   Resolution ALWAYS roots at the brain owned by the row's `user_id`
   (the resolver takes the owner from the row, never from the
   locator); the locator carries no repository, host, or user
   component, so a cross-user or traversal reference is
   unrepresentable, not merely forbidden. Resolution operates on the
   brain repository's committed git tree (tree and blob objects),
   never through filesystem path lookup: if any segment of the path
   resolves to a symbolic link — or to anything other than a tree
   (intermediate segments) or a blob (final segment) — resolution is
   refused. A symlink therefore cannot redirect a locator outside the
   owner's brain (witness §7.8).
4. **`content_hash` construction.** `content_hash` is
   `hmac-sha256:<key id>:<hex>` — HMAC-SHA-256, keyed with a platform
   integrity key held in the secrets backend and never stored in the
   database or any repository, over the length-prefixed concatenation
   of: a fixed domain-separation string naming this contract and the
   construction version, the row's `user_id`, the row's
   `category_key`, the `brain_ref`, and the canonical content bytes.
   The canonical content bytes are the exact bytes of the committed
   blob the locator resolves to (§4.3) — no normalization. Because
   the input is domain-separated per (user, category, locator), equal
   answers from different users or categories store different hashes:
   the column is neither an offline dictionary oracle (external key)
   nor an equality/correlation oracle across rows (domain
   separation). `<key id>` names the key used; rotation introduces a
   new key id for new writes, re-verification accepts any
   still-registered key id, and a key is retired only when no stored
   hash references it. On read, a hash mismatch refuses the read and
   puts the pointer into the §4.6 mismatch state.
5. **Bounded orphan repair.** Pointers are deleted when their content
   is deleted; dangling pointers are repaired toward deletion, never
   toward re-creating content in the database. Reconciliation for a
   user's pointers runs on two triggers: every profile write for that
   user, and a periodic sweep whose interval the implementing PR
   declares (at most daily). A pointer whose content is absent is
   deleted by the next triggered reconciliation cycle that reports
   COMPLETION — no orphan survives a completed cycle; a cycle that
   cannot complete within its computed maximum fails as an explicit
   surfaced error with durable carry-over (below), never silently —
   and repair performs no database content write.
   Per-pointer repair runs under the §3.7 locator lock: before
   observing content absence for a pointer, reconciliation acquires
   the pointer's locator lock, performs the §3.7 fence-advance
   under repair's own acquisition token —
   invalidating any stale in-flight operation's fence BEFORE the
   observation repair relies on, so no terminated holder's resuming
   mutation — its delayed fence-advance included, refused by the
   §3.7 token comparison — can change the locator between repair's
   observation and
   its DELETE — holds the lock across the observation and the
   DELETE, and releases it after. The sweep pass MAY use a
   try-acquire so it never blocks mid-pass behind a writer, but a
   failed try-acquire defers the locator, never discharges it — and
   the deferral RESERVES its place: at the moment the try-acquire
   fails, the sweep submits the locator's blocking lock request
   (the **reservation point**), so the request enters the lock
   queue immediately and its predecessor set — the current holder
   plus the waiters already queued at that instant — is fixed at
   skip time; contenders arriving afterward are later arrivals
   that §3.7 grant order places behind it, and there is no window
   between the try-acquire failure and queue entry in which a new
   contender can join the predecessor set. The sweep proceeds with
   the pass while the reservation waits, and before the cycle
   completes it returns to every reserved locator, completes the
   blocking acquire, and runs the same locked observation and
   token-conditioned repair. A
   triggered reconciliation cycle is COMPLETE only when every
   in-scope pointer has been processed under its locator lock;
   deferral moves work to the end of the cycle, never into a later
   one except through the exceedance transition below — which
   fails the cycle loudly, never completes it — so the
   completed-cycle bound above holds unconditionally on lock
   contention. The blocking retry terminates within a WALL-CLOCK
   BOUND, not merely eventually: every holder is subject to the
   §3.7 hold deadline (a stalled live session is terminated by the
   independent supervisor at the deadline and session end releases
   the lock — voluntary release is not assumed), and grants follow
   §3.7 arrival order (no later requester overtakes the queued
   reservation), so the reservation is granted within the current
   holder's remaining deadline plus the deadline-bounded holds of
   the waiters already queued ahead — a finite quantity fixed at
   its reservation point, independent of how many contenders
   arrive later. Cycle completion is therefore itself bounded by a
   COMPUTED maximum fixed within the cycle: the implementing PR
   declares the hold deadline `D`, a sweep-pass bound `S` over the
   cycle's scope (the scope is snapshotted when the cycle starts),
   and a per-locator repair bound `R` (at most `D`), and computes
   the cycle maximum `M = S + Σᵢ (D·(Qᵢ+1) + R)` over the reserved
   locators `i`, where `Qᵢ` is the number of waiters queued ahead
   of reservation `i` at its reservation point. Every quantity in
   the formula is a snapshot taken at or before the corresponding
   reservation, so `M` is fully determined when the last
   reservation is placed and is NEVER enlarged afterward — queue
   growth after a reservation cannot extend it, because later
   arrivals are not predecessors. **Exceedance transition.** A
   cycle that reaches its computed `M` with reserved locators
   unprocessed stops: it withdraws its pending lock reservations,
   reports FAILURE — never completion — surfaces the error, and
   PERSISTS its unprocessed reserved locators as a durable
   CARRY-OVER set. The next triggered reconciliation cycle takes
   the carry-over set into its scope FIRST — fresh reservations,
   its own computed maximum — before its own sweep. An orphan is
   deleted before its cycle reports completion, within that
   cycle's computed `M`; the only path past a cycle boundary is
   the exceedance transition, an explicit surfaced failure with
   durable carry-over — never a silent hang and never a silent
   drop.
   The repair DELETE is additionally token-conditioned like §4.7
   step 2: it records the row's `id` and `generation` when it
   observes the content absent and conditions the DELETE on BOTH —
   the uuid `id` is minted per insert and never reused, so it
   identifies the row incarnation; `generation` orders upserts
   within the incarnation. A concurrent §3.7 write that recommits
   the locator between the observation and the delete (bumping
   `generation`), and a delete-then-reinsert that reincarnates the
   locator's row (fresh `id`, `generation` back at 1), each make
   the repair match zero rows — reconciliation never unpoints
   content it did not observe absent, under either token
   dimension.
6. **Mismatch state.** A failed §4.4 verification stamps the pointer's
   `mismatch_at` and the read is refused; every subsequent read of a
   pointer with `mismatch_at` set is refused without re-serving
   content. Reconciliation (§4.5 triggers) re-verifies each flagged
   pointer: verification success clears `mismatch_at`; content absent
   → the pointer is deleted (§4.5); persistent mismatch → the pointer
   is retained flagged with reads refused — the terminal outcome. The
   subject's next successful profile write for that category replaces
   the content, recomputes the hash, and clears the flag. Mismatch
   handling never deletes brain content and never copies content into
   the database.
7. **Managed deletion protocol.** A managed deletion of sensitive
   content spans two stores and is an ordered protocol, not a single
   transaction. **Lock coverage**: the deleter FIRST acquires the
   §3.7 advisory lock on the target locator (`user_id`,
   `category_key`, slot) — the same lock ordinary writers hold —
   before reading the pointer row or touching either store, and
   holds it until step 2 commits or the deleter's session ends.
   Every blob/pointer mutator for one locator — ordinary writes
   (§3.7), managed deletion, and reconciliation repair (§4.5) —
   therefore serializes on one lock: the reverse interleaving, in
   which a deleter reads the old pointer and destroys a blob that a
   lock-holding writer has committed in its step 1 but not yet
   pointed, is unschedulable, because the deleter cannot enter its
   protocol while the writer holds the lock. **Steps**: step 1
   commits the content deletion to the user's brain repository as
   a FENCED mutation (§3.7 Brain fence — conditioned on the fence
   epoch recorded at the deleter's post-acquisition fence-advance,
   carrying the deleter's acquisition token,
   and itself minting the locator's unique absence tombstone
   epoch), recording the target row's
   `id` AND `generation` as read after
   lock acquisition and before step 1; step 2, only after step 1
   has committed, deletes the pointer row in its own database
   transaction as a **compare-and-delete** — the DELETE is
   conditioned on the recorded `id` AND `generation`. The token is
   this PAIR, not the content hash and not the generation alone,
   deliberately: every §3.7 upsert increments `generation` even
   when it recommits byte-identical content (whose §4.4 hash is
   unchanged), so a same-row rewrite moves the row past the
   recorded generation; and the uuid `id` is minted fresh on every
   insert and never reused, so a row REINCARNATION — the orphan row
   deleted by §4.5 repair, then an ordinary write recreating the
   locator with a fresh row whose `generation` is back at 1 —
   cannot reproduce the recorded pair even though the counter value
   repeats. In every such case step 2 matches zero rows, deletes
   nothing, and the fresh content stays pointed: deletion never
   removes a pointer row it did not observe. The lock excludes the
   live interleavings; the pair token makes the crash windows
   harmless — a deleter whose session dies after step 1 releases
   the lock (§3.7 rule), and whatever runs afterward, its never-run
   step 2 has no effect and a later deletion request is a NEW
   protocol run reading current state. A deleter TERMINATED at the
   §3.7 hold deadline while its step-1 brain deletion is still in
   flight is likewise harmless on the brain side: the in-flight
   deletion is fenced, so once a later holder performs its §3.7
   fence-advance — before that holder's first mutation, and
   regardless of whether its eventual commit is byte-identical to
   the state the deleter recorded — the stale deletion's
   conditional application fails and removes nothing: content
   committed and acknowledged after the termination cannot be
   destroyed by the terminated deleter's resuming operation
   (witness §7.10). Interruption before step 1
   commits leaves content presence, content bytes, and every
   database row unchanged — the deleter's post-acquisition
   fence-advance may already have minted an epoch and recorded an
   acquisition token, the only brain change the protocol permits
   before step 1. Interruption between the
   steps leaves a dangling pointer, which §4.5 repairs toward
   deletion within one reconciliation cycle. At no point does any
   compensation write content into the database (witness §7.10,
   including both concurrent-rewrite interleavings — changed
   content and identical content — the delete/reinsert
   reincarnation, and the reverse writer/deleter interleaving with
   its lock-removed control).

## 5. Consent schema and evaluation

1. **Exact columns.** Table `profile_consents`, columns exactly:

   | Column           | Type        | Constraints                                                                   |
   | ---------------- | ----------- | ----------------------------------------------------------------------------- |
   | `id`             | uuid        | primary key                                                                   |
   | `user_id`        | text        | NOT NULL, FK → users(id) ON DELETE CASCADE (the data subject, §3.6)           |
   | `grantee_type`   | text        | NOT NULL, CHECK in (`agent`, `connector`, `feature`)                          |
   | `agent_id`       | uuid        | NULL, FK → agents(id) ON DELETE CASCADE (§5.2)                                |
   | `connector_id`   | text        | NULL, FK → custody_connector_registry(connector_key) ON DELETE CASCADE (§5.2) |
   | `feature_key`    | text        | NULL, FK → custody_feature_registry(feature_key) ON DELETE CASCADE (§5.2)     |
   | `grantee_ref`    | text        | generated stored: COALESCE(agent_id::text, connector_id, feature_key)         |
   | `category_key`   | text        | NOT NULL, FK → profile_category_registry(category_key)                        |
   | `state`          | text        | NOT NULL, CHECK in (`granted`, `revoked`)                                     |
   | `granted_at`     | timestamptz | NOT NULL                                                                      |
   | `revoked_at`     | timestamptz | CHECK ((state = 'granted') = (revoked_at IS NULL))                            |
   | `actor`          | text        | NOT NULL (the authenticated principal that recorded the change)               |
   | `audit_event_id` | uuid        | NOT NULL, FK → the §1.8 audit event table                                     |

   plus: the CHECK that exactly one referent column is non-NULL and
   matches the discriminant — `(grantee_type = 'agent') = (agent_id
IS NOT NULL)` AND `(grantee_type = 'connector') = (connector_id IS
NOT NULL)` AND `(grantee_type = 'feature') = (feature_key IS NOT
NULL)` — and the partial unique index UNIQUE (`user_id`,
   `grantee_type`, `grantee_ref`, `category_key`) WHERE `state =
'granted'` — at most one active grant per (user, concrete grantee,
   category), as a database constraint.

2. **Grantee reference model.** A grantee is identified by
   (`grantee_type`, the matching referent column); its canonical
   display form is `<grantee_type>:<grantee_ref>`. The discriminant
   set is closed at the three CHECK values, and every referent is a
   real foreign key:
   - `agent` → `agent_id` references the platform **`agents` table**
     (`agents.id`, uuid) as declared in the shared schema package
     (`packages/db`). This is a binding to the live schema, named
     honestly: the ratified `identity-lifecycle.md` defines no agent
     enrollment surface or enrolled-agent table, so there is no
     sibling-defined referent to bind to — the `agents` table is
     today's only platform representation of agents, and the §7.11
     witness pins the FK's referenced table to that schema symbol.
     If a sibling amendment later introduces an agent-enrollment
     surface with its own table, an amendment to this contract
     re-binds the FK; until then, rows in `agents` are the closed
     set of possible agent grantees.
   - `connector` → `connector_id` references this contract's own
     table `custody_connector_registry`, columns exactly:
     `connector_key` (text, primary key), `since_version` (integer,
     NOT NULL), `created_at` (timestamptz, NOT NULL). Rows are added
     ONLY by amendment to this contract (§2.1 pattern); the table is
     EMPTY at version 1.
   - `feature` → `feature_key` references this contract's own table
     `custody_feature_registry`, with the same three-column shape
     (`feature_key` primary key) and the same amendment-only rule;
     EMPTY at version 1 (no feature grantee exists until an amendment
     names one).

   A consent row naming a nonexistent referent violates its FK and is
   refused by the database. A grant to one agent confers nothing on
   another agent of the same type; the constraint key includes
   `grantee_ref`, so two same-type grantees are distinct rows.

3. **Default deny.** Absence of an active `granted` row for (user,
   grantee, category) means no access. There are no implicit grants,
   no platform-admin bypass, and no mode in which default-deny is
   suspended.
4. **Mutation authority — subject only.** Every consent mutation
   (create, grant, revoke) must satisfy the single predicate: the
   authenticated actor IS the row's data subject — the server asserts
   `actor` equals the principal of the row's `user_id` before the
   mutation commits. A platform admin has no consent-mutation
   capability over another user's rows — an admin grant or revocation
   on another user's data is refused at write time, closing the
   write-side route around §5.3. There is no system-actor mutation
   predicate: revision 3's system auto-revocation is withdrawn
   (disclosed, §8), because grantee cessation is handled structurally
   instead —
   **Cessation by cascade**: when a grantee ceases to exist — the
   `agents` row is deleted, or a `custody_connector_registry` /
   `custody_feature_registry` row is removed by an amendment
   migration — the referent FK's `ON DELETE CASCADE` removes that
   grantee's consent rows in the same transaction as the referent
   deletion. Access ends with the rows (§5.3 default deny: no row, no
   access); no UPDATE is performed, so no actor question arises.
   **Cessation event.** The cascade is not silent: an AFTER DELETE
   trigger on `profile_consents` inserts, for every deleted row whose
   deletion the §5.5 trigger permitted (i.e. a cessation cascade),
   one semantic **consent-cessation event** into the §1.8 audit
   event table, in the same database transaction as the referent
   deletion — if the deleting transaction rolls back, no cessation
   event survives, and if it commits, the events commit with it.
   Each event carries: the cause (`subject-cessation` or
   `grantee-cessation`, plus the parent table and deleted key), the
   initiating actor — a platform surface that deletes a referent
   sets a transaction-local actor variable the trigger reads; absent
   one, the event records actor `system:cascade` (a migration or
   direct referential action) — an immutable snapshot of the ended
   grant identifying its exact row and subject: the deleted consent
   row's `id`, its `user_id` (the data subject), grantee type and
   ref, `category_key`, `state`, `granted_at`, and `revoked_at` —
   and the event time. The row `id` and `user_id` are REQUIRED
   precisely because the cause alone identifies only the ceasing
   referent: when two users hold grants to one ceasing grantee, the
   cascade removes both rows in one transaction, and only the
   per-row snapshot distinguishes which subject's grant each event
   records. The snapshot is consent
   metadata, never profile content, so §3.1 is not implicated. The
   audit table therefore records both ends of every grant's life:
   the mutation events written at grant/revoke time survive the
   cascade (audit events are never cascade-deleted), and the
   cessation event records when, why, and by whom the active grant
   ended. The same rule already governs subject cessation via the
   `user_id` FK. §7.12 witnesses the subject predicate, its refusal
   complement, the cascade, and the cessation event's atomicity,
   fields, and survival; every §7.3-enumerated consent-mutation
   route asserts the subject predicate.

5. **Revocation and re-grant.** Revocation flips exactly one active
   row to `revoked` and stamps `revoked_at`; it is effective for every
   access evaluated after the revoking write commits. Revoked rows are
   retained as history and never mutated again — enforced by a
   database trigger on `profile_consents` that permits UPDATE only
   when it is the granted → revoked transition (the state flip plus
   the `revoked_at` stamp, every other column unchanged), and permits
   DELETE only when the row's referenced subject or grantee no longer
   exists — the trigger checks that the `users` row named by
   `OLD.user_id`, or the referent row named by the grantee column, is
   absent, which is true exactly during a §5.4 cessation cascade
   (referential cascades delete the parent row before the dependent
   rows, so the check distinguishes a cascade from a standalone
   DELETE). Every other UPDATE and every DELETE with both referents
   alive is rejected, so a revoked row cannot be flipped back to
   `granted` and history cannot be pruned by anyone, including
   through direct SQL (witness §7.13). A re-grant after
   revocation inserts a NEW row (append-only history) — repeated
   grant/revoke cycles are represented as successive rows, and the
   §5.1 partial unique index guarantees the old revoked rows cannot
   keep access live.
6. **Evaluation placement.** Access to sensitive content is mediated by
   the platform (Gateway/tooling) evaluating consent before any brain
   read on behalf of a grantee; the evaluation fails closed
   (`rbac-grant-model.md` §3.5 pattern), including when the consent
   state cannot be read (§2.5 pattern). The user reading their own
   data is not a grantee and needs no consent row.
7. Consent records govern agentic/feature access to user data. They are
   distinct from hierarchy grants (contract 2) and confer no platform
   authorization.

## 6. Mode application

1. The §2–§5 schemas are mode-independent: Standalone and Enterprise
   use the same tables and the same routing rule.
2. **Standalone layouts.** The D14 physical split remains a MAY. Its
   two valid layouts are: **split** — sensitive user content in a
   per-user brain repository separate from the estate mosaic-brain
   (the recommended default); **unsplit** — sensitive user content in
   the dedicated user-files subtree `users/<user id>/` of the single
   mosaic-brain. §3 binds the logical user-brain region identically in
   both layouts; the layout election changes where the region lives,
   never whether routing applies.
3. **Election record.** The election lives in this contract's own
   one-row table `custody_config`, columns exactly: `id` (uuid,
   primary key), `singleton` (boolean, NOT NULL, DEFAULT true, CHECK
   (`singleton`), UNIQUE — the one-row rule as a database constraint:
   a second row cannot satisfy the CHECK and the UNIQUE index
   simultaneously), `standalone_layout` (text, NOT NULL, CHECK in
   (`split`, `unsplit`), default `split`), `registry_version`
   (integer, NOT NULL, §2.1), `elected_at` (timestamptz, NOT NULL),
   `actor` (text, NOT NULL), `audit_event_id` (uuid, NOT NULL, FK →
   the §1.8 audit event table). It is written at bootstrap and
   amended only by an explicit operator action; contract 6's exact,
   immutable mode record is not touched or extended by this contract.
   The row is also the §2.6 fence anchor.
4. **Phase timing.** v1 ships the §2–§6 schemas and the D14 database
   boundary, and the wizard collects no sensitive category in v1
   (contract 3 §3), so v1 contains no sensitive write surface. §3
   binds every sensitive write path from the moment one exists — the
   first profile surface that accepts a sensitive category (P2/P3)
   activates routing, consistent with contract 6 §5's v1 slice (D14
   database boundary only, custody mechanics outside v1).
5. **Conversion precondition.** An operator electing `unsplit` accepts
   conversion-time partitioning: `mode-conversion.md` §4.2 requires
   the per-user partition to exist before the Enterprise flip, so
   conversion from an unsplit install performs the partitioning first.
6. In Enterprise, the split is mandatory (D3 table); no-leakage between
   users is enforced by §3 placement plus §5 default-deny — there is
   no cross-user read path to sensitive content through the database,
   because the database has no content to serve.

## 7. Verification requirements

Binding on the implementing PRs. Every witness below MUST name, in its
implementation, the exact tables, columns, commands, and source roots
it scans; "the custody tables" means `profile_category_registry`,
`profile_pointers`, `profile_consents`, `custody_config`,
`custody_connector_registry`, and `custody_feature_registry`; "the
declared profile table" means `profile_answers` (§3.5).

1. **Column-allowlist witness** (hierarchy contract §6.2 style): the
   custody tables' live column sets are exactly
   §2.1/§4.1/§5.1/§5.2/§6.3 and `profile_answers` is exactly §3.5;
   no platform table outside `profile_answers` carries profile
   answer content; every `audit_event_id` FK (§3.5, §4.1, §5.1,
   §6.3) references the same physical table that the contract-1
   (hierarchy §5.2) audit write path inserts into (§1.8 binding);
   and every FK column's declared type equals the referenced
   column's live declared type — in particular each `user_id` is
   `text` matching `users.id` (§3.6 binding) and `agent_id` is
   `uuid` matching `agents.id` (§5.2 binding).
2. **Column-type allowlist witness:** the custody tables and
   `profile_answers` use only the column types named in
   §2.1/§3.5/§4.1/§5.1/§5.2/§6.3 (uuid, text, integer, bigint,
   timestamptz, boolean) — no bytea, json/jsonb, array, vector, or tsvector
   column exists in them, closing the encoded/derived-representation
   routes by type rather than by probe alone.
3. **Closed write-route witness** (hierarchy contract §6.3, full
   prong set): a static, re-export-aware inventory over `apps/`,
   `packages/`, and `plugins/` (production code, tests excluded)
   enumerates every module that writes the custody tables,
   `profile_answers`, or profile answers generally, detecting access
   through each of hierarchy §6.3's prongs — schema-symbol imports,
   SQL string literals naming the tables, raw-execution primitives,
   and runtime code construction — with the database client reachable
   only through a closed importer allowlist. Every enumerated write
   route implements §3.3 registry routing inside the §2.6 fence, and
   every enumerated consent-mutation route asserts the §5.4 subject
   predicate; a route outside the enumeration, or a client import
   outside the allowlist, fails the assertion. **Write-set closure
   for sensitive paths:** for every enumerated route that performs a
   sensitive write, the same static inventory (same prong set)
   enumerates every database table that route's code path can write;
   the set MUST equal exactly {`profile_pointers`, the §1.8 audit
   event table} — the brain write goes through the declared
   brain-write surface, never the database. Any additional
   platform-table write statically reachable from a sensitive-write
   path — an embedding insert, a cache row, any derived
   representation in any generic table — fails the witness. Control:
   a mutated route that adds one insert into an arbitrary existing
   platform table is detected. This closes the §3.1 escape in which
   a conforming-looking route also writes derived sensitive data
   elsewhere: §7.5 probes for planted content; this closure bounds
   what a sensitive-write route can write at all.
4. **Closed brain-read witness** (same style): the inventory enumerates
   every production route that reads user-brain content on behalf of a
   grantee, and every enumerated route calls the §5.6 consent
   evaluation; a brain-read route outside the enumeration fails.
5. **Routing witness:** a sensitive-category answer submitted through
   the profile surface results in a brain write plus a pointer row and
   zero content bytes in the database; a non-sensitive answer lands in
   its declared table. The no-content probe is a negative control: the
   witness first plants the fixture text in a scratch column of a
   throwaway table to prove the probe detects it, then asserts its
   absence — as plaintext, base64, hex, and JSON-string encodings —
   across every column of every table in the platform database: §3.1
   bans sensitive content in every column of every table, so the
   probe scope is the entire schema, not only the custody tables.
6. **Registry witnesses:** (a) the version-1 registry state is exactly
   the seven §2.2 rows with their classifications; (b) a
   `professional-background` answer routes relationally (or per the
   ruling's alternative); (c) an unknown `category_key` is refused
   with nothing stored (§2.3); (d) with the registry unreadable, the
   write is refused (§2.5); (e) a runtime insert/update/delete against
   `profile_category_registry` outside a migration is refused (§2.1);
   (f) a reclassification migration (non-sensitive → sensitive) on
   seeded data moves the values to brains, creates pointers, and
   leaves zero relational values (§2.4); (g) fence race: a profile
   write transaction opened before a reclassification migration takes
   its exclusive lock either commits before the lock is granted (its
   value is swept) or blocks and, on commit, routes by the new
   version — the witness drives both interleavings, INCLUDING the
   interleaving in which the writer holds `FOR SHARE` when the
   migration requests `FOR UPDATE`, the migration waits, the writer
   commits its relational row, and the migration's scan (running
   after lock grant, at `READ COMMITTED`, with the lock as its first
   statement per §2.6) is asserted to sweep that row — and asserts no
   relational value for the reclassified category exists after any
   interleaving completes (§2.6); the witness also asserts the
   migration implementation takes its exclusive lock as the first
   statement of its transaction (a migration variant that reads
   before locking fails review of this witness's static check);
   (h) with `custody_config.registry_version` set to a value the
   release's declared compile-time constant (§2.7, pinned by symbol)
   does not equal, profile writes and consent decisions are refused,
   and the witness asserts the version comparison reads the row under
   the §2.7 shared lock in the same transaction as the refused
   decision (§2.7); (i) sensitive → non-sensitive: after the
   version bump, existing brain content and pointers remain intact
   and readable, nothing is materialized into the database, and the
   next write for that category lands in `profile_answers` (§2.4).
7. **Server-side classification witness:** a client-supplied
   classification or routing override on a profile write is ignored;
   the registry decision is applied (§3.3).
8. **Pointer-constraint witnesses:** inserting a second pointer for the
   same (user, category, brain_ref) violates the §4.1 unique
   constraint; a `brain_ref` failing the §4.3 grammar (leading `/`,
   `..` segment, empty segment) is rejected by the CHECK; resolution
   of a valid `brain_ref` under user A's row never reads user B's
   brain (owner binding, §4.3); with a symlink committed at a
   locator's path or as an intermediate segment, resolution is
   refused and no content outside the owner's brain is read (§4.3
   tree-object rule).
9. **Hash witnesses:** `content_hash` verifies via the keyed §4.4
   construction; the database value alone, without the external key,
   does not equal any unkeyed digest of the fixture content (oracle
   control); the same fixture content stored for two users, and for
   two categories of one user, yields different stored hashes
   (domain-separation control — no cross-row equality oracle); after
   a key rotation, a pointer written under the old key id still
   verifies and a new write stores the new key id; a mismatch refuses
   the read and stamps `mismatch_at`, subsequent reads stay refused,
   reconciliation of a persistently mismatched pointer retains it
   flagged without deleting brain content or writing content to the
   database, and the subject's re-write clears the flag (§4.4, §4.6).
10. **Orphan and deletion-protocol witnesses:** starting from a
    PRE-EXISTING orphan (content already absent, pointer present),
    the next triggered reconciliation deletes the pointer and writes
    no content anywhere in the database (§4.5). The §4.7 protocol is
    fault-injected at both interruption points: killed before the
    brain commit, content presence, content bytes, and every
    database row are unchanged — the deleter's post-acquisition
    fence-advance may already have minted an epoch and recorded an
    acquisition token, the only brain change the protocol permits
    before step 1; killed between the brain
    commit and the pointer delete, the dangling pointer is deleted by
    the next reconciliation — and at no point is content written to
    the database or restored to the brain. **Concurrent-rewrite
    interleavings (two):** deletion step 1 commits and the deleting
    session is fault-injected dead before step 2 — the §4.7 lock
    releases with the session, opening the crash window; a
    concurrent §3.7 write then recreates the same `brain_ref` and
    completes its pointer upsert; the stale step-2 compare-and-delete
    is then replayed with the recorded token. The witness drives
    this once with CHANGED
    content (new `content_hash`, incremented `generation`) and once
    with BYTE-IDENTICAL content (unchanged `content_hash`,
    incremented `generation` — the ABA case a hash token would
    miss), and in both asserts the DELETE matches zero rows, the
    fresh pointer survives, its content resolves and verifies, and
    no unpointed content and no dangling pointer exist afterward.
    **Delete/reinsert reincarnation (third interleaving):** in the
    same crash window, §4.5 reconciliation first removes the
    now-orphan pointer row; an ordinary §3.7 write then recreates
    the locator as a FRESH row — new `id`, `generation` back at 1
    and therefore EQUAL to the recorded generation value; the
    replayed stale step 2 is asserted to match ZERO rows (the
    recorded `id` names the dead incarnation), and the fresh pointer
    survives with its content resolving and verifying — the case a
    bare generation token fails. **Reverse writer/deleter
    interleaving:** a §3.7 writer holds the locator lock and has
    committed its step-1 brain content but not its pointer upsert
    when a managed deletion for the same locator starts. The witness
    asserts the deletion BLOCKS at §4.7 lock acquisition until the
    writer's pointer upsert and release, and that afterward no
    acknowledged write's content is absent and no pointer names
    absent content; a lock-removed control schedules the deleter's
    observation and content removal inside the writer's
    step-1-to-step-2 gap and asserts the witness DETECTS the
    resulting acknowledged-write loss, establishing that the lock is
    what excludes it. **Lock-held contention control (one-cycle
    bound):** starting from a PRE-EXISTING orphan, a §3.7 writer
    session holds the orphan's locator lock while the triggered
    reconciliation's sweep pass runs. The witness asserts the pass
    completes without blocking and without repairing the held
    locator (the try-acquire skip), that the cycle does NOT report
    completion while the lock is held, and that after the holder
    releases, the queued blocking retry deletes the orphan under
    the recorded (`id`, `generation`) token BEFORE the cycle
    reports completion — the orphan is gone by cycle completion,
    proving the §4.5 bound under contention. A repeated-contention
    variant hands the lock to a second holder the moment the first
    releases and asserts the queued locator is retried until
    acquired — the orphan is still deleted by cycle completion once
    the last holder releases, never dropped after one failed
    attempt. A **hold-deadline control** starts from a pre-existing
    orphan, has a live holder acquire the orphan's locator lock and
    stall past the declared §3.7 hold deadline WITHOUT ending its
    session (idle transaction kept open), and asserts the holder's
    session is terminated at the deadline, the lock releases
    without any voluntary action by the holder, and the queued
    blocking retry then deletes the orphan under the recorded
    token before the cycle reports completion — proving termination
    does not depend on a cooperative holder. A **grant-order
    control** blocks the reserved repair behind a holder, keeps
    a stream of later contenders requesting the same locator lock
    while the reservation waits, and asserts the reservation is
    granted before every later contender and the cycle completes
    within its computed maximum `M` — proving later arrivals cannot
    starve the queued repair, and exercising the computed
    cycle maximum as a checked bound rather than prose. A
    **reservation control** has the sweep's try-acquire fail
    against a holder and asserts the blocking lock request is
    enqueued AT the try-acquire failure (the §4.5 reservation
    point), not at end of pass — then starts contenders
    immediately after the failed try-acquire and asserts every one
    is granted after the reservation, and that the cycle's `M` was
    fixed when the last reservation was placed and is not enlarged
    while the late contenders queue. Two **external-step deadline
    controls** exercise termination at the real external boundary,
    not an idle transaction. First, the stalled managed deletion:
    a §4.7 deleter acquires the locator lock and starts its step-1
    brain deletion, which is suspended mid-operation — the session
    neither idle in a transaction nor executing a database
    statement; the control asserts the independent §3.7 supervisor
    terminates the session at the deadline with no cooperation
    from the holder and the lock releases; a fresh §3.7 writer
    then acquires the lock, commits new content at the locator
    with its pointer upsert, and is acknowledged; the suspended
    stale deletion is then RESUMED and asserted REFUSED by the
    brain fence — the fresh content is still present, its pointer
    resolves and verifies, and no mismatch is stamped. Second, the
    stalled ordinary writer: a §3.7 writer stalls inside its
    step-1 brain commit, is terminated at the deadline, a fresh
    writer completes a full acknowledged write at the same
    locator, and the stale commit is resumed and asserted refused
    — the locator serves the fresh writer's bytes and the fresh
    pointer's hash verifies. Two **ABA fence controls** exercise
    value recurrence, the cases a state-equality fence misses.
    Byte-identical stale deletion: a §4.7 deleter records its
    fence, is suspended inside its step-1 brain deletion and
    terminated by the supervisor; a fresh §3.7 writer then
    recommits BYTE-IDENTICAL content (unchanged §4.4 hash) with
    its pointer upsert and is acknowledged; the stale deletion is
    resumed and asserted REFUSED — the identical bytes carry a
    fresh epoch — with the content still present, resolving, and
    verifying, and no mismatch stamped. Absence resurrection: an
    ordinary writer records the locator's ABSENCE as its fence, is
    suspended inside step 1 and terminated; a fresh writer
    completes an acknowledged write at the locator; a managed
    deletion then removes that content and its pointer, returning
    the locator to absence; the stale writer is resumed and
    asserted REFUSED — the post-deletion absence is a fresh
    tombstone epoch, not a recurrence of the recorded one — with
    the locator still absent and NO unpointed content existing
    anywhere afterward. A **handoff-invalidation control**: a
    holder is terminated mid-external-operation; the next holder
    acquires the lock and performs its §3.7 fence-advance but has
    NOT yet applied any brain mutation when the stale operation is
    resumed — the stale operation is asserted refused (the advance
    alone invalidated it, before any fresh mutation), and the new
    holder's own subsequent fenced mutation is asserted to
    succeed. A repair-observation variant runs the same schedule
    with reconciliation as the new holder: repair advances,
    observes content absence, and the stale writer resuming before
    repair's DELETE is asserted refused — the DELETE then
    completes under its recorded (`id`, `generation`) token and
    NO unpointed content is resurrected at the locator. A
    **stale-advance refusal control** exercises the §3.7 token
    guard on the advance itself: writer W1 acquires the locator
    lock and its mandatory fence-advance is suspended BEFORE its
    atomic application; the independent supervisor terminates W1's
    session at the deadline and the lock releases; the next holder
    acquires the lock (receiving a greater acquisition token) and
    completes its own fence-advance; W1's suspended advance is
    then RESUMED and asserted REFUSED by the token comparison —
    it mints no epoch and returns no usable fence — and W1's
    protocol run is asserted to abort with the error surfaced,
    performing no further brain observation or mutation, while
    the next holder's fence remains valid and its subsequent
    fenced mutation is asserted to succeed. A fresh-writer
    variant runs the next holder as an ordinary §3.7 writer
    through a full acknowledged write; a repair-observation
    variant runs it as §4.5 reconciliation — repair advances,
    observes content absence, W1's late advance resumed before
    the DELETE is asserted refused and fenceless, and the DELETE
    completes under its recorded (`id`, `generation`) token with
    no unpointed content resurrected. A **virgin-locator token
    control** exercises the §3.7 absent-token base case: the FIRST
    lock grant ever issued for a locator (no recorded acquisition
    token exists in the brain store) performs its mandatory
    fence-advance and is asserted ACCEPTED — the advance mints the
    locator's epoch and its acceptance atomically creates the
    token record — and the holder's subsequent fenced mutation
    succeeds; a delayed-first-grant race variant suspends that
    first holder's advance before application, terminates the
    session at the deadline, lets a second holder acquire the
    lock (receiving a greater token) and complete its own advance,
    then resumes the first holder's advance and asserts it REFUSED
    by the token comparison — the base case never admits a
    superseded first grant. A **pre-advance
    linearization control** exercises the §3.7 legal-outcome
    boundary: a terminated holder's stale content mutation is
    scheduled to linearize immediately BEFORE the next holder's
    fence-advance swap; the control asserts the store serializes
    the stale mutation first, the advance preserves the
    post-mutation content presence and bytes and mints its epoch
    after it, the new holder's first relied-on observation
    reflects the committed stale effect, and the end state is
    consistent — equivalent to the stale operation having
    completed before the previous holder's lock release. The
    control runs BOTH database-half branches: with the stale
    mutation a managed deletion's step-1 brain half, its stale
    step-2 DELETE remains conditioned on the recorded
    (`id`, `generation`) pair and is asserted to match ZERO rows
    against a later row incarnation; with the stale mutation an
    ordinary §3.7 writer's step-1 content commit, the terminated
    run is asserted to perform NO pointer upsert — the unpointed
    content is repaired by §4.5 within one cycle, and a retry of
    the write is asserted to run as a fresh lock acquisition
    under a new token. An **exceedance control** contrives a
    cycle that cannot finish within its computed `M` (an injected
    sequence of deadline-length holders on a reserved locator) and
    asserts the cycle reports FAILURE — never completion —
    surfaces the error, withdraws its pending reservations, and
    persists the carry-over set; the next triggered cycle is
    asserted to take the carry-over locators into scope first and
    delete the orphan before reporting completion. A
    static control asserts the deletion implementation conditions on
    BOTH the recorded row `id` and `generation` — not on
    `content_hash` and not on `generation` alone — a second
    static control asserts the managed-deletion and
    reconciliation-repair implementations each acquire the §3.7
    locator lock before their observation reads (§4.5, §4.7), and a
    third static control asserts the reconciliation implementation
    reserves a failed try-acquire's queue place at skip time for a
    within-cycle blocking acquire — an implementation that defers
    a skipped locator to a later cycle other than through the §4.5
    exceedance transition (which fails the cycle and persists the
    carry-over set), or that reports cycle completion with a
    reserved locator unprocessed, FAILS. A fourth static control asserts every
    lock-holding path (ordinary write §3.7, managed deletion §4.7,
    reconciliation repair §4.5) is configured with the declared
    §3.7 hold deadline — an implementation with any unbounded
    lock-holding path FAILS. A fifth static control asserts the
    §3.7 grant-order binding: the implementation either cites the
    pinned advisory-lock mechanism's documented queue-order grant
    guarantee or routes conflicting locator-lock acquisitions
    through an application-level FIFO queue — an implementation
    relying on an undocumented grant order FAILS. A sixth static
    control asserts the managed-deletion brain step applies its
    mutation conditionally on the recorded §3.7 fence epoch — an
    unfenced brain-deletion path FAILS. A seventh static control
    asserts the §3.7 fence-epoch invariants: every fenced-mutation
    path mints a fresh never-reused per-locator epoch atomically
    with its mutation — byte-identical recommits and transitions
    to absence (the unique tombstone) included — every
    lock-acquiring path that mutates or relies on brain state
    (ordinary write §3.7, managed deletion §4.7, reconciliation
    repair §4.5) performs the fence-advance immediately after
    acquisition and records the post-advance epoch as its fence
    value, and no path represents the fence by content bytes,
    content hash, or bare path absence — an implementation whose
    fence value can recur, or that observes before advancing,
    FAILS. The same control asserts the §3.7 acquisition-token
    invariants: the lock authority mints a fresh, strictly
    increasing per-locator token at every lock grant; every
    brain-mutation path — the fence-advance included — presents
    its holder's token unchanged across every swap-loop retry;
    the brain store's token comparison and recording are atomic
    with the mutation they guard; the advance is refused when its
    token is less than the recorded value; and a refused advance
    yields no usable fence — an implementation whose advance
    applies unconditionally, that re-mints or refreshes a token
    within an acquisition, or whose holder proceeds to any brain
    observation or mutation after a refused advance, FAILS.
11. **Default-deny and granularity witnesses:** an agent grantee with
    no active row is refused; with a `granted` row for category A
    only, category B is refused; with agent X granted, agent Y of the
    same type is refused for the same (user, category); `connector`
    and `feature` are exercised via their FK refusal paths, since
    both registries are empty at version 1 (§5.2); a consent insert
    naming a nonexistent agent id violates the `agent_id` FK, and the
    witness pins that FK's referenced table by symbol identity to the
    `agents` table exported by the shared schema package (§5.2
    binding); a platform admin requesting
    another user's sensitive content through any production read
    surface is refused — admins are not a grantee type and
    default-deny applies (the access-time complement of §7.12's
    write-time refusal); a hierarchy owner or manager grant over the
    subject confers no consent access (§5.7); with the consent state
    unreadable, evaluation refuses (§5.6).
12. **Mutation-authority witnesses:** a platform admin attempting to
    create a grant on another user's data is refused at write time; a
    hierarchy owner or manager likewise; the data subject succeeds;
    `actor` equals the subject's principal on every mutation row —
    there is no non-subject mutation path (§5.4). **Cessation
    cascade:** deleting an agent's `agents` row removes that agent's
    consent rows in the same transaction via the FK cascade, the next
    consent evaluation for that grantee refuses (no row), the linked
    audit events survive, and no other grantee's rows are touched; a
    connector-registry amendment migration removing a key cascades
    identically (§5.4). **Cessation events:** the same deletions
    each produce one consent-cessation event per removed row in the
    §1.8 audit event table, carrying the §5.4 fields (cause with
    parent table and key, actor — the surface-set actor when one
    initiated the deletion, `system:cascade` otherwise — the grant
    snapshot including the deleted consent row's `id` and `user_id`,
    and time); a **two-subject discrimination witness** has two
    distinct users each hold a `granted` row to ONE agent grantee
    for the SAME category, deletes that grantee's `agents` row, and
    asserts exactly two cessation events exist and that each event's
    snapshot carries a distinct consent-row `id` and the correct
    `user_id` — every event names exactly which subject's grant of
    which row ended; an atomicity control rolls the deleting
    transaction back after the parent DELETE and asserts no
    cessation event persists (no orphan event without a deletion,
    no deletion without its events); the events survive subsequent
    operation; and a subject-cessation (`users` row) deletion
    produces its events identically (§5.4).
13. **Revocation/re-grant witnesses:** after revocation commits, the
    next evaluation refuses and the revoked row persists unmutated; a
    full grant → revoke → re-grant cycle yields two rows (one
    revoked, one active) and access follows only the active row; a
    second concurrent grant attempt for the same key violates the
    §5.1 partial unique index; a direct SQL UPDATE flipping a revoked
    row back to `granted`, an UPDATE altering any other column of a
    revoked row, and a standalone DELETE of a row whose subject and
    grantee both still exist are each rejected by the §5.5 trigger;
    the complement controls pass: deleting the subject's `users` row
    cascades the subject's consent rows, and deleting the grantee's
    referent row cascades that grantee's rows — both permitted by the
    trigger's absent-referent prong (§5.5).
14. **Self-access witness:** the data subject reads their own content
    without consent rows (§5.6).
15. **Mode witnesses:** the custody-table schemas are byte-identical
    under Standalone and Enterprise migrations (§6.1); a fresh
    Standalone bootstrap records `standalone_layout = 'split'` by
    default, and an explicit opt-out records `unsplit` with actor and
    audit linkage (§6.3); conversion from an `unsplit` install refuses
    the mode flip until partitioning has produced the per-user region
    (§6.5, with contract 6 §4.2); in an Enterprise fixture with two
    users, user A's grantee with a grant on user A cannot reach any of
    user B's content (§6.6); inserting a second `custody_config` row
    violates the §6.3 singleton constraint.
16. **Ordered-write protocol witnesses:** the §3.7 protocol is
    fault-injected at its interruption points: killed before the
    brain commit, content presence, content bytes, and every
    database row are unchanged — the post-acquisition
    fence-advance's epoch mint and token recording are the only
    permitted brain change — and the caller receives
    no acknowledgment; killed between the brain commit and the
    pointer upsert, the brain holds unpointed content, no pointer row
    exists, no read surface serves the content, and the caller
    received no acknowledgment — then the retried write recommits the
    same deterministic locator, completes the pointer upsert, and the
    witness asserts the converged state (one pointer, verifying hash,
    content served to the subject); killed after the pointer commit
    but before the acknowledgment reaches the caller, the retry
    executes under the locator lock, observes the converged state,
    returns success, and the witness asserts exactly one pointer with
    a verifying hash and an unchanged answer (idempotent completion,
    no duplicate content commit that changes the served bytes).
    **Same-locator interleaving:** two concurrent writers to one
    (user, category, slot) run to completion; the witness asserts
    both are serialized by the §3.7 advisory lock (their step-1/step-2
    spans do not interleave), the final pointer's hash verifies
    against the blob committed at the locator, and no `mismatch_at`
    is stamped by a subsequent read; a control with the lock removed
    schedules the divergent order (A's pointer upsert after B's
    content commit) and asserts the witness DETECTS the divergence —
    proving the lock is what excludes it. **Stale retry:** writer A
    is interrupted before acknowledgment; writer B then completes a
    full write to the same locator; A's retry executes under the
    lock and the witness asserts the end state is consistent (the
    pointer's hash verifies against the current blob — last
    completed write wins, no permanent mismatch). **Locator
    injectivity:** the declared scheme maps every distinct
    (category_key, slot) pair in the registry to a distinct
    `brain_ref` (exhaustive at version 1: seven categories x the
    `answer` slot), and a deliberately colliding scheme control
    fails the witness. A static control asserts the implementation
    orders content before pointer: no enumerated sensitive-write
    route creates a pointer row before its brain commit has been
    confirmed (§3.7). A second static control asserts every
    enumerated sensitive-write route applies its step-1 brain
    commit as a fenced mutation conditioned on the recorded §3.7
    fence epoch — recorded at the route's post-acquisition
    fence-advance, the commit itself minting a fresh epoch and
    carrying the route's §3.7 acquisition token — an
    unfenced or token-free brain-commit path FAILS.

## 8. Drafting additions (PRD §12.1 disclosure)

The following are proposed drafting additions, visible here for
ratification; none is claimed as a PRD mandate, and each is severable:

1. The `feature` grantee type, with a contract-owned registry table
   that is empty at version 1 (§5.2).
2. Append-only consent history: re-grants insert new rows; revoked
   rows are retained unmutated, enforced by trigger (§5.5).
3. Cessation by cascade: when a grantee or subject referent row is
   deleted, consent rows are removed by `ON DELETE CASCADE` in the
   same transaction; access ends through default deny, and durable
   history is carried by the §1.8 audit event table. The trigger's
   DELETE prong permits a child-row DELETE only when the referenced
   subject or grantee row is absent (§5.4, §5.5). Each cascaded
   deletion additionally emits a consent-cessation audit event —
   written by an AFTER DELETE trigger in the same transaction as the
   referent deletion, carrying cause, actor (surface-set or
   `system:cascade`), the grant snapshot — which, per revision 6,
   includes the deleted consent row's `id` and its `user_id`, so
   events from different subjects' grants to one ceasing grantee
   remain distinguishable — and time — so the audit
   trail records when and why each active grant ended (§5.4,
   §7.12). This replaces the revision-2 system-actor auto-revocation
   predicate, which is withdrawn.
4. The `custody_config` election record for the Standalone layout,
   singleton by constraint (§6.3).
5. The domain-separated keyed `content_hash` construction, its key
   rotation rule, and the `mismatch_at` pointer state (§4.4, §4.6).
6. The bounded dangling-pointer reconciliation policy and the ordered
   managed-deletion protocol (§4.5, §4.7).
7. The column-type allowlist verification requirement (§7.2).
8. The amendment-only, versioned registry classifier itself, its
   unknown-key refusal, and both reclassification transition policies
   (§2.1, §2.3, §2.4).
9. The custody-config write fence and the declared supported-version
   refusal, including the revision-4 pinning: both fence transactions
   run at `READ COMMITTED` with the migration's `SELECT ... FOR
UPDATE` as the first statement of its transaction, and the
   supported version is an exported compile-time constant in the
   custody module of the shared schema package, read under the same
   lock as the decision it gates (§2.6, §2.7).
10. Subject-only consent mutation authority (§5.4, the single
    subject predicate).
11. The declared non-sensitive profile table `profile_answers` and
    its closed schema (§3.5).
12. The typed grantee referent columns, the
    `custody_connector_registry` and `custody_feature_registry`
    tables, the audit-table binding by identity (§5.1, §5.2, §1.8),
    and the agent-grantee binding by symbol identity to the `agents`
    table exported by the shared schema package (§5.2, §7.11).
13. Shipping all §2–§6 schemas in v1 ahead of any sensitive write
    surface (§6.4).
14. The committed-tree-only resolution rule: content reads resolve
    only through the committed tree of the brain repository, and
    symlink or non-tree/non-blob resolution is refused (§4.3, §7.8).
15. The text user-key binding: every `user_id` column is `text`
    matching the live `users.id` declaration, values are opaque
    strings not guaranteed to be UUIDs, and any sibling user-key
    migration triggers amendment of this contract (§3.6, §7.1).
16. The ordered sensitive-write protocol for ordinary writes:
    content-first at a deterministic locator, pointer upsert second
    in its own transaction, acknowledgement only after the pointer
    commit, idempotent retry convergence, and the
    pointer-before-content prohibition — with the revision-5
    locator scheme (injective over (category, slot), the slot set
    fixed to `answer` at version 1, stable across releases) and the
    per-locator advisory lock spanning both steps — held, per
    revision 6, by EVERY blob/pointer mutator for the locator:
    ordinary writers, managed deletion, and reconciliation repair
    (try-acquire with the revision-7 within-cycle blocking retry,
    so a failed try-acquire defers repair to the end of the cycle,
    never past it; per revision 8 every hold runs under a declared
    finite hold deadline with arrival-order grants, so the deferred
    repair — and with it cycle completion — is wall-clock bounded,
    not merely eventual; per revision 9 the deadline is enforced
    by a supervisor independent of the holder, the deferred repair
    reserves its queue place at the moment its try-acquire fails,
    the cycle maximum is a computed formula over
    reservation-point snapshots that is fixed when the last
    reservation is placed, exceedance is an explicit failed-cycle
    transition with a durable carry-over set the next cycle
    processes first — so the one-cycle orphan bound binds every
    cycle that reports completion — and every brain mutation under
    a locator lock is fenced by a conditional application; per
    revision 10 the fence value is an ABA-resistant per-locator
    fence EPOCH — minted fresh by every mutation, byte-identical
    recommits and transitions to absence (unique tombstone)
    included, and advanced by every new holder immediately after
    acquisition, before it relies on brain state — so a stale
    tail is refused even when bytes or absence recur and even
    before the new holder's first mutation; per revision 11 the
    fence-advance itself is guarded by an acquisition token —
    minted fresh and strictly increasing by the lock authority at
    every lock grant, carried unchanged across an acquisition's
    brain operations and swap-loop retries, compared and recorded
    by the brain store atomically with every brain mutation, the
    advance included — so a superseded acquisition's delayed
    advance is refused and mints nothing, a refused advance
    yields no usable fence with a mandatory
    abort-release-surface transition, and a stale operation
    either serializes wholly before the new holder's advance —
    the defined legal pre-release-completion-equivalent outcome —
    or is refused; per revision 12 the token comparison is exact
    at both ends — strictly less-than refuses, an absent recorded
    token compares lower than every minted token so a virgin
    locator's first advance is accepted and creates the record —
    and the pre-advance-linearization outcome distinguishes the
    stale database halves: pair-conditioned step 2 for deletion
    and repair, no pointer upsert ever for a terminated ordinary
    writer), so live interleavings
    between any two mutators are excluded by one serialization
    protocol, and stale external tails are excluded by the fence
    (§3.7, §4.5, §4.7, §7.16).
17. The compare-and-delete managed-deletion step: deletion step 2 is
    conditioned on the PAIR of the target row's uuid `id` (the
    immutable row-incarnation identity) and its monotonic
    `generation` counter, both recorded in step 1 under the locator
    lock (revision 6; the revision-4 `content_hash` token was
    withdrawn as ABA-prone under identical-content rewrites, and the
    revision-5 bare `generation` token is withdrawn as ABA-prone
    under delete/reinsert reincarnation, where the counter restarts
    at 1), so a stale deletion surviving a crash window is never
    able to unpoint a fresh row — changed, byte-identical, or
    reincarnated (§4.1, §4.7, §7.10).
18. The sensitive-path write-set closure: a static inventory of every
    table a sensitive-write route can write, required to equal
    exactly the pointer table plus the §1.8 audit event table
    (§7.3).

The revision-1 "reporting" rationale for relational
background/education storage is withdrawn; the traced rationale is
agent configuration (PRD D4).

## Ruling request

Ruling requested (one decision): classify `professional-background`
and `education` as **non-sensitive** in the version-1 registry (stored
relationally, used for agent configuration) — or, as the alternative,
classify both **sensitive** (user-brain custody with pointers),
accepting that agent-configuration reads then go through pointer
indirection and consent evaluation?
