{"record":{"id":"3a6a6b94fdb1bf0e","repo":"sipeed/picoclaw","slug":"unsupported-skill-frontmatter-field-q","errorCode":null,"errorMessage":"unsupported skill frontmatter field %q","messagePattern":"unsupported skill frontmatter field %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/evolution/apply.go","lineNumber":267,"sourceCode":"\t\t}\n\t}\n\tif end < 0 {\n\t\treturn \"\", body\n\t}\n\treturn strings.Join(lines[1:end], \"\\n\"), strings.TrimLeft(strings.Join(lines[end+1:], \"\\n\"), \"\\n\")\n}\n\nfunc parseSkillFrontmatterFields(frontmatter string, allowExtraFields bool) (map[string]string, error) {\n\tvar raw map[string]any\n\tif err := yaml.Unmarshal([]byte(frontmatter), &raw); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid skill frontmatter: %w\", err)\n\t}\n\tfor key := range raw {\n\t\tif key != \"name\" && key != \"description\" {\n\t\t\tif allowExtraFields {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"unsupported skill frontmatter field %q\", key)\n\t\t}\n\t}\n\n\tvar typed struct {\n\t\tName        string `yaml:\"name\"`\n\t\tDescription string `yaml:\"description\"`\n\t}\n\tif err := yaml.Unmarshal([]byte(frontmatter), &typed); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid skill frontmatter: %w\", err)\n\t}\n\treturn map[string]string{\n\t\t\"name\":        typed.Name,\n\t\t\"description\": typed.Description,\n\t}, nil\n}\n\nfunc stripLeadingH1(body string) string {\n\tlines := strings.Split(strings.TrimLeft(body, \"\\n\"), \"\\n\")","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/evolution/apply.go#L249-L285","documentation":"parseSkillFrontmatterFields (pkg/evolution/apply.go:267) rejects frontmatter keys other than name/description when allowExtraFields is false. allowExtraFields comes from allowsExistingFrontmatterFields: true only when the target skill already exists on disk AND change_kind is append or merge. So create and replace always require exactly name+description, and append/merge to a not-yet-existing skill do too.","triggerScenarios":"Create/replace draft with extra keys such as `version:`, `metadata:`, `tags:`, `license:`; append draft where the target skill does not exist (hadOriginal=false); replace draft — the existing skill's richer frontmatter is discarded and the new body must be minimal.","commonSituations":"LLM adding metadata fields beyond the prompt's two-key contract; authors copying frontmatter from other agent-skill formats that allow `metadata:`; trying to upgrade a hand-written skill with extra fields via a replace draft.","solutions":["Strip the draft frontmatter down to only name and description","Use append/merge against the existing skill — extra keys already in the existing file's frontmatter are then allowed","Store extra metadata outside SKILL.md (e.g. in the evolution profile) instead of frontmatter"],"exampleFix":"# before (create draft)\n---\nname: deploy-checks\ndescription: ...\nversion: 2\nmetadata:\n  owner: platform\n---\n\n# after\n---\nname: deploy-checks\ndescription: ...\n---","handlingStrategy":"validation","validationCode":"func frontmatterKeysAllowed(body string, allowExtra bool) error {\n\tvar raw map[string]any\n\tif err := yaml.Unmarshal([]byte(extractFrontmatter(body)), &raw); err != nil {\n\t\treturn err\n\t}\n\tfor k := range raw {\n\t\tif k != \"name\" && k != \"description\" && !allowExtra {\n\t\t\treturn fmt.Errorf(\"unsupported frontmatter field %q\", k)\n\t\t}\n\t}\n\treturn nil\n}\n\n// allowExtra mirrors allowsExistingFrontmatterFields:\n// hadOriginal && (kind == append || kind == merge)","typeGuard":null,"tryCatchPattern":"if err := applier.ApplyDraft(ctx, ws, draft); err != nil {\n\tif strings.Contains(err.Error(), \"unsupported skill frontmatter field\") {\n\t\t// strip the named key, or switch to append/merge onto the existing skill\n\t}\n}","preventionTips":["Remember the contract: create/replace bodies may contain ONLY name and description; extra keys are tolerated only in an existing skill's frontmatter during append/merge","Do not copy richer frontmatter formats (e.g. metadata-bearing skill schemas) into create/replace drafts","Store auxiliary metadata in the evolution profile, not in SKILL.md frontmatter"],"tags":["go","yaml","frontmatter","validation","schema"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}