Skip to content

Cog catalog store (digest-keyed, via collab_schema migrations) and reconciliation indexer #84

Description

@trentoliphant

Child of #7. Where indexed Cogs live and how they get there.

Problem

#7 requires a catalog populated by indexing the registry, surviving restarts, with installs pinned to digests. There is no store and no indexer.

Proposal

Store

Tables added through the collab_schema.py migration registry (advisory-locked, versioned), not a per-store CREATE TABLE IF NOT EXISTS (#42 explains why):

  • collab_cog_artifacts — one row per (source_id, repository, digest):
    source_id, host, repository, digest (PK with source+repo), tags text[], pushed_at, indexed_at, manifest_media_type, card jsonb (the reader's output, full profile preserved as structured JSON), cog_id (profile id, e.g. openteams/cog-media-transcriber), name, version, kind, publisher, manifest_schema, read_errors jsonb, removed_at (null while present in the registry).
  • Indexes on cog_id, kind, removed_at, and a GIN index on card for filtering by requires/provides/io.
  • Identity is the digest. cog_id/name are search keys; the repository path is not identity (Nebi-published repo names carry an id suffix, and one Cog may be published to several repos).

Indexer

cogs/indexer.py, a reconciliation loop:

  1. For each configured source: enumerate repositories and artifacts.
  2. Skip digests already indexed with the same tag set; upsert tag changes without refetching.
  3. For new digests: fetch manifest, select COG.md + the profile file, read the card, insert. Artifacts with no COG.md are recorded as non-Cog with a reason, so a repo full of images does not get re-read every cycle.
  4. Mark rows whose digest is no longer present as removed_at = now(); never hard-delete (installs and runs may reference them).
  5. Per-artifact failures are stored in read_errors and do not abort the sweep; a sweep summary is logged and exported as metrics (indexed, skipped, failed, removed).

Runs at startup (after migrations) and on an interval (cogs.index.interval_seconds, default 300), and on demand from the webhook receiver (#86). Single-flight under a Postgres advisory lock so replicas do not sweep concurrently. Optional cogs.index.run_on_startup=false for tests.

Tests

  • Live-Postgres tests following the existing test_postgres_* pattern (also skipped-in-CI today, see The live database test suites are skipped in CI #56): insert/upsert/removal, tag change without refetch, GIN filter queries.
  • Indexer against the static adapter with mocked OCI: new artifact, unchanged artifact, retagged artifact, removed artifact, reader failure recorded and sweep continues.

Acceptance

  • After a sweep against the dev registry, every Cog in the cogs project has a row whose card matches the reader's output; a second sweep changes nothing.
  • Deleting an artifact in the registry marks the row removed on the next sweep; the row is still readable by digest.
  • Two replicas starting together do not double-index (verified with the advisory lock test pattern from collab_schema).
  • Migrations are appended to COLLAB_SCHEMA_MIGRATIONS; no new bare DDL.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Core — right after the spinecog-executionA4 Cog execution work (tracked under #690)type: enhancement 💅🏼New feature or request

    Type

    No type

    Fields

    Priority

    None yet

    Start date

    None yet

    Target date

    None yet

    Size

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions