Files
transcription/docs/ver4/schema_v4.md
T

215 lines
7.2 KiB
Markdown

# Database Schema (Version 4)
This document defines the selected schema direction for V4 document-person relationship expansion.
V4 goals are:
- extensible role taxonomy,
- extensible document type taxonomy,
- explicit assertion state (`asserted`, `suggested`),
- policy-driven exclusivity,
- deterministic migration from V3 links.
## Scope
This specification focuses on relationship and document-type governance persistence changes. Existing `Person`, `Source`, `Job`, and `JobSource` core structures remain as in V3 unless explicitly noted.
## New/Expanded Concepts
- **Relationship role:** semantic label such as `author`, `recipient`, `mentioned`.
- **Assertion state:** whether the link is canonical (`asserted`) or pending review (`suggested`).
- **Exclusivity matrix:** configurable role-pair conflicts for same `(document_id, person_id)`.
- **Suggestion provenance:** evidence fields enabling review decisions.
- **Document type registry:** controlled taxonomy for `Document` classification with stable code identity.
## Selected Model: Role Registry + Separate Suggestion Table
This option cleanly separates canonical links from pending suggestions and enables fully data-driven role expansion.
### Tables
#### `person_role`
| Column | Type | Notes |
| --- | --- | --- |
| `id` | UUID PK | Stable key |
| `code` | TEXT UNIQUE | Canonical role code, for example `author`, `recipient`, `mentioned` |
| `label` | TEXT | UI label |
| `is_active` | BOOLEAN | Soft-enable/disable role |
| `created_at` | TIMESTAMPTZ | Audit timestamp |
| `updated_at` | TIMESTAMPTZ | Audit timestamp |
#### `document_person` (asserted links only)
| Column | Type | Notes |
| --- | --- | --- |
| `id` | UUID PK | Stable key |
| `document_id` | UUID FK | -> `document.id` |
| `person_id` | UUID FK | -> `person.id` |
| `role_id` | UUID FK | -> `person_role.id` |
| `created_at` | TIMESTAMPTZ | Audit timestamp |
| `updated_at` | TIMESTAMPTZ | Audit timestamp |
Constraints:
- `UNIQUE(document_id, person_id, role_id)`
#### `document_person_suggestion`
| Column | Type | Notes |
| --- | --- | --- |
| `id` | UUID PK | Stable key |
| `document_id` | UUID FK | -> `document.id` |
| `person_id` | UUID FK | -> `person.id` |
| `role_id` | UUID FK | -> `person_role.id` |
| `status` | TEXT | `pending`, `accepted`, `rejected` |
| `confidence` | FLOAT NULL | Optional score |
| `source_mechanism` | TEXT NULL | For example `rule`, `llm` |
| `evidence_ref` | TEXT NULL | Pointer or excerpt ID |
| `evidence_span` | JSON NULL | Optional text span payload |
| `note` | TEXT NULL | Reviewer note |
| `created_at` | TIMESTAMPTZ | Suggestion creation time |
| `reviewed_at` | TIMESTAMPTZ NULL | Decision time |
| `reviewed_by` | TEXT NULL | Operator identifier |
Constraints:
- `UNIQUE(document_id, person_id, role_id, status)` with policy for multiple pending rows defined in service layer.
- Optional stricter rule: one active pending suggestion per `(document_id, person_id, role_id)`.
#### `role_exclusivity`
| Column | Type | Notes |
| --- | --- | --- |
| `id` | UUID PK | Stable key |
| `left_role_id` | UUID FK | -> `person_role.id` |
| `right_role_id` | UUID FK | -> `person_role.id` |
| `created_at` | TIMESTAMPTZ | Audit timestamp |
Constraints:
- Canonical ordering rule to avoid duplicate pairs (`left_role_id < right_role_id` enforced in service/DB).
- `UNIQUE(left_role_id, right_role_id)`
Initial seed:
- Exclusivity pair: (`author`, `recipient`)
## Deferred Alternative (Not Selected for V4)
An enum-based shared table model was considered but is intentionally not selected for V4 because it couples canonical and provisional states in one table and increases invariant complexity.
## Assertion-State Semantics
- `asserted`: canonical relationship used for document/person metadata and business logic.
- `suggested`: non-canonical proposal requiring explicit review.
- Accept action:
- creates asserted link (or confirms existing),
- marks suggestion `accepted`.
- Reject action:
- marks suggestion `rejected`.
## Exclusivity Enforcement
Policy target:
- For a single `(document_id, person_id)`, disallow coexistence of role pairs configured as exclusive.
Enforcement layers:
1. Service-level pre-check for clear API errors.
2. Database-level guard where feasible (constraints/triggers or deterministic write path).
Initial configured rule:
- `author` and `recipient` are exclusive.
Enforcement semantics:
- Exclusivity is enforced for asserted links.
- Pending suggestions may exist even if they would conflict when asserted.
- Accepting a suggestion must run exclusivity checks and fail deterministically on conflict.
## Migration From V3
### Data Mapping
- Existing V3 `document_person` rows map to V4 `asserted` semantics.
- Existing V3 role values:
- `author` -> role `author`
- `recipient` -> role `recipient`
### Backfill Steps
1. Seed role rows (`author`, `recipient`, `mentioned`) if using Option A.
2. Migrate current links into asserted table/state.
3. Run conflict scan for exclusivity violations.
4. Apply deterministic conflict policy for any violations.
5. Enable hard enforcement after data passes validation.
## Indexing Guidance
Recommended indexes:
- `document_person(document_id)`
- `document_person(person_id)`
- `document_person(role_id)` (Option A) or `document_person(role)` (Option B)
- `document_person_suggestion(document_id, status)` (Option A)
- `document_person_suggestion(person_id, status)` (Option A)
## Selection Rationale
V4 selects role registry plus separate suggestion storage for clearer provenance boundaries, cleaner lifecycle transitions, and long-term extensibility.
## Document Type Registry Model (Selected for V4)
V4 applies the same registry governance pattern to document classification.
### Tables
#### `document_type`
| Column | Type | Notes |
| --- | --- | --- |
| `id` | UUID PK | Stable key |
| `code` | TEXT UNIQUE | Canonical type code, for example `letter`, `diary`, `book`, `postcard` |
| `label` | TEXT | UI display label |
| `is_active` | BOOLEAN | Soft-enable/disable type |
| `sort_order` | INTEGER NULL | Optional UI ordering |
| `created_at` | TIMESTAMPTZ | Audit timestamp |
| `updated_at` | TIMESTAMPTZ | Audit timestamp |
#### `document` update
| Column | Type | Notes |
| --- | --- | --- |
| `document_type_id` | UUID FK NULL | -> `document_type.id` |
### Constraints and Governance
- `document_type.code` must be stable and unique.
- `document_type.label` may change without changing canonical type identity.
- Inactive types remain valid for historical records but are excluded from default create/edit selectors.
### Initial Seeds
- Seed baseline type codes from current V3 usage set (for example `letter`, `diary`, `book`, `postcard`, `record`, `memo`) and refine labels as needed.
### Migration and Normalization (Small Corpus)
1. Seed canonical `document_type` rows.
2. Manually assign each existing document (10 total) to a canonical type via `document_type_id`.
3. Resolve any outlier values directly during this one-time pass.
4. Enforce registry-backed write validation after manual assignment is complete.
### Indexing Guidance (Document Type)
- `document_type(code)` unique index.
- `document(document_type_id)` index.
## Related Local References
- [V4 Scope Boundary](scope_boundary_v4.md)
- [V4 Requirements](requirements_v4.md)
- [V3 Schema](../schema_v3.md)