diff --git a/crates/matroska/README.md b/crates/matroska/README.md
--- a/crates/matroska/README.md
+++ b/crates/matroska/README.md
@@ -1,3 +1,19 @@
 # filmcraft-matroska
 
 A re-export of `craft_media::matroska`. Documentation: https://github.com/storytold/craft-libs/blob/main/crates/media/README.md
+
+## Oracle coverage
+
+`tests/oracle.rs` requires actual Xiph/EBML lacing on AAC and FLAC fixtures, and
+FixedOrEbml lacing on PCM. The Vorbis remux regression supplies a deliberately
+incompatible declared duration and requires the writer to keep every packet's
+original timestamp, payload, keyframe flag and codec headers without lacing.
+This avoids assuming that every encoder's `DefaultDuration` permits lacing.
+All remux cases are checked independently with ffprobe; assertions are not skipped
+when requested lacing cannot preserve timestamps.
+The encoded Block flags must match the requested mode: Xiph, EBML, or either
+fixed-size or EBML for FixedOrEbml; unlaced fallback blocks remain valid.
+
+Laced frames share a block timestamp and must be contiguous; see
+[RFC 9559 section 10.3.5](https://www.rfc-editor.org/rfc/rfc9559.html#section-10.3.5).
+The muxer implementation and media dependencies are unchanged by this oracle repair.
diff --git a/crates/matroska/tests/oracle.rs b/crates/matroska/tests/oracle.rs
--- a/crates/matroska/tests/oracle.rs
+++ b/crates/matroska/tests/oracle.rs
@@ -266,22 +266,104 @@ fn mjpeg_ac3() {
     assert!(f.tags.iter().flat_map(|t| &t.simple).any(|s| s.name == "ENCODER"));
 }
 
+/// Compare AV1 packets and track metadata with ffprobe, including the av1C configuration record.
 #[test]
 fn av1() {
     let Some((_, f, _)) = run("av1.mkv") else { return };
     assert!(matches!(&f.tracks[0].codec, Codec::Av1 { av1c } if av1c.first() == Some(&0x81)));
 }
 
-/// Re-mux a fixture with our writer using each lacing mode; ffprobe must read the result identically
-/// to our demuxer (and to the source packets).
+/// Read the owning Block's lacing bits, not the sample's index within its lace (RFC 9559 §10.3).
+fn block_lacing_flags(bytes: &[u8], sample: &Sample) -> u8 {
+    use element_ids::{BLOCK, BLOCK_GROUP, SIMPLE_BLOCK};
+
+    let offset = usize::try_from(sample.block_offset).unwrap();
+    let element = ebml::Children::new(&bytes[offset..]).next().expect("sample's block element");
+    assert!(!element.truncated);
+    let data = match element.id {
+        SIMPLE_BLOCK => element.data,
+        BLOCK_GROUP => ebml::Children::new(element.data).find(|e| e.id == BLOCK).expect("Block in BlockGroup").data,
+        id => panic!("unexpected sample element {id:#x}"),
+    };
+    let (_, track_number_len, _) = ebml::read_vint(data).expect("block's track number");
+    // Track-number VINT, signed 16-bit relative timestamp, then the flags byte.
+    data[track_number_len + 2] & 0x06
+}
+
+/// Remux without changing packet timestamps or payloads, then check the output independently
+/// with ffprobe. Requesting a lacing mode does not guarantee a lace: the supplied duration must
+/// also reproduce the packet timestamps (RFC 9559 section 10.3.5).
+fn remux_and_compare(ffprobe: &Path, file: &MkvFile, packets: &[Packet], mode: LacingMode, label: &str, expect_laced: bool) {
+    let specs: Vec<TrackSpec> = file
+        .tracks
+        .iter()
+        .enumerate()
+        .map(|(ti, t)| {
+            let mut s = TrackSpec::new(t.kind, t.codec_id.clone());
+            s.codec_private = t.codec_private.clone();
+            // Preserve a declared duration; only supply one when the input has none.
+            s.default_duration_ns = t.default_duration_ns.or_else(|| {
+                let pts: Vec<i64> = packets.iter().filter(|p| p.track == ti).map(|p| p.pts_ns).collect();
+                (t.kind == TrackKind::Audio && pts.len() > 1).then(|| ((pts[pts.len() - 1] - pts[0]) / (pts.len() as i64 - 1)) as u64)
+            });
+            s.video_size = t.video.as_ref().map(|v| (v.pixel_width, v.pixel_height));
+            s.audio = t.audio.as_ref().map(|a| (a.sampling_frequency, a.channels, a.bit_depth));
+            s.codec_delay_ns = t.codec_delay_ns;
+            s
+        })
+        .collect();
+    let opts = MuxOptions { lacing: mode, ..Default::default() };
+    let mut w = MkvWriter::new(std::io::Cursor::new(Vec::new()), specs, opts).unwrap();
+    for p in packets {
+        w.write_frame(p.track, p.pts_ns, p.keyframe, &p.data, None).unwrap();
+    }
+    let out = w.finish().unwrap().into_inner();
+    let laced = {
+        let f = open(&out[..]).unwrap();
+        // The first sample visits each block once, including blocks that did not lace.
+        for sample in f.tracks.iter().flat_map(|t| &t.samples).filter(|s| s.lace == 0) {
+            let flags = block_lacing_flags(&out, sample);
+            if flags != 0 {
+                assert!(expect_laced, "{label} {mode:?}: unexpected laced block at {}", sample.block_offset);
+                let matches_mode = match mode {
+                    LacingMode::None => false,
+                    LacingMode::Xiph => flags == 0x02,
+                    LacingMode::Ebml => flags == 0x06,
+                    LacingMode::FixedOrEbml => matches!(flags, 0x04 | 0x06),
+                };
+                assert!(matches_mode, "{label} {mode:?}: wrong block lacing flags {flags:#04x} at {}", sample.block_offset);
+            }
+        }
+        f.tracks.iter().any(|t| t.samples.iter().any(|s| s.lace > 0))
+    };
+    assert_eq!(laced, expect_laced, "{label} {mode:?}: unexpected lacing");
+    let outp = fixture_dir().join(format!("remux_{mode:?}_{label}"));
+    std::fs::write(&outp, &out).unwrap();
+    let (got_file, got) = compare(ffprobe, &outp);
+    assert_eq!(got_file.tracks.len(), file.tracks.len());
+    for (a, b) in got_file.tracks.iter().zip(&file.tracks) {
+        assert_eq!((&a.codec_id, &a.codec_private, a.codec_delay_ns), (&b.codec_id, &b.codec_private, b.codec_delay_ns), "{label} {mode:?}: codec headers");
+    }
+    assert_eq!(got.len(), packets.len());
+    for (a, b) in got.iter().zip(packets) {
+        assert_eq!(a.data, b.data, "{label} {mode:?}: payload");
+        assert_eq!((a.track, a.pts, a.keyframe), (b.track, b.pts, b.keyframe), "{label} {mode:?}: timestamp/keyframe");
+        if !expect_laced {
+            assert_eq!(a.pts_ns, b.pts_ns, "{label} {mode:?}: explicit timestamp precision");
+        }
+    }
+    std::fs::remove_file(&outp).unwrap();
+}
+
+/// Exercise each lacing mode on packets that can actually share a lace. Vorbis's encoder-
+/// supplied DefaultDuration need not match its packet spacing; that fallback is checked below.
 #[test]
 fn laced_remux() {
     let Some((ffmpeg, ffprobe)) = tools() else { return };
     for (src, modes) in [
-        ("vorbis.mkv", &[LacingMode::Xiph, LacingMode::Ebml][..]),
         ("h264_aac.mkv", &[LacingMode::Ebml, LacingMode::Xiph][..]),
         ("prores_pcm.mkv", &[LacingMode::FixedOrEbml][..]),
-        ("flac.mkv", &[LacingMode::Ebml][..]),
+        ("flac.mkv", &[LacingMode::Ebml, LacingMode::Xiph][..]),
     ] {
         let Some(path) = fixture(&ffmpeg, src) else { continue };
         let bytes = std::fs::read(&path).unwrap();
@@ -289,48 +371,33 @@ fn laced_remux() {
         let file = d.file().clone();
         let packets: Vec<Packet> = d.collect::<Result<_>>().unwrap();
         for &mode in modes {
-            let specs: Vec<TrackSpec> = file
-                .tracks
-                .iter()
-                .enumerate()
-                .map(|(ti, t)| {
-                    let mut s = TrackSpec::new(t.kind, t.codec_id.clone());
-                    s.codec_private = t.codec_private.clone();
-                    // audio without a DefaultDuration gets its mean packet spacing, so it can lace
-                    s.default_duration_ns = t.default_duration_ns.or_else(|| {
-                        let pts: Vec<i64> = packets.iter().filter(|p| p.track == ti).map(|p| p.pts_ns).collect();
-                        (t.kind == TrackKind::Audio && pts.len() > 1).then(|| ((pts[pts.len() - 1] - pts[0]) / (pts.len() as i64 - 1)) as u64)
-                    });
-                    s.video_size = t.video.as_ref().map(|v| (v.pixel_width, v.pixel_height));
-                    s.audio = t.audio.as_ref().map(|a| (a.sampling_frequency, a.channels, a.bit_depth));
-                    s.codec_delay_ns = t.codec_delay_ns;
-                    s
-                })
-                .collect();
-            let opts = MuxOptions { lacing: mode, ..Default::default() };
-            let mut w = MkvWriter::new(std::io::Cursor::new(Vec::new()), specs, opts).unwrap();
-            for p in &packets {
-                w.write_frame(p.track, p.pts_ns, p.keyframe, &p.data, None).unwrap();
-            }
-            let out = w.finish().unwrap().into_inner();
-            let laced = {
-                let f = open(&out[..]).unwrap();
-                f.tracks.iter().any(|t| t.samples.iter().any(|s| s.lace > 0))
-            };
-            assert!(laced, "{src} {mode:?}: nothing was laced");
-            let outp = fixture_dir().join(format!("remux_{mode:?}_{src}"));
-            std::fs::write(&outp, &out).unwrap();
-            let (_, got) = compare(&ffprobe, &outp);
-            assert_eq!(got.len(), packets.len());
-            for (a, b) in got.iter().zip(&packets) {
-                assert_eq!(a.data, b.data, "{src} {mode:?}: payload");
-                assert_eq!((a.track, a.pts), (b.track, b.pts), "{src} {mode:?}: timestamp");
-            }
-            std::fs::remove_file(&outp).unwrap();
+            remux_and_compare(&ffprobe, &file, &packets, mode, src, true);
         }
     }
 }
 
+/// A mode request must not force lacing when DefaultDuration cannot describe the existing PTS.
+/// In particular, some FFmpeg Vorbis fixtures declare a much shorter duration than their packet
+/// spacing. Make that mismatch deterministic without changing any source packet's timestamp.
+#[test]
+fn vorbis_lacing_preserves_pts_with_mismatched_duration() {
+    let Some((ffmpeg, ffprobe)) = tools() else { return };
+    let Some(path) = fixture(&ffmpeg, "vorbis.mkv") else { return };
+    let bytes = std::fs::read(path).unwrap();
+    let d = Demuxer::from_slice(&bytes).unwrap();
+    let mut file = d.file().clone();
+    let packets: Vec<Packet> = d.collect::<Result<_>>().unwrap();
+    assert_eq!(file.tracks.len(), 1);
+    assert_eq!(file.tracks[0].kind, TrackKind::Audio);
+    assert!(packets.len() > 1 && packets.windows(2).all(|w| w[1].pts > w[0].pts), "fixture must have distinct increasing timestamps");
+    // One nanosecond cannot advance a frame by even one of this file's timestamp ticks.
+    // This remains a mismatch regardless of the encoder's own DefaultDuration choice.
+    file.tracks[0].default_duration_ns = Some(1);
+    for mode in [LacingMode::Xiph, LacingMode::Ebml] {
+        remux_and_compare(&ffprobe, &file, &packets, mode, "mismatched_vorbis.mkv", false);
+    }
+}
+
 /// Damaged file: overwrite bytes in the middle of the clusters; the demuxer must not fail and must
 /// resync on a later cluster.
 #[test]
