Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
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

Closed

Description

@objectstack-fleet

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:

  1. 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.
  2. 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.
  3. 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.
  4. ⛔ 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:text properties 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

Activity

  1. added
    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lane
    and removed on Oct 1, 2026
  2. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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:text properties bag stops type-checking under objectui#8347's P1 mutation, and the correct spelling compiles.
    • Docs (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2) and one changeset per released package changed.
    • ⛔ 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/main b5b0afea5, 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.
  3. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  4. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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 main 3ae919307 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/types minor + @object-ui/react minor, 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 (「当前任务处理完,合并后就下班」).

    1. 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:
    2. BaseSchema.visibleWhen is string on both faces, while the spec's PageComponent.visibleWhen is string or Expression.
  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    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: types minor and react minor.

    Handed to objectui#8347, unruled:

    1. the props alias on the TS face, which is the maintainer's call; the dev recommends A, not declared;
    2. widening BaseSchema.visibleWhen to 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 props option B.

    The leftover pm:dispatched is stripped in the same act.

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

Metadata

Metadata

Assignees

Labels

domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions