Repository navigation
Commit 3cc4563
feat(studio): the right slot counts the copilot's items awaiting confirmation (#27789)
domain:ui#3 · os-bill · session_01Utp6CaJoinvGQSbHeDaWBe
<!-- claude-projects-attribution: {"github_login":"os-bill"} -->
_Requested by **Bill** · [project
thread](https://claude.ai/code/project/chan_016iMXWSZDzqWRWvWzdNo8wg?thread=cmsg_016iMXWSZDzqWRWvWzdNo8wgUY8ckC6r2uhn1K73kkTS9f)_
## 维护者速读
- 改了什么:Studio 右栏的「AI 副驾」分段上加一个数字徽标,看属性时底部折叠条读「AI 副驾 · N 项待确认 · 回到对话 →」;N
是副驾最新一张「修改方案 · N 项」卡的条数(搭建方案算 1 项)。点了「存为草稿 / 确认」或改动落地,徽标和文字立刻消失。
- 为什么改:#25943 Target 第 1、2 项要「N 项待确认」计数;main
上只有一个「方案待确认」小圆点,而且只认搭建方案,副驾给已上线应用提的「修改方案」卡根本不会点亮它。
- 风险与回滚:控制台的对话启动器(右下角 FAB、ChatDock
边条)现在在「修改方案」卡待确认时也会亮提示点,这是有意的行为扩展;plugin-chatbot
的回调多一个参数,只读第一个参数的宿主不受影响。回滚即 revert 本 PR。
-
先看:`ui/packages/plugin-chatbot/src/ChatbotEnhanced.tsx`(`newestProposal`
一段)、`ui/packages/app-shell/src/assistant/assistantBus.ts`(按会话存条数)、`ui/packages/app-shell/src/views/studio-design/StudioRightSlot.tsx`。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.planApprovalCount-25943.test.tsx
packages/app-shell/src/views/studio-design/__tests__/studioRightSlot-12725.test.tsx`,预期全部通过,其中
zh 用例断言折叠条读「AI 副驾 · 3 项待确认」。
- 需要你做的:无,席位落地。
Part of #25943
Clause-②: yes (widening)
Metadata-protocol: none
## What changes
The card's Target 1–6 were measured on `main` @ `481ade61` first: items
1–6 already hold (the right slot, select-to-properties, the copilot
strip and canvas hint, no 源码 tab, the status line and the item ⋯ menu,
change card v2, the 变更 badge and 「草稿 · N 项未发布」, pillar labels from
`lg`), with one gap: the copilot segment and the folded strip carried a
boolean 「方案待确认」 dot, not a count, and only for a build plan. This PR
closes that gap.
- **`@object-ui/plugin-chatbot`, `ChatbotEnhanced`.** The
newest-proposal reading behind `onPlanApprovalPendingChange` now also
counts a change plan (`proposedChanges`, the 「修改方案 · N 项」 card). A
change plan is pending while its card still offers its confirm / 存为草稿
button: no replay verdict, no later same-tool commit, no click in flight
(`confirmPendingIds`), no quota refusal (`quotaBlockedChanges`). The
callback gains a second argument, `count`: the rows of a pending change
plan, 1 for a pending build plan, 0 when nothing is pending. It fires
when either value changes, and still keys on the reading alone, not the
callback's identity.
- **`@object-ui/app-shell`, assistant bus.**
`publishPlanApprovalPending` takes an optional `count` (defaults to 1
while pending) and keeps it per conversation beside the owner.
`usePlanApprovalPending()` is unchanged in meaning (any pending reading
of the signed-in user). New `usePlanApprovalPendingCount()` sums the
user's conversations. `ChatPane` publishes the chat's count with its
boolean.
- **Studio right slot.** `StudioCopilotStatus` carries
`planPendingCount` in place of `planPending`. The copilot tab's dot
becomes a count badge, and the strip reads `AI 副驾 · {count} 项待确认` (en
`AI copilot · {count} awaiting your approval`) through the new studio
key `engine.studio.slot.pendingCount`; the tab's accessible description
is the same sentence.
- **Docs.** `ui/packages/plugin-chatbot/README.md` describes the
two-argument callback and the change-plan reading.
`content/docs/frontend/` has no page that documents this prop, so none
changed.
## Tests
- New `ChatbotEnhanced.planApprovalCount-25943.test.tsx` (5 cases, real
wire envelopes through the real mapper, each asserting the reading and
the card it mirrors): a change plan awaiting 存为草稿 reads `[true, 2]`; the
click reads `[false, 0]` at once (applying); a drafted replay reads
`[false, 0]`; a build plan reads `[true, 1]` and a newer change plan
supersedes it with its rows (an unchanged reading is not re-announced);
nothing proposed reads `[false, 0]`.
- New `planApprovalPendingCount-25943.test.tsx` (4 cases) for the bus:
the per-conversation sum, a count change, a reading without a count
counts 1, another user's readings are not counted.
- `AiChatPage.planApprovalReachesLaunchers-11666.test.tsx` gains one
case: the chat's count reaches the bus and its end clears it.
- `StudioDesignSurface.rightSlot-12725.test.tsx`: the marker case now
counts (badge `2`, description and strip text), plus a case with nothing
pending. `studioRightSlot-12725.test.tsx` gains the zh strip case 「AI 副驾
· 3 项待确认」.
- Five existing slot test doubles move from `planPending` to
`planPendingCount`.
Readings, all on the final head `413cff81` (the work merged with
`origin/main`) under the verification lock unless a line says otherwise:
<details><summary>Package suites: plugin-chatbot 92 files / 854 tests,
i18n 90 files / 1380 passed + 13 skipped</summary>
```
pnpm --filter @objectstack/spec build :: exit 0 (spec moved in the merge)
pnpm --filter @object-ui/plugin-chatbot test :: exit 0 · Test Files 92 passed (92) · Tests 854 passed (854)
pnpm --filter @object-ui/i18n test :: exit 0 · Test Files 90 passed (90) · Tests 1380 passed | 13 skipped (1393)
```
</details>
<details><summary>app-shell and console: the 287 test files that name a
touched module, 2130 / 2130 passed</summary>
Every `packages/app-shell/src/**` and `apps/console/src/**` test file
that names `StudioDesignSurface`, `StudioRightSlot`, `StudioAiCopilot`,
`assistantBus`, `AiChatPage`, `ChatDock`, `ConsoleChatbotFab` or
`planApproval` (`git grep -l`), `--reporter=verbose`: `Test Files 287
passed (287) · Tests 2130 passed (2130)`, lock VERDICT command-exit 0.
The whole `@object-ui/app-shell` suite (1,911 files) was started and
stopped after 16 minutes with no output (non-TTY reporter); it is left
to CI `Test UI`, which shards it.
</details>
<details><summary>Type-check and lint</summary>
```
pnpm --filter @object-ui/plugin-chatbot run type-check :: exit 0 (tsc --noEmit && tsc -p tsconfig.test.json)
pnpm --filter @object-ui/i18n run type-check :: exit 0
pnpm --filter @object-ui/app-shell run type-check :: exit 0
eslint --no-inline-config on the 11 changed source and test files :: exit 0 (0 errors; warnings are pre-existing)
pnpm --dir ui run check:new-line-citations :: exit 0 (0 new citations)
node scripts/check-platform-checklist.mjs --changed-from origin/main :: exit 0
```
</details>
<details><summary>Ablations (four, each through
scripts/ablation-replace.mjs, restored with blob == HEAD and git diff
HEAD empty)</summary>
- `ChatbotEnhanced.tsx`, the newest-proposal scan without
`tool.proposedChanges` → `planApprovalCount-25943`: 2 failed / 5 (the
change plan reads, and the newer change plan's rows).
- `ChatbotEnhanced.tsx`, the count fixed to `1` →
`planApprovalCount-25943`: 2 failed / 5.
- `StudioRightSlot.tsx`, the strip text back to
`engine.studio.slot.planPending` → `studioRightSlot-12725` zh case and
`StudioDesignSurface.rightSlot-12725` count case: 2 failed / 23.
- `assistantBus.ts`, the sum counting each reading as 1 →
`planApprovalPendingCount-25943` and the ChatPane count case: 2 failed /
9.
- app-shell and plugin-chatbot tests import `src`, so no dist build sits
between the mutation and the run.
</details>
<details><summary>Derived gates (dispatch-gates --commands on this
diff): 35 run, 35 exit 0, reconciled</summary>
```
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
```
`pnpm --dir ui exec vitest run scripts/` first exited 1 with four 15 s
timeouts (`check-hand-rolled-comment-mask`, `check-vi-mock-inherit`)
while it ran beside the locked test run; re-run alone under the lock:
`Test Files 169 passed | 2 skipped (171) · Tests 4902 passed | 2
skipped`. `dispatch-gates --ran`: 35 derived, 35 run, 0 NOT-MEASURED, 0
UNRUN.
</details>
## Acceptance notes
- **Card Acceptance.** 1: the slot's own cases were already on `main`
(`StudioDesignSurface.rightSlot-12725`, `pillarsClaimSlot-12725`,
`leafColumnSelection-12725`, `kanbanCardSelection-12725`,
`copilotDraftBadge-12725`, `itemDraftLine-12725`); this PR adds the
count. 2: screenshots are **NOT MEASURED** — a local backend serves no
AI agent catalog, so the copilot half never mounts and no proposal can
be produced; the DOM is pinned by the jsdom cases above. 3: see the
readings above; the whole `@object-ui/app-shell` suite (1,911 files) is
left to CI `Test UI`.
- **What stays on the card (B, not ui work).** The live 草稿 chip and 「由
AI 副驾在 N 分钟前作为草稿加入」 do not show on staging because the copilot's drafts
are stamped `by: "api"`: the producer (cloud `service-ai-studio`,
`stageDraft` / `applyDraft`) does not pass `surface: 'copilot'`, and
this repo's HTTP doors refuse `copilot` from a client by design. The ui
code for the marks is on `main`. Carrier: cloud.
- **Measured, not a proven ui defect (C).** On staging the Interfaces
inspector opened `hcpl_book.default` (404 on `GET /meta/view/...`) while
the copilot drafted `hcpl_book.list`. Reading of `main`:
`listViewTargetOf` falls back to `OBJECT.default` when no merged list
view is flagged `isDefault`, and the runtime's `ObjectView` promotes the
object's embedded `list` under that same identity (`defaultListViewId`)
as its default tab, so for an object that declares `list` the inspector
and the runtime agree, and the view the copilot edited is a secondary
one. They would disagree only for an object with neither `list` nor a
flagged view, where the runtime shows the first list view; whether
`hcpl_book` is that case was not measurable from here. Not changed.
- **Behaviour widened on purpose.** The console launchers' waiting
marker (objectui#11666) now also lights for a pending change plan, since
the reading counts one.
- **The count is the user's, not the package's.** Like the boolean
before it, the bus sums every conversation of the signed-in user, so a
plan awaiting approval in another app's conversation also counts in this
Studio's strip.
- `engine.studio.slot.planPending` is no longer read but stays in the
studio table, so the i18n change only widens; retiring it is a narrowing
for a later sweep.
- The harness's after-PR assignee / review request was skipped on the
maintainer's rule (the seat reviews first).
- **An empty change plan.** `ChatbotEnhanced` would report `(true, 0)`
for a pending `proposedChanges` with no rows; the assistant bus clamps a
pending reading to at least 1. No such card reaches the chat today: the
mapper lifts `proposedChanges` only when the envelope carries at least
one parseable change row (objectui#8343), so there is no visible defect
in this repository. (Added by the seat from contract review 6510852862.)
## Verification
See the readings under Tests.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Utp6CZkqrAJBqDcZ2PKjos)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2efd903 commit 3cc4563
19 files changed
Lines changed: 459 additions & 80 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- assistant
- __tests__
- console/ai
- __tests__
- views/studio-design
- __tests__
- i18n/src/namespaces/studio
- plugin-chatbot
- src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
193 | 193 | | |
194 | 194 | | |
195 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
196 | 207 | | |
197 | | - | |
198 | | - | |
| 208 | + | |
| 209 | + | |
199 | 210 | | |
200 | 211 | | |
201 | 212 | | |
| |||
205 | 216 | | |
206 | 217 | | |
207 | 218 | | |
208 | | - | |
| 219 | + | |
209 | 220 | | |
210 | 221 | | |
211 | 222 | | |
212 | 223 | | |
213 | | - | |
214 | | - | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
215 | 227 | | |
216 | 228 | | |
217 | 229 | | |
218 | 230 | | |
219 | 231 | | |
220 | 232 | | |
| 233 | + | |
221 | 234 | | |
222 | 235 | | |
| 236 | + | |
223 | 237 | | |
224 | | - | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
225 | 245 | | |
226 | | - | |
| 246 | + | |
227 | 247 | | |
228 | 248 | | |
229 | 249 | | |
| |||
234 | 254 | | |
235 | 255 | | |
236 | 256 | | |
237 | | - | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
238 | 267 | | |
239 | 268 | | |
240 | 269 | | |
241 | 270 | | |
242 | 271 | | |
243 | 272 | | |
244 | | - | |
245 | | - | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
246 | 276 | | |
247 | 277 | | |
248 | 278 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2299 | 2299 | | |
2300 | 2300 | | |
2301 | 2301 | | |
| 2302 | + | |
| 2303 | + | |
2302 | 2304 | | |
| 2305 | + | |
| 2306 | + | |
| 2307 | + | |
| 2308 | + | |
| 2309 | + | |
2303 | 2310 | | |
2304 | 2311 | | |
2305 | 2312 | | |
2306 | 2313 | | |
2307 | 2314 | | |
2308 | 2315 | | |
| 2316 | + | |
2309 | 2317 | | |
2310 | | - | |
| 2318 | + | |
2311 | 2319 | | |
2312 | 2320 | | |
2313 | 2321 | | |
| |||
2912 | 2920 | | |
2913 | 2921 | | |
2914 | 2922 | | |
2915 | | - | |
| 2923 | + | |
2916 | 2924 | | |
2917 | 2925 | | |
2918 | 2926 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
108 | | - | |
| 107 | + | |
| 108 | + | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
111 | 116 | | |
112 | 117 | | |
113 | 118 | | |
| |||
175 | 180 | | |
176 | 181 | | |
177 | 182 | | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
178 | 199 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
147 | 147 | | |
148 | 148 | | |
149 | 149 | | |
150 | | - | |
| 150 | + | |
| 151 | + | |
151 | 152 | | |
152 | 153 | | |
153 | 154 | | |
| |||
162 | 163 | | |
163 | 164 | | |
164 | 165 | | |
165 | | - | |
166 | | - | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
167 | 169 | | |
168 | 170 | | |
169 | 171 | | |
| |||
200 | 202 | | |
201 | 203 | | |
202 | 204 | | |
203 | | - | |
| 205 | + | |
204 | 206 | | |
205 | 207 | | |
206 | 208 | | |
207 | 209 | | |
208 | 210 | | |
209 | | - | |
210 | | - | |
| 211 | + | |
| 212 | + | |
211 | 213 | | |
212 | 214 | | |
213 | | - | |
| 215 | + | |
214 | 216 | | |
215 | 217 | | |
216 | 218 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
168 | 168 | | |
169 | 169 | | |
170 | 170 | | |
171 | | - | |
| 171 | + | |
172 | 172 | | |
173 | | - | |
| 173 | + | |
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
120 | | - | |
| 120 | + | |
121 | 121 | | |
122 | | - | |
| 122 | + | |
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
108 | | - | |
| 108 | + | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
0 commit comments