Allow DSP listing observations in Context enrichment

SI
5d ago
+203-0

Summary

Adds a dsp_listings enrichment topic so the Context Engine can store DSP store listings (Apple Music first) as provider observations. They are not stored as canonical context_resources, identities or release IDs. claim_context_enrichment hard-codes its topic allowlist, so the API collector in the companion PR cannot save anything until this migration is applied.

20261010214300_context_dsp_listings.sql copies the current function body from 20260927090000_context_audio_source.sql unchanged except for one token: dsp_listings is added to the topic allowlist and to the "Provider lookup requires observation evidence" list. A diff that strips ,'dsp_listings' from the new file matches the base definition exactly.

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

Merge sequencing

docs (no change) → this database PR → api recoupable/api#1025 (same branch claude/ctx-2143-dsp-listings) → app (no change).

  • No dependency on open PRs. Open database PRs #89–#115 were checked: no migration timestamp collision, and none redefines claim_context_enrichment or complete_context_enrichment.
  • Ordering rule: this migration replaces the whole function body. Any later migration that redefines claim_context_enrichment (for example one adding another provider topic) must keep dsp_listings in both lists. The rule is stated in the migration header and in tests/README.md.
  • Planner wiring (not in this slice): a planned DSP node will also need a node-to-topic mapping in save_context_execution_outcome (20260924100000_context_release_track_execution.sql); without it the save raises Execution evidence does not match node. The migration header and tests/README.md name this second place.

Verification

  • Ran on the current head (after the review follow-up commit 0be692a): LC_ALL=en_US.UTF-8 PATH=/opt/homebrew/opt/postgresql@17/bin:$PATH python3 -m unittest discover -s tests -p 'test_dsp_listings.py' → 5 passed, 0 failed (PostgreSQL 17). The suite covers:
    • claim, then one saved result holding two storefronts with one versioned source each (jp not listed, us listed), then reuse;
    • interpretation and estimate rejected for dsp_listings;
    • musicbrainz_recordings and catalog_valuation still claim under their own rules;
    • near-miss and unregistered topics still raise Invalid enrichment module;
    • the context_resources count is unchanged across claim, save and reuse (the follow-up replaced a check that ignored every Spotify row), and no resource links or new subjects;
    • plus the inherited release-cases lifecycle test.
  • Ran on the first commit: test_company_assessment.py → 3 passed. test_release_cases.py → 1 passed. The follow-up changes only the migration's header comment, so the function body is unchanged.
  • Migration check on the current head: after stripping ,'dsp_listings' and the begin;/commit; wrapper, the function definition is identical to the claim_context_enrichment definition in 20260927090000_context_audio_source.sql.
  • Ran (ad hoc, not committed): the existing supabase/tests/context_provider_evidence.sql, seeded and run after the new migration → passed.
  • Red → green: with the migration body replaced by select 1;, test_claim_save_and_reuse_storefront_scoped_listing_observation and test_listing_topic_requires_observation_evidence failed with ERROR: Invalid enrichment module. Both pass with the migration.
  • CI: the new .github/workflows/dsp-listings.yml runs the suite on PostgreSQL 15 and 16. It mirrors release-cases.yml but is a separate file, so sibling PRs editing that workflow won't conflict with this one.
  • Note: local macOS PostgreSQL 17 needs LC_ALL set, or the postmaster refuses to start ("postmaster became multithreaded during startup").
  • Not verified: hosted deployment, any hosted or production database (this migration has not been applied anywhere), live providers. Results are implemented and fixture-tested only.

Out of scope / known gaps

  • No new tables, columns, constraints or context_resources.provider values. Listings stay in context_results.
  • YouTube Music, music video, art track and uploader channel collection. YouTube Music store listings remain an open #2143 gap; #2131 covers single-video YouTube context, not store listings.
  • No planner, dispatcher, HTTP, MCP or brief-recipe support (API side; not in this PR).

🤖 Generated with Claude Code