Repository navigation
Description
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Filing gate: ③ a task on the maintainer's word. In the repo:cloud#3 seat session (2026-11-06), on how gallery (scenario demo) templates appear in the marketplace, the maintainer said, verbatim:
- 「至于分类以后有个模板的时候,我们还是需要有行业或者是多角度的分类吧。」
- 「以后可能会有上百个模板呀」
Record: objectstack-ai/cloud#3649 comment 6682254371.
Owner. The repo:cloud lane (repo:cloud#3) follows it to close. The taxonomy is a platform contract (@objectstack/spec), so the definition lands here first and cloud consumes it (maintainer, 2026-11-06: 「平台的问题应该在平台处理」, #30765). Step 1 is a proposal for the maintainer, not code.
维护者速读
以后模板会有上百个,只按一个分类找不过来,需要多维度分类,比如按行业、按部门或职能,可能还有场景类型、公司规模,可以组合筛选。另外现在有个现成的问题:平台 spec 和 cloud 各有一套分类,取值不一样。平台写的是 integration、developer-tools,有 erp、ai;cloud 写的是 integrations、devtools,有 sales、marketing、app。分类应该只有一套,定在平台,cloud 照用。第一步只出方案(有哪几个维度、每个维度有哪些取值、是否多选),给你看过再开工。
Today (measured)
- Spec:
packages/spec/src/marketplace/marketplace.zod.ts:166-183,MarketplaceCategorySchema, a single-value enum: crm, erp, hr, finance, project, collaboration, analytics, integration, automation, ai, security, developer-tools, ui-theme, storage, other. The listing also has free-formtags(:255-256) and atagsfilter (:417-418). - Cloud:
packages/service-tenant/src/marketplace-taxonomy.ts:48-64,PACKAGE_CATEGORY_OPTIONS, a strict single-valuesys_package.category: app, crm, sales, marketing, finance, hr, operations, project, productivity, data, integrations, devtools, utility, other. The browse page derives its category chips from it.is_starteris a separate template flag. - The two lists disagree in spelling (
integration/integrations,developer-tools/devtools) and in membership. Nothing reconciles them.
Asked
- Proposal (step 1). Facets and their values, for example industry, function or department, solution type (app / template / connector / theme), and maybe company size. State, for each facet, whether it is single or multi-valued, and what becomes of the current
categoryand of freetags. Show the mapping from both existing enums. Post it on this card for the maintainer. ⛔ No code before their word. - After approval: the spec defines the facets once. The listing and manifest schemas carry them (
Clause-②: judged then). The liveness ledger names the consumers: cloud's catalog (follow-up objectstack-ai/cloud#3661) and the console marketplace UI (ui/).
Related: the package-level Beta marker for gallery templates (objectstack-ai/cloud#3649, in rework) may also belong in the listing schema. That card reports its carrier.
Dedupe (2026-11-06): repo:objectstack-ai/objectstack marketplace category taxonomy is:issue → 4 hits (#30289, #13285 closed and on other subjects; #12708, #6015 seat posts); marketplace facet filter industry is:issue → 3 hits (#29002 a QA run, #15213 closed, #12708 a seat post). None covers a marketplace taxonomy.
Suggested-grade: p2 · the scenario library's gallery grows toward hundreds of templates.
Activity
objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p1 ·
domain:spec·needs-user-decision· 2026-11-06T10:53Z⛔ Not a claim.
needs-user-decision), not inpm:queue. Once ruled, the cloud catalog follow-up is objectstack-ai/cloud#3661.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Ruling: objectstack#30904 step 1, taxonomy proposal · maintainer · 2026-11-06T10:54Z
Provenance:
repo:cloud#3seat sessionsession_01Q4EjjRhUZjdy8VXs6Kkur2.industriesis defined in the spec now. It is optional and multi-valued (0–3). The filter is hidden while no listing carries it. Values follow §2 F2. Onlymanufacturingandtrade-distributionare measured; the rest may be edited at review.categorycategoryschemas retire, under ADR-0087 conversion. They are replaced bybusinessFunctions(1–3, ordered) andsolutionType. A publish carryingcategoryis refused by name with the repair. Cloud migrates its rows once (cloud#3661), and unmapped values go totags.maturity: 'beta' | 'stable'on the spec listing and package schemas. It is not a facet. It replaces the closed cloud#3649 / PR #3660.Execution: the spec work below (§6, objectstack#30904) is claimed and dispatched by this seat as epic-adjacent work. cloud#3661 stays
pm:blockedon it. The consoleui/part is filed as its own objectstack card.市场多维分类方案(objectstack#30904 第 1 步,待维护者审)
维护者速读
tags保留为自由文本,只用于搜索。is_starter不动。category;D3 Beta 的放法。三项都有推荐,见下文。ui/改筛选栏和 Beta 徽标。1. 现状实测
1.1 现在有几份分类表 [实测]
packages/spec/src/marketplace/marketplace.zod.ts:167-183MarketplaceCategorySchemaMarketplaceListingSchema.category(:253,必填)、MarketplaceSearchRequestSchema.category(:415)、MarketplaceSearchResponseSchema.facets.categories(:469-472)。除 spec 自己的测试和生成文档外,objectstack 和 cloud 都没有运行时代码 import 这三个 schema(git grep MarketplaceListingSchema|MarketplaceSearchRequestSchema在 spec 包外命中 0 次)。所以它是"声明了但没人执行"的契约。packages/spec/src/marketplace/package.zod.ts:47-51PackageCategorySchemaz.string().min(1))PackageSchema.category(:236)、CreatePackageRequestSchema.category(:309)、UpdatePackageRequestSchema.category(:331);TemplateManifestSchema继承前两者(即objectstack.manifest.json)。这是 CLI 发布和 cloud 发布门真正经过的一条路径。packages/service-tenant/src/marketplace-taxonomy.ts:48-64PACKAGE_CATEGORY_OPTIONSField.selectpackages/qa/scenarios/src/schema.ts:89-105SCENARIO_CATEGORIESdocs/qa/scenarios/*.json的categoryui/packages/i18n/src/namespaces/marketplace/en.ts:223-239(另有 8 种语言)MarketplacePage.tsx:562-566、MarketplacePackagePage.tsx:1425-1426packages/service-tenant/src/translations/{zh-CN,ja-JP,es-ES}.ts(category.options)sys_package的选择框和安装对话框直接后果 [实测 + 推断]:
crm / hr / finance / project / other五个值重合。app、integrations、devtools、sales、marketing、operations、productivity、data、utility这些值在控制台都找不到翻译,筛选按钮上直接显示原始英文值(MarketplacePage.tsx:564-565的兜底逻辑)。[推断:按代码路径判断,未在浏览器里看]integration/integrations,developer-tools/devtools。packages/create-objectstack/src/templates/blank/objectstack.manifest.json:8写的是"category": "starter"。CLI 发布时会把它原样带上(packages/cli/src/commands/package/publish.ts:639-640),cloud 发布门则会用 400 拒绝它(package-publish.ts:896-899,isValidPackageCategory('starter')返回 false)。也就是说,从空白模板新建的项目不改分类,第一次发布就会失败。[推断:代码路径,未实际运行]1.2 cloud 侧有哪些读者 [实测]
sys-package.object.ts:420-425category(严格 select);:427-431tags(JSON 文本);:502-511is_starterservice-cloud/src/routes/package-publish.ts:896-899校验 category;:187-240校验 tags(≤20 个,每个 ≤64 字符,:169-170);:952-955只有第一方 service 发布才能设is_starterroutes/package-export.ts:408,438固定写category: 'app'、is_starter: falsesys-package.object.ts:292my_apps用category = 'app'作判别条件routes/marketplace.ts:319,358-360?category=精确匹配;?q=只匹配display_name / manifest_id(:351-356),不搜 tagsmarketplace-snapshot.ts按PUBLIC_PACKAGE_FIELDS写出 category / tags / is_starter;apps/cloud/cloudflare/worker.ts:787-813同样只做?category=精确过滤,q同样不搜 tagspages/sys-package.page.ts:97-110筛选按钮从 category 的 select 选项自动生成;:175-179"App Templates" 页的基础过滤条件是is_starter = trueservice-cloud/src/migrations/category-taxonomy-migration.ts+marketplace-taxonomy.ts:79-92的别名表(tools→devtools、business/ops→operations、analytics→data、connector(s)/integration→integrations、starter→app 并置is_starter=true……)scripts/marketplace/publish-connector-apps.mjs:64-66,239-248把清单里写的 category(finance / devtools / productivity)一律改写成integrations,只在日志里提一句1.3 objectstack 侧有哪些读者 [实测]
os package publish --category(publish.ts:401-403),回落顺序是 flag 优先、清单次之(:639-640);tags 原样转发(:646);isStarter和publisher由平台决定,不随发布携带(:286-289)。MarketplacePage.tsx:552-577。分类按钮从已加载的条目里去重生成,然后在客户端过滤;目录一次只取 100 条(:407limit: 100)。卡片上显示一个 category 徽标(:965-966),详情页也是一个(MarketplacePackagePage.tsx:1542)。控制台不读 tags,也不读 is_starter(grep命中 0 次)。SCENARIO_CATEGORIES(④),9 张卡各带一个 category。1.4 目录里今天有什么 [实测,仓库可见范围;生产数据查不到]
app.objectstack.hotcrmobjectstack.manifest.jsonpackages/apps/connector-stripe/objectstack.manifest.jsonpackages/apps/connector-github/…packages/apps/connector-docusign/…create-objectstack/src/templates/blank/…com.local.samplescripts/dev-local/seed.mjs:256-266package-export.ts:408examples/app-crm、app-todo、app-showcaseexamples/sys-package.object.ts:509的字段说明;CI 实际是否这样发布,未核实)场景卡(
docs/qa/scenarios/*.json):crm_account_with_contacts=crm;invoice_with_line_items=finance;travel_expense=finance;leave_balances=hr;order_to_cash=sales;project_with_tasks=project;asset_lifecycle、requisition_to_receipt、service_desk 三张都是 operations。这张表说明了两件事:
integrations,"财务"这个信息就丢了(publish-connector-apps.mjs:64-66)。app也不是职能,而是"整套应用"这个类型。operations一个值里装了行政资产、采购、客服三种不同的职能。HotCRM 实际覆盖销售、客服、营销(看它的 tags 和src/{sales,service,marketing}),却只能挂一个crm。1.5 场景库会覆盖哪些领域 [实测,cloud#3600]
template-registry.ts:46-52RETIRED_TEMPLATES):todo、compliance、content、contracts、procurement。全部是职能型。cloud docs/strategy/marketplace-bootstrap.md§2 列的 M0 模板:Customer Operations、Support Tickets、Approval Workflow。同样是职能型。结论: 证据强力支持"职能"多选,以及把"类型"拆出来。"行业"在仓库数据里还没有需求,只有维护者的原话。"规模"既没有原话也没有数据。
1.6 先例 [实测]
git grep -i 'industry|solutionType|vertical'在packages/spec/src里只命中示例字段(比如 CRM 示例里的industry选择字段);在docs/adr里只有"industry precedent"这类泛指。plugin.zod.ts:110CONSUMER_INSTALLABLE_TYPES = ['app']):消费者市场只有"应用"这一个名词,driver、plugin、theme 都不上架。所以 ① 里的ui-theme、storage在 ADR-0019 下根本不可能出现在列表里,"类型"维度也只能是应用内部的细分,不能是packageType。manifest.zod.ts:640-666installModes):unmanaged表示"装进去归客户、可整包接管",这正是模板的语义。cloud#3600 的 Q5 v2 已裁:场景库毕业的模板走 unmanaged 上架,hot* 套件走 managed 上架。2. 维度定义
总则
packages/spec/src/marketplace/taxonomy.zod.ts导出各维度的闭合枚举,以及一张旧值映射表LEGACY_CATEGORY_MAPPING。cloud 的迁移和 CLI 的拒绝提示都从这里 import,不再各抄一份。showCount: true)和控制台今天已经是这个做法,保留。F1 职能
businessFunctions—— 多选,1–3 个,有序,第一个是主职能为什么要有: 场景库和现有所有模板都按职能来组织,这是用户找模板最主要的入口。字段名不用
functions,避免和函数、hook 这些概念混淆。salesmarketingsrc/marketing;场景库第 2 阶段待补customer-servicesrc/servicefinancehrsupply-chainadministrationprojectlegalitdevtools、①developer-tools、connector-githubcollaborationcollaboration、③productivity、cloud#3567 "日常办公"(会议纪要、公文、制度问答)F2 行业
industries—— 多选,0–3 个,可选;不填就是"跨行业通用"为什么要有: 维护者明确提出要按行业分类。模板到了上百个以后,"制造业的采购"和"零售业的采购"需要分开找。今天的数据里还没有一个包带行业,所以这个维度做成可选,没有值时不显示(见 D1)。
manufacturingtrade-distributionretaillogisticsprofessional-servicesfinancial-serviceshealthcareeducationpublic-sectorreal-estate-constructiongeneral或cross-industry这样的值。 不填就是通用。如果设一个"通用"值,就等于同一件事有两种写法,而且 AI 作者会给每个包都勾上它。F3 类型
solutionType—— 单选,默认app为什么要有: 把"它是什么"从分类里拆出来,连接器就不会再丢掉自己的职能。模板和完整应用的区别(安装后归谁、有没有升级义务)是用户在安装前就需要知道的。字段名不用
kind或type,避免和packageType、页面的kind冲突,也和 #30904 卡面上 "solution type" 的说法一致。apptemplateresolveInstallModes(manifest)包含unmanaged,否则用 400 明确拒绝connectortheme。 ADR-0019 规定主题随应用发布、不单独上架,仓库里也没有主题包。以后真出现了,按"值怎么增加"的规则补。template其实可以完全从installModes推导出来。如果你倾向"能推导的就不声明",可以只对template改成推导,其余不变。]不采纳的维度:规模
仓库里没有任何包、场景卡、ADR 或策略文档提到公司规模,套餐分档也是另一套机制。按"零需求就不做"的原则,这一维度不加。
不进入分类的字段
tags?q=应该同时搜 tags:今天 API(marketplace.ts:351-356)和 Worker(worker.ts:805-810)都不搜,tags 等于没人读。这一项放进 cloud#3661。is_starterpackage-publish.ts:952-955;CLI 不携带这个字段,见publish.ts:286-289)。它和solutionType: template不是一回事:前者回答"平台推不推荐",后者回答"装上以后归谁"。旧值category = 'starter'映射成solutionType: template,is_starter维持原状。以后要不要把两者合并,等 Q5 毕业机制跑起来再看,本轮不提。3. 映射表
规则:
other(marketplace-taxonomy.ts:122),新方案取消other。3.1 spec
MarketplaceCategorySchema(①)→ 新维度①在运行时没有读者(见 1.1),所以这张表是语义上的对应关系,不涉及数据迁移。
3.2 cloud
PACKAGE_CATEGORY_OPTIONS(③)及别名 → 新维度(对应 cloud#3661 的数据迁移)my_apps视图的过滤条件改为solution_type = 'app'is_starter维持原状(旧迁移已经置过位)3.3 仓库内现有包的建议标注 [方案,供 spec/cloud 卡落地时参考]
4. Beta 标记怎么放(cloud#3649 已裁)
已裁内容:Beta 是包级别的"尚不成熟"标记,在列表和详情页显示徽标;不是版本的预发布;Beta 模板保留它真实的分类;设置 Beta 不能绕过审核。
推荐:作为独立字段,不做成维度。 在 spec 的
MarketplaceListingSchema、PackageSchema、CreatePackageRequestSchema和UpdatePackageRequestSchema上加maturity: 'beta' | 'stable',可选,不填视为stable。理由:
solutionType: beta-template),就会把 Beta 包从它真实的类型里挤出去。preview这类状态,只需加一个值,不必再加一个布尔字段。今天只有两个值,比布尔多出的维护成本几乎为零。ui/里显示,载体就是 spec 的列表 schema(「平台的问题应该在平台处理」)。PR feat(spec): publish ISecurityService and enforce it at both ends #3660 返工时,直接用这个字段,不再在 cloud 里另起一个字段。搜索请求可以带一个
maturity过滤参数(例如"只看稳定版"),但它不出现在维度的筛选按钮里。5. 需要你裁的事
D1 行业维度现在就进 spec 吗?
一句话问题: 你要按行业找模板,但今天没有一个包、一张场景卡标了行业,而且场景库按你的指示先做职能型场景。
industries:可选、多选 ≤3、闭合枚举(10 个值,或者只留有证据的 2 个)。没有值时,控制台和 cloud 都不显示这组筛选按钮业务含义: A 像商场开业前就把楼层导视牌的位置留好,店没来就空着;B 是等店进驻了再挂牌;C 是让每家店自己在门口贴纸条。
自检:只看①选 A;②③④ 是否翻转:否(② 的零数据靠"可选、无值不显示"来消化,不改选项)。
置信缺口:10 个值里只有 2 个有实测依据。如果你希望只放有证据的值,选"A,只留 2 个"即可,后续的值走需求拉动。
D2 现有的
category怎么处理?一句话问题: 现在一个"分类"同时表示"给谁用"和"是什么",只能选一个,而且平台和 cloud 的取值不一样。连接器因此丢掉了自己的职能,一个
operations里混着三种职能。category,由businessFunctions(有序,第一个是主职能,显示在卡片徽标上)和solutionType取代。spec 里的两份 category(① 和 ②)一起删除,按 ADR-0087 退役;带 category 的发布被明确拒绝,提示里写出对应的新维度值。cloud 用一次性迁移把存量数据转过去category-taxonomy-migration.ts(cloud 曾把 category 从自由文本收紧为 select,并做过同样的一次性迁移)category作为单值的"主分类"(值域等于职能),另加多选的businessFunctionscategory,取 ① 和 ③ 的并集,只额外加一个industries业务含义: A 是把"商品类别"和"适用部门"做成两组货架标签;B 是在原来那张标签上再贴一张意思差不多的新标签;C 是把两个旧标签本子合成一本继续用。
自检:只看①选 A;②③④ 是否翻转:否。
置信缺口:
solutionType由作者声明再交叉校验(推荐做法),还是对template直接从installModes推导(见 F3),两种都可以。差别只在template这一个值上,不影响 A 的方向。D3 Beta 标记放哪里?
一句话问题: 你已经定了要给不成熟的模板打 Beta 标记,并且保留它的真实分类。现在要决定这个标记放在分类体系里,还是放在分类体系外。
maturity: 'beta' | 'stable'(可选,默认 stable);控制台在列表和详情页显示徽标;搜索可以按它过滤,但它不出现在维度筛选按钮里solutionType: beta-template)beta业务含义: A 像商品上贴的"新品试用"小标签,商品仍然摆在原来的货架上;B 是把试用品单独搬到另一个货架;C 是让店员手写一张便签贴上去。
自检:只看①选 A;②③④ 是否翻转:否。
置信缺口:
maturity由谁设置。推荐由作者声明(画廊导出器是第一方,第三方作者自己标 Beta 也是诚实的自我标注),毕业就是重新发布时不再带这个标记。如果你希望只有平台能去掉 Beta,就需要像isStarter一样把它列为"平台决定的字段",这一点可以另外裁。6. 裁定后的分工
顺序:spec 先合并并发版 → cloud 升级依赖版本 → cloud 和控制台并行。
objectstack#30904(spec,也就是本卡)
packages/spec/src/marketplace/taxonomy.zod.ts:MarketplaceBusinessFunctionSchema、MarketplaceIndustrySchema、MarketplaceSolutionTypeSchema、PackageMaturitySchema、LEGACY_CATEGORY_MAPPING(即 3.1 / 3.2 的表)。PackageSchema、CreatePackageRequestSchema、UpdatePackageRequestSchema(从而TemplateManifestSchema)、MarketplaceListingSchema:加businessFunctions/industries/solutionType/maturity;MarketplaceSearchRequestSchema的过滤参数和MarketplaceSearchResponseSchema.facets改成按维度计数。spec-property-retirement手册和 ADR-0049/0087,退役MarketplaceCategorySchema、PackageCategorySchema和三处category键,同步更新生成的基线和文档。ui/)、CLI 发布。os package publish:从清单读取各维度并携带;退役--categoryflag,改成带改法提示的明确拒绝。starter改为solutionType: template)。packages/qa/scenarios:删除SCENARIO_CATEGORIES副本,改为 import spec 的维度;9 张卡按 3.3 重标。(这属于场景库席位 epic cloud#3600 的范围,需要协调。)cloud#3661(cloud)
sys_package加列:business_functions、industries(multiselect)、solution_type(select)、maturity(select)。选项直接从 spec 的枚举生成,不再保留第二份枚举。删除marketplace-taxonomy.ts和PACKAGE_CATEGORY_OPTIONS。template和connector的交叉校验,拒绝带category的请求;环境导出写solution_type: 'app';my_apps改为按solution_type过滤;提交审核策略的"必须有 category"改为"至少 1 个职能"。package-category-taxonomy-v2:使用 spec 的LEGACY_CATEGORY_MAPPING,支持 dry-run,dry-run 列出需人工补填和需复核(operations)的包。?q=同时搜 tags。sys-package.page.ts:userFilters换成各维度。连接器上架脚本改为写solution_type: connector,并保留清单里原来的职能。maturity。控制台
ui/(objectstack,归入 #30904 的同一组卡或作为它的子卡)MarketplacePage.tsx:用三组维度筛选替代单行分类按钮,其中行业组没有值时隐藏;类型适合做成页签或分段按钮。maturity === 'beta');详情页显示全部维度。marketplace.category.*换成marketplace.businessFunction.*、marketplace.industry.*、marketplace.solutionType.*、marketplace.maturity.beta,9 种语言都要补。MarketplacePage.tsx:407)。模板到了上百个以后,必须改成服务端过滤加分页。cloud#3600 Q5 已经把"分页"列为市场的能力缺口,建议把维度过滤和分页放在同一张卡里做。objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Claim: PM loop round R5 · 2026-11-06T10:55Z
Session:
session_01Q4EjjRhUZjdy8VXs6Kkur2Account:
hotlong(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-30904-marketplace-taxonomyWorktree:
/home/user/w30904/objectstack-issue-30904Domain:
domain:spec(claimed by therepo:cloudlane that filed it; every posteddomain:specseat reads vacant)Seat:
repo:cloud#3File surface: a new
packages/spec/src/marketplace/taxonomy.zod.ts(facets +maturity);marketplace.zod.tsandpackage.zod.ts(the twocategoryschemas retired under ADR-0087, new keys on listing/package schemas); the liveness ledger;packages/clipublish (forward the new keys, refusecategory); first-party manifests and templates (create-objectstackblank template'scategory: "starter");packages/qa/scenariosSCENARIO_CATEGORIES; tests and generated baselinesContainer & model:
M,mode:subagent,model: TIER_DEFAULT,effort: highClause-②: yes (widening) — new facet keys and
maturity; it also narrows (categoryretired and refused by name); a contract review runs before the queueMetadata-protocol: none
Responsibility: one marketplace taxonomy for hundreds of templates | none | every package listing, CLI publish and the console marketplace
Thread-read: 6683195046
Premise: holds — four disagreeing category lists measured in the proposal (this card's ruling comment)
Serial constraints cleared: objectstack#30880 (in flight) edits
packages/spec/src/data/field.zod.ts/object.zod.ts, not the marketplace files; no other in-flight claim onmarketplace.zod.ts/package.zod.tsfound.Carrier: objectstack (spec, CLI, first-party manifests). Cloud's adoption is objectstack-ai/cloud#3661; the console
ui/is its own objectstack card.objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
os-dev-report
{ "issue": 30904, "status": "done", "branch": "claude/issue-30904-marketplace-taxonomy", "pr": "https://github.com/objectstack-ai/objectstack/pull/30980", "session": "session_01Q4EjjRhUZjdy8VXs6Kkur2", "premise_still_valid": true, "summary": "The marketplace taxonomy is defined once in packages/spec/src/marketplace/taxonomy.zod.ts: businessFunctions (11 values, 1-3 distinct, ordered, required on a listing), industries (10 values, 0-3, no catch-all), solutionType (app|template|connector, no theme), maturity (beta|stable, a flag not a facet), explainSolutionTypeContradiction (template needs unmanaged, connector needs a declared connector) and LEGACY_CATEGORY_MAPPING (rulings 3.1 + 3.2, aliases included, manual never folded into a catch-all). Both category schemas retire under ADR-0087: retiredKey tombstones on Package, CreatePackageRequest, UpdatePackageRequest, TemplateManifest, MarketplaceListing, MarketplaceSearchRequest (+ facets.categories), MarketplaceCategorySchema/PackageCategorySchema deleted, 6 RETIRED_KEYS + 2 RETIRED_DEFS entries, D3 marketplace-category-retired, no D2 conversion (no stack collection member; boot never reads these shapes, so nothing to tolerate or refuse at boot — refuse at publish, cloud migrates stored rows once). os package publish refuses category (file or hidden --category) before any network call naming the facets for the value, validates and forwards the four facets, and cross-checks solutionType against the artifact. Connector manifests re-tagged; the blank template drops category starter and declares no facet (it is copied into every scaffolded project; a template there would be refused by the cross-check). SCENARIO_CATEGORIES is gone: cards carry businessFunctions typed by the spec, and the gallery exporter that landed on main meanwhile reads the primary function. objectstack.manifest.json enters the liveness ledger as template_manifest naming cloud#3661 and the console card as consumers. Declaration lines deviate from the claim (see deviations).", "tests": "Final head 2f243e0d68 (verification pass ran on 598f76c3b3; the one later commit regenerates content/docs/references/marketplace/taxonomy.mdx only): spec test exit 0; spec repo scripts/file-description.test.ts exit 0 (was CI-red on the old head: the new module header opened at level 1, demoted); spec typecheck 0; cli unit exit 0 Test Files 394 passed (394) Tests 5559 passed (5559); cli typecheck 0; create-objectstack test/typecheck 0/0; scenarios test/typecheck 0/0; connector-stripe/github/docusign test 0/0/0. New: spec src/marketplace/taxonomy.test.ts (39), cli test/package-publish-taxonomy.test.ts (14). Ablations, one per rule via scripts/ablation-replace.mjs (mutation landed by anchor count + blob change, restore proven git diff HEAD empty, tree 0 modified after): F1 max 1 failed/39; F1 min 1/39; distinct 2/39; F2 max 1/39; template cross-check 1/39; connector cross-check 1/39; CreatePackageRequest.category tombstone deleted 2/39; CLI manifest refusal 2/14; CLI flag refusal 1/14; CLI cross-check wiring 3/14. Blank-template defect reproduced in two halves, not end to end: cloud half executed (cloud 8f574b9 isValidPackageCategory('starter') = false, the route's 400 at package-publish.ts:896); CLI half read at base (publish sent m.category verbatim; create-objectstack copied it into every project).", "gates": "dispatch-gates --ran on 2f243e0d68: 119 derived, 116 run exit 0, 3 NOT-MEASURED exit 3 (check:skill-examples — client-react has no .d.ts; check:dual-build-cjs-loads — packages outside the closure unbuilt; check:type-check-debt — its locked closure build never got the shared lock), 0 UNRUN. Fixed on the way: check:llms-txt, check:platform-checklist (template_manifest waiver), check:test-source-alias (connector configs alias @objectstack/spec/kernel). spec check:generated current after --fix; check:liveness 0; check:spec-parsed-alias 0; check-changeset-no-major level axis 0 against the PR body (exit 1 with the claim's lines: none-baseline-moved); check-adr-0087-registration 0; check-platform-checklist --changed-from origin/main 0. PR check-runs on 2f243e0d68: 58 success, 9 skipped, 0 failure — Lint & Repo Gates success, TypeScript Type Check success (Type Check · workspace/debt ledger/source gates/consumer gates success), Test Core success (8/8 shards), Build Core success, Governed Surface Queue Guard success, Type Check UI success, Test UI success, Build & E2E UI success, Live E2E UI success, Lint UI skipped, Check Changeset success. The previous head e71622a128 had Test Core (2/8)/(4/8) and Type Check · workspace red from main's new scenario exporter and the module header; both fixed.", "dogfood_battery": "e71622a128 · os-verify-lock VERDICT command-exit 0 (held 30m48s) · closure build exit 0 · pnpm --filter @objectstack/dogfood test exit 0 · Test Files 425 passed | 3 skipped (428) · Tests 3543 passed | 41 skipped (3584) · no failure. Not re-run on 2f243e0d68: the delta is a merge of main plus the scenario exporter conversion, a doc-comment demotion and a regenerated reference page; CI's Dogfood Regression Gate is opt-in and skipped on this PR.", "mcp_calls": "0", "api_writes": "2 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 42239679609, PR #30980; one earlier attempt refused by write-pace before sending, nothing written); this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/30904/comments). git push is not a REST write.", "open_questions": [ { "question": "Declaration lines: the claim and dispatch said Clause-②: yes (widening) / Metadata-protocol: none; Check Changeset refuses none on this diff (none-baseline-moved: authorable-surface/marketplace.json moved), and AGENTS.md defines a tombstoned key as narrowed. Which pair stands?", "options": [ "A keep what the PR carries: Clause-②: yes (narrowing) + Metadata-protocol: narrowed, spec and cli minor (Check Changeset green)", "B restore the claim's pair: edit the PR body and regrade the changeset to patch; Check Changeset stays red" ], "recommendation": "A, because the gate and AGENTS.md agree that a tombstoned authorable key is narrowed, and the (narrowing) arm is what makes the break visible to the contract review." }, { "question": "Release order with cloud: at cloud 8f574b9 the publish route drops the four facets and marketplace-policy.ts still requires category at submission, so a CLI released from this PR before cloud#3661 deploys cannot satisfy submission for a package without a stored category.", "options": [ "A hold the next CLI release until cloud#3661 (accept facets, require a business function instead of category) is deployed", "B release as is and accept a window where new marketplace submissions are refused" ], "recommendation": "A, because the window refuses every new marketplace submission and the fix is entirely on the cloud side already filed." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — enrolling objectstack.manifest.json in the liveness ledger measured name, specVersion, skills and scaffold with no reader of the file's value, and visibility/publisher/isStarter read only to be warned about and never sent; recorded dead in packages/spec/liveness/template_manifest.json, not retired (dedupe: template manifest dead keys, objectstack.manifest.json specVersion skills scaffold)", "carrier: objectstack-ai/cloud#3661 · noted, not filed — cloud publish route drops business_functions/industries/solution_type/maturity and marketplace-policy.ts requires category; ruling §6 already assigns both", "carrier: hotcrm lane · noted, not filed — hotcrm objectstack.manifest.json still carries category: crm and will be refused by the next CLI (ruling §6 item 8)", "carrier: none · noted, not filed — docs/design/marketplace-publishing.md shows os marketplace search --category, a design-record example of a command this repo does not ship" ], "ui_card_needs": [ "ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx: replace the single category chip row (state at :363, chips built from it.category at :555, client filter at :571, chips at :710-722) with facet filters — businessFunctions (multi-select, OR), industries (hidden while no loaded listing carries one), solutionType (tab or segment); AND across facets; counts per value", "MarketplacePage.tsx card badge (:965 pkg.category): show the primary business function businessFunctions[0], plus a Beta badge when maturity === 'beta'", "ui/packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx (:1425-1426 categoryLabel, :1542 badge): show every business function, the industries, the solution type and the Beta badge", "ui/packages/i18n/src/namespaces/marketplace/*.ts (9 locales; en.ts :223 category block = a copy of the retired spec enum): delete marketplace.category.*; add marketplace.businessFunction.* (11), marketplace.industry.* (10), marketplace.solutionType.* (3), marketplace.maturity.beta, keyed by iterating MarketplaceBusinessFunctionSchema / MarketplaceIndustrySchema / MarketplaceSolutionTypeSchema .options from @objectstack/spec/marketplace so no second list exists, with a parity test", "data source: the cloud catalog rows' snake_case business_functions / industries / solution_type / maturity once cloud#3661 lands; until then the fields are absent and every facet must hide rather than render raw values (drop the raw-value fallback at :560-566)", "related gap (ruling §6 ui item 4): listMarketplacePackages({ limit: 100 }) at :407 filters client-side; facets at hundreds of templates need server-side filtering and pagination — same card or a sibling" ], "deviations": "Declaration lines changed from the claim's Clause-②: yes (widening) / Metadata-protocol: none to yes (narrowing) / narrowed, with spec and cli at minor, because Check Changeset refuses none over the moved authorable-surface shard and AGENTS.md (which outranks the dispatch) defines a tombstone as narrowed; the claim's pair is quoted in the PR body for the seat to overrule. Blank template: suggested route set solutionType template; I declared no facet instead (reason in summary). A full dogfood re-run on the final head was not made (reading is on e71622a128)." }objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Ruling: objectstack#30904 release order · maintainer 「等 cloud 上线再发 CLI(推荐)」 · 2026-11-06T17:23Z
Provenance: the maintainer, in person, in the
repo:cloud#3seat sessionsession_01Q4EjjRhUZjdy8VXs6Kkur2, on open question 2 of os-dev report 6690814417.What binds. The next CLI, which publishes the four facets and refuses
category, is not released before objectstack-ai/cloud#3661 is deployed. Today the cloud publish route drops the facets andmarketplace-policy.tsrequirescategory. Sequence:chore: version packagesrelease PR merged. The maintainer holds it until step 3.Seat correction to claim 6684237536's declaration lines (os-dev open question 1, the seat's call):
Clause-②: yes (narrowing)+Metadata-protocol: narrowed, with spec and cli at minor. Check Changeset refusesnoneover the movedauthorable-surface/marketplace.json, and AGENTS.md defines a tombstoned authorable key as narrowed. The contract review runs on the narrowing face.Console part filed as objectstack#31011 (
domain:ui,pm:blockedon this card).objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: state ·
needs-user-decisionremoved · 2026-11-06T17:56Z⛔ Not a claim.
pm:dispatchedwithrepo:cloud#3; PR feat(spec)!: one marketplace taxonomy — business functions, industries, a solution type and maturity replace the single category #30980 is open.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
os-dev-report
{ "issue": 30904, "status": "done", "branch": "claude/issue-30904-marketplace-taxonomy", "pr": "https://github.com/objectstack-ai/objectstack/pull/30980", "session": "session_01Q4EjjRhUZjdy8VXs6Kkur2", "premise_still_valid": true, "summary": "REWORK round: PR #30980 was `dirty` against objectstack main 69e133ea0d. Merged origin/main with git merge (no rebase, no force-push): merge commit 95b6e07094, which is also the final head — nothing was committed after it. Two conflicts, both from main's scenario-library work. packages/qa/scenarios/src/schema.ts: kept main's new ROW_SCOPE_TAG and kept this PR's BusinessFunctionsSchema in place of main's re-carried SCENARIO_CATEGORIES/CategorySchema. docs/qa/scenarios/crm_account_with_contacts.json: kept main's added capability `access-security.row-scope` and this PR's businessFunctions [\"sales\"] in place of category: crm. Main brought no new user of category, SCENARIO_CATEGORIES, CategorySchema, MarketplaceCategorySchema or PackageCategorySchema (scanned packages/qa, packages/cli, packages/create-objectstack, packages/apps and every objectstack.manifest.json; the remaining `c.category` hits are migration-plan change categories, unrelated). All nine scenario cards still carry businessFunctions and no category. The diff itself is unchanged from the first report: the taxonomy defined once in packages/spec/src/marketplace/taxonomy.zod.ts; both category schemas retired under ADR-0087 (tombstones, no D2 conversion); os package publish refuses category and forwards and cross-checks the facets; the manifests, the scenario cards and the gallery exporter re-tagged; template_manifest enrolled in the liveness ledger. Seat ruling Q1 = A: the PR keeps `Clause-②: yes (narrowing)` + `Metadata-protocol: narrowed`, with spec and cli at minor.", "tests": "On merged head 95b6e07094, one locked pass (os-verify-lock VERDICT command-exit 0): turbo closure build exit 0. spec check:generated --fix exit 0 and regenerated nothing (tree clean after). spec test exit 0, Test Files 820 passed (820), Tests 25888 passed | 1 todo. spec repo scripts/file-description.test.ts exit 0. spec typecheck 0. cli unit exit 0, Test Files 396 passed (396), Tests 5571 passed (5571). cli typecheck 0. create-objectstack test/typecheck 0/0. scenarios test exit 0, Test Files 23 passed (23), Tests 341 passed (341); scenarios typecheck 0. connector-stripe/github/docusign test 0/0/0. Ablations are unchanged from the first round (the rule code did not move in this merge): ten, one per rule, each turned its rule's test red and was restored with git diff HEAD empty.", "gates": "dispatch-gates --ran on 95b6e07094: 119 derived, 116 run with exit 0, 3 NOT-MEASURED with exit 3, 0 UNRUN. The three not measured: check:skill-examples (client-react has no .d.ts); check:dual-build-cjs-loads (packages outside the closure unbuilt); check:i18n-coverage (`@objectstack/connector-slack` has no build output in this worktree). check:type-check-debt ran this time, exit 0. The checks the seat named: pnpm --filter @objectstack/spec check:generated 0; check:liveness 0; check-adr-0087-registration --base origin/main 0 (self-test 0); check-changeset-no-major --base origin/main 0; check-empty-changeset 0. The level axis against the live PR body passes: `✓ LEVEL AXIS: yes (narrowing) + narrowed` grading a moved package at minor. PR check-runs on 95b6e07094: 58 success, 9 skipped, 0 failure. Lint & Repo Gates success; TypeScript Type Check success; Test Core success; Build Core success; Governed Surface Queue Guard success; Type Check UI success; Test UI success; Build & E2E UI success; Live E2E UI success; Lint UI skipped; Check Changeset success. GitHub reports mergeable true, mergeable_state clean.", "dogfood_battery": "e71622a128 (first round) · os-verify-lock VERDICT command-exit 0 · Test Files 425 passed | 3 skipped (428) · Tests 3543 passed | 41 skipped (3584) · no failure. Not re-run on 95b6e07094: since then the diff gained the scenario-exporter conversion, a doc-comment demotion, a regenerated reference page and two merges of main. The merge resolution touched only the scenario library (outside the dogfood radius). CI's Dogfood Regression Gate is opt-in and skipped.", "mcp_calls": "0", "api_writes": "1 this round: this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/30904/comments). git push is not a REST write. The first round's 2 writes were the relay pr_create (#30980) and comment 6690814417.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — enrolling objectstack.manifest.json in the liveness ledger measured name, specVersion, skills and scaffold with no reader of the file's value, and visibility/publisher/isStarter read only to be warned about and never sent; recorded dead in packages/spec/liveness/template_manifest.json, not retired (dedupe: template manifest dead keys, objectstack.manifest.json specVersion skills scaffold)", "carrier: objectstack-ai/cloud#3661 · noted, not filed — cloud publish route drops business_functions/industries/solution_type/maturity and marketplace-policy.ts requires category; ruling §6 already assigns both", "carrier: hotcrm lane · noted, not filed — hotcrm objectstack.manifest.json still carries category: crm and will be refused by the next CLI (ruling §6 item 8)", "carrier: none · noted, not filed — docs/design/marketplace-publishing.md shows os marketplace search --category, a design-record example of a command this repo does not ship" ], "ui_card_needs": [ "ui/packages/app-shell/src/console/marketplace/MarketplacePage.tsx: replace the single category chip row (state at :363, chips built from it.category at :555, client filter at :571, chips at :710-722) with facet filters — businessFunctions (multi-select, OR), industries (hidden while no loaded listing carries one), solutionType (tab or segment); AND across facets; counts per value", "MarketplacePage.tsx card badge (:965 pkg.category): show the primary business function businessFunctions[0], plus a Beta badge when maturity === 'beta'", "ui/packages/app-shell/src/console/marketplace/MarketplacePackagePage.tsx (:1425-1426 categoryLabel, :1542 badge): show every business function, the industries, the solution type and the Beta badge", "ui/packages/i18n/src/namespaces/marketplace/*.ts (9 locales; en.ts :223 category block = a copy of the retired spec enum): delete marketplace.category.*; add marketplace.businessFunction.* (11), marketplace.industry.* (10), marketplace.solutionType.* (3), marketplace.maturity.beta, keyed by iterating MarketplaceBusinessFunctionSchema / MarketplaceIndustrySchema / MarketplaceSolutionTypeSchema .options from @objectstack/spec/marketplace so no second list exists, with a parity test", "data source: the cloud catalog rows' snake_case business_functions / industries / solution_type / maturity once cloud#3661 lands; until then the fields are absent and every facet must hide rather than render raw values (drop the raw-value fallback at :560-566)", "related gap (ruling §6 ui item 4): listMarketplacePackages({ limit: 100 }) at :407 filters client-side; facets at hundreds of templates need server-side filtering and pagination — same card or a sibling" ], "deviations": "None new. The declaration pair is now the seat's ruled pair (Q1 = A); Q2, the release order with cloud#3661, is with the maintainer and changes nothing in this diff. The worktree was recreated from the pushed branch at 2f243e0d68 for this round (it had been removed after the first report). Merge commit 95b6e07094 is the final head; I will push nothing further while the contract review runs.", "merge_commit": "95b6e07094", "final_head": "95b6e07094" }objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
LANDED · 2026-11-06T19:19Z
468e0c16completedbyFixes #30904State:
pm:dispatchedandpm:blockingremoved. The taxonomy is defined once inpackages/spec/src/marketplace/taxonomy.zod.ts;categoryis retired under ADR-0087. Next, per the release-order ruling: the cloud pin moves past468e0c16and objectstack-ai/cloud#3661 is dispatched now; the maintainer holdschore: version packagesuntil #3661 deploys. Follow-ups: #31011 (console facets), #31035 and theuniqueItemscard (spec), plus the hotcrm manifest (with the maintainer).