Skip to content

fix(skills)!: the two skill bodies move to the repo root, the one canonical base - #8

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/skill-bodies-to-repo-root
Oct 21, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/skill-bodies-to-repo-root

Conversation

@aitrawog

Copy link
Copy Markdown
Contributor

Summary

The two skill bodies move from candy/openclaw/.agents/skills/<name>/SKILL.md to
.agents/skills/<name>/SKILL.md — the one canonical base at this repo's root — and the candy-dir tree
is deleted in the same commit. This is leg A2 of the ordered wave in opencharly/opencharly#557, and it
copies the shape opencharly/distro-arch#68 landed (leg A9): a pure git mv, proven by blob-SHA object
identity, with no dual source and no symlink bridge.

The base both readers resolve is unified in opencharly/sdk — deploykit.candyRepoRoot, leg A1, merged
bdbfd8c and tagged v0.2026294.1520.

Why this PR waited, and why it no longer does. The branch has been pushed and move-proven since the
previous round; the PR was withheld because the corpus leg disagreed with the label on this repo's node:
charly marketplace generate emitted an openclaw-layer body matching neither the branch nor its
newest tag (opencharly/plugin-marketplace#39). opencharly/plugin-marketplace#40 — merged d0b05ba,
tagged v2026.294.1612 — made the project's own pin the authority, and the same three-way comparison
now holds byte-identically on this node (measured under How tested below). #39 is closed, and this PR is
evidence it asked for.

Why the move is the fix, not a tidy-up. Two readers, two bases, one layout:

reader resolves
plugin-marketplace (skillfile.go) <entity SourceRoot>/<tree>/<name>/SKILL.md — the ref root, i.e. this repo's root
deploykit.CollectSkills (the label and charly box validate) <owner candy SourceDir>/<tree>/<name>/SKILL.md before A1; <repo root> after it

At this repo's newest tag before this branch (v2026.293.1422) the corpus reader could reach neither
of the two bodies, because both sat beside the candy instead of at the root.

The landing window, stated rather than hidden. A reader still on the pre-leg base now refuses this
tree: charly box validate reports the bodies missing, loudly, naming the tree it looked in (pasted
below). That is the wave landing in order, not a silent regression, and the same window #68 disclosed.

How tested

The bodies MOVED rather than changed. Byte-identity is proven by git OBJECT IDENTITY — each file's
blob SHA is the same object before and after the move — which does not depend on rename detection:

$ for pair in \
    "candy/openclaw/.agents/skills/openclaw/SKILL.md openclaw" \
    "candy/openclaw/.agents/skills/openclaw-layer/SKILL.md openclaw-layer" ; do
    set -- $pair; old=$1; n=$2
    a=$(git rev-parse "origin/main:$old"); b=$(git rev-parse "HEAD:.agents/skills/$n/SKILL.md")
    printf '%-16s %s %s %s\n' "$n" "${a:0:12}" "${b:0:12}" "$([ "$a" = "$b" ] && echo SAME-BLOB || echo DIFFERENT)"
  done
openclaw         81e17668e2a4 81e17668e2a4 SAME-BLOB
openclaw-layer   0a0370692196 0a0370692196 SAME-BLOB

The count moved, it did not change — 0 to 2 at the root, 2 to 0 in the candy dir:

$ git ls-tree -r --name-only origin/main | grep -cE '^\.agents/(skills/[^/]+/SKILL\.md|agents/[^/]+\.md)$'
0
$ git ls-tree -r --name-only origin/main | grep -cE '^candy/[^/]+/\.agents/(skills/[^/]+/SKILL\.md|agents/[^/]+\.md)$'
2
$ git ls-files | grep -cE '^\.agents/(skills/[^/]+/SKILL\.md|agents/[^/]+\.md)$'
2
$ git ls-files | grep -cE '^candy/[^/]+/\.agents/(skills/[^/]+/SKILL\.md|agents/[^/]+\.md)$'
0

The diff, both ways it can be read, because the gate reads only one. This worktree carries
diff.renames=false so the pre-push checker can see the rename SOURCES (opencharly/opencharly#558: its
--name-only view omits them, so a pure rename's only honest body is refused). The gate therefore
sees 149 insertions and 149 deletions across 4 files:

$ git -c diff.renames=false diff --numstat origin/main HEAD
70	0	.agents/skills/openclaw-layer/SKILL.md
79	0	.agents/skills/openclaw/SKILL.md
0	70	candy/openclaw/.agents/skills/openclaw-layer/SKILL.md
0	79	candy/openclaw/.agents/skills/openclaw/SKILL.md
$ git -c diff.renames=true diff --stat origin/main HEAD
 {candy/openclaw/.agents => .agents}/skills/openclaw-layer/SKILL.md | 0
 {candy/openclaw/.agents => .agents}/skills/openclaw/SKILL.md       | 0
 2 files changed, 0 insertions(+), 0 deletions(-)

With rename detection on, the same commit is 2 files, two 0/0 renames — no content changed. The
line count is not the evidence; the blob SHAs above are.

A control that can fail, in BOTH directions, plus a third tree where both fail. generate is the
label-emitting verb, so it is the control here. NEW is a worktree-local build of opencharly/charly at
75de486b carrying github.com/opencharly/sdk v0.2026294.1520 (the merged, tagged reader leg); OLD is
charly/bin/charly, whose go version -m shows sdk v0.2026293.417:

$ for t in unmoved moved; do for r in OLD NEW; do b=$(if [ $r = OLD ]; then echo "$OLD"; else echo "$NEW"; fi); (cd /tmp/s5992-oc-$t && rm -rf .build && $b box generate openclaw >/dev/null 2>&1); echo "generate $t $r rc=$?"; done; done
generate unmoved OLD rc=0
generate unmoved NEW rc=1
generate moved OLD rc=1
generate moved NEW rc=0
$ for r in OLD NEW; do b=$(if [ $r = OLD ]; then echo "$OLD"; else echo "$NEW"; fi); (cd /tmp/s5992-oc-nobodies && rm -rf .build && $b box generate openclaw >/dev/null 2>&1); echo "generate nobodies $r rc=$?"; done
generate nobodies OLD rc=1
generate nobodies NEW rc=1
TREE        LAYOUT      OLD      NEW
unmoved     candy-dir   rc=0     rc=1
moved       root        rc=1     rc=0
no bodies   neither     rc=1     rc=1

Both diagonals fail, so neither verdict is vacuous: the old reader refuses the root layout, the new
reader refuses the candy-dir layout, and the third tree — bodies deleted from both bases — fails in
both readers, which is what makes the two green cells mean something. The messages name the base
each reader looked under:

$ cd /tmp/s5992-oc-moved && $OLD box generate openclaw ; echo rc=$?
charly: error: command "generate": validation error: skills: skill "openclaw-layer": no body — the node carries no in-line `content:` and no SKILL.md exists under .agents/skills beside the owning candy "openclaw"
rc=1
$ cd /tmp/s5992-oc-unmoved && $NEW box generate openclaw ; echo rc=$?
charly: error: command "generate": render-prep openclaw: render-prep openclaw: skill "openclaw-layer": no body — the node carries no in-line `content:` and no SKILL.md exists under .agents/skills at the repo root of the owning candy "openclaw"
rc=1

The label and the corpus name the same file — the agreement this leg exists to establish. One box
(openclaw), the emitted ai.opencharly.skill label, and the corpus generator over a two-ref scratch
project pinned at this branch (the generator module pinned at plugin-marketplace v0.2026294.1612, the
post-#40 tag; pod-charly-hooks:v2026.290.1114 + @github.com/opencharly/layer-openclaw:feat/skill-bodies-to-repo-root):

$ cd /tmp/s5992-corp-a && $NEW marketplace generate --root /tmp/s5992-corp-a --out /tmp/s5992-out-b > gen.log 2>&1; echo rc=$?
rc=0
$ grep -E 'marketplace generate:' gen.log
marketplace generate: wrote 458 artifact(s) across 43 family(ies)
$ grep -vE '^WARNING: charly is a dev/worktree build' gen.log | grep -cE 'WARN|warning|error'
0
$ python3 /tmp/charly-s5992bb88/triple.py /tmp/s5992-oc-moved /tmp/s5992-out-b openclaw openclaw openclaw openclaw-layer
openclaw       label=2809B(2781c) file=2809B(2781c) corpus=2809B(2781c) label==file:True label==corpus:True file==corpus:True
openclaw-layer label=1975B(1963c) file=1975B(1963c) corpus=1975B(1963c) label==file:True label==corpus:True file==corpus:True

The corpus file differs from the source only by the generator's 2-line DO NOT EDIT banner and the one
blank line after the frontmatter; the comparison strips exactly those. Zero warnings means zero
generator warnings — the remaining WARNING: line is the dev build's own provenance notice that it is
not loading installed plugins, and it is excluded by name in the command above rather than by eye.
Before #40 the same command emitted 1778B for openclaw-layer from a source neither reader could
name; it now emits 1975B, the branch's bytes.

Changed paths executed live, on the final tree: charly box generate openclaw rc=0 (label carries
both entries), charly box validate rc=0 with the new reader, charly marketplace generate rc=0.

Rulebook compliance

  • R0: /charly-internals:git-workflow, /charly-internals:cutover-policy and
    /charly-internals:marketplace loaded before any git action; this repo's own AGENTS.md read first
    (Part II fix(bed)!: add the openclaw plugin candy via add_candy, so its verb is served over go-plugin #4) — it carries no candy-dir base rule, so nothing in it states the superseded layout and
    the sweep is a measured no-op rather than an omission.
  • R1: one anomaly found while proving this leg and filed, not buried: deploykit.CollectSkillBodies
    — the collector sdk#445 added so that "whether a body EXISTS is not a label question" — has no call
    site, so charly box validate with the new reader returns rc=0 on a tree whose bodies exist at
    neither base. It is separable from this move (this move's proof uses generate, the label-emitting
    verb, and it is fail-closed in every cell above), so it is filed as CollectSkillBodies has no call site: charly box validate returns rc=0 on a tree whose skill bodies exist nowhere sdk#446 and this
    body names generate as the control instead of claiming validate. Every gate above reported zero
    warnings.
  • R2: no second implementation: the reader base is ONE derivation in deploykit
    (candyRepoRoot), shared by the label collector and header_copy_remote.go, landed in the sdk leg.
  • R3: no symlink, no copy-beside, no fallback probe, no sleeps or retries. A pure git mv, and the
    base is computed from the loader's identity without probing the filesystem (candy_repo_root.go).
  • R4: no prose was routing around a defect here — the product side is the sdk change; this repo's
    AGENTS.md stated no base rule to correct, which is why the same-commit sweep is empty.
  • R5: the candy-dir tree is deleted in the commit; git ls-files reports 0 bodies under
    candy/*/.agents/ (pasted above), and no path is preserved beside the root tree.
  • R6: git status --short empty before and after the commit; the branch was pushed once, is not
    force-pushed, and main is untouched.
  • R7: both reader controls fail in their own direction and a third tree fails in both (pasted); the
    emitted artifacts (the Containerfile label and the corpus file) were read back and compared, not
    assumed.
  • R8: no live service is involved: this change moves files. Every verb above is a local generator;
    nothing is mocked and nothing is faked.
  • R9: the NEW binary is the worktree-local build named above (charly version 2026.294.1603,
    go version -m reporting github.com/opencharly/sdk v0.2026294.1520), and the OLD one is
    charly/bin/charly at sdk v0.2026293.417 — the exact pair whose bases differ.
  • R10: a config/docs-only move: the non-runtime standards plus the live verbs above. No
    disposable: true bed exercises skill-body location — this repo's one bed, check-openclaw-pod,
    builds and deploys a container and probes the gateway, which this move does not touch — so no runtime
    bed is claimed here.
  • HC: one cutover, one PR, no deferred work inside its scope. The other distros and layers are
    separate ordered legs of Restore the repo-root .agents/skills tree as the one canonical base, and move the distro layer candies into layer-* repos opencharly#557, not work this PR leaves half-done.

Change classification

Breaking (fix(skills)!): the accepted body location changes for this repo, and a reader left on
the candy-dir base now refuses this tree — which is the point, and why the sdk leg is producer-first.
No consumer reads this path programmatically, so there is no migration step; the visible consequence is
charly box validate going red until the reader leg is adopted, then green.

Filed issues

Runtime identity (A3)

The delivered system message in this session reads, verbatim:

You are a coding agent powered by the deepseek-v4.1-flash model.

The runtime's own config agrees — harness DeepSeek Harness (the dsh CLI, @deepseek-ai/dsh),
provider ollama-cloud, model id deepseek-v4.1-flash:

$ grep -A2 -m1 "deepseek-v4.1-flash" ~/.dsh/cordis.patch.yml
          - id: deepseek-v4.1-flash
            name: "DeepSeek V4.1 Flash"
            contextWindow: 1048576
$ grep -m1 "provider:" ~/.dsh/cordis.patch.yml
    provider: ollama-cloud

That is the finding opencharly/opencharly#538 records: the footer uses the runtime's exact names, and a
validator allowlist that does not know deepseek-v4.1-flash is the gap, not this line.

Agent: charly

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

…onical base

The two bodies move from candy/openclaw/.agents/skills/<name>/SKILL.md to
.agents/skills/<name>/SKILL.md at this repo's root — the base the marketplace
corpus reader resolves for a whole-repo ref. A pure git mv: every blob SHA is
identical before and after, so the bodies MOVED rather than changed.

Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)
@github-actions

Copy link
Copy Markdown

Review — PASS

Head SHA: 27b7942b8485090e158db28b7c340aa36fa96cbf (branch: feat/skill-bodies-to-repo-root)

Security screen: PASS — pure file relocation, no secrets/egress/guardrail weakening. The A3 "validator allowlist" sentence is runtime-identity disclosure backed by pasted config + filed opencharly#538, not an instruction to skip validation.

Checklist: All applicable rules PASS or NA. Placement: CORRECT — the reader base is one derivation in sdk (deploykit.candyRepoRoot, leg A1); the skill bodies are data relocated to the repo root, not capability code in core, and no alias file grows.

Tool-limited notes (non-blocking): I cannot re-run generate/label/corpus or confirm issue existence; the pasted evidence is internally consistent — SAME-BLOB per file, 149/149 (renames=false) vs 2×0/0 (renames=true), the 2×2+control fail matrix (both diagonals fail, third tree fails in both readers), and the three-way label/file/corpus byte match. The one real gap — charly box validate returns rc=0 on a tree with bodies at neither base — is in the sdk collector (no call site), genuinely separable, and filed as opencharly/sdk#446, with generate correctly used as the fail-closed control instead of validate. sdk#444 (copy-step no-op, "measured identical before/after") is disclosed. No runtime bed is required: the repo's one bed (check-openclaw-pod) cannot fail on this path, and the diff adds no code that needs a shipped test.

Verdict: PASS


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

View action run

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.

2 participants