GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix(uploads): restore archive extraction folder parity by waleedlatif1 · Pull Request #6565 · simstudioai/sim

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #6560 (improvement/v2-route-standardization). Review only this PR's own commits; merge after its parent.

Archive extraction regressed against main in three compounding ways. Every archive containing a subdirectory failed, and 100% of Copilot archive extractions failed, because materialize-file.ts always passes rootFolderSegments and its catch only matches ArchiveError.

The three defects

  1. Non-canonical folder path. archive.ts built folderSegments.join('/') — no leading slash, no per-segment encoding — and handed it to an operation that routes to parseFolderPath, which throws FolderPathError on any path not starting with /. It also fails the encodeFolderPathSegment round-trip, so a folder name containing a space threw even after prefixing /.
  2. exactName: true rolled back whole extractions. A single colliding leaf name threw FileConflictError, and the catch deleted every already-extracted file. main auto-suffixed via allocateUniqueWorkspaceFileName and succeeded. Independently reachable today for a flat archive via POST /api/tools/file/manage.
  3. Wrong folder primitive. createWorkspaceFileFolderAtPath creates exactly one leaf, rejects an existing path, and requires the parent to exist. The extractor never created intermediates, so the first nested entry asked for a folder whose parent did not exist.

Fix

Adds ensureWorkspaceFileFolderPathOperation on the same semantic operation (fileOperations.createFolder, identical minimumRole: 'write' / workspaceApiKey: 'allow' policy), restoring main's reuse-on-conflict, create-intermediates semantics without bypassing the application-operation boundary. exactName returns to false.

Deliberately not a one-line / prefix: with only that fix applied, 4 tests still fail (Parent folder not found, A folder named "bundle" already exists), proving defect 3 is independent.

Tests

archive.test.ts previously mocked the folder operation and asserted the broken shape — which is why this shipped. The mock now enforces the real rules via the production requireNonRootFolderPath/buildFolderPath. Reverting the fix turns 6 tests red; each defect was also isolated separately.

Known gap (deliberate)

The new operation has no projectAudit/afterSuccess notify, matching main (which called ensureWorkspaceFileFolderPath directly). An archive of only empty directories therefore creates folders with no audit row or UI refresh. Flagged for a follow-up decision rather than changed here.

type-check · biome · 937 tests (lib/uploads, lib/workspace-files, copilot handlers) · check:api-validation — all pass.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

greptile-apps[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
waleedlatif1 added 2 commits August 11, 2026 16:00
@waleedlatif1
fix(uploads): restore archive extraction folder parity
94fa43b
Archive extraction into workspace files/ was rewritten onto the authorized
application-operation boundary, and three behavioral regressions came with
that move. Together they broke every archive containing a subdirectory, and
100% of copilot extract() calls (materialize-file always passes
rootFolderSegments: [baseName], and its catch only handles ArchiveError).

1. Non-canonical folder path. The extractor joined the folder segments with
   "/" and passed the result as `path` to createWorkspaceFileFolderOperation.
   That path reaches requireNonRootFolderPath -> parseFolderPath, which
   requires a leading "/" and byte-for-byte canonical per-segment encoding,
   so "bundle/data" threw FolderPathError before anything was written — and
   a folder name containing a space or a reserved character would still have
   thrown after merely prefixing a slash.

2. exactName: true. createWorkspaceFileFromBuffer was told to demand the
   exact leaf name, which sets maxAttempts = 1 and raises FileConflictError
   when the name already exists. The extractor's rollback then deleted every
   file written so far, so one colliding name destroyed the whole
   extraction. Reachable today for flat archives through the unzip action of
   POST /api/tools/file/manage. Restored to auto-suffixing via
   allocateUniqueWorkspaceFileName.

3. Wrong folder primitive. createWorkspaceFileFolderAtPath creates exactly
   one leaf, conflicts on an existing path, and requires the parent to exist
   already. The extractor never creates intermediates and caches by full
   path, so the first nested entry asked for a folder whose parent was never
   created. The correct semantics are ensureWorkspaceFileFolderPath: walk
   every segment, reuse what exists, create only what is missing.

Rather than bypass the operation boundary by calling the manager primitive
directly, this adds ensureWorkspaceFileFolderPathOperation — an authorized
application use case under files.folders.create that expresses "ensure this
whole chain exists" — and routes the extractor through it with raw decoded
segments, so no path string is built and no encoding can be malformed.

archive.test.ts previously mocked the folder operation and asserted the
broken shape (path: 'bundle'), which is why this shipped. The suite now
fakes the workspace-file store in memory while enforcing the real rules:
folder paths run through the production parseFolderPath family, the
create-one-leaf operation conflicts and requires a parent, and exactName
governs conflict vs auto-suffix. Nested, reuse, encoded-name, and collision
cases are covered and each fails against the pre-fix code.
@waleedlatif1
chore(files): tidy archive extraction cleanup
0077285
@vercel

vercel Bot commented Aug 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 11, 2026 11:25pm

Request Review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@cursor

cursor Bot commented Aug 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes workspace folder materialization and archive rollback behavior, so incorrect unwind logic could delete or leave folders incorrectly. Auth/policy is unchanged; risk is mainly data-consistency around extraction retries.

Overview
Fixes archive extraction so nested zips (including all Copilot extractions) succeed again by switching from leaf-only folder create to ensure-path semantics, and by making failed extractions roll back cleanly.

Archive extraction now calls ensureWorkspaceFileFolderPathOperation, which materializes intermediate folders and reuses existing ones instead of failing on missing parents or conflicts. On mid-extraction failure it also deletes only folders this call created, deepest-first, so retries are not blocked by leftover trees and preexisting folders stay untouched.

exactName is restored to false, so a colliding leaf name auto-suffixes instead of aborting and rolling back the whole extraction. ensureWorkspaceFileFolderPath now returns { folderId, createdFolderIds } so callers can unwind exactly their own writes.

Reviewed by Cursor Bugbot for commit 427b260. Configure here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This was referenced Aug 11, 2026
fix(billing): withhold the payer credit and storage pools from callers who cannot manage billing #6567
Merged
chore(api): remove a dead route builder, bound folder-index reads, reject invalid cursors #6568
Merged
fix(api): stop leaking failed SQL in v1 table 500s and restore the 423 lock field #6569
Merged
fix(skills): only reject a built-in name collision on an actual rename #6570
Merged
@greptile-apps

greptile-apps Bot commented Aug 11, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores archive extraction behavior by materializing complete folder chains, reusing existing folders, auto-suffixing colliding filenames, and cleaning up extraction-created folders after failures.

  • Adds an authorized ensure-folder-path operation that reports which folders it created.
  • Updates archive extraction to preserve nested and encoded folder paths.
  • Extends rollback and regression coverage for nested folders, conflicts, and partial failures.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
apps/sim/lib/uploads/archive.ts Uses ensure semantics for archive folder chains, enables collision suffixing, and extends failure rollback to folders created during extraction.
apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts Extends folder-path ensuring to return the deepest folder ID and the ordered IDs inserted by the call.
apps/sim/lib/workspace-files/application/workspace-file-folders.ts Exposes folder-path ensuring through the existing authorized workspace-file operation boundary.
apps/sim/lib/workspace-files/application/write-workspace-file-by-path.ts Adapts existing path-based file writers to the ensure helper’s new structured result.
apps/sim/lib/uploads/archive.test.ts Adds contract-oriented coverage for nested paths, encoded names, existing folders, filename conflicts, and rollback ordering.

Sequence Diagram

sequenceDiagram
  participant Caller
  participant Extractor
  participant FolderOp as Ensure Folder Path
  participant Upload as File Upload
  participant Cleanup as Delete Operations

  Caller->>Extractor: Extract archive
  loop Each archive entry
    Extractor->>FolderOp: Ensure decoded path segments
    FolderOp-->>Extractor: folderId, createdFolderIds
    Extractor->>Upload: "Create file with exactName=false"
    Upload-->>Extractor: Created file
  end
  alt Extraction succeeds
    Extractor-->>Caller: Extracted files
  else Any operation fails
    Extractor->>Cleanup: Delete created files
    Extractor->>Cleanup: Delete created folders deepest-first
    Extractor-->>Caller: Original error
  end
Loading

Reviews (2): Last reviewed commit: "fix(uploads): roll back folders archive ..." | Re-trigger Greptile

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Aug 11, 2026
View reviewed changes
Comment thread apps/sim/lib/uploads/archive.ts Resolved

Uh oh!

There was an error while loading. Please reload this page.

@waleedlatif1
fix(uploads): roll back folders archive extraction created
427b260
Extraction now materializes folders before uploading files, but the failure
path only deleted the extracted files — every folder the call created was left
behind. That is not cosmetic: `materialize_file` guards re-extraction by looking
up the root folder path and refusing when it has any child, so a half-extracted
nested archive turned every retry into "already extracted — delete that folder
first" until a human cleaned up the tree by hand.

The rollback must delete only folders this call actually inserted, never one it
reused: extracting into an existing path is normal (a sibling entry, an earlier
successful extraction), and deleting a pre-existing folder would destroy
unrelated user data. `ensureWorkspaceFileFolderPath` already distinguishes the
two while walking the segment chain, so it (and its application operation) now
reports `createdFolderIds` alongside the leaf id. The extractor accumulates
those ids in creation order and, on failure, deletes them in reverse — parents
are recorded before their children, so reverse order is deepest-first and a
parent is never removed out from under a child. Folder cleanup is best-effort
like the existing file cleanup, so a cleanup failure never masks the original
error.
@vercel
vercel Bot temporarily deployed to Preview August 11, 2026 23:25 Inactive
@waleedlatif1

waleedlatif1 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile-apps

greptile-apps[bot] reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@waleedlatif1

waleedlatif1 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator Author

@cursor review

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

cursor[bot]
cursor Bot reviewed Aug 11, 2026
View reviewed changes

@cursor cursor Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 427b260. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread apps/sim/lib/uploads/contexts/workspace/workspace-file-folder-manager.ts
}

return parentId
return { folderId: parentId, createdFolderIds }

@cursor cursor Bot Aug 11, 2026

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Partial ensure orphans rollback folders

High Severity

ensureWorkspaceFileFolderPath only returns createdFolderIds on success. If it throws after inserting some segments, those IDs never reach archive rollback, so the new folder unwind cannot run. Leftover folders under the extract root still trip the materialize_file content guard and block every retry.

Additional Locations (1)
  • apps/sim/lib/uploads/archive.ts#L375-L381
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 427b260. Configure here.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@waleedlatif1
waleedlatif1 merged commit 7af315f into improvement/v2-route-standardization Aug 11, 2026
5 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@waleedlatif1
waleedlatif1 deleted the fix/archive-extraction-folder-path branch August 12, 2026 00:18

This branch was previously deployed

1 inactive deployment
Preview — 427b2606 Deployed Aug 11, 2026 by vercel[bot]
View deployment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@cursor cursor[bot] cursor[bot] left review comments

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@waleedlatif1
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.