# Schema v25 ## Versioning - schema version: `26` - database file: `.retriever/retriever.db` - timestamps: UTC ISO 8601 with `Z` - booleans: `0` or `1` This file is a schema orientation guide. The authoritative current DDL and migrations live in `skills/tool-template/src/10_core.py`, `skills/tool-template/src/40_schema_runtime.py`, and the generated `skills/tool-template/tools.py`. ## SQLite pragmas Apply these pragmas on every write connection: ```sql PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 5000; ``` Journal mode policy: - prefer `WAL` on normal local workspaces - if the filesystem or mount rejects `WAL`, fall back to `DELETE` - some mounted or sandboxed paths may allow writes to an existing DB while rejecting journal-mode setup for a freshly created DB - `bootstrap` may remove obviously stale zero-byte SQLite artifacts and retry once before surfacing an error ## Table definitions ### `workspace_meta` Stores installation metadata for the current workspace. ```sql CREATE TABLE IF NOT EXISTS workspace_meta ( id INTEGER PRIMARY KEY CHECK (id = 1), schema_version INTEGER NOT NULL, tool_version TEXT NOT NULL, requirements_version TEXT NOT NULL, template_source TEXT NOT NULL, template_sha256 TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ``` ### `datasets` Named document collections. Source-backed datasets keep their primary source identity here for compatibility and migration, while canonical membership lives in `dataset_documents`. ```sql CREATE TABLE IF NOT EXISTS datasets ( id INTEGER PRIMARY KEY, source_kind TEXT NOT NULL, dataset_locator TEXT NOT NULL, dataset_name TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ``` ### `dataset_sources` Source bindings that automatically feed a dataset on ingest/reingest. ```sql CREATE TABLE IF NOT EXISTS dataset_sources ( id INTEGER PRIMARY KEY, dataset_id INTEGER NOT NULL REFERENCES datasets(id) ON DELETE CASCADE, source_kind TEXT NOT NULL, source_locator TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ``` ### `dataset_documents` Dataset membership rows. One document may belong to many datasets, and one dataset may contain many documents. ```sql CREATE TABLE IF NOT EXISTS dataset_documents ( id INTEGER PRIMARY KEY, dataset_id INTEGER NOT NULL REFERENCES datasets(id) ON DELETE CASCADE, document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE, dataset_source_id INTEGER REFERENCES dataset_sources(id) ON DELETE CASCADE, created_at TEXT NOT NULL, updated_at TEXT NOT NULL ); ``` ### `documents` Core registry for indexed documents. ```sql CREATE TABLE IF NOT EXISTS documents ( id INTEGER PRIMARY KEY, control_number TEXT UNIQUE, dataset_id INTEGER REFERENCES datasets(id) ON DELETE SET NULL, parent_document_id INTEGER REFERENCES documents(id) ON DELETE CASCADE, source_kind TEXT, source_rel_path TEXT, source_item_id TEXT, source_folder_path TEXT, production_id INTEGER REFERENCES productions(id) ON DELETE SET NULL, begin_bates TEXT, end_bates TEXT, begin_attachment TEXT, end_attachment TEXT, rel_path TEXT NOT NULL UNIQUE, file_name TEXT NOT NULL, file_type TEXT, file_size INTEGER, page_count INTEGER, author TEXT, content_type TEXT, custodian TEXT, date_created TEXT, date_modified TEXT, title TEXT, subject TEXT, participants TEXT, recipients TEXT, manual_field_locks_json TEXT NOT NULL DEFAULT '[]', file_hash TEXT, content_hash TEXT, text_status TEXT NOT NULL DEFAULT 'ok', lifecycle_status TEXT NOT NULL DEFAULT 'active', ingested_at TEXT, last_seen_at TEXT, updated_at TEXT, control_number_batch INTEGER, control_number_family_sequence INTEGER, control_number_attachment_sequence INTEGER ); ``` Allowed values: - `text_status`: `ok`, `partial`, `failed`, `empty` - `lifecycle_status`: `active`, `missing`, `deleted` #### Manual field lock rule Retriever treats the document columns themselves as the effective values users see and query. Manual user edits are preserved by locking the edited field names against future automated writes. - `manual_field_locks_json` stores a JSON array of user-editable document field names whose current values came from a manual action. - This applies to: - editable built-in metadata fields such as `page_count`, `author`, `content_type`, `custodian`, `date_created`, `date_modified`, `title`, `subject`, `participants`, and `recipients` - custom field columns added to `documents` through `ALTER TABLE` - When a user manually edits one of those fields, Retriever updates the column itself and adds that field name to `manual_field_locks_json`. - Automated ingest or review may refresh unlocked fields, but it must not overwrite any field named in `manual_field_locks_json`. - To accept a newly extracted or AI-produced value later, the lock must be cleared explicitly before or during the overwrite workflow. - System-managed columns such as `control_number`, `dataset_id`, `parent_document_id`, `source_kind`, `production_id`, `begin_bates`, `end_bates`, `begin_attachment`, `end_attachment`, `rel_path`, `file_name`, `file_type`, `file_size`, `file_hash`, `content_hash`, `text_status`, `lifecycle_status`, `ingested_at`, `last_seen_at`, `updated_at`, `control_number_batch`, `control_number_family_sequence`, and `control_number_attachment_sequence` are never manually settable or lockable. - `manual_field_locks_json` and the legacy `locked_metadata_fields_json` helper column are also never manually settable or lockable. Lock state must flow through dedicated Retriever behavior, not ad hoc edits to helper JSON. Because manual values live in the primary document columns, the FTS indexes and normal SQL filters continue to use the user-visible values automatically. #### Field visibility policy Retriever distinguishes between user-facing document metadata and internal helper fields when presenting field lists. - Default field views should show user-facing document fields plus custom fields: - `control_number` - `rel_path`, `file_name`, `file_type`, `file_size` - `page_count`, `author`, `content_type`, `custodian`, `date_created`, `date_modified` - `title`, `subject`, `participants`, `recipients` - custom fields registered in `custom_fields_registry` - Hide pure bookkeeping/helper columns by default: - `id` - `file_hash`, `content_hash` - `text_status`, `lifecycle_status` - `ingested_at`, `last_seen_at`, `updated_at` - `dataset_id` - `parent_document_id` - `source_kind`, `source_rel_path`, `source_item_id`, `source_folder_path`, `production_id` - `begin_bates`, `end_bates`, `begin_attachment`, `end_attachment` - `control_number_batch`, `control_number_family_sequence`, `control_number_attachment_sequence` - `manual_field_locks_json`, `locked_metadata_fields_json` - If the user explicitly asks for "all fields" or is debugging schema/runtime behavior, show every column and label helper fields as system/read-only instead of silently omitting them. - `control_number`, `content_type`, `custodian`, and `participants` are user-facing built-in metadata fields and should remain visible in default field views. - `dataset_name` is a virtual/user-facing projection derived from `dataset_documents` membership rows; it may be shown or filtered, but it is not stored as a column on `documents`. #### Control Number rule `control_number` is a stable user-facing document label for review and export. - Standalone documents and parent emails use the format `DOCXXX.YYYYYYYY`. - Child attachments use the format `DOCXXX.YYYYYYYY.ZZZ`. - `XXX` is the immutable first-seen ingestion batch of the parent family. - `YYYYYYYY` is the parent/family sequence within that first-seen batch. - `ZZZ` is the attachment sequence within the parent family. - Reindex is not a renumbering event. - New attachments on an existing parent get the next unused `.ZZZ`. - Removed attachment suffixes are retired and not reused. #### Content type rule `content_type` is a built-in metadata field on `documents`. - Default value comes from extension-to-type mapping. - That default may be overwritten during ingest when actual content inspection is more trustworthy. - Example: a `.pdf` defaults to `E-Doc`, but a PDF whose first-page body contains email headers may be classified as `Email`. - Typical override signals include: - email-style headers near the start of the document - calendar markers such as `BEGIN:VCALENDAR` #### Custodian rule `custodian` is a built-in metadata field on `documents`. - It identifies who the source material was collected from when Retriever can determine that provenance reliably. - Retriever should populate it using source-kind-specific rules rather than blindly copying `source_rel_path`. - For PST-derived message rows, default `custodian` comes from the owning `.pst` container name without the extension. - For MBOX-derived message rows, Google Vault filenames of the form `