Files
stack/docs/plans/2026-10-19_skill-read-bom-errors.md

2.1 KiB

Brief: skill reads strip a leading BOM and report some fs errors as launch-failed

Gitea #1672. Filed by Sage on Mos's ruling mos-0863 D4, from Darkwing's row 64 round 1 notes N2 and N3 (#1557, comment 29219). Low priority. It goes after row 173. Nobody starts it until it is briefed and claimed. Line numbers are at 6b6567b8b, the row 64 landing.

The problems

N2. packages/harness/src/bundle.mjs:109 builds the skill decoder as new TextDecoder("utf-8", { fatal: true }). That decoder drops a leading UTF-8 BOM, so a skill file that starts with one gets a bundle copy (:157), a digest (:159) and prompt text 3 bytes short of the enabled file. The manifest then doesn't match the file the data root holds. No repo skill has a BOM today.

N3. readSkill rethrows any fs error other than the ones it names. found (:133) maps ENOENT to skill-not-enabled and rethrows the rest, and the walk's readdirSync, lstatSync and readFileSync (:116-123) throw raw. So EACCES on a skill file, or ENOTDIR on a path, leaves readSkills as a plain Error. The launcher maps only a BundleError (packages/cli/src/launcher.mjs:256), so the person sees launch-failed or an uncaught throw instead of skill-refused. The launch still refuses, and no run is made.

What a fix needs to settle

  • The decoder keeps a BOM (ignoreBOM: true), and the copy, digest and prompt text equal the source bytes.
  • An fs error inside a skill directory, other than ENOENT on the skill or its SKILL.md, becomes a BundleError that names the skill and the path, so the launcher reports skill-refused.
  • Tests in packages/harness/tests/skills.test.mjs: a BOM file, checked against the source byte for byte; an unreadable file (chmod 000, skipped when the suite runs as root) and an ENOTDIR path, each refusing as a BundleError with code bundle-refused. One test in packages/cli/tests/launcher.test.mjs shows the launcher reports skill-refused for the EACCES case with no run made.

Out of scope

The precheck order inside the launcher (Darkwing N1, row 64 BUILD.md item 5), the SKILL_BYTES cap and the role skill lists stay as they are.