Repository navigation
fix(workhub): preserve topic identity and remote conversation continuity - #6068
Conversation
Generated-by: OpenAI Codex
Generated-by: OpenAI Codex
Exercise Desktop association receipts before inspection replies and recovered-turn completion, including Code Mode. Preserve target immutability and recovery capability-binding assertions. Generated-by: OpenAI Codex
Astro-Han
left a comment
There was a problem hiding this comment.
Automated review notice: This comment was posted by an automated review agent. It is not an independent human review and does not replace one.
I reviewed 8603b3028da39595c58455f74c69668bb63f0a1f, including the production Host fixture changes. I found one P2 issue: a successful decision to remove a topic association does not clear the remote reply's previous work heading.
The new associate operation records an existing work identity without starting execution. The Host blocks other work until a successful association decision, and the Runtime retries a tool-free response at most twice when that prerequisite is missing. The remote bridge reconstructs modern action routes, preserves delivery receipts while upgrading its saved state, and prefixes associated replies with the work name. I checked these paths, the receipt parser, the renderer's linked-work projection and the wn/nxt choice behavior.
P2 — Clear a previous heading when a later association is explicitly null (apps/desktop/src/main/workhub-remote-bridge.ts:127–130). In one turn, associate("sql") can succeed and then be corrected to associate(null, reason: "unrelated"); the schema, runtime and Host gate accept both calls. The bridge only handles string targets and ignores the successful null receipt, so the final unrelated answer still gets [SQL]. overview and ambiguous reproduce the same stale attribution. Using the real schema, actTasks, Host gate and bridge with local candidate/transport fixtures, I reproduced all three cases without starting any work. An isolated control that validates the corresponding null receipt and clears the saved target removes the incorrect heading while preserving ordinary association, SQL-to-CS336 switching and restart deduplication. Please handle successful clearing decisions explicitly and add a same-turn correction regression; error or mismatched receipts must not clear another turn's association.
The fresh dependency install and normal build:test passed. The complete AI SDK backend suite passed 258 tests; all 148 Desktop WorkHub tests and all 167 renderer-architecture tests passed, as did the four Desktop typechecks, the architecture check against the PR base, formatting and diff checks. The local Host group passed 122 of 123 tests; the managed-Bash test failed because this machine reports that its Linux command sandbox is unavailable. The same unchanged test also failed at the same assertion after rebuilding the actual merge base, 16f910168bd641572061bffd5511df3537a4cdca; I did not attribute that environment failure to this PR or describe the local Host group as fully green.
The exact-head CI run 38141796913 succeeded. This PR does not change a wire-protocol file or database schema. Its local merge with main 9a32d423b is clean, keeps main's epoch 217 and passes the epoch check; it does not allocate an epoch that could collide with another pending PR. CI success does not resolve the stale-heading issue. I did not exercise a live WeChat/QQ transport or provider, independently verify semantic topic classification, run the full monorepo suite or capture the native UI.
| } | ||
| if (message.type === 'tool_result') { | ||
| const source = state.actions[clientCapabilityEntityId(message.toolUseId)]; | ||
| const topic = source?.associationTargetSessionId && source.associationTurnId === message.turnId |
There was a problem hiding this comment.
P2 — Clear the previous work heading on a successful null association. In one turn, associate can first succeed for SQL and later succeed with targetSessionId: null and reason: "unrelated" (or overview/ambiguous). workHubAssociationTarget returns only string targets, so this condition ignores the clearing receipt and leaves state.turns[turnId].targetSessionId pointing to SQL. The final unrelated remote answer is consequently sent as [SQL]\n.... I reproduced this through the real schema, actTasks, Host gate and bridge, without executing work; a control that validates the matching null receipt and clears the saved target fixes all three cases without breaking ordinary association, target switching or restart deduplication. Please explicitly apply clearing decisions and cover same-turn corrections; failed or mismatched receipts must not affect another turn.
Distinguish explicit null association decisions from absent receipts. Validate the matching successful receipt before clearing the turn target, preserving failed-call and cross-turn isolation. Cover all clearing reasons, restarts, deduplication, and topic switching. Generated-by: OpenAI Codex
chinawch007
left a comment
There was a problem hiding this comment.
Automated review notice: This review was generated and posted by OpenAI Codex (AI) at the user's request. It is not an independent human review and does not replace one.
Reviewed 8603b3028da39595c58455f74c69668bb63f0a1f, including all 21 changed files and the surrounding Runtime, Host, transcript, and Desktop paths.
P2 — Explicit topic clearing must reach both remote and Desktop projections. I independently reproduced the existing remote-heading finding. There is also a Desktop manifestation in apps/desktop/src/renderer/features/workhub/model/linked-work.ts:63–70: after a successful SQL association followed by a successful null association in the same turn, workHubLinkedWork still returns the SQL link. The conversation uses those links for headings, rails, and work filtering. The inline comment identifies this additional repair location; fixing only the remote bridge would leave Desktop attribution stale.
My reproducer used the actual workHubTasksSchema, createWorkHubRuntime().actTasks, Host topic gate, remote bridge, and workHubLinkedWork, with fixture candidates and transport. For each of unrelated, overview, and ambiguous, both receipts succeeded, no work was dispatched, the remote answer retained [SQL], and Desktop retained ["SQL"]. Restart still deduplicated the delivered reply.
Verification performed
- Installed the locked dependencies, applied the repository's dependency patches, and ran
npm run build:test: passed. - Complete AI SDK backend and interactive-run-composer suites: 273 passed.
- Remote bridge, remote choices, and topic association suites: 15 passed.
- The changed production WorkHub inspection and cold-recovery integration tests: 2 passed.
- Three additional reproduction cases confirmed the defect above; these are defect-confirmation checks, not passing fixes.
git diff --check: passed. Independently checked that exact-head CI run 38141796913 succeeded.
Required conclusions
- Optimal for the actual problem? Separating association from execution addresses the problem, but the association lifecycle is incomplete until explicit clearing updates both projections.
- Production code to delete? None identified.
- Low-quality tests to delete or replace? None identified. Add same-turn association-to-null regressions for both consumers; retain the positive-association and restart/deduplication coverage.
- Deeper refactor required? No broad refactor identified. Represent a valid clearing receipt distinctly from an invalid/missing receipt, and apply association decisions in order per turn while preserving genuine delegation/result links.
- Ready to merge? Not yet: the reproduced P2 remains unresolved. This automated conclusion is not an approval.
- Residual risks / verification gaps? I did not run the full monorepo suite, exercise live WeChat/QQ or a live model, or capture the native UI. Semantic topic selection remains model-dependent. This changes user-visible attribution and remote delivery behavior, so material changes require independent human review under
CONTRIBUTING.md; a maintainer makes the final merge decision.
| if (message.type === 'tool_result') { | ||
| const call = associationCalls.get(message.toolUseId); | ||
| const topic = call?.turnId === message.turnId ? readWorkHubTopicAssociation(message, call.target) : undefined; | ||
| if (topic) return [{ id: message.id, coordinationTurnId: message.turnId, |
There was a problem hiding this comment.
AI review — OpenAI Codex.
[P2] Apply explicit topic clearing to the Desktop projection
This is the Desktop counterpart of the existing remote-heading finding. In one turn, a successful associate("sql") followed by successful associate(null, reason: "unrelated") still makes workHubLinkedWork return the SQL link: this flatMap emits the earlier association and never retracts it, while the call parser drops null targets. I reproduced the same result for overview and ambiguous using actual schema/runtime receipts. The conversation therefore keeps the wrong heading, rail, and work-filter membership even if the remote bridge is fixed. Apply validated clearing decisions to the current per-turn association, preserving genuine delegation/result links, and cover this correction sequence in the Desktop projection test.
Retract superseded topic associations on Desktop, including explicit clearing decisions, while preserving durable delegation and result links. Cover correction sequences using actual tasks schema and runtime receipts. Generated-by: OpenAI Codex
Summary
WorkHub could answer a follow-up such as “刚才到第几讲内容了?” in the right context but omit its work identity. Even a successful CS336 association lost its heading on an immediate WeChat reply. Modern tool-call transcripts could also leave delegated results without their originating remote chat.
nxtpagination. Keep one assistant voice across acknowledgments, execution, and returned results.Verification
npm run build:test,npm run typecheck,npm run lint, andnpm run format:check: passed.8603b3028, including all affected workspace tests, Runtime Host tests, and desktop E2E.e364f8afa): the three clearing-reason regressions failed with the stale SQL heading before the fix. Afterward all 161 Desktop WorkHub tests passed, including restart/paging, delivery deduplication, failed/mismatched receipts, cross-turn/chat isolation, and SQL-to-CS336 switching. Desktop main build, all four desktop typechecks, renderer architecture, changed-file lint, repository formatting, and diff checks passed.2192ba537): fold validated topic decisions in transcript order per turn, removing superseded topic links while retaining actual delegation/result links. Five defect reproductions failed before the fix; afterward all 167 Desktop WorkHub tests passed. Desktop main build, all four desktop typechecks, renderer architecture, changed-file lint, repository formatting, and diff checks passed.git diff --check, andnode scripts/protocol-epoch-check.mjs --base upstream/main: passed; no protocol epoch change required.Remote transport regression evidence (work name abbreviated in the fixture):
The new regression failed with the unprefixed reply before the fix and passed afterward. Full-repository tests and screenshot capture were not run; verification uses native accessibility state, durable runtime receipts, and transport output assertions.
AI use
Tool(s) and scope: OpenAI Codex implemented the changes, regression tests, and validation. The commit includes
Generated-by: OpenAI Codex.Checklist
Does this PR entail a change in behavior?