Two ways a cached entry could be wrong.
The key was built by string concatenation in parallel with the URL, so
`search_movies("dune&year=2024", None)` and `search_movies("dune",
Some(2024))` produced the same key for two different requests. Both key
and URL now come from one `Url`, percent-encoded identically, and the
API key is appended at send time so it cannot reach a key or a log.
A successful response was cached before it was decoded, so one malformed
body returned `Error::Decode` from the cache for the whole time-to-live.
Decoding now happens first and only what decoded is stored.
Search and detail lookup, carrying the two fields the policy engine
needs and that are easy to lose at the edge.
`original_language` arrives as bare ISO 639-1, which has one code for
both Portuguese variants, so the dub rule in §5.2 cannot be expressed
against it alone. `Movie` therefore also carries `origin_countries`,
from `origin_country` where TMDB has it and `production_countries`
otherwise, which is what separates a Brazilian film from a Portuguese
one.
Digital release dates come back appended to the detail call rather than
costing a second round trip, reduced to the earliest type-4 entry across
every country: a release existing in one region is a release that exists
on the indexers. `Movie::is_digitally_released` puts the §6.2 gate in
one tested place.
Responses are cached in process for a day, matching the metadata refresh
tick. Failures are never cached, so an outage cannot pin a title into a
bad state until tomorrow.
Tests are wiremock-backed, per §12.
nextest fails a zero-test run, which is the behaviour worth keeping — passing
--no-tests=pass would let a broken filter go green once real tests exist.
Placeholder test instead, same as arr-core.
The first e2e run proved the Transmission service container pulls and starts;
it failed only at the empty test run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Gate runs fmt, clippy with warnings denied, cargo-machete, and nextest.
nextest comes from a prebuilt binary rather than `cargo install` to keep the
run short. Cache covers ~/.cargo and target/, keyed on Cargo.lock plus the
toolchain file. Nothing builds --release.
e2e is a separate workflow, main plus PRs touching the crates whose seams it
covers. cargo-deny is weekly, off the push path.
Closes#2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eleven empty crates per DESIGN.md §11, versions and lints pinned once in the
root manifest, Justfile carrying the §12 gate, nix dev shell.
The frontend steps in `just ci` no-op until web/ exists. arr-core carries one
placeholder test because `cargo nextest` fails a zero-test run, and relaxing
that would let a broken filter go green later.
Closes#1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Relaxes the concurrency rule to per-crate edits, records that the PR daemon
spawns land and review-pr sessions automatically, and makes the driver the
only thing that merges — skipping a stalled reviewer rather than blocking.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the design-system issue, scaffolds .impeccable/live/config.json against
web/index.html, and records the aoe spawn commands for the codex GPT-5.6
models in the driver table.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Design for a single-service replacement of Radarr/Sonarr: domain model,
policy engine, sourcing via Prowlarr Torznab, import pipeline, reconcile
loop, crate layout, CI gate and build order.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>