Skip to content

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

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

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

Conversation

@chinawch007

@chinawch007 chinawch007 commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Keep a parent Swarm session's sidebar indicator active after its own Turn yields to folded children, through supervisor work and final summary generation. Show attention when child work needs input or is blocked, and clear activity when work settles.

Project Host-owned activity through Owner and Guest catalogs. Owner refreshes use a deduplicated FIFO queue with the existing two-read limit and partition-local mutation fences, so a fast Host cannot keep discarding another Host's successful observations. Activity versions are per Session and Host generation, merge independently of Session revisions and Turn epochs, and retain their clocks through idle. Cached rows discard both activity and its ordering authority. Presentation failures remain isolated from canonical answer processing.

Fixes #5334

Verification

Head: fa11c8f1c, rebased onto official main at 74094c632. Local toolchain: Node 24.18.0 and npm 11.16.0.

  • Full build, workspace typecheck, lint, format check, and Desktop/UI knip passed.
  • Desktop: 3,343 tests passed. Two packaged Electron cryptography tests were excluded because the Electron binary was unavailable.
  • UI: all 713 tests passed.
  • Targeted Runtime and Runtime Host graph, supervisor, interaction, activity, catalog, protocol, UDS, WebSocket, and production execution suites: all 263 tests passed, including the production three-child Swarm yield/resume/final-summary case. Initial sandbox runs could not bind local listeners or access the owner-lock registry; the same suites passed with the required local permissions.
  • Three new behavioral regressions fail with the pre-follow-up production implementations and pass after the fix: continuous cross-partition refresh progress, per-Session activity-version isolation, and authorized Guest session.shared.query isolation. Coverage also checks mutation fences and monotonic clocks through idle, purge, and authority replacement.
  • Protocol epoch guard passed against current main (208 → 212); all 17 guard tests passed. Range-diff preserved the prior patches apart from the intended epoch/history resolution. Renderer architecture, build freshness, Windows test inventory, E2E budget, and diff checks passed.

GUI E2E, packaged cross-platform checks, live cross-machine Guest transport, and the full Runtime/Runtime Host suites were not run. These are local results; hosted CI for this head must be checked before merge.

The existing component screenshots below show a completed parent Turn with three running, folded children. They were captured in an earlier revision: the before case uses the previous catalog shape; the after case receives Host-owned background activity.

Before After

Breaking change

Advance Runtime Host compatibility from main's 208 to 212. One history entry covers backgroundActivity and backgroundActivityVersion; main's usage-query compatibility note is retained. Older closed catalog decoders reject the new fields, so Hosts and Clients need matching epochs. Rerun the guard against the merge-time main.

Each Session retains an activity counter for the current Host generation once its activity changes, including while idle. This prevents a late running response from outranking newer idle activity. The new field no longer exposes other Sessions' activity transitions; the existing Host-wide catalog-event revision remains unchanged.

AI use

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

Tool(s) and scope: Codex — implementation, reviewer follow-ups, rebase integration, regression tests, validation, and PR preparation. Maka (gpt-6-astra) — earlier integration, 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; validation limits are listed 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.

@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.

@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for the follow-up review. The refresh scheduling and activity ordering issues are addressed in 5fb3881b0, after rebasing onto main at 49e01b157.

Refresh concurrency and stale authority

I reproduced the stale-authority concern with a third Owner partition while two catalog reads occupied the available slots. The existing trailing path releases its own slot before synchronously requesting another read, so that was not the dropped-request case I could reproduce. A new refresh for another partition could be dropped, leaving its last activity authoritative until another renderer read.

Refreshes now enter a deduplicated FIFO queue while retaining the two-read limit. A released slot admits a waiting partition automatically, ahead of a busy partition's next trailing read. Reads fenced by a local store revision change also requeue; queued entries are retired with their connection or authority. This preserves successful observations during continuous invalidation and convergence after events stop.

Regression tests cover the stale third partition without an extra renderer read, fairness against trailing work, bounded concurrency, local mutation fences, and connection replacement.

Independent activity ordering

This follow-up also addresses the previously deferred ordering issue. The Host now projects backgroundActivityVersion with its process generation and a monotonic activity revision. The renderer merges activity independently of the Session revision and Turn epoch, so a late list or targeted-row response cannot replace a newer idle with an older running, or discard a newer rename or Turn update.

Both Owner and Guest paths carry the version and strip it with cached activity. Revisions are compared only within one Host generation; a restarted Host can start from a lower counter. Tests cover both response orders, independent metadata/Turn updates, cache downgrade, and generation changes.

The seven new queue/order regression scenarios all fail against the implementation before this follow-up and pass after it.

Compatibility and the other observations

Current main has advanced to epoch 206. This branch now uses 208, preserving the main history and distinguishing the activity field from the subsequent ordering-field change. The guard passes against current main (206 → 208) and for the follow-up commit (207 → 208). The merge result will still need the guard against whatever main contains at merge time, including changes from #5753.

I retained the permission-response running transition because it represents asynchronous reconciliation; aggregate activity publication is already deduplicated. The restart path also processes persisted waiting_permission wakes before retryable delivery, so loss of the in-memory attention map alone does not establish a permanently lost wait. This update leaves those semantics intact rather than suppressing the transition or adding a second recovery mechanism.

Validation

Full build, workspace typecheck, lint, format check, both Desktop/UI knip checks, and the protocol guard passed. Desktop passed 3,337 tests, excluding the two packaged Electron cryptography tests because the binary was unavailable; UI passed 713. All 261 targeted Runtime and Runtime Host tests passed, including the production three-child Swarm yield/resume/final-summary case. Three UDS startup tests timed out during parallel execution and passed when rerun in isolation.

Renderer architecture, build freshness, Windows test inventory, E2E budget and diff checks passed. GUI E2E, packaged cross-platform checks, live cross-machine Guest transport and the full Runtime/Runtime Host suites were not run in this update.

This implementation and reply were prepared with OpenAI Codex; the commit includes Generated-by attribution.

@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 notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

Incremental review from the last reviewed head cae4a93e to 5fb3881b0cd7c2be366fc913ee854ab0f3020576.

Delta. Commits 1-7 were rebased from 1e80e3b8 to 49e01b15. Per git range-diff, the only changes to them are rebase context (the #isTurnBusy and groupingHeading neighbours) and the epoch renumbering. The new work is all in 5fb3881b0:

  • Owner catalog refreshes go through a deduplicated FIFO queue (#catalogPending) that keeps the two-read limit. Fenced reads are requeued.
  • The Host projects backgroundActivityVersion (hostGeneration plus a Host-wide monotonic revision). The renderer merges activity separately from the Session revision and Turn epoch.

Prior findings

  • P3, trailing Owner refresh dropped by the two-read limit (session-local-service.ts): fixed. A request that hits a full limit is now queued instead of dropped. Every task finally, connection change and authority removal drains the queue. A fenced or invalidated read re-enters at the tail, so waiting partitions go first. Tests cover the third-partition, fairness, local-fence and connection-replacement cases.
  • P3, unordered same-revision activity updates: addressed by backgroundActivityVersion and reconcileSummary. The comparison is scoped to one Host generation, and cached rows strip the version with the activity on the Owner, Guest and display paths.
  • P3, epoch 203 collision with #5753: obsolete. This PR no longer uses 203.
  • P3s the author declined (transient running on a permission response, waiting-permission attention after a Host restart): still unchanged. The author's reasoning in the follow-up reply is acceptable for non-blocking items.

Findings

  • P3: merge conflict and epoch collision; the branch must be rebased before it can land. git merge-tree --write-tree origin/main reports a content conflict in packages/runtime-host/src/protocol/index.ts, and GitHub shows CONFLICTING/DIRTY. The PR claims two epochs, 207 (activity field) and 208 (activity version). Current main is already at 208 from #5709, and open #3700 also claims 207. scripts/protocol-epoch-check.mjs --base origin/main fails with "still 208, the current base parent's value". 209 (#5548) and 210 (#5969) are claimed by open PRs, and 211 is suggested for #5980. Neither field is on main yet, so the two bumps can collapse into one: rebase onto main and take the next free epoch (212 if the others hold), with one history line covering both backgroundActivity and backgroundActivityVersion.
  • P3 (unreproduced): a fenced read can be starved across partitions. The fence at session-local-service.ts:361/369 compares SessionLocalStore.revision, and every partition's saveCatalog bumps that one global counter. Suppose one partition gets frequent Host invalidations and has fast reads, and another has slower reads. Each successful save from the first partition fences the slower read in flight, which then requeues. The slow partition can keep its last authoritative observation, possibly a stale running, until the busy partition quiets down. The new requeue is an improvement: at cae4a93e the fenced read was dropped. The residual comes from the fence being global when the hazard it guards (a local create/remove racing a read) is per partition. A per-partition revision would remove this. I did not reproduce it.
  • P3 (observation): the activity revision is Host-wide and visible to Guests. SessionBackgroundActivityProjection.#revision advances on every activity transition of any Session. It is exported in SharedSessionCatalogProjection, so a Guest sees the version and can infer the rate of activity changes in Sessions not shared with it. The leak is small, but a per-Session counter (or one kept only while a Session is non-idle) would avoid it.
  • The PR description is still stale. It cites head 88ff409f and epoch 199 -> 200.

CI and mergeability. The test check passes on 5fb3881b. Mergeability is CONFLICTING (see the epoch finding).

Validation. Clean npm ci --ignore-scripts, dependency patches and build:test on 5fb3881b. Focused suites:

  • Desktop session-local, session-catalog-* and Guest mounts: 140/140 pass.
  • Runtime Host session-catalog-protocol, session-background-activity, session-catalog-coordinator, authenticated-websocket and session-catalog-two-client-uds: 140/140 pass.

Not run: full suites, UI, Electron E2E, Windows/macOS, live Guest transport.

// 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 = 206 as const;
export const RUNTIME_HOST_COMPATIBILITY_EPOCH = 208 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: This conflicts with main, which is already at 208 (#5709), and open #3700 claims 207. protocol-epoch-check --base origin/main fails. Neither field has landed, so please collapse 207 and 208 into one bump at the next free epoch after rebasing. 209, 210 and 211 are claimed or suggested by #5548, #5969 and #5980, so that is 212 if those hold. Keep one history line covering both backgroundActivity and backgroundActivityVersion.

if (!this.#currentCatalogConnection(target, connection)) return;
// A late catalog cannot erase a Session created/removed while it read.
if (this.store.revision !== revision) return;
if (this.store.revision !== revision) {

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 (unreproduced): store.revision is global, and every partition's saveCatalog bumps it. A partition with frequent invalidations and fast reads can therefore keep fencing a slower partition's read in flight. The slow partition requeues each time and keeps its last authoritative activity until the busy one goes quiet. The requeue is already better than dropping the read. A per-partition revision would scope the fence to the local create/remove race it is meant to guard.

if (previous === next) return;
// One Host-wide clock orders observations even after an idle Session is
// removed from the deduplication map, without retaining per-Session clocks.
this.#revision += 1;

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 (observation): This counter is Host-wide and is published in the shared (Guest) catalog projection. A Guest can therefore watch it advance for activity changes in Sessions it cannot see. The leak is small. A per-Session revision, or one kept only while a Session is non-idle, would avoid it.

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
Queue Owner catalog refreshes fairly behind the existing two-read limit,
retry observations fenced by local mutations, and retire queued reads
with their connection or authority.

Version Host background activity independently of Session revisions and
Turn epochs. Preserve the newer activity across list and targeted-row
races without discarding newer durable metadata or live Turn state, and
strip activity versions from cached projections. Advance compatibility
epoch to 208 and cover queue admission, ordering and Host restart.

Generated-by: Codex
Fence Owner catalog observations with the target partition's mutation
revision, preserving local create/remove races without letting a fast
partition keep discarding a slower partition's successful reads. Retain
the existing global revision for transcript ordering and keep partition
clocks monotonic across purge and authority replacement.

Give each Session its own activity observation revision so shared queries
cannot reveal activity changes in private Sessions through this field.
Keep idle clocks for the Host generation to prevent older running
responses from outranking completed activity.

Cover sustained cross-partition invalidation, mutation isolation, idle
clock continuity and actual Guest shared queries. The three behavioral
regressions fail with the pre-fix production implementations.

Generated-by: Codex
@chinawch007
chinawch007 force-pushed the fix/swarm-session-activity branch from 5fb3881 to fa11c8f Compare October 8, 2026 19:05

@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 notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.

Incremental review from our last reviewed head 5fb3881b to fa11c8f1c02bcf7d5ade0af3827847ac8f451da7.

Delta. Commits 1-8 were rebased from 49e01b15 onto current main 74094c63. Per git range-diff, the only change to them is the epoch rework: the two bumps (207 and 208) are now one bump to 212, with one history line covering both backgroundActivity and backgroundActivityVersion. The new work is all in fa11c8f1c:

  • The Owner catalog fence now uses a per-partition mutation revision (DesktopSessionLocalStore.partitionRevision). The counter is advanced by enqueue/saveSession/removeSession/purge, is kept after purge, and the global revision remains for transcript ordering.
  • SessionBackgroundActivityProjection keeps one activity clock per Session for the life of the Host generation, replacing the Host-wide counter.

Prior findings

  • P3, epoch collision and merge conflict: fixed. Current main is at 208. Of the open PRs, #3700 claims 207, #5548 claims 209 and #5969 claims 210. The #5980 head currently says 205, and 211 is suggested there. This PR's 212 does not collide with any of them. scripts/protocol-epoch-check.mjs --base origin/main passes ("208 -> 212"). git merge-tree against main is clean, and GitHub reports MERGEABLE.
  • P3, cross-partition starvation of fenced reads: fixed. saveCatalog advances revisions only through saveSession/removeSession on its own partition, so a busy partition can no longer fence another partition's read. Same-partition create/remove races are still fenced, and keeping the counter after purge stops an in-flight read from matching an earlier revision after an authority switch. I confirmed that the new test "continuous fast Owner invalidations cannot fence a slower partition catalog" fails when the old global fence is restored.
  • P3, Host-wide activity revision visible to Guests: fixed. The version a Guest sees now advances only on transitions in its own Session. The renderer compares versions only between successive states of the same Session row, so per-Session clocks keep the ordering guarantee. Keeping idle clocks for the generation costs one integer per Session that has transitioned, which is acceptable. When I restored the Host-wide counter, both new tests failed: "activity versions reveal only their own Session while idle and running" and "Guest shared queries do not expose activity transitions in private Sessions".
  • P3s the author declined earlier (transient running on a permission response, waiting-permission attention after a Host restart): unchanged and still acceptable as non-blocking.

Findings

  • P3 (process): the PR description is stale. It still cites head 5fb3881b0 and base 49e01b15, and it says the epoch moves "from main's 206 to 208". It should say 208 -> 212 on the current base. Please also note in the description that the epoch guard must be rerun if #3700, #5548, #5969 or #5980 lands first.

I found no new defects in this delta.

CI and mergeability. The test check on fa11c8f1 was still pending when I checked. GitHub reports MERGEABLE, with merge state BLOCKED because review is required.

Validation. Clean npm ci --ignore-scripts, dependency patches and build:test on fa11c8f1, and the epoch guard against origin/main passes. Focused suites:

  • Desktop session-local, session-catalog-* and Guest mounts: 119/119 pass.
  • Runtime Host session-catalog-protocol, session-background-activity, session-catalog-coordinator, authenticated-websocket and session-catalog-two-client-uds: 142/142 pass.

I also restored the old global fence and the old Host-wide clock in the compiled output. Each regression test then fails as intended.

Not run: full suites, UI, Electron E2E, Windows/macOS, live Guest transport.

@chinawch007

Copy link
Copy Markdown
Contributor Author

Thanks for the follow-up review. All actionable findings in this review have now been addressed.
As you confirmed, fa11c8f fixes the epoch conflict, cross-partition refresh starvation, and Guest-visible activity-version leakage. The remaining description issue is also resolved: it now references head fa11c8f, base 74094c6, and the compatibility change 208 → 212, with a reminder to rerun the epoch guard against main at merge time.
The test check for this exact head has also passed. Thanks for checking the fixes and validating the regression tests.

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