Skip to content

fix(checks): validate canonical CRCs once at lane entry with memoization - #502

Merged
kstonekuan merged 1 commit into
Hebbian-Robotics:mainfrom
Sagar-024:fix/canonical-crc-guard
Sep 11, 2026
Merged

kstonekuan merged 1 commit into
Hebbian-Robotics:mainfrom
Sagar-024:fix/canonical-crc-guard

Conversation

@Sagar-024

@Sagar-024 Sagar-024 commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Closes #474.

Bug: post-sync reads open canonicals with CRC validation off, so a canonical that decayed on disk gets re-measured and re-certified. Checks stamp fresh measured rows over bytes the file's own integrity stamp refuses.

Fix: a front-door guard, not a reader flag flip. verify_canonical_integrity(path) in reader.py does one strict read with validate_crcs=True and catches CRCValidationError by its precise type. App.process calls it once, before any step runs, in every lane that spends work on canonical bytes: META, relabel, and the production re-check (process_stage_batch). On mismatch the episode is refused with ONE diagnosis: TestReport.refusal_reason plus one framework-owned check_runs row (error = 'canonical-crc-mismatch', critical, curation views read it as unverified). No check runs, so zero tracebacks is structural. hflow ingest exits 1, the runtime-finding family.

Decisions worth your eye:

  • Issue scope note superseded: the front-door shape needs one report field plus one catalog row through existing DDL. No migration. Reader default and snapshot verifier untouched.
  • Cache carries a size+mtime witness. The issue's own repro broke the path-only cache: corruption between two process calls was served the stale healthy verdict. Stated limit: rot preserving size and mtime slips past within one process; a fresh process always re-validates.
  • Dedupe for free: the run fingerprint excludes duration, so refusal replays append nothing. One record per episode across all lanes.
  • A run with no step work (sync only, check-less meta, enrichment-less relabel) pays no strict read. Pinned by test.
  • Mutation: guard disabled, the pinning test reds as re-certification, because CRC-only rot still decompresses. The N-traceback shape is the zstd-refusing species, separate follow-up.

Validation: ruff, format, ty clean on the three changed files. Full suite 1871 passed, 6 skipped on the rebased commit. Mutation red then green. ASCII swept.

Every post-sync read ran with chunk CRC validation off, so a canonical
that decayed on disk after sync was re-certified by the lanes that
exist to judge it: fresh measured findings stamped over bytes the
file's own integrity stamp refuses (Hebbian-Robotics#474).

App.process now verifies the canonical's chunk CRCs once per run at
the front door every consuming lane shares, before any step runs. A
file that fails its own stamp is refused with the named reason
canonical-crc-mismatch: one report field, one framework-owned
check_runs row (critical, so the curation views read the episode as
unverified), and no check, enrichment, or media step. The refusal
covers the meta lane, the relabel lane, and the production meta task
an online re-check flows through; a run with no step work to do pays
no strict read.

The verdict is memoized on the App keyed by path plus a size+mtime
witness, so repeated stage runs and retried batches in one process pay
for one strict read, and bytes that change under the cache are
re-validated rather than trusted. Replays of the same refusal dedupe
through the existing run fingerprint, keeping one record per episode.

Episode._reader's default and the snapshot verifier are untouched.

@kstonekuan kstonekuan left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, merging.

assert refused.checks == [] makes this the fix I asked for: zero tracebacks because nothing ran, not because something caught them. And asserting episodes.status == 'unverified' from the catalog rather than trusting critical=True.

I owe you a measurement, since the front-door shape was my call and costs a second read: 0.5% of a process call at 0.4 MB, 0.2% at 6.3 MB. It gets cheaper relative to ffmpeg as episodes grow.

Filed the zstd-refusing half as #506.

@kstonekuan
kstonekuan merged commit 9f0a0ba into Hebbian-Robotics:main Sep 11, 2026
8 checks passed
@Sagar-024

Copy link
Copy Markdown
Contributor Author

On the corpus invitation: Discussion #503 (the field report I just posted) is exactly that work. 128 real episodes, 3 factories, 6 stress passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check lanes re-certify a decayed canonical episode: post-sync reads never validate chunk CRCs

2 participants