Repository navigation
fix(sdui-parser): containment reads the declared children input, not isContainer; lockstep pins the predicate - #20065
Conversation
…isContainer Port of objectui 5ea623ea (objectui#9910 Q1-A) to the save-gate copy. Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
…2 and the five served entries Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
…cate against objectui's record Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
… input Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check5 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 16949f8349c03a32d52dbcd81bacf4fe5cd83127 && git checkout 16949f8349c03a32d52dbcd81bacf4fe5cd83127
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 66960564d9b628c446be3c8f8a8f0217c367a17f 1fb16a27f61f562b54658045c87590574ecc4b12 && git checkout -B drift-repro 66960564d9b628c446be3c8f8a8f0217c367a17f && git merge --no-ff 1fb16a27f61f562b54658045c87590574ecc4b12
node scripts/docs-audit/affected-docs.mjs --json 66960564d9b628c446be3c8f8a8f0217c367a17f |
… input Containment is the declared children input since the sdui-parser port; the fixture nests a child under flex, so it declares it. Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgments
② Semver level
Judgment:
It pulls the save gate back to the renderer's declared contract. The ③ Boundary flags
Implemented-by: VERDICT: PASS Landing condition: amend the changeset's final paragraph so it names Isolated at-tier reviewer, adopted by the Generated by Claude Code |
…or the warning's reach Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgments
② Semver levelUnchanged from the record on ③ Boundary flags
Implemented-by: VERDICT: PASS No landing conditions remain: this delta satisfies the one condition from the prior record. Isolated at-tier reviewer, adopted by the Generated by Claude Code |
Fixes #19969
Clause-②: no
What this does
The save gate's
validateTreeinpackages/sdui-parsernow decidesnot-a-containerby the component's declaredchildreninput. It no longer readsisContainer, and there is no fallback to the flag. This is the byte-equal port of objectui5ea623ea(objectui#9910 Q1-A, re-cut as objectui PR #10270), which is already inside the console pinf8a9d0fb0596(PR #20036). The objectui side it mirrors ispackages/sdui-parser/src/validate.tsat the pin:CHILD_LIST_KEYat:33,acceptsChildrenat:57-59and the containment branch at:220.comp.inputs.some((input) => input.name === CHILD_LIST_KEY). It matches on the namechildrenalone, whatever the input's coarse type. The dispatch brief said "namechildren, typeslot". objectui deliberately matches on the name only, because the five page kinds publishchildrentypedarray, and objectui pins that case. This port follows objectui, not the brief.unknown-componentand gets no containment verdict. This is unchanged and is the same on both sides.isContainerstays onManifestComponent, re-described as layout containment in the same words objectui uses.index.tsis unchanged: no new public export. objectui exportedacceptsChildrenandCHILD_LIST_KEYfrom its entry for its own census tests, and nothing here needs that.The lockstep pin (maintainer-named in the ruling)
scripts/check-sdui-lockstep.mjsnow also compares the containment predicate. The gate extracts the condition of the oneifinvalidate.tswhose body stampsnot-a-container. It adds the module-level declarations that condition reaches, transitively (acceptsChildren, thenCHILD_LIST_KEY), and reduces the result to its token sequence. Comments and wrapping do not change the reading. A changed token does. The gate compares this reading against a newcontainmentPredicatefield in the vendored objectui record.pnpm gen:sdui-lockstepagainst an objectui checkout parked on the pinf8a9d0fb0596. Every other field of the record is byte-unchanged; the only diff is the newcontainmentPredicateblock.validate.tshas zero or two containment branches, the gate refuses ([containment-unreachable]). It never measures only one of two.The five served components (tracked
sdui.manifest.jsonat0bf85eaa, 5 of 59 disagree)isContainerchildrenbadgeslot)not-a-container(false)alertslot)buttonslot)page:tabsnot-a-containerpage:accordionnot-a-containerThe new
containment-declared-slot.test.tsuses these five entries, copied verbatim from the manifest. Each gets its own case, plus a control asserting that the two predicates really disagree on every one. The file also carries objectui's 2×2 (flag × slot) reading.Ablation (a trap restores the file, and the restore is proven byte-identical to HEAD by blob hash and an empty
git diff HEAD)!comp.isContainerthroughscripts/ablation-replace.mjs: anchor 1 to 0, blob819b7b6a83acto670ce98bdb15.node scripts/check-sdui-lockstep.mjsexited 1 with[containment-drift], printing both texts. The file was then restored to blob819b7b6a83ac= HEAD.isContainerfallback to theacceptsChildrenreturn. The gate exited 1 with[containment-drift]. Restored to819b7b6a83ac.pnpm check:sdui-lockstepon HEADf3ff1e6e9exits 0: the self-test passes, and the gate reports agreement on all 26 codes and on thenot-a-containerpredicate (blob76c18fb95d1f).Verification (HEAD
f3ff1e6e9; re-run onbfb082b0bin the section below)pnpm --filter @objectstack/sdui-parser exec vitest run: 11 files, 189 tests passed (VERDICT command-exit 0).pnpm --filter @objectstack/sdui-parser typecheck: exit 0.--listFilesincludes the new test.pnpm check:sdui-lockstep: exit 0.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 81 commands.--ranreconciles: 77 exit 0, 4 NOT MEASURED, 0 unrun.check:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debteach exited 3 because they need a full workspace build.check:pm-dispatch-gates. Its self-test ran past the foreground cap, was killed twice, and is declared to CI.check-plugin-teardown-shape --self-testfirst exited 3 because this is a shallow checkout. It exited 0 after fetching its pinned fixture commit.eslint --no-inline-configover the 6 touched source files: 0 errors, 0 warnings. This is a targeted run, not a proven narrowing, so the fullpnpm lintis declared to CI.packages/lint fixture: resolved by PM ruling
5825043664The PM ruling on #19969 (comment
5825043664, Q2 = A) widened this card file surface by exactly one test fixture. Commitbfb082b0bgives the hand-madeflexentry atpackages/lint/src/validate-jsx-pages.test.ts:39thechildreninput, the same triage objectui made on its own fixtures:No other
packages/lintfile changes;authoring-rules.tsand the lint caller are untouched. Q1 = A, provisionally: the changeset stayspatchwithClause-②: no, and the contract review judges it on the final head.On head
bfb082b0b, after rebuilding the@objectstack/lintdependency closure:pnpm --filter @objectstack/lint exec vitest run: 108 of 108 files passed; 4133 tests passed, 5 skipped, 0 failed (VERDICT command-exit 0). Before this commit it was 1 failed / 4132 passed.pnpm --filter @objectstack/lint typecheck: exit 0.pnpm --filter @objectstack/sdui-parser typecheck: exit 0.pnpm check:sdui-lockstep: exit 0.dispatch-gates --commandson the 9-path set derived 82 commands (one new:check:docs-transcript-drift, exit 0 after building@objectstack/lint).--ranreconciles: 78 exit 0, 4 NOT MEASURED, 0 unrun. The four arecheck:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debt(each exit 3, prerequisite: a full workspace build) andcheck:pm-dispatch-gates(over the foreground cap). All four are declared to CI.Acceptance notes
codegen.ts'sgenerateBlockListstill labels its column "container" fromisContainer. Under Q2-A that column now means layout container. It is unchanged here, and objectui leaves it as-is too.Generated by Claude Code