# 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.