Add single-video YouTube locator entry for Context

SI
5d ago
+359-0

Summary

Adds public.create_context_video_request(p_owner uuid, p_actor uuid, p_video text, p_key text), the database half of a provider-independent, zero-spend ingest_video entry.

It saves one YouTube video (validated 11-character ID) as a video subject backed by a youtube/video resource, with:

  • an accepted identity link whose only evidence is the submitted URL
  • a customer source and version snapshot
  • a zero-cost user_submission attempt
  • an accepted customer_assertion result on a video_locator document

The request ends partial with six distinct gaps:

  • video_metadata: uncollected
  • uploader_attribution: unresolved (the uploading channel is never assumed to be the artist, a performer or a credited artist)
  • recording_match: unresolved
  • captions: uncollected (no lyrics inferred)
  • media_version: unknown
  • video_narrative: unavailable

It creates no account, roster, song, song-identifier, artist, recording or release row and writes no song or artist document. This delivers, as persisted and tested contracts:

  • the "remains explicitly unresolved" half of acceptance item 1
  • the no-silent-lyrics guarantee of item 2
  • the no-overwrite guarantee of item 3

No constraint changes: video was already permitted by the foundation and by every later constraint rewrite. So this migration cannot collide with sibling migrations that widen constraints.

Refs recoupable/app#2131 (child of epic recoupable/app#2116).

Merge sequencing

docs → database → api → app. Sibling PRs for this ticket, all on branch claude/ctx-2131-youtube-video-entry:

Release this migration before the API PR; until then, the API action fails with its generic 409. No dependency on the open database PRs (#88/#89/#90/#92/#94/#95 are untouched). CI runs in a new workflow file (context-video-entry.yml, PostgreSQL 15/16) instead of editing release-cases.yml.

Verification

  • Ran: export PATH="/opt/homebrew/opt/postgresql@17/bin:$PATH" LC_ALL=C && python3 -m unittest discover -s tests -p 'test_context_video_entry.py' → Ran 3 tests, OK. This uses PostgreSQL 17 (Homebrew) on a disposable cluster, Unix socket only. The fixture covers:
    • invalid identifiers (short, long, spaced, empty, null, full URL)
    • save → partial with one video subject and six gaps
    • resource, subject and identity-link shape; no credited-artist or release-member link
    • result and attempt shape: customer assertion, zero confirmed cost, no title/channel/lyrics keys
    • exactly one video_locator document, no song or artist documents, and read_context_documents
    • zero account, roster, song, identifier, artist, recording or release rows
    • same-key replay without new rows, and a key conflict
    • re-submitting the same video in the same workspace under a new key: the subject, resource, source and document are reused; exactly one request, attempt and accepted result are appended; the single video_locator document advances one revision; no artist, song or non-locator rows appear
    • a second video as a distinct subject, leaving the first document untouched
    • another workspace reading null, and the same video saved separately per workspace
    • source withdrawal hiding the locator in one workspace only
    • anon/authenticated denied while service_role can execute
  • The lifecycle test now re-counts resources, requests, subjects, sources, results, documents, accounts, roster, songs and identifiers after the rolled-back fixture and expects 0. The earlier standalone count test ran alphabetically before the fixture, so it could not catch a leak. The test count drops from 4 to 3 as a result.
  • Regression: test_release_cases.py → 1 test OK; test_company_assessment.py → 3 tests OK.
  • Re-run on head 23262f9 (finalizer pass, PostgreSQL 17.7, LC_ALL=C): Ran 3 tests, OK. To show the new re-submission check is not vacuous, I temporarily expected the revision to stay unchanged; the lifecycle test then failed with Resubmission did not advance the single locator document. I reverted that change.
  • Red → green: with the new migration removed from the chain, the lifecycle test fails with function public.create_context_video_request(uuid, uuid, text, unknown) does not exist. It passes with the migration.
  • grep -c 'grant execute on function public.create_context_video_request' → 1; the revoke all … from public,anon,authenticated line is present.
  • PR CI passed context-video-entry for PostgreSQL 15 and 16 on 718a36f and again on 23262f9, which includes the re-submission case. The other database workflows also passed on 23262f9.
  • Not verified:
    • production database: the migration has not been applied to production. Supabase's GitHub integration applied it automatically to this PR's ephemeral preview branch when the PR opened, and its Supabase Preview check passed. That preview branch was not exercised.
    • PostgreSQL 15/16 locally (the CI workflow covers them)
    • live providers (none are called)
    • an end-to-end API/MCP round trip through a deployed service

Out of scope / known gaps

  • Not included: metadata verification (title/channel), retention of captions/description/language, recording or artist attribution, or a list_context_video_request_target for a future default-off verify_video step. These are the named follow-ups in the API README.
  • plan does not support kind: 'video'; the API rejects it as an unsupported saved entry rather than silently planning research.
  • recoupable/app#2130 (CE14 checkpoint) still gates ticket completion. This is contract and fixture work, which the epic permits before that checkpoint.
  • Implemented and fixture-tested only; not connected, deployed or live-verified.

🤖 Generated with Claude Code