Repository navigation
Description
epic:#31527#1 · os-project-manager · session_01FEkQ5mLd7pR2PHjsPwiGkz
Filing gate: ④ coordination node: child U of the epic #31527 (parent and per-layer children), as ADR-0150 (docs/adr/0150-behavioural-metadata-addressed-by-package-objects-keep-the-literal-prefix.md, merged in #31586 at 5d1382a)'s execution plan lists it. Hand-over card: filed undispatched as the epic seat signs off (maintainer 2026-11-08T16:34Z 「专题卡当前任务处理完,合并后就下班」). ⛔ Not a claim.
Ruling-ref: per-type table A (6723462584); epic A 「立专题」 (6723499113); division of labour A 「留核心,其余交车道」 (6723571682); D2 (6724570466); ADR-0150 accepted by merge (#31586, 2026-11-08T21:16Z).
Business reason: inherited from the parent #31527. Behavioural metadata is addressed by (package, name), so two installed packages can each ship an item of the same name and every reader answers the right one, or refuses with the candidates, instead of silently taking the first match.
Reader: the next ui-lane seat in this repo picks it from the queue and accepts its PR against the red lines below.
Dedupe (open AND closed): MetadataService packageId (38; top hits #21899(closed) #23297(closed) #22024(closed), none on point: they are this epic, its parent #30806, or unrelated cards).
class: d · type: Feature
Grade: p1, inherited from the parent (self-graded under 「定级你可以自己决定」).
维护者速读
Studio 和控制台读取、编辑元数据时带上当前软件包 id(?package=X),这样同名元数据不会读错包。在服务端 G1 落地之前,这一步不改变任何返回结果。
What to build
Studio and Console pass the package id they hold through MetadataService reads and editors (D5). On the wire it travels as ?package=X (client.meta.getItem), which is the exact qualifier (D5, D2 ruling 6724570466), never a closure caller. The ~ten reads that go bare today go qualified.
Surface
- Expected file surface:
ui/packages/app-shell(services/MetadataService.ts,views/studio-design/*). - Clause-②: no.
- Narrowing: yes, once G1 lands: a qualified read of a name X does not hold is refused naming the holders (not measurable first-party). U before G1 narrows nothing.
Acceptance — the epic's three red lines (every child shares them)
- Resolution never loosens. No first-registered fallback, no environment-wide guess for a caller with a package, no silent pick.
- Ambiguity is reported, never guessed. Every refusal and every migration report names every candidate.
- Independently releasable (red line 3). The PR proves it with ADR-0150's four readings: (a) the phase-1 one-holder tests stay green; (b) bare-name behaviour (URLs, MCP, SDK) is unchanged for every name with one holder, except this card's declared narrowing; (c) any migration is re-runnable and never guesses; (d) its
Clause-②:declaration and, where it changes the protocol, its ADR-0087 migration entry. - The epic seat is off duty after hand-over: the accepting lane seat checks these lines itself, and a narrowing beyond the declared one goes to the maintainer before landing.
Parent: #31527.
Activity
objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: confirmed ·
domain:ui· p1 ·pm:queue· 2026-11-08T21:54Z⛔ Not a claim.
epic:#31527#1is online, and the maintainer ruled 「留核心,其余交车道」. The children live in lane queues withoutpm:epic, which matches [Decision] Epic-driven delivery as the next working mode: one epic = parent + epic seat + milestone table; children as sub-issues withpm:epic+Blocked-by:; rolling split; mandatory hand-over when an epic seat leaves; a cap on concurrent epics #31502 A′.domain:ui, by package ([Decision] Keepdomain:devxanddomain:serveras separate lanes, or merge them? Measured load and boundary friction, 2026-11-08 #31424 rule 1).Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo
Claim: PM loop round 1 · 2026-11-08T22:11Z
Session:
session_01QsR2jTEZFY1pWynVBfvcmz(card thread, dev) · seatsession_01QsR2jVEjiYrK7onirxUGyoAccount:
zhuangjianguoBranch:
claude/project-thread-cp5kyeWorktree:
objectstack-issue-31671Domain:
domain:uiSeat:
domain:ui#3File surface:
ui/packages/app-shell(services/MetadataService.ts,providers/MetadataProvider.tsx,hooks/useNavigationSync.ts,views/studio-design/*), their tests, root.changeset/; ⛔ no server,packages/spec/**,packages/client/**or cloud changeContainer & model: M,
mode:thread,model: default tier(dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no (app-shell is private; step 1 confirms no published
@object-ui/*export changes)Metadata-protocol: none
Narrowing: through G1's door only, as the card declares; step 1 proves no read changes its answer on today's server, and any read that does goes to the maintainer before landing
Responsibility: Studio and the Console read bucket 2/3 metadata by bare name in objectstack (
MetadataProviderby-name serve,useNavigationSyncpage read, Studio item reads) | the platform path is ADR-0150 D5: the holder's package id travels as?package=X, the exact qualifier | admins and authors on any environment where two packages hold the same behavioural name, who would otherwise be served the first matchThread-read: 6727651704
Premise: holds. On
origin/main9bf32864f,client.meta.getItem(type, name, { packageId })sets?package=(packages/client/src/index.ts~L2053), whileMetadataProvider.tsx~L1344 anduseNavigationSync.ts~L723 callmeta.getItem(type, name)with no package.Serial constraints cleared: the other
domain:uicard in build on this seat is #27196 PR 2 (the form preview: fields, plugin-form), a disjoint surfaceScope: the card's "What to build", under the epic's three red lines and ADR-0150's readings (a)–(d).
?package=, the declarations, and a slice plan if the census exceeds about 600 lines.Fixes #31671. Brief:/mnt/project-files/issue-31671/brief.md.objectstack-fleet commentedon Nov 8, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jTEZFY1pWynVBfvcmz
os-dev-report
Step 1: census of by-name metadata reads in
ui/packages/app-shell(no code written)Tree:
objectstack-ai/objectstackorigin/maine81636ce(newer than the brief's9bf32864f; every line number below is ate81636ce).Headline. The premise holds only in part. On today's tree, Studio already sends a holder id on every editor read of a bucket 2/3 type (28 sites). The bare reads that are left (20 sites) fall into four groups, and none of them holds a holder id the read could send:
?package=cannot name (its address is G1's_env);The brief's two named reads,
MetadataProvider.tsx:1344anduseNavigationSync.ts:723, both stay bare under the holder rule.MetadataService.tsholds no bucket 2/3 read at all: its only by-name read ismeta.getItem('object', …)(L937, bucket 1), and nothing inui/constructs it except the unuseduseMetadataServicehook. So no read in the claim's file surface is qualified by this card. Qualifying a Console read with the app's package would change today's answer (item 3), which is a narrowing beyond the declared one.How the census was taken (re-runnable from
ui/packages/app-shell/src)Non-test sources (
':!**/*.test.*' ':!**/__tests__/**' ':!**/test/**'), comment lines dropped:git grep -nE "meta\.getItem\("(3 hits).@object-ui/data-objectstackMetadataClient):git grep -nE "\.(get|getDraft|layered|history|diff|references|audit)GENERIC\(\s*('[a-z_]+'|[a-zA-Z_.?]+)\s*,"(49 hits, before droppingReflect.get,searchParamsand storage).git grep -nE "(readDraftForSave|readAuthoredDraft|fetchItemBody|fetchItemRead)GENERIC\(".git grep -nE "useMetadataItem\(|readItemsByName\(|readItemPreferLocal\(|getItem\('(page|action)'".git grep -nE "/meta/".GENERICstands for an optional TypeScript type argument, spelled out here so the comment survives: a less-than sign, any run of characters other than an opening parenthesis, a greater-than sign, the whole group optional.Then I dropped the bucket 1 and bucket 4 types:
object,view,object_extensionanddatasource. Their reads are listed at the end.A. Qualified today: the holder id is already sent (28 sites, nothing to do)
Holder sources:
?package=, so the rail package is the holder..custompackage, which holds the draft and the declared override of a base item._draftsrow'spackageId.?package=on the edit page, set by the list row's link. The read goes bare when the URL names none.views/studio-design/StudioDesignSurface.tsx:968draftScope)StudioDesignSurface.tsx:2680StudioDesignSurface.tsx:2681,:2709navReadAt)StudioDesignSurface.tsx:3027,:3057readAt, L3026)StudioDesignSurface.tsx:3080,:3174StudioDesignSurface.tsx:7963,:8249views/studio-design/StudioCustomizeControls.tsx:191/:197views/studio-design/StudioNavItemInspector.tsx:822views/studio-design/StudioBundleItemEditor.tsx:313,:334,:350views/studio-design/translationTable.ts:314views/studio-design/ObjectHooksPanel.tsx:243views/studio-design/pairObjectActions.ts:180views/item-tools/StudioItemTools.tsx:118packageIdwhen heldpreview/DraftChangesPanel.tsx:281(callers:1115,:1116,:1563,:1586)views/metadata-admin/ResourceEditPage.tsx:1362,:1366,:2044,:2045,:2267,:2314,:2315,:2405views/metadata-admin/PermissionMatrixEditor.tsx:987,:992,:1563,:1572,:1620,:1650views/metadata-admin/EmbeddedItemEditor.tsx:158readParentOwnerPackage)B. Bare today (20 sites): why each one stays bare
providers/MetadataProvider.tsx:1344(preview leg:1341)getItem(type, name)and itsbyNamecache are keyed by name onlyconsole/AppContent.tsx:494,layout/AppHeader.tsx:738,views/PageView.tsx:344(readItemPreferLocal)activeApp._packageId, the context packageanswer._packageId === owner, and otherwise picks prefer-local from the list (hooks/useObjectPresence.ts:190-212), so a dependency's page is a legal answerhooks/useNavTargetLabel.ts:236hooks/useRecentItemLabel.ts:126/:132RecentNamedItemstores no package (context/RecentItemsProvider.tsx:79)hooks/useActionModal.tsx:216hooks/useNavActionDispatch.ts:68ActionNavItem.actionDef.actionName, a referencehooks/useNavigationSync.ts:723usePublishAllDrafts.ts:178, for package-less (orphans) first publishespreview/usePublishAllDrafts.ts:205views/runtime-metadata-persistence.ts:383(viaviews/RuntimeDraftBar.tsx:109)views/studio-design/StudioDesignSurface.tsx:2458_packageId(StudioRailCustomizations.tsx:436-438)views/ReportView.tsx:131datasetreferenceviews/metadata-admin/PositionPermissionSetsSection.tsx:83permissionSetsreference; positions are environment-wide (D7)views/metadata-admin/previews/useDatasetCatalog.ts:282views/metadata-admin/EmbeddedItemEditor.tsx:139(its save at:188too):158layers read sendsviews/item-tools/AuditPanel.tsx:133,ReferencesPanel.tsx:69,ResourceHistoryPage.tsx:210StudioItemTools'packageIdMetadataClient.audit/references/history(ui/packages/data-objectstack/src/metadata-client.ts:2092,:1881,:2070) andpackages/clientgetAudit/getReferences/getHistory(index.ts:2494,:2445,:2199) take no package. Per the brief I stop here and report itviews/metadata-admin/previews/BookPreview.tsx:171/tree)href, not a read the app makes; outside the surfaceExcluded as bucket 1/4:
services/MetadataService.ts:937;StudioDesignSurface.tsx:5959,:5960,:6069,:6070,:6390;PackageOwdOverviewPanel.tsx:108-109;ObjectListViewInspector.tsx:587,:653,:674,:719,:721;useFormViewDraft.ts:165,:178,:190;ListViewBulkActionsSection.tsx:206;translationTable.ts:333;ResourceEditPage.tsx:1218;anchors.ts:221;useDatasetFields.ts:332,:346;useObjectFields.ts:158;external-object.tsx:69(datasource);PermissionMatrixEditor.tsx:247;InterfaceListPage.tsx:413andobjectViewTabs.ts:322(views); and the object fetches inPagePreview,AiChatPage,ExcelImportBarandexternal/api.ts.2. Which id is the right one (the cases the brief names)
StudioCustomizeControls.tsx:191/197;readAt/navReadAtabove). ⚠ This is a G1 dependency, not U's. After G1,?package=PAIRon a base-held name is a read of a name whose code holder is the base. G1's exact-qualifier arm ("X's item with its bound overlay") must count the pair's declared override, and the pair's added member, as held by the pair. Otherwise these Studio reads, which work today, are refused. I am naming the sites so that G1's card can pin them.readItemPreferLocalprefers the app's own item and accepts a dependency's. Sending the app's id would refuse the dependency's page after G1, and changes the answer today (item 3). Stays bare.?package=cannot name it; its qualified address is G1's_env. Stays bare._packageId) or from the list row.3. Today's answer, before G1 (
packages/metadata-protocol/src/protocol-meta-item-reads.tsate81636ce)A read naming package X differs from the bare read at three points:
findServedOverlayRowwith X, whose candidates are "the package's own row then the package-less row (never another package's…)" (protocol-environment-rows.ts:118).The registry tier is the same for both reads: the bare slot (
registry.ts:8188), then X's composite (:8193), then the first composite (:8199).packagelessContentPackage's "exactly one code package ships it: that package" (protocol-environment-rows.ts:224), which is H. So both reads take the same stored row and the same registry item. No change, which is reading (b).strictDraftReadAddress(L213-214), the qualified one uses O's row then the package-less one. These agree when O is the package-less address, and are not proven equal otherwise. Following the brief (⛔ never guess), I make no claim of "no change".Conclusion: no read this card would qualify changes its answer today, because the census leaves no in-surface read to qualify. The one read that would change today (B2/B3 with the app's id) is not qualified.
4. Declarations
Clause-②: no.@object-ui/app-shellis"private": true(ui/packages/app-shell/package.json:4). With no code change, no published@object-ui/*export moves.Metadata-protocol: none.5. Slice plan and recommendation (four axes)
The census needs no slices: the in-surface diff is 0 lines.
premise_still_valid: falsefor the build half. Studio already sends the holder; every bare read lacks one.Carriers, not filed by me:
packages/clientgetHistory/getReferences/getAudit/diffItemanddata-objectstackMetadataClient. Carrier: G3, plus a ui follow-up.EmbeddedItemEditordraft read and save addressed bare beside an owner-addressed layers read. That is an address mismatch inside one editor, and a candidate ui card. Dedupe words:EmbeddedItemEditor getDraft package,embedded parent draft address.409with candidates. B1'sANSWERED_REFUSAL_CODESpath should then show the candidates rather than a failed read. That is a ui card when G1 lands.{ "issue": 31671, "status": "reading", "branch": "claude/project-thread-cp5kye", "pr": null, "session": "session_01QsR2jTEZFY1pWynVBfvcmz", "head": "e81636ce (origin/main; no commit on the branch)", "premise_still_valid": false, "summary": "Census of by-name bucket 2/3 reads in ui/packages/app-shell at e81636ce: 28 sites already send the holder's package id (Studio rail/addedIn/pair, ledger rows, edit-page URL); 20 stay bare because no holder can be named there (Console context package with possible dependency holder, D3 references, environment items, SDK gap on history/references/audit). No read in the claim's surface is qualified; MetadataService has no bucket 2/3 read. Qualifying Console reads with the app's package would change today's answer (dependency holder's bound stored row skipped), so they stay bare.", "declarations": "Clause-②: no · Metadata-protocol: none · Narrowing: none today (no code)", "files_changed": [], "line_budget": "0 changed lines", "deviations": "none; no worktree created, nothing written to the repo", "gates": "NOT MEASURED: no diff", "tests": "NOT MEASURED: no diff", "dogfood_battery": "n/a — no code", "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2) on #31671; no MCP writes", "api_writes": "1 — this os-dev-report comment via post-stamped", "open_questions": [ { "question": "With no in-surface read to qualify, how does #31671 close?", "options": ["A close on this census, no PR", "B pins-only PR (holder sent / control bare), about 150 test lines", "C qualify with the context package (rejected: changes today's answer)"], "recommendation": "A, because every remaining bare read lacks a nameable holder and table A already sends one; B guards no behaviour change" } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: G3 (client SDK) · history/references/audit/diff take no package (packages/client index.ts:2199/2445/2494/2598; data-objectstack metadata-client.ts:2070/1881/2092/2049) · noted, not filed", "carrier: G1 · Studio reads at the customize pair for a base-held name (StudioCustomizeControls.tsx:191, StudioDesignSurface.tsx:2681/3027/8249, StudioBundleItemEditor.tsx:313/334, pairObjectActions.ts:180) must stay answered by the exact-qualifier arm · noted, not filed", "carrier: none · EmbeddedItemEditor.tsx:139/188 draft read and save go bare beside the owner-addressed layers read at :158 (an address mismatch inside one editor, not measured as a wrong answer) · noted, not filed · dedupe: EmbeddedItemEditor getDraft package; embedded parent draft address" ] }objectstack-fleet commentedon Nov 8, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jVEjiYrK7onirxUGyo
席位核对 · step 1 accepted · close not planned · 2026-11-08T22:24Z
Seat
domain:ui#3read the dev's step-1 report 6727959027 (premise_still_valid: false,pr: null) for its claim 6727842128, and spot-checked it againstorigin/maine81636ce.Ruling: option A. The census is the record of this card, and no PR follows. #31671 closes
not_planned. Reopening is free, and the maintainer can veto.ui/packages/app-shell.addedIn, from the ADR-0146 pair, from the_draftsledger row, or from the edit page's?package=.?package=cannot name (their address is G1's_env);MetadataService.tsholds no bucket 2/3 read; its only by-name read isobject, at ~L937. So the card's "~ten bare reads" have no holder to send.readItemPreferLocal(hooks/useObjectPresence.ts~L190–212) accepts a by-name answer only when_packageIdequals the app's package. Otherwise it picks prefer-local from the list, so a dependency's page is a legal answer today.findServedOverlayRow,protocol-environment-rows.ts~L118). So qualifying a Console read with the app's id changes today's answer whenever a dependency holder has a bound stored row.MetadataProvider.tsx~L1344 seam is keyed by name only (byName). It has no holder to send either.Clause-②: no,Metadata-protocol: none, and no narrowing today.packages/clientgetHistory,getReferences,getAuditanddiffItem, and@object-ui/data-objectstackMetadataClient.history,referencesandaudit, take no package. Carrier: client: optional namespace on automation.*, actions.invoke and meta.getItem (ADR-0150 child G3) #31695 (G3), noted there.StudioCustomizeControls.tsx:191;StudioDesignSurface.tsx:2681,:3027,:8249;StudioBundleItemEditor.tsx:313,:334;pairObjectActions.ts:180) must stay answered by G1's exact-qualifier arm. Carrier: rest: by-name doors take an optional namespace segment and the_envsegment, and answer 409 with the candidates on ambiguity (ADR-0150 child G1) #31691 (G1), noted there.409with candidates, and the Console reader then owes a face for it. Carrier: rest: by-name doors take an optional namespace segment and the_envsegment, and answer 409 with the candidates on ambiguity (ADR-0150 child G1) #31691 (G1). The ui follow-up is filed when G1's door is measurable.EmbeddedItemEditor.tsx:139and:188(the draft read and save) go bare beside the owner-addressed/layersread at:158. Dropped: the read and the save use the same address, so they agree, and no wrong answer is measured. A measured divergence reopens it.Epic pointer posted on #31527.