Skip to content

skills

Recipe card from the charly-internals plugin (Development — contributor internals).

Skills are living documents at marketplace/<family>/skills/<name>/SKILL.md. They are the primary procedural knowledge source for every supported agent harness and are always loaded before codebase exploration. This skill covers when and how to update them.

The repository has two complete root dispatchers: CLAUDE.md is the harness-specific adapter and AGENTS.md is the harness-neutral rulebook for compatible readers. Keep their trigger → skill mappings and overall policy equivalent while confining harness-specific mechanics to the adapter and skills. When multiple triggers apply, load all matching skills before acting. Full index: marketplace/README.md.

The corpus must actually LOAD — audit delivery, not just content

Section titled “The corpus must actually LOAD — audit delivery, not just content”

A skill that never reaches the agent is indistinguishable from one that was never written. Enablement and installation are SEPARATE halves of the harness contract, and satisfying only the first fails SILENTLY:

  • Claude CodeenabledPlugins in .claude/settings.json flips enablement only. Skills load when the harness ALSO holds an install record for that plugin scoped to the CURRENT project. A settings.json listing a plugin whose install record belongs to a different project, or whose recorded installPath no longer exists, loads NONE of its skills — the marketplace, manifest, and SKILL.md frontmatter can all be valid while the corpus is entirely absent.
  • claude plugin list reads enablement from settings.json and prints Status: enabled regardless of whether the install record resolves, so it CONFIRMS a corpus that is not loading. It is not evidence; never close an audit on it.

The only reliable probe is a fresh session’s own answer:

Terminal window
claude -p "List the skills available to you whose name contains ':'"

The fix writes both halves — never hand-edit the settings JSON, which would reproduce the same half-configured state:

Terminal window
claude plugin install <plugin>@<marketplace> --scope project

Run the probe after any change to a marketplace, to a plugin’s enablement, or to a harness config — and BEFORE concluding that a dispatcher row is wrong. A dispatcher pointing at a skill the session cannot load presents exactly like a badly written dispatcher, and the wrong fix (rewriting the row) leaves the real defect in place.

Scope the enabled set to what the repo actually does. A repo that never builds images or runs pods should not enable the image/pod plugins: besides the token cost, each one contributes its pod-provided MCP servers, which then log connection failures for boxes that were never meant to be running there.

Updating the marketplace is NOT updating the plugin. The corpus is cached per marketplace COMMIT (~/.claude/plugins/cache/<marketplace>/<plugin>/<sha>/). claude plugin marketplace update <marketplace> refreshes the marketplace clone only — an installed plugin keeps resolving its OLD commit, so a fix that has already merged and regenerated stays invisible: a removed skill still loads, a removed MCP server still fails on every session. Move the plugin itself, at the scope it is installed at:

Terminal window
claude plugin update <plugin>@<marketplace> --scope project

It names the commits it moved between (updated from <old> to <new>), and that line is the confirmation to look for — a refreshed marketplace clone is not evidence that any session sees the change. --scope defaults to user and fails outright on a project-scoped plugin.

