Skip to content

fix(app-shell,plugin-chatbot): AI build page preview header reads the build card's label and live state; progress items show labels - #26538

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-ore7se
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-ore7se

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Fixes #26469

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:AI 构建页右侧预览的标题改用构建结果卡片同一份状态:显示应用名称(不再是机器名),卡片说已上线时去掉「未发布 · 仅自己可见」,发布后随之更新;进度列表有 label 时显示 label;卡片底部说明改为「在对话里点「发布」即可上线」。
  • 为什么改:同一页面上结果卡片说「已搭建并上线」,预览标题却说「未发布」,让客户怀疑应用是否真的能用。
  • 风险与回滚:只改 ui 显示逻辑,新增一个可选回调 onBuildResult;物化且 appHidden: false 的构建现在在卡片上也算已上线。回滚即 revert 本 PR。
  • 先看:ui/packages/app-shell/src/console/ai/AiChatPage.tsx(handleBuildResult)、ui/packages/plugin-chatbot/src/ChatbotEnhanced.tsx(BuildProgressPanel 的上报 effect 与 buildIsLive)、ui/packages/app-shell/src/console/ai/LiveCanvas.tsx(标题三分支)。
  • 两分钟验证:cd ui && pnpm exec vitest run packages/app-shell/src/console/ai/LiveCanvas.header-26469.test.tsx packages/app-shell/src/console/ai/__tests__/AiChatPage.canvasHeader-26469.test.tsx,预期 8 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • Preview header reads the build card (LiveCanvas.tsx, AiChatPage.tsx). LiveCanvas takes appLabel and live. The header shows the label (falling back to the machine name), and when live is true it reads 「运行预览 · LABEL」 / "Live app — LABEL" (new key console.ai.liveCanvasLive), with no unlisted note whatever materialized says. A materialized build that is not live keeps 「(未发布 · 仅自己可见)」. AiChatPage keeps the card's answer per app name (buildResults), fed only by onBuildResult, and hands it to both canvas mounts (desktop split and mobile overlay). It is stored apart from canvasApp because the card's effect runs before its host's: on a reopened conversation the report arrives before onBuildMaterialized opens the canvas. Cleared with the canvas on a conversation change.
  • The card hands its answer to the host (ChatbotEnhanced.tsx). New optional prop onBuildResult({ appName, label?, live }), passed to BuildProgressPanel. The panel reports, from an effect, the very values its title renders: builtApp.name, checklist.app.label ?? appLabel, and isDone && live. It fires again whenever one of them changes, which is how the header follows a publish. No second state derivation: the header shows what the card shows.
  • A listed materialized build is live (ChatbotEnhanced.tsx buildIsLive, mapMessages.ts). DraftReview lifts the envelope's appHidden when it is a boolean. buildIsLive also holds when the draft review says materialized: true and appHidden: false (ADR-0045: publish = live AND visible). appHidden: true adds nothing. What else reads buildIsLive: only the finished card's live prop (its 「已搭建并上线」 vs 「已搭建」 title) and, through the panel, the new onBuildResult report. Nothing else in the file reads it.
  • Progress items show labels (mapMessages.ts, ChatbotEnhanced.tsx). extractBuildProgress keeps a non-empty string label on an item; ChatBuildProgress.items gains the optional label; the panel renders label and falls back to the _sample-stripped name. Until objectstack-ai/cloud#3310 adds label to the frames, the panel looks as before.
  • Footnote (chatbot.build.draftPosture, every locale). Names Publish in the chat instead of Studio: zh 「之后的修改先存草稿,在对话里点「发布」即可上线。」, en "Later changes are saved as drafts; click Publish here in the chat to make them live." Each locale quotes its own console.ai.publishDrafts label.
  • Root changeset .changeset/26469-ai-build-canvas-header-labels.md: @object-ui/app-shell, @object-ui/plugin-chatbot, @object-ui/i18n, patch.

The draft tool card's 「发布 (N)」 block (shared with #26471) is untouched.

Tests

New:

  • plugin-chatbot/src/__tests__/ChatbotEnhanced.buildResultHeader-26469.test.tsx (9): a labelled item shows its label and an unlabelled one its name; the stream reader keeps label; a published build reports {label, live: true}; a drafted build reports live: false and reports live: true after a rerender into published; the checklist label wins as on the title; materialized + appHidden: false reads 「Built and live」 and reports live; materialized + appHidden: true is not live; detectDraftResult lifts appHidden only when present; the zh footnote.
  • app-shell/src/console/ai/LiveCanvas.header-26469.test.tsx (4, shipped zh pack): published → label, no 未发布; materialized not live → badge; no label → machine name; draft preview by label.
  • app-shell/src/console/ai/__tests__/AiChatPage.canvasHeader-26469.test.tsx (4): materialized + card not live → canvas props materialized, not live, label; card turns live → canvas turns live; a report before the canvas opens still reaches it; another app's report leaves the canvas alone.

Forced test edits:

  • plugin-chatbot/src/__tests__/ChatbotEnhanced.buildChat-12724.test.tsx pinned the old zh posture sentence (「之后的修改先存草稿,由你在 Studio 发布。」); it now pins the new one.
  • i18n/src/__tests__/de-quote-pairing-3876.test.ts counts the de pack's correctly paired 「„…“」 spans; the new de draftPosture adds one (the chat's „Veröffentlichen“ button), so its two pins move 96 → 97 with a line in each running tally, as every earlier addition did.

Acceptance notes

  • The header follows the card's live, which is the existing result-card signal (published envelope, the package's pending-draft count reaching 0, this chat's publish signal) plus the new listed-materialized arm. When the card says live but the build was not materialized (an auto-publish environment), the iframe still opens the draft overlay (?preview=draft) as before; only the header text changed. With no pending drafts the overlay shows the published app.
  • The cloud half (frames carrying label, the cloud posture strings) is objectstack-ai/cloud#3310. Nothing here fakes it.

Verification

Everything at head af7a3dae, branched off origin/main 00a4593a; no file of this PR moved on main since (checked against origin/main before opening).

Targeted vitest (7 files, 90 tests)

cd ui && pnpm exec vitest run --maxWorkers=2 over the three new files plus ChatbotEnhanced.buildChat-12724.test.tsx, mapMessages.test.ts, LiveCanvas.test.tsx, AiChatPage.previewDraft-12968.test.tsx: Test Files 7 passed (7), Tests 90 passed (90), lock VERDICT command-exit 0.

Package suites
  • pnpm --filter @object-ui/plugin-chatbot test --maxWorkers=2: 85 files, 814 tests passed.
  • pnpm --filter @object-ui/i18n test --maxWorkers=2: 87 files passed; 1353 tests passed, 13 skipped.
  • cd ui && pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/console/ai/: 45 files, 245 tests passed. The whole app-shell suite (about 1,700 files) is left to CI Test Core / Test UI; this is a declared narrowing to the directory the change lives in.
Ablations (each restored, blob == HEAD, `git diff HEAD` empty)

All through scripts/ablation-replace.mjs (anchor 1 → 0 on disk); ui vitest resolves @object-ui/* to src/, so no build sits between mutation and run.

  1. AiChatPage.tsx handleBuildResult stores live: false: AiChatPage.canvasHeader-26469 2 failed / 2 passed (the publish-update and report-before-open cases).
  2. LiveCanvas.tsx displayName = appName (label ignored): LiveCanvas.header-26469 3 failed / 1 passed.
  3. LiveCanvas.tsx live branch forced off: LiveCanvas.header-26469 1 failed / 3 passed (the published case).
  4. ChatbotEnhanced.tsx buildIsLive listed-materialized arm never matches: ChatbotEnhanced.buildResultHeader-26469 1 failed / 8 passed (appHidden: false reads live).
Derived gates (`dispatch-gates --commands`, 35)

All 35 at af7a3dae, each exit 0 (codes captured before any pipe); dispatch-gates --ran: 35 derived, 35 run, 0 NOT-MEASURED (a derived zero). pnpm --dir ui exec vitest run scripts/: 178 files passed, 2 skipped; 5097 tests passed.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

Also run (outside the derived list): ui check:i18n-keys, check:i18n-drift, check:i18n-dead-keys, check:i18n-designer-parity, each exit 0.

Type check

pnpm --filter @object-ui/plugin-chatbot --filter @object-ui/i18n --filter @object-ui/app-shell run type-check (both tsc --noEmit and tsc -p tsconfig.test.json per package) after turbo run build --filter='@object-ui/app-shell^...': lock VERDICT command-exit 0.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-27T07:27Z · #26469

Head af7a3dae (read from the PR object). Fixes #26469.

  • Shape. 21 files, +624 / −30, with a root changeset. The surface is the claim's plus the seat's widening (6434575104 thread note): plugin-chatbot/src/mapMessages.ts, and in ChatbotEnhanced.tsx the optional onBuildResult prop, its destructure, the pass at the panel mount and buildIsLive.
  • What changes.
    • The Live Canvas header reads the result card's own state through onBuildResult, the one source the triage asked for. A published build shows 「运行预览 · LABEL」 and no 未发布.
    • buildIsLive also requires materialized && appHidden === false. Its own test covers both ways: appHidden: true is not live.
    • Progress items render label ?? name, so they work before the cloud half lands.
    • The chatbot.build.draftPosture wording now names the chat's own 发布 in all 10 locales. There is one new key, console.ai.liveCanvasLive.
  • Forced test edits, re-read by the seat.
    • de-quote-pairing-3876: 96 → 97. The reworded de string adds one correctly paired „…“ span; this is a transcription pin.
    • ChatbotEnhanced.buildChat-12724: the expected 中文 posture string follows the reword.
    • Neither weakens a check.
  • Seat ruling on the open question. A build that is live but not materialized keeps the draft-overlay iframe, and only the header text changes. That stays as is.
  • Governed surface. scripts/pm/check-governed-merges.mjs --pr 26538: not governed.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Evidence (dev). plugin-chatbot 814/814, i18n 1353 passed, app-shell console/ai 245/245. The 4 ablations go red, then pass again once restored. Type-checks (chatbot, i18n, app-shell) exit 0. Gates: 35/35.
  • Serial constraints. Draft card keeps 「发布 (N)」 after the chat banner publishes: the pending count never refreshes (no subscriber to the metadata-refresh pulse) #26471 (draft card publish count) shares ChatbotEnhanced.tsx and the AiChatPage mount, and lands second, after merging main. onBuildResult sits on its own line with no reflow.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 07:28
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 07:28
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit a89074a Oct 27, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ore7se branch October 27, 2026 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants