Skip to content

fix(desktop): preserve Swarm session activity during delegated work - #5495

Open
chinawch007 wants to merge 7 commits into
apache:mainfrom
chinawch007:fix/swarm-session-activity
Open

chinawch007 wants to merge 7 commits into
apache:mainfrom
chinawch007:fix/swarm-session-activity

Conversation

@chinawch007

@chinawch007 chinawch007 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Keep the parent Swarm session’s sidebar indicator active after it yields to folded child agents, through queued supervisor work and final summary generation. Show attention when child work needs input or is blocked, and clear the indicator once work settles.

Project Host-owned background activity into Owner and Guest catalogs without changing completed Turns or inventing running Turn IDs. Preserve progress during sustained catalog invalidation, clear stale activity after refresh failures, and isolate presentation failures from canonical interaction processing.

Fixes #5334

Verification

Current head: 88ff409fac89688fa4f988d0c407b28a3d39583f, rebased onto main at 0aa2707b5fb07c95bc5e3a21080ccb709e438acb.

After a clean rebuild:

  • Build, workspace typecheck, lint, format check, and Desktop/UI knip passed.
  • Desktop: 3,120 tests passed, excluding two Electron runtime tests described below.
  • UI: 694 tests passed.
  • Runtime graph/supervisor/reconciliation: 65 tests passed.
  • Runtime Host interaction, activity, catalog/protocol, WebSocket and two-client suites: 148 tests passed.
  • The production three-child Swarm yield/resume/final-summary test passed.
  • Protocol compatibility guard passed: 199 → 200.
  • Renderer architecture, build freshness, Windows test inventory, E2E budget and diff checks passed.

The rebase preserves the previously reviewed fixes and regression tests, including presentation-failure isolation originally carried by a merge commit.

Coverage limits: the Electron binary download stalled, so the two packaged-Electron cryptography tests in app-update-attestation were excluded; its six remaining tests passed. GUI E2E, packaged cross-platform checks, live cross-machine Guest transport, and the full Runtime/Runtime Host suites were not rerun in this update.

CI for this revision is currently running.

The UI evidence below uses the real sidebar component with a completed parent Turn and three running, folded children. The before case uses the previous catalog shape; the after case receives Host-owned background activity.

Before After

Breaking change

Bump the Runtime Host compatibility epoch from 199 to 200. Older closed catalog decoders reject the new field, so Hosts and Clients must use matching versions.

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Codex — implementation, regression tests, review fixes, rebase integration, validation and PR preparation. Maka (gpt-6-astra) — earlier merge conflict resolution, presentation-failure isolation, regression tests and validation. Retain the Generated-by attribution when squash-merging.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally; coverage limits are documented above

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

@github-actions github-actions Bot added the effort/XXL Over 2500 readable lines label Sep 18, 2026
@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch 2 times, most recently from 4f575f0 to 50255b3 Compare September 19, 2026 04:29
@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch 3 times, most recently from 573dffc to 717ec29 Compare September 24, 2026 03:36

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review summary

Current head: 717ec29b6895961e1b382005b717e138c147acd9

P2 — Owner catalog stale activity survives a failed TTL refresh

After an Owner catalog reaches an authoritative state, a failed TTL refresh does not revoke #catalogFresh. The stale Swarm backgroundActivity can therefore remain projected as running or waiting indefinitely in the UI.

Evidence:

  • apps/desktop/src/main/session-local-service.ts:278-293 refreshes after the five-second TTL but does not clear freshness first.
  • apps/desktop/src/main/session-local-service.ts:347-367 reports listSessions() failure without clearing #catalogFresh or emitting a cached fallback.
  • apps/desktop/src/renderer/session-status-presentation.ts:70-78 therefore does not remove the stale activity, and packages/ui/src/session-history-list.tsx:1723-1735 continues to project it.

The review reproduced this on the current head by starting with an authoritative running catalog, advancing past 6001 ms, and making subsequent listSessions() calls reject. The stale result remained authoritative and continued to report running. Please add a regression that asserts authoritative=false, localState=cached, and removal of backgroundActivity and runningTurnIds after a failed refresh, followed by recovery after a successful retry.

The reviewed change has no schema or migration changes. Hosted test is successful and the merge tree is clean. One local production Bash sandbox test could not run because this environment rejected unshare/bwrap; it was not attributed to the PR. Cross-process Guest disconnect/network-partition behavior and native Windows/macOS coverage were not exercised.

Automated review notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finding

P2 - Expired Owner catalogs remain authoritative after a refresh failure

catalog() continues to derive authoritative only from the current connection's existing #catalogFresh entry (apps/desktop/src/main/session-local-service.ts:278-293). When the five-second TTL expires, it starts a refresh but leaves that entry intact. If the current connection's listSessions() then rejects, the catch path only reports the error (apps/desktop/src/main/session-local-service.ts:347-352); it does not revoke freshness or notify consumers that the stored catalog is now cached.

This is reachable on the current head: seed an Owner catalog whose root session has backgroundActivity: 'running', advance the mocked clock by 6001 ms, and make subsequent listSessions() calls reject. Before, during, and after the failed refresh, repeated catalog() reads still return authoritative: true with backgroundActivity: 'running'. Persistent refresh failures therefore allow a finished or unreachable Swarm session to remain shown indefinitely as running or waiting. The renderer only strips background activity after the row has been marked cached (apps/desktop/src/renderer/session-status-presentation.ts:70-78), while the session list directly renders authoritative background activity (packages/ui/src/session-history-list.tsx:1723-1735).

Please revoke the current freshness generation when a TTL refresh fails, emit the cached state, and retry from unknown state. A regression test should start from an authoritative running catalog, fail the same-connection TTL refresh, assert authoritative: false, localState: 'cached', and cleared backgroundActivity/runningTurnIds, then verify that a successful retry restores authoritative state. The existing tests cover reconnect recovery failure and successful TTL refresh, but not this same-connection failure path.

Review scope: exact head 717ec29b6895961e1b382005b717e138c147acd9; current main b62ca805e58fcd8975620112c153c76ebe515099; merge-tree clean; hosted test successful. Focused Runtime, Desktop, UI, Host catalog/IPC, typecheck, lint, format, ASF-header, renderer-architecture, E2E-budget, and protocol-epoch checks passed. One unchanged production Bash sandbox test could not run in this environment because its managed sandbox boundary was unavailable; I did not attribute that failure to this PR. I did not run packaged Electron E2E or native Windows/macOS validation.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for catching this. Fixed in commit 0326988.
When a catalog refresh fails on the current Owner connection, we now revoke its freshness and notify consumers to reload the cached state. The catalog then returns authoritative: false and localState: 'cached', with backgroundActivity and runningTurnIds cleared from the returned rows.
We retain the previous state while a refresh is pending and emit the downgrade notification only once, preventing repeated failures from creating a notification/retry loop. A successful retry restores authoritative state.
Added regression tests for both running and waiting-for-user activity, covering repeated failures and successful recovery. Both tests failed before the fix and pass afterward. All 395 focused tests passed.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: 0326988cbdc71b5fcfb84463d6a583a6ffc5e52f

I found no new P0-P3 issue on this head. The previously reported P2 is fixed.

The current increment changes only session-local-service.ts and its tests. On a failed refresh for the current Owner connection, the service now deletes the partition's freshness entry and emits a change on the live-to-cached transition (apps/desktop/src/main/session-local-service.ts:348-374). Catalog consumers therefore receive authoritative: false cached rows, with stale execution activity removed. The added regression covers both running and waiting_for_user, verifies that repeated failures do not create a notification/retry loop, and verifies successful recovery (apps/desktop/src/main/__tests__/session-local.test.ts:468-526).

I compiled and ran those current tests against the exact parent 717ec29b6895961e1b382005b717e138c147acd9; both failed at the expected authoritative: true assertion. They pass on this head.

Validation on the exact head:

  • Desktop: 2757/2757 passed; UI: 643/643 passed.
  • Focused compiled session-local suite: 46/46 passed.
  • Full build, typecheck, lint, format, ASF-header, protocol-epoch, renderer-architecture, renderer stale-output, and E2E-budget checks passed.
  • Hosted test is successful.
  • The merge tree against current main 4d1e35898fa27bb32408a2bee7746e09ad2f85a9 is clean. A synthetic merge completed the full test build and the 46-test session-local suite successfully. The two latest main commits touch Runtime model-adapter/scheduled-task files and do not overlap this PR's file set.
  • No schema or migration changes are present.

Remaining coverage gaps: I did not run packaged Electron E2E or native Windows/macOS validation. The production Owner catalog failure path is covered by the focused service tests and the broader Desktop/UI suites.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 0326988 to 3673874 Compare September 24, 2026 17:51

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: 36738742d32284838e7c90c64ebc3caa07ea0611

I found no P0-P3 issue on this head. The previously reported stale Owner activity issue remains fixed.

This four-commit series derives process-local background activity from graph scheduling/claims, pending interactions, and supervisor wake delivery (packages/runtime/src/stream-graph-coordinator.ts:275-354, packages/runtime/src/agent-graph-supervisor-wake.ts:279-343, packages/runtime-host/src/server/session-background-activity.ts:22-57). Runtime Host publishes that strict protocol projection through session catalogs, and cached Guest/Owner projections strip activity once their live authority is lost (packages/runtime-host/src/protocol/session-catalog.ts:1015-1027, apps/desktop/src/shared/shared-session-catalog-projection.ts:48-53, apps/desktop/src/main/runtime-host-guest-session-mounts.ts:234-299). The renderer distinguishes background running, waiting, and blocked activity from a Session's own live turn (packages/ui/src/session-history-list.tsx:1683-1725). No database schema or migration changes are present.

The final commit revokes Owner catalog freshness after a current-connection refresh failure, emits the live-to-cached transition once, and immediately permits recovery without retaining stale execution activity (apps/desktop/src/main/session-local-service.ts:327-377). Its running and waiting_for_user regressions cover pending refresh, failure, repeated failure, no notification/retry loop, retained disk history, and successful recovery (apps/desktop/src/main/__tests__/session-local.test.ts:488-545). Both tests fail at the expected authoritative-state assertion when compiled and run against the exact parent 46a399e50aa6f24f2f31df02c729eb6e562e7410, and pass on this head.

Validation on this exact head:

  • Full Runtime: 3530 passed, 13 skipped; full Desktop: 2781 passed; full UI: 659 passed.
  • Focused Desktop/Guest/Owner/UI: 144 passed; graph/supervisor: 49 passed; Host protocol/catalog/activity: 95 passed; production Host Swarm/interaction: 4 passed.
  • Clean install and test build, typecheck, lint, format, ASF headers, protocol epoch guard and its 17 tests, renderer architecture, renderer build/staleness, and E2E budget checks passed.
  • Full Runtime Host: 2119 passed, 19 skipped, and one sandbox-boundary test failed because the local Linux sandbox could not establish the expected boundary. The same unchanged test fails at the same assertion on exact main 99cfeb7e94728dfece75e7a47677c82c7506bb39, so I did not attribute it to this PR.
  • Hosted test is successful. The PR is 4 commits ahead / 0 behind current main; git diff --check passes and the merge tree is clean.

Remaining coverage gaps: I did not run packaged Electron E2E, native Windows/macOS validation, or a live cross-machine Guest transport.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 3673874 to 40567bd Compare September 26, 2026 00:27

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: 40567bdb625992a7e3cbf26269c4f0a338468187

I found no P0-P3 issue on this head. The previously reported stale Owner activity issue remains fixed.

The four-commit series projects Swarm/graph activity, pending interactions, and supervisor wakes into the Host session catalog without making the projection a durable execution authority. Runtime derives activity from durable graph/claim state and current driver generations (packages/runtime/src/stream-graph-coordinator.ts:276-444, packages/runtime/src/agent-graph-supervisor-wake.ts:280-405). Desktop now revokes catalog freshness after a failed refresh for the same live connection, so cached rows lose backgroundActivity and runningTurnIds until a successful retry restores authority (apps/desktop/src/main/session-local-service.ts:327-377). The two regressions cover both running and waiting_for_user, including retry recovery (apps/desktop/src/main/__tests__/session-local.test.ts:488-545). No storage schema or migration changes are introduced.

The rebase preserves the final three patches equivalently. Its first patch only adapts the current-main session collaboration test helper and advances the Runtime Host compatibility epoch from main's 189 to 190 (packages/runtime-host/src/protocol/index.ts:106).

Validation on the exact head:

  • Clean npm ci and npm run build:test with Node 24.18.1.
  • Runtime: 3,530 passed / 13 skipped; Desktop: 2,761/2,761; UI: 660/660.
  • Runtime Host: 2,124 passed / 19 skipped / 1 failed. The sole managed-sandbox boundary failure was reproduced on exact current main at the same file, line, and assertion, so it is not attributable to this PR.
  • Focused activity-path suites: Runtime graph/supervisor 49/49; Desktop Owner/Guest/UI 167/167; relevant Runtime Host paths 180 passed with the same baseline sandbox failure.
  • Typecheck, lint, format, ASF headers, protocol epoch tests and guard, renderer architecture, renderer build/stale check, Windows test inventory, E2E budget, git diff --check, and hosted test passed.
  • Current main is 87fc9f69cd11648f31048c1b633bb813aca5f515; the PR is 4 commits ahead and 0 behind, and the merge tree is conflict-free.

Residual scope: I did not run packaged Electron E2E, native Windows/macOS validation, or a live cross-machine Guest transport test.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 40567bd to cf2ab03 Compare September 27, 2026 13:20

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: cf2ab03302db899788c8453ff2cbe726c4be2cc9

I found no substantiated P0-P3 issue on this head. The previously reported stale Owner activity issue remains fixed.

The session catalog keeps background activity authoritative only while the current connection owns a fresh catalog generation; after a same-connection refresh failure, it revokes freshness and emits the live-to-cached transition once (apps/desktop/src/main/session-local-service.ts:270-297,328-377). Cached rows therefore clear backgroundActivity and runningTurnIds. The regressions cover both running and waiting_for_user, repeated failure without a notification loop, retained disk history, and successful retry recovery (apps/desktop/src/main/__tests__/session-local.test.ts:488-545). Runtime Host compatibility is explicitly advanced to epoch 193 for the catalog projection (packages/runtime-host/src/protocol/index.ts:102-107). No storage schema or migration changes are introduced.

The final commit only aligns test fixtures with the current Runtime context; the production activity path and the Owner TTL fix remain intact. Validation on this exact head included a clean install and test build, Runtime 3,662 passed / 13 skipped, Desktop 2,842/2,842, UI 684/684, and focused activity-path tests 324 passed with one managed-sandbox boundary failure. Full Runtime Host produced 2,146 passed / 19 skipped / the same single failure; that unchanged assertion also fails on exact current main, so I did not attribute it to this PR. Typecheck, lint, format, ASF headers, protocol epoch, renderer architecture/build/staleness, Windows inventory, E2E budget, locale hygiene, and git diff --check passed. Hosted test is successful.

Current main is 18827d99c5704e185398b94d9d4e1ca666d2219f. The merge tree is conflict-free, and a synthetic current-main merge completed build:test plus 285 focused activity tests. The only main commit after the earlier synthetic validation changes unrelated interrupted-resume/UI files and has no path overlap with this PR.

Residual scope: I did not run packaged Electron E2E, native Windows/macOS validation, or a live cross-machine Guest transport test.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@hqhq1025

Copy link
Copy Markdown
Contributor

Freshness update after the review above: main advanced immediately afterward to fffc19fb5ead391e5c432c9d9961a50e20b22785 via #5741. The PR head is still cf2ab03302db899788c8453ff2cbe726c4be2cc9, but a fresh merge-tree now has content conflicts in packages/core/src/session.ts, Runtime Host catalog protocol/coordinator files, and adjacent catalog tests. The code findings in the exact-head review remain bound to that head, but the PR is not currently merge-ready; resolve the conflicts and revalidate the resulting head.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@me2seeks me2seeks left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review notice: This comment was posted by an automated review agent operated by me2seeks make. It is not an independent human review and does not replace one.

Summary

The issue is real: in base, the sidebar running indicator derives solely from session.runningTurnIds (packages/ui/src/session-history-list.tsx:1687-1691 on main), so when a Swarm parent's own Turn completes after yielding to folded children, the indicator dies while child work continues. The fix direction is right: the Host is the only authority on graph/supervisor activity, and projecting a Host-owned backgroundActivity through the catalog protocol (epoch 192→193), with live/cached downgrade on desktop owner and guest paths, fixes it at the correct layer. However, the PR is conflicting with main and one new presentation-only read now gates a canonical-continuity path.

Findings

  1. [P1] PR metadata — mergeable: "CONFLICTING", and the body's "Protocol compatibility guard 164 → 165" is stale: the diff actually bumps RUNTIME_HOST_COMPATIBILITY_EPOCH 192→193 (packages/runtime-host/src/protocol/index.ts:106). The branch predates current main around exactly the protocol-guard area this PR touches, so the claimed compat-guard verification no longer applies. Rebase and re-run the guard before merge.
  2. [P2] packages/runtime-host/src/server/execution-composition.ts:1083 — await interactionActivity.refresh(sessionId) is inserted ahead of continuityCoordinator.refreshCanonical(...) and sessionAdmission.detach(...) in the Interaction coordinator's canonical-refresh callback. The new SessionInteractionActivityProjection.refresh (session-interaction-activity.ts:47-49) performs two store reads; if either rejects, the callback now throws before canonical continuity refresh and admission detach run — a failure mode that did not exist on this path before, introduced by a presentation-only projection. Failure-isolate it (catch + report) so sidebar state can never gate canonical refresh.
  3. [P3] packages/runtime/src/agent-graph-supervisor-wake.ts:331-332 — notifyPermissionResponse clears #sessionAttention/#parkedWakeIds before #runTracked, which immediately publishes (:523); the settle then re-publishes. Each permission response produces a transient waiting_for_user → running → idle/waiting_for_user flip, and every transition fans out hostChanges.publishSessionCatalog to all clients. Suppress the intermediate publish.

Verdict

needs-changes — conflicting with main (stale epoch evidence in the body) and a non-isolated presentation read on the canonical-refresh path; core design and regression coverage are otherwise sound.

@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. We addressed P1 and P2 in 01c614fe2. For P3, we reviewed the state transitions and notification path, but do not think suppressing the intermediate publication is appropriate as proposed.

P1 — Merge conflicts and outdated compatibility information

We merged main at 542f04a4f and resolved the conflicts in the Session model, catalog coordinator, protocol epoch, and related tests.

The resolution preserves both sets of changes: main’s live-run epoch/Host-generation fields and this PR’s Host-owned background-activity projection. Since the merged main already uses compatibility epoch 196, we advanced this PR to 197, rather than retaining the previous branch value of 193.

We also updated the PR description to remove the stale 164 → 165 information and distinguish current validation from earlier results. The protocol compatibility guard passes against the merged main, and GitHub now reports the PR as conflict-free.

P2 — Isolate presentation failures from canonical interaction processing

Agreed. The newly added activity refresh could reject before canonical continuity refresh and WorkHub notification ran. On answer-processing paths, that could also prevent the subsequent application of an already-committed answer.

We fixed this at the SessionInteractionActivityProjection boundary:

  • Refresh failures are caught and reported, rather than propagated into canonical interaction processing.
  • Cached counts for the affected Session are invalidated on failure. Reads return unknown, rather than retaining stale pending-interaction counts or incorrectly reporting zero.
  • The cache invalidation notifies the activity observer when a cached projection actually existed; repeated read failures do not repeatedly invalidate an already-unknown projection.
  • Observer and diagnostic callback failures are also isolated.
  • A later successful refresh restores the projection.

The refresh remains awaited under the existing Session admission, preserving its ordering on the successful path. We did not move it into an untracked asynchronous operation or add a broad catch around canonical processing itself.

Three new regression tests cover failures from each of the two pending-request stores, plus observer/diagnostic failures. They verify that canonical publication, WorkHub notification, and answer application continue, and cover cache invalidation, repeated failures, and recovery. All three fail against the pre-fix implementation and pass with the fix.

P3 — Retain the intermediate running publication

We agree that unnecessary catalog notifications are worth avoiding. However, we do not currently see sufficient evidence that this transition is incorrect, for the following reasons.

1. running represents real work after the permission response.

After the user answers, the coordinator reconciles the wake: it acquires the activity lease, inspects unsettled attempts, and may schedule a supervisor retry. That work is asynchronous and is not guaranteed to complete immediately.

A transition such as:

waiting_for_user → running → idle

can therefore accurately mean “waiting for an answer → processing/resuming after the answer → finished.” Its short duration alone does not make it a spurious state.

2. Suppressing the publication can leave clients displaying stale attention.

If the coordinator changes to running internally but does not publish that change, catalog-only clients may continue displaying waiting_for_user until a later notification, even though the user has already answered and reconciliation is underway.

Keeping the internal attention state unchanged instead would introduce a different problem: it would mask actual in-flight work. Either approach needs a more specific justification than eliminating an intermediate transition.