Trigger Action
Deployment step fails or needs undocumented workaround Update the relevant /charly-core:*, /charly-build:*, /charly-check:*, /charly-automation:*, kind plugin (/charly-image:*, /charly-vm:*, /charly-kubernetes:*, /charly-local:*, /charly-pod:*), per-pod plugin (/charly-jupyter:*, /charly-coder:*, …), or split-foundation plugin (/charly-distros:*, /charly-languages:*, /charly-infrastructure:*, /charly-tools:*)
Verification check missing from image skill Add to the image skill’s Verification section
Skill’s recommended defaults are wrong Fix in the skill, not the project rulebook (AGENTS.md / CLAUDE.md)
New feature added to charly CLI Update /charly-core:<cmd> or /charly-build:<cmd> skill + /charly-internals:go source map
New candy or box added Create skill via charly box new candy scaffold or manual SKILL.md
Bug fix changes behavior Document the fix in affected skills
Cross-skill behavior discovered Update Cross-References in all affected skills
A doc / skill / comment diverges from observed reality (discovered by ANY means — not only a bed or a deleted identifier) Treat as an incident (R1): RCA it, then sweep EVERY sibling doc/skill/comment carrying the same false/outdated/misleading claim and fix them all in the current cutover (blocking, R2). The rows below are special cases of this
A live bed contradicts a skill’s claim (Risk Driven Development found it stale) Fix the stale skill in the same change — RDD keeps the living docs honest; for a high-risk claim the running system is ground truth, not the doc
Removed identifier still referenced in skill paragraph (R5 self-test failed) Update / delete the paragraph in the same commit as the removal (R5)
Project-rulebook heading / R-number / clause name changes (they are a public API) Sweep every mirroring surface in the same commit — see “Mirroring surfaces” below (R5)
Rule DETAIL accretes inside the project rulebook (matrix, catalog, worked example growing in place) Move the detail to its owning skill (see the Authoritative-copy registry); the project rulebook keeps the mandate + a *Detail:* pointer

Skills are PUBLISHED — the corpus is a public surface

Section titled “Skills are PUBLISHED — the corpus is a public surface”

Every skill in this repo is rendered as a page on opencharly.ai by charly docs generate (see /charly-build:docs). Three consequences bind every skill edit:

  • The frontmatter description: is public. It becomes the page’s subtitle and meta description, so it is read by people who have never seen the repo.
  • A dangling /charly-<plugin>:<skill> cross-reference BREAKS THE DOCS BUILD. References are rewritten into site links at generation time and an unresolvable one is a hard error, not a silent dead link. Renaming or deleting a skill therefore means sweeping every reference to it in the same change — which the build now enforces rather than trusting.
  • references/*.md split files become child pages, linked from their entry card. They carry no frontmatter by contract; only the entry SKILL.md does.

The upside is that the corpus’s internal consistency is now machine-checked: the docs repo’s drift gate (its deploy workflow regenerates and fails on any diff) and the docs build together catch a stale cross-reference that a git grep sweep missed.

  • Ephemeral issues — use conversation context or memory
  • User-specific config — use the active harness’s private memory or user configuration
  • Bug fixes in charly code — the fix is in git; document behavioral changes in skills only
  • Anything derivable from code — skills document usage, not implementation details
  • Historical / version-history content — dated change notes, renames, completed cutovers, retired identifiers → the repo’s CHANGELOG/, never a skill or the project rulebook (the project rulebook “Where things are documented” owns the full doc-split). Skills describe current behavior in present tense only; write a landing cutover’s narrative to its release CHANGELOG/<YYYY.DDD.HHMM>.md entry and state the new standing rule forward-looking in the skill, with no history.
  1. Edit the OWNING candy repo’s skill: entity, then regenerate. Since the candy de-submodule cutover the charly repo owns NO skill: entities — each family lives in its standalone owning repo (internals → layer-charly-internals (+ layer-charly- internals-extra for strict-policy/vm-spec/agents); build → layer-charly-build; check → layer-charly-check; kubernetes → layer-charly-kubernetes; image → layer-charly-image; core → pod-charly-core; automation → pod-charly-automation; local → layer-charly-local; pod → layer-charly-pod; hooks/verbs → pod-charly-hooks — see /charly-internals:marketplace). Regenerate the corpus with charly -C <marketplace-checkout> marketplace generate --root <marketplace-checkout> --out <marketplace-checkout>; the marketplace repo’s refs list (candy/charly-marketplace/charly.yml) pins the owning repos, and the deploy drift gate requires the regenerated corpus to ship with any refs/source change. Most of the corpus under marketplace/**/SKILL.md is a PROJECTION and carries a DO-NOT-EDIT banner: an edit there is reverted by the next regeneration, silently and without conflict. git grep -l 'DO[- ]NOT[- ]EDIT' tells you which files are generated — but triage the hits rather than trusting them, since a file may MENTION the banner without carrying one (agent definitions under marketplace/<family>/agents/ are hand-authored).
  2. If the insight affects cross-skill behavior, update the project rulebook (AGENTS.md / CLAUDE.md) too
  3. After any non-trivial deployment session, ask: “Did we learn anything that future sessions should know?”

Regenerate ONLY the projections of the sources you edited

Section titled “Regenerate ONLY the projections of the sources you edited”

A projection merges before the superproject source that pins it, so prose can be correct and landed in plugins/docs while its candy source is still in flight on another branch. Regenerating from a superproject tree that lacks that pending source does not bring the projection up to date — it drags it backwards, and the diff is indistinguishable from a legitimate update.

Measured: a wholesale charly marketplace generate touched 21 files in plugins and 41 in docs when three candy sources had changed. The extra ones were reverts, ~500 lines of landed prose including 317 from one git-workflow reference and 107 from the pr-validator agent spec. Nothing in the output flagged it.

So: after regenerating, restore every file whose candy source you did not touch, and audit the deletions that remain — each one should be a line you deliberately replaced.

Terminal window
git -C plugins diff --numstat # +added -deleted per file
# Every deleted line, read in full. Do NOT use `grep '^-[^-]'` to skip the
# `--- a/file` header: a deleted markdown BULLET renders as `-- text`, so that
# pattern silently drops it — and in a prose repo bullets are the majority shape,
# not an edge case. Measured on one real regeneration: 90 reported vs 96 actual,
# every missed line a bullet. Exclude the header explicitly instead, and
# cross-check against --numstat, which counts independently of any regex: if the
# two disagree, the regex is wrong, and that disagreement IS the alarm.
git -C plugins diff | grep '^-' | grep -v '^--- '
git -C plugins diff --numstat | awk '{s+=$2} END{print s}' # must equal the count above

After ANY merge, update-branch, or conflict resolution, RE-RUN this audit against the NEW base. A revert introduced by catching up is invisible to the old one, and the old number keeps looking right. Measured on one branch: against the original merge-base 580 deletions; against origin/main after the catch-up, 1170 — the second is what the squash actually lands. Both numbers were correct; only one was the merge. A grep-vs---numstat cross-check agreed perfectly throughout and was measuring the wrong baseline, so agreement between two methods does not rescue a wrong base. The one-line form that surfaces every offender:

Terminal window
git diff --numstat origin/main...HEAD | awk '$2>$1' # every file losing net lines

It caught eleven files at once, including a whole skill file deleted outright — after five consecutive audits had reported the branch clean.

And a conflict resolution is not separate from the exclusion list — it is the same concern. Resolving conflicts by regenerating is right for projections, but a regeneration run WITHOUT the protected-file list re-introduces exactly what the list exists to prevent. This recurred on two hops within an hour, the second at ~19x the scale of the first. A file you never edited appearing in that list is the signal, and “it was probably just stale” is the reading that ships the revert.

This is the SHAPE the projection EMITS, not a file you create. You author it on the candy’s

skill: entity — frontmatter fields as entity fields, the body as its content — and

charly marketplace generate writes the SKILL.md. Every emitted file carries a DO-NOT-EDIT

banner; if you find yourself editing one, the change belongs one hop upstream.

---
name: <skill-name>
description: |
One-sentence description of when to invoke this skill.
For layers: "Use when working with <component>."
For images: "MUST be invoked before building, deploying, or troubleshooting the <image> image."
---
# <Title>
## Overview / Properties
## Key Sections (varies by type)
## Cross-References (related skills)
## When to Use This Skill

Both halves are GENERATED. You author the split on the candy’s skill: entity — the entry body plus one reference: entry per topic — and charly marketplace generate writes the SKILL.md and references/*.md files. The shapes below describe what the projection must come out looking like, never files to create or edit by hand; every one of them carries a DO-NOT-EDIT banner. This is stated here because the section reads as a set of file operations, and the previous revision of this skill opened its update instructions by telling the reader to edit SKILL.md directly — so a reader arriving at this section had already been pointed the wrong way once.

A skill is either a single SKILL.md, or an entry SKILL.md plus sibling references/*.md files in the same skill directory, loaded on demand by path. Split once a single file grows past ~400 lines or spans more than one cleanly-separable topic.

  • The entry SKILL.md keeps its frontmatter unchanged, an overview, a “when to use” section, and an index table mapping each topic to its references/<file>.md with a one-line description — entries stay ~100–150 lines total.
  • Each references/<file>.md carries the full detail for its topic in plain markdown with NO frontmatter (only the entry file is dispatched by the Skill tool).
  • Cross-file links are relative (references/<name>.md); a heading another file quotes keeps its text stable across the split.
  • An Authoritative-copy registry owner (below) may keep its canonical full text inside a references/<file>.md split file rather than the top-level SKILL.md body — the registry still names the SKILL as sole owner; the reference file is where the detail actually lives.
Content type Where it belongs
Project philosophy, architecture, key rules harness root rulebook (CLAUDE.md or AGENTS.md)
Command usage, flags, examples /charly-core:<cmd> or /charly-build:<cmd> skill
Layer properties, packages, ports per-pod plugin (/charly-jupyter:<name>, /charly-coder:<name>, …) or split-foundation plugin (/charly-distros:*, /charly-languages:*, /charly-infrastructure:*, /charly-tools:*) for base layers
Image composition, deployment, verification per-pod plugin or /charly-distros:<name> / /charly-infrastructure:<name> for base images
Skill disambiguation (which skill to use) the active harness root rulebook’s R0 Skill Dispatcher
Detailed operational patterns Relevant /charly-core:* / /charly-build:* / /charly-check:* / /charly-automation:* / kind-plugin skill
Hard rule / gate / mandate (the WHAT and the MUST) each harness root rulebook, with equivalent policy and an owning-skill pointer
Operationalization / matrix / catalog / worked example (the HOW) The ONE owning skill (see the Authoritative-copy registry below)
Version history / past changes / renames / cutover narration the repo’s CHANGELOG/ (per-CalVer <YYYY.DDD.HHMM>.md) — never the project rulebook or a skill
Long-term thesis / vision / aspiration (“why & where it’s going”) VISION.md (repo root) — never restating command usage, architecture, or history

Mandate in the harness rulebook, detail in the skill

Section titled “Mandate in the harness rulebook, detail in the skill”

The canonical split for every rule: each harness root rulebook states the equivalent mandate and points at exactly one owning skill; the owning skill carries the operationalization — forbidden-pattern catalogs, decision matrices, worked examples, command sequences. Every other document links to the owner and NEVER restates it (restated copies drift; the linked original cannot).

The complete harness rulebooks own the Skill Dispatcher, RDD, ADE, and SDD mandates, the acceptance checklist, and AI-attribution tiers. Keep their policy equivalent while preserving harness-specific tool language.

Matrix / catalog / definition Sole owner
“R10 gate by change class” matrix (incl. the class → gate → tier cross-walk) + “Flag discipline” catalog /charly-check:check
R1–R5, RDD (incl. “The spike”), ADE operationalization (forbidden patterns, risk table, worked examples) /charly-internals:strict-policy
SDD operationalization (the schema→generated-code pipeline map, the schema-change recipe, the generation-coverage current state) /charly-internals:go
Hard-cutover workflow, forbidden patterns, deliverables /charly-internals:cutover-policy
disposable: / preemptible: flag semantics, “What counts as an R10 run” /charly-internals:disposable
Landing mechanics (branch loop, the two-step PR + pr-validator merge/tag, CalVer-generated-at-merge, branch protection, multi-repo order) /charly-internals:git-workflow
Agent/workflow/team primitives, hooks doctrine /charly-internals:agents
Kernel/plugin doctrine (core = kernel; every capability a plugin candy), the two authoring shapes, placement, the three-lane transport doctrine, the seams catalog, the kernel/plugin boundary law (E/M/B/D/R) + the incomplete-seam mandate /charly-internals:plugin
Skill Dispatcher, RDD/ADE/SDD mandates, acceptance checklist, attribution tiers, Documentation-only change class anchor, Key Rules index harness root rulebooks (CLAUDE.md and AGENTS.md)

A registry owner may hold its canonical text in a references/<file>.md split file rather than the top-level SKILL.md body (see “Progressive disclosure” above) — the row still names the SKILL as sole owner regardless of which file inside it carries the detail.

Mirroring surfaces — sweep when rulebook wording changes

Section titled “Mirroring surfaces — sweep when rulebook wording changes”

The project rulebook’s section headings, R-numbers, and named clauses (“flag-override clause”, “gate by change class”, “Documentation-only change class”, “documentation reviewed” tier, “Acceptance checklist”, “Post-Execution Policies”, …) are a public API. These surfaces reference them and MUST be swept in the same commit as any rename or removal (R5):

  • the 2 hooks in .claude/hooks/ (pre-commit-gate.sh, pre-push-gate.sh — deterministic command-mechanics gates only; there is no reminder-hook layer),
  • the agents in marketplace/internals/agents/*.md,
  • the 9 per-directory signpost CLAUDE.md files (charly/, candy/, marketplace/, docs/, each box/<distro>),
  • the workflows in .claude/workflows/*.js,
  • every SKILL.md that quotes a section name (grep before assuming).

The sweep test: grep -rn '<old phrase>' across the superproject + submodules returns only CHANGELOG/ context afterwards. Prefer keeping headings and clause names STABLE when rewording content — a stable name keeps every mirroring surface valid for free.

Most skills under marketplace/core/skills/ and marketplace/build/skills/ map 1:1 to a top-level charly command (e.g. /charly-build:buildcharly box build, /charly-core:charly-statuscharly status). Topic skills are the exception: they don’t correspond to a top-level command but cover a cross-cutting concept surfaced by flags or layer composition. Today’s topic skills:

Skill Surfaced via What it covers
/charly-automation:enc charly config --encrypt, charly config mount, charly config unmount, charly config passwd Encrypted-volume (gocryptfs) semantics, keyring resolution, charly-enc-<image>-<volume>.scope lifecycle
/charly-automation:openclaw-deploy Composing openclaw-* layers OpenClaw AI gateway deployment story
/charly-automation:sidecar charly config --sidecar tailscale Sidecar-container model, pod networking, env-var routing

When adding a new command, always create a matching command skill. Consider a topic skill when a concept spans multiple commands or layers and the natural home isn’t any single command’s skill. Keep the frontmatter description: explicit about the topic nature (the blocking Skill: tool dispatcher matches on description keywords).

Plugins are sorted into four use-case buckets. Directory names live at marketplace/<name>/ (no charly- prefix); plugin.json name: fields keep the charly- prefix; every skill is invoked as /charly-<plugin>:<skill>.

The authoritative per-plugin skill counts and purposes are the bucket tables in marketplace/README.md — point there, never copy them (counts drift).

Agents (marketplace/<family>/agents/<name>.md)

Section titled “Agents (marketplace/<family>/agents/<name>.md)”

Sub-agents are markdown + YAML frontmatter (name, description, tools, model, …), discovered from a plugin’s agents/ directory (charly-internals/agents/, charly-automation/agents/). Plugin-loaded agents IGNORE the hooks, mcpServers, and permissionMode frontmatter fields — keep those out of plugin agents (use .claude/agents/ or settings.json if you genuinely need them). The charly roster splits into enforcers (root-cause-analyzer, layer-validator, testing-validator — gate claims) and executors (check-bed-runner, deploy-verifier — drive charly check and return verbatim proof). Full story: /charly-internals:agents. Dynamic workflows are NOT plugin content — they live in the superproject’s .claude/workflows/*.js.

Per-directory CLAUDE.md signposts (hybrid)

Section titled “Per-directory CLAUDE.md signposts (hybrid)”

The repo-root CLAUDE.md is the complete R0–R10 harness adapter. Per-directory CLAUDE.md files (charly/, candy/, marketplace/, docs/, and each box/<distro> submodule) are THIN signposts only: they name the skills to load for that area and point back to root. They MUST NOT restate any rule body — duplication drifts (an earlier layer-validator and the reminder hooks both drifted exactly this way; the reminder hooks now name rules as pointers/triggers, never restating their bodies). Subagents and teammates load the full CLAUDE.md hierarchy from their working directory, so a signpost reaches a worker scoped to that subtree without bloating root. Compatible harnesses that read AGENTS.md use its independent, harness-neutral dispatcher and do not depend on these signposts.

The project uses two complementary sync mechanisms with .gitignore as the boundary:

What Synced by Visibility
Code, AGENTS.md / CLAUDE.md, skills, layers, images Git Public (committed)
.claude/memory/ (auto-memory) Syncthing Private (gitignored)
.claude/settings.local.json (personal overrides) Syncthing Private (gitignored)
.claude/settings.json (project policy) Git Public (committed)

Memory setup: autoMemoryDirectory: ".claude/memory" in .claude/settings.local.json. Both settings.local.json and memory/ propagate via Syncthing so your working state follows you across machines without polluting the public repo.

Rule of thumb: if it’s useful to every contributor, it lives in git (skills, AGENTS.md / CLAUDE.md, code). If it’s useful only to you, it lives in the Syncthing-synced half (memory, personal settings).

A generalizable PROVEN insight saved only to memory is a knowledge LEAK — every other charly user (and every fresh validator/teammate, which never reads your memory) re-hits the same problem and re-derives the same fix. So the moment an insight is PROVEN (a spike verdict, a discovered mechanism, an operational failure-mode + its fix), codify it into the OWNING committed skill IMMEDIATELY as a documentation-only cutover — never batch codification to a program-end docs phase, and never let memory be its only home. Memory keeps the operator-specific remainder: host facts (this box’s hardware, enabled features), personal preferences, in-flight session state. The test: would a stranger cloning the repo want this? Yes → committed skill, now. Only you → memory. (Skills also survive context compaction — conversation context does not — which is the same reason insights can’t live only in the transcript.)

The replacement claim carries the removal’s burden of proof

Section titled “The replacement claim carries the removal’s burden of proof”

When a doc/skill/comment is found false, the fix is TWO claims, not one: the deletion, and whatever is written in its place. Both need evidence. The failure mode is applying rigor to the deletion and a plausible guess to the successor — the false claim is gone, a new one ships, and the sweep that would have caught it already passed.

This is recorded because it happened four times in one cutover, across three repos. Two of the four were corrections to a stale directory: claim (charly, sdk); the other two were corrections to the cross-reference rewriter’s own documentation (plugins). What they share is not the subject but the mistake:

Replacement written Why it was also false
“inline entries have no source directory of their own” ScanInlineCandy is passed rootDir; an inline candy’s SourceDir always equals its declaring file’s dir
“the branch fires for a remote (@github) or submodule-vendored candy” CandyCopySource early-returns on GetRemote() as its FIRST statement — a remote candy can never reach that branch
“the 11 frontmatter refs are published as plain subtitle text” firstLine truncates the description to its first sentence; all 11 sit past the cut and are published nowhere
“3665 references … every reference is rewritten” 3654 are rewritten; the count was taken whole-file and described as body-scope

Every one shares a root cause: measuring or reasoning at one scope, then describing another.

Practical rules:

  • Trace the successor before writing it. A claim about which branch runs needs the early returns above it read; a claim about what is published needs the emitter read, not the producer.
  • State a partial enumeration as partial. “an inline candy … and a discover: entry can point elsewhere” is honest; an implied-complete list you cannot defend is not.
  • Re-derive figures at the scope the sentence names, with the code’s own matching rules where one exists — not an approximation of them.
  • A correction is a new claim, not a cleanup, and gets the same gate the original edit would.
  • Sweep the claim, not the sentence — and defeat line wrapping. A claim-keyed git grep of the phrase as written silently misses a copy whose text wraps mid-phrase: … four consecutive PASS / runs proved … matches neither "four consecutive PASS runs" nor a line-scoped regex. Grep the shortest distinctive fragment that cannot straddle a line break, or normalize first (git grep -h '' -- '*.md' | tr '\n' ' '). This bit twice in one PR: once as string-keyed vs claim-keyed (a table row restating corrected prose), once as a wrapped sentence reported clean.
  • Count the surfaces before declaring a sweep done. The same claim routinely lives in a skill, a CHANGELOG, a candy comment and a PR body. Fixing the history and leaving the guidance is the worst outcome: a skill is what the next session loads, a CHANGELOG is what nobody reads for advice.

Many skills carry a source map of the Go code — Source: frontmatter, file-listing tables (| charly/foo.go | … |), and inline identifier references (type/function names). These silently desync when the code is refactored (a renamed file or symbol that no skill’s grep self-test caught). Audit and fix them as follows:

  1. Extract the claims: pull every charly/*.go filename reference and identifier reference out of the skills, and check each against the live source — file exists? symbol still defined? (gopls/grep confirm.)
  2. Search INSIDE the submodule: marketplace/ is a git submodule, so a superproject git grep -- plugins is a FALSE ZERO (git grep does not cross the gitlink). Use git -C plugins grep or filesystem grep -rn.
  3. Filter the false-positive classes before flagging:
    • URL substrings (pkg.go.dev, dl.google.com → matches .go),
    • substring artifacts (mcp.go contains p.go; localpkg.go contains pkg.go),
    • legitimate non-charly refs (upstream files like storage_dest.go),
    • prose anti-pattern examples (a skill mentioning deprecated.go as a thing to avoid).
  4. Claim-keyed sweep (R5): a stale claim is fixed across EVERY skill that repeats it, not just the file where it surfaced — git -C plugins grep '<claim>' returns only CHANGELOG context afterwards. Key the grep to the CLAIM CLASS — every wording AND syntactic variant of the false claim — NOT the one surfacing string you just edited: the same false claim survives under a paraphrase and under a singular/plural or wrapper-form variant (a retired-grammar vms: fix must ALSO grep the vm:-wrapper, cpus:, and deployments: variants of the same dead grammar; a sweep keyed only to the first string leaves a sibling variant alive and costs a second validator round). The sweep also covers the cutover’s OWN freshly-drafted narrative — the CHANGELOG entry (step 5), PR description, and commit message — since a false claim can be reintroduced there even when every pre-existing skill was correctly fixed.
  5. Land docs-only: skill edits are the Documentation-only change class → documentation reviewed (no beds); the plugins commit carries a CHANGELOG/<YYYY.DDD.HHMM>.md entry too (every plugins landing does — plugins is no CHANGELOG-exception), and a cutover that regenerates the docs mirror lands a docs commit carrying its OWN entry (every docs landing does — docs is no CHANGELOG-exception either); plugins commit → docs commit → superproject pointer bumps → all tagged v<YYYY.DDD.HHMM> at merge (plugins and docs are tagged too, same as every repo — the one finalized CalVer names each repo’s changelog file and its tag). See /charly-internals:git-workflow.

Alias residue is a special case — never assert its exact location. A charly/*_aliases.go line is transitional K-wave residue that ZERO-ALIASES/K5 deletes, so a skill asserting its exact per-symbol file:line desyncs on every alias relocation and documents a path already scheduled for deletion. State the stable spec-source (the owning plugin/sdk package) and the K5-dissolution destination instead of a concrete alias location, and grep -rn every concrete alias-attribution claim (a file:line or symbol name) against the live tree before asserting it. Failure mode: a row naming exact residue locations produces alias-attribution errors round after round once that residue moves.

The code-side companion (golangci-lint, the .go compliance checklist) is /charly-internals:go-quality.

Invoke when updating documentation, creating new skills, auditing skill coverage, or deciding where new information belongs (the project rulebook vs skill vs memory).