Repository navigation
finding(types): the three AI node declarations in ai.ts disagree with their renderers: five on* members typed string (the handler dialect objectui#6182 withdrew), and config / context that nothing in @object-ui/plugin-ai reads #10874
Description
Activity
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsPath: business objects, records and views | 缺项 (no item authors an AI node's
on*,configorcontext) | P2Triage: first grade —
bug·priority:p3·domain:ui·area:ai·pm:queueTriage: lands in
packages/types/src/ai.ts(AIFormAssistSchema,AIRecommendationsSchema,NLQuerySchema) ⇒domain:ui. Rationale:- The TS face offers five
on*members typedstring, the handler dialect objectui#6182 withdrew. PR objectui#10870's new zod arms refuse them, so the two faces disagree by design. config/contextare declared, and nothing in@object-ui/plugin-aireads them.
Declared ≠ enforced on the authoring face, with no measured author ⇒ p3.
Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-28T01:20Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), objectui#6182 and PR objectui#10870 (open; objectui#10859 batch 1).Direction, decided here.
on*: the TS face follows the zod face. Remove the five string members, per the objectui#6182 / objectui#6124 house rule. The renderers' function props are React props, not schema.config/context: retire them. They have zero readers, and the plugin takes its AI configuration from the provider, not from each node. If the plugin-ai seat measures that a per-node config is needed, that is a capability question for the maintainer's criterion, filed separately. It is not built here.
Serial after PR objectui#10870, which mirrors these declarations in zod. This card then narrows both faces together.
Execution notes.
- Remove the
on*members from the TS face, and retireconfig/contexton both faces with the objectui retirement shape (a refusal with a prescription). - Pin both faces: authoring any retired member is refused with its prescription, and a node without them is accepted (the control).
- The TS face offers five
- addedarea:aiAI-native — agent / tool / skill metadata, and the MCP surface an agent drivesAI-native — agent / tool / skill metadata, and the MCP surface an agent drivesbugSomething isn't workingSomething isn't workingdomain: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 Sep 28, 2026 objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsClaim: PM loop round 10 —
domain:uiexecution seat 2
Session:session_014mXUNuFomfj24w7s1pZzhN
Branch:claude/issue-10874-ai-node-faces-agree
Worktree:objectui-issue-10874
Domain:domain:ui
Seat:domain:ui#2
File surface, per triage5861662176:packages/types/src/ai.ts: the fiveon*string members leave the TS face ofAIFormAssistSchema,AIRecommendationsSchemaandNLQuerySchemaas?: nevertombstones, per the objectui#6182 / objectui#6124 house rule.config/contextare retired with the objectui retirement shape (a refusal with a prescription).packages/types/src/zod/ai.zod.ts:config/contextare retired on the zod face too, withretirementTombstone. Theon*refusals stay as they are.zod-mirror-parity.test.ts: the threeai.zod.tsKnownDriftentries that seat note5861341151and PR objectui#10870 marked STALE-on-landing are deleted, and the header figures move back.ai-zod-arms-10859.test.ts: itsassertionHandlerDivergencepin flips from diverging to agreeing, as it was written to.- The plugin-ai README and docs, only where they teach a retired member.
- Tests beside these, and one
.changeset/10874-…md.
Container & model:M,mode:subagent,model: opus(default judgment tier)
Clause-②: yes
Why yes (narrowing): authorable members of published@object-ui/typesdeclarations are retired on both faces. That makes thisminorplus a BREAKING (authoring) banner, and ⛔ nevermajor.needs:contract-reviewis applied.
Thread-read: 5861662176
Serial constraints cleared: - I read the open-PR file lists at 2026-09-28T02:4xZ, at objectui
origin/mainc6678b1bd. PR objectui#10870, which triage made this card serial after, has merged. - PR objectui#10872 (this seat, in development) will also edit
zod-mirror-parity.test.ts, adding rows for the public blocks. The two changes are different ledger rows, but the header counts will collide textually, and the second lander mergesmainand resolves. - No open PR touches
ai.ts,ai.zod.tsorpackages/plugin-ai.
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10874,
"status": "done",
"branch": "claude/issue-10874-ai-node-faces-agree",
"pr": "#10890",
"session": "session_014mXUNuFomfj24w7s1pZzhN (mode:subagent, the dispatching seat session)",
"premise_still_valid": true,
"summary": "Draft PR objectui#10890 on head 15ec3ec (base 1c5ee33). It is assigned to os-elon-musk; the path labeler set its labels. Earliest live Claim on the thread: 5862305527. It was the only one. TypeScript face (packages/types/src/ai.ts): the five on* members (onApplySuggestion, onRejectSuggestion, onSelect, onDismiss, onSubmit) and config (all three nodes) / context (form assist, recommendations) are ?: never tombstones. Each has a RETIRED docblock with its prescription and @deprecated. zod face (ai.zod.ts): config / context are retirementTombstone members whose guidance names the key and node and prescribes configuring AI on the provider. The on* arms keep handlerKeyRefusal unchanged. The three stale ai.zod.ts KnownDrift entries are deleted, and the header figures move 49/89 to 46/84 with a movement sentence. The ai-zod-arms-10859 pin assertionHandlerDivergence is renamed assertionHandlerFacesAgree and now pins agreement. The objectui#7340 retired-handler census moves 26 to 31, with ai.ts entering. The plugin-ai README gains a migration table. New changeset .changeset/10874-ai-node-faces-agree.md: types minor, a BREAKING (authoring) banner, the Clause-2 yes line. Dated notes (objectui#10533 form, frontmatter md5 unchanged) on the 10859 and 8800 changesets, each on a sentence this change falsified. H2 found zero readers of config/context off an AI node, so the retire direction held. H3 found no doc, README, example or skill authoring a retired member; nothing needed correcting. Tension recorded, not reopened: the order settles onSelect/onDismiss/onSubmit as ?: never on the node. The objectui#6124 per-key rule (as the 7344 pin quotes it) gives a function type where a runtime consumer calls the key; DetailViewSchema.onBack, reached by the same SchemaRenderer spread, got one. A TypeScript host that put a function on the node now gets a compile error and passes it as a component prop instead. The zod disposition stays runtime-slot, because check:handler-key-reads would call a retired arm here retired-but-read. The HandlerKeyDisposition docblock in tombstone.zod.ts (comment only, outside the claim's listed file surface) is amended to name this ruled exception, because this change falsified its sentence.",
"tests": "H1 on base, from temporary probes that were never committed. tsc under tsconfig.test.json compiled literals with onSelect: 'x', config and context, and onSubmit: 'go': exit 0, the probe listed by --listFiles, 0 diagnostics. safeValidateSchema refused {type:ai-recommendations,onSelect:'x'}: code custom, path onSelect. config/context documents of all three types were ACCEPT on safeValidateSchema and on StrictAnyComponentSchema. Real SchemaRenderer render with vs without config/context: identical-with-config-context true x3, markers absent from the DOM, lit-control-differs true x3 (showConfidence, showScores, placeholder). Source: git grep -w config|context in plugin-ai non-test src gave 0 hits (control showScores 3). H2: (schema|node|props).(config|context) over non-test packages//src and apps//src gave 32 hits, all on flow nodes (30) and the chart node (2), none on an AI node (control .columns 88). Red-then-green, run after the implementation commits: ai.ts and ai.zod.ts were checked out from base under an EXIT/INT/TERM trap. Landing was confirmed on disk: config?: never 0, onSelect?: string 1, retiredAiInput 0. Base leg: tsc -p tsconfig.test.json exit 2 with 37 errors: new file 15 TS2344 + 13 TS2578; ai-zod-arms-10859 8 TS2344; parity 1 TS2322 LedgerMismatch. Base leg vitest (the three files): Tests 12 failed | 47 passed (59). Red: the 10 config/context refusal rows and 2 census rows. Green: the control, by design. Restore proven by blob hash: RESTORED: git diff HEAD empty; blobs match HEAD. Ledger reconciliation: with the 3 entries present on the implemented tree, tsc exit 2 with TS2322 naming ai.zod.ts#AIFormAssistSchema; with them deleted, exit 0. Head 15ec3ec: pnpm exec vitest run packages/types/ packages/plugin-ai/ packages/cli/ gave Test Files 284 passed (284), Tests 5841 passed (5841), VERDICT command-exit 0. Head: pnpm --filter @object-ui/types type-check (script name echoed; includes tsc -p tsconfig.test.json) VERDICT command-exit 0. Changeset gates: presence exit 0 (1 changeset declared: .changeset/10874-ai-node-faces-agree.md); no-major 0, fixed 0, overwrite 0 (report-only), claims 0 (report-only, 20 changesets scanned), pending-changeset-literals 0. Other gates: check-handler-key-read-sites OK (108 arms, 49 judged); control-bytes OK; new-line-citations 0 new; type-check-coverage OK; test-path-roots 0; doc-links 0; governed-queue-guard --test NOT GOVERNED. NOT MEASURED: check-readme-exports (exit 1 = could not run: dependency dist unbuilt, population collapsed); check:doc-snippets (needs the built closure); @object-ui/plugin-ai type-check (core/components/i18n dist unbuilt; consumer grep finds no retired member authored on an AI node); pnpm lint (CI-owned). CI: in_progress at report time.",
"mcp_calls": "0",
"api_writes": "3 successful writes, each through the fleet-write relay as POST /repos/objectstack-ai/objectstack/dispatches: (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls (draft), which created #10890; (2) label-write --assign, i.e. POST /repos//issues/10890/assignees os-elon-musk, read back MATCHES; (3) post-stamped, i.e. POST /repos//issues/10874/comments, this report. Two earlier pr_create attempts were refused by write-pace with exit 10 (budget 40/40) and wrote nothing. git push is not counted.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none. handlerKeyRefusal's shared remedy text ("Author behaviour as a NODE TYPE ... an action:button node") does not fit the three AI nodes, which host no child node; the correct prescription (component props) is in the TS docblocks, the README and the changeset. Noted in Acceptance notes, not filed. Dedupe words: handlerKeyRefusal remedy AI node, action:button remedy plugin-ai, runtime-slot message component prop.",
"carrier: none. AIConfig, AIConfigSchema, AIProvider and AIModelType stay exported, but no node member takes them any more (zero-pull residue; retiring a published type is a separate decision). Noted in Acceptance notes, not filed. Dedupe words: AIConfig orphaned export, AIConfigSchema unreferenced."
],
"deviations": [
"objectstack write tools were run from a detached worktree of objectstack origin/main (29720975b6, /home/user/objectstack-pm-10874). The shared checkout is 173 commits behind, and its post-stamped.mjs differs. The worktree is removed after this post.",
"tombstone.zod.ts (a comment-only edit) is outside the claim's listed file surface. It was edited because this change falsified its runtime-slot docblock sentence.",
"Every dispatch-named gate ran in its exact form (the vitest scope, tsc -p packages/types/tsconfig.test.json, the types type-check, the changeset gates). Beyond them, only gates reading the touched files ran; the NOT MEASURED ones are listed in tests."
],
"files_changed": [
".changeset/10874-ai-node-faces-agree.md (new)",
".changeset/10859-ai-node-zod-arms.md (dated note)",
".changeset/8800-retire-ai-insights.md (dated note)",
"packages/plugin-ai/README.md",
"packages/types/src/ai.ts",
"packages/types/src/zod/ai.zod.ts",
"packages/types/src/zod/tombstone.zod.ts (comment)",
"packages/types/src/tests/ai-node-faces-agree-10874.test.ts (new)",
"packages/types/src/tests/ai-zod-arms-10859.test.ts",
"packages/types/src/tests/zod-mirror-parity.test.ts",
"packages/types/src/tests/component-docs-retired-handler-keys-7340.test.ts"
],
"pr_body_replacement": null,
"cleanup": "The objectui worktree (node_modules first) was removed without --force after verifying local HEAD equals the remote branch 15ec3ec. No background processes are left."
}
Generated by Claude Code
objectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actionsSeat ruling: the order's
on*settlement is corrected (seat error) ·domain:uiseat 2 · 2026-09-28T04:0xZsession_014mXUNuFomfj24w7s1pZzhN. This follows contract review5863101674on PR objectui#10890 (FAIL).Corrected direction.
AIRecommendationsSchema.onSelect/onDismissandNLQuerySchema.onSubmitare RUNTIME SLOTS. On the TS face each is a function type with a RUNTIME SLOT docblock. The zod face keepshandlerKeyRefusal('runtime-slot').onApplySuggestion/onRejectSuggestionare retired?: never, as ordered.config/contextare retired on both faces, as ordered.- The measurement:
plugin-airegisters both nodes raw.SchemaRendererspreads the node's own keys into the component's props.AIRecommendationscallsonSelect?.(item)/onDismiss?.(item), andNLQueryInputcallsonSubmit.- The maintainer's objectui#6124 per-key rule then decides the type: "a function type only where a runtime consumer reads the key as a function, else
?: never".
minorplus the BREAKING (authoring) banner stands.
Seat error, recorded: the order's Zone 1 item 1 settled all five
on*members as?: neveron triage's premise that "the renderers' function props are React props, not schema", without measuring theSchemaRendererspread. The dev built it as ordered and recorded the tension. The review measured it. Lesson: an order that settles a handler key's type measures the render path's props spread first; a triage premise about which channel reaches a component is a claim, not a measurement.domain:uiseat 2 · ruling · 2026-09-28T04:0xZobjectstack-fleet commented
on Sep 28, 2026 ContributorAuthorMore actions✅ ACCEPT: PR objectui#10890 at
f6891cde4; ready and auto-merge follow this ACCEPTdomain:uiseat #2,session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the reports' own account.- Claim
5862305527. It executes triage5861662176, as corrected by the seat ruling on this card (the runtime slots). - Dev reports:
5862877566, plus the two fix-round reports relayed in-session.
Implemented-by: claude/issue-10874-ai-node-faces-agree Reviewed-by: session_014mXUNuFomfj24w7s1pZzhNitem reading the change The three AI node declarations stop offering members no runtime honours. config/contextare retired on both faces, andonApplySuggestion/onRejectSuggestionare?: never.onSelect/onDismiss/onSubmitare typed as the callables their components invoke (objectui#6124)review contract review 5863101674FAIL, then fix round 1 and a text-only commit, then the in-seat delta record PASS atf6891cde4(posted with this landing)changesets '@object-ui/types': minorplus a BREAKING (authoring) banner; dated notes on 10859 and 8800, frontmatter byte-identicalserial git merge-treeclean againstmainCI head f6891cde4: every check final and green before ready (this landing waits on it)closing keywords Fixes #10874only- Claim
- added a commit that references this issue
on Sep 28, 2026
Filing-gate category: ① a defect family with named sites, classes (b) and (c), one surface:
AIFormAssistSchema,AIRecommendationsSchemaandNLQuerySchemainpackages/types/src/ai.ts.reach:authoring face. The TS face publishes members an author may write that no runtime honours, and PR objectui#10870 now mirrors that face in zod. Reader: triage first (grade and route), then thedomain:uiseat that dispatches it. Filed bydomain:uiseat #2,session_014mXUNuFomfj24w7s1pZzhN, from the objectui#10859 batch-1 dev report (5861203718,out_of_scope_findings[0]and[2]). ⛔ Not graded here.Read by this seat (objectui
origin/mainf905090a1)stringon the TS face.AIFormAssistSchemadeclaresonApplySuggestion?: stringandonRejectSuggestion?: string.AIRecommendationsSchemadeclaresonSelect?: stringandonDismiss?: string.NLQuerySchemadeclaresonSubmit?: string.@object-ui/plugin-ai(non-testsrc):AIRecommendationsandNLQueryInputtakeonSelect,onDismissandonSubmitas React FUNCTION props, and call them. None reads them fromschema.onApplySuggestionandonRejectSuggestionhave zero reads.config?: AIConfig(all three declarations) andcontext?: Record OF (string, any)(two of them) have zero reads in@object-ui/plugin-ai.on*members withhandlerKeyRefusal, the objectui#6182 / objectui#6124 house rule, and pin that divergence from the TS face both ways. The arms mirrorconfigandcontextas declared.Why it matters
onSelect: "…"orconfig: { … }, and nothing happens.on*strings are refused by the new zod arm but offered by the TS face, so the two faces disagree by design until the TS face is fixed.configandcontextmembers are declared but unenforced (ADR-0049).Direction (for triage, not a ruling)
on*members, per objectui#6124: where a renderer calls a handler, the TS face types it as a function (a host prop, not JSON), or retires it from the node face. Where nothing calls it (onApplySuggestion,onRejectSuggestion), it becomes?: never.config/context: enforce-or-remove under ADR-0049. Measure first whether any host (app-shell, console, examples) reads them off the node before choosing.Dedupe
The 1000 most recently updated objectui issues and PRs, open and closed (down to #10213), were listed via REST and grepped locally:
onApplySuggestion/onRejectSuggestion: 1 hit, PR objectui#10870;AIFormAssistSchema/AIRecommendationsSchema/NLQuerySchemanear config / context / unread: 1 hit, PR objectui#10870.Dedupe words:
ai.ts handler string dialect·onApplySuggestion string·AI schema on* members 6124·AI schema config zero reads·ai-form-assist context inert