Best Practices for Organizing Katas¶
Katas are the procedural complement to rules — where rules prescribe what must or should be done, katas provide tested, reusable step-by-step procedures for how to do it.
See also: Organizing Rules | Core Concepts — Katas
Part I: When to Create a Kata vs Write a Rule¶
Create a kata when:
- The guidance is a sequence of steps with a defined order
- The procedure is reusable across tasks or sessions
- An agent benefits from following it step-by-step rather than keeping it in mind continuously
- There is a correct sequence that matters (step 2 depends on step 1)
- The procedure has a name that makes it directly retrievable by agents
Write a rule when:
- The guidance is a constraint that applies continuously (MUST/SHOULD language)
- There is no inherent ordering — it just needs to be in the agent's awareness at all times
- The guidance is a single assertion, not a workflow
The litmus test: "Can this be expressed as step 1, step 2, step 3 with a defined end state?" If yes, kata. "Does this need to be in the agent's head at all times regardless of what it's doing?" If yes, rule.
Anti-pattern: Don't rewrite a rule as a kata to add false structure. "Always use type hints" is a rule, not a kata. A kata version would be: "When adding type hints to an existing module — step 1: run mypy to find untyped functions, step 2: ..."
Part II: Writing Clear, Actionable Procedures¶
Number every step. Unnumbered bullets create ambiguity about ordering. Use 1., 2., 3. explicitly.
Start each step with an imperative verb: Run, Open, Create, Verify, Commit, Check, Add, Delete.
One action per step. A step that says "Create the file and add the imports" is two steps.
Include a verifiable outcome where possible. After describing the action, add a condition the agent can check:
tdd-cycle:
1. Write a failing test for the target behaviour. Run `pytest -x` — confirm it fails.
2. Write the minimal implementation to pass the test. Run `pytest -x` — confirm it passes.
3. Refactor for clarity. Run `pytest` — confirm all tests still pass.
The when field should be a present-tense trigger condition: "When starting a new TDD cycle for a Python function." Not "TDD" or "TDD steps."
Keep procedures focused. If a kata exceeds ~10 steps, consider splitting into two named katas or introducing a sub-procedure as a separate kata that the first one references by name.
Part III: Naming Conventions¶
- Use kebab-case:
tdd-cycle,deploy-to-staging,code-review-checklist - Use action-object pattern: verb or action-phrase followed by object —
open-acquire-implement-validate,migrate-database,review-pull-request - Names must be unique within a category. Override semantics are name-based: a child kata with the same name replaces the parent's entirely.
- Names must be self-descriptive without category context. An agent calling
get_kata("tdd-cycle")should have an intuitive idea of what it will get, even without knowing it lives underpython.testing. - Avoid generic names:
process,workflow,steps,guide. They provide no domain or action signal.
Part IV: Category Organisation for Katas¶
Katas share the same dot-path category namespace as rules. Prefix-based filtering applies identically: categories=python.testing matches python.testing and all descendants.
Reuse existing category keys where logical. A TDD kata belongs under python.testing — the same category that holds testing rules. Katas and rules coexisting in the same category is intentional.
Place katas in the most specific applicable category. Prefer python.testing over python. Prefer development.lifecycle.review over development.
Kata-only categories are fine when no existing rule category fits: workflow.onboarding, workflow.incident-response. Follow the same naming conventions as rule categories.
Avoid a single mega-category for all katas. A flat katas category defeats the purpose of filtering and makes index navigation useless.
Part V: Inheritance and Override Strategy¶
Kata merging uses override-by-name semantics: a child kata with the same name in the same category completely replaces the parent's procedure. There is no step-level merging.
Override deliberately. When a child scope needs a modified procedure, copy all steps from the parent and modify what's needed. The parent procedure is no longer applied at all once overridden.
The when field overrides independently — a child can provide a different context description without the parent's steps being inherited. However, since the whole kata is replaced, always re-provide all fields you need (both when and the procedure).
New categories in child scopes are purely additive. A child that adds a deployment.katas category does not affect any parent category.
To audit overrides: compare GET /scopes/{child}/katas against GET /scopes/{parent}/katas for same-named katas across categories.
Part VI: Templating in Procedures¶
Kata procedures support the same Jinja2 template variables as rules:
scope.name,scope.description,scope.tags— scope metadata- All config variables from
settings.toml(e.g.,TEAM_NAME,SLACK_CHANNEL) daimyo_version— the running daimyo version
Always use default() for portability:
deploy-to-staging:
1. Run `{{ DEPLOY_COMMAND | default('make deploy') }}` targeting `{{ scope.tags.env | default('staging') }}`.
2. Verify the deployment in `{{ MONITORING_URL | default('the monitoring dashboard') }}`.
Keep templates minimal. The procedure must remain human-readable without substitution. Avoid complex conditionals inside steps — if the procedure varies significantly by environment, consider separate katas per environment rather than one heavily templated kata.
See Jinja2 Templates for the full variable reference and error handling behaviour.
Part VII: Flowchart Katas (fcyaml)¶
Use the #!fcyaml format when a procedure has branching logic, retry loops, or parallel paths that prose steps would obscure. A flowchart kata is still rendered as numbered steps — the format only changes how you author the procedure, not how agents read it.
Choose fcyaml when:
- The procedure has a decision point with two or more distinct paths
- A step must be retried until a condition is met (loop / back-edge)
- Two or more steps can execute in parallel and must converge before continuing
- The procedure has more than one possible end state
Stay with plain text when:
- The procedure is a strictly linear sequence with no branches
- The extra YAML overhead would outweigh the clarity benefit
- The procedure is short enough that adding
#!fcyamladds noise without signal
Design the flowchart with an explicit START node as the first entry in the nodes list. Daimyo treats the first node as the entry point; placing it first makes the intent unambiguous.
Keep nodes granular. Prefer many small nodes over one large process node with a paragraph description. The numbered step output is only as useful as the granularity of the nodes.
Use role consistently. If the procedure involves multiple roles (developer, reviewer, DevOps), tag each node with its responsible role. Agents can use this to determine which steps apply to them.
Model retries explicitly. A back-edge from a decision back to an earlier process node produces a "Return to step N (retry)" annotation. This is more precise than writing "repeat step 3 if necessary" in a prose procedure.
Pair every fork with a sync. An unpaired fork (no downstream sync node) is valid YAML but will omit the convergence annotation. If parallel paths genuinely never converge, a sync is not required — but document the divergence in the node descriptions.
Jinja2 templates work inside fcyaml. Variables are substituted before YAML parsing, so you can template node descriptions or conditions:
my-deploy: |
#!fcyaml
flowchart:
name: "Deploy to {{ TARGET_ENV | default('staging') }}"
nodes:
- id: START
type: terminator
description: "Begin {{ TARGET_ENV | default('staging') }} deployment"
next: END
- id: END
type: terminator
description: "Deployed"
Quick Reference Checklist¶
When creating or reviewing a kata:
- [ ] Name is kebab-case, action-object pattern, unique within category
- [ ]
whenfield is a present-tense trigger condition - [ ] All steps are numbered and begin with an imperative verb
- [ ] Each step describes one action; verifiable outcome included where possible
- [ ] Category is the most specific applicable domain
- [ ] If overriding a parent kata: full procedure re-provided, override is deliberate
- [ ] Templates use
default()filters for portability across scopes - [ ] Not a disguised rule — no MUST/SHOULD language in the procedure steps