Add per-project settings - #2567
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review 2 blocking correctness issues found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
# Conflicts: # apps/web/src/components/ChatView.tsx # apps/web/src/components/ProjectScriptsControl.tsx
# Conflicts: # apps/server/src/git/GitManager.test.ts # apps/server/src/git/GitManager.ts # apps/server/src/serverSettings.ts # apps/web/src/components/GitActionsControl.tsx # packages/contracts/src/model.ts # packages/contracts/src/settings.ts # packages/shared/src/serverSettings.ts
# Conflicts: # apps/web/src/components/ChatMarkdown.tsx # apps/web/src/components/ProjectFavicon.tsx # apps/web/src/components/Sidebar.tsx # apps/web/src/components/SidebarV2.tsx
There was a problem hiding this comment.
Effect service conventions review: one finding on the new ProjectDetailsError.
Posted via Macroscope — Effect Service Conventions
Co-Authored-By: Claude <noreply@anthropic.com>
| const { draft, source, validationError } = draftState; | ||
| const draftKey = t3ProjectFileDraftKey(draft); | ||
| const isDirty = draftKey !== t3ProjectFileDraftKey(source); | ||
| const editingDisabled = |
There was a problem hiding this comment.
🟡 Medium components/T3ProjectFileSettings.tsx:80
When state.status === "unavailable", the editor is left enabled and Save becomes a Create button, but unavailable is used for any read failure — not just a missing file. If an existing t3.json is temporarily unreadable due to an RPC or permission error, this component shows an empty draft and allows the user to save it, overwriting the real repository configuration and deleting all of its actions and icon settings. The editingDisabled check at line 80 only blocks loading, disabled, and invalid, leaving unavailable fully editable. Consider distinguishing a confirmed not-found result from other read failures, and only permit creation when the file is genuinely absent.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/T3ProjectFileSettings.tsx around line 80:
When `state.status === "unavailable"`, the editor is left enabled and `Save` becomes a `Create` button, but `unavailable` is used for any read failure — not just a missing file. If an existing `t3.json` is temporarily unreadable due to an RPC or permission error, this component shows an empty draft and allows the user to save it, overwriting the real repository configuration and deleting all of its actions and icon settings. The `editingDisabled` check at line 80 only blocks `loading`, `disabled`, and `invalid`, leaving `unavailable` fully editable. Consider distinguishing a confirmed not-found result from other read failures, and only permit creation when the file is genuinely absent.
Projects can now pin whether new threads start in the checkout or in a
fresh worktree instead of always following the app-wide New threads
setting. Both keys default to null ("Global"), so untouched projects keep
the existing behavior and still track later changes to the global setting.
The per-project value lives in ProjectSettings on the project's own
environment, alongside the other project overrides, and resolves one key
at a time: a project can pin the mode while still inheriting the global
start-from-origin choice.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Project settings opened as a full route, which meant leaving the thread you were working in to change a project default. It is a dialog now, mounted once at the app root and opened from either sidebar (row click and the context menu) through a small store, so every entry point shares one surface and the URL never changes. The dialog leads with the settings people actually reach for — grouping rule, new threads, default model, providers — and hides the rest behind Advanced settings. Opening that pins the dialog height and scrolls the extra rows instead of resizing the dialog under the pointer; the height is measured from clientHeight because the popup carries an open animation transform that would otherwise pin a short height. Rows share one rhythm: a 48px floor, 4px gaps, and the same type scale as the settings pages. t3.json is ruled off as its own section, and its shared actions are now a row per action with a dialog editor that writes the file in one step, matching how local project actions already work. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| setEditorError("Command is required."); | ||
| return; | ||
| } | ||
| const scripts = |
There was a problem hiding this comment.
🟠 High components/T3ProjectFileSettings.tsx:218
commitEditorAction captures the edited script's position by array index, but that index can become stale while the dialog is open. If the sourceDraft effect reconciles the draft to a refreshed t3.json that inserted, removed, or reordered scripts, the draft.scripts.map(... index === editorState.index ...) branch replaces whichever script now occupies that index with the stale editor entry. The subsequent save(nextDraft) then silently overwrites the wrong shared action. Consider closing or reconciling the editor when the source changes, or using a stable identity (e.g. script.id) to locate the target before writing.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/T3ProjectFileSettings.tsx around line 218:
`commitEditorAction` captures the edited script's position by array index, but that index can become stale while the dialog is open. If the `sourceDraft` effect reconciles the draft to a refreshed `t3.json` that inserted, removed, or reordered scripts, the `draft.scripts.map(... index === editorState.index ...)` branch replaces whichever script now occupies that index with the stale editor entry. The subsequent `save(nextDraft)` then silently overwrites the wrong shared action. Consider closing or reconciling the editor when the source changes, or using a stable identity (e.g. `script.id`) to locate the target before writing.
Seven files conflicted. Resolutions: SidebarV2 — main grew its own in-sidebar project settings dialog while this branch was replacing that surface with the standalone ProjectSettingsDialog. Kept this branch's removal: the project actions button opens the new dialog, which covers everything main's dialog did plus providers, default model, git, t3.json and scripts. Main's clipboard helpers stay, since it also wired them into the thread rows, which this branch keeps. ws.ts — main extracted the RPC scope map into auth/RpcAuthorization.ts as a type-checked object, so the local Map is gone and this branch's two project methods move into that map instead. The project-details helpers stay. VcsStatusBroadcaster — main added per-cwd demand tracking so background policy can gate remote polling; this branch keys pollers by project-aware input. Both survive: the demand map threads through the composite-key poller. The rest were additive: settings tests keep both sides' cases, imports merge. Two type fixes the merge forced. LocalApi.server is gone from main, so syncProjectScriptKeybinding takes a small port that ChatView and the settings dialog implement over the serverEnvironment RPC commands. And the ServerSettings mock in makeManagedServerProvider.test.ts needed this branch's project methods. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 3 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d4da67b. Configure here.
Main landed the project file picker and content search (pingdotgg#4855), which adds project RPCs beside this branch's. Every conflict was a keep-both: - rpc.ts, RpcAuthorization.ts: projectsSearchContents sits alongside projectsGetDetails and projectsUpdateSettings in the method map, the RPC export list, and the scope map. - ws.ts, contracts/project.ts: import lists gain both sides' symbols. - contracts/project.test.ts: main's search-input describe block joins this branch's project-details fixtures. - SidebarV2: the thread row keeps main's aria-busy for title regeneration and this branch's sidebar-v2-thread-card class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Closing in favor of #2829 (orchestration V2). #2829 deletes the V1 orchestration layer this PR builds on — This is not a judgement on the change itself. Several of these are real gaps we still want fixed; the base just moved out from under them. Once #2829 merges, please rebase onto |
|
no worries. |

Summary
Adds a project settings page for configuring behavior that belongs to one project without changing global server settings or renaming the folder on disk.
Project settings are persisted by the server and applied throughout source-control detection, VCS status, provider selection, thread startup, setup scripts, and project actions.
What changed
Project settings UI
T3CODE_*keys.Runtime integration
projects.getDetailsandprojects.updateSettingsRPCs backed by per-project settings inServerSettings.thread.turn.startwhen the selected provider is disabled for that project.Save and refresh safeguards
UI
Grouped project environments
General settings and provider availability
Custom remote, fetch interval, and project actions
Action environment
Validation
Checklist
Note
High Risk
Touches core git/VCS caching, source-control provider selection, and project settings persistence; sibling cache fan-out and override precedence can change behavior for multi-project repos sharing one cwd.
Overview
Adds per-project persisted settings on the server (
getProjectSettings/updateProjectSettings) and wires them through git status, source-control provider resolution, setup scripts, and turn-start provider policy.New RPCs
projects.getDetailsandprojects.updateSettings(with auth scopes) return detected vs effective remotes, project settings, and enforce “at least one provider enabled” on updates.Project-aware VCS: status and PR lookup caches key on
(cwd, projectId); invalidation and refreshes fan out to sibling cache entries for the same repo.SourceControlProviderRegistryapplies project remote overrides before detected remotes;GitManagerprefers branch remotes over override-only context where appropriate.Runtime: project
actionEnvironmentis merged into setup-script terminal env;thread.turn.startrejects disabled providers for the project.Misc: shared
platformErrortype guards replaceinstanceof PlatformErrorin several paths; mobile project favicon usesrounded-project-icon;@pierre/diffsimport fix; CLIrunMaintyping + diagnostics pragma.Reviewed by Cursor Bugbot for commit a13478b. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add per-project settings with provider overrides, action environments, and VCS remote overrides
ProjectSettings(stored in server settings) with per-project fields: disabled provider instances, action environment variables, default thread env mode, worktree start-from-origin, and remote override.projects.getDetails,projects.updateSettings) backed byEnvironmentApithat return detected VCS info combined with persisted settings and enforce that at least one provider stays enabled per project.ProjectSettingsDialogcomponent mounted at the app root and opened via a centralized store; the sidebar project header and context menu now open this dialog.VcsStatusBroadcaster,GitManager,SourceControlProviderRegistry) are now keyed by(cwd, projectId)so project overrides (remote URL, provider) are respected and sibling cache entries are refreshed together.defaultThreadEnvModeandnewWorktreesStartFromOriginoverrides and seed model selection from the project's default model if configured.projectScriptRuntimeEnvnow strips user-suppliedT3CODE_*env vars and gives base runtime env precedence over userextraEnv.vcs.refreshStatusnow expects aVcsStatusInputobject instead of a barecwdstring; callers passing a plain string still work via a compatibility shim but typed callers must update.Macroscope summarized a13478b.