This release includes 1 security fix for security teams reviewing exposed deployments.
Topics
Affected surfaces
Summary
AI summaryUpdates Status: Approved v1, Files changed, and New across a mixed release.
Full changelog
The "skills can write skills" Lisp-shape primitive now works from inside a
running skill — not just on the outside MCP wire. v0.15.0 wires the SkillStore
as an McpConnector so $ skill_write and $ skill_read are dispatchable
from in-skill code, closing the substrate-symmetry asymmetry where $ data_write
worked in-skill (via DataStoreMcpConnector bootstrap wiring) but SkillStore
mutators required outside-MCP dispatch.
Pairs with two structural fixes from the same arc: the mutation gate now
catches resource_action names structurally (no more explicit data_write
special-case), and three bundled Phase 1+ demo skills ship pre-stamped with
valid approval tokens — cp from node_modules/skillscript-runtime/examples/skillscripts/
into $SKILLSCRIPT_HOME/skills/ and execute_skill({name}) runs immediately.
New: SkillStoreMcpConnector bridge — $ skill_write works in-skill
Parallel structure to DataStoreMcpConnector. Bootstrap auto-registers the
bridge under skill_read and skill_write so bare-form dispatch from inside
an executing skill resolves to the substrate:
# Skill: parent
# Status: Approved v1:...
# Autonomous: true
run:
$ skill_write name="generated-child" source="..." -> W
$ skill_read name="generated-child" -> R
emit(text="wrote ${W.name}; read ${R.source|length} bytes")
default: run
Use case: agent factories, template materialization, dynamic skill libraries —
the "skill that writes a classic Anthropic skill to the SkillStore" pattern
becomes a usable in-skill primitive.
Trust boundary: in-skill writes force # Status: Draft
In-skill $ skill_write overrides any # Status: Approved declaration in
the written body, stamping # Status: Draft instead. To make the written
skill runnable, an authorized agent (human via dashboard, or MCP-direct)
reviews and promotes via the outside-MCP skill_status tool.
Different threat model from $ data_write: a bad data_write lands one
bad row (bounded blast radius); a bad skill_write lands an executable
artifact that fires arbitrarily many times in arbitrary contexts (unbounded).
The Draft-default gate prevents an Autonomous parent that calls
~ qwen prompt="generate a skill" and writes the LLM output from creating
immediately-runnable skills with zero human review.
The Lisp-shape primitive (skill-writes-skill) is preserved. Only
immediate-loop execution (parent writes child, child runs same fire) is
gated. Outside-MCP skill_write (cold-author agents authoring directly)
keeps its existing "body declares status, auto-stamp via store" behavior.
Bridge surface (v0.15.0): write + read only
$ skill_write name="..." source="..." [overwrite=true] -> R— Draft-only override applied here.$ skill_read name="..." [version="..."] -> R— returns body, status, version.
Deferred to a later release with threat-model context attached:
skill_delete— destructive; no use case articulated yet.skill_update_status— the gate-bypassing op (Draft → Approved). Allowing
it in-skill would let an Autonomous parent both write Draft AND promote
Approved in the same fire, defeating the Draft-default trust boundary.
Mutation gate widened — structural fix, not enumeration
MUTATING_TOOL_PATTERN changed from prefix-anchored (/^(?:write_|update_|...)/ )
to underscore-boundary-anchored (/(?:^|_)(?:write|update|...)(?:_|$)/).
Resource_action shapes (skill_write, skill_delete, memory_write,
skill_update_status) now classify uniformly with action_resource shapes
(write_file, update_status). The pattern subsumes the v0.14.1 explicit
data_write special-case; data_write keeps its kind tag for back-compat
in error copy but no longer needs name-specific recognition.
Closes the discipline-only-contract gap structurally — the gate doesn't
care how a new mutating tool is named, as long as its name carries a
recognized verb token. False-positive guards: data_writer (no boundary
after verb), created_at (no boundary after verb), data_read (no verb
in token list) all correctly pass through.
New: three pre-stamped bundled Phase 1+ demo skills
examples/skillscripts/:
hello-world.skill.md— no substrate, baseline runtime check.# Vars: WHO=worlddemonstrates--inputthreading. Supersedeshello.skill.md.skill-store-roundtrip.skill.md— writes a child skill via$ skill_write, reads it back via$ skill_read, exercises the Lisp-shape primitive. Body explicitly comments the Draft-default gate.data-store-roundtrip.skill.md—$ data_write+$ data_readFTS round-trip.
All three ship with valid # Status: Approved v1:<token> stamps. Adopters
cp them into $SKILLSCRIPT_HOME/skills/ and execute_skill({name}) works
out of the box — no skill_write round-trip needed for Phase 1 dogfood.
New: token-stamp integrity CI guard
tests/dogfood-t7.test.ts case #14 — for every examples/skillscripts/*.skill.md,
recompute computeApprovalToken(body, "v1") and assert it matches the stamp
in the file. Catches body-edit-without-restamp drift on every pnpm test.
Fix path: node scripts/stamp-bundled-skills.mjs (idempotent; only writes
when the body actually changed). Sibling structural-guard pattern to the
hardcoded version assertion (case #1) and the LOC ceiling test (case #7).
Breaking: hello.skill.md → hello-world.skill.md
The legacy examples/skillscripts/hello.skill.md (Draft, no approval
stamp) is replaced by hello-world.skill.md (Approved, stamped). The
scaffold copy (scaffold/examples/hello-world.skill.md) is what
skillfile init copies into $SKILLSCRIPT_HOME/examples/. Adopters
referencing the old path need to update.
Docs
examples/README.md— restructured to highlight the three Phase 1+
demos. The "Connector requirements" table flags the new no-deps
baseline (hello-world.skill.md).src/cli.tsper-command--helpexamples updated to point at the
new filename.
Files changed
- New:
src/connectors/skill-store-mcp.ts(~95 LOC; bridge +forceDraftStatus). - New:
examples/skillscripts/{hello-world, skill-store-roundtrip, data-store-roundtrip}.skill.md. - New:
scaffold/examples/hello-world.skill.md. - New:
scripts/stamp-bundled-skills.mjs(idempotent stamper, run on body edits). - New:
tests/v0.15.0-skill-store-bridge.test.ts(16 cases). - Removed:
examples/skillscripts/hello.skill.md,.provenance.json,scaffold/examples/hello.skill.md. - Updated:
src/mutation-gate.ts(pattern widened),src/connectors/index.ts(bridge re-export),src/connectors/config.ts(bridge in KNOWN_CONNECTOR_CLASSES),src/bootstrap.ts(bridge auto-wire),src/cli.ts(hello-world references),tests/v0.14.1-mutation-gate.test.ts(10 new cases for widened pattern),tests/dogfood-t7.test.ts(version + LOC ceiling + file count + token-stamp guard + bundled-skills existsSync).
Why v0.15.0 not v0.14.2
New dispatch surface (SkillStore as in-skill bridge) — minor bump, not patch.
Per the pre-adoption rule (no external users yet, no published install base),
the bump is cheap; matching the version to the size of the claim avoids
underclaiming.
Parser hardening — \" escape + interpretDoubleQuotedEscapes consistency
Cold-adopter probe of skill-store-roundtrip surfaced three discipline-only
gaps in the string-escape surface — closed in v0.15.0:
tokenizeKeywordArgs+extractParenBodydidn't honor\"inside quoted
strings.$ skill_write source="...emit(text=\"hi\")..."truncated at
the first internal\". Both quote-state machines now recognize\",
\',\\as escapes inside their corresponding quote pair.coerceKwargValue(runtime $ op kwarg path) stripped quotes but didn't
interpret escapes — inconsistent withprocessSetValue(used for$set- function-call kwargs since v0.7.2). Now calls the shared
interpretDoubleQuotedEscapesfor double-quoted values, preserves
single-quoted as literal, handles triple-quote"""..."""multi-line
literals.$ skill_write source="...\n..."lands real newlines.
- function-call kwargs since v0.7.2). Now calls the shared
stampApprovalTokenregex matched any# Status:line in the body —
including ones inside string literals. When a parent skill body contained
source="...# Status: Approved..."and lacked its own outer Status header,
the stamper mutated the inner string content. Now only considers
# Status:lines inside the HEADER BLOCK (lines from start until first
blank line or first non-#-comment line).
interpretDoubleQuotedEscapes is now exported from parser.ts (previously
internal) so runtime + future call sites can share the implementation.
Architecture invariants reinforced
- Discipline-only contracts are bugs — the prefix-only pattern + explicit
data_writespecial-case combo was the recurring shape. Underscore-boundary
pattern + drop-the-special-case is the structural fix. The parser-hardening
pass is the same lens applied to string-escape interpretation:processSetValue
did it for some kwarg contexts,coerceKwargValuedidn't for$op
kwargs — silent-inconsistency surface eliminated. - Capability granularity follows threat model, not implementation convenience —
banked from Perry'sf2a85892thread reply. data_write's bounded blast
radius vs skill_write's unbounded blast radius is the lens that drove the
Draft-default boundary. Symmetric-for-symmetry's-sake would have been the bug. - Build/probe before claiming done — banking the lesson that running
pnpm testalone wasn't enough; the in-skill$ skill_writedemo only
surfaced as broken when actually executed via CLI. Test fixtures wrote
short bodies that didn't trigger the tokenization edge cases.
Breaking Changes
- Removal of `examples/skillscripts/hello.skill.md`; replaced by `hello-world.skill.md` (requires path updates).
- `$ skill_write` now forces the written skill to have `# Status: Draft` regardless of declared status.
- Mutation gate pattern widened from prefix‑anchored to underscore‑boundary‑anchored, eliminating special‑case handling for `data_write`.
Security Fixes
- Draft‑default enforcement for in‑skill `$ skill_write` mitigates the risk of unreviewed executable artifacts being created (unbounded blast radius vs. bounded data writes).
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About Skillscript
All releases →Related context
Related tools
Earlier breaking changes
- v0.35.0 Editing `# Deadline:` on an approved skill drops it to Draft (signature invalidates).
Beta — feedback welcome: [email protected]