Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/11988-build-verify-receiver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@object-ui/plugin-chatbot': minor
'@object-ui/i18n': minor
---

The chat build panel reads cloud's post-apply verification loop, and an unknown build phase no longer reads as "Building" (objectui#11988, the receiver objectstack-ai/cloud#2172's ruling A orders).

After `apply_blueprint` finishes, cloud's agent loop reports its verification hops on a second `data-build-progress` part with the id `build-verify`, beside the build tree. A hop is `{ phase: 'verify', hop, tool }` and the exit is `{ phase: 'done' }`. The receiver used to take the last `data-build-progress` part whatever its id, and turned every phase other than `data` and `done` into `structure`. So the first verification hop replaced the finished tree with "Building your app…".

- **The tree and the verification part are read apart, by part id.** The `build-verify` part never displaces the tree, whichever order the two parts arrive in. A message that carries only the tree maps and renders exactly as before.
- **The build panel shows a verification line under the tree.** It reads "Checking the change… step N" while a hop runs, with the hop's tool name as its tooltip, and "Checked the change" once the `done` frame arrives. A `build-verify` part on a message that has no build tree is not drawn as a tree.
- **Phases are read against the spec's vocabulary.** The receiver's phase table and frame type are typed by `@objectstack/spec/ai`'s own `BuildProgressPhase` and `BUILD_PROGRESS_FRAME_TYPE`, so a phase the spec adds or drops fails the type check here instead of drifting. The spec's runtime module is not imported, because it would put its whole AI schema module on the console's first load. A phase outside the vocabulary, or a frame without one, is now `unknown` and shows as a warning line ("Unknown build phase") on the tree header or on the verification line. It is no longer coerced to `structure`.
- **Type change.** `ChatBuildProgress.phase`, reached through `ChatMessage['buildProgress']`, widens from `'structure' | 'data' | 'done'` to `'structure' | 'data' | 'verify' | 'done' | 'unknown'`: the spec's phase vocabulary plus `'unknown'`, held equal to the spec's union by a compile-time test. `ChatBuildProgress` also gains an optional `verify` member, `{ phase, hop?, tool? }`. The published typings name no new `@objectstack/spec` symbol, so the package's spec range is unchanged.
- **New language-pack keys, in all ten packs:** `chatbot.build.verifying`, `chatbot.build.verifyStep`, `chatbot.build.verified` and `chatbot.build.unknownPhase`.
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3584,6 +3584,10 @@ const ar = {
seed: "بيانات تجريبية",
},
moreArtifacts: "+{{n}} أخرى",
verifying: "جارٍ التحقق من التغيير…",
verifyStep: "الخطوة {{n}}",
verified: "تم التحقق من التغيير",
unknownPhase: "مرحلة إنشاء غير معروفة",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3317,6 +3317,10 @@ const de = {
seed: "Beispieldaten",
},
moreArtifacts: "+{{n}} weitere",
verifying: "Änderung wird geprüft…",
verifyStep: "Schritt {{n}}",
verified: "Änderung geprüft",
unknownPhase: "Unbekannte Build-Phase",
},
},
chatbotError: {
Expand Down
7 changes: 7 additions & 0 deletions packages/i18n/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3847,6 +3847,9 @@ const en = {
// `building`/`built` interpolate `appFallback` when the
// build has no app label, so each pack must keep BOTH
// frames in the one case/gender that noun phrase is in.
// `verifying`/`verifyStep`/`verified` are the post-apply
// verification line and `unknownPhase` the warning for a
// phase outside the spec's vocabulary (objectui#11988).
// `plan.*` — the "N objects · N views · N dashboards" strip. Plural
// FAMILIES: one slot per CLDR category the pack's language
// selects (objectui#11432), plus the base key for a call
Expand Down Expand Up @@ -3935,6 +3938,10 @@ const en = {
seed: 'Sample data',
},
moreArtifacts: '+{{n}} more',
verifying: 'Checking the change…',
verifyStep: 'step {{n}}',
verified: 'Checked the change',
unknownPhase: 'Unknown build phase',
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3382,6 +3382,10 @@ const es = {
seed: "Datos de ejemplo",
},
moreArtifacts: "+{{n}} más",
verifying: "Comprobando el cambio…",
verifyStep: "paso {{n}}",
verified: "Cambio comprobado",
unknownPhase: "Fase de creación desconocida",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3379,6 +3379,10 @@ const fr = {
seed: "Données d’exemple",
},
moreArtifacts: "+{{n}} autres",
verifying: "Vérification de la modification…",
verifyStep: "étape {{n}}",
verified: "Modification vérifiée",
unknownPhase: "Phase de création inconnue",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3319,6 +3319,10 @@ const ja = {
seed: "サンプルデータ",
},
moreArtifacts: "他 {{n}} 件",
verifying: "変更を確認しています…",
verifyStep: "ステップ {{n}}",
verified: "変更を確認しました",
unknownPhase: "不明なビルドフェーズ",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3316,6 +3316,10 @@ const ko = {
seed: "샘플 데이터",
},
moreArtifacts: "외 {{n}}개",
verifying: "변경 사항 확인 중…",
verifyStep: "{{n}}단계",
verified: "변경 사항 확인 완료",
unknownPhase: "알 수 없는 빌드 단계",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3377,6 +3377,10 @@ const pt = {
seed: "Dados de exemplo",
},
moreArtifacts: "+{{n}} mais",
verifying: "Verificando a alteração…",
verifyStep: "etapa {{n}}",
verified: "Alteração verificada",
unknownPhase: "Fase de criação desconhecida",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3463,6 +3463,10 @@ const ru = {
seed: "Демоданные",
},
moreArtifacts: "ещё {{n}}",
verifying: "Проверка изменения…",
verifyStep: "шаг {{n}}",
verified: "Изменение проверено",
unknownPhase: "Неизвестный этап сборки",
},
},
chatbotError: {
Expand Down
4 changes: 4 additions & 0 deletions packages/i18n/src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3464,6 +3464,10 @@ const zh = {
seed: '示例数据',
},
moreArtifacts: '+{{n}} 个',
verifying: '正在检查改动…',
verifyStep: '第 {{n}} 步',
verified: '已检查改动',
unknownPhase: '未知的构建阶段',
},
},
chatbotError: {
Expand Down
50 changes: 47 additions & 3 deletions packages/plugin-chatbot/src/ChatbotEnhanced.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,20 @@ export interface ChatChart {
series: Array<{ dataKey: string; label?: string }>;
}

/**
* A build-progress frame's phase: `@objectstack/spec/ai`'s closed
* `BuildProgressPhase` vocabulary, plus `unknown` for a value outside it, which
* is surfaced as a warning — never as "Building" (objectui#11988). Spelled out
* rather than imported so the published typings need no newer spec floor;
* `buildVerify-11988.test.tsx` asserts at compile time that it equals the
* spec's union, both ways.
*/
type ChatBuildPhase = 'structure' | 'data' | 'verify' | 'done' | 'unknown';

/** A reconciled snapshot of an in-flight app build (apply_blueprint). */
export interface ChatBuildProgress {
/** Coarse phase: drafting structure, generating sample data, or finished. */
phase: 'structure' | 'data' | 'done';
phase: ChatBuildPhase;
/** Human label for the app being built (for the panel header). */
appLabel?: string;
/** Artifacts drafted so far, cumulative. */
Expand All @@ -176,6 +186,12 @@ export interface ChatBuildProgress {
* heartbeat). Absent from older runtimes.
*/
seq?: number;
/**
* The post-apply verification loop, read from its own `build-verify` part
* beside the tree (objectui#11988): `verify` while a hop runs, `done` once
* the loop exits. `hop` and `tool` are the spec frame's own fields.
*/
verify?: { phase: ChatBuildPhase; hop?: number; tool?: string };
}

/**
Expand Down Expand Up @@ -4222,13 +4238,17 @@ function BuildProgressPanel({
stalledLabel?: string;
offlineLabel?: string;
}) {
const { phase, appLabel, items, done, total, seq } = progress;
const { phase, appLabel, items, done, total, seq, verify } = progress;
// objectui#7388 — every string this panel OWNS goes through the pack. The
// labels it receives as props (`openBuiltAppLabel`, the connection cues, …)
// are already localized by the host; these were the island left behind.
const { t } = useObjectTranslation();
const groupLabelOf = useBuildGroupLabel();
const isDone = phase === 'done';
// objectui#11988 — only the phases the tree draws read as "Building"; any
// other value is surfaced as a warning, never coerced into one of them.
const unknownPhase = t('chatbot.build.unknownPhase', { defaultValue: 'Unknown build phase' });
const isUnknown = !isDone && phase !== 'structure' && phase !== 'data';
// The unnamed-build stand-in is itself a translated noun phrase, so it can be
// interpolated into the two header frames the same way a real app label is —
// one hole per frame, which is what `check-i18n-call-site-keys` checks.
Expand Down Expand Up @@ -4258,13 +4278,17 @@ function BuildProgressPanel({
<div className="mb-2 flex items-center gap-2 font-medium">
{isDone ? (
<CheckCircle2 className="size-4 shrink-0 text-emerald-600" />
) : isUnknown ? (
<TriangleAlert className="size-4 shrink-0 text-amber-600" />
) : (
<Loader2 className="size-4 shrink-0 animate-spin text-primary" />
)}
<span>
{isDone
? t('chatbot.build.built', { app: appName, defaultValue: 'Built {{app}}' })
: t('chatbot.build.building', { app: appName, defaultValue: 'Building {{app}}…' })}
: isUnknown
? unknownPhase
: t('chatbot.build.building', { app: appName, defaultValue: 'Building {{app}}…' })}
</span>
{!isDone && phase === 'data' ? (
<span className="text-xs font-normal text-muted-foreground">
Expand Down Expand Up @@ -4334,6 +4358,26 @@ function BuildProgressPanel({
);
})}
</ul>
{verify ? (
// The verification line advances per hop and closes on `done`.
<div className="mt-2 flex items-center gap-2 text-xs text-muted-foreground" data-testid="build-verify" title={verify.tool}>
{verify.phase === 'done' ? (
<CheckCircle2 className="size-3.5 shrink-0 text-emerald-600" />
) : verify.phase === 'verify' ? (
<Loader2 className="size-3.5 shrink-0 animate-spin" />
) : (
<TriangleAlert className="size-3.5 shrink-0 text-amber-600" />
)}
{verify.phase === 'done'
? t('chatbot.build.verified', { defaultValue: 'Checked the change' })
: verify.phase === 'verify'
? t('chatbot.build.verifying', { defaultValue: 'Checking the change…' })
: unknownPhase}
{verify.phase === 'verify' && verify.hop !== undefined
? ` ${t('chatbot.build.verifyStep', { n: verify.hop, defaultValue: 'step {{n}}' })}`
: null}
</div>
) : null}
{isDone && builtApp && (onDesignBuiltApp || onOpenBuiltApp || onPreviewDraftApp) ? (
<div className="mt-3 flex items-center gap-2">
{/* ADR-0080 D5 cold-start handoff: Studio is the built app's
Expand Down
Loading
Loading