Repository navigation
feat(spec): a navigation entry may declare one API data source, which its visible reads as data - #24427
Conversation
…y its visible (shape 1) The page element's ElementApiDataSourceSchema by reference on the shared nav base; visible reads the bound value as data. Planned, not yet rendered: liveness row navigation.children.dataSource graded planned with authorWarn. Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu Co-authored-by: Claude <noreply@anthropic.com>
…ss counts; drill the planned dataSource row Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu Co-authored-by: Claude <noreply@anthropic.com>
…v-item-api-data-source
📓 Docs Drift CheckThis PR changes 1 package(s): 25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. 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 0943caac806d8462842557cca41a2eeb7df43b96 && git checkout 0943caac806d8462842557cca41a2eeb7df43b96
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e22defb13c171900e2c1c54d1d15c24ed85fe91b 98e3434b0e2300491bd6acc474e48b8962a1efb6 && git checkout -B drift-repro e22defb13c171900e2c1c54d1d15c24ed85fe91b && git merge --no-ff 98e3434b0e2300491bd6acc474e48b8962a1efb6
node scripts/docs-audit/affected-docs.mjs --json e22defb13c171900e2c1c54d1d15c24ed85fe91b
|
Contract reviewServed-tier: Inputs: card #24332 body and its four comments (triage ① Derived judgments
② Semver level
③ Boundary flagsDev deviations (os-dev-report
Open questions (also on the card):
Acceptance notes:
Out of scope: the Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 39195742017 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
|
Queue ejection ·
Generated by Claude Code |
|
Re-queue, once ·
Generated by Claude Code |
|
Consumer pointer from objectui The objectui renderer half has landed. objectstack-ai/objectui#13029 → PR objectstack-ai/objectui#13095 → objectui
Owed on objectstack (
The liveness reading should wait until objectstack's Generated by Claude Code |
Part of #24332 (the
visiblehalf of shape 1 lands here; thelabeland record-target halves stay open on the card as two forks, below).Clause-②: yes (widening)
What this does
A navigation entry may declare ONE API data source, the page element's
ElementApiDataSourceSchemaBY REFERENCE (packages/spec/src/ui/page.zod.ts), and itsvisiblereads the bound value asdata:packages/spec/src/ui/app.zod.ts:BaseNavItemSchemagainsdataSource: ElementApiDataSourceSchema.optional(), so the nine branches that spread the base carry it (object,dashboard,page,url,report,action,component,doc,group). Aseparatorspreads nothing and refuses it with its own prescription (the key joinsSEPARATOR_NAV_ITEM_GUIDANCE). Thevisibledescribe now says an entry with adataSourcereads it asdata, and an entry without one has nodata.[PLANNED — not yet rendered]and claims no reader. Onlyvisiblereadsdata: the describe sayslabeland the target keys (recordId,filters,params) take nodataread, and a group's binding is not published to itschildren.packages/spec/liveness/app.json: rownavigation.children.dataSource, gradedplanned,authorWarn: truewith anauthorHint, and drilledprovider/read.url/read.method/path.check:livenessrefuses an undeclared container inheritance, so the four child keys are drilled. The carrier is the objectui renderer half, which the seat files when this lands. The precedent forplanned+authorWarnin this same file isnavigation.runActionbefore objectui#5216.authorable-surface/ui.json(+9 rows, one per branch),content/docs/references/ui/app.mdx,liveness/state-counts/app.md..changeset/24332-nav-item-api-data-source.md:@objectstack/specminor.The premise, measured before building (ruling item 2)
page.zod.ts's transitive import closure is 52 files and does not containapp.zod.ts. Nothing moved.dataroot.objectui-shapin0707b4ae37and at main749ed76gives the same reading.objectui: packages/react/src/SchemaRenderer.tsxreads a node's API binding (useElementApiDataSource) and puts the answer in that node's evaluation scope asdata(pin:1157/:1231, main:1174/:1248). That oneExpressionEvaluatorserves the node's${…}text holes, inline locale-map members included (objectui#12936), and itsvisibleWhen(CEL envelope viaevaluateCondition). The binding is RENDERED at the pin, so the spec's[PLANNED]text onElementApiDataSourceSchemaand the "nodataroot" text onvisibleWhenare stale. That is #24300's surface, held off here.visibleobjectui: packages/app-shell/src/hooks/useNavMemberGates.tsevaluates a navvisiblethroughevaluateNavigationVisibilitywith the sameExpressionEvaluator.evaluateCondition, so a per-entrydatain scope is the same dialect and the same root name. Built.label${…}in a string or an inline-map member), but the label has a layer a page element does not:translateApp(packages/spec/src/system/i18n-resolver.ts) REPLACES a present label with the bundle entry keyed by the entry id, andwalkNavigationinpackages/cli/src/utils/i18n-extract.tsseeds every present label into the bundle as a translatable literal. The ruled row's own entry (nav_environments) has a zh-CN bundle entry, so a data label there would show the static noun in zh-CN. How a data-bearing label meets translation is a contract decision. Not built, see open question 1.recordIdobjectui: packages/layout/src/NavigationRenderer.tsxresolveHrefsubstitutesrecordIdthroughapplyNavTemplate, whose vocabulary is{current_user_id},{current_org_id}and a context selector's own{id}(single brace, no dots). A${data.id}there is taken as written. Reading data would add a second template dialect to the field, or a second spelling of a data read. Not built, see open question 2.passesNavItemGuards) evaluatesvisibleon every type,groupincluded, so a group can honour its own binding.Tests (all at
98e3434b0e, the PR head, unless noted)packages/spec/src/ui/app-nav-item-api-data-source.test.ts: 19 tests. Every branch parses with the binding and adata.*visible; the app door accepts it on a top-level entry and on a group's child; and the bare binding parses. Five refusals are each compared with the page element binding's own issues for the same value (code+path, not prose): unknown key, off-originread.url(3 shapes), credential key,POST, object binding. A separator refuses the key. The describe marker and theplannedliveness row are pinned.node scripts/ablation-replace.mjsrenamed the key todataSourceAblated, with anchor x1 → x0 and blob791b2943→c37dfcd0. Result: 17 failed / 2 passed (the separator refusal and the ledger row stay green, as they should). Restored, blob == HEAD,git diff HEADempty. The tests import./app.zodfrom source, so no dist leg applies.pnpm --filter @objectstack/spec test: 697 files, 21270 passed (at82e723d6e5; the merge after it brought nopackages/specchange).typecheckpassed, andcheck:test-typecheckOK.pnpm --filter @objectstack/spec check:generated: all 14 artifacts up to date after a rebuild at98e3434b0e.@objectstack/spec):@objectstack/lintfull suite: 145 files, 6864 passed. It includesvalidate-nav-*,validate-translation-referencesandlint-liveness-properties.@objectstack/metadata-protocolfull suite: 246 files passed (3 skipped), 20655 tests.@objectstack/cliunit tier, NARROWED: the full unit tier ran past a 540 s bound (exit 124, NOT MEASURED, and declared to CI). The run coverednav-contribution-groups*and 23test/i18n-*files: 25 files, 298 passed.liveness-planned-propertyfinding. The lint fans out over the top-level entries only, as the ledger README states.Gates
The derived list is
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this diff: 111 families, pluscheck:dev-prereqs(local-only, touches the spec build). All 112 were run locally on98e3434b0e, and--ranreconciles 111 of 111. Every one exits 0 except:pnpm check:platform-checklist: exit 1, red onorigin/mainas well. The finding isareas/access-security.json: ABSENT SYMBOL packages/core/src/security/resolve-authz-context.ts#legacyGrantAnchorRetired. This diff touches neither file. Onorigin/main71fa166e96,legacyGranthas 0 hits in that file, against a control ofexportwith 13 hits. Reported to the seat.Six families first answered exit 3 (PREREQUISITE NOT MET: no lint / client / objectql dist). After the consumer closure build (turbo, 62 tasks, all cache hits), all six re-ran with exit 0:
check:doc-formula-expressions,check:doc-security-posture,check:skill-examples,check:docs-transcript-drift,check:lean-entry-closure,check:dual-build-cjs-loads.Open questions for the seat (also in the report on #24332)
recordIdhas its own{…}template dialect.data.*in avisibleon an entry with nodataSource. It would be BREAKING and need an ADR-0087 disposition. Measured reach: 0data.reads among the navvisiblepredicates in this repo's 38 navigation-declaring files. The control is the same files'current_userpredicates, e.g.packages/spec/src/conversions/registry.ts:2091.read.urlof a page element'sapidata source …") on the navigation door too. That text ispage.zod.ts's, a hold-off for this card.Acceptance notes
objectui: packages/types/src/zod/app.zod.ts,NavigationItemObject) is a non-strictz.objectand stripsdataSource. This is renderer-half work for the carrier card.data.*read in a navvisibleFAULTS (and so withholds the entry underevaluateNavigationVisibility) or readsundefinedis NOT MEASURED: objectui is not installed in this container. TheauthorHintsays only that there is no value to read.Generated by Claude Code