# @yemaya/svc-study-workspace

Service for the Yemaya Study & Deconstruction Workspace (checklist YSD-1003;
architecture:
`docs/adr/ADR-0074-yemaya-study-deconstruction-workspace-architecture.md`).

Current surface is operational only — health, readiness, metrics, request
identity/tracing, structured logging, graceful shutdown, fail-fast
configuration. Business routes land with their checklist items (walking skeleton
YSD-1030 onward); there are intentionally no placeholder API routes.

## Run locally

```bash
pnpm --filter @yemaya/svc-study-workspace serve       # tsx watch on :4020
curl -s localhost:4020/health | jq
curl -s localhost:4020/ready | jq                     # postgres probe activates when DATABASE_URL is set
curl -s localhost:4020/metrics | head
```

## Verify

```bash
npx vitest run --config apps/yemaya/svc-study-workspace/vitest.config.ts
npx tsc --noEmit -p apps/yemaya/svc-study-workspace/tsconfig.app.json
```

## Configuration

See `src/config.ts` — zod-validated environment with production consistency
rules (DATABASE_URL required, JSON logs enforced, shutdown grace capped under
the ECS stopTimeout). Deployment contract: `deploy/README.md`.

## Migrations (YSD-3008)

Forward-only SQL files in `migrations/`, applied in filename order by
`src/persistence/migrate.ts`:

- **Forward-only with checksums.** Each applied file is recorded in
  `study_schema_migrations` with a sha256; editing an applied file is a hard
  `MigrationDriftError`. Fixes ship as NEW files, never edits.
- **Rollback / forward-fix instructions** live in each migration's header
  comment (what to `DROP` to back out, or why the migration is forward-only).
  Write the header before the DDL — a migration without one fails review.
- **Zero/low-downtime discipline.** Every migration transaction runs under
  `lock_timeout` (default 5s): a rollout that queues behind a held lock aborts
  fail-fast — unrecorded and retryable — instead of blocking every later
  reader/writer while it waits. Prefer additive DDL (`IF NOT EXISTS`, new
  tables/columns/indexes); large-table index builds and rewrites need their own
  plan. Verified by `src/persistence/migrations-rollout.integration.spec.ts`:
  fresh-vs-stepwise schema equivalence, fail-fast under a held lock, and a live
  writer running through a rollout with a bounded worst-case write stall — all
  on scratch databases created and dropped by the suite.

## Seed data

```bash
pnpm --dir apps/yemaya/svc-study-workspace exec tsx scripts/seed-dev-data.ts [database-url]
```

Loads one deterministic, contract-valid study project (fixed UUIDs, tenant
`tenant-dev-seed`): two works — one broadly granted, one with an expired grant
so every rights-denial surface is explorable — editions, a link-only
stream-reference asset (no media bytes are fabricated), tracks, a scene/shot
hierarchy, anchors, observations, a claim, a creator statement, and a typed
contrast link. Idempotent by check-then-skip (the schema forbids DELETE by
trigger). Media playback needs a real ingest through the UI/API; the seed
deliberately stores no bytes. E2E stack bootstrap (migrations + MinIO bucket + a
synthesized rights-cleared film) stays `scripts/e2e-bootstrap.ts`.
