Skip to content

Add intent-preserving cell structural edits (Phase 8)#156

Merged
hackerwins merged 2 commits into
mainfrom
feat/cell-structural-edits
Apr 23, 2026
Merged

Add intent-preserving cell structural edits (Phase 8)#156
hackerwins merged 2 commits into
mainfrom
feat/cell-structural-edits

Conversation

@hackerwins

@hackerwins hackerwins commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add insertBlockAfter(siblingBlockId, block) to DocStore interface — works for both top-level and cell-internal blocks via unified blockId resolution
  • Migrate splitBlock HR/page-break in cell, insertTableInCell, and deleteTableInCell from updateTableCell (full cell replacement) to intent-preserving editByPath/deleteBlock
  • Retain LWW for mergeCells covered cell reset and updateBlockDirect paste where full replacement is the intent

Test plan

  • Unit tests for insertBlockAfter (top-level, cell-internal, table block)
  • Unit test for deleteBlock on cell-internal block
  • Concurrent integration test: cell block insert + text edit convergence
  • pnpm verify:fast — 580 docs tests, 156 frontend tests all pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Phase 8 marked shipped; adds sibling-relative block insertion within table cells to insert a block directly after another.
  • Refactor

    • Cell edits migrated from full-cell replacements to intent-preserving, block-level operations; retains full-replacement where resets/pastes are intended. Adds guard to replace a sole-deleted block with an empty paragraph to avoid empty cells.
  • Tests

    • New unit and concurrent integration tests covering cell block insertion, deletion, and convergence.
  • Documentation

    • Implementation plan and lessons documenting indexing/cache considerations and rollout checklist.

Migrate cell-internal block insert/delete from updateTableCell (full
cell replacement) to editByPath (block-level CRDT operations) so
concurrent edits within the same cell are preserved.

- Add insertBlockAfter(siblingBlockId, block) to DocStore interface,
  MemDocStore, and YorkieDocStore
- Migrate splitBlock HR/page-break in cell to insertBlockAfter
- Migrate insertTableInCell to insertBlockAfter
- Migrate deleteTableInCell to deleteBlock with empty-cell guard
- Add unit tests for insertBlockAfter and cell-internal deleteBlock
- Add concurrent integration test (cell block insert + text edit)
- Mark Phase 8 as shipped in design doc

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b3d55270-748e-4bea-9171-d9bb4aa93a80

📥 Commits

Reviewing files that changed from the base of the PR and between 2732ec8 and 668d3e6.

📒 Files selected for processing (4)
  • docs/tasks/active/20260423-cell-structural-edits-lessons.md
  • docs/tasks/active/20260423-cell-structural-edits-todo.md
  • packages/frontend/src/app/docs/yorkie-doc-store.ts
  • packages/frontend/tests/app/docs/yorkie-doc-store.test.ts
✅ Files skipped from review due to trivial changes (2)
  • docs/tasks/active/20260423-cell-structural-edits-lessons.md
  • docs/tasks/active/20260423-cell-structural-edits-todo.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/frontend/tests/app/docs/yorkie-doc-store.test.ts
  • packages/frontend/src/app/docs/yorkie-doc-store.ts

📝 Walkthrough

Walkthrough

Phase 8 moves cell-internal block insert/delete from full-cell replacement to intent-preserving, block-level CRDT edits. A new insertBlockAfter store API (and Mem/Yorkie implementations) is added; Doc call sites (split/insert/delete) now delegate to store-level block operations and tests/docs updated.

Changes

Cohort / File(s) Summary
Design & Tasks
docs/design/docs/docs-intent-preserving-edits.md, docs/tasks/active/20260423-cell-structural-edits-todo.md, docs/tasks/active/20260423-cell-structural-edits-lessons.md
Phase 8 marked shipped; design specifies migrating cell-internal edits to editByPath and documents new insertBlockAfter/deleteBlock behaviors, implementation plan, tests, and a cache-indexing lesson.
Doc model call sites
packages/docs/src/model/document.ts
splitBlock, insertTableInCell, and deleteTableInCell refactored to delegate to store.insertBlockAfter / store.deleteBlock (with special-case replacement to avoid empty cells).
Store interface
packages/docs/src/store/store.ts
Adds public insertBlockAfter(siblingBlockId: string, block: Block): void to DocStore.
MemDocStore implementation
packages/docs/src/store/memory.ts
Implements insertBlockAfter via sibling lookup, deep-clone, and array splice.
YorkieDocStore implementation
packages/frontend/src/app/docs/yorkie-doc-store.ts
Adds insertBlockAfter that resolves sibling tree path, performs tree.editByPath insertion, and updates the in-memory cached document for top-level and cell-internal insertions.
Tests
packages/frontend/tests/app/docs/yorkie-doc-store.test.ts, packages/frontend/tests/app/docs/yorkie-doc-store-concurrent.integration.ts
Unit tests for insertBlockAfter (top-level, cell-internal, header-aware, nested table case) and a concurrent integration test covering simultaneous cell block insertion and inline text edits convergence.

Sequence Diagram(s)

sequenceDiagram
  participant Doc as Document
  participant Store as DocStore
  participant Yorkie as YorkieEngine
  participant Cache as InMemoryCache

  Doc->>Store: insertBlockAfter(siblingId, block)
  Store->>Yorkie: resolveBlockTreePath(siblingId) / editByPath(insert node)
  Yorkie-->>Store: ack / updated tree
  Store->>Cache: update cached doc blocks (splice new block)
  Cache-->>Doc: return refreshed cursor/state
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐇 I hop through cells, nibble blocks in line,
I splice siblings gently, keep structure fine.
DFS maps my tunnels, Yorkie writes the tune,
No full reset now—just insert by noon.
Hooray—blocks settle, converged and green! 🌱

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add intent-preserving cell structural edits (Phase 8)' accurately and specifically describes the main change: introducing block-level CRDT operations for cell editing that preserve operation intent instead of full-cell replacement.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cell-structural-edits

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Verification: verify:self

Result: ✅ PASS in 121.1s

Lane Status Duration
sheets:build ✅ pass 13.5s
docs:build ✅ pass 8.2s
verify:fast ✅ pass 61.3s
frontend:build ✅ pass 15.9s
verify:frontend:chunks ✅ pass 0.3s
backend:build ✅ pass 4.8s
cli:build ✅ pass 1.8s
verify:entropy ✅ pass 15.2s

Verification: verify:integration

Result: ✅ PASS

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 7.69231% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
packages/docs/src/model/document.ts 7.69% 12 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
docs/tasks/active/20260423-cell-structural-edits-todo.md (2)

394-404: Completion checklist is missing task archive/index step

After verify + design update + commit, add a final step to archive and reindex tasks so the task lifecycle is fully closed.

Suggested addition
 - [ ] **Step 3: Commit**
@@
 git add docs/design/docs/docs-intent-preserving-edits.md
 git commit -m 'Mark Phase 8 (cell structural edits) as shipped'

+- [ ] Step 4: Archive and index tasks
+
+Run: pnpm tasks:archive && pnpm tasks:index
+Expected: Active task archived and task index updated


</details>

  
Based on learnings: “Before marking a task done: (1) Update `docs/design/` if architecture changed, (2) Run `pnpm verify:fast` and confirm pass, (3) Archive with `pnpm tasks:archive && pnpm tasks:index`.”

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/tasks/active/20260423-cell-structural-edits-todo.md around lines 394 -
404, Add a final checklist item after the commit step to archive and reindex
tasks so the task lifecycle is closed: insert a new "Step 4: Archive and index
tasks
" item that instructs running pnpm tasks:archive && pnpm tasks:index
and states the expected result ("Active task archived and task index updated");
ensure the step appears after the commit commands and follows the same
formatting/style as the other checklist entries.


</details>

---

`93-94`: **Use repo-relative commands instead of machine-specific absolute paths**

The run commands are tied to `/Users/hackerwins/...`, which makes the checklist non-portable for other contributors/agents. Prefer commands from repo root (or `cd "$(git rev-parse --show-toplevel)"` first).




Also applies to: 140-141, 283-284, 363-364, 396-397

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @docs/tasks/active/20260423-cell-structural-edits-todo.md around lines 93 -
94, Replace machine-specific absolute cd commands (e.g., the string starting
with cd /Users/hackerwins/Development/wafflebase/wafflebase && pnpm ...) with
repo-relative commands so the checklist is portable: either run from repo root
using pnpm frontend test -- --test-name-pattern "insertBlockAfter" or prepend
a generic safe cd like cd "$(git rev-parse --show-toplevel)" && pnpm frontend test -- --test-name-pattern "insertBlockAfter". Apply the same change to the
other occurrences that contain similar absolute paths/commands (the other
instances noted in the comment).


</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against the current code and only fix it if needed.

Inline comments:
In @docs/tasks/active/20260423-cell-structural-edits-todo.md:

  • Around line 384-386: The two fenced code blocks that currently start with (showing the table row like "| 8 | Cell structural edits (editByPath) | In Progress |") are missing a language identifier and trigger MD040; update both fenced blocks to use a language tag such asmd or text so the blocks become md or ```text respectively, ensuring the examples (the block
    containing the "| 8 | Cell structural edits (editByPath) | In Progress |" row
    and the other similar block later) include the language identifier.
  • Around line 1-404: The PR is missing the required paired lessons file for the
    non-trivial active task described in 20260423-cell-structural-edits-todo.md;
    create a new docs/tasks/active/20260423-cell-structural-edits-lessons.md that
    mirrors the todo file and includes a short task summary, implementation notes
    referencing insertBlockAfter, MemDocStore.insertBlockAfter,
    YorkieDocStore.insertBlockAfter and Document methods
    (splitBlock/insertTableInCell/deleteTableInCell), test results/commands run
    (unit and integration commands from the todo), any gotchas or concurrency
    lessons learned, and the final commit messages so reviewers can see outcome and
    rationale; add and commit this new lessons file as part of the PR.

In @packages/frontend/src/app/docs/yorkie-doc-store.ts:

  • Around line 1346-1349: The code uses the tree-path index from siblingPath
    directly when splicing blocksArray, which is wrong for body blocks because
    resolveBlockTreePath() returns indexes offset by the header; update the splice
    index to the local index within the returned blocksArray by subtracting the
    tree/body offset before splicing. Specifically, compute localIdx as the last
    element of siblingPath adjusted by the offset used when mapping tree paths to
    doc.blocks (e.g., subtract the body/tree offset produced by resolveBlockTreePath
    or the region/treeOffset value) and then call blocksArray.splice(localIdx + 1,
    0, block); update the code around getBlocksArrayForPath, resolveBlockTreePath,
    siblingPath and the localIdx computation to use this adjusted local index.

Nitpick comments:
In @docs/tasks/active/20260423-cell-structural-edits-todo.md:

  • Around line 394-404: Add a final checklist item after the commit step to
    archive and reindex tasks so the task lifecycle is closed: insert a new "Step
    4: Archive and index tasks
    " item that instructs running pnpm tasks:archive && pnpm tasks:index and states the expected result ("Active task archived and task
    index updated"); ensure the step appears after the commit commands and follows
    the same formatting/style as the other checklist entries.
  • Around line 93-94: Replace machine-specific absolute cd commands (e.g., the
    string starting with cd /Users/hackerwins/Development/wafflebase/wafflebase && pnpm ...) with repo-relative commands so the checklist is portable: either run
    from repo root using pnpm frontend test -- --test-name-pattern "insertBlockAfter" or prepend a generic safe cd like cd "$(git rev-parse --show-toplevel)" && pnpm frontend test -- --test-name-pattern "insertBlockAfter". Apply the same change to the other occurrences that contain
    similar absolute paths/commands (the other instances noted in the comment).

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `85acd846-bab2-42d0-ab00-e85a86ece77f`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 713422a6c4786f8bdc9a208ff4df9cf9a7b4851f and 2732ec82960b302d876164623b91b57bce73cd3c.

</details>

<details>
<summary>📒 Files selected for processing (8)</summary>

* `docs/design/docs/docs-intent-preserving-edits.md`
* `docs/tasks/active/20260423-cell-structural-edits-todo.md`
* `packages/docs/src/model/document.ts`
* `packages/docs/src/store/memory.ts`
* `packages/docs/src/store/store.ts`
* `packages/frontend/src/app/docs/yorkie-doc-store.ts`
* `packages/frontend/tests/app/docs/yorkie-doc-store-concurrent.integration.ts`
* `packages/frontend/tests/app/docs/yorkie-doc-store.test.ts`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread docs/tasks/active/20260423-cell-structural-edits-todo.md
Comment thread docs/tasks/active/20260423-cell-structural-edits-todo.md Outdated
Comment thread packages/frontend/src/app/docs/yorkie-doc-store.ts
The tree path includes bodyTreeOffset when a header exists, but the
cache array doc.blocks is zero-indexed. Use getRegionBlocks().topIndex
for top-level blocks to subtract the offset correctly.

Also add lessons file and fix fenced code block language tags.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@hackerwins
hackerwins merged commit 3b91c2c into main Apr 23, 2026
4 checks passed
@hackerwins
hackerwins deleted the feat/cell-structural-edits branch April 23, 2026 07:39
hackerwins added a commit that referenced this pull request Apr 23, 2026
Migrate cell-internal block insert/delete from updateTableCell (full
cell replacement) to editByPath (block-level CRDT operations) so
concurrent edits within the same cell are preserved.

- Add insertBlockAfter(siblingBlockId, block) to DocStore interface,
  MemDocStore, and YorkieDocStore
- Migrate splitBlock HR/page-break in cell to insertBlockAfter
- Migrate insertTableInCell to insertBlockAfter
- Migrate deleteTableInCell to deleteBlock with empty-cell guard
- Fix cache index offset for documents with headers by using
  getRegionBlocks().topIndex instead of raw tree path index
- Add unit tests for insertBlockAfter, cell-internal deleteBlock,
  and header-document edge case
- Add concurrent integration test (cell block insert + text edit)
- Mark Phase 8 as shipped in design doc

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant