# 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 ```bash 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.