# kanban@0.1

**Status:** normative. **Inherits:** [`../conventions.md`](../conventions.md) — envelope, lazy
identifiers, inline fields, tags, wikilinks, preservation rules, and the shared diagnostic codes
`MDTO001`–`MDTO099`.
**Schema:** [`schema.json`](schema.json), which `$ref`s the item model from
[`../todo/schema.json`](../todo/schema.json) rather than restating it.
**Fixtures:** [`fixtures/`](fixtures/). **Owns diagnostic codes:** `MDTO200`–`MDTO299`.

---

## 1. Purpose

`kanban@0.1` describes a board as a plain Markdown file — a level-2 heading is a column, a task list
item beneath it is a card — where the file is the complete state of the board. It is a grouped view
over exactly the same item model as [`todo@0.1`](../todo/SPEC.md), so that in any ordinary Markdown
viewer a board degrades to a useful sectioned checklist rather than to noise.

---

## 2. Grammar

### 2.1 Envelope

```yaml
---
markdownto: kanban@0.1
title: Markdown To launch   # optional
id: mdto-launch             # optional
columns:                    # optional, spec-defined
  - Backlog
  - Doing
  - Done
---
```

`kanban@0.1` defines two additional frontmatter keys:

| Key | Type | Meaning |
|---|---|---|
| `columns` | sequence of strings | Optional declaration of the board's column set and order. |
| `done-column` | string | Optional name of the **done-role column** (§2.4). Default: the last column. |

When `columns` is **absent**, the columns are whatever the document's `##` headings say — the normal
case, and the one a hand-author gets for free. When it is **present**, the board opts in to strict
checking: a declared column with no heading is `MDTO206`, a heading not declared is `MDTO207`, and a
different relative order is `MDTO208` (warning). A wrong type on the key is `MDTO007`.

`done-column` names the column that means *finished*, for a board whose last column is not it — an
Icebox, an Archive, a parking lot. It is matched against the column names the way every column
argument is: case-insensitively after trimming.

**`done-column` is recognized by the envelope and deliberately unvalidated in 0.1.** Read that
literally, because the two halves are easy to mistake for a contradiction:

- *Recognized* — the key is spec-defined, not an unknown key. §2.4 gives it meaning, and the verbs
  and any board UI MUST honour it.
- *Unvalidated* — nothing about it is checked. **It carries no diagnostics in 0.1**: no code says the
  name is misspelled, and a `done-column` naming no column on the board falls back to the last column
  **silently**. In the IR it is carried in `frontmatter` verbatim, the way every other key is;
  `schema.json` gives it no property of its own and constrains it in no way. Contrast `columns:`,
  which has both a dedicated IR property and three codes (`MDTO206`–`MDTO208`).

Both halves are deliberate for one revision, and §2.4 records them as **open questions** rather than
leaving them to be discovered. **If you are repairing a board, do not add a `MDTO2xx` for a
`done-column` that resolves to nothing, and do not "correct" the key to the last column's name —
either would invent the answer to a question this version left open.** Adding the key to
`schema.json` is likewise not a repair: the schema is as intended (§2.4).

### 2.2 Document structure

| Construct | Meaning |
|---|---|
| One optional level-1 heading, before any other content | Board title (informative) |
| A level-2 heading | **Column** |
| A top-level task list item under a column | **Card** |
| A task list item nested inside a card | An item on that card's **checklist** |
| Anything else — paragraphs, blockquotes, tables, code fences, images | Prose. Preserved, rendered, never interpreted |

- A board must have **at least one column** (`MDTO201`).
- **There is no implicit column.** Every card belongs to exactly one named column, so a card before
  the first `##` is `MDTO202`. Prose before the first column is fine — a board description is normal.
- Column order in the document is the board's left-to-right layout, and card order within a column is
  the priority order. Both are meaningful; nothing sorts them.
- A column runs from its heading to the next `##`. Cards are collected in document order across list
  boundaries, so a paragraph between two lists does not split a column.
- A column name is required (`MDTO204`) and unique (`MDTO205`), compared case-insensitively after
  trimming. Source casing is preserved.
- An empty column — a heading with no cards — is valid and common.
- Heading levels 3 and deeper are not part of `kanban@0.1` (`MDTO203`).
- A column heading carries no metadata: an inline field or block identifier on a heading is `MDTO209`.

**Recovery.** A heading invalid for its *level* (`MDTO203`) does not open a column; the cards below it
belong to the enclosing column, and produce no `MDTO202` as long as some column is already open. A
heading invalid for its *content* still opens a column: for `MDTO209` the name is the heading text
with the offending constructs removed, for `MDTO204` it is the empty string. An unusable `columns:`
declaration (`MDTO007`) is discarded, and the set and order checks `MDTO206`–`MDTO208` are skipped
rather than firing once per heading.

### 2.3 Cards

A card **is** a todo item. The grammar is [`todo@0.1` §2.3–2.4](../todo/SPEC.md) verbatim, and the
schema says so: `specs/kanban/schema.json` `$ref`s `specs/todo/schema.json#/$defs/item`.

```markdown
## Doing

- [ ] Build the patch engine [owner:: @akshay] [priority:: high] [due:: 2026-08-20] ^k3
  - [x] validate
  - [ ] render
```

- `[ ]` unchecked, `[x]` / `[X]` checked; no other state (`MDTO031`).
- Bullet lists only (`MDTO032`); every list item is a task item (`MDTO030`); a card needs text once
  metadata is removed (`MDTO033`).
- Nested task items are the card's **checklist**, not nested cards. Only top-level task items in a
  column are cards, at any board depth.
- A card's checked state is not derived from its checklist.
- Fields, tags, and lazy `^ids` work exactly as in todo. Nothing inside a code span or fence is scanned.

### 2.4 The done-role column

**In the grammar the two facts stay independent.** `- [x]` marks a card done **wherever it sits**;
an unchecked card in the final column is a legal file; a checked card in the first column is a legal
file. A parser reads exactly what is written, and neither fact is ever derived from the other. Every
diagnostic in §2.5 is unchanged by this section: there is no error, and no warning, for a card whose
box disagrees with its column.

**At the verb and UI layer they are coupled.** Exactly one column carries the **done role**, and a
card *arriving* in it is done:

- The done-role column is the **last column in document order** — the same rule whether the columns
  come from the headings or from a `columns:` declaration, and the same rule for any column
  vocabulary: Done, Shipped, Complete, Live, 完了, `Q3 — released`. Flow reads left to right and the
  rightmost column is where work stops; nothing in the rule reads a name.
- `done-column: <name>` in the envelope (§2.1) overrides that, for a board whose last column is a
  parking lot rather than an ending. Here the done role is `Shipped`, and a card dragged into
  `Icebox` crosses *out* of it:

  ```markdown
  ---
  markdownto: kanban@0.1
  done-column: Shipped
  ---

  ## Doing
  ## Shipped
  ## Icebox
  ```
- A move that crosses **into** the done-role column also checks the box; a move that crosses **out**
  of it unchecks the box. A move *within* a column — including within the done-role column — is a
  reorder and touches no box, which is what leaves a hand-written `- [ ]` sitting in the last column
  showing its actual state instead of being quietly corrected.
- A board that is a mutation surface draws no per-card checkbox: the column is the control. Checked
  state is still rendered — struck through and dimmed — because the file may carry a state no
  gesture produced.

The two claims fit together because the coupling is a property of the *verbs*, not of the grammar.
`mdto kanban move` writes both facts (§5, and `--keep-state` opts out); `mdto kanban done` and
`mdto kanban edit --check` still write the box alone, and never move a card. A file therefore stays
hand-authorable in every combination, and a tool never has to guess which of two disagreeing facts
the author meant.

**Open questions, deliberately deferred:** whether a card whose box disagrees with its column
deserves a `MDTO2xx` warning; whether `done-column` naming no column should be a diagnostic;
whether `schema.json` should constrain the key. All three are questions for a minor version, to be
answered there **with fixtures** — none is an implementation's to guess, and until one is answered
the behaviour above is the whole contract. `kanban@0.1` therefore ships no fixture exercising
`done-column`: a key with no diagnostic has nothing for an invalid fixture to assert, and the valid
case is already covered by the unknown-key preservation every fixture exercises (conventions §10.1).

### 2.5 Diagnostics owned by this spec

| Code | Severity | Meaning |
|---|---|---|
| `MDTO201` | error | Board has no columns. |
| `MDTO202` | error | Card appears before the first column heading; there is no implicit column. |
| `MDTO203` | error | Heading level is not valid in a kanban document: only one leading `#` title and `##` columns. |
| `MDTO204` | error | Column heading has no name. |
| `MDTO205` | error | Duplicate column name. |
| `MDTO206` | error | Column declared in frontmatter `columns` has no heading in the document. |
| `MDTO207` | error | Column heading is not declared in frontmatter `columns`. |
| `MDTO208` | warning | Column order differs from the frontmatter `columns` declaration. |
| `MDTO209` | error | Inline field or block identifier on a column heading; columns carry no metadata in 0.1. |

Everything a card can get wrong is a shared code from the item model — see conventions §9.3. That
overlap is the point: the same broken card produces the same code in every spec that shares the item
model — `todo`, `kanban`, and `backlog` alike — and only the message names the spec. `MDTO031` is
where that matters most, because the legal marker set differs between them (conventions §9.3).

---

## 3. Rationale

*Read this before repairing a file. Most bad repairs come from guessing at intent.*

**Why headings are columns.** It is the only mapping that survives a viewer that knows nothing about
Markdown To. Open a conforming board in GitHub, Obsidian, or `less` and you get a sectioned checklist
that is still genuinely useful — that graceful degradation *is* the portability promise, not a
side effect of it. Any encoding that made a board unreadable without the tool (a table of columns, a
YAML block, HTML comments) would have broken the promise on line one.

**Why a board is a todo list with more columns.** One item model means one parser, one validator, one
patch engine, and one set of habits for anyone — human or agent — who has learned either spec. The
schemas make the claim structurally rather than by convention: kanban's card definition is a `$ref`
to todo's item definition, so the two cannot drift apart without someone deliberately breaking the
reference.

**Why there is no implicit column, when todo has an implicit section.** The two specs differ here on
purpose. A todo list is fundamentally a list, and grouping is an optional convenience — so a bare
list is valid. A board is fundamentally a *partition*: a card that is in no column has no position on
the board, and rendering it would require inventing a column that the file does not contain. The
`MDTO202` repair is always to add a heading or move the card, never to invent an "Inbox".

**Why `columns:` is optional.** Requiring it would mean every hand-authored board starts with a
declaration that duplicates the headings directly below it, and duplicated truth drifts. Deriving
from headings is the default because the document is the state. The declaration exists for boards
where the column set is a *policy* — a shared team board where "someone added a Shipped column"
should be caught — and declaring it opts into exactly that check.

**Why order mismatch (`MDTO208`) is only a warning.** The document is authoritative about layout. If
the headings and the declaration disagree about order, the file still describes an unambiguous board;
only the author's intent is in question. Missing and undeclared columns, by contrast, are set
disagreements that a tool cannot render past, so they are errors.

**Why the column is the state.** A kanban board has exactly one idiom, and it is that you finish
work by dragging it to the right. A board that also asks you to tick a box has two controls for one
fact, which is one more than the fact has: sooner or later a card sits in the last column unticked,
or ticked three columns back, and now the file says two things and the tool has to guess which. So
`move` writes both facts and the board draws no checkbox. **This is the one place `kanban@0.1`
spends a guess deliberately, and §2.4 is where it is written down.**

**Why the file still degrades to a coherent checklist.** This coupling is not a retreat from the
portability promise; it is what protects it. Because a move writes the box as well as the position,
a board edited entirely through the verbs opens in GitHub or `less` as a sectioned checklist whose
ticks are *right* — the finished work is ticked, the rest is not. Under a rule where a move never
touched the box, the same board degraded to a checklist where nothing was ever ticked, which is a
worse lie than any coupling.

**Why the last column, rather than a column named "Done".** Boards call that column Done, Shipped,
Complete, Live, 完了, or `Q3 — released`, and a name-matching rule works for the first of those and
fails silently for the rest — worst of all in a language whose author never suspected the rule
existed. Position is the part of the idiom that is actually universal. `done-column:` exists for the
board whose last column is a parking lot, and it is an *override*, so the common case stays
zero-configuration. [`backlog@0.1`](../backlog/SPEC.md) makes the opposite call — it *reserves* the
names `Someday` and `Done` — for the opposite reason, and says so in its §3: a board is drawn by its
author, a backlog is queried. Neither spec's rule may be carried into the other.

**Why a hand-written disagreement is not a diagnostic.** A checked card in Doing means something to
the person who wrote it — "finished but not demoed", "done, waiting on review" — and the grammar has
always allowed it (§2.4). Coupling the *verbs* does not make that file wrong; it makes it a file no
verb produced. A tool renders it exactly as written, and does not tidy it up on the way past.
**Do not check, uncheck, or flag a card because of a disagreement you did not create.**

**Why nested items are a checklist, not sub-cards.** A board has one level of grouping — columns.
Cards nested inside cards would need a position, a column, and a rendering, and would make "move the
card at position 2" ambiguous. Nesting stays what it is in the item model: subtasks, drawn on the
card.

**Why WIP limits are not in 0.1.** `## Doing [limit:: 3]` is the obvious next feature and `MDTO209`
exists to say so out loud: column-level metadata is a *known extension point*, deliberately deferred,
not an oversight. Adding it means deciding what a validator does when the limit is exceeded (error?
warning? a `MDTO2xx` code?) and that decision belongs to a minor version with fixtures, not to an
implementation's private guess. **Do not silently start honouring `[limit:: …]`.**

**Why the minimal-diff rule matters most here.** This is the spec that proves the round-trip promise.
Dragging a card on the live board must produce exactly the source edit a person would have made by
hand — one line removed, one line inserted, unrelated prose untouched, unknown fields intact — and if
the file changed underneath the board, the conflict surfaces via the source hash rather than being
overwritten. A board that reformats the file on save has failed at the only thing that makes this
format worth having.

**Deliberate non-features of 0.1**, each a plausible minor-version addition: WIP limits, swimlanes,
further column roles (`archive`, `blocked` — the done role is the only one 0.1 defines, §2.4),
card-level history, per-column sort directives, assignee validation, and extended checkbox states.

---

## 4. Examples

### 4.1 Valid

The complete set lives in [`fixtures/valid/`](fixtures/valid/).

**Minimal** ([`fixtures/valid/minimal.md`](fixtures/valid/minimal.md)) — three columns, four cards, no
metadata, no identifiers. Note that it is also a perfectly readable checklist:

```markdown
---
markdownto: kanban@0.1
---

## Backlog

- [ ] Write the conformance fixtures
- [ ] Sketch the landing page

## Doing

- [ ] Build the patch engine

## Done

- [x] Choose the portable envelope
```

**Declared columns** ([`fixtures/valid/declared-columns.md`](fixtures/valid/declared-columns.md)) —
frontmatter `columns:` matching the four headings in order.

**Full metadata** ([`fixtures/valid/full-metadata.md`](fixtures/valid/full-metadata.md)) — typed
fields, labels, an unknown frontmatter key (`board-owner`), an unknown inline field (`[effort:: 1w]`),
and identifiers on exactly the three cards tooling has touched.

**Checklists** ([`fixtures/valid/checklists.md`](fixtures/valid/checklists.md)) — cards with nested
checklists, a checked card sitting in Doing (the grammar keeps the two facts independent, §2.4), and
an empty `## Blocked` column.

**Edge cases** ([`fixtures/valid/edge-cases.md`](fixtures/valid/edge-cases.md)) — prose before the
first column (legal), `#1234` as a literal issue reference, a field inside a code span, and a fenced
code block containing a complete card with an identifier.

### 4.2 Invalid, with the exact diagnostics they produce

Each is a fixture in [`fixtures/invalid/`](fixtures/invalid/) with a sibling `.diagnostics.json`.

**`no-columns.md`** — a note that names itself a board:

```markdown
---
markdownto: kanban@0.1
title: Someday
---

Ideas I have not sorted into columns yet. …
```

→ `MDTO201` error, line 2: *Board has no columns: a kanban document needs at least one `##` column heading.*

**`card-before-column.md`** — cards above the first heading:

```markdown
- [ ] Build the patch engine
- [ ] Write the CLI verb layer

## Backlog
```

→ `MDTO202` error, line 6: *Card appears before the first column heading; kanban@0.1 has no implicit column.*
→ `MDTO202` error, line 7: *Card appears before the first column heading; kanban@0.1 has no implicit column.*

**`columns-mismatch.md`** — declared `Backlog, Doing, Done`; document has `Doing, Backlog, Shipped`:

→ `MDTO208` warning, line 4: *Column order differs from the `columns` declaration: declared `Backlog, Doing`, found `Doing, Backlog`.*
→ `MDTO206` error, line 7: *Column `Done` is declared in frontmatter `columns` but has no heading in the document.*
→ `MDTO207` error, line 18: *Column `Shipped` is not declared in frontmatter `columns`.*

**`duplicate-column.md`** — an unnamed column and a repeated one:

→ `MDTO204` error, line 14: *Column heading has no name.*
→ `MDTO205` error, line 18: *Duplicate column name `Doing`: already declared on line 10.*

**`bad-cards.md`** — column metadata, a level-3 column, and two shared item-model failures:

```markdown
## Backlog [limit:: 3]

- [ ] Obsidian Kanban import adapter

### Doing

- [~] Build the patch engine
- [ ] [priority:: high]
```

→ `MDTO209` error, line 6: *Inline field `limit` on a column heading: columns carry no metadata in kanban@0.1.*
→ `MDTO203` error, line 10: *Heading level 3 is not valid in a kanban document: a column is a `##` heading.*
→ `MDTO031` error, line 12: *Unrecognized checkbox state `~`: kanban@0.1 defines only `[ ]`, `[x]`, and `[X]`.*
→ `MDTO033` error, line 13: *Card has no text: it consists only of metadata.*

**`bad-envelope.md`** — `title` as a sequence, `columns` as a comma-separated string:

→ `MDTO007` error, line 3: *Frontmatter key `title` has the wrong type: expected a string, found a sequence.*
→ `MDTO007` error, line 5: *Frontmatter key `columns` has the wrong type: expected a sequence of strings, found a string.*

---

## 5. Verb reference

`kanban@0.1` owns the vocabulary **`add`, `move`, `done`, `rm`, `edit`**. It overlaps `todo@0.1` and
`backlog@0.1` because the three share an item model, not because any platform contract requires it —
and the overlap is in shape only. A verb with the same name does not have the same contract in
another spec: `kanban move` writes the checkbox when it crosses the done role (§2.4), while
`todo move` and `backlog promote`/`demote` never write one. **Read the verb reference of the spec the
file declares.** The universal commands `validate`, `render`, and `spec` come from the universal
adapters and are not listed here — though note that `render` still writes the validation report when
the file has error diagnostics, and exits 1 rather than 0 to say so.

**Why there is no `undone` verb, when todo has one.** Un-checking is a routine operation on a
checklist and a rare correction on a board, so kanban spends its vocabulary on `edit` — which changes
text, fields, labels, and checked state — instead of a dedicated inverse. `mdto kanban edit k3 --uncheck`
performs exactly the edit `mdto todo undone` would. This is what "verb vocabularies are spec-owned"
means in practice: each spec spends its verbs on what its domain does often.

### Addressing a card — `<ref>`

1. A pinned identifier, with or without the caret: `k3`, `^k3`.
2. Otherwise a case-insensitive substring of the card's normalized text: `"patch engine"`.

If a text `<ref>` matches zero or more than one card, the verb **fails with a non-zero exit code and
changes nothing**. Ambiguity is never resolved by picking the first match.

**A column argument is either a search narrower or a destination, never both — check which before you
pass one:**

| Verb | How the column is given | What it means |
|---|---|---|
| `done`, `rm`, `edit` | `--column <name>` | **Narrows the search**: only look for `<ref>` in that column. |
| `add` | `--column <col>`, **required** | The **destination**. There is no implicit column to fall back on, and it never narrows a search. |
| `move` | the **positional `<col>`**, not a flag | The **destination**. `move` takes no `--column`. |

Column arguments are matched case-insensitively against column names after trimming, the same way
`done-column:` is (§2.1).

### Common flags

`--file <path>` (default: the single `*.kanban.md` in the working directory) · `--json` (emit
`{edits, pinned, addresses, hash}`, where each address carries `id`/`text`/`group`/`line`) ·
`--dry-run` (print the unified diff, write nothing) · `--pin` (pin an identifier on the touched card
and return it) · `--expect <hash>` (apply only if the file still hashes to `<hash>`; conventions §6.5).

**No verb pins an identifier unless `--pin` is passed.** The live web board is the standing exception:
it must track cards across re-renders, so it pins on first interaction — which is why exactly one card
in the conventions example carries `^t3`.

**Every mutating verb requires a matching source hash before it writes.** The CLI computes it from the
file it just read unless `--expect <hash>` supplies one from an earlier read; a mismatch is a conflict
and nothing is applied (conventions §6.5, and see §3's rationale). `--force` overrides only the
validation refusal below, never this check.

### The verbs

| Verb | Signature | Source edit |
|---|---|---|
| `add` | `mdto kanban add "<text>" --column <col> [--create-column] [--priority p] [--due d] [--owner o] [--top\|--bottom\|--before <ref>\|--after <ref>]` | Inserts **one line**. |
| `move` | `mdto kanban move <ref> <col> [--keep-state] [--top\|--bottom\|--before <ref>\|--after <ref>]` | Deletes the card's lines, reinserts them under `<col>`; sets the box when the move crosses the done role. |
| `done` | `mdto kanban done <ref>…` | Replaces `[ ]` with `[x]` on one line. |
| `rm` | `mdto kanban rm <ref>…` | Deletes the card's line and its checklist lines. |
| `edit` | `mdto kanban edit <ref> [--text "…"] [--set k=v]… [--unset k]… [--tag +t\|-t]… [--check\|--uncheck]` | Rewrites **one line** in place. |

**`add`** — inserts a single line at the end of the target column (`--bottom`, the default), or at the
position given by `--top` / `--before` / `--after`. `--column` is required: there is no implicit
column to fall back on. The bullet marker and indentation are copied from the nearest sibling card.
Fields are appended after the text in the canonical order `[priority:: …] [due:: …] [owner:: …]`; tags
stay where the author put them in `<text>`. Targeting a column that already exists is never an error,
whether or not `--create-column` is passed — the card is simply appended there (a duplicate heading is
its own error, `MDTO205`, so create-or-fail is unreachable). A column that does not exist is an error
unless `--create-column` is passed, which inserts a blank line before the new `## <name>` heading
(only if the file does not already end in a blank line) and the card beneath it — heading, blank
line, card, the way every other column on the board is written — both at the end of the file. And
if the board declares `columns:`, it also adds the name to that declaration, because leaving them
inconsistent would immediately produce `MDTO207`.

**`move`** — the showpiece, and the operation the patch engine is built and fuzz-tested against. It
deletes the card's line together with its checklist lines and reinserts them at the destination with
the text, the fields, the tags, the identifier and the relative indentation **verbatim**. It
collapses at most one blank line left doubled by the removal. A drag on the live board is this verb,
and its diff is one removal plus one insertion. Moving a card to the position it already occupies is
a successful no-op producing an empty diff.

The checkbox is the one thing a move may rewrite, and only when the move **crosses the done role**
(§2.4): into the done-role column it is checked, out of it unchecked, and a card already in the
implied state is left alone. Reordering inside a column never touches it. The two changes are one
batch and one diff — the card's line comes out and goes back in with its box already set — so the
file is never briefly in a state where the column and the box disagree. `--keep-state` moves the
card and writes no checkbox at all, which is the escape hatch for a correction ("this was filed in
the wrong column") as opposed to a transition.

**§2.4 and this paragraph are the contract, and they supersede any earlier statement that a move
never touches the box.** That earlier rule appears in drafts of this spec and in tools written
against them; where the two disagree, this document wins. It is called out rather than quietly
replaced so that a reader who has met the older wording knows which one to trust. §3 has the
reasoning; the short version is that a board is finished by dragging right, and a file whose
checklist never ticks is not the graceful degradation this spec promised.

**`done`** — a three-character replacement inside the checkbox. The card does not move, no column
changes, no timestamp is stamped, the checklist is untouched. `done` on an already-checked card is a
successful no-op producing an empty diff. To uncheck, use `edit --uncheck`. The coupling runs one
way only: a move writes the box, but checking the box never moves the card — the position of a card
is information (`move` reorders as well as regroups) and no verb invents one.

**`rm`** — removes the card's line and every line of its checklist, then collapses at most one doubled
blank line. It never renumbers, reflows, or re-indents neighbouring cards. Removing a pinned card
retires its identifier permanently; it is never reissued.

**`edit`** — rewrites the card's own line and nothing else. `--text` replaces the text; the card's
identifier is always preserved. Fields are preserved too, but not in place: they are re-emitted after
the new text, in their existing source order — a field the author had placed *before* the prose moves
after it. **In-place positioning is promised only for `--set`.** Tags are preserved by default: any tag
that was in the old text and is missing from the new one is re-appended right after the new text,
before the fields (the patch engine's `keepTags: false` is the only way to opt out, and no CLI flag
exposes it). `--set k=v` adds or replaces one field in place, keeping its existing position on the
line if it is already there and appending in canonical order if it is not. `--unset k` removes one
field; unsetting a key that is not on the card is a no-op. `--tag +t` / `--tag -t` add or remove one
label; `-t` removes every occurrence of the tag on the line, and both are no-ops when the tag is
already in the requested state. `--check` / `--uncheck` set the checkbox. Unknown fields are only
touched when named explicitly.

### What no verb ever does

Reformats a line it was not asked to change · normalizes bullet markers, indentation, or blank lines ·
reorders cards or columns it was not asked to reorder · drops an unknown field or an unrecognized
value · checks a box on a card it was not asked to move, or on a move that stays inside one column
(§2.4) · moves a card because its box changed · pins an identifier without `--pin` · rewrites the
`markdownto` version · edits a file that fails validation with an `error` (pass `--force` to
override, at your own risk) · writes a file whose source hash no longer matches the one it read (the
conflict surfaces instead).
