Seed the beamline the pilot needs, and prove ingest against it - #587
Merged
Conversation
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>
Coverage reportClick to see where and how coverage changed
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>
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.
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 whatrecord_acquisition's gate checks. A database seeded before this change gets a loudversion_familyremedy, 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 withSELF_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
PostgresAssetLookupprojection join; andingest_scanthen 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