{"record":{"id":"2fd02ed80b667b23","repo":"sipeed/picoclaw","slug":"skill-frontmatter-name-is-required","errorCode":null,"errorMessage":"skill frontmatter name is required","messagePattern":"skill frontmatter name is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/evolution/apply.go","lineNumber":161,"sourceCode":"\treturn strings.Contains(strings.ToLower(err.Error()), \"directory not empty\")\n}\n\nfunc 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 {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/evolution/apply.go#L143-L179","documentation":"After splitting and parsing the frontmatter, validateAppliedSkillBody requires a non-empty trimmed value for the `name` field. The error means `name` is absent, null, empty, or whitespace-only in the frontmatter block. (A follow-on check, not this error, enforces that name matches the target skill.)","triggerScenarios":"Applying a skill whose frontmatter has only description (---\\ndescription: x\\n---); `name:` with no value or `name: \"\"`; name written with a tab indent so the field parser does not recognize the key; name key misspelled (Name:, title:) so fields[\"name\"] is never set.","commonSituations":"Hand-written frontmatter omitting name because the filename seems sufficient; YAML copied from another tool using a different key; indentation errors from mixing tabs and spaces; LLM-generated frontmatter dropping fields.","solutions":["Add `name: <exact target skill name>` as a top-level (column-0) frontmatter key","If the value contains special YAML characters, quote it: name: \"my-skill:v2\"","Check the key spelling and that it is not indented (must sit at column 0 within the block)","Validate locally by parsing the frontmatter and asserting a non-blank name before applying"],"exampleFix":"# before\n---\ndescription: Deploys the app\n---\n# Deploy\n...\n\n# after\n---\nname: deploy\ndescription: Deploys the app\n---\n# Deploy\n...","handlingStrategy":"validation","validationCode":"func frontmatterNamePresent(body string) bool {\n    b := strings.TrimSpace(body)\n    if !strings.HasPrefix(b, \"---\\n\") {\n        return false\n    }\n    rest := strings.TrimPrefix(b, \"---\\n\")\n    end := strings.Index(rest, \"\\n---\")\n    if end < 0 {\n        return false\n    }\n    for _, line := range strings.Split(rest[:end], \"\\n\") {\n        if v, ok := strings.CutPrefix(line, \"name:\"); ok {\n            return strings.TrimSpace(v) != \"\"\n        }\n    }\n    return false\n}","typeGuard":null,"tryCatchPattern":"if err := apply.Change(body); err != nil {\n    if strings.Contains(err.Error(), \"skill frontmatter name is required\") {\n        return fmt.Errorf(\"add 'name: <target-skill>' to the frontmatter block: %w\", err)\n    }\n    return err\n}","preventionTips":["Include 'name:' at column 0 in every frontmatter block, matching the target skill name","Quote values containing colons or special YAML characters","Lint generated frontmatter (name and description non-empty) before submitting evolution changes"],"tags":["evolution","skills","validation","frontmatter","yaml"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}