Skip to content

File formats

A plan is a directory whose root holds kumihimo.yaml. Format version 1.

myplan/
  kumihimo.yaml        # manifest: meta, kinds, compile defaults
  view.yaml            # layout sidecar (canvas-maintained, optional)
  .gitignore           # ignores .kumihimo/ (scaffold writes this)
  .kumihimo/
    events.jsonl        # advisory mutation log (canvas-maintained, optional)
  nodes/
    api-endpoints.md
    auth/login-flow.md # subfolders namespace ids: "auth/login-flow"

Node files

YAML frontmatter between --- lines, then the body — plain Markdown prose, preserved byte-for-byte by every tool operation.

---
kind: task
title: Rate-limit middleware
needs: [api-endpoints, pick-algorithm]
in: [ship-guarded-api]
effort: M
acceptance:
  - 429 + Retry-After on breach
links:
  - {to: redis-outage, rel: threatened-by}
---
Middleware on every authenticated route. Fail *open* on Redis errors.

Reserved keys: kind, title (optional — defaults from the id), needs, in, links (strings or {to, rel} maps), agents, skills, trains (mention edges — see below), priority (int, breaks ordering ties). Every other key is a field for the kind to validate. Scalars coerce where obvious (needs: api means [api]).

Ids come from filenames: lowercase [a-z0-9-], / for namespaces — enforced so plans survive case-insensitive filesystems.

Mentions: agents, skills, trains

Three more reserved keys, parsed exactly like needs/in (scalar-or-list, salvaged to empty on the wrong type). They are mention edges — recorded and kind-checked, but never consulted by the topological sort or the cycle guard:

---
kind: task
title: Rate-limit middleware
needs: [api-endpoints]
agents: [claude-fable-5]      # each target must be kind: agent
skills: [kumihimo-iteration]  # each target must be kind: skill
trains: [kumihimo-retro]      # each target must be kind: agent or skill
---

check treats a dangling mention target as an error, the same as a dangling needs/in/links target, and — once the target exists and its own kind resolves — additionally checks that the target's kind matches the key: an agents: target that isn't kind agent, a skills: target that isn't kind skill, or a trains: target that's neither agent nor skill, is also an error. A mention edge counts as a connection for the orphan rule, the same as any other edge.

Braid rendering

