Skip to content

fix(catalog): 校验技能指针的客户端契约,撤回 REA 的单段版本 / validate skill pointers against the client contract - #239

Merged
yi-ge merged 2 commits into
mainfrom
fix/skill-pointer-client-contract
Oct 31, 2026
Merged

yi-ge merged 2 commits into
mainfrom
fix/skill-pointer-client-contract

Conversation

@yi-ge

@yi-ge yi-ge commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

概述 / Summary

reverse-engineer-anything 标为 installable,但所有客户端都在索引时丢弃它:客户端索引 144 个技能,manifest.stats.totalSkills 是 145。本 PR 恢复这个条目,并让目录校验器按客户端契约校验技能指针,让这类问题在目录评审时报错。

reverse-engineer-anything is listed as installable, yet every client drops it while indexing: 144 skills indexed against a declared 145. This PR restores the listing and makes the catalog validator enforce the client contract for skill pointers, so the same mistake now fails at review.

根因 / Root cause

  • 客户端用 marketSkillEntrySchema 校验 entry.json,latestVersion 只接受 x.y 或 x.y.z,不通过就跳过整个条目。fix: 同步上游健康巡检 #213 两轮已审查的市场引用 #227 按上游 metadata.version 写入了 "50"。
  • 校验器只用客户端 Schema 校验 Agent 与 Team 指针,没有校验技能指针。技能指针读的还是不存在的 version 字段,所以 release 与 latestVersion 的对照对技能指针从未生效。

Clients validate entry.json against marketSkillEntrySchema, whose latestVersion admits only x.y or x.y.z, and skip the whole entry otherwise; #227 recorded the upstream metadata.version value "50". The validator checked only Agent and Team pointers against client schemas. For skill pointers it also read a nonexistent version field, so comparing release with latestVersion never ran.

修法与理由 / Fix and rationale

条目恢复为 release: unknown 且不声明 latestVersion,即 #212 审查时的形态。固定提交和合规审查不变。

  • 不改客户端。 README.md 已要求新版本格式“先发布兼容客户端,再进入目录”。放宽 Schema 只对升级后的客户端有效,已发布的客户端仍会丢这个条目;改目录则在下次市场同步后对所有客户端生效。客户端测试也明确拒绝单段版本 1。
  • 不写 50.0 或 50.0.0。 这能通过校验,但上游从未发布过这个版本,违反“保留上游标识、不捏造版本”的规则。
  • 代价: 卡片显示“版本未知”,与目录里 89 个 release: unknown 的条目相同。这里也不损失更新能力:客户端对单段版本的 compareSemver("50", "51") 返回相等,所以 latestVersion 本来也推不动这个条目的更新。

The listing returns to release: unknown with no latestVersion, the shape reviewed in #212; the pin and compliance review are unchanged.

  • Client unchanged. README.md already requires a compatible client release before a new version format enters the catalog. Widening the schema helps only upgraded clients, whereas a catalog fix reaches every client on its next market sync. A client test also explicitly rejects the bare version 1.
  • No 50.0 or 50.0.0. Either would pass validation, but upstream never published such a version, which breaks the rule to preserve upstream identifiers rather than invent them.
  • Cost: the card shows an unknown version, like the 89 listings already at release: unknown. No update capability is lost: compareSemver("50", "51") compares equal in the client, so latestVersion could not drive updates for this listing anyway.

校验器变更 / Validator changes

  • 新增 schemas/market-skill-entry.client.schema.json,从 v10.0.195 的 marketSkillEntrySchema 导出;技能指针不通过时报 skill-entry-schema。
  • 补上客户端在 Schema 之外的规则:children 与 source.path 互斥、子技能 ID 不重复、entry.id 与目录名一致。
  • 技能指针改读 latestVersion,合集父条目的版本按客户端语义忽略。
  • release 按客户端判据双向比较:只有 sidecar 声明版本,或 versionScheme 与客户端推导结果不同,都报错。客户端遇到这两种情况会拒绝整个 sidecar,条目退化为 listing-only。
  • category、tags、requiredClientVersion、riskLevel、skillType、compatibleAgents、provenance.content 同样改为双向比较,覆盖技能指针、内置技能和 Agent。Team 的 sidecar 客户端不读取,保持原规则。

差分验证时还发现 video-use:sidecar 声明了指针无法声明的 skillType,一直被客户端拒绝,校验器却报 0 错误。本 PR 去掉这个字段。条目本来就是 listing-only,用户可见的安装状态不变。

  • Adds schemas/market-skill-entry.client.schema.json, exported from marketSkillEntrySchema at v10.0.195; a failing skill pointer reports skill-entry-schema.
  • Adds the client's rules that live outside its schema: children and source.path are mutually exclusive, child IDs are unique, and entry.id equals the directory slug.
  • Skill pointers now read latestVersion, and a collection parent's own version is ignored as in the client.
  • release is compared in both directions by the client's rules: a version declared only in the sidecar, or a versionScheme other than the one the client derives, is an error. The client rejects the whole sidecar in both cases, which downgrades the listing to listing-only.
  • category, tags, requiredClientVersion, riskLevel, skillType, compatibleAgents and provenance.content are now compared both ways too, for skill pointers, built-in skills and Agents. Clients do not read Team sidecars, so Team rules are unchanged.

The differential run also found video-use: its sidecar declared a skillType no pointer can declare, so the client always rejected it while the validator reported 0 errors. This PR removes the field. The listing was already listing-only, so its installability does not change.

