Service: job event stream, async in-memory backend, preview routes

Ported from PR #15 (packages-side surface only; the fork's dev UI page is
intentionally dropped - the API surface is the product):
- job_events table + repository, worker emits stage transitions, additive
  GET /v1/clones/:id/events polling route. The fork's hand-written
  migration was never registered in Drizzle's journal (it silently never
  applied); regenerated properly via drizzle-kit from schema.ts with the
  (job_id, seq) index declared, and verified against a live Postgres
- In-memory backend moves to the documented enqueue-202 + poll contract
  with single-flight BUSY guard; POST honors backend httpStatus
- app-preview static serving routes; the preview BUILD half is deferred
  until the compiler exports buildApp/DEFAULT_HARNESS_DIR - routes 404
  cleanly until then
- preview option threaded through core types; cache key intentionally
  unchanged while the flag is inert

All workspaces typecheck; api 18/18, core 13/13, worker 1/1 pass;
migration verified applying (table + index + FK) on postgres:16.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samraaj Bath
2026-07-04 15:00:55 -07:00
co-authored by Claude Fable 5
parent f991f469cf
commit bbe4a5643b
16 changed files with 701 additions and 55 deletions
+10 -1
View File
@@ -37,7 +37,16 @@ export async function processCloneJob(deps: ProcessDeps, jobId: string): Promise
const options = (job.options ?? {}) as CloneOptions;
// Provision the isolated harness only when this job actually verifies (build).
const harnessDir = (options.verify || options.asyncVerify) && deps.harnessProvider ? await deps.harnessProvider() : undefined;
const result = await deps.runJob({ url: job.url, options, runsDir: base, harnessDir, tier: deps.tier, captureCacheDir: deps.captureCacheDir || undefined, captureCacheTtlMs: deps.cacheTtlMs });
const result = await deps.runJob({
url: job.url,
options,
runsDir: base,
harnessDir,
tier: deps.tier,
captureCacheDir: deps.captureCacheDir || undefined,
captureCacheTtlMs: deps.cacheTtlMs,
log: (e) => { void repo.appendJobEvent(db, jobId, e); },
});
const manifest = await store.putClone(jobId, result.files);
const envelope = { files: manifest.files, routes: result.routes, bundleKey: manifest.bundleKey };