diff --git a/MIGRATION.md b/MIGRATION.md
index 0a22b58..03004bd 100644
--- a/MIGRATION.md
+++ b/MIGRATION.md
@@ -27,12 +27,15 @@ the error's `path` naming the card, since Atlassian's schema gives a card no lin
`link` mark from that card before you write it with `0.3.0`; to keep the link's target, put the
link on a `text` node beside the card.
-`0.3.0` refuses, as `unsupported-node-shape`, a block or non-text inline node holding `text`, and a
-leaf block or non-text inline node holding non-empty `content`. Atlassian's schema gives no node
-those. `adfToHtml` and `adfToPortableMarkdown` wrote such a node in `0.2.0`, and `adfToLosslessMarkdown`
-wrote one inside a carried node; all three now refuse it, and `losslessMarkdownToAdf` and `htmlToAdf`
-refuse a stored carry holding one. Read a stored document with `0.2.0`, remove what the error's
-`path` names and write it with `0.3.0`, once.
+`0.3.0` refuses, as `unsupported-node-shape`, a known block holding `text`, a known non-text inline
+node holding `text` or non-empty `content`, and a known block that takes no content, such as `rule`,
+holding non-empty `content`. Atlassian's schema allows none of these. `adfToPortableMarkdown` wrote
+such a node in `0.2.0`, and `adfToLosslessMarkdown` wrote one inside a carried node. Both now refuse
+it, and `losslessMarkdownToAdf` refuses stored markdown whose carry holds one.
+
+Read such stored markdown with `0.2.0`, then write the document with `0.3.0`. Each refusal names one
+wrong key, `text` or `content`, on the node the error's `path` names, and a call reports only the
+first: delete that key and write again until the call succeeds.
## From `0.1.0` to `0.2.0`
diff --git a/spec/flavour.md b/spec/flavour.md
index 7f7c7bb..5579d39 100644
--- a/spec/flavour.md
+++ b/spec/flavour.md
@@ -582,10 +582,11 @@ open or close where the run sits (`un**-real**istic`), or one CommonMark's match
the merged run's pairing to another delimiter — rides the inline carry whole. So does every node
holding `em`, `strike` or `strong` in a line still read back wrong after 32 tries. An opaque carry inside
a mark spelling is a named error in input: the carry restores its node exactly, marks included
-(`docs/decisions.md` §Unknown nodes ride the carry). An inline card a link marks, a block or non-text inline
-node holding `text`, and a leaf block or non-text inline node holding `content` ride no carry:
+(`docs/decisions.md` §Unknown nodes ride the carry). An inline card a link marks, a known block holding
+`text`, a known non-text inline node holding `text` or non-empty `content`, and a known block that
+takes no content, such as `rule`, holding non-empty `content` don't ride the carry:
`adfToLosslessMarkdown` refuses each as `unsupported-node-shape`, and so does `losslessMarkdownToAdf`
-reading a carry that holds one, since Atlassian's schema gives no node those.
+reading a carry that holds one, since Atlassian's schema allows none of these.
```
!adf:textColor[**Overdue**]{color="#ae2e24"}, H!adf:subsup[2]{type=sub}O, !adf:underline[signed].
diff --git a/spec/html.md b/spec/html.md
index 36d00fb..967ec5f 100644
--- a/spec/html.md
+++ b/spec/html.md
@@ -20,7 +20,7 @@ Blocks are separated by a line feed, and a document holding any ends with one. A
| `orderedList` | as `bulletList` with `
`; `` for an `order` of 3, a safe integer other than `-0`; no `start` where the list holds no `order` |
| `paragraph` | `…
` |
| `rule` | `
` |
-| `table` | ``, a line feed, a `` where its first row holds cells that are all `tableHeader`, then a `` for the other rows where there are any, a line feed, `
`. Each section is its tag, a line feed, its rows a line each, a line feed, its closing tag |
+| `table` | ``, a line feed, its sections parted by a line feed, a line feed, `
`: a `` where its first row holds cells that are all `tableHeader`, and a `` for the other rows where there are any. Each section is its tag, a line feed, its rows a line each, a line feed, its closing tag |
| `tableCell`, `tableHeader` | ``, ` | `, each holding its blocks as a `listItem` does: ` | Bolt M8 | `; attributes `colspan`, `rowspan`, `style="background-color:#fffae6"` for `background`, and `data-colwidth="120,80"` for `colwidth`, first and in that order |
| `tableRow` | ``, its cells with no line feed between them, `
` |
@@ -28,19 +28,20 @@ A container holding no `content` key is its element holding nothing: ``, ``, ``, `
`, ` | `; so is a paragraph or heading,
``.
-A table and each of its rows count one nesting level; a cell counts none, as a list item does.
-
```html
-| Part | Stock |
+| Part | Stock | Bin |
-| Bolt M8 | Low |
+| Bolt M8 | Low |
+| 12 | A4 |
```
+A table and each of its rows count one nesting level; a cell doesn't count, nor does a list item.
+
On a paragraph, heading, quote, list, list item, table, row, cell, code block or rule, each attribute its type holds
(`spec/flavour.md` §Block nodes) that the element does not write as `level`, `order`, `language`,
`colspan`, `rowspan`, `background` or `colwidth` is a `data-*` attribute, named as HTML's `dataset` names it, `localId` as `data-local-id`, after the
@@ -145,14 +146,15 @@ later release writes it as its own element.
attribute its type does not hold: the whole list rides the carry;
- a table holding a node that is no `tableRow`, a row holding a node that is no cell, or a table,
row or cell holding a mark, an empty key or an attribute its type does not hold: the whole table
- rides the carry. So does a cell whose `colspan` or `rowspan` is no positive safe integer, whose
- `colwidth` is no list of numbers, or whose `background` holds anything but letters and digits
- after an optional `#`;
+ rides the carry. So does a cell whose `colspan` or `rowspan` isn't a positive safe integer, whose
+ `colwidth` isn't a list of numbers, or whose `background` isn't a string of one or more letters
+ and digits, after an optional `#`;
- a `listItem`, `tableRow` or cell outside its parent, an inline node in block position and a block
node inline.
-- An inline card a link marks, a block or non-text inline node holding `text`, and a leaf block or
- non-text inline node holding `content` are refused as `unsupported-node-shape`, carried or not, by
- `adfToHtml` and by `htmlToAdf` reading a carry element: Atlassian's schema gives no node those.
+- An inline card a link marks, a known block holding `text`, a known non-text inline node holding
+ `text` or non-empty `content`, and a known block that takes no content, such as `rule`, holding
+ non-empty `content` are refused as `unsupported-node-shape`, carried or not, by `adfToHtml` and by
+ `htmlToAdf` reading a carry element, since Atlassian's schema allows none of these.
- A block carry's JSON nests at most 500 levels less the depth it stands at, an inline carry's at
most 500, as in markdown.
diff --git a/todo.md b/todo.md
index 81d204c..f3e89f2 100644
--- a/todo.md
+++ b/todo.md
@@ -67,8 +67,8 @@
### 168. Lower the reading cost of the files a chunk reads most before its first commit.
-The last 5 chunks' median was 215876 new input tokens before their first commit, and the 5 before
-them 147097. Most read: `todo.md` 36, `docs/decisions.md` 25, `README.md` 21, `spec/flavour.md` 16,
+As of 2026-11-05, the last 5 chunks' median was 215876 new input tokens before their first commit,
+and the 5 before them 147097. Opens and searches of each file over those last 5 chunks: `todo.md` 36, `docs/decisions.md` 25, `README.md` 21, `spec/flavour.md` 16,
`ci.sh` 15, `src/html/parse/html-to-adf.test.ts` 13, `src/markdown/emit/adf-to-markdown.test.ts` 11,
`CHANGELOG.md` 9, `src/conformance/corpus.test.ts` 8, `MIGRATION.md` 7.