Addresses operator reports: Stop never finishes, the agent polls curator
constantly, and stale-cached pages get mistaken for a failed deploy.
- Stop is prompt: flip _running BEFORE any lock so /status + worker loops see
"stopped" immediately, and add a stop/shrink checkpoint in _process (after
decode, before the expensive detect+embed) that releases the job and bails —
so a Stop doesn't wait out heavy GPU work.
- Lazy curator polling: the queue snapshot is fetched only while a browser is
actually watching (a /status hit within UI_IDLE_GRACE) and on a 5s cadence,
not a constant background loop. The work loop's own lease/submit is curator's
only visitor otherwise — nothing polls just to poll.
- Build marker: VERSION is embedded in the page and reported on /status; the UI
shows a "reload" banner when they differ, so a browser-cached page can't be
mistaken for "the new image didn't deploy" (complements the no-store header).
CI: the lint lane now also `ruff check`s agent/ and compileall-parses it, so the
GPU agent is linted + syntax-checked before its image builds (build.yml only
`docker build`s it). Fixed the agent's pre-existing UP037/B905 so it passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
With fsync-off the whole integration suite runs in ~45s (was ~13min across
shards), so the 3-way split only triplicated the ~2min fixed overhead
(container + install + migrate) and consumed 3 of 6 runner slots for no
wall-clock gain. Merge intapi/intimp/intcore into one `integration` job:
spin up once, install once, migrate once, run `pytest -m integration` over
the whole suite. Frees 2 runner slots (6 jobs -> 4) and drops ~140 lines of
near-duplicate YAML.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Option 1 (pooling the teardown connection) left teardowns at ~1.5-2s/test, so
the cost is the per-test TRUNCATE's commit forcing an fsync, not the connect
handshake. Each shard now ALTER SYSTEM SETs fsync/synchronous_commit/
full_page_writes off + pg_reload_conf() right after deps install, before
alembic — sighup/user-context GUCs apply with no restart. The DB is ephemeral
(rebuilt per run) so fsync-off is safe; the step is non-fatal so a perms
surprise can't red a shard. Speeds up every test's commit (setup inserts +
the teardown TRUNCATE), stacking on the pooled engine from the prior commit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ruff is pre-installed in the ci-python image, so a new `lint` job runs it
with no dependency install and fails in seconds — surfacing the common lint
bounce class without waiting on the backend job's ~30-60s wheel install.
Dropped the now-redundant ruff step from backend-lint-and-test (same job
name, required-checks unchanged).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ci.yml: drop pull_request: trigger — push: branches: [dev, main] already covers it; pull_request was duplicating ci.yml runs on every dev push with an open PR. (No fork PRs in this repo.)
build.yml: drop dev from push triggers — operator doesn't use the :dev image. Add tags: ['v*'] trigger + tag-push branch in the Determine-tag logic so cutting a release tag publishes an immutable :v26.05.26.X image (rollback story) without re-publishing :latest. Extend the XPI-download step to fire on tag pushes too so the versioned image carries the signed extension.
Net per hotfix cycle: 5 runs → 3 (no tag) / 4 (with tag).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
uv falls back to pip install on runners without uv binary, so this
change is forward-compatible with the current ci-python image. When
the runner image gets uv pre-installed in a future bump, the warm
install path drops from ~2 min to ~10 seconds.
pytest-xdist parallelization is OUT OF SCOPE for this commit:
tests/conftest.py uses a TRUNCATE ALL TABLES RESTART IDENTITY CASCADE
fixture after every integration test against a single shared
database; xdist workers running in parallel would nuke each other's
mid-test state. A future refactor to per-worker databases or
per-worker schema isolation is the prerequisite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CI failure resolving 'postgres' hostname was the symptom; the cause is
that the workflow violated FabledRulebook/forgejo.md's "CI philosophy —
lint + short unit tests only" rule. Integration tests against a real
Postgres are supposed to run locally via docker-compose, not in CI.
Changes:
- Marked 8 DB-dependent test files with @pytest.mark.integration:
test_tag_service, test_importer, test_gallery_service, test_api_gallery,
test_api_tags, test_api_settings, test_api_import_admin, test_maintenance.
- CI workflow drops the postgres/redis service containers and the alembic
upgrade smoke step entirely.
- Pytest invocation in CI changes to `pytest -v -m "not integration"`.
- Added pytest marker registration to pyproject.toml.
- DB_PASSWORD and SECRET_KEY env vars retained because config.py reads
them at import time even though unit tests don't actually use them
(set to placeholder values).
What CI now runs:
- ruff check
- pytest on the 6 unit test files: test_slug, test_paths,
test_migration_0002, test_thumbnailer, test_celery_smoke,
test_tasks_register.
- npm install + npm run build
What CI no longer runs:
- alembic upgrade (no live DB)
- the 8 integration test files (these run locally via docker-compose)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ruff:
The remaining I001 errors came from ruff treating `alembic` as a first-
party module (because the alembic/ directory exists in the repo root)
rather than third-party. Ran `ruff check --fix` locally — auto-sorted
import groupings to put alembic/sqlalchemy alongside backend.* as first-
party, and trimmed redundant blank lines after a few import blocks.
Frontend:
`npm run check` (vue-tsc --noEmit) was failing because vue-tsc has no
tsconfig.json to read against, and the frontend is pure JS without
JSDoc annotations — vue-tsc had nothing to do. Skipping the step until
we add a tsconfig + convert to TS or add JSDoc annotations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per feedback-no-local-runs we don't run npm locally, so no
package-lock.json is tracked. npm ci fails without a lockfile; npm
install works fine. We lose strict reproducibility, which is acceptable
for a pre-v1 project — if we want it later, commit a package-lock.json
and flip back to npm ci.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per FabledRulebook forgejo.md, toolchain versions live on the runner
image, not in the workflow. Two changes here, paired with the runner
image bump (RUFF_VERSION 0.9.7 -> 0.15.13 in CI-Runner/CI-python):
- Drop "ruff>=0.9,<1.0" from ci.yml's pip install — the runner image's
pre-installed ruff is authoritative now. Previously this constraint
matched the existing 0.9.7 in the image so pip never upgraded.
- Flip ruff.toml's target-version back to py314 now that the runner
has a ruff new enough to know about it.
Next bump path: edit RUFF_VERSION in CI-Runner/CI-python/Dockerfile,
'make push', done. No workflow churn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generic python-ci runner is reusable across the family (FabledScribe,
FabledSteward, NhenArchiver, StashHandler, etc.) rather than scoped to
just this project. Runner image lives at CI-Runner/CI-python/ in the
operator's workspace; pattern mirrors CI-Runner/CI-go and CI-Runner/CI-flutter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Backend job spins up Postgres+pgvector and Redis as services, runs ruff,
applies the initial migration to confirm it's clean, and runs pytest.
Frontend job runs vue-tsc and vite build.
Requires a runner labeled "fabledcurator-ci" with Python 3.14, ruff,
and Node 22 pre-installed. Integration tests run locally via
docker-compose with testing.Short() gating per FabledRulebook
verification.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>