Skip to content

feat!: align CLI and supported-runtime contracts - #22

Merged
NikAcc merged 7 commits into
mainfrom
review/lifecycle-alignment
Aug 8, 2026
Merged

feat!: align CLI and supported-runtime contracts#22
NikAcc merged 7 commits into
mainfrom
review/lifecycle-alignment

Conversation

@NikAcc

@NikAcc NikAcc commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • use the shared fileworks exit-code vocabulary and clean up unreadable HTTP response bodies
  • document the current published release and condense duplicated overview material
  • test and declare every supported Python version

Validation

  • ruff format --check .
  • ruff check .
  • mypy
  • pytest: 132 passed, 1 skipped

NikAcc added 7 commits August 8, 2026 16:24
`download_original` discarded its temporary on a transport drop, a 404 and an
OS error. `httpx.DecodingError` is an `HTTPError` but not a `TransportError`,
so a corrupt or truncated compressed stream escaped every handler and left the
part-written temporary on disk. The published output stayed correct — promotion
is hash-gated — but the litter accumulated across retries.

`_paged_search` also called `int(page_token)` unguarded. `nextPage` is an
ordinal in every spec version this client targets, so a server answering with
an opaque cursor ended a half-finished export with a bare `ValueError` and no
statement of what had happened.

Both paths now have tests.
BREAKING CHANGE: exit codes have changed. `1` was "unexpected error" and is now
PARTIAL; unexpected failures and unwritable output are both FATAL (4); a partial
run no longer exits 5. Scripts that branch on the old numbers must be updated.

The three fileworks CLIs are driven from the same scripts, so a code has to mean
the same thing in each. It did not: `1` was "unexpected error" here and "partial
success" in `unpacksort`, and `3` and `4` disagreed the same way. `ExitCode`
mirrors `unpacksort.models.ExitOutcome`:

    0 SUCCESS · 1 PARTIAL · 2 USAGE · 3 CONFLICT · 4 FATAL · 130 INTERRUPTED

Ctrl-C now exits 130 instead of falling through as an unexpected error. Tests
assert the names rather than the numbers, so the two cannot drift again.

Also converged onto unpacksort's tooling: `ruff select = ["ALL"]` with a
reviewed ignore list, and pinned dev dependencies so a gate cannot change under
you between two runs of the same commit. Two deliberate blind excepts now carry
their reason at the site. AGENTS.md added, as the sibling repos have.
All three READMEs opened the section with "the same codes mean the same thing …
so one script can drive all three", then gave three different meanings for code
3 — an unreachable Immich server, an unavailable Paperless API, an incompatible
destination journal.

`ExitCode.CONFLICT` is deliberately broader than any of them ("something this
tool depends on is unreachable or in a state it cannot proceed from"), so a
script branching on 3 was never actually misled. What the text got wrong was the
level: the tools share a vocabulary of outcome classes, not identical
conditions. Saying so makes each table's narrower wording read as the tool's
instance of the shared class rather than as a contradiction of the sentence
above it.
"Verified behavior" promised exit code 5 for asset-specific integrity failures.
No such code exists: ExitCode runs 0, 1, 2, 3, 4, 130, and cli.py raises PARTIAL
(1) when the report carries errors — which
test_cli_returns_partial_and_publishes_successes already asserts.

The 5 dates from ef1941f and survived the renumbering onto the shared
vocabulary, so the one number a script would have been written against was the
one number that was wrong.

test_release_docs_are_bounded_and_preserve_v003_history asserted the README
contained "exit code `5`", which is why the mistake survived: the guard was
holding the wrong number in place. The assertion now requires the code the tool
actually returns rather than being deleted.
`uv sync --all-extras --dev` was the only one of the three repos' Development
blocks that did not pass --locked, so a contributor following this README could
silently resolve past uv.lock and get a different tree than CI builds.

--all-groups replaces the deprecated --dev spelling; --all-extras is retained
for symmetry with paperless-export, which does declare an extra.
@NikAcc
NikAcc requested a review from Gykonik as a code owner August 8, 2026 14:33
@NikAcc
NikAcc enabled auto-merge (squash) August 8, 2026 14:41
@NikAcc
NikAcc merged commit 6023f58 into main Aug 8, 2026
7 checks passed
@NikAcc
NikAcc deleted the review/lifecycle-alignment branch August 8, 2026 14:49
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