Brings main up to dev. Every commit here passed CI on dev; the head, 31c94d6, is green across all lanes, signing, build, smoke and promote (run 10249).
Changes
fix: phase 3 stops importing at a deadline and leaves the rest for the next chunk (3f52248)
feat: the extension picks a Discord source's posters from who posts in the channel, stored by id (ef591ed, 0928d2f)
feat: a Discord source can remove the posts it took from people outside its poster list (1efece0)
fix: a Discord source takes only messages with an image attached, from the posters it names, and knows its channel's name (29c22af)
fix: the single-color filter no longer takes line art for a blank image (d6d3184)
style: a source's backfill state chips are filled, so their text reads on the dark row (20aeac8)
ci: integration finds only its own service containers (3c30451)
test: set Celery config through a celery_conf fixture, not monkeypatch (e92497c)
ci: the weekly refresh reuses main's signed extension; worker tests pin the machine (31c94d6)
Why now
The Sunday base refresh tests and builds main. It has failed every week since at least 2026-10-04, so :latest hasn't been rebuilt since 2026-09-25. The test fixes it needs exist only on dev.
Brings main up to dev. Every commit here passed CI on dev; the head, 31c94d6, is green across all lanes, signing, build, smoke and promote (run 10249).
## Changes
- fix: phase 3 stops importing at a deadline and leaves the rest for the next chunk (3f52248)
- feat: the extension picks a Discord source's posters from who posts in the channel, stored by id (ef591ed, 0928d2f)
- feat: a Discord source can remove the posts it took from people outside its poster list (1efece0)
- fix: a Discord source takes only messages with an image attached, from the posters it names, and knows its channel's name (29c22af)
- fix: the single-color filter no longer takes line art for a blank image (d6d3184)
- style: a source's backfill state chips are filled, so their text reads on the dark row (20aeac8)
- ci: integration finds only its own service containers (3c30451)
- test: set Celery config through a celery_conf fixture, not monkeypatch (e92497c)
- ci: the weekly refresh reuses main's signed extension; worker tests pin the machine (31c94d6)
## Why now
The Sunday base refresh tests and builds main. It has failed every week since at least 2026-10-04, so `:latest` hasn't been rebuilt since 2026-09-25. The test fixes it needs exist only on dev.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- A message with no image or video attachment is chat: extract_media returns
nothing for it, so nothing downloads and no post record is written. A
message that passes keeps every file, numbered as gallery-dl numbers them.
- `discord_authors` in a source's config limits the walk to those posters
(id, username or display name); the edit dialog has a field for it and no
longer drops config keys it has no field for.
- source.display_name (migration 0115), refreshed by every Discord walk, shows
on Subscriptions in place of the two-id URL; a Discord post card names its
channel from the record's `channel`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five of Todding's Discord doodles (pencil lines on white, 3000px) were skipped
on import as "single color", so their posts showed text and no image. The
predicate sampled a 64px BILINEAR thumbnail, which blends thin strokes into
the paper, and 0.95 "one color" is below how white a doodle is.
- Sample 256x256 by NEAREST, so each sample is a real pixel.
- Default threshold 0.995: blank means essentially blank. Migration 0116 moves
a stored 0.95 (the old default) with it; the settings slider now spans
0.9-1 in 0.005 steps so the value is reachable.
- The Cleanup audit shares the predicate, so it stops flagging sketches too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The theme's success and info colours are dark; a tonal chip draws its text in
that colour, which vanished against the Subscriptions row.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Remove posts from other posters" on a Discord source with an "Only posts by"
list previews what goes, per poster, then deletes it behind a typed token:
- posts whose record names a poster not on the list (by id, username or
display name); posts that record no poster are left alone and counted;
- images found only on those posts; one also on a kept post stays, and a
synthetic drop's link never keeps one;
- their attachments, and every drop that absorbed one of them, so the grouping
sweep regroups what remains.
Preview and apply share one predicate, and a parity test holds them to it.
The post record now also saves the poster's display name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a Discord channel page the Add panel lists who posted in the newest 200
messages, the creator the server is named for (or owned by) ticked. On a
channel FabledCurator already follows, the chip opens the same list with Save
and Open artist. The list is kept as user ids, so a rename never quietly stops
it matching; the name each was picked under rides beside it for display, and
the Subscriptions dialog shows it.
- DiscordClient.recent_posters + rank_posters (owner / name-matches-server;
bots never suggested; it suggests, the operator ticks).
- GET/POST /api/extension/discord/posters; quick-add takes discord_authors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runner's docker daemon is shared, so `--filter name=<job> | head -n1` can
pick another repo's Postgres when two jobs run side by side (Steward run 8358,
Inkwell run 8653). Scope the lookup to this job's own task prefix and require
exactly one match. Done from Inkwell #5313 with the operator's permission; the
recipe is rule 79.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Discord recapture chunk on #banana-land was killed at the 1350s soft limit
twice (events 92354 on 2026-09-28 and 92623 on 2026-09-29). The walk's
budget (CHUNK_TOTAL_SECONDS, #4407) assumed 2.5s per import; measured on
those chunks, a real import of the source's doodles took about 5s, so ~290
imports the walk had planned to fit took about twice the time reserved.
- PHASE3_DEADLINE_SECONDS (1200s from task start): on native platforms
phase 3 starts no import past it. The files left stay on disk with no
record and are not marked seen; the next chunk resumes on the page that
holds the first of them and imports them through the existing "on disk
but never imported" path. A walk that reached its end becomes PARTIAL so
the backfill is not marked complete while files wait.
- DownloadResult.written_cursors records each written file's page cursor;
mark_seen_after_import takes skip_paths.
- PHASE3_IMPORT_SECONDS 2.5 -> 5.0, the slow end of what was measured, so
the walk rarely leans on the deadline.
Task #4493.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pytest 9.2.0 (released 2026-10-27, changelog #10644) changed how
monkeypatch.setattr restores an attribute the object does not hold in
its own __dict__: undo now deletes it instead of assigning the old value
back. Celery's conf serves every key through __getattr__, so undo called
delattr, the delete raised AttributeError, and pytest skipped the rest
of that test's undos. That made 53 teardown errors across six files
(cleanup API, task-run signals, tasks admin, backup, library audit, the
soft-limit salvage test), and could leak patches into later tests.
CI installs pytest unpinned, so the first run after the release picked it
up: the weekly base refresh of main, 2026-11-01, and every run since. The
gate held, so nothing was published.
tests/conftest.py gains `celery_conf(**values)`: it records each key's
value through conf[key], writes with conf.update(), and restores the
saved values after the test. The six files use it.
Task #6864.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two more reasons CI could not go green, found while fixing #6864.
1. sign-extension's "never go backwards" guard refused every weekly base
refresh since at least 2026-10-04 (runs 8023, 9015, 9400, 9818). The
refresh builds main, whose extension version (2026.9.25.1201) trails
the highest one dev has signed since (2026.9.28.1403), so the guard
exited before the cache lookup that would have found main's version
already signed and reused it. The web image (:latest) was never
refreshed. A version that is already signed now passes the guard: it
is reused from its release, not signed again, so it cannot be a
downgrade.
2. tests/test_api_workers.py read each lane's ceiling from the CI
container's CPU quota and memory limit. Since run 10242 (2026-11-05)
the runner's container allows fewer slots than two tests set (6 worker
slots, 2 ML), so both got 400 "above what this container can hold".
An autouse fixture now pins a 16-core, 64 GiB container, as
test_worker_lanes.py already does.
Task #6864.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Brings main up to dev. Every commit here passed CI on dev; the head,
31c94d6, is green across all lanes, signing, build, smoke and promote (run 10249).Changes
3f52248)ef591ed,0928d2f)1efece0)29c22af)d6d3184)20aeac8)3c30451)e92497c)31c94d6)Why now
The Sunday base refresh tests and builds main. It has failed every week since at least 2026-10-04, so
:latesthasn't been rebuilt since 2026-09-25. The test fixes it needs exist only on dev.🤖 Generated with Claude Code