M2: Pushes, files, retention sweeps and the reset CLI (ENG-251) #15

Merged demi.dev
targeting main from stoopkidddd/eng-251-m2-pushes-files-retention-sweeps-and-the-reset-cli
Merged opened by and assigned to demi.dev
targeting main from stoopkidddd/eng-251-m2-pushes-files-retention-sweeps-and-the-reset-cli

Linear: https://linear.app/engineereddev/issue/ENG-251

What's in this PR:

  • index-store/: boot-loads pushes/ (concurrency 32) into a (createdAt, id)-sorted index with an id map; before/limit pagination with nextBefore; a 30 s re-list of the first 100 keys merges unknown records and emits push.
  • files/: POST /api/files (UUIDv7 fileId; a presigned PUT in direct mode or /api/files/:id/content in proxy mode; 413 over MAX_FILE_BYTES), GET /api/files/:id (presigned GET for 10 min, or the proxy URL), and proxy content routes that stream with the exact declared size.
  • pushes/: POST /api/pushes with the §8 validations and idempotency (200 same body / 409 different), GET list and one, and DELETE cascading to the file object. Emits push and push_deleted on the event bus.
  • sweeps/: hourly orphan-file sweep (> 24 h) and daily retention (RETENTION_DAYS > 0), on the injected clock.
  • cli.ts → dist/cli.js: info and reset --yes (refuses without --yes).
  • storage/s3.ts: aborts an S3 PUT when its input stream errors (a hung PUT seen on MinIO), with a regression test.

Verify (lead, on 445041d, based on 4291842):

  • pnpm install --frozen-lockfile → exit 0
  • mise run check:server → exit 0 (Tests 197 passed (197))
  • mise run e2e:server → exit 0 (4 files, 14 tests on MinIO: direct-mode presigned upload + push, proxy upload/download byte-equal)
  • pnpm --filter @pneumo/server build → exit 0 (dist/index.js, dist/cli.js)
  • node apps/server/dist/cli.js reset (no --yes) → "Refusing to reset without --yes.", exit 1
  • No pneumo-e2e-* containers left.

Deviations:

  • An unknown but valid pagination cursor returns an empty page (the spec doesn't say).
  • storage/s3.ts (ENG-249's file) gains stream-error abort handling, found during MinIO testing.
  • The integration build runs once in the e2e global setup, so concurrent clean: true builds don't race.

🤖 Generated with Claude Code

Commits 2

M2: Release push mutex during proxy uploads (ENG-251)
M2: Add pushes, file transfers, retention and reset CLI (ENG-251)