Base.metadata's convention applies ck_%(table_name)s_%(constraint_name)s.
ml_settings and tag_allowlist passed already-prefixed names
(ck_ml_settings_singleton / ck_tag_allowlist_confidence_range), so the
ORM-side names came out doubled (ck_ml_settings_ck_ml_settings_singleton
etc.) and the migration-0003 smoke tests failed.
Same class of bug fixed in FC-2a for ImportSettings — should have applied
that lesson here. Bare names ('singleton', 'confidence_range') let the
convention produce the final names that match migration 0003's literal
DDL. Migration unchanged; only the model __table_args__.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
test_health.py:
@pytest.fixture on an `async def` function is rejected by pytest-asyncio
1.x strict mode. Switched to @pytest_asyncio.fixture.
backend/app/models/import_settings.py:
My constraint name was 'ck_import_settings_singleton' and Base.metadata's
naming convention applies 'ck_<table>_<name>' on top, so the final ORM
name was 'ck_import_settings_ck_import_settings_singleton' (double prefix).
The migration creates the DB constraint as 'ck_import_settings_singleton'
via raw alembic, so they didn't match. Fix: bare name 'singleton' in the
model → convention produces 'ck_import_settings_singleton', matching the
migration's literal name.
tests/test_tasks_register.py:
Celery's include=[...] parameter on the constructor is lazy — task
modules aren't imported until a worker boots. The test only imported
the Celery instance, so the @celery.task decorators in scan.py /
import_file.py / thumbnail.py never ran. Fix: explicit `import` of
those modules for side-effect at the top of the test file.
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>
Ruff lint surfaced 23 violations across three rules; all addressed:
UP017 (Use datetime.UTC alias):
Replaced 13 sites of datetime.now(timezone.utc) with datetime.now(UTC),
also adjusted from-imports accordingly. UTC is a Python 3.11+ alias for
timezone.utc that ruff's pyupgrade rules prefer.
UP042 (StrEnum):
Replaced `class TagKind(str, Enum)` and `class SkipReason(str, Enum)`
with `class Foo(StrEnum)`. StrEnum was added in Python 3.11 stdlib and
is the modern idiom. Behavior is equivalent for our usage (the .value
attribute, str(member) semantics).
I001 (Import sorting):
Added `known-first-party = ["backend"]` to ruff.toml's [lint.isort] so
ruff groups `backend.*` imports correctly. Without it, ruff treated
them as third-party and demanded a different grouping. The existing
import order is stdlib → third-party → first-party → local relative,
which ruff now accepts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Tag.kind enum (artist/character/fandom/general/series/archive/post/meta/rating),
Tag.fandom_id FK with CHECK constraint (only valid for kind='character'), and a
kind-aware uniqueness index so the same name can exist across kinds and the same
character name can exist in different fandoms.
Adds ImportBatch + ImportTask state-machine tables for scan tracking, plus a
single-row ImportSettings table (CHECK id=1) holding the importer's filter knobs.
Adds image_record.integrity_status column (defaults to 'unknown'); FC-2e
populates this via the integrity verifier.
Drops the unused tag.namespace column from FC-1 — superseded by kind.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements the data model from spec §3 in one go so FC-2/FC-3 don't need
schema-adding migrations of their own. Artist is the unified entity for
both gallery 'artist:' tags and GallerySubscriber Subscriptions
(is_subscription flag). ImageProvenance is many-to-one, enabling the
enrich-on-duplicate rule for downloaded content that pHash-matches an
existing record.
The SigLIP embedding column uses pgvector(1152) for SigLIP-so400m;
swapping models in FC-2 will require a column-width migration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Configures stable constraint naming so autogeneration produces clean diffs.
Alembic uses the sync psycopg driver while the runtime app uses asyncpg.
Also fixes a .gitignore bug caught during this task: the bare 'models/'
rule for the ML weights volume was matching backend/app/models/ (Python
package). Anchored all volume rules to repo root (/images/, /import/,
/downloads/, /models/, /postgres_data/, /redis_data/).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>