QA-source: #21330 · studio-authoring.view-authoring-live · outside the item's clauses (found while driving it and platform-core.metadata-authoring-roundtrip)
What fails
A runtime view container whose list names no key expands to <object>.default. Saved under any name — in a writable package or as an org overlay — for an object another package ships, that expansion overwrites the packaged <object>.default on the object door, with no diagnostic, a wrong _packageId, and a by-name read that disagrees.
Reproduction (stock showcase on 17.6.0 617f25f8, admin)
- Baseline:
GET /api/v1/meta/view?object=showcase_task → showcase_task.default label "All Tasks", _packageId: com.example.showcase, 7 columns.
POST /api/v1/packages {"manifest":{"id":"com.example.repairassets","name":"Repair assets","version":"0.1.0","type":"app","namespace":"repair"}} → 201.
PUT /api/v1/meta/view/os_qa_shadow_probe?mode=draft&package=com.example.repairassets {"name":"os_qa_shadow_probe","object":"showcase_task","list":{"type":"grid","columns":["title","status"]}} → 200.
POST /api/v1/packages/com.example.repairassets/publish-drafts → 200, outcome: published, probes.issues: [].
GET /api/v1/meta/view?object=showcase_task → showcase_task.default now has label "showcase_task.default", 2 columns, still stamped _packageId: com.example.showcase, isDefault: true. The console's Task list default tab becomes this view (with a column the object lacks, the tab shows "This view's query was rejected").
GET /api/v1/meta/view/showcase_task.default → still "All Tasks", 7 columns — the two read doors disagree.
DELETE /api/v1/meta/view/os_qa_shadow_probe → the list read returns to "All Tasks".
- The same happens with no
?package= (an org-scoped PUT /api/v1/meta/view/os_qa_shadow_probe2).
- Control: the sanctioned override
PUT /api/v1/meta/view/showcase_task.default → both reads agree.
Reproduced on two fresh boots by the runner and three times by an independent verifier.
Mechanism
expandViewContainerWithDiagnostics (packages/spec/src/ui/view.zod.ts) names a bare list <object>.default; uniqueViewName de-duplicates only within the one container.
getMetaItems (packages/metadata-protocol/src/protocol.ts) then sets every overlay's expansion by name over the merged items, bypassing the package-aware overlay merge that runs just before it; expandRuntimeViewContainer appears to copy the shadowed artifact's protection / _packageId onto the item.
- The by-name read never expands containers, so it keeps the packaged item.
Expected
ADR-0005 keeps overlays name-keyed; ADR-0126 rules out silent override. Either the expansion takes a name that does not collide (seed the de-dup with existing names), or the save / publish is refused or flagged with a collision diagnostic. Independently, the two read doors must agree and the expanded item must carry its own package.
Scope
Not an authorization issue: authoring the container needs manage_metadata (or manage_org_presentation org-scoped), and that caller can already override showcase_task.default by name. It is a silent, reversible integrity defect. Severity (verifier): medium. Owning repo: objectstack.
Full run evidence: #21330.
Generated by Claude Code
QA-source: #21330 · studio-authoring.view-authoring-live · outside the item's clauses (found while driving it and platform-core.metadata-authoring-roundtrip)
What fails
A runtime view container whose
listnames no key expands to<object>.default. Saved under any name — in a writable package or as an org overlay — for an object another package ships, that expansion overwrites the packaged<object>.defaulton the object door, with no diagnostic, a wrong_packageId, and a by-name read that disagrees.Reproduction (stock showcase on 17.6.0
617f25f8, admin)GET /api/v1/meta/view?object=showcase_task→showcase_task.defaultlabel "All Tasks",_packageId: com.example.showcase, 7 columns.POST /api/v1/packages {"manifest":{"id":"com.example.repairassets","name":"Repair assets","version":"0.1.0","type":"app","namespace":"repair"}}→ 201.PUT /api/v1/meta/view/os_qa_shadow_probe?mode=draft&package=com.example.repairassets{"name":"os_qa_shadow_probe","object":"showcase_task","list":{"type":"grid","columns":["title","status"]}}→ 200.POST /api/v1/packages/com.example.repairassets/publish-drafts→ 200,outcome: published,probes.issues: [].GET /api/v1/meta/view?object=showcase_task→showcase_task.defaultnow has label "showcase_task.default", 2 columns, still stamped_packageId: com.example.showcase,isDefault: true. The console's Task list default tab becomes this view (with a column the object lacks, the tab shows "This view's query was rejected").GET /api/v1/meta/view/showcase_task.default→ still "All Tasks", 7 columns — the two read doors disagree.DELETE /api/v1/meta/view/os_qa_shadow_probe→ the list read returns to "All Tasks".?package=(an org-scopedPUT /api/v1/meta/view/os_qa_shadow_probe2).PUT /api/v1/meta/view/showcase_task.default→ both reads agree.Reproduced on two fresh boots by the runner and three times by an independent verifier.
Mechanism
expandViewContainerWithDiagnostics(packages/spec/src/ui/view.zod.ts) names a barelist<object>.default;uniqueViewNamede-duplicates only within the one container.getMetaItems(packages/metadata-protocol/src/protocol.ts) then sets every overlay's expansion by name over the merged items, bypassing the package-aware overlay merge that runs just before it;expandRuntimeViewContainerappears to copy the shadowed artifact's protection /_packageIdonto the item.Expected
ADR-0005 keeps overlays name-keyed; ADR-0126 rules out silent override. Either the expansion takes a name that does not collide (seed the de-dup with existing names), or the save / publish is refused or flagged with a collision diagnostic. Independently, the two read doors must agree and the expanded item must carry its own package.
Scope
Not an authorization issue: authoring the container needs
manage_metadata(ormanage_org_presentationorg-scoped), and that caller can already overrideshowcase_task.defaultby name. It is a silent, reversible integrity defect. Severity (verifier): medium. Owning repo:objectstack.Full run evidence: #21330.
Generated by Claude Code