Skip to content

Commit f7eaadc

Browse files
committed
fix(chatbot): type ChatToolInvocation.draftReview as DraftReview so the published type declares kind (objectui#10109)
The inline copy of DraftReview's fields on ChatToolInvocation.draftReview stopped matching when DraftReview gained the producer's optional kind, so ChatbotEnhancedToolInvocation under-declared what the runtime delivers. It now references DraftReview; the copy's field docs move onto DraftReview so the published type keeps them. AiChatPage's DraftReview upcast is redundant and is dropped (type-only, same emitted code). The DraftReview.kind doc names the readers that actually check it. Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0811a11 commit f7eaadc

4 files changed

Lines changed: 30 additions & 45 deletions

File tree

‎.changeset/10109-edit-envelope-not-built-app.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ An incremental edit is no longer read as a whole-app build. An `apply_edit` resu
66

77
- `detectBuiltAppPackage` returns `undefined` for an envelope that says `kind: 'edit'`, in both the drafted and the auto-publish (`status: 'published'`) postures.
88
- `DraftReview` has a new optional `kind?: 'edit'`, and `detectDraftResult` fills it from the envelope. The edit still gets its draft card, its items and its `packageId`, so one-click publish still works.
9+
- `ChatbotEnhancedToolInvocation['draftReview']` (`ChatToolInvocation.draftReview`) is now typed as `DraftReview` instead of an inline copy of its fields, so it declares `kind` too. The two were field-identical before this change, and the published tool-invocation type now describes exactly what the chat mapper puts on it.
910
- `buildProgressFromDraftReview` builds no finished "Built X" panel for a draft review whose `kind` is `'edit'`, so a reloaded edit shows the same thing it showed live.
1011

1112
An envelope without `kind` behaves as before. `DraftReview.kind` declares only the value these readers act on, and an envelope that says anything else leaves it unset.

‎packages/app-shell/src/console/ai/AiChatPage.tsx‎

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ import {
8787
toRuntimeMessages,
8888
type AgentDescriptor,
8989
type ChatbotEnhancedToolInvocation,
90-
type DraftReview,
9190
// The ENHANCED message shape — the one `<ChatbotEnhanced>` renders and the
9291
// one this file actually produces (`toolInvocations`, `buildProgress`).
9392
//
@@ -2032,9 +2031,7 @@ export function ChatPane({
20322031
if (pkg) return pkg;
20332032
// objectui#10109 — an INCREMENTAL edit's draft review can list the `app`
20342033
// it re-staged; the producer's `kind: 'edit'` says it is not a build.
2035-
// Read through the detector's own `DraftReview`, which declares that
2036-
// `kind` (every draft review here is minted by `detectDraftResult`).
2037-
const dr: DraftReview | undefined = tool.draftReview;
2034+
const dr = tool.draftReview;
20382035
if (dr?.packageId && dr.kind !== 'edit' && dr.items?.some((it) => it.type === 'app')) {
20392036
return dr.packageId;
20402037
}

‎packages/plugin-chatbot/src/ChatbotEnhanced.tsx‎

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ import {
8282
SourcesContent,
8383
Source,
8484
} from './elements/sources';
85-
import { parseResultEnvelope, detectAuthoringVerdict } from './mapMessages';
85+
import { parseResultEnvelope, detectAuthoringVerdict, type DraftReview } from './mapMessages';
8686

8787
export interface ChatMessage {
8888
id: string;
@@ -268,43 +268,13 @@ export interface ChatToolInvocation {
268268
* lifts the reviewable targets here so chat UIs can render a "Review N
269269
* change(s)" affordance that opens the designer's review/diff. Nothing is
270270
* live until the human publishes — this is the review entry point.
271+
*
272+
* The shape is `mapMessages.ts`'s `DraftReview`, the type `detectDraftResult`
273+
* returns, referenced rather than repeated: an inline copy of its fields
274+
* drifted the moment `DraftReview` gained the producer's `kind`
275+
* (objectui#10109).
271276
*/
272-
draftReview?: {
273-
items: Array<{ type: string; name: string }>;
274-
summary?: string;
275-
packageId?: string;
276-
/**
277-
* Backend lifecycle intent (from the tool result). `true` for whole-app
278-
* builds (apply_blueprint) — eligible for the auto-publish "magic moment".
279-
* Omitted for incremental edits, which stay drafts for explicit review.
280-
*/
281-
autoPublishable?: boolean;
282-
/** Count of artifacts that failed in a partial build, surfaced not hidden. */
283-
failedCount?: number;
284-
/**
285-
* ADR-0045: the build was MATERIALIZED in-turn — real tables and seed
286-
* rows exist; the app is live but `hidden` (unlisted). Preview should
287-
* open the REAL app URL, not the draft overlay.
288-
*/
289-
materialized?: boolean;
290-
/**
291-
* ADR-0038 L1 graph-lint verdict for the staged build. Rendered as a
292-
* verified/issues chip so "drafted" and "verified" read as the two
293-
* separate statements they are. Absent on older tool output.
294-
*/
295-
verification?: { errors: number; warnings: number };
296-
/**
297-
* ADR-0038 L1 — the individual findings behind the `verification` counts,
298-
* surfaced under the chip so "N issues" expands into WHAT is wrong instead
299-
* of being a dead-end badge.
300-
*/
301-
issues?: Array<{ severity: 'error' | 'warning'; code: string; message: string; fix?: string }>;
302-
/**
303-
* Post-build "what's next" steps (apply_blueprint `nextSteps`). Rendered as
304-
* a short getting-started checklist under the build summary.
305-
*/
306-
nextSteps?: string[];
307-
};
277+
draftReview?: DraftReview;
308278
/**
309279
* ObjectStack extension. `propose_blueprint` returns a PLAN before anything
310280
* is staged (`status: 'blueprint_proposed'`). `mapMessages.ts` lifts the

‎packages/plugin-chatbot/src/mapMessages.ts‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,25 @@ export interface DraftReview {
207207
items: Array<{ type: string; name: string }>;
208208
summary?: string;
209209
packageId?: string;
210+
/**
211+
* Backend lifecycle intent (from the tool result). `true` for whole-app
212+
* builds (apply_blueprint) — eligible for the auto-publish "magic moment".
213+
* Omitted for incremental edits, which stay drafts for explicit review.
214+
*/
210215
autoPublishable?: boolean;
216+
/** Count of artifacts that failed in a partial build, surfaced not hidden. */
211217
failedCount?: number;
218+
/**
219+
* ADR-0045: the build was MATERIALIZED in-turn — real tables and seed
220+
* rows exist; the app is live but `hidden` (unlisted). Preview should
221+
* open the REAL app URL, not the draft overlay.
222+
*/
212223
materialized?: boolean;
224+
/**
225+
* ADR-0038 L1 graph-lint verdict for the staged build. Rendered as a
226+
* verified/issues chip so "drafted" and "verified" read as the two
227+
* separate statements they are. Absent on older tool output.
228+
*/
213229
verification?: { errors: number; warnings: number };
214230
/**
215231
* ADR-0038 L1 — the individual lint findings behind the `verification`
@@ -231,10 +247,11 @@ export interface DraftReview {
231247
* from the envelope's own `kind`: `apply_edit` answers `kind: 'edit'`, and a
232248
* whole-app `apply_blueprint` build carries no `kind`. An edit's `items` may
233249
* honestly include the `app` artifact it re-staged (an `add_object` op
234-
* merges the nav), so every reader that asks "is this a whole-app build?"
235-
* checks this before inferring a build from the staged artifact types.
236-
* Only the value such a reader acts on is declared: an envelope that does
237-
* not say `'edit'` lifts nothing here.
250+
* merges the nav), so the readers that infer a whole-app build from a draft
251+
* review's staged artifact types check this first: `buildProgressFromDraftReview`
252+
* here, and app-shell's built-moment transition (`detectBuiltAppPackage` reads
253+
* the envelope's own `kind` instead). Only the value those readers act on is
254+
* declared: an envelope that does not say `'edit'` lifts nothing here.
238255
*/
239256
kind?: 'edit';
240257
}

0 commit comments

Comments
 (0)