fix(project-files): requeue rendered-artifact search after commit and trim Project read transactions

Merged
WA
3d ago
+574-310

Summary

All changes are on flag-gated Project file paths (behind the projects rollout) plus small shared helpers whose workspace behavior is unchanged.

  • Lock upgrade in Project artifact reads. readArtifact holds the Project lock and the source row shared, but its final transaction called markFileSearchArtifactReadyInTx, which takes the Project lock exclusively and file rows FOR UPDATE. Every compiled render did this (a cache hit still returns artifactKey), so concurrent generated-document reads in one Project could deadlock or wait out the 5 s Project lock timeout. The read now records the search completion and requeues after commit via requeueFileSearchForRenderedArtifact (lock-free current check, then its own transaction). That transaction now takes the owner lock under the 1 s artifact-ready bound instead of the 5 s Project lock bound; a timeout is caught and logged and never fails the read.
  • Fewer authorized transactions. Artifact, inline, and CSV preview reads authorize from their own snapshots (prepareFromSnapshot) instead of a separate preflight. Artifact inputs are snapshotted and admitted only when the source references any; the final transaction still rechecks the source, every input, and provenance after storage reads. Artifact: 4 transactions / 3 admissions → 2 / 2 without inputs, 3 / 3 with. Inline and CSV: 3 → 2. Search: drops the nested listProjectFileFolders use case.
  • Artifact inputs. Downloads run with bounded concurrency after checking declared sizes against the per-file and total budgets. Each read is capped at its declared size plus the unclaimed budget, and the first failure aborts the rest. The request signal now reaches render, downloads, and compile.
  • Project inline delivery streams instead of buffering. It fences the file head after storage opens and serves workspaceFileCacheControl(contentAddressed) like workspace inline. It no longer lists every Project folder.
  • Project search admits, withholds stale previews, and reports provenance after its snapshot commits. loadFileSearchDelivery returns an explicit current set, and provenance is classified only when a content gate or delivery observer needs it.
  • Foreign page embeds. A Project page that embeds another owner's image now fails as a missing input (not_found) instead of a 500, through a typed ProjectPageForeignEmbedError.
  • Cleanup.
    • Page inline limits are exported from page-document.server.ts and reused.
    • knip.jsonc narrows the project-files.ts entry to types.
    • SearchProjectFileContentInput is un-exported.
    • The CI isolate build step uses paths: ["."] and JOBS=4, matching docker/app.Dockerfile.

Expected rebase conflicts in the open Project file stack:

  • #9018: none expected.
  • #9019 and #9034 (still based on pre-squash #9017): conflicts in artifacts.ts, previews.ts, search.ts, artifact-ready.ts, delivery.ts, and checks.yml; take staging's side.
    • The #9034 inline route must present the new stream result (stream, contentLength, contentAddressed).
    • readProjectFileArtifact keeps file.folderPath for its VFS paths.

Type of Change

  • Bug fix

Testing

  • Biome on all changed files; lib/workspace-files/page-compile.test.ts.
  • New integration checks in content-rendered.integration.ts:
    • A generated artifact read beside a concurrent shared Project holder completes and requeues search. On the previous code it times out on the exclusive Project lock.
    • A contended requeue gives up within its short bound without failing the read.
    • A Project page embedding another owner's image is not_found.
  • Type-check, audits, and integration suites run in CI.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)