Formalize C0/C1/C2 change classification #259

Merged
eblume merged 8 commits from formalize-change-classification into main 2026-02-23 16:19:54 -08:00
Showing only changes of commit ff482c2b96 - Show all commits

Add C2 conventions: commit naming, branch naming, cold-start, finalize exception

- Commit convention: C2(<chain>): plan/impl/close/finalize <description>
- Branch convention: mikado/<chain-stem>
- Goal card branch: frontmatter linking chain to branch
- Planning/research phase documented as first step of C2
- Cold-start session guidance via docs-mikado --resume
- Finalize as explicit exception to the one rule
- Verification happens on impl commits before closing leaf nodes
- Document planned tooling (docs-mikado --resume, mikado-branch-invariant-check)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Erich Blume 2026-02-23 16:08:26 -08:00

View file

@ -66,15 +66,26 @@ Upgrade to C2 if any of these happen during a C1 change:
A complex, multi-session change managed through the [Mikado method](https://mikadomethod.info/) with a strict branch discipline called the **Mikado Branch Invariant**.
### Planning and research
Before writing any code, invest in understanding the problem:
1. Run `mise run ai-docs` to load context
2. Search related docs, reference cards, and existing how-to guides for the change area
3. Think through the dependency graph — what prerequisites exist? What could go wrong?
4. Create Mikado cards for everything you can anticipate (you'll discover more later — that's the point of the method)
This planning phase can span multiple sessions. Cards introduced during planning are merged to main and become the foundation for work cycles later.
### The Mikado Branch Invariant
The invariant governs how commits are ordered on a C2 feature branch. The branch must always have this structure:
```
main ← [card commits] ← [code, close] ← [code, close] ← ... ← [finalize]
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
main ← [plan commits] ← [impl, close] ← [impl, close] ← ... ← [finalize]
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Planning layer Repeating work cycles
(cards only) (code then close, one leaf at a time)
(cards only) (impl then close, one leaf at a time)
```
eblume marked this conversation as resolved

honestly this feels like a weaker version of the previous rule, no? Let's think about how we can express this invariant as succinctly as possible. It occurs to me that there's another possibility we haven't precluded, which is a commit that closes a mikado leaf node PRIOR to the code which resolves that leaf node. This would imply that we closed a leaf node that wasn't actually done. Not sure it needs to be called out... the rule is just "don't close mikado leaf nodes that aren't done" I guess? Probably not worth calling out.

honestly this feels like a weaker version of the previous rule, no? Let's think about how we can express this invariant as succinctly as possible. It occurs to me that there's another possibility we haven't precluded, which is a commit that closes a mikado leaf node PRIOR to the code which resolves that leaf node. This would imply that we closed a leaf node that wasn't actually done. Not sure it needs to be called out... the rule is just "don't close mikado leaf nodes that aren't done" I guess? Probably not worth calling out.
**Rules:**
@ -88,26 +99,81 @@ main ← [card commits] ← [code, close] ← [code, close] ← ... ← [finaliz
**The length-zero case:** It is valid for the "planning layer" to have zero commits on the branch — this happens when all Mikado cards were introduced in earlier sessions and are already in main's history. The invariant is satisfied.
**Exception — finalize:** The terminal commit of a completed chain rewrites Mikado cards to historical documentation. This is a card modification after code commits, and is the only permitted violation of the one rule (see "Completing a chain" below).
### Conventions
#### Branch naming
C2 branches must be named `mikado/<chain-stem>`, where `<chain-stem>` is the filename stem of the goal card. Example: goal card `deploy-authentik.md` → branch `mikado/deploy-authentik`.
#### Goal card `branch:` frontmatter
The goal card of a C2 chain must include a `branch:` field once work begins:
```yaml
---
title: Deploy Authentik
status: active
branch: mikado/deploy-authentik
requires:
- configure-postgres
- setup-redis
tags:
- how-to
---
```
A goal card with `status: active` but no `branch:` field indicates a chain that has been planned but not yet started — the planning-phase cards exist but no implementation branch has been created.
#### Commit message convention
All commits on a `mikado/*` branch must use this format:
```
C2(<chain-stem>): <verb> <short description>
```
Verbs and their meanings:
| Verb | Phase | What it means |
|------|-------|---------------|
| `plan` | Planning layer | Introduces or modifies a Mikado card (no code changes) |
| `impl` | Work cycle | Code progress toward closing a leaf node (no card changes) |
| `close` | Work cycle | Closes a leaf node by removing `status: active` |
| `finalize` | Terminal | Rewrites cards to historical docs, adds changelog |
Examples:
```
C2(deploy-authentik): plan add postgres and redis prerequisite cards
C2(deploy-authentik): impl configure external-secrets for authentik
C2(deploy-authentik): close configure-postgres
C2(deploy-authentik): finalize rewrite cards as historical documentation
```
The `mikado-branch-invariant-check` pre-commit hook validates this convention and the invariant ordering.
### Process
1. **Goal card:** Create a how-to doc in `docs/how-to/` describing the desired end state
- Add `status: active` to frontmatter
- Add `status: active` and `branch: mikado/<chain-stem>` to frontmatter
- Create prerequisite cards discovered during planning, each with `status: active`
- Commit all cards together (or in a sequence of card-only commits)
- Commit all cards together (or in a sequence of card-only commits) using `C2(<chain>): plan ...` messages
2. **Open a PR** after the first card commits so the user can review the Mikado graph
3. **Work leaf nodes** — pick a leaf (a card with `status: active` and no unmet `requires`):
- Commit code changes that progress toward closing it
- Commit the card closure (remove `status: active`)
- Commit code changes (`C2(<chain>): impl ...`) that progress toward closing it
- **Verify the change works** (deploy from branch, run tests, etc.) before closing
- Commit the card closure (`C2(<chain>): close ...`) — remove `status: active`
- Push to origin — this is the save point
4. **Repeat** until the chain is complete
5. **New agent sessions** pick up state by running `mise run docs-mikado`
5. **New agent sessions** pick up state by running `mise run docs-mikado --resume`
### Discovering new prerequisites
When you discover a new prerequisite during code work, you must restore the Mikado Branch Invariant:
1. **Reset the branch** back to the top of the Mikado commit stack — the point where all card-introducing commits end and code commits begin
2. **Add a new commit** introducing the new prerequisite card (and updating `requires` on existing cards if needed)
1. **Reset the branch** back to the top of the Mikado commit stack — the last `C2(<chain>): plan` or `C2(<chain>): close` commit before your current `impl` commits
2. **Add a new commit** (`C2(<chain>): plan ...`) introducing the new prerequisite card (and updating `requires` on existing cards if needed)
3. **Replay the Mikado process** from the new state of the card stack
**Saving work across resets:** It is acceptable to cherry-pick or rebase code commits from before the reset back onto the branch after adding the new card. This is a pragmatic exception — use it only when you are confident the saved work is still valid given the new prerequisite. When in doubt, redo the work from scratch.
@ -120,8 +186,22 @@ When the final leaf node is closed and no `status: active` cards remain:
- Remove transient technical details (specific version numbers, temporary workarounds) that won't matter in the future
- Frame the content as "what to do if someone wanted to repeat this process"
- Add appropriate context about what was learned
- Remove `branch:` from the goal card frontmatter
2. **Add changelog information** in `docs/changelog.d/`
3. The user reviews and merges the PR
3. Commit as `C2(<chain>): finalize ...` — this is the one permitted exception to the invariant's "no card changes after code" rule
4. The user reviews and merges the PR
### Cold-start: resuming a chain in a new session
When starting a new session to continue C2 work:
1. Run `mise run ai-docs` to load context
2. Run `mise run docs-mikado --resume` — this will:
- Detect the current branch and match it to an active chain
- Show the chain state, ready leaf nodes, and current position in the invariant
- If on main, list active chains and suggest which to resume
3. Check PR comments with `mise run pr-comments <pr_number>`
4. Pick the next ready leaf node and continue with a work cycle
### Build artifacts
@ -140,6 +220,7 @@ Mikado resets apply to branch code, not build artifacts. Container images in the
---
title: Deploy Authentik
status: active # omit when complete
branch: mikado/deploy-authentik # goal cards only; omit when complete
requires: # explicit dependencies
- configure-postgres
- setup-redis
@ -149,6 +230,7 @@ tags:
```
- `status: active` marks in-progress work; remove when done (this is the ONLY way a card is marked complete)
- `branch` is set on goal cards only, linking the card to its `mikado/<chain-stem>` branch. A goal card with `status: active` but no `branch` indicates a chain that is planned but not yet started. Remove `branch` when the chain is finalized.
- `requires` lists card stems (filenames without `.md`) that must be completed first. **Keep `requires` permanently** even after prerequisites are done — it documents the dependency graph history
- `required-by` is NOT stored — it's computed by `docs-mikado`
@ -164,7 +246,7 @@ tags:
- **C0:** Commit directly to main
- **C1:** Single feature branch, PR early, push often
- **C2:** Single feature branch, Mikado Branch Invariant enforced, PR early, push after every leaf-node closure
- **C2:** Branch named `mikado/<chain-stem>`, Mikado Branch Invariant enforced, `C2()` commit convention, PR early, push after every leaf-node closure
- **Deploy from branches** — C1 and C2 changes deploy from the unmerged branch (ArgoCD `--revision`, Ansible from checkout, etc.). Reset to main after merge.
- GitOps requires pushing to test — if a pushed commit breaks, revert it promptly
@ -172,9 +254,13 @@ tags:
| Command | Purpose |
|---------|---------|
| `mise run docs-mikado` | List all active Mikado chains |
| `mise run docs-mikado` | List all active Mikado chains with branch status |
| `mise run docs-mikado <card>` | Show dependency chain for a goal card |
| `mise run docs-mikado <card> --all` | Include completed cards in full |
| `mise run docs-mikado --resume` | Resume a chain: detect branch, show state and next steps |
| `mise run docs-mikado --resume <chain>` | Resume a specific chain with branch consistency check |
The `mikado-branch-invariant-check` pre-commit hook runs automatically on `mikado/*` branches, validating commit message conventions and invariant ordering.
## Related