Commit Graph
3 Commits
Author SHA1 Message Date
OrcaWinandOrcaWin fdcf887bf0 chore(lint): fix suppressed lint violations instead of ignoring them (#33294)
* chore(lint): remove disable directives that suppress nothing

Each was reported unused by the oxlint config that owns its rule (root,
casting, react-doctor, anti-slop, native-plugins), including 17 file-level
react-doctor blanket disables whose rule no longer fires. An unused
react-hooks disable was masking a react/purity finding in
useAgentBucketCounts (compiler rules skip functions with hook
suppressions); suppress that one explicitly where the code explains it.

* test(tab-bar): use typed tab/file fixtures instead of @ts-expect-error partials

* chore(lint): fix misc suppressed lint violations instead of disabling

- consistent-type-imports: the "vi.importActual requires inline import()"
  rationale was wrong; use typed vi.mock(import(...), importOriginal) or a
  type-only namespace import.
- no-control-regex: source-control commit/push failure summaries, shell PATH
  hydration, and ipynb ANSI parsing reuse the shared ansi-escape-sequences
  stripper and control-character pattern instead of private copies.
- no-explicit-any / no-useless-spread / no-this-alias: typed `this` in
  bridge test mocks, drop blanket file-level disables, drop a this-alias.

* refactor(main): replace avoidable type assertions with checked narrowing

17 production cast suppressions in main/wsl-guest removed: untrusted IPC
and RPC payloads (Jira issue update, WSL guest plugin install, handshake
type) now narrow through isRecord/type guards and build typed results;
redundant casts dropped; hand-rolled object checks and errno casts use the
existing isRecord / hasErrorCode helpers.

* refactor(renderer,mobile): replace avoidable type assertions with checked types

13 production cast suppressions removed: RPC failures read through the
`ok` discriminant instead of casting to RpcFailure; react-native-web host
refs narrowed with instanceof HTMLElement; redundant reply/diff casts
become checked annotations; the TUI agent param uses z.custom(isTuiAgent)
like its sibling schemas; the agent-status slice cast that hid missing
actions is gone so the creator's return type checks the whole slice.

* chore(lint): drop dead tiptap wrapper; scope anti-slop mocking rule to test support

- safe-react-node-view-renderer patched handleSelectionUpdate, which
  @tiptap/react 3.31.3 no longer has, so it always returned early. Use the
  stock ReactNodeViewRenderer.
- The anti-slop test override only matched *.test.{ts,tsx}, forcing
  file-level no-module-mocking disables in test harnesses and fixtures.
  Match *-test-harness, *-test-fixture(s), .test-fixture and *.test.mjs,
  then drop those disables and the now-redundant per-file exemptions.

* test: replace repeated cast suppressions with typed fixtures (main/shared/e2e)

Net -119 cast directives. Typed fixtures replace recurring casts:
unusedSshConnection/unusedOrcadHostTransport placeholders, a typed
readPersistedState harness read (exposed a test treating optional
httpProxyUrl as always set), real Response objects in fetch spies, an
updater main-window fixture, a CDP webContents adapter, one shared
releaseModuleExport for cross-version tests, and requireOverlayDir in
place of `as string` on possibly-undefined overlay paths.

* fix(runtime): validate loaded device rows and host scope instead of casting

Two cast suppressions claimed checks that did not happen:
- device-registry-file-read: "normalizeLoadedDeviceRow re-validates every
  field" was false; deviceId, token, name and timestamps were never
  checked. Rows without an identity are now dropped and the file is set
  aside through the existing corrupt-file path (registry reads as
  doubtful, so no revocation verdicts); a non-string token is stripped
  so the row stays listed but cannot authenticate.
- client-ui: "only optionality differs" was false; any string reached
  persisted workspaceHostScope. Narrow it with isWorkspaceHostScope and
  ignore unknown scopes (wire schema unchanged for mixed versions). The
  remaining cast now states what it actually covers.

* test(runtime,ipc): drop redundant cast suppressions; typed runtime store fixture

-98 cast directives across runtime/ipc tests: 61 casts that typecheck
without the assertion; a typed vi.fn for scoped invalidation; a
partialRuntimeStore fixture in place of `store as never` for
OrcaRuntimeService; typed worker terminal/authority builders in the
incarnation-fallback suite (the old stub lacked runtimeId, ptyId,
worktreeId and launchTokenHash) plus removal of stacked duplicate
directives there.

* chore(lint): typed vi.mock imports and explicit listener snapshot in shared-bucket tests

Remaining files from the misc-rule pass that overlapped other buckets:
codex-accounts mocks use the typed vi.mock(import(...)) form instead of
a consistent-type-imports disable; the daemon late-exit fixture copies
its listener set with Array.from instead of a no-useless-spread disable.

* refactor(github,mobile): narrow routing settings type; drop remaining avoidable casts

- work-item-routing cast a one-field object to AppState['settings'] under
  a claim that readers only consume activeRuntimeEnvironmentId. Return the
  existing RuntimeFocusSettings pick instead and narrow the cache-key
  chain's parameters, so the compiler enforces that claim.
- mobile file preview reads refusals through the RpcResponse `ok`
  discriminant rather than a cast.
- terminal document host-notify accepts `unknown` errors, removing the
  message-bridge cast.
- native chat readability drops a redundant repo-list cast.

* test(renderer): replace repeated cast suppressions with typed fixtures

-140 cast directives (and ~150 unsuppressed casts of the same shapes):
- mockPanePtyArgs owns the connectPanePty fixture casts, so a wrong
  argument order or fixture is now a type error; drop binding casts for
  members PanePtyBinding already declares.
- Seed AppState tab rows with the typed makeTerminalTab; this exposed a
  split direction 'row' that is not a TerminalPaneSplitDirection.
- Partial GlobalSettings use getDefaultSettings overrides; Jira-only
  stores use createTestStore; runtime environment rows/statuses come from
  a typed paired-runtime-environment fixture.

* chore(lint): drop no-module-mocking disable now covered by the test-fixture glob

* test(e2e): fix all 221 grandfathered e2e type errors; baseline to zero

The e2e typecheck baseline grandfathered 221 errors in 119 files. About a
third were tests drifted from the current API, several of which would
fail or mislead at runtime:
- terminal-shortcuts called a nonexistent xterm buffer method;
- the daemon-lifecycle fake lacked terminateOwnedTree, hidden by importing
  its type from a module that does not export it;
- arguments in the wrong position (listProcesses(hostId, opts), a dropped
  pty.write inputKind, an extra toWebTerminalSurfaceTabId argument);
- removed enum values ('drawer', 'idle', 'editor',
  'session-insecure-origin') and a launch_source the telemetry schema
  rejects;
- missing required fields (turnScope, childWorkOpen, providerIdentity,
  reviewLookupOutcome, runtimeServiceAccount behind an `as RelayConfig`);
- dead reads (tab.customTitle, bucket.targets, changedPixels).
Also lifts variables that narrowed to `never` after poll-callback
assignment, and adds a typed xterm render-dimensions accessor. Includes
the cross-version suites' shared releaseModuleExport adoption.

* chore(typecheck): gate the now-clean e2e typecheck in local lint

The e2e baseline is empty, so the ratchet now rejects any tests/ type
error. CI already runs it via run-ratchets; add it to pnpm lint's
explicit ratchet list and replace the stale 'not yet clean' note.

* test(mobile): typecheck every mobile test; baseline 105 files -> 0

mobile/tests-typecheck-baseline.txt grandfathered 105 test files (531
errors) whose types CI never checked. Contract changes had landed without
these tests following, and type pins in them silently checked nothing.

- Root cause: salvagedOptional (src/shared/zod-salvage.ts) cast away
  zod's optional marker, so every reply type built on it claimed
  salvaged fields were always present. Return a real ZodOptional;
  runtime is unchanged (the outer optional short-circuits undefined).
- Production now takes the port it uses: RpcOperationSender instead of
  RpcClient for send-only callers, a narrow fetch signature, and only the
  request fields push-receive reads.
- findAllByHostType/findByHostType replace `'Text' as never` lookups;
  stubRpcClient gives tests a typed RpcClient.
- Real test bugs: settings-host-client-lifecycle "relay" hosts were
  direct hosts (relayHostId no longer exists); two toEqualTypeOf pins
  could never hold; a recovery hook ran with an undefined scope key;
  a create-PR test cast to a never-imported type; "no URL" assertions
  passed on null; plus stale required props, args and enum values.

* test(e2e): parse workerStart result instead of casting it

* test(runtime): typed partial runtime stores with production-shaped fixture data

Route every partial store passed to OrcaRuntimeService through
partialRuntimeStore with typed row builders, removing 60 cast directives
that hid fixture data production never produces:
- paired-client-navigation-test-harness keyed setWorktreeMeta writes by
  `[object Object]` (it took an id where the store takes a target), so
  reads never saw them;
- invalid worktree lineage origin/capture and incomplete workspace lineage;
- getWorkspaceSession returning null where the store returns the default
  session; getGitHubCache returning undefined;
- orcad migration states missing migrationId/manifest/receipt, with a
  toEqual pinned to that incomplete shape;
- partial repos, metadata, folders, project groups and notification rows.

* refactor(renderer): resolve suppressed React hook lint instead of disabling it

109 of 187 react-hooks/react-doctor suppressions fixed:
- ~95 exhaustive-deps disables only hid stable refs and setters passed
  through controller objects; list them (identity never changes, so
  nothing re-runs), plus a few real stale closures (tabId/worktreeId).
- Effects keyed on one value that read the latest props now use
  useEffectEvent (WebConnect, skill freshness, Bitbucket dialog seeding,
  Landing preflight, automation catalog, structured-session recovery,
  file explorer).
- The conditional layout-owner hook is selected once at module load from
  the build constant instead of a rules-of-hooks disable that also hid
  the whole controller from compiler and react-doctor rules.
- A hook disable was masking a react/purity Date.now() in the palette's
  recent-tabs memo; sample it when the snapshot is captured.
- Three identical debounced-search effects share
  useTaskSearchDebouncedApply; layout snapshot merge helpers move out to
  stay under max-lines.
Remaining suppressions are mount-once resources, invalidation epochs,
deliberately keyed fields, and test harnesses that mock the dispatcher.

* chore(ratchet): exempt test harnesses and fixtures from the focus-read ratchet

The ratchet skipped *.test-fixture.ts but scanned *-test-harness,
*-test-fixture(s) and *.test-support files. Those seed settings with
getDefaultSettings (which the ratchet discovers as a focus reader) and
never route host work, so 17 baseline entries were false positives, and
replacing a test's `settings: {...} as never` with typed defaults tripped
it. Match the repo's test-support names and prune (which also drops one
stale entry), and rename the frame-ordering harness to *-test-harness like
its siblings. A blanket "-harness" match would also exempt
e2e-terminal-pty-harness, which production imports, so the names stay
explicit.

* test: merge duplicate shared/constants imports

* revert(file-explorer): restore original tree load effects pending SSH e2e investigation

* test(runtime): drop RuntimeStore cast now that the fixture is typed

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-10-24 19:37:19 -07:00
Brennan Benson 5e3effc32f fix(native-chat): show every user message on the message rail, not just loaded ones (#22558)
* fix(native-chat): show every user message on the message rail, not just loaded ones

The message rail was built only from transcript rows the renderer had
loaded, so any prompt above the loaded page had no tick, and the rail lost
ticks when a long live session trimmed its retained window.

The host now answers `agentSession.conversationOutline`: every user message
in a structured session's journal (item id, creation sequence, a preview
cut to 200 characters, image count) plus the journal position it is current
through. It is derived from the reduced journal on each request with the
same projection the transcript runs, so an entry's id and preview are what
the loaded row shows. The reply is bounded like a history page: previews
shorten, then drop, and only then do the oldest entries go.

The renderer asks only while the pane is visible and older history is
unloaded, uses outline entries only for messages older than its loaded
window (the window is authoritative for the rest), and falls back to loaded
messages while the outline is stale (epoch change, or the window trimmed
past what it covers). Selecting a tick with no row pages older history in
until the row exists, then uses the existing rail jump.

The method is negotiated with `agent-session.conversation-outline.v1`; a
client never calls a host that does not advertise it, and any failure
leaves the rail on loaded messages.

