Repository navigation
Agent Dashboard: selected sidebar entry, pop-out settings menu - #2
Merged
Merged
Conversation
The sidebar entry looked identical whether the Agent Dashboard was on screen or not, and clicking it while the pop-out was open only re-focused that window — there was no way to hide the board from the sidebar. The entry now derives "showing" from the surface actually on screen: the in-window drawer's open state, or the pop-out window's (read once on mount via getPopoutOpen, then followed through onPopoutOpenChanged). Showing renders the sidebar's documented active-row treatment plus aria-pressed / data-current; clicking it hides the board again, which needed a new dashboardPopout:close channel restricted to the trusted main renderer. A mode change made in the experiment pane also closes a drawer it leaves behind, so the entry can never read unselected while the board is visible. That check keys off the transition into pop-out mode rather than the mode itself: the reverse handoff opens the drawer from the main process a tick before the new mode reaches the store, and a level check would close the board the user just asked for.
The pop-out window rendered the board with no header actions, so it had no settings button and no way to switch back to the in-window board — the mode control only existed in the in-window drawer, which the pop-out replaces. The pop-out renderer already runs its own settings-synced store (popout.tsx seeds appearance from the main process and tracks settings:changed), so the existing menu works there unchanged: DashboardPopoutRoot now passes AgentDashboardSettingsMenu as headerActions, and the menu's onSwitchToPopout prop became the host-agnostic onModeChange so the in-window host can narrow it to the pop-out direction. Main owns the handoff because it is the only place that knows whether the pop-out window exists: when the mode stops being "popout" while the window is open, it closes the window and tells the trusted main renderer to take the board over in the drawer, so switching modes never loses the board. The same listener covers the experiment pane, which previously could leave the pop-out open while the setting said in-window. Verified end to end with a rendered Playwright spec driven over CDP with ORCA_BACKGROUND_LAUNCH=1 (no window ever reaches the screen): the entry's selected state in both modes, the pop-out's settings button, and the in-window handoff.
The gate's local fallback hardcoded origin/main. In this fork the trunk is nplez1/main — origin/main is a stale mirror 47 commits behind it — so every local run diffed the fork's own trunk and reported unrelated findings (40 findings across 512 files on a two-commit branch). origin/HEAD is the remote's actual default branch and resolves to origin/main on an ordinary checkout, so the fallback now honours it. CI passes the pull-request base SHA explicitly and is unaffected.
Owner
Author
|
Diagnosis of the failing checks. None of them come from this branch — every one reproduces on Proven pre-existing on the trunk (run at
|
| Failure | Cause |
|---|---|
test 2/8 — 4 × agent-hook-listener-grok-completion (child-session Stop/StopFailure/StopCancelled/SessionEnd) |
The listener diverges from upstream here (+681 lines across 9 files: descendant-events.ts, descendant-pane-state.ts, the descendant-pane-lifecycle merge). The child-session guard no longer suppresses descendant events, so the pane is reported working where the test expects no observation. |
test 7/8 — 6 × worktree-rm-host-qualification |
local(worktrees) added deleteRemoteBranch to the worktree.rm payload; the upstream test asserts the exact option list, so it fails on the extra key (+ "deleteRemoteBranch": false). |
static analysis — 6 anti-slop findings |
session-search-list-latency.test.ts uses shape in symbol names (×5) and AgentSessionSearchSection.test.ts uses Reflect.apply. Both files come from the cherry-picked session-search work. |
Reproduction: same counts (10 failed / 6 findings) at the trunk, so these are not regressions from this PR.
Environmental, also unrelated
package(linux) —pr.ymlverifies the root-package marker at a hardcoded./opt/Orca/resources/package-type, but this fork setsproductName: 'Orca NP', so the deb/rpm install dir is/opt/Orca NP. Upstream's ownresources/linux/packaging/after-install.shsays the directory name varies byproductName— the CI assertion is the one place that still assumesOrca.package(windows) —windows-hook-upgrade.test.tshardcodes~/.orca/agent-hooks/codex-hook.cmdwhilelocal(identity)moved the hook home to~/.orca-np. The test isdescribe.skipIf(process.platform !== 'win32'), so it can only ever fail in this job.track-community-pr— needssecrets.BUFO_BOT_PRIVATE_KEY(StablyAI-internal,actions/create-github-app-tokenfor ownerstablyai), which does not exist in a fork.
Passing here
typecheck, e2e / changed e2e specs (this branch's new agent-dashboard-sidebar-toggle.spec.ts runs and passes), e2e / build e2e app, root directory guard, test vs non-test LoC, detect code-relevant changes.
Suggested follow-up (separate from this PR)
- Marker check: derive the
/opt/<dir>fromproductNameinstead of hardcodingOrca. windows-hook-upgrade.test.ts: take the script path from the installer's own resolver rather than a literal.orca, so the next identity rename cannot miss it.track-community-prs.yaml: guard the job withgithub.repository == 'stablyai/orca'.- Anti-slop findings: rename the
shapesymbols to their domain role and dropReflect.apply. - The 10 genuine test failures: update
worktree-rm-host-qualificationfor the new option, and fix the child-session suppression in the descendant listener (that one looks like a real bug, not a stale test).
None of these come from the dashboard change; each reproduces on nplez1/main with those commits absent. A child's END with no lead state to gate on now publishes nothing. The descendant layer derives the pane state from the lead, and falls back to `working` when no lead record exists — correct for a child that STARTS (the lead spawned it), but for a child that ENDS it relabels a pane nobody has described and no later child event can settle. The grok child-session contract is that a child's terminal events are ignored; the fork's own descendant tests always start a lead turn first, so they were never exercising this case. worktree.rm host qualification: the local deleteRemoteBranch option was added to the forwarded payload without updating the helper that lists them. windows-hook-upgrade.test.ts asserted the managed command against the PRE-upgrade script path, so the identity move to ~/.orca-np made it expect a command the installer no longer writes. It now takes the managed path from the installer (getManagedScriptPath) while keeping the old path as the fixture, so it covers the cross-identity upgrade and cannot drift again. Windows-only, so it can only fail in that job. pr.yml verified the root-package marker at a hardcoded /opt/Orca, which is /opt/<productName>; discover it instead, as after-install.sh already does. track-community-prs.yaml needs the StablyAI bufo-bot key to file the PR in a Stably project, so it cannot run on a fork; guard it to the upstream repo.
The PTY smoke step spawned dist/win-unpacked/Orca.exe, but the unpacked exe is named after the Windows executableName, which this fork sets to "Orca NP". Same rename fallout as the /opt marker check: the job only reached this step once the hook test ahead of it was fixed, so the failure was masked. The step reads executableName from electron-builder.config.cjs and quotes the path, so a space in the product name cannot split the argument.
The local(identity) rename is a rename of every name an official install owns, and the packaging checks were the places that still spelled the old ones. Each of these was masked by the failure before it in the same job. - The unpacked Windows exe is named after win.executableName, so the PTY smoke now reads that from the build config instead of hardcoding Orca.exe. - smoke-packaged-cli.mjs looks the launcher up in the package's own resources/bin (orca-np.exe beside orca-np.cmd here) rather than naming it. - The headless-serve oracle asserts the registered command, which is CLI_COMMAND_NAME (orca-np), while the symlink target stays the bundle's launcher name (orca-ide). The runner now passes the command name in, read from cli-install-constants.ts. - The workflow test that pins that step follows the new derivation. Two stale expectations that were only selected once the change set widened, both failing at the fork's trunk: worktree.rm now forwards deleteRemoteBranch (second file with that helper), and this build's single release stream means a dev-channel tag resolves to nplez1/orca rather than upstream's dedicated hourly repo.
The static-analysis job failed on six anti-slop findings from the cherry-picked session-search work: five symbols named `shape` (they label the query under test, so `queryLabel`) and a Reflect.apply in the settings test's handler invoker (now a named HandlerLike guard, so the call is typed). The design-system report also flags every fill in status-bar/icons.tsx (22 of them, including the Fireworks mark's #6720FF). Those are vendor logo colors — a theme token or currentColor renders the mark wrong — so the rule is turned off for that file in the design-system config, which is this repo's pattern for a file-scoped exemption (an inline directive naming a rule the root scan does not load always reads back as an unused directive there). The icons module is byte-identical; only the config changed.
The Windows CLI launcher resolved the app as Path.Combine(appDirectory, "Orca.exe"). The app is named after productName, which this fork sets to "Orca NP", so the packaged launcher exited with "Unable to locate Orca.exe" and the orca-np CLI was dead on Windows — not only in CI: the same launcher ships in release builds. It now mirrors the Linux launcher: try the names a build can produce, then fall back to the install root's own executable, skipping Chromium's crashpad helper and the NSIS uninstaller. The error message no longer names a file the build might not produce. The launcher is compiled from this source during the build (native/windows-cli-launcher/.build/orca.exe -> resources/bin/orca-np.exe), so the fix takes effect on the next Windows package; the launcher's own launch test runs on Windows in CI.
all-host-repo-catalog-actions.ts imported ./repo-catalog-merge and ./repo-catalog-fencing twice each — a type-only import and a value import — so audit:code-quality:native failed on the denied import/no-duplicates warning. Pre-existing on the trunk; it only became visible once the anti-slop errors ahead of it were fixed. Folded both type imports into their value imports, which is also what the inline `type` specifiers already used in those same blocks.
The localization gate flagged a new unlocalized renderer string: the sentence teardown returns when a host answered without a cleanup. It was in the wrong layer as well as unlocalized — RemoteBranchCleanup.message is documented as Git's own text, and the toast already translates the title around it. The store now reports only `status: 'failed'` for that case and the toast supplies the sentence (message ?? localized fallback), with the string added to the catalog under the toast's existing node. No behavior change: Git's own text still shows when there is one.
smoke-managed-hook-runtime-node18.mjs asserted the installed hook commands under ~/.orca and probed ~/.orca/agent-hooks/*.sh. The home directory is an identity this fork renames (.orca-np), so the smoke failed the moment the job's path filter selected it. It now reads HOME_DIRECTORY_NAME from src/shared/app-directory-names.ts, the same module the installers under test resolve it from.
nplez1
pushed a commit
that referenced
this pull request
Sep 25, 2026
… manifest (OTA phase C follow-up) (stablyai#22376) * perf(mobile): keep four bundle chunk reads in flight across the whole manifest The fetch ran one worker per asset and paged inside an asset sequentially, so the largest script's 71 chunks were 71 serial round trips while the other readers idled. One window of four chunk reads now covers every (asset, offset) on the host's chunk grid, largest asset first. A read_limited refusal narrows the window and retries the read; eof is still read from the reply. Synthetic manifest (one 71-chunk asset, five small): 72 round trips -> 19. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the bundle-fetch family under pipelined reads Baseline moves to a1ee317, the pipelined fetch. 781 goldens change only their `baseline` header line. Six bodies move: mobile-web-bundle-fetch-paged, mobile-web-bundle-build-changed, and the four matrix-mobileweb.bundle-fetch-* goldens. The two bundle-fetch scenarios now bind requests in pipelined order, largest asset first, with every chunk sent before any reply: index.html@0 (#1), index.html@16 (#2), assets/app.js@0 (#3). - fetch-paged: the request set is identical, only reordered. The chunk sender names/ordinals and the scenarioSha256 moved; the replies and the fetched bytes did not. - build-changed: the same reorder, plus one request that is new because pipelining puts it in flight before the refusal lands (index.html@16). The refusal and the checkpoint are unchanged. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): the bundle chunk comment no longer says a reply picks the next offset Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the bundle comment fix Baseline moves from a1ee317 to e94bde3, the comment-only commit on a fenced path. All 787 goldens and the scenarios file change only their `baseline` line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): pool four workers over planned bundle chunks, report progress per chunk Design-review fix round, sketch C: four workers take reads from one planned chunk queue, largest asset first. They replace the central pump and the read_limited narrowing. A host frees its read slot before it replies, so a lone fetch capped at four cannot trip the limit. A refusal now fails the fetch, as it did on base, and stops the other reads. - Each asset's buffer is allocated when the plan is built. That removes the nullable buffer and its guard. The per-asset byte count is gone, and the hash is the oracle (S1, S2). - The caller's signal is checked before each read and once after the pool drains, so an abort during the final window rejects with fetch-stopped (S3). The stopped check now covers only the caller's abort. The internal stop only makes late replies skip checks, hashing and progress (S4). - Progress is reported per accepted chunk. completedAssets still counts on completion (S6). - Renames: MAX_CONCURRENT_CHUNK_READS, and `reply` for the RPC reply (N1). - The slot check states exact-slot acceptance once, then classifies the refusal (N3). - Stale test titles and comments are renamed (N4). The synthetic manifest still takes 19 round trips. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): say why bundle reads settle at on-settle under pipelined chunks Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the bundle-fetch family under per-chunk progress Baseline moves to 34fda6f. 782 goldens and the scenarios file change only their `baseline` line. Five bodies move: mobile-web-bundle-fetch-paged and the four matrix-mobileweb.bundle-fetch-* goldens. The only change is bundle-progress effects. One report now lands after the first accepted chunk of index.html (0 assets, 16 bytes), and the later progress ordinals shift by one. Requests, replies and fetched bytes are identical. mobile-web-bundle-build-changed keeps its body, because its one accepted chunk is the whole of assets/app.js. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): drain the fake host after the fetch settles so the sibling-stop bounds can fail The wave host stopped releasing replies once the fetch settled. Reads that should have been stopped were never answered, so the read_limited bound (7) and the chunk-failure bound (5) held even with no sibling stop at all. It now drains until nothing waits. With the worker's stopped.abort() removed, both bounds fail at 76 requests. assertChunkDescribesAsset's parameter is renamed to `reply`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the sibling-stop test fix Baseline moves from 34fda6f to 97b13ec. All 787 goldens and the scenarios file change only their `baseline` line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
nplez1
pushed a commit
that referenced
this pull request
Sep 25, 2026
…otiated mobileWeb.bundle.range (OTA phase C follow-up) (stablyai#22381) * feat(mobile-web): serve gzipped 384 KiB bundle ranges behind a capability Adds mobileWeb.bundle.range with its own strict params and result, so shipped chunk readers see no reply change. The host gzips each range at level 6 and sends identity when gzip does not shrink it, sharing the chunk method's read-slot budget and per-asset verification. status.get advertises mobileWeb.bundle.range.v1 beside mobileWeb.bundle.v1, and the method is allowlisted for paired phones. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): read the bundle range capability and range replies Adds the range reply reader and operation, and picks range or chunk from the status.get capabilities the connection already proved, so an older desktop keeps being paged in chunks with no probe round trip. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * perf(mobile): keep four bundle chunk reads in flight across the whole manifest The fetch ran one worker per asset and paged inside an asset sequentially, so the largest script's 71 chunks were 71 serial round trips while the other readers idled. One window of four chunk reads now covers every (asset, offset) on the host's chunk grid, largest asset first. A read_limited refusal narrows the window and retries the read; eof is still read from the reply. Synthetic manifest (one 71-chunk asset, five small): 72 round trips -> 19. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * chore(mobile): add fflate 0.8.2 for gzip bundle ranges Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): decode gzip bundle ranges into a bounded buffer Inflates each range into a buffer one byte past its window, so a gzip bomb costs at most that allocation and an overlong body is visible. A corrupt, truncated or unknown-encoding body refuses as range-undecodable; a body of the wrong decoded length refuses as range-length-mismatch. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): pass the bundle read method from the session to the fetch The download reads the capabilities of the gates the reducer decided under and hands the fetch range or chunk. The fetch does not act on it yet; the range read lands on the pipelined window. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the bundle-fetch family under pipelined reads Baseline moves to a1ee317, the pipelined fetch. 781 goldens change only their `baseline` header line. Six bodies move: mobile-web-bundle-fetch-paged, mobile-web-bundle-build-changed, and the four matrix-mobileweb.bundle-fetch-* goldens. The two bundle-fetch scenarios now bind requests in pipelined order, largest asset first, with every chunk sent before any reply: index.html@0 (#1), index.html@16 (#2), assets/app.js@0 (#3). - fetch-paged: the request set is identical, only reordered. The chunk sender names/ordinals and the scenarioSha256 moved; the replies and the fetched bytes did not. - build-changed: the same reorder, plus one request that is new because pipelining puts it in flight before the refusal lands (index.html@16). The refusal and the checkpoint are unchanged. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): the bundle chunk comment no longer says a reply picks the next offset Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): read gzipped bundle ranges on the pipelined window A host that advertised mobileWeb.bundle.range.v1 is paged in 384 KiB ranges on the range grid, through the same four-read window and queue as chunks; any other host keeps the chunk grid. Each range is decoded to its exact window length before the fill checks and the asset hash. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the bundle comment fix Baseline moves from a1ee317 to e94bde3, the comment-only commit on a fenced path. All 787 goldens and the scenarios file change only their `baseline` line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the corpus at the range-read pin Repins baseline to the range-read commit and re-records every golden. Only the baseline and lockfileSha256 headers move: the lockfile gained fflate, and the bundle-fetch adapter pages the chunk path, whose requests and replies are unchanged, so no golden body moves. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): state the on-settle reason that holds for pipelined bundle reads Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the on-settle comment fix Baseline moves from 252c592 to fe41226, the comment-only commit on a fenced path. Re-recorded: all 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * chore(mobile): keep the lockfile's patch block in main's form Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the lockfile patch-block restore Baseline moves from fe41226 to 84d6fca, which restores main's patchedDependencies form in mobile/pnpm-lock.yaml. Re-recorded: all 787 goldens change only baseline and lockfileSha256, and the scenarios file only baseline. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): pool four workers over planned bundle chunks, report progress per chunk Design-review fix round, sketch C: four workers take reads from one planned chunk queue, largest asset first. They replace the central pump and the read_limited narrowing. A host frees its read slot before it replies, so a lone fetch capped at four cannot trip the limit. A refusal now fails the fetch, as it did on base, and stops the other reads. - Each asset's buffer is allocated when the plan is built. That removes the nullable buffer and its guard. The per-asset byte count is gone, and the hash is the oracle (S1, S2). - The caller's signal is checked before each read and once after the pool drains, so an abort during the final window rejects with fetch-stopped (S3). The stopped check now covers only the caller's abort. The internal stop only makes late replies skip checks, hashing and progress (S4). - Progress is reported per accepted chunk. completedAssets still counts on completion (S6). - Renames: MAX_CONCURRENT_CHUNK_READS, and `reply` for the RPC reply (N1). - The slot check states exact-slot acceptance once, then classifies the refusal (N3). - Stale test titles and comments are renamed (N4). The synthetic manifest still takes 19 round trips. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile): say why bundle reads settle at on-settle under pipelined chunks Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web): announce bundle ranges on the manifest reply The manifest reply now names the range grid in an optional rangeBytes, beside chunkBytes, and the status capability is gone. The range method takes exactly the chunk params on that grid instead of a caller length. Both methods share one verified read that returns the six-field header, and the range handler checks the connection again before deflating. Range schemas move into the bundle RPC contract; SHA256_PATTERN is shared from the manifest contract. Shared refusals are tested once over both methods. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): drop the range capability read and its session threading The phone will read rangeBytes off the loose manifest reply instead, so the read-method module goes and the session effects and hook return to the pipeline branch's version. Range imports move to the bundle RPC contract, the reply reader reuses the shared SHA256_PATTERN, and a new test pins that node's level-6 gzip from the host encoder inflates with fflate to the same bytes. The fetch and window-read modules still import the deleted names until part 2. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the bundle-fetch family under per-chunk progress Baseline moves to 34fda6f. 782 goldens and the scenarios file change only their `baseline` line. Five bodies move: mobile-web-bundle-fetch-paged and the four matrix-mobileweb.bundle-fetch-* goldens. The only change is bundle-progress effects. One report now lands after the first accepted chunk of index.html (0 assets, 16 bytes), and the later progress ordinals shift by one. Requests, replies and fetched bytes are identical. mobile-web-bundle-build-changed keeps its body, because its one accepted chunk is the whole of assets/app.js. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): page bundle ranges through one window reader chosen by the manifest The fetch keeps the pipeline's four-worker pool and builds one window reader from the manifest reply: ranges on rangeBytes when the host names it, chunks on chunkBytes otherwise. The reader returns the six-field header and a lazy bytes() so the stop and misroute checks run before any decode. A range that inflates to the wrong length now falls to the slot checks, with the one-byte-over buffer as the memory bound, so range-length-mismatch is gone. A rangeBytes this build cannot page reads as absent. Fetch names say window, not chunk. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): drain the fake host after the fetch settles so the sibling-stop bounds can fail The wave host stopped releasing replies once the fetch settled. Reads that should have been stopped were never answered, so the read_limited bound (7) and the chunk-failure bound (5) held even with no sibling stop at all. It now drains until nothing waits. With the worker's stopped.abort() removed, both bounds fail at 76 requests. assertChunkDescribesAsset's parameter is renamed to `reply`. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the window-reader commit Baseline moves to a25a355. Re-recorded: every golden and the scenarios file move only baseline, and the five bundle-fetch goldens also move lockfileSha256 to this branch's lockfile. Every golden body is byte-identical to the pipeline branch's. The recording adapter's scripted host names no rangeBytes, so the bundle family still records the chunk path. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus after the sibling-stop test fix Baseline moves from 34fda6f to 97b13ec. All 787 goldens and the scenarios file change only their `baseline` line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the second pipeline merge Baseline moves from a25a355 to 4d2cab3, the merge of the pipeline's sibling-stop test fix. Re-recorded: every golden and the scenarios file move only baseline. Against the pipeline branch, only baseline and lockfileSha256 differ; every golden body is identical. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): read the bomb inflation without depending on call order With the fetch's sibling stop removed, a read left over from the previous test inflated into the bomb test's record first, and indexOf(601) picked it. The test now asserts some inflation stopped at 601 and none exceeded its buffer, whatever else ran. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the bomb-test fix Baseline moves from 4d2cab3 to 73fde15, the test-only commit on a fenced path. All 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web): tighten the bundle window contract and pin the range sibling stop The range bomb test reads only its own host's inflations, keyed by the gzip bodies that host sent, and plans twenty reads so a missing sibling stop is visible: with stopped.abort() removed it sends all twenty. The range params are an alias of the chunk params, and the chunk data bound is the exact base64 length of a full chunk. The phone's chunk and range replies share one header shape. The window reader closes over the client and bytes() takes the slot length the fetch computes. The host's positional read is readMobileWebBundleAssetWindow. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the window-contract commit Baseline moves from 73fde15 to 7346e00. All 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the main merge Baseline moves from 7346e00 to 9c0fe1a, the merge of main at 98a6a53. Recorded with --record: all 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile-web): drop a test cast and shape-named field maps The bomb test's inflation log is typed by its hoisted factory's return instead of an assertion, and the zod field maps shared by the bundle window schemas are windowParamsFields and windowHeaderFields. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the lint fix Baseline moves from 9c0fe1a to f4f0915. Recorded with --record: all 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): page the range fetch fixture on a small advertised grid The fake host names a 4 KiB range grid and a 1 KiB chunk grid on its manifest reply, which the phone pages as it would the real ones, so the fixtures shrink to a few KiB with the same shapes and each asset is hashed once. The file runs in about 360 ms instead of 3.5 s, which a loaded CI runner pushed past the 5 s test timeout. The desktop range suite still pins that the real host names MOBILE_WEB_BUNDLE_RANGE_BYTES. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-pin the recording corpus at the range fixture fix Baseline moves from f4f0915 to d4d6aad. Recorded with --record: all 787 goldens and the scenarios file change only their baseline line. No golden body moved. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
nplez1
pushed a commit
that referenced
this pull request
Oct 19, 2026
…nce off the deprecated owner resolver (stablyai#30484) The eight mirroring callers (#23-#30) read the owner through runtimeEnvironmentIdWithLegacyFallbackUnlessTwin, so a twin is still not mirrored. Sort-order persistence (#2) leaves out ids that two hosts publish, and ids whose only server proof is a repo row (isUnprovenServerSshRoute), instead of writing them to this desktop's or a guessed server's meta. Deprecated importers 40 -> 31.
nplez1
pushed a commit
that referenced
this pull request
Oct 21, 2026
…win projections stay on their host (review-5 #2 #9 #11) (stablyai#30890) * fix(renderer): host writes route only through the strict owner, and twin projections stay on their host (review-5 #2 #9 #11) A selected ssh: host that two paired servers proxy now resolves ambiguous at the one selected-host resolver, so no caller can fall back to this app's own SSH connection. File Explorer and SSH mutation expectations derive the full {endpoint, place} pair from resolveOwner, so a server folder's nested SSH target stays the write's host. A local lineage write projects onto its own host's rows only. The id-only store-write ratchet now also covers persistence repo-row lookups by id alone (review-5 #10's shape). * fix(ratchet): the id-only lookup scan also sees optional chaining and destructured ids * fix(renderer): an unstamped folder keeps reading its host from its repos; editor focus fallback left for a follow-up * docs(renderer): the File Explorer owner comment names its folder fallback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two Agent Dashboard fixes, plus a fix for this fork's changed-code gate baseline.
ELI5
The sidebar button for the Agent Dashboard never looked "on", even while the board was on screen — and the board's settings were only reachable from the in-window version, so once you switched to the pop-out window there was no way back.
What Changed
The sidebar entry shows and toggles its state. It now reads "showing" from whichever surface is actually on screen: the in-window drawer's open state, or the pop-out window's (read once on mount, then followed through
popoutOnOpenChanged). While showing, it renders the sidebar's active-row treatment plusaria-pressed/data-current; clicking it hides the board instead of only re-focusing it. Hiding the pop-out needed a newdashboardPopout:closechannel, restricted to the trusted main renderer and gated on the experiment flag.The pop-out has the settings menu.
DashboardPopoutRootnow passes the settings menu asheaderActions. The pop-out renderer already runs its own settings-synced store, so the menu is reused unchanged; its host-specificonSwitchToPopoutprop became the host-agnosticonModeChange. Main owns the mode flip because it is the only place that knows whether the pop-out window exists, so switching modes hands the board over instead of losing it.Changed-code gate baseline. The gate's local fallback hardcoded
origin/main. In this fork the trunk isnplez1/mainandorigin/mainis a stale mirror 47 commits behind it, so a two-commit branch reported 40 findings across 512 files. The fallback now honoursorigin/HEAD— the remote's real default branch, which resolves toorigin/mainon an ordinary checkout. CI passes the pull-request base SHA explicitly and is unaffected.Why
The selected state had no single source of truth: in-window it came from the store, and the pop-out had no signal at all. Deriving it from the surface that is showing keeps both modes consistent, and the drawer-close check keys off the transition into pop-out mode rather than the mode itself — the reverse handoff opens the drawer from the main process a tick before the new mode reaches the store, so a level check would close the board the user just asked for.
Reusing the existing settings menu instead of writing a pop-out-specific one keeps one control for one setting; the only change needed was making the host-specific handoff a callback.
Testing
pnpm tc,pnpm lint, andpnpm run check:code-quality:changed(0 findings across the branch's 21 files, baselined onnplez1/main).tests/e2e/agent-dashboard-sidebar-toggle.spec.ts, driven over CDP withORCA_BACKGROUND_LAUNCH=1, so no window ever reaches the screen. It walks the whole flow — in-window toggle both ways, switch to pop-out from the drawer menu, pop-out settings button present, switch back to in-window — and asserts the selected state is painted (the computed background changes), not merely announced to assistive tech.