Keep each differential capture to its own event type

Merged
VA
2d ago
+27-9

Cause

Breaking commit: #821 be953d15c (Batch filesystem staging and reuse request encoding).

#821 added ("decode document", n) / ("decode index", n) trace points ((&'static str, u64)) to the shared JSON decoder executor::json::decode::value, under cfg(all(test, feature = "filesystem")). Every canonical encoding now runs through that decoder.

The effect-cut workflow tests capture effect_root_tests::Observation while running those encodings. The test-support trace accepted exactly one event type and panicked on any other. All three tests therefore panicked with trace event type at test-support/src/differential.rs:16:

  • store::effect_heads::tests::cut_workflow::old_and_new_cut_workflows_preserve_results_errors_and_storage_order
  • store::effect_heads::tests::cut_workflow::workflow_oracle_detects_lost_authentication_error
  • store::effect_heads::tests::cut_workflow::workflow_oracle_detects_storage_reordering

Fix (structural)

Observation points of different types are independent streams over shared code paths. differential::record now records into the active capture only when the event type matches. Otherwise it does nothing, exactly as when no capture is active.

A capture subscribes to one stream by type, so adding instrumentation to any shared primitive can no longer break unrelated captures. Ordering, nesting rejection, panic reset and destructor recording are unchanged.

Tests

  • New: captures_keep_only_their_event_type. Interleaves u8, u16 and (&str, u64) records and asserts that each capture keeps exactly its own type, in order, in both arms.
  • The existing test drops only its wrong-type-panics case and is renamed capture_resets_after_panic_and_rejects_nesting. No repo test relied on the panic (grep "trace event type" finds none).

Note for open PRs

#915 and #839 add differential::reserve<T> with the strict downcast. They merge independently. If they land after this PR, reserve should return None on a foreign type, for the same reason.

Why the merge gate missed it

PR CI (.github/workflows/static.yml and the others) runs no harness Rust test target.

Verification (bb1)

  • cargo test --locked -p acyclic-test-support --lib differential: 14 passed.
  • cargo test --locked -p acyclic-harness --all-features --lib -- store::effect_heads::tests::cut_workflow executor::json effect_root_tests: all cut_workflow and executor::json tests pass. The only failure is effect_root_tests::exact_work_oracle_detects_restored_decimal_serialization, a separate #821 cause fixed by #995.
  • Bisect: these tests pass at d6e6422ca (parent of #821) and fail at be953d15c.

Guarantee

Class: I, verification infrastructure coupled to unrelated instrumentation. Structural fix: a capture subscribes to one event type. record of any other type is a no-op, identical to recording with no active capture, so new trace points in shared code can't break unrelated captures.
Proof: captures_keep_only_their_event_type interleaves three event types and asserts that each capture keeps exactly its own events, in order. Negative control: restoring the strict downcast panics on the first foreign event. Nesting rejection and panic reset stay covered by capture_resets_after_panic_and_rejects_nesting.
Entry points: differential::record is the only recording entry on main. differential::reserve (#915/#839) is not on main yet; it must take the same rule, and that has been flagged on those PRs.
Gaps: none on main.

🤖 Generated with Claude Code