Skip to content

feat(plugin-chatbot,i18n): the copilot's change card draws the draft posture, and the applied card offers review and undo (objectui#12894) - #12951

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12894-copilot-draft-card
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-12894-copilot-draft-card

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Part of #12894
Part of #12725
Clause-②: yes

The copilot's change card and applied card draw the draft posture objectstack-ai/cloud now sends (ruling A on cloud#2924; cloud PR #2974 as f9e45145, and cloud PR #3004 as 1e3e04f8 for apply_blueprint's extend mode). This is also objectui#12725's Target item 4 (change card v2), as the StudioRailCopilot artboard draws it, without the composer half (see "Not drawn"). objectui#12725 stays open for its other items.

Premise, re-read on main (03f1d652)

  • changesConfirmLabel was a host prop of ChatbotEnhanced (default 'Confirm'); nothing on the envelope could change it.
  • detectProposedChanges kept summary and the rows. detectReplayOutcome kept kind, packageId and the publish failure. posture, primaryAction, draftState, existingRows and appliedCard were dropped before render.
  • The drafted outcome drew a badge and a Publish button only.

The premise holds.

The producer (read at cloud 1e3e04f8, packages/service-ai-studio)

  • changesProposedEnvelope (metadata-tools.ts) adds posture (from postureEnvelope: mode, liveRecords, optional platformPackage and buildTurnRows) and primaryAction (save_draft or confirm). It adds both only when the environment publishes AI changes in the turn. summary carries confirmChangeAsDraft in the user's language. The rows come from describeEditOps / describeChange (confirm-gate.ts); a row's details is changeParams (label, default, required).
  • describeLandedChange stamps draftState (readDraftState in change-posture.ts: pending, items, others) on the applied envelope. On a change saved as a draft it also stamps existingRows and appliedCard (savedAsDraft, existingRowsEmpty, checkedStructure).
  • No schema declares these envelopes (cloud's build-cards.schema.json covers the build cards only). So the producer's source is the declaration. The fixtures copy it, and the producer's own test edit (RENEWAL_EDIT in copilot-change-posture.test.ts).

What changed

Mappers (mapMessages.ts):

  • detectProposedChanges keeps posture and primaryAction, and nothing else new. A posture without both mode and liveRecords, or an action outside save_draft / confirm, is dropped whole.
  • detectReplayOutcome keeps draftState, existingRows and appliedCard, on a drafted replay only. A draft state without both counts, a card without a title, and malformed rows are dropped.
  • Every absent key leaves exactly the shape an older cloud produced.
  • The app-shell's hydration path calls the same two detectors, so a reloaded conversation lifts the new keys too.

Change card (ChangePlanBody in ChatbotEnhanced.tsx), drawn when the envelope carries posture or primaryAction:

  • the header 「修改方案 · N 项」 / "Change plan · N changes";
  • one row per change: a + / ✎ / - symbol, the kind (字段 / 视图 / 对象 …), object · field, and the type and details in mono small type;
  • the summary as an amber note when posture.mode is draft, else as plain text;
  • the primary button reads 存为草稿 / "Save as draft" when primaryAction is save_draft, and the host's changesConfirmLabel otherwise. 调整 is unchanged.

Applied card (AppliedDraftCard), drawn on a drafted replay that carries appliedCard:

  • appliedCard.title beside a tick, and its lines;
  • 查看变更并发布 / "Review changes and publish" through a new host door, onReviewDrafts(packageId);
  • 撤销 / "Undo" through a new host door, onDiscardDrafts(packageId).

discard-drafts drops EVERY pending draft of the package (framework runtime domains/packages.ts, protocol discardPackageDrafts, read at the pin be076dad28a0). So when draftState.others is non-zero, 撤销 says so (「撤销(连同另外 2 项草稿)」), and a confirm names the counts before anything runs (「撤销会丢弃这个应用包全部 3 项未发布的草稿,其中 2 项不属于这次修改。」, then 全部撤销 / 取消).

i18n: new chatbot.changes.* keys in all ten packs: the title, the actions and the confirm (count families with every CLDR category each language selects), and the row kinds.

Measured: neither door reaches the console without packages/app-shell

This is the dispatch's stop rule, and it turned out to apply to both buttons, not only to 查看变更并发布.

  • 撤销. The chat has no fetch of its own. The publish call the dispatch asked to mirror is the host door onPublishDrafts, which ChatPane (packages/app-shell/src/console/ai/AiChatPage.tsx) wires through metadataClient.publishPackageDrafts. So 撤销 is a host door here too, onDiscardDrafts. The console half is ChatPane passing it. MetadataClient has no discardPackageDrafts today; PackagesPage's discardDrafts posts to discard-drafts with a bare fetch.
  • 查看变更并发布. The 在 Studio 中查看 navigation (onDesignBuiltApp in ChatPane) goes to Studio's Interfaces pillar. Studio's 变更 drawer is changesOpen, component state in StudioDesignSurface, with no URL parameter or bus that opens it. So the door is onReviewDrafts(packageId). The console half is ChatPane passing it, plus a way into StudioDesignSurface's changesOpen.
  • Until a host wires them, the applied card draws its title and lines and keeps the inline Publish button (onPublishDrafts) where 查看变更并发布 would be. It draws no 撤销. Both doors are drawn and tested here behind their props. The console wiring waits for objectui#12725's phase 5, which holds app-shell.

Decisions for review

  • The v2 gate. The card draws v2 only when posture or primaryAction is present; the producer sends the two together. An envelope without them (an older cloud, or an environment that does not publish AI changes in the turn) draws the old card, with the host's title and verb labels.
  • The removal symbol. The artboard draws + and ✎. A removal row (delete_field) draws - in red rather than ✎.
  • When 撤销 asks first. 撤销 runs at once only when it can show that this change's drafts are the only ones pending. It asks first when draftState.others is above 0, or when there is no draftState. It also asks when the host's live fetchPendingDraftCount reads more drafts than this change staged: a later change in the same conversation stages more drafts into the same package, and the snapshot on an older card cannot know that. The console already wires fetchPendingDraftCount.
  • The message 存为草稿 sends. It is unchanged: the button sends changesConfirmMessage. 「存为草稿」 alone does not match cloud's PROPOSAL_APPROVAL_RE, so changing the message would leave the button inert.
  • Row kinds. A metadata type the pack has no word for renders as its machine name; a verb no kind covers renders as its verb label.

Not drawn

  • the composer's 「借阅看板 ×」 chip and its placeholder: objectui#12725 phase 5, in app-shell;
  • 补默认值: objectui#12929. The mapper keeps existingRows for it;
  • the 变更 badge: objectui#12725 phase 5;
  • the artboard's 「等待确认」 and 「未发布」 chips. No key declares them, and the title already says 「· N 项未发布」.

Tests

At a1601be5 (after merging main at 0707b4ae), every run through the shared verify lock:

  • pnpm exec vitest run --maxWorkers=2 packages/plugin-chatbot/: 68 files passed (68), 711 tests passed (711). git ls-files counts 68 tracked test files in the package.
  • pnpm exec vitest run --maxWorkers=2 packages/i18n/: 83 files passed (83), 1325 passed and 13 skipped (1338). 83 tracked test files.
  • New: mapMessages.changePosture-12894.test.ts covers each kept key, each absent key degrading, malformed values dropped whole, no other envelope key lifted, and the live mapper carrying both lifts. ChatbotEnhanced.changeCardV2-12894.test.tsx covers 存为草稿 on save_draft and the host label on confirm; the rows' symbol, kind, target and small type; the amber note; the old card for an older cloud; the applied card's title, lines and both doors; 撤销 at once with nothing else pending; 撤销 asking with the counts when others is above 0, or the live count is higher, or there is no draft state; cancel; a refused discard; and the Publish fallback without a review door. Both run through the real mapper from raw tool parts, on the fixtures in fixtures/changePosture.fixtures.ts.

Ablation (one, on a mapper): through objectstack's scripts/ablation-replace.mjs, primaryAction was dropped from detectProposedChanges's return (anchor 1 to 0, blob 1b39ebbf to bd565dbe). The two new suites went red: 6 failed and 26 passed of 32. Those were four mapper cases and the zh and en render cases, where the button read 确认修改 / Confirm. Restore: blob back to 1b39ebbf, equal to HEAD, and git diff HEAD empty. The suites import mapMessages and ChatbotEnhanced relatively, so no dist was involved.

Gates (local, at a1601be5)

  • type-check for @object-ui/plugin-chatbot and @object-ui/i18n: exit 0 each. tsc -p tsconfig.test.json --listFiles lists 68 test files in the chatbot program, the two new ones among them, plus the fixture.
  • lint for both packages: exit 0, 0 errors. None of the 120 warnings falls on an added line.
  • Exit 0 each: check:i18n-keys, check:i18n-drift (0 en values changed, 25 keys added), check:i18n-dead-keys, check:i18n-designer-parity, check:new-line-citations (0 new), check:control-bytes, check:test-path-roots, check:unreferenced-sources, check:phantom-deps, check:unused-deps, check:self-import, check:published-tsconfig-exclude, check:changeset-claims, check:pending-changeset-literals, the three check:vi-mock-*, check:shell-escape-residue, check:comment-mask-corpus, check:lint-rule-coverage, check:spec-symbols, check:esm-specifiers, check:handler-key-reads, check:registry-bare-names, check:doc-fences, check:doc-json, check:doc-types, check:icon-record-names, check:action-ref-convention, and the scripts check-changeset-presence, check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-doc-links, check-doc-expression-carriage, check-type-check-coverage and check-lint-coverage.
  • NOT MEASURED here, so CI's runs are the evidence: @object-ui/app-shell's type-check and suite. It is the one consumer of the widened types, and its dependency closure (19 unbuilt packages) was not built in this shared container. The widening only adds optional members. check:readme-exports and check:eager-locale-catalogues need dists this run did not build; this diff touches no README.

Screenshots

On the evidence branch claude/issue-12894-evidence (25b88b08, never merged; no image is in this diff), rendered at 1280 px, light, through the real mapper and ChatbotEnhanced in a 360 px right slot:

The branch's docs/design/findings/12894/README.md says how they were rendered and what is faked: the envelopes, and the three host doors as no-ops.

Acceptance notes

  • A conversation restored from the console's local display cache draws the drafted badge, not the applied card. The cache copy in useChatConversation (replayOutcomeToCachedResult) keeps only the minimal replay envelope. Widening it is an app-shell change outside this claim. Carrier: none; noted, not filed.
  • 撤销's done state lives in the component, like the confirm card's other client-side states. After a reload, an undone card offers 撤销 again, and a second run discards whatever is pending then. The live-count check asks first when more drafts are pending than this change staged. A count does not name items, so it cannot tell one unrelated draft from this change's own, and that one case runs without asking. Carrier: none; noted, not filed.

Published faces

  • @object-ui/plugin-chatbot:
    • ProposedChanges gains optional posture and primaryAction;
    • ReplayOutcome gains optional draftState, existingRows and appliedCard;
    • ChatbotEnhancedToolInvocation's proposedChanges and replayOutcome now reference those two types instead of repeating them inline, so they widen the same way;
    • ChatbotEnhancedProps gains optional onReviewDrafts and onDiscardDrafts.
    • The new member types (ChangePosture, ChangeDraftState, PendingDraftItem, ExistingRowsLine, AppliedCard in mapMessages.ts) are not re-exported from the package entry.
  • @object-ui/i18n: new chatbot.changes.* keys in every pack.

The claim declares Clause-②: yes for this, and the seat's contract review is on this PR.

Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX

…posture, and the applied card offers review and undo (objectui#12894)

The mappers keep the keys cloud#2924's envelopes declare, and only those:
detectProposedChanges keeps `posture` and `primaryAction`; detectReplayOutcome
keeps `draftState`, `existingRows` and `appliedCard` on a drafted replay. A
value without its declared required members is dropped whole, and every
absent key leaves the shape an older cloud produced.

The change card draws v2 (the StudioRailCopilot artboard) when the envelope
carries the posture: 「修改方案 · N 项」, one row per change (symbol, kind,
object · field, type and details in small type), the summary as an amber note
for a change saved as a draft, and 存为草稿 as the primary label when
`primaryAction` is `save_draft` (the message it sends is unchanged). The
applied card draws `appliedCard.title` and its lines, 查看变更并发布 through a
new `onReviewDrafts` host door, and 撤销 through a new `onDiscardDrafts` door.
Because discard-drafts drops every pending draft of the package, 撤销 says
so and asks first whenever other drafts are pending, by `draftState.others`
or the host's live count. Without the new keys or doors, the card draws as
before.

Claude-Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX
Co-authored-by: Claude <noreply@anthropic.com>
…s two doors; changeset (objectui#12894)

Claude-Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation plugin tests package: i18n labels Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 75 chunks) 3161.3 KB 3744.1 KB
Main entry chunk (gzip) 80.6 KB 350 KB
Entry file index-BEWbfAaV.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 21.41KB 7.88KB
app-shell (runtime-config.js) 24.52KB 8.48KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 45.59KB 12.03KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 20.67KB 6.12KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.74KB 3.94KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.62KB 0.83KB
auth (authStyles.js) 5.27KB 1.86KB
auth (createAuthClient.js) 42.84KB 11.66KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.26KB 8.03KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (commentAuthorship.js) 1.39KB 0.78KB
collaboration (index.js) 1.93KB 0.85KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 612.39KB 149.02KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 248.83KB 69.44KB
fields (index.js) 291.72KB 74.47KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.33KB 11.96KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 55.04KB 16.10KB
plugin-charts (index.js) 85.06KB 23.39KB
plugin-chatbot (index.js) 230.81KB 54.29KB
plugin-dashboard (index.js) 147.53KB 39.95KB
plugin-designer (index.js) 233.95KB 50.08KB
plugin-detail (index.js) 266.04KB 70.50KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 181.16KB 46.84KB
plugin-gantt (index.js) 178.46KB 45.13KB
plugin-grid (index.js) 272.87KB 75.68KB
plugin-kanban (index.js) 63.37KB 19.51KB
plugin-list (index.js) 139.74KB 35.99KB
plugin-map (index.js) 27.17KB 9.00KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 46.66KB 13.13KB
plugin-timeline (index.js) 39.31KB 11.88KB
plugin-tree (index.js) 15.27KB 5.40KB
plugin-view (index.js) 94.68KB 24.36KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 125.37KB 41.04KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.71KB 2.11KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.81KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (product-blocks.js) 0.20KB 0.19KB
types (record-approval-decision.js) 0.20KB 0.20KB
types (record-components.js) 0.20KB 0.19KB
types (record-panels.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: TIER_DEFAULT
Head-sha: a1601be5637c3240ee6832baad6c4a19cf3c4726
Local-runs: none

Face (reviewed 2026-10-18T09:52Z): the Clause-②: yes declaration of the claim 6247753055 on objectui#12894. The PR's file list carries no packages/spec/src/** path and no governed path.

Inputs read:

  • the card, the claim, and objectui#12725 item 4;
  • the diff at the head above, with the changeset;
  • @object-ui/plugin-chatbot's entry;
  • the producer at cloud 1e3e04f8:
    • service-ai-studio/src/tools/change-posture.ts ChangePosture (:84) and DraftState / PendingDraft (:212-224);
    • metadata-tools.ts postureEnvelope (:1980) and ExistingRowsLine (:2012).

Nothing was built or run locally.

① Derived judgments

  • ProposedChanges (exported) gains optional posture?: ChangePosture and primaryAction?: 'save_draft' | 'confirm'.
    • ChangePosture { mode: 'draft' | 'publish'; liveRecords; platformPackage?: true; buildTurnRows? } is exactly postureEnvelope's return type.
    • primaryAction is the producer's two literals; any other value lifts nothing.
    • Correct.
  • ReplayOutcome (exported) gains optional draftState, existingRows and appliedCard, kept on a drafted replay only:
    • ChangeDraftState { pending, items: { type, name }[], others } is the producer's DraftState / PendingDraft;
    • ExistingRowsLine { object, field, label, rows } is the producer's ExistingRowsLine;
    • AppliedCard { title, lines: string[] } is describeLandedChange's appliedCard.
    • A value missing a required member is dropped whole (pinned).
    • Correct: the reader declares nothing the producer does not write.
  • ChatbotEnhancedToolInvocation references those two types instead of inline copies: the same shape plus the optional members. Correct.
  • ChatbotEnhancedProps gains optional onReviewDrafts(packageId) and onDiscardDrafts(packageId), host doors in the shape of the existing onPublishDrafts.
    • Without them the applied card keeps today's Publish button and draws no 撤销 (pinned). A prop nobody passes draws nothing, so nothing is declared that the runtime does not honour.
    • Correct.
  • TranslationKeys gains 25 chatbot.changes.* keys (with plural slots) in every pack. No key is removed, and no en value changes. Correct.
  • Behaviour:
    • 存为草稿 on save_draft; the sent confirm message is unchanged, so cloud's approval reading is untouched.
    • 撤销 asks first, naming the count, when draftState.others > 0, when there is no draftState, or when the host's live count exceeds this change's.
    • discard-drafts discards every pending draft of the package (framework pin be076dad28a0, discardPackageDrafts).
    • Correct, and the safer side.

② Semver level

The changeset bumps @object-ui/plugin-chatbot and @object-ui/i18n minor. Optional members, optional props and new keys fit minor; there is no major. Consistent.

③ Boundary flags

  • The dev's open question (where the console wiring lands) is answered by the seat:
    • it lands on objectui#12894 itself, which this PR leaves open (Part of #12894; the PR body's first line is corrected in this act);
    • it dispatches after objectui#12725's phase 5, which holds app-shell;
    • it needs ChatPane to pass both doors, plus a way into StudioDesignSurface's changesOpen.
  • None goes to the maintainer.

Implemented-by: claude/issue-12894-copilot-draft-card
Reviewed-by: session_01Cbdek8SKSHxddGqTPZXdqX

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · repo:cloud#2 · session session_01Cbdek8SKSHxddGqTPZXdqX · 2026-10-18T09:53Z

Thread-read: objectui#12894's newest comment is the os-dev-report. PR #12951 is at head a1601be5.

  • 形态:
    • Draft, base main (0707b4ae merged into the branch). Assignee os-zhuang.
    • First lines Part of #12894 (corrected in this act from Fixes; see 落地条款), Part of #12725, Clause-②: yes.
    • 17 files, +1710/−33:
      • mapMessages.ts: the two detectors keep the declared keys;
      • ChatbotEnhanced.tsx: change card v2 and the applied card;
      • ten locale packs, 25 keys;
      • two test files and a fixture;
      • plugin-chatbot.mdx;
      • a changeset.
    • No governed path, and no packages/spec/src/** path.
  • 前提实测 (the seat's own reads):
    • The widened types equal the producer's at cloud 1e3e04f8, member by member: postureEnvelope, DraftState / PendingDraft, ExistingRowsLine, appliedCard. The contract review on this PR has the detail.
    • The doors are not reachable in the console today, and the PR says so. 撤销 and 查看变更并发布 are host doors (onDiscardDrafts / onReviewDrafts, in the shape of onPublishDrafts), and no host passes them until app-shell's ChatPane does.
    • So in the console the applied card draws its title and lines and keeps today's Publish button.
    • The card's user outcome (undo and review from the card) is not delivered by this PR alone. Hence Part of #12894.
  • 门禁 check-runs on a1601be5: green or an expected skip at landing (the gate below reads them). Local, per the report:
    • plugin-chatbot 68 files / 711 tests (= 68 tracked);
    • i18n 83 / 1325 (+13 skipped);
    • both type-checks 0, and lint 0 errors;
    • 37 check:* and changeset scripts 0.
    • Ablation: primaryAction dropped from the mapper turned 6 of 32 red, the button reading 确认修改. It was restored by blob hash with an empty git diff HEAD.
    • zh and en screenshots are on claude/issue-12894-evidence 25b88b08.
  • 接受的偏差:
    • 机制假设 3 was falsified: the chat has no fetch of its own. 撤销 is a host door, as publish already is. That is the right home (contract-first), not a work-around.
    • A removal row draws a red -, which the artboard does not show. The producer emits removals, so the card draws them.
    • 撤销 also asks first when there is no draftState, or when the live count is higher: the safer side, given discard-drafts discards every pending draft.
    • The app-shell type-check and suite are left to CI. It is the one consumer, and the members are optional.
    • The dev fetched origin/main once in the shared cloud checkout (a ref move only; allowed).
  • 范围外去向:
    • The console wiring (ChatPane passing both doors, plus a way into StudioDesignSurface's changesOpen) stays on objectui#12894 itself, which this PR leaves open. It is dispatched after objectui#12725's phase 5, which holds app-shell. No new card.
    • Acceptance notes, carrier none:
      • a cache-restored conversation draws the drafted badge, not the applied card;
      • after a reload an undone card can offer 撤销 again without the count check;
      • no schema declares the change envelopes, so the fixtures copy the producer's source.
  • 落地条款:
    • In this act: the PR body's first line is corrected, and the TIER_DEFAULT contract review (PASS) is posted.
    • With every check green, ready and auto-merge follow.
    • After the merge objectui#12894 stays open for the console wiring, serial after objectui#12725's phase 5.

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 package: i18n plugin tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants