npm run backfill:images cannot run in QA or production. The script from #226 exists only in the repository, never in the image, so the half of that issue that closes the exposure on already-stored photos has no way to execute where the photos actually are.
Three separate reasons, each sufficient on its own:
backend/tsconfig.json declares "include": ["src"] and "rootDir": "src", so scripts/ is never compiled — there is no dist/scripts.
The Dockerfile copies dist, migrate.js and migrations. scripts/ never enters the image.
tsx, which the npm script invokes, is a devDependency and is stripped by npm install --omit=dev in the final stage.
Found while writing the QA verification checklist for #226, before it cost anyone a trip to the NAS. New uploads are unaffected and were verified working in QA — this is only the backfill.
Why it happened
scripts/bench-hash-latency.ts is the pattern that was followed, and it is a development tool that never needs to run in a deployed environment. This script has the opposite requirement: it is an operational task that can only be useful where the images are. migrate.js is the right precedent — it sits at a path the image copies, and the container invokes it directly.
Fix
Move it to src/backfillImageReencode.ts so it compiles into dist and ships. Both of its runtime dependencies, sharp and pg, are production dependencies already, so nothing else needs to change in the image.
Point the npm script at the compiled output rather than at tsx, so one command behaves identically on a laptop and inside the container:
Guard the entry point with require.main === module so that a file living in src/ cannot execute a catalogue-wide rewrite merely by being imported.
Verification
dist/backfillImageReencode.js exists after npm run build
It runs from a built image with --omit=dev installed, with no tsx present
Report mode and --apply still behave as they did, and a second --apply still skips
Blocks Task 5 of docs/superpowers/plans/2026-08-29-strip-exif-and-reencode.md. Follows #226.
`npm run backfill:images` cannot run in QA or production. The script from #226 exists only in the repository, never in the image, so the half of that issue that closes the exposure on **already-stored** photos has no way to execute where the photos actually are.
Three separate reasons, each sufficient on its own:
- `backend/tsconfig.json` declares `"include": ["src"]` and `"rootDir": "src"`, so `scripts/` is never compiled — there is no `dist/scripts`.
- The Dockerfile copies `dist`, `migrate.js` and `migrations`. `scripts/` never enters the image.
- `tsx`, which the npm script invokes, is a devDependency and is stripped by `npm install --omit=dev` in the final stage.
Found while writing the QA verification checklist for #226, before it cost anyone a trip to the NAS. New uploads are unaffected and were verified working in QA — this is only the backfill.
## Why it happened
`scripts/bench-hash-latency.ts` is the pattern that was followed, and it is a development tool that never needs to run in a deployed environment. This script has the opposite requirement: it is an operational task that can *only* be useful where the images are. `migrate.js` is the right precedent — it sits at a path the image copies, and the container invokes it directly.
## Fix
Move it to `src/backfillImageReencode.ts` so it compiles into `dist` and ships. Both of its runtime dependencies, `sharp` and `pg`, are production dependencies already, so nothing else needs to change in the image.
Point the npm script at the compiled output rather than at `tsx`, so one command behaves identically on a laptop and inside the container:
```
"backfill:images": "node dist/backfillImageReencode.js"
```
Guard the entry point with `require.main === module` so that a file living in `src/` cannot execute a catalogue-wide rewrite merely by being imported.
## Verification
- `dist/backfillImageReencode.js` exists after `npm run build`
- It runs from a built image with `--omit=dev` installed, with no `tsx` present
- Report mode and `--apply` still behave as they did, and a second `--apply` still skips
Blocks Task 5 of `docs/superpowers/plans/2026-08-29-strip-exif-and-reencode.md`. Follows #226.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
npm run backfill:imagescannot run in QA or production. The script from #226 exists only in the repository, never in the image, so the half of that issue that closes the exposure on already-stored photos has no way to execute where the photos actually are.Three separate reasons, each sufficient on its own:
backend/tsconfig.jsondeclares"include": ["src"]and"rootDir": "src", soscripts/is never compiled — there is nodist/scripts.dist,migrate.jsandmigrations.scripts/never enters the image.tsx, which the npm script invokes, is a devDependency and is stripped bynpm install --omit=devin the final stage.Found while writing the QA verification checklist for #226, before it cost anyone a trip to the NAS. New uploads are unaffected and were verified working in QA — this is only the backfill.
Why it happened
scripts/bench-hash-latency.tsis the pattern that was followed, and it is a development tool that never needs to run in a deployed environment. This script has the opposite requirement: it is an operational task that can only be useful where the images are.migrate.jsis the right precedent — it sits at a path the image copies, and the container invokes it directly.Fix
Move it to
src/backfillImageReencode.tsso it compiles intodistand ships. Both of its runtime dependencies,sharpandpg, are production dependencies already, so nothing else needs to change in the image.Point the npm script at the compiled output rather than at
tsx, so one command behaves identically on a laptop and inside the container:Guard the entry point with
require.main === moduleso that a file living insrc/cannot execute a catalogue-wide rewrite merely by being imported.Verification
dist/backfillImageReencode.jsexists afternpm run build--omit=devinstalled, with notsxpresent--applystill behave as they did, and a second--applystill skipsBlocks Task 5 of
docs/superpowers/plans/2026-08-29-strip-exif-and-reencode.md. Follows #226.