Skip to content

feat(spec): make manifestId optional on TemplateManifestSchema + gate every shipped template manifest (#7319) - #7383

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-7319-template-manifest-id
Aug 10, 2026
Merged

feat(spec): make manifestId optional on TemplateManifestSchema + gate every shipped template manifest (#7319)#7383
os-zhuang merged 1 commit into
mainfrom
claude/issue-7319-template-manifest-id

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #7319.

Options 2 + 3 of the issue, combined. Option 1 (invent a template-level literal id) rejected per the dispatch: the scaffolder derives the identity per project, so a literal would name a package nobody publishes.

Premise — re-measured on merged main (f188ed6)

The blank template's manifest still fails to parse against the schema it declares as its $schema, with exactly the complaint the issue recorded and nothing else:

blank template manifest parses? false
issues: [ { "expected": "string", "code": "invalid_type",
            "path": ["manifestId"],
            "message": "Invalid input: expected string, received undefined" } ]

And the live path confirms the key is genuinely optional on this file: objectstack package publish reads it as raw JSON and resolves --manifest-id ?? m.manifestId ?? deriveManifestId(artifact, artifactPath) (packages/cli/src/commands/package/publish.ts:317-320). A template tree that declares none publishes fine — the fallback derives local.<slug> from the compiled artifact. Nothing on any live path requires a template-level manifestId, so the direction stands.

Built on merged main, which already carries #6861 / PR #7317's scaffold-only namespace extra. Both intents are kept, and the new gate exercises both.

(2) The widening is LOCAL — both schemas verified

packages/spec/src/cloud/template-manifest.zod.ts omits the inherited field and re-declares it optional, reusing the publish field's value constraints — the same omit-then-extend split namespace uses, in the other direction:

-    .omit({ ownerOrgId: true, createdBy: true, namespace: true })
+    .omit({ ownerOrgId: true, createdBy: true, namespace: true, manifestId: true })
     .extend({
+      manifestId: CreatePackageRequestSchema.shape.manifestId.optional().describe(…),

packages/spec/src/cloud/package.zod.ts is not in the diff at all — the publish request is untouched. Measured before and after, in one run:

before after
blank template manifest parses manifestId invalid_type
CreatePackageRequest without manifestId parses ❌ (unchanged)

The generated reference docs are the third witness: only content/docs/references/cloud/template-manifest.mdx moved (manifestId required → optional). The create-request page is byte-identical.

Optional is not unvalidated — a malformed id still fails at ['manifestId'] with invalid_format, and every other required key (displayName, name, specVersion) is still required. Pins in the new packages/spec/src/cloud/template-manifest-id.test.ts hold all of it, including that the two field instances are distinct and the publish field's own describe is unchanged.

(3) The gate

pnpm --filter @objectstack/spec check:template-manifests parses every objectstack.manifest.json under packages/create-objectstack/src/templates/ against TemplateManifestSchema.

Wired the way the repo's other source audits are: a check: script in packages/spec/package.json (self-test then run), classified in the check:generated ledger's NO_GENERATOR bucket (it reads src/ and writes nothing — a failure is a manifest to fix or a schema to fix, never a gen: to run), and one step in lint.yml's required, unfiltered TypeScript Type Check job, in the pre-build group since it needs no dist.

🧪 Parsing every shipped objectstack.manifest.json against TemplateManifestSchema...

  ✓ packages/create-objectstack/src/templates/blank/objectstack.manifest.json

✅ 1 shipped manifest(s) satisfy TemplateManifestSchema — their $schema line is a verified claim.

No edit to the blank manifest was needed for the manifestId gap — that was the acceptance condition, and it holds.

Reverse verification (predicted in writing, then run)

Predicted: reverting only template-manifest.zod.ts to origin/main makes the new gate exit 1 on the blank template with exactly the manifestId complaint, the self-test fails on its green fixture first, and restoring returns both to green.

Observed, exactly:

--- REVERTED: self-test ---
✗ self-test: green fixture: a manifest with no manifestId must PASS — that relaxation is #7319 itself;
  got ["manifestId: Invalid input: expected string, received undefined (invalid_type)"]
self-test exit=1
--- REVERTED: real run ---
  ✗ packages/create-objectstack/src/templates/blank/objectstack.manifest.json
      manifestId: Invalid input: expected string, received undefined (invalid_type)
real exit=1

template-manifest-id.test.ts went 3 failed / 4 passed under the same revert. Restored → gate exit 0, tests 7/7.

Gates run locally

  • pnpm --filter @objectstack/spec build then check:generatedall 11 generated artifacts up to date; the four-step regen produced exactly one artifact diff (content/docs/references/cloud/template-manifest.mdx, reviewed above). check:api-surface green — no export moved, so no dual-snapshot work.
  • pnpm --filter @objectstack/spec typecheck (tsc + check:scripts-typecheck + check:test-typecheck) ✅
  • pnpm --filter @objectstack/spec test → 363 files / 9475 tests ✅
  • check:template-manifests (self-test + run) ✅ · check:published-files ✅ · check:workflow-status-functions ✅ · check:empty-changeset ✅ · check:adr-0087-registration ✅ (no declared-breaking changeset) · ESLint on the changed files ✅

Changeset

@objectstack/spec: minor — an acceptance-face widening on a cloud-facing schema plus a new gate, no removal or rename, so no ADR-0087 disposition marker is owed (check:adr-0087-registration confirms: 0 declared-breaking changesets).

No docs/adr/** and no content/docs/releases/ files are touched.


Generated by Claude Code

…te every shipped template manifest (#7319)

`TemplateManifestSchema` describes the on-disk `objectstack.manifest.json`, and
the bundled blank template declares it as its `$schema` — but the file did not
satisfy it. The schema inherited `manifestId` from `CreatePackageRequestSchema`
as required, and no template has ever declared one. Measured on main, that was
the only complaint the shipped file produced:

    manifestId: Invalid input: expected string, received undefined (invalid_type)

Nothing broke because nothing parsed the file: `create-objectstack` reads and
rewrites it as raw JSON and does not depend on the spec at all, and
`objectstack package publish` also reads it raw, resolving the id as
`--manifest-id ?? m.manifestId ?? deriveManifestId(artifact, path)`. That
fallback is the measurement — on this file the id is a declarative default, not
a requirement.

Two changes:

1. `manifestId` is relaxed to optional on `TemplateManifestSchema`, declared
   LOCALLY: the inherited field is omitted and re-declared `.optional()`,
   reusing the publish field's value constraints — the same omit-then-extend
   split `namespace` uses (#6861), in the other direction. A malformed id is
   still rejected. `CreatePackageRequestSchema` is untouched: the publish
   request still requires `manifestId`, because a request with no package
   identity is not a request.

2. New gate `check:template-manifests` parses every `objectstack.manifest.json`
   under `packages/create-objectstack/src/templates/` against the schema, wired
   unfiltered into the required `TypeScript Type Check` job and classified in
   the `check:generated` ledger as a source audit. It is the check that would
   have caught both drifts this file has accumulated (#6861's stripped
   `namespace`, this one), so the `$schema` line stops being an unverified
   claim. It walks the tree rather than a file list, and fails rather than
   reporting success when it finds nothing to parse.

Pins in `template-manifest-id.test.ts` hold both halves: optional-but-validated
on the descriptor, still-required on the publish request, distinct field
instances. Reference docs regenerated.
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 8:23am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file tests tooling labels Aug 10, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 10, 2026 08:58
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 10, 2026
Merged via the queue into main with commit a87c5cd Aug 10, 2026
28 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-7319-template-manifest-id branch August 10, 2026 09:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The shipped blank template's objectstack.manifest.json does not validate against TemplateManifestSchema — it declares no manifestId

2 participants