This is a major release because four things an existing caller could depend on have changed. Worker takes its quarantine queue as a required keyword argument; in 5.0.1 it defaulted to None, and a worker built that way reported a payload that failed its schema or mapping rules as quarantined, deleted it from the work queue and sent it nowhere. SqsQueue.delete_batch and SqsQueue.purge, which nothing in the repository called, are removed together with conduit.core.retry.backoff_schedule, and SqsQueue.client exposes the boto3 client the handle wraps. Log lines from every CLI command are written to stderr, where in 5.0.1 structlog's default logger printed them to stdout, and the stream is looked up at write time so an in-process run survives typer's CliRunner swapping it; dlq list and dlq replay configure logging like the other commands. Envelope.submitted_at is an AwareDatetime, so a producer that enqueues a naive timestamp, which 5.0.1 accepted and delivered, now has its message treated as malformed.
A queue body that does not parse as an envelope no longer stops the worker. SqsQueue.receive logs queue.invalid_message with the queue name and the message id, never the body, leaves the message unacknowledged for the redrive policy, and returns the valid messages from the same response as a MessageBatch that also records how many messages arrived, so a response holding nothing valid is not mistaken for an idle poll. In 5.0.1 the validation error raised out of receive and ended Worker.run.
The circuit breaker's half-open probe is a lease that only a delivery attempt can hold. The breaker gate now comes after the idempotency claim, so a duplicate or a key held by another worker is answered before the probe is spent; CircuitBreaker.abandon_probe hands back a probe whose delivery reached no verdict, and the worker calls it after a retry loop that exhausted on 429; and a message the breaker turns away has its claim released and is held for the larger of the queue's visibility timeout and the time left on the breaker. In 5.0.1 the gate came first. With a duplicate as the first message after the recovery window, the breaker took it as the probe, the duplicate returned without a verdict, and the worker turned every later message away with a one second visibility hold each time, an hour later still, without calling the target again: a script driving the 5.0.1 worker through that sequence shows two adapter calls in total, where 6.0.0 makes five and closes the breaker. tests/unit/test_worker.py pins the duplicate, the claim held elsewhere, the probe exhausted on 429 and the visibility hold, a random walk of 5,000 steps in tests/unit/test_breaker.py checks that a closed breaker always admits, an open one never does and a half-open one admits exactly one probe whenever none is in flight, and tests/integration/test_backpressure.py proves against LocalStack that an outage which recovers into 429s still drains both tasks.
A status row write that DynamoDB refuses is logged as status.publish_failed and tried again on the next tick; in 5.0.1 the ClientError raised out of Worker.run. The fakes take retry_after_seconds, from FAKE_RETRY_AFTER_SECONDS or the faults endpoint, and send it as Retry-After on every 429 they inject, so test_retry_after_pauses_the_connector_not_just_the_message now asserts that a second task on the connector reaches the fake at least that many seconds after the header, where the 5.0.1 test only counted the header as honoured. tests/integration/test_concurrency.py runs two workers on one queue and asserts 20 deliveries and 10 deduplications for 30 messages, a key in progress elsewhere re-polled and then deduplicated, and a claim abandoned with a two second lease taken over once it expires. make test-unit and CI fail below 85 percent line coverage of conduit/, and the suite reports 86.98 percent at this commit.
make demo now submits 302 tasks: 240 unique, 60 resubmits and two payloads their source schema rejects, a priority outside the enum in schemas/jira-support/v2.yaml and a status outside the one in schemas/webhook-crm/v1.yaml, and the block reports both held in quarantine with their notes while the ops table's quar column reads 1 for each of those connectors. The run first clears quarantined payloads an earlier run left behind, because they are never acknowledged, derives the length of the connector YAML it writes, which is 8 lines where the 5.0.1 block said 10, and stamps its commit, package version, LocalStack image and date on its second line. demo/check_readme.py, run by make readme-check and by CI, fails when that commit is not in the repository or not an ancestor of HEAD, when the version or the image no longer match, and when the block reports a MISMATCH or carries no must-equal checks, and it lists the commits that have touched the measured code since. The block in README.md is run D930E66 at commit f008099, conduit 6.0.0, localstack/localstack:3.8, 2026-09-28. ARCHITECTURE.md describes the worker loop as it runs; the 5.0.1 copy still said a misfit was deleted from the queue as delivery.rejected, the v2 behaviour that v4 replaced with quarantine.
The browser demo reads its configuration from the repository. scripts/embed-config.mjs writes connectors/.yaml, schemas//v*.yaml and the Adapter class into web/src/sim/config.generated.ts, npm run embed:check fails when they disagree and CI runs it, and the simulation parses those files rather than the simplified copies it carried; that brought the mapping stage the port lacked, so a rule violation lands in quarantine with stage mapping as it does in conduit/core/mapping.py. Each of the 49 self-check lines prints both operands of every comparison it makes with the relation that holds between them, so a line cannot say ok beside a predicate it did not test; the idempotency key is compared with the digest conduit/core/idempotency.py produces for the same input, and each retry delay with the ceiling of the attempt it followed. run reproducible hashes the summary together with every value reachable from the engine and requires the same hash from a fresh engine at the same seed and from that engine run again after reset(), which is what Run again does on the page, and it fails when any module in src/sim names Math.random, Date.now, performance.now or new Date. That check exposed that reset() left the rng where the previous run had ended and the fakes counting across runs: on 5.0.1 a second run on the same engine reports the jira fake returning 429 120 times instead of 60, with different delays and latencies, and on 6.0.0 the two runs are identical.
CI gained the web job, runs make readme-check after the unit suite, pins uv 0.11.7 through setup-uv v6, checks out the full history so the ancestry check can run, and accepts workflow_dispatch. At this commit the unit suite is 185 tests at 86.98 percent line coverage, the LocalStack suite 32 tests, the terraform suite 3, and the web self check 49 assertions, all passing in CI run 36468470785.