Skip to content

feat(codex): add native Goal lifecycle controls - #7935

Open
stekman08 wants to merge 21 commits into
pingdotgg:mainfrom
stekman08:feat/codex-goal-control
Open

feat(codex): add native Goal lifecycle controls#7935
stekman08 wants to merge 21 commits into
pingdotgg:mainfrom
stekman08:feat/codex-goal-control

fix(web): remove unreachable goal toast

07aa754
Select commit
Loading
Failed to load commit list.
MacroscopeApp / Macroscope - UI Consistency succeeded Aug 25, 2026 in 4m 49s

UI Consistency: No issues found

All clear

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.


🔍 Agent activity — 17 tool calls

💭 I want to check whether prior findings were addressed at the current head commit 07aa754, focusing on ChatView.tsx and threads.ts to see the relevant diff regions.

1. git_tools — codexGoal|goalCommand|TargetIcon — rejected: repo.url: required parameter is missing or empty;repo: required parameter is missing or empty

2. git_tools082e6ea..07aa754

💭 The prior findings look addressed—tooltip, status labels, banner ordering, goalCommandRunning state, and the unreachable toast removal are all in place. Now I want to dig into ChatView.tsx more closely to check for any remaining issues.

3. git_tools — codexGoal|goalCommand|TargetIcon

💭 I want to check the banner stack region, the send handling logic, and the sendDisabledReason area to understand how these pieces fit together.

4. browse_code — apps/web/src/components/ChatView.tsx

💭 With banner ordering settled, I'm checking how the description renders using a Tooltip with a line-clamped trigger span, comparing it against how ComposerBannerStack and other banners like branch-mismatch handle their descriptions to confirm the tooltip pattern is consistent. I also want to verify the description type expected by ComposerBannerStack. Now I want to check the send flow's disabled reasoning and see how goalCommandRunning factors into the disabled logic elsewhere in the file.

5. browse_code — apps/web/src/components/ChatView.tsx

💭 I want to check whether the send button is actually disabled while a goal command runs, or if it's just tracked in state with a reason label but not enforced in the UI.

6. browse_code — apps/web/src/components/ChatView.tsx

💭 Good, sendDisabledReason mirrors the feedbackUploading pattern. Now I want to confirm it actually disables the send button in ChatComposer, check whether the /goal slash command gets picked up automatically since the server provides slashCommands, and verify that TargetIcon is properly exported from the icons module for the codexGoal banner.

7. browse_code — apps/web/src/components/ChatView.tsx

💭 I noticed a duplicate import statement from the same module, but that's a minor lint-level style issue, not worth flagging. Mobile files are out of scope here, and the banner icon sizing looks consistent with other icons in the codebase, so that's fine too. Let me check the useCodexGoal hook usage in the web state/threads.ts file to see if it matches the existing patterns there.

8. browse_code — apps/web/src/state/threads.ts

💭 That matches the existing pattern I've seen. Now I need to trace where environmentId comes from in ChatViewContent for the goal RPCs—specifically whether hasActiveCodexGoalSession and useCodexGoal use the thread's environment or the currently active one, so I should check its definition around that section.

9. git_tools — const environmentId|const activeThreadKey|const routeThreadKey|const activeThreadId

💭 I'm checking that environmentId matches the same pattern used by uploadThreadFeedback, so it looks consistent with existing code rather than a new issue—though I want to double check there isn't a subtle mismatch with activeThreadEnvironmentId. Now I want to verify that activeThread is derived using the same environmentId and threadId from the route, so there shouldn't be a mismatch there. Next I should check ComposerBannerStack to see if its description prop supports ReactNode content and whether existing usages wrap Tooltip components properly.

10. browse_code — In apps/web/src/components (repo pingdotgg/t3code, commit 07aa754), show the full source of the ComposerBannerStack component and its ComposerBannerStackItem type, including how description, title, icon, actions, onDismiss, and className are rendered, and how non-front items are hidden.

💭 Placing the Goal banner last in the stack means it only becomes visible when it's the sole banner, otherwise it stays hidden behind others—since it has no dismiss or actions, that's fine as passive info, similar to how mobile handles it. The tooltip inside a stacked item still works fine since hovering the stack reveals it. Checking the truncated description pattern with TooltipTrigger rendering a span—this matches the existing branch-mismatch pattern elsewhere, so it's consistent rather than something new. Now I want to check whether the web composer actually surfaces the new /goal slash command from the server's config, so I should look for where slash command hints are rendered in the web app.

11. git_tools — slashCommand

12. browse_code — apps/web/src/components/chat/ChatComposer.tsx

💭 I'm wondering whether selecting a provider-slash-command item in the menu inserts text or sends it directly to the provider, and whether that behavior might bypass the interception I set up in ChatView's onSend for /goal.

13. git_tools — provider-slash-command

💭 Selecting a slash command inserts the corresponding text into the composer, which ChatView then intercepts on send—that seems intentional. I'm now comparing this to the feedback command flow using parseCodexFeedbackCommand, and noticing that the condition for isUnadornedCodexCommand now includes !directAnnotation, which wasn't there before—I want to verify whether that exclusion was already present. Checking the diff, this condition seems redundant since directAnnotation would already be captured by the existing composerImages/composerPreviewAnnotations checks, but it's harmless either way. I also verify the goal status flow properly handles the "stopped" session case with a warning toast and cleanup, and confirm the in-flight tracking and thread-key comparisons match the pattern used in the feedback flow, then move on to checking UI consistency around the tooltip popup styling. Looking at whether the persistent goal banner might disrupt layout when it's the sole banner attached to the composer, I decide this is a product decision already reflected on mobile and not worth flagging again. I also check keyboard accessibility for the description tooltip and find it's consistent with the branch-mismatch pattern elsewhere, so nothing new there, and confirm the banner id and per-thread WebSocket subscription cleanup both look fine given the atom family setup. I also notice the goal banner disappears when the session status is "stopped" and reappears on wake, which seems acceptable. The icon choice matches how other banners use lucide icons directly. More importantly, I spot that the PR adds a second import statement from the same threads module instead of merging it with the existing import, which could trip a duplicate-imports lint rule depending on whether the project enables that check.

14. git_tools — ^} from "@t3tools/client-runtime/state/threads";

💭 That duplicate import matches an existing pre-existing pattern, so it's not a real finding. I'm narrowing scope to just threads.ts and ChatView.tsx, and now checking whether selectedProvider reflects the composer's current selection rather than the thread's actual running provider, which could be a real regression around hasActiveCodexGoalSession. If a claude thread has codex selected in the dropdown, that flag goes true and triggers a codexGoal subscription for a claude thread — tracing through resolveCodexGoalRoute and subscribeCodexGoal, this hits a ProviderValidationError since claude has no codexGoal support, which becomes a CodexGoalOperationError that fails the RPC stream. I need to check whether that failure just quietly resolves to null in the atom or actually surfaces as a toast/error loop, and whether selectedProvider is actually locked to the thread's provider for existing server threads rather than reflecting composer state.

15. git_tools — selectedProvider =|lockedProvider =

💭 For a server thread with a session, the locked provider derives from the thread itself, and the subscription's providerInstanceId falls back correctly between session and model selection—both look safe. The mobile version's driver check via serverConfig providers versus the web's selectedProvider also seems consistent, so the prior web-side issues appear resolved and I want to do one more careful pass on the new code. One possible gap: after a /goal set or clear, there's no toast feedback—only the banner subscription updates, and if the session was stopped and gets recovered, there might be a moment with no visible confirmation the command worked. It's plausible but speculative since the thread status likely updates quickly. I also want to check /goal status on a session-less thread (shows the correct warning) and look at the isDraftHeroState banner case. Checking the styling rules, the mobile app uses a hard-coded blue border/background instead of a semantic token, which would normally be a violation, but since mobile is outside the included apps/web scope, I can't flag it. The web side's classes look fine. I want to confirm nothing else was missed in the web state file and ChatView, and check whether the newly added activeThreadKeyRef with its layout effect duplicates an existing ref used elsewhere, like in the feedback path.

16. git_tools — ThreadKeyRef|threadKeyRef

💭 Let me reconsider the sendDisabledReason finding — I want to verify how ChatComposer actually uses it, whether it disables the send button, and if goalCommandRunning is truly tied to that state or if there's a separate isSendBusy-like check.

17. git_tools — sendDisabledReason

💭 The send-disabled reasoning with aria label looks consistent, and the ChatView diff checks out across the reviewed sections. I confirm the goal command atom exists in client-runtime, and now I'm checking whether the tooltip trigger inside the goal banner's AlertDescription poses any accessibility concerns. Comparing against the branch-mismatch tooltip pattern elsewhere in the stack, the hover-only reveal on non-focusable text is consistent with existing conventions, so no issue there. With no findings to report, I'm now reconciling the instruction to output "All clear" exactly against the required JSON schema — I'll treat the schema as the binding constraint and set the state to success with a summary of "All clear" and no additional details.


Agent Credits: 185 credits