Version or commit
Current main
Environment
Ubuntu 24.04 (WSL2), Python 3.12, x86_64, file:// obstore root
Minimal reproduction
Using a file:// bucket-backed storage root with independent worker mirrors:
- Worker 1 runs the full profile on clean footage (v1). Scratch is populated, measurements pass (black_frame_pct=0.00).
- The canonical object in the remote store is overwritten with altered camera payload (v2, bumped pipeline_version, true black_frame_pct=50.00). Worker 2 processes this and records the true v2 measurements.
- Worker 1 runs a sync-omitted profile (e.g., META + MEDIA only). Its etag-verified fetch downloads the new v2 canonical, but the scratch directory is not invalidated.
Expected behavior
Worker 1 fetches the new v2 canonical, invalidates or bypasses the stale scratch cache, measures the v2 pixels, and records the true v2 measurements (50.00) under the new episode_id.
Actual behavior
Worker 1 fetches the v2 canonical but keeps the v1 scratch (identical sha and mtime_ns). It measures the v1 pixels and records v1 measurements (0.00) under the v2 episode_id.
Because the stale run's measurement values differ from the truth, its _run_fingerprint is unique, so the catalog appends it as a new row. measurements_latest ranks by recorded_at, so the stale, newer measurement wins the wide view.
Worse, the poisoning is self-perpetuating: if a fresh worker (Worker 3) later tries to repair this by re-measuring, it produces the true values, which match the original v2 fingerprint. The catalog dedupes the repair (written=False), and it fails to overwrite the stale latest view. The system actively protects the lie.
Additional context
Root cause: The only scratch invalidation is shutil.rmtree(scratch_dir) at app.py:2391, guarded by if Stage.SYNC in enabled_stages (app.py:2347). The sync-omitted route (app.py:2395-2412) fetches the new canonical but never touches the scratch directory. Episode.video() relies on a bare output.exists() check (episode.py:551-554), serving the stale remux.
Why it matters: Curation cuts and gates accept or reject episodes using footage that isn't the footage. The downstream training corpus inherits mislabeled quality verdicts that survive the exact byte-change the content-hash was supposed to catch. The append-only deduplication logic (_run_fingerprint) weaponizes the repair path, making the poison durable.
Fix direction: Make the scratch content-bound: key scratch_dir on content_episode_id(canonical_path), or drop a digest marker inside the scratch dir and remux whenever it disagrees with the fetched canonical. Additionally, the measurement deduplication logic needs a retraction or force-overwrite path for later-arriving stale appends that lose to the truth.
Definition of done:
- A sync-omitted run on a worker with stale scratch correctly measures the newly fetched canonical bytes, not the old ones.
_run_fingerprint deduplication does not allow a stale measurement to permanently shadow a true measurement in the latest views.
- Mutation proof: tamper the canonical payload without touching scratch, and the recorded measurements must change.
- Existing app/storage/catalog suites stay green.
Version or commit
Current main
Environment
Ubuntu 24.04 (WSL2), Python 3.12, x86_64, file:// obstore root
Minimal reproduction
Using a file:// bucket-backed storage root with independent worker mirrors:
Expected behavior
Worker 1 fetches the new v2 canonical, invalidates or bypasses the stale scratch cache, measures the v2 pixels, and records the true v2 measurements (50.00) under the new episode_id.
Actual behavior
Worker 1 fetches the v2 canonical but keeps the v1 scratch (identical sha and mtime_ns). It measures the v1 pixels and records v1 measurements (0.00) under the v2 episode_id.
Because the stale run's measurement values differ from the truth, its
_run_fingerprintis unique, so the catalog appends it as a new row.measurements_latestranks byrecorded_at, so the stale, newer measurement wins the wide view.Worse, the poisoning is self-perpetuating: if a fresh worker (Worker 3) later tries to repair this by re-measuring, it produces the true values, which match the original v2 fingerprint. The catalog dedupes the repair (
written=False), and it fails to overwrite the stale latest view. The system actively protects the lie.Additional context
Root cause: The only scratch invalidation is
shutil.rmtree(scratch_dir)atapp.py:2391, guarded byif Stage.SYNC in enabled_stages(app.py:2347). The sync-omitted route (app.py:2395-2412) fetches the new canonical but never touches the scratch directory.Episode.video()relies on a bareoutput.exists()check (episode.py:551-554), serving the stale remux.Why it matters: Curation cuts and gates accept or reject episodes using footage that isn't the footage. The downstream training corpus inherits mislabeled quality verdicts that survive the exact byte-change the content-hash was supposed to catch. The append-only deduplication logic (
_run_fingerprint) weaponizes the repair path, making the poison durable.Fix direction: Make the scratch content-bound: key
scratch_dironcontent_episode_id(canonical_path), or drop a digest marker inside the scratch dir and remux whenever it disagrees with the fetched canonical. Additionally, the measurement deduplication logic needs a retraction or force-overwrite path for later-arriving stale appends that lose to the truth.Definition of done:
_run_fingerprintdeduplication does not allow a stale measurement to permanently shadow a true measurement in thelatestviews.