A task with mentions gets Assigned: (agents), With: (skills), and Trains: (trains) lines, each rendering only when that key is non-empty — a plain task with no crew gets none of them. Each cites its targets as Title (id): unlike a needs dependency, a mentioned agent or skill is not guaranteed a number in the document (the grouped strategy's Cast section, below, pulls agent/skill nodes out of the numbered flow entirely, and a --for slice may not select the mentioned node at all), so the id is the one handle that always resolves. See the CLI reference for braid --for and kumihimo crew.

A links: entry with rel: consult whose target is kind reference renders as its own line instead of folding into the generic See-also list:

*Consult:* Ward postmortem — docs/postmortems/ward.md (via recall query ward --corpus v1)

(via <retriever>) is omitted when the reference's retriever field is empty. A rel: consult link to a non-reference target is not a consult-link and renders exactly as any other link always has.

The Cast section

The grouped strategy (only) gets its own Cast section, right after the "how to read this braid" rubric and before the first work section — briefing the crew before the work, the same reason a preamble goes before the plan. Cast introduces every crew member the braid's text actually cites: agent/ skill nodes that are themselves selected, plus any agent/skill any selected node's agents:/skills:/trains: names even when that crew member isn't itself selected (a --where filter can drop an agent from the selection — agent kind carries no status field — while the tasks that name it stay selected and keep citing it by title), plus --for's own agent always. Each entry lists its title and kind, then whichever of its informative fields are actually set — agent: runtime, model, entry, trained; skill: invocation, source, cadence, trained — no empty placeholders. Cast members are never numbered and never appear a second time among the ordinary items (only when they are themselves selected — a cited-but-unselected crew member was never going to be numbered either way); the linear strategy has no Cast section, so a plan compiled --strategy linear renders its agent/skill nodes as ordinary items instead.

braid --for <agent-id>

Compiles one agent's work orders: every node whose agents:/skills:/ trains: mentions that agent, the skill nodes those mentioned tasks in turn mention, and the agent's own node. Deliberately not whatever the agent node's own needs:/in:/links:/mentions point at — those are not part of the selection, and degrade through the usual stub mechanism (a needs target still outside the selection becomes a stub, same as for any other slice) or simply don't appear, exactly like an out-of-selection dependency anywhere else in the braid. --where/--from/--until/--in still narrow the result the same way they always compose. An id that exists but isn't kind agent is a KumihimoError naming the kind it actually is. When the agent node carries a retrieval field, the compiled text opens with it right after the top header:

# Braid: API Guard
*Ground with:* grep the repo for the symbol first, then check docs/

silently omitted when the agent has no retrieval.

kumihimo crew / the crew MCP tool

Lists every agent/skill/reference node, sorted by kind then id, with its informative fields, its trained date, and mention counts — how many nodes reference it via each of agents:/skills:/trains:, plus (for references) its consult-link count. Dates print exactly as written and are never compared to the clock: this library has none (PLAN2 §3.6). Deciding a skill is overdue for retraining is the reader's judgment on crew's output, not something check enforces.

kumihimo export --format jsonl

One JSON object per line, sorted by node id: id, kind, title, body, effective (the node's fields with kind defaults filled in), and edges (needs, in, links: [{to, rel}], agents, skills, trains). Compact separators and ensure_ascii are pinned so the same plan exports the same bytes on every OS; the file ends with exactly one trailing newline. This is the RAG ingestion shape (PLAN2 §3.7): any indexer reads it offline, and the library itself never fetches or embeds anything. jsonl gates on check errors, the same refusal braid gives; mermaid/dot do not — see the CLI reference for why.

Shipped in the engineering pack alongside task/milestone/decision/ risk/question are the three kinds mentions typically point at. No field on any of them is required — a bare kind: agent node still loads and checks clean. trained is a str field: write it as trained: "2026-08-24", quoted — an unquoted 2026-08-24 parses as a YAML date, and check correctly rejects it as a type error rather than silently coercing it.

agent

Field Type Notes
runtime choice claude-code, cloud, human, other
model str e.g. claude-fable-5
entry str how it's invoked
scope list what it may touch
retrieval str its standing grounding command
trained str date it was last trained or tuned

skill

Field Type Notes
invocation str e.g. /kumihimo-iteration
source str path or URL to its definition
cadence str prose, e.g. "milestone close or 10 iterations"
trained str date it was last retrained

reference

Field Type Notes
locator str path, URL, or corpus name
retriever str the command that fetches it

@id prose mentions — read-only

A body may write @id to mention a node in prose ("hand this to @claude-fable-5, who runs @kumihimo-iteration"). check scans for these and warns when one dangles: body mentions '@x' but no node 'x' exists. That is the entire effect — bodies are never rewritten; the scanner only reads them. A prose mention is not itself a graph edge: it doesn't rescue an otherwise-orphaned node the way agents:/skills:/trains: do — prose is analysis-read, not structure.

The scanner is one documented regex: @ followed by an id-shaped token ([a-z0-9][a-z0-9-]*(?:/[a-z0-9-]+)*), matched only where it opens a line or follows a whitespace character. That boundary keeps a mid-word @ — an email address typed into prose — from matching, but it is not a Markdown parser: an @token that happens to open a line inside a fenced code sample (a Python decorator, say) is indistinguishable from a real mention and will still be scanned. That imprecision is accepted and documented, not fixed — see MENTION_RE in kumihimo/core/validate.py.

kumihimo.yaml

format: 1
plan: API Guard
description: One paragraph of what this plan is.
kinds:
  from: engineering        # optional pack; omit to define everything here
  task:                    # extend or define kinds
    fields:
      component: {type: str}
    # template: inline Jinja2 or a path under the plan root
compile:
  strategy: grouped        # or linear
  preamble: |
    Prepended to every braid.
  epilogue: |
    Appended to every braid.
  diagram: true            # embed the Mermaid overview
  # cord: my-cord.j2       # replace the whole document wrapper

Field spec keys: type (str | int | bool | list | choice), options (for choice), required, default.

view.yaml

layout:
  api-endpoints: {x: 40, y: 200}
collapsed: [ship-guarded-api]

layout: integers, sorted keys, flow-style — a layout shuffle is a two-line diff. collapsed: which container ids (PLAN2.md §2.3 lens 1 — any node named in another node's in) are currently folded to a chip on the canvas; sorted, flow-style, and the key is dropped entirely rather than persisted empty. Semantics never live here; deleting the file costs you an arrangement and which containers were folded, nothing more.

.kumihimo/events.jsonl

An advisory log of recent mutations, one JSON object per line, created on demand by the first op that runs against the plan:

{"actor": "cli", "op": "add_node", "targets": ["b"]}
{"actor": "mcp", "op": "update_node", "targets": ["rate-limit-core"]}

actor is set by the thin client that ran the op — "cli", "mcp", or "editor" (the running canvas's own HTTP ops API) — and defaults to "api" for a raw library call with none of those in front of it. op is the mutation's name (add_node, update_node, link, unlink, rename_node, remove_node, restore_node); targets is every node id a fresh payload digest diff would see change — for rename_node, the old id, the new id, and every referrer whose file got rewritten; for remove_node with force, the removed id plus every referrer stripped; for restore_node, just the restored id — referrers a force-remove stripped are never resurrected by a restore (see the editor guide). No timestamp, ever — this library has no clock (PLAN2 §3.6, the same guarantee crew's trained dates rely on); the running editor correlates purely by tailing the file from its own last-seen byte offset, which is all its attribution toasts need. The log grows to 400 lines before it's truncated back down to the newest 200 (oldest dropped first) — hysteresis, not a tight cap at 200: truncating on every single append once past a tight cap forced the editor's tailer to replay the whole log far more often than truncation itself actually needed to run. Best- effort throughout: a write that fails (a read-only mount, a locked file) is silently skipped rather than failing the op it's attached to, and two writers appending at nearly the same moment can race, with one's line lost to the other's — acceptable for an advisory log, unlike a node file. .kumihimo/'s mere presence, with any content, never changes what check or braid compute — the store's load path only ever reads nodes/**/*.md. Gitignored by kumihimo new's own .gitignore; see the editor guide for how this log becomes attributed toasts and pulses on the canvas.

Fidelity guarantees

Untouched files are never rewritten. Written files keep frontmatter comments, key order, quoting, newline style (LF/CRLF), and BOM; bodies are verbatim. One documented normalization: block-sequence indentation in a file an operation actually edited settles to the canonical two-space-dash style.