Skip to content

feat(app-shell): the Studio Changes badge and the item header follow the copilot's drafts as they land (objectui#12725, part) - #12952

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-12725-draft-badge-marks
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-12725-draft-badge-marks

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #12725
Clause-②: no

What changes

Phase 5 of objectui#12725 takes two remainder items of the card: item 5 (the 变更 badge counts the copilot's drafts the moment they land; the item header shows 「草稿 · N 项未发布」) and item 3's marks (the 草稿 chip and the 来源 line), which were measured first and are not drawn (below).

  • The badge re-reads as the copilot's drafted result lands, not when the turn ends. ChatPane (the chat host the Studio copilot mounts) announces each tool result that carries an authoring envelope (draftReview, or a 确认修改 replay's replayOutcome) on the assistant bus (emitMetadataRefresh) the moment it appears in the stream. Each result is announced once, keyed by its message and its position in that message; the results already in the thread when the pane mounts are history and are not announced. No reload, no navigation: the badge's own ledger (usePendingDrafts, GET /meta/_drafts) already re-read on that pulse.
  • The item header shows 「草稿 · N 项未发布」 (en "Draft · N unpublished") on the Interfaces, Data and Automations pillars while the open item is a pending draft, in place of the "Unpublished draft" chip. It reads the ledger the badge counts (new studioItemDraftStatus.ts, provided by the surface to its pillars), so a draft the copilot writes to the open item shows as it lands, and the line goes when a publish or a discard takes the item off the ledger. N is the badge's number: publishing is package-level, so the line says this item is one of N unpublished changes, and its tooltip says that. The pillar's own reading of its item (taken at load) counts only while the ledger is behind a Studio save (objectui#11787's behindSave); where the ledger could not be read, the header keeps the old chip with no count.
  • Two new rows in the Studio designer table (en, zh): engine.studio.itemDraftPending, engine.studio.itemDraftPendingTitle.

The measured chain (before this change, at 32b492b6)

  • What the badge reads: StudioDesignSurface holds usePendingDrafts({ packageId }), whose fetchPendingDrafts reads GET /api/v1/meta/_drafts?packageId=…, the route the framework's listDrafts serves. The count is the row count (shownPendingCount, at least one while behindSave). The cloud envelope's draftState is read from the same listDrafts server-side (cloud f9e45145, readDraftState in change-posture.ts).
  • When it re-reads: on mount; on publishNonce; on a Studio draft save (onDraftSaved → refresh({ afterSave: true })); and on the assistant bus's pulse, which usePendingDrafts subscribes to (subscribeMetadataRefresh).
  • Who sends the pulse for a copilot result: only ChatPane, in the effect commented "objectui#5801 — when a turn that STAGED or PUBLISHED something finishes": it returned early while isLoading, and read the last assistant message only. So a drafted result reached the badge only when the stream finished, after the agent's whole narration.
  • The seam. The Studio copilot is StudioChatDock → StudioCopilotConversation → ChatPane; ChatPane owns the stream (useObjectChat) and is the only host-side holder of the tool results. ChatDock.tsx is the console's own dock, not Studio's. The callback the chat already exposes to its host, ChatbotEnhanced's onDraftArtifacts, is not enough: it fires only when the cumulative type:name set grows (a second change to the same item, or one after a publish, never fires it), and mapMessages suppresses draftReview on a confirm-replay (replayOutcome ? undefined : detectDraftResult(result)), so the 确认修改 path, the one cloud#2924's draft posture goes through, never reaches it. The change therefore lives in ChatPane's own effect (AiChatPage.tsx); ChatbotEnhanced.tsx and mapMessages.ts are untouched.
  • The header before: each pillar's chip read its own hasDraft, taken once when the item loads and set by its own save, and not re-read on the pulse; an object leaf on Interfaces carries none. A copilot draft to the open item never marked its header (live reading below).

Item 3, the 草稿 chip and the 来源 line: measured, not drawn

No declared key carries per-field draft provenance (who drafted it, from which surface, when), so nothing is drawn for item 3, and nothing is inferred from timing or from "the last tool call touched this field".

  • The framework's draft rows (pin be076dad28a0): ListDraftsResponseSchema (packages/spec/src/api/protocol.zod.ts) declares, per draft row, type, name, member, label, packageId, updatedAt and updatedBy. Those are per ITEM, not per field. SysMetadataRepository.listDrafts fills updatedBy from row.updated_by ?? row.created_by, the acting user; the copilot stages with that same user as actor (cloud stageDraft passes input.actor, which the tools set from exec?.actor?.id), so a copilot draft and the user's own hand edit carry the same updatedBy. sys_metadata's source column (filesystem | database | api | migration) is storage origin, and listDrafts does not project it.
  • The copilot's envelopes (cloud f9e45145, applyDraft and describeLandedChange in metadata-tools.ts): status, type, name, packageId, summary, changedKeys, note, then posture, draftState (pending, items as type/name, others), existingRows and appliedCard. changedKeys names the keys one call wrote, in the conversation only: it carries no time, is not stored with the draft, and is not true any more once the user or a later call edits the same key. Drawing the chip from it is exactly the "last tool call touched this field" inference the dispatch rules out.
  • The producer card this needs is in the report's out-of-scope findings.

Tests

  • New StudioDesignSurface.copilotDraftBadge-12725.test.tsx (5): the real surface, top bar, usePendingDrafts, right slot and Studio copilot (StudioChatDock → StudioCopilotConversation → ChatPane), the chat package's own uiMessagesToChatMessages fed the producer's envelope shapes; the transport plays a scripted stream. (1) a drafted apply_edit result re-reads the badge to 2 while the turn is still running, with the same surface tree and location; (2) a 确认修改 replay (replay_… id) does the same; (3) each result is announced once, later chunks and the turn's end send no second pulse; (4) CONTROL: a read tool's result sends no pulse; (5) a drafted result already in the thread at mount is history, not announced.
  • New StudioDesignSurface.itemDraftLine-12725.test.tsx (4): the Data pillar's header shows the line with the badge's number as the copilot's draft lands; gone after a publish (the surface's own doPublish); gone after a discard takes the item off the ledger; CONTROL: another item's draft is counted by the badge and marks no header.
  • New __tests__/studioItemDraftStatus-12725.test.ts (7): the derivation rules one by one (listed, stored-type fold, absent, a stale own reading, the behindSave bridge, no ledger, no item).
  • Before (base 32b492b6 sources checked out over the committed tests, restored by git checkout HEAD, blobs equal to HEAD and git diff HEAD empty): 7 red, 2 green (the two CONTROL cases). Red: the three landing cases (no pulse while the turn runs), the history case (the base announced a hydrated thread's last authoring turn on mount: 1 pulse), and the three header-line cases (the header never shows the copilot's draft).
  • Ablation (an isolated worktree at 111d6816, through objectstack's scripts/ablation-replace.mjs in wrap mode; no dist step, the tests import app-shell src relatively): A1, ChatPane's if (landed) emitMetadataRefresh(); replaced by void landed;: 3 red (the three landing cases), the CONTROL and the history case green; restored, blob == HEAD (ad1d0a6041b1) and git diff HEAD empty. A2, the header line's if (!listed && !(ownDraft && pending.behindSave)) return undefined; replaced by if (!ownDraft) return undefined; (the pillar's own reading only, the old source): 6 red (the three header-line cases, three derivation cases), 5 green; restored, blob == HEAD (6c981c2758f8) and git diff HEAD empty.
  • Full package vitest run packages/app-shell/ (the package script's command) at 111d6816, in 8 --shard runs under the verify lock (one run exceeds the 10-minute foreground cap), each EXIT 0: 1568 files run (1567 passed, 1 skipped) of 1568 tracked test files; 14740 tests passed, 9 skipped. After merging origin/main (0707b4ae, which brought objectui#12724's mapMessages and ChatbotEnhanced changes) at a2326866: the new files plus console/ai/__tests__, studio-design/__tests__, layout, preview and the six surface files that read the badge or the status chip, 176 files / 1118 tests, EXIT 0. The whole package was not re-run after the merge; that run is CI's.

Gates

Local, exit codes captured before any pipe.

  • pnpm --filter '@object-ui/app-shell^...' build: VERDICT command-exit 0, at 111d6816 and again at a2326866.
  • pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json): EXIT 0, 0 error TS, at b151ed07 and at a2326866; the test project lists all three new test files (--listFilesOnly).
  • eslint on the seven changed files: 0 errors; warnings per file unchanged against the base (AiChatPage.tsx 34, StudioDesignSurface.tsx 13, i18n.ts 0); the new files 0.
  • EXIT 0 at a2326866: check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:new-line-citations (0 new), check:control-bytes, check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:changeset-claims, changeset:check, scripts/check-changeset-presence.mjs (7 source files of 1 released package, 1 changeset). EXIT 0 at 111d6816: check:i18n-dead-keys (report-only; the two new keys are not among its candidates), check:pending-changeset-literals, check:spec-symbols, check:phantom-deps, check:self-import, check:esm-specifiers, check:unreferenced-sources, check:handler-key-reads, check:metadata-write-doors, check:comment-mask-corpus.
  • CI check-runs are the PR's own.

Live reading

Evidence branch claude/issue-12725e-evidence (917af6f8), evidence/12725e/: screenshots at 1010 and 1280 px, before (32b492b6) and after (111d6816), the DOM readings as JSON, and the driver. Published backend 18.0.0-next.3 (showcase); the copilot's chat stream and the _drafts ledger are played by the driver (the community backend serves no agent): the tool answers drafted with cloud#2924's envelope shape, then the agent narrates for 20 s.

run 2.5 s after the tool answered, turn still running turn ended first ledger read after the tool answered
before 1280 / 1010 no badge, no header line badge 2, no header line 20.1 s later, about 0.1 s after the turn ended
after 1280 / 1010 badge 2, 「草稿 · 2 项未发布」 badge 2, line 0.63 s / 0.65 s later, while the agent was still narrating

No reload and no navigation in any run. At 1010 px the header chip folds to its pencil glyph (objectui#12611), its text in the tooltip.

Acceptance notes

  • Overlap with objectui#12724. Its phase 2 claim names AiChatPage.tsx "only for labels it passes"; it landed as PR feat(ai-build): the plan and result cards draw the v2 rows cloud declares, and step names read as sentences (objectui#12724) #12947 before this branch merged main, and touched no line of this file. This change is the authoring-emit effect only.
  • Pulse cost. The pulse now fires once per authoring result instead of once per authoring turn. It is the same pulse (every mounted MetadataProvider re-reads its loaded types, the Studio rails re-read); a turn with several authoring calls sends several, spaced by the agent's own calls.
  • Not in this change, reported to the seat: an in-turn published result (cloud's maybeAutoPublish, which promotes every pending draft of the package) sends no pulse, so the badge keeps the swept drafts; at 1010 px the copilot's chat panel overflows its 300 px slot once the thread holds a message. Both are in the report.
  • Not this phase: item 4 (change card v2) and objectui#12894, after objectui#12724.

Generated by Claude Code

…opilot's drafts as they land (objectui#12725)

ChatPane announces each authoring tool result on the assistant bus the
moment it lands, keyed by message and position, instead of when its turn
ends; the Studio badge's ledger re-reads on that pulse. The pillars' item
headers read the same ledger: 「草稿 · N 项未发布」 while the open item is a
pending draft, N the badge's number.

Claude-Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX
Co-authored-by: Claude <noreply@anthropic.com>
…our it is (objectui#12725)

Claude-Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX
Co-authored-by: Claude <noreply@anthropic.com>
… item header line (objectui#12725)

Claude-Session: https://claude.ai/code/session_01Cbdek8SKSHxddGqTPZXdqX
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 75 chunks) 3158.8 KB 3744.1 KB
Main entry chunk (gzip) 80.6 KB 350 KB
Entry file index-B2lnFigY.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) 217.79KB 51.51KB
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

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

Thread-read: objectui#12725's newest comment is the os-dev-report 6248434538. PR #12952 is at head a2326866.

  • 形态:
    • Draft, base main (0707b4ae merged into the branch). First lines Part of #12725 and Clause-②: no. Assignee os-zhuang.
    • 8 files, +879/−21:
      • AiChatPage.tsx (ChatPane's authoring-emit effect only);
      • StudioDesignSurface.tsx, plus a new studioItemDraftStatus.ts;
      • the designer i18n table metadata-admin/i18n.ts;
      • three test files;
      • a changeset (@object-ui/app-shell minor).
    • No governed path. No packages/plugin-chatbot file, as the claim required.
  • 前提实测 (the seat's own reads):
  • 门禁 check-runs on a2326866: green or an expected skip at landing (the gate reads them). Local, per the report:
    • the full app-shell suite at 111d6816, sharded 8 ways: 1568 / 1568 tracked files, 14740 tests;
    • after the merge, 176 files / 1118 tests over the touched areas;
    • type-check 0, and lint with no new warnings;
    • 22 check:* scripts 0.
    • Before reading at base: 7 of 9 red.
    • Ablations: A1, the pulse removed, turned the three landing cases red; A2, the ledger rule widened, turned 6 red. Both were restored by blob hash with an empty git diff HEAD.
    • Evidence (12 PNGs, 1010 / 1280, before / after) is on claude/issue-12725e-evidence 917af6f8.
  • 接受的偏差:
    • The seam was ChatPane, not the dock. The hypothesis that the chat exposes a callback was falsified: onDraftArtifacts never fires on a 确认修改 replay, which is the path cloud#2924's posture takes. So AiChatPage.tsx's effect is the surface. objectui#12724's claim on that file had already landed, with no overlap.
    • The header line replaces the 'Unpublished draft' chip on all three pillars that show one, not only on Interfaces, so the chip stays one rule (objectui#12524). N is the package's pending count, as the artboard draws it.
    • Citations by file and symbol, per objectui AGENTS.md.
    • Live-reading stubs for the agent stream and the ledger, disclosed in the evidence README.
    • Commit b151ed07's subject carries CJK. Not rewritten, because there is no force-push.
  • 范围外去向: the remainder of objectui#12725, recorded in its LANDED.
  • 落地条款:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants