Classification
Problem
The P-022 roadmap correctly requires Python↔Rust behavioral parity and #260 requires dual-engine shadow mode plus standalone reproduction artifacts.
A final diagnostic diff answers:
It does not answer:
Where did the two implementations first diverge?
Was the first mismatch in lowering, state transfer, merge, fixpoint convergence,
MOS inference, evidence construction, or final rendering?
Without a normalized internal trace, every mismatch risks becoming a manual binary search through two implementations. That is slower than a sedated sloth and considerably less charming.
Goal
Add an optional, deterministic AnalysisTrace contract shared by Python and Rust, then provide tooling that reports the first semantic divergence and minimizes the reproduction input.
The trace is diagnostic infrastructure only. Final diagnostics/evidence remain the production contract and Python remains authoritative until explicit cutover.
Trace vocabulary
Names are reviewable. The trace should cover semantically load-bearing checkpoints, not every incidental allocation or hash-map iteration:
InputAccepted
FunctionEntered
BlockInputState
InstructionApplied
BlockOutputState
MergePerformed
WorkItemScheduled
FixpointReached
SummaryInferred
EvidenceEdgeAdded
DiagnosticConstructed
Each event should carry stable typed identifiers and normalized semantic payloads, for example:
function_id
block_id
instruction_id/op
resource/handle IDs normalized by construction order
before_state_digest
after_state_digest
predecessor state digests
diagnostic code/evidence edge
Normalization requirements
- No pointer/object-identity values.
- No hash-map iteration order.
- IDs must be stable for the same normalized input across Python and Rust.
- Equivalent sets/maps are sorted canonically before hashing/rendering.
- Source locations are normalized separately from semantic state so harmless line drift does not hide the first semantic mismatch.
- Trace schema is versioned and unknown events fail loudly in the comparison tool.
- Tracing is off by default and must not affect verdicts.
Tooling surface
A narrow dev surface is sufficient:
own-oracle explain-divergence repro.ownir.json
Expected output shape:
first divergence:
phase: transfer
function: CustomerViewModel::.ctor
block: B7
instruction: Release(subscription#12)
Python:
before: ...
after: Released
Rust:
before: ...
after: {Owned, Released}
The command should also emit machine-readable JSON for CI artifacts.
Minimization
When a mismatch exists, provide a reducer that attempts to remove irrelevant:
- components/functions;
- resources/handles;
- flow statements/branches;
- DI/effect facts;
- unrelated evidence locations;
while preserving the same first-divergence signature.
The minimized reproduction becomes a permanent fixture once the divergence is understood and fixed.
The reducer may be deliberately conservative. A small valid repro is more useful than an aggressive reducer that silently changes semantics.
Integration with #260
Shadow mode should continue comparing normalized final outputs first.
On mismatch only:
- retain the exact OwnIR bytes;
- rerun both engines with trace enabled;
- compute the first-divergence report;
- optionally run bounded minimization;
- store original input, traces, report and minimized fixture as the zero-diff reproduction artifact.
This keeps successful runs cheap and makes failed runs explainable.
Required controls
- Identical engines/traces compare equal byte-for-byte after normalization.
- Deliberate transfer-function mutation reports the exact first differing instruction.
- Deliberate merge mutation reports merge as the first divergence, not a later diagnostic symptom.
- Different internal allocation/object IDs normalize to equality.
- Different map iteration order normalizes to equality.
- A rendering-only difference does not masquerade as an analysis-state divergence.
- Minimizer removes unrelated components while preserving the mismatch.
- Reducer refuses malformed/semantically invalid candidates instead of manufacturing a tiny lie.
- Trace-off and trace-on runs produce identical final diagnostics/evidence.
- A fixed divergence lands with a minimized permanent regression fixture.
Acceptance
Non-goals
- No production user-facing trace format commitment.
- No replacement of diagnostics, Evidence or SARIF.
- No behavior change or new analyzer rule.
- No generic debugger UI.
- No requirement to trace every parser token or allocation.
- No cutover decision in this issue.
Classification
own-bridgeand dual-engine cutover workProblem
The P-022 roadmap correctly requires Python↔Rust behavioral parity and #260 requires dual-engine shadow mode plus standalone reproduction artifacts.
A final diagnostic diff answers:
It does not answer:
Without a normalized internal trace, every mismatch risks becoming a manual binary search through two implementations. That is slower than a sedated sloth and considerably less charming.
Goal
Add an optional, deterministic AnalysisTrace contract shared by Python and Rust, then provide tooling that reports the first semantic divergence and minimizes the reproduction input.
The trace is diagnostic infrastructure only. Final diagnostics/evidence remain the production contract and Python remains authoritative until explicit cutover.
Trace vocabulary
Names are reviewable. The trace should cover semantically load-bearing checkpoints, not every incidental allocation or hash-map iteration:
Each event should carry stable typed identifiers and normalized semantic payloads, for example:
Normalization requirements
Tooling surface
A narrow dev surface is sufficient:
Expected output shape:
The command should also emit machine-readable JSON for CI artifacts.
Minimization
When a mismatch exists, provide a reducer that attempts to remove irrelevant:
while preserving the same first-divergence signature.
The minimized reproduction becomes a permanent fixture once the divergence is understood and fixed.
The reducer may be deliberately conservative. A small valid repro is more useful than an aggressive reducer that silently changes semantics.
Integration with #260
Shadow mode should continue comparing normalized final outputs first.
On mismatch only:
This keeps successful runs cheap and makes failed runs explainable.
Required controls
Acceptance
Non-goals