Skip to content

Add per-project settings - #2567

Closed
shivamhwp wants to merge 39 commits into
pingdotgg:mainfrom
shivamhwp:per-project-settings
Closed

Add per-project settings #2567
shivamhwp wants to merge 39 commits into
pingdotgg:mainfrom
shivamhwp:per-project-settings

Conversation

@shivamhwp

@shivamhwp shivamhwp commented May 6, 2026

Copy link
Copy Markdown
Collaborator

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

  • Open project settings directly from the project row in the sidebar; the separate caret still expands and collapses its threads.
  • Configure the project display name, default model, and inspect its workspace path.
  • Enable or disable provider instances per project while requiring at least one provider to remain available.
  • Configure a custom source-control provider, remote name, remote URL, and web URL when automatic detection is not sufficient.
  • Inspect the detected Git root and branch and set a project-specific background fetch interval.
  • Add and edit project-scoped actions and their keybindings.
  • Configure project action environment variables with validation for duplicate, invalid, and reserved T3CODE_* keys.

Runtime integration

  • Adds projects.getDetails and projects.updateSettings RPCs backed by per-project settings in ServerSettings.
  • Applies project remote overrides to provider resolution, Git actions, status broadcasting, diff/branch UI, and stacked VCS actions.
  • Keys relevant VCS caches by project context and refreshes sibling entries for the same repository.
  • Filters disabled providers in the composer and blocks thread.turn.start when the selected provider is disabled for that project.
  • Merges the project action environment into project scripts and setup-script execution.
  • Seeds new-thread model selection from the project default before falling back to sticky/global defaults.

Save and refresh safeguards

  • Project-settings map patches merge by project instead of replacing unrelated project entries.
  • Optimistic values are rolled back on failed saves and are cleared only after refreshed details contain the committed values.
  • Commit queues are isolated across project route changes so an older refresh cannot clear a newer project's draft.
  • Disabling the current default provider clears the default model only after the provider update succeeds.

UI

Grouped project environments

Project environment selector with grouped entries

General settings and provider availability

Project settings general section and provider controls

Custom remote, fetch interval, and project actions

Project custom remote, fetch interval, and actions

Action environment

Project action environment variables

Validation

  • 16 focused test files passed: 470 tests.
  • Targeted formatting, lint, and type checks passed for the affected server, web, shared, contracts, and client-runtime scopes.
  • Verified in a real Helium browser against an isolated local environment.
  • Confirmed persistence after reload for project name, provider policy, default-model fallback, custom remote, fetch interval, and action environment values.

Checklist

  • I explained what changed and why.
  • I included current screenshots for the new UI.
  • Focused automated tests pass.
  • The affected web flow was verified in a real browser.
  • No video needed; no animation or motion behavior changed.

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.getDetails and projects.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. SourceControlProviderRegistry applies project remote overrides before detected remotes; GitManager prefers branch remotes over override-only context where appropriate.

Runtime: project actionEnvironment is merged into setup-script terminal env; thread.turn.start rejects disabled providers for the project.

Misc: shared platformError type guards replace instanceof PlatformError in several paths; mobile project favicon uses rounded-project-icon; @pierre/diffs import fix; CLI runMain typing + 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

  • Introduces 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.
  • Adds two new WebSocket RPCs (projects.getDetails, projects.updateSettings) backed by EnvironmentApi that return detected VCS info combined with persisted settings and enforce that at least one provider stays enabled per project.
  • Adds a ProjectSettingsDialog component mounted at the app root and opened via a centralized store; the sidebar project header and context menu now open this dialog.
  • VCS status caching and provider resolution (VcsStatusBroadcaster, GitManager, SourceControlProviderRegistry) are now keyed by (cwd, projectId) so project overrides (remote URL, provider) are respected and sibling cache entries are refreshed together.
  • New threads honor per-project defaultThreadEnvMode and newWorktreesStartFromOrigin overrides and seed model selection from the project's default model if configured.
  • projectScriptRuntimeEnv now strips user-supplied T3CODE_* env vars and gives base runtime env precedence over user extraEnv.
  • Risk: vcs.refreshStatus now expects a VcsStatusInput object instead of a bare cwd string; callers passing a plain string still work via a compatibility shim but typed callers must update.

Macroscope summarized a13478b.

@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 78e8bd91-0174-4761-a0cd-92a7d6ba80b4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels May 6, 2026
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/components/Sidebar.tsx Outdated
@macroscopeapp

macroscopeapp Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: 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.

@shivamhwp shivamhwp changed the title Add per-project settings and remote overrides Add per-project settings May 7, 2026
@shivamhwp shivamhwp changed the title Add per-project settings Add per-project settings (wip) May 7, 2026
Comment thread apps/server/src/sourceControl/RemoteOverride.ts
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/server/src/git/GitManager.ts
Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/server/src/ws.ts
Comment thread apps/web/src/hooks/useHandleNewThread.ts
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/web/src/routes/projects.$environmentId.$projectId.tsx Outdated
Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/lib/projectScriptKeybindings.ts Outdated
Comment thread apps/web/src/lib/projectScriptKeybindings.ts
Comment thread apps/server/src/provider/Layers/ProviderRegistry.test.ts
Comment thread apps/server/src/serverSettings.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/web/src/lib/projectScriptKeybindings.ts
Comment thread apps/web/src/components/ChatView.tsx
@shivamhwp shivamhwp changed the title Add per-project settings (wip) Add per-project settings May 8, 2026
Comment thread apps/server/src/sourceControl/SourceControlProviderRegistry.ts
Comment thread apps/web/src/components/DiffPanel.tsx
Comment thread apps/web/src/components/ChatView.tsx Outdated
Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts Outdated
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/components/chat/ChatComposer.tsx Outdated
Comment thread apps/server/src/provider/Layers/ProviderRegistry.test.ts
Comment thread apps/web/src/lib/gitStatusState.ts Outdated
Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts
Comment thread apps/server/src/ws.ts
Comment thread apps/server/src/project/ProjectSetupScriptRunner.ts Outdated
Comment thread apps/web/src/projectSettingsCommit.ts
Comment thread apps/server/src/ws.ts
Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts
# Conflicts:
#	apps/web/src/components/ChatView.tsx
#	apps/web/src/components/ProjectScriptsControl.tsx
Comment thread apps/server/src/sourceControl/SourceControlProviderRegistry.ts
Comment thread apps/web/src/components/ProjectSettingsDialog.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
Comment thread apps/server/src/sourceControl/SourceControlProviderRegistry.ts
Comment thread apps/web/src/components/T3ProjectFileSettings.tsx
# 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

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

Effect service conventions review: one finding on the new ProjectDetailsError.

Posted via Macroscope — Effect Service Conventions

Comment thread packages/contracts/src/project.ts Outdated
Co-Authored-By: Claude <noreply@anthropic.com>
const { draft, source, validationError } = draftState;
const draftKey = t3ProjectFileDraftKey(draft);
const isDirty = draftKey !== t3ProjectFileDraftKey(source);
const editingDisabled =

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.

🟡 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.

shivamhwp and others added 2 commits July 29, 2026 21:00
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 =

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.

🟠 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.

Comment thread apps/web/src/components/ProjectSettingsDialog.tsx
Comment thread apps/web/src/components/T3ProjectFileSettings.tsx
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>

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

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).

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 d4da67b. Configure here.

Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts
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>
@juliusmarminge

Copy link
Copy Markdown
Member

Closing in favor of #2829 (orchestration V2).

#2829 deletes the V1 orchestration layer this PR builds on — apps/server/src/orchestration/**, provider/Layers/*Adapter.ts and provider/Services/** are removed and replaced by apps/server/src/orchestration-v2/**, with the IPC surface renamed to ORCHESTRATION_V2_WS_METHODS. The files this PR touches either no longer exist or are rewritten, so it can't be rebased — it would need reimplementing against the V2 adapters.

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 main, port the change to the V2 equivalent, and reopen (or open a fresh PR). Ping me and I'll prioritise the review.

@shivamhwp

Copy link
Copy Markdown
Collaborator Author

no worries.
you can also just ping me when you merge the orchestration one. then i can start working on it again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants