Repository navigation
fix(plugin-chatbot): a plan that overlaps an existing app asks whether to extend it or build a new one, instead of offering Build it - #27595
Conversation
…tead of offering Build it A propose_blueprint result whose decision is iterate_or_new overlaps an app the user already has. The chat drew it as an ordinary plan card with a "Build it" button, so one click could build a duplicate of that app. detectProposedPlan now lifts the decision and existingAppOverlap's name / label as ProposedPlan.iterateOrNew, and the plan card draws the extend-or-new question in place of its approve / adjust actions: the question names the existing app, and its two choices send "keep improving APP" or "build a separate app". The two replies are outbound text, so the console resolves them by the conversation's language through resolveOutboundAgentText, like every other outbound message. The cache-fallback reload keeps the question (proposedPlanToCachedResult writes both keys back); the server-history reload re-parses the stored result. Claude-Session: https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d Co-authored-by: Claude <noreply@anthropic.com>
… reload The mapper lifts the question from the envelope (plain proposal and other decisions lift nothing; no objects still asks); the card asks instead of offering Build it and each choice sends its answer; the question survives both reload paths (server history and cache fallback); the two answers follow the conversation's language and never read as approval to the cloud confirm gate. Claude-Session: https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d Co-authored-by: Claude <noreply@anthropic.com>
…estion The done-when assertion goes first, so an ablation of the question face fails on the Build it button it brings back. Claude-Session: https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d Co-authored-by: Claude <noreply@anthropic.com>
…te_or_new question Claude-Session: https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe |
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe Contract reviewServed-tier: Face: declaration only ( ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…te census
The de pack's quote census pins every correctly paired „…“ span. The
iterate_or_new question and its extend choice each name the existing app
(„{{app}}“), so the census moves from 98 to 100 paired spans.
Claude-Session: https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d
Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe |
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe Contract reviewServed-tier: Face: declaration only. Delta review by the same reviewer as the PASS on ① Derived judgments
② Semver levelUnchanged: ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe 席位核对ACCEPT · head
Generated by Claude Code |
|
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe Merge-queue kick-out, 2026-10-29T15:24Z: the only red in group |
Requested by Bill · project thread
Fixes #27480
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
decision: 'iterate_or_new'),方案卡不再显示「开始搭建」,改为提问「在「X」上继续完善,还是另建一个独立应用」,两个选项各自发出对应回复;普通方案不变。iterate_or_new时换掉按钮行,其余方案走原路径;回复文字按会话语言发出,且不会被确认门当作同意;回滚即还原本 PR。ui/packages/plugin-chatbot/src/ChatbotEnhanced.tsx的IterateOrNewQuestion与按钮行三分支;其次mapMessages.ts的liftIterateOrNew;再看useChatConversation.ts缓存回写。pnpm --dir ui exec vitest run packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.iterateOrNew-27480.test.tsx,预期 10 个测试全绿。What changes
mapMessages.ts).detectProposedPlanliftsdecision: 'iterate_or_new'into a new optionalProposedPlan.iterateOrNew, with the overlapping app'snameand optionallabelread fromexistingAppOverlap. A result with the decision but no objects still yields a plan, so the card asks the question instead of falling back to the generic "Build it" card. An overlap that names no app yieldsiterateOrNew: {}.ChatbotEnhanced.tsx). For such a plan the card keeps its rows and replaces "Build it" / "Adjust" withIterateOrNewQuestion. The question names the app (label, else the host'sresolveAppLabel(name), elsename). Its two choices are "Keep improving X" and "Build a separate, new app", and each sends its answer throughonSendMessage. Without a send channel, or without an app name, the card shows the question and a reply hint and offers no one-click choice. Once a build has run after the plan, the question stays and the choices go, as the plan's own buttons already do.planExtendExistingMessage(app)andplanBuildNewAppMessage, with English defaults. The console wires both throughresolveOutboundAgentText, so they follow the conversation's language like the plan card's other outbound messages. The new keysconsole.ai.planExtendExistingMessageandconsole.ai.planBuildNewAppMessageare in theenandzhpacks only, as the other outbound keys are.proposedPlanToCachedResult) now writes backdecisionandexistingAppOverlap, so a cache-fallback reload asks the question too.chatbot.plan.iterateOrNew.{question, questionUnnamed, extend, buildNew, replyHint}in all ten packs.content/docs/frontend/plugins/plugin-chatbot.mdxafter the plan-card description.@object-ui/plugin-chatbot,@object-ui/app-shelland@object-ui/i18n.Tests
mapMessages.iterateOrNew-27480.test.ts(7): the lift from the bare result and from the{type, value}wrapper; a plain result has noiterateOrNew; anotherdecisionvalue is ignored; a result with no objects still yields a plan;labelis optional and an overlap with no app gives{}; the live mapper carries the field.ChatbotEnhanced.iterateOrNew-27480.test.tsx(10): no approve, adjust or fallback button and the question names the app (asserted first); the plain proposal keeps "Build it" and "Adjust" (control); each choice sends its answer; the host's outbound text is sent when supplied; zh labels; no objects still asks;resolveAppLabel; the unnamed overlap; noonSendMessage; the settled state after a build.AiChatPage.iterateOrNewReload-27480.test.tsx(3): server history re-parses to the question; the cache fallback keeps both keys and both re-maps render the question; a plain proposal is cached without the keys (control).AiChatPage.planCardLocale.test.tsx: the two answers follow the conversation, not the UI locale (zh thread under en/zh/de UI, en thread under zh/en/de UI).outboundAgentText.test.ts,outbound-agent-messages.test.ts,all-locales-key-parity.test.ts,console-namespace-3546.test.tsx: the two outbound keys are registered.i18n.test.ts: neither answer, in zh or en, matches the confirm gate's approval pattern.Acceptance notes
existingAppOverlap.name/.labelas the card quotes them and tolerates their absence: a missing or differently nested app gives the unnamed question with a reply hint, never the "Build it" card. The cloud#3369 owner should confirm the nesting.scripts/ablation-replace.mjs, each anchor x1 to x0 with the blob changed, and each file restored to equal HEAD withgit diff HEADempty:mapMessages.ts): 16 red, 4 green (the plain controls) over the three new files.ChatbotEnhanced.tsx): 11 red, 9 green, with the mapper tests green. A2b, run alone on the first test, fails on the missing-approve assertion.useChatConversation.ts): only the cache-fallback test red.AiChatPage.tsx): the six conversation-language tests red.all-locales-key-parity,console-namespace-3546,i18n.test.ts) gained the two keys. They are the same key ledgers the namedoutbound-agent-messages.test.tsreads.Verification
See the PR's check-runs for every family CI runs. Local readings (targeted vitest, type-check of the three packages, the derived gate list) are in the os-dev report on #27480.
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01Utp6Ca9ZbSdPg2iuzBYA3d