Skip to content

feat(spec)!: retire five inert objectstack.manifest.json keys (name, specVersion, skills, scaffold, visibility) - #31064

Draft
objectstack-fleet[bot] wants to merge 6 commits into
mainfrom
claude/project-thread-5986qn
Draft

objectstack-fleet[bot] wants to merge 6 commits into
mainfrom
claude/project-thread-5986qn

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Clause-②: no (narrowing) — five authorable keys of TemplateManifestSchema become tombstones, and os package publish refuses the file's visibility by name
Metadata-protocol: narrowed
Skills-docs: none — no skills/** page teaches objectstack.manifest.json; the docs pages that did are updated here

Part of #31035

维护者速读

  • 改了什么:objectstack.manifest.json 里五个没人读的键(name、specVersion、skills、scaffold、visibility)改成 retiredKey() 墓碑:tsc 和 schema 解析都按键名拒绝并给出替代做法;os package publish 遇到文件里的 visibility 直接拒绝(指向 --visibility),其余四个只警告让作者删除;blank 模板、三个 connector 应用的清单和脚手架不再写这些键;版本同步脚本不再盖 specVersion。
  • 为什么改:ADR-0049「要么执行要么删除」。逐键实测(见 spec liveness: template_manifest keys nothing reads (name, specVersion, skills, scaffold, visibility, publisher, isStarter) — enforce or remove under ADR-0049 #31035 的逐键表):这四个键在 CLI、create-objectstack、hotcrm 里都没有读者,visibility 只被读来警告「我忽略你」。publisher / isStarter 有 hotcrm 的服务模式发送链,不在本 PR,等单独的决策卡。
  • 风险与回滚:收窄(BREAKING 横幅 + adr-0087 登记)。还写着这五个键的旧清单在 schema 解析时被拒;os package publish 只对 visibility 拒绝,其他四个继续发布并警告,所以老脚手架项目还能发。hotcrm 的清单和版本测试在它下次升级平台时要删这几个键(与 category 同一载体)。回滚即 revert。
  • 先看:packages/spec/src/marketplace/template-manifest.zod.ts(五个墓碑和提示语);packages/cli/src/commands/package/publish.ts(visibility 拒绝 + 四键警告);scripts/sync-template-versions.mjs(去掉 specVersion 盖章,自测基线重测)。
  • 两分钟验证:pnpm --filter @objectstack/spec exec vitest run --project repo src/marketplace/template-manifest-inert-keys-retirement.test.ts,预期 12 个用例全绿(逐键拒绝、两条替代指引、publisher/isStarter 对照、tsc 墓碑、登记、全树清单扫描)。
  • 需要你做的:无,席位落地。

What changes

  • Spec (TemplateManifestSchema). name, specVersion, skills, scaffold and visibility become retiredKey() tombstones. The schema is non-strict, so a bare delete would strip silently; a tombstone refuses each key at its own path with the prescription `TemplateManifest.<key>` was removed in @objectstack/spec 18 (ADR-0049) — …. visibility names os package publish --visibility; specVersion names manifest.engines.protocol.
  • ADR-0087 bookkeeping. Five RETIRED_KEYS_BY_MAJOR[18] rows (marketplace/TemplateManifest:<key>), one D3 semantic entry marketplace-template-manifest-inert-keys-retired naming all five, and its STEP18_RATIONALE fragment. No D2 conversion: this file is not a stack collection member (the category precedent on the same schema), so os migrate meta has nothing to rewrite.
  • Liveness. The five template_manifest rows stay dead with a REMOVED note; the scaffold.* children are removed. publisher / isStarter notes now say they are pending a maintainer decision (hotcrm is a producer).
  • os package publish. A manifest declaring visibility is refused before any network call (retiredVisibilityRefusal(), the category shape). name, specVersion, skills and scaffold get a non-fatal warning naming them for deletion; the warning list no longer carries visibility.
  • create-objectstack. The blank template's manifest drops name, specVersion, skills and scaffold; scaffolding strips every retired key (category and these five) from the template's manifest via stripRetiredManifestKeys instead of writing name; the key list RETIRED_TEMPLATE_MANIFEST_KEYS is pinned to the spec's RETIRED_KEYS_BY_MAJOR rows by scaffold-retired-manifest-keys.test.ts.
  • Release tooling. scripts/sync-template-versions.mjs no longer stamps specVersion; its self-test battery floors are re-measured (Control A 14, Control C 4, Control I now pins engines.protocol at 9), and the create-objectstack version tests follow.
  • Shipped manifests. packages/apps/connector-{docusign,github,stripe}/objectstack.manifest.json drop name and specVersion.
  • Pins. template-manifest-inert-keys-retirement.test.ts (spec, repo project): refusal per key at its own path with its prescription, the --visibility and engines.protocol pointers, a publisher / isStarter control, @ts-expect-error tsc pins, the ADR-0087 registration (five rows, one D3 entry, no D2 conversion), and a tree-scoped walk of every objectstack.manifest.json under packages/ and examples/ (at least 4 found, none declaring a retired key). It is listed in vitest.repo-tests.json and is a heldBy witness in scripts/cross-package-test-inputs.mjs. check:template-manifests' self-test base drops the retired keys and gains a RED specVersion fixture. Spec, CLI and create-objectstack fixtures that still wrote the keys are trimmed.
  • Docs. Getting-started tree, upgrading guide (step 3 is now engines.protocol only), marketplace handbook and CLI reference. The content/docs/references/ page is regenerated, not hand-edited.
  • Changeset. @objectstack/spec minor, @objectstack/cli minor, create-objectstack patch, with both declaration lines, the BREAKING banner, the FROM → TO list and the adr-0087 marker.

Why

ADR-0049 enforce-or-remove. Measured on origin/main 115a43a (per-key table on #31035): no reader of the file's name, specVersion, skills or scaffold exists in the CLI, create-objectstack or hotcrm; create-objectstack wrote name and never read it back, the protocol check reads the artifact's engines.protocol, and the skills install is the whole catalog. os package publish read visibility only to warn that it ignores it.

Verification

All runs on head 4f2f3c85 in a dedicated worktree, base origin/main 115a43ad, except where the rework block below says 5da9dd3c.

Rework on 5da9dd3c (contract-review should-fix, commits 073cd355 + 5da9dd3c): create-objectstack vitest run Test Files 25 passed (25), Tests 374 passed (374), pnpm typecheck :: exit 0; dispatch-gates --commands 136 derived, 136 run, 0 NOT-MEASURED, all exit 0 (record /mnt/project-files/spec-seat-1/r2026-11-06/out/31035-gates-ran-5da9dd3c.list). Ablations restored after each: the call site back to delete m.name → 1 red; dropping category from the list → 2 red; specVersion added to connector-github's manifest → check:template-manifests exit 1.

Tests
  • pnpm --filter @objectstack/spec test (local project): Test Files 820 passed (820), Tests 25908 passed, 1 todo
  • pnpm --filter @objectstack/spec test:repo (repo project, which now carries the new pin): Test Files 91 passed (91), Tests 1551 passed (1551)
  • pnpm --filter @objectstack/spec check:test-typecheck :: exit 0; check:generated :: exit 0 after a declaration build
  • create-objectstack vitest run: Test Files 24 passed (24), Tests 369 passed (369); pnpm typecheck :: exit 0
  • @objectstack/cli unit project: Test Files 396 passed (5 of them re-run after a declaration build; their first local run was red on a dts-less build, not on the diff), pnpm typecheck :: exit 0
  • packages/apps/connector-{docusign,github,stripe} pnpm test (objectstack validate && vitest run): 28, 14 and 20 tests passed
  • node scripts/sync-template-versions.mjs --self-test :: exit 0, 44 assertions

The CLI, create-objectstack and connector runs are on 58b292e7; 4f2f3c85 only adds the new test's path to packages/spec/vitest.repo-tests.json.

Ablations

Through scripts/ablation-replace.mjs, each restored to the committed blob afterwards:

  • Visibility refusal guard in publish.ts disabled (if (false && …)): 1 test red (the retired visibility › refuses …); restored, green.
  • specVersion tombstone swapped back to z.string().optional(): 2 tests red (refusal at its own path; the engines.protocol prescription); restored, green.
Derived gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 134 families for the 41-path change set; all 134 ran with exit 0. --ran reconciliation: 134 derived, 134 run, 0 NOT-MEASURED, 0 UNRUN. One finding on the first pass, fixed in 4f2f3c85: check:cross-package-test-inputs asked for the new tree-walking test to be listed in vitest.repo-tests.json. Four first runs answered exit 3 (prerequisite, nothing measured) and were re-run green after the prerequisite: check-plugin-teardown-shape --self-test (pinned fixture commit fetched), check:skill-examples, check:dual-build-cjs-loads and check:i18n-coverage (declaration build of ./packages/* and ./packages/*/*). check-platform-checklist --changed-from origin/main :: exit 0 (no anchored file in the change set). Record: /mnt/project-files/spec-seat-1/r2026-11-06/out/31035-gates-ran-4f2f3c85.list.

Dogfood: full battery on 4f2f3c85 (pnpm --filter @objectstack/dogfood test, no shard, closure built first, under the verify lock): Test Files 429 passed, 3 skipped (432); Tests 3569 passed, 41 skipped (3610); lock VERDICT command-exit 0.

Acceptance notes

  • publisher and isStarter are excluded, pending their own decision card: hotcrm's scripts/lib/marketplace-listing.mjs#packageUpsertBody sends both from its manifest in service mode, so their fate is a maintainer decision. The control case in the new test holds that this PR did not take them.
  • Known downstream follow-up (hotcrm carrier, next platform bump, same carrier as category). hotcrm's objectstack.manifest.json still writes name, specVersion and skills, and its test/docs-declared-versions.test.ts and test/marketplace-listing.test.ts parse or stamp them; both go red on the first hotcrm bump to a spec carrying this change.
  • Design choice the seat may strike: the four inert keys only warn in os package publish (every project scaffolded from the blank template before this release carries them); only visibility refuses.
  • check:template-manifests now walks create-objectstack's templates and packages/apps/* (a zero-file guard per root; 4 manifests parsed), so the three connector manifests are gated by check:generated too. The new test's tree-scoped walk also covers them and is registered as a heldBy witness in scripts/cross-package-test-inputs.mjs.
  • .github/workflows/release.yml still has a comment mentioning the manifest stamp. It is a governed file, so it is left for a maintainer edit; nothing executes it.
  • The Claim said Metadata-protocol: none; a tombstone is narrowed, declared here per the seat's ruling.
  • The harness assignee / reviewer step is skipped: the PR is opened through the relay as a draft, and the seat lands it.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes (CI-owned).
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.

🤖 Generated with Claude Code

https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW

…specVersion, skills, scaffold, visibility)

Each is a retiredKey tombstone on TemplateManifestSchema (ADR-0049
enforce-or-remove): nothing read the file's value of any of them, and
os package publish read visibility only to warn that it ignores it.
publisher and isStarter are left for a maintainer decision.

WIP: generated artefacts, docs and changeset follow.

Claude-Session: https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW
Co-authored-by: Claude <noreply@anthropic.com>
…e manifest retirement

Claude-Session: https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW
Co-authored-by: Claude <noreply@anthropic.com>
…roject

Its tree-scoped walk reads every objectstack.manifest.json under packages/
and examples/, so it belongs in vitest.repo-tests.json, whose turbo hash
moves with those inputs (check:cross-package-test-inputs).

Claude-Session: https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 7 package(s): @objectstack/connector-app-docusign, @objectstack/connector-app-github, @objectstack/connector-app-stripe, @objectstack/cli, create-objectstack, objectstack-blank, @objectstack/spec, touching 17 documentable anchor(s). ⚠️ 17 changed file(s) yielded no anchor (packages/apps/connector-docusign/objectstack.manifest.json, packages/apps/connector-github/objectstack.manifest.json, packages/apps/connector-stripe/objectstack.manifest.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

12 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/declarative-endpoints.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/concepts/metadata-lifecycle.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/deployment/cli.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema), os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/deployment/index.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/deployment/marketplace-handbook.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), isStarter (literal, a string literal in UNCARRIED_MANIFEST_KEYS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema), os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/deployment/publish-and-preview.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/protocol/kernel/index.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/protocol/kernel/lifecycle.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/protocol/kernel/metadata-service.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/protocol/objectql/index.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/protocol/objectql/schema.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/upgrading.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))

⛔ 8 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-1.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))
  • content/docs/releases/v17/17-3.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))
  • content/docs/releases/v17/17-4.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))
  • content/docs/releases/v17/17-5.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema), os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/releases/v17/17-6.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema), os package publish (command, read off packages/cli/src/commands/package/publish.ts))
  • content/docs/releases/v17/17-7.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))
  • content/docs/releases/v18/18-0.mdx (via specVersion (symbol, a field of const object RETIRED_TEMPLATE_MANIFEST_KEY_REASONS), specVersion (literal, a string literal in RETIRED_INERT_MANIFEST_KEYS; a string literal in RETIRED_TEMPLATE_MANIFEST_KEYS; a string literal in TemplateManifestSchema))
  • content/docs/releases/v9.mdx (via os package publish (command, read off packages/cli/src/commands/package/publish.ts))

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.

What this run could not see
  • 17 changed file(s) yielded no anchor (packages/apps/connector-docusign/objectstack.manifest.json, packages/apps/connector-github/objectstack.manifest.json, packages/apps/connector-stripe/objectstack.manifest.json, …) — pages documenting those are invisible to this run
  • 6 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 63 of 224 client-bound route-ledger rows — the other 161 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 161: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 57 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 104 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 181 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json cad764d3e2d71210bce48653aa823a3b940071d7 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d612225a19facf86f8151be852edb74705d31764 — the merge of head 5da9dd3c992a8eebf258e58c3c74543dad86853f into base cad764d3e2d71210bce48653aa823a3b940071d7, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d612225a19facf86f8151be852edb74705d31764 && git checkout d612225a19facf86f8151be852edb74705d31764
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cad764d3e2d71210bce48653aa823a3b940071d7 5da9dd3c992a8eebf258e58c3c74543dad86853f && git checkout -B drift-repro cad764d3e2d71210bce48653aa823a3b940071d7 && git merge --no-ff 5da9dd3c992a8eebf258e58c3c74543dad86853f

node scripts/docs-audit/affected-docs.mjs --json cad764d3e2d71210bce48653aa823a3b940071d7

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs cad764d3e2d71210bce48653aa823a3b940071d7 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibzzqHZdAVqgBAvxXKB

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4f2f3c8534bcaa614c1c994ea35f58894b4ed5ca
Local-runs: none
Scope: packages/spec/src/marketplace/template-manifest.zod.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__name.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__scaffold.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__skills.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__specVersion.ts, packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__visibility.ts, packages/spec/src/migrations/entries/semantic/18.marketplace-template-manifest-inert-keys-retired.ts, packages/spec/src/migrations/registry.ts.template + packages/spec/src/shared/retired-key.ts, packages/spec/src/marketplace/taxonomy.zod.ts (the category guidance precedent), packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__category.ts, packages/spec/src/migrations/entries/semantic/18.marketplace-category-retired.ts, packages/spec/src/migrations/entries/README.md, packages/spec/scripts/build-migration-registry.ts, packages/spec/src/marketplace/template-manifest-inert-keys-retirement.test.ts, packages/spec/vitest.repo-tests.json, scripts/cross-package-test-inputs.mjs, packages/spec/scripts/check-template-manifests.ts, packages/spec/liveness/template_manifest.json, packages/spec/scripts/liveness/tombstoned-row-status.test.ts, packages/spec/authorable-surface/marketplace.json, packages/spec/json-schema.manifest/marketplace.json, packages/cli/src/commands/package/publish.ts, packages/cli/test/package-publish-uncarried-manifest-keys.test.ts, packages/create-objectstack/src/index.ts, packages/create-objectstack/src/templates/blank/objectstack.manifest.json, packages/apps/connector-docusign/objectstack.manifest.json, packages/apps/connector-github/objectstack.manifest.json, packages/apps/connector-stripe/objectstack.manifest.json, scripts/sync-template-versions.mjs, .changeset/31035-template-manifest-inert-keys-retired.md, scripts/check-adr-0087-registration.mjs, scripts/check-changeset-no-major.mjs, .claude/skills/spec-property-retirement/SKILL.md, docs/adr/0049-no-unenforced-security-properties.md, .github/workflows/release.yml (line 459, read for deviation 3), objectstack-ai/hotcrm objectstack.manifest.json and scripts/lib/marketplace-listing.mjs (cross-repo, read for the publisher / isStarter exclusion). The rest of the diff (docs prose, create-objectstack and CLI test fixtures, liveness README and state-counts, the strictness-ledger counts) is the lane seat's ACCEPT at TIER_DEFAULT.

Inputs read: card #31035 body and all three comments (triage 6694088718, claim 6695659165, dev report 6698579350); PR #31064 body, 41-file list and the net diff against main (merge-base 115a43a); the 44 check-runs on the head, read once at 2026-11-06T23:33Z.

Check-runs at read time — success: Auto Label, Dogfood Regression Gate, Plan Test Core shards, Spec property liveness, Check PR Size, Check Changeset, filter, Governed Surface Queue Guard, Flag docs affected by code changes, Check Documentation Links, PR-body guards. Pending (in_progress, reported as pending, not polled): Test Core 1/8 to 8/8, Build Core, Build Docs, Build UI Deps, Live E2E UI, Type Check (workspace, source gates, consumer gates, debt ledger), Lint (ESLint, docs and code guards, repo and tooling gates, CI/release/engine gates, PM dispatch-gates, three scoped), Scaffold with repo dist. Skipped: UI Published Artifacts, Temporal Conformance, Dogfood Verify CLI, Dogfood Regression Gate shards, Schedule gate, Registry canary, 18.x upgrade harness, Packed-tarball smoke. No check-run has concluded failure. The pending families (build-schemas gate b, the migrate-sentence and rationale-merge pins, the repo-project pin, tsc on the @ts-expect-error channel) are the landing condition the seat holds, not a finding here.

① Derived judgments

  • TemplateManifestSchema accept set narrows: name, specVersion, skills, scaffold, visibility go from accepted (two of them formerly REQUIRED: name, specVersion) to refused at their own path with a prescription (retiredKey() = z.never().optional(), invalid_type). Right: every row was dead in the ledger since feat(spec)!: one marketplace taxonomy — business functions, industries, a solution type and maturity replace the single category #30980 and the tree confirms no reader (publish loads the file as raw JSON and reads only manifestId, the facets, tagline, readmePath, translations, tags; create-objectstack wrote name and never read it; protocol-version-gap.ts reads the artifact's engines.protocol). visibility is the one override of an inherited live key (CreatePackageRequestSchema.visibility stays live, the .extend() re-declares it on the file only). Right.
  • TemplateManifest (z.infer, published type) narrows: the five keys type as the retirement mark; displayName-only objects still satisfy it. Right, and the pin test exercises it on both channels.
  • RETIRED_KEYS_BY_MAJOR[18] gains five exact marketplace/TemplateManifest:KEY rows, spelled as authorable-surface/marketplace.json spells them (minus the [RETIRED] mark); filenames derive from the ids per shardNameFor. MIGRATIONS_BY_MAJOR[18].semantic gains one D3 entry. ALL_CONVERSIONS unchanged — no D2, correct for a file that no stack-collection seam ever loads (same reasoning as category and PackageDependency:optional). Right.
  • authorable-surface/marketplace.json: five rows gain [RETIRED] — the tombstone route's expected reading. json-schema.manifest/marketplace.json unchanged — also expected: it lists defs (marketplace/TemplateManifest stays emitted), and feat(spec)!: one marketplace taxonomy — business functions, industries, a solution type and maturity replace the single category #30980 moved it only because it unpublished two defs. api-surface unchanged — a key-level narrowing is invisible there (skill §4). Right.
  • Liveness ledger: five rows keep status: "dead" with a REMOVED note (the tombstone keeps the key in the walked shape; deleting the rows would red UNCLASSIFIED), scaffold's two child rows removed (a z.never has no children; keeping them would red ORPHAN). tombstoned-row-status.test.ts holds dead for every [REMOVED] key on this schema. Spec property liveness concluded success on the head. Right.
  • os package publish (@objectstack/cli): accept set narrows for a manifest declaring visibility (exit 1 before any network call, after the category refusals and before readPublishTaxonomy); the four inert keys add a non-fatal warning. Published export UNCARRIED_MANIFEST_KEYS narrows (visibility leaves); RETIRED_INERT_MANIFEST_KEYS, retiredInertManifestKeys, retiredVisibilityRefusal are added. The partition pin (every TemplateManifestSchema key is sent, warned about, refused, retired, or scaffold-only) still covers the whole shape with no key in two buckets. Right.
  • create-objectstack: the blank template drops four keys (it never wrote visibility); rewriteProjectIdentity deletes name instead of writing it. Output change for scaffolded projects, no accept-set change of its own. Right as far as it goes (see ④ should-fix on the remote-template path).
  • Shipped manifests: the three connector apps drop name and specVersion; the tree holds exactly four objectstack.manifest.json files, all under packages/, none under examples/, and the new tree-scoped walk asserts that set with an anti-vacuity floor of 4. check:template-manifests self-test gains a RED retired-key fixture (discovery 7 to 8). Right.
  • scripts/sync-template-versions.mjs stops stamping specVersion and drops range from loadScaffolderVersion(); battery floors re-measured. Release tooling, unpublished. Right.
  • Docs: content/docs/references/marketplace/template-manifest.mdx is the regenerated projection (five never rows carrying the prescriptions). skills/** carries no page teaching objectstack.manifest.json (grep at head: zero hits), so Skills-docs: none holds.
  • Governed surfaces: none in the file list; Governed Surface Queue Guard success. Not a Tier H PR.

② Semver level

  • Changeset 31035-template-manifest-inert-keys-retired.md: @objectstack/spec minor, @objectstack/cli minor, create-objectstack patch. Under ruling A as the gate reads it (check-changeset-no-major.mjs: a declared kind must grade at least one moved package minor or above), narrowed with spec and cli at minor is correct; create-objectstack at patch is allowed (the protocol it publishes against is unchanged; it only stops writing refused keys). Check Changeset concluded success.
  • Declarations: PR body and changeset both carry Clause-②: no (narrowing) with the (narrowing) arm and Metadata-protocol: narrowed; the title carries !; the changeset carries the BREAKING banner, the FROM → TO list for all five keys with the one-line fix each, and exactly one marker adr-0087: registered marketplace-template-manifest-inert-keys-retired, whose id this diff itself adds under entries/semantic/. Matches the diff: a tombstone is narrowed (AGENTS.md Post-Task §3, ruling A), so the Claim's Metadata-protocol: none was wrong and the PR's declared deviation (1) is the correct correction.
  • No major (launch-window rule); the breaking semantics ride the banner and the ADR-0087 disposition. Level judged right.

③ Boundary flags

  • open_questions[0] — publisher and isStarter: verified in the cited code, hotcrm scripts/lib/marketplace-listing.mjs#packageUpsertBody sends publisher: manifest.publisher and is_starter: manifest.isStarter ?? false to the hosted packages route in service mode, so they are producer-read in a first-party repo and not mechanical dead. Exclusion is right; the control case in the pin holds the schema still accepts both. ESCALATED: the seat files the decision card (options A/B/C of the per-key table) — not a defect of this PR.
  • Deviation (1) Metadata-protocol none to narrowed: answered in ②, correct.
  • Deviation (2) warn-not-refuse for name/specVersion/skills/scaffold at publish: holds. Publish never read them, so refusing would break every pre-release blank scaffold for no effect gained; visibility is refused because an author who wrote it meant to set something publish would otherwise silently not set — the same asymmetry category landed with. The warning text names the deletion and the schema refusal. Not struck.
  • Deviation (3) .github/workflows/release.yml:459 still reads "per bundled template objectstack.config.ts / objectstack.manifest.json" beside a stamp that now touches only objectstack.config.ts: stale comment, governed file (Tier H), nothing executes it. SHOULD-FIX as a maintainer follow-up, not here.
  • Deviation (4) harness assignee / reviewer step skipped, PR opened as draft through the relay; (5) gate sweep re-run after a container restart — process notes, no bearing on the tree.
  • out_of_scope[0] hotcrm carrier: verified, hotcrm objectstack.manifest.json still writes name, specVersion (^18.3.0), skills (and category), and test/docs-declared-versions.test.ts / test/marketplace-listing.test.ts read them. hotcrm pins its spec version, so nothing reds until its next bump; same carrier as category. ESCALATED: attach to the hotcrm platform-bump card so the manifest and both tests are fixed in that bump.
  • out_of_scope[1] check:template-manifests walks only create-objectstack's templates: the three connector manifests are covered only by the repo-project pin. SHOULD-FIX (low): widen the gate's walk to packages/apps/* so a connector manifest regressing reds check:generated, not only test:repo.
  • "Design choice the seat may strike" — same as deviation (2); not struck.
  • Premise: holds on the tree at the head — no reader of the file's value of any of the five keys exists in packages/cli/src, packages/create-objectstack/src, packages/spec, scripts/, ui/, nor in hotcrm's publish script (which hard-codes visibility: 'marketplace' and never reads the manifest's).

④ Design judgment

Each hits file judged against the spec-property-retirement playbook at the head, ADR-0049 / ADR-0087 as amended, the category precedent on the same schema (#30980), and the first consumers (os package publish, check:template-manifests, create-objectstack, the new pin).

  • packages/spec/src/marketplace/template-manifest.zod.ts — HOLDS. Route: retiredKey() tombstones on a non-strict shape (a bare delete would strip silently; the schema header says so). Guidance strings follow the five house conventions: backticked fully-qualified key first (TemplateManifest.KEY), was removed in @objectstack/spec 18 (ADR-0049), one dash clause on why it was inert, imperative fix (Delete the key.) plus the mechanism that actually works (manifestId / package.json name, manifest.engines.protocol, the whole skills catalog, nothing, --visibility), and no os migrate meta sentence because no conversion exists — identical to retiredCategoryGuidance; the migrate-sentence pin judges only strings carrying the marker, so none is owed. No issue number in a guidance string (#31035 sits in comments only). The visibility override on an inherited live key is the one subtle piece and it is correct: .omit().extend() replaces the inherited field on this file only, CreatePackageRequestSchema is untouched, and the pin's --visibility control proves the flag path still sends it. Tried to break it: "visibility": null is refused by both the schema (z.never) and publish (!== undefined), consistent; specVersion was REQUIRED before, so any manifest that satisfied the old schema now fails — which is the declared BREAKING, carried by the banner and the FROM → TO list. The tsc channel is moot for a JSON file; the real author channels are the $schema JSON-schema projection (regenerated from the never), check:template-manifests, and publish's warning / refusal — the same channels category has.
  • packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__{name,scaffold,skills,specVersion,visibility}.ts — HOLD. Filenames derive from the ids per shardNameFor (/ and : to __), entry values are the exact authorable-surface spellings, one file per key with its why-comment above export const entry (the shape build-schemas gate b matches as a set member); the comment prose names retiredKey() the way the category entry already on main does, so no live textual ratchet is newly matched. Registered in the same PR as the tombstones, so gate (b2) "registration with no consumer" cannot fire. The ageing clock starts at 18, as the ADR-0087 2026-10-26 amendment wants.
  • packages/spec/src/migrations/entries/semantic/18.marketplace-template-manifest-inert-keys-retired.ts — HOLDS. One D3 entry per family, kebab id ending in -retired, surface as prose joined with /, replacement, reason (states the D3-not-D2 ground: no seam ever loads this file; moving visibility to the command line is a judgement) and acceptanceCriteria (every claim in it is true on the tree: tsc mark, parse refusal, publish refusal for visibility before any network call, four shipped manifests clean, create-objectstack no longer writes name). It names publisher / isStarter as deliberately outside the family, which is what a reader of the upgrade guide needs.
  • packages/spec/src/migrations/registry.ts.template — HOLDS. The STEP18_RATIONALE fragment is inserted at its sorted position (between marketplace-category-retired and memory-persistence-placeholder-refused), order: 120 is one more than the previous highest (119), so it renders directly after the category sentence, and its text ("It also retires five ...") reads correctly there. Only the template's prose was touched; registry.ts is generated and git-ignored.

Findings:

  • must-fix: none.
  • should-fix (this PR or a tight follow-up): packages/create-objectstack/src/index.ts#rewriteProjectIdentity deletes only name. A REMOTE template authored before this release still carries specVersion, skills, scaffold (and possibly visibility), and the scaffolder — which already rewrites this file and knows the retired set — hands the new project a manifest the schema refuses and publish warns on (or, for visibility, refuses). Delete all five retired keys there; a template's visibility has no meaning for the scaffolded project.
  • should-fix (maintainer, governed): .github/workflows/release.yml:459 comment still names objectstack.manifest.json as a stamped surface.
  • should-fix (low): widen check:template-manifests' walk to packages/apps/* so the connector manifests are gated by check:generated as well as the repo-project pin.
  • escalations: file the publisher / isStarter decision card; attach the hotcrm manifest and two tests to hotcrm's next platform-bump card.

Implemented-by: claude/project-thread-5986qn
Reviewed-by: session_01L9qibzzqHZdAVqgBAvxXKB

VERDICT: PASS

…lding, not only name

rewriteProjectIdentity deleted only `name`, so a template authored before
spec 18 would still hand a new project `specVersion`, `skills`, `scaffold`,
`visibility` or `category`, which the schema refuses (and publish refuses
`visibility` and `category`). One list, RETIRED_TEMPLATE_MANIFEST_KEYS in
rewrite-identity.ts, now drives the strip; a test pins it equal to the
spec's RETIRED_KEYS_BY_MAJOR rows for TemplateManifest (which is how it
picked up `category`, retired in #30980), proves a pre-18 manifest parses
after the strip, and pins the call site.

check:template-manifests also walks packages/apps, so the three connector
manifests are parsed by the gate too.

Claude-Session: https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl and removed size/l labels Nov 6, 2026
… subpaths to source

The retired-key pin imports both; through dist/ its verdict would follow
build state instead of the checkout (check:test-source-alias).

Claude-Session: https://claude.ai/code/session_01L9qibz5AiZzgut2WFyXVqW
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-tesla · session_01L9qibzzqHZdAVqgBAvxXKB

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5da9dd3c992a8eebf258e58c3c74543dad86853f
Local-runs: none
Scope: delta review of 4f2f3c8..5da9dd3 (two commits, 073cd35 and 5da9dd3, verified as the whole range) on top of record 6698836904 (PASS at 4f2f3c8). The hits files of the schema face (packages/spec/src/marketplace/template-manifest.zod.ts, the five retired-keys entries, the D3 entry, registry.ts.template) are byte-identical across the delta; the prior ④ on them stands. Delta files judged: packages/create-objectstack/src/rewrite-identity.ts, packages/create-objectstack/src/index.ts, packages/create-objectstack/src/scaffold-retired-manifest-keys.test.ts, packages/create-objectstack/vitest.config.ts, packages/spec/scripts/check-template-manifests.ts, packages/spec/src/marketplace/template-manifest-inert-keys-retirement.test.ts (comment only), .changeset/31035-template-manifest-inert-keys-retired.md + cited: packages/create-objectstack/src/scaffold-namespace-cap.test.ts and the SCAFFOLD_NAMESPACE_MAX_LENGTH docblock (the restated-constant precedent), packages/create-objectstack/package.json (deps, scripts), packages/create-objectstack/tsconfig.json, packages/spec/src/migrations/index.ts and packages/spec/src/marketplace/index.ts (the subpaths the pin imports), packages/spec/package.json (prepare runs gen:migration-registry), scripts/check-test-source-alias.mjs (rules 3 and 5), packages/spec/src/migrations/entries/retired-keys/18.marketplace__TemplateManifest__category.ts and packages/cli/src/commands/package/publish.ts retiredCategoryRefusal (for the category judgment), and the git tree's four objectstack.manifest.json files.

Inputs read: card #31035 (no comment newer than the dev report 6698579350); PR #31064 body, 44-file list and the delta diff; the 44 check-runs on 5da9dd3c, read once at 2026-11-07T00:35:48Z. The dev's gate list /mnt/project-files/spec-seat-1/r2026-11-06/out/31035-gates-ran-5da9dd3c.list (136 families, every line exit 0, including check:template-manifests, check:test-source-alias, check:cross-package-test-inputs, check:undeclared-dep-imports, check-changeset-no-major, check-adr-0087-registration) was read as the dev's account only; the check-runs are the gate verdicts. The three stale PR-body spots the seat will patch (head sha, create-objectstack count, the check:template-manifests note) are excluded as instructed.

Check-runs at read time — success: Auto Label, Dogfood Regression Gate, Plan Test Core shards, Spec property liveness, Check PR Size, filter, Governed Surface Queue Guard, Flag docs affected by code changes, Check Documentation Links, PR-body guards. Pending (in_progress, reported as pending, not polled): Test Core 1/8 to 8/8, Build Core, Build Docs, Build UI Deps, Live E2E UI, Type Check (workspace, source gates, consumer gates, debt ledger), Lint (ESLint, docs and code guards, repo and tooling gates, CI/release/engine gates, PM dispatch-gates, three scoped), Scaffold with repo dist, Check Changeset. Skipped: eight opt-in or push-only families. No check-run has concluded failure. Test Core (where the new create-objectstack pin and the spec repo-project pin run) and Lint (where check:test-source-alias runs) are the landing condition the seat holds.

① Derived judgments

  • create-objectstack: rewriteProjectIdentity now strips six retired keys (category, name, specVersion, skills, scaffold, visibility) from any template's manifest before stamping it, via stripRetiredManifestKeys over RETIRED_TEMPLATE_MANIFEST_KEYS in rewrite-identity.ts. Scaffolded output changes for a pre-18 remote template only; the bundled blank template carries none of the six, so its output is unchanged. No accept-set or published-API change (rewrite-identity.ts is an internal module of a bin). Right.
  • check:template-manifests (spec, check:generated member) widens from one root to two: packages/create-objectstack/src/templates and packages/apps, each with its own zero-file guard; today it parses 1 + 3 manifests. Self-test unchanged (8 fixtures, empty-tree guard). Repo gate only. Right.
  • create-objectstack vitest.config.ts: two anchored source aliases (@objectstack/spec/marketplace, @objectstack/spec/migrations) beside the existing kernel one. Test resolution only. Right.
  • Changeset: the create-objectstack bullet now states the strip (six keys, one list pinned to the registry rows); levels and both declaration lines unchanged. Right.
  • Spec repo-project pin: comment updated to say the gate now covers packages/apps; no assertion changed. Right.

② Semver level

Unchanged from record 6698836904: @objectstack/spec minor, @objectstack/cli minor, create-objectstack patch, Clause-②: no (narrowing), Metadata-protocol: narrowed, one adr-0087: registered marker. The delta adds a scaffolder fix and a gate widening, neither of which moves a protocol kind, so create-objectstack at patch stays right under ruling A (spec and cli already carry the kind at minor). Check Changeset is in_progress on this head — pending.

③ Boundary flags

  • Should-fix 1 of the prior record (remote-template scaffold still carrying retired keys): CLOSED. The strip covers every key the spec registry names for marketplace/TemplateManifest, the call sits in the same rewrite block as displayName / namespace, and the pin proves a pre-18 manifest (all six keys present) becomes one TemplateManifestSchema accepts with nothing else touched.
  • Low item of the prior record (connector manifests gated only by the repo-project pin): CLOSED. check:template-manifests walks packages/apps with a per-root guard, so a connector manifest regressing now reds check:generated as well as test:repo.
  • Stripping category: RIGHT. category is a retiredKey() tombstone on this schema since feat(spec)!: one marketplace taxonomy — business functions, industries, a solution type and maturity replace the single category #30980 (registry row marketplace/TemplateManifest:category under 18), and os package publish refuses a manifest that declares it before any network call. A scaffolded project is a new package whose facets (businessFunctions, industries, solutionType) are its author's declaration; the template's category described the template, and the D3 entry marketplace-category-retired says the facet choice for a manual value is a judgement no mechanical edit can make, so translating it in the scaffolder would be wrong and leaving it would make the project's first publish fail on a key its author never wrote. The one list, pinned to the registry rows rather than hand-chosen, is also what makes the earlier PR's omission visible: the pin found category missing, which is the mechanism working.
  • Restating the list rather than importing it: SAFE. Same ground as SCAFFOLD_NAMESPACE_MAX_LENGTH: @objectstack/spec is a devDependency of create-objectstack (runtime deps are chalk, commander, validate-npm-package-name), a value import would be bundled into the published scaffolder (check:undeclared-dep-imports), and the pin is exactly the spec registry rows for TemplateManifest holds equality in BOTH directions over every major of RETIRED_KEYS_BY_MAJOR (a key retired in the spec but missing here reds; a key here the spec never retired reds). The pin judges source, not a build: the aliases resolve both subpaths to packages/spec/src/**/index.ts, migrations/index.ts exports RETIRED_KEYS_BY_MAJOR from the generated registry.ts, and that file exists in any installed checkout because spec's prepare runs gen:migration-registry. check:test-source-alias keeps the aliases honest for any future value import. The list cannot age out from under the scaffolder either: registry rows are append-only history, so a key whose tombstone leaves the schema one major later stays in the list and the strip stays correct.
  • Escalations from the prior record stand unchanged: the publisher / isStarter decision card; hotcrm's manifest and two tests on its next platform-bump card; the .github/workflows/release.yml:459 comment (governed, maintainer).

④ Design judgment

  • packages/create-objectstack/src/rewrite-identity.ts — HOLDS. Six keys as const in a fixed order; stripRetiredManifestKeys deletes by own-property (so a null value is stripped too, matching the schema's refusal of null under z.never) and returns what it found. The docblock names both source PRs, the two channels that refuse the keys, and the precedent for restating; it carries the precedent's phrase "two-dependency npx entry point" while package.json lists three runtime dependencies — pre-existing wording of the precedent, not a finding.
  • packages/create-objectstack/src/index.ts — HOLDS. The strip runs before displayName / namespace are stamped and before description is dropped, inside the existing try block, so the write-back carries the cleaned manifest. The return value is discarded, so a template author scaffolding from their own pre-18 template sees the keys vanish without a word. Tried to break it: no scaffolded value is lost that the project author wrote (the keys come from the template, never from the user), and the blank template never reaches the branch. Should-fix (low, non-blocking): print one line naming the dropped keys when found is non-empty, so the removal is audible on the remote-template path the way the playbook wants removals to be.
  • packages/create-objectstack/src/scaffold-retired-manifest-keys.test.ts — HOLDS. Four pins: list equality with the registry rows (both directions, not vacuous: six keys against a toEqual), every listed key refused by the schema, the strip of a full pre-18 manifest leaves exactly namespace / displayName / isStarter / readmePath and then parses, and a source-text pin that index.ts's manifest block calls stripRetiredManifestKeys(m); and no longer delete m.name (justified: index.ts runs program.parse() on import and the bundled template cannot reach the branch end-to-end). One forward-looking note, should-fix (low): names only keys the schema refuses assumes every registry row is still a tombstone in the shape; when a tombstone ages out one major later (ADR-0087 as amended 2026-10-26) the non-strict schema will accept that key again and this case reds, which the age-out PR will then have to narrow — either scope that case to keys whose describe still carries [REMOVED], or leave it as the deliberate tripwire and say so in the file header. Not a defect today.
  • packages/create-objectstack/vitest.config.ts — HOLDS. Both aliases are end-anchored regexes resolving to a file under src/, which is the gate's rule 5 (an unanchored object alias would swallow deeper subpaths and resolve through a file extension); first-match ordering is unaffected because the three patterns are disjoint.
  • packages/spec/scripts/check-template-manifests.ts — HOLDS. MANIFEST_ROOTS with a per-root zero-file guard means a moved packages/apps reds instead of going quiet, which is strictly stronger than the prior single-root guard; findManifests tolerates a missing directory and leaves the report to the caller; the walk skips node_modules and dist; the git tree holds exactly four tracked manifests, all under the two roots, so the gate and the spec repo-project walk now hold the same set by two routes. Self-test and its discovery count are unchanged.
  • .changeset/31035-template-manifest-inert-keys-retired.md — HOLDS. The reworded create-objectstack bullet is true of the tree (six keys, one list pinned to the registry rows, name no longer written); the FROM → TO list, banner, declaration lines and the adr-0087 marker are untouched.

Findings:

  • must-fix: none.
  • should-fix (low): rewriteProjectIdentity discards stripRetiredManifestKeys's return; print the dropped keys when any were found.
  • should-fix (low): decide now how names only keys the schema refuses behaves at tombstone age-out (scope to [REMOVED]-marked keys, or document it as the tripwire).

Implemented-by: claude/project-thread-5986qn
Reviewed-by: session_01L9qibzzqHZdAVqgBAvxXKB

VERDICT: PASS

This branch has not been deployed

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

Labels

documentation Improvements or additions to documentation size/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant