feat: zed target — .zed detection and Zed's silent frontmatter rules - #92
Merged
Conversation
Zed reads the vendor-neutral .agents/skills/<name>/SKILL.md path, but
nothing in a Zed-only repo announces that: the `agents` adapter probes
.agents/ and .codex/, neither of which Zed creates. A repo whose only
agent marker was .zed/ therefore compiled to nothing but the eager
AGENTS.md floor — paying a skill's whole body every session on an agent
that can lazy-load it for free.
The new target detects .zed/ and emits the byte-identical file `agents`
does, so a repo with both compiles that skill once, not twice. Ten
targets, nine output files.
It also surfaces two constraints Zed enforces and KSF does not, both of
which Zed applies silently — the skill is dropped at load with no
diagnostic in the UI, which is the capability loss spec §2 forbids:
- name must match ^[a-z0-9]+(-[a-z0-9]+)*$. KSF's NAME_RE is looser and
permits a doubled or trailing hyphen, so `tidy--commits` is a legal
skill name Zed will not load.
- description must be non-empty and at most 1024 UTF-8 *bytes*, not
characters — a trap for non-ASCII text, where 600 characters can be
1,200 bytes. Manifested skills are bounded at 200 characters, so only
an unmanifested SKILL.md, whose frontmatter is copied through
unvalidated, can breach either bound.
No disable-model-invocation key is emitted: KSF has no field meaning
"never auto-invoke" (triggers.auto is a hint *for* auto-invocation), so
synthesizing a value would guess at the author's intent.
Fixes a related honesty bug found while wiring it up. `explain <skill>
zed` answered "no capability degradation" for a skill Zed discards
entirely — true, but it reads as false. Adapters can now declare
target-specific constraints via an optional lint() separate from emit(),
and explain reads them directly.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
The bug
Zed reads the vendor-neutral
.agents/skills/<name>/SKILL.mdpath — Kitbash was already emitting it. But nothing in a Zed-only repo announces that: theagentsadapter probes.agents/and.codex/, and Zed creates neither.So a repo whose only agent marker was
.zed/compiled to nothing but the eagerAGENTS.mdfloor, paying a skill's whole body every session on an agent that can lazy-load it for free. Forprereviewthat is ~560 standing tokens instead of ~40 — the 13× tax the benchmark exists to measure, charged to a target that never needed to pay it.The target
zeddetects.zed/and emits the byte-identical fileagentsdoes, so a repo with both compiles that skill once, not twice. Ten targets, nine output files.It also enforces two constraints Zed applies and KSF does not. Both fail silently in Zed — the skill is dropped at load with no diagnostic in the UI, which is the capability loss spec §2 forbids:
namemust match^[a-z0-9]+(-[a-z0-9]+)*$. KSF'sNAME_REis looser and permits a doubled or trailing hyphen, sotidy--commitsis a legal skill name Zed will not load. Zed's 64-character cap is unreachable from here — KSF caps names at 41.descriptionmust be non-empty and at most 1024 UTF-8 bytes, not characters — a trap for non-ASCII text, where 600 characters can be 1,200 bytes. Manifested skills are bounded at 200 characters (600 bytes at worst), so only an unmanifestedSKILL.md, whose frontmatter is copied through unvalidated, can breach either bound.Both are warnings, so
compile --strictfails on them.No
disable-model-invocationkey is emitted. Zed accepts one, but KSF has no field meaning "never auto-invoke" —triggers.autois a hint for auto-invocation — so synthesizing a value would guess at the author's intent and could suppress a skill they expect to fire.The honesty bug this surfaced
kitbash explain <skill> zedanswered "no capability degradation" for a skill Zed discards entirely. Literally true, reads as false, and it is exactly the class of silent loss this project exists to prevent — in our own tool.Adapters can now declare target-specific constraints through an optional
lint()separate fromemit().explainreads it directly rather than emitting files to find out.Verification
.zed/-only detection, frontmatter shape, no false positives on a conforming skill, shared-path single-write with no conflict, each of the three constraint breaches,--strictfailure, andexplainsurfacing the rejection.npm run checkclean · full suite green ·npm run benchdeterministic ·node site/build.mjs --checkcurrent.zedrow; docs, site, config id table and target counts updated (9 → 10 targets; "nine files" prose left alone, since it is still nine files).Release
Version bumped to 0.12.0 with a CHANGELOG section. Tag
v0.12.0after merge to triggerrelease.yml.