fix(skills): 技能文件包 - 修复旧数据迁移回填元数据超限
This commit is contained in:
@@ -118,7 +118,9 @@ func (s *Service) EnsureSkillPackages() error {
|
||||
}
|
||||
for index := range skills {
|
||||
skill := &skills[index]
|
||||
archive, err := archiveFromMarkdown([]byte(skill.Instruction), skill.Name, skill.Description)
|
||||
// Legacy fields are only fallbacks for package metadata. Bound them at
|
||||
// this migration boundary without rewriting source fields or relaxing uploads.
|
||||
archive, err := archiveFromMarkdown([]byte(skill.Instruction), truncateSkillMetadata(skill.Name, 80), truncateSkillMetadata(skill.Description, 500))
|
||||
if err != nil {
|
||||
return fmt.Errorf("迁移技能 %s 文件包失败: %w", skill.ID, err)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package skills
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -27,6 +28,73 @@ func TestArchiveFromMarkdownInfersMetadata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportedBuiltinSkillPackage(t *testing.T) {
|
||||
var definitions []builtinSkillDefinition
|
||||
if err := json.Unmarshal(builtinSkillsJSON, &definitions); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, skill := range definitions {
|
||||
if skill.SkillID != "16000000000049" {
|
||||
continue
|
||||
}
|
||||
archive, err := archiveFromMarkdown([]byte(skill.Instruction), skill.SkillName, skill.Description)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len([]rune(archive.Metadata.Description)) != 500 || string(archive.Files["SKILL.md"]) != skill.Instruction {
|
||||
t.Fatal("reported skill must have bounded metadata and unchanged instruction")
|
||||
}
|
||||
return
|
||||
}
|
||||
t.Fatal("reported builtin skill is missing")
|
||||
}
|
||||
|
||||
func TestEnsureSkillPackagesBoundsLegacyFallbackMetadata(t *testing.T) {
|
||||
for _, source := range []int{3, skillSourceUser} {
|
||||
db, err := gorm.Open(sqlite.Open("file:"+kernel.NewID()+"?mode=memory&cache=shared"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { _ = sqlDB.Close() })
|
||||
if err := db.AutoMigrate(&model.Skill{}, &model.SkillVersion{}, &model.SkillFile{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
svc := New(repository.New(db), t.TempDir(), nil)
|
||||
skill := model.Skill{ID: kernel.NewID(), Name: strings.Repeat("名", 81), Description: strings.Repeat("文", 503), Instruction: "## 旧技能\n", Status: skillStatusEnabled, Source: source}
|
||||
if err := db.Create(&skill).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for range 2 {
|
||||
if err := svc.EnsureSkillPackages(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
assertSkillVersionCount(t, db, skill.ID, 1)
|
||||
var saved model.Skill
|
||||
if err := db.First(&saved, "id = ?", skill.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if saved.Name != skill.Name || saved.Description != skill.Description || saved.Instruction != skill.Instruction {
|
||||
t.Fatal("migration changed original skill fields")
|
||||
}
|
||||
version, err := svc.repo.SkillVersion(saved.CurrentVersionID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
body, err := svc.readSkillArchiveEntry(version, "SKILL.md")
|
||||
if err != nil || string(body) != skill.Instruction {
|
||||
t.Fatalf("original instruction not preserved: %v", err)
|
||||
}
|
||||
}
|
||||
if _, err := archiveFromMarkdown([]byte("## 旧技能\n"), strings.Repeat("名", 81), strings.Repeat("文", 503)); err == nil {
|
||||
t.Fatal("non-migration archive input must still reject oversized fallback metadata")
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchiveFromMarkdownTruncatesInferredDescriptionWithinLimit(t *testing.T) {
|
||||
longDescription := bytes.Repeat([]byte("描述内容。"), 140)
|
||||
data := append([]byte("# 风格库四级匹配序\n\n"), longDescription...)
|
||||
|
||||
@@ -5,6 +5,12 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
|
||||
|
||||
# 待测试
|
||||
|
||||
## 技能文件包迁移元数据长度
|
||||
|
||||
- [x] 启动迁移对旧技能名称、简介的回填元数据按字符数限制长度,省略号计入上限;不改写原始名称、简介和正文,不放宽非迁移入口的校验。
|
||||
- [x] 新增回归先复现旧回填路径的长度错误,修复后技能域全量测试通过;覆盖内置与用户旧技能、重复初始化不重复建版本、原始正文和字段保留,以及 `16000000000049` 内置技能包。
|
||||
- [ ] 在报错用户的实际部署核对后端镜像与构建提交,并用包含修复的新产物验证启动迁移。本地通过不代表用户环境已修复;本轮未发布镜像、未操作生产数据库。
|
||||
|
||||
## 模型失败原因在画布卡片展示
|
||||
|
||||
- [x] 后端 Provider、错误分类和请求日志专项回归通过;本地模拟上游验证 HTTP 400 与 HTTP 200 业务失败均保留安全原因,覆盖图片响应和通用 JSON 响应。前端 4 项专项测试通过,覆盖具体原因、HTTP 状态提示、审核元数据及 URL 隐藏。
|
||||
|
||||
Reference in New Issue
Block a user