You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
spec(types,react): the spec's public blocks, page kinds and the action:button node have no TypeScript authoring type, so objectui#8347's index-signature removal leaves their TS authors no face (29 literals measured in components' tests) #11364
Filing-gate category: ① — a named landing point (@object-ui/types' TypeScript face, and SchemaRenderer's schema prop type), with measured reach and named producers, ordered by the objectui#7927 ruling's step 1. A derived child of objectui#8347, filed with the parent's domain:spec and priority:p2.
Reader: the domain:spec @ objectui seat (objectui#10217) dispatches it, and an os-dev executes it.
Parent: objectui#8347, which stays pm:blocked until this card and its siblings land.
What was measured (objectui#11347's report 5927683269, PR objectui#11360 at bba957503)
With objectui#8347's P1 mutation applied (BaseSchema's [key: string]: any deleted alone), @object-ui/components' test program still reads 29 non-probe errors after every literal that could be retyped was retyped. Each of them authors a node that has no declaration on @object-ui/types' TypeScript face, so no type exists to retype the literal to:
node
what the literal authors
literals
the spec's public blocks element:text, element:text_input, element:record_picker
their properties bag
14
page:tabs
items
3
action:button (the node; UIActionSchema is the action definition)
executor keys actionType / params
5
page kind home
kind (PageNodeSchema is type: 'page' only)
2
any node
the props alias
4
a test-registered probe type
(test-local)
2
TS2353 names only the first excess key per literal, so the real count is at least this.
The decisive measurement: the spec's own PageComponent (re-exported as SpecPageComponent) is NOT assignable to SchemaRenderer's schema prop under the mutation (TS2322). Neither a retype nor a spec-type route exists today.
Reach: every TypeScript author of these nodes loses the face at the removal. The named producers are the objectstack showcase pages, examples/schema-catalog, and the React hosts that build page JSON in TS. Today they compile through the index signature, and after objectui#8347 they cannot.
Direction (the seat's, per 协议为基准 and the objectui#7927 ruling)
objectui#7927's ruling (5565351677) says the TypeScript face is a contract and "the real extension keys are declared". For these nodes, that means:
TypeScript node types for the spec's public blocks and page kinds, by reference to their zod arms or spec rows (for example z.input of the block schema). ⛔ Not hand-written twins.
SchemaRenderer's schema prop (and SchemaNode) accepts them. Measure the prop's published type, and widen it only by those referenced node types. ⛔ Never by an index signature.
The action:button node's executor keys: read objectui#11183 first. It moves the taught action:* node's executor keys into the properties bag (domain:skills, governed). The TS face follows that shape. ⛔ Never the retired flat spelling.
⛔ Not decided here: the props alias on the authoring face. The maintainer's 2026-08-18 ruling (quoted in SchemaRenderer.tsx's docblock) keeps the alias working at runtime. Whether the TS authoring face declares it is a separate question, and AGENTS.md #0.1 weighs against a second spelling. Measure who authors props in TS, and report it as an open_question with the four-axis frame.
Clause-②:yes, because published TS types are added. The at-tier contract record is owed.
Acceptance (executable)
With objectui#8347's P1 mutation applied locally: the 29 literals (re-derived) compile against the new types, except the props ones, which wait for their ruling.
With the signature present: everything stays green.
A pin: a misspelled key inside an element:textproperties bag stops type-checking once the mutation is applied, and the correct spelling compiles.
Serial
objectui#11349 (react, in flight) holds SchemaRenderer.tsx's internal working type. This card touches only the published schema prop type, so it dispatches after objectui#11349 lands.
objectui#10859 / objectui#11276 (domain:ui) are still moving flat arms into the bag. Read the merged state.
Dedupe
Local grep over every objectui issue updated since 2026-09-01 (2,212 issues, open and closed, with bodies, read 2026-10-01T08:3xZ):
element:text|element:record_picker|ElementTextBlock with a TypeScript-face term: 10 hits, none this defect. The nearest are:
objectui#10872 (open; objectui validate refuses namespaced types on the zod face);
objectui#11223 (closed; nested-node validation);
objectui#7450 (closed; the ui:text variant vocabularies).
public block with TypeScript|TS face|TS type: 0 hits.
Dedupe words: public block TypeScript authoring type element:text properties bag · SchemaRenderer schema prop SpecPageComponent not assignable · page kind home PageNodeSchema · action:button node executor keys TS face · BaseSchema index signature removal no TS face
Claim: PM loop round 2 (this card as filed: TypeScript node types for the spec's public blocks, page kinds and the action:button node, by reference, plus SchemaRenderer's published schema prop accepting them; objectui#8347's preparation)
Session: session_01VhxTqosz7wn54ahqyxgERT
Account: os-litant (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-11364-public-block-ts-faces
Worktree: objectui-issue-11364
Domain: domain:spec
Seat: domain:spec#1 (objectui#10217)
File surface:
packages/types/src: new TS node types for the spec's public blocks (element:* with their properties bag), the page kinds (page kind home), page:tabs and the action:button node. Each is by reference to its zod arm or spec row (z.input), ⛔ never a hand-written twin. Plus SchemaNode's union and the zod-mirror-parity rows those types open.
packages/react/src/SchemaRenderer.tsx: only the published SchemaRendererProps.schema type, widened by those referenced node types. ⛔ Never by an index signature. The internal working type (objectui#11349, landed) stays as it is.
The 29 component test literals objectui#11347 handed over, retyped to the new types.
A pin: a misspelled key inside an element:textproperties bag stops type-checking under objectui#8347's P1 mutation, and the correct spelling compiles.
⛔ Not decided here: the props alias's TS authoring face. It is bound by the maintainer's 2026-08-18 ruling and goes back as an open_question with the four-axis frame.
Stop on breach; explain in the report.
Container & model: M, mode:subagent, model: opus (TIER_DEFAULT; no path-derived mandate)
Clause-②: yes
Why yes: new published TS node types are added, and SchemaRendererProps.schema accepts them, so the published TS surface widens. The at-tier contract record is owed on the landing head.
Thread-read: none
Serial constraints cleared: read 2026-10-01T10:53Z at objectui origin/mainb5b0afea5, over all open PRs' file lists and every open pm:dispatched claim.
objectui#11349's PR objectui#11374 landed (b5b0afea5), so SchemaRenderer.tsx is free.
objectui#11183 (the action:* executor keys into the properties bag) landed, and its shape is the one to follow.
PR objectui#11335 (in the merge queue) edits objectql.ts's tree / map / form arms. This card's types are in other regions.
{
"issue": 11364,
"status": "done",
"branch": "claude/issue-11364-public-block-ts-faces",
"pr": "https://github.com/objectstack-ai/objectui/pull/11385",
"session": "session_01VhxTqosz7wn54ahqyxgERT",
"premise_still_valid": true,
"summary": "@object-ui/types gains authoring-nodes.ts, derived by reference: PublicBlockNode (one member per arm of PublicBlockComponentSchema, each the arm's z.input read off its SHAPE with the object config closed, because the zod BaseSchema is .passthrough() and a bare z.input has keyof = string), PublicBlockNodeOf, ElementTextInputNode and ElementRecordPickerNode (no zod arm exists, so each is built the way an arm is built from the spec's ComponentPropsMap row; the picker also declares the node-level dataSource its renderer reads and leaves the bag's object optional for the spec gate's dataSource waiver), PageDocumentNode (spec PageSchema input, page kind required in type) and their union AuthoringNode. @object-ui/react's SchemaRendererProps.schema is BaseSchema | AuthoringNode | string | null | undefined, with no index signature or Record. Under objectui#8347's P1 mutation the components test program falls from 30 to 10 (base b5b0afea5 to head 29acb0107); the 10 are the 4 props-alias literals and 6 probes no referenced type can or should accept (content 1, a test-registered probe type 2, CASE A's node-level params 1, the spec's deprecated `visibility` alias on element:text 2, which objectui#10872 kept off the TS face). H3 is a fork, not a widening: SpecPageComponent fails the prop under P1 on exactly one key, BaseSchema.visibleWhen (string vs the spec's string-or-Expression), so it goes back as open_questions[1] together with the props question.",
"tests": "All at head 29acb0107 unless noted, under os-verify-lock, exit captured before any pipe. (1) P1 census, mutation through ablation-replace.mjs --delete on BaseSchema's comment+signature block (anchor hits 1, blob ce2bc9e31dc6 -> 7fa27a8f1580), types rebuilt on both legs: base b5b0afea5 'COMPONENTS_TEST_PROGRAM_EXIT=2 errors=30', 'COMPONENTS_SRC_PROGRAM_EXIT=0 errors=0'; head 8b59daf00 and 11eaa8ae5 and 29acb0107 each 'errors=10' / src 0. Every leg: 'ok restored: blob == HEAD (ce2bc9e31dc6) and `git diff HEAD` is empty', 'git diff HEAD base.ts bytes: 0', 'RESTORED dist index-sig count (base.d.ts): 2' (mutated legs read 1). Two earlier attempts were no-ops and are not counted: the tool reads its repo from cwd (exit 2, refused, nothing written), then a replacement that is a substring of the anchor (exit 1, restore proven). (2) builds: '@object-ui/components^...' closure exit 0 at 11eaa8ae5; types BUILD_EXIT=0, react BUILD_EXIT=0. (3) type-check, scripts echoed '> tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json' (types) and '> tsc --noEmit && tsc -p tsconfig.test.json' (react, components): TYPECHECK_EXIT=0 errors=0 for all three. (4) vitest: packages/types/ 'Test Files 313 passed (313)', 'Tests 8000 passed (8000)'; packages/react/ 'Test Files 106 passed (106)', 'Tests 1389 passed (1389)'; packages/components/ in --shard legs: 1/3 'Test Files 117 passed (117)' 'Tests 1136 passed | 7 skipped', 2/3 'Test Files 117 passed (117)' 'Tests 1363 passed | 10 skipped' (both at 11eaa8ae5), 3/3 'Test Files 115 passed | 1 skipped (116)' 'Tests 988 passed | 7 skipped' (29acb0107); between those heads only a type-alias default in a declarations-only file moved. A first unsharded components run timed out (VERDICT command-exit 124, NOT MEASURED) and shard 3's first take was exit 99 (queue-timeout, NOT MEASURED); both were retaken. (5) the new pin authoring-nodes-11364.test.ts: every @ts-expect-error consumed (an unused one is TS2578), with a control proving the no-index-signature check fires on the bare z.input of the same arms. (6) H5 dist diff against a base build in a comparison worktree: types adds authoring-nodes.d.ts and one export line; 13 zod/*.zod.d.ts differ only in member print order (token multiset equal to base after normalising whitespace, the union bar and trailing punctuation); react SchemaRenderer.d.ts gains the import and the widened prop, plus a reordered union in an existing inferred render() return. No reverse-verification ablation of the new pin was run separately: the pin is type-level and its directives are TS2578-checked both ways, and the P1 legs above are the mutation that measures the prop.",
"mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "3 REST writes, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectui/pulls, draft forced, read-back 13402 bytes sent / 13402 stored, identical (objectui#11385); (2) label-write --assign os-litant -> POST /repos/objectstack-ai/objectui/issues/11385/assignees, read back MATCHES, zero label writes; (3) this os-dev-report -> POST /repos/objectstack-ai/objectui/issues/11364/comments. Plus 4 git pushes (the empty-branch probe, the four family commits, the merge of origin/main, the lint fix); git push is not REST.",
"open_questions": [
{
"question": "The `props` alias on the TypeScript authoring face (the dispatch's ruling: not decided here). The maintainer's 2026-08-18 ruling keeps `props` working at RUNTIME; BaseSchema's TS face does not declare it today (the 4 alias-precedence literals compile only through the index signature). After objectui#8347, does the TS face declare it?",
"options": [
"A — Do not declare `props` on the TS face. Runtime keeps the alias under the 2026-08-18 ruling; a TS author of `props` gets TS2353 after the removal; the 4 alias-precedence probe literals become objectui#8347's flips (a cast or @ts-expect-error that names the ruling). Cost: none on a measured producer.",
"B — Declare `props?: unknown` (deprecated) on BaseSchema's TS face. Cost: a second spelling of `properties` on every node's TS face, with zero measured TS producers; AI-authored `props` compiles and silently takes the alias path.",
"C — Declare `props` only on the AuthoringNode types. Cost: the same second dialect, confined to exactly the nodes whose bag is typed, which re-opens the untyped channel beside the typed one."
],
"recommendation": "A. Real business need, measured: objectui's 1720 non-test TS/TSX files author no node `props` (lit control `properties: {` reads 28 on the same population); tracked JSON has 0 files with `\"props\": {` (control 10); objectstack examples/** TS at local 95fed33a2 has 0 (control 52); the published skill (skills/objectui/rules/protocol.md) teaches node-level `props` as WRONG. The only authors are the 4 alias-precedence test probes. Long-term: 协议为基准 — the spec's PageComponentSchema has no `props` key, its bag is `properties`; declaring `props` is the second dialect AGENTS.md #0.1 forbids. AI-proofing: A makes an AI-authored `props` a compile error at the authoring site, while B/C let it compile and ride the tolerant alias path. Startup focus: A adds nothing; B/C add declared surface with no pull, and A is compatible with the 2026-08-18 ruling because that ruling binds the runtime, not the TS face."
},
{
"question": "H3 fork: should the spec's own PageComponent (re-exported as SpecPageComponent) be assignable to SchemaRenderer's `schema` prop after objectui#8347? Measured under P1: it fails on exactly ONE shared key, `visibleWhen` (BaseSchema declares `string`; the spec declares string or an Expression envelope and normalises a bare string into `{ dialect: 'cel', source }` at parse; the renderer evaluates the envelope). No union of referenced types fixes that without losing the bag check.",
"options": [
"A — Widen BaseSchema.visibleWhen (TS face and its zod mirror) to the spec's string-or-Expression, by reference. Measured by type-level simulation with ExpressionWire: SpecPageComponent becomes assignable AND `properties: { contnet }` on element:text stays refused. Cost: an edit to BaseSchema, which this card excluded (objectui#8347's file); it also ends the validator refusing the spec's own parsed envelope (out_of_scope_findings[0]).",
"B — Add the spec's PageComponent to the prop's union. Measured: SpecPageComponent becomes assignable, but `{ type: 'element:text', properties: { contnet: 'x' } }` then compiles, because PageComponent's `properties` is Record of string to unknown. Cost: loses the pin on the busiest authoring surface.",
"C — Leave it unassignable; a host holding a spec-parsed PageComponent converts it through a bridge. Cost: the decisive measurement on objectui#11364 stays open, and the declaration stays narrower than both the spec and the renderer."
],
"recommendation": "A, in objectui#8347's lane (BaseSchema is its file) or a sibling card. Real business need: spec-parsed pages carry the envelope at runtime (the spec normalises every bare visibleWhen string), and the renderer already evaluates it, so this is a real path, not a speculative one. Long-term: the spec declares the union and the renderer implements it; objectui's declaration is the lagging side, so aligning it is protocol-first, while B is exactly the Record-typed tolerance the removal exists to end. AI-proofing: A keeps every typed bag closed; B reopens the silent channel on precisely the nodes this card typed. Startup focus: A widens one existing key's value to the spec's, adds no key, and closes a measured validator refusal at the same time."
}
],
"out_of_scope_findings": [
"class: b · reach: public door — measured through the published `@object-ui/types/zod` validator: AnyComponentSchema.safeParse of { type: 'element:text', properties: { content: 'x' }, visibleWhen: { dialect: 'cel', source: 'true' } } answers invalid_type at path visibleWhen ('expected string, received object'), while the spec's own PageComponentSchema.safeParse of visibleWhen: 'record.x == 1' yields { dialect: 'cel', source: 'record.x == 1' } and SchemaRenderer evaluates that envelope. Contract text: the spec types PageComponent.visibleWhen as string or Expression. Seam: spec:PageComponentSchema.visibleWhen -> runtime:SchemaRenderer evaluateVisibilityPredicate; objectui declares string on both faces (base.ts BaseSchema.visibleWhen, base.zod.ts `visibleWhen: z.string()`). Same root as open_questions[1]; fold it into objectui#8347's family rather than a single-point card. · dedupe words: visibleWhen expression envelope refused · BaseSchema visibleWhen string only · spec normalizes visibleWhen cel envelope · SpecPageComponent not assignable visibleWhen",
"carrier: objectui#11183's lane (domain:skills, governed) · noted, not filed — AGENTS.md #4 still teaches the flat action:button executor spelling ({ type, label, actionType, target } on the node) that objectui#11183 moved into the properties bag; the public-blocks.zod.ts module header likewise still calls the flat spelling 'the TAUGHT spelling'. AGENTS.md section 4's abridged BaseSchema also lists `props` typed as a Record of string to any, which the real BaseSchema does not declare (P1 reads TS2353 'props' does not exist in type 'BaseSchema').",
"carrier: 承接者:无 · noted, not filed — the TS face now types element:text_input, element:record_picker and page documents, which AnyComponentSchema still refuses at `type` (invalid_union, 'No matching discriminator'); that is the population registered-types-validate-ratchet-10859 already counts, not a new defect.",
"carrier: 承接者:无 · noted, not filed — the spec carries ComponentPropsMap rows for element:image, element:metadata_viewer, element:filter and element:form with no zod arm and no TS node type here; none appears in the census, so none was typed."
],
"gates": {
"build": "pnpm --filter '@object-ui/components^...' build exit 0 (11eaa8ae5); @object-ui/types and @object-ui/react build exit 0 (29acb0107)",
"type-check": "types / react / components TYPECHECK_EXIT=0 errors=0 at 29acb0107, script names echoed",
"test": "types 313 files / 8000 tests; react 106 / 1389 (29acb0107); components shards 117+117 (11eaa8ae5) + 115 passed 1 skipped (29acb0107); 10 changed+adjacent files 77 tests (8b59daf00)",
"p1_census": "components test program 30 (b5b0afea5) -> 10 (29acb0107), src 0 -> 0; restore proven every leg",
"check_exit_0": "check:doc-fences, check:doc-types, check:new-line-citations (0 new), check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check:spec-symbols, check:test-path-roots, check:phantom-deps, check:self-import, check:unreferenced-sources, check:vi-mock-specifiers, check:doc-example-ids, check:component-surface-parity, check:esm-specifiers, check-changeset-presence (9 source files, 3 packages, 1 changeset), check-changeset-no-major, check-governed-queue-guard --test: NOT GOVERNED (14 paths)",
"lint_narrowed": "9 touched .ts/.tsx files (population: eslint.config.js `**/*.{ts,tsx}`), --format json at 29acb0107: 1 error / 19 warnings; base edfcf5a5e same 7 pre-existing files 1 error / 19 warnings; the error is the base-present no-restricted-imports on FormField in packages/types/src/index.ts; invariance: no parserOptions.project / projectService (type-aware off); full pnpm lint is CI's",
"not_measured": "check:doc-snippets, reason: prerequisite 34-package build — narrowed instead: the 2 added fences compile --strict against types/dist/authoring-nodes.d.ts and react/dist/SchemaRenderer.d.ts (exit 0, read from --listFiles), control with `contnet` fails TS2561. check:readme-exports exit 1 is its could-not-judge state (431 self-imports in unbuilt packages); --list reads both new README imports as real."
},
"deviations": [
"SchemaNode is NOT widened, although the claim's file surface lists 'SchemaNode's union'. The dispatch made it conditional ('if it is the union' the prop uses); the prop is typed as its own union, and all 30 census literals reach the prop directly. Measured: with the signature present, a reader of a widened SchemaNode that reads an undeclared key (`columns`) turns TS2339 on the closed member, which would break 'every edit compiles with the signature present'.",
"No zod-mirror-parity row opened: no zod const was added, and the existing EXCLUSIONS reason for the public-block arms ('no TS declaration in this package restates the node') stays true because the new types derive from the arms.",
"Files outside the claim's listed surface, each forced: packages/react/src/__tests__/SchemaRenderer.propsResolution.test.ts (assertion 1 pins the prop's exact union), .changeset/9256-public-blocks-content-channels.md (dated append-only note per the dispatch's changeset rule, frontmatter md5 unchanged), packages/react/README.md (docs for the prop).",
"The H3 widening stops at the union of referenced types; SpecPageComponent's assignability is reported as a fork (open_questions[1]) per Zone 3, not implemented.",
"Residual 10 under P1 includes 2 `visibility` literals on element:text in page-variables (the spec's deprecated alias, off the TS face by objectui#10872) that TS2353 hid behind `properties` at base; they are classified with the probe flips, and the seat may classify them otherwise.",
"origin/main moved by 6 commits during the run; it was fetched into a private ref and merged (no rebase), the unpushed merge commit amended only to add the trailer pair, and every gate after the merge was retaken on the merged tree.",
"Commit trailers use the model-free pair AGENTS.md prescribes, and the PR footer uses the session-URL form, instead of the harness's model-named trailer and footer.",
"The `verify` skill was not run before commits: its scope is designer previews in a browser, and this diff is types, a prop type and test literals.",
"Scratch probes lived briefly in gitignored packages/*/.objectui-tmp inside the worktree; removed before the worktree was removed. Both worktrees (objectui-issue-11364 and the comparison objectui-issue-11364-cmp) are removed; the branch is fully pushed."
],
"files_changed": 14,
"line_budget": "n/a — no skills/** or ratcheted ledger touched; diff +481 / -45 against merge-base edfcf5a5e",
"overlaps": "none found with in-flight objectui#11348 / objectui#11355; the merge of origin/main had no conflicts"
}
ACCEPT: report 5933487223, PR #11385 at 29acb0107, ready for the at-tier contract review (Refs). From the domain:spec @ objectui seat (objectui#10217), session session_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T14:28Z.
Checked against the PR, not the report's prose
check-governed-merges reads NOT governed (0 of 14 paths). Size: +481 / -45.
git merge-tree against main3ae919307 is clean.
The PR opens with Refs #11364 / Clause-②: yes. That is the right arm on the diff's face: new published TS node types, and SchemaRendererProps.schema accepts them. No face narrows.
The changeset is @object-ui/typesminor + @object-ui/reactminor, objectui#7722's class for a published member gained.
What lands
packages/types/src/authoring-nodes.ts:
PublicBlockNode: one member per arm of PublicBlockComponentSchema, each the arm's z.input read off its shape, with the bag closed;
ElementTextInputNode / ElementRecordPickerNode, built from the spec's ComponentPropsMap rows;
PageDocumentNode, from the spec's PageSchema input;
their union, AuthoringNode.
SchemaRendererProps.schema is BaseSchema | AuthoringNode | string | null | undefined, with no index signature and no Record.
Under objectui#8347's P1 mutation, the components test program goes from 30 to 10. The 10 are the 4 props-alias literals and 6 probes.
Deviations accepted
SchemaNode is not widened. The dispatch made it conditional. The measurement shows that a closed member would turn existing undeclared reads into TS2339 while the signature is still present.
No zod-mirror-parity row opens, because no zod const is added.
Three files sit outside the listed surface, each forced: the propsResolution test that pins the prop's exact union; a dated, append-only note on the 9256 changeset; and the react README.
For the record to judge
ElementTextInputNode and ElementRecordPickerNode have no zod arm, and AnyComponentSchema still refuses both at type. Judge whether a TS face that accepts what the published validator refuses is a face split this PR opens, or the population registered-types-validate-ratchet-10859 already counts, as the report says.
The picker's node-level dataSource is declared because its renderer reads it. Judge whether that holds.
Open questions: neither is ruled by this seat. Both go to objectui#8347, whose last PR owns BaseSchema and the residual literals. This card is discharged when the PR lands. The maintainer closed the shift at 13:54Z (「当前任务处理完,合并后就下班」).
The props alias on the TS authoring face. It is bound by the maintainer's 2026-08-18 ruling, so it is the maintainer's call. The report's options:
C: declared on AuthoringNode only.
The report recommends A, with measured producers: 0 non-test TS, 0 tracked JSON, and 0 objectstack examples author a node props.
BaseSchema.visibleWhen is string on both faces, while the spec's PageComponent.visibleWhen is string or Expression.
Measured at the public door: @object-ui/types/zod's AnyComponentSchema refuses visibleWhen: { dialect: 'cel', source: 'true' }, the envelope the spec's own parse produces from a bare string and that SchemaRenderer evaluates. It is also the one key that keeps a spec-parsed PageComponent off the prop under P1.
{
"issue": 11364,
"round": 2,
"status": "done",
"branch": "claude/issue-11364-public-block-ts-faces",
"pr": "https://github.com/objectstack-ai/objectui/pull/11385",
"session": "session_01VhxTqosz7wn54ahqyxgERT",
"premise_still_valid": true,
"summary": "Round 2 answers contract record 5933841900 (FAIL on 29acb0107), head now 6af8e9da3, PR still draft. A: SpecRowNode (ElementTextInputNode / ElementRecordPickerNode) is now the type-level twin of an arm's BaseSchema.extend(...): Omit of the zod BaseSchema shape by the own keys, then the type literal, typeof NODE_ENVELOPE (responsiveStyles), properties, and body / children as retirementTombstone's return type, all type-only imports. Under the real P1 mutation, through the actual SchemaRenderer schema prop, responsiveStyles now compiles on element:text, element:text_input and element:record_picker, and a node-level children is refused (TS2353) on all three, exactly as on element:text. Both halves are pinned in authoring-nodes-11364.test.ts beside the element:text control, and the pin's reverse check turned red in the predicted direction. The three published sentences (changeset, authoring-nodes.ts docblock, PR body) now state what the type is built from. B: 11364's changeset says toRenderableSchema's return widens with the prop; 8908 gets a dated append-only note, and so does schema-input-bridge-permanent-4622 (it spells the prop's old union, found by the same rule). C: the compile sentence now rests on its real ground: assignment is preserved, reads are not guaranteed on a union, and no caller here reads a key off the prop's type or the bridge's return. origin/main (3ae919307) was merged, not rebased. The props and visibleWhen questions are unchanged and unruled; they go to objectui#8347.",
"tests": "Round 2, under os-verify-lock, exit captured before any pipe. (1) Before the fix, type-level P1 simulation (the pin's PostRemovalSchema construction), ten cases: through the union, element:text + responsiveStyles compiled, element:text_input / element:record_picker + responsiveStyles were TS2353, element:text + node children was refused, element:text_input + node children compiled; on the node types, both spec-row nodes accepted children (element:text refused it, TS2322) and refused responsiveStyles. After the fix, the same ten cases all match element:text. (2) Real P1 at 7638daa9e via ablation-replace.mjs (anchor 1 -> 0, blob ce2bc9e31dc6 -> 7fa27a8f1580), types rebuilt mutated, a throwaway JSX probe compiled through the actual SchemaRenderer prop against the built react dist and mutated types dist: 'PROBE_EXIT=2 errors=3', the 3 errors exactly the three node-level children cases (TS2353), and the three responsiveStyles cases compile; 'COMPONENTS_TEST_PROGRAM_EXIT=2 errors=10' (unchanged population), src 0; restore 'ok restored: blob == HEAD (ce2bc9e31dc6) and `git diff HEAD` is empty', 'git diff HEAD base.ts bytes: 0', dist index-sig count 2. (3) Reverse check of the new pin, with the fix committed first (f525620db): ablation-replace on SpecRowNode's own keys (marker on disk 1, NODE_ENVELOPE spread on disk 0) -> 'MUTATED_PIN_TSC_EXIT=2': TS2353 x2 on the two styled spec-row literals, TS2578 x3 on the three spec-row children directives, element:text control unchanged; restore 'ok restored: blob == HEAD (a2e6ac965eec)', diff 0 bytes, 'RESTORED_PIN_TSC_EXIT=0'. (4) At 7638daa9e: '@object-ui/components^...' 'CLOSURE_BUILD_EXIT=0'; type-check types / react / components 'TYPECHECK_EXIT=0 errors=0', scripts echoed ('> tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json' for types, '> tsc --noEmit && tsc -p tsconfig.test.json' for react and components); vitest of the pin + SchemaRenderer.propsResolution 'Test Files 2 passed (2)', 'Tests 8 passed (8)'. (5) At 6af8e9da3 (only a changeset .md after 7638daa9e): check-changeset-presence, check-changeset-no-major, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:spec-symbols, check:phantom-deps, check:unreferenced-sources, each EXIT=0. Lint of the two edited .ts files: eslint exit 0, no output.",
"mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "2 REST writes this round, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) issue_patch -> PATCH /repos/objectstack-ai/objectui/issues/11385 (the PR body), read-back 17040 bytes sent / 17040 stored, identical; (2) this round-2 os-dev-report -> POST /repos/objectstack-ai/objectui/issues/11364/comments. Plus 1 git push (29acb0107..6af8e9da3); git push is not REST. Reads only otherwise (the record 5933841900, the PR state).",
"open_questions": [
{
"question": "The `props` alias on the TypeScript authoring face (the dispatch's ruling: not decided here). The maintainer's 2026-08-18 ruling keeps `props` working at RUNTIME; BaseSchema's TS face does not declare it today (the 4 alias-precedence literals compile only through the index signature). After objectui#8347, does the TS face declare it?",
"options": [
"A — Do not declare `props` on the TS face. Runtime keeps the alias under the 2026-08-18 ruling; a TS author of `props` gets TS2353 after the removal; the 4 alias-precedence probe literals become objectui#8347's flips (a cast or @ts-expect-error that names the ruling). Cost: none on a measured producer.",
"B — Declare `props?: unknown` (deprecated) on BaseSchema's TS face. Cost: a second spelling of `properties` on every node's TS face, with zero measured TS producers; AI-authored `props` compiles and silently takes the alias path.",
"C — Declare `props` only on the AuthoringNode types. Cost: the same second dialect, confined to exactly the nodes whose bag is typed, which re-opens the untyped channel beside the typed one."
],
"recommendation": "A. Real business need, measured: objectui's 1720 non-test TS/TSX files author no node `props` (lit control `properties: {` reads 28 on the same population); tracked JSON has 0 files with `\"props\": {` (control 10); objectstack examples/** TS at local 95fed33a2 has 0 (control 52); the published skill (skills/objectui/rules/protocol.md) teaches node-level `props` as WRONG. The only authors are the 4 alias-precedence test probes. Long-term: 协议为基准 — the spec's PageComponentSchema has no `props` key, its bag is `properties`; declaring `props` is the second dialect AGENTS.md #0.1 forbids. AI-proofing: A makes an AI-authored `props` a compile error at the authoring site, while B/C let it compile and ride the tolerant alias path. Startup focus: A adds nothing; B/C add declared surface with no pull, and A is compatible with the 2026-08-18 ruling because that ruling binds the runtime, not the TS face."
},
{
"question": "H3 fork: should the spec's own PageComponent (re-exported as SpecPageComponent) be assignable to SchemaRenderer's `schema` prop after objectui#8347? Measured under P1: it fails on exactly ONE shared key, `visibleWhen` (BaseSchema declares `string`; the spec declares string or an Expression envelope and normalises a bare string into `{ dialect: 'cel', source }` at parse; the renderer evaluates the envelope). No union of referenced types fixes that without losing the bag check.",
"options": [
"A — Widen BaseSchema.visibleWhen (TS face and its zod mirror) to the spec's string-or-Expression, by reference. Measured by type-level simulation with ExpressionWire: SpecPageComponent becomes assignable AND `properties: { contnet }` on element:text stays refused. Cost: an edit to BaseSchema, which this card excluded (objectui#8347's file); it also ends the validator refusing the spec's own parsed envelope (out_of_scope_findings[0]).",
"B — Add the spec's PageComponent to the prop's union. Measured: SpecPageComponent becomes assignable, but `{ type: 'element:text', properties: { contnet: 'x' } }` then compiles, because PageComponent's `properties` is Record of string to unknown. Cost: loses the pin on the busiest authoring surface.",
"C — Leave it unassignable; a host holding a spec-parsed PageComponent converts it through a bridge. Cost: the decisive measurement on objectui#11364 stays open, and the declaration stays narrower than both the spec and the renderer."
],
"recommendation": "A, in objectui#8347's lane (BaseSchema is its file) or a sibling card. Real business need: spec-parsed pages carry the envelope at runtime (the spec normalises every bare visibleWhen string), and the renderer already evaluates it, so this is a real path, not a speculative one. Long-term: the spec declares the union and the renderer implements it; objectui's declaration is the lagging side, so aligning it is protocol-first, while B is exactly the Record-typed tolerance the removal exists to end. AI-proofing: A keeps every typed bag closed; B reopens the silent channel on precisely the nodes this card typed. Startup focus: A widens one existing key's value to the spec's, adds no key, and closes a measured validator refusal at the same time."
}
],
"out_of_scope_findings": [],
"gates": {
"build": "pnpm --filter '@object-ui/components^...' build exit 0 at 7638daa9e",
"type-check": "types / react / components TYPECHECK_EXIT=0 errors=0 at 7638daa9e, script names echoed",
"test": "authoring-nodes-11364 + SchemaRenderer.propsResolution: 2 files / 8 tests passed at 7638daa9e",
"p1_census": "components test program 10 at 7638daa9e (unchanged population), src 0; P1 JSX probe through the real prop: responsiveStyles compiles on all three, node-level children refused on all three",
"pin_reverse_check": "fix ablated -> TS2353 x2 + TS2578 x3, control unchanged; restored -> pin exit 0",
"check_exit_0": "check-changeset-presence (9 source files, 3 packages, 1 changeset), check-changeset-no-major, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:spec-symbols, check:phantom-deps, check:unreferenced-sources — all at 6af8e9da3",
"not_measured": "the full types / react / components suites were not re-run this round: the round's source edit is type-level only (authoring-nodes.ts holds only type declarations and type-only imports) and the pin file was run; CI runs the suites on the new head"
},
"deviations": [
"schema-input-bridge-permanent-4622.md got a dated, append-only note beyond the record's list: it spells the prop's union as `BaseSchema | string | null | undefined`, which this release widens, so the dispatch's changeset rule covers it exactly as it covers 8908. Frontmatter md5 unchanged.",
"The record's sentence 'a node-level children is refused, exactly as on element:text' was measured, not assumed: through the prop union with a `properties` bag present (the probe and the pin both carry one), and on the node types themselves. A node carrying `children` and no bag was NOT MEASURED through the union.",
"The new docblock sentence says the spec's PageComponentSchema refuses a node-level children 'as it does on element:text', narrowed to what was measured (element:text, element:text_input, element:record_picker), not 'every arm'.",
"origin/main moved by 3 commits (including objectui#11348's landing); merged via a private ref with a trailer-carrying merge message, no rebase, no conflicts. The first merge attempt used `-F -`, which git merge does not read; it merged nothing (HEAD and tree unchanged) and was retried with `-m`.",
"The worktree was recreated from the pushed branch at 29acb0107 and removed again after the push; the private refs used for fetching were deleted."
],
"files_changed": 16,
"files_changed_this_round": 5,
"line_budget": "n/a — no skills/** or ratcheted ledger touched; branch diff +539 / -45 against merge-base 3ae919307",
"overlaps": "none; the merge of origin/main had no conflicts"
}
Landed: PR objectui#11385 merged through the queue as 304f61137 (Refs). This card is discharged and closed by the seat in the same act. Its two open questions go to objectui#8347. From the domain:spec @ objectui seat (objectui#10217), session session_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T15:28Z.
Verification
Merge content matches the PR: the merge commit's git patch-id --stable equals the PR's net diff (8a3b0b0d70dc on both sides).
Reviewed head is the landed head:
5933841900 FAIL on 29acb0107: SpecRowNode lacked the arm envelope, and two changeset sentences were false or half true.
5934278074 PASS on 6af8e9da3.
Nothing was pushed after the PASS.
CI on that head: 40 success and 3 skipped by design.
Changeset prose, judged by this seat under the maintainer's ruling A on objectstack#21187 (the isolated review covers the contract faces only). Each sentence was read against the head before enqueue:
11364-public-block-ts-faces: every bullet. In particular, the spec-row nodes are "built the way an arm is built" (ExtendedShape + NODE_ENVELOPE + the tombstones in authoring-nodes.ts); the prop's union is SchemaRenderer.tsx:1058; toRenderableSchema returns SchemaRendererProps['schema'] (schema-input.ts:76); and the assignment-vs-read sentence holds.
The dated, append-only notes on 8908-bridge-falsy-primitives, 9256-public-blocks-content-channels and schema-input-bridge-permanent-4622 each describe what this release changes, with their frontmatter untouched.
Delivered. objectui#8347's preparation for the public-block TS faces.
@object-ui/types gains authoring-nodes.ts, derived by reference:
PublicBlockNode / PublicBlockNodeOf, one member per arm, with the bag closed;
ElementTextInputNode / ElementRecordPickerNode, built as an arm is built from the spec rows;
PageDocumentNode;
their union, AuthoringNode.
SchemaRendererProps.schema is BaseSchema | AuthoringNode | string | null | undefined, with no index signature.
Under objectui#8347's P1 mutation, the components test program goes from 30 to 10. The 10 are the 4 props-alias literals and 6 probes.
Changesets:typesminor and reactminor.
Handed to objectui#8347, unruled:
the props alias on the TS face, which is the maintainer's call; the dev recommends A, not declared;
Activity
objectstack-fleet commentedon Oct 1, 2026
Claim: PM loop round 2 (this card as filed: TypeScript node types for the spec's public blocks, page kinds and the
action:buttonnode, by reference, plusSchemaRenderer's publishedschemaprop accepting them; objectui#8347's preparation)Session:
session_01VhxTqosz7wn54ahqyxgERTAccount:
os-litant(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-11364-public-block-ts-facesWorktree:
objectui-issue-11364Domain:
domain:specSeat:
domain:spec#1(objectui#10217)File surface:
packages/types/src: new TS node types for the spec's public blocks (element:*with theirpropertiesbag), the page kinds (pagekindhome),page:tabsand theaction:buttonnode. Each is by reference to its zod arm or spec row (z.input), ⛔ never a hand-written twin. PlusSchemaNode's union and thezod-mirror-parityrows those types open.packages/react/src/SchemaRenderer.tsx: only the publishedSchemaRendererProps.schematype, widened by those referenced node types. ⛔ Never by an index signature. The internal working type (objectui#11349, landed) stays as it is.element:textpropertiesbag stops type-checking under objectui#8347's P1 mutation, and the correct spelling compiles.propsalias's TS authoring face. It is bound by the maintainer's 2026-08-18 ruling and goes back as anopen_questionwith the four-axis frame.Stop on breach; explain in the report.
Container & model:
M,mode:subagent,model: opus(TIER_DEFAULT; no path-derived mandate)Clause-②: yes
Why yes: new published TS node types are added, and
SchemaRendererProps.schemaaccepts them, so the published TS surface widens. The at-tier contract record is owed on the landing head.Thread-read: none
Serial constraints cleared: read 2026-10-01T10:53Z at objectui
origin/mainb5b0afea5, over all open PRs' file lists and every openpm:dispatchedclaim.b5b0afea5), soSchemaRenderer.tsxis free.action:*executor keys into thepropertiesbag) landed, and its shape is the one to follow.objectql.ts's tree / map / form arms. This card's types are in other regions.objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{ "issue": 11364, "status": "done", "branch": "claude/issue-11364-public-block-ts-faces", "pr": "https://github.com/objectstack-ai/objectui/pull/11385", "session": "session_01VhxTqosz7wn54ahqyxgERT", "premise_still_valid": true, "summary": "@object-ui/types gains authoring-nodes.ts, derived by reference: PublicBlockNode (one member per arm of PublicBlockComponentSchema, each the arm's z.input read off its SHAPE with the object config closed, because the zod BaseSchema is .passthrough() and a bare z.input has keyof = string), PublicBlockNodeOf, ElementTextInputNode and ElementRecordPickerNode (no zod arm exists, so each is built the way an arm is built from the spec's ComponentPropsMap row; the picker also declares the node-level dataSource its renderer reads and leaves the bag's object optional for the spec gate's dataSource waiver), PageDocumentNode (spec PageSchema input, page kind required in type) and their union AuthoringNode. @object-ui/react's SchemaRendererProps.schema is BaseSchema | AuthoringNode | string | null | undefined, with no index signature or Record. Under objectui#8347's P1 mutation the components test program falls from 30 to 10 (base b5b0afea5 to head 29acb0107); the 10 are the 4 props-alias literals and 6 probes no referenced type can or should accept (content 1, a test-registered probe type 2, CASE A's node-level params 1, the spec's deprecated `visibility` alias on element:text 2, which objectui#10872 kept off the TS face). H3 is a fork, not a widening: SpecPageComponent fails the prop under P1 on exactly one key, BaseSchema.visibleWhen (string vs the spec's string-or-Expression), so it goes back as open_questions[1] together with the props question.", "tests": "All at head 29acb0107 unless noted, under os-verify-lock, exit captured before any pipe. (1) P1 census, mutation through ablation-replace.mjs --delete on BaseSchema's comment+signature block (anchor hits 1, blob ce2bc9e31dc6 -> 7fa27a8f1580), types rebuilt on both legs: base b5b0afea5 'COMPONENTS_TEST_PROGRAM_EXIT=2 errors=30', 'COMPONENTS_SRC_PROGRAM_EXIT=0 errors=0'; head 8b59daf00 and 11eaa8ae5 and 29acb0107 each 'errors=10' / src 0. Every leg: 'ok restored: blob == HEAD (ce2bc9e31dc6) and `git diff HEAD` is empty', 'git diff HEAD base.ts bytes: 0', 'RESTORED dist index-sig count (base.d.ts): 2' (mutated legs read 1). Two earlier attempts were no-ops and are not counted: the tool reads its repo from cwd (exit 2, refused, nothing written), then a replacement that is a substring of the anchor (exit 1, restore proven). (2) builds: '@object-ui/components^...' closure exit 0 at 11eaa8ae5; types BUILD_EXIT=0, react BUILD_EXIT=0. (3) type-check, scripts echoed '> tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json' (types) and '> tsc --noEmit && tsc -p tsconfig.test.json' (react, components): TYPECHECK_EXIT=0 errors=0 for all three. (4) vitest: packages/types/ 'Test Files 313 passed (313)', 'Tests 8000 passed (8000)'; packages/react/ 'Test Files 106 passed (106)', 'Tests 1389 passed (1389)'; packages/components/ in --shard legs: 1/3 'Test Files 117 passed (117)' 'Tests 1136 passed | 7 skipped', 2/3 'Test Files 117 passed (117)' 'Tests 1363 passed | 10 skipped' (both at 11eaa8ae5), 3/3 'Test Files 115 passed | 1 skipped (116)' 'Tests 988 passed | 7 skipped' (29acb0107); between those heads only a type-alias default in a declarations-only file moved. A first unsharded components run timed out (VERDICT command-exit 124, NOT MEASURED) and shard 3's first take was exit 99 (queue-timeout, NOT MEASURED); both were retaken. (5) the new pin authoring-nodes-11364.test.ts: every @ts-expect-error consumed (an unused one is TS2578), with a control proving the no-index-signature check fires on the bare z.input of the same arms. (6) H5 dist diff against a base build in a comparison worktree: types adds authoring-nodes.d.ts and one export line; 13 zod/*.zod.d.ts differ only in member print order (token multiset equal to base after normalising whitespace, the union bar and trailing punctuation); react SchemaRenderer.d.ts gains the import and the widened prop, plus a reordered union in an existing inferred render() return. No reverse-verification ablation of the new pin was run separately: the pin is type-level and its directives are TS2578-checked both ways, and the P1 legs above are the mutation that measures the prop.", "mcp_calls": "0 — no MCP GitHub calls", "api_writes": "3 REST writes, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectui/pulls, draft forced, read-back 13402 bytes sent / 13402 stored, identical (objectui#11385); (2) label-write --assign os-litant -> POST /repos/objectstack-ai/objectui/issues/11385/assignees, read back MATCHES, zero label writes; (3) this os-dev-report -> POST /repos/objectstack-ai/objectui/issues/11364/comments. Plus 4 git pushes (the empty-branch probe, the four family commits, the merge of origin/main, the lint fix); git push is not REST.", "open_questions": [ { "question": "The `props` alias on the TypeScript authoring face (the dispatch's ruling: not decided here). The maintainer's 2026-08-18 ruling keeps `props` working at RUNTIME; BaseSchema's TS face does not declare it today (the 4 alias-precedence literals compile only through the index signature). After objectui#8347, does the TS face declare it?", "options": [ "A — Do not declare `props` on the TS face. Runtime keeps the alias under the 2026-08-18 ruling; a TS author of `props` gets TS2353 after the removal; the 4 alias-precedence probe literals become objectui#8347's flips (a cast or @ts-expect-error that names the ruling). Cost: none on a measured producer.", "B — Declare `props?: unknown` (deprecated) on BaseSchema's TS face. Cost: a second spelling of `properties` on every node's TS face, with zero measured TS producers; AI-authored `props` compiles and silently takes the alias path.", "C — Declare `props` only on the AuthoringNode types. Cost: the same second dialect, confined to exactly the nodes whose bag is typed, which re-opens the untyped channel beside the typed one." ], "recommendation": "A. Real business need, measured: objectui's 1720 non-test TS/TSX files author no node `props` (lit control `properties: {` reads 28 on the same population); tracked JSON has 0 files with `\"props\": {` (control 10); objectstack examples/** TS at local 95fed33a2 has 0 (control 52); the published skill (skills/objectui/rules/protocol.md) teaches node-level `props` as WRONG. The only authors are the 4 alias-precedence test probes. Long-term: 协议为基准 — the spec's PageComponentSchema has no `props` key, its bag is `properties`; declaring `props` is the second dialect AGENTS.md #0.1 forbids. AI-proofing: A makes an AI-authored `props` a compile error at the authoring site, while B/C let it compile and ride the tolerant alias path. Startup focus: A adds nothing; B/C add declared surface with no pull, and A is compatible with the 2026-08-18 ruling because that ruling binds the runtime, not the TS face." }, { "question": "H3 fork: should the spec's own PageComponent (re-exported as SpecPageComponent) be assignable to SchemaRenderer's `schema` prop after objectui#8347? Measured under P1: it fails on exactly ONE shared key, `visibleWhen` (BaseSchema declares `string`; the spec declares string or an Expression envelope and normalises a bare string into `{ dialect: 'cel', source }` at parse; the renderer evaluates the envelope). No union of referenced types fixes that without losing the bag check.", "options": [ "A — Widen BaseSchema.visibleWhen (TS face and its zod mirror) to the spec's string-or-Expression, by reference. Measured by type-level simulation with ExpressionWire: SpecPageComponent becomes assignable AND `properties: { contnet }` on element:text stays refused. Cost: an edit to BaseSchema, which this card excluded (objectui#8347's file); it also ends the validator refusing the spec's own parsed envelope (out_of_scope_findings[0]).", "B — Add the spec's PageComponent to the prop's union. Measured: SpecPageComponent becomes assignable, but `{ type: 'element:text', properties: { contnet: 'x' } }` then compiles, because PageComponent's `properties` is Record of string to unknown. Cost: loses the pin on the busiest authoring surface.", "C — Leave it unassignable; a host holding a spec-parsed PageComponent converts it through a bridge. Cost: the decisive measurement on objectui#11364 stays open, and the declaration stays narrower than both the spec and the renderer." ], "recommendation": "A, in objectui#8347's lane (BaseSchema is its file) or a sibling card. Real business need: spec-parsed pages carry the envelope at runtime (the spec normalises every bare visibleWhen string), and the renderer already evaluates it, so this is a real path, not a speculative one. Long-term: the spec declares the union and the renderer implements it; objectui's declaration is the lagging side, so aligning it is protocol-first, while B is exactly the Record-typed tolerance the removal exists to end. AI-proofing: A keeps every typed bag closed; B reopens the silent channel on precisely the nodes this card typed. Startup focus: A widens one existing key's value to the spec's, adds no key, and closes a measured validator refusal at the same time." } ], "out_of_scope_findings": [ "class: b · reach: public door — measured through the published `@object-ui/types/zod` validator: AnyComponentSchema.safeParse of { type: 'element:text', properties: { content: 'x' }, visibleWhen: { dialect: 'cel', source: 'true' } } answers invalid_type at path visibleWhen ('expected string, received object'), while the spec's own PageComponentSchema.safeParse of visibleWhen: 'record.x == 1' yields { dialect: 'cel', source: 'record.x == 1' } and SchemaRenderer evaluates that envelope. Contract text: the spec types PageComponent.visibleWhen as string or Expression. Seam: spec:PageComponentSchema.visibleWhen -> runtime:SchemaRenderer evaluateVisibilityPredicate; objectui declares string on both faces (base.ts BaseSchema.visibleWhen, base.zod.ts `visibleWhen: z.string()`). Same root as open_questions[1]; fold it into objectui#8347's family rather than a single-point card. · dedupe words: visibleWhen expression envelope refused · BaseSchema visibleWhen string only · spec normalizes visibleWhen cel envelope · SpecPageComponent not assignable visibleWhen", "carrier: objectui#11183's lane (domain:skills, governed) · noted, not filed — AGENTS.md #4 still teaches the flat action:button executor spelling ({ type, label, actionType, target } on the node) that objectui#11183 moved into the properties bag; the public-blocks.zod.ts module header likewise still calls the flat spelling 'the TAUGHT spelling'. AGENTS.md section 4's abridged BaseSchema also lists `props` typed as a Record of string to any, which the real BaseSchema does not declare (P1 reads TS2353 'props' does not exist in type 'BaseSchema').", "carrier: 承接者:无 · noted, not filed — the TS face now types element:text_input, element:record_picker and page documents, which AnyComponentSchema still refuses at `type` (invalid_union, 'No matching discriminator'); that is the population registered-types-validate-ratchet-10859 already counts, not a new defect.", "carrier: 承接者:无 · noted, not filed — the spec carries ComponentPropsMap rows for element:image, element:metadata_viewer, element:filter and element:form with no zod arm and no TS node type here; none appears in the census, so none was typed." ], "gates": { "build": "pnpm --filter '@object-ui/components^...' build exit 0 (11eaa8ae5); @object-ui/types and @object-ui/react build exit 0 (29acb0107)", "type-check": "types / react / components TYPECHECK_EXIT=0 errors=0 at 29acb0107, script names echoed", "test": "types 313 files / 8000 tests; react 106 / 1389 (29acb0107); components shards 117+117 (11eaa8ae5) + 115 passed 1 skipped (29acb0107); 10 changed+adjacent files 77 tests (8b59daf00)", "p1_census": "components test program 30 (b5b0afea5) -> 10 (29acb0107), src 0 -> 0; restore proven every leg", "check_exit_0": "check:doc-fences, check:doc-types, check:new-line-citations (0 new), check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check:spec-symbols, check:test-path-roots, check:phantom-deps, check:self-import, check:unreferenced-sources, check:vi-mock-specifiers, check:doc-example-ids, check:component-surface-parity, check:esm-specifiers, check-changeset-presence (9 source files, 3 packages, 1 changeset), check-changeset-no-major, check-governed-queue-guard --test: NOT GOVERNED (14 paths)", "lint_narrowed": "9 touched .ts/.tsx files (population: eslint.config.js `**/*.{ts,tsx}`), --format json at 29acb0107: 1 error / 19 warnings; base edfcf5a5e same 7 pre-existing files 1 error / 19 warnings; the error is the base-present no-restricted-imports on FormField in packages/types/src/index.ts; invariance: no parserOptions.project / projectService (type-aware off); full pnpm lint is CI's", "not_measured": "check:doc-snippets, reason: prerequisite 34-package build — narrowed instead: the 2 added fences compile --strict against types/dist/authoring-nodes.d.ts and react/dist/SchemaRenderer.d.ts (exit 0, read from --listFiles), control with `contnet` fails TS2561. check:readme-exports exit 1 is its could-not-judge state (431 self-imports in unbuilt packages); --list reads both new README imports as real." }, "deviations": [ "SchemaNode is NOT widened, although the claim's file surface lists 'SchemaNode's union'. The dispatch made it conditional ('if it is the union' the prop uses); the prop is typed as its own union, and all 30 census literals reach the prop directly. Measured: with the signature present, a reader of a widened SchemaNode that reads an undeclared key (`columns`) turns TS2339 on the closed member, which would break 'every edit compiles with the signature present'.", "No zod-mirror-parity row opened: no zod const was added, and the existing EXCLUSIONS reason for the public-block arms ('no TS declaration in this package restates the node') stays true because the new types derive from the arms.", "Files outside the claim's listed surface, each forced: packages/react/src/__tests__/SchemaRenderer.propsResolution.test.ts (assertion 1 pins the prop's exact union), .changeset/9256-public-blocks-content-channels.md (dated append-only note per the dispatch's changeset rule, frontmatter md5 unchanged), packages/react/README.md (docs for the prop).", "The H3 widening stops at the union of referenced types; SpecPageComponent's assignability is reported as a fork (open_questions[1]) per Zone 3, not implemented.", "Residual 10 under P1 includes 2 `visibility` literals on element:text in page-variables (the spec's deprecated alias, off the TS face by objectui#10872) that TS2353 hid behind `properties` at base; they are classified with the probe flips, and the seat may classify them otherwise.", "origin/main moved by 6 commits during the run; it was fetched into a private ref and merged (no rebase), the unpushed merge commit amended only to add the trailer pair, and every gate after the merge was retaken on the merged tree.", "Commit trailers use the model-free pair AGENTS.md prescribes, and the PR footer uses the session-URL form, instead of the harness's model-named trailer and footer.", "The `verify` skill was not run before commits: its scope is designer previews in a browser, and this diff is types, a prop type and test literals.", "Scratch probes lived briefly in gitignored packages/*/.objectui-tmp inside the worktree; removed before the worktree was removed. Both worktrees (objectui-issue-11364 and the comparison objectui-issue-11364-cmp) are removed; the branch is fully pushed." ], "files_changed": 14, "line_budget": "n/a — no skills/** or ratcheted ledger touched; diff +481 / -45 against merge-base edfcf5a5e", "overlaps": "none found with in-flight objectui#11348 / objectui#11355; the merge of origin/main had no conflicts" }Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
ACCEPT: report
5933487223, PR #11385 at29acb0107, ready for the at-tier contract review (Refs). From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T14:28Z.Checked against the PR, not the report's prose
check-governed-mergesreads NOT governed (0 of 14 paths). Size: +481 / -45.git merge-treeagainstmain3ae919307is clean.Refs #11364/Clause-②: yes. That is the right arm on the diff's face: new published TS node types, andSchemaRendererProps.schemaaccepts them. No face narrows.@object-ui/typesminor+@object-ui/reactminor, objectui#7722's class for a published member gained.What lands
packages/types/src/authoring-nodes.ts:PublicBlockNode: one member per arm ofPublicBlockComponentSchema, each the arm'sz.inputread off its shape, with the bag closed;ElementTextInputNode/ElementRecordPickerNode, built from the spec'sComponentPropsMaprows;PageDocumentNode, from the spec'sPageSchemainput;AuthoringNode.SchemaRendererProps.schemaisBaseSchema | AuthoringNode | string | null | undefined, with no index signature and noRecord.props-alias literals and 6 probes.Deviations accepted
SchemaNodeis not widened. The dispatch made it conditional. The measurement shows that a closed member would turn existing undeclared reads into TS2339 while the signature is still present.zod-mirror-parityrow opens, because no zod const is added.propsResolutiontest that pins the prop's exact union; a dated, append-only note on the9256changeset; and the react README.For the record to judge
ElementTextInputNodeandElementRecordPickerNodehave no zod arm, andAnyComponentSchemastill refuses both attype. Judge whether a TS face that accepts what the published validator refuses is a face split this PR opens, or the populationregistered-types-validate-ratchet-10859already counts, as the report says.dataSourceis declared because its renderer reads it. Judge whether that holds.Open questions: neither is ruled by this seat. Both go to objectui#8347, whose last PR owns
BaseSchemaand the residual literals. This card is discharged when the PR lands. The maintainer closed the shift at 13:54Z (「当前任务处理完,合并后就下班」).propsalias on the TS authoring face. It is bound by the maintainer's 2026-08-18 ruling, so it is the maintainer's call. The report's options:props?: unknownonBaseSchema.AuthoringNodeonly.The report recommends A, with measured producers: 0 non-test TS, 0 tracked JSON, and 0 objectstack examples author a node
props.BaseSchema.visibleWhenisstringon both faces, while the spec'sPageComponent.visibleWhenis string orExpression.@object-ui/types/zod'sAnyComponentSchemarefusesvisibleWhen: { dialect: 'cel', source: 'true' }, the envelope the spec's own parse produces from a bare string and thatSchemaRendererevaluates. It is also the one key that keeps a spec-parsedPageComponentoff the prop under P1.BaseSchemais spec(types)!: remove BaseSchema's index signature from the authoring face, declare the riders, pin the compile-fail — executes the #7927 removal ruling (governed: AGENTS.md #0.1) #8347's reserved file, so it is booked there, ⛔ not carded separately.visibleWhenwithExpression/ envelope /dialect/BaseSchemagave 23 hits. None is open on this defect; objectui#11044 and finding(types): the predicate wire union is still spelled inline in five places beside the sharedExpressionWire, and twocontent/docs/corerows spell the pre-#7530 union #7725 are adjacent and closed.objectstack-fleet commentedon Oct 1, 2026
os-dev-report
{ "issue": 11364, "round": 2, "status": "done", "branch": "claude/issue-11364-public-block-ts-faces", "pr": "https://github.com/objectstack-ai/objectui/pull/11385", "session": "session_01VhxTqosz7wn54ahqyxgERT", "premise_still_valid": true, "summary": "Round 2 answers contract record 5933841900 (FAIL on 29acb0107), head now 6af8e9da3, PR still draft. A: SpecRowNode (ElementTextInputNode / ElementRecordPickerNode) is now the type-level twin of an arm's BaseSchema.extend(...): Omit of the zod BaseSchema shape by the own keys, then the type literal, typeof NODE_ENVELOPE (responsiveStyles), properties, and body / children as retirementTombstone's return type, all type-only imports. Under the real P1 mutation, through the actual SchemaRenderer schema prop, responsiveStyles now compiles on element:text, element:text_input and element:record_picker, and a node-level children is refused (TS2353) on all three, exactly as on element:text. Both halves are pinned in authoring-nodes-11364.test.ts beside the element:text control, and the pin's reverse check turned red in the predicted direction. The three published sentences (changeset, authoring-nodes.ts docblock, PR body) now state what the type is built from. B: 11364's changeset says toRenderableSchema's return widens with the prop; 8908 gets a dated append-only note, and so does schema-input-bridge-permanent-4622 (it spells the prop's old union, found by the same rule). C: the compile sentence now rests on its real ground: assignment is preserved, reads are not guaranteed on a union, and no caller here reads a key off the prop's type or the bridge's return. origin/main (3ae919307) was merged, not rebased. The props and visibleWhen questions are unchanged and unruled; they go to objectui#8347.", "tests": "Round 2, under os-verify-lock, exit captured before any pipe. (1) Before the fix, type-level P1 simulation (the pin's PostRemovalSchema construction), ten cases: through the union, element:text + responsiveStyles compiled, element:text_input / element:record_picker + responsiveStyles were TS2353, element:text + node children was refused, element:text_input + node children compiled; on the node types, both spec-row nodes accepted children (element:text refused it, TS2322) and refused responsiveStyles. After the fix, the same ten cases all match element:text. (2) Real P1 at 7638daa9e via ablation-replace.mjs (anchor 1 -> 0, blob ce2bc9e31dc6 -> 7fa27a8f1580), types rebuilt mutated, a throwaway JSX probe compiled through the actual SchemaRenderer prop against the built react dist and mutated types dist: 'PROBE_EXIT=2 errors=3', the 3 errors exactly the three node-level children cases (TS2353), and the three responsiveStyles cases compile; 'COMPONENTS_TEST_PROGRAM_EXIT=2 errors=10' (unchanged population), src 0; restore 'ok restored: blob == HEAD (ce2bc9e31dc6) and `git diff HEAD` is empty', 'git diff HEAD base.ts bytes: 0', dist index-sig count 2. (3) Reverse check of the new pin, with the fix committed first (f525620db): ablation-replace on SpecRowNode's own keys (marker on disk 1, NODE_ENVELOPE spread on disk 0) -> 'MUTATED_PIN_TSC_EXIT=2': TS2353 x2 on the two styled spec-row literals, TS2578 x3 on the three spec-row children directives, element:text control unchanged; restore 'ok restored: blob == HEAD (a2e6ac965eec)', diff 0 bytes, 'RESTORED_PIN_TSC_EXIT=0'. (4) At 7638daa9e: '@object-ui/components^...' 'CLOSURE_BUILD_EXIT=0'; type-check types / react / components 'TYPECHECK_EXIT=0 errors=0', scripts echoed ('> tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json' for types, '> tsc --noEmit && tsc -p tsconfig.test.json' for react and components); vitest of the pin + SchemaRenderer.propsResolution 'Test Files 2 passed (2)', 'Tests 8 passed (8)'. (5) At 6af8e9da3 (only a changeset .md after 7638daa9e): check-changeset-presence, check-changeset-no-major, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:spec-symbols, check:phantom-deps, check:unreferenced-sources, each EXIT=0. Lint of the two edited .ts files: eslint exit 0, no output.", "mcp_calls": "0 — no MCP GitHub calls", "api_writes": "2 REST writes this round, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) issue_patch -> PATCH /repos/objectstack-ai/objectui/issues/11385 (the PR body), read-back 17040 bytes sent / 17040 stored, identical; (2) this round-2 os-dev-report -> POST /repos/objectstack-ai/objectui/issues/11364/comments. Plus 1 git push (29acb0107..6af8e9da3); git push is not REST. Reads only otherwise (the record 5933841900, the PR state).", "open_questions": [ { "question": "The `props` alias on the TypeScript authoring face (the dispatch's ruling: not decided here). The maintainer's 2026-08-18 ruling keeps `props` working at RUNTIME; BaseSchema's TS face does not declare it today (the 4 alias-precedence literals compile only through the index signature). After objectui#8347, does the TS face declare it?", "options": [ "A — Do not declare `props` on the TS face. Runtime keeps the alias under the 2026-08-18 ruling; a TS author of `props` gets TS2353 after the removal; the 4 alias-precedence probe literals become objectui#8347's flips (a cast or @ts-expect-error that names the ruling). Cost: none on a measured producer.", "B — Declare `props?: unknown` (deprecated) on BaseSchema's TS face. Cost: a second spelling of `properties` on every node's TS face, with zero measured TS producers; AI-authored `props` compiles and silently takes the alias path.", "C — Declare `props` only on the AuthoringNode types. Cost: the same second dialect, confined to exactly the nodes whose bag is typed, which re-opens the untyped channel beside the typed one." ], "recommendation": "A. Real business need, measured: objectui's 1720 non-test TS/TSX files author no node `props` (lit control `properties: {` reads 28 on the same population); tracked JSON has 0 files with `\"props\": {` (control 10); objectstack examples/** TS at local 95fed33a2 has 0 (control 52); the published skill (skills/objectui/rules/protocol.md) teaches node-level `props` as WRONG. The only authors are the 4 alias-precedence test probes. Long-term: 协议为基准 — the spec's PageComponentSchema has no `props` key, its bag is `properties`; declaring `props` is the second dialect AGENTS.md #0.1 forbids. AI-proofing: A makes an AI-authored `props` a compile error at the authoring site, while B/C let it compile and ride the tolerant alias path. Startup focus: A adds nothing; B/C add declared surface with no pull, and A is compatible with the 2026-08-18 ruling because that ruling binds the runtime, not the TS face." }, { "question": "H3 fork: should the spec's own PageComponent (re-exported as SpecPageComponent) be assignable to SchemaRenderer's `schema` prop after objectui#8347? Measured under P1: it fails on exactly ONE shared key, `visibleWhen` (BaseSchema declares `string`; the spec declares string or an Expression envelope and normalises a bare string into `{ dialect: 'cel', source }` at parse; the renderer evaluates the envelope). No union of referenced types fixes that without losing the bag check.", "options": [ "A — Widen BaseSchema.visibleWhen (TS face and its zod mirror) to the spec's string-or-Expression, by reference. Measured by type-level simulation with ExpressionWire: SpecPageComponent becomes assignable AND `properties: { contnet }` on element:text stays refused. Cost: an edit to BaseSchema, which this card excluded (objectui#8347's file); it also ends the validator refusing the spec's own parsed envelope (out_of_scope_findings[0]).", "B — Add the spec's PageComponent to the prop's union. Measured: SpecPageComponent becomes assignable, but `{ type: 'element:text', properties: { contnet: 'x' } }` then compiles, because PageComponent's `properties` is Record of string to unknown. Cost: loses the pin on the busiest authoring surface.", "C — Leave it unassignable; a host holding a spec-parsed PageComponent converts it through a bridge. Cost: the decisive measurement on objectui#11364 stays open, and the declaration stays narrower than both the spec and the renderer." ], "recommendation": "A, in objectui#8347's lane (BaseSchema is its file) or a sibling card. Real business need: spec-parsed pages carry the envelope at runtime (the spec normalises every bare visibleWhen string), and the renderer already evaluates it, so this is a real path, not a speculative one. Long-term: the spec declares the union and the renderer implements it; objectui's declaration is the lagging side, so aligning it is protocol-first, while B is exactly the Record-typed tolerance the removal exists to end. AI-proofing: A keeps every typed bag closed; B reopens the silent channel on precisely the nodes this card typed. Startup focus: A widens one existing key's value to the spec's, adds no key, and closes a measured validator refusal at the same time." } ], "out_of_scope_findings": [], "gates": { "build": "pnpm --filter '@object-ui/components^...' build exit 0 at 7638daa9e", "type-check": "types / react / components TYPECHECK_EXIT=0 errors=0 at 7638daa9e, script names echoed", "test": "authoring-nodes-11364 + SchemaRenderer.propsResolution: 2 files / 8 tests passed at 7638daa9e", "p1_census": "components test program 10 at 7638daa9e (unchanged population), src 0; P1 JSX probe through the real prop: responsiveStyles compiles on all three, node-level children refused on all three", "pin_reverse_check": "fix ablated -> TS2353 x2 + TS2578 x3, control unchanged; restored -> pin exit 0", "check_exit_0": "check-changeset-presence (9 source files, 3 packages, 1 changeset), check-changeset-no-major, check:changeset-claims, check:pending-changeset-literals, check:control-bytes, check:new-line-citations (0 new), check:spec-symbols, check:phantom-deps, check:unreferenced-sources — all at 6af8e9da3", "not_measured": "the full types / react / components suites were not re-run this round: the round's source edit is type-level only (authoring-nodes.ts holds only type declarations and type-only imports) and the pin file was run; CI runs the suites on the new head" }, "deviations": [ "schema-input-bridge-permanent-4622.md got a dated, append-only note beyond the record's list: it spells the prop's union as `BaseSchema | string | null | undefined`, which this release widens, so the dispatch's changeset rule covers it exactly as it covers 8908. Frontmatter md5 unchanged.", "The record's sentence 'a node-level children is refused, exactly as on element:text' was measured, not assumed: through the prop union with a `properties` bag present (the probe and the pin both carry one), and on the node types themselves. A node carrying `children` and no bag was NOT MEASURED through the union.", "The new docblock sentence says the spec's PageComponentSchema refuses a node-level children 'as it does on element:text', narrowed to what was measured (element:text, element:text_input, element:record_picker), not 'every arm'.", "origin/main moved by 3 commits (including objectui#11348's landing); merged via a private ref with a trailer-carrying merge message, no rebase, no conflicts. The first merge attempt used `-F -`, which git merge does not read; it merged nothing (HEAD and tree unchanged) and was retried with `-m`.", "The worktree was recreated from the pushed branch at 29acb0107 and removed again after the push; the private refs used for fetching were deleted." ], "files_changed": 16, "files_changed_this_round": 5, "line_budget": "n/a — no skills/** or ratcheted ledger touched; branch diff +539 / -45 against merge-base 3ae919307", "overlaps": "none; the merge of origin/main had no conflicts" }Generated by Claude Code
objectstack-fleet commentedon Oct 1, 2026
Landed: PR objectui#11385 merged through the queue as
304f61137(Refs). This card is discharged and closed by the seat in the same act. Its two open questions go to objectui#8347. From thedomain:spec @ objectuiseat (objectui#10217), sessionsession_01VhxTqosz7wn54ahqyxgERT, 2026-10-01T15:28Z.Verification
git patch-id --stableequals the PR's net diff (8a3b0b0d70dcon both sides).5933841900FAIL on29acb0107:SpecRowNodelacked the arm envelope, and two changeset sentences were false or half true.5934278074PASS on6af8e9da3.Nothing was pushed after the PASS.
11364-public-block-ts-faces: every bullet. In particular, the spec-row nodes are "built the way an arm is built" (ExtendedShape+NODE_ENVELOPE+ the tombstones inauthoring-nodes.ts); the prop's union isSchemaRenderer.tsx:1058;toRenderableSchemareturnsSchemaRendererProps['schema'](schema-input.ts:76); and the assignment-vs-read sentence holds.8908-bridge-falsy-primitives,9256-public-blocks-content-channelsandschema-input-bridge-permanent-4622each describe what this release changes, with their frontmatter untouched.Delivered. objectui#8347's preparation for the public-block TS faces.
@object-ui/typesgainsauthoring-nodes.ts, derived by reference:PublicBlockNode/PublicBlockNodeOf, one member per arm, with the bag closed;ElementTextInputNode/ElementRecordPickerNode, built as an arm is built from the spec rows;PageDocumentNode;AuthoringNode.SchemaRendererProps.schemaisBaseSchema | AuthoringNode | string | null | undefined, with no index signature.props-alias literals and 6 probes.typesminorandreactminor.Handed to objectui#8347, unruled:
propsalias on the TS face, which is the maintainer's call; the dev recommends A, not declared;BaseSchema.visibleWhento the spec's string-or-Expression, the public-door validator refusal measured here.A hand-off comment on spec(types)!: remove BaseSchema's index signature from the authoring face, declare the riders, pin the compile-fail — executes the #7927 removal ruling (governed: AGENTS.md #0.1) #8347 carries both, with the record's fact on
propsoption B.The leftover
pm:dispatchedis stripped in the same act.feat(types,react): TypeScript authoring types for the spec's public b…