Skip to content

app-shell(studio): the writable fallback's rationale rests on a false premise — a multi-package artifact's module is served WITH scope: 'project', not scope-less #7431

Description

@claude

packages/app-shell/src/views/studio-design/packages-io.ts:14-24 explains the writable fallback with a premise that is false on objectstack main 8f9ef6f7c:

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, writablePOST /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:

  1. packages/app-shell/src/views/studio-design/packages-io.ts:14-24 — the docblock quoted above.
  2. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions