Skip to content

Dual-Run Equivalence Test

Equivalence audit — instantiates Definition-Time Context Binding

Runs one behavior unit under both its original and a new context and compares the outputs, so context-coupling bugs surface as divergences instead of silent drift.

A Dual-Run Equivalence Test takes a single behavior unit, executes it under two contexts — the one it was defined against and the one it is being moved to — feeds both the same inputs, and compares the results. Its purpose is to catch the failure this whole archetype exists to prevent: a unit that looks portable but whose meaning was quietly coupled to invisible origin assumptions, so that it produces different answers in its new home. Its defining move is that it does not reason about the binding at all — it empirically demonstrates sameness or difference by observation. And crucially, it compares against a determinism profile: an explicit account of what is allowed to differ (timestamps, generated IDs, ordering) so that only meaningful divergences count as failures rather than every incidental one.

Example

A bank is replacing the aging engine that computes overdraft fees. The rules are the same on paper, but the old engine has twenty years of subtle behavior baked into a context nobody fully remembers. Before cutting over, the team runs both engines side by side on the same live stream of transactions — a parallel run — and compares every fee the two produce. The determinism profile is set first: the two may differ in the internal trace IDs and the millisecond timestamp on each record, but not in a single computed fee. For weeks the runs agree, then diverge on a narrow class of accounts — the old engine, it turns out, had been reading a rounding mode from a context the new one bound differently. That divergence, logged with both inputs and both outputs, is the entire value of the test: a context-coupling bug made visible before it charged a customer, not discovered afterward.

How it works

  • Fix the determinism profile first. Declare which outputs are semantically meaningful and which differences are incidental noise, so the comparison judges the right thing.
  • Run both, same inputs. Drive the unit under the original and candidate contexts on identical inputs — replayed history or a live shadow stream — capturing outputs from each.
  • Compare and log divergences. Diff the results modulo the profile; every meaningful mismatch is recorded with the inputs and both outputs so it can be triaged, not just counted.

Tuning parameters

  • Input source — replayed historical inputs, or a live shadow of production traffic. Replay is repeatable and safe; shadowing exercises real, current distributions.
  • Comparison strictness — exact-match on meaningful outputs, or tolerance bands for numeric drift. Tight strictness catches subtle bugs but raises noise; loose hides small real differences.
  • Determinism profile breadth — how much variation is waved off as incidental. Too narrow floods you with false alarms; too broad masks genuine divergence.
  • Coverage vs. cost — every input, or a sampled/targeted subset. Full coverage maximizes confidence; sampling makes long or expensive runs feasible.

When it helps, and when it misleads

Its strength is that it needs no theory of why two contexts might differ — it catches coupling bugs that static reasoning misses, which is why parallel-run and shadow comparison are the gold standard before a risky cutover.[1] The honest failure mode is that it only exercises the inputs it is fed: a divergence that appears only on a rare input the run never saw stays hidden, so a clean result means "equivalent over what we tried," not "equivalent." The other trap is a mis-set determinism profile — too broad and it silently absorbs real differences as "noise," reporting agreement that isn't real. The classic misuse is running the comparison after cutover to reassure stakeholders rather than before to gate it. The discipline is to set the profile honestly, drive realistic and adversarial inputs, and treat a clean run as evidence bounded by its coverage, not proof.

How it implements the components

  • cross_context_equivalence_test — it is the comparison: same unit, two contexts, same inputs, results diffed for meaningful difference.
  • determinism_profile — it makes explicit which output variation is incidental and which is meaningful, so the comparison judges semantics, not noise.
  • observability_and_audit_surface — it records each divergence with inputs and both outputs, producing an inspectable evidence trail for triage.

It detects that two contexts differ but does not record the mapping between them or roll it forward — that is Context Migration Record — and it consumes, rather than produces, the two captured contexts it compares (e.g. a Versioned Context Manifest of each).

  • Instantiates: Definition-Time Context Binding — empirically verifies that a unit's meaning survived being moved from its definition context to a new one.
  • Consumes: the two captured contexts it runs against — for instance a Versioned Context Manifest of each version.
  • Sibling mechanisms: Context Migration Record · Closure Serialization · Versioned Context Manifest · Versioned Configuration Snapshot · Signed Context Manifest · Explicit Environment Object

References

[1] Differential testing runs two implementations (or one unit in two contexts) on the same inputs and treats disagreement as a signal of a bug in one of them. Parallel-run and shadow deployment are the operational forms used to de-risk a cutover before it goes live.