Scaffold a new knowledge-commons graph in the current project, or wire an existing hand-built graph into promotion. Interviews the user, writes .commons.yml, scaffolds the atlas/maps/type directories, and generates project-owned process and knowledge-graph skills. Use when the user wants to "set up a knowledge graph", "init a graph in this project", "scaffold /process for this repo", "wire this graph to the commons", "instantiate the commons", or says "graph-init". Also fires for a bare config pass with no scaffolding via the --config-only flag.
This is the generator. It runs in the target project — the project that wants a
graph, not the plugin repo — and writes real, project-owned files: a .commons.yml, a scaffold, and
(full mode only) two generated skills. Nothing it writes is a stub or a wrapper around this skill;
once written, the project owns it and is expected to sharpen it from real runs.
Two modes, chosen by the invocation: full (default, /graph-init) sets up a graph from scratch.
Config-only (/graph-init --config-only) writes just .commons.yml for a graph that already has
working hand-written skills — it exists so /promote has something to read.
Before asking anything, check the project root for .commons.yml. If it exists, this graph is
already initialized — say so, and offer to re-interview one or two specific blocks (name them by
number, see the interview below) rather than starting over. Don't silently overwrite a working
config.
If no .commons.yml exists, proceed to the interview. Once block 1 has answered root:, before
scaffolding (step 5), check whether that directory already holds files. An empty or missing
directory scaffolds cleanly; a populated one means this is probably a hand-built graph that needs
--config-only, not a fresh scaffold — stop and say so rather than writing over existing notes.
Run these six blocks in order, using AskUserQuestion. Batch the questions within a block into as
few calls as the tool allows — but completeness beats round-trip economy: no question may be
dropped to make a block fit one call. AskUserQuestion takes at most four questions per call, so a
block of five takes two calls, not one truncated one. Ask every question in the block before moving
on, and don't skip a question because a similar one was asked earlier — each is here for a reason.
Don't improvise additional questions beyond this list, and never answer one yourself on the user's
behalf; if something later turns out to need a decision this list doesn't cover, ask it inline when
it comes up, not as a seventh block.
Block 1 — Graph.
What's this graph's name? It becomes domain: on every note this graph promotes — name it for the
kind of work (orchard, wellstead), not for any person or party.
Where does it live? A root path, relative to the project (e.g. knowledge/).
What should the atlas — the graph's single navigation root — be called? Any name works
(principium.md, atlas.md, index.md); it's recorded as graph.atlas and every map's genitor
points at it. Don't assume a default without asking.
What does it promote to — a path to an existing commons, "none" for a leaf graph, or "I don't have
one yet"? If the answer is "none" — a leaf graph that promotes nowhere — record that and move on;
the rest of this question doesn't apply and there is nothing to go looking for. For either of the
other two answers, read on. Before you act on them, understand what a commons is: one person's single
cross-domain graph, shared by every domain graph they own, on every machine they work from. However
they move it between machines — a git remote, a synced folder, a copy carried across by hand — it is
normally not new. A local filesystem search tells you whether it is present here; it tells you
nothing about whether it exists. Those are different questions, and conflating them is how a machine
ends up with a second empty graph also named commons, diverging silently from the real one — the
failure surfaces months later as "why can't this domain see the other domain's claims".
So resolve provenance in this order, and don't skip to the end:
.commons.yml there. If
that misses, search likely roots (ls -d ~/commons ~/Developer/commons 2>/dev/null, then a shallow
scan such as ls ~/*/.commons.yml ~/Developer/*/.commons.yml 2>/dev/null) and confirm any candidate
whose graph.name is commons with the user.gh repo list --limit 100 2>/dev/null | grep -i commons) can accelerate the question by giving
you a candidate to confirm — but treat it as a hint only. It proves nothing when it comes back
empty: gh may be absent, unauthenticated, or pointed at the wrong account, and the commons may
live somewhere it can't see or under a name this grep won't match. A silent tool is not a
negative answer — that is the same mistake as reading a local miss as "doesn't exist". If a
commons turns up by either route, you are adopting, not creating: in full mode that's step 3's adopt
path; in config-only mode adoption is out of scope, so follow that mode's own handling below instead
of coming here.Block 2 — Types. This block has six questions and so needs two AskUserQuestion calls —
questions 5 through 7, then 8, 9, and 9b. Don't try to fit it into one; the tail of this block is what
gets lost, and 9 and 9b shape the source tiers and whether a synthesis tier exists at all.
9 and 9b are deliberately separate questions. They were one question joined by "and" until a real
run showed why that fails: the tool returns one answer per question, so a reader who answers the first
half leaves the second unanswered, and the generator fills the silence with an assumption. That is the
same silent-loss shape as dropping the question outright, just harder to notice — the answer looks
present. Never recombine them to save a slot; both calls have room.
5. What does this domain call its evidence type — the atomic, provenanced note (e.g. observation,
claim)?
6. What attractor types does it need? For each, is it open (accumulates evidence, no verdict —
like a pattern) or settled (a decision with reasoning attached)?
7. Are there entities worth lookup-only notes — the nouns worth a name but no synthesis? Name them or
say none.
8. Does this graph need a reference tier for unbounded lookup facts that are never an association
surface?
9. Are there source tiers whose raw material must be preserved verbatim, distinct from the types above?
A transcript, a recording, an original document — material where the distillation is not a substitute
for the thing itself.
9b. Do any rich bounded sources — a call, a meeting, a session — warrant a synthesis: one note
distilling the whole event for a human reader, produced alongside the atomic evidence rather than
in between the source and it? Ask two things here, and record both: whether this graph wants the tier
at all, and what produces the note — a skill the project already has (a journaling skill, a
write-up skill), or /process itself. If the project already keeps such a document — a chronicle, a
session journal — say so plainly: those existing files are the synthesis tier, adopted at the path
they already occupy, and the answer to "what produces it" is whatever writes them today.
Block 3 — Sources. Five questions, so two AskUserQuestion calls — 10 through 12, then 12b and
13. Don't drop 12b to fit one call: it is what decides whether the generated pipeline has a redaction
gate, and a graph generated without one archives straight to its destination with nothing in the way.
10. What arrives on its own, without being asked for — a chronicle directory and glob, pasted URLs,
something else? Name every tier.
11. Are there on-demand sources too — articles, docs, reference material you'd point the pipeline at
when they come up, rather than anything arriving on a schedule? These get a resolve-and-preserve
pipeline (fetched material can vanish; keep a source note) but no queue.
12. For each tier: how does an input resolve to the canonical source: identity the ledger keys on,
and is there a resolver skill, or is it already local content?
12b. For each tier whose raw material is too large to sit inside a note: where does the archived copy
live, and is that destination public or shared? The path is a directory in the project, under
version control — the point of archiving is that the material outlives the session, which a scratch
or gitignored directory does not deliver. The public/shared answer is not a preference: it selects
whether the generated pipeline carries a redaction gate before it writes anything there. A repo
other people can read, now or later, is public. Ask it per tier, not once for the graph. For tiers
small enough to inline verbatim in the source note, there is nothing to archive — say so and move on.
13. What signal classes should inspection look for in this source — the categories of thing worth
turning into a note?
Block 4 — Sinks. 14. Where do non-graph outputs go — tasks, tracker rows, anything that isn't a note? Name every sink and which skill handles it. 15. What's the approval mode per sink — per-item (the sink confirms itself), batch (one combined confirmation), or silent?
Block 5 — Procedure. 16. Walk one source through, start to finish: what gets created, in what order, and what else has to update alongside it — attractor evidence sections, entity notes, map entries? 17. What's durable here versus operational — the line between what earns a note and what belongs in an operational system? 18. What must never be captured in this graph — sensitive personal data, anything with a legal or safety sensitivity specific to this domain?
Block 6 — Judgment. 19. When it's unclear whether something clears the bar, capture or skip — and which error costs more here, a thin note or a missed one? 20. What tends to generalize out of this domain, and what must never leave it?
Block 1 question 4 ends in one of three states, and they are not interchangeable. Already present here — nothing to do; use the confirmed path. Exists but isn't on this machine — adopt it. Doesn't exist at all — create it. Reaching the create branch without having genuinely ruled out the other two is the one mistake in this skill that quietly costs the most, so don't arrive here by default.
Where it lives, and why that isn't a free choice. Every domain graph on every machine writes the
literal string promotes-to: <commons root> into its own .commons.yml, and those files travel with
their projects. The path has to resolve to the same graph on every machine that reads it. ~/commons is
the default for exactly that reason — it is machine-independent and it is what the other machines already
wrote. This holds however the commons is kept in sync, or even if it isn't: the string is what has to
match, not the transport. A different root (~/dev/commons, a repo-relative path) is fine only if it is
chosen once and used everywhere; choose it here alone and the breakage is silent, deferred, and lands on
a different machine than the one where the choice was made. So when confirming the root, say why it
matters rather than presenting it as bare preference — and if the user already has a commons, its path is
already decided, not up for discussion.
Adopting an existing commons. Get a copy to the agreed root by whatever means the user already uses
to move it between machines — cloning a repo, pointing at a synced folder, copying it across. Ask if it
isn't obvious; don't assume a version control system. Then verify rather than assume: read the copy's
.commons.yml and confirm graph.name is commons. If it is, you're done — an existing commons already
carries its own config, its own atlas, and its own claims. Write nothing. No .commons.yml, no
scaffold, no knowledge-graph skill; that graph has all of them, sharpened by real runs, and regenerating
them would overwrite work. Just record its root as this project's promotes-to: and move on to step 4.
If graph.name is something other than commons, stop and ask — you've got the wrong directory, or this
person's commons is named differently and the rest of the setup needs to know.
When the copy can't be obtained now. Often it can't — the commons is on a machine you can't reach,
or the user syncs it by hand and won't do that mid-session. That is a normal outcome of the motivating
case, not an error, and it must not collapse back into creating one. Agree the root with the user,
record it as this project's promotes-to: unverified, and move on to step 4. Then say plainly what
that means: the commons isn't on this machine yet, you couldn't check its graph.name, and promotion
won't fire until the user puts it at that root — at which point it starts working with no further setup.
Write nothing into that root — not a .commons.yml, not a scaffold, not a .gitkeep. An empty
directory that later receives the real commons is fine; a scaffolded one is the second-commons bug
arriving by a slower path, and the user's own copy is what has to land there.
Creating a new commons. Only once the user has confirmed no existing commons — an empty local search
is not that confirmation. Offer to instantiate it before touching the project graph. The commons is a
fixed preset, not a fresh interview — confirm only its root path (default ~/commons, per the reasoning
above) and its atlas name (same question as block 1 question 3), then write it directly:
graph:
name: commons
root: <confirmed root>
atlas: <confirmed atlas name>
types:
evidence: { name: claim, dir: claims/, supports: principles-or-questions }
attractors:
- { name: principle, dir: principles/ }
- { name: question, dir: questions/ }
reference: { name: reference, dir: reference/ }
promotes-to: ~/.claude/rules/
generated:
knowledge-graph:
template-version: <this plugin's version>
applied: [<every delta id in the log whose file is knowledge-graph>]No sources: and no sinks: — the commons never processes anything; everything in it arrives
by promotion. Scaffold it per step 5 and generate its knowledge-graph skill per step 6 — but not
a process skill: a graph with no sources has nothing to orchestrate.
generated: is the one part of that preset that isn't literal. This path writes the commons config here
and never enters step 4, so build the block by step 4's rules rather than pasting those two placeholder
lines: read the version and the delta log, and write the knowledge-graph sub-key alone. There is no
process sub-key, because there is about to be no process skill — claiming state for a file you are
deliberately not generating is a lie the patcher would later act on.
.commons.ymlAt the project root, write the config from the interview answers, following the shape in the spec's
.commons.yml sketch (§5): graph:, types:, sources:, sinks:, promotes-to:. Under types:,
entity and synthesis tiers (when the interview declared them) follow the reference tier's shape —
entity: { name: <name>, dir: <dir>/ }, synthesis: { name: <name>, dir: <dir>/ }. Omit sources:
and sinks: entirely for a graph that has none — don't write empty lists. Do not invent top-level keys
the spec doesn't name (there is no feeders: or similar registry in this design).
Three sub-keys come from the questions added to blocks 2 and 3, and none of them is a new top-level key:
types:
synthesis:
name: <synthesis type name>
dir: docs/chronicle/ # MAY sit outside graph.root — see below
produced-by: <skill that writes one> # from block 2 q9b; omit if /process writes it itself
sources:
- type: <tier>
# ...path, glob, identity, resolver...
archive: knowledge/sources/raw/ # from block 3 q12b; omit for a tier that inlines
gate: public # public | shared | private — selects the redaction gatearchive: names a committed directory in the project. Never a gitignored or scratch path: the
whole point is that the material outlives the session. Omit it for a tier whose material inlines
verbatim into the source note.gate: is what makes the generated preserve stage carry a redaction gate (public, shared) or
not (private). Write it on every tier that has an archive:. When the user was unsure, write
public — the gate costs friction, and its absence costs a leak that git history outlives.types.synthesis.dir: may name a path outside graph.root. That is the normal case when the
project already keeps the document — the existing files are the synthesis tier, adopted where they
are. Record the real path; do not relocate them into the graph and do not invent a parallel directory.On a fresh write — and only on a fresh write — also write a generated: block, the legal sixth key
holding applied-delta state. The re-run path is different and is handled at the end of this step; read that
before writing the block over a config that already exists.
The block records which template deltas this project's generated skills already contain, and
/graph-patch reads it to decide what is still pending. Writing it here is what keeps a freshly
generated graph out of /graph-patch's bootstrap path: the skills you are about to write in step 6 are rendered from the current templates, so
every delta in the log at this version is already baked into them, and recording those ids as applied is
exactly what stops the patcher from proposing edits the prose already contains. Shape:
generated:
process:
template-version: <this plugin's version>
applied: [<every delta id in the log whose file is process>]
knowledge-graph:
template-version: <this plugin's version>
applied: [<every delta id whose file is knowledge-graph>]Build it by reading two plugin files at generation time — both resolve for you, the generator:
${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json for version, and
${CLAUDE_PLUGIN_ROOT}/references/deltas.md for the ids, taking every entry whose file: matches, in log
order.
Read the log; never write the ids from memory. A list hardcoded into this skill is correct only until
the next delta is written, and then it is wrong in the direction that doesn't announce itself: the new
delta gets recorded as already applied in a skill that was generated before it existed, so /graph-patch
treats it as done and never offers it again. That is the silent-loss failure this whole mechanism exists to
end, reproduced by the generator that feeds it. If the log is missing, or has no entries for a file, write
applied: [] — an empty list is a true statement (nothing to apply yet), not a gap.
template-version means generated at when you write it. When /graph-patch later updates it, it means
patched through — the highest delta version it actually stamped. Those two coincide today and will
diverge the first time the plugin bumps a version carrying no new deltas. That's harmless and worth
knowing rather than fixing: applied: is the source of truth in both directions, and the patcher selects
by set membership on ids, never by comparing version numbers.
Order: this block is written here, in step 4, before step 6 writes the skills it describes. That is the
opposite of the ordering /graph-patch uses (it stamps only what verifiably landed), and the divergence
is deliberate. A run that dies between step 4 and step 6 leaves a config claiming state for skills that
don't exist — loud, and repaired by re-running the generator. Writing the block after step 6 instead would
mean a run that dies mid-generation leaves generated skills with no record at all, which looks exactly
like a pre-mechanism graph and lands in /graph-patch's 0.1.8 bootstrap at the wrong version. Prefer the
failure that shows.
Write a sub-key only for a skill you actually generate — a sub-key for a file you don't write claims state
the patcher will later act on. In practice this step generates both, so both appear here; the sourceless
case is the commons, whose config is written on step 3's own path with a knowledge-graph sub-key alone.
By the same test, --config-only writes no generated: block whatsoever: it generates nothing, and the
hand-written skills it wires up never came from these templates, so no delta describes a change they are
missing.
The re-run path is the exception — writing over a config that already exists, from step 1. A re-run
does not regenerate the skills, so the premise that justifies the block above is false here: nothing is
being rendered from current templates, and the state of the existing skills is not something this run
knows. generated: is not yours to compute on this path, in either of its two cases:
generated: block — carry it across untouched. It records patches
/graph-patch applied to prose you are not regenerating. It isn't part of the interview and won't come
from any answer, so a rewrite assembled purely from interview output silently drops it.generated: block — write none. Do not fall back to the fresh-write
instruction above. This is the live case, not a hypothetical: every graph generated before this
mechanism existed has no block, so a re-run there would read the log and stamp every delta in it as
applied to skills that contain not one of them. /graph-patch would then report nothing pending,
permanently. That is the same silent loss the "never write the ids from memory" paragraph warns about,
reached by a different route — and it is worse, because it is written against skills the run never
looked at. Leave those graphs to /graph-patch's bootstrap, which assumes 0.1.8 and is correct for
them by construction.Create, under graph.root:
{atlas} (the file named in graph.atlas) with initial links to the maps you're about to create.maps/, with one map file per type that will hold notes immediately — every source, evidence,
attractor, entity, and reference type declared in this graph's config. Each map carries frontmatter:
genitor: pointing at the atlas, tags: [map]. This is the one place graph-init
deliberately overrides "maps are never created empty" (graph-conventions.md's Navigation section):
the type directories are about to receive their first notes, so seed each with an index from the
start rather than waiting for five notes to accumulate with nowhere to go. Don't extend this
exception past initialization — once the graph is running, new maps still wait for the ~5-note
threshold.{evidence-dir}, each attractor's dir:, entity dir if any,
{reference-dir} if any, and a sources/ directory if this graph has sources). In a git repo, drop
a .gitkeep in each still-empty type directory — git doesn't track empty directories, and a scaffold
that vanishes on first commit is a confusing first impression.archive:, with a .gitkeep. Create it here rather
than leaving the first /process run to create it: a preserve stage that has to invent its own
destination is a preserve stage that will invent a different one under pressure.changelog.md at the graph root.Never create or overwrite a synthesis directory that sits outside graph.root. When
types.synthesis.dir: names an existing project path — a chronicle, a journal — those files are the
tier, and they are not yours. Scaffolding into that path writes a stub map or a .gitkeep into a
directory the project already curates; overwriting anything there destroys work the graph exists to
read. Verify the path exists and move on. If it doesn't exist yet, say so in the report rather than
creating it — an empty directory the project hasn't started keeping yet is the project's to open.
Fill ${CLAUDE_PLUGIN_ROOT}/references/templates/process.md and
.../templates/knowledge-graph.md and write the results to <project>/.claude/skills/process/SKILL.md
and <project>/.claude/skills/knowledge-graph/SKILL.md. Skip process/SKILL.md entirely for a graph
with no sources (the commons).
For each template:
Replace every {brace} value with the concrete config value it names, rendered as natural prose
where a literal paste would read oddly (root: . becomes "the project root", not a bare .).
Where a template refers to "the plugin's references/graph-conventions.md", stamp the resolved
concrete path (${CLAUDE_PLUGIN_ROOT}/references/graph-conventions.md resolves for you, the
generator, even though it won't for the generated skill) — a generated skill in a project has no
other way to find the plugin's files.
Write prose into every <!-- SLOT: ... --> block from the matching interview answers — the
comment names which block. The example under each SLOT shows the kind of prose expected, not
content to reuse; write this domain's own.
Delete every SLOT comment and its example after filling it, and delete the template's top
instruction note (the blockquote in knowledge-graph.md explaining the brace convention to you,
the generator — it has no business in a file a session will read as its own skill).
In process.md, include the promotion-tail section only if this graph's promotes-to: is
set; omit the whole section, not just its content, when there's nothing to promote to.
The preserve stage is generated into every process skill, unconditionally. What varies is its
form, never its presence. Fill its preserve-form SLOT per tier from the archive: answers: an
inlining tier says so plainly and names no directory; an archiving tier names its concrete committed
path. A stage generated only when the config asks for it reproduces the defect this stage was added to
fix — a missing stage is one nobody notices is missing.
The redaction-gate subsection is conditional on gate:. Generate it when any tier archiving to a
destination is public or shared; omit it entirely — no header, no placeholder — when every such
tier is private. When you generate it, say in the prose which destination assumption it was written
under, so a later reader can see the assumption instead of reconstructing it. Stamp the resolved path
to ${CLAUDE_PLUGIN_ROOT}/scripts/scan-secrets.sh the same way you stamp the resolved path to
references/graph-conventions.md — the generated skill lives in the project and has no other way to
find it.
Three pieces are conditional on types.synthesis, and they are conditional together. Generate
all three or none:
Count them before you generate. Omitting (2) is the easy mistake — it is one paragraph in a section
that is mostly about something else — and it is the one that breaks the pipeline's single invariant:
a run that writes a synthesis the plan never proposed has put a write outside the one approval gate.
Omitting (3) leaves a plan promising something nothing does. When generating, fill in the synthesis
type's name, its directory, and the produced-by: skill. When omitting all three, renumber the
sections that follow rather than leaving a gap.
For a graph with no sources (the commons), the knowledge-graph template's extraction-workflow
section has no pipeline to describe: replace it with a short "How notes arrive" section — claims
arrive via the plugin's promote skill carrying domain:, nothing is authored directly, and the
association step (existing principle / open question / new question) is where new material meets the
graph. Narrow the awareness protocol accordingly: structural noticing (drift, duplicates, a question
ready to graduate), not new-evidence capture.
The file you write must contain no braces, no SLOT comments, and no orchard or shopcraft examples — those exist in the templates to teach this generation step, not to ship. If you finish and a brace or a SLOT comment remains, that's a generation bug — go back and fill it, don't ship it.
State what was written and where — .commons.yml, the scaffold, and (if generated) the two skill
paths. Then check the repo for CI workflows with path-based triggers (.github/workflows/*.yml,
lefthook.yml, similar): processing commits will touch the graph root and the generated skill paths on
every run, and shouldn't trigger builds or deploys — flag any workflow whose path filters would fire on
them and suggest the exclusion. Finally, suggest the first concrete step: /process on one real source
file. For a commons-only run,
suggest running /promote from the domain graph that names it instead.
--config-only)For an existing, working graph — the reference implementation is the motivating case — that just
needs .commons.yml so /promote can read it. Interview only:
graph.atlas is still recorded in the config and still must be
asked rather than assumed; an existing graph already has a navigation root and only its owner knows
what it's called.This mode never adopts and never creates a commons. Question 4's provenance procedure still runs —
you still need to know which of the three states you're in — but where full mode would branch into step
3, config-only stops at recording the path. Concretely: if the commons is present here, verify it and
record it. If it exists elsewhere, agree the root with the user and record it unverified, saying that
promotion won't fire until they put it there. If it doesn't exist at all, record the agreed root, and say
that the commons has to exist before /promote can do anything — offer a full /graph-init run as the
way to create it, but don't create it here. In all three cases the deliverable is identical — the
config, and nothing else. Step 3 is full mode's; don't enter it from this mode.
Write .commons.yml and stop. Touch no notes, no maps, no skills. Say explicitly that this wires an
existing hand-built graph into promotion — the graph's actual skills stay exactly as they are.
Then check that promotion can actually fire. The graph's existing pipeline was written before this
config existed, so if promotes-to: was set, grep its processing/orchestrator skills for an in-band
promotion step ("promote", "generalize"). If none exists, say so plainly: with promotes-to: set but
no promotion tail in the pipeline, promotions will only ever happen manually via /promote — the
byproduct-of-work path this config exists for won't fire. Offer the promotion-tail section of
${CLAUDE_PLUGIN_ROOT}/references/templates/process.md as the model for a hand-added tail (screen the
run's findings, candidates in the same plan, promote invoked last, once per approved candidate) — but
don't edit the existing skills yourself; surfacing the gap is this mode's job, closing it is the
owner's.
graph-init or promote skills into a project. Only the templates
get instantiated, as project-owned files..claude/skills/process or knowledge-graph already exist
without asking first — a silent overwrite can destroy hand-sharpened prose.commons diverge silently and nothing in this design will ever tell anyone.--config-only. That mode records a path; it never adopts,
creates, or scaffolds a commons, whichever of the three states question 4 lands in..commons.yml keys beyond graph:, types:, sources:, sinks:,
promotes-to:, and generated:. This constrains the top level only — the sub-keys inside those
six are the config's vocabulary and grow as the design does (archive: and gate: under a source
tier, produced-by: under types.synthesis, and whatever a later version adds). Read as a ban on
all keys, this line would forbid writing the config the rest of this skill tells you to write. What it
actually forbids is a new registry at the top level — there is no feeders: in this design. That last
key is sanctioned but not yours: /graph-patch owns it, and it records
which template deltas have already been applied to this project's generated skills. Write it only at
generation time; never rewrite or prune it afterward, and leave it untouched when re-running over an
existing graph. Dropping it doesn't look like data loss — it looks like /graph-patch re-proposing
deltas the prose already contains, and the bug appears to be in the wrong skill. If some other
future need surfaces a gap, that's still a spec question, not a generator improvisation.24e103e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.