Repository navigation
chore(sync): absorb upstream main through fe219c4de1 - #239
Conversation
… a giant heading (pingdotgg#25804) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tgg#25695) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gg#25829) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…5830) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…dotgg#25793) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…iB page kernels (pingdotgg#25825) TEMPORARY workaround for an upstream Node bug: nodejs/node#67048 Node 26.9+ --build-sea writes linux-arm64 executables that 16/64 KiB page kernels cannot load. linux-arm64 now injects with postject, and the archive build rejects incompatible ELF layouts. One-time exception. Revert once Node ships a fix: grep for the Node issue URL and drop postject from scripts/package.json. Fixes pingdotgg#23433. Replaces pingdotgg#23443. Co-authored-by: Lakshmi Tanmay <lakshmi@voltcrash.com> Co-authored-by: maria-rcks <maria@kuuro.net> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#25666) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s 4x faster (pingdotgg#25824) Co-authored-by: Omar <76748259+omar9848492@users.noreply.github.com> Co-authored-by: Carlos D. Escobar-Valbuena <carlosdavidescobar@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (Claude Code) <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net> Co-authored-by: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
…ingdotgg#25816) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…h no list open (pingdotgg#25809) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…saves a draft twice (pingdotgg#25768) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ering (pingdotgg#25846) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rning (pingdotgg#25819) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…first commit (pingdotgg#25817) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rom screen effects (pingdotgg#25820) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otgg#25837) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otgg#25860) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#25812) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ngdotgg#25882) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…d one (pingdotgg#25839) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s it unavailable (pingdotgg#25632) Co-authored-by: Theo Browne <me@theo.li>
…otgg#25841) Co-authored-by: jason chien <3936511+jason5545@users.noreply.github.com>
…b checks agent-device 0.21.32 folds the iPhone Duo in Simlock's device set. The folded Duo keeps its side rail inside a thread, so the hide-on-push tab checks only apply to bottom tab bars. The Duo poses stay pending: alert accept cannot see the open-link prompt on the Duo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…gdotgg#25854) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…derive instead of syncing (pingdotgg#25813) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…effects (pingdotgg#25895) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otgg#24937) Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Merges 1069 upstream commits. Upstream split ChatView, ChatComposer and MessagesTimeline into hooks and row modules, so those files were rebuilt from upstream's versions with the fork's fenced intents re-applied at their new homes (useComposerSend, useThreadLifecycle, ThreadActivityBanners, useComposerSelection, chatRouteCommands, Timeline*Row, AppearanceSettingsPanel). Notable fork-side follow-ups: - GitHub PR automation provides its own GitHubRest, which upstream now builds only inside its GitHub driver. - Window fill colours follow upstream's new #000000 dark background. - New upstream lint rules are deferred for fork-owned code with baselines; over-long fork comments in upstream files were shortened. - Upstream-replaced files are fenced into knip ignores; dead fork CSS for removed upstream elements is deleted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream's new error-level comment rule fails three fork comments in the vite configs, which would turn CI's `vp check` red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sage Upstream now keeps the thread mounted under the Settings and Usage modals and gates its global shortcuts on BackgroundThreadContext. Fork hooks that own global input did not: - pending-question digits 1-9 could answer the hidden thread's question - the dictation hotkey recorded into the hidden thread's draft instead of the fallback toast; a session still preparing went on to record unseen (stop() is a no-op before recording, so it is now cancelled) Also restores the pre-sync ChatView focus effect that reads splitPane.focused without depending on it, so clicking into a split pane keeps its caret. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NoahHendrickson
left a comment
There was a problem hiding this comment.
Code-quality review of 51b3c52.
Scope: this is an upstream sync, so upstream code taken as-is was not re-litigated. I reviewed the fork-side work: the two commits on top, the fork intents re-applied in upstream's new split modules, the server layer resolutions, and the lint baselines. GitHub could not place inline comments on a diff this size, so file and line references are given below.
No structural regressions in the merge itself. No fork delta pushes a file past 1k lines (I checked every modified file against upstream fe219c4). The server resolutions (GitHubRest provided inside GitHubPrAutomation.layer, the isNonRepositoryGitStderr fence, the branch-follow layer wiring) are direct and give their reasons. The PR also merges cleanly with #238, which landed on custom after this branch was cut; the data-fork-* hooks #238 targets survive the merge.
1. The rule "this composer owns global input" has no single home
Each site rebuilds the rule from splitPane.focused and BackgroundThreadContext, in a different shape every time:
components/ChatView.tsx~1684 and ~2827 build!backgroundThread && splitPane.focused(open-in-editor shortcut,useChatShortcuts).components/chat/useComposerStash.ts~691-709 uses two separate early returns.custom/useComposerPendingUserInputCard.ts:123-127uses two locals, two guards and two deps.custom/voice/useForkDictationController.ts:126folds both into a variable still calledpaneFocused.
This commit exists because two of those sites were missed when upstream added backgrounding. The next fork hook with a document listener will be missed the same way.
Move the rule to where the concept lives, in custom/splitThreads/splitPaneContext.ts:
/** This pane takes window-level input: focused in a split, and not behind Settings or Usage. */
export function useOwnsGlobalInput(): boolean {
return useSplitPane().focused && !use(BackgroundThreadContext);
}The two fork hooks each drop to one local, one guard and one dep. The fenced sites in upstream files can keep upstream's own !backgroundThread and swap only the fenced splitPane.focused for the hook. The overlap is harmless, and those fences stay one token wide.
Related, in useForkDictationController.ts: paneFocused now means "focused and not backgrounded", and the stop/cancel effect below it inherits that meaning silently. Its comment still says "a recording left running in a pane that loses focus". But opening Settings or Usage mid-recording now also stops the recording and transcribes it into the hidden thread's draft. That's a reasonable behavior, since before the sync the thread unmounted and the recording died with it. But it is new, and neither the comment nor the PR body says so. Rename the variable to ownsInput and have the comment cover both cases. Likewise, the comment above the digit listener in useComposerPendingUserInputCard.ts mentions only the split.
2. Non-blocking: lint baseline
The t3code/no-low-value-comments baseline in .fork/lint-deferred.mjs went from 6 to 876. The ratchet mechanism is fine, but at 876 this rule no longer gates fork code. Its comment still says the deferral covers "section dividers in the vendored design-mode engine". Consider giving the vendored engine its own scope, or updating that comment and filing a burn-down item, so the number keeps meaning something.
Verdict
Not approving: finding 1 is a cheap fix that removes a whole class of repeat bugs, and it should land with the commit that just paid for it. Everything else in the fork-side resolution looks sound.
NoahHendrickson
left a comment
There was a problem hiding this comment.
Reviewed commit 51b3c5201d7bcc668efadd4d632dfd945224e5e5 using the supplied event base e63be3e7f92f64f7e095131a6ec6b39442ea3dda. Fetched PR #239 into the isolated worktree and verified HEAD before reviewing. Review focused on merge resolutions and fork integrations.
One reproduced P2 finding is inline. Validation: the five focused automation/composer test files passed (88 tests), and the fork guard suite passed (627 tests; some overlap with the focused files). A temporary regression probe using the existing GitHub automation fixture failed because the deferred run used the wrong project, root workspace, and no PR context. The probe was removed afterward. No browser verification or repository-wide checks were run.
| yield* releaseFailedDelivery(entry.delivery_id); | ||
| } else { | ||
| yield* sql`UPDATE scheduled_task_webhook_deliveries SET outcome = 'accepted', error = NULL, rendered_prompt = ${rendered.prompt.slice(0, WEBHOOK_DELIVERY_LOG_PROMPT_LIMIT)}, missing_fields_json = ${encodeMissingFieldsJson(rendered.missing)} WHERE delivery_id = ${entry.delivery_id}`; | ||
| if (!(yield* runWebhookDelivery(task, entry.delivery_id, rendered.prompt))) |
There was a problem hiding this comment.
[P2] Preserve GitHub PR routing in the deferred dispatch path
The upsert schema accepts schedule.github together with dedupe.coalesceKey, so a valid GitHub automation can reach this worker. Unlike the immediate path, it renders only task.prompt and calls runWebhookDelivery without the GitHub trigger. That drops the exact PR URL/commit context and makes runTask fall back to the task's project/workspace instead of the event's repository and isolated worktree. I reproduced this with a task rooted in project d2, watching owner/no3y, and coalesceKey: "{{body.pull_request.number}}" with a 10-second window: PR #108 launched in d2 with { type: "root" }, and its prompt contained no PR URL. Reconstruct/preserve the GitHub trigger and context for deferred deliveries too, or reject this combination when saving the task.
There was a problem hiding this comment.
Confirmed. Upstream's coalescing path (pingdotgg#22234), new in this sync, renders only task.prompt and calls runWebhookDelivery without the GitHub trigger. The fork UI can't set dedupe on a GitHub task, but a stored dedupe survives conversion, and RPC and MCP callers can send both fields. Proposed fix: strip dedupe when a GitHub task is saved. This one is held because it changes server behavior; see the triage comment.
Upstream's new budget test pins the MCP tool list at its own size; the fork's GitHub PR automation adds 342 bytes to schedule_task, so the test failed. The budget now records the fork's measured size, and the budget gate compares a PR with its base branch (custom) instead of main, the upstream mirror, which would otherwise fail every fork PR. Folded into ci-on-custom with a guard row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NoahHendrickson
left a comment
There was a problem hiding this comment.
Code review: 51b3c52
Reviewed commit 51b3c5201d7bcc668efadd4d632dfd945224e5e5. The PR head has since moved to 0838852580, but that commit only touches CI and none of the files below.
Scope. I didn't re-review upstream code taken as-is. I covered:
- the merge's conflict resolution (
git show --remerge-diff 7f237e4c0d, 204 files) - re-homed fork fences, compared with the pre-sync fork (
47a31babbf/e63be3e7f9) f443c22654and51b3c5201d
Every finding was checked against the code at the reviewed commit. Nothing was run: no tests, typecheck or app. GitHub's file list stops at 3,000 files, so findings are linked here rather than posted inline.
High
1. The permissions chip crashes the composer whenever it shows. ComposerFooterControls.tsx#L104-L114
- Upstream turned the permissions chip from a
<Select>into a<Menu>. The resolution kept the fork'sComposerRuntimeModeTriggeras theMenuTriggerrender element. - That component renders
ComposerSelectControl, which isSelectPrimitive.Trigger(ComposerShell.tsx#L211-L224). Nothing above it is aSelect.Rootany more. - Base UI 1.5.0's
useSelectRootContext()throwsSelectRootContext is missingin that case. - Failure: the chip renders whenever the runtime mode isn't the default, or the model has no effort menu (
composerControlsOwnership.ts). In either case the composer throws during render. Upstream'sComposerPermissionsSection.browser.test.tsxrenders this control, soTest Webshould go red too. - Fix: build the fork's chip on a non-Select control, e.g.
ComposerControlwith the mode-chip style and thedata-fork-composer-mode-chipattribute.
2. The side chat window now runs every app-wide service. main.tsx#L43
- Upstream moved the root coordinators into
startAppServices()(appInit.tsx): thread notifications, slow-RPC, host-freeze and permission toasts, the update launch notice,installChatRouteCommands, workspace focus and more. main.tsxcalls it unconditionally. The fork'sisSideChatSurfacebranch in__root.tsxnow only skips JSX that no longer holds those coordinators.- Failure: with the side chat window open, every finished turn notifies twice (sound and badge), and app-wide notices appear in the side chat too. The fence comment at
__root.tsx#L192-L195exists to prevent exactly this. - Fix: gate the main-window-only services on
!isSideChatSurface.
3. Toasts show twice in the side chat window. __root.tsx#L196-L209
RootRouteViewnow wraps every branch inToastProvider/AnchoredToastProvider(L142-L146). The re-applied side-chat branch still adds its own pair.- Both providers bind the same module-level
toastManager, and each renders its own viewport (toast.tsx#L544-L548). - Failure: every toast in the side chat appears twice, stacked.
- Fix: drop the inner providers.
4. "New worktree" no longer leaves an existing worktree. useComposerSelection.ts#L231-L237
- The re-homed fence tests the stored
draftThread?.envMode !== "worktree". Before the sync it tested the effective env mode, which reads as"local"for a draft with aworktreePath. - "Use previous worktree" (
BranchToolbar.tsx#L680-L687) storesenvMode: "worktree"with aworktreePath. The fence also replaced upstream'smode === "worktree" && draftThread?.worktreePath ? { worktreePath: null }clear, so neither guard is left. - Failure: in a draft, pick "Use previous worktree", then "New worktree". Nothing is cleared, and the thread starts in the old worktree on its branch instead of a fresh one.
- Fix: also clear when
draftThread?.worktreePathis set, and updateforkNewWorktreeDefaultBase.test.ts, which pins the current string.
5. A multi-model send loses design-change pills on failure and can send them twice. useComposerSend.ts#L1131-L1151
- Before the sync, the pills were detached when the draft cleared and restored by
restoreFailedDraftwhen a target failed. NowforkDesignChanges.detachruns only afterawait sendToMultipleModels(...)finishes, and runs unconditionally.useMultipleModelSend.tshas no restore. - Failure: a target fails. The prompt text comes back, and the trailing
detachthen clears the pills. The "Restore prompt" toast path loses them the same way. - Failure: a second send while threads are starting.
useMultipleModelSendcallsresetLocalDispatch()beforeawait starts, so the composer is free while the pills are still pending. A send in that window carries the same<design_change_request>blocks again, which is what the fence comment says must not happen. - Side effect: the old multi-model
restorelanded in the single-send failure path (L1431-L1433). There it does nothing, except afterrekey, where it re-adds the entries under the deleted bootstrap thread's key.
Medium
6. "Open automation" on a PR-review run opens the generic webhook editor. ScheduledTasksSettings.tsx#L400-L403
- Upstream's rewritten
?taskId=handling callssetEditordirectly, skipping the fork'sopenForEdit. That function sends GitHub PR tasks to/scheduled. - Failure: "Open automation" (from
TimelineUserMessageRow,QueuedRunsControlorThreadAutomationsPanel) on a PR-review run opens upstream's webhook editor instead of the Automations page. - Fix: route the linked task through
openForEdit.
7. Find in a split pane can lose its timeline. threadFindStore.ts#L56-L68
- Upstream's store keeps one global
attachedTimeline. Each pane's timeline re-attaches wheneverrows,isWorkingortimelineEntrieschange (useThreadFindPositionReader.ts#L89). - Failure: with pane B streaming, Cmd+F in pane A finds
timelineFor(A) === null. Live-follow isn't released, and the search ignores A's viewport position. - Before the sync, each ChatView had its own reader. The fork's old per-pane fence in
useThreadFind.tswas dropped and has no replacement.
8. "Compact idle threads: Never" no longer hides the resume-compaction card. ChatView.tsx#L2601-L2617
- Upstream pingdotgg#23037 (in this range) gated the resume-compaction offer on
resolveIdleCompaction(settings, instance) !== "off". The fork'suseResumeCompactionBannerreplaced that expression and doesn't read the setting. - Failure: with the setting on Never (globally or for one instance), the "Resume with less context" card still appears.
9. Automation runs inflate the new "Settled (N)" count. Sidebar.tsx#L2748
- The server-paged older-settled query (
ProjectionStore.listOlderSettledThreads) excludes archived threads and subagents, but not threads with ascheduledTaskId. The fork filters those out client-side (isScheduledRunOffSidebar). - Failure: "Settled (N)" and "Show N more" count runs that never render.
useOlderSettledShelfGrowthalso keeps paging while the filtered list stays short, so a heavy PR-automation user can page through their whole run history to show nothing.
10. The startup-recovery window points fork users at upstream releases. DesktopStartupRecovery.ts#L21
- This file is new from upstream and hardcodes
https://github.com/pingdotgg/t3code/releases. - Failure: "Reinstall" opens
pingdotgg/t3code/releases/tag/v<fork version>. That's a 404, or a real upstream release, so the user can end up installing upstream T3 Code over no3y Code. The copied error text also says "T3 Code".
Low
11. The fork's Settings nav styling no longer applies. SettingsSidebarNav.tsx#L417
- Settings now renders in a portaled
AppRouteModal. Every[data-fork-chrome-row]rule (theme.custom.glass-surfaces.css,theme.custom.palettes.css) requires a[data-sidebar-version="v2"]ancestor, and the modal has none. So the Glass active pill and the row pill shape no longer apply in the Settings nav. useForkSettingsMarkerstill sets[data-fork-settings]on:root. Settings is now a modal over the chat, so the settings-button restyle also hits the chat's outline buttons behind it.
12. GitHub PR automation builds a second GitHub sign-in stack. GitHubPrAutomation.ts#L221-L228
GitHubRest.layerWithDependenciescreates its ownGitHubAppSignIn, whoserefreshLockis separate from the driver's.- Failure: if the app token has expired and a webhook connect/remove overlaps a driver read, both can post the same single-use refresh token. The loser gets
bad_refresh_token. If it runsreplaceSession(host, session, null)before the winner saves, it signs the user out. - The window is narrow. The comment at L221-L222 also contradicts the two lines after it.
Notes, not regressions
.fork/customizations.yamlstill describesgit-config-lock-retrycode that upstream's own retry replaced. The entry has zero fences, and no guard catches that.- Upstream's test purge removed several fork identity assertions:
appId, product name and protocol name inbuild-desktop-artifact.test.ts; the Linux desktop entry name;setName "no3y Code Beta".forkAppIdentity.test.tsstill pins the product name and profile.
Checked and clean
51b3c5201dis correct:BackgroundThreadContextgates the digits and dictation, a preparing dictation is cancelled, and the focus-effect deps match pre-sync.f443c22654only changes comments.- Split-pane
focusedgates in ChatView, the thread-state cards and liveness inputs inuseThreadLifecycle, the timeline row fences, Appearance palette rows, and the composer fork fences (other than #1) all moved correctly. - The
ScheduledTaskServicewebhook claim and release flow is correct. - Desktop identity is intact: profile
t3code-fork-v2, app id, WMClass,~/.t3-fork. - No migration collisions.
- The lint deferral scope doesn't ungate upstream files beyond
Sidebar.tsxandSidebarChrome.tsx, which were already in it.
Claude Opus 5.5 in Claude Code (T3 Code).
…ault mode Upstream moved the permissions chip from a Select to a Menu. The fork's ComposerRuntimeModeTrigger still rendered a Base UI Select.Trigger, which throws without a Select root, so any thread whose mode differed from the default crashed the composer. It now renders upstream's ComposerControl. Upstream's ScheduledTasksSettings case for a user-opened New task editor racing a late-loading link cannot happen in the fork: New task opens the fork's /scheduled page. The case is fenced and replaced with one pinning that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
Review of the sync found fork behavior lost where upstream moved code: - Side chat: upstream starts every app-wide service from main.tsx, so the side chat window doubled notifications, sounds and toasts; it now stops after workspace focus and skips main-app location tracking. Its root no longer nests a second toast provider pair. - New worktree from a previous worktree's draft clears the stored path again. - Multi-model sends detach and restore design-change pills with the cleared draft (in useMultipleModelSend), not after the await, so a failure keeps them and a send during thread start cannot repeat them. - Find keeps one attached timeline per thread, so a streaming split pane no longer steals the focused pane's. - "Compact idle threads: Never" hides the resume card, as upstream's setting does. - "Open automation" on a PR-review task opens /scheduled, like a list click. - Startup recovery links to the fork's releases. - Glass Settings nav rows and buttons match inside the new Settings modal, and the Settings button restyle no longer reaches the chat behind it. - One useOwnsGlobalInput() hook for fork window listeners; the stale git-config-lock-retry manifest entry (replaced upstream) is removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Triage of the three reviews on 51b3c52. Each finding was checked against the pre-sync fork (47a31ba) and upstream (fe219c4) before acting. Fixed
Held, needs a server change (decision pending with the maintainer)
Not acted on
|
…e GitHub sign-in Upstream's new webhook coalescing path dispatches without the GitHub trigger, so a PR-review task that kept a stored dedupe launched in the task's own project with no PR context. Saving a GitHub task now drops dedupe; its deliveries are already one per PR event. Upstream builds GitHubRest only inside its GitHub driver, so the automation built a second GitHubAppSignIn with its own refresh lock. The source-control layer now builds GitHubRest first in the server's memo map, which the driver's forked map and the automation both reuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Update on the held server items. Fixed in b9241ac:
Still held:
|
|
#9 (Settled (N) counts automation runs): left as a follow-up, per the maintainer. On web the count reads slightly high, and mobile is unchanged. An exact fix needs a separate run count from the server. |
NoahHendrickson
left a comment
There was a problem hiding this comment.
Review of the upstream sync: 5 bugs, 2 fork-rule issues (the deleted manifest entry and an unfenced edit), 3 cleanups.
I'd fix these before merging:
- Old GitHub PR-review tasks with a stored dedupe still drop PR context (
ScheduledTaskService.ts). - The
git-config-lock-retrymanifest entry was removed instead of ported. - The Glass Settings button style misses portaled dialogs.
This PR has more than GitHub's 3,000-file inline-comment limit, so the findings below are on files that can't take inline comments.
apps/web/src/theme.custom.palettes.css:780
Bug: the Glass Settings button style no longer reaches portaled dialogs.
The rule now needs a [data-app-modal] ancestor. Settings' nested dialogs and popups portal to <body>, outside the modal popup, so their dark:bg-input/32 buttons lose the Glass look. Portaled content is why useForkSettingsMarker stamps the root in the first place.
Repro: on Glass, open Settings, then Scheduled tasks, then edit a task (or open any confirm dialog from Settings). The outline buttons in the portaled dialog use the upstream style, while the ones in the modal behind it are glass. The same scoping is in theme.custom.glass-surfaces.css (~511).
apps/web/src/theme.custom.glass-surfaces.css:511
Same scoping problem as theme.custom.palettes.css ~780: content portaled out of the Settings modal doesn't match [data-app-modal], so it misses this rule.
apps/web/src/components/chat/useComposerStash.ts:693
Cleanup: this PR adds useOwnsGlobalInput() (focused pane, and not behind Settings or Usage), but this check and ChatView's useChatShortcuts / useOpenFavoriteEditorShortcut checks still combine !backgroundThread && splitPane.focused by hand. The same rule now lives in four places in two forms and will drift. Consider using the helper here.
apps/web/src/custom/voice/useForkDictationController.ts:146
Cleanup: this effect repeats settleWithoutControls (stop if recording, cancel if preparing). If one copy gains a phase such as transcribing and the other doesn't, losing focus will handle a live session differently from controls unmounting. Call settleWithoutControls() here instead.
apps/web/src/components/chat/useComposerSelection.ts:234
Cleanup: draftThread?.envMode !== "worktree" || draftThread?.worktreePath works out resolveEffectiveEnvMode again, where an existing worktree reads as "local". Before the sync this code used the effective envMode directly. If upstream changes how a draft with a worktreePath resolves, this "only on the way in" check will drift and a reselect will clear the chosen base again. Use the shared resolver instead.
🤖 Generated with Claude Code
| @@ -1173,8 +1273,11 @@ export const layer = Layer.effect( | |||
| secret, | |||
| }); | |||
| }); | |||
| // Deliveries are already one per PR event, and the coalesced path dispatches | |||
| // without the PR trigger, so a GitHub task never keeps a stored dedupe. | |||
| const { dedupe: _dedupe, ...withoutDedupe } = schedule; | |||
There was a problem hiding this comment.
Bug: GitHub PR-review tasks saved before this PR still drop the PR trigger.
This only removes dedupe when a GitHub task is saved. Delivery (~line 2174) still decides with schedule.dedupe === undefined. A PR-review automation that was saved with a dedupe before this PR takes the coalesced path. That path dispatches without githubEvent, so the review thread starts in the task's own project with no PR context, and it keeps doing that until someone re-saves the task.
Suggested fix: treat schedule.github as no-dedupe at delivery time as well. That covers tasks already stored too.
There was a problem hiding this comment.
Fixed in 8f55cb7. Delivery now decides with const dedupe = schedule.github ? undefined : schedule.dedupe, so a GitHub task stored with a dedupe before the save-time strip also takes the direct path with its PR trigger. New case in GitHubPrAutomation.test.ts plants a stored dedupe in schedule_json and checks the launch lands in no3y with the PR URL and a worktree. It fails without the fix.
| verify: | ||
| - apps/web/src/__fork_guards__/forkImageAttachmentNoChip.test.ts | ||
|
|
||
| - id: git-config-lock-retry |
There was a problem hiding this comment.
Fork rule: the git-config-lock-retry manifest entry was deleted.
.fork/AGENTS.md (Never): "Remove fork:begin/fork:end fences, … or manifest entries — even if the surrounding code moved. Port them."
Upstream's withSharedConfigLock only serializes writes inside the T3 server process. The fork's jittered retry also waited out writers outside the process, such as an agent or another git process holding .git/config.lock while T3 writes branch.<name>.gh-merge-base on first push. With this entry gone, those pushes fail right away with config_locked, and no guard or manifest row flags that the behavior is gone.
There was a problem hiding this comment.
Not restoring this one. Upstream pingdotgg#25700 adopted the retry this entry was waiting for, and the entry's own intent said "drop this fence if it adopts a retry". The claim that out-of-process holders now fail right away does not hold. retryWhileConfigLocked (GitVcsDriverCore.ts ~3399) retries config_locked 10 × 50 ms, and its comment names that case: "git run outside T3, such as an agent's, can hold it". withSharedConfigLock is a separate, extra in-process serializer. The push-path gh-merge-base / t3-merge-base-ref writes (~2676) go through writeConfigValue, which retries. The other parts of the fork intent are upstream too: LC_ALL=C in executeGitWithStableDiagnostics and the "File exists" pattern. No fork code is left, so there is nothing to port. A zero-fence entry would only describe upstream behavior.
| hidden: | ||
| pendingUserInputs.length > 0 || | ||
| phase === "running" || | ||
| resolveIdleCompaction( |
There was a problem hiding this comment.
Bug: the fork's resume-compaction card skips two of upstream's suppressions.
It took upstream's resolveIdleCompaction === "off" check, but not !hasHeldQueuedRuns or multipleModelSelections === null. On a thread with held queued runs or an active multi-model selection and an expired cache, upstream deliberately offers no compaction. The fork card still offers Compact, and compacting then runs ahead of, or against, the held queue.
There was a problem hiding this comment.
Fixed in 8f55cb7. The card's hidden now also covers hasHeldQueuedRuns and multipleModelSelections !== null, next to the resolveIdleCompaction === "off" check, which matches upstream's suppressions.
| /* fork:begin fork-side-chat — see .fork/customizations.yaml#fork-side-chat | ||
| The side chat window is only a chat; the main window runs everything below once, so a | ||
| second copy would double notifications, sounds and app-wide toasts. */ | ||
| if (isSideChatSurface) return router; |
There was a problem hiding this comment.
Bug: the side chat window starts app-wide subscriptions a second time.
This early return comes after startAgentOpenedPreviews, startPreviewTitleRecording, subscribeDraftThreadPromotion, subscribeComposerDraftModelReconciliation and installChatRouteCommands. Each of these covers every thread and environment, and the main window already runs them. With the side window open, agent-opened previews and preview-title recording run in both windows, which doubles the work and can open preview state in the side window. Before this sync the work was per-ChatView, so the side window only covered its one thread. Moving the return above these calls would fix it.
There was a problem hiding this comment.
Fixed in 8f55cb7, more narrowly than suggested. startAgentOpenedPreviews and startPreviewTitleRecording are now gated on !isSideChatSurface, because both follow every thread with previews and the side chat shows no panels. I left the early return where it was. The other three ran in the side chat before this sync too: chat-route shortcuts from the _chat layout, draft model reconciliation from AppRoot, and draft promotion acting on the window's own write-isolated draft store. Moving the return above them would take those away from the side chat. Guarded in forkSideChat.test.ts.
| }); | ||
| }; | ||
| const scrollToEndStable = useStableCallback(scrollToEnd); | ||
| // fork-split-threads: splitPane.focused is read, not a dependency. Focus |
There was a problem hiding this comment.
Fork rule: unfenced fork edit in an upstream file.
The fork-only comment and the oxlint-disable-next-line have no fence. .fork/AGENTS.md: "inline edit of an upstream file → keep the hunk small, fence it." Fence audits (grep -a fork:begin) can't see this, so the next sync can take upstream's effect and quietly bring back the splitPane.focused dependency this commit removed.
- PR-review automations saved with a dedupe before the save-time strip still took the coalesced path and lost their PR trigger. Delivery now treats any GitHub task as no-dedupe. - The fork's resume-compaction card also stands down for held queued runs and a multi-model selection, as upstream's offer does. - The side chat skips agent-opened previews and preview title recording; both follow every thread and the window shows no panels. - Glass Settings buttons match any popup, so dialogs and menus portaled out of the Settings modal keep the chip look. - The stash shortcut, open-in-editor and chat shortcuts read useOwnsGlobalInput(); dictation's focus-loss effect and settleWithoutControls share one settleSession; the new-worktree fence asks resolveEffectiveEnvMode; the split-pane deps comment is fenced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Triage of the Oct 30 review on b9241ac. Fixes are in 8f55cb7. The inline items have replies on their threads, except the Fixed
Not changed
Checks run: the Claude Opus 5.5 in Claude Code (T3 Code). |
… after the sync Upstream's simpler composer (pingdotgg#25869) changed three things the fork leaned on without a conflict: - composer-host paints its own glass ::before when a context row is present, which drew a dark tray behind the fork's vessel. It joins the shell pseudo-elements the fork already switches off. - The shell's context marker is data-context-row now, not data-with-context, so the 8px gap between the chips and the vessel never applied. - The chips' and the model trigger's carets went with ComposerControlChevron. The worktree and branch chips and the fork's model picker shadow draw them again. Guards in forkComposerShell.test.ts pin all three. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… PR review A GitHub task saved with a dedupe before the save-time strip still passed its replay key to the immediate path, so the same PR number in a second watched repository read as a duplicate. Delivery hands that path the task without its stored dedupe; capacity and rate reservations still apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fork lint gate holds no-low-value-comments at its 876 baseline, and the "(pingdotgg#25869)" reference made it 877, failing Lint on the sync PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #240 (landing draft cleanup) and #241 (design mode fixes). Upstream moved the right panel's width hook from ChatView into ChatRightPanel, so design mode's panel-room floor now reaches it through a fenced minWidth prop; manifest watch entry and guard follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The landing-draft header and a guard header were over four lines, the panel-room effect had no stated reason, and the boot browser test cleaned up in afterEach instead of a disposable. All four tripped the lint-owned ratchet once custom merged in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Takes the upstream sync (#239) ahead of it landing on custom. The one conflict is the draft overlay in ChatView: both the Space Home stamp and the new-agent-draft className fence stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problem
customis 1069 upstream commits behindpingdotgg/t3codemain (through fe219c4).Change
Merges upstream main directly. 93 files conflicted. Upstream split ChatView, ChatComposer and MessagesTimeline into hooks and row modules, so those files were rebuilt from upstream's versions and the fork's fenced intents re-applied at their new homes (useComposerSend, useThreadLifecycle, ThreadActivityBanners, useComposerSelection, chatRouteCommands, Timeline*Row, AppearanceSettingsPanel).
Fork-side follow-ups in the merge:
Two commits on top:
no-low-value-commentsrule and would have turnedvp checkred.BackgroundThreadContext. The fork's own global-input hooks did not, so pressing 1-9 could answer the hidden thread's pending question, and the right Command dictation hotkey recorded into the hidden draft instead of the fallback toast (a session still preparing went on to record unseen). Both now stand down. The same commit restores the pre-sync ChatView focus effect, so clicking into a split pane no longer pulls the caret to the end of its prompt.schedule_task. The budget now records the fork's size, and the budget gate compares a PR with its base (custom) instead ofmain, the upstream mirror, which would otherwise fail every fork PR. Folded intoci-on-customwith a guard row.ComposerRuntimeModeTriggerstill rendered a Base UISelect.Trigger, which throws without a Select root. Any thread in a non-default permission mode crashed the composer. It now renders upstream'sComposerControl. Upstream's ScheduledTasksSettings case for a user-opened New task dialog cannot happen here (New task opens the fork's/scheduledpage), so it is fenced and replaced with a case pinning that.1f1b565re-homes fork behavior that upstream's split modules dropped (side chat services and toasts, New worktree from a previous worktree, multi-model design pills, per-pane Find, the compaction "Never" setting, Open automation routing, startup recovery URL, Glass Settings nav in the new modal).b9241ackeeps PR-review automations on their PR routing when a stored dedupe exists and makes the automation share the GitHub driver's sign-in. See the triage comment for each finding.ChatRightPanel, so design mode's minimum width now reaches it through a fencedminWidthprop (manifest watch entry and guard updated). Code from fix(web): blank drafts no longer pile up in the sidebar #240/fix(web): bring design mode back in line with the fork's design system #241 that tripped the sync's new lint rules is fixed: two over-long comments, a panel-room effect reason, and a browser test that now cleans up withusing.Scope and approval
Routine fork upstream sync, per
.fork/AGENTS.md. Upstream code is taken as-is; the follow-up commits fix only fork code the sync broke.Verification
vp run typecheck: all 36 packages pass.vp test run src/__fork_guards__fromapps/web): 84 files, 627 tests pass. Three new cases (background digits, background dictation fallback, dictation preparing when backgrounded) each fail with their fix reverted.vp lint: 0 errors.node .fork/lint-owned.mjs,vp run knip:check,vp fmt --check, and both GraphQLgenerate --checks pass.lint:mobile(ktlint/detekt not installed here). Known host-only failures on Apple Silicon, not regressions: the Windows cross-arch case inscripts/build-desktop-artifact.test.tsand DesktopLinuxUrlHandler theme-icon tests on APFS.Reviewed with sol-loop: 3 rounds with gpt-6.1-sol on high. Sol verdict: approved
Follow-up fixes after review (8f55cb7, fa6bf57, 16b448d) reviewed with sol-loop: 2 rounds with gpt-6.1-sol on high. Sol verdict: approved
Claude Opus 5.5 in Claude Code (T3 Code).
🤖 Generated with Claude Code