Skip to content

Add Personal Cloud project library#12574

Merged
franknoirot merged 19 commits into
mainfrom
codex/cloud-project-library
Jul 24, 2026
Merged

Add Personal Cloud project library#12574
franknoirot merged 19 commits into
mainfrom
codex/cloud-project-library

Conversation

@franknoirot

@franknoirot franknoirot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adds the cloud project library type and the default cloud-personal Personal Cloud library
  • keeps the cloud-sync plugin's static default off so registry/settings startup is deterministic
  • applies the cloud-sync rollout policy from the app runtime: feature-flagged users with no explicit preference get cloud sync enabled, and toggling cloud sync on materializes the Personal Cloud library
  • on web, treats Personal Cloud as the canonical project library by replacing only the recognized default directory row instead of adding a duplicate library
  • keeps desktop behavior additive: directory libraries and Personal Cloud can appear side by side
  • uses the existing browser project storage root as Personal Cloud's local clone folder, while desktop resolves an OS-visible Zoo/personal folder
  • hides directory-library creation, reordering, type-switching, and path-editing controls on web while still allowing users to remove existing directory-library rows
  • wires cloud-only Home entries into the Personal Cloud library

Why

This is the Personal Cloud library PR in the project-library stack. It adds the cloud library model and default personal library without including the legacy synced-directory migration prompt.

The cloud-sync activation behavior is intentionally modeled as app product policy instead of plugin startup reconciliation. The plugin can stay statically default-off, while the settled app runtime decides when a feature-flagged user should be opted in and when the Personal Cloud library row should be added to app.libraries. That avoids a brittle settings mutation during registry construction and keeps user opt-outs respected.

The web policy is also intentional: OPFS is an implementation detail, so enabling Personal Cloud should not create a second visible directory-style library or physically move the browser's default project storage. Instead, the default browser project library is represented as Personal Cloud once cloud sync is enabled. Browser users also cannot create or edit directory libraries, but they can remove a directory-library row if one already exists in their settings.

Stack

Validation

  • npm run test -- src/registry/contracts/projectLibraries.spec.ts src/lib/projectLibraries/settings/ProjectLibrariesSettingInput.spec.tsx src/lib/cloudSync/registry/plugin.spec.tsx
  • npm run test:integration -- src/lib/settings/settingsUtils.spec.ts src/lib/app.spec.ts
  • npm run tsc -- --noEmit
  • npm run lint -- src/lib/app.ts src/lib/app.spec.ts src/lib/cloudSync/index.test.ts src/lib/cloudSync/paths.ts src/lib/cloudSync/registry/plugin.spec.tsx src/lib/projectLibraries/settings/ProjectLibrariesSetting.tsx src/lib/projectLibraries/settings/ProjectLibrariesSettingInput.tsx src/lib/projectLibraries/settings/ProjectLibrariesSettingInput.spec.tsx src/lib/routeLoaders.ts src/lib/settings/settingsUtils.ts src/machines/settingsMachine.ts src/registry/contracts/projectLibraries.ts src/registry/contracts/projectLibraries.spec.ts src/registry/extensions/homeProjects/index.ts src/registry/extensions/settings/index.ts src/unitTestUtils.ts
  • npx biome check --formatter-enabled=true --linter-enabled=false --assist-enabled=false --files-ignore-unknown=true src/lib/app.ts src/lib/app.spec.ts src/lib/cloudSync/index.test.ts src/lib/cloudSync/paths.ts src/lib/cloudSync/registry/plugin.spec.tsx src/lib/projectLibraries/settings/ProjectLibrariesSetting.tsx src/lib/projectLibraries/settings/ProjectLibrariesSettingInput.tsx src/lib/projectLibraries/settings/ProjectLibrariesSettingInput.spec.tsx src/lib/routeLoaders.ts src/lib/settings/settingsUtils.ts src/machines/settingsMachine.ts src/registry/contracts/projectLibraries.ts src/registry/contracts/projectLibraries.spec.ts src/registry/extensions/homeProjects/index.ts src/registry/extensions/settings/index.ts src/unitTestUtils.ts
  • git diff --check

@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
modeling-app Ready Ready Preview, Comment Jul 24, 2026 9:15pm

Request Review

@pierremtb pierremtb 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.

Approving now so you're not blocked on me since I'm off today.

Dropped a few notes. Testing felt great on web and desktop (macOS)!

Comment thread src/main.ts Outdated
Comment thread src/lib/cloudSync/paths.ts
Comment on lines +540 to +549
onDragOver={
canReorderLibraries
? (event) => handleDragOver(event, index)
: undefined
}
onDrop={
canReorderLibraries
? (event) => handleDrop(event, index)
: undefined
}

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.

This is cool!

Comment thread src/lib/app.ts
Comment on lines +575 to +584
return (
snapshot.context as unknown as Record<
string,
| Record<
string,
{ current?: unknown; default?: unknown; user?: unknown } | undefined
>
| undefined
>
)[activationSetting.category]?.[activationSetting.settingName]

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.

We might want to improve this if we can 😅

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Totally fair, although this type was actually introduced way back here, and it's really just being moved around here (you can see the code gets removed down at lines 560–566, it used to be called desiredActive).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ultimately that type is correct, but I do agree we should find a way to

  1. make that more readable
  2. Figure out a cleaner way to accomplish what syncPluginSettings does: it's hella effectful.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

But I'd like to do that in future PRs

Base automatically changed from codex/project-library-settings-details to main July 24, 2026 10:35
@franknoirot
franknoirot force-pushed the codex/cloud-project-library branch from cea7df4 to e29d88d Compare July 24, 2026 12:53
@franknoirot
franknoirot marked this pull request as ready for review July 24, 2026 12:53
@franknoirot
franknoirot requested a review from a team as a code owner July 24, 2026 12:53
@franknoirot
franknoirot requested a review from a team as a code owner July 24, 2026 14:58
@franknoirot
franknoirot enabled auto-merge (squash) July 24, 2026 14:59
Comment thread src/lib/cloudSync/README.md Outdated
Co-authored-by: Jace Browning <jacebrowning@gmail.com>
Comment thread src/lib/cloudSync/paths.ts
...runtimeConfig.value,
enabled: runtimeConfig.value.enabled && cloudSyncPluginEnabled,
projectDirectoryPath: currentSettings
? getDefaultDirectoryProjectLibraryPath(

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.

I think this should use getDefaultCloudProjectDirectoryPath instead?

Now what happens:

  • I create a new cloud project in the browser
  • open ZDS -> Cloud sync
  • the new project appears under Personal Cloud (correct)
  • but once I click on it, which downloads it, will make it appear under Default Project Directory too

I assume that's not intended?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No that is not intended! Thank you for finding that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the way that I need to fix this is actually to get the the appropriate directory into ensureCloudProjectLocallySynced.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That fires during the project "open" action, and does the actual downloading. That needs to know what library the project hails from.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed here a5e049c

@andrewvarga

Copy link
Copy Markdown
Contributor

Something from user testing: if you disable Cloud-sync setting in the web, then your cloud projects go away (Expected), but your web-local projects don't come back and you can't create any new ones either.
They do come back if you restart the app.

@franknoirot

Copy link
Copy Markdown
Contributor Author

Something from user testing: if you disable Cloud-sync setting in the web, then your cloud projects go away (Expected), but your web-local projects don't come back and you can't create any new ones either.
They do come back if you restart the app.

Oh! Since we want that policy to be that web users can't have "local", directory-type libraries, should we model this as an optional plugin only on desktop, and a required extension on web?

franknoirot and others added 2 commits July 24, 2026 13:15
…orrectly

Addresses @andrewvarga's comment
[here](#12574 (comment))
by letting projects from cloud libraries supply their local sync
directory location into `ensureCloudProjectLocallySynced`, so that they
can materialize the project to the correct local path, which now might
not be the "default project directory" setting.
On web the Personal Cloud library replaces the default directory library
and points at the same OPFS folder, so after enabling cloud sync it is the
only configured library. But the `cloud` library type's readEntries/
createProject lived inside the toggle-able cloud-sync plugin slot, so
disabling cloud sync removed the only way to list or create projects,
stranding the user until reload.

- Register the `cloud` project-library type as always-on infrastructure
  (new extensions/cloudSyncLibrary barrel) instead of inside the plugin
  slot; the plugin now toggles only the sync surface (remote entries,
  status bar, project-menu actions, Personal Cloud row).
- Relax the type's createProject so it no longer requires sync to be
  enabled: it creates in the local folder and only enrolls for sync when
  cloud sync is on.
- Make cloud sync non-optional on web via a single registration: hide the
  activation toggle on web (hideOnPlatform threaded through createZdsPlugin)
  and have the auto-enable policy ignore a prior opt-out on web, keeping the
  setting the source of truth and the activation-keyed migrations untouched.
  Desktop stays opt-in.
- Add defense-in-depth warnings when a configured library has no registered
  type handler / no create target.

Addresses #12574 (comment)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@franknoirot

Copy link
Copy Markdown
Contributor Author

@andrewvarga this commit should address your comment af0c8f9. We'll block turning off cloud sync on web.

PluginsList rendered every plugin's activation toggle regardless of its
hideOnLevel/hideOnPlatform config, so on web a flagged user could still
flip cloud sync off from the Plugins tab even though its settings field
is hidden there.

Filter the list through the same shouldHideSetting logic the settings
panel and command bar use, and move cloud sync's feature gate onto its
activation setting (hideWithoutFeature) so the filter is generic instead
of special-casing the plugin id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remote-only cloud projects (listed from the cloud index but not yet
materialized locally) had their rename/delete actions disabled: the home
project capability guards required a local path, and the cloud
project-library type defined no rename/delete operations.

Add remote-acting rename/delete to the cloud library type. When a project
has a local materialization they mutate the local files and let sync
replicate; when it is still remote-only they act on the remote directly via
new engine functions (rename re-uploads the archive with a new title under
expected_revision, since the cloud API has no title-only update). Relax the
home project capability guards to drop the local-path requirement now that
each operation owns its local-vs-remote handling.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@franknoirot

franknoirot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

One critical thing I changed at the end here: I fixed a problem where cloud projects couldn't be renamed even after they were "materialized" locally. I fixed that by actually allowing renaming and deletion even on "unmaterialized" projects, which just updates the remote project entry. That required additional code, which is why the diff has grown.

@franknoirot
franknoirot merged commit 27e0872 into main Jul 24, 2026
54 checks passed
@franknoirot
franknoirot deleted the codex/cloud-project-library branch July 24, 2026 21:42
@andrewvarga

Copy link
Copy Markdown
Contributor

One critical thing I changed at the end here: I fixed a problem where cloud projects couldn't be renamed even after they were "materialized" locally. I fixed that by actually allowing renaming and deletion even on "unmaterialized" projects, which just updates the remote project entry. That required additional code, which is why the diff has grown.

Confirmed these are working!
One thing that seems missing from here still:

  • go offline in ZDS
  • create a project cloud project
  • it will be opened automatically (fine)
  • go back Home, try to open again
    -> won't open, even though it should be able to?

@franknoirot

Copy link
Copy Markdown
Contributor Author

Confirmed these are working! One thing that seems missing from here still:

  • go offline in ZDS
  • create a project cloud project
  • it will be opened automatically (fine)
  • go back Home, try to open again
    -> won't open, even though it should be able to?

Ah good point, you should be able to open locally-materialized projects from cloud projects without a connection. Let's make that an issue.

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.

4 participants