* fix(native-chat): keep a rail jump from the bottom from re-arming follow and cancelling itself

A rail jump started by a reader following the end stopped a few pixels
above the bottom instead of reaching the message. Paging older history in
for a jump always leaves the reader following at the very end, so jumps to
unloaded messages hit it every time; a jump to a loaded message from the
end did too.

The jump scrolls smoothly, and only its landing is marked as the
application's own scroll. Its first frames move a pixel or two, still
inside the band where a reader event re-arms follow, so the list read the
jump leaving the end as the reader arriving at it. The next frame, just
outside the band, then read as the reader taking over and rebased the view
with an instant write, which cancels the smooth scroll.

Re-arming follow now needs the reader to be arriving at the end: an
unmarked event that moved the view up never reattaches a detached reader.

* test(native-chat): check a rail jump left the end before reading where it landed

* fix(native-chat): keep the rail's message list still while a press selects an item

With the whole conversation in the rail, its hover list overflows and opens
scrolled to the message being read. Clicking an older item did nothing:
pressing it focuses it, which turns the hover preview interactive, and that
switch re-ran the effect that scrolls the lit row into view and focuses it.
The list moved under the pointer between press and release, so the click
landed on the list instead of the item, and focus jumped to the lit row.

Revealing the lit row now follows the list opening (and its rows shifting),
not the switch between hover and interactive. Entering interactive moves
focus into the list only when focus is not already on one of its items.

* perf(native-chat): keep the rail's outline entries stable while a trimmed window slides

