nexus/.planning/phases/40-job-infrastructure/40-01-SUMMARY.md

113 lines
5.8 KiB
Markdown

---
phase: 40-job-infrastructure
plan: "01"
subsystem: content-jobs
tags: [db-schema, services, async-jobs, live-events]
dependency_graph:
requires: []
provides:
- content_jobs DB table with queued/running/done/failed lifecycle
- contentJobStore CRUD service
- contentJobRunner async dispatcher with live events
- MAX_GENERATED_ASSET_BYTES constant (500MB)
- assets.sourceTaskId column
- LIVE_EVENT_TYPES extended with content_job.* events
affects:
- packages/db/src/schema/
- packages/shared/src/constants.ts
- server/src/services/
- server/src/attachment-types.ts
tech_stack:
added: []
patterns:
- Drizzle ORM pgTable schema with $type<> for typed status fields
- Fire-and-forget async job dispatch with void pattern
- DB write before live event publish (ordered consistency)
- renderContent stub for future renderer plug-in by jobType
key_files:
created:
- packages/db/src/schema/content_jobs.ts
- server/src/services/content-job-store.ts
- server/src/services/content-job-runner.ts
- packages/db/src/migrations/0046_tense_randall.sql
modified:
- packages/db/src/schema/assets.ts
- packages/db/src/schema/index.ts
- packages/shared/src/constants.ts
- server/src/attachment-types.ts
- server/src/services/index.ts
decisions:
- content_jobs.resultAssetId has no FK to assets — populated post-creation, circular FK avoided
- assets.sourceTaskId is nullable text with no FK — task IDs are string identifiers, not UUIDs
- renderContent is a stub returning placeholder.txt — phases 41-45 add real renderers keyed by jobType
- Migration applied via drizzle-kit generate from worktree dist/ (main repo has pre-existing duplicate migration files)
metrics:
duration: "~10 minutes"
completed: "2026-04-04"
tasks_completed: 2
tasks_total: 2
files_created: 4
files_modified: 5
---
# Phase 40 Plan 01: Schema, constants, and services for content jobs Summary
**One-liner:** content_jobs table + async job runner with store/runner services using fire-and-forget dispatch and ordered DB-before-event lifecycle.
## What Was Built
The foundational data model and async execution engine for v1.7 content generation. Phase 40 Plan 01 establishes:
1. **DB schema** (`content_jobs` table) with full lifecycle columns and two composite indexes for efficient querying by company+status and company+createdAt.
2. **assets.sourceTaskId** — nullable text column allowing generated assets to be traced back to their originating conversation task.
3. **LIVE_EVENT_TYPES** — extended with `content_job.queued`, `content_job.running`, `content_job.done`, `content_job.failed` for real-time job progress.
4. **MAX_GENERATED_ASSET_BYTES** — 500MB constant (vs 10MB upload limit) for generated/namespace storage, configurable via `PAPERCLIP_GENERATED_ASSET_MAX_BYTES`.
5. **contentJobStore** — CRUD service with create/getById/listByCompany/transition operations against the content_jobs table.
6. **contentJobRunner** — async fire-and-forget dispatcher that transitions jobs through running→done/failed, stores generated assets with sourceTaskId, and publishes live events after each DB write.
## Tasks Completed
| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | Schema, constants, and migrations | 8bf4bd91 | content_jobs.ts, assets.ts, index.ts, constants.ts, attachment-types.ts, migration 0046 |
| 2 | contentJobStore and contentJobRunner | b359fec9 | content-job-store.ts, content-job-runner.ts, services/index.ts |
## Verification
- `pnpm tsc --noEmit --project packages/db/tsconfig.json` — PASS
- `pnpm tsc --noEmit --project packages/shared/tsconfig.json` — PASS
- `pnpm tsc --noEmit --project server/tsconfig.json` — PASS
- Migration file `0046_tense_randall.sql` generated with correct DDL
## Deviations from Plan
### Migration apply limitation (pre-existing blocker, out of scope)
**Found during:** Task 1
**Issue:** The main repo (`/opt/nexus`) has pre-existing duplicate migration files (`0047_nebulous_klaw.sql` and `0047_overjoyed_groot.sql`, `0048_add_chat_messages_updated_at.sql` and `0048_flashy_marrow.sql`) from parallel agent work. The `check:migrations` script prevents both `db:generate` and `db:migrate` in the main repo.
**Impact:** Migration `0046_tense_randall.sql` was generated successfully from the worktree's clean migration state but could not be applied via `pnpm db:migrate`.
**Resolution:** Migration will be applied when the worktree is merged and the duplicate migration numbering conflict is resolved. The SQL is correct and ready for execution. This is a pre-existing issue deferred to `.planning/deferred-items.md`.
**Migration file content verified:** Creates `content_jobs` table, adds `source_task_id` to `assets`, creates both compound indexes, adds FK to companies.
### Known Stubs
**renderContent stub** in `server/src/services/content-job-runner.ts`:
- Returns `{ filename: "placeholder.txt", contentType: "text/plain", buffer: Buffer.from("placeholder output") }` for all jobTypes
- This is intentional — the plan specifies this as a stub for phases 41-45 to fill in real renderers keyed by jobType
- Does NOT prevent the plan's goal (job infrastructure) from being achieved
## Self-Check: PASSED
Files verified to exist:
- `/opt/nexus/.claude/worktrees/agent-ac2e6085/packages/db/src/schema/content_jobs.ts` — FOUND
- `/opt/nexus/.claude/worktrees/agent-ac2e6085/server/src/services/content-job-store.ts` — FOUND
- `/opt/nexus/.claude/worktrees/agent-ac2e6085/server/src/services/content-job-runner.ts` — FOUND
- `/opt/nexus/.claude/worktrees/agent-ac2e6085/packages/db/src/migrations/0046_tense_randall.sql` — FOUND
Commits verified:
- `8bf4bd91` — FOUND (feat(40-01): schema, constants, and migrations for content jobs)
- `b359fec9` — FOUND (feat(40-01): contentJobStore and contentJobRunner services)