3. The notification path already has two levels of deduplication.

The supervisor publishes only when its activity value changes. The Host’s combined graph/supervisor projection then publishes a catalog invalidation only when the aggregate activity changes.

For example, if graph activity remains waiting_for_user, a supervisor transition to running does not change the aggregate projection and does not generate a catalog invalidation through this path. Similarly, a permission response received while the effective state is already running need not produce a new running-state notification.

Consequently, a permission response does not necessarily produce every state transition or client notification described in P3.

4. This is an intentional existing contract, not merely incidental behavior.

The parked-wake retry regression explicitly checks that activity becomes running immediately after notifyPermissionResponse(), then returns to idle after processing completes. That test is not, by itself, proof that the design cannot improve, but it documents the intended visibility of reconciliation work.

For these reasons, we have left P3 unchanged. If there is a concrete scenario demonstrating redundant aggregate catalog invalidations or visible flicker, we would be happy to evaluate targeted notification coalescing while preserving the visibility of legitimate running work.

Validation

The CI run for the exact updated head completed successfully, including workspace and Runtime Host tests, Desktop E2E, Storybook smoke, transcript geometry checks, and CLI release-candidate validation. Local rerun results and their caveats are documented separately in the PR description.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: 01c614fe25177b8fa897300f066e7311e603534e

I found no substantiated P0-P3 issue on this head. The previous merge conflict and the presentation-read gating issue are resolved, and the earlier Owner catalog freshness fix remains intact.

The merge resolution preserves both current-main run ordering (runEpoch and runHostGeneration) and this PR's Host-owned backgroundActivity projection in the Session contract and catalog coordinator (packages/core/src/session.ts:407-430, packages/runtime-host/src/server/session-catalog-coordinator.ts). Compatibility is advanced to epoch 197 with the intervening epoch history retained (packages/runtime-host/src/protocol/index.ts:102-116).

The new interaction projection isolates both source-read failures and observer/diagnostic failures. A failed read invalidates cached counts to unknown, but cannot stop canonical continuity refresh, WorkHub notification, or answer application (packages/runtime-host/src/server/session-interaction-activity.ts:45-68, packages/runtime-host/src/server/execution-composition.ts:1067-1094). The new regressions exercise failures from both interaction sources, repeated failure, recovery, and throwing observers (packages/runtime-host/src/__tests__/interaction-coordinator.test.ts:209-315). I also checked the production call graph: these refreshes are reached through the per-Session admission coordinator, so the projection's lack of a separate async generation does not create a reachable stale-overwrite race in the current composition.

The Owner path still revokes catalog freshness after a failed current-connection refresh, clears cached execution activity, emits the live-to-cached transition once, and permits recovery (apps/desktop/src/main/session-local-service.ts:270-297,328-377). No storage schema or migration changes are introduced.

Validation on this exact head with Node 24.18.1:

  • Clean install and build:test passed.
  • Runtime: 3,660 passed / 13 skipped; Desktop: 2,852/2,852; UI: 681/681.
  • Runtime Host: 2,160 passed / 19 skipped / 1 failed. The sole managed Bash sandbox-boundary failure reproduced independently on exact base/main 542f04a4f328de7a49b96e5146f391fb923fa369 at the same assertion, so I did not attribute it to this PR.
  • Focused current-head suites passed: Runtime Host activity/catalog/protocol 148/148, Runtime graph/supervisor 49/49, Desktop Owner/Guest/rail projection 134/134, and the production three-child Swarm activity scenario 1/1.
  • Typecheck, lint, format, ASF headers, model metadata, renderer architecture, renderer production build/staleness, Windows test inventory, E2E budget, TUI copy, locale hygiene, and git diff --check passed.
  • Hosted test is successful. Current main remains 542f04a4f328de7a49b96e5146f391fb923fa369; the PR is 6 commits ahead / 0 behind and the merge tree is conflict-free.

Residual scope: I did not run packaged Electron E2E, native Windows/macOS validation, or a live cross-machine Guest transport test.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent second review of head 01c614fe (a different model lineage from the parallel review). This head is the merge of the previously reviewed cf2ab033 with main 542f04a4. No P0/P1 found; one P2 is worth addressing.

What was checked:

  • The conflict resolution with #5741 is correct. backgroundActivity sits alongside runEpoch/runHostGeneration, argument order is right on both the get and list paths, and the compat epoch goes 196 → 197 with the comment chain intact.
  • The new failure isolation works. An interaction-read failure, or a throwing observer or diagnostic callback, no longer blocks the canonical answer commit or the WorkHub notification. The three new tests cover failure, repeated failure and recovery.

P2: the Owner catalog refresh has no progress guarantee under sustained invalidation (apps/desktop/src/main/session-local-service.ts:343, :366-370).

  • Any invalidation that arrives during listSessions() discards the whole result and issues another read.
  • Each invalidation also demotes rows to cached, which drops the running and background-activity indicators.
  • In a Swarm with several delegated children emitting frequent events, and a full-catalog round trip longer than the gap between events (likely with a remote Host), every read is invalidated before it lands. The sidebar then never shows the running indicator, which is exactly during the delegated work this PR is meant to cover.
  • The previous code saved the result regardless of invalidation. This is derived from the code; real event rates weren't measured.
  • Consider saving a result that is fresher than the current cached one even if invalidated, and then re-reading. Alternatively, cap the discard-and-retry count.

