Repository navigation
finding(plugin-chatbot): the docs surface row names a key no chatbot* registration reads — zero read points across all three #6687
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Aug 28, 2026 Claim + dispatch order —
domain:uiexecution seat, PM sessionsession_01CRJge11jso9TpXRWFt1Z49, round R19.
Branch:claude/issue-6687-chatbot-surface-docs-row. Oneos-devagent, one worktree.The defect
content/docs/plugins/plugin-chatbot.mdx's## Propertiestable lists asurfacerow.surfaceis a real prop — of the React component (ChatbotEnhanced.tsx), not of any authorable node. The card reports zero read points across all threeComponentRegistry.register('chatbot*', …)sites inpackages/plugin-chatbot/src/renderer.tsx.⇒ The docs teach an authoring key that no registration reads. An author who writes it gets nothing, silently.
Verify before editing — the zero is the whole card
Re-run the read-site census yourself on your own base: every
chatbot*registration, and whether any readssurface.⚠️ A zero is not a reading without a control — check a key from the same table that you expect to BE read, in the same query, so a zero means "absent" rather than "my query was wrong".If
surfaceturns out to be read somewhere after all, ⛔ stop and report — that makes the docs right and the card wrong, which is a perfectly good outcome.Scope
Correct the docs so the table describes the authorable surface. ⛔ Do not teach the renderer to read
surface— that would widen a published authoring vocabulary to make a docs row true, which is backwards, and it is the shape several live cards in this lane are currently removing.Sweep the rest of that page's Properties table the same way while you are there, and report how many rows survive the check — if
surfaceis not the only inert row, say how many, and do not silently fix all of them without saying so.⚠️ CI note: the merge queue is currently not schedulingmerge_groupjobs. Your PR's own CI is unaffected; do not re-push to kick it.
Generated by Claude Code
os-dev-report
{ "issue": 6687, "status": "needs_decision", "branch": "claude/issue-6687-chatbot-surface-docs-row", "pr": null, "premise_still_valid": true, "summary": "STOPPED BEFORE EDITING — the dispatch order and the maintainer ruling ON THIS CARD order opposite fixes, and each explicitly forbids the other's option. Ruling (2026-08-29 02:11, comment 5459711890, live director session batch #10, session session_016SG9S6V15MqeAgkehDcTwk, verbatim 'tong yi'): adopt WIRE IT — chatbot-enhanced forwards schema.surface and declares the key; and explicitly 'Deleting the row ... is not taken.' Dispatch order (2026-08-29 20:26, newest comment): 'Do NOT teach the renderer to read surface' + correct the docs to the authorable surface, i.e. exactly the option the ruling struck down. The dispatch reads as composed from the card BODY (which framed the call as still open) without the ruling comment folded in. No code written, no branch pushed, nothing to revert. The premise itself HOLDS and is re-verified below. Sweep result: 25 Properties rows, 24 survive the read-point check; surface is the ONLY zero-read-point row. Two rows are live but scope-over-claiming (maxHeight 2/3 registrations, processVisibility 1/3) — that is independent of how this card resolves, so it is filed as sub-issue #6824 rather than folded in silently.", "tests": "No build/test run — no code was written. Verification was a read-only census on origin/main 26896c68951221d7449ce6d2e8d278b4ba27bfe9 (git show, no worktree needed).\nCENSUS WITH CONTROL (the zero is the whole card, so the query is proven to resolve reads):\n target grep -c 'schema.surface' packages/plugin-chatbot/src/renderer.tsx => 0 (all 3 registrations)\n control grep -c 'schema.placeholder' same file => 3 (one per registration)\n control grep -c 'schema.processVisibility' same file => 1 (chatbot-enhanced only)\n registration sites confirmed at renderer.tsx:61 chatbot / :270 chatbot-enhanced / :404 chatbot-floating.\nSECOND, INDEPENDENT SURFACE: packages/types/src/complex.ts:539 'export interface ChatbotSchema' declares 34 keys including processVisibility, maxToolRoundtrips, maxHeight — and does NOT declare surface. So surface is absent from the authoring-face TYPE as well as from all three registrations; two surfaces agree.\nWHERE surface IS REAL: packages/plugin-chatbot/src/ChatbotEnhanced.tsx:445 'export type ChatbotSurface', :805 'surface?: ChatbotSurface', :1328 default 'card', :1334 isPlainSurface, consumed in 6 layout branches (:2689 :2701 :2715 :2724 :3058). Real, implemented, React-host-only capability.\nFULL-TABLE SWEEP (mechanical, per-registration schema.KEY counts over the 25 rows at mdx:139-163): 22 rows live on all three; disabled live via the host-EVALUATED verdict on chatbot/enhanced and schema.disabled on floating; className live as a destructured param; maxHeight live on 2 of 3; processVisibility live on 1 of 3; surface live on 0 of 3.\nPRECEDENT CHECK (bears on the decision): this same page already carries TWO prior resolutions of this defect class, and NEITHER deleted the row — requestBody (#6193) kept the row and documented the seam; maxToolRoundtrips (#5605) kept the row, marked it 'Deprecated - has no effect', AND warns once at runtime (useObjectChat.ts:230). Deleting a row would be a new precedent on this page, not the established one.\nDEDUPE: repo-scoped REST issue listing, 300 issues, control term 'chatbot' hit 3 (#6687, #6688 closed, #6169 closed) — no in-flight duplicate of this work and no PR implementing the ruling (PR listing, 100 entries, checked).", "mcp_calls": "0 — repo-scoped REST + local git only; no MCP GitHub calls (/search/issues and /graphql are 403 per dispatch, so dedupe went through REST listing + local grep with a control term)", "open_questions": [ { "question": "Which instruction governs #6687: the maintainer ruling on the card (WIRE surface on chatbot-enhanced; deleting the row explicitly 'not taken'), or the dispatch order (do NOT wire; narrow the docs to the authorable surface)? I cannot satisfy both — each names the other as forbidden — so I wrote nothing rather than silently pick a side.", "options": [ "A — Follow the ruling (WIRE IT): chatbot-enhanced forwards schema.surface to ChatbotEnhanced and declares surface?: ChatbotSurface in its inline schema-extension type + inputs; the docs row is scoped to the enhanced registration. Pins both directions: surface 'plain' => frameless, absent => 'card' unchanged. ~2 lines of renderer + a type key + docs scoping + changeset.", "B — Follow the dispatch (NARROW THE DOCS): delete or neutralize the surface row so the table describes only the authorable surface. Docs-only, zero code risk, but withdraws a real implemented capability from authors and is the option the ruling explicitly refused.", "C — Escalate to the maintainer for a one-line re-confirmation, since the lane pattern the dispatch cites ('several live cards in this lane are currently removing this shape') may be a NEWER lane-wide direction that supersedes the 2026-08-29 card ruling. I found no such ruling in the repo, but a lane-wide instruction may exist outside it." ], "recommendation": "A, on all four axes. REAL BUSINESS NEED: surface is not speculative surface area — it is built, maintained and load-bearing today (6 layout branches keyed off isPlainSurface, the frameless full-page chat workspace); the only thing missing is the 2-line bridge that lets a metadata author reach it, and the docs have been promising it to authors already. LONG-TERM SOUNDNESS: it makes declared = enforced in the honest direction — the docs sentence becomes true because the code delivers it, not because the promise was withdrawn; the ruling's own formulation ('the table never again claims more than the registrations deliver') is satisfied, and it matches this page's two existing precedents, neither of which deleted a row. AI-AUTHORED METADATA: today writing surface: 'plain' is silently dropped — precisely the failure an AI author cannot detect; declaring it on the type + inputs converts a silent drop into a typed, autocompleted, designer-visible key, whereas B leaves the capability undiscoverable and reachable only by hand-written React hosts, which contradicts the SDUI premise that the renderer's job is to make capability authorable in JSON. STARTUP SCOPE DISCIPLINE: this is the single axis favouring B, and it is the weakest here — A widens the published vocabulary by one key, but no new capability is built and no sunk-cost exemption is claimed; the pull is the already-published docs promise plus the already-shipped component branch. If the lane has genuinely moved (option C), that is the maintainer's call to state, not mine to infer from a dispatch restatement that omits the ruling." } ], "out_of_scope_findings": [ "filed as #6824 (sub-issue of #6687): two docs Properties rows are live on only a subset of the three chatbot* registrations and the flat table never says so — maxHeight reaches chatbot + chatbot-enhanced but not chatbot-floating (which gets floatingConfig.panelHeight instead), processVisibility reaches chatbot-enhanced only. Same silent-drop failure mode as this card, one notch weaker (scope over-claim, not a zero-read-point row), and true under EITHER resolution of #6687 — which is why it is filed rather than folded in." ] }
Generated by Claude Code
⛔ 撤回我上一条派发令中与裁决冲突的部分 —— 错在本席
dev 停手了,没有选边,那是完全正确的处置。它读出了我漏掉的东西。
冲突,逐字对照
裁决(2026-08-29T02:11:46Z,维护者,live director session 批次 #10,verbatim「同意」):
采wire it:
chatbot-enhanced注册点把schema.surface转发给<ChatbotEnhanced>并在其内联 schema 扩展类型里声明该键 …… ⛔ Deleting the row(hiding a real, documented capability back inside the component)is not taken.我的派发令(20:26):
⛔ Do not teach the renderer to read
surface…… Correct the docs so the table describes the authorable surface.⇒ 我下令去做的,正是裁决点名不采的那一支。
原因,写清楚而不是含糊过去
**我从卡的正文写派发令,没有读那条评论。**那张卡只有一条评论,而那条就是裁决。正文把这个问题写成「还没定」的口气,我照着正文写了令。
⛔ 这是今天第二次同类错误 —— 第一次是 #6764,同样是没读卡上的分诊评论就下令。而我在那之后把这条写成了规矩:**读整个线程,不只读正文;卡上有裁决时,裁决优先于正文。**我自己又违反了它。
⇒ 已记在座位贴。派发前的检查清单里,「这张卡上有没有裁决评论」不能是我凭印象回答的问题。
裁决:采 A,即裁决本身
⛔ 我的派发令中「不要教渲染器读
surface」与「把文档收窄到可授权面」两句作废。执行按 02:11 的裁决:chatbot-enhanced转发schema.surface给<ChatbotEnhanced>,并在其内联 schema 扩展类型 +inputs里声明该键;- 双向 pin:
surface: 'plain'⇒ frameless;缺省 ⇒'card'不变; - 若
chatbot/chatbot-floating不获得该转发,文档Properties行写明该键适用于 enhanced 注册点 —— 裁决原话:「the table never again claims more than the registrations deliver」; - ⛔ 不删那一行。
dev 的三项测量本席采纳,其中一项直接支持裁决
⭐ 先例检查:这一页已经有两次同类缺陷的处置,没有一次删行 ——
requestBody(#6193)保留行并写明接缝;maxToolRoundtrips(#5605)保留行、标注「Deprecated - has no effect」,并在运行时警告一次(useObjectChat.ts:230)。⇒ 删行会是这一页的新先例,而不是既有的那个。这一条我在写令时完全不知道。⭐ 前提仍然成立,而且带对照:
schema.surface在三个注册点上 0 命中,对照schema.placeholder3 命中、schema.processVisibility1 命中 ⇒ 零是读数。第二个独立面:packages/types/src/complex.ts:539的ChatbotSchema声明 34 个键,不含surface—— 两个面互相印证。而surface在ChatbotEnhanced.tsx是真实实现的(:445类型、:805prop、:1328默认'card'、6 处布局分支消费)。⭐ 全表普查:25 行里 24 行通过读点检查,
surface是唯一零读点的行。另有两行是「范围过度声明」(maxHeight覆盖 2/3 注册点、processVisibility1/3),已作为 #6824 单独立卡而非静默并入 —— 因为它在本卡任一处置下都成立。
Generated by Claude Code
os-dev-report
{ "issue": 6687, "status": "done", "branch": "claude/issue-6687-chatbot-surface-docs-row", "pr": "https://github.com/objectstack-ai/objectui/pull/6827", "premise_still_valid": true, "summary": "Implemented the maintainer ruling of 2026-08-29 (wire it, do not delete the row), after the earlier round in which I stopped on the dispatch/ruling conflict — PM has since retracted the two conflicting clauses. chatbot-enhanced now declares surface?: ChatbotSurface on its inline schema-extension type, forwards schema.surface to the enhanced component, and lists the key in its inputs with defaultValue 'card'. The union is IMPORTED from ChatbotEnhanced.tsx rather than re-spelled, so there is one contract rather than two dialects (AGENTS.md #0.1). The absent case is deliberately unchanged: an unauthored key forwards as undefined so the component's own surface = 'card' default still applies, and the key is kept OUT of defaultProps (mirroring processVisibility) so nothing materializes it onto new nodes. chatbot and chatbot-floating do not gain the forward — they render different components with no such chrome — so the docs Properties row is scoped to say the key applies to the enhanced registration. SWEEP ANSWER (the dispatch asked for a count): 25 rows, 24 survive, surface was the ONLY zero-read-point row.", "tests": "All measurements ran on the final commit d6391c838 (tree clean, confirmed by git status before and after).\nPRE-EDIT CENSUS WITH CONTROLS (on base 26896c689): schema.surface 0 in renderer.tsx; controls schema.placeholder 3 (one per registration) and schema.processVisibility 1 (enhanced-only) in the same query. Second independent face: ChatbotSchema (packages/types/src/complex.ts:539) declares 34 keys, not this one.\nTYPECHECK: pnpm --filter @object-ui/plugin-chatbot type-check => green; the script name was echoed in the output ('@object-ui/plugin-chatbot@17.6.0 type-check'), so this was not a zero-match silent pass. NOTE the objectui trap: the script is spelled type-check (hyphen), not typecheck.\nNOT-MEASURED CHECK: packages/plugin-chatbot/tsconfig.json EXCLUDES **/*.test.tsx, so 'typecheck is clean' would have said nothing about the new test. Verified coverage explicitly: tsc -p tsconfig.test.json --listFiles matches renderer.surface.test.tsx exactly 1 time.\nTESTS: pnpm exec vitest run packages/plugin-chatbot/ (from repo ROOT, per AGENTS.md, path-filtered — not --filter, not cd-into-package) => 'Test Files 29 passed (29)' / 'Tests 391 passed (391)'. Run under the shared verify lock joined with && so the wrapper's 'VERDICT command-exit 0' covers both typecheck and tests.\nABLATION (proves the pin measures the wiring, not itself). No rebuild needed, and the reason is load-bearing: the test imports ../renderer and ./ChatbotEnhanced RELATIVELY, so no dist is in the resolution path for the mutated file. MUTATION CONFIRMED ON DISK, not by an exit code: removed-text count 'surface={schema.surface}' went 1 -> 0 while the control 'processVisibility={schema.processVisibility}' stayed 1, and git diff --stat showed exactly 1 deletion. DIRECTION PREDICTED AND OBSERVED: RED — the 'plain' case failed with Received 'flex min-h-0 flex-col overflow-hidden rounded-lg border bg-background', i.e. it kept the card chrome; the other 4 cases stayed green, which is correct since they do not depend on the forward (ABLATION_VITEST_EXIT=1, captured directly, not through a pipe). RESTORE LEG PROVEN TWICE by observation: git diff HEAD empty AND git hash-object equal to the HEAD blob (16ca3198...) — 'MATCH'. Trap 'git checkout HEAD -- ABSOLUTE_PATH' on EXIT INT TERM throughout; an earlier attempt proved the trap fires at shell exit, so the mutation and the measurement were re-run in ONE shell.\nGATES (objectui has no scripts/pm/dispatch-gates.mjs — that script lives only in objectstack and would answer about objectstack's tree, so the family was derived MANUALLY from this repo's package.json + .github/workflows/): check:control-bytes green ('check-control-bytes: OK (scanned 5649 tracked text file(s); skipped 85 binary)'); check:docs-route-closure green ('1353 modules crawled from 148 route roots (144 MDX), every specifier resolved'); check:designer-field-key-parity exit 0; check:spec-symbols exit 0; check:esm-specifiers exit 0; check-changeset-presence green ('2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'). Plus a self-scan for control bytes over my 4 files: none.\nLINT: ran the FULL repo scan rather than a narrowed one — eslint . --no-inline-config --format json fit inside the foreground cap. 3954 files linted (count read from the json, not guessed); repo baseline 90 errors / 11551 warnings across 75 files, all pre-existing. My files: renderer.surface.test.tsx 0 errors 0 warnings; renderer.tsx 0 errors and 3 no-explicit-any warnings that are pre-existing — the '[key: string]: any' index signatures count 3 on base and 3 on head, so this diff adds zero findings.\nTWO GATES ARE 'NOT MEASURED', read as neither green nor red: check:sdui-registration-pins exited 2 with its own text 'This is exit 2, not a pass ... Build the console first' (missing console build). Checked whether it even weighs this diff: --list shows its derived population is 12 keys, all from layout and app-shell; plugin-chatbot is not among them, so this change is outside what it measures. check:readme-exports exited 1 announcing its OWN population collapse ('packagesRead: found 9, floor is 25', 28 unbuilt) — a container prerequisite state, not a verdict on this diff. CI runs both regardless.", "mcp_calls": "0 — repo-scoped REST + local git throughout, for reads, issue creation, sub-issue attach, comments and PR creation; no MCP GitHub calls in either round", "open_questions": [], "out_of_scope_findings": [ "filed as #6824 (sub-issue of #6687), untouched here per the PM's scope instruction: two docs Properties rows are live on only a subset of the three chatbot* registrations while the flat table never says so — maxHeight reaches chatbot + chatbot-enhanced but not chatbot-floating (which gets floatingConfig.panelHeight instead), and processVisibility reaches chatbot-enhanced only. Same silent-drop failure mode as this card, one notch weaker (scope over-claim, not a zero-read-point row), and true under either resolution of #6687." ] }
Generated by Claude Code
ACCEPT — PR #6827. No rework. CI green on head
d6391c838(27 success, 3 skipped, 0 red,clean).This card's history matters to the grading, so it goes first: my original dispatch order contradicted the maintainer ruling on this card, because I composed it from the card body without reading its one comment — and that comment was the ruling. The dev stopped and refused to pick a side rather than following either. That was the correct call, I retracted the two conflicting clauses, and this is the round that followed.
The ruling, executed clause by clause
Maintainer, 2026-08-29, verbatim 「同意」 — adopting wire it. Checking each half against what landed:
ruling delivered the registration forwards schema.surfaceto<ChatbotEnhanced>✅ forwarded, and declared on the inline schema-extension type declares the key in its inline schema-extension type ✅ surface?: ChatbotSurface, listed in inputs withdefaultValue: 'card'pins both directions: 'plain'⇒ frameless, absent ⇒'card'unchanged✅ both asserted if chatbot/chatbot-floatingdo not gain the forward, the docs row states the key applies to the enhanced registration✅ they do not (different components, no such chrome), and the row is scoped accordingly ⛔ deleting the row is not taken ✅ the row stands ⭐ The union is IMPORTED from
ChatbotEnhanced.tsxrather than re-spelled — one contract instead of two dialects (AGENTS.md #0.1). And the absent case is deliberately unchanged: an unauthored key forwards asundefinedso the component's ownsurface = 'card'default still applies, with the key kept out ofdefaultProps(mirroringprocessVisibility) so nothing materializes it onto new nodes. That is the difference between "the default still works" and "the default is now written into every node that never asked for it."Sweep answer, which the dispatch asked for: 25 rows, 24 survive —
surfacewas the only zero-read-point row. So the docs table's exposure to this class is now measured rather than suspected.The measurements
- Pre-edit census with controls in the same query:
schema.surface0 inrenderer.tsx, controlsschema.placeholder3 (one per registration) andschema.processVisibility1 (enhanced-only) — so the zero is a reading. Second independent face:ChatbotSchema(packages/types/src/complex.ts:539) declares 34 keys, not this one. - ⭐ The not-measured trap was checked, not assumed:
packages/plugin-chatbot/tsconfig.jsonexcludes**/*.test.tsx, so "typecheck is clean" would have said nothing about the new test. Verified withtsc -p tsconfig.test.json --listFiles→ matchesrenderer.surface.test.tsxexactly once. (And the repo's script istype-check, hyphenated — noted so the next reader does not run a zero-match silent pass.) - Ablation with the direction predicted before it was run: removing the forward turned the
'plain'case red withReceived 'flex min-h-0 flex-col overflow-hidden rounded-lg border bg-background'— i.e. it kept the card chrome, exactly the failure the wiring exists to prevent. The other four cases stayed green, which is correct, since they do not depend on the forward. Mutation confirmed on disk (removed-text 1→0 while the controlprocessVisibilitystayed 1), restore proven twice (emptygit diff HEADandgit hash-objectequal to the HEAD blob). - ⭐ A methodological self-correction, volunteered: an earlier attempt established that the
trapfires at shell exit, so the mutation and the measurement were re-run in one shell. An ablation whose restore fires between the mutation and the measurement produces a confident, meaningless green; catching that in your own harness is the difference between an ablation and a ritual.
Nothing here needed a second round. The one round that was wasted was mine.
Generated by Claude Code
- Pre-edit census with controls in the same query:
Filed unassigned out of the objectui#6169 census (the
chatbotnode's authoring-face-type lift). While read-site-censusing that node's keys I checked everyComponentRegistry.register('chatbot*', ...)site inpackages/plugin-chatbot/src/renderer.tsxforsurface, since the docs table also lists it, and found it nowhere.The fact
content/docs/plugins/plugin-chatbot.mdx's## Propertiestable has a row:surface'card' | 'plain''card'surfaceis a real prop — but of the React component, not of any authorable node:None of the three registrations in
renderer.tsx(chatbot,chatbot-enhanced,chatbot-floating) forward it:chatbot-enhancedis the one that renders<ChatbotEnhanced>, and its registration's own inline schema-extension type doesn't declaresurfaceeither, so an author writingsurface: 'plain'on ANYchatbot*node has the key silently dropped —<ChatbotEnhanced>always falls back to its own default ('card').Why it's worth a card rather than a quick delete
This reads like the sibling of the already-fixed
body/requestBodyrow (PR #6193, same page): a table row naming a key that reaches nothing, with no error and no signal to the author. But it's a different shape —bodycollided with a real, differently-meaning key (BaseSchema.body);surfacehas zero read points anywhere in this package, on any of the three registrations. Whether the fix is wiringschema.surfacethrough onchatbot-enhanced(the node that actually has a<ChatbotEnhanced surface>to forward it to) or retiring the row is a real call — same "declared-but-unbacked" pattern objectui#6169's ADR-0049 clause exists for, just on the docs side rather than the type side.Reproduce
Out of scope for objectui#6169 itself: that card's fence is
renderer.tsx:62'schatbot-node intersection specifically, and its docs-table instruction was "adding a type name is in scope; deleting rows is not" — this row isn't one of the census's 11 keys and touching it would be a different defect class.