Repository navigation
Proposed-plan card actions are clickable while the proposing turn is still streaming, which overlaps turns #10925
Description
Activity
- addedbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsSerial note, ⛔ not a claim and no state change. From
domain:uiseat 4,session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T09:11Z.This card is held behind PR #10924 (epic objectui#10899, open and non-draft at this read). That PR edits
packages/plugin-chatbot/src/ChatbotEnhanced.tsx, which renders the proposed-plan card whose actions this card disables, and it addsChatbotEnhanced.hydratedStepTurns-10899.test.tsxbeside it. Same file, same region, so it runs serial across rounds.Known before dispatch, so the next taker does not re-derive it:
- The plan card surfaces on objectui
main1dae95a:packages/plugin-chatbot/src/ChatbotEnhanced.tsxandchatMessageAdapter.ts, pluspackages/app-shell/src/console/ai/AiChatPage.tsxandhooks/useChatConversation.ts(agit grepfor the plan-card copy and forProposedPlan). - The card's own source is PR objectui#10924's root-cause analysis (option C), so read that PR's diff after it lands before designing the disabled state. Its hydration change may already expose whether a turn is still streaming.
- This seat takes this card once PR objectui#10924 merges, unless another seat claims it first.
Generated by Claude Code
- The plan card surfaces on objectui
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsClaim: PM loop round 2
Session:session_015AUunPkX7UTkCH9e7AdZo1
Account:os-zhuang(the seat's linked user asGET /useranswers it; the maintainer authorised this seat to run on it, verbatim 「允许你用当前账户继续开发」, seat-post marker5864093687)
Branch:claude/issue-10925-plan-card-wait-stream
Worktree:objectui-issue-10925
Domain:domain:ui
Seat:domain:ui#4
File surface:packages/plugin-chatbot/src/ChatbotEnhanced.tsx(the proposed-plan card's action buttons:planApproveand the adjust action) and a test beside it underpackages/plugin-chatbot/src/__tests__/, plus one.changeset/10925-*.md.packages/app-shell/src/console/ai/AiChatPage.tsxonly if the turn's streaming state does not reach the card, and then only the prop that carries it. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(default judgement tier;dispatch-gates --tier --repo objectstack-ai/objectuion these paths: no path-derived mandate)
Clause-②: no
Thread-read: 5866915376
Serial constraints cleared: the serial condition in5866915376is met: PR objectui#10924 merged asac15833eb1. At 2026-09-28T10:01Z no open PR (7) touchesChatbotEnhanced.tsx, and no claim on the lane's 15pm:dispatchedcards holds it (this seat's objectui#10926 names the file only to say it does not touch it). objectui#10900 (seat 1) holds the Build Doctor region ofAiChatPage.tsx, and objectui#10926 (this seat) holds its build-thread scope region. If this card must touch that file, it is the plan-card prop only; mergemainbefore opening the PR.Take order: p2
bug. The maintainer placed the cloud-epic cards on this seat, verbatim 「全部接 (推荐)」 (this session's chat, 2026-09-28).Re-pricing after the unblock (was: behind PR objectui#10924): no effect. PR objectui#10924's
ChatbotEnhanced.tsxhunks change how hydrated step turns render (!message.streaming && !showsActivity …,hasVisibleProse). The plan card (proposedPlanon a tool invocation,ChatbotEnhanced.tsxnear L286 and L1063–1170) is untouched. The per-messagestreamingflag the fix needs is already read in the same component.
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsos-dev-report
{
"issue": 10925,
"status": "done",
"branch": "claude/issue-10925-plan-card-wait-stream",
"pr": "#10952",
"session": "session_015AUunPkX7UTkCH9e7AdZo1",
"premise_still_valid": true,
"summary": "The proposed-plan card actions (Build it, Adjust, the one-click answer chips, on the structured and the fallback plan card) now carry the native disabled attribute while isLoading is true, in packages/plugin-chatbot/src/ChatbotEnhanced.tsx; AiChatPage.tsx untouched (it already passes isLoading from useObjectChat). Zone 2 measured true: message.streaming = isLoading on the trailing assistant message (uiMessagesToChatMessages); ai@7.0.65 makeRequest sets status ready only after consumeStream ends; cloud origin/main 2611cdd AIService.streamChatWithTools awaits the store of each tool result and of the final reply before yield finishPart, and ObjectQLConversationService.addMessage awaits engine.insert, so neither signal drops before the tail persists. Zone 3 route changed: the gate reads isLoading, not message.streaming, because the per-message flag is a subset that misses the submitted phase and a newer turn streaming below an older card, both of which overlap turns the same way (pinned by case 5). One patch changeset; Clause-2 stays no (no schema, export or accept set moved; only the isLoading JSDoc text changed).",
"tests": "All on HEAD 847cb69, clean tree. New pin ChatbotEnhanced.planActionsWaitForTurn-10925.test.tsx (5 cases through the real mapper): Test Files 1 passed (1), Tests 5 passed (5). Ablation via objectstack scripts/ablation-replace.mjs, anchor const planActionsLocked = isLoading; replaced by = false; anchor x1 to x0, blob 1aa9b4bd0f9e to ae9939a163a9, run: Tests 5 failed (5), each toBeDisabled Received element is not disabled; restore blob == HEAD 1aa9b4bd0f9e, git diff HEAD empty; test imports the component from source by relative path, so no build or dist leg. Package suite pnpm exec vitest run --maxWorkers=2 packages/plugin-chatbot/: Test Files 52 passed (52), Tests 553 passed (553); git ls-files lists 52 test files. Closure build pnpm --workspace-concurrency=2 --filter @object-ui/plugin-chatbot^... build: 9 projects in scope, VERDICT command-exit 0. pnpm --filter @object-ui/plugin-chatbot type-check: exit 0; tsc -p tsconfig.test.json --listFiles lists the new test. Lint narrowed: eslint --no-inline-config --format json on the 2 touched ts/tsx files, JSON lists 2 files; ChatbotEnhanced.tsx 0 errors 10 warnings at HEAD and 0/10 at base 5c94589 (same 3 rules), new test 0/0; population from eslint.config.js files **/*.{ts,tsx}; invariance: no type-aware linting (no project, projectService or type-checked preset). Gates exit 0: check-changeset-presence, check-changeset-no-major, check-changeset-overwrite, check-changeset-claims (report-only; names .changeset/6687-chatbot-surface-authorable.md, read, its surface paragraph stays true), check-changeset-fixed, check-pending-changeset-literals, check-new-cross-file-line-citations (0 new), check-control-bytes, check-test-path-roots, check-vi-mock-specifiers, check-vi-mock-inherit, check-vi-mock-override-shape, check-lint-coverage, check-i18n-call-site-keys, check-phantom-dependencies, check-package-self-import, check-unreferenced-sources, check-handler-key-read-sites, check-type-check-coverage; check-governed-queue-guard --test: NOT GOVERNED. Every build/test ran through os-verify-lock (slot issue-10925-dev). CI at report time: 42 check runs, 20 success, 3 skipped, 19 in_progress, 0 failed. NOT MEASURED: in-browser or live E2E, reason: no rig in this dispatch; consumer app-shell suites, reason: exported types unchanged apart from JSDoc and the one plan-card consumer test renders isLoading false.",
"mcp_calls": "0",
"api_writes": "3 — all through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches each): pr_create to POST /repos/objectstack-ai/objectui/pulls (PR 10952, draft forced); label-write assign to POST /repos//issues/10952/assignees (read back: os-zhuang); os-dev-report comment to POST /repos//issues/10925/comments. Plus 2 git push (branch probe, commit 847cb69), not REST.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: 承接者:无 · noted, not filed — same family as this card (a confirm action on a proposal card that sends the next turn while the proposing turn streams): the confirm-change card proposed-changes-confirm sends changesConfirmMessage and stays enabled while isLoading is true. One-off probe, not kept: update_metadata changes_proposed on the streaming tail, isLoading true, confirm toBeEnabled and a click sent the message (1 passed). No public-door measurement, so no reach; the same planActionsLocked guard would close it. In PR Acceptance notes. Dedupe words: proposed-changes confirm streaming, changesConfirmMessage isLoading, confirm card overlaps turns"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorMore actionsACCEPT — PR #10952 @
847cb69domain:uiseat 4,session_015AUunPkX7UTkCH9e7AdZo1, at 2026-09-28T10:49Z. Checked against GitHub andorigin/main, not against the report.- Form: draft, base
main, body first lineFixes #10925, and no other closing keyword. 3 files, +283/−6:packages/plugin-chatbot/src/ChatbotEnhanced.tsx, one pin, and.changeset/10925-plan-card-wait-stream.md. All are inside the claim's file surface.AiChatPage.tsxis untouched, and the file does not overlap the held PR feat(app-shell): the console dock binds its build thread to the current authorable app (objectui#10926) #10950. Nothing undercontent/docs/releases/. - CI on the head
847cb69341: 43 check-runs, 40successand 3skipped, none failed. Governed-surface guard (scripts/check-governed-queue-guard.mjs --test, 3 paths): NOT GOVERNED. - Contract review: PASS at
CONTRACT_REVIEW_TIERon this head,5868396957.@object-ui/plugin-chatbotpatchmatches a fix with no export, prop or type change (JSDoc only).Clause-②: noholds. Every changeset sentence is true against the plugin source and cloud'sstreamChatWithToolsstore-before-finishordering. - Route deviation accepted: the gate reads the component-wide
isLoading, not the suggested per-messagestreaming. The per-message flag is derived fromisLoading, and it misses two windows that overlap turns the same way:submittedwith no reply yet, and a newer turn below an older card. Case 5 pins both. The cost is that an older plan card also waits while a newer turn is in flight. That matches the composer, which the same signal already holds, so no capability is lost.Adjustonly focuses the composer, which is disabled in that window. - Acceptance:
- 1: the structured approve and adjust, the fallback approve and adjust, and the one-click chips carry native
disabledwhile a turn is in flight. - 2: they are enabled at
ready. - 3: five cases through the real mapper pin both states. The report's ablation (
planActionsLocked = false) turns all five red.
- 1: the structured approve and adjust, the fallback approve and adjust, and the one-click chips carry native
- Out-of-scope finding (same defect class, routed to the maintainer, not filed): the proposed-changes confirm card (
proposed-changes-confirm,handleChangesConfirm) sends the next turn and stays enabled whileisLoadingis true. The dev's probe measured a click sending the message. The contract review confirms it is the same class and leaves the carrier open: this PR, a new card under epic objectstack-ai/cloud#2440, or nowhere. The seat does not widen this PR: the confirm card has its own state machine, and it is outside the claim. The question is in the seat's round report to the maintainer.
Next: ready → auto-merge (merge queue), followed to MERGED by this seat.
Generated by Claude Code
- Form: draft, base
- added a commit that references this issue
on Oct 7, 2026
Source
objectui#10899 item 5b, the replay ordering. The dev root-caused it in PR #10924 on 2026-09-28. Epic objectstack-ai/cloud#2440.
What happens
The proposed-plan card's build / adjust buttons are clickable while the proposing turn is still streaming. In the E2E, 「确认,开始搭建」 was sent while the previous turn was still persisting its tail. That overlapped two turns, and the stored history interleaved.
Direction
The epic PM takes the objectui half of the dev's option C: keep the plan card's actions disabled until the proposing turn has finished streaming. The producer-side fix (ordering by turn in the cloud conversation store) is its own cloud card.
Acceptance