Repository navigation
feat(spec)!: one marketplace taxonomy — business functions, industries, a solution type and maturity replace the single category - #30980
Conversation
Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…ario cards re-tagged Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…changeset Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…into claude/issue-30904-marketplace-taxonomy
…plate manifest Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…t configs Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…li at minor Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 8 package(s): 13 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 181 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e2e24b15b6ea88b503300b66f70205a9e40eaee0 && git checkout e2e24b15b6ea88b503300b66f70205a9e40eaee0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 69e133ea0d9734dd0447d08875bbd8e3af0e1517 95b6e070942530efdf3f947b7f90e8d089fc9ea7 && git checkout -B drift-repro 69e133ea0d9734dd0447d08875bbd8e3af0e1517 && git merge --no-ff 95b6e070942530efdf3f947b7f90e8d089fc9ea7
node scripts/docs-audit/affected-docs.mjs --json 69e133ea0d9734dd0447d08875bbd8e3af0e1517
|
…into claude/issue-30904-marketplace-taxonomy
…ess function; the taxonomy module header opens below level 1 Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…c block Claude-Session: https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2 Co-authored-by: Claude <noreply@anthropic.com>
…into claude/issue-30904-marketplace-taxonomy # Conflicts: # docs/qa/scenarios/crm_account_with_contacts.json # packages/qa/scenarios/src/schema.ts
|
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2 Contract-review-claim: session_01Q4EjjRhUZjdy8VXs6Kkur2 · head 95b6e07 · 2026-11-06T18:30Z Face: declared |
|
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2 Contract reviewServed-tier: Reviewed read-only at 2026-11-06T18:41Z against the card's rulings (comments 6683195046 and 6691021157), the net diff ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Findings, by weight: must-fix: none. Should-fix: ①5 route the Implemented-by: VERDICT: PASS |
|
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2 席位核对 · ACCEPT · 2026-11-06T18:44ZHead-sha:
|
…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>
Fixes #30904
Clause-②: yes (narrowing) — new facet keys and
maturitywiden;categoryretired and refused by name narrows; a contract review runs before the queueMetadata-protocol: narrowed
维护者速读
businessFunctions(1–3 个,第一个是主职能)、行业industries(0–3 个,可选)、类型solutionType(app / template / connector),另加独立的maturity(beta / stable)。两份旧的category按 ADR-0087 退役,带category的发布被点名拒绝并给出改法。category同时表示“给谁用”和“是什么”,平台和 cloud 各有一套取值且互相对不上;按维护者裁决 D1–D3 合并为一套。空白模板的"starter"会让每个新项目第一次发布被 cloud 拒绝,这里一并删掉。category的清单在发布时被拒(附映射)。运行时启动不读这些 schema,不受影响。cloud 在 cloud#3661 落地前会忽略新字段,且其提交审核仍要求category,需要先落 cloud 再发 CLI(见 Acceptance notes)。回滚即 revert 本 PR。packages/spec/src/marketplace/taxonomy.zod.ts(取值、上限、交叉校验、旧值映射);packages/cli/src/commands/package/publish.ts的retiredCategoryRefusal/readPublishTaxonomy;packages/spec/liveness/template_manifest.json(新纳入的台账与消费方)。pnpm --filter @objectstack/spec exec vitest run src/marketplace/taxonomy.test.ts,预期 39 个测试全部通过;在任一objectstack.manifest.json里加"category": "finance"后跑os package publish,预期在联网前被拒,提示"businessFunctions": ["finance"], "solutionType": "app"。What changes
spec — the taxonomy, defined once
packages/spec/src/marketplace/taxonomy.zod.ts, exported from@objectstack/spec/marketplace:MarketplaceBusinessFunctionSchema(11 values, ruling §2 F1) andMarketplaceBusinessFunctionsSchema(1–3 distinct, ordered, the first is primary).MarketplaceIndustrySchema(10 values, §2 F2 / D1) andMarketplaceIndustriesSchema(0–3 distinct; no catch-all value — absence means cross-industry).MarketplaceSolutionTypeSchema(app|template|connector; notheme, ADR-0019) andPackageMaturitySchema(beta|stable, D3 — a flag, not a facet).explainSolutionTypeContradiction(solutionType, { installModes, connectorCount })— the publish cross-check:templateneedsunmanagedamong the install modes (ADR-0131 D6),connectorneeds a declared connector. One rule and one sentence for the CLI and, through cloud#3661, the cloud publish gate.LEGACY_CATEGORY_MAPPING— ruling §3.1 + §3.2: every value of the retired spec enum, the cloud list and its folded aliases →mapped/manual(author chooses the functions; never a catch-all) /unlistable(ADR-0019).operationsis mapped and flaggedreview.PackageSchema/CreatePackageRequestSchema/UpdatePackageRequestSchema/TemplateManifestSchema/MarketplaceListingSchemacarry the four keys. Row shapes defaultsolutionType: 'app'andmaturity: 'stable'; requests and the manifest carry no default (absent stays absent, so a re-publish never rewrites a stored value).businessFunctionsis required on the listing.MarketplaceSearchRequestSchemafilters bybusinessFunctions/industries/solutionTypes(OR inside a facet, AND across) andmaturity;MarketplaceSearchResponseSchema.facetshas one count list per facet.spec — the ADR-0087 retirement of both category schemas
categoryis aretiredKey()tombstone on all six schemas (andfacets.categorieson the search response). The schemas are not strict, so a bare deletion would strip silently; the tombstone refuses with the prescription in both channels (tsc, parse).TemplateManifestSchemare-declares its own so the refusal names the file an author writes.MarketplaceCategorySchema/MarketplaceCategoryandPackageCategorySchema/PackageCategoryare deleted:RETIRED_DEFS_BY_MAJOR[18](two entries), the deliberatejson-schema.manifest/marketplace.jsondeletion, and the twoscripts/lib/renamed-defs.tsrows retired with a note.RETIRED_KEYS_BY_MAJOR[18]entries, one D3 semantic entrymarketplace-category-retired, oneSTEP18_RATIONALEfragment. No D2 conversion: none of these shapes is a stack collection member (themarketplace/PackageDependency:optionalprecedent), so a conversion would be a transform no seam runs.category: publish refuses it (the CLI before any network call; a parse of any of the schemas, with the prescription). Boot: nothing — no runtime boot readsobjectstack.manifest.json, a package row or a listing (measured: the only readers of the file areos package publish, create-objectstack andcheck:template-manifests). So there is nothing for the boot to tolerate or refuse; ADR-0087's load-path conversion has no seam here, and the decision recorded in the D3 entry is refuse-at-publish, migrate-stored-rows-once (cloud#3661, readingLEGACY_CATEGORY_MAPPING).authorable-surface,authorable-defaults,json-schema.manifest,api-surface,export-origins,declaration-map,content/docs/references/marketplace/**, the strictness-ledger counts;dropped-refinements.baseline.jsondeclares the uniqueness refinement on the two facet lists (no projectable arm for "distinct items" exists); six ADR-0122 isomorphic pins (837 → 841).CLI, first-party manifests, scenario library, docs
os package publish(packages/cli/src/commands/package/publish.ts):categoryinobjectstack.manifest.jsonand the now-hidden--categoryflag before any network call, naming the facets for the value it held (fromLEGACY_CATEGORY_MAPPING); astartercopied from the blank template is told it never classified the package;solutionTypeagainst the built artifact (resolveInstallModes+ the artifact'sconnectors), and sends them asbusiness_functions/industries/solution_type/maturity; an absent facet is not sent.[finance], connector-github[it], connector-docusign[collaboration, legal], eachsolutionType: connector(their listing tests now assert the stack declares the connector that makes them one). The create-objectstack blank template dropscategory: "starter"and declares no facet: create-objectstack copies this file into every scaffolded project, so a facet here would be inherited by projects it does not describe, and atemplatethere would be refused by the new cross-check on the first publish (the blank config permits onlymanaged).packages/qa/scenarios: the hand copySCENARIO_CATEGORIESis gone; a card's key isbusinessFunctions, typed by the spec schema. The nine cards are re-tagged per ruling §3.3.content/docs/deployment/marketplace-handbook.mdx,content/docs/deployment/cli.mdx,content/docs/protocol/kernel/plugin-spec.mdx,docs/qa/scenarios/README.md— no hand copy of the value lists (the handbook links the generated taxonomy reference).objectstack.manifest.json(TemplateManifestSchema) enters the ledger astemplate_manifestviaSPEC_ONLY_SCHEMAS.businessFunctions/industries/maturityareplanned, naming their consumers — the cloud catalog (objectstack-ai/cloud#3661) and the console marketplace card the seat files;solutionTypeislivethrough the publish cross-check;categoryis the tombstone row. The platform checklist carries a measured waiver for it (no checklist item can drive a hosted publish).Tests
Measured on head
7cd7fe941c(the commits after it are the changeset declaration and nothing a test reads).Suites
pnpm --filter @objectstack/spec test— exit 0 (818 + 1 files; the one earlier red, the dropped-refinements header totals, fixed and re-run green: 27/27).pnpm --filter @objectstack/spec typecheck— exit 0.pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2— exit 0,Test Files 390 passed (390),Tests 5496 passed (5496). Integration tier declared to CI (no spawn entry or driver path touched).pnpm --filter @objectstack/cli typecheck— exit 0.pnpm --filter create-objectstack test/typecheck— exit 0 / 0.pnpm --filter @objectstack/scenarios test/typecheck— exit 0 / 0.test— exit 0 each.packages/spec/src/marketplace/taxonomy.test.ts(39 cases: one block per rule — F1, F2, F3, D3, the cross-check, the refusal on every schema, the registration, the mapping) andpackages/cli/test/package-publish-taxonomy.test.ts(14 cases).Ablations — one per rule, each through scripts/ablation-replace.mjs (mutation proven on disk, restore proven against HEAD)
.max(MAX_BUSINESS_FUNCTIONS,→.max(99,.min(1,→.min(0,.max(MAX_INDUSTRIES,→.max(99,if (false)if (false)CreatePackageRequest.categorycategoryif (false)--categoryif (false)Every leg ends
git diff HEADempty for its file; the final tree carried 0 modified source paths.Acceptance notes
Every caller class that newly sees a refusal
categoryonPackage/CreatePackageRequest/UpdatePackageRequest/TemplateManifest/MarketplaceListing/MarketplaceSearchRequest—tscerror (the retired mark).categorypresent — parse refusal with the prescription. Measured at cloud8f574b9: cloud reads onlyPackageSchema.shape.manifestIdandCreatePackageRequestSchema.shape.tags, so its pin bump is not refused by this.MarketplaceCategorySchema/PackageCategorySchema/ their types — compile error (zero importers here and in cloud at8f574b9).os package publishwithcategoryinobjectstack.manifest.json— refused before any network call. This includes every project already scaffolded from the blank template ("starter").os package publish --category …— refused.solutionType: templatewithout theunmanagedinstall mode, orconnectorwith no connector, or a malformed facet — refused (new keys, no existing caller).MarketplaceListingSchemawithoutbusinessFunctions— refused (new requirement; no producer in this repo).category, or a business function outside the facet — loader refusal.check:template-manifests— any shipped manifest withcategoryreds.Sequencing, declarations and what this PR deliberately does not do
8f574b9the publish route picks named body keys, so the four facets are dropped until objectstack-ai/cloud#3661 lands, andmarketplace-policy.tsstill requirescategoryat marketplace submission. A CLI released from this PR before cloud#3661 deploys can no longer satisfy that submission rule for a package that has no stored category. Order: cloud#3661 (accept the facets, require a business function instead ofcategory) before the next CLI release reaches authors.Check Changesetaccepts and what AGENTS.md defines; if the seat keepsnone, the changeset must go back topatchand the level gate stays red on the moved authorable-surface shard.8f574b9'sisValidPackageCategory("starter")returnsfalse(the route answers 400 on it atpackage-publish.ts:896). CLI half read: at the base, publish sentm.categoryverbatim and create-objectstack copied the template'scategoryinto every project.customer-service,trade-distribution, …) are the ruling's verbatim values and match the marketplace enums' existing spelling (usage-based,in-review); Prime Directive Implement ObjectStack protocol specification with Zod schemas and TypeScript interfaces #3's snake_case is the general rule for machine names.explainSolutionTypeContradictionlives in the spec as a pure function besideresolveInstallModes, so the CLI and the cloud gate share one rule and one sentence instead of two copies.name,specVersion,skills,scaffold, and the warned-and-unsentvisibility/publisher/isStarterhave no reader of the file's value. Recordeddeadin the new ledger, not retired here.ui/(the seat files that card);docs/design/marketplace-publishing.md(a design record whose--categorysearch example describes a command this repo does not ship).Verification
Gates — dispatch-gates.mjs derivation, reconciled
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran …→✓ 119 derived famil(ies) accounted for — 115 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3).check:skill-examples(client-react has no.d.ts),check:i18n-coverage(@objectstack/connector-slackunbuilt),check:dual-build-cjs-loads(packages outside the closure unbuilt),check:type-check-debt(its own locked closure build never got the shared lock).check:llms-txt(the marketplace row and the inventory total),check:platform-checklist(thetemplate_manifestwaiver),check:test-source-alias(the connector configs alias@objectstack/spec/kernelto source).pnpm --filter @objectstack/spec check:generated— all artifacts current after--fix;check:liveness,check:spec-parsed-alias,check-adr-0087-registration,check-changeset-no-major(level axis, against this body) — exit 0.node scripts/check-platform-checklist.mjs --changed-from origin/main— exit 0 (no anchored file in the change set).Dogfood battery
Owed on the final head (title
!,(narrowing)arm). Its reading is in the report comment on the card.Generated by Claude Code