Skip to content

feat(files): workspace file version history over the v2 API, CLI, and MCP - #7997

Merged
waleedlatif1 merged 11 commits into
stagingfrom
feat/file-versions-api
Sep 19, 2026
Merged

waleedlatif1 merged 11 commits into
stagingfrom
feat/file-versions-api

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Every workspace file content write now records a version in a new workspace_file_version table. Content writes were already copy-on-write, so the outgoing object is kept instead of deleted
  • Collaborative-editor persists and repeated workflow writes from the same author fold into one version per 10-minute window (new version after 5 min idle); uploads, API/CLI/MCP writes, editor saves, Sim edits and reverts always get their own version. Identical bytes don't create a version
  • Files with no history show an implicit v1, materialized on the first write, so nothing needs a backfill; empty create-shells aren't kept as a version
  • Each version snapshots its secret provenance, and a revert reinstates that snapshot, so old bytes never come back with a more permissive classification
  • New v2 endpoints (also exposed through the generated MCP server and CLI): list versions (keyset cursor), get, read text, download (binary, audited), revert (optional expectedCurrentVersion precondition), delete a superseded version. File metadata now returns currentVersion
  • Retained version keys are denied on key-addressed access (metadata fallback in file authorization) and refused at model-safety gates, so old bytes are only reachable through the version routes
  • Retention: new cleanup-file-versions job + cron route; free 30d / pro+team 180d / enterprise configurable (fileVersionRetentionHours, API-only for now), newest 10 versions always kept, free capped at 100 versions, and a 500-superseded-version ceiling enforced inline on write
  • Version objects are deleted by the soft-delete purge, account deletion, and archive rollback; released keys go through the storage-cleanup outbox instead of a best-effort delete
  • Version bytes are not counted toward storage billing

Type of Change

  • New feature

Testing

  • New real-Postgres integration suite (implicit v1, coalescing window/idle, identical-bytes repoint, concurrent writers, revert + provenance reinstatement, stale precondition, empty shell, delete, retention pruning), wired into CI
  • Unit tests for the version use cases, version-key denial in file authorization and model-safety gates, outbox helpers; mutation-checked the key guards
  • Full apps/sim unit suite, sim-cli suite, bun run lint, check:audits (OpenAPI/CLI/MCP generation in sync), check:api-validation:strict, check:migrations (new table only), turbo run type-check all pass

Checklist

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

@vercel

vercel Bot commented Sep 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Sep 19, 2026 6:15am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the prior findings are resolved and no new actionable defects remain.

Summary

This PR adds end-to-end workspace file version history across storage, the database, public v2 APIs, generated MCP operations, and the CLI.

  • Records copy-on-write file versions with provenance-aware revert behavior and controlled coalescing.
  • Adds version listing, reading, downloading, reverting, and deletion endpoints.
  • Adds plan-aware retention, durable object cleanup, and inline history ceilings.
  • Preserves authorization and model-safety boundaries around retained storage keys.
  • Adds PostgreSQL integration coverage and updates generated API and CLI surfaces.

Changes since the previous review consolidate metadata/version reads into one database snapshot, centralize storage-cleanup batching, reduce version-query payloads, and add search-index safeguards and tests.

Diagram
sequenceDiagram
    participant Caller as API / CLI / MCP
    participant App as File application service
    participant DB as PostgreSQL
    participant Store as Object storage
    participant Outbox as Cleanup outbox

    Caller->>App: Write new file content
    App->>Store: Store copy-on-write object
    App->>DB: Lock file and record/supersede version
    DB-->>App: Current file and version
    App-->>Caller: Updated file metadata

    Caller->>App: Read or revert historical version
    App->>DB: Authorize and load version metadata
    App->>Store: Read retained version object
    App-->>Caller: Historical content or reverted version

    App->>DB: Retention deletes superseded version row
    App->>Outbox: Enqueue object deletion atomically
    Outbox->>Store: Delete released object with retries
Loading

Reviews (9) · Last reviewed commit: "Merge remote-tracking branch 'origin/sta..."

Comment thread apps/sim/background/cleanup-file-versions.ts Outdated
Comment thread apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 72 files

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread apps/sim/app/api/files/authorization.ts
Comment thread apps/sim/app/api/v2/files/utils.ts Outdated
Comment thread apps/sim/background/cleanup-soft-deletes.ts Outdated
Comment thread apps/sim/background/cleanup-file-versions.ts Outdated
Comment thread apps/sim/lib/api/contracts/v2/openapi/files-audit.ts Outdated
Comment thread apps/sim/lib/workspace-files/application/file-versions.ts
Comment thread apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts
Comment thread apps/docs/openapi-v2-files-audit.json
Comment thread apps/docs/openapi-v2-files-audit.json Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 76 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/lib/api/contracts/v2/file-versions.ts Outdated
Comment thread apps/sim/background/cleanup-soft-deletes.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 76 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/background/cleanup-soft-deletes.ts Outdated
Comment thread apps/sim/background/cleanup-file-versions.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 76 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/background/cleanup-soft-deletes.ts Outdated
Comment thread apps/sim/background/cleanup-soft-deletes.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 76 files

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/lib/workspace-files/application/read-workspace-file-metadata.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

Comment thread apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 76 files

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/lib/uploads/contexts/workspace/workspace-file-versions.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 76 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Re-trigger cubic

- read metadata and its current version in one statement; drop the retry and 409
- move provenance policy branching into the provenance module
- project stored provenance out of list, head, and get reads; revert reads it on demand
- chunk storage-cleanup enqueues to the outbox bulk limit in one place
- drop the write-only content_updated_at version column (unreleased 0365)
- reuse findCause, the shared cleanup batch constants, and the version-number primitives
- share the v2 text presenter between the file and version routes
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

…-api

# Conflicts:
#	.github/workflows/test-build.yml
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@waleedlatif1
waleedlatif1 merged commit f76aca1 into staging Sep 19, 2026
34 checks passed
@waleedlatif1
waleedlatif1 deleted the feat/file-versions-api branch September 19, 2026 06:20
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 83 files

Confidence score: 3/5

  • apps/sim/background/cleanup-file-versions.ts can process unbounded batches during one Trigger run because the 50-batch limit resets for each file and workspace chunk, potentially causing excessive runtime or resource usage—enforce a run-wide row or batch budget.
  • apps/sim/lib/api/contracts/v2/openapi/files-audit.ts documents ten-minute write folding, but after five minutes of writer inactivity a collaborative or workflow write may create a new version sooner, producing unexpected audit-version splits—align the implementation and API contract, then add coverage for this timing case.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/api/contracts/v2/openapi/files-audit.ts">

<violation number="1" location="apps/sim/lib/api/contracts/v2/openapi/files-audit.ts:450">
P3: After five minutes of writer inactivity, a collaborative or workflow write starts a new version even when the existing version is under ten minutes old. This description says all writes within ten minutes fold, so callers will mispredict version boundaries; document both coalescing cutoffs.</violation>
</file>

<file name="apps/sim/background/cleanup-file-versions.ts">

<violation number="1" location="apps/sim/background/cleanup-file-versions.ts:152">
P2: With enough candidate files, this job resets the 50-batch loop for each file and workspace chunk, so one Trigger run can process unbounded batches. Carry a run-wide row or batch budget and stop when it is exhausted.</violation>
</file>

You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.

Fix all with cubic | Re-trigger cubic

for (const group of chunkArray(workspaceIds, DEFAULT_WORKSPACE_CHUNK_SIZE)) {
const candidates = await selectCandidateFileIds(group, cutoff, maxSuperseded)
for (const fileIds of chunkArray(candidates, FILES_PER_QUERY)) {
for (let batch = 0; batch < DEFAULT_MAX_BATCHES_PER_TABLE; batch++) {

@cubic-dev-ai cubic-dev-ai Bot Sep 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: With enough candidate files, this job resets the 50-batch loop for each file and workspace chunk, so one Trigger run can process unbounded batches. Carry a run-wide row or batch budget and stop when it is exhausted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/background/cleanup-file-versions.ts, line 152:

<comment>With enough candidate files, this job resets the 50-batch loop for each file and workspace chunk, so one Trigger run can process unbounded batches. Carry a run-wide row or batch budget and stop when it is exhausted.</comment>

<file context>
@@ -0,0 +1,172 @@
+  for (const group of chunkArray(workspaceIds, DEFAULT_WORKSPACE_CHUNK_SIZE)) {
+    const candidates = await selectCandidateFileIds(group, cutoff, maxSuperseded)
+    for (const fileIds of chunkArray(candidates, FILES_PER_QUERY)) {
+      for (let batch = 0; batch < DEFAULT_MAX_BATCHES_PER_TABLE; batch++) {
+        const expired = await selectExpiredVersions(fileIds, cutoff, maxSuperseded)
+        if (expired.length === 0) break
</file context>
Fix with cubic

operationId: 'listFileVersions',
summary: 'List File Versions',
description:
'List the versions of a file, newest first by default. Each write that changes the bytes records one; identical rewrites do not. Collaborative edits within ten minutes fold into one version, as do repeated workflow writes by one author. Renames and moves are not versions. An empty file is version 1 until its first content replaces it. Retention keeps the newest ten and removes older versions by plan, so numbers can have gaps.',

@cubic-dev-ai cubic-dev-ai Bot Sep 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: After five minutes of writer inactivity, a collaborative or workflow write starts a new version even when the existing version is under ten minutes old. This description says all writes within ten minutes fold, so callers will mispredict version boundaries; document both coalescing cutoffs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/lib/api/contracts/v2/openapi/files-audit.ts, line 450:

<comment>After five minutes of writer inactivity, a collaborative or workflow write starts a new version even when the existing version is under ten minutes old. This description says all writes within ten minutes fold, so callers will mispredict version boundaries; document both coalescing cutoffs.</comment>

<file context>
@@ -418,6 +440,216 @@ const declaredRoutes = [
+      operationId: 'listFileVersions',
+      summary: 'List File Versions',
+      description:
+        'List the versions of a file, newest first by default. Each write that changes the bytes records one; identical rewrites do not. Collaborative edits within ten minutes fold into one version, as do repeated workflow writes by one author. Renames and moves are not versions. An empty file is version 1 until its first content replaces it. Retention keeps the newest ten and removes older versions by plan, so numbers can have gaps.',
+      errors: RESOURCE_ERRORS,
+      success: { description: 'A page of file versions.' },
</file context>
Suggested change
'List the versions of a file, newest first by default. Each write that changes the bytes records one; identical rewrites do not. Collaborative edits within ten minutes fold into one version, as do repeated workflow writes by one author. Renames and moves are not versions. An empty file is version 1 until its first content replaces it. Retention keeps the newest ten and removes older versions by plan, so numbers can have gaps.',
'List the versions of a file, newest first by default. Each write that changes the bytes records one; identical rewrites do not. Collaborative edits and repeated workflow writes by one author fold only while the current version is less than ten minutes old and has received a write within the last five minutes. Renames and moves are not versions. An empty file is version 1 until its first content replaces it. Retention keeps the newest ten and removes older versions by plan, so numbers can have gaps.',
Fix with cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant