Skip to content

fix(git): resolve PR badges for untracked prNNNN checkouts by number - #376

Open
omegent-app[bot] wants to merge 141 commits into
fork/devfrom
fix/pr-badge-numeric-branch-fallback
Open

omegent-app[bot] wants to merge 141 commits into
fork/devfrom
fix/pr-badge-numeric-branch-fallback

Conversation

@omegent-app

@omegent-app omegent-app Bot commented Aug 7, 2026

Copy link
Copy Markdown

Closes the last gap in the PR-badge chain: threads whose agents check out a PR with
git fetch origin <head-branch>:prNNNN — a renamed local branch, no upstream tracking.

The problem

The badge lookup finds a thread's PR by head selector: the local branch name, or the head branch
its tracking points to. A pr2182-style checkout has neither — the local name matches no PR's head,
and there is no tracking to derive the real one from. The thread reviewing PR pingdotgg#2182 was the live
case: gh pr list --head pr2182 correctly returns nothing, so the row showed no badge for the very
PR it existed to review. It only lit up after hand-setting branch.pr2182.merge on the worktree.

The fix

In findLatestPrForHeadContext, when the selector search finds nothing and the branch has no
tracking-derived head (headBranch === localBranch) and the name matches ^pr[-_]?\d+$, resolve
the PR by that number via the provider's existing getChangeRequest (gh pr view N /
glab mr show N — provider-neutral). A failed by-number lookup degrades to "no PR", exactly as
before.

The tracking gate matters: a tracked branch that happens to be named pr123 keeps the precise
selector path, so the number can never override a real head selector — covered by the negative test.

Tests

  • status resolves the PR by number for an untracked prNNNN checkout — fails on the pre-fix
    implementation (verified by stashing the fix), passes after; also asserts the pr view 2182 call
    actually happened.
  • status does not resolve by number once a prNNNN branch gains tracking — asserts pr view is
    never called and the row stays badge-less when the tracked head genuinely has no PR.
  • Full GitManager.test.ts: 84 pass. Typecheck clean.

Retroactive by design: existing worktrees need no touch-up — the fallback keys off state the server
reads on every sweep.

Co-authored by @patroza

opened by Patrick Roza in chat thread Discord · Discord · T3

github-actions Bot and others added 30 commits August 5, 2026 15:48
Add custom "Open with" applications

Source: tim-smart#4
Source head: 8c4bdfbc5b57f6b600233244d330f9efa41dc498
Source commits: 08e1a4fb949585c3c441d6d00455fe904f72cd7b,cd43a401c6c148f1fe26cff72104ac527ea189f3,a8370e7502c552ebb064436e42e1c00f86f0946b,8c4bdfbc5b57f6b600233244d330f9efa41dc498
Imported: complete product delta from the source PR.

(cherry picked from commit 9fae005)
Load direnv environments for provider sessions

Source: tim-smart#5
Source head: 8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Source commits: e4f07014d39964fde2498bcb35588974cc5e6232,0d1463af61e0bd174f698b2519ebf3b207a2eaca,a66e4160d5f4b79140ec8fbcbc6aa66af750a991,8f5fc87c13f4628c179cda44d4f32f7fe4d316b2
Imported: complete product delta from the source PR.

(cherry picked from commit 0da8bfe)
Add unsigned retry for commit signing failures

Source: tim-smart#6
Source head: 7d65c5a224e97a6b811b0a84892f1fda065c5963
Source commits: 18ee567ecfdb11c9372153127b26b5cf57213a76,72a6fae23c86708080c4fed346d5bf0f136f0221,6614b28239ed2330a8f601357a413f2d50da195a,ec169369daa554541511aa28f551b36f3dd26485,7d65c5a224e97a6b811b0a84892f1fda065c5963
Imported: complete product delta from the source PR.

(cherry picked from commit 03671a2)
Add /new command for contextual threads

Source: tim-smart#7
Source head: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Source commits: 2051a8003041fe2806fcb4bc7a0d8940579fc543
Imported: complete product delta from the source PR.

(cherry picked from commit 4d94f31)
Add session dashboard board

Source: tim-smart#8
Source head: d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Source commits: 268fb8df9863ffbda51b975a8dbe68f11c41500c,dde20f271f674da22dd8f3a08201c2acf5e58ee5,df4a145e7b2cd2dc17a7a595267d2d8eb0a2a3f0,ce5723ddb0bf630a18d4cb8227b5344d12626e72,ad8c1a6af41161e1fc38a52f681b306517c7b918,6281887e6125317da0c7b4252d59bfd41c9bf35e,550db6316c634febdbe1cb27334d1347c23c7b2a,d9f8e4d0a8dc22231ca315f3c595c3597f3b13e5
Imported: complete product delta from the source PR.

(cherry picked from commit cd0e281)
Recover interrupted provider turns after server restarts

Source: tim-smart#9
Source head: b181832560177250b90bbfe07b0882c9e5b93493
Source commits: 7f69028a25be21f1882ecba14b62f387ad60cf2a,1d52bce1376766d804ef884d7d50b8b6d1b48cf7,b181832560177250b90bbfe07b0882c9e5b93493
Imported: complete product delta from the source PR.

(cherry picked from commit 83de8f5)
Avoid repeated thread snapshot loads during subscription retries

Source: tim-smart#10
Source head: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Source commits: c8c9eadb9de3026706bc3a403ca05b12d0da8dd5
Imported: complete product delta from the source PR.

(cherry picked from commit 9e400c3)
Add image upload button to compact chat composer

Source: tim-smart#11
Source head: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Source commits: 1ff63f9b9c418ef56a46c6422d22c01de97581a8
Imported: complete product delta from the source PR.

(cherry picked from commit 720ec65)
Truncate mobile branch toolbar controls

Source: tim-smart#12
Source head: 1b7d44428472511bc98d8f936654359ce2536901
Source commits: 1b7d44428472511bc98d8f936654359ce2536901
Imported: complete product delta from the source PR.

(cherry picked from commit dc2bbb4)
Clean up worktrees when archiving threads

Source: tim-smart#13
Source head: a23f42d6ac671ea36b8db5d03934c089a31be448
Source commits: 4a194707ed134f993502ac5fdf36a8425f1769cd,1b6688aa5b641010cb2e9dad23d36d87257403ad,9ed32aa3923fb674380564b1ffcb3268290069b9,a23f42d6ac671ea36b8db5d03934c089a31be448
Imported: complete product delta from the source PR.

(cherry picked from commit 7e02dc9)
Pass hosted app channel into Vercel web builds

Source: tim-smart#14
Source head: de6966a6784b4703145c20b84fc482703bca4fa2
Source commits: de6966a6784b4703145c20b84fc482703bca4fa2
Imported: complete product delta from the source PR.

(cherry picked from commit 6333d8d)
Allow worktrees to reuse the selected branch

Source: tim-smart#15
Source head: 2d3900ba36c9397dc4fbe879c613a809f6b45384
Source commits: cd60531253fbafc470f5a5ac18d3e44832d3376d,2d3900ba36c9397dc4fbe879c613a809f6b45384
Imported: complete product delta from the source PR.

(cherry picked from commit 5e7dff2)
Add optional worktree removal confirmation

Source: tim-smart#16
Source head: c3f509fe8f690b704bb34692d9c132c0644db777
Source commits: 76f063e983ca3c39b20f79d8ea83783ab034251a,c3f509fe8f690b704bb34692d9c132c0644db777
Imported: complete product delta from the source PR.

(cherry picked from commit 9886109)
Stop retrying unavailable thread subscriptions

Source: tim-smart#17
Source head: 1359af8ba0b146e3d49f89b72c250f681e86199d
Source commits: 1359af8ba0b146e3d49f89b72c250f681e86199d
Imported: complete product delta from the source PR.

(cherry picked from commit 7b37a7a)
…nd; green tip

Bring Tim layer tip to typecheck green by joining main ref-refresh VCS client
state with fork failureKind/worktree-cleanup contracts, restoring
filterBrowseEntries/reuse-base-branch surfaces Tim dropped, and fixing
ChatView/Board call-site type errors left by incomplete Tim joins.

(cherry picked from commit 0e24917)
Bring fork/tim typecheck/test green after main pingdotgg#2679 + Tim client-runtime
rewrite: rejoin EnvironmentSubscriptionRpcTag/localApi/ws scopes, wire
BackgroundPolicy/ResourceTelemetry layers, force openpgp for signing tests
on hosts with gpg.format=ssh, and treat TRACE2 child_exit without
child_class as hook finish (git 2.55+).
"work" is no longer an empty full-catalog query once Worktree remove
confirmation is searchable. Keep the word-wrap false-positive check and
assert the worktree setting is the sole full-catalog hit.
Source: pingdotgg#4018
Source SHA: de8fd65

Imported: bounded server activity snapshots, cursor pagination, lazy web history loading, reconnect-safe reset/dedup, and disabled eager browser sidebar hydration.
Adapted: preserved Tim thread lifecycle handling and Omega composer/minimap behavior while resolving current-stack conflicts.
Excluded: none of the source PR behavior; native mobile pagination remains separate because pingdotgg#4018 intentionally excludes it.
…#3510) (#35)

Source: pingdotgg#3510
Source SHA: 034f4936d7a1435887bb62ac3f2db61f08928cbf

Imported: native mobile lazy loading for older thread activity, a 1,000-event subscription catch-up ceiling with snapshot fallback, and synchronized stale snapshot watermarks.
Adapted: applied above the refreshed pingdotgg#4018 web/server candidate and preserved Tim lifecycle handling plus our mobile composer changes.
Excluded: pingdotgg#3510 server/web pagination duplicated by pingdotgg#4018, the later shared-hook refactor, formatting-only commits, and contract comments. The shared refactor can be revisited independently after production validation.
Source: pingdotgg#4176
Source SHA: 56b6615

Imported: O(1) command read-model maps, deleted-thread eviction, VCS cache cleanup, browser surface cleanup, preview idle TTL, and per-thread UI cleanup.
Adapted: preserved our thread settlement, snooze, and sequential worktree deletion actions while wiring upstream cleanup into the current hook.
Excluded: none.

Co-authored-by: Rusiru Sadathana <27785781+RusiruSadathana@users.noreply.github.com>
#44)

Source: pingdotgg#4506

Source SHA: f7eaa00

Imported unchanged as one candidate provenance commit.
…tgg#4558)

Imported from https://github.com/pingdotgg/t3code/pull/4558\n\nAdapted to retain our provider restart-recovery constants while replacing the local default-title check with the shared policy.
…gdotgg#4379) (#312)

Imported from pingdotgg#4379 at
a27510d060645809ae1472bba4dbb248dc624e25.

Open file previews revalidate on mount and subscribe to debounced
native filesystem watches so external edits (editors, git, agents)
show without a manual refresh.

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Enrico Polanski <16064771+enricopolanski@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
… (#328)

Imported from pingdotgg#5344 at source SHA
783fd02 (commits b623dc2 + 783fd02
squashed into one provenance commit).

Imported behavior:
- `reduceThreadStreamItems`, a pure reducer that folds a batch of thread
  stream items into one state and one persistable snapshot.
- `Stream.groupedWithin(64, 16ms)` on the live subscription so a burst of
  thread events publishes the `SubscriptionRef` once instead of per event,
  and web/mobile stop rebuilding large thread views per streamed event.
- `eventBatchSize` on `EnvironmentThreadStateOptions`, plus the upstream
  regression tests for ordered single-publication bursts and for persisting
  a settled snapshot when a batch ends with a non-persistable turn start.

Local adaptations:
- Kept our `httpSnapshotLoadAttempted` guard around the HTTP snapshot
  fallback; the call now goes through `applyItems([...])`.
- Restored `setDeleted` (removed upstream) for the terminal
  `thread-deleted` subscription failure, which never reaches the item
  stream and so cannot go through the batch reducer. Cache removal is
  shared with the reducer path via `removeCachedThread`.

Excluded:
- `tasks/todo.md`, the author's scratch checklist.

Follow-up (fork/changes, not this layer): our `reload-required` branch and
`reloadFromServer` are built on the deleted `setThread`, so rebasing
fork/changes onto this layer must re-express them against the reducer
(split the batch at the reload point, then re-enter `applyItems` with the
remainder).

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
omegent-app Bot and others added 18 commits August 7, 2026 05:52
Eight commits, headlined by pingdotgg#5493 "paginate thread loading with user-anchored
turn windows" -- 2093 insertions plus a keyset index migration.

Candidate #29 ("perf: import bounded web thread history", upstream pingdotgg#4018) is
confirmed superseded: upstream never merged pingdotgg#4018 and ships loadOlderTurns
instead. The candidate is removed from web, mobile and client-runtime --
olderThreadActivities.ts deleted, ChatView and the mobile composer/feed/screens
rewired to upstream's loadEarlier model. The SERVER half stays: deployed mobile
builds still call orchestration.getThreadActivities, so the RPC, its schemas and
the activity window constant are retained as a compatibility surface and marked
as such.

All 24 textual conflicts resolved to upstream. The expensive work was what git
auto-merged wrong or left dangling, found by typecheck and tests, not markers:

- threads.ts: upstream's applyItemLocked header welded onto the fork's batch
  reducer body, referencing an out-of-scope identifier and silently dropping
  upstream's synchronized branch. The fork's batching layer (groupedWithin,
  reduceThreadStreamItems, eventBatchSize) is removed with its two tests; the
  fork's load-once HTTP fallback guard is reimplemented on upstream's model and
  its regression test passes again.
- ws.ts: the fork's reuseBaseBranch worktree flow and upstream's pingdotgg#5556
  no-origin fallback are combined; neither side alone compiled.
- ProjectionSnapshotQuery: upstream's new windowed message query lacked the
  fork's source_json column, failing decode on every windowed read; the bounded
  detail query destructured nine results from seven queries -- the fork's
  queued-messages and pending-turn-start members are restored.
- BranchToolbar/SidebarV2/MessagesTimeline/ChatView: prop and rename skews
  reconciled; fork surface-existence assertion updated for the new feed call.
- Migration ledger fixtures extended for upstream migration 037, which lands in
  the upstream namespace and does not collide with the fork's renumbered 037.
- Upstream's new tests adapted to fork-required fields (queuedMessages,
  pendingTurnStart) and the fork's projection-wait in bootstrap.

Verified: full recursive typecheck clean across 17 packages; 2264 tests pass
including upstream's 11 pagination and 7 windowed-detail tests. The single
failure (CodexTextGeneration structured output) predates this merge.

fork/tim touches threads.ts, ProjectionSnapshotQuery and contracts; no tim
commit is merged upstream, so review those diffs with tim provenance in mind.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main 6b73b3d into fork/dev
Adopts the ten upstream commits after #368: timeline live-follow (pingdotgg#5566),
reconnect-loop handling during server stalls (pingdotgg#5561), plans folded into chat
(pingdotgg#5558/pingdotgg#5551), plus server settle/reconnect fixes and the transfer-budget CI
test (pingdotgg#5350).

The fork's "keep the open WebSocket lease when a foreground liveness probe
fails" is replaced by upstream's probe model. Both target reconnect churn during
server stalls; upstream splits the wake reasons (probe vs reconnect), adds
tolerance windows and a first-attempt ladder skip. Keeping both was incoherent:
the auto-merge left the fork's swallow in front of upstream's wakeProbeFailed
path, making it dead code. The fork's diagnostics log, labelled ping-timeout
errors and followNetworkStatus resume handling are kept around it.

Welds found by typecheck and tests, not by conflict markers:
- ChatView and ThreadFeed each ended up with two follow-state variables, the
  fork's superseded one still being written; converged on upstream's.
- findSidebarProposedPlan was dropped from session-logic while ChatView still
  called it; restored.
- Two timeline row-union members merged into one malformed member.
- The fork's pre-latch ThreadFeed handlers survived as duplicates alongside
  upstream's; removed, with the fork's unread-activity dot rebuilt on the latch.
- resolveOlderHistoryAutoLoad, left over from the superseded pagination
  candidate, was referenced only by its own tests; removed with them.
- Upstream's transfer-budget test opened a socket without the fork's omegent-t3
  product handshake, so the environment rejected it.

Plan sidebar wiring is removed with the surface upstream deleted; the fork's
settle-independent follow-up composer gate is kept.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main 48aa875 into fork/dev
…373)

PR badges stopped appearing in sidebar v2 for **active** threads, while
settled rows kept theirs.
Root-caused on the running server, fix verified against the exact
failing invocation.

## What was happening

The server's VCS shell-outs resolve `gh` through each repo's
`.tools/bin` — the agent policy shim
installed by `scripts/install-git-hooks.mjs`. Caught live off the server
process:

```
CMD=node /var/lib/t3/deploy/t3code/scripts/agent-gh.mjs pr list --head release-cut-mako-easylife \
    --state all --limit 20 --json number,title,url,...
```

That shim resolves the binary to delegate to via `findRealGh`, which
preferred `$T3_GITHUB_REAL_GH`
above everything else. **That variable names the unauthenticated `gh`**
— the binary the GitHub App
wrapper execs *after* minting an installation token. The host exports it
(see the server start
script: _"still export REAL_GH / token paths so children that reorder
PATH cannot lose minting
config"_) as configuration for the wrapper, not as an authenticated
entry point.

So the shim skipped minting entirely and ran raw `gh`:

```
$ env -i $(server env) .tools/bin/gh pr list --head release-cut-mako-easylife --state all ...
To get started with GitHub CLI, please run:  gh auth login
exit=4
```

Exit 4 → `SourceControlProviderError` → the warning that has been
filling the journal every 45s:

```
WARN: PR lookup failed; keeping last known PR state.  operation: lookupStatusPr
      errorTag: SourceControlProviderError   branch: release-cut-mako-easylife
```

## Why settled rows still showed badges

That asymmetry is the tell. Settled worktrees take the durable settle
freeze added in #362, which
returns `resolveLastKnownPr(...)` **without calling the provider at
all**. A frozen row can't fail a
lookup it never makes. Every active row goes down the live path, hits
the auth failure, and falls
back to a last-known value that is empty — so the card renders with no
badge.

Introduced by `2ef070919` (2026-08-02, "enforce repository-owned agent
ship gates"), which added the
`T3_GITHUB_REAL_GH` branch to `findRealGh`.

## The fix

Resolution order becomes:

1. `AGENT_GH_REAL` — this shim's own re-entry override, unchanged.
2. **PATH** — where the App-aware `gh` lives.
3. `T3_GITHUB_REAL_GH` — last resort only, so hosts without a wrapper
still work.

## Verification

The same command that returned exit 4, under the server's exact
environment, after the change:

```
$ .tools/bin/gh pr list --head sync/upstream-48aa875c0 --state all --limit 5 --json number,state
[{"number":372,"state":"MERGED"}]
```

Regression test added to `scripts/agent-pre-push.test.mjs` pinning all
three tiers of the order; it
fails on the pre-fix implementation and passes after. Full file: 26
tests pass.

The deploy checkout was patched only to prove the fix live and has been
restored to its released
state, so the fleet stays consistent with its deployed SHA until this
lands.

Co-authored by [@patroza](https://github.com/patroza)

opened by [Patrick Roza](https://discord.com/users/95218063095377920) in
chat thread **Discord** ·
[Discord](https://discord.com/channels/1083767712431480922/1534783738322485399/1534783738322485399)
· [T3](https://t3vm/?thread=584a9ad3-243e-4308-8a13-49acdd758b17)

Co-authored-by: omegent-app[bot] <306514130+omegent-app[bot]@users.noreply.github.com>
Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Adopts the six upstream commits after #372: per-device provider settings
(pingdotgg#4479), the modular theme library and theme editor (pingdotgg#5226), Done/Woke sidebar
semantics (pingdotgg#5579), the non-pulsing Working label (pingdotgg#5580), mobile T3 Connect
device cleanup (pingdotgg#5563), and a download-page copy fix (pingdotgg#4511).

Upstream's new filePicker/projectSearch/themeEditor shortcuts arrive with an
overlay-mode host for the command palette. The fork rewrote that component, so
the host was ported rather than taken: the palette reducer gains `mode` and
`ToggleMode`, and the two overlays bring their own popup instead of the popup
being lifted out of OpenCommandPaletteDialog. Without that the new default
keybindings would have been dead.

Welds found by typecheck and tests, not by conflict markers:
- SidebarV2 ended up with two per-row VCS status queries: upstream's full-mode
  `vcsEnvironment.status` auto-merged in above the fork's budgeted
  `listStatus`. Upstream's new isWoke reads prState, so the fork's query and its
  pr/prState derivation are hoisted above it and the duplicate dropped. Keeping
  the fork's list mode matters: full mode is one poller per worktree.
- ElectronDialog: the fork's pickApplication and upstream's pickFiles are
  independent additions that collided at every declaration site.
- The keybindings test fixture kept the fork's list, silently dropping
  upstream's two new bindings while its new assertions expected them.

The v1 sidebar body is a fork rewrite; upstream restructured the same region for
its own layout, so both sides were whole-body alternatives and the fork's is
kept.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main 23f0a1a into fork/dev
Adopts the three upstream commits after #374: mobile keyboard avoiding
(pingdotgg#5451), dev-runner cold-start speedup (pingdotgg#5584), and rewritten --share
instructions (pingdotgg#5586).

Resolutions: dev-runner.ts unions the fork's METRO_PORT passthrough with
upstream's launcher-env scrubbing (T3_SERVICE_LAUNCHER_CONTEXT /
T3_BOOT_SERVICE_UNIT deletion). AGENTS.md's auto-union dropped upstream's
rewritten "Dev servers" bullets into the fork's ship-gate section; the
share/pairing guidance is folded into the fork's own Dev Servers section
instead, replacing its older --share bullet. Lockfile regenerated.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main 8100062 into fork/dev
Adopts the four upstream commits after #375: thread actions from the chat
header title (pingdotgg#5592), per-turn runtime-mode recording (pingdotgg#5593), chat timeline
positioning stabilization (pingdotgg#5449), and the background-work banner no longer
hiding behind the update notice (pingdotgg#5595).

The chat header gains upstream's inline rename and title-menu (shared
threadActionMenu.logic) woven around the fork's remote-VS-Code open and
usage-dot/host-resource chrome. SidebarV2 adopts the shared menu builder in
place of the fork's buildSidebarV2ThreadContextMenuItems; the fork's
copy-thread-id entry moves into the shared menu (and gains a handler in
useThreadActionMenu) so both surfaces offer it.

ChatView takes upstream's folded reconnect/version banner logic (pingdotgg#5595, a
superset of the fork's resuming-update suppression) and the anchor-clearing
rAF scroll (pingdotgg#5449), keeping the fork's unread-flag clearing and its
unread-activity observer.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main b2ee17d into fork/dev
Adopts the seven upstream commits after #377: iOS terminal clear no longer
resets the session (pingdotgg#5440), Android scroll padding above the nav bar (pingdotgg#5415),
Android composer opacity (pingdotgg#5582), Clerk auth navigation headers (pingdotgg#5140), the
mobile app version bump to 1.0.2 (pingdotgg#5588), click-to-unpin (pingdotgg#5578), and the
correct new-thread shortcut in the sidebar tooltip (pingdotgg#5594).

Three resolutions:

- ThreadComposer: pingdotgg#5582 moves the backdrop gradient off Animated.View, where
  Reanimated silently drops experimental_backgroundImage on Android — the very
  bug it fixes. The fork's gradient was on that Animated.View, so it is removed
  there and the fork's stronger lower-band stops (0.82/0.96 dark, 0.88/0.98
  light) ride on upstream's plain-View backdrop instead.
- app.config: upstream's 1.0.2 version bump with the fork's
  MOBILE_RUNTIME_VERSION_OVERRIDE escape hatch kept; its default policy is the
  same fingerprint upstream documents.
- SidebarV2: upstream's pingdotgg#5594 label ordering (chat.new before chat.newLocal)
  adopted — the fork's button has exactly the semantics upstream describes,
  routing multi-project setups through the palette picker and creating
  immediately otherwise. The fork's board and command-palette labels stay.

Upstream's two new react-mocking harness tests (pingdotgg#4479) are added to the fork's
isolatedUnitTestFiles. The fork's unit project runs isolate: false to reuse each
worker's module graph; upstream has no such config, so those tests assume full
isolation. Under a shared registry the component graph can already be bound to
the real react/compiler-runtime by an earlier file, the vi.mock("react") never
applies, and the compiled component reports a memo-cache hit and skips the hooks
the test asserts on. That is what made AddProviderInstanceDialog.environment
fail this PR's CI while passing in isolation. Two other react-mocking tests were
already in that list.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main 239ef1c into fork/dev
Adopts the two upstream commits after #378: reconnects no longer shift the
mobile thread list (pingdotgg#5372) and the v0.0.32 release prep.

pingdotgg#5372 moves connection status out of the lists into the header title slot
(WorkspaceConnectionTitle / getConnectionAwareBrandHeaderOptions) and deletes
WorkspaceConnectionStatus. The fork rendered that status in four places across
HomeScreen and ThreadNavigationSidebar — exactly the row-shifting this fixes —
so those in-list surfaces are removed and the header slots adopted. The
auto-merge left one dangling {connectionStatus} in HomeScreen whose definition
upstream had deleted; typecheck caught it.

Fork surfaces kept around the new title:
- multi-environment selection: HomeScreen keeps its selectedEnvironmentIds
  label derivation and environmentLabelById (BoardScreen consumes it) instead
  of upstream's single selectedEnvironmentId.
- board mode: ThreadNavigationSidebar keeps its board branch, board-specific
  solid header, and board-gated search bar, with upstream's connection-aware
  brand slot merged into the same header options.
- HomeRouteScreen keeps the fork's in-flow HomeHeader as title owner rather
  than also restoring upstream's native-stack title, which would surface the
  connection status twice.

Adversarial review caught two holes in the first pass, both fixed here:
getConnectionAwareBrandHeaderOptions hardcodes the brand lockup and "Threads",
so spreading it over the fork's list-mode titles silently replaced "Board" /
"Projects"; and iOS Home has its own IosHomeHeader owning the native title, so
dropping upstream's route-level options left that path with no status surface
at all. The helper now takes an optional title/brand, and both the sidebar and
IosHomeHeader pass their mode title through it.

A second review pass caught one more surface — the custom (Android split)
sidebar large title still hardcoded "Threads" — plus the status offset being
applied to caller-supplied titles that lack the brand lockup's inset. Both
fixed, and mobileSurfaceExistence now asserts every adopting surface passes its
own list-mode title (that assertion fails on the pre-fix tree).

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
Adopts pingdotgg#5581 (drag pinned threads into your own order) on top of the pingdotgg#5372
merge already on this branch.

Pinned rows now sort by the user's arranged pinOrderKey on every platform,
which is deliberately independent of the fork's thread-grouping preference:
that preference still orders the active rows, but a pin is an explicit
arrangement and upstream documents the sort as identical everywhere so
mixed-version fleets cannot disagree. Server capability gates dragging only,
never the sort. SidebarV2 keeps the fork's environment/ownership filters around
the new pinned block and gains upstream's reorderablePinnedKeys.

Welds the auto-merge produced, all caught by typecheck or tests:
- useThreadActions.ts: upstream's ThreadPinReorderUnsupportedError landed
  inside the fork's clearPerThreadClientState, leaving it unclosed.
- threadListV2.ts: two `for (const thread of ...)` headers stacked on one
  loop body; the pinned block takes upstream's order-key sort.
- useThreadListActions.ts: duplicate threadEnvironment import plus local
  copies of helpers this fork imports from threadActionMessages.
- HomeRouteScreen: upstream's single-environment onEnvironmentChange handler
  does not exist in this fork's multi-select screen.
- projector.pinned.test.ts indexed state.threads[0]; this fork's read model
  keys threads by id in a HashMap.
- Six migration-ledger fixtures pinned the tail at 37; upstream's 038
  ProjectionThreadsPinOrderKey extends it.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
## Summary
- iOS crashed on the composer **+** attachment button because
`pickComposerImages` called `requestMediaLibraryPermissionsAsync()`
while the app ships with `photosPermission: false` (no
`NSPhotoLibraryUsageDescription`).
- Open the system image picker directly (no media-library permission
preflight), wrap launch failures in a structured error, and add unit
coverage that the permission API is never called.

## Test plan
- [x] `vp test run apps/mobile/src/lib/composerImages.test.ts` (8
passed)
- [ ] On iOS: expand thread composer → tap **+** → system photo picker
opens (no crash)
- [ ] Cancel picker → no attachment, no error toast
- [ ] Pick an image → attachment strip shows preview

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
sync(upstream): merge upstream/main 5661c61 into fork/dev
Adopts the thirteen upstream commits after #379. The headline is pingdotgg#5672 making
sidebar v2 the default, which is a file-level restructure: upstream renamed the
classic sidebar to LegacySidebar.tsx and moved v2's content into Sidebar.tsx,
deleting SidebarV2.tsx.

Git could not follow either rename because both sides had changed both files,
so each was merged three-way by hand:

- Sidebar.tsx = the fork's SidebarV2.tsx (multi-env and ownership filters,
  identity marks, budgeted listStatus, pin ordering, copy-thread-id, unread
  and regenerating-title chrome) with upstream's pingdotgg#5672 delta applied. That
  delta is a rename sweep — SidebarV2* → Sidebar*, group/v2-row →
  group/sidebar-row, sidebar-v2-* test ids — plus aria-busy on the rows.
- LegacySidebar.tsx = the fork's old Sidebar.tsx with upstream's rename and
  settings-nav hoist applied. AppSidebarLayout now renders SettingsSidebarNav
  for both sidebars, so the legacy one no longer renders it itself.

forkSurfaceExistence, the fork's anti-stack-drop net, was retargeted at the
renamed files and ids rather than relaxed: all 17 assertions still run, and
they are what caught LegacySidebar initially being upstream's v1 rather than
the fork's.

Other resolutions:
- GitManager: upstream's per-branch exponential backoff for rate-limited PR
  lookups (pingdotgg#5673) with the fork's terminal-state freeze helper kept.
- Composer: pingdotgg#5554 shows Stop while input is pending, which is exactly what the
  fork's shouldShowComposerInterruptAction suppressed; the predicate and its
  tests are retired rather than merged.
- PreviewView: the fork's resolveNavigableUrl (asks the environment about
  tailnet routing) keeps resolving, with upstream's browser-history recording
  (pingdotgg#5270) layered on the successful-navigation path.
- Settings: sidebarV2Enabled / sidebarV2ConfiguredByUser and
  enableAssistantStreaming are retired in favour of upstream's fresh keys
  (legacySidebarEnabled, enableLegacyTokenStreaming), which deliberately reset
  prior opt-ins; the mobile list toggle inverts the same way, so HomeScreen now
  reads through resolveThreadListV2Enabled.
- .github/VOUCHED.td stays deleted: it is upstream's contributor-vouching file.

Adversarial review caught one surface the key migration missed: the mobile
ThreadNavigationSidebar still gated v2 on the retired threadListV2Enabled, so
the iPad/split list would have stayed on the legacy layout with no setting able
to change it, disagreeing with Home. It now reads through
resolveThreadListV2Enabled like HomeScreen.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
patroza and others added 4 commits August 8, 2026 15:39
sync(upstream): merge upstream/main 0640410 into fork/dev
Adopts the three upstream commits after #381: a cross-environment usage page
reading provider transcripts (pingdotgg#5684), its chart fix (pingdotgg#5697), and one mobile
sheet for model and thread settings (pingdotgg#5625).

Resolutions:

- server.ts / ws.ts / client-runtime state: upstream's UsageService and its
  usageSummary atom family are additive next to the fork's diagnostics services
  (HostResourceProbe, ProcessResourceMonitor, TraceDiagnostics, BackgroundPolicy)
  and hostResourceSnapshot family — unioned.
- ThreadComposer: pingdotgg#5625 folds the model picker and provider options into a
  single settings sheet, replacing the fork's ControlPillMenu. The sheet is
  adopted, and the fork's usage signal rides on it: the trigger keeps
  ProviderUsageIcon with the live marker rather than upstream's plain
  ProviderIcon, so quota state stays visible at a glance. The fork-only
  collapsed-composer pill (upstream has none) now opens the same sheet instead
  of the retired menu.

Adversarial review caught that retiring the model menu also orphaned the fork's
numeric usage note: the marker survived on the trigger icon but the quota
percentage the menu rows carried had no home. It now hangs off the trigger
label, so both halves of the fork's usage signal survive the consolidation.
The plain ProviderIcon import went with upstream's replaced icon.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
sync(upstream): merge upstream/main a20923c into fork/dev
The review-a-PR habit fetches a PR head into a renamed local branch —
`git fetch origin <head-branch>:pr2182` — leaving no upstream tracking. Every
head selector the PR lookup can derive then names a branch that no PR has for
its head, so the thread stays badge-less for the PR it exists to review.

When the selector search finds nothing, the branch has no tracking-derived
head, and its name matches ^pr[-_]?\d+$, resolve the PR by that number via the
provider's getChangeRequest. Gated on headBranch === localBranch so a tracked
branch that merely resembles the pattern keeps the precise selector path; a
lookup failure falls back to "no PR" as before.

Verified against the live case: the "Adversarial Review of PR 2182" thread's
worktree, whose badge only appeared after hand-setting branch tracking — this
makes that repair unnecessary for every past and future such checkout.

Co-authored-by: Patrick Roza <42661+patroza@users.noreply.github.com>
@omegent-app
omegent-app Bot force-pushed the fix/pr-badge-numeric-branch-fallback branch from a4b5377 to 740b1ed Compare August 8, 2026 14:10
@patroza

patroza commented Aug 9, 2026

Copy link
Copy Markdown
Owner

@omegent I would first question why agents setup branches like these without tracking, and see if we can address that

@omegent-app

omegent-app Bot commented Aug 9, 2026

Copy link
Copy Markdown
Author

I’ve patched the status fallback to try to reattach the branch to the real PR head branch once the PR number is known. Next I’m adding a regression test that proves the branch stops being “untracked by design” after the first successful lookup.

gpt-5.4 · ↑52k ↓1.8k

@patroza
patroza force-pushed the fork/dev branch 4 times, most recently from 80434dc to fc6701e Compare October 3, 2026 10:00

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants