Skip to content

feat(nzb-postproc): publish typed terminal failure codes - #150

Closed
thedancingdeveloper wants to merge 2 commits into
feat/idempotent-queue-admissionfrom
feat/typed-failure-codes
Closed

thedancingdeveloper wants to merge 2 commits into
feat/idempotent-queue-admissionfrom
feat/typed-failure-codes

Conversation

@thedancingdeveloper

Copy link
Copy Markdown
Collaborator

Summary

Ports typed terminal failure codes from MrVampy/rustnzb (e0870d8): persist an engine-owned failure code with each terminal history row instead of making consumers parse human-readable diagnostic prose, and make the durable terminal row authoritative over a job's lingering queue view.

⚠️ Stacked on #149 (idempotent queue admission). This PR's base is feat/idempotent-queue-admission, because it extends QueueAdmissionState::History (added there) and its DB migration follows that one's. Review/merge #149 first; the diff here is the failure-code change alone.

What it adds

  • JobFailureCode enum (nzb-core) with stable snake_case wire values (Display + FromStr): articles_unavailable, repair_failed, archive_password_required, archive_invalid, storage_unavailable, download_failed.
  • Post-processing assigns codes from causal stage state: run_pipeline threads a failure_code (articles-unavailable, repair-failed) and run_extract_stage returns a typed code — including a typed ArchivePasswordRequired error that replaces the anyhow::bail!("archive is password-protected") prose consumers previously had to string-match (this prose-matching debt was still live on main's unpack.rs).
  • Persistence: HistoryEntry.failure_code (DB migration v12 adds the column and backfills existing failed rows to download_failed); history_insert enforces the invariant that a Failed row carries exactly one code and a non-Failed row carries none.
  • Authoritative terminal row: queue_manager tracks the code in memory across pause/resume/clear and writes it on terminal transition; queue_admission_observe now consults the history row before the queue view and reports the code via QueueAdmissionState::History. Exposed on the history API response.

Port notes

  • DB migration renumbered v10 → v12 (main's v10/v11 are damage_ledger / queue_admissions).
  • History SQL column indices adjusted for main's retry_data column (fork didn't have it); new failure_code column appended to SELECTs to avoid index churn.
  • Pipeline changes reapplied onto main's run_pipeline_with_cleanup structure (fork's was run_pipeline_with_resources).
  • QueueAdmissionState::History extends the variant added in feat(nzb-web): idempotent queue admission via Idempotency-Key #149.
  • Fork flake/workspace-version bumps excluded.

Tests

  • JobFailureCode wire-value roundtrip; typed ArchivePasswordRequired survives as a downcastable error.
  • run_extract_stage / run_pipeline code assignment (ArchiveInvalid, ArticlesUnavailable).
  • DB migration backfill (failed→download_failed, completed untouched); history invariant (Failed ⇔ code present); terminal-history authority over the queue view.
  • queue_manager terminal-code persistence (ArchiveInvalid for raw-artifacts, DownloadFailed fallback).
  • Full nzb-core / nzb-postproc / nzb-web / rustnzb suites pass; cargo clippy --workspace --all-targets + cargo fmt --check clean.

Original author: @MrVampy (credited via Co-Authored-By; ported by hand due to migration collisions and divergent pipeline/history code on main).

🤖 Generated with Claude Code

thedancingdeveloper and others added 2 commits September 27, 2026 10:08
Persist an engine-owned failure code with each terminal history row instead
of requiring consumers to parse human-readable diagnostic prose, and make the
durable terminal row authoritative over a job's lingering queue view.

- New `JobFailureCode` enum (`nzb-core`) with stable snake_case wire values
  (Display + FromStr): articles_unavailable, repair_failed,
  archive_password_required, archive_invalid, storage_unavailable,
  download_failed.
- Post-processing assigns codes from causal stage state: `run_pipeline`
  threads a `failure_code` (articles-unavailable, repair-failed) and
  `run_extract_stage` returns a typed code, including a typed
  `ArchivePasswordRequired` error (replacing the `anyhow::bail!("archive is
  password-protected")` prose that consumers had to string-match).
- Persist `HistoryEntry.failure_code` (DB migration v12 adds the column and
  backfills existing failed rows to `download_failed`); `history_insert`
  enforces the invariant that a Failed row carries exactly one code and a
  non-Failed row carries none.
- `queue_manager` tracks the code in memory across pause/resume/clear and
  writes it on terminal transition; `queue_admission_observe` now consults the
  authoritative history row before the queue view, and reports the code via
  `QueueAdmissionState::History`. Exposed on the history API response.

Ported from MrVampy/rustnzb (e0870d8), adapted to main: DB migration
renumbered v10 -> v12 (main's v10/v11 are damage_ledger / queue_admissions);
history SQL column indices adjusted for main's `retry_data` column; the
pipeline changes reapplied onto main's `run_pipeline_with_cleanup` structure;
and `QueueAdmissionState::History` extends the variant added in the idempotent
queue-admission change this branch is stacked on. Fork flake/workspace bumps
excluded.

Tests: JobFailureCode wire-value roundtrip; typed ArchivePasswordRequired
survives as a downcastable error; run_extract_stage / run_pipeline code
assignment; DB migration backfill, history invariant, and terminal-history
authority over the queue view; queue_manager terminal-code persistence
(ArchiveInvalid / DownloadFailed). Full nzb-core / nzb-postproc / nzb-web /
rustnzb suites pass; clippy/fmt clean.

Co-Authored-By: MrVampy <4302946+MrVampy@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The desktop app depends on `rustnzb`/`nzb-web` by path; the new dependencies
added by this change must be reflected in desktop/src-tauri/Cargo.lock so the
`desktop` CI job (built with --locked) accepts it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

1 participant