验证 / Validation

  • 修条目前,校验器只报 reverse-engineer-anything(2 条 skill-entry-schema),其余条目没有新增错误。修复后 validate_catalog_metadata.py --require-complete:0 errors,publishableSkills=145。

  • 用客户端 validateMarketSkillEntry / Agent / Team 扫描全部指针(技能 104、Agent 7、团队 2):修复前丢 1 个,修复后 0 个。

  • 差分验证:对同一份目录施加 43 类变异,真实 MarketSync 与校验器的判定全部一致(第一个提交之后仍有 16 类不一致,由第二个提交补齐)。未变异的本分支:索引 145 技能、9 Agent、2 团队,0 个 sidecar 被拒。

  • 独立实例冷启动(伪 HOME、出站守卫、拦截 git 网络命令):当前 dev 种子列表 144、详情 not found;本分支种子列表 145,详情返回 installable、可获取,日志不再有 校验失败。

  • test_validate_catalog_metadata.py(62 例,新增用例先确认失败)、test_collection_generator.py、test_check_upstream_health.py、test_validate_i18n.py、validate-i18n.py(0 errors)、translate.py --check 通过。

  • 主仓 market-i18n-real-repo.test.ts 指向本分支:22/22 通过,日志不再有 校验失败。

  • 未运行: 私有敏感词零结果扫描,本机没有清单。已人工自查差异、提交信息、分支名和本文,只含公开上游信息与通用描述。

  • Before the listing fix, the validator reported only reverse-engineer-anything (two skill-entry-schema errors) and no new error elsewhere. After it, validate_catalog_metadata.py --require-complete reports 0 errors with publishableSkills=145.

  • Scanning every pointer (104 skills, 7 Agents, 2 Teams) with the client's validateMarketSkillEntry / Agent / Team: one dropped before, none after.

  • Differential run: 43 mutation classes applied to one catalog, with full agreement between the real MarketSync and the validator (16 classes still disagreed after the first commit; the second closes them). This branch unmutated indexes 145 skills, 9 Agents and 2 Teams with 0 sidecars rejected.

  • Cold start of an isolated instance (fake HOME, egress guard, git network commands blocked): the current dev seed lists 144 and detail returns not found; a seed from this branch lists 145, and detail returns the listing as installable and acquirable, with the validation-failure log line gone.

  • test_validate_catalog_metadata.py (62 tests, with the new cases first confirmed failing), test_collection_generator.py, test_check_upstream_health.py, test_validate_i18n.py, validate-i18n.py (0 errors) and translate.py --check pass.

  • The client's market-i18n-real-repo.test.ts against this branch: 22/22 pass, and the validation-failure log line is gone.

  • Not run: the private sensitive-token scan, as the list is not available here. The diff, commit message, branch name and this text were reviewed by hand and contain only public upstream facts and generic descriptions.

合并后在主仓同步 market.zip。

market.zip is synced in the client repository after this merges.

…inst the client contract and withdraw the REA bare revision

客户端按 `marketSkillEntrySchema` 校验 `skills/<id>/entry.json`,`latestVersion`
只接受 `x.y` 或 `x.y.z`,不通过的条目在索引时被跳过。#227 把上游修订号 `50`
写入 `latestVersion`,所有客户端都看不到 reverse-engineer-anything,而
`manifest.stats.totalSkills` 仍计入它。

- reverse-engineer-anything 恢复为 `release: unknown` 且不声明 `latestVersion`,
  即 #212 审查时的形态;固定提交与合规审查不变。
- 校验器用导出的客户端 Schema 校验技能指针,并补上客户端在 Schema 之外的合集规则
  与 `entry.id` 目录一致性。
- 技能指针此前读的是不存在的 `version` 字段,`release` 对照从未生效,现改读
  `latestVersion`;同时按客户端判据比较 `versionScheme`,并拒绝只有 sidecar
  声明版本的情况。

Clients validate `skills/<id>/entry.json` against `marketSkillEntrySchema`, whose
`latestVersion` admits only `x.y` or `x.y.z`, and skip any entry that fails while
indexing. #227 recorded upstream revision `50` there, so every client lost
reverse-engineer-anything while the manifest still counted it. This restores the
shape reviewed in #212 and makes the catalog validator enforce the client contract
for skill pointers, including the `release` comparison that never ran for them.
@github-actions
github-actions Bot requested a balanced review from Copilot October 31, 2026 08:09
…acts both ways like the client and fix video-use

差分对比真实 MarketSync 与校验器后发现:客户端双向比较 `category`、`tags`、
`requiredClientVersion`、`riskLevel`、`skillType`、`compatibleAgents` 和
`provenance.content`,差异会拒绝整份 sidecar,条目退化为 listing-only;校验器
只做了单向比较或没有比较。video-use 正是这样:sidecar 声明的 `skillType`
指针无法声明,sidecar 一直被客户端拒绝,校验器却报 0 错误。

校验器补齐这些比较,覆盖技能指针、内置技能和 Agent;Team 的 sidecar 客户端不读取,
保持原规则。video-use 去掉 `skillType`。

A differential run of the real MarketSync against this validator showed the client
compares these facts in both directions and rejects the whole sidecar on any
difference, while the validator compared them one way or not at all. video-use was
a live case: its sidecar declared a `skillType` no pointer can declare. The validator
now compares them for skill pointers, built-in skills and Agents, and video-use
drops the field.
@yi-ge
yi-ge merged commit 7189d91 into main Oct 31, 2026
7 checks passed
@yi-ge
yi-ge deleted the fix/skill-pointer-client-contract branch October 31, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant