fix(editor): load a body followed by two or more item rows #231

Merged
dominik.polakovics merged 1 commit from lab/20261012-1740 into main 2026-10-12 18:29:52 +02:00

Closes #230

Problem

An items owner whose file pretty-prints its rich-text body and rows onto their own lines (Body. then two {{< img />}} lines, the shape text-media.yaml and AGENTS.md teach) could not be opened in the editor once it had two or more rows. GET /api/element/body answered 422 adjacent text nodes: it pulled the rows out of the children and serialized the rest, and the newline text nodes the rows had separated were now side by side.

The save path (elemops.ApplyEdit) already merged such runs with its own coalesceText. The read path did not. One split, implemented twice, one copy correct.

Change

  • elemops.PartitionItems now returns a serializable rest itself (adjacent text merged into fresh copies, bytes preserved), so every caller is covered by construction.
  • ApplyEdit drops its separate merge; handleElementBody gains a comment pointing at the guarantee.
  • Regression tests: TestPartitionItemsCoalescesLeftoverText (elemops: merges, serializes, never mutates the caller's tree) and TestElementBodyLoadsBodyFollowedByPrettyPrintedRows (server: the GET loads, GET then save is a fixed point for the body the editor sees). Both fail on main.

Not in scope

The save still writes rows and body glued onto one line (noisy diffs, inter-row whitespace leaking into the translation hash). That is a canonical-layout change with manual and skill impact and is tracked in a separate issue.

No documented behaviour changes: AGENTS.md and the skills are untouched.

🤖 Generated with Claude Code

Closes #230 ## Problem An items owner whose file pretty-prints its rich-text body and rows onto their own lines (`Body.` then two `{{< img />}}` lines, the shape `text-media.yaml` and AGENTS.md teach) could not be opened in the editor once it had two or more rows. `GET /api/element/body` answered `422 adjacent text nodes`: it pulled the rows out of the children and serialized the rest, and the newline text nodes the rows had separated were now side by side. The save path (`elemops.ApplyEdit`) already merged such runs with its own `coalesceText`. The read path did not. One split, implemented twice, one copy correct. ## Change - `elemops.PartitionItems` now returns a serializable rest itself (adjacent text merged into fresh copies, bytes preserved), so every caller is covered by construction. - `ApplyEdit` drops its separate merge; `handleElementBody` gains a comment pointing at the guarantee. - Regression tests: `TestPartitionItemsCoalescesLeftoverText` (elemops: merges, serializes, never mutates the caller's tree) and `TestElementBodyLoadsBodyFollowedByPrettyPrintedRows` (server: the GET loads, GET then save is a fixed point for the body the editor sees). Both fail on `main`. ## Not in scope The save still writes rows and body glued onto one line (noisy diffs, inter-row whitespace leaking into the translation hash). That is a canonical-layout change with manual and skill impact and is tracked in a separate issue. No documented behaviour changes: AGENTS.md and the skills are untouched. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(editor): load a body followed by two or more item rows
All checks were successful
ci / build-and-test (pull_request) Successful in 16m12s
ci / release (pull_request) Has been skipped
045f0b94f6
A hand-authored items owner that pretty-prints its rich-text body and
rows onto their own lines ("Body.\n{{< img />}}\n{{< img />}}") failed
to open in the editor: GET /api/element/body pulled the rows out of the
children and serialized the rest, and the two newline text nodes the
rows had separated were now adjacent, which the serializer refuses.
The save path already merged such runs with its own coalesceText; the
read path did not — one split implemented twice, one copy correct.

PartitionItems now hands back a serializable rest itself, so every
caller is covered and ApplyEdit's separate merge goes away. Regression
tests at the elemops layer (the split merges and copies) and the HTTP
layer (the GET loads and GET → save is a fixed point for the body).

Closes #230

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Author
Owner

[autoland] verdict: pass

This was generated by AI while landing a PR.

PASS. Validated against validation-core.md.

  • Signal relied on: Forgejo Actions ci / build-and-test green on the PR head (run 227, 16m12s). Not re-run.
  • Conventions: Conventional Commits title; body carries Closes #230; head is a lab run branch.
  • Diff scope: matches #230 exactly. elemops.PartitionItems now coalesces adjacent text in rest (fresh copies, bytes preserved); ApplyEdit drops its own merge; regression tests at the elemops and HTTP layers. Every other caller of the split (WouldClobberBody, forms.go, translation.go) either uses only the items side or checks block children, so the merged rest changes nothing for them.
  • Manual/skills: no documented behaviour changes; AGENTS.md and the scaffolded skills correctly untouched.
  • Conflicts: a dry-run merge against main is clean; the four commits main gained since the branch point are all web/i18n.

No CONCERNS.

[autoland] verdict: pass > *This was generated by AI while landing a PR.* **PASS.** Validated against `validation-core.md`. - **Signal relied on:** Forgejo Actions `ci / build-and-test` green on the PR head (run 227, 16m12s). Not re-run. - **Conventions:** Conventional Commits title; body carries `Closes #230`; head is a lab run branch. - **Diff scope:** matches #230 exactly. `elemops.PartitionItems` now coalesces adjacent text in `rest` (fresh copies, bytes preserved); `ApplyEdit` drops its own merge; regression tests at the elemops and HTTP layers. Every other caller of the split (`WouldClobberBody`, `forms.go`, `translation.go`) either uses only the items side or checks block children, so the merged rest changes nothing for them. - **Manual/skills:** no documented behaviour changes; AGENTS.md and the scaffolded skills correctly untouched. - **Conflicts:** a dry-run merge against `main` is clean; the four commits main gained since the branch point are all web/i18n. No CONCERNS.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/deckle!231
No description provided.