Skip to main content

Module migrate

Module migrate 

Source
Expand description

ev migrate — backfill an existing decision history into the ledger.

Four PURE, format-aware extractors turn a source substrate (&str) into a Vec<MigrationRecord>: a chat-room/git log (## R<N> records), the to-human RESOLVED/FLAG markdown blocks (the authority substrate), a decisions-immutable §N document, and an escalation log (the SAME RESOLVED/FLAG reader, path-parameterized). The extractors parse rulings + structured rejected-roads only — they NEVER NLP a free-text reason into a ground (grounds_are_never_ synthesized): a road becomes a ground iff the source declares it structurally (a rejected: token), otherwise the record carries zero grounds and stays an honest capture.

The command driver then runs an IDEMPOTENT backfill loop (deterministic source_key sort → prospective-parent compute_id → ticks_dir pre-check → skip-if-present) on top of the shared capture::append, plus a --reconcile join and a --bind-check harvest.

Structs§

BackfillSummary
The outcome of one backfill pass (idempotent): how many records were imported, skipped (already present by content-addressed id), re-linked (a back-dated mid-chain insert that re-parented), and how many were source-only gaps that could not be appended (e.g. a source lacking authors with no --blame fallback). Rendered by the command layer.
MigrationRecord
One extracted, not-yet-appended decision from a source substrate. source_key is the stable, deterministic dedup/sort key (e.g. R2289, #555, §3) used to order the backfill and to reconcile against the store; observe carries that key as a durable token so reconcile can read it back from the HASHED payload, not from the events log. Grounds are ONLY the structurally declared rejected-roads — never synthesized from prose.
ReconcileReport
A reconcile bucket count: how many source rulings are IN BOTH the source and the store, how many are SOURCE-ONLY (the capture gap — a ruling the source has that the ledger never captured), how many are STORE-ONLY (in the ledger, absent from this source), and how many store ticks could not be keyed at all (no round token in their hashed observe). Keys come from the non-hashed source_ref or the hashed observe, never from events.jsonl, so they are durable.

Functions§

backfill
Run the idempotent backfill of records into the store at repo. Deterministic order: records are sorted by source_key first so a re-run replays the same chain. Idempotency is keyed on the durable source_key (the non-hashed source_ref’s derived key, or a token in the hashed observe): a record whose key is already in the store is SKIPPED — chain-position-independent, so a re-run over a now-non-empty store writes nothing. The chain is kept by threading the PROSPECTIVE parent (the id we just wrote/found) instead of re-reading the live HEAD each step, so the lineage stays stable across re-runs. A skipped record whose stored parent differs from where it would now land is a back-dated mid-chain insert and is reported as re-linked. blame_fallback supplies the author for a record carrying none; a record with neither is a source-only gap (R5 stays intact — we never invent an author). jurisdiction_map (source_key → A/B/C/D bucket) tags each imported decision: a record whose key is in the map carries that jurisdiction, one absent imports untagged (None) — so the map is purely additive (an empty map ⇒ every record None, the prior behavior). jurisdiction is NON-hashed, so tagging never moves a tick id (idempotency holds across re-runs). --dry-run reports the would-import count but writes nothing.
bind_check
The --bind-check harvest: build a harvested Check::Test (counter_test None, full liveness) for the given selector, reusing the Task-5 migrate-only constructor. This is the SAME constructor the harvested-binding path uses — no second half-harvest gate. The caller attaches it to a ground.
canonical_records
Parse a Canonical Decision Intake stream (JSONL) into MigrationRecords — the format-neutral intake both an adopter’s legacy adapter and a future live runner emit. This IS the trust boundary: the producer supplies STRUCTURE, and ev RE-VALIDATES it here through the very read-path validators (ground_from_value, the vocab checks) that guard an on-disk tick — never a parallel serde decode that could trust an unchecked Ground. Per line: skip blank / #-comment lines; require the fixed kind discriminator and reject any unknown envelope key loudly; require a non-empty decision and a grounds array (which may be empty — the honest zero-grounds capture); validate every declared tag against its closed vocabulary. The durable dedup/sort key mirrors store_key: the opaque source_ref’s derived key, else the first round/#issue token in observe.
extract_decisions_immutable
Extractor 3 — decisions-immutable: a document split on ## N. / ## §N section headers, one decision per numbered section. The section number is the source_key; the header text after the number is the decision; structured rejected-roads in the section body become grounds.
extract_escalation
Extractor 4 — escalation: the SAME RESOLVED/FLAG reader, path-parameterized — escalation is just the reader over a different file, with NO hardcoded layout of its own.
extract_gitlog
Extractor 1 — gitlog / chat-room: each ## R<N> … header is one decision; the header text after the round token (and an optional em-dash separator) is the decision; any structurally declared rejected-road line in that record’s body becomes a ground. The R<N>/#<n> token is the source_key and is carried into observe as a durable provenance token. Reasons are NEVER NLP’d.
extract_to_human
Extractor 2 — to-human: the RESOLVED/FLAG markdown blocks (the authority substrate).
reconcile
Reconcile a source’s extracted records against the store. The store-side key is read from each the derived key of its non-hashed source_ref if present, else the first round/#N token in the hashed observe — so the join is durable (NOT dependent on the events log). A source key with no store match is a SOURCE-ONLY gap (the capture gap to surface); a store key with no source match is STORE-ONLY; a store tick with no derivable key is counted separately as un-keyable.