packages/app-shell/src/views/studio-design/packages-io.ts:14-24 explains the writable fallback with a premise that is false on objectstackmain8f9ef6f7c:
The scope !== 'project' expression below is ONLY the fallback for servers that predate that field, and it is WRONG for one row: a type: module sub-package of a multi-package artifact (ADR-0130 D4) normally omits scope — the schema default is applied at PARSE time, while the artifact load path deliberately hands the RAW manifest body to registerApp, so the served row has no scope key at all.
The artifact load path does not hand a raw body to registerApp. defineStack parses every packages[] entry through ManifestSchema (packages/spec/src/stack.zod.ts:233), whose scope is .default('project') (packages/spec/src/kernel/manifest.zod.ts:311), so the default is materialised at COMPILE time and travels in the artifact. Measured on a real boot of examples/app-multi-package: dist/objectstack.json and both GET /api/v1/packages rows carry scope: "project". No package of a compiled artifact is ever scope-less.
What to change, and what NOT to
⛔ The BEHAVIOUR is correct and must not change. Reading the server's writable and never re-deriving it is right, and the scope !== 'project' compatibility fallback stays. Only the RATIONALE is misattributed.
The conclusion the rationale supports is also still true, and should survive the edit: a scope-less booted package is read-only while a scope-less Studio-created base is writable, nothing in the raw row separates them, and only the server's engine.manifests does. What is wrong is solely the example named as the source of the scope-less row.
The scope-less rows are real, but they come from the paths that never run a ManifestSchema parse (installPackage stores a key-by-key copy that applies no defaults):
- booted, read-only — a marketplace install, an offline file import, or a ledger rehydrate:
packages/cloud-connection/src/marketplace-install-local-plugin.ts:291 and :827 hand the stored/downloaded body to manifestService.register(...), which routes to registerApp; - database base, writable —
POST /api/v1/packages: packages/runtime/src/domains/packages.ts:383 takes body.manifest || body straight through protocol.installPackage.
Sites to correct, both comment/naming only:
packages/app-shell/src/views/studio-design/packages-io.ts:14-24 — the docblock quoted above.packages/app-shell/src/views/studio-design/packages-io.writableVerdict.test.ts:10-12 ("a booted multi-package module") and the case name at :38 ("the ADR-0130 module sub-package"). The assertions stay green either way: the row is hand-built with no scope, which the marketplace-import path really does produce — so only the label is wrong.
Filed unassigned by the dev of objectstack#14597, which corrected the same claim at its source (the fixture docblock, README and dogfood harness). objectstack#14803 sweeps the five remaining copies on the objectstack side. That card's triage ruled this repo's half a separate follow-up, which is this one.
Generated by Claude Code
packages/app-shell/src/views/studio-design/packages-io.ts:14-24explains thewritablefallback with a premise that is false onobjectstackmain8f9ef6f7c:The artifact load path does not hand a raw body to
registerApp.defineStackparses everypackages[]entry throughManifestSchema(packages/spec/src/stack.zod.ts:233), whosescopeis.default('project')(packages/spec/src/kernel/manifest.zod.ts:311), so the default is materialised at COMPILE time and travels in the artifact. Measured on a real boot ofexamples/app-multi-package:dist/objectstack.jsonand bothGET /api/v1/packagesrows carryscope: "project". No package of a compiled artifact is ever scope-less.What to change, and what NOT to
⛔ The BEHAVIOUR is correct and must not change. Reading the server's
writableand never re-deriving it is right, and thescope !== 'project'compatibility fallback stays. Only the RATIONALE is misattributed.The conclusion the rationale supports is also still true, and should survive the edit: a scope-less booted package is read-only while a scope-less Studio-created base is writable, nothing in the raw row separates them, and only the server's
engine.manifestsdoes. What is wrong is solely the example named as the source of the scope-less row.The scope-less rows are real, but they come from the paths that never run a
ManifestSchemaparse (installPackagestores a key-by-key copy that applies no defaults):packages/cloud-connection/src/marketplace-install-local-plugin.ts:291and:827hand the stored/downloaded body tomanifestService.register(...), which routes toregisterApp;POST /api/v1/packages:packages/runtime/src/domains/packages.ts:383takesbody.manifest || bodystraight throughprotocol.installPackage.Sites to correct, both comment/naming only:
packages/app-shell/src/views/studio-design/packages-io.ts:14-24— the docblock quoted above.packages/app-shell/src/views/studio-design/packages-io.writableVerdict.test.ts:10-12("a booted multi-package module") and the case name at:38("the ADR-0130 module sub-package"). The assertions stay green either way: the row is hand-built with noscope, which the marketplace-import path really does produce — so only the label is wrong.Filed unassigned by the dev of
objectstack#14597, which corrected the same claim at its source (the fixture docblock, README and dogfood harness).objectstack#14803 sweeps the five remaining copies on theobjectstackside. That card's triage ruled this repo's half a separate follow-up, which is this one.Generated by Claude Code