Skip to content

[Bug]: t3_project_update saves scripts and project defaults that are ignored once project settings are folded #14991

Open

Description

@AngeloTadeucci

Note

Written by Claude Opus 5.5 on behalf of @AngeloTadeucci, and filed at Angelo's request.

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Use an install whose settings.json has projectSettingsFolded: true. The one-time fold of legacy
    project settings sets it
    at serverSettings.ts#L615.
  2. From an agent, call t3_project_update with a setup action:
    scripts: [{ id: "setup", name: "Setup", command: "...", icon: "configure", runOnWorktreeCreate: true }].
    The response echoes the new scripts, and t3_project_read returns them.
  3. Call t3_thread_launch for that project with workspaceStrategy: { type: "worktree", ... }.

Expected behavior

The setup action runs in the new worktree. If the tool can no longer change this setting, it should
say so instead of reporting success.

Actual behavior

The worktree is created and no setup action runs. Once settings are folded, resolveProjectScripts
returns the projectSettingsOverrides entry or the environment default and never reads the project's
own scripts
at projectScripts.ts#L23.
t3_project_update still writes to the project aggregate, so the change is saved and then never read.

Writing the same action into projectSettingsOverrides.<projectId>.defaultProjectScripts in
settings.json made it run on the next launch, which confirms where the runtime reads from.

The same gate covers defaultModelSelection and defaultThreadEnvMode in resolveProjectOverrides
at projectSettings.ts#L151,
so I expect those fields of t3_project_update to be ignored too. I only verified scripts at
runtime, and I did not check autoPull.

Impact

Minor bug or occasional failure

Version or commit

desktop 0.0.46-nightly.20261003.2610; source read at 8ed276c246b6

Environment

Windows 11 Pro 10.0.26200, agent on Claude Code through the T3 MCP server

Workaround

Edit projectSettingsOverrides in settings.json, or set the action in the project settings UI.

Activity

  1. juliusmarminge commented on Oct 3, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the precise report, @AngeloTadeucci. We confirmed it on main at 8ed276c246. Once project settings have been folded, t3_project_update still saves scripts on the project and returns them, but launch never reads that copy. We didn't find an existing issue for this.

    What launch reads: ProjectSetupScriptRunner calls resolveProjectScripts in projectScripts.ts. If there's a projectSettingsOverrides[projectId].defaultProjectScripts entry, it wins. If projectSettingsFolded is set and there's no entry, launch uses the environment default and never looks at the project's own scripts. t3_thread_launch does pass project.scripts to the runner (ThreadLaunchService.ts), but the resolver drops them. The folded-settings test already expects that behavior.

    What the tool writes: t3_project_update only calls projects.update (handlers.ts), which saves to the project record. The tool says it updates project settings through the same service as the app (tools.ts), but the app's Actions UI now writes to projectSettingsOverrides instead. t3_project_read returns the project record, so the echo you saw is the stored value, not the list launch will run. t3_project_create also tells callers to set scripts or defaultModelSelection afterwards with t3_project_update (handlers.ts), so that follow-up hits the same gap. No MCP tool writes per-project overrides, and t3_environment_preferences_update only covers the whole environment.

    Folded is the normal state: foldLegacyProjectSettings turns the flag on at the next trusted settings load once any project row or legacy override exists, and after that it never reads the project record again (serverSettings.ts).

    The other fields: you read resolveProjectOverrides correctly (projectSettings.ts). Once folded, the project record's defaultModelSelection and defaultThreadEnvMode are ignored for new threads created in the app. autoPull is also never read on this path, even before folding. Auto-pull reads defaultAutoPull from the resolved settings (VcsStatusBroadcaster.ts), and the fold copies the stored value there only once. Title, icon, favicon, and workspace root still belong on the project record, so updating those works fine.

    Workaround: set the action in the project settings UI, or write projectSettingsOverrides.<projectId>.defaultProjectScripts the way you already did. The model, env mode, and auto-pull work the same way, through defaultModelSelection, defaultThreadEnvMode, and defaultAutoPull.

    We're leaving this open as a bug. When t3_project_update reports success, the change should actually reach what launch and the new-thread UI read, which means writing these fields into projectSettingsOverrides.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 3, 2026
  3. invivek26 commented on Oct 8, 2026

    @invivek26

    Confirming on macOS (0.0.46-nightly.20261007.2774), and it's broader than t3_project_update. A "Setup worktree" action (runOnWorktreeCreate: true) added after projectSettingsFolded was set is ignored too, and so is the checked-in t3.json. Every new worktree thread silently skipped setup: ProjectSetupScriptRunner.runForThread returned no-script in about 0.02 ms and never opened a terminal, with nothing shown in the UI.

    Writing projectSettingsOverrides.<projectId>.defaultProjectScripts fixed it immediately, without a restart.

    Suggest falling back to project.scripts when there's no override, or showing a warning when a runOnWorktreeCreate script won't run. For us the impact was "every new worktree unusable until set up by hand", higher than "Minor".

  4. jheinem1 commented on Oct 9, 2026

    @jheinem1

    Note

    Written by Claude Opus 5.5 on behalf of @jheinem1, and posted at their request.

    Confirming on 0.0.46-nightly.20261008.2819 (Windows desktop app connected to a WSL server). It isn't limited to setup actions: scripts saved through t3_project_update also never appear in the thread header's project scripts menu. The menu shows only Add project script, even after a reload. Two projects were affected. In both, t3_project_read and projection_projects.scripts_json show the scripts, and the client's IndexedDB cache contains them, but the menu is empty because projectSettingsFolded is true.

    A script added through the UI went to projectScriptOverrides and projectSettingsOverrides.<projectId>.defaultProjectScripts in settings.json and showed up right away. Writing the tool-set scripts into those same keys is the workaround.

  5. coygeek commented on Oct 15, 2026

    @coygeek

    Note

    Written by Claude Opus 5.5 and posted at the account holder's request.

    This still happens on macOS 0.0.46-nightly.20261014.3132 (5a4e998). Two MCP entry points not covered above hit the same gap:

    • t3_project_create with a workspaceRoot accepts scripts in the same call and saves them on the project record (handlers.ts#L248-L249). On a folded install, a project created with a runOnWorktreeCreate action has no setup action from the start.
    • delegate_task with isolation: "worktree" promises a checkout "with project setup completed before the child starts" (tools.ts#L68). For a project created as above, the worktree was created and the child started without setup. ProjectSetupScriptRunner.runForThread returned in about 0.03 ms without opening a terminal, so the setup stage was marked skipped (ThreadLaunchService.ts#L617). Both the delegate_task and task_status results reported completed, with nothing to show setup had not run. The file the setup action writes was missing in the child's worktree.

    Agents use worktree isolation to get a prepared checkout, so until this is fixed, a skipped setup should at least appear in the delegate_task and task_status results.

  6. NeilMazumdar commented on Oct 17, 2026

    @NeilMazumdar

    Still happens on macOS 0.1.1-nightly.20261017.3272. Scripts set through t3_project_update were saved to the project record and returned by t3_project_read, but the header's scripts menu stayed empty, including after quitting and reopening the app. Writing the same scripts to projectSettingsOverrides.<projectId>.defaultProjectScripts in settings.json made them appear.

  7. torturado commented on Oct 19, 2026

    @torturado

    Confirming this behavior on October 19, 2026 in a T3 Code MCP-connected project. It also reproduces with a regular project action, not only a setup/worktree hook:

    • t3_project_update succeeded with a script named Build release APK, using id build-release-apk, icon build, and runOnWorktreeCreate: false.
    • t3_project_read returns that script from the project record, but it does not appear in the project's actions menu.
    • A separate action (a) added through the UI does appear in the menu, but is not returned by t3_project_read.

    The script id is a valid slug and the icon is an allowed value, so this does not appear to be a malformed payload. This confirms the project-record/settings-override mismatch described here for a normal menu action too. I verified persistence and visibility; I have not tested whether the MCP-written action runs.

  8. dmeyerhoff commented on Oct 21, 2026

    @dmeyerhoff

    Note

    Written by Claude Opus 5.5 on behalf of @dmeyerhoff.

    Possibly the same root cause from the other side: on a folded install, scripts added through the GUI are saved in settings.json → projectSettingsOverrides.<projectId>.defaultProjectScripts, but t3_project_read returns scripts: [] for that project (and projection_projects.scripts_json is []). So an agent can't see the project's actions. T3 Code (Beta) 0.1.0, macOS.

  9. JoakimBellander commented on Oct 24, 2026

    @JoakimBellander

    Still happens on 0.1.3-nightly.20261024.3617 (Linux server, macOS desktop client), and confirming the defaultThreadEnvMode case at runtime. An agent set a project to defaultThreadEnvMode: "local" through t3_project_update, and t3_project_read still returns local, but new threads follow the global default instead. After switching global to worktree, every new thread created from web/desktop (26 of 26) and mobile (3 of 3) got a fresh worktree, leaving 29 unneeded worktrees in about 2.5 days. I've applied the project-scope override from triage as a workaround.

    On #22827: it notes that existing aggregate-only saves stay inactive until re-saved. This install is that case. The value was saved through the tool after the fold and is still ignored, while t3_project_read keeps reporting it, so nothing tells the user to re-save. Reconciling existing project values into overrides once (or showing the mismatch) would cover them.

  10. adambuchweitz commented on Oct 29, 2026

    @adambuchweitz
    Contributor

    Confirming this exact mismatch on October 29, 2026 in T3 Code: t3_project_update accepted the four Listwork actions and returned them, but the Settings → Code → Projects → Actions panel still showed “No actions configured.” The reason matches this issue: the project tool writes the legacy project record while the panel reads projectSettingsOverrides.<projectId>.defaultProjectScripts. I cleared the misleading legacy scripts and applied the actions through the settings override workaround.

  11. stanley50z commented on Nov 1, 2026

    @stanley50z

    Note

    Written by Claude Opus 5.5 on behalf of @stanley50z, and posted at their request.

    Still happens on Windows desktop 0.1.6-preview.20261030.3948 and macOS 0.1.3-nightly.20261025.3669, both with projectSettingsFolded: true.

    One case not covered above: removing scripts is lost too. With actions saved in projectSettingsOverrides.<projectId>.defaultProjectScripts, t3_project_update with scripts: [] returns an empty list and t3_project_read agrees, but the override in settings.json is unchanged. resolveProjectScripts keeps returning the old actions, so an agent asked to remove stale actions reports success while they stay in the menu. Writing "defaultProjectScripts": [] into the override removes them.

  12. Averus89 commented on Nov 2, 2026

    @Averus89

    Note

    Written by Claude Opus 5.5 on behalf of @Averus89, and posted at their request.

    Still happens on Linux server t3 v0.1.5 with projectSettingsFolded: true, and it affects scheduled tasks: three projects got defaultModelSelection (Claude Sonnet, Codex gpt-6.1-sol, Grok 4.7 Fast) via t3_project_update, and t3_project_read returns them, but their interval and webhook tasks run as the model of the thread that created the task (claude-opus-5-5). For us that meant an agent meant to be a different reviewer was the same model. schedule_task has no model field, so there is no way to choose the model per task.

  13. added a commit that references this issue on Nov 4, 2026
  14. DanielJomphe commented on Nov 5, 2026

    @DanielJomphe

    Note

    Written by Claude Opus 5.5 (Claude Code, via T3 Code triage) on behalf of @DanielJomphe, and posted at their request.

    Important

    Fixing the write path, as triage proposed, does not fix the read path. t3_project_read and t3_project_list return the legacy project row for the folded fields. Edits made in Settings never reach that row. So agents keep reporting stale settings even after t3_project_update writes to projectSettingsOverrides. This happens with no MCP write involved.

    Seen on macOS desktop 0.1.8-nightly.20261105.4218 (337c5858, same as main today), with projectSettingsFolded: true.

    1. Stale reads with no t3_project_update involved

    ProjectService.toProject returns the raw projection_projects columns for defaultModelSelection, defaultThreadEnvMode, autoPull and scripts (apps/server/src/project/ProjectService.ts:177-181).

    • Project A's column held codex / gpt-5.6-luna / xhigh from before the fold. The model was later reset in Settings, so the override has no defaultModelSelection, the page shows the inherited Opus 5.5 / High, and new threads start on it. t3_project_list still returned Luna, and an agent told the user the project was set to Luna.
    • All 12 projects return autoPull: false. The global defaultAutoPull is true, and no project overrides it.

    Fix needed: the read tools return the effective value for each folded field, ideally with its source (project or environment). Alternatively, they drop those fields.

    2. A one-time reconcile from the row would undo resets made in Settings

    An earlier comment suggested copying existing project values into the overrides once. For project A, that would bring back the Luna model the user had reset. The fold itself guards against this. Its comment says "projectSettingsFolded prevents restarts from undoing later UI resets" (serverSettings.ts:605). After the fold, the row is not a reliable source for any of these fields. The safe direction is to stop reading it, or to null the folded columns, not to copy from it.

    3. schedule_task with no parent thread reads the raw model

    OrchestratorMcpService.scheduleTask uses parent?.thread.modelSelection ?? project.defaultModelSelection (lines 2001 and 1040–1050). This differs from the scheduled-task report above, where a parent thread's model was used. Without a parent thread, it takes the stale row model. When the row is null, it fails with "has no default model", even when Settings defines a project or environment default. Once writes go to the overrides, the row stays null for new settings, so this failure becomes the usual result. It should use resolveProjectSettings(...).defaultModelSelection, as t3_thread_launch already does (mcp/toolkits/project/handlers.ts:183).

    Local cleanup: t3_project_update with defaultModelSelection: null cleared project A's stale column. There is no equivalent for autoPull or scripts, because writing effective values into the row would only make copies that go stale again.

  15. paul-vd commented on Nov 5, 2026

    @paul-vd
    Contributor

    Note

    Written by Claude Opus 5.5 (Claude Code, via T3 Code triage) on behalf of the account holder, and posted at their request.

    Still happens on macOS desktop 0.1.8-nightly.20261105.4234 (8b570d2) with projectSettingsFolded: true. This is the read side of defaultModelSelection, and it caused an agent to launch a thread on the wrong model.

    What happened

    • A project was registered again at a new folder through t3_project_create, after the fold had already run. Its project record got defaultModelSelection: claude-fable-5-1, the value the old project at the old folder had. projectSettingsOverrides.<projectId> has no defaultModelSelection.
    • The app ignores the record value, as described above. The project settings page and new threads in the UI use the environment default, Opus 5.5 High.
    • t3_project_list and t3_project_read still return the record's defaultModelSelection as the project's default. An agent dispatching work with t3_thread_launch read it and passed it as modelSelection. The thread started on a model the user had never chosen and could not find anywhere in Settings. They had to stop it and restart it on the right model.

    Repro

    1. Use an install with projectSettingsFolded: true.
    2. Give a project a record-level model with t3_project_create or t3_project_update (defaultModelSelection: { instanceId: "claudeAgent", model: "<model A>" }). Set the environment default to a different model B.
    3. Settings and new UI threads show model B. t3_project_list and t3_project_read report model A as the project default.

    One more path, read from code but not run
    OrchestratorMcpService.projectDefaultModelSelection (OrchestratorMcpService.ts L1040-L1050 at 8b570d2, still present on main) returns the record field. A caller with no parent thread would get the ignored record model, or a "has no default model" error when only the override is set.

    Suggestion
    Have the project tools report the resolved value from resolveProjectSettings, or at least not present a record-only value as the default once settings are folded.

    Workaround
    Clear the stale record value with t3_project_update and defaultModelSelection: null. Verified here: the record and t3_project_read now return null, so the tools no longer contradict the settings page. The environment default and the project's override entry were not changed.

  16. added a commit that references this issue on Nov 6, 2026
  17. ThatChair commented on Nov 6, 2026

    @ThatChair

    I hit this too, and I dug into the source. The mechanism checks out.

    • Version: server 0.1.8-nightly.20261106.4262, Linux x64. I read main at 6577c6b, so line numbers may drift a bit.
    • What happened: The project had projectSettingsOverrides[<project>].defaultProjectScripts set to []. An agent then called t3_project_update with two scripts. The tool reported success, but the UI still showed no scripts.
    • Why: resolveProjectScripts (packages/shared/src/projectScripts.ts) returns projectSettingsOverrides[id].defaultProjectScripts whenever it's defined, even if it's []. After folding, it falls back to the global defaultProjectScripts and never reads project.scripts. t3_project_update writes project.scripts, so once settings are folded, nothing it writes is ever shown.
    • Workaround that worked: Writing the scripts into that projectSettingsOverrides entry in settings.json made them appear without a restart.

    Suggested fix: when settings are folded, have t3_project_update write scripts to projectSettingsOverrides[id].defaultProjectScripts, or return an error saying the value won't take effect.


    Posted by Haiku 5.5 acting on behalf of Thatcher.

  18. dnevzorov commented on Nov 6, 2026

    @dnevzorov

    Note

    Written by Claude Opus 5.5 on behalf of @dnevzorov, and posted at their request.

    Still happens on macOS 0.1.7, with projectSettingsFolded: true. Actions saved with t3_project_update on 5 projects never showed in Settings → Projects → Actions.

    Workaround without editing settings.json: write the same actions to t3.json at the repo root (drop id), then use Import from t3.json in Settings → Projects → Actions. The import writes projectSettingsOverrides.<projectId>.defaultProjectScripts.

  19. hardikg2907 commented on Nov 8, 2026

    @hardikg2907

    Note

    Written by Claude Opus 5.5 on behalf of @hardikg2907, and posted at their request.

    Still reproduces on desktop 0.1.8-nightly.20261107.4340 (macOS).

    One more visible symptom: the scripts also never show in the app. The thread's side panel lists only "Add project script".

    1. Create a project with t3_project_create, then set four scripts with t3_project_update.
    2. t3_project_read returns all four, and a second t3_project_update moves updatedAt.
    3. In the app, the side panel for a thread in that project shows no scripts.

    settings.json has projectSettingsFolded: true and an empty projectSettingsOverrides, which matches the triage above: the app reads only the overrides. The project was created over MCP, so its id has the mcp: prefix. I don't think the prefix matters, but I note it in case it does.

  20. added a commit that references this issue on Nov 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions