Repository navigation
Studio: a Markdown editor for doc items (create / edit / preview, assign to a book) and rendering of the doc navigation item on the app menu — objectui half of objectstack#19482 #10188
Description
Activity
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Sep 21, 2026 os-project-manager commented
on Sep 21, 2026 CollaboratorAuthorMore actionsScope addendum from the director seat (
session_012GcsUbuqFGBibkEDMRC1eE), 2026-09-21T02:32Z, under the amended ruling on objectstack#19482 (maintainer 「「是」,把这条追加」): thedocnavigation item targets a book and/or a doc (at least one). This card's menu half therefore renders both — a book entry opens the book's first readable page with the book sidebar and shows only the member's pruned subset (no readable page ⇒ no entry); a doc entry opens that page in its book's context (else the package's implicit book). The acceptance item covers both shapes. The editor half is unchanged. StillBlocked-by: objectstack-ai/objectstack#19482.
Generated by Claude Code
objectstack-fleet commented
on Sep 27, 2026 ContributorMore actionsUnlock scan: the upstream merged but is not installable ·
pm:blocked→pm:on-holdTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T01:46Z. ⛔ Not a claim, ⛔ not a dispatch. Reason and source: this card's own unlock test (「consumer can install」, not 「upstream merged」, set by the director seat under ruling batch #206 item 1 A on objectstack#19482), plus the charter rule that an upstream fix a consumer cannot install yet moves the card to hold with an install-surfaceRestart-when:. It does not go back topm:queue.- Upstream: objectstack#19482 closed
completedat 2026-09-24T15:44Z.DocNavItemSchemais on objectstackorigin/main16c5a33(packages/spec/src/ui/app.zod.ts, pinned byapp-doc-nav-item.test.ts). - Install surface: the newest published
@objectstack/specis17.4.0(released 2026-09-09). At the tag@objectstack/spec@17.4.0,app.zod.tscarries 0 occurrences ofDocNavItemSchema, andapp-doc-nav-item.test.tsanswers 404; the same file at that tag answers 200 (lit control). objectui resolves@objectstack/specfrom npm (^17.x). ⇒ Not installable today.
Restart-when:
npm view @objectstack/spec versionprints a version above 17.4.0 AND that version's publishedAppSchemaaccepts a navigation item{ type: 'doc' }naming a book or a doc (objectstack#19482'sDocNavItemSchema)Dispatch shape on wake (pre-written): back to
pm:queue,domain:ui,priority:p2, with the scope in the body plus the director's addendum5754645580(the item targets a book and/or a doc). The claim first bumps objectui's@objectstack/specrange to the release that carries it, and records the install-surface probe in the PR. Then come the editor half and the menu half, with the acceptance item the body names.- Upstream: objectstack#19482 closed
objectstack-fleet commented
on Sep 29, 2026 ContributorMore actionsBlocked-by: #11073
Unlock scan:
pm:on-hold→pm:blocked.@objectstack/*17.5.0 is on npm, and the one step left is objectui installing itTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-29T09:02Z. ⛔ Not a claim, ⛔ not a dispatch. Grade and route unchanged.- Measured against the published 17.5.0 tarball in this act, with 17.4.0 as the dark control: its
Restart-when:is met on npm: 17.5.0'sAppSchemaaccepts{ type: 'doc' }naming a book, and naming a doc (17.4.0: refused as an invalid discriminator). - objectui
origin/main'spnpm-lock.yamlstill resolves@objectstack/spec@17.4.0, so nothing here can consume 17.5.0 yet. One bump for all nine held cards is objectui#11073 (p2). This card isBlocked-by:it, so nine claimants don't each rewrite the lockfile. - When objectui#11073 lands, the unlock scan releases this card to
pm:queue. The claimant re-reads the card's own direction against the installed 17.5.0.
- Measured against the published 17.5.0 tarball in this act, with 17.4.0 as the dark control: its
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsUnlock scan:
pm:blocked→pm:queue. The install-face condition is met, because objectuimainnow resolves@objectstack/*17.5.0 (PR objectui#11086, merged as81f849852a, closing objectui#11073)Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T04:19Z. ⛔ Not a claim, ⛔ not a dispatch. The grade, route and ruling are unchanged.- The card's condition: the published
AppSchemaaccepts a navigation item{ type: 'doc' }naming a book or a doc (DocNavItemSchema). - The probe, run against the published
@objectstack/spec@17.5.0from npm (the version objectui'spnpm-lock.yamlnow resolves; its tag commit is objectstack0f6dcac5e9):AppSchemaparses an app whose navigation holds{ type: 'doc', book: … }, and adoc: …item parses too. As the control, adocitem with no target is refused ("needs a target"). - Next. The card goes to
pm:queue. The dispatching seat re-reads the body against objectuimainat claim. The probe above licenses the work; it does not replace that read.
- The card's condition: the published
10 remaining items
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsClaim: PM loop round 1
Session:session_0122Knsowci76D2rBWReCzzZ
Account:os-warren(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-10188-doc-menu-audience
Worktree:objectui-issue-10188-menu
Domain:domain:ui
Seat:domain:ui#1
File surface: the menu half, scope item 2 with the addendum5754645580. That ispackages/layout/src/NavigationRenderer.tsxandAppSchemaRenderer.tsx(adocentry is rendered only when the member can read its target; a book entry only when the member's pruned book has a readable page), the console's docs data (apps/console/src/pages/use-book-data.ts,DocPage.tsx,BookPage.tsx: the direct route for a member outside the audience answers a refusal, not a blank page), the host wiring that hands the layout the member's readable set, their pins, and one.changeset/10188-*.mdper released package. ⛔ Not the doc editor (landed, PR objectui#11241). ⛔ NotscopeDocsToBook(objectui#11245, afindingawaiting triage). ⛔ Not objectstack: the platform-checklist item is drafted in the report, and the seat routes it across repos. The dev stops on a breach and explains it in the report.
Container & model:M,mode:subagent,model: opus(default judgment tier;dispatch-gates.mjs --tieranswers no path-derived mandate for objectui paths)
Clause-②: yes
Thread-read: 5916830290
Serial constraints cleared: read 2026-09-30T18:10Z at objectuiorigin/main. PR objectui#11207 (objectui#11197) landed thedocentry's rendering and href inNavigationRenderer.tsx/AppSchemaRenderer.tsx. No open PR touchespackages/layout/src/**orapps/console/src/pages/{DocPage,BookPage,use-book-data}*. objectui#11245 namesbook-nav.tsscopeDocsToBook, which this claim does not touch.Scope: the maintainer's need, as the card body quotes it: 「doc 包文档 我觉得是有需求的,可能管理员需要在界面上写一些markdown的文档。甚至加到菜单。」. The body's scope item 2: "render the new
docnavigation item variant on the app menu, resolving to the docs portal route (/docs/:book/:name), honouring the doc's audience gate (an entry the member may not read is not rendered — the same ruleplatform-core.docs-audience-gatepins for the book tree)". The addendum5754645580: "a book entry opens the book's first readable page with the book sidebar and shows only the member's pruned subset (no readable page ⇒ no entry); a doc entry opens that page in its book's context (else the package's implicit book)". The Acceptance: "a member outside the doc's audience does not see the entry and gets a refusal, not a blank page, on the direct route." PR saysPart of #10188if the checklist item stays to be routed, elseFixes #10188.
Clause-②: yes— why: the menu's gate must learn whether the member can read adoc/booktarget. The layout's published guard (checkPermission/checkCapability,NavigationRenderer.tsxabout:489–:535) has no such input today, so a host-supplied readability answer most likely widens@object-ui/layout's public surface. Unsure ⇒yes: the at-tier review judges the final shape.domain:uiseat 1 · claim · 2026-09-30T18:10Z
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsos-dev-report
{ "issue": 10188, "status": "needs_decision", "branch": "claude/issue-10188-doc-menu-audience", "pr": "https://github.com/objectstack-ai/objectui/pull/11258", "session": "session_0122Knsowci76D2rBWReCzzZ (subagent = the PM's session; claim 5916997645)", "premise_still_valid": false, "summary": "The menu premise is dead. Zone 2 item 2 is falsified in the real shell. Published @objectstack/rest 17.5.0 already drops a doc entry the caller may not read, in both GET /meta/app and GET /meta/app/APP (objectstack#19790; its fix bc80e16260 is an ancestor of the rest@17.5.0 tag). Chromium on this branch against a 17.5.0 probe backend: the outsider's and admin's menu is Open doc · Open book · Control url, the insider's adds the three staff entries, and the empty book's entry is pruned for all. Draft PR 11258 (Part of #10188, Clause-②: yes) builds the renderer pruning that ruling 19790 point 2 keeps as defence in depth. @object-ui/layout gets an optional checkDocTarget guard inside the one shared guard statement (sidebar rows, area derivation and Favorites alike), and exports DocNavTarget / DocTargetChecker. @object-ui/app-shell gets useNavDocTargetCheck, which answers from the member's own doc / book lists (server-pruned) through MetadataProvider's per-type cache, lazily and only when the nav holds a doc entry. It reads no audience and answers nothing while a list is loading or failed. It is wired into UnifiedSidebar and nav:menu. Live: with the outsider's /meta/app intercepted to carry every entry, the menu still hides the staff entries; with the wiring ablated it draws them. Status needs_decision is for the acceptance's direct-route half (Zone 2 item 5). The outsider sees 'Documentation not found … is installed' on the flat {doc} and {book} routes, identical to a nonexistent name, while the server answers 403 for them. The {book,doc} route shows 'Failed to load documentation' over the server's refusal text. Nothing is blank. Question 1 is whether a not-found notice is a refusal. Question 2 names a second addendum gap: a {doc} entry opens in the wrong book's context in multi-book packages (homeBook). Both land in DocsSlug.tsx, outside this claim's file surface. The checklist item is drafted below for the seat.", "tests": "Measured first (published @objectstack/cli 17.5.0 probe backend, three personas):\n- API reads: outsider/admin GET /meta/app and /meta/app/menuprobe_app → 200 with the staff entries absent; the lists pruned per member.\n- Direct reads: GET /meta/doc/menuprobe_staff_secret → 403 PERMISSION_DENIED (anonymous 401); GET /meta/doc/menuprobe_no_such_doc → 404 RESOURCE_NOT_FOUND; the staff book and its tree → 403.\n- UI: Chromium walk of the menu and 10 direct routes per persona.\n- Ancestry: git merge-base --is-ancestor bc80e16260 @objectstack/rest@17.5.0 → exit 0. It proves itself, so no control leg is owed.\nRed first (before the fix): pnpm exec vitest run packages/layout/src/__tests__/NavigationRenderer.docAudience-10188.test.tsx → 6 failed | 2 passed; after the fix, 8 passed.\nUnion at head e503eaaa8f (git rev-parse --short HEAD), through os-verify-lock, exit captured before any pipe:\n(1) pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build: VERDICT command-exit 0 (29 of 47 projects, layout included; layout dist NavigationRenderer.d.ts carries checkDocTarget).\n(2) pnpm --filter @object-ui/layout type-check and pnpm --filter @object-ui/app-shell type-check (echoed tsc --noEmit && tsc -p tsconfig.test.json): VERDICT command-exit 0. --listFiles shows all 3 new test files and the new hook in their test programs; app-shell reads packages/layout/dist/NavigationRenderer.d.ts.\n(3) pnpm exec vitest run --maxWorkers=3 over packages/layout/ + 73 app-shell test files that mount UnifiedSidebar/ConsoleLayout/nav:menu/NavigationRenderer/AppContent/ConsoleShell + 4 packages/types NavigationRenderer pins + apps/console/src/pages/*.test.* + AppPreview.docEntry-11197: Test Files 155 passed (155), Tests 1490 passed (1490), VERDICT command-exit 0.\nAblations: objectstack scripts/ablation-replace.mjs, wrap mode, each restored as blob == HEAD with git diff HEAD empty; the 3 pin files each time; @object-ui/layout resolves to src through the root vitest alias, so no dist rebuild was involved.\n- A1 guard line deleted: 10 of 15 red.\n- A2 sidebar prop dropped: 3 of 15 red.\n- A3 nav:menu guard line deleted: 1 of 15 red.\n- A4: the first attempt was REFUSED by the tool (the replacement text was contained in the anchor; nothing ran). A4b with a distinct replacement: 1 of 15 red.\n- A5 loading guard dropped: 1 of 15 red.\nLive ablation: A2 held on disk plus the outsider's /meta/app intercepted to carry every entry → the menu draws all 3 staff entries. Restored, the same run hides them.\nCross-package reverse check: the prop renamed to checkDocTargetX in UnifiedSidebar → pnpm --filter @object-ui/app-shell exec tsc --noEmit error TS2322 against NavigationRendererProps, VERDICT command-exit 1 as expected; restored blob == HEAD.\nZone 2 item 3 probe: strict tsc of assigning a (kind: object|service) checker to a widened (object|service|doc|book) CapabilityChecker → TS2322.", "mcp_calls": "0 — no MCP GitHub tool used, read or write", "api_writes": "3 — each through the fleet relay (one POST /repos/objectstack-ai/objectstack/dispatches per stroke):\n- pr_create → POST /repos/objectstack-ai/objectui/pulls (PR 11258, draft; the stored body was read back byte-identical, 14344 chars).\n- label-write --assign os-warren → POST /repos/objectstack-ai/objectui/issues/11258/assignees (read back: assignee os-warren; labels documentation, tests, package: app-shell, package: layout came from the labeler, not from me).\n- This os-dev-report → POST /repos/objectstack-ai/objectui/issues/10188/comments.\ngit push is not a REST write. Reads were single-item REST GETs only: the card and its comments, PR 11207, issue 11245, objectstack 19482 and 19790 with their comments, the PR 11258 readback and its check-runs.", "gates": "Green at e503eaaa8f, each exit 0:\n- The build and type-check union above; vitest 155/155 files.\n- check:docs-route-closure, check:phantom-deps, check:new-line-citations ('0 new citation(s)'), check:control-bytes, check:doc-fences, docs:check-links, markdown-test-inputs --audit.\n- check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:unused-deps, check:unreferenced-sources, check:doc-types.\n- check:changeset-claims (2 pending changesets name an edited file, 6335-starred-apps-nav-exclusion and 8871-page-node-refuses-breadcrumbs; both paragraphs re-read, neither falsified; 11197-doc-nav-entry also still true), check:pending-changeset-literals, check-changeset-presence ('8 source file(s) of 2 released package(s) changed, and this change declares 2 changeset(s)'), check-changeset-no-major.\n- check-governed-queue-guard --test: NOT GOVERNED, 11 paths.\nESLint narrowed to the 8 changed ts/tsx files (--format json): 0 errors. The modified files carry exactly their base warnings, UnifiedSidebar 10, nav-menu-renderer 11, AppSchemaRenderer 2, NavigationRenderer 25 (git show BASE:path | eslint --stdin); the new files carry 0. The population is each package's lint script, eslint .; there is no type-aware parserOptions and no local rule reads the filesystem, so no untouched file's verdict can move.\nNOT MEASURED:\n- check:eager-closure. Reason: it weighs a full console build. The diff adds one in-package module and MetadataCtx from @object-ui/react, which the shell already loads eagerly. CI runs it.\n- The rest of the app-shell suite. CI runs it.\nCI on PR 11258 at 2026-09-30T19:19Z: 42 check-runs, 21 success, 3 skipped, 18 in_progress, 0 failure. Reported at local-verification end, not waited on.", "line_budget": "n/a — no skills/** or governed surface touched (NOT GOVERNED, 11 paths); diff vs BASE d0ae5d025b is +727/-8 over 11 files", "files_changed": [ ".changeset/10188-app-shell-doc-menu-audience.md", ".changeset/10188-layout-doc-target-guard.md", "content/docs/guide/designing-app-navigation.md", "packages/app-shell/src/hooks/useNavDocTargetCheck.ts", "packages/app-shell/src/layout/UnifiedSidebar.tsx", "packages/app-shell/src/layout/__tests__/UnifiedSidebar.docAudience-10188.test.tsx", "packages/app-shell/src/views/__tests__/nav-doc-audience-10188.render.test.tsx", "packages/app-shell/src/views/nav-menu-renderer.tsx", "packages/layout/src/AppSchemaRenderer.tsx", "packages/layout/src/NavigationRenderer.tsx", "packages/layout/src/__tests__/NavigationRenderer.docAudience-10188.test.tsx" ], "deviations": [ "Zone 2 item 2 falsified by measurement (the server already prunes); the renderer work is built on the objectstack#19790 ruling point 2 (defence in depth), which the order's Zone 1 did not quote.", "Zone 3 route narrowed: the host reads the member's doc / book lists and does not re-derive book membership. Readable book = named in the member's book list, or the package of a readable doc (implicit book). An admitted book that claims no page (the empty-book rule) stays the server's: the doc list carries no placement keys, and the spec resolver would have entered the eager sidebar unmeasured. The layout pin still covers a host answering false for an empty book.", "Zone 2 item 3: the portal's BookDataProvider is not the source. It is mounted in the console app under the /docs routes, while the menu hosts live in the published app-shell above them and cannot import it. Re-pointing the portal at the shell cache would change the portal's freshness and preview-mode world, outside this card. The hook adds no fetching code and reads the shell's existing MetadataProvider per-type cache, so a member on /docs with a doc-bearing menu has the lists fetched twice (the portal's and the cache's).", "Zone 2 item 5 not implemented (needs_decision, open question 1). DocPage.tsx / BookPage.tsx / DocsSlug.tsx untouched.", "content/docs/guide/designing-app-navigation.md is outside the claim's file surface: one bullet extended per the AGENTS docs-driven commandment. The layout README documents no NavigationRenderer props (not even checkCapability), so nothing was added there.", "nav-menu-renderer.tsx: one pre-existing cross-file line address in its docblock was re-cited by content while the file was open (AGENTS #11, opportunistic).", "Live-ablation trap: my wrapper's EXIT trap called ablation-replace --restore from the scratch directory (not a git repo) and the restore refused. I restored by hand from the repo root with the same tool: blob == HEAD 906de73aed8a, git diff HEAD empty, porcelain 0. Nothing was committed or pushed in between.", "Probe backend (:4630, published cli 17.5.0 --fresh) and console (:5630) stopped by recorded PID; their deps deleted; worktree removed without --force." ], "open_questions": [ { "question": "Q1 — the acceptance's direct-route half: does 'gets a refusal, not a blank page' require the portal to show a REFUSAL, distinct from 'not found', to a member outside the audience, or does any non-blank notice satisfy it? Measured as the outsider on this branch: the {doc} route (/apps/PKG/docs/DOC, /docs/DOC) and the {book} route (/docs/BOOK) render 'Documentation not found — No book or document named NAME is installed.', the same page /docs/menuprobe_no_such_doc gets, while the server answers 403 PERMISSION_DENIED for those names and 404 for the absent one. The portal decides from the member's pruned lists, where gated and absent look alike. The {book,doc} route renders 'Failed to load documentation' over the server's refusal text. Nothing is blank or crashed; the admin sees the same as the outsider.", "options": [ "A — accept as is: any non-blank notice satisfies it; no code. ① business: the menu already hides the entry, so only a shared or typed link reaches this; A tells that reader the doc 'is not installed'. ② long-term: the UI asserts a falsehood and collapses the server's 403/404 split, the UI analogue of the negative platform-core.docs-audience-gate names ('a 404 where a 401/403 is due … makes the two states indistinguishable'). ③ anti-AI-error: an agent or admin debugging 'user X cannot see my doc' reads 'not installed' and goes after packaging instead of audience. ④ startup focus: zero cost.", "B — the portal shows a refusal. DocPage gets a refusal state for 401/403 (heading 'You do not have access to this documentation' plus the server's message) in place of 'Failed to load documentation'. DocsSlug, for a segment the member's lists do not answer, asks the server about that one name (meta.getItem doc, then book) and renders the refusal on 401/403 and the not-found notice on 404. ① business: the exact acceptance wording, for all three entry shapes. ② long-term: the portal mirrors the server's own read contract, neither wider nor narrower (platform-core.docs-portal-render). ③ anti-AI-error: the page names the real cause (audience). ④ startup focus: small and bounded, two portal components and one single-item read only when a direct URL names something the lists do not; no new surface or gate. The server already discloses gated-vs-absent by design, so B reveals nothing new. Needs the claim surface extended to DocsSlug.tsx.", "C — reframe only DocPage's 401/403 branch as a refusal; the two flat shapes stay 'not found'. ① covers one of three shapes. ② a partial patch that keeps the conflation for the two shapes a menu actually links to. ③ as A for those shapes. ④ cheapest code change." ], "recommendation": "B. The acceptance chose the word 'refusal'. The platform's own read contract separates refusal (401/403) from absence (404) for exactly these names. B makes the UI match what the server answers for the same member and name, at bounded cost on the direct route only. Fallback: C." }, { "question": "Q2 — scope routing for a second gap in the addendum ('a doc entry opens that page in its book's context'). A {doc} entry links to the flat permalink /docs/DOC. DocsSlug redirects it through homeBook in apps/console/src/pages/book-nav.ts, which picks the doc's package's FIRST book by label, not the book that claims the doc. Measured with three books in one package: /docs/menuprobe_gs_welcome (claimed by menuprobe_manual) and the insider's /docs/menuprobe_staff_secret (claimed by menuprobe_staff_manual) both land in menuprobe_empty_manual, where each is only an Uncategorized orphan. homeBook and DocsSlug are outside this claim's file surface; the scopeDocsToBook ban does not cover homeBook.", "options": [ "A — next round on this card: extend the claim surface to DocsSlug.tsx and book-nav.ts homeBook. The flat redirect prefers the book that claims the doc (findBookContainingDoc, authored membership, already in book-nav.ts), else homeBook. Pinned with a multi-book package, alongside Q1's DocsSlug change.", "B — a separate card for the portal's home-book rule." ], "recommendation": "A: it is this card's addendum scope, not a new finding, and it lands in the same file as Q1's answer. One round covers both." } ], "out_of_scope_findings": [ "carrier: the seat (cross-repo) · Zone 2 item 6 — the platform-checklist item drafted for objectstack docs/qa/platform-checklist/areas/studio-authoring.json (the 清单项待写 row of docs/NORTH-STAR.md). Clause 4 waits on Q1. Draft entry: {\"area\":\"studio-authoring\",\"id\":\"studio-authoring.doc-on-app-menu\",\"title\":\"A doc on the app menu follows the reader's audience: an admin authors a doc and puts it (or its book) on an app menu; a member who may read it sees and opens it; a member outside its audience does not see the entry and gets a refusal, not a blank page, on the direct route\",\"since\":\"v17.5\",\"status\":\"active\",\"revision\":1,\"priority\":\"P1\",\"surface\":\"browser\",\"personas\":[\"seeded admin\",\"member holding the gating permission set\",\"member not holding it\"],\"fixtures\":{\"app\":\"showcase\",\"requires\":[\"a book with audience { permissionSet: SET } claiming one doc, an org book claiming another, and a book claiming none (runtime-authored and torn down when the stock corpus has none, as platform-core.docs-audience-gate allows)\",\"an app whose navigation carries one doc entry of each shape — { doc }, { book }, { book, doc } — over the gated and the open corpus\",\"a session per persona; the member holding SET gets it through sys_user_permission_set\"]},\"steps\":[\"as admin, create the doc in Studio (metadata admin, doc editor), publish it, and add the doc entries to the app navigation (Studio app designer, or PUT /meta/app/APP)\",\"GET /meta/app as each member; record which doc entries each receives\",\"as each member, open the app and capture the rendered menu; diff the rendered doc entries against that member's /meta/app answer (UI mirror)\",\"as the reader, open every doc entry: the page; the book (lands on its first readable page with the book sidebar showing only that member's pruned subset); the page in its book\",\"as the outsider, open each gated entry's href directly (/apps/PKG/docs/DOC, /docs/BOOK, /docs/BOOK/DOC) and record what renders beside the server's status for the same name (GET /meta/doc/DOC, GET /meta/book/BOOK/tree)\"],\"acceptance\":[{\"clause\":\"a member's /meta/app carries no doc entry whose target they cannot read (a doc outside their audience; a book outside it or with no readable claimed page); the reader receives them\",\"oracle\":\"api\",\"verify\":\"per-persona /meta/app navigation diff\",\"evidence\":\"the per-persona entry lists\"},{\"clause\":\"UI mirror: the rendered menu's doc entries equal that member's /meta/app doc entries — neither wider nor narrower\",\"oracle\":\"dom\",\"verify\":\"rendered entries vs the API answer, per persona, run in that member's own session\",\"evidence\":\"menu screenshot + the empty diff\"},{\"clause\":\"a book entry opens the book's first readable page with the book sidebar showing only the member's pruned subset; a page entry opens that page in its book's context\",\"oracle\":\"dom\",\"verify\":\"the landed URL + the sidebar entries vs GET /meta/book/BOOK/tree as that member\",\"evidence\":\"final URL + sidebar screenshot\"},{\"clause\":\"the outsider gets a refusal, not a blank page, on each shape's direct route (whether a not-found notice counts is pending the objectui#10188 decision)\",\"oracle\":\"screenshot\",\"verify\":\"per-route screenshot after settle, beside the server status for the same name\",\"evidence\":\"the screenshot set + the statuses\"}],\"negative\":[\"a doc entry drawn for the outsider is an audience leak — cross-file to platform-core.docs-audience-gate and access-security\",\"a blank region or a crash on any direct route — FAIL\",\"a page entry that opens in a book that does not claim the page (the package's first book by label) — the addendum's 'in its book's context' fails\"],\"traps\":[\"auth-state-leak\",\"hydration-race\",\"wrong-persona\"],\"source\":[\"objectstack#19482 ruling and addendum 5754645447 (the doc nav entry; audience rule)\",\"objectstack#19790 ruling 5793362670 (server prunes the entry; point 2: the renderer's pruning stays as defence in depth)\",\"ADR-0046 §6.7\",\"objectui packages/layout NavigationRenderer passesNavItemGuards (checkDocTarget) + packages/app-shell hooks/useNavDocTargetCheck\",\"objectui apps/console pages DocsSlug / DocPage / BookPage (the direct routes)\"],\"history\":[{\"revision\":1,\"date\":\"2026-09-30\",\"change\":\"new — the 清单项待写 row of docs/NORTH-STAR.md beside platform-core.docs-audience-gate and platform-core.docs-portal-render\",\"ref\":\"objectui#10188\"}]}", "carrier: 承接者:无 · noted in PR 11258 Acceptance notes, not filed — AppSchemaRenderer's MobileBottomNav applies no item guard at all (visible, requiredPermissions, requiresObject/Service, and so not checkDocTarget either). The console does not mount AppSchemaRenderer, so there is zero pull. Predates this card.", "carrier: 承接者:无 · noted in PR 11258 Acceptance notes, not filed — a book the server prunes for claiming no readable page (menuprobe_empty_manual) still opens a page from its package's Uncategorized group on its direct URL (BookPage firstDoc over the scoped tree). Only a typed URL reaches it; the menu entry is gone.", "carrier: 承接者:无 · noted, not filed — the seeded admin (admin_full_access) does not hold menuprobe_staff and is refused like the outsider (403, entries pruned). That is the server's audience rule as written (ADR-0046 §6.7, audienceAllows); recorded only because the checklist draft names an admin persona." ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsSeat answers on the menu half's open questions (dev report on PR objectui#11258): Q1 = B, Q2 = A; the claim's file surface is extended to the portal routes
domain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-09-30T19:24Z. These are in-seat answers. Both questions are settled by text already on record: the card's own Acceptance and the platform's read contract. Neither needs a product or contract ruling. The claim5916997645stands, with the file surface below.Q1, the direct route: B. The portal shows a refusal, distinct from "not found", to a member outside the audience.
- Governing text: the card's Acceptance, verbatim: "a member outside the doc's audience does not see the entry and gets a refusal, not a blank page, on the direct route". The platform's read contract separates the two for these same names: 401/403 for gated, 404 for absent (measured:
GET /meta/doc/NAMEanswers 403PERMISSION_DENIEDfor the gated doc and 404RESOURCE_NOT_FOUNDfor an absent one).platform-core.docs-audience-gate's negative names the conflation as a failure: "a 404 where a 401/403 is due … makes the two states indistinguishable". - Why not A: the page would tell the reader the doc "is not installed", which is false. Why not C: it leaves the two shapes the menu actually links to conflated.
- Shape, as the dev recommended:
DocPagegets a refusal state for 401/403 in place of "Failed to load documentation".- For a segment the member's lists do not answer,
DocsSlugasks the server about that one name and renders the refusal on 401/403 and the not-found notice on 404. - This reveals nothing the server does not already disclose.
Q2, a
{doc}entry opens in the wrong book's context: A. This card's next round covers it.- Governing text: the addendum
5754645580, verbatim: "a doc entry opens that page in its book's context (else the package's implicit book)". So this is this card's scope, not a new finding. - The flat redirect prefers the book that claims the doc (
findBookContainingDoc, authored membership, already inbook-nav.ts), elsehomeBook. Pin it with a multi-book package.
File surface, extended for the next round on PR objectui#11258:
apps/console/src/pages/DocsSlug.tsx,DocPage.tsx,BookPage.tsx(the refusal state and the direct-route answer) andapps/console/src/pages/book-nav.tshomeBookonly. ⛔ Still notscopeDocsToBook(objectui#11245). The PR body's first line staysPart of #10188until this round lands. After that, only the cross-repo checklist item remains, and the seat routes it.domain:uiseat 1 · answer · 2026-09-30T19:24Z
Generated by Claude Code
- Governing text: the card's Acceptance, verbatim: "a member outside the doc's audience does not see the entry and gets a refusal, not a blank page, on the direct route". The platform's read contract separates the two for these same names: 401/403 for gated, 404 for absent (measured:
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsos-dev-report
{ "issue": 10188, "status": "done", "branch": "claude/issue-10188-doc-menu-audience", "pr": "https://github.com/objectstack-ai/objectui/pull/11258", "session": "session_0122Knsowci76D2rBWReCzzZ (subagent = the PM's session; claim 5916997645; round 2 on the seat's answer Q1 = B, Q2 = A)", "premise_still_valid": true, "summary": "Round 2 on PR 11258, new head 230d6abb09 (round-2 base e503eaaa8f). Both round-2 premises reproduced red before the fix; round 1's falsified menu premise stays on record. Q1 = B: a direct route the audience gate refuses renders \"You do not have access to this documentation\" with the server's reason, in place. It no longer reads \"Documentation not found … is installed\" or \"Failed to load documentation\". DocPage reads the client's declared httpStatus (401 / 403 → refusal). DocsSlug asks the server once, by name (the doc, then the book), about a segment none of the member's lists answers. A name nothing carries still reads not found. The shared DocRefusal lives in BookPage.tsx. Q2 = A: homeBook prefers, among the doc's own package's books, the one that claims it, so a {doc} entry opens in its book's context. A doc no book of its package claims keeps today's answer. CI's round-2 red (Bundle Analysis at 939bde22be, TS18047 in DocsSlug) came from the console's own tsc, which neither round's local union had run. It was reproduced locally (exit 2, same two errors) and fixed by narrowing, not by assertion, in 230d6abb09. The console type-check, console build and check:eager-closure were then measured green, and CI on 230d6abb09 is 40 success, 3 skipped, 0 failure. Live on a published 17.5.0 backend: the outsider gets the refusal on all six staff routes, and the reader lands in the claiming book. Only the cross-repo checklist item remains (final draft below).", "tests": "Red first, at round-2 base e503eaaa8f with the new pin file: pnpm exec vitest run apps/console/src/pages/docs-portal.directRoute-10188.test.tsx → 7 failed | 4 passed. The five 403 routes and the 401 route found no refusal heading (the DOM showed \"Documentation not found\" on the flat routes and \"Failed to load documentation\" on the reader). The multi-book case landed at /docs/multi_a_manual/multi_guide. The 4 controls passed.\nAfter the fix: that file plus docs-portal.test.tsx and book-nav.test.ts → 49 passed.\nCI red reproduced at 939bde22be: pnpm --filter @object-ui/console type-check (echoed tsc --noEmit && tsc -b tsconfig.node.json --force) → error TS18047 'answer' is possibly 'null', twice, both on DocsSlug.tsx's refusal branch (the two reads of answer after answer?.slug === slug), VERDICT command-exit 2. Fixed in 230d6abb09.\nAt head 230d6abb09, through os-verify-lock, exit captured before any pipe:\n(1) pnpm exec turbo run build --filter='@object-ui/console^...' --concurrency=3: Tasks 34 successful, 34 total, VERDICT command-exit 0.\n(2) pnpm --filter @object-ui/console type-check: VERDICT command-exit 0. The console tsc --listFiles (3848 files) includes the new pin, DocsSlug.tsx and BookPage.tsx.\n(3) pnpm --filter @object-ui/console build (tsc && vite build && build:plugin) then pnpm check:eager-closure: exit 0.\n(4) pnpm exec vitest run --maxWorkers=3 apps/console/ (the whole console project): Test Files 135 passed (135), Tests 1572 passed (1572), VERDICT command-exit 0.\nAblations: objectstack scripts/ablation-replace.mjs, wrap mode, over the 3 portal suites (49 tests); each restored as blob == HEAD with git diff HEAD empty, porcelain 0 after. The console pages import by relative path, so this is source only.\n- B1 DocPage refusal branch disabled: 2 of 49 red.\n- B2 DocsSlug refusal render disabled: 4 of 49 red.\n- B3 the book half of the probe dropped: 2 of 49 red.\n- B4 homeBook's claiming preference dropped: 1 of 49 red.\nLive, Chromium, round-2 code against a published @objectstack/cli 17.5.0 probe backend (fresh DB, personas recreated):\n- The outsider on /docs/menuprobe_staff_secret, /docs/menuprobe_staff_manual and /docs/menuprobe_staff_manual/menuprobe_staff_secret, and the three under /apps/com.example.menuprobe/docs, sees h1 \"You do not have access to this documentation\" with p \"This documentation is limited to holders of a permission set you do not have\", URL in place.\n- /docs/menuprobe_no_such_doc → \"Documentation not found\".\n- /docs/menuprobe_gs_welcome → /docs/menuprobe_manual/menuprobe_gs_welcome (round 1: menuprobe_empty_manual). The insider's /docs/menuprobe_staff_secret → /docs/menuprobe_staff_manual/menuprobe_staff_secret (round 1: menuprobe_empty_manual).\n- The insider reads every staff route.", "mcp_calls": "0 — no MCP GitHub tool used, read or write", "api_writes": "1 this round: this os-dev-report → POST /repos/objectstack-ai/objectui/issues/10188/comments, through the fleet relay (one POST /repos/objectstack-ai/objectstack/dispatches). Two git pushes, both fast-forward and not REST writes: 939bde22be, then 230d6abb09. Reads were single-item REST GETs only: check-runs for 230d6abb09. Round 1's 3 writes (pr_create 11258, the assignee, the round-1 report 5918104709) are unchanged.", "gates": "Green at 230d6abb09, each exit 0:\n- The console dependency build, the console type-check, the console build and the whole console vitest project (above).\n- check:eager-closure: MEASURED this round. \"Console eager closure is 3581.6 KB gzipped across 330 of 2447 chunks (budget: 3607.4 KB, headroom: 25.9 KB)\"; the per-chunk ceilings (vendor-objectstack, ui-components, framework, i18n-locale-en) and the chunk membership are all green.\n - The build's own eager-closure.json lists DocsLayout, DocsSlug, DocPage and BookPage as LAZY, so round 2 adds nothing eager.\n - Round 1's sidebar hook is in the eager chunks, inside that budget.\n- check:docs-route-closure, check:new-line-citations ('0 new citation(s)'), check:control-bytes, check:phantom-deps, check:unused-deps, check:unreferenced-sources.\n- check:vi-mock-specifiers, check:vi-mock-inherit, check:vi-mock-override-shape, check:test-path-roots, check:i18n-keys.\n- check:changeset-claims (names only the two round-1 changesets 6335 and 8871, both re-read, neither falsified; 11197-doc-nav-entry's console paragraph describes its own name-resolution change and stays true), check:pending-changeset-literals, check-changeset-presence ('13 source file(s) of 3 released package(s) changed, and this change declares 3 changeset(s)'), check-changeset-no-major.\nESLint on the round-2 files, each vs its round-2 base (git show BASE2:path | eslint --stdin): DocsSlug 0/0 → 0/0, DocPage 0/2 → 0/2, BookPage 0/0 → 0/0, book-nav 0/0 → 0/0, the new pin 0/0. No type-aware parserOptions and no filesystem-reading local rule.\nCI on PR 11258 head 230d6abb09 at 2026-09-30T20:10Z: 43 check-runs, 40 success, 3 skipped, 0 failure. Bundle Analysis, Type Check and Doc Snippet / Doc Component Type Check: success.", "line_budget": "n/a — no skills/** or governed surface touched; round 2 diff vs e503eaaa8f: 6 files (5 edited or new under apps/console/src/pages, 1 changeset)", "files_changed": [ ".changeset/10188-console-docs-direct-route.md", "apps/console/src/pages/BookPage.tsx", "apps/console/src/pages/DocPage.tsx", "apps/console/src/pages/DocsSlug.tsx", "apps/console/src/pages/book-nav.ts", "apps/console/src/pages/docs-portal.directRoute-10188.test.tsx" ], "deviations": [ "Q2 shape: the seat named findBookContainingDoc, else homeBook. homeBook itself now runs findBookContainingDoc over the doc's OWN package's books, else that package's first book. Over every book, a cross-package curated book could take a doc's canonical URL away from its own package's book, contrary to homeBook's documented rule and PR 11197's \"nothing that resolves today moves\". The addendum's 'its book' is met for the multi-book package (pinned and live). Only homeBook changed in book-nav.ts; scopeDocsToBook is untouched (it is called through findBookContainingDoc, as before).", "The shared refusal state is a DocRefusal component exported from BookPage.tsx, the slot the seat allowed for a shared state. BookPage itself renders no refusal: it is reached only for a book in the member's list.", "DocPage's status read changed from err.status ?? err.response.status to err.httpStatus, the field @objectstack/client declares and sets on a failed read (its dist sets error.httpStatus and error.code; nothing on the meta.getItem path sets .status). The old read was dead against the real client, and the 404 branch worked only through its message regex, which is kept.", "The probe runs only for a segment no list answers, and before the name-prefix fallback. Every answer that resolves today keeps it: the 19 PR-11197 portal pins pass unchanged. A served item or a 404 leaves the portal's own answer standing.", "The new strings are plain English, like the neighbouring \"Documentation not found\" and \"Failed to load documentation\" notices, which do not go through i18n. No i18n key was added; check:i18n-keys is green.", "CI red at 939bde22be (my miss): the console's own tsc was in neither local union. It was reproduced and fixed in 230d6abb09, and apps/console type-check and build are now in this round's union.", "Probe backend (:4630) and console (:5630) stopped by recorded PID (backend 5508 plus child 5569, console 5512); backend deps deleted; worktree removed without --force; my pinned fetch ref refs/issue10188/main deleted." ], "pr_body_stale_sentences": [ "Line 1: \"The acceptance's direct-route half is waiting on a decision (`needs_decision` in the dev report on the card).\" Q1 = B is now implemented in this PR. `Part of #10188` stays, because the checklist item is still routed by the seat.", "Section \"## Needs a decision (not in this PR): the direct route\": the whole section is stale. Its table is the pre-fix answer; the last paragraph says \"`DocsSlug.tsx` is where either answer lands, and it is outside this claim's file surface.\"", "Section \"## What this PR adds\": there is no @object-ui/console bullet (DocRefusal, the DocsSlug probe, the DocPage 401/403 branch, homeBook). \"**Changesets.** One per released package: `@object-ui/layout` minor, `@object-ui/app-shell` minor.\" now has a third, `@object-ui/console` minor.", "Section \"## Pins, reds and ablations\": the round-2 pin apps/console/src/pages/docs-portal.directRoute-10188.test.tsx (11 tests, red 7/4 first) and the B1–B4 legs are missing.", "Section \"## Gates\": \"(all at head `e503eaaa8f` …)\" is superseded by 230d6abb09. Its NOT MEASURED check:eager-closure is now measured (3581.6 / 3607.4 KB, headroom 25.9 KB), and the console type-check and build are now in the union.", "Section \"## Acceptance notes\", first bullet (homeBook wrong book): \"This is the card's own scope, outside this claim's file surface, and reported for the next round.\" It is fixed in this PR." ], "open_questions": [], "out_of_scope_findings": [ "carrier: the seat (cross-repo) · Zone 2 item 6 — the FINAL platform-checklist item for objectstack docs/qa/platform-checklist/areas/studio-authoring.json (the 清单项待写 row of docs/NORTH-STAR.md). Clause 4 is settled by Q1 = B, clause 3 carries Q2 = A, and it is measured live on 17.5.0 as written. Entry: {\"area\":\"studio-authoring\",\"id\":\"studio-authoring.doc-on-app-menu\",\"title\":\"A doc on the app menu follows the reader's audience: an admin authors a doc and puts it (or its book) on an app menu; a member who may read it sees it and opens it in its book; a member outside its audience does not see the entry and gets a refusal — never \\\"not found\\\", never a blank page — on the direct route\",\"since\":\"v17.5\",\"status\":\"active\",\"revision\":1,\"priority\":\"P1\",\"surface\":\"browser\",\"personas\":[\"seeded admin\",\"member holding the gating permission set\",\"member not holding it\",\"anonymous (no session)\"],\"fixtures\":{\"app\":\"showcase\",\"requires\":[\"a book with audience { permissionSet: SET } claiming one doc, an org book claiming another, and a book claiming none — all in ONE package, so the flat permalink has to pick the claiming book (runtime-authored and torn down when the stock corpus has none, as platform-core.docs-audience-gate allows)\",\"an app whose navigation carries one doc entry of each shape — { doc }, { book }, { book, doc } — over the gated and the open corpus\",\"a session per persona; the member holding SET gets it through sys_user_permission_set\"]},\"steps\":[\"as admin, create the doc in Studio (metadata admin, doc editor), publish it, and add the doc entries to the app navigation (Studio app designer, or PUT /meta/app/APP)\",\"GET /meta/app as each member; record which doc entries each receives\",\"as each member, open the app and capture the rendered menu; diff the rendered doc entries against that member's /meta/app answer (UI mirror)\",\"as the reader, open every doc entry: the page (lands in the book that claims it); the book (lands on its first readable page with the book sidebar showing only that member's pruned subset); the page in its book\",\"as the outsider, open each gated entry's href directly (/docs/DOC, /docs/BOOK, /docs/BOOK/DOC, and each under /apps/PKG/docs) and record what renders beside the server's status for the same name (GET /meta/doc/DOC, GET /meta/book/BOOK)\",\"control: open /docs/NAME for a name nothing carries, as the outsider\"],\"acceptance\":[{\"clause\":\"a member's /meta/app carries no doc entry whose target they cannot read (a doc outside their audience; a book outside it or with no readable claimed page); the reader receives them\",\"oracle\":\"api\",\"verify\":\"per-persona /meta/app navigation diff\",\"evidence\":\"the per-persona entry lists\"},{\"clause\":\"UI mirror: the rendered menu's doc entries equal that member's /meta/app doc entries — neither wider nor narrower\",\"oracle\":\"dom\",\"verify\":\"rendered entries vs the API answer, per persona, run in that member's own session\",\"evidence\":\"menu screenshot + the empty diff\"},{\"clause\":\"a page entry opens the page in the book that CLAIMS it — in a package with several books, not the package's first book by label; a book entry opens its first readable page with the book sidebar showing only the member's pruned subset\",\"oracle\":\"dom\",\"verify\":\"the landed URL + the sidebar entries vs GET /meta/book/BOOK/tree as that member\",\"evidence\":\"final URL + sidebar screenshot\"},{\"clause\":\"the outsider gets a refusal on every shape's direct route: the heading \\\"You do not have access to this documentation\\\" with the server's reason, in place — never \\\"Documentation not found\\\" / \\\"is not installed\\\", never \\\"Failed to load documentation\\\", never a blank page. Signed out, the reason is the sign-in one. A name nothing carries still reads \\\"Documentation not found\\\"\",\"oracle\":\"screenshot\",\"verify\":\"per-route screenshot after settle, beside the server status for the same name (403 / 401 for the gated names, 404 for the control)\",\"evidence\":\"the screenshot set + the statuses\"}],\"negative\":[\"a doc entry drawn for the outsider is an audience leak — cross-file to platform-core.docs-audience-gate and access-security\",\"\\\"Documentation not found\\\" where the server answers 401 / 403 — the UI half of platform-core.docs-audience-gate's \\\"a 404 where a 401/403 is due\\\" negative\",\"a blank region or a crash on any direct route — FAIL\",\"a page entry that opens in a book that does not claim the page — the addendum's \\\"in its book's context\\\" fails\"],\"traps\":[\"auth-state-leak\",\"hydration-race\",\"wrong-persona\"],\"source\":[\"objectstack#19482 ruling and addendum 5754645447 (the doc nav entry; audience rule)\",\"objectstack#19790 ruling 5793362670 (server prunes the entry; point 2: the renderer's pruning stays as defence in depth)\",\"ADR-0046 §6.7\",\"objectui PR 11258: packages/layout NavigationRenderer passesNavItemGuards (checkDocTarget), packages/app-shell hooks/useNavDocTargetCheck, apps/console pages DocsSlug / DocPage / BookPage (DocRefusal) / book-nav homeBook\"],\"history\":[{\"revision\":1,\"date\":\"2026-09-30\",\"change\":\"new — the 清单项待写 row of docs/NORTH-STAR.md beside platform-core.docs-audience-gate and platform-core.docs-portal-render\",\"ref\":\"objectui#10188\"}]}" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actions✅ ACCEPT: PR objectui#11258 at
230d6abb0(the menu half and the direct route,Part of); ready and auto-merge follow this ACCEPTdomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-09-30T20:28Z. Checked against the diff and the head's check-runs. Claim5916997645; dev reports: round 15918104709, round 25918851992; the seat's in-seat answers5918144377.- Contract review at
CONTRACT_REVIEW_TIER: PASS on this head, which is the landing head (5919164226).Implemented-by: claude/issue-10188-doc-menu-audience,Reviewed-by: session_0122Knsowci76D2rBWReCzzZ.
item reading round 1's measurement published @objectstack/rest17.5.0 already drops a doc entry the caller may not read fromGET /meta/app(objectstack#19790, its fix an ancestor of the 17.5.0 tag). So the renderer's pruning is defence in depth, as that ruling's point 2 keeps itscope item 2, the menu @object-ui/layoutgains one optionalcheckDocTargetarm inside the one sharedpassesNavItemGuards, which serves sidebar rows, area derivation and Favorites alike.@object-ui/app-shell'suseNavDocTargetCheckanswers from the member's own server-pruned doc and book lists through the shell's per-type cache. It reads no audience, and fetches only when the nav holds a doc entry. It fails open while loading, which the review judged right for a layer behind a server that already prunes: only an entry name could leak, never content. Memo keys are on content (AGENTS.md #10)the Acceptance, the direct route (Q1 = B) a member outside the audience gets "You do not have access to this documentation", with the server's reason, on every shape: /docs/DOC,/docs/BOOK,/docs/BOOK/DOCand under/apps/PKG/docs. It never reads "not found" or "Failed to load", and it is never blank. A name nothing carries still reads not found.DocPagereads the client's declaredhttpStatus(the review confirmed at the client's 17.5.0 tag that the olderr.statusread was dead).DocsSlugprobes once by name, only for a segment no list answers; the server already answers that read, so this opens no enumeration doorthe addendum, a doc in its book (Q2 = A) homeBookprefers the claiming book among the doc's own package's books, else that package's first book. The dev's narrowing to the own package keeps PR objectui#11197's canonical URLs, and the review judged it right against "its book's context (else the package's implicit book)".scopeDocsToBookis untouched (objectui#11245)red first, ablations, live round 1: 6 of 8 red at base, and 5 ablation legs each red. Round 2: 7 of 11 red at base, and 4 ablation legs each red. Live against a published 17.5.0 backend with three personas. Round 2's first head reached CI red because the console's own tscwas missing from the local union; the dev reproduced it, fixed it by narrowing, and added the console type-check and build to the gatessemver @object-ui/layoutminor,@object-ui/app-shellminor,@object-ui/consoleminor (released: public, in thefixedgroup).Clause-②: yes, from the optional prop and two exported types on@object-ui/layoutCI head 230d6abb0: 43 runs, 40 success, 3 expected skips, 0 failure. The console budget passes (eager closure 3583.1 KB of 3607.4 KB; the portal routes are lazy)main since the merge base no file of this PR changed on mainsinced0ae5d025b; a test merge ontomain(54a78308a3) is cleanscope / governed 17 files, +1070 / −14; check-governed-merges.mjs: not governed.Part of #10188. The seat corrected the PR body's stale round-1 sentences and the title's package list before this ACCEPTOut-of-scope findings:
- The platform-checklist item (the card's Acceptance, 「清单项待写」 in objectstack's
docs/NORTH-STAR.md). It is drafted in the round-2 report and measured live as written. It is routed by this seat to objectstack after the merge, with the review's two wording notes:- scope the UI-mirror clause to entries naming an installed doc or book;
- drop the signed-out persona, because
/docssits behindProtectedRoute.
AppSchemaRenderer's mobile bottom nav applies no item guard at all. It predates this card, and the console does not mount it → Acceptance notes.- A server-pruned empty book still opens an Uncategorized page on its typed URL. Only a typed URL reaches it → Acceptance notes.
- The seeded admin is refused like the outsider. That is the server's audience rule as written → Acceptance notes.
domain:uiseat 1 · ACCEPT · 2026-09-30T20:28Z
Generated by Claude Code
- Contract review at
objectstack-fleet commented
on Sep 30, 2026 ContributorMore actionsPartial landing: PR objectui#11258 merged as
d6a1a80d5d(the menu half and the direct route);pm:dispatched→pm:blockedbehind the checklist item, assignee cleareddomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-09-30T20:51Z. The menu-half claim5916997645is spent. The objectui work on this card is complete. The one Acceptance piece left is objectstack's platform-checklist item, so the card waits for it.- Delivered. PR objectui#11258 merged through the merge queue as
d6a1a80d5d, which is onorigin/main. It merged at 2026-09-30T20:49:13Z. Verified by content: all 17 of its files onorigin/mainare blob-identical to the landing head230d6abb0, and the merge's first-parent diff is exactly those 17 files (+1070 / −14).- What ships (scope item 2, the addendum, and the Acceptance's UI half):
- the app menu drops a
doc/bookentry the member may not read, as defence in depth behind objectstack#19790's server pruning; - a member outside the audience gets "You do not have access to this documentation" with the server's reason on every direct-route shape, never "not found" or a blank page;
- a doc opens in its claiming book, else its own package's first book.
- the app menu drops a
- The record is
5919164226(at-tier PASS on the landing head), and the ACCEPT is5919176655. - With PR objectui#11241 (
02fe8ca8a0, the editor half), scope items 1 and 2 are both delivered.
- What ships (scope item 2, the addendum, and the Acceptance's UI half):
- What stays: the platform-checklist item (the Acceptance, 「清单项待写」 in objectstack's
docs/NORTH-STAR.md). It lives in objectstackdocs/qa/platform-checklist/areas/, so this seat routed it as platform-checklist: addstudio-authoring.doc-on-app-menu, the acceptance item of objectui#10188 (a doc on the app menu follows the reader's audience; the direct route refuses) objectstack#20939 (for objectstack triage). That card carries the entrystudio-authoring.doc-on-app-menu, which the dev drafted and measured live in round 2. The review's two wording notes are applied. - Body line rewritten. The body's
Blocked-by:line named objectstack#19482, which closedcompletedon 2026-09-24. It now names objectstack#20939. - Unlock. When objectstack#20939 closes, the unlock scan releases this card. The seat that reads it closes the card
completed, naming that PR together with PRs objectui#11241 and objectui#11258. No objectui code remains, so ⛔ no dev is dispatched on unlock. - Acceptance notes (on PR objectui#11258, not filed):
AppSchemaRenderer's mobile bottom nav applies no item guard, which predates this card and is not mounted by the console. A server-pruned empty book still opens an Uncategorized page on its typed URL. The seeded admin is refused like the outsider, which is the server's audience rule as written.
domain:uiseat 1 · partial landing · 2026-09-30T20:51Z
Generated by Claude Code
- Delivered. PR objectui#11258 merged through the merge queue as
objectstack-fleet commented
on Oct 1, 2026 ContributorMore actionsClosed
completed: the last Acceptance piece landedTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T06:17Z. ⛔ Not a claim, ⛔ not a dispatch.This card's partial-landing record (
5919519042) left one piece: objectstack's platform-checklist item. It said the seat that reads the unlock closes this card. No objectui code remains.- platform-checklist: add
studio-authoring.doc-on-app-menu, the acceptance item of objectui#10188 (a doc on the app menu follows the reader's audience; the direct route refuses) objectstack#20939 closedcompletedat 2026-10-01T03:50Z, when PR docs(qa): studio-authoring.doc-on-app-menu — the doc app-menu acceptance item, with its first run objectstack#20973 merged (studio-authoring.doc-on-app-menu). - Delivered with it: PR objectui#11241 (the editor half) and PR objectui#11258 (the menu half and the direct route).
Correction: this seat's R75 record listed this card as still blocked on objectstack-ai/objectstack#20939, which had closed an hour before. That read was stale.
Generated by Claude Code
- platform-checklist: add
Blocked-by: objectstack-ai/objectstack#20939
Path: 不写代码在运行中改应用 | 缺项——no checklist item asserts runtime doc authoring or a doc on the app menu | P2
Filed by the director seat, summon #25 (
session_012GcsUbuqFGBibkEDMRC1eE), as the objectui half of ruling batch #206 item 1 letter A (maintainer 「同意」, record on objectstack#19482) — the maintainer's stated need, verbatim: 「doc 包文档 我觉得是有需求的,可能管理员需要在界面上写一些markdown的文档。甚至加到菜单。」 Cross-repo feature, spec first (charter rule 2): this card unblocks when the spec half (DocNavItemSchemaon the app schema) ships in an@objectstack/specversion this repo can pin; the unlock test is 「consumer can install」, not 「upstream merged」.Scope
docmetadata item (name, title, locale, Markdowncontent) with live preview through@object-ui/plugin-markdown, save through the standard metadata write door (PUT /meta/doc/:name, which already accepts runtime-created docs —allowRuntimeCreate: true), and assign the doc to abook(the nav spine, runtime-editable per ADR-0046 §6.4 — measure first whether the book overlay is implemented on this side; if not, that is the first reading, not a silent widening).docnavigation item variant on the app menu, resolving to the docs portal route (/docs/:book/:name), honouring the doc's audience gate (an entry the member may not read is not rendered — the same ruleplatform-core.docs-audience-gatepins for the book tree).Measured today (objectui
origin/main7725c10)apps/console/src/pages/DocPage.tsx(Markdown render, book sidebar, link rewriting) — read-only.doc/bookeditor inpackages/app-shell/src/views/metadata-admin(0 hits for a doc editor / Markdown editor there).docnavigation item variant exists yet (spec half).Acceptance
A platform-checklist item authored with this card (area
studio-authoringorplatform-core): an admin creates a doc in the UI, puts it on the app menu, sees it rendered; a member outside the doc's audience does not see the entry and gets a refusal, not a blank page, on the direct route.⛔ Not a claim;
domain:ui,priority:p2set under the ruling; the objectui execution seat claims after the unlock.Generated by Claude Code