fix(spec): the four ComponentPropsMap navigation doors list all seven NavigationModeSchema modes - #19675
Conversation
…tionModeSchema modes The `navigation` describes on `object-grid`, `object-map`, `object-gantt` and `object-tree` are the WHOLE account of those doors' shape — the doors are `z.unknown()`, so nothing else tells an author what a mode may be. Three listed six of the seven `NavigationModeSchema` values (no `new_window`) and the `object-grid` row, the precedent the other three copied, listed five (no `popover` either). Re-measured against the live `.objectui-sha` pin rather than the pin the finding was taken at: all four blocks hand `schema.navigation` straight to the shared `useNavigationOverlay` hook, that hook types its own mode union AS the spec's `NavigationModeSchema`, and its click router carries a `new_window` branch that falls through to `window.open` when no `onNavigate` is supplied. So every one of the seven reaches every one of these doors, and `popover` is anchored per-row in all four. The list now names the schema instead of standing alone, so the next member added to `NavigationModeSchema` has a named edge to these rows. Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
…mponent-props-map-describes
…t correction The corrected describes are published bytes: `src/ui/component.zod.ts` ships verbatim under this package's `files[]` entry `src/**/*.zod.ts`, and the compiled strings ship in `dist/ui/index.js` and `dist/ui/index.mjs`. Measured with `npm pack --dry-run --json` against the built tree, with a positive control (an untouched describe from the same rows) and a negative control (a string that exists nowhere). Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check4 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 136 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 8a5d2350ec3257d2576d62cada260286fa642e3c && git checkout 8a5d2350ec3257d2576d62cada260286fa642e3c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 93cdc43d51dac6723a9dc88244193fe8629d61be 55dd769453a00c5fe4f587dbe9ec1f59914e029c && git checkout -B drift-repro 93cdc43d51dac6723a9dc88244193fe8629d61be && git merge --no-ff 55dd769453a00c5fe4f587dbe9ec1f59914e029c
node scripts/docs-audit/affected-docs.mjs --json 93cdc43d51dac6723a9dc88244193fe8629d61be |
⛔
|
Check Changeset run |
started | conclusion |
|---|---|---|
106639835934 |
2026-09-22T06:28:54Z | ✅ success |
| — | 2026-09-22T06:33:31Z | needs:contract-review |
106640972793 |
2026-09-22T06:33:36Z | ❌ failure |
106641124401 |
2026-09-22T06:34:15Z | ❌ failure |
Same commit, same diff, same changeset, same Clause-②: no body line. The only thing that changed is the label. ⇒ the head is the control and the label is the variable; this is measured, ⛔ not inferred from timing alone.
Why the label reds a patch PR — the gate says it in its own words
Clause ② is declared ONCE, FOR THE PR — the
needs:contract-reviewcarrier is a PR label and theClause-②:line is a PR-body line …
A purely additive widening of a published package's public surface takes AT LEASTminor
⇒ ⭐ the carrier is not inert. Hanging it declares clause ② to this gate, which then requires minor; this changeset is patch. So the label and the body's Clause-②: no contradicted each other, and the gate was right to say so.
The remedy is the gate's own option 2, ⛔ not a tolerance
- The level is right and the DECLARATION is wrong → correct it at the producer: the
needs:contract-reviewcarrier is the review seat's to place and to clear … ⛔ Do not add a tolerance here to route around a declaration that says something its author did not mean.
The level is right and the declaration was wrong — the carrier outlived the review it was raised for:
- The path limb (
packages/spec/src/**) genuinely obliged a contract review, so hanging the carrier was correct. - That review is done and PASS, at
CONTRACT_REVIEW_TIER, recorded on the card at5772201004. Evidence from the patch: +4/−4, declared type on removed and added lines byte-identical (z.unknown().optional().describe(), and 0 added lines that are not a.describe().z.unknown()accepts everything ⇒ no accept/reject verdict can move, no export added or removed. - ⇒ the carrier is cleared citing that PASS, which is the ordinary end of its lifecycle.
patchstands.
⛔ The seat's actual mistake, stated plainly
The ordering. The carrier was hung before the review it carries was recorded, so for ~5 minutes this PR declared a clause ② that its body denied and its diff disproved. ⇒ place the carrier and clear it inside the same act when the review is already done, or the gate will correctly red the gap. ⛔ No test was touched, ⛔ no tolerance added, ⛔ no re-run spent.
⭐ Recorded as a platform fact for other seats: needs:contract-review is read by Check Changeset as a clause-② declaration, so it is ⛔ never a free annotation on a patch PR.
Generated by Claude Code
…ds at the live pin and make them assertable (objectstack-ai#19743) Part of objectstack-ai#18459 (items N3 and N4). Item N1 was already repaired on `main` before this round and item N2 landed as PR objectstack-ai#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 objectstack-ai#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 objectstack-ai#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 objectstack-ai#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>
Part of #18459 (item N2)
Clause-②: no
Summary
The card carried two shipped imprecisions. Re-measured at the LIVE objectui pin
87af769e9a3ee28ace099fdd653d3ebd79fe82e2before anything was changed, one is dead andone survives — and the surviving one is wider than the card scoped it.
This PR fixes the surviving one. The falsification of the other is recorded below rather
than acted on.
Zone 2 — what the re-measurement found
N1 (lat/lng pair marked required) — FALSIFIED, already fixed. No change here.
At PR #18403 the
object-mapmaprow read:'Map field config, the author face: { latitudeField, longitudeField, locationField?, titleField?, descriptionField?, zoom?, center?, style? }. Taken WHOLE when present ...'— the enumeration the card objected to. Commit
1aa5026e30(PR #18904, "declarestyleonListMapConfigSchemaand pointobject-map.mapback at it") deleted that enumeration.On
maintoday the row reads'Map field config, the author face — the same block ListViewSchema.map declares ...', and the generated reference renders the TYPE fromListMapConfigSchemaitself:| **map** | { latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … } | optional | ...Every key optional, no required pair claimed. The statement the card called false no longer
exists in the source or in the shipped reference.
Reproduce:
git log -S "Map field config, the author face: { latitudeField" -- packages/spec/src/ui/component.zod.tsreturns exactly two commits,
a3d4c59d69(added, PR #18403) and1aa5026e30(removed, PR #18904).N2 (navigation list omits
new_window) — CONFIRMED, and it is four rows, not three.At the live pin,
object-grid,object-map,object-ganttandobject-treeeach handschema.navigationstraight into the shareduseNavigationOverlayhook(
ObjectGrid.tsx:2321,ObjectMap.tsx:1041,ObjectGantt.tsx:1680,ObjectTree.tsx:891).That hook binds its own mode union to this package's
NavigationModeSchema— its docblocksays so in as many words, "the seven modes this hook switches on are exactly the seven the
exported union publishes", held by a parity test on that side — and its click router carries
a
new_windowbranch that delegates toonNavigateand otherwise falls through to awindow.open.object-ganttimplements that action itself as well.The four
.describe()strings listed six of the seven (nonew_window), exceptobject-grid— the row the other three were copied from — which listed five (no
popovereither). All fourdoors are
z.unknown(), so that sentence is the entire published account of the shape, andthe reference page renders the type beside it as
any.N3 (two comment citations in
component.test.ts) — NOT fixed here, and the card's own correction is stale.Both citations still exist (
component.test.ts:3649label (:1849),:3750ObjectGantt.tsx:501). But they are HISTORICAL citations anchored to the retired pin53ded82b, and the corrections the card proposes (:1871,:500) were measured at thatsame retired pin. At the live pin the true positions are
:503for theGanttConfigSchema.safeParseand:2181for the label resolution — so applying thecard's numbers would replace one unverifiable pair with another.
component.test.tsis alsooutside this dispatch's declared file surface. Left for the card.
N4 (historical pin-citation spelling) — unchanged, measured.
check:objectui-pin-citations --liston this head: 48 historical / 29 asserting / 7assertion entries. 30 historical citations still name the retired
53ded82b, ten ofthem in
component.zod.ts, including the three headers this card is about(
component.zod.ts:3561map,:3715gantt,:3906tree). The gate permits the historicalform by design, so nothing is violated and nothing reds — those anchors simply are not
re-checked on a pin bump, which is exactly what the card recorded. N3's re-measurement above
shows the drift is real and not hypothetical. Converting them is a re-measurement job, not a
spelling change, and is left for the card.
The change
Four
.describe()strings, plus the reference page regenerated from them withpnpm --filter @objectstack/spec check:generated --fix(never by hand):ObjectGridPropsSchema.navigationObjectMapPropsSchema.navigationObjectGanttPropsSchema.navigationObjectTreePropsSchema.navigationEach now lists
page | drawer | modal | split | popover | new_window | noneand namesNavigationModeSchemaas the source of that list, so the next member added to the enum has anamed edge into these rows instead of four independently drifting lists.
Why
object-gridis in scope. The card names it as the precedent the other three copiedand says "fixing it means fixing the family, not just these three rows". It is the same defect
class, in the same file, in the same gate family, adds no verification surface, and understates
by two rather than one. Correcting three while leaving the source of the pattern intact would
leave the family in the state that produced the defect.
No accept set moves: the doors stay
z.unknown().Changeset — measured, not pattern-matched
patchon@objectstack/spec. The changed bytes ARE published bytes:packages/spec/package.jsonfiles[]containssrc/**/*.zod.ts, sosrc/ui/component.zod.tsships verbatim in the tarball.dist/ui/index.jsanddist/ui/index.mjs.npm pack --dry-run --jsoninpackages/speclists all three of those paths among its2030 entries.
@objectstack/specisprivate: falseat17.4.0— a released package.dist files; a negative control (a string present nowhere) is found in none.
So
skip-changesetwould have been wrong here, and no changeset would have been wrong too.Verification
Head
55dd769453.origin/mainmerged in atf2101d0492viascripts/pm/os-regen-merge.shbefore the final measurements; all 15 spec generated artifacts re-checked clean after it.
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived102 families from the actual changed paths. All 102 run, exit codes captured to a file
BEFORE any pipe, reconciled with
--ran: 102 run, 0 NOT MEASURED, 0 UNRUN.pnpm --filter @objectstack/spec run check:react-declaration-parityin its bare spelling, which exits 1 by design with no
MANIFEST. 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 vs the accepted baseline.
pnpm --filter @objectstack/spec test— 512 files, 14953 passed, 1 todo.pnpm --filter @objectstack/spec typecheck— exit 0 (tsc --noEmit,check:scripts-typecheck,check:test-typecheck).pnpm lint(eslint . --no-inline-config, the whole repo, not narrowed) — exit 0.pnpm --filter @objectstack/spec check:generated— all 15 artifacts up to date.scripts/pm/os-verify-lock.sh; verdicts read from itsVERDICT command-exitline.Acceptance notes
Observed while measuring, deliberately NOT changed here and not filed:
modeonly.useNavigationOverlayalso readssize,openNewTabandpreventNavigation, and the TYPED sibling rows (object-kanban,object-calendar, the list-view entry) name all four members. On az.unknown()door thedescribe is the whole account, so naming one member of four understates the block — a
separate imprecision from the one this card carries. Carrier: the next PR touching these
rows, or a decision to point these four doors at
NavigationConfigSchemathe way theirtyped siblings already do.
plugin-tree/src/ObjectTree.tsxat the live pinstates "measured on the installed spec:
navigationis declared on exactly oneComponentPropsMapentry,object-grid". It is now declared on several. A comment inanother repository with no contract behind it. Carrier: objectui.
Generated by Claude Code