Skip to content

refactor(parser): source-addressed OracleItemIr and OracleDocBuilder (unit 3a)#5504

Merged
matthewevans merged 1 commit into
mainfrom
ship/parser-source-addressed-doc-ir-3a
Jul 10, 2026
Merged

refactor(parser): source-addressed OracleItemIr and OracleDocBuilder (unit 3a)#5504
matthewevans merged 1 commit into
mainfrom
ship/parser-source-addressed-doc-ir-3a

Conversation

@matthewevans

Copy link
Copy Markdown
Member

Introduce a source-addressed document IR as the safe midpoint of the Oracle
parser provenance refactor. Zero lowered-output change: data/card-data.json
is byte-identical under a same-base A/B regen, and no snapshot outside
oracle_ir/snapshots moves.

  • OracleItemIr { id, source, node } + OracleNodeIr, retaining the four
    PreLowered* variants as unit-4 debt.
  • OracleDocBuilder emits items through a single guarded path. emit fails
    closed on fragment/precision mismatch, duplicate item position, and
    overlapping sibling spans, in that order, before mutating any state.
  • PrintedAbilityIndex / PrintedTriggerIndex newtypes for CR 707.9a printed
    slots. Private field, no From<usize>, no Default; the sole escape hatch
    from_category_vector_len is the exact, compiler-enforced cutover list for
    unit 3b. Adding it surfaced two absolute index derivations in
    oracle_class.rs that no grep or runtime assertion had found, because the
    Class path early-returns before the main dispatch loop.
  • ability_index() is spells_emitted.len() rather than a parallel counter:
    two values that must agree can drift, one value cannot. take_last_spell()
    pops that emission-ordered stack, mirroring the parser's only category
    pop(), so an earlier spell's printed slot cannot be freed.
  • SpanPrecision { Exact, WholeDocument } so a consumer can refuse to print a
    line number rather than print a coarse one. fragment is Option<String>,
    None unless the span is exact.
  • Child spans are validated by UnitAllocator::allocate_with_span, the only
    way to mint an OracleUnitSource, which nested parsers actually receive.

Three review rounds found five latent CR 707.9a mis-indexing hazards, none of
them reachable by any current card and none detectable by the test suite, the
snapshots, or the 35k-card corpus.

…(unit 3a)

Introduce a source-addressed document IR as the safe midpoint of the Oracle
parser provenance refactor. Zero lowered-output change: `data/card-data.json`
is byte-identical under a same-base A/B regen, and no snapshot outside
`oracle_ir/snapshots` moves.

- `OracleItemIr { id, source, node }` + `OracleNodeIr`, retaining the four
  `PreLowered*` variants as unit-4 debt.
- `OracleDocBuilder` emits items through a single guarded path. `emit` fails
  closed on fragment/precision mismatch, duplicate item position, and
  overlapping sibling spans, in that order, before mutating any state.
- `PrintedAbilityIndex` / `PrintedTriggerIndex` newtypes for CR 707.9a printed
  slots. Private field, no `From<usize>`, no `Default`; the sole escape hatch
  `from_category_vector_len` is the exact, compiler-enforced cutover list for
  unit 3b. Adding it surfaced two absolute index derivations in
  `oracle_class.rs` that no grep or runtime assertion had found, because the
  Class path early-returns before the main dispatch loop.
- `ability_index()` is `spells_emitted.len()` rather than a parallel counter:
  two values that must agree can drift, one value cannot. `take_last_spell()`
  pops that emission-ordered stack, mirroring the parser's only category
  `pop()`, so an earlier spell's printed slot cannot be freed.
- `SpanPrecision { Exact, WholeDocument }` so a consumer can refuse to print a
  line number rather than print a coarse one. `fragment` is `Option<String>`,
  `None` unless the span is exact.
- Child spans are validated by `UnitAllocator::allocate_with_span`, the only
  way to mint an `OracleUnitSource`, which nested parsers actually receive.

Three review rounds found five latent CR 707.9a mis-indexing hazards, none of
them reachable by any current card and none detectable by the test suite, the
snapshots, or the 35k-card corpus.
@matthewevans
matthewevans enabled auto-merge July 10, 2026 13:14

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a structured document builder (OracleDocBuilder) and source-span tracking types (OracleSourceSpan, OracleUnitSource) to transition the MTG Oracle IR parser from category-grouped ordering to a source-positioned, document-ordered model. It also replaces raw usize counters with type-safe newtypes (PrintedAbilityIndex, PrintedTriggerIndex) to ensure strict compliance with CR 707.9a for copy effects. The feedback highlights an opportunity to clean up dead code by removing the unused issued field from the UnitAllocator struct.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +479 to +487
pub(crate) struct UnitAllocator {
item: OracleItemId,
/// The owning item's span. Held here, not on `ItemSlot`, because the
/// allocator is what nested parsers actually receive (through `ParseContext`)
/// — see `allocate_with_span`.
parent_span: OracleSourceSpan,
next_ordinal: u32,
issued: Vec<u32>,
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The issued field in UnitAllocator is completely unused (it is initialized and appended to, but never read). Ironically, because UnitAllocator itself is marked with #[allow(dead_code)] on line 478, this dead field was hidden from the compiler's unused-field analysis—exactly the kind of silent defect warned about in the module's header comment (line 19).

Please remove issued from the struct definition, and also clean up its initialization in UnitAllocator::new (line 497) and UnitAllocator::allocate (line 540).

pub(crate) struct UnitAllocator {
    item: OracleItemId,
    /// The owning item's span. Held here, not on `ItemSlot`, because the
    /// allocator is what nested parsers actually receive (through `ParseContext`)
    /// — see `allocate_with_span`.
    parent_span: OracleSourceSpan,
    next_ordinal: u32,
}

@matthewevans
matthewevans added this pull request to the merge queue Jul 10, 2026
@github-actions

Copy link
Copy Markdown

Parse changes introduced by this PR

✓ No card-parse changes detected.

Merged via the queue into main with commit bdcb1e9 Jul 10, 2026
13 checks passed
@matthewevans
matthewevans deleted the ship/parser-source-addressed-doc-ir-3a branch July 10, 2026 13:40
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