A long live session holds a head-trimmed window, so every new row moved the
oldest-loaded edge and rebuilt the outline view even when no user message
crossed it. The rail then re-merged, re-rendered and re-read the scroll
geometry on each new row. The view is now reused while the set of entries
older than the edge is unchanged.

* fix(native-chat): let a rail jump wait out an older page already loading

Scrolling to the top of the loaded window asks for the next older page. A rail
jump made while that page was in flight asked again, got the lane's immediate
no-op return, read it as a page with no progress, and dropped the click. The
jump now waits for the in-flight page to land before deciding.

* fix(native-chat): reattach follow when content shrinking clamps a reader onto the end

The rule that stops a smooth scroll leaving the end from re-arming follow
compared offsets, so it also refused a reader whose offset dropped because
settled content folded away beneath them and the browser clamped them onto
the end. They sat at the bottom without following, and the next reply grew
out of view. Re-arming now requires closing on the end rather than moving
down, which still rejects a scroll leaving it.

* fix(native-chat): keep the rail hooks' ref writes out of render

Both hooks wrote a ref while rendering, which React may replay or discard.
The rail's structural-sharing baseline is now recorded after commit, and the
history jump calls the lane's page loader from its effect instead of through
a render-updated ref.

* fix(native-chat): let the latest rail pick win over a jump still paging

A jump to an unloaded message keeps paging older history until it lands. A
pick made meanwhile lost to it: a loaded message scrolled into view, then the
earlier jump finished and pulled the reader away; another unloaded message was
ignored. Picking a loaded message now cancels the paging jump, and picking an
unloaded one retargets it without asking for a second page.

* fix(native-chat): step a rail history jump with a functional update

The step that requests the next page wrote the pending jump from the value
its effect closed over, so a pick or cancel queued since that commit would
be overwritten.

* refactor(native-chat): run a rail history jump as one abortable awaited loop

The jump through unloaded history was an effect-driven state machine that
guessed "no progress" from the message list's identity and could not be
cancelled by anything but another rail pick. A diff reveal, "Jump to latest"
or the reader scrolling left it paging, and when its page landed it pulled
the reader away; a history read that kept failing during a live turn could
repeat back to back.

Loading an older page now reports how it ended, and a second request while
a page is in flight joins it instead of being refused. The jump is an
awaited loop that reads the rail from a commit after each page, stops on
anything but a page that moved the window, and is aborted by any other
navigation, reader input (wheel, touch, scroll keys, scrollbar), a session
switch or unmount. The latest pick wins.

* fix(native-chat): derive the rail outline from the transcript's own projection

The host built the outline from user items alone, while the transcript
orders every message by when it was observed, folds tool results into the
turn above and then drops harness turns. An imported user row carrying a
tool result beside harness text therefore got a rail tick previewing the
harness text, and clicking it paged history for a row that never draws.

The transcript's order-fold-strip projection now lives in one shared
function. The renderer's list projection wraps it with its own tail-row
order, and the host runs it over the whole journal and keeps the user rows
that draw content, so the outline lists the same messages in the same order.

* fix(native-chat): retry a failed rail outline read a few times

A failed outline read left the rail on loaded messages until a new gap
opened, the pane was shown again or the epoch changed. The client now
rejects a failed read (a host without the outline still resolves to
nothing, without being called), and the rail retries up to three times
with doubling backoff.

* test(native-chat): give the rendered-transcript fixture the older-page result contract

* perf(native-chat): sort the shared transcript projection without a spread copy

* fix(native-chat): let a wheel over the rail cancel a rail jump still paging

The rail forwards its wheel to the transcript, so a reader scrolling there is
scrolling the transcript. That wheel never reached the scroller's reader-input
handlers, so the jump kept paging and later pulled the reader to its target.

* fix(native-chat): keep the rail's list open while a picked message pages in

Picking a message that is not loaded yet can take several pages of older
history. The list closed on the pick, so its busy item was never seen and
the click looked ignored. The list now stays open with that item pulsing
until the jump lands or is abandoned, and stops revealing the lit row
meanwhile so the rows do not move under the pointer.

* fix(native-chat): keep the shared transcript projection loadable on mobile

The projection moved to src/shared, which mobile's Hermes engine also loads,
and switching its sort to toSorted broke the Hermes compatibility guard.
Sort a copy made with Array.from instead, as other shared code does.
2026-09-23 20:26:54 -07:00
NeilandBrennan Benson 4d3b0cca87 fix(omp): publish parent transcript paths for native chat (#20615)
* fix(omp): publish owning session transcript paths

Adopt stablyai/orca#19529 on the child-session ownership fence. Preserve id-based resume and execution-host transcript boundaries.

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>

* test(omp): render authoritative transcript reader output

---------

Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
2026-09-19 00:47:32 -07:00