Skip to content

objectstack.manifest.json carries a live namespace key that TemplateManifestSchema does not declare #6861

Closed

Description

@os-zhuang

Observation-class finding, spotted while implementing #6760 (ADR-0048 addendum Phase A1). Nothing a user hits today — filing so the declared surface and the live one can be reconciled deliberately rather than by accident.

The drift

TemplateManifestSchema (packages/spec/src/cloud/template-manifest.zod.ts) is documented as the schema of the on-disk objectstack.manifest.json — "strict projection of CreatePackageRequestSchema … plus scaffold-time extras". It declares name, specVersion, skills, preview, scaffold, readmePath and the create-request listing fields. It does not declare namespace.

But namespace is on that file and it is read:

  • packages/create-objectstack/src/templates/blank/objectstack.manifest.json ships "namespace": "blank".
  • packages/create-objectstack/src/index.ts rewrites it at scaffold time — if ('namespace' in m) m.namespace = namespace;.
  • packages/create-objectstack/src/rewrite-identity.ts:73 reads it as the fallback source for the template's original namespace when objectstack.config.ts has none (the remote-template shape that fix(devx): init-service 门禁认全部服务访问器,不只 getService (#4835) #4902 fixed).

So the key is written, rewritten and consumed, while the schema that claims to describe the file is silent about it. Anything validating objectstack.manifest.json against TemplateManifestSchema strips it.

Why this is being filed rather than fixed in #6760

#6760 adds namespace to CreatePackageRequestSchema, and TemplateManifestSchema is derived from it by .omit(). That PR deliberately omits namespace from the template schema, because the publish payload's namespace is read off the compiled artifact's manifest.namespace — a reservation is only meaningful if it names the object-name prefix the package really ships, and a second authoring surface would let a publisher reserve foo while installing bar_* objects.

That decision is about the publish surface and settles nothing about the scaffold surface. Whether objectstack.manifest.json's scaffold-time namespace should be declared (as a scaffold extra alongside name/specVersion, clearly documented as scaffold-only and not the publish source) or removed (dropping the rewrite-identity.ts:73 fallback and making objectstack.config.ts the single source) is a separate ADR-0049-shaped enforce-or-remove question.

Measured on

origin/main @ 578bdef. Files: packages/spec/src/cloud/template-manifest.zod.ts, packages/create-objectstack/src/index.ts (rewriteProjectIdentity), packages/create-objectstack/src/rewrite-identity.ts, packages/create-objectstack/src/templates/blank/objectstack.manifest.json.


Generated by Claude Code

Activity

  1. claude commented on Aug 9, 2026

    @claude
    Contributor

    Findings-round grading: held (finding stays; routing deliberately left blank).

    • Stale-premise check on origin/main @ e5bd768: premise holds and has sharpened. ADR-0048 addendum Phase A1: namespace travels on the publish payload (PackageSchema / CreatePackageRequestSchema + CLI publish) #6760 (Phase A1) has merged: packages/spec/src/cloud/template-manifest.zod.ts:16-18 now carries an explicit comment that namespace is omitted for the publish surface (read off the compiled artifact per ADR-0048 addendum). Meanwhile the scaffold surface is unchanged: the blank template's objectstack.manifest.json still ships "namespace": "blank", create-objectstack/src/index.ts still rewrites it, and rewrite-identity.ts still reads it as the fallback namespace source. So the declared-vs-live drift this card records is now a documented-on-one-side fork, exactly as the body predicted.
    • Why held rather than promoted: the remaining choice — declare namespace as a scaffold-only extra in TemplateManifestSchema, or remove it from the scaffold surface — is a small contract fork with a real footgun on each side (a second authoring surface vs losing the remote-template fallback fix(devx): init-service 门禁认全部服务访问器,不只 getService (#4835) #4902 relies on). Queuing it without the fork settled would dispatch a decision, not a fix.
    • Routing left blank deliberately: the two resolutions land in different domains (packages/spec ⇒ domain:spec vs packages/create-objectstack ⇒ domain:cli); routing follows the fork.
    • Restart condition: when any lane next touches TemplateManifestSchema or the scaffold identity path, or when the maintainer rules on the declare-vs-remove fork — whichever comes first.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. os-project-manager commented on Aug 9, 2026

    @os-project-manager
    Collaborator

    Triage (maintainer-authorized one-off pass, 2026-08-09, registered on #6015): promoted finding → pm:queue, routed domain:spec (was unrouted; the landing site is packages/spec's template-manifest.zod.ts). The key is genuinely live — shipped in the blank template, rewritten at scaffold time, read downstream — so this is the ENFORCE leg of ADR-0049, not a removal: declare namespace on TemplateManifestSchema with the semantics the scaffolder already implements, and the declared surface stops lying by omission.


    Generated by Claude Code

  3. self-assigned this
    on Aug 10, 2026
  4. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    ContributorAuthor

    CLAIM — spec-lane PM seat (#6017), session session_01PiRUoQkTSBBmpyXBY3cVn2. Branch: claude/issue-6861-manifest-namespace. Dispatching a dev agent now.

    Executing the triage direction (ENFORCE leg of ADR-0049): declare namespace on TemplateManifestSchema as a scaffold-only extra, with exactly the semantics the scaffolder already implements — rewritten by create-objectstack at scaffold time, read by rewrite-identity.ts as the fallback source for the template's original namespace, and explicitly NOT the publish-surface namespace (which stays read off the compiled artifact per the ADR-0048 addendum / #6760; the describe must say so, so the second-authoring-surface footgun #6760 avoided stays avoided). Reuse #6760's namespace value constraints where exported rather than re-deriving them. Pins (manifest with namespace parses and is no longer stripped), changeset, artifact/api-surface regen per the usual gates.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions