Files

Legacy CLI archive (US07-01)

Frozen, read-only sources of the command-line tools this application was extracted from. They are reference material and rollback evidence — provenance for behavior that now lives in photo_pipeline/, and the only way to answer "what did the original actually do?" once the replacement has drifted.

Nothing here is production code. No module under photo_pipeline/ imports or executes anything in this directory, and this directory is not on the application's import path. tests/unit/test_legacy_archive.py enforces both, along with the checksums and the redaction below.

What is here

Path Role
src/photo_analyzer.py the analysis CLI: discovery, hashing/dedup, vision analysis, EXIF writing, SQLite schema, album naming
src/nsfwtag/ NSFW scoring, EXIF safety keywords, and the review server (__init__ 1.1.0)
src/webapp/ the stdlib review web app: FTS search, stats, subprocess runner, HTML shell (__init__ 0.1.0)
src/nsfw_tag.py thin backwards-compatible entry point for nsfwtag
src/compare_models.py dev-only model comparison script
src/test_dedup.py, src/test_nsfw_skip.py the CLIs' own standalone self-checks (never pytest suites)
donor_ledger.yaml the donor ledger: every migrated behavior, its target, its tests, and every intentional delta
requirements-lock.txt the dependency versions the frozen sources were last verified against
photo_analyzer.env.sample the CLI's configuration surface, with every value replaced by a placeholder
CHECKSUMS.sha256 SHA-256 of every archived source file

photo_analyzer.py carries no __version__; its identity is its checksum, recorded in CHECKSUMS.sha256 and taken at commit 9b7ee6b (the merge of US06-06, the last commit before archival).

Verifying the archive

cd legacy_cli_archive && shasum -a 256 -c CHECKSUMS.sha256

Any edit to an archived source must be accompanied by a regenerated checksum file and a note here explaining why a frozen archive changed — the normal answer being that it should not.

Schema notes

photo_analyzer.py owned a path-keyed SQLite database (SCHEMA, near the top of the file):

  • photos(id, path UNIQUE, status, phash, file_sha1, dup_of, description, tags, people_count, setting, time_of_day, season, mood, location_hint, approx_year, raw_response, error_message, analyzed_at, exif_written_at);
  • photos_fts — an FTS5 external-content index over path, description, tags, mood, location_hint, kept in sync by insert/update/delete triggers;
  • late columns (phash, file_sha1, dup_of) were added by an in-code _migrate_schema() rather than a migration tool, and their indexes are created only after the ALTER.

The replacement keeps the same analysis fields but re-keys everything to a stable assets.id (Alembic migrations 0001…), because a path is not an identity: the donor's path UNIQUE is exactly what broke on every move and rename.

nsfwtag kept its safety scores outside the database in nsfw_scores.csv (path,nsfw_score, four decimals, unreadable rows dropped). That file is no longer a source of truth; photo_pipeline/services/legacy_import.py imports it into assets.safety_score and reports exactly what matched, what did not, and why.

Redaction

The archive contains no credentials. photo_analyzer.env.sample documents the configuration surface (LLM_API_KEY, LLM_BASE_URL, LLM_MODEL, and the tuning variables) with placeholder values only; the CLI itself never contained a key, it read one from photo_analyzer.env or the environment. No .env, database, log, CSV, or photo from the author's library is archived.

Why these tools were retired

Each behavior's fate is recorded per row in donor_ledger.yaml: reuse, extract, refactor, or replace, with the target module, the characterization tests that pinned the donor's behavior, the parity tests the replacement passes, and — where the replacement deliberately does something else or nothing at all — a delta saying so. Rows still marked pending name the backlog story that will resolve them; they are the honest list of what has not been carried over yet.