docs: specify M10 — attribution - #28
Merged
Merged
Conversation
An asset can say how it entered the library and nothing about whose work it is. `Asset.source` is routinely mistaken for attribution and is not: it records the pipeline, not the credit. The cost of deferring is asymmetric, which is why this jumps ahead of M8 and M9. Captured at ingest it is a form field; reconstructed later it means opening every file by hand, and for material gathered from the open web the answer is frequently gone. Neither M8 (needs a fal.ai account) nor M9 (needs GVC to exist) has that property. Three decisions taken with the user, recorded with their rejected alternatives so a later session reads them as decisions rather than accidents: - Eight structured fields, not one free-text credit. A credit line cannot answer "everything from this programme" without a parser over prose, and GVC needs the publisher as a field, not a substring. - Embedded file metadata is written directly; an AI may only suggest. EXIF/ID3/mp4 tags are facts about the file — ffprobe is already fetching `format.tags` and discarding them. What a model reads off a chyron is a claim, and a fabricated citation is worse than a blank one: a blank field prompts a fix, a confidently wrong publisher looks finished and ends up crediting the wrong outlet. Proposals must quote the evidence they were read from or be omitted. - Clips inherit from the parent at read time, with per-field override. Copying at creation rots: correcting a publisher would never reach the clips already cut from it, silently. Two schema notes that break house convention on purpose, so they are not later "fixed": `published_date` is an ISO partial-date string because a datetime cannot represent "1994" without fabricating precision, and `credit_line` is an override rather than the composed value, because a stored composition is stale the moment a component field changes. Also corrected two entries in the outstanding list that the code has since overtaken — the tag-management screen (`TagPanel.tsx`, mounted at `SettingsView.tsx:513`) and search's `asset_type`/`limit` handling. Both were built and left listed as gaps, which is the exact failure this list exists to prevent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019RWM7S6z1UPsZqso4p2HAj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Docs only. No code, no migrations, nothing to deploy — this is the spec for M10, plus two corrections to the outstanding list.
Why M10 jumps ahead of M8 and M9
An asset can currently say how it entered the library and nothing about whose work it is.
Asset.source(local_upload | url | ai_generated | gvc_export | clip | sub_video) is routinely mistaken for attribution and is not — it records the pipeline, not the credit.The cost of deferring is asymmetric. Captured at ingest, attribution is a form field. Reconstructed later it means opening every file by hand and guessing, and for material gathered from the open web the answer is often unrecoverable — the tab is closed and the page may be gone. Neither M8 (needs a fal.ai account) nor M9 (needs GVC to exist) has that property, and both can wait. A library that cannot say where anything came from also cannot be published from: GVC cannot build a credits roll out of prose.
The three decisions
Each was put to the user explicitly. Rejected alternatives are recorded in the doc, because the reasoning is not recoverable from the schema alone.
Eight structured fields, not one free-text credit.
source_url,creator,publisher,source_title,published_date,retrieved_at,license,credit_line. A credit line cannot answer "everything from this programme" or "everything published before 2020" without a parser over hand-written prose, which is a worse problem than the one it solves. A leaner four-field option was offered and declined: it defers exactly the columns that cannot be added cheaply later, since a migration does not recover information that was never captured.Embedded file metadata is written directly; an AI may only suggest. EXIF
Artist, ID3 tags, mp4format.tagsand PDFAuthorare facts about the file, so they are written into empty fields at ingest — and GAM is discarding them today, sinceingest/probe.pyalready calls ffprobe with-show_formatand drops everything but duration and codec. What a model reads off a chyron is a claim, so it goes through the existingSuggestionaccept/reject flow. The failure modes are not symmetric: a blank field is visibly incomplete and prompts a fix, while a confidently wrong publisher looks finished and ends up crediting somebody else's work to the wrong outlet. Proposals must quote the evidence they were read from, and a field the model cannot ground is omitted rather than guessed.Clips inherit from the parent at read time, with per-field override. Copying at creation is simpler to query and rots: correcting a publisher six months on would never reach the clips already cut from it, silently. Resolution costs nothing —
to_read_modelalready receives the parent andparents_for_manyalready batch-loads them, because M7 needed that forfile_url.Two schema notes that break house convention on purpose
Both are commented in the spec so they are not later "fixed":
published_dateis an ISO partial-date string, not adatetime. Publication dates are routinely partial — a book is from 1994, a magazine piece from March 2019 — and adatetimecolumn forces a fabricated precision indistinguishable from a real one, which in a citation record is the exact falsehood this milestone exists to prevent.YYYY/YYYY-MM/YYYY-MM-DDsort and range-compare correctly as plain strings, so the filters need no parsing.credit_lineis an override, not the composed value. A stored composition is stale the moment a component field is corrected — the same rot that made copy-on-create wrong for clips.field_provenancealso gains a third value,"embedded", so a later suggestion can propose over a camera-supplied name without ever proposing over something the user typed.Risk flagged for implementation
SQLite FTS5 cannot
ALTER TABLE ADD COLUMN, so indexing attribution means dropping and recreatingasset_fts— which, because it stores its own copy of the text rather than using external-content mode, loses the index for every existing asset unless the migration repopulates it. A recreate that skips the repopulate passes any test that only checks the schema and silently empties keyword search in production. The spec splits this into its own Alembic revision, separate from the eightadd_columncalls, so a failure in the risky half does not strand the columns.f68af8dexists because7d4b9c1a6f28hit the neighbouring version of this.Corrections to the outstanding list
Two entries were overtaken by code and left listed as gaps. Verified against the source, not assumed:
TagPanel.tsxis mounted atfrontend/src/views/SettingsView.tsx:513and wiresrename/remove/recategoriseplus the full category CRUD.asset_typeandlimit— built.SearchView.tsxpasses both, with type chips and a load-more towardMAX_LIMIT, persisted in the URL. There is still nooffset, so it is a growing page rather than true pagination — deliberate at this scale.A stale gap is worse than no list, since it invites rebuilding something that works.
Testing
Docs only; nothing to run. Prettier covers
frontend/src/**/*.{ts,tsx,css}only, so markdown is outside the CI format gate.🤖 Generated with Claude Code
https://claude.ai/code/session_019RWM7S6z1UPsZqso4p2HAj
Generated by Claude Code