Single-file pipeline: validate as image, apply filter rules (min dimensions,
transparency), SHA256 hash dedup, atomic copy to /images/{subdir}/, create
ImageRecord with origin='imported_filesystem' and integrity_status='unknown',
auto-derive top-level folder name as Artist + artist tag.
Importer is intentionally sync (consumed by a Celery worker process); the
async Quart side uses the same ORM through its own async session. The
db_sync fixture in conftest.py was added in Task 3 to support these tests.
Thumbnail generation is NOT inlined; the calling Celery task enqueues a
separate thumbnail task so the import queue keeps moving on big batches.
pHash dedup is FC-2d, not here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generates 400px-max thumbnails: PNG with alpha preserved when source has
transparency, JPEG (q=85, progressive) otherwise. Storage layout is
/images/thumbs/{sha[:3]}/{sha}.{jpg|png} so each prefix bucket holds at
most ~4k files (16^3 buckets across all possible SHAs).
Video path uses ffmpeg's first-frame snapshot at ~5% offset; we have a
1-second floor for short clips. ffmpeg call has a 60s timeout. Video
tests are deferred to FC-2e's integration suite where transcoding is
also wired up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Validates kind/fandom rules at the service layer: fandom_id only allowed
for kind='character', and must reference an existing kind='fandom' tag.
Autocomplete ranking: exact match > prefix match > substring, tie-broken
by image_count descending. Image-tag association uses INSERT ON CONFLICT
DO NOTHING for idempotent re-tagging.
conftest.py adds a transactional AsyncSession fixture; each test rolls
back so they don't pollute each other. Also includes a sync Session
fixture (db_sync) for the Importer tests in Task 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
slugify() produces ASCII-only lowercase slugs from arbitrary text (used for
artist slugs and tag slugs). paths.derive_subdir/derive_top_level_artist
extract the destination layout and folder→artist convention from an import
source path. hash_suffixed_name builds IR-style 'stem__hash10.ext' filenames.
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>
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>
Pushes :dev on every dev push; pushes :main and :latest on main.
Uses RELEASE_TOKEN (a broader-scoped Forgejo PAT covering write:package,
read:package, write:release, write:issue) so the same secret can serve
future release-cutting and issue-management workflows.
README now documents the fabledcurator-ci runner label and the required
RELEASE_TOKEN scopes.
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>
Forward-looking pin per operator direction ("build for the future and not
rebuild the past"). Touches everywhere a version is named so all
downstream artifacts (Dockerfiles, ruff config, package.json engines)
agree.
Python 3.14 (released Oct 2025) is the current stable. Node 22 is the
most recent LTS line still receiving updates (Maintenance LTS since
Oct 2025); Node 24 (released Apr 2026) goes Active LTS in Oct 2026 and
will be the natural next bump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ML deps (torch, transformers, onnxruntime, opencv) are ~4GB and stay out
of the regular web/worker image. Models self-heal into /models on start
(implemented in FC-2). HuggingFace cache vars point inside /models so
weight downloads are persistent across restarts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Multi-stage build: node:20 builds the SPA, python:3.12-slim runs the app.
Same image handles web, worker, scheduler roles via entrypoint.sh's first
arg. ffmpeg + unar are baked in for FC-2's transcode + archive paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The frontend blueprint is registered after the api blueprint so /api/*
routes are matched first; everything else falls through to the SPA.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nav lists every FC-2 and FC-3 surface with a placeholder view that names
the sub-project that will fill it. System store polls /api/health on mount
and renders a status pip in the nav footer.
Favicon uses Curator's aged amber accent (#A87338).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Curator signature accent (#A87338, aged amber) is baked into
fabled-tokens.js. Vuetify theme consumes the same tokens module the rest
of the SPA does, so updating design system tokens propagates throughout.
Vite dev server proxies /api and /ws to the Quart backend.
App.vue references AppShell (lands in Task 9) and main.js references
router.js (lands in Task 9) — these are intentionally split so Task 8 is
the framework scaffold and Task 9 is the navigation/shell content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Routes are pre-declared for FC-2/FC-3 task modules (import, ml, thumbnail,
download, scan, maintenance). Queue lanes match the ImageRepo pattern where
beat+maintenance run on a separate worker so long imports don't starve
periodic tasks. Smoke ping task confirms the wiring in eager mode for CI.
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>
Config reads from env vars (12-factor); .env.example documents defaults.
Health endpoint is liveness-only (no DB/Redis touch). Test added for CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pins runtime and ML deps separately so the regular web image stays lean.
Configures ruff for py312 with bugbear, async, and pyupgrade lints enabled.
psycopg sync driver included up-front for alembic.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Establishes the FabledRulebook-required ignore patterns (docs/superpowers/specs,
docs/superpowers/plans) plus Python/Node/Vite/extension/volumes/IDE rules from
spec §6.1 before any other code lands, so accidental adds can't leak. Also
ignores .claude/settings.local.json (per-user local overrides; shared
.claude/settings.json may be committed in the future).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>