{"record":{"id":"947fc0fc0a1e358e","repo":"multica-ai/multica","slug":"clear-hermes-skills-dir-w","errorCode":null,"errorMessage":"clear hermes skills dir: %w","messagePattern":"clear hermes skills dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/daemon/execenv/hermes_home.go","lineNumber":787,"sourceCode":"\t\t}\n\t\tadd(filepath.Clean(entry))\n\t}\n\t// The shared skills dir, referenced (not copied) so the user's global and\n\t// builtin skills stay visible. It differs from the task-local skills dir,\n\t// so Hermes won't fold it into the local root, and being last it yields to\n\t// the bound skills on a name collision.\n\tadd(filepath.Join(sharedHome, \"skills\"))\n\treturn out\n}\n\n// writeHermesBoundSkills rebuilds the task-local skills/ dir from scratch so a\n// skill removed since the last run can't linger, then writes only the\n// Multica-bound skills. They keep their natural slug (no user skills share this\n// dir) and therefore take precedence over any same-named external skill.\nfunc writeHermesBoundSkills(hermesHome string, workspaceSkills []SkillContextForEnv, logger *slog.Logger) error {\n\tskillsDir := filepath.Join(hermesHome, \"skills\")\n\tif err := os.RemoveAll(skillsDir); err != nil {\n\t\treturn fmt.Errorf(\"clear hermes skills dir: %w\", err)\n\t}\n\tif len(workspaceSkills) == 0 {\n\t\t// Defensive: callers gate on a non-empty set, but stay correct if that\n\t\t// ever changes — an empty local dir just means the external root is the\n\t\t// only source, matching un-redirected behavior.\n\t\treturn os.MkdirAll(skillsDir, 0o700)\n\t}\n\t// Skills live under env.RootDir/hermes-home, which the GC loop (cloud) or\n\t// env teardown (local_directory) wipes wholesale — no sidecar manifest.\n\treturn writeSkillFiles(skillsDir, workspaceSkills, nil)\n}\n\n// existingHermesExternalDirs reads the raw skills.external_dirs entries from a\n// parsed config document, accepting either a single string or a list (both of\n// which Hermes accepts). Returns nil when absent.\nfunc existingHermesExternalDirs(doc *yaml.Node) []string {\n\ttop := yamlDocumentRoot(doc)\n\tif top == nil {","sourceCodeStart":769,"sourceCodeEnd":805,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/daemon/execenv/hermes_home.go#L769-L805","documentation":"writeHermesBoundSkills could not os.RemoveAll the task-local skills/ directory before rebuilding it from scratch. The rebuild-from-empty design guarantees a skill removed since the last run cannot linger; a failed wipe therefore fails the overlay instead of risking a stale skill winning a name collision.","triggerScenarios":"skills/ (or something inside it) is undeletable: read-only or foreign-owned files, a Windows handle held open by a watcher/indexer/AV scanning skill files, or an immutable flag.","commonSituations":"Windows file-watching tools (editors, Dropbox, AV) pinning skill files while a task starts; overlay dirs touched by root during debugging; concurrent tasks sharing one env RootDir.","solutions":["Inspect <hermesHome>/skills ownership and permissions; fix so the daemon UID can remove it.","On Windows, exclude the env RootDir from AV/indexing/sync tools, or stop the tool holding handles, then retry.","Delete the skills dir manually (it is fully regenerated) or wipe the whole overlay.","Ensure a single daemon instance owns a given env RootDir at a time."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := writeHermesBoundSkills(hermesHome, skills, logger); err != nil {\n\tif strings.Contains(err.Error(), \"clear hermes skills dir\") {\n\t\t// wipe retry once: skills dir is fully rebuilt from the skill set\n\t\tif os.RemoveAll(skillsDir) == nil {\n\t\t\treturn writeHermesBoundSkills(hermesHome, skills, logger)\n\t\t}\n\t}\n\treturn err\n}","preventionTips":["On Windows, keep watchers/AV off the env root so skills/ can be wiped.","Rebuild-from-scratch semantics mean the dir must always be deletable — never mark it read-only.","One writer per env RootDir at a time."],"tags":["filesystem","windows","skills","hermes","execenv","file-locking"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}