Skip to content

feat(skill-marketplace): seed 4 demo Skills with publish→package→download pipeline (DR-51) - #53

Merged
pjwan2 merged 3 commits into
mainfrom
feature/DR-51-seed-demo-skills
Jun 22, 2026
Merged

feat(skill-marketplace): seed 4 demo Skills with publish→package→download pipeline (DR-51)#53
pjwan2 merged 3 commits into
mainfrom
feature/DR-51-seed-demo-skills

Conversation

@taowis

@taowis taowis commented Jun 20, 2026

Copy link
Copy Markdown

背景 / Problem

DR-51 要求播种 4 个已发布 + 已打包 + 可下载的 demo Skill,用于 R2「下载 → 运行 → 路由」演示。但其依赖票(DR-46/47/48/79/81 + tier 别名注册表)此前均未实现——仓库里只有 skills 表和只读的 marketplace/admin/ops GET 路由,缺少 skill_versions 模型、写入/发布/打包/下载 API 与 tier 别名。因此「播种」无法仅靠现有 API 完成,需要先补齐最小后端纵切。

经确认采用演示纵切方案,并以 Go cmd + 迁移 驱动播种。

改动 / Fix

模块 位置 对应票
平台 tier 别名注册表(smart/balanced/fast-tier),校验 + 解析 internal/skill/tiers/ DR-110 / DR-96,D-09 #2
skill_versions 模型 + 迁移(sha256、单 active 版本部分唯一索引、快照) internal/skill/model/skill_version*.go(接入 model/main.go DR-47
发布即打包:zip = manifest.json + 可读 instruction_template.txt + SKILL.md + 瘦客户端 run.py;构建期守卫拒绝凭证/具体模型 id internal/skill/packaging/ DR-79,FR-A20
下载端点 GET /api/v1/marketplace/skills/:id/download + 新增 SkillUserAuth(无登录 → AUTH_REQUIRED internal/skill/handler/download.gomiddleware/skill-auth.gorouter/skill-router.go DR-81
播种器(draft→version→publish,按 slug 幂等)+ 可运行 cmd internal/skill/seed/cmd/seed-skills/ DR-46/47/48/51

D-09 合规(4 个 Skill 全部满足):

  1. 能力型——干活那步调 DeepRouter 路由(run.py POST 路由 API;包内无凭证/无路由逻辑,离线跑不了)
  2. 声明 tier 而非模型——model_whitelist 仅含平台别名,已校验
  3. 输入/指令分离——用户内容走结构化 input,绝不拼接进指令
  4. 服务端权威——下载绑定 skill_version_id,包内模板不被信任
  5. 运行者自带 key 计费——Authorization: Bearer <runner key>,无 key → AUTH_REQUIRED

example_outputs 全部为合成数据。

验证 / Verification

go test ./relay/ ./internal/... 全部 ok

用例 覆盖 结果
tier 校验/解析、拒绝硬编码模型 tiers
skill_versions 迁移、sha256、单 active 版本唯一约束、对象列默认 {} model
打包内容 + 构建期守卫拒绝凭证/模型 id/非 active 版本 packaging
下载成功 / AUTH_REQUIRED / 404 / 草稿不可下载 handler
播种 4 个已发布 Skill + 幂等(重跑全 up-to-date,无增量)+ 模板变更生成 v2 seed
SQLITE_PATH=… go run ./cmd/seed-skills 端到端跑通 手测 ✅ 4 个 published + active version

仍缺 / Out of scope

包内 run.py 调用的公开路由执行端点 POST /api/v1/skills/run(服务端解析 tier→model、套用权威模板、路由、对运行者计费)尚未实现——属 relay/执行层(M05 / DR-68),独立票。目前 create→version→publish→package→download 全部可用,仅「运行调用」缺服务端落点。

备注:SQLite 上对已存在库重跑全量 AutoMigrate 会触发 glebarez 驱动对 IN(...) CHECK 的已知 bug(见 internal/skill/model 集成测试注释);生产为 PostgreSQL,不受影响。播种器逻辑本身幂等。

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

…oad 链路 (DR-51)

为 R2 下载演示补齐 DR-51 依赖的后端最小纵切,使 4 个 demo Skill 达到
「已发布 + 已打包 + 可下载」状态,满足 D-09 合规五条。

新增:
- internal/skill/tiers: 平台 tier 别名注册表(smart/balanced/fast-tier),
  ValidateWhitelist 校验(DR-110)+ Resolve 解析(DR-96);tier→model 解析
  表仅留在服务端,绝不进入下载包。
- internal/skill/model: skill_versions 表 + 迁移(sha256、单 active 版本部分
  唯一索引、快照字段),迁移已接入 model/main.go。
- internal/skill/packaging: 发布即打包,BuildPackage 生成 zip(manifest.json
  + 可读 instruction_template + SKILL.md + 瘦客户端 run.py);构建期守卫拒绝
  凭证与具体模型 id(FR-A20)。
- internal/skill/handler/download.go + 路由 GET /api/v1/marketplace/skills/:id/download
  (DR-81),由新增 middleware.SkillUserAuth 鉴权;无 key → AUTH_REQUIRED。
- internal/skill/seed + cmd/seed-skills: draft→version→publish 播种 4 个 Skill
  (polished-writer / faithful-translator / code-helper / data-analyst),按
  slug 幂等。

D-09 合规:声明 tier 而非模型;输入/指令分离(用户内容走结构化 input,绝不
拼接进指令);服务端权威(下载绑定 skill_version_id,不信任包内模板);运行者
自带 key 计费(包内无凭证)。example_outputs 全部为合成数据。

仍缺(live demo 闭环的最后一块,属独立票 M05/DR-68):包内 run.py 调用的公开
路由执行端点 POST /api/v1/skills/run 尚未实现。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taowis taowis changed the title feat(skill-marketplace): seed 4 个 demo Skills 并补齐 publish→package→download 链路 (DR-51) feat(skill-marketplace): seed 4 demo Skills with publish→package→download pipeline (DR-51) Jun 20, 2026
@taowis

taowis commented Jun 20, 2026

Copy link
Copy Markdown
Author

完整测试覆盖与结果

命令:go test -count=1 ./relay/ ./internal/... → 全部 ok(无 FAIL)。下列为本 PR 新增的测试,逐条 PASS。

internal/skill/tiers(tier 别名注册表,DR-110/DR-96)

测试 断言 结果
TestValidAndResolve 三个 tier 均有效且能解析到具体模型;硬编码模型 id 无效 ✅ PASS
TestValidateWhitelist 合法 tier 列表通过;混入硬编码模型被拒;空列表被拒 ✅ PASS
TestResolvedModelsNonEmpty 解析模型集非空;至少 3 个 tier ✅ PASS

internal/skill/model(skill_versions 模型 + 迁移,DR-47)

测试 断言 结果
TestMigrateSkillVersions_SQLite_SucceedsFromEmptyDB 空库迁移成功 ✅ PASS
TestComputeTemplateSHA256_Stable sha256 确定性、64 位、内容不同则不同 ✅ PASS
TestSkillVersion_BeforeCreate_DefaultsAndSha BeforeCreate 补 id/sha;对象列默认 {}、数组列默认 [] ✅ PASS
TestSkillVersion_OneActivePerSkill_SQLite 同一 skill 第二个 active 版本触发唯一约束 ✅ PASS
TestMonetizationSnapshotJSON 快照含字段;nil quota 被省略 ✅ PASS

同包既有 skills / user_enabled_skills 测试(约 55 个)全部仍 PASS。

internal/skill/packaging(发布即打包 + 构建期守卫,DR-79/FR-A20)

测试 断言 结果
TestBuildPackage_Contents zip 含 manifest/SKILL.md/template/run.py;模板可读;manifest 绑定 skill_version_id + tier;run.py 强制 AUTH_REQUIRED + runner key ✅ PASS
TestBuildPackage_GuardRejectsConcreteModel 包内出现具体模型 id 时构建被拒 ✅ PASS
TestBuildPackage_GuardRejectsCredential 包内出现凭证样式 token 时构建被拒 ✅ PASS
TestBuildPackage_RejectsNonActiveVersion 非 active 版本拒绝打包 ✅ PASS

internal/skill/handler(下载端点,DR-81)

测试 断言 结果
TestDownloadSkillPackage_Success 200 + application/zip + 文件名;返回真 zip(PK 魔数);写入 entitlement 行 ✅ PASS
TestDownloadSkillPackage_AuthRequired 未登录 → 401 AUTH_REQUIRED ✅ PASS
TestDownloadSkillPackage_NotFound 未知 slug → 404 ✅ PASS
TestDownloadSkillPackage_DraftNotDownloadable 草稿 Skill 不可下载 → 404 ✅ PASS

同包既有 marketplace/admin 列表与详情测试(约 28 个)全部仍 PASS。

internal/skill/seed(播种器,DR-46/47/48/51)

测试 断言 结果
TestSeedDemoSkills_CreatesFourPublishedPackagedSkills 4 个 Skill 均 published + 有 active version + tier 合法 + 可成功打包 ✅ PASS
TestSeedDemoSkills_Idempotent 重跑全部 up-to-date,skills/versions 各 4 条无增量 ✅ PASS
TestSeedDemoSkills_NewVersionOnTemplateChange 模板变更后生成 v2 并保持唯一 active 版本 ✅ PASS

包级汇总

ok  github.com/QuantumNous/new-api/relay
ok  github.com/QuantumNous/new-api/internal/billing
ok  github.com/QuantumNous/new-api/internal/kids
ok  github.com/QuantumNous/new-api/internal/policy
ok  github.com/QuantumNous/new-api/internal/quota
ok  github.com/QuantumNous/new-api/internal/skill/api
ok  github.com/QuantumNous/new-api/internal/skill/availability
ok  github.com/QuantumNous/new-api/internal/skill/enums
ok  github.com/QuantumNous/new-api/internal/skill/errcodes
ok  github.com/QuantumNous/new-api/internal/skill/handler
ok  github.com/QuantumNous/new-api/internal/skill/model
ok  github.com/QuantumNous/new-api/internal/skill/packaging
ok  github.com/QuantumNous/new-api/internal/skill/seed
ok  github.com/QuantumNous/new-api/internal/skill/tiers
ok  github.com/QuantumNous/new-api/internal/smart_router_client

main 已独立实现 Skill Marketplace 后端底座(skill_versions 模型 + 迁移、
download 端点 + 打包 + D-09 守卫、SkillUserAuth、DR-68 relay/executor、
skill_usage_events)。本次合并以 main 的实现为准,解决冲突并把 DR-51 收敛为
「在 main 底座之上播种 4 个 demo Skill」这一真正独有的交付物。

冲突解决(全部采用 main 版本):
- internal/skill/handler/download.go(+test)
- internal/skill/model/skill_version.go
- middleware/skill-auth.go(main 已有 SkillUserAuth)
- router/skill-router.go(main 已有 download 路由)
- model/main.go 自动合并(main 已接入 MigrateSkillVersions)

删除我方冗余重复实现(main 已提供等价物):
- internal/skill/model/skill_version_migrate.go、skill_version_test.go
- internal/skill/packaging/*(改用 main 的 download 打包)

保留并适配的独有交付物:
- internal/skill/seed/*、cmd/seed-skills:适配 main 的 SkillVersion
  (OutputSchema *SkillJSONB、RequiredPlanSnapshot 强类型、显式算 sha256、
  内联 monetization snapshot);并在 Description 注入「## Work step」DeepRouter
  路由调用,使 capability 包通过 main 的 D-09 下载守卫、可真正下载。
- internal/skill/tiers/*:tier 别名注册表(DR-110 校验)。
- internal/skill/relay/executor.go:selectModel 接入 tiers.Resolve(DR-96),
  tier 别名解析为具体模型,字面模型名透传,使播种的 tier 型 Skill 真正可路由。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taowis

taowis commented Jun 22, 2026

Copy link
Copy Markdown
Author

更新:已合并 main 并解决冲突(PR 收敛为「在 main 底座上播种」)

origin/main 这期间独立实现了 Skill Marketplace 后端底座(skill_versions 模型+迁移、download 端点+打包+D-09 守卫、SkillUserAuth、DR-68 internal/skill/relay executor/resolver、skill_usage_events)。我把 main 合并进本分支,冲突一律采用 main 版本,删除我方冗余重复实现,PR 净改动收敛为真正独有的部分:

净改动 vs origin/main(9 文件,+1143/−3): internal/skill/seed/*cmd/seed-skillsinternal/skill/tiers/*internal/skill/relay/executor.goselectModel 接入 tier 解析,+16/−3)、internal/skill/handler/seed_download_test.go

  • 删除(main 已提供等价物):skill_version_migrate.goskill_version_test.gopackaging/*
  • 适配 main 的 SkillVersionOutputSchema *SkillJSONBRequiredPlanSnapshot 强类型、显式算 sha256、内联 monetization snapshot。
  • 在每个 Skill 的 Description 注入「## Work step」DeepRouter 路由调用 → 通过 main 的 D-09 下载守卫,capability 包可真正下载。
  • selectModel 接入 tiers.Resolve(DR-96):tier 别名解析为具体模型,字面模型名透传 → 播种的 tier 型 Skill 真正可路由。

gh pr view 现示 MERGEABLE(冲突已解决)。


测试(focused + regression):数据集 / 验证点 / 结果

1. internal/skill/tiers(tier 注册表 DR-110/DR-96)

  • 数据集/fixture:纯内存注册表,无 DB;输入为 tier 名字符串与硬编码模型 id。
  • 验证点 / 结果
    • TestValidAndResolve — smart/balanced/fast-tier 均 ValidResolve 出非空具体模型;硬编码 id 无效 → ✅
    • TestValidateWhitelist — 合法 tier 列表通过;混入 claude-3-opus-20240229 返回该项+false;空列表 false → ✅
    • TestResolvedModelsNonEmpty — 解析模型集非空、tier ≥ 3 → ✅
  • coverage:100.0%

2. internal/skill/seed(DR-51 播种器)

  • 数据集/fixture:file-based SQLite(t.TempDir,避开 :memory: 事务可见性问题),迁移 skills+skill_versions;fixture = 4 个真实 DemoSkillDef(polished-writer / faithful-translator / code-helper / data-analyst)。
  • 验证点 / 结果
    • TestSeedDemoSkills_CreatesFourPublishedPackagedSkills — 4 个均 published、有 active_version_idpublished_at;whitelist 全为合法 tier;Description 含「## Work step」+DeepRouter;active version 状态=active、sha=stored 模板的 sha、required_plan_snapshot/model_whitelist_snapshot/max_input_tokens_snapshot/output_schema/monetization_snapshot 均落库 → ✅
    • TestSeedDemoSkills_Idempotent — 重跑全部 up-to-date,skills/versions 各 4 条无增量 → ✅
    • TestSeedDemoSkills_NewVersionOnTemplateChange — 改 sha 后重跑 → updated v2,且仅 1 个 active 版本 → ✅
    • TestMonetizationSnapshot_QuotaBranches — 有 FreeQuotaPerMonth 时含 free_quota_per_month:50;nil 时省略(覆盖两条分支)→ ✅
  • coverage:79.4%(未覆盖为 db 错误/marshal 失败等错误分支;computeTemplateSHA256 100%)

3. internal/skill/relay(executor selectModel 接入 DR-96)

  • 数据集/fixture:纯函数;输入为 whitelist 切片(tier 别名 / 字面模型 / 含空串 / nil)。
  • 验证点 / 结果
    • TestSelectModel_ResolvesTierAlias["smart-tier","balanced-tier"] 解析为 tiers.Resolve("smart-tier") 的具体模型 → ✅
    • TestSelectModel_LiteralModelPassesThrough["gpt-4o"] 原样透传 → ✅
    • TestSelectModel_SkipsEmptyEntries["","fast-tier"] 跳过空串、解析 fast-tier → ✅
    • TestSelectModel_EmptyWhitelistErrors — nil → 错误码 → ✅
    • main 既有 executor/resolver 测试(TestSelectModel_*TestResolve_*TestRewriteForSingleTurn_* 等约 60 个)全部仍 PASS(字面模型透传保持向后兼容)
  • selectModel coverage:100.0%

4. internal/skill/handler(seed → download 集成,DR-81 + D-09 守卫)

  • 数据集/fixture:file-based SQLite,迁移 skills+skill_versions+user_enabled_skills+skill_usage_events;先 seed.SeedDemoSkills 播种 4 个;以 ctx(user=1, group="default")main 的 DownloadSkillPackage
  • 验证点 / 结果TestDownloadSkillPackage_SeededDemoSkills):
    • 4 个 slug 下载均 200 + application/zip → ✅
    • 解压:含 manifest.json+SKILL.md;SKILL.md 含 deeprouter+/v1/chat/completions证明通过 main 的 D-09 运行时依赖守卫);manifest 含 skill_version_id+requires_deeprouter_key → ✅
    • 4 次下载各 upsert 一条 user_enabled_skills → ✅
    • main 既有 download/skills handler 测试全部仍 PASS

全量回归

go test -count=1 ./relay/ ./internal/... ./middleware/... ./router/... → 全部 ok

ok  relay
ok  internal/abuse
ok  internal/billing
ok  internal/kids
ok  internal/policy
ok  internal/quota
ok  internal/skill/api
ok  internal/skill/availability
ok  internal/skill/enums
ok  internal/skill/errcodes
ok  internal/skill/handler
ok  internal/skill/model
ok  internal/skill/relay
ok  internal/skill/seed
ok  internal/skill/tiers
ok  internal/smart_router_client
ok  middleware
ok  router

端到端 smoke:SQLITE_PATH=… go run ./cmd/seed-skills → 4 个 Skill createdpublished + active v1 + tier whitelist + Description 含 Work step + sha(64)。

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

@pjwan2

pjwan2 commented Jun 22, 2026

Copy link
Copy Markdown

Findings
P1 — Seeded demo Skill packages tell users to call the ordinary chat endpoint, bypassing the public routing API abuse gate
Files:
internal/skill/seed/demo_skills.go
internal/skill/handler/seed_download_test.go
PR #53 seeds demo Skill package work steps with:
POST /v1/chat/completions
But the current public routing API contract and DR-82 abuse controls are wired to:
POST /v1/routing/chat/completions
Evidence:
router/relay-router.go applies markSkillPublicRoutingAPI() and PublicRoutingAbuseControl() only to /v1/routing/chat/completions.
docs/tasks/dr82-public-api-abuse-controls-prd.md explicitly says not to apply the stricter throttle to ordinary /v1/chat/completions.
The PR’s test currently asserts the wrong endpoint, so it locks in the bypass instead of preventing it.
Expected fix:
Change seeded workStepSection to use /v1/routing/chat/completions.
Update seed_download_test.go to assert /v1/routing/chat/completions, not /v1/chat/completions.
Ideally assert the package text references the public routing endpoint and not only any DeepRouter endpoint.

…t (DR-82)

Review P1 (@pjwan2): seeded work steps used POST /v1/chat/completions, the
ordinary chat endpoint, which bypasses the public-routing abuse gate — only
/v1/routing/chat/completions is wired to markSkillPublicRoutingAPI +
PublicRoutingAbuseControl (router/relay-router.go), and DR-82 explicitly excludes
the ordinary endpoint from the stricter throttle.

- workStepSection now references POST /v1/routing/chat/completions (still an
  accepted marker for main's D-09 download guard).
- seed_download_test.go now asserts the package references
  /v1/routing/chat/completions AND NOT /v1/chat/completions, locking out the
  bypass instead of pinning it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@taowis

taowis commented Jun 22, 2026

Copy link
Copy Markdown
Author

@pjwan2 已修复 P1(commit 602b619)。感谢指出——确实把运行者引到了普通 chat 端点、绕过了公开路由滥用网关。

改动

  • internal/skill/seed/demo_skills.goworkStepSection 的端点由 POST /v1/chat/completions 改为 POST /v1/routing/chat/completions(即接入 markSkillPublicRoutingAPI + PublicRoutingAbuseControl 的公开路由路径;/v1/routing/chat/completions 仍是 main D-09 下载守卫接受的 marker,所以包仍可下载)。并补注释说明为何不能用普通端点(DR-82 明确不对 /v1/chat/completions 施加严格限流)。
  • internal/skill/handler/seed_download_test.go:断言改为
    • require.Contains(SKILL.md, "/v1/routing/chat/completions"),且
    • require.NotContains(SKILL.md, "/v1/chat/completions")——直接锁死绕过路径,而不是把它固化下来。

验证

  • focused:internal/skill/seedinternal/skill/handler 全 PASS(含强化后的 TestDownloadSkillPackage_SeededDemoSkills:下载 4 个包并校验 SKILL.md 指向公开路由端点、且不含普通端点)。
  • 全量回归 go test ./relay/ ./internal/... ./middleware/... ./router/... 全部 ok;gofmt 干净。
  • smoke:go run ./cmd/seed-skills 后 4 个 Skill 的 description 均含 /v1/routing/chat/completions、不含普通端点。

第三点建议(断言指向公开路由端点而非任意 DeepRouter 端点)也一并做了:正向断言公开路由路径 + 负向断言普通路径缺失。

@pjwan2
pjwan2 merged commit cb91baa into main Jun 22, 2026
4 checks passed
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.

2 participants