feat(app): link verified projects locally - #4052
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b566b72be4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2d0c6d59f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
| Filename | Overview |
|---|---|
| packages/app/src/projects/local-project-links.ts | Defines Git identity normalization, link validation, suggestions, and conservative grouping overrides. |
| packages/app/src/projects/workspace-structure.ts | Integrates validated local links into the canonical grouped-project projection. |
| packages/app/src/hooks/use-projects.ts | Supplies hydration state, stored links, placements, and suggestions to project derivation. |
| packages/app/src/projects/local-project-links-store.ts | Adds validated device-local persistence and link/unlink actions. |
| packages/app/src/components/project-links-sheet.tsx | Adds the project-link review, confirmation, unavailable-member, and unlink interface. |
| packages/app/e2e/browser/sidebar-project-grouping.spec.ts | Covers reviewing, unlinking, and retaining access to saved links when a project disappears. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Host project and workspace snapshots] --> B[Resolve normalized Git identity]
B --> C{Same remote and subdirectory?}
C -- Yes --> D[Offer device-local link]
D --> E[Persist link locally]
E --> F{All members present and verified?}
F -- Yes --> G[Apply linked grouping override]
F -- No --> H[Defer or block override for review]
H --> I[Keep saved link available for unlinking]
Reviews (3): Last reviewed commit: "test(app): cover linked hosts omitted du..." | Re-trigger Greptile
8c7fd4d to
d8338c6
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d8338c6b8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3442c65d2e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| projectLinkSuggestions: buildProjectLinkSuggestions({ | ||
| placements: projectLinkPlacements, | ||
| links: localProjectLinks, | ||
| }), |
There was a problem hiding this comment.
Exclude unhydrated replicas from link suggestions
When a host still has an unhydrated cached directory, its cached Git identity is included in projectLinkPlacements and passed directly to the suggestion builder, even though the same function already identifies that host as unhydrated for grouping. Because ProjectsScreen renders cached projects while loading, the user can accept a purportedly Git-verified suggestion based on stale remote or subdirectory facts; after hydration, the newly persisted link can immediately become invalid or may have linked the wrong projects. Gate suggestions until every participating replica is hydrated, or exclude unhydrated placements from suggestion generation.
Useful? React with 👍 / 👎.
| serverId: z.string().trim().min(1), | ||
| projectId: z.string().trim().min(1), |
There was a problem hiding this comment.
Preserve opaque member IDs verbatim
When a compatibility project has a path-shaped opaque projectId containing leading or trailing whitespace—for example, a valid POSIX project root ending in a space—these .trim() transforms rewrite its identity during persistence; normalizeMembers also performs the same rewrite at link creation. The saved member then cannot match the exact (serverId, projectId) placement, so a newly accepted link is immediately treated as invalid and never groups. Validate non-emptiness without altering these opaque IDs.
Useful? React with 👍 / 👎.
3442c65 to
fe4288e
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe4288ea91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| link.members.some((member) => unhydratedServerIds.has(member.serverId)) || | ||
| memberKeys.some((memberKey) => !placementsByKey.has(memberKey)) | ||
| ) { |
There was a problem hiding this comment.
Distinguish deleted members from omitted hosts
When every participating host is hydrated and a linked project has actually been deleted, this branch treats the missing placement like a temporarily omitted host and skips all overrides. The surviving member can then fall back to automatic projectKey grouping with another placement, contradicting the new data-model contract that a missing Git fact disables the saved link rather than silently regrouping it. Defer only for omitted or unhydrated hosts; for an authoritative deletion, keep the present members blocked until the link is reviewed.
Useful? React with 👍 / 👎.
| let value = input.trim().replaceAll("\\", "/"); | ||
| if (!value) return null; |
There was a problem hiding this comment.
Preserve trailing whitespace in project paths
On POSIX hosts, trailing spaces are valid filename characters, but trimming the complete path rewrites /repos/app/pkg to /repos/app/pkg. Thus a project rooted in pkg and one rooted in pkg can derive the same supposedly verified subdirectory identity and be offered for linking even though they select different directories. Check for an empty input without removing whitespace from filesystem path segments.
Useful? React with 👍 / 👎.
|
Closing this feature PR. Please discuss the workflow and shared need in Discussions, following CONTRIBUTING.md. |
Summary
Validation
npx vitest run --config vitest.config.ts src/screens/projects-screen.test.tsx src/projects/local-project-links.test.ts src/projects/workspace-structure.test.ts src/hooks/use-projects.test.ts src/i18n/resources.test.ts --bail=1(62 passed)npm run lintnpm run typecheckPLAYWRIGHT_BROWSERS_PATH=/tmp/paseo-playwright-browsers npm run test:e2e --workspace=@getpaseo/app -- e2e/browser/sidebar-project-grouping.spec.ts --grep 'reviews and unlinks a device-local cross-host project link|keeps saved project links reachable after their host project is removed'(2 passed; isolated daemons)