Repository navigation
[finding] an app author's draft baseline is read through the pruned plain read (GET /meta/app/:name?state=draft), and the designers merge it over the whole stored app, so a draft save drops the navigation entries withheld from that author #20290
Description
Ruled: 5861438413 · letter A · 2026-09-28T00:56Z
Filing gate: ① a defect with a named landing site. The server half is packages/rest/src/rest-server.ts, the plain read's ?state=draft branch. The client half is objectui's two app editors. Finding class (a), with reach: measured at a public door: an authoring save silently loses data.
Found by the os-dev round on #20156 (PR #20284, the ruled author exemption on the stored-version doors). The server half was confirmed at source by that PR's at-tier contract review (record 5858783654). Filed by the domain:cli execution seat (#6024, session_01UYBdGBzWSrAMzpW8ah3GbP). ⛔ Filed bare: routing, grading and the choice of carrier are triage's. ⛔ Not a claim.
What happens
.objectui-sha f8a9d0fb. The seat did not re-read objectui.
- Producers (objectui):
- app-shell
StudioDesignSurface.tsxbuilds its app baseline as{ ...layered.effective, ...getDraft }(about :1760-:1767) and saves navigation withclient.save('app', …, { mode: 'draft' })(about :1960); ResourceEditPage.tsx, the generic editor (no bespoke app edit page is registered), merges the same way (about :1016-:1056) and saves at about :1500.
- app-shell
- The door:
GET /meta/app/:name?state=draft, the plain read.- It sets
state: 'draft'ongetMetaItem(rest-server.tsabout :6988-:7002). - It then runs
metaItemReadGate(..., { arms: 'all', app: 'gate' })and servesverdict.document(about :7060-:7068). - It attaches no author exemption, so it prunes the navigation entries the caller's
requiredPermissionsor documentation audience withhold, authors included.
- It sets
- Measured by the dev on PR fix(rest): the stored-version doors serve an app whole to whoever may save it, and pruned to everyone else #20284's head
9d955a36: an author holdingmanage_metadatabut notfinance.accessread?state=draftasnavigation: [nav_leads], while/layersserved[nav_leads, nav_finance_ledger]. - Result: when an app has a pending draft, the draft's PRUNED navigation overrides the author's whole effective navigation in the editor. The next draft save writes the pruned list back, and the withheld entries are deleted.
- Not caused by PR fix(rest): the stored-version doors serve an app whole to whoever may save it, and pruned to everyone else #20284. On
main, the plain read already prunes?state=draftfor every caller. PR fix(rest): the stored-version doors serve an app whole to whoever may save it, and pruned to everyone else #20284 makes/layerswhole for authors (ruling B), which is what exposes the gap between the two reads.
The contract it contradicts
Ruling 5856774816 on #20156 (letter B), in its rationale: 「whoever can save it must see it whole, or a save drops entries silently」. The same ruling: 「Read-to-display is pruned per user; read-to-edit is whole for the editor.」 The designers' draft read is a read-to-edit, but it goes through a read-to-display door.
Candidate carriers (for triage, ⛔ not presumed here)
- Server: the plain read's
?state=draftbranch honours the author exemption, since a draft is a stored version, not a rendered one. Or the draft is served on a stored-version door (/layerswith a draft layer). - Client: objectui reads its edit baseline, draft included, through a stored-version door rather than the plain read.
If choosing between them changes what a non-author may read of a draft, that is a question for the decision box, not for a dev. Ruling B covers the three stored-version doors only.
Who acts
Triage decides the landing repository and lane. The server half is domain:cli (packages/rest); the client half is repo:objectui.
Dedupe
MCP issue search, run 2026-09-27:
- objectstack, 「app draft state=draft read pruned for author, Studio designer merges draft over layered effective and draft save drops withheld navigation entries」 → 15 hits, none this defect. The top 8 are closed
publishPackageDraftsand draft-scope cards (SqlDriver org auto-scope makes SysMetadataRepository.listDrafts blind to env-wide drafts (organization_id IS NULL ∧ injected org = always empty); mode=draft saves drop package_id #11087, publishPackageDrafts writes no audit rows — Studio's "publish whole app" leaves the audit trail empty #8400, A draft row stored under a manifest-absent plural (fields,seeds,external_catalogs,translations) is promotable by publishPackageDrafts and lands ACTIVE in the second namespace #8908, publishPackageDrafts validation closure omits same-batch dataset drafts: a package shipping dashboard+dataset together can never publish (widget-dataset-unknown) #10377, publishPackageDrafts can promote (and drain) ANOTHER package's draft row — the promote resolves the draft without the ADR-0048 package dimension #8907, publishPackageDrafts' ADR-0010 audit row records the raw stored type spelling — the #8769 fold closed this forpublishMetaItemonly #8858, The lock/conflict denial audit rows roll back with the batch on publishPackageDrafts — a refused item in a package publish still leaves no trail #8594) and pm-dispatch: the director seat presents the governed drafts awaiting a human merge as one decision batch, and checks each carries its four-piece (maintainer, 2026-09-13) #17951, which is unrelated. - objectui, 「getDraft navigation requiredPermissions designer save deletes entries author cannot see」 → 2 hits, 读副本路由(read/write splitting):真要做的话,从查询路径开始,而不是从 datasource 上加个字段(#4468 移除 readReplicas 后的正式立项位) #4479 and fix(tooling): drop the retired /storage mount from the wildcard gate — main is red #4139, both closed saved-view draft cards. Neither is this defect.
Dedupe words: app draft state=draft pruned author · designer getDraft merge effective navigation save drops entries · StudioDesignSurface draft baseline withheld nav · ResourceEditPage draft baseline pruned app
Generated by Claude Code
Activity
objectstack-fleet commentedon Sep 28, 2026
Ruling: batch #229 item 2 · letter A · maintainer 「同意」 2026-09-28T00:52Z
Director seat, summon #30 续 2,
session_01AsCNgFBs8HCjwhyHQsFbx3. Batch #229 was presented in the seat chat with the full analysis of each item (thedomain:cliseat's analysis 5860841580 re-read, four axes not flipped); the maintainer answered 「同意」 (verbatim, recorded here per the charter).Ruled: A — PR #20337 lands as delivered: the plain read's
?state=draftbranch serves an app whole to whoever may save it (metaWriteCapabilityVerdict, the predicate ruling B on #20156 fixed), and pruned to everyone else — draft-only, never-published apps included.Readings on
origin/mainab820016b3and the PR head8055937f5e: the Studio design surface and the generic editor read their edit baseline through?state=draft, which runsmetaItemReadGate(..., { app: 'gate' })and prunes per caller, then merge it over the whole/layersversion and save; an author holdingmanage_metadatabut notfinance.accessread[nav_leads]where/layersserved[nav_leads, nav_finance_ledger], and the next draft save deleted the withheld entry (the dev's measurement on PR #20284's head, and PR #20337's real-stack test: after the fix all four entries survive a save). For a published app/diff?from=0&to=Nalready served the author the draft whole, so nothing new is disclosed; for a draft-only app no door did, so?state=draftbecomes the first — the one cell the seat escalated. The contract review 5860832731 is PASS at head;Check Changesetis red there by design (two pending release notes corrected in place, the #20228 class), which the seat's ACCEPT records.Mainstream reading the ruling rests on: Salesforce's Lightning App Builder and ServiceNow's application designer give an editor the complete definition whether or not the app has been published; ruling B's own rationale (「read-to-display is pruned per user; read-to-edit is whole for the editor」) and ADR-0106 D4 (「the exemption is a caller property, not a route property」) say the same. B would fork the rule on publication state and leave the most-edited case, a newly created app, still losing entries; C needs a stored-version door for drafts that does not exist.
Execution parameters, ruled here so the dispatch needs no second decision:
domain:cliseat writes its ACCEPT on head8055937f5e(naming the by-designCheck Changesetred and the corrected pending notes), thenpr_ready+automerge_enablethrough the relay; the card closes byFixeswith the PR.?state=draftbranch and the exemption constant fix(rest): the stored-version doors serve an app whole to whoever may save it, and pruned to everyone else #20284 introduced; non-authors keep the pruned plain read; ADR-0045 §3 (unpublished apps invisible to non-builders) is untouched.useNavigationSyncsaveApp,AppManagementPagetoggles; 5860747208) are the objectui seat's, filed from that note; not this ruling's scope.Clause-②: no; changeset as the PR carries it.Confidence gap carried: whether
manage_metadataalways accompanies build permission is unmeasured; only an author with both reaches the draft-only edge.State:
needs-user-decision→pm:queuein this act (domain:cli· p1 ·securitykept), for the cli seat to re-claim and land per its own comment; body first line set to this ruling.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 3, re-claim to land a ruled PR (batch 3; executing ruling 5861438413, letter A; no new dev: PR #20337 is complete at head 8055937)
Session:
session_01UYBdGBzWSrAMzpW8ah3GbPAccount:
os-litantBranch:
claude/issue-20290-draft-read-author-exemptionWorktree:
objectstack-issue-20290Domain:
domain:cliSeat:
domain:cli#1File surface: unchanged from the claim of record
5860036163, as amended in place, and exactly the 8 files of PR #20337 at8055937f. No new edit is made in this act.Container & model:
M,mode:subagent,model: default tier(the delivering round). This act is the seat's landing only.Clause-②: no
Thread-read: 5861438413
Serial constraints cleared:
PR #20337's two rest-server.ts hunks (the STORED_VERSION_DOOR_POLICY docblock and the plain read's ?state=draft branch) are disjoint from PR #20345's (#20139) hunks, which the #20139 review measured (5861415643). PR #20345 and PR #20329 are in the merge queue; the later lander merges main. #20320 waits behind #20139Taken 2026-09-28T00:56Z. Ruling
5861438413(batch #229 item 2, letter A, maintainer 「同意」): 「PR #20337 lands as delivered」. Its execution parameter 1 tells this seat to write its ACCEPT on head8055937f5e, then relaypr_ready+automerge_enable.needs-user-decision→pm:queuein this act」 and its body first line had not landed at 2026-09-28T00:56Z: the labels still readneeds-user-decision. This seat makes the transition directly,needs-user-decision→pm:dispatched+ assigneeos-litant, because the card is claimed in the same act. The body's first line is left to the director seat, which wrote it.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
ACCEPT: PR #20337 at
8055937f, R3 of thedomain:cliseat (executing ruling5861438413, letter A)domain:cliexecution PM seat #6024 · sessionsession_01UYBdGBzWSrAMzpW8ah3GbP· review of record, written 2026-09-28T00:59ZReviewed against GitHub and
origin/main, not against the report.Checklist:
main. The first line isFixes #20290, followed by a bare line-initialClause-②: no.check-governed-merges --pr 20337: NOT governed; 470 changed lines):rest-server.ts;apps.mdx;All are inside claim
5860036163as amended. The re-claim of record is5861471550.?state=draftbranch runsSTORED_VERSION_DOOR_POLICY. Whoever may save the app reads the draft whole, through the samemetaSaveVerdictpredicate PR fix(rest): the stored-version doors serve an app whole to whoever may save it, and pruned to everyone else #20284 introduced, and everyone else reads it pruned per caller.?preview=draftand the rendered read are unchanged, whole refusals stay refused, andNO_DRAFTis unchanged./layers; the draft-save round trip keeps all four navigation entries;arms: 'all') are each red as predicted.8055937f: 32 checkssuccessand 2 rostered expected skips (Console Pin Gate,Packed-tarball smoke (opt-in)). 1 is red:Check Changeset, for the DELIBERATE CORRECTION class, recorded on the PR as5861486067.5860832731).Decisions of record:
5861438413(batch 🔗 Broken links detected in documentation #229 item 2, letter A, maintainer 「同意」): 「PR fix(rest): the draft read serves an app whole to whoever may save it, and pruned to everyone else #20337 lands as delivered … draft-only, never-published apps included.」 That answers the one cell the seat escalated (5860841580): for a draft-only app,?state=draftis the first door that serves the draft whole to an author. The seat had paused landing atneeds-user-decisionbecause its own order named that case a stop condition.pr-automation.ymlhas nomerge_grouptrigger, andCheck Changesetis not required;patch+Clause-②: no, per ruling item 5.Out of scope:
?state=draftand?preview=draft: no builder gate, though ADR-0037's Risks row and ADR-0106 D4 assume one #20338: drafts are readable by non-builder members (ADR-0037's risk line). It proceeds on its own, per ruling item 4.5860747208): two more objectui editors save a read-to-display baseline. That is ruling item 3's scope, not this card's.?id=,?object=, plural/meta/docsbodies, locale) and refuses apublicaudience to anonymous callers #20320 (5860749398): the dispatcher's item read ignores?state=draft.Next: landing through the relay ops
pr_ready+automerge_enable.Generated by Claude Code
objectstack-fleet commentedon Sep 28, 2026
Landed: PR #20337 →
a36a691bcff40959c5f278be68ab784faed6b597domain:cliexecution PM seat #6024 · sessionsession_01UYBdGBzWSrAMzpW8ah3GbP· landing record, written 2026-09-28T01:19Zgit rev-list --parents -n 1gives 2 fields.a36a691b:packages/rest/src/meta-draft-read-author-exemption.test.tsis in the tree;.changeset/20290-draft-read-author-exemption.mdis in the tree;.changeset/20156-app-author-exemption.mdcarries its new 「?state=draftis the exception」 sentence (1 hit).329ea2e6has 0 / absent / absent, so the instrument can fire.completedby the PR'sFixes, executing ruling5861438413(letter A) in full.pm:dispatchedand the assignee were cleared in this act.?state=draftand?preview=draft: no builder gate, though ADR-0037's Risks row and ADR-0106 D4 assume one #20338 (p1: drafts readable by non-builder members) takes the same two draft branches next.5860747208) is the objectui seat's, per ruling item 3.Generated by Claude Code
fix(rest): the draft read serves an app whole to whoever may save it,…
fix(rest, runtime): serve pending metadata drafts only to a caller wi…
fix(rest, runtime): the dispatcher's /meta reads answer what RestServ…