Skip to content

Commit 2bdb81f

Browse files
os-justinclaude
andauthored
fix(spec): re-measure the ComponentPropsMap objectui read-point records at the live pin and make them assertable (#19743)
Part of #18459 (items N3 and N4). Item N1 was already repaired on `main` before this round and item N2 landed as PR #19675; this PR carries the two the previous seat deliberately left on the card. Whether the card can be retired afterwards is triage's call, so ⛔ no closing keyword here — the repo-wide remainder is measured at the bottom. Clause-②: no ⚠️ **Fourth seat correction at 2026-09-22T23:07Z, after the at-tier review `5785756910` at head `95987e2722` (**VERDICT: PASS**, blocking: none).** Five passages were stale against this head and are fixed below; round 4 is now recorded. ⛔ **Three of the five are the seat’s own, and one of them is the same failure mode a third time:** an earlier revision of this line said the `95 → 102` worklist figure was 「superseded by **104**」 — while the passage further down still read **102**. ⭐ That is **a partial correction inside the body**: a correction header that updates a number the body itself does not. Both are superseded by the **110** measured at this head. The seat’s two earlier slips of the same shape are kept on the record below rather than tidied away. ⚠️ **Second seat correction at 2026-09-22T20:39Z, after the at-tier review `5783719817`.** Three claims in this body were wrong or imprecise and are fixed below rather than left standing: the retired-pin reachability sentence (⛔ the seat’s own, and WRONG — see the acceptance notes), the gate-invisible count’s semantic scope, and which anchor count the `--verify-anchors` line was quoting. ⚠️ **Body corrected by the seat at 2026-09-22T18:44Z, at head `60f75eeb27`.** Four anchors were wrong in the text above and are fixed here: `ListView.tsx:3271`→`:3270` (the flatten resolving `titleField` into `labelField`; `:3271` is `fields:`), `ObjectMap.tsx` `:149-157`→`:150-157` (`:149` is a blank comment line), `ObjectMap.tsx:159-161`→`:159-160` (the quoted sentence ends at `:160`), and the gate-invisible-citation acceptance note is re-measured below. All four were re-read by the seat against `git show 87af769e9a3e:<path>`, ⛔ not carried from the round report. ## What this is `packages/spec/src` carries READ-POINT RECORDS: docblocks that say "this key is LIVE, and here is the objectui `file:line` that reads it". Each anchors itself to the objectui tree its numbers were counted in, and `check:objectui-pin-citations` recognises two spellings for that anchor: | spelling | what it claims | what the gate does | |:--|:--|:--| | ASSERTING — `` `.objectui-sha` = `<sha>` `` | this IS the pin we build against | checked against the pin file on every run; a pin bump reds on it | | HISTORICAL — `` `.objectui-sha` pin `<sha>` `` | measured AT that pin, a dated record | permitted, and re-checked by nothing, ever | Eleven records in `ui/component.zod.ts` and `ui/component.test.ts` were in the historical spelling naming the RETIRED pin `53ded82b`, although every one of them is a live read-point record whose whole purpose is to stay re-checkable. Each was re-READ against objectui at `87af769e9a3e` and converted to the asserting spelling. ⛔ This is a RE-MEASUREMENT, not a spelling change: the asserting form ASSERTS the anchors hold at the live pin, so nothing was converted that was not opened and read in the pinned tree. Every anchor in this diff was re-derived from `git show 87af769e9a3e:<path>`; ⛔ none was carried. ## Item N3 — the two anchors, as measured here ⛔ The card's own proposed corrections (`:1871`, `:500`) were themselves taken at the retired pin. The previous seat's handover gave `:503` and `:2181`. **Both re-measured here and both confirmed:** | citation | card proposed | measured at `87af769e9a3e` | |:--|:--|:--| | `component.test.ts` gantt read set, `label` | `:1871` | **`:2181`** — `?? resolveInlineI18nLabel(schema.label, displayLocale)`, the one `schema.label` read outside comments in the file | | `component.test.ts` config-block test, the gantt safeParse | `:500` | **`:503`** — `const result = GanttConfigSchema.safeParse(config);`, inside the `getGanttConfig` branch that opens at `:501` | ⚠️ Correcting only those two numbers would have left a record whose header named one pin and whose numbers named two. So the whole describe block was re-measured and its citation converted — which is also this file's share of N4. ⭐ **The card’s retired-pin story IS verified now, and an earlier sentence here was wrong.** This line used to say the story 「could NOT be verified … `53ded82b` is unreachable … NOT MEASURED」. ⛔ Both halves are false: `53ded82b` is a **readable commit** in the sibling checkout (`git cat-file -t` → `commit`), and the at-tier review read the card’s own numbers there — `ObjectGantt.tsx:1871` at `53ded82b` **IS** `?? resolveInlineI18nLabel(schema.label, displayLocale)` (that file’s only non-comment `schema.label` read at that pin) and `:500` **IS** `GanttConfigSchema.safeParse(config)`. ⇒ **the card’s `:1871` / `:500` were correct RETIRED-pin readings**, and the handover’s `:2181` / `:503` are the live-pin ones. ⚠️ The seat’s 2026-09-22T20:39Z correction fixed the acceptance-notes version of this claim and **left this sentence standing** — a partial correction, which is the defect this PR keeps finding in others. ## Item N4 — what converted, and what the re-read found ⭐ **Round 3 (head `872e06ddf6`) — recorded here because the narrative below stops at `60f75eeb27`.** Three comment-only edits: the blocking `ObjectTimeline.tsx` anchor re-pointed `:518` → `:706-708` **with its file spelled** (`:518` is the gate at the RETIRED pin and a comment line at the live one — a carried reading on a line the conversion never touched); the bare `ObjectView.tsx:1725` spelled `plugin-view/src/…` (two such files exist at the pin and the gate refuses an ambiguous suffix); and the `ElementDataSourceGate` count corrected with its method (above). ⭐ The round also re-audited **every anchor in every converted record end to end — the whole block, not the diff hunks** — 183 occurrences at `60f75eeb27`, 186 at `872e06ddf6`, **0 unresolvable and 0 content mismatches**, independently reproduced by the at-tier review. ⭐ **Round 4 (head `95987e2722`) — two false comparative claims this PR had ADDED, both in published bytes, plus three fold-ins.** (a) The changeset’s 「moved from 7 hits per control to 3」 — refuted by this PR’s own `component.zod.ts:3974-3983`; re-measured at BOTH pins by three methods (3/3/3/3 and tree 0 per shell; 7/5/11/5/0 whole-package) ⇒ **nothing moved**, and the paragraph now carries its method. (b) `component.zod.ts:4104-4105`’s 「the one anchor in this record whose NUMBER did not move」 — false: **exactly two** are identical at the same number at both pins. ⭐ The round named both **existentially** rather than minting a new whole-set superlative, because its enumeration covered one record — the at-tier review ruled that restraint right. Folded in: the four ambiguous bare suffixes now spell `plugin-timeline/src/…` (they resolved to **2** and **21** files and were therefore invisible to the gate’s worklist entirely), `:3957` now names the LIVE pin as where it was measured, and the `0 hits` claim carries its grep and its node-face scope. ⭐ And a **third** superlative the round’s own sweep found on lines this PR added — 「the one number the re-read did not reach, **because** it sat on a line the conversion never touched」 — was measured false (**nine** anchors sat on untouched lines; the untouched line does not discriminate) and replaced with the discriminator that is. The sweep covered all **341** added prose lines against a marker set, **70 hits, every one enumerated with a verdict**. **Eleven of the thirty repo-wide historical `53ded82b` citations converted, which is every one in the two files this claim holds.** Ten in `ui/component.zod.ts` (`object-kanban` navigation, `object-calendar` navigation, the `object-map` / `object-gantt` / `object-tree` / `object-timeline` headers, the tree `objectName` gate-count control, the timeline `data` door, and the two `ComponentPropsMap` row comments) and one in `ui/component.test.ts`. **Three anchors could not have been repaired by refreshing a number — the read itself is gone or different:** - `ObjectMap.tsx` — the array-shorthand head inside `getDataConfig` is DELETED (objectui#8348). An authored `data` array now reaches that renderer through the React props channel alone, never through the record-source ladder. The record used to cite it at `:169-172`; `:150-157` is now the note recording the deletion. - `ObjectTree.tsx` — the `?? schema.titleField` rung in `getTreeConfig` is DELETED (objectui#8841). ⭐ The flat-spelling prescription that names `titleField` stays TRUE on its OTHER half: `ListView`'s flatten still resolves `treeCfg.titleField` into `labelField` before emitting (`ListView.tsx:3270`), and that is now what the record cites. Reading only the renderer half would have produced a "this prescription is stale" finding that is wrong. - `object-kanban`'s navigation read no longer carries the `(schema as any)` cast the record quoted, while its `object-calendar` twin still does, ledgered there as the one objectui#8651 left standing. **And one reading is a COUNT, not an anchor — and the count this line used to carry was wrong.** It said the `plugin-tree` registry shell’s `ElementDataSourceGate` control reading 「moved from 7 hits per control to 3」. ⛔ **Nothing moved.** Re-measured at BOTH pins by one stated method (occurrences of that identifier in each `src/index.tsx`): map / gantt / grid / calendar **3 each** and tree **0** at `87af769e9` **and** at `53ded82bf`; whole-package counts read 7 / 5 / 11 / 5 at both (the lone 7 is plugin-map’s, almost certainly where the carried number came from). The live reading the record rests on — tree **0** against **3 each**, four controls, the zero discriminating — is unchanged and correct. ⭐ **A count is a reading only with its METHOD beside it**; without one, re-stating the carried number is exactly what survives a re-measure. ⭐ `renderer.tsx:1215`’s number is the same at both pins — **and so is `plugin-timeline/src/index.tsx:333`’s**, an anchor of the same record. ⚠️ An earlier revision of this line called `:1215` 「the one anchor in the entire set」 whose number did not move; tested across the whole set, **exactly two** anchors have identical content at the same number at both pins, so the superlative was false. ⛔ A superlative is a claim about a whole SET and needs that set enumerated, exactly as a count needs its method — which is the point this line was trying to make. **Two further shapes the re-read surfaced, both repaired in place:** - The `object-timeline` header was a MIXED record: its `limit` clause had been re-read at the live pin one day earlier while every other anchor beside it, in the same file, was still a `53ded82b` reading — and the historical spelling is what let the two sit together with nothing to tell them apart. Every anchor in it is now at one pin. - Three records in these same blocks cited an objectui sha WITHOUT naming `.objectui-sha` (`Measured at the objectui pin ...`), so they were outside the gate's population entirely — neither asserting nor historical, simply unseen, and stale. They are re-measured and spelled so the gate can see them. See the acceptance notes: there are more of these. **Also made unambiguous:** `ObjectView.tsx:1725` is now spelled `plugin-view/src/ObjectView.tsx:1725`. objectui carries a second `ObjectView.tsx` in `app-shell`, and the gate REFUSES an ambiguous suffix rather than resolving it, so the bare form could never have been content-verified. ## What was deliberately NOT done - ⛔ **No content assertions were added** (the `` `<path>:<line>` first line `<the line>` `` form the gate can verify against the tree), and the floor stays at **7** — ⭐ **which the at-tier review confirmed is the correct, green state, not an omission.** `ASSERTED_ANCHOR_FLOOR` is a const at `check-objectui-pin-citations.ts:815`, enforced as an EQUALITY both ways at `:1271`, asserted against the tree again by `--self-test` at `:1099-1105`, with the failure text at `:1317` demanding the edit **in the same PR** — and `packages/spec/scripts/**` is a TOOLING landing path under ruling #202 B, outside this claim's surface. ⇒ adding one assertion here would red both the main run and the self-test. The gate reports 7 verified. ⚠️ This diff DOES grow the `--verify-anchors` worklist, which at head `95987e2722` reports **212** anchors carrying no content assertion, because re-measuring spelled several read points as proper backticked anchors that were previously prose and made one previously-ambiguous anchor resolvable. ⚠️ **Which count that is, named:** the gate's own `--verify-anchors` worklist reads **95 → 110** for these two files at head `95987e2722` (82 `component.zod.ts` + 28 `component.test.ts`). ⚠️ Earlier revisions of this line read **102**, then **104** in the header above while this passage still said 102 — ⭐ **a partial correction inside the body**, the very defect this PR keeps catching elsewhere; both are superseded by the 110 measured here; the 「106 → 112」 figure quoted in an earlier revision of this line was a `scanAnchors` count and is NOT the worklist figure — it is NOT MEASURED against `scanAnchors` directly here, so it is withdrawn rather than defended. - ⛔ **Nineteen historical `53ded82b` citations remain repo-wide**, all outside the two files this claim holds, and several in files under other claims — `migrations/registry.ts` carries four and is held by four open PRs, `ui/view.zod.ts` carries two and is held by two. They are listed in the report so they can be carried as their own work. - ⛔ The four remaining historical citations in these two files name `190fbd01`, a different and older pin. They are dated records of past measurements, which is what the historical spelling is FOR; they are not the `53ded82b` class the card names and were left alone. ## Verification Every heavy run went through `scripts/pm/os-verify-lock.sh`; verdicts read from its `VERDICT command-exit` line, never a bare `$?`. Gate exit codes were landed to a file BEFORE any pipe. | run | exit | |:--|:--| | `pnpm --filter @objectstack/spec build` | 0 | | `pnpm --filter @objectstack/spec test` — 515 files, 15043 passed, 1 todo | 0 | | `pnpm --filter @objectstack/spec typecheck` | 0 | | `pnpm --filter @objectstack/spec check:generated` — all 15 artifacts up to date, nothing to regenerate | 0 | | `pnpm --filter @objectstack/spec check:objectui-pin-citations` — 43 asserting match the pin, 37 historical recorded, 7 anchor assertions verified | 0 | | `check-objectui-pin-citations.ts --verify-anchors` (needs the sibling checkout; run against it) | 0 | | `pnpm lint` — `eslint . --no-inline-config`, whole repo, ⛔ NOT narrowed | 0 | | `pnpm exec turbo run build --concurrency=2` — 74/74 successful | 0 | `scripts/pm/dispatch-gates.mjs` derives **83** families from the changed paths. At head `872e06ddf6` the honest reconciliation is **79 run, 4 NOT MEASURED** — `check:doc-formula-expressions`, `check:dual-build-cjs-loads`, `check:lean-entry-closure` and `check:type-check-debt` each exit **3** (`PREREQUISITE NOT MET`, an unbuilt closure), and the round **killed a closure build at 420s rather than hold the shared verify lock for a comment-only change**. ⛔ An exit-3 run measured nothing and is ⛔ never written up as a pass. All four run green in CI at this head: `check:dual-build-cjs-loads` and `check:lean-entry-closure` in `Build Core`, `check:type-check-debt` in `Type Check · debt ledger`, `check:doc-formula-expressions` in `Type Check · consumer gates`. ⚠️ The 「83 run, 0 NOT-MEASURED, 0 UNRUN」 reading this line used to carry was taken at an earlier head. ⚠️ **The paragraph above supersedes this one.** An earlier revision said all four exit-3 families 「were re-run to a real verdict after the full workspace build and all four exit **0**」 — that was true at an earlier head, where the round paid for a full build. At head `95987e2722` the round deliberately did **not** hold the shared verify lock for a comment-only change, so the four stay **NOT MEASURED** locally and are covered by the named CI jobs above. ⛔ An exit-3 run measured nothing and is ⛔ never written up as a pass. ⭐ Separately, two gates needed a second, CORRECT run and then passed: `check:react-declaration-parity` (exits 1 bare, 0 as CI spells it with `MANIFEST` + `--baseline` + `--strict`) and `check:query-options-erasure` (killed by the round’s own 150 s per-command cut, exit **124**; it takes ~165 s and exits 0 without that cut). ⚠️ The at-tier review ruled that distinction explicitly: **NOT MEASURED is for a gate whose prerequisite was deliberately not met, ⛔ not for one the harness interrupted and then let finish.** `check:react-declaration-parity` exits **1** with no `MANIFEST`, which is the script's documented behaviour rather than a finding. Run as CI runs it — `MANIFEST="$PWD/sdui.manifest.json" ... --baseline react-declaration-parity.baseline.json --strict` — it exits **0** with no new declaration divergence against the accepted baseline. ⛔ No ablation was performed and none is owed: every changed line is a comment, so there is no branch to ablate and no diagnostic to invert. ## Changeset — `patch` on `@objectstack/spec`, measured `packages/spec` `files[]` carries `src/**/*.zod.ts`, so `src/ui/component.zod.ts` ships VERBATIM. Measured against the real packed tarball, not asserted: the text this diff adds appears **7** times in `package/src/ui/component.zod.ts` inside the tarball (positive control), a token present nowhere appears **0** times (negative control), and the retired-pin spelling appears **0** times in the shipped file. `src/ui/component.test.ts` is NOT in the tarball — the pack carries **0** `src/**/*.test.ts` entries — so only one of the two changed sources publishes. ⚠️ **A conflict worth naming rather than deciding quietly.** The os-dev role file lists comments among the fast-path categories that "do not publish", which would point at `skip-changeset`. The measurement above says the opposite for THIS file: the comments are inside a source file that ships verbatim, so the published bytes move. The dispatch order's rule — a changeset is owed if published bytes change, measure it — is the one followed here, and the fast-path line is reported rather than silently overridden. ## Acceptance notes — reviewed, ⛔ nothing filed from this PR - **Docs Drift Check (`5780983322`) — read, and no doc change is owed. Measured, not asserted.** It lists `content/docs/protocol/objectui/layout-dsl.mdx` via the `ComponentPropsMap` symbol, and three release-owned `content/docs/releases/v17/*` pages (read-only by AGENTS.md, ⛔ not edited here). The seat measured the diff at head `60f75eeb27` and re-measured it at `872e06ddf6` (387 changed lines) and again at `95987e2722` (**414** changed lines across the two `.ts` files, **480** with the changeset, **0 non-comment** either way) against its merge-base `16d090ede0`: across both changed `packages/spec/src/ui/*.ts` files there are **ZERO** non-comment changed lines — every added and removed line is a `//`, `*`, `/*` or blank line. The two `ComponentPropsMap` hunks move comment prose only; the rows (`object-map`, `object-gantt`, `object-tree`, `object-timeline`), their schemas and the `as const` are byte-identical to the base. ⇒ A diff that changes no behaviour cannot have falsified a page that documents behaviour, so ⛔ no hand-written doc is re-verified here and none is edited. - ⭐ **The gate's population has a hole, and this diff sat in it.** `check:objectui-pin-citations` builds its population from `.objectui-sha` mentions only. A record spelled `Measured at the objectui pin ...` names a sha that can rot and is invisible to every arm of the gate — not historical, not asserting, not even the hard-failure arm for an unrecognised spelling, because that arm only fires inside a `.objectui-sha` window. ⚠️ **The remainder is not "outside these files" — the seat re-measured it and the round undercounted twice.** Counted by the seat at head `60f75eeb27` with the pattern `objectui pin ` + backtick, excluding lines that also carry `.objectui-sha`: **15** gate-invisible citations across `packages/spec/src`, and **7 of them are inside the two files this claim holds** (`component.zod.ts` `:2703`, `:2744`, `:3333`; `component.test.ts` `:2313`, `:2394`, `:3838`, `:3904`). The round's own correction said five; it counted only the ones naming the retired `53ded82b` and missed `component.test.ts:3838` and `:3904`, which name the LIVE pin `87af769e9` and are gate-invisible all the same — so a future pin bump will never red on them either. The remaining 8 sit in five other files (`migrations/registry.ts` 2, `ui/view.zod.ts` 2, `ui/page.test.ts` 1, `calendar-config-allday-prescription-17054.test.ts` 1, and one each in two `migrations/entries/semantic/18.*` files), naming `53ded82b`, `a472b07` and `00d3f09c`. ⚠️ **Semantic correction (at-tier review `5783719817`):** those numbers reproduce EXACTLY under the literal pattern above, but the pattern **misses a sha wrapped to the next line or held in parentheses** — three more inside the claimed files (`component.zod.ts:2412` `00d3f09c`; `:3050-3051` and `:3313-3314` `53ded82b`) and `view.zod.ts:1922-1923` `a472b07`. ⇒ the **semantic** reading is **19 repo-wide / 10 in the two files**. ⭐ Writing the pattern down is what made that refutable at all. ⚠️ NOT MEASURED: this **15** does not reconcile with the **17 / 9 remain** stated one round earlier, and the seat did not establish whether the two counts used the same pattern. Take the pattern above as the reading, ⛔ not the number on its own. ⛔ **What this remainder is NOT.** The round's report escalates `component.zod.ts:2744` and `:3333` to a *falsified* claim, on the ground that they assert `convertSortToQueryParams` still honours the legacy string clause while at `87af769e9a3e` it refuses it out loud. **The seat checked and does not carry that.** Both records anchor themselves to `53ded82b` in their own first line and say "at THIS pin", and both name objectui#8221's PR #8758 — merged 2026-09-09, *after* that pin — as the change that drops the string arm. The live tree matches exactly what the record predicted: `sort-query.ts:62-78` at `87af769e9a3e` says a retired string is REFUSED OUT LOUD. ⇒ A dated record measured at a retired pin is not falsified by a tree that pin predates; measuring its anchors at the live pin and calling them stale is the same category error in reverse. The defect here is the SPELLING, which is what the card says. ⛔ **CORRECTION by the seat — the sentence that stood here was WRONG, and it was written as the "sharper replacement" for a claim the seat had just withdrawn.** It read: 「`53ded82b` is **unreachable from this workspace** … a record anchored to it can never be re-verified by anyone, in either direction.」 The at-tier reviewer (record `5783719817`) refuted it and *used* the retired pin to verify the sort records above; the seat re-measured and confirms the reviewer: `git cat-file -e 53ded82b^{commit}` exits 0, `git rev-parse` resolves it to `53ded82bf7a494f54e344e19099dbf00854b8694`, and `git show 53ded82b:packages/core/src/utils/sort-query.ts` reads the string arm at `:66-70`. The earlier reading was taken on a checkout **before the session container was recycled**, where `cat-file -e` did fail — so it measured that checkout's history depth, ⛔ not a property of the repository. ⭐ What IS true, and it is much narrower: `git fetch origin 53ded82b` answers `couldn't find remote ref` ⇒ **the remote does not serve it as a bare sha**. Whether the object is present locally depends on the checkout's depth at that moment ⇒ re-verifying such a record is **a matter of luck**, ⛔ not "never, by anyone". ⚠️ A new statement does not become true by virtue of replacing a false one — and a universal quantifier ("anyone, in either direction") is the last thing that should be written at that moment. Carrier for the class: card #19744. - The objectui docblock at `plugin-map/src/ObjectMap.tsx:159-160` states "MEASURED: `@objectstack/spec` 17.4.0 has NO `ComponentPropsMap['object-map']` row". That row exists now. A comment in another repository with no contract behind it, same class as the `plugin-tree` one the previous round noted. ⛔ Not filed. - `ObjectTree.tsx` passes `resolveRecordSourceConfig` the arm `'undeclared'` while `object-map` and `object-gantt` pass `'view-data'`, and the objectui comment beside it calls the tree "an unruled block" — which it no longer is, since this repo now publishes its row. The divergence is in the HARMLESS direction (the renderer is wider than the door, so the door still refuses what it always refused) and it is objectui-side. Recorded in the record itself; ⛔ not filed. --- _Generated by [Claude Code](https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent c120dbd commit 2bdb81f

3 files changed

Lines changed: 341 additions & 139 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): the `ComponentPropsMap` objectui read-point records are re-measured at the live pin and now assert it (#18459)
6+
7+
Clause-②: no
8+
9+
`packages/spec/src` carries READ-POINT RECORDS: docblocks that say "this key is
10+
LIVE, and here is the objectui `file:line` that reads it". A record anchors
11+
itself to the objectui tree its numbers were counted in, and
12+
`check:objectui-pin-citations` recognises two spellings for that anchor — an
13+
ASSERTING one (`.objectui-sha` = `<sha>`, checked against the pin file on every
14+
run, so a pin bump reds on it) and a HISTORICAL one (`.objectui-sha` pin
15+
`<sha>`, a dated record that a later bump does not falsify, and that nothing
16+
re-checks).
17+
18+
Eleven records — ten in `src/ui/component.zod.ts`, one in its sibling test —
19+
were in the historical spelling naming the RETIRED pin `53ded82b`, although
20+
every one of them is a live read-point record whose whole purpose is to stay
21+
re-checkable. Each was re-READ
22+
against objectui at `87af769e9a3e` and converted to the asserting spelling, so
23+
the next pin bump fails on them instead of carrying them.
24+
25+
**The drift was real, not hypothetical, and three anchors could not have been
26+
repaired by refreshing numbers:**
27+
28+
- `ObjectMap.tsx`'s array-shorthand head inside `getDataConfig` is DELETED
29+
(objectui#8348); an authored `data` array now reaches that renderer through
30+
the React props channel alone, never through the record-source ladder.
31+
- `ObjectTree.tsx`'s `?? schema.titleField` rung is DELETED (objectui#8841).
32+
The flat-spelling prescription that names `titleField` stays TRUE on its
33+
other half — `ListView`'s flatten still resolves `treeCfg.titleField` into
34+
`labelField` before emitting — and that is now what the record cites.
35+
- `object-kanban`'s navigation read no longer carries the `(schema as any)`
36+
cast the record quoted, while its `object-calendar` twin still does.
37+
38+
A fourth is a count rather than an anchor: the `plugin-tree` registry shell's
39+
`ElementDataSourceGate` control reading. It was re-taken at BOTH ends of the hop
40+
by ONE method — occurrences of that identifier in each control's own
41+
`src/index.tsx` — and nothing moved: 3 each for `plugin-map`, `plugin-gantt`,
42+
`plugin-grid` and `plugin-calendar`, 0 for `plugin-tree`, at
43+
`87af769e9` and at `53ded82bf` alike, so the ZERO that discriminates is the
44+
whole reading. The `7 each` the record used to carry is reproducible at neither
45+
pin by that method, nor by a whole-package count (7 / 5 / 11 / 5). ⛔ A count is
46+
a reading only with its METHOD beside it — without one, re-stating the carried
47+
number is exactly what survives a re-measure.
48+
49+
`plugin-timeline/src/renderer.tsx:1215` is at the same number with the same
50+
content at both pins — and it is not alone there:
51+
`plugin-timeline/src/index.tsx:333` (`limit: 'limit',`), an anchor of that same
52+
record, is too, read by the same method at both pins. Which is exactly why a
53+
number that did not move is no more a reading on its own than one that did.
54+
55+
Three further records in the same blocks cited an objectui sha WITHOUT naming
56+
`.objectui-sha`, so they sat outside the gate's population entirely — neither
57+
asserting nor historical, simply unseen. They are re-measured and spelled so
58+
the gate can see them.
59+
60+
**Why `Clause-②: no`.** Every changed line is a comment. No schema, describe
61+
string, export, key or refusal text moves, so no input's accept/reject verdict
62+
can change; `check:generated` reports all fifteen spec artifacts up to date
63+
with nothing to regenerate. It ships as a `patch` rather than as no changeset
64+
because the bytes are published: `src/ui/component.zod.ts` ships verbatim under
65+
this package's `files[]` entry `src/**/*.zod.ts`, measured against the packed
66+
tarball with a positive and a negative control.

‎packages/spec/src/ui/component.test.ts‎

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,7 +3618,13 @@ describe('ObjectMetricPropsSchema icon liveness (#10053)', () => {
36183618
// 「8348 以协议为准」 (batch #83, 2026-09-08) and batch #136 item 3 (Q1-C).
36193619
//
36203620
// The acceptance the card names, pinned: each row's KEY SET is the one the
3621-
// renderer's read points support at the `.objectui-sha` pin `53ded82b`, and
3621+
// renderer's read points support at the pin this repo builds against
3622+
// (`.objectui-sha` = `87af769e9`; re-READ at that pin 2026-09-22 — all three
3623+
// renderers moved hard on the hop from `53ded82bf`, so NO anchor in this
3624+
// block is carried and every one was re-derived from the pinned tree. Four
3625+
// changed CONTENT rather than position and say so where they are cited: the
3626+
// map's array-shorthand head, the tree's record-source ARM, the cast on the
3627+
// tree's `schema.data` read, and the tree's `titleField` rung), and
36223628
// `Object.keys(ComponentPropsMap)` lists the three. The key sets are asserted
36233629
// WHOLE rather than by spot-check — a row derived from read points is a claim
36243630
// about a complete set, and only an equality can hold a later addition to
@@ -3635,21 +3641,29 @@ describe('the three #18305 object blocks — key sets derived from the renderers
36353641
};
36363642

36373643
it('object-map declares exactly its measured read set', () => {
3638-
// ObjectMap.tsx @ 53ded82b: data (:169 -> resolveRecordSourceConfig :174),
3639-
// staticData / objectName (the shared ladder's rungs 2 and 3), filter
3640-
// (:742), sort (:743), map (:370), mapStyle (:365), navigation (:889),
3641-
// enableClustering (:905).
3644+
// ObjectMap.tsx @ 87af769e9: data (:183 — `getDataConfig` is now that one
3645+
// `resolveRecordSourceConfig(schema, 'view-data')` statement; the
3646+
// array-shorthand head that used to open it is GONE, and the docblock at
3647+
// :168-176 records that an authored array now reaches this renderer only
3648+
// through the React props channel), staticData / objectName (the shared
3649+
// ladder's rungs 2 and 3, record-source.ts :296 and :303), filter (:814
3650+
// and :895 — the inline and the object fetch), sort (:815 and :896), map
3651+
// (:382), mapStyle (:377), navigation (:1042), enableClustering (:1058).
36423652
expect(keysOf('object-map')).toEqual([
36433653
'data', 'enableClustering', 'filter', 'map', 'mapStyle', 'navigation', 'objectName', 'sort', 'staticData',
36443654
]);
36453655
});
36463656

36473657
it('object-gantt declares exactly its measured read set', () => {
3648-
// ObjectGantt.tsx @ 53ded82b: the ladder (:593), filter (:738), sort
3649-
// (:739), gantt (:499), navigation (:1487), label (:1849), skipWeekends
3650-
// (:1205), holidays (:1206), persistLayout (:1357), viewName (:1359),
3651-
// markers (:1826), criticalPath (:1829), showBaselines (:1832), readOnly
3652-
// (:1833), mobileReadOnly (:1834).
3658+
// ObjectGantt.tsx @ 87af769e9: the ladder (:613), filter (:844), sort
3659+
// (:845), gantt (:501-503), navigation (:1615), label (:2181 — the
3660+
// `resolveI18nLabel` call in the export-file-name chain; the retired
3661+
// reading :1849 was the COMMENT above that chain even at its own pin, and
3662+
// at this one is a BLANK line two above an unrelated callback's comment),
3663+
// skipWeekends (:1333), holidays (:1334), persistLayout (:1485),
3664+
// viewName (:1487), markers (:2136),
3665+
// criticalPath (:2139), showBaselines (:2142), readOnly (:1985 and
3666+
// :2143), mobileReadOnly (:2144).
36533667
expect(keysOf('object-gantt')).toEqual([
36543668
'criticalPath', 'data', 'filter', 'gantt', 'holidays', 'label', 'markers', 'mobileReadOnly',
36553669
'navigation', 'objectName', 'persistLayout', 'readOnly', 'showBaselines', 'skipWeekends',
@@ -3659,14 +3673,19 @@ describe('the three #18305 object blocks — key sets derived from the renderers
36593673

36603674
it('object-tree declares exactly its measured read set — and `data` IS in it', () => {
36613675
// The card's open question, answered by measurement rather than by family
3662-
// symmetry: ObjectTree.tsx @ 53ded82b reaches `schema.data` through
3663-
// `resolveRecordSourceConfig(schema)` at :359 — rung 1 of the shared
3664-
// ladder, which returns the authored value VERBATIM as a `ViewData`. That
3676+
// symmetry: ObjectTree.tsx @ 87af769e9 reaches `schema.data` through
3677+
// `resolveRecordSourceConfig(schema, 'undeclared')` at :582 — rung 1 of
3678+
// the shared ladder, which returns the authored value VERBATIM. That
36653679
// ONE site is the whole support for the object arm, and it is sufficient.
3666-
// ⛔ :496 is NOT a second one: `(rest as any).data ?? (schema as any).data`
3667-
// is gated by `Array.isArray(passed)` on the next line, so it honours only
3680+
// ⚠️ The ARM is `'undeclared'` here, not the `'view-data'` its siblings
3681+
// pass, so rung 1 honours any truthy value at the renderer — WIDER than
3682+
// this row, which is the harmless direction: the door below refuses the
3683+
// bare array the renderer would have taken.
3684+
// ⛔ :775 is NOT a second one: `(rest as any).data ?? schema.data` (the
3685+
// cast on `schema` went away with objectui#8655) is gated by
3686+
// `Array.isArray(passed)` on the next line, so it honours only
36683687
// the bare-ARRAY shorthand this row REFUSES (pinned below). objectui#9234
3669-
// left the :359 read marked `undeclared` because neither published face
3688+
// left the rung-1 read marked `undeclared` because neither published face
36703689
// carried the key; the protocol row follows the READ POINTS, which is what
36713690
// 「以协议为准」 resolving for this block means.
36723691
expect(keysOf('object-tree')).toEqual([
@@ -3740,14 +3759,23 @@ describe('the three #18305 object blocks — key sets derived from the renderers
37403759
// renderer's flat branch still reads beside `dependenciesField`.
37413760
expect(setFor('object-gantt', 'OBJECT_GANTT_FLAT_CONFIG_KEYS'))
37423761
.toEqual([...Object.keys(GanttConfigSchema.shape), 'dependencyField'].sort());
3743-
// tree: `TreeConfigSchema`'s shape PLUS `titleField`, which `getTreeConfig`
3744-
// reads only as `labelField`'s last fallback (ObjectTree.tsx:117).
3762+
// tree: `TreeConfigSchema`'s shape PLUS `titleField`. ⚠️ Re-READ at
3763+
// 87af769e9 and the read point is GONE, not moved: objectui#8841 deleted
3764+
// the `?? schema.titleField` rung from `getTreeConfig` (now :235-248),
3765+
// and the docblock above it (:197-218) records why — it read the
3766+
// FLATTENED NODE, never the block, and the key is declared on neither
3767+
// face. The key stays in THIS set, and the prescription that names it
3768+
// stays TRUE, on the OTHER half of the sentence: `ListView`'s flatten
3769+
// still resolves `treeCfg.titleField` into `labelField` before emitting
3770+
// (`ListView.tsx:3270` at this pin), so an authored `titleField` is still
3771+
// only ever the block's `labelField`. What died is the renderer's own
3772+
// fallback, not the flatten's.
37453773
expect(setFor('object-tree', 'OBJECT_TREE_FLAT_CONFIG_KEYS'))
37463774
.toEqual([...Object.keys(TreeConfigSchema.shape), 'titleField'].sort());
37473775
});
37483776

37493777
it('the config blocks are the spec own schemas where the renderer names one, `z.unknown()` where it does not', () => {
3750-
// gantt: `ObjectGantt.tsx:501` validates the authored block against
3778+
// gantt: `ObjectGantt.tsx:503` validates the authored block against
37513779
// `GanttConfigSchema` imported from `@objectstack/spec/ui`, so the read
37523780
// point names the schema and the door takes it — a misspelling inside the
37533781
// block is refused here exactly as the renderer's own safeParse warns.
@@ -3762,7 +3790,7 @@ describe('the three #18305 object blocks — key sets derived from the renderers
37623790
expect(door('object-tree').safeParse({ tree: { labelFeild: 'name' } }).success).toBe(false);
37633791
// map: `ListMapConfigSchema` — the ratchet the previous posture deferred,
37643792
// taken now that `style` is declared on that block (the key `getMapConfig`
3765-
// reads at `ObjectMap.tsx:365`, `schema.mapStyle || schema.map?.style`). The
3793+
// reads at `ObjectMap.tsx:377`, `schema.mapStyle || schema.map?.style`). The
37663794
// two assertions that used to record the divergence are INVERTED here: the
37673795
// list-view face accepts the style URL, and the door accepts it through the
37683796
// spec's own schema rather than through an open value.

0 commit comments

Comments
 (0)