Skip to content

fix(snapshot): enforce the receipt inventory content_id at verify time - #483

Merged
kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
Sagar-024:fix/473-inventory-content-id
Sep 10, 2026
Merged

kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
Sagar-024:fix/473-inventory-content-id

Conversation

@Sagar-024

Copy link
Copy Markdown
Contributor

fix(snapshot): enforce the receipt inventory content_id at verify time

Closes #473.

What changed

verify_dataset_snapshot now enforces the integrity.content_id guarantee documented at docs/how-to/export-dataset-snapshot.md:77. After the receipt is parsed and before the per-file loop, the verifier recomputes the inventory hash from the receipt's own table and asset entries (the identical normalization the exporter uses at snapshot.py:194, via the shared _inventory_content_id helper) and compares it against the stored value.

The exit-2 decision

A content_id that disagrees with the recomputed inventory means the marker is internally inconsistent, which points at tampering or a truncated write, not at damaged bytes. Damaged bytes are what findings and exit 1 are for; an inconsistent receipt is unreadable input. The gate therefore raises through the same ValueError the unparsable-marker path uses (snapshot.py:862), so the CLI maps it to exit 2 with no new exit code. A DAMAGED finding was considered and rejected: findings are evidence about delivered files, and the failure here is in the receipt itself, which per-file findings cannot express without inventing a new reason code for a non-file.

The same handling covers a missing or malformed content_id on a marker that carries an integrity block: one gate, one behavior, one exit code. Pre-#401 markers with no integrity key keep the existing no-receipt UNVERIFIABLE path untouched.

Test shape, and why

  • Pinning test: a receipt entry is removed from the marker and its file is deleted, then verify must raise (and the CLI must map to exit 2). This is the case only the stored content_id catches: the surviving entries agree with each other, so every per-file check passes; the comparison against the hash of the original set is the only witness.
  • Negative control (as asked on the issue): the file is deleted but the receipt entry is kept. This exercises the ordinary missing path and passes without the fix; under the mutation run it stayed green while the pinning test went red, proving the two tests pin different behaviors.

Related issues, separate PRs

Gates

15 passed in tests/test_snapshot_verify.py (13 existing + pinning test + negative control), 31 passed across the verify and dataset-snapshot suites, ruff check / ruff format / ty clean, zero non-ASCII and zero em dashes in changed files. Mutation run: inventory comparison neutered, pinning test red, negative control green, restored, all green.

…cs#473 gate

The gate is two refusals. The mismatch branch is held by the pinning test;
deleting the missing-or-malformed branch left the whole suite green. Four
shapes: absent, empty, non-string, wrong type, each through the API and the
CLI exit code.

@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.

Pushed one commit: the missing-content_id branch had no case, so four shapes through the API and the CLI exit code.

integrity and content_id arrived in the same commit (#401), so no snapshot we wrote carries one without the other. That is what makes it unreadable input rather than damage.

@kstonekuan
kstonekuan merged commit 1890dfb into Hebbian-Robotics:main Sep 10, 2026
6 checks passed
@Sagar-024

Copy link
Copy Markdown
Contributor Author

Thanks @kstonekuan .

#472 is next then i will see what i can do on (Egocentric-10K or Egocentric-100K on Hugging Face)

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.

verify snapshot never checks integrity.content_id, so the documented deleted-member guarantee is unenforced

2 participants