Show cancelled PR checks as Cancelled with separate counts on desktop and mobile, preserving genuine failures, pending checks, existing action permissions and older-client fallback. Carry complete provider summaries through caches and presentation; preserve Azure source completeness, folder attention priority and muted cancellation details.
Tracked in #15847 and STA-5102.
* 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
* fix(review): reconcile hosted reviews after failed writes
* fix(review): reconcile PR page, phone and Checks after failed writes
- PR page: merge, auto-merge, close/reopen and reviewer changes reread details after every attempted write, and the page's displayed state follows a freshly read details state so buttons reflect what landed.
- Phone: the PR actions engine and title save reread once per attempted write after busy clears; a failed reread never replaces the write's outcome or turns a successful write into an error.
- Checks: a failed merge's error stays visible when the reread shows the review merged; a GitLab reread that settles after the panel switched reviews no longer commits details for the other context.
* fix(review): reconcile GitHub and GitLab item dialogs after failed writes
- GitHub item dialog: merge, auto-merge, close/reopen and reviewer changes reread details after every attempted write, after pending clears and after the close/reopen revert. Success toasts and patches stay success-only.
- GitLab MR dialog: merge, close, reopen, details save and reviewer changes run the dialog's existing refresh after every attempted write. The write's error toast and the user's drafts stay.
* fix(review): keep shown review data and ownership across write rereads
- Phone: a write's follow-up read that fails keeps the PR already shown and the write's own error instead of replacing the panel with a load error. Manual Recheck, Retry and HEAD refreshes still show a failed read.
- Phone: a load started for an earlier branch can no longer claim the current one, and a title write that settles after the view moved to another PR stays silent and skips its reread.
- Phone: an older title Save that finishes late no longer closes a newer edit or hides its failure.
- Checks: the GitLab details read after a write lands only on the review the panel currently shows; an old or fallback MR can no longer take over a replacement MR's checks and comments.
* fix(review): keep loaded item details on screen through rereads
- GitHub item dialog and PR page: when a write's reread (or any other invalidation) drops the cached details, the same item keeps showing its last loaded details while exactly one fresh read runs and if it fails, so the comment composer and its unsent draft stay mounted. A cold first load, a retry with no data and switching items still show loading and errors.
- GitLab MR dialog: the state badge and Merge/Close/Reopen follow the item's freshly read details, falling back to the list row; the Reviewers section stays visible while a reread runs; the load-error banner shows only when there is nothing loaded; switching items drops the previous MR's details before paint.
* fix(review): keep loaded details at their cache and scope GitLab details to the full target
- GitHub item dialog and PR page: invalidation keeps the loaded details in the cache entry, retires its pending read and owes exactly one fresh read, so Viewed, description, reviewer and checks updates keep landing on what is shown, even while that read runs or after it fails. A failed read, an empty result or an optimistic comment does not start another read. Removes the separate retained-details hook.
- GitLab MR dialog: details, reviewer/label options and drafts belong to the full target (task source, GitLab project, item and repo); switching source or instance with the same item id drops the previous target's reviewers and state, and a late result for the old target is ignored.
* fix(review): keep owed and running detail rereads through local field updates
Each GitHub details cache (item dialog and PR page) gets one payload-only patch operation that copies the entry's read ownership (an owed reread and the running read) by construction. Viewed, description, reviewer, checks and the optimistic comment all go through it; read start, settlement and invalidation stay separate. A comment that lands with a write's invalidation no longer cancels the owed reread, and a comment during a running reread no longer lets an unrelated invalidation discard that read. A comment alone still starts no read.
* fix(mobile): reread the PR after every attempted write on main's PR tab
Rebuilds this PR's mobile behavior on main's restructured PR tab:
- Engine and title/description edits reread after every attempted
write once busy/saving clears; the write's outcome is the only
feedback and a failed reread is contained.
- The controller gives writes a dedicated reread entry that a poll
can't skip, keeps the shown PR when the read fails, and retires a
details read started before the write.
- A reread whose checks read failed still shows the PR's new state
(merged/closed), keeping prior checks only for the same review.
- Details carry, dedup and completion key on repo + number, so the
same PR number in another repo doesn't inherit details.
- An older Save can't close a newer edit; a write that settles after
the view moved to another PR shows no error and rereads nothing.
* test: repair CI for the reread tests
- Use a fixed fetchedAt for the unrelated cache entry in the details
hook tests instead of reading the wall clock.
- Mock platform haptics in the mobile controller probe and text action
tests so they don't load react-native, and give the act callbacks
that awaited a load result a Promise<void> body.
* fix(mobile): publish PR details against the latest queued state
Phase-two details spread the last rendered PR data, so when they settled
before phase one rendered (a fast read, or a closed client failing at
once) they restored the PR state the reread had just replaced: a Close
or Merge the host applied still showed open. Details now patch only
their field through a functional update whose identity, sequence and
same-review checks run against the state React applies them to.
* fix: type the reread test fixtures and commit identity refs after render
- Replace type assertions in this PR's tests with typed fixtures; the
GitLab primary-actions test keeps one partial dialog-state cast with a
SAFETY note, as neighbouring hook tests do.
- Assign the mobile controller and text-action identity refs and the
GitLab dialog owner ref in a layout effect, so a render React discards
can't leave an identity that never committed for a late result to
check against.
* fix(github): route the saved-body patch through the details payload helper
Main's issue-body rule (a saved description supersedes a read already
running) now goes through the one field-patch helper, which keeps an
owed reread and copies the running read for every other patch.
* fix(github): replace a read retired by a saved issue description
A saved issue description drops the details read already running so it
can't restore the older body. When that read was an ordinary one (such
as the reread after a write), the dialog now owes one replacement read,
so the new conversation still arrives with the saved body. A Refresh
read keeps relying on its own loop, which already retries a superseded
attempt. Starting the replacement consumes the obligation, and a failed
replacement owes no retry.
* test: fix typecheck errors in the dialog and checks-panel reread tests
* fix(gitlab): keep a late write on the previous MR from touching the next one
A save, reviewer change, close/reopen/merge or comment that settled after
the dialog moved to another MR still reread and cleared busy state for
whatever was shown, restarting the new MR's first read and discarding its
answer. Those finalizers now reread and clear busy only while their own
MR is still shown, as pipeline retry already did, and switching MRs
resets the save, action and comment busy flags so the next MR is never
left busy.
* test(gitlab): give the pipeline tab test a details owner after the main merge
* fix(github): prefer the open PR when a branch has several
The branch lookup asked GitHub for one PR across all states. GitHub sorts
a branch's PRs newest first, so a closed PR opened after the live one
shadowed it and got linked to the worktree on every checkout.
Fetch a page of the branch's PRs, REST and the `gh pr list` fallback alike,
and take the open one; with none open, keep the newest as before.
* refactor(github): keep branch lookup casts on their own lines
---------
Co-authored-by: agustin2606 <agustinvarela2606@gmail.com>
* Preserve repositoryless GitHub Project items
* Keep only draft and restricted Project items without a repository
Key the repository-scope exemption on item kind on desktop and phone so
an older host's content-less Issue/PR rows stay hidden as before. Draft
titles, cards and bars stay non-interactive with no row browser icon;
the toolbar's Open view in GitHub is the Project-level action. Phone
labels restricted items "Restricted" to match desktop. Normalization
assertions move to a main-process test so renderer tests no longer
import main code.
* Cover draft Status normalization and phone restricted-item copy
Restore the check that a draft's single-select Status survives
normalization, and render the phone Project list and detail drawer with
a draft and a restricted item using the real status labels.
* 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>
The PR tab and the Checks and Comments screens refresh by themselves while
they are open and the app is in the foreground, on desktop's checks-panel
cadence (policy moved to src/shared). A failed background read keeps the
PR on screen and backs off instead of replacing it with an error.
Review threads show the lines they are about: the host forwards the tail
of each thread root's diff hunk, and mobile numbers it back from the
thread's line. Bot findings get "Ask <agent> to fix", which launches
desktop's Resolve comments prompt (moved to src/shared) for that thread.
Fix and Ask buttons name the agent that would start.
Adds github.markPRDraft (gh pr ready --undo) on the host, allowlisted for
paired phones and advertised with mobile.github.markPRDraft.v1 so older
hosts simply hide the action.
A user act admits any gated work for its distro that runs inside it,
including work it merely kicks off. Three starters could lend that
allowance to background work: repo username enrichment's shared pass,
the PR refresh queue's drain timer, and gate-note listeners, which the
act's own wake note runs. Each now starts outside the act, so its Git
or WSL access is checked against the running list like any background
read. The act's own direct work is unchanged.
* fix(github): start explicit Checks refreshes after the action
* fix(github): give explicit Checks refreshes fresh checks and comments
The same Refresh or Link click that now reads PR metadata after the
action also reads checks and comments after it: an explicit request no
longer joins or waits on a read begun before the click, in the renderer
or (for checks) the host coalescer. A superseded read's callers show the
owner's answer, so a late pre-click reply cannot repaint the panel, and
comments posted during either read are kept.
A lost IPC/RPC reply is reported only to the click that asked; polls stay
silent as before, and a later answer clears a failure older than itself.
The manual hook takes its verdict from its own lookup, the link flow's
card-read failure no longer skips checks, and the web shim forwards the
intent.
* fix(github): land the older Checks reply when a newer explicit read fails
When a click's checks or comments read fails while the read it replaced
succeeds, the click's read now lands that older reply through its owner
path instead of leaving the cache behind it. Post-write comments
refreshes (mutation events, bulk resolve) carry the explicit intent so
they cannot be answered by a read begun before the write.
* test(github): drop type assertions from the explicit-refresh tests
The RPC tests call the handlers with a real runtime service and spies,
and the panel test passes typed fixtures straight to the hooks.
* test(github): type the Checks panel fixtures for the renderer project
The renderer tests build their PR fixture locally instead of importing a
main-process harness, and the panel setter mocks take the hooks'
SetStateAction form.
* fix(github): resolve project accounts at the request boundary
* fix(github): retain account identity through request settlement
* fix: isolate GitHub leaf caches by held execution identity
* fix: use canonical options type for merged review membership
* fix(wsl): user clicks on cwd-less gh may start the stopped default distro
#35709 stopped background gh/glab from booting a stopped default distro,
which also refused explicit clicks for users whose gh lives only in WSL.
Starring Orca, the auth diagnosis, the project picker, opening a work
item and board edits now run as a user act on the distro the fallback
uses; the board table reload, which also runs unprompted, stays gated.
The GitHub and GitLab rate-limit cards keep their last snapshot on the
not-running refusal instead of flipping to unavailable.
* fix(wsl): only an explicit click may start the stopped distro for project gh calls
Wrapping the project-view and auth-diagnosis IPC handlers as user acts
also let mount effects (useProjectViewTable listing a restored board's
views, GhAuthErrorHelp's diagnosis) start a stopped default distro. The
handlers now wake only when the renderer passes wakeWslDistro, which the
picker browse/select, opening an item and every edit set; mount effects
leave it unset, and the auth diagnosis never wakes.