Skip to content

feat(spec): an app can name a landing per audience with landing - #28449

Merged
objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/project-thread-9ddkwd
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 11 commits into
mainfrom
claude/project-thread-9ddkwd

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D

Requested by Tesla · project thread

Fixes #28022

Clause-②: yes (widening)
Metadata-protocol: added

domain:spec#2 · os-tesla · session_01L9qibwXXtueDQZXqxv2FsL

Ruling: record 6545223246 on the card, letter A (maintainer 「同意」 2026-10-31T15:10Z). This is the spec half only. Follow-up: #28450, the domain:ui landing card (console landing redirect, plus attaching checkAppLandingItems to objectui's AppComponentSchema, which drops the one notAttachable entry this PR adds).

维护者速读

  • 改了什么:应用新增 landing 键,按受众声明落地页:[{ item, visible }],第一个 visible 成立的条目决定读者打开哪个导航项;无匹配或不写时照旧打开第一个导航项。
  • 为什么改:同一个应用给销售和客服共用时,只能有一个落地页;把"销售"挪到"客服"前面,客服的人一进来就落在线索上。
  • 风险与回滚:只加不减,旧应用不受影响;item 指向不存在的导航项、或指向分组/分隔线/动作项,保存时直接拒收(不会像退役的 homePageId 那样悄悄落到第一项)。回滚即还原本 PR。
  • 先看:packages/spec/src/ui/app.zod.ts(checkAppLandingItems 与 AppLandingEntrySchema)、packages/spec/liveness/app.json(landing 标为 planned 并提示作者)、packages/lint/src/validate-expressions.ts(落地谓词的表达式检查)。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run src/ui/app-landing.test.ts,应见 14 个用例全绿,其中悬空 item 在 landing.0.item 处被拒、并列出存在的导航项 id。
  • 需要你做的:无,席位落地。

What changes

  • AppSchema.landing (packages/spec/src/ui/app.zod.ts): an optional, non-empty list of AppLandingEntrySchema entries, { item, visible }, both required. The console asks the entries in order and opens the first whose visible holds for the reader (and whose item the reader can see). No match, or no key: the app opens on its first navigation item by order, as today. The describe text states both rules.
  • The refusal the homePageId tombstone asked for. checkAppLandingItems, a superRefine on AppSchema, runs on every app parse (so defineApp, os validate / os build and the metadata save door all refuse alike). It refuses, at landing.N.item, an item that names no entry of the app's own navigation or areas[].navigation (any depth through children), listing the openable ids that exist; and an item that names a group, separator or action entry, none of which opens a page. It also refuses, at landing.N.visible, a predicate that reads data (a landing entry has no dataSource), with the same lexer the navigation entry's own data refusal uses. It is exported (and barrel-reachable) for the same reason as navItemVisibleDataNeedsDataSource: a mirror attaches the function instead of restating the rule.
  • Why an id into navigation, not an inline target (the order's either/or): the landing is a sidebar entry, so the sidebar can highlight what the reader landed on, and there is one copy of each navigation target. The id's old failure mode, dangling into a silent fallback, is closed by the parse-time refusal above, so the cross-reference is checked rather than trusted.
  • Why visible is required: an entry with no predicate would land every reader there, which is homePageId again under a new name. "Open somewhere else for everyone" stays "put that entry first in navigation".
  • Spellings: landings / landingPages route to landing; on an entry, navigationId / navItem / itemId / target / page route to item, and visibleWhen / visibleOn / when / condition to visible. The retired homePageId prescription (and its home / homepage / landingpage spellings) now also names landing, and the tombstone docblock's last paragraph says how landing answers both of its objections.
  • Lint (packages/lint/src/validate-expressions.ts): os validate / os build judge each landing predicate with the shared validator's data-independent verdicts (CEL syntax, size, unknown function, has() shape). It passes no object and no record scope, because the slot binds no record and a record-scoped call would prescribe record.FIELD.
  • Ledgers and tables the gates force: liveness app.landing row planned + authorWarn + authorHint (children item, visible); evaluated-slot census 32 → 33; dogfood expression-conformance row cel-app-landing (experimental, unevaluated); form-reconciliation omit row (planned, no form control); /meta/types app count 22 → 23; dropped-refinements.baseline.json (+1 site in six entries, the new object-level check, hand-edited per the ledger's rule); objectui census spec-object-refinements-7715.test.ts names checkAppLandingItems under notAttachable (test-only line, allowed by the claim amendment 6545506798), owed to the follow-up ui card.
  • Docs: content/docs/ui/apps.mdx gains "Landing per audience" (shape, order rule, refusals, a Planned callout) and a property-table row; references regenerated.
  • Changeset: .changeset/28022-app-landing-per-audience.md, @objectstack/spec and @objectstack/lint minor.

Why

An app shared by more than one audience has one landing: its first navigation entry. In HotCRM, moving Sales above Service moves service staff's landing to Leads (#27386 row 5). The maintainer ruled letter A: an app-level landing list with visible predicates, first match wins.

Tests

  • packages/spec/src/ui/app-landing.test.ts (new, 14 cases): accepted shapes (top-level, nested, area-only items; bare string normalised to the CEL envelope), the control (no key parses as before), refusals at the entry's own path (dangling item listing the existing ids, group / action / separator targets, a data read, a missing visible, an empty list, near-miss spellings), barrel identity, and both visible spellings through the exported function.
  • packages/lint/src/validate-expressions.test.ts: a non-CEL landing predicate is an error at app 'crm' · landing[0] ('nav_leads') visible; a healthy reader predicate is clean; the validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017 read-surface table gains appDef (AppSchema) and landingEntry (AppLandingEntrySchema), and stack gains apps.
  • Ablations (one-off, then restored; restore proven by an empty git diff HEAD): removing the .superRefine(checkAppLandingItems) mount → app-landing.test.ts 5 failed / 9 passed; removing the lint check(...) call → validate-expressions.test.ts -t landing 2 failed (the landing case and the read-surface row).

Verification

Local runs, head 1308512

Derived list (node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack), run at 4437a3b, each line with stdin from /dev/null. Reconciled: dispatch-gates --ran: 108 derived, 108 run, 0 NOT-MEASURED. Five lines first exited 3 or 1 on a missing prerequisite (an unfetched pinned commit; packages with no built declarations) and were rerun after git fetch of the pinned sha and a full pnpm build with declarations; the record below carries the rerun codes.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:api-surface :: exit 0
pnpm --filter @objectstack/spec run check:authorable-surface :: exit 0
pnpm --filter @objectstack/spec run check:browser-reachable-entries :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:dual-source-exports :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:entry-nameability :: exit 0
pnpm --filter @objectstack/spec run check:export-origins :: exit 0
pnpm --filter @objectstack/spec run check:exported-any :: exit 0
pnpm --filter @objectstack/spec run check:generated :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:llms-txt :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:skill-refs :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:durability-log-level :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-checklist :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:pm-prior-rulings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:spec-docblock-symbol-anchors :: exit 0
pnpm check:spec-parsed-alias :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

Then main was merged (8 commits touching the same generated ui artifacts), the artifacts were regenerated (check:generated --fix, then check:generated exit 0), and these were rerun at 1308512:

pnpm --filter @objectstack/spec exec vitest run src/ui src/shared src/system/metadata-form-zod-reconciliation.test.ts :: exit 0
pnpm --filter @objectstack/spec exec tsc --noEmit -p tsconfig.json :: exit 0
pnpm --filter @objectstack/lint exec vitest run src/validate-expressions.test.ts :: exit 0
pnpm --filter @objectstack/metadata-protocol exec vitest run src/protocol.meta-types-degenerate-derivation.test.ts :: exit 0
pnpm --dir ui exec vitest run packages/types/src/__tests__/spec-object-refinements-7715.test.ts :: exit 0
pnpm --filter @objectstack/dogfood exec vitest run test/expression-conformance :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0

Acceptance notes

  • How this meets the tombstone's "enforce-first" sentence. The maintainer ruled spec first. The liveness row is planned with authorWarn, so an author who writes landing is told no console applies it yet; the describe text opens with [PLANNED — not yet rendered]; the docs page carries a Planned callout. The tombstone's other objection, the silent dangling fallback, is closed now, at parse.
  • Not judged at the app save door: the predicate's CEL syntax. validateStackExpressions does not list app in its runtimeTypes, so the lint half runs at os validate / os build only. The parse refusals (dangling item, non-opening target, data read, blank / ast-only predicate) do run at the save door. Widening the rule's runtime types is a publish-gate change, left out of this card.
  • Not judged: which roots the predicate reads. A landing predicate reading record.x passes lint (no bare-identifier or root verdict on this slot). The console redirect card decides the evaluation scope; a root verdict belongs with it.
  • navigationContributions items cannot be named. The refusal reads the app as parsed, and a contributed item is not part of it. Stated in the describe text and the docs.
  • Follow-up Console: an app opens on the first landing entry whose visible predicate holds #28450: the domain:ui landing card: the console landing redirect reads landing, flips the liveness row to live, attaches checkAppLandingItems to objectui's AppComponentSchema and drops the notAttachable entry, and re-classifies the cel-app-landing conformance row.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes (repo-wide lint is CI's run).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

Generated by Claude Code

… name an app navigation entry

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibwXXtueDQZXqxv2FsL
…census owes it to the landing card

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibwXXtueDQZXqxv2FsL
@github-actions

github-actions Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/lint, @objectstack/spec, touching 13 documentable anchor(s). ⚠️ 8 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/authorable-surface/ui.json, packages/spec/declaration-map/ui.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

15 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via service_agent (literal, a string literal in AppLandingEntrySchema; a string literal in a comment in AppSchema))
  • content/docs/automation/queues-and-assignment-rules.mdx (via service_agent (literal, a string literal in AppLandingEntrySchema; a string literal in a comment in AppSchema))
  • content/docs/concepts/architecture.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/getting-started/quick-reference.mdx (via AppSchema (symbol, a top-level const))
  • content/docs/permissions/access-recipes.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/authentication.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/delegated-administration.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/index.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/permission-sets.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/permissions-matrix.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/permissions/positions.mdx (via sales_rep (literal, a string literal in a comment in AppSchema), service_agent (literal, a string literal in AppLandingEntrySchema; a string literal in a comment in AppSchema))
  • content/docs/permissions/profiles.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/protocol/objectql/security.mdx (via sales_rep (literal, a string literal in a comment in AppSchema))
  • content/docs/ui/apps.mdx (via AppLandingEntry (symbol, a top-level type), nav_leads (literal, a string literal in a comment in AppSchema), nav_service_console (literal, a string literal in a comment in AppSchema), sales_rep (literal, a string literal in a comment in AppSchema), service_agent (literal, a string literal in AppLandingEntrySchema; a string literal in a comment in AppSchema))
  • content/docs/ui/index.mdx (via nav_leads (literal, a string literal in a comment in AppSchema))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via AppSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-0.mdx (via AppSchema (symbol, a top-level const))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 8 changed file(s) yielded no anchor (packages/spec/api-surface/ui.json, packages/spec/authorable-surface/ui.json, packages/spec/declaration-map/ui.json, …) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 212 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 168 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json bede9deaed99f86111194c8955edd7fb3ad15fc2 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 813f5914466eb6468fb78515ff8655752ef95925 — the merge of head 010f9645a73044bc655376a5e193a6e62f5303c2 into base bede9deaed99f86111194c8955edd7fb3ad15fc2, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 813f5914466eb6468fb78515ff8655752ef95925 && git checkout 813f5914466eb6468fb78515ff8655752ef95925
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bede9deaed99f86111194c8955edd7fb3ad15fc2 010f9645a73044bc655376a5e193a6e62f5303c2 && git checkout -B drift-repro bede9deaed99f86111194c8955edd7fb3ad15fc2 && git merge --no-ff 010f9645a73044bc655376a5e193a6e62f5303c2

node scripts/docs-audit/affected-docs.mjs --json bede9deaed99f86111194c8955edd7fb3ad15fc2

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs bede9deaed99f86111194c8955edd7fb3ad15fc2 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 1308512cc0d6e8f9d4f4358f541f94ac028b0f74 · 2026-10-31T17:16Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract review of head 1308512cc0d6e8f9d4f4358f541f94ac028b0f74 is read and waiting on a green head · 2026-10-31T17:18Z

  • Test Core (2/8) is red on this head: packages/spec/scripts/dropped-refinements.test.ts › "the committed ledger › names at least one site per entry, and its header totals match its body" — AssertionError: expected 942 to be 948. The hand-edited packages/spec/dropped-refinements.baseline.json (the +1 site in six entries the PR body names) left its header total behind its body. The fix is the ledger's own rule in that file's header, not a contract matter.
  • The lane does not PASS a red head. The claim above stays; on the next head the lane re-claims and reviews the delta. The spec reading so far raises nothing against the surface (AppSchema.landing, AppLandingEntrySchema, checkAppLandingItems, the lint predicate check, the liveness/census rows).

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 5f376c41bea27949b20cd6de19e7439abcccc99b · 2026-10-31T17:24Z

Delta since the hold note: one commit, packages/spec/dropped-refinements.baseline.json header total 942 → 948 (the red Test Core 2/8 leg). Record follows once the new head's check-runs finish.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5f376c41bea27949b20cd6de19e7439abcccc99b
Local-runs: none
Scope: packages/spec/src/ui/app.zod.ts (AppLandingEntrySchema strict { item, visible } with spellings; checkAppLandingItems exported and mounted as AppSchema's superRefine; landing: z.array(…).min(1).optional() with landings / landingpages spellings; the homePageId tombstone text and docblock), packages/lint/src/validate-expressions.ts (landing predicates through the shared validator, no object, no record scope), the new app-landing.test.ts (14), lint tests, liveness app.landing row (planned, authorWarn, children item / visible), evaluated-slot census 32 → 33, dogfood cel-app-landing row, form-reconciliation omit row, /meta/types app count 22 → 23, dropped-refinements ledger (+1 site in six entries, header total 942 → 948), objectui census notAttachable: ['checkAppLandingItems'] (test-only line, claim amendment 6545506798), content/docs/ui/apps.mdx + regenerated references, changeset (@objectstack/spec + @objectstack/lint minor). 23 files, +595/−31. Fixes #28022 (ruling A 6545223246; claim 6545300407 + amendment; follow-up ui card #28450 open, pm:blocked). Not governed.
Inputs: card #28022 thread (ruling, claim, amendment), the PR body, the PR diff at the held head 1308512 (spec, lint, liveness, ledger, census and changeset hunks read in full; tests by case) plus the one-commit delta to this head (dropped-refinements.baseline.json header droppedRefinementSites 942 → 948, the fix for the red Test Core 2/8 leg named in the hold note 6546648889), git grep on origin/main (celReadsRootData L1017, authoredVisibleEnvelope L1046, navItemVisibleDataNeedsDataSource L1097 reused; a navigation entry's visible is the same EvaluatedExpressionInputSchema; AppSchema had no object-level superRefine before, so the ledger's new "" site under ui/App and the six …apps.element sites are the one new drop), check-runs (read 2026-10-31T17:58Z: all 65 latest-per-name check-runs completed, 58 success + 7 skipped, none failed, none running) — 65 checks. Nothing built or run here; the seat's numbers (108 derived gates, 14/14, two ablations) are the PR body's.

① Derived judgments

  • Letter A as ruled. A new app-level key, a list of { item, visible } entries, first match wins, no match or no key keeps today's rule (first navigation item by order). The ruling named the ripple — liveness row, lint of the predicate, docs — and the diff carries each, plus the gate-forced ledgers. B (reuse navigationContributions) was not taken and is not smuggled in: a contributed item cannot even be named.
  • The tombstone's two objections are answered in the shape, not in prose only. item is an id into the app's own navigation, but checkAppLandingItems runs on every app parse, so a dangling id or a group / separator / action target is refused at landing.N.item with the openable ids listed, instead of resolving to the first item silently. visible is required and an empty list is refused, so there is no "everyone lands here" under a new name. The data read refusal reuses the navigation entry's lexer at landing.N.visible.
  • Mounting is the only object-level refinement on AppSchema, so the dropped-refinements ledger gains exactly one site per place AppSchema is embedded in a published shape (six entries, including the bare "" under ui/App), and the header total moves by six — the delta commit. The isomorphism, census, /meta/types count and form-reconciliation rows move by the one new key each.
  • Lint half is scoped right. Landing predicates get the data-independent verdicts (syntax, size, unknown function, has() shape) with no object and no record scope, because the slot binds no record; objectWrite reads an empty list because apps are not an object body. The PR states what is not judged (the save door does not run the lint half; no root verdict on record.x) and leaves both to the console card — honest and acceptable.
  • Planned, declared loudly. Liveness planned + authorWarn + hint, [PLANNED — not yet rendered] first in the describe text, a Planned callout in the docs, the dogfood row experimental / unevaluated. An author who writes landing today is told nothing reads it. The flip to live is Console: an app opens on the first landing entry whose visible predicate holds #28450's, named in the row's note.
  • Declarations. Clause-②: yes (widening) — a new authorable key; Metadata-protocol: added; spec + lint minor. The ui census line is test-only and inside the amended claim; no ui/** source edit. Fixes #28022 is right: the spec half is the card; the redirect is Console: an app opens on the first landing entry whose visible predicate holds #28450.

② Rulings cited

  • 6545223246 (spec seat 2 recording the maintainer's 「同意」 on the 2026-10-31 list item 3, 2026-10-31T15:10Z): letter A. Implemented as ruled; no open ruling.

③ Narrowing / widening

  • Widening only: app.landing becomes authorable. Existing apps parse as before (control case pinned). New refusals apply to the new key alone.

④ Behaviour / flags

Implemented-by: claude/project-thread-9ddkwd
Reviewed-by: session_01L9qibw58ehQ2gRHP2h2Puz
VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D

维护者速读

  • 改了什么: 应用(AppSchema)新增可选键 landing:一组 { item, visible },按顺序取第一个 visible 条件成立的导航项作为落地页;都不成立时沿用现规则,即第一个导航项。指向不存在的导航项、分组/分隔线/动作项,或在条件里读数据,都会在解析时直接拒绝,不会像旧的 homePageId 那样悄悄退回第一项。
  • 为什么改: 你裁定 An app cannot give each audience its own landing page #28022 选 A。同一个应用给不同受众用时(如 HotCRM 的销售与服务),每类人要能落在自己的首页。
  • 风险与回滚: 只加一个可选键,不写它的应用解析和行为都不变。这次只落契约一半,活性台账标 planned,作者写了会收到“尚未生效”的提示。回滚即 revert。
  • 先看: packages/spec/src/ui/app.zod.ts(landing 和 checkAppLandingItems)。
  • 两分钟验证: 跑 PR 正文“验证”一节列的 spec 应用测试,预期全绿。
  • 需要你做的: 无。不属于受管面,契约复审已 PASS,由席位落地。界面那一半(控制台读取 landing 跳转)是 Console: an app opens on the first landing entry whose visible predicate holds #28450。
  • 席位意见: ACCEPT,可落地。

席位核对 · ACCEPT · domain:spec seat 2 (#18549) · 2026-10-31T18:00Z

Head 5f376c41bea27949b20cd6de19e7439abcccc99b. Card #28022, claim 6545300407 + amendment 6545506798 (one test-only notAttachable line in ui/packages/types/src/__tests__/spec-object-refinements-7715.test.ts); ruling 6545223246, letter A (maintainer 「同意」 2026-10-31T15:10Z).

  • Shape: draft to main, made by the relay; Fixes #28022 (the ruling splits the card: spec here, the console redirect on Console: an app opens on the first landing entry whose visible predicate holds #28450); Clause-②: yes (widening), Metadata-protocol: added at line start. Body's follow-up lines rewritten to #28450 by the seat (read back identical).
  • Contract review: PASS by the lane review thread, record 6547108962 on this head (delta after the ledger header fix 942 → 948 at 5f376c41), reviewer not the drafter. No must-fix. Its flags all ride Console: an app opens on the first landing entry whose visible predicate holds #28450.
  • Diff read by the seat: 23 files, +595/−31. The tombstone's objection to homePageId (a dangling id that silently fell back) is answered by checkAppLandingItems at parse, on every save door. No-match keeps today's rule. Liveness planned + author warning; the gate-forced rows are named in the body (evaluated-slot census 32 → 33, /meta/types 22 → 23, conformance row, omit row, dropped-refinements ledger).
  • Follow-ups filed: Console: an app opens on the first landing entry whose visible predicate holds #28450 (domain:ui: console redirect reads landing, attach checkAppLandingItems to AppComponentSchema and drop the notAttachable line, liveness → live, root verdict on the predicate). Skills pages (skills/objectstack-ui/rules/pages.md "an app opens on its first navigation item") stay true until the console reads the key, so they move with Console: an app opens on the first landing entry whose visible predicate holds #28450.
  • Gates: check-runs on this head 58 success, 7 skipped, 0 red. mergeable_state clean. check-governed-merges --branch: NOT governed, 23 paths, +595/−31.
  • Authority: maintainer standing go-ahead 「按 pm-dispatch 审过的pr你可以自主落地」 (2026-10-30T14:20Z, project thread).
  • Landing: pr_ready + automerge_enable in this act.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 31, 2026 18:01
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 31, 2026 18:01
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 31, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 31, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#2 · os-tesla · session_01L9qibx5r5nhDMwpRs4SL6D

Seat re-arm · 2026-10-31T18:46Z: the queue removed this PR (MERGE_CONFLICT, 2026-10-31T18:17Z). New head 010f9645 is a merge of main plus the regenerated ui generated artifacts (api-surface/ui, export-origins/ui, references/index.mdx, check:generated exit 0); the code diff ACCEPTed in 6547127321 is unchanged. automerge_enable again in this act; auto-merge waits for this head's checks.

@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 31, 2026 18:47
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 31, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract-review-claim: session_01L9qibw58ehQ2gRHP2h2Puz · head 010f9645a73044bc655376a5e193a6e62f5303c2 · 2026-10-31T19:15Z

Delta since PASS 6547108962 (head 5f376c4): merge of main plus regenerated ui artifacts, per the seat re-arm 6547646522. The lane reads the interdiff and records it.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz

Contract review (delta)

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 010f9645a73044bc655376a5e193a6e62f5303c2
Local-runs: none
Scope: the delta from the passed head 5f376c41 (PASS 6547108962): the merge of origin/main (85d33681, seven main commits: #28437 connector-mcp OAuth, #28439 report seriesBy, #28441 org-admin grant, #28442 forward refusal, #28447 publish-drafts request schema, #28454 sys_file columns) and one regeneration commit (010f9645: api-surface / export-origins ui, content/docs/references/index.mdx). Seat re-arm note 6547646522.
Inputs: git fetch of the head, git merge-tree vs origin/main (clean), git show --cc 85d33681 (zero conflict hunks), the interdiff of the PR-only diff at 5f376c41 against the PR-only diff at this head (the same 23 files; the only text changes are the published-schema count in references/index.mdx moving from 1574 to 1575 because main gained PublishPackageDraftsRequestSchema under #28447, and hunk offsets), check-runs (read 2026-10-31T19:17Z: all 65 latest-per-name check-runs completed, 58 success + 7 skipped, none failed, none running) — 65 checks. Nothing built or run here.

① Derived judgments

  • The code half ACCEPTed in 6547127321 and PASSed in 6547108962 is unchanged. app.zod.ts, validate-expressions.ts, the liveness row, the ledgers, the pins and the changeset carry the same hunks at this head; the regeneration commit is check:generated --fix output for a count main moved.
  • No incoming overlap. None of the seven merged commits touches packages/spec/src/ui/app.zod.ts, the lint pass or the app liveness file; the merge is textual and conflict-free.
  • Declarations unchanged (Clause-②: yes (widening), Metadata-protocol: added, spec + lint minor).

② Rulings cited

  • 6545223246 (letter A), as in the full record. No open ruling.

③ Narrowing / widening

  • As recorded on 5f376c41; nothing moves in the delta.

④ Behaviour / flags

  • Seat lands (re-armed; needs:contract-review was pulled on the earlier head and is absent now).

Implemented-by: claude/project-thread-9ddkwd
Reviewed-by: session_01L9qibw58ehQ2gRHP2h2Puz
VERDICT: PASS (delta)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

An app cannot give each audience its own landing page

1 participant