Eight skills in this repo are reachable by exactly one route: a literal
Read(...) line in an agent body. Every one of those lines carried a comment explaining
why it exists — and the explanation was false in the direction that gets the line deleted.
comment (9 lines, 8 agent files): "The `rag-retrieval` skill is slash-only, so it cannot be preloaded." verified at HEAD, all 8 skills: user-invocable: false → there is NO slash form disable-model-invocation: true → the model cannot auto-select it
Both doors are shut. The
Read works precisely because it bypasses the Skill tool entirely. So the comment names
the one path that does not exist and omits the fact that the line beneath it is the only one that
does.
A careful editor reads "slash-only", checks the skill, finds user-invocable: false,
concludes the comment is stale, and tidies it. Or reads "slash-only" and removes the
Read as redundant with a slash command. Either way 8 skills lose their only load
path and nothing fails — no linter counts this route, so the loss is invisible.
These lines are not incidental. test-skill-reachability.mjs:245-249 records that
#3221 deleted 94 dead preload edges and CONVERTED the 7 load-bearing ones into exactly these
Read lines. They are the surviving mechanism, documented by a sentence that argues
for their removal.
rag-retrieval monitoring-observability langgraph llm-integration multimodal-llm vite-advanced product-frameworks web-research-workflow
- The `langgraph` skill is slash-only, so it cannot be preloaded. Load it …
+ The `langgraph` skill is `user-invocable: false` AND
+ `disable-model-invocation: true`, so it has no slash form and the model
+ cannot auto-select it. **This `Read` is its only load path — do not remove it.**
Correcting eight comments does not stop a ninth from
being written. tests/manifests/test-agent-read-path-truthful.mjs asserts the two things
that matter:
| assertion | catches |
|---|---|
no agent claims "slash-only" about a user-invocable: false skill | the wording returning |
every Read() target resolves to a real skill | a rename missing these string literals — nothing else resolves them |
$ node tests/manifests/test-agent-read-path-truthful.mjs PASS 9 agent Read() line(s) found (the 4th activation path is present) PASS every Read() target resolves to an existing skill PASS no agent claims "slash-only" about a non-user-invocable skill control 1 — reintroduce the false wording: FAIL claims "slash-only" about `langgraph`, which is user-invocable: false control 2 — rename a Read() target: FAIL Read() targets a skill that does not exist: langgraph-renamed restore verified, zero residue
The first assertion also fails if the
count of Read lines ever hits zero — a guard that passes an empty repo is the failure
mode this milestone exists to remove.
$ bash scripts/ci/run-tests.sh tests/manifests [RUN] test-agent-read-path-truthful.mjs [PASS] test-agent-read-path-truthful.mjs Total tests: 11 · Passed: 11 · Failed: 0
A new test file in this repo has to
join three rosters or it silently never runs. Both run-all-tests.sh:297 and
ci.yml:633 delegate to scripts/ci/run-tests.sh, whose glob covers
test-*.sh AND test-*.mjs (:57) — a .mjs-blind
glob had already silently skipped test-skill-activation-channels.mjs once. Confirmed by
running the roster, not by reading it.
Where this sits in #3313. The issue reports "29 of 105 skills unreachable". Scoped against the real linters, genuinely broken is 1; 8 are these — reachable in practice, invisible to every gate; 20 are inert reference libraries nothing depends on. This PR fixes neither the 1 nor the 20. It removes the booby trap first, because it is the cheapest change with the worst failure mode: leave it and a future cleanup silently deletes eight working paths, after which the "unreachable" count becomes accurate for the wrong reason.