"""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"], )