CtrlK
BlogDocsLog inGet started
Tessl Logo

graph-init

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.

Invalid
This skill can't be scored yet
Validation errors are blocking scoring. Review and fix them to unlock Quality, Impact and Security scores. See what needs fixing →
SKILL.md
Quality
Evals
Security

graph-init

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.

Full mode

1. Orient

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.

2. Interview

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.

  1. 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.

  2. Where does it live? A root path, relative to the project (e.g. knowledge/).

  3. 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.

  4. 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:

    • Is it present here? If a path was given, verify it — check for a .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.
    • Does it exist somewhere else? A local miss means "not on this machine", nothing more — so ask outright: do you already have a commons somewhere I should copy in, on another machine or somewhere I can't see from here? Ask this every time the local search fails to produce a confirmed commons — that includes finding a candidate the user then rejects, which is a miss for this purpose even though the search returned something. It is the whole check, not a fallback for when some other check fails, and it's the only one that works regardless of how this person stores things. If they use a git host you can reach, a search there (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.
    • Only once the user has confirmed there is no existing commons, offer to create one. Creating is the last branch, and it needs an actual answer to get there, never merely an empty search. In config-only mode there is nothing to create — see that mode's handling.

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?

3. Adopt or create the commons

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.

4. Write .commons.yml

At 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 gate
  • archive: 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:

  • The existing file has a 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.
  • The existing file has no 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.

5. Scaffold

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.
  • One directory per declared type ({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.
  • The raw archive directory named by any tier's 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.
  • An empty 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.

6. Generate the skills

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:

    1. the synthesize section, which checks for an existing synthesis and decides what to do,
    2. the paragraph in the plan step naming which of those two it will be, and
    3. the write paragraph in the run step, making it a sibling of extraction.

    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.

7. Report

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 mode (--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:

  • Block 1 in full — all four questions: name, root, atlas, promotes-to. The atlas is easy to skip here because this mode writes no files, but 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.
  • From block 2, type names only — evidence name, attractor names, entity/reference names if present. Skip directories, sources, sinks, procedure, and judgment entirely; this mode reads an already-working graph, it doesn't shape one.

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.

Never

  • Never copy this plugin's own graph-init or promote skills into a project. Only the templates get instantiated, as project-owned files.
  • Never generate into a graph whose .claude/skills/process or knowledge-graph already exist without asking first — a silent overwrite can destroy hand-sharpened prose.
  • Never create a commons off a search coming up empty. A local miss means "not present here", not "doesn't exist"; a repo search that finds nothing may just mean the tool is missing, unauthenticated, or looking in the wrong place. No search result substitutes for asking the user. Two graphs named commons diverge silently and nothing in this design will ever tell anyone.
  • Never regenerate config, scaffold, or skills into a commons you just copied in. It arrives complete; writing into it overwrites work done on another machine.
  • Never assume the user keeps the commons in git, or in any particular tool. Ask how they move it between machines rather than reaching for a clone command.
  • Never enter full mode's step 3 from --config-only. That mode records a path; it never adopts, creates, or scaffolds a commons, whichever of the three states question 4 lands in.
  • Never write anything into a commons root you recorded but couldn't verify. An empty directory that later receives the user's real commons is the intended outcome; scaffolding it is the second-commons bug on a delay.
  • Never invent top-level .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.
Repository
ddehart/claude-code-plugins
Last updated
First committed

Is this your skill?

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.