P3 (non-blocking):

  • Refreshes above the concurrency cap are silently dropped. The global "at most 2 catalog reads" cap (session-local-service.ts:332) drops a refresh when three or more Owner partitions refresh together. That partition stays cached until something else triggers a read.
  • Some waiting states don't survive a Host restart (partly unverified). Two cases show the parent as idle first:
    • a supervisor wake waiting on a permission (agent-graph-supervisor-wake.ts:347-358 only recovers retryable wakes);
    • the in-memory interaction counts, which are lost until the next interaction event.
  • Some "needs attention" states have no way out. Supervisor wake errors (agent-graph-supervisor-wake.ts:316/339/481) and graph dispatch failures (stream-graph-coordinator.ts:284) keep the state until the next event, with no matching action in the UI.
  • The coordinator keeps a shadow copy of graph state. stream-graph-coordinator.ts:177-192 maintains a graph-state model with 8+ update sites, parallel to the read model's derivation, plus three layers of publish de-duplication. It's correct today but easy to drift.
  • Background-activity changes rely on an undocumented ordering assumption. They don't bump the revision or run epoch, so they aren't protected by #5741's ordering. Correctness relies on serialization in the Desktop main process, which isn't documented.

Tests run:

  • npm run build:test passes.
  • Runtime and runtime-host suites: 236/237 pass. The one failure, the sandbox-boundary Bash test, also fails on an unrelated worktree on this machine, so it's environmental.
  • Desktop: 168/168 pass.
  • A temporary trace on the happy path shows running → idle with no idle or blocked flicker.

Not verified: renderer typecheck, lint and E2E locally (hosted CI is green); real Swarm event rates for the P2; child pending-interaction recovery after restart.

The AI-use disclosure and the Generated-by trailers (Codex; Maka gpt-6-astra) are consistent.


Automated review (Claude lineage) by the Qronos review line on behalf of @Astro-Han; key claims were checked against the code, but please verify before acting.

.then((sessions) => {
if (!this.#current(target)) return;
if (!this.#currentCatalogConnection(target, connection)) return;
if (connection.invalidationVersion !== invalidationVersion) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: any invalidation during the read discards the whole result, and finally re-reads. Under a steady stream of Swarm child events, with a round trip longer than the gap between events, this never lands, so the sidebar stays cached and loses the running indicator during delegated work. Consider saving the result when it is fresher than the current cached one and then re-reading, or bounding the retries.

@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 01c614f to a889be9 Compare September 29, 2026 16:48
@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. I reproduced the P2 and have addressed it in a889be974.

Ordinary Host notifications now mark the Owner catalog as needing refresh without revoking the last successful observation from the current connection. A successful full-catalog read is published even when more notifications arrived during the read; those notifications coalesce into one trailing read. This allows progress under sustained invalidation and convergence once the notifications stop. It deliberately allows an observation to lag a concurrent Host change while the next refresh is pending.

Connection/Host-generation changes, authority removal, and local-store mutation fences remain in place. Read failures still revoke live activity and notify the live-to-cached transition, including when a trailing retry is needed. The earlier failed-TTL-refresh fix is preserved.

The regression test injects multiple invalidations during each of eight consecutive reads, checks that every successful observation is published, and checks that refreshing stops after the final clean read. It fails on the previous implementation. Tests also cover coalescing, failure notification, local deletion protection, connection replacement and TTL failure/recovery.

For the five non-blocking P3 items, I would prefer to keep them outside this update:

  • Refresh concurrency cap: This is a real limitation of the existing Owner catalog scheduler and predates this PR. A fair pending-partition queue should be a separate scheduler change. This P2 fix does not address starvation caused by the global cap.
  • Restart recovery: The wake recovery path processes unsettled wakes before retryable wakes, including the waiting-permission case. Interaction restart recovery also settles orphaned pending requests and refreshes the activity projection. Losing the in-memory counts alone therefore does not establish that a still-valid wait is permanently lost. I have not verified every restart timing window, so I am leaving a broader recovery audit out of this indicator change.
  • Needs-attention recovery/UX: The indicator projects existing execution conditions. Coordinator exceptions have existing error/drain handling, and graph state has reconciliation paths; introducing a separate UI action for every failure would expand this into a recovery-workflow change. I am deferring that work rather than claiming every blocked state already has a sufficient user-facing exit.
  • Shadow graph state: I agree that the cached activity projection adds maintenance cost. The current tests exercise dispatch, completion, pending interactions and recovery, and the review identifies no current divergence. I would keep consolidation with the graph read model as a separate refactor instead of combining it with the refresh-liveness fix.
  • Independent background-activity ordering: Agreed that neither the Session revision nor the run epoch independently orders this field. The P2 fix provides progress for full Owner refreshes; it does not establish a total order across targeted getSession and full-list observations. I am not relying on an assertion that the Desktop main process serializes all of those paths. Adding an activity revision and defining its merge semantics across these paths is deferred as a separate catalog-contract change. The status may transiently lag; this update does not claim to eliminate every cross-path regression.

I also rebased onto current main, retained its supervisor-wake exhaustion behavior, and advanced the existing protocol compatibility epoch from 198 to 199 for this PR's activity field. This does not add an independent activity-ordering version.

Validation: 302 relevant Desktop, Runtime and Runtime Host tests passed on the final build. npm run build, npm run typecheck, npm run lint, npm run format:check, both Desktop/UI knip checks, and the protocol epoch guard passed. GUI E2E was not run; the Electron binary download failed in this environment.

This update and reply were prepared with OpenAI Codex; the implementation commits include Generated-by attribution.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review result

Exact head: a889be974ef8004b4a6e1afea89622f0952e170a

I found no remaining P0-P3 issue on this head. The previously reported Owner catalog liveness P2 is fixed.

Host catalog changes now increment the live connection's invalidation generation without revoking its last successful observation (apps/desktop/src/main/session-local-service.ts:158-173). A successful in-flight read is published even when newer invalidations arrived, while the captured generation keeps that observation dirty and the finally path coalesces all intervening changes into one trailing refresh (apps/desktop/src/main/session-local-service.ts:275-303,335-385). Connection replacement and local-store revision fences still reject reads that could cross authority or restore a locally removed Session (apps/desktop/src/main/session-local-service.ts:307-333,347-356). Failed refreshes still revoke authority and notify consumers before a dirty trailing retry.

The new regressions cover successful, removed, and failed dirty observations, continuous invalidations, final convergence, and local deletion protection (apps/desktop/src/main/__tests__/session-local.test.ts:361-460,693-718). As a discrimination check, I temporarily restored the old "discard a result when its invalidation version changed" guard: the focused suite then failed three of these cases, including the continuous-invalidation progress assertion. After restoring the exact head, the focused suite passed 49/49.

Validation on this exact head with Node 24.18.1:

  • Clean npm ci and npm run build:test passed.
  • Desktop passed 2,884/2,884; the focused session-local suite passed 49/49.
  • Desktop typecheck, lint, format, ASF headers, model metadata, renderer architecture (112/112), renderer production build/staleness, Windows test inventory, E2E budget, and git diff --check passed.
  • Hosted test is successful.
  • Current main is b39330436311f54c0512c06aabf0be4525bba645; the PR is 7 commits ahead / 2 behind and the merge tree is conflict-free. A synthetic merge with current main completed a fresh install, build:test, the focused suite 49/49, and the full Desktop suite 2,885/2,885.

The PR description still names revision 01c614fe2 and compatibility epoch 196 to 197, while the current series reaches epoch 199; please refresh that metadata before merge.

Residual scope: I did not run packaged Electron E2E, native Windows/macOS validation, or a live cross-machine Guest transport test.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from a889be9 to 88ff409 Compare September 30, 2026 05:44
@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for taking another look. For context, these points have already been discussed in the earlier review rounds, and I’ve replied to them individually:

  • The stale activity after a failed Owner catalog refresh was fixed and documented in this reply.
  • The merge conflicts and presentation-read failure isolation were addressed in this reply. That reply also explains why I retained the intermediate running publication after a permission response.
  • The sustained-invalidation P2 was fixed in the subsequent update. This reply also addresses all five non-blocking P3 observations individually, including the remaining limitations and why I’m keeping the broader changes outside this PR.
    I’ve now rebased onto main at 0aa2707, resolved the conflicts, and retained all previously reviewed fixes and regression tests. The current head is 88ff409, and the PR description now reflects the correct compatibility epoch, 199 → 200, and the latest validation results.
    Local checks passed within the documented coverage limits; CI for the new head is still running. If there is a remaining concern that the earlier replies do not address, please point me to the specific scenario so I can follow up.

@hqhq1025 hqhq1025 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current head retains the Host-owned background activity projection, Owner catalog refresh handling, and the newly restored isolation of interaction-activity read and observer failures from canonical answer processing. I found no separate defect in those paths, but this revision is not merge-ready against current main.

[P1] main now uses compatibility epoch 200 for Agent Graph operator output, while this branch independently uses 200 for Session catalog background activity. A three-way merge conflicts in protocol/index.ts. Resolving the text conflict while keeping 200 would let peers with different wire contracts pass the same epoch handshake; the merge-result protocol guard would also reject an incompatible protocol change without an epoch advance. Rebase on current main, move the epoch beyond its current value, retain both compatibility notes, and rerun the merged guard and affected tests.

The hosted test run for this exact head succeeded, and local protocol-epoch guard tests passed (17/17). git merge-tree against current main fails at the epoch declaration. I did not rerun the full Desktop/Runtime suites, packaged Electron, native Windows/macOS, or cross-machine Guest transport.

Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.

// Increment when the same protocol version no longer guarantees safe Client-Host
// interoperability. Mismatches are rejected before domain commands are admitted.
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 199 as const;
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 200 as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Current main already assigns epoch 200 to Agent Graph operator output, but this branch assigns 200 to a different strict catalog field. The merge conflicts here. Keeping 200 after conflict resolution would advertise incompatible wire contracts under the same handshake epoch. Please rebase and advance beyond the current base epoch, preserving both compatibility notes, then run the merge-result epoch guard.

Project graph and supervisor wake activity into session catalogs so the
session list stays active after the parent yields and until its summary
finishes. Preserve logical Turn completion and pending interaction states.

Invalidate cached activity across Owner and Guest reconnects, retry failed
Guest catalog refreshes, and provide a manual recovery action. Cover graph
lifecycle, interaction, reconnect, sidebar, and two-client protocol behavior.

Generated-by: Codex
Surface failed graph projections after child execution settles. Fence Owner catalog responses against same-connection invalidations and issue a trailing read for newer state.

Add regression coverage for terminal projection failures and superseded Owner catalog results. Validated with 304 related tests, workspace builds, Desktop typechecking, and targeted lint.

Generated-by: Codex
Update remote catalog assertions for the Host-owned idle projection on created and renamed Sessions.

Generated-by: Codex
Downgrade failed same-connection TTL refreshes to cached state and notify consumers once. Cover running and waiting activity, repeated failures, and successful recovery.

Generated-by: Codex
Treat Host catalog notifications as refresh demand without revoking a live connection's last successful observation. Publish successful in-flight reads and coalesce intervening notifications into one trailing read, so sustained activity cannot indefinitely suppress catalog progress.

Retain connection, authority, and local store mutation fences. Publish failed-refresh authority loss before scheduling a trailing retry. Cover continuous invalidations, convergence, failure notification, and local deletion protection.

Generated-by: Codex
Retain the activity projection failure isolation and its regression tests
from the previous merge commit when replaying the branch on current main.
Keep the supervisor coordinator-failure test independent of exhausted
provider retries and retain the reviewed catalog field layout.

Generated-by: Maka (gpt-6-astra)
Generated-by: Codex
@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 88ff409 to cae4a93 Compare October 1, 2026 05:47

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of exact head cae4a93ec842f053422aad615708ff18bbba02e2.

What changed: The Host projects a per-root backgroundActivity (idle/running/waiting_for_user/blocked) from three sources: Agent Graph coordinator facts (schedule, claims, operator projections), supervisor wake delivery, and canonical pending interactions. It publishes the value through the Owner and Guest Session catalogs, so the sidebar keeps a parent Swarm session marked active after its own Turn yields to folded children. Owner and Guest desktop paths strip the field once they lose live authority. The compatibility epoch moves 202 -> 203. There are no storage schema or migration changes; all activity state is in process memory.

Scope checked:

  • The branch is rebased directly on current main 1e80e3b88 (0 behind) and git merge-tree is clean. scripts/protocol-epoch-check.mjs --base origin/main passes (202 -> 203).
  • Prior findings:
    • Fixed and still present after the rebase: the stale Owner activity after a failed TTL refresh, the merge conflict and epoch-200 collision with main, the presentation read gating canonical refresh (session-interaction-activity.ts:45-63 isolates read, observer and diagnostic failures), and the Owner refresh liveness under sustained invalidation (invalidationVersion plus a trailing refresh).
    • Still open: the earlier non-blocking P3s (transient flip on permission response, waiting-permission attention not recovered after a Host restart, unordered same-revision activity updates). I found nothing that raises their severity.
  • I reviewed the activity derivation for delegated work and child sessions: epoch handover via #currentDrivers/#activityEpochs, child-to-driver mapping, the stop, close and failure paths, and the supervisor version fencing in #runTracked. I found no new correctness defect there.

Findings:

  • P3: Epoch 203 is also claimed by open #5753, so whichever PR merges second must move to 204.
  • P3: A trailing Owner refresh can be dropped by the existing two-read cap. The row then stays authoritative with stale activity.
  • The PR description is stale: it still cites head 88ff409f and epoch 199 -> 200.

Validation (Node, clean npm ci + patches + build:test):

  • Focused Runtime graph/supervisor, Runtime Host interaction/activity/catalog/protocol/UDS/WebSocket/execution-composition, and Desktop session-local/Guest mounts/rail/projection/IPC/preload/join-dialog/startup suites: 413/414 pass. The one failure is the production Bash sandbox-boundary test. Its test body is unchanged from main, and this environment cannot establish that sandbox.
  • UI: 679/679 pass.

Not exercised: full Runtime, Runtime Host and Desktop suites; packaged Electron E2E; native Windows/macOS; live cross-machine Guest transport.

Automated review notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

// Increment when the same protocol version no longer guarantees safe Client-Host
// interoperability. Mismatches are rejected before domain commands are admitted.
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 202 as const;
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 203 as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 (merge coordination): Epoch 203 is also claimed by open #5753 (fix(transcript): recover from oversized invocations, head 4ee3709d, whose line 108 reads // 203: Transcript omission notes ...). Current main is 202, so either PR is correct on its own. Once one lands, though, the other must move to 204 and keep both history lines. If a conflict resolver keeps 203 for both, peers with different strict catalog/transcript decoders would pass the same epoch handshake. Please coordinate the merge order, and rebase and bump if #5753 lands first.

this.deps.changed(target.scope);
if (connection.invalidationVersion !== invalidationVersion) {
// Consume all changes received during this read with one request.
this.#refreshCatalog(target, connection);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This trailing refresh still goes through the global #catalogTasks.size >= 2 cap (line 339), which can return without scheduling anything. Before this PR, a Host change deleted #catalogFresh, so a dropped refresh left the row cached with activity stripped. Now changed() only bumps invalidationVersion, and catalog() decides authoritative from fresh.connection === connection alone. A dropped trailing refresh therefore keeps the last observation (for example backgroundActivity: 'running' after children have finished) authoritative until another renderer catalog() call finds a free slot. This needs at least three Owner partitions refreshing at once, so it is rare. Consider exempting trailing refreshes from the cap or rescheduling them, or treat fresh.invalidationVersion !== connection.invalidationVersion as non-authoritative once the read has settled.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/XXL Over 2500 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(esktop): swarm session appears idle while child agents are still running

4 participants