Repository navigation
chore(objectui): bump the console pin to 87af769e9a3e with 27 re-measured citations, and fold in the two-code sdui-parser port (#17645) - #19398
Conversation
Pin bump produced by scripts/bump-objectui.sh --no-commit; the ADR-0087 disposition placeholder is still unanswered in this commit. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
…ockstep The pin bump's changeset now carries a real ADR-0087 disposition, and the sdui-parser lockstep record is re-taken from objectui at the new pin. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
The pinned commit declares the same published @object-ui 17.6.0 the old pin did, so sdui.manifest.json is byte-identical (sha256 49211fee7792) and only the provenance record moves. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
The six corpus-absence records are re-counted against objectui 87af769e9a3e (8228 tracked files): every subject still reads 0, and each lit control is re-measured. The logging record's `timeout` control was wrong at the pin it named (702 recorded, 832 measured there) and is corrected to the new pin's 1075. The api-methods batch record's two anchors were re-READ, not carried: both spans are byte-identical and both moved. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
… new pin Every anchor in the tab-item, accordion-item, button, object-metric, object-kanban limit and quickAdd records was re-READ against objectui 87af769e9a3e rather than re-pointed by arithmetic. Four read points changed substance and say so: the two container registrations lost their per-input `label`, the button registration's ranges shrank, `resolveIcon` no longer indexes lucide-react directly, the kanban `$top` now runs through `resolveRowLimit`, and `KanbanSchema` was retired in favour of `ObjectKanbanSchema` in `@object-ui/types`. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
…e new pin Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
migrations/registry.ts is regenerated from the six entry files it concatenates (gen:migration-registry), so its six citations follow theirs. The dataset `format` and view `span` records both recorded behaviour the range reverses, and each is re-stated from the new tree rather than re-pointed: objectui#8352 makes the datetime measure arm honour `short` / `relative`, which the old docblock and its describe both denied; objectui#9244 makes the form renderer emit one clamped col-span class per multi-column tier, which the span record itself asked to be re-read at the bump that absorbed it. All seven ListMapConfig anchor quotes moved; one lost the symbol it quoted -- `FLAT_MAP_CONFIG_KEYS` became the total `FLAT_MAP_CONFIG_SPELLING` map under objectui#9950, so `style` now reaches the flat product as `mapStyle`. check:objectui-pin-citations and --verify-anchors both exit 0. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
…ibes gen:docs output only; check:generated reports all 15 artifacts current. The spec changeset declares the two sentences the pin bump corrected. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 137 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 ff53bb6f7f47a676c6b47bd379df9e37c706ece0 && git checkout ff53bb6f7f47a676c6b47bd379df9e37c706ece0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 50bae37f3df71e759f1730b906d5af35baa7b0f7 fc25a5925d7d6b55ed25b81d5f1fcef4be836109 && git checkout -B drift-repro 50bae37f3df71e759f1730b906d5af35baa7b0f7 && git merge --no-ff fc25a5925d7d6b55ed25b81d5f1fcef4be836109
node scripts/docs-audit/affected-docs.mjs --json 50bae37f3df71e759f1730b906d5af35baa7b0f7
|
|
Director seat ( What is failing: Generated by Claude Code |
…lands via #19398 first The Single-Claim Path Guard on the pin-bump PR fails with "At most one open PR may modify a declared single-writer path": .objectui-sha is a declared single-writer path and this draft still rewrites it (53ded82bf7a4 to cfcc17d9dd04), so it counts as an open claimant. Per ruling batch #200 item 4 the pin bump lands on its own PR first. So this branch releases the claim: both pin-carried files are restored to main's blobs byte for byte (.objectui-sha and scripts/sdui-manifest.record.json, the latter because check-sdui-manifest requires record pin == .objectui-sha). Nothing else is touched — the parser port, its tests and the changeset are unchanged. check:sdui-lockstep is RED in the interim, by design: [pin-moved], the lockstep record was taken at cfcc17d9dd04 while .objectui-sha now reads 53ded82bf7a4. This PR stays draft and re-records the lockstep at the new pin in a merge lap after the pin bump lands. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
|
Director seat ( What is failing: Generated by Claude Code |
|
Director seat ( Generated by Claude Code |
|
Director seat ( What is failing: the guard read the carrier ( Generated by Claude Code |
… objectui Folds the two-code sdui-parser port onto the pin-bump branch, verbatim: the `kanban-quick-add.ts` source, the `ManifestInput.of` member-kind check across validator, serializer and codegen, both test files with their ablation legs, and the port's minor changeset. Every file is byte-identical to the port branch's blob. The lockstep record is NOT text-merged from that branch: this branch already carries the record re-taken at the live pin, and `--update` re-reads objectui's side only, so nothing here can launder a divergence. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
`check-issue-citations` judges only what a change ADDS, and this branch rewrote both lines, so both are ours to answer. view.zod.ts: spell the citation bare. The qualified form named this repository, which the classifier recognises — and then looks the number up on a board the probe never asked for it, because the probe only fetches unqualified citations. The target exists; the bare spelling is the grammar's form for this repository and it now resolves. component.zod.ts: the number this sentence carried is DELETED, probed with `--probe-cause` (minted, gone from the board, 404 on the web endpoint too), so no target resolves and none is guessed. The sentence now states that in prose and names the live record — this block, and the pin-citation gate whose header carries the same rule. The later paragraph's unchanged mention is left as it stands on main. Claude-Session: https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Isolated at-tier reviewer; reviewed 2026-09-21T00:13Z. Merge-base ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Provenance — director seat, summon #25 (
Generated by Claude Code |
…s from its displayed scale (objectstack-ai#19442) Fixes objectstack-ai#19320 Clause-②: yes (widening) ⭐ Declared from the **measurement**, ⛔ not from the shape of the change: the accept-set delta over 1950 cells is **36 ADDED / 0 REMOVED**, so the narrowing arm is empty.⚠️ The seat rewrote this line from a backticked, prose-trailing spelling that the repo’s own `readClause2Line` reads as `{kind: near-miss, reason: describing}` — a near-miss is ⛔ not a declaration, and `Check Changeset`’s level axis would have had no input from this body. ✅ **Both halves of the ruling are now here.** The behaviour half (the validator's percent arm) and the `packages/spec` docblock half landed in the same branch; the docblock half needed a file outside the boundary this card was dispatched with, was reported rather than taken, and was then authorized by the dispatching seat. The closing keyword is therefore a closing keyword. ## The ruling this makes live Maintainer ruling batch objectstack-ai#161 item 3 letter B (objectui#9810, comment `5729749935`, 「其他同意」 2026-09-18T12:07Z). Quoted, not translated: > - `packages/spec` `FieldSchema.scale` docblock (and the field reference page): for `percent`, `scale` is the number of decimal places of the percentage-point value as displayed and entered; stored precision follows the storage scale (`fraction` ⇒ `scale + 2` places; `whole` ⇒ `scale`). > - `record-validator.ts` `max_scale` branch: when `def.type === 'percent'` and `percentScaleOf(def) === 'fraction'`, compare against `def.scale + 2`; a pin per storage scale (fraction `scale: 2` accepts `0.1234`, refuses `0.12345`; whole `scale: 2` unchanged). ## Premise re-verification — first-hand, on today's tip, with a lit control The card's premise was second-hand. Both halves were re-measured against `origin/main` at base `24162f95`, through the **real** record validator imported from the built `dist` of `@objectstack/objectql` — no harness, no source shortcut. | case | ruling B requires | measured BEFORE this PR | | --- | --- | --- | | fraction `scale: 2`, write `0.1234` (scale+2 places) | ACCEPT | **REFUSE** `max_scale` `{scale:2, actual:4}` | | fraction `scale: 2`, write `0.123` (scale+1) | ACCEPT | **REFUSE** `max_scale` `{scale:2, actual:3}` | | fraction `scale: 3`, write `0.33333` (the ruling's 33.333%) | ACCEPT | **REFUSE** `max_scale` `{scale:3, actual:5}` | | fraction `scale: 0`, write `0.33` | ACCEPT | **REFUSE** `max_scale` `{scale:0, actual:2}` | | fraction `scale: 2`, write `0.12345` (scale+3) | REFUSE | REFUSE (agrees) | | whole `max: 100, scale: 2`, write `12.34` / `12.345` | ACCEPT / REFUSE | ACCEPT / REFUSE (agrees) | **Lit control, same instrument, same run** — so the refusals above are a reading and not a dead instrument: the validator ACCEPTED `0.12` and `0.5` on the same field, and REFUSED for three *different* reasons — `max_value` on `max: 1` with `5`, `min_value` on `min: 0` with `-0.5`, and `invalid_number` on `'abc'`. `number` / `currency` / `slider` all refused at `scale + 1` in the same run. Docblock half, read at source: `FieldSchema.scale`'s `.describe()` (`packages/spec/src/data/field.zod.ts`) states the 0-100 platform ceiling and nothing about `percent`; `percentScaleOf`'s docblock (`packages/spec/src/data/percent-scale.ts`) states the fraction/whole rule and says nothing about `scale`. **Both halves of the card hold. The premise is TRUE.** ## Accept-set delta — measured in BOTH directions Both predicates (current and ruled) were run over an exhaustive corpus of 1,950 cells: 5 numeric field types x 5 `max` declarations x 6 `scale` values x 13 decimal-place counts. ``` corpus cells: 1950 unchanged: 1914 ADDED (accept set grows): 36 REMOVED (accept set shrinks): 0 declaration classes whose allowance moves: percent max=undefined, percent max=0.5, percent max=1 ``` - The narrowing arm is **empty** — 0 of 1,950 cells. Nothing that writes today stops writing; no stored value is re-read; no migration is implied. - Only fraction-stored `percent` moves. `percent` with `max` above 1, and `number` / `currency` / `slider` / `rating` at every `max`, are byte-identical in verdict. - ⇒ `Clause-②: yes (widening)` is what the measurement supports. It was dispatched as a claim to check; the claim survives the check.⚠️ **One flag for the contract review, not a re-adjudication.** The ruling's own Execution section declares `Clause-②: no`. The mechanical criterion in `pm-dispatch` is 「本卡放宽接受集或扩大公开面吗」, and the accept set is measurably relaxed, so the conservative routing arm is `yes`. The declaration is by design provisional (「按设计临时…⛔ 非终审」), so this is a routing difference to be recorded at review, not a change to the ruling. ## What this PR implements `packages/objectql/src/validation/record-validator.ts` — the `max_scale` branch gains its percent arm. The fraction/whole split is **read from the spec's `percentScaleOf`**, not re-derived from `max` at this seam, so the edit widget, the analytics wire and the validator keep answering from one source. The refusal envelope now reports the allowance that was **applied**: on a fraction-stored `scale: 2` field, `0.12345` is still refused and reports `constraint: { scale: 4, actual: 5 }`. Reporting the raw declaration beside a stored fraction's place count would render "must have at most 2 decimal places (got 5)" on a field that accepts four — a true refusal described by a false constraint. This is the one detail the ruling's letter leaves open; it is decided in the direction that keeps the machine-readable surface honest, and it is pinned. ## The spec half — and the boundary that gated it The ruling's first bullet is the `packages/spec` `FieldSchema.scale` docblock **and the field reference page it generates**. That is `packages/spec/src/data/field.zod.ts`, which was **outside** the four-file boundary this card was dispatched with, so it was reported before being touched rather than taken quietly. The two `packages/spec` paths the dispatch originally named (`numeric-column-representation.ts` and its test) are about the **DDL column** (`numeric_precision` / `numeric_scale`) and mention `percentScaleOf` only inside a prose comment — they are not part of this repair and are untouched. What landed, after the seat authorized the corrected surface: - `packages/spec/src/data/field.zod.ts` — `FieldSchema.scale`'s `.describe()` now states **both** meanings: what the number counts on a `percent` field (decimal places of the displayed percentage-point value) and what it permits in storage (`fraction` ⇒ `scale + 2`, `whole` ⇒ `scale`, every other numeric type ⇒ `scale`). Both halves go in the **describe**, not only in a source comment, because the reference page is generated from the describe and an author who reads only that page is the author the ruling is about. - `content/docs/references/data/field.mdx`, `data/object.mdx`, `system/migration.mdx` — regenerated by `pnpm --filter @objectstack/spec gen:schema && gen:docs`, ⛔ never hand-edited. **Exactly those three tracked files moved and nothing else**, which is what the pre-commit source/regeneration split was arranged to make legible: the source edits were committed first, so the regeneration commit's file list is the regeneration's own output. - `packages/spec/src/data/percent-scale.ts` — the optional cross-reference, **taken**. The card's own measurement table named *this* docblock as the one silent about `scale`, and `percentScaleOf` is the function the validator calls, so a reader who lands here should find the consequence rather than re-derive it. Written as a pointer, ⛔ not a second copy: the rule is stated once on `FieldSchema.scale` and enforced once in the validator. **Serial constraint re-measured for those paths** before any of it was written, same instrument as the dispatch used: 20 open PRs, `GET /pulls/N/files` each, **0 unreadable file lists**, and no open PR holding any of the eight paths. Lit control on the same run: `packages/spec/src/**` matches 7 open PRs (objectstack-ai#19398, objectstack-ai#19374, objectstack-ai#19373, objectstack-ai#19335, objectstack-ai#19314, objectstack-ai#19090, objectstack-ai#18319), so the zeros are absences the instrument could see. ## Verification **Ablation** — `scripts/ablation-replace.mjs`, anchor `? def.scale + 2` in the production file. -⚠️ The **first attempt was a no-op and its reading is void**: the replacement string was a prefix of the anchor, so its occurrence count could not rise, and the tool refused before running anything. Recorded rather than quietly retried. - Second attempt landed: anchor `x1 -> x0`, blob `2e2d3981d29a -> 7b082941b44f`, command executed, restore proved `blob == HEAD (2e2d398)` with `git diff HEAD` empty. - Under ablation: **8 failed / 100 passed (108)**. All 8 are in the new block and fail for the right reason — the fraction-stored writes are refused with `max_scale`, and the envelope/message read `2` where the ruling requires the applied `4`. - ⭐ **5 of the 13 new cases cannot discriminate, and are not counted as evidence**: the scale+3 refusal, the whole-percent pin, the other-numeric-types controls, the other-refusal-reasons control and the no-declared-scale control pass on both trees **by design** — they are anti-vacuity and lit-control pins, there so that a branch which simply stopped enforcing `scale` on percent fails this block too. **Gate family** — derived from the real changed paths with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, every command run with its exit code captured before any pipe, reconciled with `--ran` carrying the recorded codes: ``` 111 derived · 111 run · 107 green · 0 red · 4 NOT MEASURED · 0 unrun ``` - One real red was found and repaired in this PR: `check:error-code-casing` read the envelope pin's bare `code: 'max_scale'` as an ADR-0112 D1 emission because its recognizer window held no field-addressed neighbour. Naming the field is the repair and a stronger assertion. Re-run exit 0: "no unlisted lowercase error codes in 6371 scanned file(s)". - NOT MEASURED, each with its reason, none of them a red: - `check:dual-build-cjs-loads` — exit 3, PREREQUISITE NOT MET: reads built output, 66 packages have no `dist/` in this worktree. - `check:type-check-debt` — exit 3, PREREQUISITE NOT MET: needs the whole workspace closure built. - `check-plugin-teardown-shape.mjs --self-test` — exit 3: its positive-control fixture is pinned to a commit this shallow clone cannot reach. - `check-engine-split-ratio.mjs --days 90` — exit 2: refuses to compute an ADR-0076 D7 ratio on a shallow clone whose oldest visible commit sits inside the window. Counted as "run" by the reconciler (exit 2 is not the prerequisite code) but it measured nothing, so it is reported here as NOT MEASURED. - One gate **refused its prerequisite while spelling it `exit 1`**: `check:skill-examples` reported that `packages/client-react/dist` held no declarations, which is a refusal and ⛔ not a finding. Rather than report it as NOT MEASURED, the package closure was built and the gate re-run to a real verdict: exit 0, **258 prose examples type-check across 3 surfaces**, including the 10 spec-source TSDoc blocks — the surface this PR edits. - The two remaining `exit 3` families were **deliberately left unmeasured**: both need a whole-workspace build, and both are whole-tree families unrelated to a describe string and a validator arm. CI builds everything and measures them there. The `check:skill-examples` closure was built because that gate reads the spec source surface this diff touches — the choice is principled, ⛔ not a budget. - The reconciler's own verdict, DERIVED from the recorded codes rather than claimed: `111 derived famil(ies) accounted for — 108 run, 3 NOT-MEASURED (3 DERIVED from a recorded exit 3)`. **Suites and lint**, at the final commit: - `pnpm --filter @objectstack/objectql test` — **303 files / 5050 tests passed**, exit 0. - `pnpm --filter @objectstack/spec test` — **505 files / 14,752 tests passed**, exit 0; `pnpm --filter @objectstack/spec typecheck` exit 0. - `pnpm --filter @objectstack/objectql typecheck` — exit 0; `check:test-typecheck` OK, ledger unchanged at 40 files / 234 errors / 65 pinned signatures. - `pnpm --filter @objectstack/spec check:generated` — exit 0, **all 15 generated artifacts up to date** against the edited `FieldSchema.scale`. Both gates the ruling's docblock half puts at risk are green by name: **`check:docs`** (the three regenerated reference pages) and **`check:authorable-surface`** (authorable surface + JSON schemas). `authorable-surface.base.json` did not move — a regular build never writes it. - `pnpm lint` — repo-wide `eslint . --no-inline-config`, exit 0 at `bb9f9274`, the final commit. The full union ran; no narrowing was needed, so no narrowing is claimed. - **The ablation reading still describes the shipped file**: `git hash-object packages/objectql/src/validation/record-validator.ts` is `2e2d3981d29a…`, byte-identical to the blob the ablation restored to, so nothing landed on the production file after it was proved able to fail. **Import-side pins**: the public surface of `@objectstack/objectql` is byte-unchanged (no export added, removed or retyped), so only behaviour could move a consumer pin. Every non-`objectql` test file mentioning `'percent'` was checked for a co-occurring `scale`; the six hits are `packages/spec` schema tests and two `service-analytics` wire tests, none of which exercises the record validator. The grep returning six files is its own lit control. ## Acceptance notes Noted, not filed — neither meets the three filing classes, and the carrier for each is named: - The `max_scale` message template (`packages/spec/src/system/validation-message.ts`) reads "must have at most N decimal places", which on a fraction-stored percent now describes the STORED fraction rather than the number the author typed. It is accurate and it is not what the author sees in the widget. Whether a percent-specific sentence is wanted is a display decision that belongs with the ruling's author, not a defect. Carrier: the contract review on this PR. - `packages/spec/src/data/numeric-column-representation.ts` already carries an accurate prose account of the fraction storage rule in its `percent` entry. It is documentation of the column, not of `scale`, and needs no change under this ruling. Carrier: none needed — recorded so the next reader does not re-derive the same dead end. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01HnRAeVTLJevtQ5iCPX6JSm --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
…il-closed quick_actions requiredPermissions fix) with 46 re-measured citations, the FormField.span describe re-point and the lockstep re-record (objectstack-ai#19832) Fixes objectstack-ai#19503 Clause-②: no Moves the console pin to objectui `main` `62597c588072`. That tip carries objectui#10153, the fail-closed `record:quick_actions.requiredPermissions` fix. The PR also carries the bump's two required follow-ups, folded in under the PM ruling on objectstack-ai#19503 (comment 5791879469, option A): the sdui-parser lockstep re-record, and a re-MEASURE of the `packages/spec/src/**` citations that `check:objectui-pin-citations` names. The citation edits touch comments and migration-rationale prose only; there are no schema or export changes. The one published sentence that moves is the `FormField.span` `.describe()` pin citation, authorised by the second ruling (comment 5793509969) and carried with `.changeset/19503-pin-bump-describe-corrections.md` (`@objectstack/spec: patch`) plus a regenerated `content/docs/references/ui/view.mdx`. Clause-②: no, because every authored document is accepted and refused exactly as before. `check:objectui-pin-citations` exits 0 on the head: "47 asserting objectui pin citation(s) match .objectui-sha (62597c588) … 7 anchor content assertion(s) verified". ## Ancestry reading (fix → new pin: AHEAD) Measured in a FULL (non-shallow) clone of `objectstack-ai/objectui`. `git rev-parse --is-shallow-repository` returns `false`, so an exit 0 is self-proving: | command | exit | reading | |---|---|---| | `git merge-base --is-ancestor 28b065800c7d1b6069c06aa906d0716377b5e281 62597c588072636e9c30ea35b3d89b1e46fd765d` | 0 | the fix is IN the new pin | | `git rev-list --count 28b065800c7d..62597c588` | 33 | the new pin is 33 commits ahead of the fix | | `git merge-base --is-ancestor 87af769e9a3e 28b065800c7d` | 0 | the old pin was 4 behind the fix (the card's REST `compare`: behind_by 4) | The bump's own changeset lists the fix: `fix(plugin-detail): record:quick_actions.requiredPermissions is an ADR-0066 capability set, read fail-closed ... (objectui 28b065800)`. ## What moved, and how each artefact was produced | artefact | producer | |---|---| | `.objectui-sha` + `.changeset/console-62597c588072.md` | `bash scripts/bump-objectui.sh --no-commit 62597c588072…` exit 0 (27 releasing changesets, 0 breaking, `@objectstack/console: minor`) | | `scripts/sdui-manifest.record.json` | `node scripts/gen-sdui-manifest-node.mjs --objectui-version 17.6.0` exit 0. `sdui.manifest.json` came out BYTE-IDENTICAL (sha256 `49211fee7792`, 57 components), because `packages/core` ships `17.6.0` at both pins; only the record moves | | `packages/sdui-parser/objectui-lockstep.json` | `pnpm gen:sdui-lockstep` with `OBJECTUI_ROOT` at a checkout of `62597c588`, exit 0. Diff: 3+/3-, only `rev`, `revDate` and `recordedAgainstPin`. Grammar blob `0131f27cf86d` unchanged, 214 lines, all 26 diagnostic codes agree. `check:sdui-lockstep` exit 0: "byte-identical to objectui@62597c588072". No parser port is owed | | `packages/spec/src/migrations/registry.ts` | `pnpm --filter @objectstack/spec gen:migration-registry`, from the six edited entries | | the console | `bash scripts/build-console.sh` exit 0: "Console dist ready (60524 KB) from objectui@62597c588072", bundle canary present. `check:console-sha` exit 0 | ## Citation re-measure: method - **Read-point records.** For every cited objectui file I ran `git diff --quiet 87af769e9a3e 62597c588 -- FILE`. - Unchanged file: the anchor holds by identity. The prose names that, keeps the earlier hop's history, and says where the last content re-read happened. - Changed file: each anchor was mapped through the `git diff -U0` hunks and its text compared at both pins. It was then re-read in the new tree. - **Corpus counts.** These are the six migration entries plus `registry.ts`. I re-counted with `git grep -o -F TOKEN SHA | wc -l`. The method was calibrated first: at `87af769e9a3e` it reproduces every recorded count exactly (8228 files, `useState` 2383, `timeout` 1075, `objectstack` 12966, `@objectstack/spec` 4997, `window` 3464, `interval` 170, `tenant` 976, …). - Every dark token still counts 0 at `62597c588`: the def exports and key names each entry names, 95 tokens in all. - The `Span` / `SpanSchema` collisions are unchanged at 486 / 53. - Only the corpus size (8303) and the lit controls move. ## Re-read sites (all 46 the gate named) | site(s) | objectui file(s) | reading at `62597c588` | |---|---|---| | 6 migration entries + their 6 `registry.ts` copies | whole corpus | re-counted: dark tokens still 0. The `kernel-health-check…` entry keeps its `87af769e9` lit-control clause verbatim (now dated) and adds the new counts after it. `check:future-spec-major` holds a measurement exemption to that exact sentence, and the new figure "`@objectstack/spec` 5043" would read as a version | | `component.test.ts` accordion/tabs icon; `component.zod.ts` `PageTabs` / `PageAccordion` `icon` | `containers.tsx` (changed, `4c6f549ef`) | re-read: `853-859`, `912`, `1069-1075` and `1116` did NOT move, text byte-identical | | `component.test.ts` button icon; `component.zod.ts` `element:button` `icon` | `resolve-icon.ts`, `button.tsx`, `lazy-icon.tsx` | byte-identical, so no anchor moved | | `component.zod.ts` metric `icon` | plugin-dashboard `index.tsx`, `ObjectMetricWidget.tsx`, `MetricWidget.tsx` | byte-identical | | `component.test.ts` / `component.zod.ts` kanban `limit` ×3, `quickAdd` ×2, `navigation` | plugin-kanban (all), `objectql.ts`, `ElementDataSourceGate`, `ListView.tsx`, `ObjectView.tsx`, `plugin-kanban.mdx` | byte-identical.⚠️ The `quickAdd` block's lit control said 6 `onCardClick`; the identical file counts 11 at both pins (the test-side twin already says 11). It was miscounted, not moved, and is corrected; the verdict rests on the 0 | | `component.zod.ts` calendar `navigation` | `ObjectCalendar.tsx` (changed, `afb228418`) | re-read: `921-935`, `936` and `938-939` unmoved; `1293→1317`, `1218→1237` and `1228-1241→1250-1263`, text byte-identical | | `component.zod.ts` `object-map` header, `filter`, `sort`; `component.test.ts` / `component.zod.ts` map/gantt/tree rows; `functional-completeness.ts` map row | `ObjectMap.tsx` (changed, `2252653d0`: only `warnOnTopLevelStyleUrl`'s docblock and dev-warning text) | every anchor from `:289` on MOVED 16-19 lines with its text byte-identical (e.g. `814→833`, `895→914`, `377→396`, `382→401`, `1058→1077`); no read point or key set changed | | `component.zod.ts` gantt / tree ×3 / timeline ×2 headers; `functional-completeness.ts` gantt, timeline rows | plugin-gantt, plugin-tree, plugin-timeline, `record-source.ts`, `ListView.tsx` | byte-identical. The tree's `ElementDataSourceGate` count was re-taken: tree 0, map/gantt/grid/calendar 3 each (`plugin-grid/src/index.tsx` changed and still reads 3) | | `api-methods-batch-conformance.test.ts` `sys_api_key` | `ObjectGrid.tsx` (changed, `804831c2a` + `62597c588`) |⚠️ the selection block's CONTENT moved: objectui#10218 rewrote the `selection` arm to "presence enables; an explicit off wins". The span is now `4024-4051` (hash `c88443302d40`, was `6133933199…` over the last three hops). Re-read, the record's claim holds: with neither `selection` nor `selectable` authored, multi-select is enabled only by `hasBulkActions`, and none of the four views declares `selection` | | `dataset.zod.ts` measure `format` | `date-display.ts` (changed, `516583b54`), `dataset-format.ts` | `formatDate` now parses via `toDisplayDate` (date-only values keep their calendar day); the cited style handling is unchanged; `198-233→271-306`, `152→225` | | `view.zod.ts` view-face `limit` reach (objectstack-ai#19228) | 9 files | both instruments re-run: the probe returns 0, and the control returns the same 13 lines across 6 files, line for line. All 9 cited files are byte-identical | | `view.zod.ts:1975/:1978` ObjectMap anchors (content-asserted) | `ObjectMap.tsx` | re-READ against `2252653d0`: it rewrote the docblock and dev warning ABOVE `getMapConfig` (the remedy now names the declared `map: { style }`, carried out as `mapStyle`). `getMapConfig` is byte-identical, so `385→404` and `390→409`; the quoted `getMapConfig` comment is intact (`:361→:380`) | | `view.zod.ts` `FormField.span` docblock | `autoLayout.ts` (changed, `e026e15f9`), `form.tsx` | `resolveColSpan` `153→154` (an import added above it). `form.tsx` is byte-identical, so `spanLadderFor` still emits the ladder | `pnpm exec tsx scripts/check-objectui-pin-citations.ts --verify-anchors` reads: all 7 content-asserted anchors pass at `62597c588`. ## The two sites first reported, then resolved under the second ruling (comment 5793509969, option A) 1. **`packages/spec/src/kernel/functional-completeness.ts`, the calendar row: its claim had become FALSE, so it is RESTATED.** The row said the "Calendar configuration required" literal "is the text RENDERED at the pin: objectui#10101 landed AFTER the pin". `git merge-base --is-ancestor afb228418 62597c588` → 0 (and → 1 against `87af769e9a3e`). - At the new pin the screen's copy goes through `tt('calendar.configRequired', …)` (`ObjectCalendar.tsx:1207-1208`). Its default and the `en` pack read the same English sentence; `zh`, `de` and the other packs render their own words. - The `if (!calendarConfig)` refusal arm is unchanged (the `ObjectCalendar.tsx` diff on this hop is copy-only). - The row now says what renders, names the arm rather than the English sentence as the thing to match, and cites `.objectui-sha` = `62597c588`. Comment only. 2. **`packages/spec/src/ui/view.zod.ts` `FormField.span` `.describe()`: its claim still holds, so the citation is RE-POINTED.** The describe now names `.objectui-sha` = `62597c588`. - Re-read there: `plugin-form`'s `WIDE_FIELD_TYPES` is unchanged (it shifted one line; repeater still reaches it through `field:grid`), and `form.tsx` is byte-identical. - Regenerated with `pnpm --filter @objectstack/spec gen:schema && gen:docs`, which touches only the two `span` rows in `view.mdx`. - `.changeset/19503-pin-bump-describe-corrections.md` (`@objectstack/spec: patch`) states the correction, after PR objectstack-ai#19398's `17429-pin-bump-describe-corrections.md`. ## Gates `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` on head `3fd40bedd` derives 124 families. The docs families are added by `view.mdx`. Per-family exit codes are in the os-dev-report comment on objectstack-ai#19503. Summary: - `check:objectui-pin-citations` exit 0; `pnpm --filter @objectstack/spec check:generated` exit 0 (all 15 artefacts) - run as CI runs it and green: `check:react-declaration-parity` (`MANIFEST=$PWD/sdui.manifest.json … --baseline react-declaration-parity.baseline.json --strict`, "no new DECLARATION divergence"); the derived spelling omits the manifest input - `check:skill-examples` read exit 3 (PREREQUISITE NOT MET: `@objectstack/client-react` unbuilt) in the sweep; after `pnpm --filter '@objectstack/client-react...' build` it reads exit 0 (259 prose examples type-check). `dispatch-gates --ran`: 124 derived, 122 run, 2 NOT-MEASURED, 0 UNRUN - NOT MEASURED, left to CI: `check:dual-build-cjs-loads` and `check:type-check-debt` (both exit 3, PREREQUISITE NOT MET: they need a full workspace `pnpm build`; this diff changes only comments and migration-rationale strings in spec) - tests: `@objectstack/spec` vitest on `component.test.ts`, `api-methods-batch-conformance.test.ts`, `src/migrations`, `functional-completeness` and `src/ui/view*`: 22 files, 1562/1562 pass Changed lines: 560 (+364 / -196, 19 files), under the 5000 threshold. `origin/main` was merged in twice (`d3ff597a` before the PR was opened; `16329059b` for this round, with `view.mdx` regenerated from the merged tree as the pre-push `os-regen` check requires). ## Acceptance notes - `component.zod.ts`'s flat-map guidance block also cites `ObjectMap.tsx:382-396` without a pin. That spelling is outside the gate's population, and it now reads `401-415` (byte-identical). It was left as it is: the gate does not name it. - objectstack-ai#18508 is not absorbed here: its trigger commit `bd09957380` is already an ancestor of the OLD pin `87af769e9a3e`. ## 维护者速读(草稿) - **改了什么**:把控制台钉住的 objectui 版本推进到 `62597c588`(包含权限修复 objectui#10153:`record:quick_actions.requiredPermissions` 改为失败即拒绝);同时按 PM 裁决 A,重录 sdui-parser 的对齐记录(只动了出处字段),并在新版本上逐条重新核对 spec 里引用 objectui 代码行号的注释。 - **为什么改**:当前发布的控制台仍按旧逻辑“失败即放行”,这是 objectstack-ai#19186 的解锁前提;钉住版本一动,引用旧版本的注释门禁必然变红,所以这些注释必须随版本重新测量,不能只替换哈希。 - **风险与代价(含回滚)**:不改任何 schema 或导出,已写的元数据接受/拒绝结果完全不变;唯一对外文字变化是 `FormField.span` 的 describe 把所引版本改为新钉住版本(说法本身仍成立,附 spec patch changeset)。控制台行为只随 objectui 这 37 个提交变化(27 条带发布说明)。日历那一行说法在新版本上已不成立(提示文案改为按语言翻译),已按第二次裁决改写。回滚:还原本 PR 即可。 - **席位意见**: - **你要做的**:本 PR 需要在最终 head 上做一次同档合约审查,记录后才能排队;PR 保持草稿。 Dev session: `session_01852ixPjE785Lt8MUeYGDEb` (PM seat `domain:devx#1`, `session_01VDtqoecgES7ScQYGbFVDRv`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01852ixPjE785Lt8MUeYGDEb --- _Generated by [Claude Code](https://claude.ai/code/session_01852ixPjE785Lt8MUeYGDEb)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Fixes #17429
Part of #17645 — that card's deliverable (the two-code port) lands here; the director seat ends card 17645 with the landing record after the merge (a seat-written record, not a closing reference). The card's take-over from the offline
domain:devxseat is the maintainer's instruction (「18723 os-try-charles 已下班,你也接手」), recorded on the card; the carrier reader's cross-author row (C9) cannot be cleared without the offline holder's ownRelease:, so the card is carried asPart of, not as a closing reference.Clause-②: yes
Corrected from
nounder ruling batch #201 item 1 (maintainer 「201 A」, record 5753263180 on #17645): this PR now also carries #17645's two-code sdui-parser port, so the save gate's diagnostic set grows (inert-quick-add,member-type-mismatch); the pin bump and the 27 re-measured citations alone moved no accept set. Both landed on this branch: the fold-in commit6ef8344645and the citation-spelling fixfc25a5925d(the head); the lockstep record needed no re-recording (see the fold-in section).(Pin-bump half, as originally declared:) Declared from the diff, not from the card: nothing in the pin bump adds, removes or renames an authorable key. The two
packages/specedits with a published face are.describe()SENTENCES (Datasetmeasureformat,FormField.span);check:authorable-surfaceandcheck:api-surfaceboth report their artefacts current with no regeneration, andpnpm --filter @objectstack/spec check:generatedreports all 15 artefacts up to date aftergen:docsandgen:migration-registryran. The same authored metadata is accepted and refused as before, so neither arm of the closed pair applies.What moved
.objectui-sha:53ded82bf7a494f54e344e19099dbf00854b8694→87af769e9a3ee28ace099fdd653d3ebd79fe82e2(objectuiorigin/maintip read 2026-09-20;cfcc17d9dd04— the revision PR #18723 ported from — is an ancestor of it,git merge-base --is-ancestorexit 0).Produced by
scripts/bump-objectui.sh --no-commit 87af769e9a3e, never by hand. Everything the pin travels with:.changeset/console-87af769e9a3e.md@objectstack/console: minor, 98 declared-breaking entries listedsdui.manifest.json+scripts/sdui-manifest.record.jsonnode scripts/gen-sdui-manifest-node.mjs. The manifest is BYTE-IDENTICAL (sha25649211fee7792…, 57 components): the pinned commit declares the same published@object-ui17.6.0the old pin did, so only the provenance record moves.check:sdui-manifestexit 0packages/sdui-parser/objectui-lockstep.jsonpnpm gen:sdui-lockstepagainst a worktree parked at the new pin (--updaterefuses off the pin). See the RED below.objectui-shacitations underpackages/spec/src/**content/docs/references/ui/{dataset,view}.mdxgen:docs, from the two corrected describes.changeset/17429-pin-bump-describe-corrections.md@objectstack/spec: patchfor those two sentencesLockfile / override state:
pnpm installin this worktree produced nopnpm-lock.yamlchange, and the bump script writes none — the console SPA is vendored as a builtdist/, not as a dependency, so this bump moves no dependency edge in this repo.Fold-in: the two-code sdui-parser port (ruling batch #201 item 1, maintainer 「201 A」)
Commit
6ef8344645brings PR #18723's port onto this branch verbatim. Eight files, each byte-identical to the blob onclaude/issue-17645-sdui-lockstep-port(git hash-objecthere vsgit rev-parse claude/issue-17645-sdui-lockstep-port:PATHthere — all eight SAME, no hunk rewritten):packages/sdui-parser/src/kanban-quick-add.ts20f9f74b9f05packages/sdui-parser/src/validate.ts156a264073f7packages/sdui-parser/src/types.ts567bc1259beapackages/sdui-parser/src/index.ts2ea35795b9cfpackages/sdui-parser/src/codegen.ts76a7d654f1fdpackages/sdui-parser/src/__tests__/inert-quick-add.test.ts723cad21333apackages/sdui-parser/src/__tests__/member-type-mismatch.test.ts4b87cadaa067.changeset/17645-sdui-parser-lockstep-port.md7c171fddfc43Deliberately NOT brought from that branch:
.objectui-sha,scripts/sdui-manifest.record.json(both already this branch's, at87af769e9a3e) andpackages/sdui-parser/objectui-lockstep.json.The lockstep record was neither text-merged nor re-recorded — and needed neither
This branch already carries the record taken at the live pin in the pin-bump round:
objectui.rev=recordedAgainstPin=87af769e9a3ee28ace099fdd653d3ebd79fe82e2, 26 diagnostic codes includinginert-quick-addandmember-type-mismatch.--updatere-reads objectui's side only, so a re-record here would have rewritten the same bytes. Attempted anyway, and it REFUSED, which is the generator working:pnpm gen:sdui-lockstepexit 1,[head-off-pin] the objectui checkout is at c2f0f48329de, and .objectui-sha names 87af769e9a3e; record file byte-unchanged across the attempt (e0a6988b1c9abefore and after). The remedy it prints — parking the sibling checkout on the pin — was not taken: that is a git write inside a read-only checkout this container shares with other agents, and the record it would produce is the one already committed.Result —
check:sdui-lockstepexit 0 on the head:OK — this copy is byte-identical to objectui@87af769e9a3e over 214 grammar line(s) [blob 0131f27cf86d] and agrees on all 26 diagnostic code(s), across 8 non-test source(s).--self-testexit 0 alongside.check:sdui-manifestexit 0 (manifest intact, sha25649211fee7792, recorded at the live pin).The type-check red PR #18723 carried is closed by the pin, not by a code change
The red was
check:objectui-pin-citationsinside theType Check · source gatesjob. It reds exactly on the two heads of #18723 that claimed.objectui-sha=cfcc17d9dd04; on this branch, with the live pin, the gate is exit 0 (27 asserting citations match, 48 historical recorded and not checked, 7 anchor content assertions verified). Reverse-verified here throughscripts/ablation-replace.mjs: rewriting.objectui-shatocfcc17d9dd04turns the gate RED with five anchor-content mismatches; restored and proven (blob back to035b6937d641,git diff HEADempty). So the fix is the fold direction itself: the port lands on the branch whose citations are already re-measured at the pin. No edit was made insidepackages/sdui-parser/**for it, and no@ts-ignoreoranywas added anywhere;tsc --listFilesconfirms both new test files are inside the package program.Ported from
cfcc17d9, re-verified against the LIVE pinobjectui DID move
packages/sdui-parserbetweencfcc17d9dd04and87af769e9a3e: one unrelated feature, objectui#6771's retiredbodychild-list dialect (body-dialect.ts+99,index.ts+1,validate.ts+31/−7, its own test). It touches neither ported code and stamps onlynot-a-containerandunknown-prop, both already in the 26-code set — which is why26 = 26holds at the live pin with it unported. Against objectui at87af769e9a3ethe whole non-comment divergence of the five ported sources is four items, three pre-existing (ValidationResultvsManifestValidationResult; thecode: 'inert-quick-add'inline literal forced bycheck:dispatcher-error-vocabulary, pinned equal to the constant by a test row;provenance.tsandassertFullyLoaded/RegistryConfigLike.lazyabsent here) and one new sincecfcc17d9(body-dialect.tsabsent here — see Acceptance notes). The port strictly REDUCES divergence in every file it touches:validate.ts146 → 73,index.ts114 → 92,codegen.ts30 → 3,types.ts96 → 85 diff lines vs objectui at the live pin.Ablations — re-run on this tree
Three, each through
scripts/ablation-replace.mjs(anchor ×1, mutation proven on disk by anchor count and blob change, restore proven byblob == HEADAND an emptygit diff HEAD): (1)checkKanbanQuickAddstamps nothing — 5 failed / 8 passed of 13; (2) the validator's member check reads no declared arms — 7 failed / 14 passed of 21; (3) the codegen narrows no member type — 3 failed / 18 passed of 21. Baselines 13 and 21 passed; in each leg the failures are the SUBJECT rows and every control row stays green. Both suites import../index.js, a relative intra-package specifier, so no rebuild is in the resolution path.Citation spelling fix (
fc25a5925d)CI's
Lint & Repo Gateswent red oncheck-issue-citationsat6ef8344645: two issue-number citations on docblock lines this branch rewrote.packages/spec/src/ui/view.zod.ts—objectstack#17328respelled bare#17328(the issue exists, closed completed by merged PR #17671; the gate probes only unqualified citations, so the this-repo-qualified spelling was judged against a board never asked for it).packages/spec/src/ui/component.zod.ts—#10274measured deleted by--probe-cause(gone from the board and 404 on the web endpoint, not transferred); no replacement guessed — the sentence now states in prose that the number no longer resolves and names the live record (that block pluscheck:objectui-pin-citations).node scripts/check-issue-citations.mjsexit 0 at the head (31 judged, 0 findings);check:generatedunchanged (TSDoc, not.describe()).Post-landing smoke evidence (triage's condition, issue comment 5690507317)
packages/console/distsizesha256 259d2dfecaa12507fd3166a3f91c66c52581990e5ccac3899757027fa6ac108doverfind … -type f | sort | xargs sha256sum | sha256sumpackages/console/dist/.objectui-sha=87af769e9a3ee28ace099fdd653d3ebd79fe82e2pnpm check:console-shaThe console build is GREEN at the new pin. 584 commits of objectui change came with it and none of them broke it.
The citations — re-MEASURED, never re-pointed
27 asserting citations, all of them stale at the new pin. Each was re-derived by reading objectui at BOTH shas and comparing content, not by arithmetic on a line number.
pnpm --filter @objectstack/spec check:objectui-pin-citationsexit 0;--verify-anchorsexit 0 with 7 of 7 content assertions verified against objectui at87af769e9.Corpus-absence records (7 citations + 6 generated copies)
These cite a zero-hit count over the pinned tree, not a file:line. Re-counted with
git grep -F -oat both shas; the METHOD was calibrated first by reproducing the old pin's own numbers exactly (6409 tracked files,useState2304).87af769e918.kernel-health-check-and-hot-reload-durations-unit-in-key.ts:48plugin-lifecycle-advanced.zod.ts+debounceDelay= 0 / 6409 files; controlsobjectstack10171,@objectstack/spec347918.kernel-runtime-config-timeout-unit-in-key.ts:45resourceLimits.timeout0; controlstimeout832,RuntimeConfig236,resourceLimits2resourceLimitshits are still prose inpackages/app-shell18.logging-durations-unit-in-key.ts:48useState2304,timeout702timeout702 was WRONG AT THE PIN IT NAMED: that corpus reads 832. An error since written, found only by re-measuring, corrected here18.system-metrics-jsdoc-durations-unit-in-key.ts:70window2710,timeout832,period160,interval156,metrics30118.system-tracing-otel-exporter-durations-unit-in-key.ts:66tracing.zod.ts+ 4 key names 0;Span404 /SpanSchema40 unrelated; controls 10171, 347918.tenant-schema-cache-ttl-unit-in-key.ts:31schemaCacheTTL0; controlsTTL112,tenant819migrations/registry.ts×6pnpm --filter @objectstack/spec gen:migration-registryRead-point records (20 citations + 7 content assertions)
87af769e9data/api-methods-batch-conformance.test.ts:64ObjectGrid.tsx:3538-3553:3940-3955git hash-object6133933199…both sides)hooks/useBulkExecutor.ts:284-289:298-3030108334833…);ui/component.zod.tstab-itemicon+ui/component.test.ts:376containers.tsx:730-736:853-859containers.tsx:789(registrationitemsinput):912label: 'Tabs', gainedof: 'object'and a longer description; the member list the record cites is unchangedui/component.zod.tsaccordion-itemicon+component.test.ts:286containers.tsx:919-925:1069-1075containers.tsx:966:1116ui/component.zod.tsbuttonicon+component.test.ts:3489form/button.tsx:43,:72,:74form/button.tsx:85-102(inputs),:103-107(defaultProps):85-97,:98-102labelanddefaultValue; the four input names and the ABSENCE of aniconinput, which is what the record asserts, both holdaction/resolve-icon.ts:129-132(resolveIcon):322-328lucide-react'siconsrecord; it asksrecordIconNameand hands the pair tolazyIconComponent. Accept/reject behaviour unchangedresolve-icon.ts:117-120,:100-105,:90-92:302-305,:153-158,:143-145lib/lazy-icon.tsx:66-92:98-124ui/component.zod.tsobject-metriciconplugin-dashboard/src/index.tsx:204:237(registration now opens at:227)label: 'Icon (Lucide name)'is GONE; the key is still published, now as a bare{ name: 'icon', type: 'string' }ObjectMetricWidget.tsx:142/:474;MetricWidget.tsx:312-321/:373-382;lazy-icon.tsx:66-80:174/:483;:351-360/:412-421;:98-112ui/component.zod.tskanbanlimit×2 +component.test.ts:3360ObjectKanban.tsx:264($top: schema.limit ?? DEFAULT_KANBAN_LIMIT):676($top: resolveRowLimit(schema.limit, DEFAULT_KANBAN_LIMIT)):553). The pinned fact, thatlimitlowers into the query's top-level$top, holdsObjectKanban.tsx:71;plugin-kanban/src/index.tsx:395-398:84;:447-450ObjectKanban.tsx:143+plugin-kanban/src/types.ts:134(KanbanSchema.limit?: number)KanbanSchemawas RETIRED at this pin (maintainer ruling 2026-09-09) andtypes.tsdeclares the member no more. Re-derived rather than re-pointed: the published twin isObjectKanbanSchema, imported atObjectKanban.tsx:10, declaringlimit?: numberatpackages/types/src/objectql.ts:3735ElementDataSourceGate.tsx:236-241:316-331describeDisplacedRowLimitreportui/component.zod.tskanbanquickAdd+component.test.ts:3427ObjectKanban.tsx:931;plugin-kanban/src/index.tsx:196;KanbanImpl.tsx:355/:368:1563;:313;:621/:634quickAdd/onQuickAdd0 each inObjectKanban.tsx, against 11 (was 6) for the siblingonCardClickui/dataset.zod.ts:207measureformatdataset-format.ts:185-198,date-display.ts:131-164/:117,:195:229-264(routed at:370),:198-233/:152,:260-262ui/view.zod.ts:2945FormField.span53ded82bf7spanLadderFor,form/form.tsx:204-231ui/view.zod.tsListMapConfig, 7 content assertionsListView.tsx:113/:67;ObjectView.tsx:1381;objectql.zod.ts:562/:313;ObjectMap.tsx:373/:378:146/:85;:1764;:1574/:734;:385/:390FLAT_MAP_CONFIG_KEYSbecame the totalFLAT_MAP_CONFIG_SPELLINGmap (objectui#9950), sostylenow DOES reach the flat product, asmapStyle. The quote was re-read, not re-pointedThe two records whose SUBSTANCE the range reverses
Both are claims about what the SHIPPED renderer does, so the pin is what dates them, and both were re-stated from the new tree. Each also carried a published
.describe()saying the same now-false thing, which this bump is what falsifies — so both sentences are corrected here andcontent/docs/references/ui/{dataset,view}.mdxregenerated from them. That is the@objectstack/spec: patchchangeset in this PR.Datasetmeasureformatsaid "a datetime value ignores it". objectui#8352 is inside the range:formatMeasureDate's datetime arm now SELECTS a formatter —relativetoformatRelativeDate(:260),shorttoformatDateTime(v, { locale, style: 'compact' })(:261), everything else to the bareformatDateTime(v, { locale })(:262). A date PATTERN is still ignored on both arms, which is the half that survives.FormField.spansaid only the widest container-query tier's class is emitted, so a'full'field was one cell of two at 720px ([finding] absolute-colspan-discouraged asserts a misalignment that does not happen — measured in Chromium at all three surface widths, and its recommended alternative span: 'full' is the spelling that misrenders #17328). objectui#9244 / objectui#9253 (bd09957380) are inside the range:spanLadderForemits one clamped col-span class per multi-column tier. ⭐ The previous revision of this block asked in writing to be re-read at the bump that absorbed it; this is that re-read. The'auto'half — textarea, markdown, html, richtext and repeater — re-measured UNCHANGED.Gates
Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(122 commands for this 19-path, 757-line change set). Run locally, exit codes captured before any pipe:plus 27 further derived families, every one exit 0:
check:nul-bytes,check:merge-driver,check:watch-hint-literal,check:comment-mask-adoption,check:comment-mask-corpus,check:doc-frontmatter,check:docs-section-name,check:scripts-symbol-anchors,check:spec-docblock-symbol-anchors,check:ci-filter-parity,check:keyed-text-bounds,check:undeclared-dep-imports,check:section-landing-index,check:liveness,check:duration-unit-keys,check:llms-txt,check:spec-changes,check:upgrade-guide,check:strictness-ledger,check:export-origins,check:api-surface,check:authorable-surface,check:docs,check:doc-anchors,check:docs-spec-enumerations,check:quick-reference-counts,check:corpus-claim-drift,check:changeset-gate-self-tests,check:pm-widening-tells,check:published-files, and both changeset self-tests.The rest of the 122 are left to CI, which runs the whole farm.
origin/mainmoved 7 commits while this was built and touches NONE of this diff's 19 paths, so no merge was taken; the queue's rebuilt generation is what validates the join.Acceptance notes
check-issue-citationsblind spot (measured on [finding] absolute-colspan-discouraged asserts a misalignment that does not happen — measured in Chromium at all three surface widths, and its recommended alternative span: 'full' is the spelling that misrenders #17328 at6ef8344645): a citation qualified with this repository's own name (objectstack#Norowner/repo#N) is recognised as naming THIS repo and resolved against the board, but the board is only asked for unqualified citations and--probe-cause's guard is!cite.qualifiertoo — a live issue readsallocated-but-absentand NOT MEASURED under--probe-cause. Not fixed here (the gate is not this PR's surface); recorded for the next author ofscripts/check-issue-citations.mjs.objectui's
packages/sdui-parser/body-dialect.ts(objectui#6771, landed betweencfcc17d9dd04and the live pin) has no counterpart in this copy: for an authoredbodykey the two copies stamp different codes (not-a-containerunder a non-container there, the bareunknown-prophere), invisible tocheck:sdui-lockstep's code-SET comparison because both codes are already in the 26-set. Both severities arewarning, socompile().okand the save verdict are unchanged. A third code path the ruling does not name; carrier: the next sdui-parser port round.provenance.tsandassertFullyLoaded/RegistryConfigLike.lazyremain absent from this copy (zero diagnostic codes; sdui-parser: port inert-quick-add and member-type-mismatch from objectui, re-record the lockstep and bump the pin #18723's acceptance notes already recordprovenance.ts).pnpm gen:sdui-lockstepis unrunnable in this container because the shared sibling checkout sits off the pin and the generator's remedy is a git write in someone else's tree; it cost nothing this round and will bite the next pin bump, which must park its own checkout..changeset/18516-span-auto-wide-types.md(pending, not yet released) describes theFormField.spandescribe text this PR corrects, including "At this pin only the widest tier's class is emitted".check:empty-changesetrefuses any diff that MODIFIES a changeset present at the merge base, so it is deliberately left untouched here and reported instead.The
element:buttonicon.describe()still says the renderer resolves throughlucide-react'siconsmap. At the new pin that is one hop indirect (recordIconName+lazyIconComponent) but the glyph set and the accepted spellings are unchanged, so it was left as written rather than churned.Original author: the director seat's
os-devrounds (pin bump; fold-in6ef8344645; citation fixfc25a5925d), session_012GcsUbuqFGBibkEDMRC1eE; body maintained by the director seat,session_012GcsUbuqFGBibkEDMRC1eE.Generated by Claude Code