Repository navigation
feat(spec/ui): export navItemVisibleDataNeedsDataSource, so a mirror of NavigationItemSchema attaches it by identifier - #24628
Conversation
…ttaches it by identifier Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
…igationItemSchema attaches Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
… add the changeset Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
…port-nav-visible-data-check
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 143 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 58706ce75e32a532cfd9f3349786f4b05e577957 && git checkout 58706ce75e32a532cfd9f3349786f4b05e577957
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 973917c5795f7a440d46a15bc5cf8961af9d8703 e058ce508e78c913754deabacd401acb6d2e20c5 && git checkout -B drift-repro 973917c5795f7a440d46a15bc5cf8961af9d8703 && git merge --no-ff e058ce508e78c913754deabacd401acb6d2e20c5
node scripts/docs-audit/affected-docs.mjs --json 973917c5795f7a440d46a15bc5cf8961af9d8703 |
席位核对 (seat check) · PR #24628 ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, assignee zhuangjianguo. Line 1 is Fixes #24534. Clause-②: yes (widening). 5 files, +308 / −1. Not governed. The changeset is minor on @objectstack/spec. |
| What holds | navItemVisibleDataNeedsDataSource is exported the way its siblings are (src/ui/index.ts export *, exports["./ui"], the api-surface and export-origins ui shards). The schema keeps attaching the same function, and its issue code, path and message are unchanged. The spec suite and all 89 derived gates are green. |
| The gap the dev measured (P2) | The function reads the spec's PARSED entry (visible as { dialect, source }). objectui's mirror keeps visible as the authored string, so attaching the export there as it stands refuses nothing on the card's own entry. The export alone does not meet the card's purpose, which is that the mirror closes the door by identifier. |
| Seat answer: A | The function also reads a bare-string visible as CEL source, the spelling the spec's own shorthand normalises to the envelope. The four axes all point to A. Long term: one rule, in spec, attached by identifier (objectui#7715), with no restated normalisation in the mirror. AI error: attaching the export is enough by itself, with no silent no-op. Business need: the bare string is the dominant authored spelling (16 to 1 in examples/**, the dev's count). Scope: S, with no gate added. The accept set does not move: the schema's own refinement only ever sees the envelope, and the function is new in this PR, with no caller outside the schema. B restates the shorthand in objectui. C is objectui's own call for its boolean and envelope divergences, and stays out of this PR. Not escalated: it shapes how a new helper reads its input, inside the ruling (no change to what the schema accepts or refuses). |
| The patch | One commit on this branch. (1) The bare-string branch beside the envelope read, normalised exactly as the spec's shorthand is (cite the normaliser). A boolean visible and an absent visible judge nothing, as today. (2) Pin leg 5 flips: the card's entry as written (bare-string visible, no dataSource) now draws the door's identical issue from a direct call. Add a control: the same bare string with a dataSource draws none. (3) The docblock and the changeset say the function accepts the authored and the parsed spelling. (4) The PR body records the answer. Fixes #24534 stays. |
| Accepted deviations | (1) The ablation re-run with a distinct marker; that reading is the one cited. (2) The three gates that were PREREQUISITE NOT MET before the merge exit 0 after the builds. (3) The os-regen-merge.sh merge carries the script's own message. |
| Finding | objectui's visible arm accepts visible: true and refuses the envelope; spec does the reverse. It is carried by the objectui follow-up this seat files when this lands (Done-when 2), as context for that card. Not filed separately. |
| Landing | After the patch, a CONTRACT_REVIEW_TIER review on the new head (schema face, widening), then landing on a green head. |
Generated by Claude Code
…re-string visible too The export now normalises a bare-string visible through EvaluatedExpressionInputSchema, the slot's own schema, so a mirror whose visible stays the authored string chains it as it is. NavigationItemSchema accepts and refuses exactly what it did: its own check only ever sees the parsed envelope. Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
|
CI note (seat
Why this is not this PR's failure:
Fix: none exists yet. The case's budget is vitest's default and it is not sized to its loop. That is the oversubscription class #19828 describes. The seat is filing the flake as its own card in the runtime lane, so this PR does not widen to Generated by Claude Code |
Contract reviewServed-tier: Read: card #24534 (body and all six comments, 6267689111 through 6274643516), PR #24628 (body, file list, and the net diff of five files against the merge base ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Contract review adopted ·
Generated by Claude Code |
Fixes #24534
Clause-②: yes (widening)
What this does
@objectstack/spec/uinow exportsnavItemVisibleDataNeedsDataSource. That is the refinementNavigationItemSchemaalready runs: it refuses a navigation entry with nodataSourcewhosevisiblereadsdata. It is exported the same way asobjectNavTargetExclusivity,checkPageRegionPlacementandcheckPageMainRegionWidth, so a hand-written mirror of the navigation item can chain the spec's own function instead of restating the rule.packages/spec/src/ui/app.zod.ts: the declaration gainsexport, and its docblock gains the sibling paragraph (what it judges, the issue it raises, who attaches it) After the seat's answer A (comment 6273673166), the function also reads an authored bare-stringvisible. The string goes throughEvaluatedExpressionInputSchema, the slot's own schema, via a module-privateauthoredVisibleEnvelope, so the shorthand normalisation is reused and never restated. The docblock says the function accepts both spellings. The.superRefine(navItemVisibleDataNeedsDataSource)on the union insideNavigationItemSchemais unchanged.packages/spec/src/ui/app-nav-visible-data-needs-data-source-export.test.ts: the export pin (5 legs, 36 tests, below).packages/spec/api-surface/ui.json,packages/spec/export-origins/ui.json: regenerated bygen:api-surface(it reportedtouched: ui.json) andgen:export-origins(1 shard(s) rewritten). Each gains one row. No other shard moved..changeset/24534-export-nav-visible-data-check.md:minoron@objectstack/spec,Clause-②: yes (widening). A widening carries no ADR-0087 disposition marker, andcheck-adr-0087-registrationis green without one.Nothing an author can write is accepted or refused differently. The issue is the same:
customatvisible, with the same message. The existing rule pins inapp-nav-visible-data-needs-data-source.test.tsare untouched and green.Premises
P1 holds. On
origin/main5a871e4e41:packages/spec/src/ui/app.zod.ts:1041declaresfunction navItemVisibleDataNeedsDataSource(item: unknown, ctx: z.RefinementCtx): voidwith noexport.:1107attaches it with.superRefine(navItemVisibleDataNeedsDataSource)on the discriminated union thatNavigationItemSchema'sz.lazyreturns.export function checkPageRegionPlacement(atpage.zod.ts:1766andexport function checkPageMainRegionWidth(at:1846.They reach
@objectstack/spec/uithis way:src/ui/index.tshasexport * from './app.zod'(:21) andexport * from './page.zod'(:54);package.jsonexports["./ui"]maps todist/ui/index.{mjs,js,d.mts,d.ts};api-surface/ui.jsonand one inexport-origins/ui.json.So adding
exportis the whole route.Measured after the build, from
packages/core(a consumer that resolves the workspace package through itsexports):import('@objectstack/spec/ui')givestypeof navItemVisibleDataNeedsDataSource === 'function', length 2, resolved topackages/spec/dist/ui/index.mjs;require('@objectstack/spec/ui')givesfunction, fromdist/ui/index.js;declare function navItemVisibleDataNeedsDataSource(item: unknown, ctx: z.RefinementCtx): void;.P2 holds for the signature, but not for what a mirror gets when it chains the function as it is.
Signature.
(item: unknown, ctx: z.RefinementCtx) => voidis what asuperRefinecallback takes. Thectxtype is the same oneobjectNavTargetExclusivity(item, ctx)takes, and objectui'sNavigationItemSchemaalready chains that function. I read objectuiorigin/main50ba1bb0a6,packages/types/src/zod/app.zod.ts. That code follows objectui#7715's rule: re-attach the spec's object-level checks by identifier. The function needs no state a mirror would have to supply. Its CEL reader (celReadsRootData/lexCel) is closed over in the module.Behaviour. The function judges the entry after the spec's parse, where
visibleis the{ dialect, source }envelope thatEvaluatedExpressionInputSchemaproduces. Measured on the card's entry,{ id: 'nav_envs', type: 'object', objectName: 'sys_environment', visible: 'data.total == 1' }:NavigationItemSchema.safeParse) refuses it with onecustomissue atvisible;visibleparsed ({ dialect: 'cel', source: 'data.total == 1' }) raises the identical issue: code, path and message;visible) raises nothing.objectui's mirror. It declares
visible: z.union([z.boolean(), z.string()]).optional(), so its parsedvisibleis still the authored string. Chaining this export there as it is would refuse nothing on the card's entry.What this PR does about it. The first head (
50c3ba8a4a) wrote only the export and raised the difference as an open question. The seat answered A (comment 6273673166 on this PR): the function also reads the authored bare string, normalised throughEvaluatedExpressionInputSchema(the slot's own schema), so the spelling a mirror hands it is judged exactly as the spec's parse would judge it. Measured one058ce508e:dataSourcedraws nothing;visiblejudges nothing.NavigationItemSchemaaccepts and refuses exactly what it did, because its own check only ever sees the parsed envelope. The schema pins inapp-nav-visible-data-needs-data-source.test.tsare unchanged and green (23 passed).The pin (
app-nav-visible-data-needs-data-source-export.test.ts, 36 tests)object,dashboard,url,group; 4 accepting paths: the card's entry with adataSource, an ambient-root predicate, a member nameddata, and novisible), the direct call andNavigationItemSchema.safeParseagree issue for issue. These fixtures writevisibleas the envelope, the one spelling whose parsed value equals what was written.customcheck, and its issue vector over the matrix equals the export's.export function navItemVisibleDataNeedsDataSource(once and chains.superRefine(navItemVisibleDataNeedsDataSource)once, on a code line../indexexports the same function object, with arity 2.visibleand on the entry as written.dataSource) draws nothing from the door or the export.visiblejudges nothing.Ablation (reverse verification), at
42945c2f43with the implementation committed. The mutation wasexport function navItemVisibleDataNeedsDataSource(→/* ablated: export removed */ function navItemVisibleDataNeedsDataSource(, applied throughnode scripts/ablation-replace.mjs:8755bfe0b8eb→07a6fe68bd65;8755bfe0b8eb, andgit diff HEADis empty.The test imports
./app.zodand./indexrelatively (src, notdist), so the ablation needed no rebuild. The first attempt was a no-op: the replacement text was a substring of the anchor, and the tool refused before it ran anything. It was re-run with a distinct marker. (That reading is at the first head, where the pin had 27 tests.)Ablation of the bare-string read, at
e058ce508ewith the patch committed. The mutation wasconst visible = (typeof entry.visible === 'string'→const visible = (/* ablated: bare-string read removed */ false && typeof entry.visible === 'string', throughablation-replace.mjs:74d3de955a29→c459f2d5767d;app-nav-visible-data-needs-data-source.test.ts: 5 failed / 54 passed (59);74d3de955a29, andgit diff HEADis empty.Tests and gates (final head
e058ce508e)origin/mainc3954057fewas merged at50c3ba8a4athroughscripts/pm/os-regen-merge.sh. Main's later commits touch no file this PR touches, so it was not merged again.pnpm --filter @objectstack/spec test: 702 files passed, 21348 tests passed, 1 todo.pnpm --filter @objectstack/spec typecheck:tsc --noEmit,check:scripts-typecheckandcheck:test-typecheckall pass. The test-typecheck debt is unchanged at 52 files / 242 errors / 132 pinned signatures, so the new test adds no errors.app-nav-visible-data-needs-data-source.test.tsis unchanged since50c3ba8a4aand passes 23.pnpm --filter @objectstack/spec check:generated:All 14 generated artifacts are up to date, against thedistthis head built.check:api-surfacereportspublic API surface + factory signatures unchanged, andcheck:export-originsreportscurrent.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, 89 commands, identical before and after the merge) was run on this head with each exit code captured before any pipe. All 89 exited 0.dispatch-gates --rananswers:89 derived famil(ies) accounted for — 89 run, 0 NOT-MEASURED (a DERIVED zero — all 89 recorded an exit code and none of them is 3). This includescheck:api-surface,check:export-origins,check:entry-nameability,check:browser-reachable-entries,check:dual-source-exports,check:exported-any,check:docs,check-adr-0087-registration,check-changeset-no-major,check-empty-changeset,check:published-files,check:nul-bytes,check:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debt. On both heads, three of them (check:doc-formula-expressions,check:dual-build-cjs-loads,check:lean-entry-closure) first exited 3 (PREREQUISITE NOT MET, nothing measured) in a freshly created worktree. They exited 0 once their packages were built, and--ranrecords those exit-0 re-runs.check:dev-prereqsexit 0 andcheck:skill-frame-freshnessexit 0.e058ce508e(40 runs), read when the patch report was written: every required context exceptTest Core (6/6)is green (Lint & Repo Gates,TypeScript Type Check,Build Core,Governed Surface Queue Guard,Test Core (1/6)to(5/6)).Test Core (6/6)then went red on a runtime share-link case that timed out at vitest's 5000 ms default. That failure is not this PR's (seat CI note 6274396318) and is filed as Flaky on Test Core (6/6):packages/runtimeshare-links-refusal-order.test.ts› dispatcher twin › "[#23372] email audience: the refusal is the one an unknown token and a password-less email link answer" times out at vitest's 5000 ms default #24633. Its one re-run is in flight. The patch report on spec(ui): export navItemVisibleDataNeedsDataSource, so mirrors of NavigationItemSchema can attach it by identifier — objectui's NavigationItemSchema accepts a nav entry whose visible reads data with no dataSource, which the spec refuses #24534 cites the check-runs.Acceptance notes
visibleas well as the parsed envelope, so objectui's mirror can attach the export as it is, by identifier. B (objectui restates the shorthand) and C (objectui takesEvaluatedExpressionInputSchemaby reference) are not taken here. C stays objectui's own call for its boolean and envelope divergences.visiblearm differs from the spec's in two more ways. It acceptsvisible: true/false, which the spec'sEvaluatedExpressionInputSchemarefuses. It refuses the{ dialect, source }envelope, which the spec accepts. I read this from the source and did not measure it throughobjectui validate. It belongs to the objectui follow-up this card names (Done-when 2).Generated by Claude Code