{"record":{"id":"39285d4aae9ce9d9","repo":"sipeed/picoclaw","slug":"skill-frontmatter-name-q-does-not-match-target-sk","errorCode":null,"errorMessage":"skill frontmatter name %q does not match target skill %q","messagePattern":"skill frontmatter name %q does not match target skill %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/evolution/apply.go","lineNumber":164,"sourceCode":"func validateAppliedSkillBody(body, targetSkillName string, allowExtraFrontmatterFields bool) error {\n\tbody = strings.TrimSpace(body)\n\tif !strings.HasPrefix(body, \"---\\n\") {\n\t\treturn fmt.Errorf(\"skill frontmatter is required\")\n\t}\n\tif !strings.Contains(body, \"\\n# \") {\n\t\treturn fmt.Errorf(\"skill heading is required\")\n\t}\n\tfrontmatter, _ := splitSkillFrontmatter(body)\n\tfields, err := parseSkillFrontmatterFields(frontmatter, allowExtraFrontmatterFields)\n\tif err != nil {\n\t\treturn err\n\t}\n\tname := strings.TrimSpace(fields[\"name\"])\n\tif name == \"\" {\n\t\treturn fmt.Errorf(\"skill frontmatter name is required\")\n\t}\n\tif name != targetSkillName {\n\t\treturn fmt.Errorf(\"skill frontmatter name %q does not match target skill %q\", name, targetSkillName)\n\t}\n\tif strings.TrimSpace(fields[\"description\"]) == \"\" {\n\t\treturn fmt.Errorf(\"skill frontmatter description is required\")\n\t}\n\treturn nil\n}\n\nfunc allowsExistingFrontmatterFields(kind ChangeKind, hadOriginal bool) bool {\n\treturn hadOriginal && (kind == ChangeKindAppend || kind == ChangeKindMerge)\n}\n\nfunc renderAppliedBody(draft SkillDraft, existingBody string, hadOriginal bool) (string, error) {\n\tswitch draft.ChangeKind {\n\tcase ChangeKindCreate:\n\t\tif hadOriginal {\n\t\t\treturn \"\", fmt.Errorf(\"cannot create skill %q: skill already exists\", draft.TargetSkillName)\n\t\t}\n\t\treturn renderDeployableSkillBody(draft.BodyOrPatch), nil","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/evolution/apply.go#L146-L182","documentation":"Thrown by validateAppliedSkillBody (pkg/evolution/apply.go:164) after a draft is rendered into the final SKILL.md body: the YAML frontmatter `name` value must exactly equal the draft's TargetSkillName, which is also the on-disk directory under <workspace>/skills/. The evolution applier enforces this so a deployed skill file always self-identifies with the folder it lives in and skill loaders/recall never see a name/folder mismatch. For append/merge the existing file's frontmatter is kept verbatim, so it is the existing skill that gets re-validated here.","triggerScenarios":"ApplyDraft / runtime cold-path apply with change_kind create or replace where draft.BodyOrPatch frontmatter says `name: commit_helper` while draft.TargetSkillName is `commit-helper` (case, underscores vs hyphens, renamed concept); or change_kind append/merge onto an existing <workspace>/skills/<name>/SKILL.md whose frontmatter name was hand-edited away from its directory name.","commonSituations":"LLM draft generators producing a slug different from the target_skill_name set by the organizer; a developer renaming a skill directory without editing SKILL.md frontmatter or vice versa; re-applying an old draft after the target skill was renamed; copy-pasting frontmatter from another skill file.","solutions":["Make the frontmatter `name` field exactly equal draft.TargetSkillName (same case, hyphen-separated, <=64 chars)","For append/merge onto a hand-edited skill, fix the existing SKILL.md frontmatter name to match its directory name before applying","If the new frontmatter name is the intended one, change draft.TargetSkillName (and the target directory) instead of the body","Regenerate the draft body with the name pinned to the target skill"],"exampleFix":"// before (draft)\nfrontmatter: \"---\\nname: commit_helper\\ndescription: ...\\n---\\n# ...\"\nTargetSkillName: \"commit-helper\"\n\n// after\nfrontmatter: \"---\\nname: commit-helper\\ndescription: ...\\n---\\n# ...\"\nTargetSkillName: \"commit-helper\"","handlingStrategy":"validation","validationCode":"func frontmatterNameMatches(body, target string) error {\n\tlines := strings.Split(strings.TrimSpace(body), \"\\n\")\n\tif len(lines) < 2 || strings.TrimSpace(lines[0]) != \"---\" {\n\t\treturn nil // missing frontmatter fails earlier checks\n\t}\n\tend := -1\n\tfor i := 1; i < len(lines); i++ {\n\t\tif strings.TrimSpace(lines[i]) == \"---\" {\n\t\t\tend = i\n\t\t\tbreak\n\t\t}\n\t}\n\tif end < 0 {\n\t\treturn nil\n\t}\n\tvar fm struct {\n\t\tName string `yaml:\"name\"`\n\t}\n\tif err := yaml.Unmarshal([]byte(strings.Join(lines[1:end], \"\\n\")), &fm); err != nil {\n\t\treturn fmt.Errorf(\"invalid frontmatter: %w\", err)\n\t}\n\tif strings.TrimSpace(fm.Name) != target {\n\t\treturn fmt.Errorf(\"frontmatter name %q != target %q\", fm.Name, target)\n\t}\n\treturn nil\n}\n\n// before apply:\n// if err := frontmatterNameMatches(draft.BodyOrPatch, draft.TargetSkillName); err != nil { ... }","typeGuard":null,"tryCatchPattern":"if err := applier.ApplyDraft(ctx, ws, draft); err != nil {\n\tif strings.Contains(err.Error(), \"does not match target skill\") {\n\t\t// fix draft frontmatter name or TargetSkillName, not the store\n\t}\n}","preventionTips":["Pin the frontmatter name to draft.TargetSkillName in the draft generator prompt and in any hand-authored draft","Before enabling evolution on a workspace, lint every skills/<dir>/SKILL.md so frontmatter name == directory name (append/merge re-validates the existing file)","Normalize skill slugs once (lowercase, hyphens) at draft creation so generator output and target names cannot drift"],"tags":["go","skills","frontmatter","yaml","validation","llm-draft"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}