CI / lint (push) Successful in 2s
CI / extension-version (push) Successful in 3s
Build images / sign-extension (push) Successful in 3s
Build images / build-agent (push) Successful in 6s
CI / frontend-build (push) Successful in 26s
CI / backend-lint-and-test (push) Successful in 33s
Build images / build-web (push) Successful in 59s
Build images / smoke-web (push) Skipped
Build images / build-ml (push) Successful in 1m55s
Build images / promote (push) Skipped
CI / integration (push) Successful in 2m22s
Milestone #421 built a sweep that compared each image's `artist_id` to the name of the directory holding its file, and called every mismatch a misplaced image. It reported 33,789 of 63,605 as wrongly filed. That number described the comparison, not the library. What it actually was: 32,475 (97.1%) one artist's own folder, spelled differently — Telepurte/ vs telepurte/. Same artist, same art. 657 ( 2.0%) loose at the images root 328 ( 1.0%) in a folder named after a different artist And the 1% did not mean what the tool assumed either. `ImageProvenance` records the post and source every file was downloaded from — the authoritative answer, which the tool never consulted. Querying it for all 328: 144 provenance agrees with the record (move would be right) 87 provenance agrees with the FOLDER (the record is wrong; move wrong) 53 provenance names SEVERAL artists (no single correct folder) 41 no provenance at all 3 agrees with neither So the sweep would have misfiled or arbitrarily picked for ~41% of the only set it was really needed for. The system already knew where each file came from; the tool inferred it from a column and a directory name instead. Operator, 2026-09-21: *"the current system consistently records where items are and where they came from this is just complicating something works and doesn't need fixing."* Correct on both counts. Removed: the service, the tasks, the model and migration 0099's table, the /api/cleanup/layout and /placement/* endpoints, the Maintenance card and its store actions, and the tests. 0100 drops the table (rule #22 — no legacy). KEPT deliberately, per the operator: - `utils.paths.canonical_subdir` — new filesystem imports derive their directory from the artist's slug, matching what the downloader always did. Not part of this tool; removing it would be churn that fixes nothing. - The 327 files run 1 moved (InsoUwu/ -> insouwu/). Same artist either way, and the gallery renders them correctly. - Everything from #4223 (three-gate dedup, 256-bit pHash) and #4234 (backup credential exclusion). Those fixed problems that were actually reported. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LVjrnpQjRgHdvq95rASoiR
110 lines
4.1 KiB
Python
110 lines
4.1 KiB
Python
"""Drop library_placement_run — the placement reconciler is removed.
|
|
|
|
Milestone #421 built a sweep that compared each image's `artist_id` to the
|
|
name of the directory its file sat in, and called every mismatch a misplaced
|
|
file. On the operator's library that reported 33,789 of 63,605 images as
|
|
wrongly filed.
|
|
|
|
That number was an artefact of the comparison, not a fact about the library:
|
|
|
|
- **97.1%** of it was one artist's own folder spelled differently —
|
|
`Telepurte/` versus `telepurte/`. Same artist, same art, nothing wrong.
|
|
- Of the 1% that sat in a differently-named folder, querying `ImageProvenance`
|
|
— which records the post and source each file was actually downloaded from —
|
|
showed 87 where provenance agreed with the FOLDER and not the record, and 40
|
|
genuinely posted by several creators. The sweep would have misfiled or
|
|
arbitrarily picked for roughly 41% of that set.
|
|
|
|
The system already knows where every file came from. The reconciler inferred
|
|
it from a column and a directory name instead, and manufactured work out of a
|
|
naming convention. Operator's call, 2026-09-21: *"the current system
|
|
consistently records where items are and where they came from this is just
|
|
complicating something works and doesn't need fixing."*
|
|
|
|
Rule #22 — no legacy to preserve. The table goes with the code.
|
|
|
|
## What is deliberately kept
|
|
|
|
`utils.paths.canonical_subdir` stays: new filesystem imports derive their
|
|
directory from the artist's slug, matching what the downloader has always
|
|
done. It is not part of this tool and removing it would be churn for no fix.
|
|
Run 1's 327 moved files (`InsoUwu/` -> `insouwu/`) also stay where they are —
|
|
same artist either way, and the gallery renders them correctly.
|
|
|
|
Revision ID: 0100
|
|
Revises: 0099
|
|
Create Date: 2026-09-21
|
|
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
revision: str = "0100"
|
|
down_revision: Union[str, None] = "0099"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.drop_index(
|
|
"ix_library_placement_run_artist_id",
|
|
table_name="library_placement_run",
|
|
)
|
|
op.drop_index(
|
|
"ix_library_placement_run_status", table_name="library_placement_run",
|
|
)
|
|
op.drop_table("library_placement_run")
|
|
|
|
|
|
def downgrade() -> None:
|
|
# Recreates the table only. The three runs it held (one applied, two
|
|
# planned-and-never-run) are not restored and are not worth restoring —
|
|
# the code that reads them is gone.
|
|
op.create_table(
|
|
"library_placement_run",
|
|
sa.Column("id", sa.Integer(), nullable=False),
|
|
sa.Column(
|
|
"status", sa.String(length=16), server_default="running",
|
|
nullable=False,
|
|
),
|
|
sa.Column("artist_id", sa.Integer(), nullable=True),
|
|
sa.Column(
|
|
"started_at", sa.DateTime(timezone=True),
|
|
server_default=sa.text("now()"), nullable=False,
|
|
),
|
|
sa.Column("finished_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column(
|
|
"planned_count", sa.Integer(), server_default="0", nullable=False,
|
|
),
|
|
sa.Column(
|
|
"moved_count", sa.Integer(), server_default="0", nullable=False,
|
|
),
|
|
sa.Column(
|
|
"refused_count", sa.Integer(), server_default="0", nullable=False,
|
|
),
|
|
sa.Column(
|
|
"moves", postgresql.JSONB(astext_type=sa.Text()),
|
|
server_default=sa.text("'[]'::jsonb"), nullable=False,
|
|
),
|
|
sa.Column(
|
|
"refusals", postgresql.JSONB(astext_type=sa.Text()),
|
|
server_default=sa.text("'[]'::jsonb"), nullable=False,
|
|
),
|
|
sa.Column("error", sa.Text(), nullable=True),
|
|
sa.ForeignKeyConstraint(
|
|
["artist_id"], ["artist.id"],
|
|
name="fk_library_placement_run_artist_id", ondelete="SET NULL",
|
|
),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(
|
|
"ix_library_placement_run_status", "library_placement_run", ["status"],
|
|
)
|
|
op.create_index(
|
|
"ix_library_placement_run_artist_id", "library_placement_run",
|
|
["artist_id"],
|
|
)
|