[codex] Use updated web splash icon#3672
Conversation
Polish open-with controls
Align apps with Cotrim design system
Prune mobile and marketing surfaces
Add projectless chat support
Add flexible scheduled task checks across server and web
Update app icons for all targets
Add Kupo empty thread welcome
Add Mognet workflow MCP tools
Show status on standalone chat rows
Respect new chat worktree defaults
…trols Add standalone chat sidebar controls
Sync upstream t3code (2026-07-03)
|
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 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 |
| const [form, setForm] = useState<FormState>(() => formFromTask(task, defaults)); | ||
| const [saving, setSaving] = useState(false); | ||
|
|
||
| useEffect(() => { |
There was a problem hiding this comment.
🟡 Medium settings/ScheduledTasksSettings.tsx:320
The useEffect at lines 320–325 resets the form state whenever defaults changes. Because defaults is a memo recomputed from visibleProviders, projects, and settings, any background atom update while the dialog is open triggers setForm(formFromTask(task, defaults)), discarding any title, prompt, or model edits the user has already typed. Consider resetting the form only when open or task changes, not on every defaults refresh.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/ScheduledTasksSettings.tsx around line 320:
The `useEffect` at lines 320–325 resets the form state whenever `defaults` changes. Because `defaults` is a memo recomputed from `visibleProviders`, `projects`, and `settings`, any background atom update while the dialog is open triggers `setForm(formFromTask(task, defaults))`, discarding any title, prompt, or model edits the user has already typed. Consider resetting the form only when `open` or `task` changes, not on every `defaults` refresh.
| }); | ||
| const [dialogOpen, setDialogOpen] = useState(false); | ||
| const [editingTask, setEditingTask] = useState<ScheduledTaskSnapshot | null>(null); | ||
| const [busyTaskId, setBusyTaskId] = useState<string | null>(null); |
There was a problem hiding this comment.
🟡 Medium settings/ScheduledTasksSettings.tsx:711
busyTaskId stores a single task id, so concurrent mutations on different tasks overwrite each other. If the user triggers an action on task A and then another on task B, the second setBusyTaskId replaces A's id; when either request finishes it resets the shared state to null, re-enabling buttons for a task whose mutation is still in flight. This allows duplicate run, delete, or update requests to be sent for the same task. Consider tracking busy state per task id (e.g. a Set<string> of in-flight ids) instead of a single value.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/ScheduledTasksSettings.tsx around line 711:
`busyTaskId` stores a single task id, so concurrent mutations on different tasks overwrite each other. If the user triggers an action on task A and then another on task B, the second `setBusyTaskId` replaces A's id; when either request finishes it resets the shared state to `null`, re-enabling buttons for a task whose mutation is still in flight. This allows duplicate `run`, `delete`, or `update` requests to be sent for the same task. Consider tracking busy state per task id (e.g. a `Set<string>` of in-flight ids) instead of a single value.
| }); | ||
| } | ||
|
|
||
| const project = |
There was a problem hiding this comment.
🟡 Medium mognet/handlers.ts:309
When standalone === true, resolveNewThreadTarget sets project to null instead of loading the STANDALONE_CHAT_PROJECT_ID project, so the fallback project?.defaultModelSelection is skipped. Starting a standalone thread with no current thread and no explicit modelSelection fails with No modelSelection was supplied... even when the standalone chat project has a default model configured. Consider loading the standalone project so its defaultModelSelection is used as a fallback.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/mcp/toolkits/mognet/handlers.ts around line 309:
When `standalone === true`, `resolveNewThreadTarget` sets `project` to `null` instead of loading the `STANDALONE_CHAT_PROJECT_ID` project, so the fallback `project?.defaultModelSelection` is skipped. Starting a standalone thread with no current thread and no explicit `modelSelection` fails with `No modelSelection was supplied...` even when the standalone chat project has a default model configured. Consider loading the standalone project so its `defaultModelSelection` is used as a fallback.
| worktreePath: null, | ||
| createdAt, | ||
| }, | ||
| prepareWorktree: { |
There was a problem hiding this comment.
🟠 High mognet/handlers.ts:406
startThread builds bootstrap.prepareWorktree without a branch field in worktree mode. Since ThreadTurnBootstrapDispatcher passes bootstrap.prepareWorktree.branch as newRefName to gitWorkflow.createWorktree, the new worktree is not created on a fresh branch — it either reuses baseBranch or fails when that branch is already checked out, breaking the isolated worktree behavior advertised for workspaceMode: "worktree". Consider generating a unique branch name and including it in prepareWorktree.branch so the worktree gets its own branch.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/mcp/toolkits/mognet/handlers.ts around line 406:
`startThread` builds `bootstrap.prepareWorktree` without a `branch` field in worktree mode. Since `ThreadTurnBootstrapDispatcher` passes `bootstrap.prepareWorktree.branch` as `newRefName` to `gitWorkflow.createWorktree`, the new worktree is not created on a fresh branch — it either reuses `baseBranch` or fails when that branch is already checked out, breaking the isolated worktree behavior advertised for `workspaceMode: "worktree"`. Consider generating a unique branch name and including it in `prepareWorktree.branch` so the worktree gets its own branch.
| ReadonlyArray<ScheduledAgentTask>, | ||
| ScheduledTaskError | ||
| > = Effect.gen(function* () { | ||
| const exists = yield* fs.exists(config.scheduledTasksStatePath).pipe( |
There was a problem hiding this comment.
🟡 Medium src/scheduledTasks.ts:281
The fs.exists call on line 281 maps I/O and permission errors to false via Effect.orElseSucceed(() => false), so when the state file is inaccessible the service treats it as missing and returns an empty task list instead of reporting the error. This causes all scheduled tasks to silently disappear from the API until the process is restarted. Consider propagating the fs.exists error as a ScheduledTaskError rather than swallowing it, so the read failure surfaces instead of being masked as an empty file.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/scheduledTasks.ts around line 281:
The `fs.exists` call on line 281 maps I/O and permission errors to `false` via `Effect.orElseSucceed(() => false)`, so when the state file is inaccessible the service treats it as missing and returns an empty task list instead of reporting the error. This causes all scheduled tasks to silently disappear from the API until the process is restarted. Consider propagating the `fs.exists` error as a `ScheduledTaskError` rather than swallowing it, so the read failure surfaces instead of being masked as an empty file.
| <Button variant="outline" onClick={() => onOpenChange(false)}> | ||
| Cancel | ||
| </Button> | ||
| <Button disabled={!validInput || saving} onClick={() => void submit()}> |
There was a problem hiding this comment.
🟡 Medium settings/ScheduledTasksSettings.tsx:680
When no providers are available, defaults.providerInstanceId falls back to "codex", and validInput only checks that providerInstanceId is non-empty — it never verifies the id exists in providers. The Create button becomes enabled once a title and prompt are entered, and toScheduledTaskInput persists a task with an unregistered provider id. The task later fails at runtime with No provider instance registered for id .... Consider disabling the Create button when visibleProviders is empty, or rejecting the form in toScheduledTaskInput when the selected id is not in providers.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/components/settings/ScheduledTasksSettings.tsx around line 680:
When no providers are available, `defaults.providerInstanceId` falls back to `"codex"`, and `validInput` only checks that `providerInstanceId` is non-empty — it never verifies the id exists in `providers`. The Create button becomes enabled once a title and prompt are entered, and `toScheduledTaskInput` persists a task with an unregistered provider id. The task later fails at runtime with `No provider instance registered for id ...`. Consider disabling the Create button when `visibleProviders` is empty, or rejecting the form in `toScheduledTaskInput` when the selected id is not in `providers`.
| const invocation = yield* McpInvocationContext.McpInvocationContext; | ||
| const includeSourceContext = input.includeSourceContext ?? true; |
There was a problem hiding this comment.
🟡 Medium mognet/handlers.ts:623
delegateTask calls getThreadOrFail and getProjectOrFail before startThread invokes requireOrchestration, so a caller without the orchestration capability still gets Thread '…' was not found. or Project '…' was not found. errors. This leaks whether threads and projects exist before the capability check eventually rejects the operation. Consider calling requireOrchestration("delegate_task") at the top of delegateTask, before any snapshot queries.
const invocation = yield* McpInvocationContext.McpInvocationContext;
+ yield* requireOrchestration("delegate_task");🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/mcp/toolkits/mognet/handlers.ts around lines 623-624:
`delegateTask` calls `getThreadOrFail` and `getProjectOrFail` before `startThread` invokes `requireOrchestration`, so a caller without the `orchestration` capability still gets `Thread '…' was not found.` or `Project '…' was not found.` errors. This leaks whether threads and projects exist before the capability check eventually rejects the operation. Consider calling `requireOrchestration("delegate_task")` at the top of `delegateTask`, before any snapshot queries.
| path: Path.Path, | ||
| verbose: boolean, | ||
| ) { | ||
| function generateMacIcns(sourcePng: string, targetIcns: string, verbose: boolean) { |
There was a problem hiding this comment.
🔴 Critical scripts/build-desktop-artifact.ts:768
generateMacIcns runs sips -s format icns, but sips does not support icns as an output format — it only handles raster formats like png, jpeg, and tiff. This command fails, so every mac desktop build breaks when staging the .icns icon. The previous iconset + iconutil flow is required to produce a valid .icns file — consider restoring it.
🤖 Copy this AI Prompt to have your agent fix this:
In file @scripts/build-desktop-artifact.ts around line 768:
`generateMacIcns` runs `sips -s format icns`, but `sips` does not support `icns` as an output format — it only handles raster formats like `png`, `jpeg`, and `tiff`. This command fails, so every mac desktop build breaks when staging the `.icns` icon. The previous `iconset` + `iconutil` flow is required to produce a valid `.icns` file — consider restoring it.
| export const layer = MognetToolkitRegistrationLive.pipe(Layer.provideMerge(McpTransportLive)); |
There was a problem hiding this comment.
🟡 Medium mcp/McpHttpServer.ts:228
Changing layer to export MognetToolkitRegistrationLive instead of PreviewToolkitRegistrationLive exposes the entire Mognet toolkit on the public /mcp endpoint for every issued bearer token. MognetToolkitRegistrationLive merges the preview registration with MognetWorkflowToolkitRegistrationLive, so the endpoint now serves mognet_scheduled_tasks_create, mognet_scheduled_tasks_delete, mognet_thread_start, mognet_delegate_task, and mognet_thread_handoff. Any provider MCP credential can now mutate orchestration state and scheduled tasks, a significant privilege expansion from the previous preview-only surface. If this is intentional, consider documenting why all tokens receive destructive orchestration and scheduled-task capabilities. Otherwise, restore PreviewToolkitRegistrationLive or gate the Mognet toolkit registration behind a capability check.
-export const layer = MognetToolkitRegistrationLive.pipe(Layer.provideMerge(McpTransportLive));
+export const layer = PreviewToolkitRegistrationLive.pipe(Layer.provideMerge(McpTransportLive));Also found in 1 other location(s)
apps/server/src/mcp/McpSessionRegistry.ts:117
issue()now grants every provider-issued MCP credential the"orchestration"and"scheduled-tasks"capabilities in addition to"preview". The same bearer token is accepted byMcpHttpServerfor the fullMognetToolkit, and handlers such asmognet_thread_start,mognet_thread_handoff, and all scheduled-task mutation endpoints only checkinvocation.capabilities.has(...). Any provider process that receives its normal MCP token can now create/open threads and create/update/delete/run scheduled tasks, which broadens a previously preview-scoped credential into one that can mutate application state.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/mcp/McpHttpServer.ts around line 228:
Changing `layer` to export `MognetToolkitRegistrationLive` instead of `PreviewToolkitRegistrationLive` exposes the entire Mognet toolkit on the public `/mcp` endpoint for every issued bearer token. `MognetToolkitRegistrationLive` merges the preview registration with `MognetWorkflowToolkitRegistrationLive`, so the endpoint now serves `mognet_scheduled_tasks_create`, `mognet_scheduled_tasks_delete`, `mognet_thread_start`, `mognet_delegate_task`, and `mognet_thread_handoff`. Any provider MCP credential can now mutate orchestration state and scheduled tasks, a significant privilege expansion from the previous preview-only surface. If this is intentional, consider documenting why all tokens receive destructive orchestration and scheduled-task capabilities. Otherwise, restore `PreviewToolkitRegistrationLive` or gate the Mognet toolkit registration behind a capability check.
Also found in 1 other location(s):
- apps/server/src/mcp/McpSessionRegistry.ts:117 -- `issue()` now grants every provider-issued MCP credential the `"orchestration"` and `"scheduled-tasks"` capabilities in addition to `"preview"`. The same bearer token is accepted by `McpHttpServer` for the full `MognetToolkit`, and handlers such as `mognet_thread_start`, `mognet_thread_handoff`, and all scheduled-task mutation endpoints only check `invocation.capabilities.has(...)`. Any provider process that receives its normal MCP token can now create/open threads and create/update/delete/run scheduled tasks, which broadens a previously preview-scoped credential into one that can mutate application state.
| } | ||
|
|
||
| try { | ||
| isomorphicLocalStorage.setItem(key, legacyValue); |
There was a problem hiding this comment.
🟡 Medium hooks/useLocalStorage.ts:53
When removeLocalStorageItem removes the mognet:* key, read falls back to the unmigrated t3code:* legacy key, returns the stale legacy value, and re-writes it to the mognet:* key — so the deleted item reappears on the next read. Consider removing the legacy key as part of the migration in read, or deleting it in removeLocalStorageItem.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/web/src/hooks/useLocalStorage.ts around line 53:
When `removeLocalStorageItem` removes the `mognet:*` key, `read` falls back to the unmigrated `t3code:*` legacy key, returns the stale legacy value, and re-writes it to the `mognet:*` key — so the deleted item reappears on the next read. Consider removing the legacy key as part of the migration in `read`, or deleting it in `removeLocalStorageItem`.
What changed
Why
The embedded web startup view was showing an older flat mark and previously used an icon with a dark tile background that did not match the page background. The macOS app icon artwork is the current desired look, but the macOS icon asset itself should stay unchanged.
Impact
The startup/loading screen now shows the updated 3D Mognet artwork without the mismatched background tile. Desktop app icon assets are untouched.
Validation
vp checkvp run typecheck/mognet-splash-icon.pngserves locally with200 OK.Note
Update web splash icon and rebrand product from T3 Code to Mognet
T3CODE_*to Mognet /MOGNET_*across the entire monorepoScheduledTasksservice with persistence, scheduling, and CRUD RPC endpoints (scheduledTasks.list/create/update/delete/runNow), plus a new/scheduled-tasksUI routeThreadTurnBootstrapDispatcherservice and introduces standalone (projectless) chat threads backed bySTANDALONE_CHAT_PROJECT_IDExternalLauncher.launchTerminalwith ashell.openInTerminalRPC endpoint andOpenInTerminalPickerUImognet_thread_start,mognet_delegate_task, scheduled task tools, etc.) registered on the MCP HTTP serverlocalStoragekeys fromt3code:*tomognet:*with transparent legacy fallback readsT3CODE_*), localStorage keys (t3code:*), well-known path (/.well-known/t3/environment), and protocol schemes (t3code://) are no longer read; existing data under old keys requires the migration path or will be lost📊 Macroscope summarized fb67b14. 174 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.