From 8079d8dc9166fd5faf7212995e30cb196ecbece8 Mon Sep 17 00:00:00 2001 From: fred Date: Wed, 26 Aug 2026 19:39:04 -0500 Subject: [PATCH] =?UTF-8?q?docs(custody):=20revision=202=20=E2=80=94=20cla?= =?UTF-8?q?ssifier=20registry,=20grantee=20model,=20exact=20schemas,=20wit?= =?UTF-8?q?ness=20closure=20(sol=20r1=20F1-F6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/requirements/custody-schema.md | 458 +++++++++++++++++++++------- 1 file changed, 348 insertions(+), 110 deletions(-) diff --git a/docs/requirements/custody-schema.md b/docs/requirements/custody-schema.md index a36d446b..d4c818d8 100644 --- a/docs/requirements/custody-schema.md +++ b/docs/requirements/custody-schema.md @@ -11,161 +11,399 @@ 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. -This contract binds the sensitive-category registry (§2), the custody +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). + +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), and witnesses (§7). It defines -schemas and placement; wizard step flow stays with contract 3, mode and -conversion with contract 6 (`mode-conversion.md`), identity with +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 (in Standalone, - the user-files region of the single mosaic-brain; in Enterprise, the - user's own brain repository). +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 - sensitive category. + category classified `sensitive`. 3. **Pointer**: a database record referencing sensitive content that lives in a user brain, carrying no content (§4). -4. **Consent record**: a database record granting a named grantee scope - access to a category of a user's data (§5). +4. **Grantee**: a non-subject principal that may be granted access to a + user's sensitive content: an enrolled agent, 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). -## 2. Sensitive-category registry +## 2. Profile-category registry -1. The registry is a closed, versioned list in the platform database; - entries are added or reclassified by amendment to this contract, - never ad hoc. -2. Initial registry, drawn from the D4 profile step and D14's - "disabilities, family, communication style, and similar": +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: - | Category key | Covers | - | --------------------- | ------------------------------------------------------------------- | - | `disabilities` | disabilities including ADHD/autism/PDA/vision | - | `family-social` | family, pets, friends | - | `communication-style` | desired agent communication style, voice-matching interview product | - | `personal-interests` | hobbies, likes/dislikes | - | `connector-content` | email and drive content reached through user connectors | + | 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 | -3. **Fail-closed classification.** A profile category not in the - registry is treated as sensitive until an amendment classifies it. - Non-sensitive by classification (not by default) are structural - fields the platform needs relationally: e.g. professional - background/education summaries used for agent configuration MAY be - classified non-sensitive by the ruling below; account identity - fields (email, name, credentials) are identity-contract data, not - profile custody data. + 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. ## 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 embeddings or - other derived representations that reconstruct content. + 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, and the category registry of §2. - Nothing else. -3. Every write path for profile answers routes by category: sensitive - → brain write + pointer upsert; non-sensitive → its declared - platform table. The routing decision is made server-side from the - registry, never by the client. + 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` → its + declared platform table; unknown → refusal (§2.3). The routing + decision is made server-side from the registry at its current + version; 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. ## 4. Pointer schema -A pointer row carries exactly: +1. **Exact columns.** Table `profile_pointers`, columns exactly: + + | Column | Type | Constraints | + | ---------------- | ----------- | ----------------------------------------------------------- | + | `id` | uuid | primary key | + | `user_id` | uuid | NOT NULL, FK → users(id) ON DELETE CASCADE | + | `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) | + | `created_at` | timestamptz | NOT NULL | + | `updated_at` | timestamptz | NOT NULL | + | `audit_event_id` | uuid | NOT NULL, FK → the platform audit log table (audit linkage) | + + plus the database constraint UNIQUE (`user_id`, `category_key`, + `brain_ref`) — the one-pointer rule is a constraint, not a + convention. -1. `id`, `user_id` (the owning user), `category_key` (§2 registry FK), - `brain_ref` (an opaque locator — repository-relative path or key in - the owning user's brain), `content_hash` (integrity check of the - referenced content), `created_at`/`updated_at`, and audit linkage. 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 (category + path - discipline), not descriptive. -3. One pointer per (user, category, brain_ref); pointers are deleted - when their content is deleted (dangling pointers are repaired - toward deletion, never toward re-creating content in the DB). + 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. +4. **`content_hash` construction.** `content_hash` is + `hmac-sha256:` — HMAC-SHA-256 over the canonical content + bytes, keyed with a platform integrity key held in the secrets + backend and never stored in the database or any repository. Because + the key is external, the stored value is not an offline dictionary + oracle for low-entropy answers. On read, a hash mismatch refuses + the read and flags the pointer for §4.5 reconciliation. +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 — an orphan survives + at most one cycle, and repair performs no database content write. ## 5. Consent schema and evaluation -1. A consent row carries exactly: `id`, `user_id` (the data subject), - `grantee_scope` (a typed reference: an enrolled agent, a connector, - or a platform feature — closed enum of grantee types), `category_key` - (§2 FK), `state` (`granted` | `revoked`), `granted_at`/`revoked_at`, - `actor` (who recorded the choice), and audit linkage. -2. **Default deny.** Absence of a `granted` consent row for (user, - grantee scope, category) means no access. There are no implicit - grants, no platform-admin bypass, and no mode in which default-deny - is suspended. -3. **Granularity.** Consent is per grantee scope × category (the D4 - "granular agentic-access consent"). A grant to one agent or - connector confers nothing on another. -4. **Revocation.** Revocation is effective for every access evaluated - after the revoking write commits; revoked rows are retained as - history (state flip, not row deletion). -5. **Evaluation placement.** Access to sensitive content is mediated by +1. **Exact columns.** Table `profile_consents`, columns exactly: + + | Column | Type | Constraints | + | ---------------- | ----------- | --------------------------------------------------------------- | + | `id` | uuid | primary key | + | `user_id` | uuid | NOT NULL, FK → users(id) ON DELETE CASCADE (the data subject) | + | `grantee_type` | text | NOT NULL, CHECK in (`agent`, `connector`, `feature`) | + | `grantee_id` | text | NOT NULL (§5.2 per-type referential integrity) | + | `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 platform audit log table | + + plus the partial unique index UNIQUE (`user_id`, `grantee_type`, + `grantee_id`, `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 the pair + (`grantee_type`, `grantee_id`); its canonical display form is + `:` (e.g. `agent:`). The + discriminant set is closed at the three CHECK values. Referential + integrity is per type: `agent` ids reference the enrolled-agent + registry of `identity-lifecycle.md`; `connector` ids reference the + platform connector registry; `feature` ids reference a closed + feature-key list owned by amendments to this contract, and that + list is EMPTY at version 1 (no feature grantee exists until an + amendment names one). A grant to one agent confers nothing on + another agent of the same type; the constraint key includes + `grantee_id`, 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.** Only the authenticated data subject may + create, grant, or revoke consent rows for their own data: the + server enforces that `actor` is the subject's principal and equals + the row's `user_id` before any consent mutation commits. A platform + admin has no consent-mutation capability over another user's rows — + an admin self-grant is refused at write time, closing the + write-side route around §5.3. One system exception (disclosed, + §8): when a grantee ceases to exist (e.g. agent retirement under + `identity-lifecycle.md`), the platform auto-revokes its active + rows, recording a system principal as `actor`. +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. 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). The user reading their own + (`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. -6. Consent records govern agentic/feature access to user data. They are - distinct from hierarchy grants (contract 2) and confer no - platform authorization. +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 §3–§5 schemas are mode-independent: Standalone and Enterprise +1. The §2–§5 schemas are mode-independent: Standalone and Enterprise use the same tables and the same routing rule. -2. In Standalone, D14 makes the physical split a MAY. This contract - keeps it a MAY and binds the recommended default: a fresh v1 - Standalone install routes sensitive content per §3 from the start, - so the Enterprise conversion precondition (`mode-conversion.md` - §4.2, brains) is already satisfied. An operator electing not to - keep the split accepts the resulting conversion-time partitioning - work; the election is recorded. -3. In Enterprise, the split is mandatory (D3 table); no-leakage between +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//` 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), `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 platform audit log 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. +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: +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`, and `custody_config`. -1. **No-content witness:** a column-allowlist assertion (contract 1 - §6.3 style) that the pointer and consent tables' column sets are - exactly §4.1/§5.1, and that no platform table outside the declared - non-sensitive profile tables carries profile answer content. -2. **Routing witness:** a sensitive-category answer submitted through +1. **Column-allowlist witness** (hierarchy contract §6.2 style): the + custody tables' live column sets are exactly §2.1/§4.1/§5.1/§6.3, + and no platform table outside the declared non-sensitive profile + tables carries profile answer content. +2. **Column-type allowlist witness:** the custody tables use only the + column types named in §2.1/§4.1/§5.1/§6.3 (uuid, text, integer, + timestamptz) — 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 style): a + static, re-export-aware inventory over `apps/` and `packages/` + (production code, tests excluded) enumerates every module that + writes the custody tables or writes profile answers, and every + enumerated route implements §3.3 registry routing; a route outside + the enumeration fails the assertion. +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 (asserted by content-hash - presence in the brain and absence of the plaintext in any DB - column); a non-sensitive answer lands in its declared table. -3. **Fail-closed classification witness:** an answer in an unregistered - category routes as sensitive. -4. **Opacity witness:** pointer rows for seeded sensitive fixtures - contain no fixture text in any column. -5. **Default-deny witness:** an agent grantee with no consent row is - refused; with a `granted` row for category A only, access to - category B is refused. -6. **Revocation witness:** after revocation commits, the next access - evaluation refuses; the revoked row persists as history. -7. **Self-access witness:** the data subject reads their own content - without consent rows. -8. **Deletion witness:** deleting sensitive content removes its - pointer; no path re-materializes content into the database. + 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 the custody tables and declared profile + 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). +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). +9. **Hash witnesses:** `content_hash` verifies via the keyed §4.4 + construction; a mismatch refuses the read and flags the pointer; + the database value alone, without the external key, does not equal + any unkeyed digest of the fixture content (oracle control). +10. **Orphan 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); a managed deletion removes its pointer in + the same transaction. +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); each of the + three grantee types is exercised — `feature` via its refusal path, + since the version-1 feature list is empty (§5.2). +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 committed row + (§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 (§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). + +## 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 closed feature-key list that is + empty at version 1 (§5.2). +2. Append-only consent history: re-grants insert new rows; revoked + rows are retained unmutated (§5.5). +3. System-actor auto-revocation when a grantee ceases to exist (§5.4). +4. The `custody_config` election record for the Standalone layout + (§6.3). +5. The keyed `content_hash` construction and its mismatch handling + (§4.4). +6. The bounded dangling-pointer reconciliation policy (§4.5). +7. The column-type allowlist verification requirement (§7.2). + +The revision-1 "reporting" rationale for relational +background/education storage is withdrawn; the traced rationale is +agent configuration (PRD D4). ## Ruling request -Ratify sections 1–7 as written, with one decision embedded: - -- Decision (§2): the initial sensitive-category registry is the five - rows of §2.2, with fail-closed classification for anything - unregistered, and with professional background/education classified - **non-sensitive** (they exist to configure agents and reporting and - are stored relationally). Alternative if rejected: classify - background/education sensitive too — safe, but it moves data the - platform legitimately queries into pointer-indirected storage and - that cost should be chosen deliberately, not defaulted into. +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?