Files
WCX/docs/reference-database-read-contract.md

160 lines
6.3 KiB
Markdown

# Read contract for the WCX reference database
## Purpose and ownership
This document defines the current SQLite read contract used by
`scripts/match_filenames.py` when matching local video filenames against the
WCX reference database. The contract identifies the database objects that the
file-management code depends on and the compatibility boundary that the
reference-data project must preserve.
The reference-data project owns the database schema, canonical metadata,
history, and aliases. It is the sole writer to the reference database.
File-management code is a consumer only and must open the database through a
SQLite URI with `mode=ro` and `uri=True`.
Local file paths, local processing state, matching decisions, and collection
state are outside this contract and must not be written to the reference
database.
## Connection contract
`scripts/match_filenames.py` resolves the configured database path, converts
it to a file URI, appends `?mode=ro`, and opens it as follows:
```python
database_uri = f"{database_file.resolve().as_uri()}?mode=ro"
with sqlite3.connect(database_uri, uri=True) as connection:
connection.row_factory = sqlite3.Row
```
The database file must already exist. The consumer must not fall back to a
normal writable SQLite connection.
## Tables and columns read
The current contract contains exactly three tables and ten selected columns:
| Table | Columns read |
| --- | --- |
| `movie` | `id`, `name`, `duration_seconds` |
| `movie_name_alias` | `movie_id`, `alias`, `normalized_alias`, `source` |
| `movie_history` | `movie_id`, `duration_seconds`, `archived_at` |
No other table or column is read by `load_movies()`.
### Column meanings for matching
| Column | Meaning in the file matcher |
| --- | --- |
| `movie.id` | Stable external identity for a movie. It links the current movie to aliases and historical durations and is the identifier that file-management data may reference externally. |
| `movie.name` | Current canonical display name and the primary name used to generate filename candidates. Empty names are excluded. |
| `movie.duration_seconds` | Current known duration. It supplies the current duration version used to support or distinguish name matches. |
| `movie_name_alias.movie_id` | Associates an alias with `movie.id`. |
| `movie_name_alias.alias` | Human-readable alternative name used to generate additional filename candidates. Empty aliases are excluded. |
| `movie_name_alias.normalized_alias` | Stored normalized form used directly for name comparison and duplicate suppression. |
| `movie_name_alias.source` | Describes the alias source and is retained in match results so the reported match can identify how the alias was obtained. |
| `movie_history.movie_id` | Associates a historical duration with `movie.id`. |
| `movie_history.duration_seconds` | Earlier known duration. Positive values are used as historical duration versions so an older local release can still support a match. |
| `movie_history.archived_at` | Identifies when the historical version was archived and is retained with the historical duration for diagnostics and reporting. |
`movie.id` is the stable external identity in this contract. `movie.name` is
not an external identity: the site importer can update the current name and
archives the previous movie state in `movie_history`. File-management records
must therefore refer to `movie.id`, never to a name as an identifier.
## SQL queries
The current implementation executes exactly these three queries.
### Current movies
```sql
SELECT
id,
name,
duration_seconds
FROM movie
WHERE name IS NOT NULL
AND TRIM(name) <> ''
ORDER BY name, id
```
### Aliases
```sql
SELECT
movie_id,
alias,
normalized_alias,
source
FROM movie_name_alias
WHERE alias IS NOT NULL
AND TRIM(alias) <> ''
ORDER BY movie_id, alias
```
### Historical durations
```sql
SELECT
movie_id,
duration_seconds,
archived_at
FROM movie_history
WHERE duration_seconds IS NOT NULL
AND duration_seconds > 0
ORDER BY movie_id, archived_at
```
These are read-only `SELECT` statements. The matcher does not issue database
writes or invoke another database-writing component.
## Backward compatibility
The following schema or semantic changes would be backward-incompatible for
the current file-management code:
- removing or renaming any of the three contracted tables,
- removing or renaming any selected column,
- changing a selected value so it can no longer be converted as currently
expected (`id`, names, alias source, and archive time to text; durations to
positive integer seconds),
- making `movie.id` unstable or reusing an ID for another movie,
- breaking the relationships from `movie_name_alias.movie_id` or
`movie_history.movie_id` to `movie.id`,
- changing `duration_seconds` to another unit or meaning,
- changing `normalized_alias` so it no longer represents the normalized alias
expected by filename comparison,
- changing the meaning of `source` such that it can no longer identify the
alias provenance shown in match output, or
- preventing the three queries from running through a SQLite `mode=ro`
connection.
Adding unrelated tables or columns is compatible. Adding rows, updating a
movie name while retaining the same `movie.id`, adding aliases, and appending
historical durations are also compatible with the current query contract.
## Process for contract changes
Before the reference-data project makes an incompatible change:
1. Identify the affected table, column, value semantics, or relationship and
document the proposed replacement.
2. Update the file-management consumer to support the new contract, preferably
with a transition period in which it can read both forms.
3. Create a disposable database containing representative current names,
aliases, current durations, and historical durations under the proposed
schema.
4. Verify all three queries and representative filename matching against that
database through `mode=ro`.
5. Verify that a write through the same connection is rejected.
6. Release or deploy the compatible file-management version before removing
the old contract from the reference database.
7. Remove the old contract only after both projects explicitly agree that no
active consumer depends on it.
The projects must coordinate any incompatible change; the reference-data
project must not silently break or reinterpret the documented read contract.