Skip to content

fix(files,realtime): issue a revision on revert and stop resurrecting cleared cursors - #8098

Merged
waleedlatif1 merged 2 commits into
stagingfrom
fix/pr8096-review-followups
Sep 21, 2026
Merged

waleedlatif1 merged 2 commits into
stagingfrom
fix/pr8096-review-followups

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

Follow-ups to the review findings on #8096. Ten findings were raised there; I investigated each one. Three were real and are fixed here, seven were not and were dismissed on the threads.

Fixed

  • Revert now issues a revision. POST /api/v2/files/:id/versions/:version/revert accepts expectedRevision and invalidates it by succeeding, but was the only v2 file write that didn't return the replacement. Callers had to re-read metadata to chain a second conditional write — and the gap between the revert and that read reopens exactly the TOCTOU window the revision exists to close.
  • Cleared cursors stop coming back. The presence roster merge coalesced cursor with ??. The fallback is load-bearing — the realtime server rebuilds a socket's presence record on join, so a re-join broadcasts a roster with cursor omitted, and blanking it would flicker peers' pointers. But an explicit null is a pointer the peer cleared on leaving the canvas, and treating it as absent re-pinned a ghost cursor whenever the clearing cursor-update was missed (dropped by the visibility gate during a join, or by a rejoin that never refreshed the roster). Now undefined keeps, null clears. selection is untouched — a cleared selection is {type:'none'} and that wire type admits no null. Pulled the merge out to socket-presence-merge.ts so it's testable, matching the sibling socket-join-target.ts / socket-join-controller.ts pattern.
  • Two File block output descriptions named operations the block doesn't have. revision claimed a get operation — this block has Get Content, which emits no revision — and lineCount named insert, which is an edit mode under Apply Edit, not an operation.

Also collapses the five hand-rolled copies of the revision conditional spread into workspaceFileRevisionField, beside the token whose omission contract it encodes. That includes the private revisionField the internal file surface had already extracted for itself.

Investigated and dismissed (details on each thread in #8096)

  • Compose POSTGRES_PASSWORD URI-unsafe characters — pre-existing and unchanged by the PR; only /, #, % break new URL(), not the @/: that were flagged, and Compose builds the DSN in YAML so the route can't encode it.
  • output-select losing chip chrome — that is the change; the editor sub-block tree has 0 ChipCombobox against 33 Combobox.
  • Metadata revision emitted without a content timestamp — content_updated_at is NOT NULL, and the comparison uses the same coalesce as the issuer.
  • Version-head retry — head.version + 1 is the documented implicit current version, and the read is a single-statement correlated subquery.
  • Unbounded cleanup dispatch — the per-call cap is the documented family-wide contract, and the task runs on a queue with concurrencyLimit: 1.
  • toStringOrNull wire-shape change — byte-identical to the asString it replaced.
  • CodeQL insufficient password hash — a cache key compared for equality, not a password verifier; high-entropy API keys, so a slow KDF buys nothing.

Type of Change

  • Bug fix

Testing

  • 1,062 vitest tests green across the affected areas, including 9 new ones. Verified each new test fails against the unfixed code before keeping it.
  • bun run check:audits 47/47, bun run lint:check 26/26, type-check clean on apps/sim and packages/sim-cli.
  • OpenAPI, CLI API, MCP operations, and CLI docs regenerated; the response gains an optional field only.

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)

🤖 Generated with Claude Code

… cleared cursors

Follow-ups to review findings on the v0.8.48 release PR.

- POST /api/v2/files/:id/versions/:version/revert now returns the revision
  naming the content the file holds after the revert. A revert consumes the
  caller's revision, so without it chaining a second conditional write needed a
  metadata re-read, and the gap between the two reopened the TOCTOU window the
  revision exists to close.
- The presence roster merge now distinguishes an absent cursor from an explicit
  null. The fallback is load-bearing: the server rebuilds a socket's presence
  record on join, so a re-join broadcasts a roster with cursor omitted. But a
  null is a pointer the peer cleared, and coalescing both with ?? re-pinned a
  ghost cursor whenever the clearing cursor-update was missed.
- Corrected two File block output descriptions that named operations the block
  does not have: revision claimed a get operation (this block has Get Content,
  which emits no revision) and lineCount named insert, which is an edit mode.

Also collapses the five hand-rolled copies of the revision conditional spread
into workspaceFileRevisionField, beside the token it describes.
@vercel

vercel Bot commented Sep 21, 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 21, 2026 4:30am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

The PR appears safe to merge; no actionable new defects or outstanding previous findings remain.

Summary

This PR improves file-write concurrency metadata and realtime presence reconciliation.

  • Returns the current content revision from file-version revert responses so callers can safely chain conditional writes.
  • Centralizes optional revision response-field construction across public and internal file operations.
  • Distinguishes an omitted cursor from an explicitly cleared cursor when merging presence rosters.
  • Corrects File block output descriptions and regenerates the OpenAPI audit and CLI response types.
  • Adds focused tests for revert revisions and presence roster merging.
Diagram
sequenceDiagram
  participant Client
  participant RevertAPI as File Revert API
  participant FileService as Workspace File Service
  Client->>RevertAPI: POST revert(expectedRevision)
  RevertAPI->>FileService: Revert version conditionally
  FileService-->>RevertAPI: Updated file and current version
  RevertAPI-->>Client: File, version, and replacement revision
  Client->>RevertAPI: Next conditional write(new revision)
Loading

Reviews (3) · Last reviewed commit: "fix(docs): match the revert example revi..."

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@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 16 files

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

Fix all with cubic | Re-trigger cubic

Comment thread apps/docs/openapi-v2-files-audit.json Outdated
The revert response example carried a revision token encoding
2026-01-16T09:12:00.000Z while its file and version showed
2026-01-15T10:30:00Z. A revision is base64url(fileId:contentUpdatedAt), so
the token has to name the record it ships with; every other example in the
document already pairs them, and the one using the later timestamp overrides
updatedAt to match. Echoing the old example back as expectedRevision would
have named content the response never described.
@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 21, 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 16 files

Confidence score: 5/5

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

Re-trigger cubic

@waleedlatif1
waleedlatif1 merged commit 3f4c101 into staging Sep 21, 2026
34 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/pr8096-review-followups branch September 21, 2026 04:48

This branch was successfully deployed

1 active deployment
Preview d2e1475f Deployed Sep 21, 2026 by vercel[bot]
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