* Keep keyboard focus when an approval is answered, its turn folds, Stop ends it, or Jump to latest is used (CF-8) (#342)
* Keep keyboard focus when an approval is answered and its turn folds
The CF-8 keyboard walkthrough found that pressing Approve or Deny with
the keyboard dropped focus onto the page body: the answer buttons
unmount once pressed, and a finished turn then hides its folded work.
A keyboard or VoiceOver user lost their place twice.
Focus now moves to the card's answer line ("Sending decision…", then
"Approved" or "Denied"). When the finished turn folds work that holds
focus, it moves to the turn's "Worked for" toggle. Focus elsewhere,
such as the composer, is left alone.
Red first: both keyboard tests failed on main (answer line inactive).
e2e/keyboard-answers.spec.ts passes 3/3; the full desktop e2e passes
366 with 6 opt-in skips.
Part of #281 (CF-8), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Hand focus to the message box when a keyboard Stop ends the turn
The second walkthrough probe found the same defect at Stop: the button
goes away when the turn ends, and focus fell onto the page body. When
Stop was pressed with focus on it, focus now moves to the message box,
where the next message is typed.
Red first: the new keyboard test failed on the previous commit
(composer inactive); keyboard-answers.spec.ts passes 4/4 and the full
desktop e2e passes 367 with 6 opt-in skips.
Part of #281 (CF-8), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Hand focus to the message box after Jump to latest from the keyboard
The third walkthrough probe found that Jump to latest, once activated,
goes away at the bottom and left focus on the page body. When the
button held focus, focus now moves to the message box there.
Red first: the new test failed (message box inactive). The keyboard
spec passes 5/5, app.spec with it 19/19, and conversation-continuity
plus typed-turns, which also scroll, 8/8.
Part of #281 (CF-8), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Restore main's status.md after the #339 merge, keeping only this PR's entry
The merge kept both sides of a block #339 had moved, which duplicated
the released entries. The page is now main's, plus this PR's one entry.
Coven-Harness: Claude Code / claude-opus-5-5
* Show a Codex web search's query and the results Codex reported (CF-5) (#344)
A live codex-cli 0.163.0 capture (test/fixtures/codex-websearch-0.163.0.json)
shows the webSearch item starting with an empty query, no action and
no results, and completing with the query, a search action and
text_result entries (title, url, domain, snippet). Modex titled the row
"search " from the empty start, read neither action nor results, and
stored the JSON string "" as its output.
codexWebSearch (shared/evidence.ts) keeps the query, the action and
results with an http(s) link, each text bounded, and counts what it
drops. The Codex backend titles the row "search the web" until the
query arrives, then "search <query>", and carries the search on the
tool item. Expanded, the results show as links with snippets, marked
data-provenance="codex-search" and labelled as unchecked. The renderer
checks each link again before showing it, and nothing is fetched until
a link is clicked. Older searches no longer show "".
Red first: the three evidence tests and the Codex backend test failed
on a stub, and the e2e test failed ("done", not "2 results"). Now
evidence, codex-backend and conversation-events pass 69/69, the
conversation-evidence spec passes 7/7, and the desktop unit suite
1,709 passed with 16 skipped.
Part of #278 (CF-5), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Land main through a merge queue (#345)
main requires branches to be up to date, so every landing sent each open
PR back through CI. On 2026-10-19 that cost #309 three extra macOS runs,
and on 2026-10-20 #336, #340 and #343 one or more each.
The ruleset file gains a merge_queue rule (squash, all-green groups, up
to 3 groups building and 5 PRs per merge, a 90-minute check timeout,
since the macOS job alone may take 45). It drops the up-to-date
requirement, which the queue replaces: a queued PR is tested on top of
main and the PRs ahead of it.
CI runs on merge_group, so each queued group reports the required
unit + e2e (macOS) check; without it the queue drops every PR. The
iPhone job and the coverage upload skip queued groups: neither is
required, the PR already ran them, and main's push run repeats them.
README, AGENTS.md and status.md say PRs land through the queue. The live
ruleset (23989202) takes this file once this lands, so the queue starts
only after main can report checks for it.
Coven-Harness: Claude Code / claude-opus-5-5
* Say the phone no longer shows a withdrawn approval as declined (#343)
human-requests.md still said the iPhone shows a withdrawn approval as
declined. Since #335, mobileItem (companion.ts) sends a withdrawn,
sending or uncertain approval to the phone as a notice that says so.
The companion fallback test now covers the withdrawn case, which had
no test (CF-6 #279, finding 7).
Coven-Harness: Claude Code / claude-opus-5-5
* Note that the merge queue needs auto-merge, and list the keyboard Jump to latest CI failure (steward cycle 17) (#347)
The first PR queued after #345 failed to enqueue: "Auto merge is not
allowed for this repository". gh pr merge adds a PR to a merge queue by
enabling auto-merge, so the repository now allows it, as OpenCoven/coven
does, and README says why.
e2e/keyboard-answers.spec.ts:88 (#342) failed in three CI runs of #345 in
a row and passed on #342, on main and 25/25 locally. It goes under Known
rough edges with what the failure snapshot shows and a link to the
analysis on #342.
Coven-Harness: Claude Code / claude-opus-5-5
* Prove a queue-landed branch merged when its tip merges cleanly into the landed tree (#350)
Since #345, main lands through a merge queue, which squashes each PR onto main as it stands at
the PR's turn. A branch that merged main into itself, then landed behind another PR, had no proof
that worktree.sh remove would accept:
- the squash tree also holds the PRs ahead of it, so it is not the tip's tree;
- the branch's merge commits rule out the binary-diff proof.
cf6-docs-followup (#343) hit this on 2026-10-20 and had to be pointed at its squash commit by hand.
The new merge-tree proof merges the exact PR tip onto the squash commit's parent with Git's own
merge. It accepts only a clean merge (exit 0) whose tree is exactly the landed tree. A configured
merge.<name>.driver turns the check off, because a driver can drop a side's changes and still
report a clean merge.
New tests:
- a queued branch with merge commits is proven and removed, and its archive ref holds the tip;
- a wrong head, a wrong base, a different tree, a conflicted merge (even when the squash holds
exactly the conflicted tree) and a configured driver that keeps main's side each stay
not-proven-merged.
Checked against #343's real branch and PR record in a throwaway clone. The old proof returns null.
The new proof returns merge-tree with tree 6fc493b, the tree of 0a67354.
Coven-Harness: Claude Code / claude-opus-5-5
* Hold Jump to latest when the composer grows or a Page Up is still moving (#352)
* Hold Jump to latest when the composer grows or a Page Up is still moving
e2e/keyboard-answers.spec.ts:88 (#342) failed on slow CI runners: after
Enter, Jump to latest stayed. A trace in the app found two causes.
- A scroll event is delivered before the browser reports a resize. If
the composer grew right after the jump (a follow-up chip), onScroll
measured a shorter view and stopped following. It now keeps following
while the view has not moved above where auto-scroll put it, as
follow() already did.
- A Page Up still animating keeps applying its remaining distance after
the page sets a new position: plain assignment, scrollTo instant and
an overflow toggle all lose to it. For a second after a jump, a scroll
with no wheel, key, pointer or touch input since the jump is that
leftover motion, and the view pins back to the bottom. The reader's
own scroll still wins at once.
Red first, each test alone on main's ThreadView: the composer-growth
test failed (the button stayed), the Page Up test failed (the view went
back up 138 px), and the scroll-up guard passed. With the fix,
keyboard-answers passes 24/24 (8 tests x 3), and app, conversation-
continuity, typed-turns, conversation-rendering and layout 43/43.
Part of #281 (CF-8), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Move the Jump to latest rough edge to the resolved list, with its cause
The entry #347 added leaves Known rough edges, as written, under #352,
with the two causes the trace found.
Coven-Harness: Claude Code / claude-opus-5-5
* Invalidate retained PR data after authoritative visibility loss
* Say whether a check verified a turn's last change (CF-7 verification) (#351)
* Say whether a check verified a turn's last change, in its task record (CF-7)
verification() in shared/task-context.ts reads a turn's tools in
transcript order:
- A check is a known test, build, typecheck or lint command, read
inside sh/bash/zsh -c with quotes respected. An exit-zero echo or
the model saying "tests pass" is not one.
- A check passes only as succeeded with exit code 0 or none reported.
- A check goes stale when an edit that was not denied, or a command
that might write, runs after it.
- The turn reads verified, failed, unknown, stale or none.
taskRecord carries it, and the note gets a Verification section that
marks stale checks. Anything short of verified goes under Not settled;
a failure a later fix superseded does not. Plan steps keep the CLI's
statuses: neither CLI links a check to a step.
Red first: five of six new tests failed on a stub, then edits and
displayCommand on theirs. task-context passes 13/13; the desktop
unit suite 1,723 passed, 16 skipped.
Part of #280 (CF-7), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* Show a turn's verification beside Save to Notes (CF-7)
A finished turn's footer reads, for example, "Verified: pnpm test passed
after the last change" or "Not verified: pnpm test ran before a later
change". It also covers a check that failed, a check that did not say
how it ended, and a turn whose changes no check followed. A check
shows as the script its shell ran, with the exact command on hover. A
turn that changed nothing and ran no check shows nothing.
Red first: the new e2e test found no verification lines (expected 2,
received 0). task-to-notes.spec.ts passes 3/3.
docs/task-context.md gains a Verification section, and status.md an
entry.
Part of #280 (CF-7), tracker #273.
Coven-Harness: Claude Code / claude-opus-5-5
* List #352's Jump to latest fix under On main, unreleased (steward cycle 19) (#353)
#352 changed what readers see: after a jump, the transcript no longer stops short of the bottom
when the message box grows or a Page Up is still scrolling. It moved the CI rough edge to the
resolved list but added no unreleased entry, so the next release review would not have listed it.
The entry sits beside CF-8's keyboard entry and links the resolved list's account of the causes.
Coven-Harness: Claude Code / claude-opus-5-5
* Mark the CF-7 verification plan done in #351 (steward cycle 20) (#356)
#351 landed every task in docs/superpowers/plans/2026-10-20-cf7-verification.md, but the plan
still read as not started. The five boxes are ticked from #351's own evidence:
- the red and green unit tests;
- the footer e2e (3/3);
- task-context.md and status.md updated;
- pnpm build and typecheck passed locally;
- the full e2e suite passed in CI, on the PR and in the merge queue (run 39508701952). The local
run stopped when the disk filled.
A dated status line says that linking a check to a plan step stays open on #280.
Coven-Harness: Claude Code / claude-opus-5-5
* Invalidate shared read resources without losing partial inbox results
Superseded by #366, which carries all reviewed CR2 work and the merged #354/#361 repairs onto current main while preserving the newly landed CF7 changes. The original evidence below remains historical; #366 records fresh integration validation. #321 remains open until the replacement lands and acceptance is audited.
Current integration status
The visibility-loss repair #354 and production Electron IPC acceptance test #361 are merged here, now at
8882aadbe70d54146f6edcc469d754849f43cfb8. Denial/not-found/identity failures invalidate retained PR data and concurrent sibling reads while preserving unrelated repositories and ordinary offline/rate-limit fallback.#361 passed the required macOS unit/E2E check on
c180c72187e9dbbeaa3df5815161774f3babb358before merging. Its actual production preload/IPC/service test covers inbox → overview → files, distinct repository identities, foreign-account rejection and an unchanged checkout. Removing the production registration made it fail. Local build/typecheck and 1,955 unit checks passed (16 skips); the restored focused Electron test passed. Validate this resulting parent head and its main merge group before closing #321.The original implementation and its SHA-specific evidence follow; their counts are not results for the current head.
CR-2 of tracker #319, for #321: main's read service on the
code-review:readchannel. It serves the contract #334 defined, and CR-3 (#346) binds its UI to it. Read-only. No checkout, no comment or review, no merge, no model call. Nothing is visible in the app until CR-3 lands.How it reads
Whose login: Val's own
ghlogin ([CR-1][P1] Establish the Code Review reference contract and deterministic fixtures #320 decision 3). Modex adds no sign-in, and never sees, stores or asks for a token.The command: each read is
gh api <fixed path> --hostname github.com --method GET --include, run throughexecFilewith no shell.What
gh.tsrefuses:user, the issue search, or underrepos/;{owner}placeholder or dot segment;GH_DEBUG, which is never forwarded.Nothing
ghprints to stderr is kept.The channel:
handle("code-review:read", …, { localOnly: true })inindex.ts, so paired desktops can't reach it. The payload is untrusted untilvalidateRequesthas rebuilt it. Browser development and the Store demo answer the channel without GitHub.The design page's new Read service section holds the details: principal and scopes, how each
ghfailure maps to a reason, keys and generations, revisions, limits and memory, and refresh.What's in it
372edbfBridgeCommandsline,"code-review:read": { req: CodeReviewRequest; res: CodeReviewReply | null }.cancelanswers null.a34ef72ae6ea30197276e,f2c2de7,d31f661src/main/engine/code-review/{gh,github,read-service}.ts, the handlers, three test files, the design page andstatus.md.CR-3 (#346) carries the first three commits as cherry-picks.
Contract changes:
stackHintbecomesboolean | null, because search results name no base branch.Unavailablegainshead-changed.parseRevisionis exported.Acceptance, criterion by criterion
The fixture evidence comes from a synthetic GitHub: two accounts, and two repositories that both have a #73. The authenticated evidence is separate, under AC5.
AC1: list → detail → files through main's handler, with no local mutation.
code-review-ipc.test.tssliceshandle("code-review:read", …)out of compiledindex.jsand checks it is registeredlocalOnly. It drives that handler closure, the real service and the realghtransport against a fakeghexecutable: connection, inbox, overview of both #73s, then files.ipcMain. The sharedhandle()wrapper (trusted sender and document epoch) is covered by the existing main tests.fsorgit.tsimport, no write call, no-X,graphqlor write method, and no Discard.AC2: identical numbers and account switches.
acceptsReplyrejects each for the other.hosts.yml, whichgh auth switch,loginandlogoutrewrite.gh api user(a free 304). A check that fails, fails the read.connectionread in between, answerssigned-outwith nothing in it. This holds whether the read was aborted orghfinished it as the new account.AC3: recoverable states, with no secret leakage.
signed-out, and the identity is droppednot-founddenieddenied, with a message to authorizeghfor single sign-oninvalid, with no request madetimeoutrate-limitedwithretryAfter; main makes no request until the resetRetry-After)rate-limitedpartial, reasontimeoutcapped;p35and foreign cursors are refusedcappedbinary(covered)too-large(missing)stale, with the last good answerFake
ghstderr and bodies carryingghp_…strings never reach a reply.AC4: head changes.
stalewith the last good copy, orunavailable: head-changedwhen there is none.stale: head-changed.stale, sosummarizeCheckscalls themincomplete.contextrefuses a revision the PR never had, without making a request.not-found.AC5: authenticated read-only smoke test. Recorded separately below.
AC6: no regressions, no broad permissions, no write endpoint, no cloud service.
gh api … --method GET.AC5: authenticated smoke test (TypeSafeAI/modex, authorized by Val on 2026-10-20)
apps/desktop/.probes/cr2-live-smoke.mjsis git-ignored. It runs the compiled service with the real transport through Val'sghlogin and prints no token, header value or response body.BunsDev(id 68980965) ongithub.com, gh 2.98.0, API version 2022-11-28TypeSafeAI/modex, stable id13868761705106c0d8e56d4ea3f72d37c1b1afe45f9d30a57e, basemainate2d7703a63e76d08373fb165b50c010510b56017, merged; 2026-10-20T19:59:15Z6f3a6c98c7cdaaacee25570c12a0c303ff4c8b0d, which includes a real bot reviewResults, the same on both PRs:
stack(waiting for [CR-4][P1] Reconstruct PR overview, activity, checks, stack and preview cards #323), a key for another account (signed-out), and an unknown repository id (not-found).304s. Only one wasgh api user, because the unchangedhosts.ymlfingerprint confirms the account after every other read. Full results are indocs/reviews/2026-10-20-cr2-read-smoke.md.The first live run found two mapping gaps, both fixed here with failing tests first:
After the fix, real timelines for #295, #316, #330 and #334 map with nothing dropped.
Also checked live, and written into the design page:
repository_urlbut norepositoryobject, though the schema lists one.304exits 1 but still prints its status line, and isn't charged.Evidence
Red first:
Cannot find module …/gh.js, then…/read-service.js).Code Review tests: 48 pass across the four files: the contract test plus the three new ones.
Mutation checks: I broke the compiled read path and the contract 44 ways, one at a time, and every break failed a test. That includes 19 aimed at the review and re-review fixes. Among them:
denied;GH_DEBUGforwarded;Full suite:
pnpm build && pnpm testexited 0 ond31f661(base43d5b9c, macOS, Node 24.18.1):A trial merge with current
main(ac7f743) is clean, and the merge queue tests the combination.E2E: not run locally. There is no renderer change, and free disk was low (2.4 to 7.5 GB). CI's
unit + e2e (macOS)runs it.Review
An independent review of
197276erequested changes: one AC2 blocker, five should-fix and a dozen nits. All of them are fixed inf2c2de7, each with a failing test first.A re-review of
f2c2de7found two more AC2 holes in the new after-read check, plus a partial fix for item 5. They are fixed ind31f661. Both replies are in the PR comments.Not done here
stackwaits for [CR-4][P1] Reconstruct PR overview, activity, checks, stack and preview cards #323's choice of source.activity: a commit's author is a git identity, soauthoris null.deployedevents are plain events until [CR-4][P1] Reconstruct PR overview, activity, checks, stack and preview cards #323 decides how deployments are read.Coven-Harness: Claude Code / claude-opus-5-5