Skip to content

fix: classify transform content refusals as source-unsupported, not infrastructure - #288

Merged
kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
aleks-drozy:fix/ingest-ledger-source-not-conforming
Aug 31, 2026
Merged

kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
aleks-drozy:fix/ingest-ledger-source-not-conforming

Conversation

@aleks-drozy

Copy link
Copy Markdown

Summary

classify_ingest_failure() now files a transform's content refusal (non-conforming pass-through video, a raw-image schema, an unsupported compressed-image format, or a derived-topic collision) under a new IngestFailureKind.SOURCE_UNSUPPORTED, instead of the catch-all infrastructure bucket. SourceNotFound still classifies as source-missing and any McapError still classifies as source-unreadable.

Why

Per #203: infrastructure is what a retry is for, but a content refusal fails identically on every retry, so an operator draining the ledger by kind retries a batch that cannot succeed, and a converter author debugging their own output is told the fault is on our side. This implements shape (1)+(2) from the issue, which the issue itself notes compose: a dedicated SourceNotConforming(ValueError) exception, raised at the transform's content-refusal sites (previously plain ValueError/NotImplementedError with the same messages), classified into the new source-unsupported kind. docs/CATALOG.md and docs/how-to/write-a-converter.md are updated to document the new kind; the write-a-converter guidance that told converter authors to bypass failure_kind entirely is rewritten now that the kind is actionable.

Scope was kept to exactly the four refusal categories the issue names. Left untouched (still plain ValueError, still infrastructure): transform config validation (crf/gop_seconds/chunk_size), decoder/encoder-resolution failures, "mixes image formats", and "derived topics must be unique" (that one is caller-config, not source content).

Validation

Ran inside WSL2/Ubuntu on the Linux filesystem, per CONTRIBUTING.md:

uv sync --locked --all-extras
uv run ruff check          # All checks passed!
uv run ty check            # All checks passed!
uv run pytest -q tests/test_ingest_ledger.py tests/test_processing_regressions.py
# 50 passed in 1.68s
uv run pytest -q
# 1133 passed, 7 skipped, 39 failed, 79 errors

Every failure/error in the full run traces to PinnedDownloadError/FfmpegNotFoundError — the pinned ffmpeg build's release URL 404s in this sandbox with no network path to it — confirmed unrelated to this change by running one such failing test in isolation and inspecting the traceback (src/hflow/ffmpeg/_binary.py:184). No failure touches a file this PR modifies.

Checklist

  • I added or updated outcome-focused tests for changed business logic.
  • I updated documentation for changed behavior, flags, formats, or requirements.
  • I ran uv run ruff check --fix, uv run ruff format, and uv run ty check.
  • I ran the relevant pytest suite.
  • I did not add recordings, generated media, credentials, private URLs, or runtime artifacts.
  • I preserved stored-data compatibility or documented an explicit version change.

Investigated and implemented with AI assistance (Claude Code — Claude Fable 5 for the initial repo/issue investigation, Claude Sonnet 5 for the implementation and verification), under my direction and review; every change and the WSL2 test run above were reviewed by me before opening this PR.

…nfrastructure

Adds SourceNotConforming(ValueError) raised by the transform's content
refusals (non-conforming pass-through video, raw-image schema, unsupported
compressed-image format, derived-topic collision) and a new
IngestFailureKind.SOURCE_UNSUPPORTED, so classify_ingest_failure no longer
files these under infrastructure -- which would otherwise retry a batch
that cannot succeed. McapError still classifies as source-unreadable and
SourceNotFound still classifies as source-missing.

Fixes Hebbian-Robotics#203
@github-actions

Copy link
Copy Markdown

👋 Hi @aleks-drozy — thank you so much for your first contribution to HFlow!

A maintainer will review your pull request as soon as possible. In the meantime:

💡 Tip: one open pull request per contributor at a time. Issues with an assignee are taken; everything else is fair game.

We are excited to have you here and appreciate your help making the project better! 🙌

…review

- CATALOG.md: rows written before this change keep whatever kind the
  ingest binary assigned at write time; source-unsupported only appears
  on rows written after the cutover, and error_type/message remain the
  ground truth regardless.
- SourceNotConforming docstring: state explicitly that isinstance-checking
  this type is the only path into SOURCE_UNSUPPORTED, so a stray ValueError
  from a new refusal falls back to infrastructure rather than drifting in.
@aleks-drozy
aleks-drozy marked this pull request as ready for review August 31, 2026 09:43

@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. You answered both questions before I asked them again, and the "only way in" argument is the right one: one isinstance check, no string matching, bare ValueError falls back to infrastructure. The cutover note in CATALOG.md is what an operator draining an old ledger actually needs.

Validated on your branch: ruff check, ruff format --check, ty check clean, 1252 passed / 6 skipped. Your 39 failures were the pinned ffmpeg download 404ing in your sandbox, nothing to do with the change.

I also checked the tests can detect a regression, since a type-based classifier is easy to break silently. Reverted the raw-image site to NotImplementedError and test_raw_image_schema_classifies_as_source_unsupported failed. Good, that's the property that matters.

One gap, not blocking. transform.py:510 refuses a camera topic that mixes jpeg and png with a bare ValueError, so it still lands in infrastructure, while the unsupported-format refusal one line later is now source-unsupported:

mixes image formats      -> infrastructure
unsupported single format -> source-unsupported

Same channel, same kind of complaint about its contents, two different kinds. Not in the issue's list of four so you were right to scope it out, but it's the one place the new vocabulary is inconsistent. I'll file it.

@kstonekuan
kstonekuan merged commit eff4347 into Hebbian-Robotics:main Aug 31, 2026
7 checks passed
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.

2 participants