Repository navigation
chore(objectui): bump the console pin to db11afd4967c (carries objectui#11119, the objectui#11105 fix) - #20706
Conversation
…ui#11119, the objectui#11105 fix)
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 5 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 138 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 fa9e366c44a895f3660c22c0d34202e3ec23fc53 && git checkout fa9e366c44a895f3660c22c0d34202e3ec23fc53
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7053333e1e2194cea2e21d8b2e1c9b6f081fcfa6 ef6276c0e9cc91a3fdcc9acc5acea9e897b0fb7f && git checkout -B drift-repro 7053333e1e2194cea2e21d8b2e1c9b6f081fcfa6 && git merge --no-ff ef6276c0e9cc91a3fdcc9acc5acea9e897b0fb7f
node scripts/docs-audit/affected-docs.mjs --json 7053333e1e2194cea2e21d8b2e1c9b6f081fcfa6
|
Every record asserting the pin was re-read at the new pin: anchors mapped through the pin-to-pin diff, hashes and counts re-taken with the method each record names, and the ListMapConfigSchema record corrected where objectui#5157 closed ObjectMapConfigSchema. Comments, citation strings and one describe sentence only.
… bump Replaces the bump script's placeholder in .changeset/console-db11afd4967c.md with `not-required (no-migration-prescription)`, the maintainer's disposition, takeover claim 5903537467, worded after the last pin bump's (3cf6449). The reason block states what this diff moves: the pin, the console changeset, the sdui manifest and record, the lockstep record, and the pin citations in packages/spec/src, which change describe text and migration-entry description text with no key added, removed or renamed. The six declared-breaking entries are objectui's own package surfaces. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Read: card #20638 (body and all 7 comments, including the triage target rule ① Derived judgmentsContainment (REST compare, not the dev's table).
The re-measured numbers, re-taken at the pin (nine, beyond the three asked):
Migration registry. Only the generated region moves, and it moves by exactly the six entries' text (43 lines each side, identical hunks); ② Semver level
ADR-0087 disposition
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…undle in view (objectstack-ai#20646) (objectstack-ai#20743) Fixes objectstack-ai#20646 Clause-②: no `Console Pin Gate` is red on `main` since `fbec216e2d` (PR objectstack-ai#20695, the `@objectstack/spec/migrations` entry split). Its build step exits 2: "Neither spec appears in the built console — no @objectstack/spec content matched." The injection works. The check was reading text the console never bundles. This PR makes the build-time probe derivation choose with the bundle in view. The fresh leg stops reading entries the console never imports, and the stale leg becomes strictly stronger. ## Root cause, measured `scripts/console-spec-probes.mjs` builds each spec's blob from every JS file the package's `exports` map resolves to. The assertion then took the alphabetically first unique `.describe()` text on each side. A console bundles only the entries it imports, so either probe could come from an entry the bundle can never carry. The readings below compare the published `@objectstack/spec` 17.4.0 that objectui locks at pin `dd3f7e1be356` with the framework spec. | leg | framework | fresh witness (carried by) | in bundle | stale detector (carried by) | in bundle | old check | | --- | --- | --- | --- | --- | --- | --- | | dark | `f927864ea0` (main before the split) | "A public export added or removed by one release." (`.`) | yes | "Accepted developer agreement version" (`./cloud`) | no | exit 0 | | lit | `fbec216e2d` (`origin/main`) | the same text, now (`./migrations`) | no | the same (`./cloud`) | no | exit 2 | - The witness moved with the split from the root to `./migrations`, which the console never imports. Meanwhile 102 of the 142 injected-only descriptions ARE in that bundle; the first sorts from `./ui`. - The stale leg was already blind. Its detector sits in the published `./cloud` entry, which objectui's shipped source never imports: 116 `/ui`, 62 `/data`, 17 `/kernel` and so on, and no `/cloud`. A console built WITHOUT the injection carries 56 of the 160 published-only descriptions, and not that one. - The spec side is not the cause. The console never imported the change manifest, and the injected spec is in the bundle. `packages/spec` is untouched here. ## The fix (`scripts/console-spec-probes.mjs`, `chooseProbes`) - **Fresh witness:** the first injected-only description the bundle DOES carry. With none carried, "neither spec appears" is still exit 2. - **Stale leg:** judged over EVERY published-only description. It fails when ANY of them is in the bundle, and reports the first one found with the count. A bundle the old single pick flagged is still flagged, so this is strictly stronger. - **Stamp:** keeps its shape and version. With nothing published in the bundle, the stamped detector is the same one `pickProbe` chose, so `check:console-injection`'s cache-hit replay is unchanged. - **Assertion script:** `scripts/assert-console-spec-injection.mjs` calls `chooseProbes` and prints the counts. The exit ladder is unchanged: 0 verified, 1 published spec bundled, 2 unverified. - **Self-test:** `scripts/check-console-injection.mjs` gains battery 13 (roster floor 10 to 11). It runs the real assertion script on fixtures whose first unique candidate is not in the bundle: - a working injection verifies, and its stamp replays green; - a published-only description the old pick skipped fails the build and writes no stamp; - neither side present stays exit 2. I checked that the battery can fail: reverting `chooseProbes` to first-pick semantics made it red with 5 failures, and restoring the fix turned it green again. ## Proof All legs ran against real `vite build`s of the console, with the old and the new assertion on the same bundle. | console build | objectui pin | new check | old check | | --- | --- | --- | --- | | injected spec from `fbec216e2d` | `dd3f7e1be356` | exit 0 (witness from `./ui`; 102/142 present; 0/160 published) | exit 2 | | NOT injected (published spec) | `dd3f7e1be356` | exit 1 (56/160 published-only descriptions present) | exit 2 | | a bundle with no spec text | — | exit 2 ("Neither spec appears") | — | | injected spec from `f927864ea0` (dark) | `dd3f7e1be356` | exit 0 (111/142 present) | exit 0 | | injected spec from `fbec216e2d` | `db11afd49670` (PR objectstack-ai#20706's pin) | exit 0 (102/142; 0/160) | exit 2 | | NOT injected (published spec) | `db11afd49670` | exit 1 (56/160) | exit 2 | On this branch's head, the Console Pin Gate's steps run locally all pass: `bash scripts/build-console.sh` with the injected spec exits 0; the dist presence assert passes; `pnpm check:console-sha` exits 0; and `pnpm check:console-injection --require-stamp` exits 0 and replays the new stamp. ## Verification record - `dispatch-gates --commands` for the 3 changed paths derives 31 families. `--ran` reconciles them as 30 run, all exit 0, and 1 NOT MEASURED: `check:pm-dispatch-gates`, whose self-test alone outruns the 590-second foreground cap here. It does not read these files' behaviour. - `node scripts/check-console-injection.mjs --self-test` passes: 44 assertions, battery floor met. - Lint, narrowed and proven: `eslint --no-inline-config` on the 3 changed files reports 0 errors and 0 warnings. The config enables no type-aware linting, so untouched files' verdicts cannot move. - No changeset: the diff touches only root `scripts/`, which ship in no published package (`@objectstack/spec-monorepo` is private). ## Acceptance notes - **The cache-hit replay's stale leg is still a single stamped detector.** At this pin that detector is `./cloud` text no console bundles, so on a dist-cache HIT the replay cannot catch a published spec. The build-time leg now can. Fixing the replay needs a multi-detector stamp (a `stampVersion` bump that invalidates every cached dist under the unchanged cache key), which is outside this claim. Carrier: the `domain:spec` seat. Noted, not filed. - **CI on this head:** `Console Pin Gate` (job 109696257740) concluded success. The dist cache missed, so step 11 built the console and ran the changed assertion (105 of 145 injected-only descriptions present, all 160 published-only absent), and steps 12 to 14 passed on that fresh dist. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ Co-authored-by: Claude <noreply@anthropic.com>
…s to the commits that decided them (stage 10) (objectstack-ai#20750) Part of objectstack-ai#20234 Clause-②: no Stage 10 of the dead-citation sweep: the migration registry's hand-written entries. Every tracker number in `packages/spec/src/migrations/entries/**` that no longer exists on the board now cites the commit that decided it, in ruling C+D form C (ruling `5749154545` on objectstack-ai#19123). Where the number alone carried the meaning, the line now says what was decided. That is 131 sites over 21 numbers. All of them are comments; the entries' string literals carry no dead number. `migrations/registry.ts` moves only by `gen:migration-registry`. No entry id, literal, order, `conversionIds` or code token moves, and no live citation is removed. ## Boundary - **In:** all of `entries/**`. The gate's census reads 117 sites there, under the claim's ~120 slicing threshold, so there is no slice. My raw walk finds 14 more comment sites that the census does not count (see *Census* below), which gives 131 in total. They are the same dead card on the line after a counted site, plus one README line, so they are rewritten with it. - **Regenerated only:** `migrations/registry.ts`, 128 lines. `spec-changes.json` and `docs/protocol-upgrade-guide.md` do not move, because entry comments are never projected into them. Both `check:` scripts pass with no regeneration. - **Out, per the claim:** `registry.ts`'s hand-written parts. They still hold 13 dead sites, listed under Acceptance notes. The six `18.*-unit-in-key.ts` entries that PR objectstack-ai#20706 edits carry no dead site, and I did not touch them. I did not touch `conversions/` or other lanes' sites. ## The anchors (one per number, reused from earlier stages where they anchored the same number) | number | sites | anchor | what the rewritten line says it decided | |---|---|---|---| | objectstack-ai#13135 | 26 (13 are `re-charter objectstack-ai#13135`) | commit 9e0ba21 | retires the paper metadata-customization protocol; re-charter of objectstack-ai#12057, which stays | | objectstack-ai#8495 | 23 | commit 4bfe1a5 (PR objectstack-ai#8666 kept) | the precedent: the first 17.x-line narrowing registered under protocol 18 (its own second commit says so) | | objectstack-ai#8715 | 16 | commit 2c86fe3 | the ApiKeySchema retirement; its message names the "route 3" kit (no carrier key, no tombstone, no D2) | | objectstack-ai#14691 | 11 | commit b3a63d3 | retires the ten inert `RestServerConfig` keys | | objectstack-ai#10724 | 11 | commit be21955 | retires the nine dead `contributes` members | | objectstack-ai#14369 | 10 | commit a3d5724 | the liveness census that recorded the 15 `dead` rows | | objectstack-ai#10485 | 6 | commit 35ad101 | retires the `themes` carrier and `ThemeSchema` | | objectstack-ai#11846 | 5 | commit 0c2334f | retires preview mode; its own text carries the ruling record (objectstack-ai#12428 kept) | | objectstack-ai#14676 | 5 | commit 13c48c2 | retires `connector.errorMapping` | | objectstack-ai#11332 | 3 | commit dce5cd4 | retires the manifest's three dead containers | | objectstack-ai#6361 | 2 | commit 90bbf25 | retires the notification-list `cursor` on both halves | | objectstack-ai#10627 | 2 | commit be21955 | that commit records the controlled monorepo census | | objectstack-ai#14365 | 2 | commit f60ab90 | the open `z.partialRecord` proposal that commit's changeset recorded; the retirement leaves no record to reshape | | objectstack-ai#14526 | 2 | commit db16b94 | the landing of the client envelope convergence (anchor block, and the README's measured case) | | objectstack-ai#6363 | 1 | commit 17d0954 | "ruled jointly with the `unreadCount` fix" | | objectstack-ai#6239 | 1 | commit f549a0d | the ViewProtocol retirement sweep | | objectstack-ai#10726 | 1 | commit bc56e18 | retires `contributes.routes` (Option B) | | objectstack-ai#10812 | 1 | commit be21955 | "the cloud census", whose 2026-08-24 reading @5b5925a that commit's message records | | objectstack-ai#9041 | 1 | commit d491625 | the url-branch refinement (objectstack-ai#9147, live, stays) | | objectstack-ai#14996 | 1 | commit db16b94 | the ADR-0087 registration, which landed in the same squash (its body: "Registration requested on objectstack-ai#14996") | | objectstack-ai#14312 | 1 | commit e944fdb | the `oauth.*` binding, which left `applications.delete` out as a behaviour change (PR objectstack-ai#15445 kept) | Two lines change without a number, so the sentence still reads: `patterns`' follow-on line and the `oauth` anchor block's continuation line. In total 133 lines are removed and 133 added in 86 files, and every file is balanced. ## Verification record (final head `1ee5841c09`; base `fbec216e2d`) **Census** (the gate's own `check-issue-citations.mjs --census --json`, board enumerated, 186 pages): | subtree | base (00:21Z, frontier objectstack-ai#20740) | head (01:18Z, frontier objectstack-ai#20743) | |---|---|---| | `entries/retired-keys` | 73 | 0 | | `entries/retired-defs` | 40 | 0 | | `entries/semantic` | 4 | 0 | | `registry.ts`, generated regions | 114 | 0 | | `registry.ts`, hand-written parts | 2 | 2 | | `chain.ts`, `types.ts`, `index.ts`, `spec-changes.ts`, tests | 0 | 0 | | **`migrations/`** | **233** | **2** | | `packages/spec/src` | 235 | 4 | - **Site-set difference:** 254 sites are only at base. 231 are this diff's (117 plus 114 copies). The other 23 are `plugin-audit`'s, from `main`'s objectstack-ai#20737. 0 sites are only at head. - **Kind** (every census site is a comment): a TypeScript 6.0.3 walker classes all 131 entry sites as comments. It also finds 13 dead string sites, all in `registry.ts`'s hand-written rationale (see Acceptance notes). - **Probe:** REST `issues/N` without following redirects, over all 302 distinct in-repo numbers of 100 or more in `migrations/`. 277 answer 200 and 25 answer 404. Controls were read at start, every 50 and end: lit objectstack-ai#20234 200 (8/8), dead objectstack-ai#8710 404 (8/8). - **Blind spots:** the census does not count 14 of the 131 entry sites. - 13 are `re-charter objectstack-ai#13135`, because `NON_CITATION_HEADS` reads `re-charter #N` as an ordinal. - 1 is in `entries/README.md`, which is outside the gate's `packages/**/src/**/*.ts` surface. - **Numbers:** no tracker number is added. The only numbers on added lines are the live ones kept from the same lines: objectstack-ai#8666, objectstack-ai#12057, objectstack-ai#8586, objectstack-ai#12428, objectstack-ai#9147 and objectstack-ai#15445. The diff-scoped gate judges them: "every citation this change adds resolves". **Residue** (scratch walker over the TypeScript parser, per file, base vs head, 86 `.ts` files): - The file with every comment range cut out, everything else byte for byte, is IDENTICAL. - The leaf-token stream is IDENTICAL: 38,417 tokens, aggregate `67c1f6db944e93ed`. - **Controls** mutate the head text in memory only, 259 of 259 as expected: - Expected identical: a comment insertion in every file. - Expected to differ: a string-literal edit, a template-literal edit, a regex-literal edit (where the file has one) and an appended declaration. **Regeneration** (`gen:migration-registry`): - `check:migration-registry` exits 1 before and 0 after. - The registry diff is 128 lines out and 128 in. As (old, new) pairs they equal the entry diff's pairs, re-indented by four spaces. - 0 changed lines fall outside the generated regions. - 5 entry pairs are deliberately not carried: the README line, and the semantic "Anchors" header lines, which sit above a blank line and so are not in the carried comment run. - `check:spec-changes` and `check:upgrade-guide` exit 0 with no regeneration. **Build and tests** (under `os-verify-lock`): - Build: `turbo run build` over `./packages/*` and `./packages/*/*`, 71 of 71, at `845e90fea4` and again at `1ee5841c09`. - `check:generated`: all 15 artifacts up to date, at both heads. - spec `local` project: 576 files, 16,991 passed and 1 todo, at both heads. - spec `repo` project: 41 of its 45 files, 666 passed, at both heads. - spec `typecheck`: exit 0; 53 files / 251 errors / 138 pinned signatures held. **Gates:** `dispatch-gates --commands` derives 82 gates, and the derivation is identical before and after the second merge. At `1ee5841c09` all 82 exit 0. `--ran` reconciles: 82 derived, 82 run, 0 NOT-MEASURED, a derived zero. **Lint** (a proven narrowing): - `eslint --no-inline-config --format json` over the 86 touched `.ts` files: 86 files, 0 errors, 0 warnings. - `isPathIgnored` is false for all 86. - `eslint.config.mjs:327-328` states that type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. **Changeset:** `patch`. - In the built `dist`, the new wording (for example "the precedent of commit 4bfe1a5, PR objectstack-ai#8666") appears in `dist/migrations/index.js` and `.mjs`, and the old wording appears in 0 files. - Control: an unchanged phrase appears in the same 2 files. - So the published `@objectstack/spec/migrations` entry carries these comments. **Merges:** `origin/main` was merged twice through `scripts/pm/os-regen-merge.sh`. Neither merge left a regeneration to commit. - Since then, `main` has advanced to `97005aed04`, and none of those commits touches `packages/spec`. - Driver-free `merge-tree` probes (from a bare `--shared` scratch clone with no `merge.*` config) exit 0 against that `main` and against PR objectstack-ai#20706's head `d8bac3877d`. ## Acceptance notes - **The next stage for this card: 13 dead sites left in `registry.ts`'s hand-written parts.** They are outside this claim, which says `registry.ts` is regenerated only. - **Comments (form C), 2:** - `:5940` objectstack-ai#8495, the step-18 doc comment on the persistence placeholder refusal. - `:22819` objectstack-ai#6239, the `RETIRED_DEFS_BY_MAJOR[17]` doc comment. - **Author-shown rationale strings (form D), 11:** - Step 17's `:344` objectstack-ai#6345. It projects into `docs/protocol-upgrade-guide.md:68`, so that stage regenerates the guide. - Step 18's fragments: `:5121` objectstack-ai#14676, `:5455` objectstack-ai#12868, `:5526` objectstack-ai#10329, `:5544` objectstack-ai#8495, `:5555` objectstack-ai#13135, `:5742` objectstack-ai#10724, `:5745` objectstack-ai#10627, `:5752` objectstack-ai#10726, `:5799` objectstack-ai#10485, `:5816` objectstack-ai#10926. These are not projected into either artifact yet. - **Census blind spots, measured here and not filed** (a coverage boundary, not one of the three filing classes; new gates default to no): - `NON_CITATION_HEADS` skips `re-charter #N` even where N is a tracker card. - `.md` files under `packages/**/src` are outside the surface. - **NOT MEASURED locally, left to CI:** - `scripts/build-schemas-check-mode.test.ts` hit the foreground cap alone (exit 124 after 560 s, no output). It parses `registry.ts`'s source; the residue check above shows that source's code and string tokens are unchanged. - The other three heavy `repo`-project files (`def-key-collisions`, `publish-smoke-boot-failure`, `publish-smoke-port-collision`) were not run, as in stages 8 and 9. - **Hypothesis 6's baseline half is falsified by construction.** `scripts/doc-authoring-prose-id.baseline.json` has no `packages/spec` row, because its leg excludes `packages/spec`. `check:doc-authoring` reads strings only, so a comment-only diff cannot move it. It read 808 sites across 229 files at `845e90fea4` and 794 across 227 at `1ee5841c09`. The difference is `main`'s own re-anchor commits; this diff touches no row. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #20638
Clause-②: no
Bumps the console pin:
.objectui-shadd3f7e1be356->db11afd4967c(objectuiorigin/mainHEAD at bump time, on main). The pin ships@object-ui/console17.6.0 (apps/console/package.jsonat the pin and@object-ui/coreworkspace version both 17.6.0;npm view @object-ui/console@17.6.0 versionanswers 17.6.0).Containment (objectui
merge-base --is-ancestor C db11afd4967c, full clone, not shallow)a5841be35199878d8e3a49f76725aaae582b70a5b2ea175703100bef65cdd3f7e1be3(old pin)The target is objectui
origin/mainHEAD (git branch -r --containslistsorigin/main).What changed
.objectui-sha,sdui.manifest.json,scripts/sdui-manifest.record.json(viascripts/gen-sdui-manifest-node.mjs, no arguments: the--objectui-versionroute is retired per that script), and.changeset/console-db11afd4967c.md(written byscripts/bump-objectui.sh, minor).pnpm objectui:buildat the new pin built the console dist (64404 KB) with no API break against framework code.Follow-up (pushed on this branch, two commits, written 2026-09-29T20:01Z)
Requested by the PM seat after the first report: the two gates that sat outside the first file surface.
packages/sdui-parser/objectui-lockstep.jsonre-recorded withpnpm gen:sdui-lockstepagainstdb11afd4967c;pnpm check:sdui-lockstepnow exits 0.dd3f7e1be356..db11afd4967cdiff and re-read, hashes and counts re-taken with the method each record names, and only then the pin and the moved numbers updated. Files:component.zod.ts,component.test.ts,view.zod.ts,dataset.zod.ts,functional-completeness.ts,api-methods-batch-conformance.test.ts, sixmigrations/entries/semantic/18.*files and the regeneratedmigrations/registry.ts.pnpm --filter @objectstack/spec run check:objectui-pin-citationsexits 0 (49 asserting citations match, 7 anchor content assertions verified).content/docs/references/ui/view.mdxregenerated withgen:docs: theFormField.spandescribe names the pin, so its sha moved. This is the only non-comment text change; no schema or behaviour moved.ObjectMapConfigSchemawith.strict(), so theListMapConfigSchemarecord inview.zod.tsno longer holds when it says the renderer's schema is a plainz.objectthat parses an undeclared key clean. The three anchors were re-pointed and the two false sentences were qualified in the comment. That prose wants a human read..changeset/objectui-pin-citations-db11afd4967c.mdcarries an@objectstack/specpatch (commitd8bac3877d).Resolved since the first report
5903537467on Bump .objectui-sha past objectui a5841be351 (objectui#11028), which also carries objectui#11008 and objectui#10964 — the one pin move #20287, #20578 and #20293 wait on #20638):.changeset/console-db11afd4967c.mdnow carriesadr-0087: not-required (no-migration-prescription), worded as in chore(objectui): bump the console pin to dd3f7e1be356 (carries the injected-client boot fix) with the showcase div→box and trash-icon follow-through #20436.@object-ui/types,@object-ui/core,@object-ui/plugin-dashboard).check-adr-0087-registration --base origin/mainexits 0.origin/main7053333e1emerged atef6276c0e9throughscripts/pm/os-regen-merge.sh. Onlypackages/spec/src/migrations/registry.tsoverlapped; it text-merged, andcheck:migration-registryreads it current.check:dual-build-cjs-loadsandcheck:type-check-debtboth exit 0 after a whole-workspace build.Boot at the new pin
scripts/build-console.shatdb11afd4967c, with this tree's client and spec injected and the canary present.assert-console-spec-injectionpasses: 105 of 145 injected-only descriptions present, all 160 published-only absent.chunk-membershipreadsclientinvendor-objectstack.examples/app-showcasebooted withobjectstack dev --ui --fresh --no-seed-admin;bootstrap-statusreadhasOwner: false./_console/setupand submitted the owner form.bootstrap-statusflipped tohasOwner: true, and the session is the new owner (platform_admin,org_owner). The wizard landed on/_console/home, with Showcase and Setup listed./_console/login: Showcase opens its Capability Map html page, and Setup opens System Overview. 0 page errors, 0TypeError, 0 5xx. The only 404 isGET /api/v1/usage/storage, as at the old pin.Governed surface
None of the 20 changed paths is a row of
GOVERNED_SURFACESinscripts/pm/check-governed-merges.mjs(rows:docs/adr/,.claude/,skills/,AGENTS.md,CLAUDE.md,docs/NORTH-STAR.md). No release act performed.Gates at head
ef6276c0e9: 125 derived, 125 run, 0 NOT-MEASURED, 0 unrun; all 125 exit 0.packages/spectests: 16991 passed (576 files).sdui-parser: 199 passed. Spec and sdui-parser typecheck clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv