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.pyenforces 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 overpath, 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 theALTER.
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.