Repository navigation
fix(agent-status): keep restored spinner titles from reading as working - #43
Merged
Merged
Conversation
Before: a cold restore handed checkpoint.json's lastTitle — written on
checkpoint ticks, so it can capture a mid-turn spinner frame (real values
on this machine: "⠴ π - orca", "⠧ π - Jenkins-Details",
"⠧ π - Remove-Team") — into the PTY record and title tracker via
seedTerminalRestoreTail -> applySeededAgentStatus. The (re)attach
snapshot in getTerminalSideEffectSnapshot replayed that title to every
surface, and because normalizeTerminalTitle canonicalizes braille frames
but never strips them, the restored title classified as working through
detectAgentStatusFromTitle. The sidebar worktree row, the worktree card,
and the tab bar all flipped to working together and only self-corrected
via the 30s cold-park delays and the 60s watchdog heal.
After: the title tracker records provenance — a seeded title is restored
state this process never observed on the wire, and the first live frame
(including synthetic hook frames) confirms it. The replay snapshot stamps
such facts restoredUnconfirmed (mirroring the agent-status hydration
stamp), and the renderer fact handler — the single choke point every
reader's titles flow through — strips working indicators from an
unconfirmed fact that asserts working, feeding readers a title that can
never claim working until a live frame re-asserts it. Both title fields
are cleared so the completion coordinator cannot arm on the restored raw
title and later mint a false task-complete. Permission claims ("π !")
keep signalling, the OMP static marker is rewritten through its protocol
table ("π :" -> "π >"), and observed titles — park/unpark and
worktree-switch replays — pass through untouched. The treatment is host
agnostic: local-daemon and SSH PTYs share the tracker and fact handler,
and remote clients run the same handler.
Route: clearing only the record-title fallback (where normalizedTitle is
null) would not have fixed the bug. The restore seed populates the
tracker, so the working title is served from the non-null tracker branch
(a runtime probe returned "⠋ π - Jenkins-Details" with a tracker entry
present). Since normalizedTitle being null does not identify a cold
restore, the fix takes the provenance route: explicit restoredUnconfirmed
state plus reader-side treatment, never a blanket clear on replay.
Accepted, as agreed: an agent genuinely mid-turn across a relaunch keeps
its indicator quiet until the next live frame re-asserts it — the same
treatment restored hook rows already receive (restoredUnconfirmed state
is never fresh). The same applies to the residual same-run reclaim whose
seed-only title asserts working (e.g. an OMP static working marker that
never repaints): quiet until its next title change. A stale working title
on a PTY that keeps producing output is still healed by main's 3s
stale-title timer exactly as before.
The checkpoint wire format is unchanged; the fact channel only gains an
optional field.
nplez1
force-pushed
the
nplez1/Restored-Title-Working
branch
from
October 1, 2026 03:42
e4df419 to
d22f94e
Compare
Before: `pnpm exec oxlint` fails with six max-lines errors, so static analysis is red on nplez1/main and on every branch cut from it. The upstream sync re-seated this fork's rate-limits, status-bar, sidebar and identity patches onto upstream files that had grown in the meantime, and the combined results crossed the 300/400-line caps. After: the linter is clean again. Per repo rules there is no max-lines disable and no cap bump anywhere — each over-cap file yields one cohesive unit to a domain-named sibling module, and the behavior is unchanged: - service-full-cycle-preparation.ts: the copilot stored-token-vs-gh credential selection moves to service-copilot-cycle-credentials.ts; the three repeated settled-result promise adapters move (as trackSettledProviderResult) onto service-sibling-provider-result.ts - service-full-cycle-application.ts: the nine settled-result to error-snapshot fallback blocks collapse into providerResultSnapshot in service-provider-result-snapshot.ts, shapes preserved exactly, including opencode-go's explicit monthly slot - tooltip.tsx: the rate-limit window unit (window derivation, usage urgency colors, per-window bar) moves to provider-rate-limit-window-sections.tsx - use-status-bar-controller.ts: the usage visibility projection moves to status-bar-usage-projection.ts - tui-agent-config.ts: the per-agent authoring table moves to tui-agent-config-table.ts - worktree-activation.ts: the sidebar reveal group moves to worktree-activation-sidebar-reveal.ts Pure refactor: no behavior changes, no test edits.
nplez1
added a commit
that referenced
this pull request
Oct 1, 2026
…ng (#43) * fix(agent-status): keep restored spinner titles from reading as working Before: a cold restore handed checkpoint.json's lastTitle — written on checkpoint ticks, so it can capture a mid-turn spinner frame (real values on this machine: "⠴ π - orca", "⠧ π - Jenkins-Details", "⠧ π - Remove-Team") — into the PTY record and title tracker via seedTerminalRestoreTail -> applySeededAgentStatus. The (re)attach snapshot in getTerminalSideEffectSnapshot replayed that title to every surface, and because normalizeTerminalTitle canonicalizes braille frames but never strips them, the restored title classified as working through detectAgentStatusFromTitle. The sidebar worktree row, the worktree card, and the tab bar all flipped to working together and only self-corrected via the 30s cold-park delays and the 60s watchdog heal. After: the title tracker records provenance — a seeded title is restored state this process never observed on the wire, and the first live frame (including synthetic hook frames) confirms it. The replay snapshot stamps such facts restoredUnconfirmed (mirroring the agent-status hydration stamp), and the renderer fact handler — the single choke point every reader's titles flow through — strips working indicators from an unconfirmed fact that asserts working, feeding readers a title that can never claim working until a live frame re-asserts it. Both title fields are cleared so the completion coordinator cannot arm on the restored raw title and later mint a false task-complete. Permission claims ("π !") keep signalling, the OMP static marker is rewritten through its protocol table ("π :" -> "π >"), and observed titles — park/unpark and worktree-switch replays — pass through untouched. The treatment is host agnostic: local-daemon and SSH PTYs share the tracker and fact handler, and remote clients run the same handler. Route: clearing only the record-title fallback (where normalizedTitle is null) would not have fixed the bug. The restore seed populates the tracker, so the working title is served from the non-null tracker branch (a runtime probe returned "⠋ π - Jenkins-Details" with a tracker entry present). Since normalizedTitle being null does not identify a cold restore, the fix takes the provenance route: explicit restoredUnconfirmed state plus reader-side treatment, never a blanket clear on replay. Accepted, as agreed: an agent genuinely mid-turn across a relaunch keeps its indicator quiet until the next live frame re-asserts it — the same treatment restored hook rows already receive (restoredUnconfirmed state is never fresh). The same applies to the residual same-run reclaim whose seed-only title asserts working (e.g. an OMP static working marker that never repaints): quiet until its next title change. A stale working title on a PTY that keeps producing output is still healed by main's 3s stale-title timer exactly as before. The checkpoint wire format is unchanged; the fact channel only gains an optional field. * fix(lint): split the six files the sync left over the max-lines cap Before: `pnpm exec oxlint` fails with six max-lines errors, so static analysis is red on nplez1/main and on every branch cut from it. The upstream sync re-seated this fork's rate-limits, status-bar, sidebar and identity patches onto upstream files that had grown in the meantime, and the combined results crossed the 300/400-line caps. After: the linter is clean again. Per repo rules there is no max-lines disable and no cap bump anywhere — each over-cap file yields one cohesive unit to a domain-named sibling module, and the behavior is unchanged: - service-full-cycle-preparation.ts: the copilot stored-token-vs-gh credential selection moves to service-copilot-cycle-credentials.ts; the three repeated settled-result promise adapters move (as trackSettledProviderResult) onto service-sibling-provider-result.ts - service-full-cycle-application.ts: the nine settled-result to error-snapshot fallback blocks collapse into providerResultSnapshot in service-provider-result-snapshot.ts, shapes preserved exactly, including opencode-go's explicit monthly slot - tooltip.tsx: the rate-limit window unit (window derivation, usage urgency colors, per-window bar) moves to provider-rate-limit-window-sections.tsx - use-status-bar-controller.ts: the usage visibility projection moves to status-bar-usage-projection.ts - tui-agent-config.ts: the per-agent authoring table moves to tui-agent-config-table.ts - worktree-activation.ts: the sidebar reveal group moves to worktree-activation-sidebar-reveal.ts Pure refactor: no behavior changes, no test edits.
nplez1
added a commit
that referenced
this pull request
Oct 6, 2026
…ng (#43) * fix(agent-status): keep restored spinner titles from reading as working Before: a cold restore handed checkpoint.json's lastTitle — written on checkpoint ticks, so it can capture a mid-turn spinner frame (real values on this machine: "⠴ π - orca", "⠧ π - Jenkins-Details", "⠧ π - Remove-Team") — into the PTY record and title tracker via seedTerminalRestoreTail -> applySeededAgentStatus. The (re)attach snapshot in getTerminalSideEffectSnapshot replayed that title to every surface, and because normalizeTerminalTitle canonicalizes braille frames but never strips them, the restored title classified as working through detectAgentStatusFromTitle. The sidebar worktree row, the worktree card, and the tab bar all flipped to working together and only self-corrected via the 30s cold-park delays and the 60s watchdog heal. After: the title tracker records provenance — a seeded title is restored state this process never observed on the wire, and the first live frame (including synthetic hook frames) confirms it. The replay snapshot stamps such facts restoredUnconfirmed (mirroring the agent-status hydration stamp), and the renderer fact handler — the single choke point every reader's titles flow through — strips working indicators from an unconfirmed fact that asserts working, feeding readers a title that can never claim working until a live frame re-asserts it. Both title fields are cleared so the completion coordinator cannot arm on the restored raw title and later mint a false task-complete. Permission claims ("π !") keep signalling, the OMP static marker is rewritten through its protocol table ("π :" -> "π >"), and observed titles — park/unpark and worktree-switch replays — pass through untouched. The treatment is host agnostic: local-daemon and SSH PTYs share the tracker and fact handler, and remote clients run the same handler. Route: clearing only the record-title fallback (where normalizedTitle is null) would not have fixed the bug. The restore seed populates the tracker, so the working title is served from the non-null tracker branch (a runtime probe returned "⠋ π - Jenkins-Details" with a tracker entry present). Since normalizedTitle being null does not identify a cold restore, the fix takes the provenance route: explicit restoredUnconfirmed state plus reader-side treatment, never a blanket clear on replay. Accepted, as agreed: an agent genuinely mid-turn across a relaunch keeps its indicator quiet until the next live frame re-asserts it — the same treatment restored hook rows already receive (restoredUnconfirmed state is never fresh). The same applies to the residual same-run reclaim whose seed-only title asserts working (e.g. an OMP static working marker that never repaints): quiet until its next title change. A stale working title on a PTY that keeps producing output is still healed by main's 3s stale-title timer exactly as before. The checkpoint wire format is unchanged; the fact channel only gains an optional field. * fix(lint): split the six files the sync left over the max-lines cap Before: `pnpm exec oxlint` fails with six max-lines errors, so static analysis is red on nplez1/main and on every branch cut from it. The upstream sync re-seated this fork's rate-limits, status-bar, sidebar and identity patches onto upstream files that had grown in the meantime, and the combined results crossed the 300/400-line caps. After: the linter is clean again. Per repo rules there is no max-lines disable and no cap bump anywhere — each over-cap file yields one cohesive unit to a domain-named sibling module, and the behavior is unchanged: - service-full-cycle-preparation.ts: the copilot stored-token-vs-gh credential selection moves to service-copilot-cycle-credentials.ts; the three repeated settled-result promise adapters move (as trackSettledProviderResult) onto service-sibling-provider-result.ts - service-full-cycle-application.ts: the nine settled-result to error-snapshot fallback blocks collapse into providerResultSnapshot in service-provider-result-snapshot.ts, shapes preserved exactly, including opencode-go's explicit monthly slot - tooltip.tsx: the rate-limit window unit (window derivation, usage urgency colors, per-window bar) moves to provider-rate-limit-window-sections.tsx - use-status-bar-controller.ts: the usage visibility projection moves to status-bar-usage-projection.ts - tui-agent-config.ts: the per-agent authoring table moves to tui-agent-config-table.ts - worktree-activation.ts: the sidebar reveal group moves to worktree-activation-sidebar-reveal.ts Pure refactor: no behavior changes, no test edits.
nplez1
pushed a commit
that referenced
this pull request
Oct 19, 2026
…iew off the deprecated owner resolver (stablyai#30562) * refactor(renderer): fold browser creation, file actions and doc-preview callers off the deprecated owner resolver The creation-action policy and browser creation (#3, #48) fold together, so the provider the policy offers is the one creation uses. Browser panes, page lifecycle and conversion, tab duplication, the explorer tree pane, doc-preview actions and editor path moves (#21, #43, #45-#47, #49, #50) read runtimeEnvironmentIdWithLegacyFallbackUnlessTwin. File-explorer writes on a folder (#51) take the owner from the rows alone, never from the focused server. Deprecated importers 16 -> 6. * refactor(renderer): fold file preview off the deprecated owner resolver; Open in Orca Browser refuses a twin A project two hosts publish reads as no server for the doc-preview plan, but the file:// path now refuses it with its own message instead of opening this machine's copy (#7). Deprecated importers 6 -> 5. * fix(renderer): a twin's file preview and open-externally refuse instead of opening this machine's copy Codex P2 on the F7 folds: a folder twin read as no server, so the preview plan built a local file:// tab and open-externally handed the path to this machine's OS. Both now check the owner for ambiguity first. Also fix two test fixtures that broke the renderer typecheck. * refactor(renderer): move the file:// open target out of file-preview.ts file-preview.ts crossed its line limit after main grew it; the file:// target, its type and its two refusal messages move to workspace-file-browser-open-target.ts, which they never depended on file-preview for. * test: mock the replacement resolver in the quick-action availability suite (Codex P2)
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.
ELI5
After restarting the app, Orca could show a "working" spinner on worktrees whose agents were actually all done, then quietly correct itself a minute or two later. The app was mistaking an old saved terminal title — one caught mid-task by an automatic save — for proof that work was happening right now. Now Orca remembers where a title came from: a title restored from saved state can name the pane, but only titles observed live can claim the agent is working.
What Changed
Before: relaunching the app while every agent was idle could flip the sidebar worktree row, the worktree card, and the tab bar to "working" at the same time, for roughly 30 seconds to 2 minutes, then silently fall back to idle.
After: a restored title never reads as "working". As soon as a real title frame arrives from the terminal, the indicator shows exactly what the agent is doing again.
Mechanism: the terminal daemon checkpoints each session, including its last terminal title. Checkpoints are taken on a timer, so one can catch a mid-task spinner frame (real values from this machine:
⠧ π - Jenkins-Details,⠧ π - Remove-Team,⠴ π - orca). On relaunch that saved title was replayed to the UI as fresh state, and title normalization only standardizes spinner frames — it never removes them — so the restored title classified as "working" in every reader at once. We now track provenance in the title tracker: a title seeded from saved state is marked "restored, unconfirmed"; the first title observed live (real bytes or a hook frame) confirms it. The replayed title fact carries that stamp — mirroring the existingrestoredUnconfirmedstamp on restored agent-status rows — and the renderer fact handler, the single point every reader's titles flow through, strips working indicators from an unconfirmed working claim before any UI can see it. Permission titles (π !) keep signalling, the OMP static state marker is rewritten through its own protocol table (π :→π >), and observed titles (park/unpark, worktree-switch replays) pass through untouched.Why
Alternatives considered:
Provenance is the trust boundary the codebase already uses elsewhere: restored agent-status rows are stamped
restoredUnconfirmed(never fresh until a live update) and seeded status is markedlastAgentStatusObservedLive: false. Accepted tradeoff (also in the commit body): an agent genuinely mid-turn across a relaunch keeps its indicator quiet until its next live title frame — the same treatment restored hook rows already receive.Linked Issue
N/A — internal fix, no pre-existing issue.
Visual Proof
N/A — the change removes a transient wrong status after app relaunch (see Testing for the repro shape); no layout, styling, or interaction design change to capture.
Testing
Regression tests added where the harness runs them:
normalizeTerminalTitle/detectAgentStatusFromTitle/clearWorkingIndicatorson the three real checkpoint titles (normalized form still classifies working — the bug's root gap — cleared form reads idle), plus OMP marker rewrite and permission-marker preservation,Repro before the fix requires an app relaunch with a mid-turn checkpoint title, so no manual run was performed in this branch (the app was intentionally not relaunched). Verified by full test sweep of
src/main/runtime,src/shared, andsrc/renderer/src/components/terminal-pane(23,369 tests passing),pnpm tc,oxlint,oxfmt, andpnpm run check:code-quality:changed. Platform impact: the fix is host-agnostic — local-daemon and SSH PTYs share the same title tracker and fact handler, remote clients run the same handler code; the checkpoint wire format is unchanged.AI Disclosure
AI-assisted (pi coding agent).
Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
mobile/srcperforms no title-activity classification (status comes from hook rows, which keep their ownrestoredUnconfirmedgate), so no impact.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)