Skip to content

P-022 diagnostics: normalized AnalysisTrace and first-divergence minimizer for Python↔Rust shadow mode #269

Description

@PhysShell

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:

What disagreed?

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

  1. No pointer/object-identity values.
  2. No hash-map iteration order.
  3. IDs must be stable for the same normalized input across Python and Rust.
  4. Equivalent sets/maps are sorted canonically before hashing/rendering.
  5. Source locations are normalized separately from semantic state so harmless line drift does not hide the first semantic mismatch.
  6. Trace schema is versioned and unknown events fail loudly in the comparison tool.
  7. 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:

  1. retain the exact OwnIR bytes;
  2. rerun both engines with trace enabled;
  3. compute the first-divergence report;
  4. optionally run bounded minimization;
  5. 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

  1. Identical engines/traces compare equal byte-for-byte after normalization.
  2. Deliberate transfer-function mutation reports the exact first differing instruction.
  3. Deliberate merge mutation reports merge as the first divergence, not a later diagnostic symptom.
  4. Different internal allocation/object IDs normalize to equality.
  5. Different map iteration order normalizes to equality.
  6. A rendering-only difference does not masquerade as an analysis-state divergence.
  7. Minimizer removes unrelated components while preserving the mismatch.
  8. Reducer refuses malformed/semantically invalid candidates instead of manufacturing a tiny lie.
  9. Trace-off and trace-on runs produce identical final diagnostics/evidence.
  10. A fixed divergence lands with a minimized permanent regression fixture.

Acceptance

  • Versioned normalized AnalysisTrace contract is documented.
  • Python and Rust emit comparable traces for currently ported layers.
  • First-divergence CLI/report exists and identifies the earliest semantic mismatch.
  • P-022 step 7a: add dual-engine shadow mode and zero-diff reproduction artifacts #260 mismatch artifacts can include traces without changing success-path cost materially.
  • A bounded minimizer produces valid smaller repros for at least ownership and one fact-level family.
  • Trace generation cannot change final verdicts.
  • Regression tests prove normalization is independent of incidental identity/order.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions