Repository navigation
feat(spec)!: a navigation entry whose visible reads data must declare dataSource, refused at parse - #24490
Conversation
… dataSource Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…eset, refinement ledger) Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…usal Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…v-visible-data-needs-datasource
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
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 aac99f95ae59ddd126ffcdc937e975f0dbba5948 && git checkout aac99f95ae59ddd126ffcdc937e975f0dbba5948
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ae6bd1a696fa630cf8a3fc052489dd7af03cfcdc b911ad135b1fe2c62f71e060fa9a7781975bbc89 && git checkout -B drift-repro ae6bd1a696fa630cf8a3fc052489dd7af03cfcdc && git merge --no-ff b911ad135b1fe2c62f71e060fa9a7781975bbc89
node scripts/docs-audit/affected-docs.mjs --json ae6bd1a696fa630cf8a3fc052489dd7af03cfcdc
|
Review: ACCEPT · PR #24490 @
|
Fixes #24450
Clause-②: no (narrowing)
Question 2 of the ruling recorded on #24332 (comment 6259393328), letter A, verbatim as the director recorded it:
What this does
packages/spec/src/ui/app.zod.ts: a module-private refinement,navItemVisibleDataNeedsDataSource, mounted once onNavigationItemSchemaitself (asuperRefineon the discriminated union). An entry with nodataSourcewhosevisibleis a CEL predicate that reads the rootdatagets onecustomissue at its ownvisible. The message names the fix: declaredataSourceon this entry, or drop thedata.*read and gate oncurrent_userorfeatures. Because the check sits on the union, it reaches every navigation door:app.navigation,app.areas[].navigation, everychildrenarray at any depth, and a navigation contribution'sitems. The exported per-branch schemas do not chain it. That is the same posture asobjectNavTargetExclusivity, and no runtime consumer parses a branch schema directly (measured withgit grep: 0 non-test hits).celReadsRootData, a small CEL lexer in the same file (about 100 lines, no new export). It refuses only when it is sure. It skips strings (single, double, triple-quoted, andr/bprefixed) and//comments. It does not count member selection (x.data), a call nameddata, or a field or key name before:in a{ ... }literal. If a comprehension macro (exists,all,map,filter,cel.bind, ...) binds a variable nameddata, the reader returns "no read" for the whole predicate. A leading-dot absolute name (.data.x) and a${...}-wrapped predicate are read as root reads. It imports nothing from a runtime package (Prime Directive ✨ Set up Copilot instructions #2). The precedent for a predicate reader next to the schema that refuses on it is the settings manifest'svisiblegrammar check insystem/settings-manifest.zod.ts.visibledescribe and TSDoc now say the read is refused without adataSource.content/docs/references/ui/app.mdxwas regenerated bygen:docs.packages/spec/src/migrations/entries/semantic/18.nav-item-visible-data-needs-data-source.ts: the D3 semantic entry (protocol 18). No key is removed, so there is no tombstone. There is no D2 conversion because the shape cannot be converted: only the author knows which endpoint the entry meant to read, and dropping the read would change what the gate means. The chart-binding narrowing (view-chart-binding-dataset-required) has the same shape.packages/spec/dropped-refinements.baseline.json: +31 sites, one...lazysite per published schema that carriesNavigationItemSchema. The build printed the corrected entries and they are copied verbatim.measured.droppedRefinementSitesgoes from 781 to 812. JSON Schema cannot express a CEL lexer, so these are real new gaps of the projection, not of the rule..changeset/24450-nav-visible-data-needs-data-source.md:@objectstack/specminorwith the BREAKING banner (pre mode),Clause-②: no (narrowing), one ADR-0087 marker (registered nav-item-visible-data-needs-data-source), and the FROM → TO mapping plus a one-line fix.Premise, measured on
origin/main26501ae24before buildingexpression-scope-data-root.pin.test.ts)packages/spec/src. The only predicate tokenizer in spec is the settings manifest's, and it is a different grammar (not CEL). So one was written, small and conservative, as described above.data.reads in navigationvisiblepredicates.ts/.tsx/.mtsfile at26501ae24counted object literals with anid, a navigationtypeand avisible. Result: 11 entries in 4 files. 2 readdata, and both are inapp-nav-item-api-data-source.test.tsand declare adataSource. 0 readdatawithout one. Control leg: 3 of the same entries readcurrent_user. The settings-manifestvisiblepredicates (service-settings/src/manifests/*, 20${data.provider ...}hits) are a different schema and were excluded from the census by path. JSON/YAML: 0"visible": "...data."hits, against a control of 0"visible"string values at all, so the JSON corpus has nothing to say here either way. objectui and cloud were not measured.visiblepathvisiblerelative to the entry, for examplenavigation.0.children.0.visible.Tests (at
b911ad135b, the head after mergingorigin/main31ce45c07)packages/spec/src/ui/app-nav-visible-data-needs-data-source.test.ts, 23 tests. Refused, each with exactly onecustomissue atvisibleand a message that names the fix:data.total == 1,!data.has_more, baredata,has(data.total), a mix withcurrent_user,.data.total,${data.total == 1},data['total'], a read after a raw string ending in a backslash, the{ dialect: 'cel', source }envelope, thedashboard/page/url/groupbranches, and every door:navigation, a group's child whose parent declares adataSource(the binding is not inherited),areas[].navigation, and a contribution'sitems. Accepted: the same entry with adataSource(itsvisibleenvelope unchanged),current_user,features, both together,current_user.data.total,datainside a string, inside a triple-quoted string or inside a comment, a macro iteration variable nameddata, a selection on a call result, an entry with novisible, and a separator.node scripts/ablation-replace.mjs. Anchorif (!celReadsRootData(visible.source)) return;, count x1 → x0, blob90cf1bbc→56f1eb19. Result: 12 failed / 11 passed. Every refusal case went red and every acceptance case stayed green, which is the expected direction. Restored: blob == HEAD (90cf1bbc) andgit diff HEADis empty. The test imports./app.zodfrom source, so there is no dist leg.pnpm --filter @objectstack/spec test: 699 files passed, 21290 tests passed (1 todo).pnpm --filter @objectstack/spec test:repo: 70 files, 1162 tests passed.pnpm --filter @objectstack/spec typecheck: exit 0.pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date after a rebuild of the merged tree.@objectstack/spec):@objectstack/lint: full suite, 145 files, 6866 tests passed.@objectstack/metadata-protocol: full suite, 246 files passed (3 skipped), 20655 tests passed.@objectstack/cli: unit tier, NARROWED to the navigation and i18n files (*nav*.test.ts,i18n-*.test.ts). Of 35 named files, 27 are in the unit project: 27 files, 308 tests passed. The rest of the unit tier and the integration tier are declared to CI.datato read"). The only hits are the regenerated reference page. No test elsewhere pins the old acceptance.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this diff derived 112 families. All 112 ran onb911ad135b, and--ranreconciles 112 of 112 run, 0 NOT MEASURED, every one at exit 0. Six of them (check:doc-formula-expressions,check:doc-security-posture,check:skill-examples,check:docs-transcript-drift,check:dual-build-cjs-loads,check:lean-entry-closure) first exited 3 (PREREQUISITE NOT MET: no lint / formula / objectql dist). After a turbo build of the@objectstack/lint...,@objectstack/objectql...,@objectstack/client...and@objectstack/formula...closures (36 tasks, all cache hits), all six exited 0. The derivation warned thatorigin/mainhad moved 8 commits past the merge, touching.github/workflows/ci.yml,.github/workflows/spec-shape-gate.ymlandscripts/ci/spec-shape-gate.mjs. CI reads those from the merge ref.Acceptance notes
visibleWhentwin. A page component whosevisibleWhenreadsdata.*with no APIdataSourcebinding is the same shape. It is outside this ruling, which covers navigation entries only.dataas a map-literal key ({data: 1}), which in CEL is an expression that readsdata. That spelling is under-refused on purpose, because it cannot be told apart from a message field name without a type table.METADATA_TYPE_SCHEMAS.app=AppSchema(packages/spec/src/kernel/metadata-type-schemas.ts). Its HTTP answer (422 INVALID_METADATA) is not measured here, and storedsys_metadataapp rows are not measured either. The census found no such row in this repo.Generated by Claude Code