Skip to content

Seed the beamline the pilot needs, and prove ingest against it - #587

Merged
xmap merged 2 commits into
mainfrom
worktree-pilot-seed
Jul 29, 2026
Merged

Seed the beamline the pilot needs, and prove ingest against it#587
xmap merged 2 commits into
mainfrom
worktree-pilot-seed

Conversation

@xmap

@xmap xmap commented Jul 29, 2026

Copy link
Copy Markdown
Owner

The bridge from CORA describes 2-BM to this CORA instance knows 2-BM, scoped by its own gate review to what the read-only pilot's ingest path actually exercises.

Why not the full descriptor seeder

The descriptor-reconciling onboarding was designed and gate-reviewed, and the review overturned it as v1: the descriptor cannot provide either of ingest's two prerequisites (its six supplies are beamline utilities, storage is deployment infrastructure; the Camera family lacked Capturing by roster design), and 52 of its 53 instances have no production reader in a read-only pilot. The full reconciler stays deferred with a named trigger and its design notes preserved (per-aggregate identity table, mapping policies walked out of the real file, drift rename-blindness).

What ships

Capturing graduates onto the Camera seed family (_family_seed_registry.py + catalog lockstep). The affordance's own contract, "produces a Data BC Acquisition fact on every capture", is what record_acquisition's gate checks. A database seeded before this change gets a loud version_family remedy, never a silent mutation.

python -m cora.api.pilot_seed: an idempotent operator ceremony registering the beamline root Unit Asset, the camera Device Asset, its Camera family attachment, and one Storage Supply, which it also marks Available (a merely-registered Supply satisfies neither the run-preflight gates nor the legacy-Distribution backfill paired with SELF_FACILITY_DEFAULT_STORAGE_SUPPLY_CODE). Reports one line per instance (seeded / exists / retired / error), three exit codes, first-class --dry-run.

Identity is per-aggregate, matching each aggregate's locked design: path-qualified uuid5 for Assets (no bare name reserved repo-wide, the Role/Imager lesson), resolve-only for Families (bare-name ids are federation-portable by design) and Enclosures (boot-seeded, minted ids), minted-plus-address-pre-check for Supplies. The ceremony runs the lifespan's own idempotent bootstrap hooks and drains projections itself, so it works against a never-booted database.

The proof

Integration tests with nothing faked: a fresh database seeds (exit 2); a re-run reports all-exists and appends zero events (exit 0); dry-run writes nothing; the seeded camera surfaces Capturing through the real PostgresAssetLookup projection join; and ingest_scan then records a real Data Exchange file end to end, retiring the in-memory lookup fake as the suite's only Capturing source.

The re-run test caught a real bug during development: the kernel construction site's default lookups are test stubs, and a ceremony pre-checking idempotency against a synthetic-supply stub re-seeds on every run. The real projection-backed lookups are now wired explicitly, with the trap documented.

Full tiers green: 45,781 unit/architecture/contract (the roster change's blast radius is every create_app()), 1,088 integration/e2e, docs build clean.

🤖 Generated with Claude Code

Scan ingest shipped requiring two things no production path registers:
an Asset whose family affordances include Capturing, and a
Storage-kind Supply. The full descriptor-reconciling onboarding was
designed, gate-reviewed, and deliberately deferred: the review showed
the descriptor cannot provide either prerequisite (its six supplies
are beamline utilities, storage is deployment infrastructure; the
Camera family lacked Capturing by roster design) and 52 of its 53
instances have no production reader in a read-only pilot. What ships
instead is the minimal honest pair.

First, Capturing graduates onto the Camera seed family. The
affordance's own contract ("produces a Data BC Acquisition fact on
every capture") is what record_acquisition's gate checks, and a camera
is precisely the thing it describes. The catalog moves in lockstep. A
database seeded before this change reports the version_family remedy
rather than being silently mutated: family definitions belong to the
seed registry's graduation governance, and the ceremony never defines
or edits one.

Second, python -m cora.api.pilot_seed registers, idempotently: the
beamline root Unit Asset, the camera Device Asset, its Camera family
attachment, and one Storage Supply, which it also marks Available,
because a merely-registered Supply satisfies neither the run-preflight
gates nor the legacy-Distribution backfill that resolves
SELF_FACILITY_DEFAULT_STORAGE_SUPPLY_CODE by the same name.

Identity is per-aggregate, matching each aggregate's locked design
rather than one blanket rule: Assets get deterministic path-qualified
uuid5 ids (no bare name reserved repo-wide); Families are resolved via
their bare-name-derived ids, never defined (a Camera at APS and at
MAX IV must share one id or Assembly content hashes fork); Enclosures
are not touched (boot-seeded, minted ids, address pre-check);
Supplies keep minted ids with the address pre-check as the idempotency
surface.

The ceremony runs the same idempotent bootstrap hooks the app lifespan
runs and drains projections itself, because the registration deciders
demand lookup results that production fills from projections and a
standalone kernel has no worker. The kernel wires the REAL
projection-backed lookups explicitly; the construction-site defaults
are test stubs, and a ceremony that pre-checks idempotency against a
synthetic-supply stub re-seeds on every run, which is exactly the bug
the re-run test caught.

The proof is the integration flow the seeder design's review demanded:
a fresh database seeds (exit 2), a re-run reports all-exists and
appends zero events (exit 0), a dry run writes nothing, the seeded
camera surfaces Capturing through the real PostgresAssetLookup join,
and ingest_scan then records a real Data Exchange file with nothing
faked anywhere in the chain, retiring the in-memory lookup fake as the
suite's only Capturing source.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  apps/api/src/cora/api
  pilot_seed.py 210-216, 226-227, 260-262, 300, 389, 393, 405-406
  apps/api/src/cora/federation/adapters
  postgres_facility_lookup.py
Project Total  

This report was generated by python-coverage-comment-action

The diff-coverage gate flagged the error paths: unknown facility,
unknown family, the CLI exception catch, and main itself. Each is now
pinned by the test that documents its behavior (loud exit 1 with the
instance named), which is worth having independent of the gate: the
refusal branches are the operator-facing half of the ceremony.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@xmap
xmap merged commit 0a44245 into main Jul 29, 2026
16 checks passed
@xmap
xmap deleted the worktree-pilot-seed branch July 29, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant