Commit Graph
12 Commits
Author SHA1 Message Date
Brennan Benson 252b427dfd Display private GitHub attachment images in fetched items (#39250)
* Display private GitHub attachment images in fetched items

* Keep attachment event identity aligned with committed renders

* Complete GitHub image response type contracts

* Return private image sources with accepted issue edits

* Keep issue edits successful without optional display data

* Cover unusable issue edit image responses

* Keep optional GitHub image parsing from failing accepted saves

* Fetch GitHub body images with the existing body read
2026-11-08 11:58:15 -08:00
Neil c10881fa8c refactor(shared): organize execution and automation defining owners (#39776)
* refactor: import journal queue state from defining owners

* refactor(shared): organize execution and automation owners

* fix: retain type imports used by surviving test cases

* refactor: finish work item owners and refresh validation metadata

* test: infer reconnect status fixture type

* test(claude): resolve source files beside same-named directories

* test(runtime): group repository creation mock registrations
2026-11-08 06:46:09 -08:00
Brennan Benson 40f9d2a246 Keep desktop fork pull requests on their opened repository (#36172)
* Keep opened GitHub work items bound to their exact repository

* Bind GitHub work item ownership through consumers and dispatch

* Keep empty mention lists from consuming selection keys

* Preserve GitHub action and consumer ownership across resource changes

* Exercise repository account updates through their production store action

* Exercise account publication and cache settlement through GitHub consumers

* Simplify fork PR contract verification

* Make contract verification imports and clocks deterministic

* Correct mobile task test fixtures and model types

* Correct task recording fixtures and CI types

* Correct task fixture types and static checks

* Consolidate replay test RPC imports

* Separate GitHub metadata imports from Linear hooks

* Scope mobile PR sidebar reads to the selected resource

* Separate desktop GitHub changes from phone updates

* Fix relay authority probe census across topology cases

* Repair repository owner proof fixtures and compiler allocation

* Preserve repository row observation in host lookup

* Seed metadata fixture before server-side store acquisition

* Preserve nullable fixture settings and discover mixed test types

* Preserve GitHub detail argument types and optional capabilities

* Let keyed details queries own pending read replacement

* Repair GitHub RPC and relay test fixture boundaries

* Verify expected GitHub account at repository execution capture

* Preserve captured GitHub account expectations through client dispatch

* Retain observed runtime status after switching environments

* Fix account contract fixture types and RPC setup timing

* Restore the negative-capability switch probe assertion

* Align account fixtures with runtime identity and configured origin

* Consolidate details test imports

* Drop duplicate fork pull request identity tests
2026-11-02 13:22:48 -08:00
Brennan BensonandMax Hsu 8e326f2629 Keep GitHub task assignees and rows current after dialog edits (#34245)
* fix(tasks): publish confirmed dialog assignee changes to the list

Record successful per-login changes in the existing assignee snapshot and
patch the Tasks cache. Qualify snapshot writers, composition and quiet
release by canonical repository so fork and host twins stay isolated.

Co-authored-by: Max Hsu <maxmilian@gmail.com>

* test(tasks): bound confirmed assignee publication work

* Add explicit task refresh through registered details

* Refresh registered tasks with finite request ownership

* Preserve addressed task display and ordinary edit boundaries

* Derive task membership from owned rows and batch cache updates

* Preserve task list intent across account changes and query resets

* Preserve GitHub task content through final delivery

* Preserve accepted task content across cold query changes

* Batch finite task delivery publications and index ordered list folds

* Capture immutable Tasks publication inputs and translate Refresh feedback

* Preserve live scalar settlement beside captured publication inputs

* test: make task refresh contracts deterministic for static checks

* test: derive lifetime token annotations from production origins

* test: initialize current repo catalog defaults for refresh fixtures

* Fix refresh import dependencies for static checks

* Fix GitHub task refresh adapters and verification inputs

* Exercise omitted mutation observation in overlap contract

* fix(tasks): refresh live list bases and align delivery contracts

* test(tasks): retain actual pagination setter inputs

* fix(tasks): express page array mutability with checked overloads

* fix(tasks): preserve complete models through refresh stages

* fix(tasks): align origin and query row input contracts

* test: declare portable task refresh mock contracts

* test: allow zero-argument task refresh mutation calls

* Keep GitHub task copies fresh after edits and refresh

* Keep task rows visible and simplify the shared task record

Rows are never filtered for a stale connection or account binding; only a
current record for the row's own repo overlays its facts, derived at read
from repoId/repoHostId. Records cover issues only, conflicts compare only
state, title, label names and assignee logins, and truncated Project lists
never count as conflicts. Project tables stay raw in the store and are
projected where they are read.

Ordinary local dialog opens keep cached, lenient reads; only Refresh asks
for the fresh, strict read. Refresh waits on the record's own pending edits
instead of a second write registry, reports a failed user Refresh once
through the existing toast, and explains in its tooltip when the server
needs an update. Background verification never shows a failure.

A successful dialog assign keeps the selection until a newer reading lands,
main's soft-hide for Tasks-row edits is unchanged, and pins/eviction are
gone because only user edits and Refreshes create records.

* Let saved descriptions win over Refresh and roll back disposed edits

Refresh is now the only fresh, strict read: it writes the same details
entry ordinary loads use, and later reopens and post-edit refetches go
back to the cached, lenient path. An issue's saved description supersedes
any read already in flight, so Refresh rereads after the save instead of
restoring the older body. A failed Refresh with nothing loaded yet leaves
only the body's existing load error.

A record disposed while its edit is in flight still settles the copies its
edit patched, so a failed edit rolls back in Tasks and the dialog as before.
A record whose own connection fence went stale is dropped when next
looked up. Rows without a record pass through acceptSearch unchanged.

* Settle disposed edits through their owner and connect Project edits

A late edit whose record was disposed mid-flight now finishes through the
same base/pending reducer: copies receive only that operation's own change
(one label or login added or removed, a state set), a replacement record
reconciles with its own item read, and settle never returns null so dialog
and Tasks rollback always run. Untracked edits publish the same semantic
change at begin and undo it on failure. Dialog callers stop re-patching
Tasks rows; an unsupported server's live record keeps new edits joined
until its last one settles.

Refresh clicked during the first details load waits for that load, then
runs the strict read; a failed click always shows its one toast and never
installs an inline load error.

Project-row issue edits now publish to Tasks copies with no prior record,
seeded from a complete Tasks row for the matched repo binding, or
established by an item read after the edit lands.

* Give a task record its base only from an accepted provider reading

Starting an edit or a Refresh no longer turns the caller's cached row into
the record's full value. Until an owned canonical read is accepted, a record
holds only its pending changes and the coalesced successful ones, and every
copy (Tasks rows, pickers, the board) shows its own facts plus only those
changes. A successful edit applies just its change to the stored copies and
keeps it until one coalesced read through the existing read lane
establishes the base; a failed edit with nothing else owed retires the
owner, and a failed first Refresh leaves nothing behind. Search deliveries
and board fetches wake a read still owed.

Edits are now semantic change values (set title or state, add or remove a
named label or login) instead of closures, so they coalesce and apply to any
copy. Project edits keep their producer but no longer seed from a cached
row, and the separate establishing read is gone. A Project save with no
title, label or assignee change (a body-only save) skips the record
entirely.

* Keep pending task edits out of stored copies and re-admit queued reads

Accepted records now follow the rule records without a reading already
followed: stored Tasks copies hold only accepted readings and confirmed
changes, and pending changes reach users only through read-time projection.
A search during a pending edit stores the accepted facts, never the edit; a
failed or disposed edit just drops its token instead of computing an undo
over current copies, so a newer reading can't be overwritten by an old
edit's rollback. The cross-copy undo helper is removed.

Without a tracked owner (no binding, or a server without the item route),
other copies change only when an edit succeeds, and a board edit on a
server known to lack the item route stays board-only. Dialog state and
label edits roll back to what the dialog showed.

A canonical read queued behind another one re-enters admission after each
wait, so a Refresh clicked after a discarded one still reads, and a read
whose binding or server support went away meanwhile ends without leaving
an owner.

* Store only readings, keep task data across reconnects, park failed reads

Stored Tasks copies (the fresh-cache fetch return, details settle) hold only
accepted readings and confirmed edits; Cmd-J applies its open-only filter to
what it shows, pending edits included. Records, details entries and
optimistic comments are keyed by account binding and issue; a connection
change re-captures the binding and fences each read and send without
deleting what the dialog shows. A server's missing route marks only the
connection that answered. An unusable canonical read keeps what is known
and waits for the next provider delivery, which retries one owed read per
binding, the longest-waiting first. The dialog's label chips follow the
displayed item, and the duplicate target fence and write-only details
generation are removed.

* Park only reads that were owed, so a failed Refresh never delays a later one

A failed Refresh on a record with nothing owed no longer marks it as a parked
retry; a later obligation on that issue reads on the next delivery instead of
waiting behind every parked record. The refresh hook stops returning its
unused target.

* Keep the ordinary-open details case after main removed its suite

Ports the one case that pins an ordinary open with an issue repository to the
cached, lenient read (a failed timeline read still returns details) into the
main details suite.

* Keep pull-request lookups off the task records and repair CI tests

Pull requests never have a task record, so their lookups no longer load the
store or report a search reading; terminal PR-link confirmation reads as on
main. Tests follow the explicit canonical details flag, the current dialog edit
signature (records hold a dialog close over a lagging search), confirmed-only
untracked publication, and a fixed board timestamp.

* Keep main's close hold where no task record can own it, and split the record cache

A dialog close or reopen that no task record can own (no binding, or a server
without the item route) keeps main's confirmed-state hold, so a lagging search
cannot show the issue open again; a record that takes over retires it. Tracked
edits still go through the record only.

The record cache moves, unchanged, into model, owners and reads layers under
the Project-facing cache, so no module needs a max-lines exception.

* Hold an untracked dialog close only after it is confirmed

A dialog close or reopen no record can own changes no stored copy while the
request runs, so a refresh during a failed request leaves every copy as it was
and nothing needs undoing. At success the edit is admitted again: a record that
can now own it (after a reconnect or upgrade) settles it, otherwise main's
confirmed-state hold is asserted and the change is published once.

* Name the task binding incarnation token type

The binding incarnation, compared by reference, gets its own named type instead
of the broad object type; no behavior change.

* Import Cmd-J task search results from one statement

* Type changed code without assertions and keep the assignee callback stable

Tests build typed fixtures and hoisted mocks instead of casting; the landing
refresh takes only the model parts it reads. Lookups and the comments parse
annotate their values instead of asserting them. The edit section's empty
assignee list is one shared value, so its toggle callback keeps its identity.

* Stamp the opened row onto main's details item before it reaches typed record reads

* Pin the parked-read and board-read budgets at 10 and 100 records

* Size the dialog's Refresh task button like Copy link and Open on GitHub

---------

Co-authored-by: Max Hsu <maxmilian@gmail.com>
2026-11-01 19:34:54 -08:00
Jinjing c24d916f31 Route hosted review operations to the owning host (#36003)
* Route hosted review refreshes to their owning host

* Route remote review operations to their owning host

- Refresh the correct hosted review after remote git operations
- Scope comment mutations and AI acknowledgements to the owner cache

* Route legacy paired SSH reviews through their server owner

- Recover proxied runtime owners for older SSH worktree listings
- Preserve direct and ambiguous host routing safeguards

* Preserve review ownership across remote workspace operations

- Route checks, comments, and linked-review clears through the selected repository owner
- Reject ambiguous or mismatched host context to prevent cross-machine mutations

* Keep check detail tabs scoped to their remote owner

* Preserve remote worktree ownership across reconstructed check tabs

* Preserve direct SSH check tabs when proxy listings overlap

* Scope PR refresh ownership state and safely roll back comment mutations

- Preserve linked-review clear scopes beyond cache eviction and clean them up on expiry
- Handle failed or refused comment reactions and thread resolutions without stale optimistic UI

* Preserve remote review ownership when SSH copies overlap

* Fix review routing for overlapping remote repository hosts

- Preserve server-owned review refreshes when a direct SSH copy exists
- Fail comment mutations and refetches safely when ownership becomes invalid
- Resolve check and sidebar data through the workspace’s owning host

* Relax cache-sensitive test expectations

* Route check refreshes to runtime owners and isolate restored requests

* Scope reconstructed check detail tabs by execution owner

* Fix review refresh ownership for shared remote hosts

- Prefer explicit execution owners when matching worktrees.
- Avoid repeated tab scans when restoring check details.

* Fix hosted review polling for workspaces on multiple hosts

* test: trim redundant review ownership scenarios
2026-11-01 12:57:45 -07:00
Neil 8c3551bde3 fix(wsl): background Git, GitHub and GitLab reads never start a stopped distro; explicit opens may (E9) (#35410)
* fix(wsl): keep background git and GitHub reads from booting a stopped distro

Git history, diff and status-follower reads, GitHub issue/work-item/check
polls, chat link existence checks, the composer's create-base prefetch,
the worktree-root fan-out and the Settings/Automations orca.yaml previews
now refuse or skip a stopped WSL distro instead of starting it (E9).

* fix(wsl): explicit opens may start a stopped distro; more polls gated (E9)

Folds the parallel G1-G6 work into this branch and splits background
reads from user acts:

- An explicit open runs as a user act for its distro and may boot it:
  showing a diff tab or its Retry, showing Commits or its Refresh,
  opening the checks panel or entering its tab, checks Refresh, a check
  action, and linking or creating a PR. Main takes a wakeWslDistro flag
  on git:diff, git:branchDiff, git:commitDiff, git:history and
  gh:prChecks and runs those reads under runAsWslUserAct.
- Polls, timers, refreshes after Git actions, prefetches and fan-outs
  stay background and are refused while the distro is stopped.
- Also gated: the hosted-review card poll for every provider, the GitLab
  checks-panel poll (background flag), and the composer's open-time
  orca.yaml check (Create re-checks in the foreground).
- Pollers keep the data they already show on a refusal: the issue on a
  card, the checks list, an open diff and loaded submodule children, and
  stop logging it.

* docs(wsl): note that a hosted-review poll's stopped-distro refusal is still logged (E9)

* fix(wsl): Settings re-asks orca.yaml once a stopped distro runs; drop link-check gating (E9)

- Link existence checks (shell:pathsExist / pathExists) are no longer
  gated here: answering "missing" cached chat and terminal links as
  absent and toasted "File not found" on markdown link clicks. The
  pathsExist refusal lives in its own PR, which throws instead.
- Settings no longer sits on "checking" forever: a repo whose distro
  refused the background check is re-asked on window focus, on
  visibility, and after 10 s, until it answers. An unverifiable distro is
  now marked (wsl-distro-unverifiable) and treated as unread too, instead
  of being stored as "no orca.yaml".
- The sidebar issue poll and the Tasks list and count refuse with "Open a
  terminal in it to start it", since their Retry is itself a background
  read; the checks panel keeps "Retry", whose Refresh is a user act.

* fix(wsl): Tasks Refresh, interactive review refresh and manual PR lookups may start a stopped distro (E9)

- Tasks: a Refresh, Retry or search, or Tasks opened on a single repo,
  passes wakeWslDistro through gh:listWorkItems and gh:countWorkItems
  (IPC only; the RPC twins and web routes drop it). A multi-repo open
  stays background. Their refusal says "Retry" again; only the sidebar
  issue poll says "Open a terminal".
- hostedReview:forBranch wakes for an interactive request (the checks
  panel's manual Refresh), so GitLab, Bitbucket, Azure DevOps and Gitea
  reviews with nothing cached can load.
- gh:prForBranch, the renderer's fallback when refreshPRNow is missing,
  is gated like the other card polls; a manual lookup wakes, matching
  refreshPRNow's manual-only rule. The fallback call moves into its own
  module, built from the refresh candidate.

* fix(wsl): only an explicit Refresh starts a stopped distro for review status (E9)

hostedReview:forBranch woke on admissionTier 'interactive', but the
visible-review scheduler sends 'interactive' for the selected card on a
timer, so that poll restarted a stopped distro every interval. The wake
is now an explicit wakeWslDistro flag (desktop IPC only). The card
refresh helper sets it, since every caller follows a user act (the
checks panel's manual Refresh, or a review created, linked or edited),
and the manual Refresh also wakes its pre-refresh git status.
admissionTier is back to priority only.

* fix(wsl): an explicit review Refresh never joins a refused poll (E9)

fetchHostedReviewForBranch let a forced card Refresh join an in-flight
forced scheduler poll, since the join only compared force. On a stopped
WSL distro that poll is refused, so the click got the refusal. The
in-flight entry now records the wake, and a waking request joins only a
waking flight; a poll may still join an explicit Refresh.
2026-10-28 18:16:49 -07:00
OrcaWinandOrcaWin 0efc842028 [comment audit] Shorten preload comments (#32257)
* chore: audit preload root comments and track repository coverage

* docs: track repository comment audit coverage

* chore: keep comment audit tracker local

* chore: shorten preload API contract comments

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-23 02:16:59 -07:00
Neil 8ae5264c63 Record confirmed PR observations from their terminal pane 2026-10-08 02:04:04 -07:00
NeilandKatsuma Takehisa ce07786266 Keep opened issue details and edits in the selected repository (#24729)
Keeps a fork issue’s details, metadata and edits bound to the repository the user opened, including same-number issues in fork and upstream. Repairs selected-assignee leakage and delayed failed edits repainting another issue.

Fixes #24378

Incorporates and cross-reviews contributor PR #24379, including its source-resolver correction and regression material. Covers the contributor PR’s Project-row identity and retained-dialog mutation findings. The final published head passes focused tests, hidden macOS rendering and current CI; the callback-timing bot thread has an evidence-based response.

Co-authored-by: Katsuma Takehisa <k.takehisa@nissogr.com>
2026-10-03 23:19:55 -07:00
Neil 83117f2860 refactor(integrations): split issue-tracker clients under the max-lines budget (#14704)
The GitLab, GitHub, Jira and Linear integration modules, their two IPC
registrars, and the shared GitHub project types each carried a file-level
`eslint-disable max-lines` and ran 351-614 counted lines against a 300-line
budget. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all
eight suppressions and prunes their entries (341 -> 333).

Pure move, no behavior change. Each client is cut along the seam it already
had: per-operation modules for the issue APIs (create / update / comment /
field options), and for Jira the request queue, site credential store,
authenticated request, and site identity. The two IPC registrars keep their own
handlers and delegate the rest to per-domain sub-registrars, so they remain
real entry points rather than re-export shims.

The IPC surface is proved intact rather than assumed: comparing (method,
channel) multisets between HEAD and the split gives 52 registrations across 52
distinct channels on both sides.

Provider-neutrality is preserved -- GitLab and GitHub keep separate, parallel
module layouts rather than being merged behind a shared abstraction.

Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the one remaining failure is a pre-existing load flake in an untouched file,
green when re-run serially), no new runtime import cycles among 744 modules,
and no lint suppression added anywhere.
2026-08-15 18:17:20 -07:00
Neil 77f23b013f refactor(shared): drop the shared/types barrel and import from the real modules (#14447)
#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.

Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.

2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.

Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:

- Modules inside `src/shared` import the barrel as `./types`, not
  `shared/types`. A pre-filter on the latter string skipped 176 of them and
  left imports dangling at a deleted file, which surfaced as confusing
  `Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
  errors rather than "module not found".
- The barrel RENAMED one type on the way through
  (`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
  in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
  TypeScript parses that `;` as the import statement's terminator, so
  replacing through `statement.getEnd()` deletes it and breaks ASI. The
  rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
  from it, because the barrel re-exported those same names — which trips
  `import/no-duplicates` under `--deny-warnings`. A post-pass merges
  declarations sharing a specifier and type-only-ness; the `import type` plus
  `import` pair from one module is left alone, since that form is allowed.

Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
2026-08-13 22:48:24 -07:00
Neil 4221f8d429 refactor(preload): split the preload contract into per-domain api modules (#14403)
`src/preload/api-types.ts` was 3,752 raw lines (3,533 counted, 11.8x the
300-line budget) behind an `eslint-disable max-lines`. Almost all of it was a
single `PreloadApi` object type whose ~83 namespace properties were declared
inline, so any IPC surface change meant editing one 2,600-line type.

Give each namespace a named type in its own module under `src/preload/api/`
(`pty-api.ts`, `filesystem-api.ts`, `github-pull-request-api.ts`, ...) and
recompose `PreloadApi` from those names. `api-types.ts` keeps the `declare
global` Window augmentation and re-exports every moved name, so all 52 import
sites are untouched.

Two shapes needed care to stay type-identical rather than merely compatible:

- Three keys (`gh`, `git`, `ui`) are composed from two modules each. A plain
  intersection is NOT identical to the original flat object literal, so those
  use a `Merged<T>` mapped type; a negative control confirmed that dropping it
  fails the parity assertion.
- Keys whose module groups several namespaces use indexed access
  (`fs: FilesystemApi['fs']`) to preserve exact identity and source order.

`config/tsconfig.web.json` and `tsconfig.tc.web.json` enumerate files by path,
so they need `src/preload/api/**/*` alongside the existing `api-types.ts` seed
or the web projects fail TS6307.

Verified by exact type identity, not assignability: 41 assertions of the form
`Equals<Now.X, Before.X>` against a frozen pre-split snapshot, covering every
exported name, plus a per-key pass over all 83 `PreloadApi` keys. All three
projects typecheck clean with those assertions active.

Verification note: these tsconfigs are `composite: true`, and `tsc --noEmit`
will reuse a stale `.tsbuildinfo` and report clean for a state that genuinely
fails. Every result above was produced after deleting the buildinfo, including
a negative control confirming the gate still fails on deliberate drift.

Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
2026-08-13 20:52:04 -07:00