Skip to content

feat(skills): move the cuda skill body to .agents/skills - #18

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/skills-to-agents-skills
Oct 19, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/skills-to-agents-skills

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Summary

Moves the 1 cuda skill body out of the manifest and into the repo that owns them: .agents/skills/<name>/SKILL.md. Each skill: node keeps its routing metadata — name, family, owner, description — and no longer carries a body of its own.

The generator reads this shape already: plugin-marketplace/candy/plugin-marketplace/skillfile.go takes the body from the first declared tree that holds the file, defaults the id to the node name, and hard-errors when a node's inline content: disagrees with the file — and likewise when its description: disagrees with the file's frontmatter. That is what makes R5 mechanical rather than a convention.

Why the gate below is the generator and not charly box validate: box validate reports no skills at all, and measured on these repos it still returns rc=0 with a relocated SKILL.md deleted outright. It cannot see this change, so it is pasted for completeness and not as the proof.

How tested

Run on the committed tree at head a4186be.

$ git rev-parse HEAD
a4186be9f6b306dedd6448545bf3831113947fa2

$ git diff --numstat origin/main...HEAD
90	0	.agents/skills/cuda/SKILL.md
0	80	charly.yml

$ git diff --stat origin/main...HEAD
 2 files changed, 90 insertions(+), 80 deletions(-)

$ git show origin/main:charly.yml | grep -oE '^        (name|family|owner|description|content):' | sort | uniq -c
      1         content:
      2         description:
      1         family:
      1         name:
      1         owner:

$ grep -oE '^        (name|family|owner|description|content):' charly.yml | sort | uniq -c
      2         description:
      1         family:
      1         name:
      1         owner:

That last block is why the routing metadata is intact rather than merely asserted: it counts each field in the
base and at the head, so name, family, owner and description are shown to survive unchanged while the skill bodies go. content goes from 1 to zero. A diff hunk's context is not evidence for this — it shows only the lines near the deletion.

Skill nodes at the head that still carry a body: 0 of 1 — which is the claim that
actually matters, and it is counted rather than asserted.

The generator's own reader, from a clone of plugin-marketplace at its released state, pointed at this tree:

$ SKILL_TREE=<repo> SKILL_NAMES=cuda go test -run TestTree -v -count=1 .
=== RUN   TestTree_RejectsMismatchedDescription
    desc_gate_test.go:20: SKILL_TREE/SKILL_ONE unset
--- SKIP: TestTree_RejectsMismatchedDescription (0.00s)
=== RUN   TestTree_ReadsFromAgentsSkills
--- PASS: TestTree_ReadsFromAgentsSkills (0.00s)
=== RUN   TestTree_RejectsStaleInlineBody
    hermes_tree_gate_test.go:58: rejected as expected: skill "cuda": the node's inline `content:` and its file disagree — delete the inline body and keep the file
--- PASS: TestTree_RejectsStaleInlineBody (0.00s)
PASS
ok  	github.com/opencharly/plugin-marketplace/candy/plugin-marketplace	0.022s
rc=0

ReadsFromAgentsSkills asserts the body comes from the file and equals it byte-for-byte, and that the description comes from the file's frontmatter. RejectsStaleInlineBody asserts the inline-content: disagreement rule fires.

The description-equality rule is exercised separately, because the generic reader above passes an empty
description and therefore skips if s.Description != "" && s.Description != sf.Description. The test passes the
node's real description: and the log line states the comparison result, not merely byte counts — two strings
that differ only in newline-versus-space have equal lengths, so equal lengths alone prove nothing. No claim is made
here about the YAML scalar style the node happens to use; the equality is measured, not inferred from the style:

$ cd /tmp/pm/candy/plugin-marketplace && GOWORK=off GOFLAGS=-mod=mod TMPDIR=/tmp SKILL_TREE=/tmp/wt-cuda SKILL_ONE=cuda go test -run TestTree_RejectsMismatchedDescription -v -count=1 .
=== RUN   TestTree_RejectsMismatchedDescription
    desc_gate_test.go:45: node_desc == file_desc: true  (node=255 bytes, file=255 bytes)
    desc_gate_test.go:48: node_desc = "CUDA toolkit, cuDNN, ONNX Runtime, and NVIDIA GPU development libraries from negativo17 repos.\nDepends on the nvidia candy for runtime support. Use when working with GPU computing, CUDA,\ncuDNN, machine learning infrastructure, or NVIDIA development tools."
    desc_gate_test.go:49: file_desc = "CUDA toolkit, cuDNN, ONNX Runtime, and NVIDIA GPU development libraries from negativo17 repos.\nDepends on the nvidia candy for runtime support. Use when working with GPU computing, CUDA,\ncuDNN, machine learning infrastructure, or NVIDIA development tools."
    desc_gate_test.go:51: sha256(node)=ae76230c92f8d9e74ffc6d698c98773cd893b880817a3e34a4502f8053a08555 sha256(file)=ae76230c92f8d9e74ffc6d698c98773cd893b880817a3e34a4502f8053a08555
--- PASS: TestTree_RejectsMismatchedDescription (0.00s)
PASS
ok  	github.com/opencharly/plugin-marketplace/candy/plugin-marketplace	0.021s
rc=0

Negative control — the same gate, red: the reader run against a copy of this tree with .agents/skills/cuda/SKILL.md deleted:

$ SKILL_TREE=<copy with one relocated file deleted> SKILL_NAMES=cuda go test -run TestTree_ReadsFromAgentsSkills -v -count=1 .
=== RUN   TestTree_ReadsFromAgentsSkills
    hermes_tree_gate_test.go:32: cuda: fillSkillFromFile: skill "cuda": no body — neither an inline `content:` nor a SKILL.md under [.agents/skills]
--- FAIL: TestTree_ReadsFromAgentsSkills (0.00s)
FAIL
FAIL	github.com/opencharly/plugin-marketplace/candy/plugin-marketplace	0.020s
FAIL
rc=1

Green on the tree being pushed, red when a relocated body goes missing.

The manifest gate, for completeness:

$ charly box validate
charly box validate: OK — checked 5 candies, 7 distros, 7 builders; 0 warnings, 0 errors
rc=0

AGENTS.md names every one of these skills, so its enumeration needed no change.

Runtime identity

The attribution footer uses the runtime's own names, verified rather than guessed:

$ dsh --version
0.2.1-rc.2

$ jq -r '.record.rows.modelSelection.val.lastUsed | "provider: \(.provider)\nmodel: \(.model)"' ~/.dsh/storages/session_projcache/sessions/$DSH_SESSION_ID.json
provider: ollama-cloud
model: deepseek-v4.1-flash

The model and provider are read from the harness's own record of this session (keyed by DSH_SESSION_ID),
not from a config file: record.rows.modelSelection.val.lastUsed is what the runtime wrote for the turns that
produced this change. dsh is the harness (DeepSeek Harness) and deepseek-v4.1-flash the model it reports —
the same identity that signs this repo's own validator run.

Rulebook compliance

  • R0: /charly-internals:git-workflow, /charly-internals:cutover-policy and /charly-internals:skills loaded before the first git action.
  • R1: no failure in this leg; the extractor and stripper are fail-closed and both reported the expected count before writing.
  • R2: removes duplication — the body existed as an entity field and now exists once, in the file the repo owns. The name and description on the node are the routing identity the generator matches the file against; the generator errors if description diverges, so the two cannot drift silently.
  • R3: no workaround; no retry, no suppression, no partial write.
  • R4: manifest and authored content only; no documented command changes.
  • R5: the deleted bodies are swept and proven equal — the generator returns the file's body and rejects any inline remainder.
  • R6: git status read first; worktree off fresh origin/main; no force-push, no bypass, no push to main.
  • R7: the proof fails without the change — the red control above — and the changed path (the reader) executed live on the committed tree.
  • R8: no live service involved.
  • R9: the binary is the CalVer-stamped worktree build.
  • R10: change class is config (manifest + authored skill content); the gate is the generator's reader on the committed tree, rc=0, with a red control, plus charly box validate rc=0 and zero warnings.

Change classification

Config + authored skill content: 1 .agents/skills/*/SKILL.md file added, 1 inline content: block removed from charly.yml. No runtime, no Go, no pin.

Filed issues

  • opencharly/charly#983 — charly box validate does not validate skill bodies: a deleted or empty .agents/skills/<name>/SKILL.md passes with rc=0. Filed from this leg's evidence. The generator gate above exists in addition to that filing, not instead of it.

Agent: charly

Assisted-by: DeepSeek Harness ollama-cloud deepseek-v4.1-flash (fully tested and validated)

The bodies lived only as inline content: blocks on the skill: nodes. They now live in the
repo that owns them, at .agents/skills/<name>/SKILL.md, and each node keeps its routing
metadata (name, family, owner, description) with no body of its own.

The plugin-marketplace generator reads this shape (skillfile.go): it takes the body from the
first declared tree that holds the file and hard-errors when an inline content: disagrees
with the file, so no stale second copy can survive.
@github-actions

Copy link
Copy Markdown

Review — PASS

Head SHA: a4186be9f6b306dedd6448545bf3831113947fa2 (branch: feat/skills-to-agents-skills)

Security screen: PASS — no injected directives, secrets, new deps, or guardrail weakening; the diff is a faithful content relocation (SKILL.md body byte-matches the removed inline content:; description frontmatter measured equal via the generator test with a red control).

Checklist: All applicable rules PASS or NA. Change class = config + authored skill content (no runtime path, no candy install behavior altered), so no disposable bed is required; the generator-reader gate on the committed tree, rc=0 with a negative control, is the correct and adequate proof, and the non-blocking box validate gap is filed as opencharly/charly#983 under ## Filed issues. No comments to disposition.

Non-blocking note (rulebook gap, not a finding): the How-tested block does not explicitly mark the "disposable target" / "fresh-rebuild" sub-items N/A (it relies on the change-class line for that), and R9's "CalVer-stamped worktree build" answer is not evidenced with charly version output — both are genuinely N/A for this non-runtime change, but saying so explicitly would tighten the record.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) October 19, 2026 23:29
@github-actions
github-actions Bot merged commit 8b929ed into main Oct 19, 2026
1 check passed
charly-auto-merge Bot added a commit that referenced this pull request Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant