fix: validate skills/agents/extends are lists (#12)#20
Merged
Conversation
A YAML scalar in skills, agents, or extends survived the 'as string[]' cast and was iterated character-by-character downstream — crashing 'umbel list' (TypeError on .join) and producing per-character nonsense refs/parents in 'umbel build'. Apply the same Array.isArray + every(string) guard already used for hooks/mcps so a scalar yields one actionable 'must be a list' error. Discovery already catches the throw and marks the bundle malformed, so 'umbel list' no longer crashes — matching the existing hooks/mcps path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #12
Problem
loadManifestvalidated thathooksandmcpsare lists of strings, butskills,agents, andextendswere castas string[]with no runtime check. A YAML scalar (e.g.extends: good,skills: local/demo) survived the cast as a plain string and was iterated character-by-character downstream:umbel listcrashed with an uncaughtTypeError: …join is not a function(list.ts:58), blocking the entire listing.umbel buildreported nonsense per-character parents/refs (extends missing parent 'g', nineskills/<char>refs).Fix
Apply the same
Array.isArray(...) && every(typeof === "string")guard already used forhooks/mcpstoextends,skills, andagents, throwing a single actionableUsageError:extends→'extends' must be a list of namesskills/agents→'…' must be a list of qualified <source>/<name> refs(wording matched to thehooks/mcpsprecedent, since these are the same kind of source-qualified ref)Discovery (
discover.ts:46-50) already catches a throwingloadManifestand marks the bundlemalformed, soumbel listno longer crashes — it now treats the scalar bundle exactly like a bad-shapehooks/mcpsbundle.Acceptance criteria
skills/agents/extendsproduces a single "must be a list" validation error.umbel listdoes not crash on a parseable bundle with a scalar field (treated as malformed).extends: [good]) still works (verified end-to-end).everyclause).Out of scope
How malformed bundles are displayed/surfaced in
list/build(tracked in #6) is unchanged — a malformed bundle surfaces as "not found" onbuild, matching the pre-existinghooks/mcpsbehavior.Verification
npm test→ 347 passing (+3 new)tsc --noEmit→ cleanbiome check .→ cleanumbel listno longer crashes; control list-formextends: [good]renders correctly.