Repository navigation
chore(sync): merge upstream v0.0.42 (T3O-46) - #110
Conversation
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…tgg#11017) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…gg#11381) Co-authored-by: yashranaway <yashranaway@users.noreply.github.com>
…ngdotgg#8309) Co-authored-by: Julius Marminge <julius0216@outlook.com>
…tgg#11958) Co-authored-by: Antony <tnybyn@gmail.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…sync (T3O-46) PR #57 (5698920) landed the v0.0.38 upstream sync as a squash, so t3o kept upstream's content but lost the parent pointer: merge-base with upstream sat at 0640410 (2026-08-08) and a v0.0.42 merge produced 891 conflicted files. This is a '-s ours' merge. The tree is byte-identical to its first parent; it only records v0.0.38 (c0995d2) as a second parent, which is truthful because t3o's tree already contains v0.0.38 in full. With it, the v0.0.42 merge drops to ~50 conflicted files. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1,051 upstream commits. 50 conflicted files (~120 hunks) after the ancestry graft in the previous commit; 891 without it. Resolutions worth knowing about: - Forgejo: the fork's fgj-based implementation (t3o-28) is retired in favour of upstream's fj/tea one, which landed at the same paths. 12 fork files and the redundant registration markers are gone. The board's two merge seams (mergeChangeRequest, changeRequestMergeState) are re-attached through a fork-owned forgejoMerge.ts built on upstream's REST `api` surface; the provider carries one delegating spread. - ws.ts shell coalescing: upstream now slims window events to routing fields. Board deltas are built from the payload, so board events stay whole and a stock event keeps one bit (todosChanged). Lives in shellCoalesce.ts. - OrchestrationEventStore: the fork's rows-scanned paging re-expressed on upstream's Stream.paginate. - ProjectionState gained upsertMany; the board-aware repository routes each row to the database that owns its projector. - MCP capabilities: upstream's typed requireMcpCapability, plus "board". - Composer: upstream extracted runtimeModeConfig itself, so AccessLevelPicker is dropped and the board's model-row menu moved to a fork-owned BoardModelControlsMenu; CompactComposerControlsMenu is upstream's verbatim. - VcsProcess: a pinned GitHub credential outranks the per-project gitenv token. - docs/internals/scripts.md deleted upstream; accepted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Upstream's runtime pipeline now commits every projector cursor as one batch. The board-aware repository splits it into one statement per database and orders listAll like upstream's, so cursor snapshots compare equal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hermes has no Array#toSorted (new upstream rule), an eslint-disable went unused, and the re-wrapped JSX needed formatting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Merge-log row, the decisions taken on this sync, a never-squash landing rule with the -s ours graft documented as the repair, refreshed marker census (177 across 66 files) and unmarked-edit debt table, the Forgejo inventory rows retired and the two new merge-seam rows added. docs/t3o/dev-ports.md carries the section rescued from upstream's deleted docs/internals/scripts.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
brentkelly
left a comment
There was a problem hiding this comment.
High-level summary
This PR repairs the upstream ancestry the squash-merged v0.0.38 sync destroyed (an -s ours graft of v0.0.38), merges upstream v0.0.42 into t3o, re-attaches the fork's seams, and retires the fork's fgj-based Forgejo implementation in favour of upstream's fj/tea one, with the board's two merge seams rebuilt in a fork-owned forgejoMerge.ts on top of ForgejoCli.api. I reviewed the conflict resolutions (git show --remerge-diff de4c844a0), the fork's delta over v0.0.42 in the upstream-owned files, and the three follow-up commits, rather than upstream's own 1,051 commits. The resolutions are careful: the event-store paging is correctly re-expressed on rows scanned, upsertMany is routed per database and matches upstream's statement, contracts keep both sides so persisted settings still decode, and no dangling references to the deleted Forgejo/AccessLevelPicker/openPullRequestLink code remain (web and mobile typecheck clean). I found no critical problems. There are 3 improvements — the tea path drops the forge's refusal text the merge seam was written to surface, board card chats lose the model/access controls while upstream's new resting composer is at rest, and upstream's new first-run wizard exits to the threads home rather than the board — and 4 nitpicks. Reminder for landing, since it is easy to get wrong from the GitHub UI: this must be a real --no-ff merge, never a squash.
GitHub caps a PR diff at 3,000 files and this one has ~14,700 (11,700 of them vendored
.repos/), so none of the files below are resolvable as inline anchors — the API rejects them withPath could not be resolved. The findings are therefore listed here, each linked to its exact lines at the reviewed commit.
Findings
apps/server/src/sourceControl/forgejoMerge.ts:66-68
Warning
Improvement (1/3): the forge's refusal text is lost whenever the server is authenticated through tea
forgejoRefusalDetail lifts Gitea's {"message": …} out of an error detail shaped … (HTTP 405): <body>, and its doc comment says ForgejoCli.api reports failures that way. That is only true of the fj branch (requestFj). The tea branch of ForgejoCli.api never includes the response body: a 4xx becomes the constant Forgejo API request failed (HTTP 405). (ForgejoCli.ts, the status >= 400 block after tea api --include). So on a tea-authenticated server a refused merge reaches the card as a bare status code, and the message this module calls "the product" — what tells a conflict from a failing required check — is gone. Classification still works because it comes from the changeRequestMergeState probe, so this is degraded output rather than a wrong verdict, but the only test (refused(405, JSON.stringify({ message … }))) exercises the fj shape and would not notice.
Suggested fix: in fail, when cause.command === "tea" and the detail carries no body, fall back to a status-specific sentence (405 → "Forgejo refused the merge; open the pull request to see what it is waiting on", 409 → conflict), or re-read the PR and derive the reason from the merge-state probe; add a test with the tea-shaped error. Correct the forgejoRefusalDetail comment either way.
apps/web/src/components/ChatView.tsx:3711-3721
Warning
Improvement (2/3): embedded (board card) chats lose the model / traits / access controls while the composer is resting
Upstream's v0.0.42 resting composer relocates those controls out of the footer and portals them into a host inside the context strip: in ChatComposer.tsx they render only when composerControlsInStrip && restingControlsHost, and the footer renders nothing for them while resting. This seam forces mountComposerContextStrip to false for chrome === "embedded", so restingComposerControlsHost stays null in a card modal, and with a null host the resting controls render nowhere. Before the merge the footer always carried them. Upstream tolerates a missing strip, so nothing crashes, but a card chat that is scrolled up / unfocused now shows no model or access-level control until the composer is focused again — a regression specific to the fork's seam interacting with new upstream behaviour.
Suggested fix: for embedded chrome, either mount a bare host div for the resting controls (without the branch/worktree strip the board deliberately hides), or pass the composer a flag that disables the resting relocation. Whichever is chosen, note it at this T3o: comment.
apps/web/src/routes/welcome.tsx:38-47
Warning
Improvement (3/3): upstream's new first-run wizard exits to the threads home, bypassing the fork's board landing
On a fresh install the fork's cold-start redirect sends / to /board; upstream's new FirstRunGate then replaces that with /welcome, and this onDone navigates to / after the cold-start flag has already been consumed. The user therefore finishes onboarding on the stock threads view. Pairing has a deliberate exit rule for exactly this (resolvePairExitTarget); the wizard arrived with this merge and has none, and docs/t3o/seams.md does not record a decision either way.
Suggested fix: decide it explicitly. Either add a one-line T3o: seam that routes the no-project exit through the same board-home helper the pairing exit uses, or record in docs/t3o/seams.md that the wizard intentionally exits to threads.
apps/server/src/orchestration/shellCoalesce.ts:80
Note
Nitpick (1/4): per-aggregate collapse discards an earlier event's todosChanged bit
Stock events collapse to the last event per aggregate, and todosChanged is a property of one specific event. A turn.plan.updated activity followed in the same window by any other event for that thread (a message delta, a session status) survives only as the later event with todosChanged: false, so boardCardThreadsShellEvents skips the refetch. This matches pre-merge behaviour, and is currently masked by the known TODO(shell-sibling-sequence) gap (the client drops the sibling delta anyway), which is why this is a nit — but it will surface the moment that TODO is fixed.
Suggested fix: when replacing a stock entry in latest, OR the bit forward: todosChanged: previous.todosChanged || event.todosChanged; add a case to shellCoalesce.test.ts.
Note
Nitpick (2/4): fork insertion into an upstream-owned file without a T3o: marker
This import is a fork insertion into ws.ts but carries no T3o: marker, unlike the two imports below it. The same goes for the toShellStreamEvents doc comment further down, which mentions t3o-18 but not the greppable T3o: prefix. The marker census in seams.md relies on the grep.
Suggested fix: prefix both with a // T3o: line naming the reason.
apps/web/src/components/settings/BoardModelControlsMenu.tsx:23
Note
Nitpick (3/4): doc comment refers to itself
"It began as an extension of the chat composer's BoardModelControlsMenu" — a rename slip; the composer's menu is CompactComposerControlsMenu.
Suggested fix: name CompactComposerControlsMenu here.
Note
Nitpick (4/4): the inherited-workflows table was not updated for v0.0.42, and no longer matches the repo
This sync brings in cursor-hygiene-webhook.yml (fires on every PR open; it ran on this PR and no-op'd only because the secrets are absent — if they were ever set it would POST the PR event payload to a third-party URL) and desktop-macos-preview-publish.yml (workflow_run + pull_request_target). Neither is in the table. Separately, gh workflow list --all shows the table has drifted from reality: ci.yml, pr-size.yml and pr-vouch.yml are listed as "kept on" but are disabled, while desktop-macos-preview.yml, web-preview.yml and mobile-fingerprint-check.yml are listed as disabled but are active. The runbook also has no step telling the syncer to check for newly inherited workflows.
Suggested fix: add the two new workflows to the table (and disable them with gh workflow disable), reconcile the table with gh workflow list --all, and add a "check for new workflows" step to the sync runbook.
… (T3O-46) Two round-1 review findings. `forgejoRefusalDetail` only ever saw a forge message on a server whose credentials belong to `fj`; `tea`'s branch of `ForgejoCli.api` reports the status alone, so a refused merge reached the card as a bare `(HTTP 405).` A refusal status with no message is now rendered from the status. An embedded chat (the board card modal) suppresses the composer context strip, which also removed the host that upstream's resting composer portals its model, traits and access controls into — so a card's chat lost them the moment the composer rested. Embedded chrome now mounts a controls-only stand-in for that strip. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s (T3O-46) The rest of round 1. Upstream's new first-run wizard exited to the threads home. It opens after the cold-start redirect has already been spent, so setting T3o up for the first time was the one path that never saw the board; it now exits where pairing exits. A wizard that set up a project still opens a thread in it. A stock shell-window survivor now carries a collapsed `turn.plan.updated`'s `todosChanged` bit, so a plan revision followed in the same window by any other event for that thread still refetches the card's thread todos. Also: `T3o:` markers on the two unmarked `ws.ts` insertions, a doc comment that named itself instead of `CompactComposerControlsMenu`, and an inherited-workflows table that had drifted from the repo in both directions plus a sync-runbook step to stop it drifting again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Triage — round 1All seven findings fixed, at GitHub still cannot anchor inline threads on this PR (~14,700 files vs. the 3,000-file diff cap), so the replies are here, one per finding id.
|
Adjudication — round 1 (T3O-46)Adjudicated at I re-derived every claim from the code rather than from the triage notes, and for each of the four Verdict: 7 upheld, 0 incomplete, 0 absent.
Test evidenceReverting only the three changed server sources to their parent state, with the new tests left in All 41 tests in those three files pass on HEAD, as do the 12 in r1-i1 — fix-upheldThe stated cause is true in the source, not merely plausible. One narrow behaviour change worth knowing about rather than fixing: an r1-i2 — fix-upheldThis is the one fix with no test, and the stated reason for that holds up:
Residual, cosmetic and new rather than unfixed: r1-i3 — fix-upheldThe ordering the fix rests on is real: r1-n1 — fix-upheldThe OR-forward is correct and correctly scoped: board events are excluded from the merge on both r1-n2 — fix-upheldBoth insertions the finding named now carry markers. Checked against r1-n3 — fix-upheld
r1-n4 — fix-upheldI re-ran the author's verification independently rather than taking the table on trust, and every Landing rule unchanged and worth repeating, since it is the one thing this PR cannot recover from: |
brentkelly
left a comment
There was a problem hiding this comment.
High-level summary — round 2 (re-review at 7b7b796d6)
This PR repairs the upstream ancestry the squash-merged v0.0.38 sync destroyed, merges upstream v0.0.42 into t3o, re-attaches every fork seam and retires the fork's Forgejo implementation for upstream's. This round I re-read the two fix commits since round 1 line by line, re-ran the tests they touch (41 server + 12 web, all green at this head), and swept the remaining fork delta over v0.0.42 in upstream-owned files — including the destructive multi-database snapshot/restore in serviceLauncher.ts and the SERVICE_LAUNCHER_PROTOCOL = 3 bump, both of which survived the merge intact, and the consumer of the coalesced todosChanged bit in board/rpc.ts, which reads only the bit and so is safe with the OR-forward merge. All seven round-1 findings are closed. Nothing blocks: no critical and no improvement findings this round, and one nitpick — a cosmetic side effect of the r1-i2 fix. The handful of fork edits in upstream files that still carry no T3o: marker (serviceLauncher.ts, serviceProtocol.ts, contracts/model.ts, contracts/git.ts, the DM Sans import) all predate this PR, and the plan's "mark only where a conflict forces a touch" rule covers them, so they are not raised.
Landing reminder, unchanged: a real git merge --no-ff by hand, never squash.
Status of round-1 findings
| # | Prior finding | Status |
|---|---|---|
| r1-i1 | 🟡 Forge refusal text lost when the server is authenticated through tea | ✅ closed — 405/409 rendered from httpStatus when no message was embedded; fj message still wins; tests green |
| r1-i2 | 🟡 Embedded board-card chats lose model/traits/access controls while resting | ✅ closed — BoardRestingComposerControlsStrip supplies the host; mutually exclusive with the stock strip. Untested by necessity (no DOM test env). Left one cosmetic residue, see the nitpick below |
| r1-i3 | 🟡 First-run wizard exits to the threads home, bypassing the board | ✅ closed — both navigate exits use resolveOnboardingExitTarget; project-created path deliberately unchanged |
| r1-n1 | 🟢 Per-aggregate collapse discards an earlier todosChanged bit |
✅ closed — OR-forward on stock events only; test added |
| r1-n2 | 🟢 Fork insertions in ws.ts without a T3o: marker |
✅ closed |
| r1-n3 | 🟢 Doc comment refers to itself | ✅ closed |
| r1-n4 | 🟢 Inherited-workflows table stale | ✅ closed — table rewritten, runbook gained the workflows diff step |
Findings
GitHub still cannot anchor inline comments on this PR (~14,700 files against the 3,000-file diff cap — the API answers "Path could not be resolved"), so the round's one finding is here with a permalink.
Note
Nitpick (1/1): Embedded chat's resting controls render a leading separator with nothing before it
t3code-orchestrator/apps/web/src/components/ChatView.tsx
Lines 9712 to 9714 in 7b7b796
restingControlsHaveLeadingContext is passed as isGitRepo || showComposerEnvironmentIndicator regardless of chrome. In the Threads view that is right: the branch/environment context sits in the strip ahead of the portalled controls, and ChatComposer.tsx:4897 draws a ComposerControlSeparator between them. In an embedded card chat the new BoardRestingComposerControlsStrip holds the host and nothing else, so on any Git project the controls open with a divider that divides nothing. It is hidden below a 400px composer surface, so only a wide card pane shows it. Purely cosmetic.
Suggested fix: pass chrome !== "embedded" && (isGitRepo || showComposerEnvironmentIndicator) under the existing T3o: seam comment.
…trols (T3O-46) restingControlsHaveLeadingContext was passed as `isGitRepo || showComposerEnvironmentIndicator` regardless of chrome. An embedded board card chat mounts BoardRestingComposerControlsStrip, which holds the relocated controls alone, so on a Git project the composer drew a ComposerControlSeparator with nothing ahead of it — and measureRestingComposerControls charged its width to the fixed budget. Gate the flag on composerContextStripAllowed, the same named gate the two neighbouring strip derivations use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Triage — round 2One finding this round, fixed. Pushed as (Reply posted as a PR comment rather than an inline thread for the same reason as rounds 1 and 2: this PR's diff is ~14,700 files against GitHub's 3,000-file cap, so every anchor is rejected with "Path could not be resolved".) 🟢
|
Brings T3O-48's origin-pinned `gh` invocations onto the upstream-sync branch. Two conflicts, both in source-control tests, both "each side appended its own tests after the same anchor": - SourceControlProviderRegistry.test.ts — v0.0.42 declares `github`/`gitlab` stubs on `makeRegistry` itself, so T3O-48's own `github` field and its T3o marker are dropped as redundant; its two origin-vs-upstream tests are kept, appended after upstream's. The Forgejo mock resolves to upstream's `listLogins` shape, and the fork's four `fgj` routing tests stay retired — this branch swapped fgj for upstream's fj/tea. - GitHubSourceControlProvider.test.ts — upstream's `resolveLink` and read/decode-failure tests kept in place, T3O-48's five repository-pinning tests appended after them. Every other file both sides touched auto-merged to exactly "sync branch plus T3O-48's change", verified hunk by hunk: board/rpc.ts (the coalesced `todosChanged` consumer alongside the new refresh result), supervisorReactor, supervisorHarness.testkit, BoardCardDetail, contracts/board.ts, client-runtime/state/board.ts and seams.md. Checks: apps/server, apps/web, contracts and client-runtime typecheck with zero errors; 245 sourceControl + 916 board server tests and 2,925 web board / contracts / client-runtime tests pass; `vp lint apps/server` reports no errors and `vp fmt --check` is clean. Landing rule unchanged: `git merge --no-ff` by hand, never squash. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Syncs the fork to upstream
v0.0.42. The v0.0.38 sync was squash-merged, which dropped upstream ancestry, so this branch first graftsv0.0.38with-s ours(tree unchanged) and then mergesv0.0.42for real. Fork seams are re-applied, the fork's fgj-based Forgejo implementation is retired in favour of upstream's fj/tea one, and the runbook indocs/t3o/seams.mdis updated.Do not squash-merge. Land by hand with
git merge --no-ff; a squash destroys the upstream ancestry again.Claude Fable 5.1 via Claude Code.
🤖 Generated with Claude Code