Skip to content

docs: finish-the-engine plan — Tier 3 divergences + remaining Tier 2 (#696)#711

Draft
jedrazb wants to merge 11 commits into
mainfrom
docs/engine-tier2-remainder-tier3
Draft

docs: finish-the-engine plan — Tier 3 divergences + remaining Tier 2 (#696)#711
jedrazb wants to merge 11 commits into
mainfrom
docs/engine-tier2-remainder-tier3

Conversation

@jedrazb

@jedrazb jedrazb commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Draft. Scopes the work after the #710 clean half so the engine unification can be finished. Stacked on #710 (base feat/engine-spine-tier2); this PR's diff is only the OpenSpec planning.

Tier 3 — resolve the silent divergences

Routing both adapters through one computeLayout (in #710) surfaced places where React and Vue render/behave differently for the same document with no test catching it. The engine-divergence-tier3 change records a canonical resolution + a parity fixture for each:

  • HF resolution — React resolves header/footer from final ?? initial section props but gates titlePg from initial (internally inconsistent); Vue uses initial for both. Canonical: resolve and gate from the same section, validated against Word with a multi-section fixture.
  • Vue render-option gaps — Vue drops headerDistance/footerDistance/pageBorders that computeLayout returns; canonical is Vue passes the full set (page borders + authored HF distances render in Vue).
  • Intentional divergencesresolvedCommentIds (Vue has no comment sidebar) and the [Bug] Vue: image selection overlay stays on the old page after the image is pushed to the next page #670 overlay scrollTop offset are deliberate; document + assert them so they can't silently flip.
  • Column-width semanticsequalWidth="0" with explicit per-column widths renders equal in both adapters, and mid-document separator/equalWidth is dropped. Pre-existing engine limitation now reachable from Vue; decide fix vs document (leaning fix).

Remaining Tier 2 — the harder half

Open questions for reviewers

  1. Does Word attach trailing-page HF to the final section or the initial? (decides the HF canonical)
  2. Is the vanilla package (Add vanilla JS package (@eigenpal/docx-editor-js) #89) wanted this cycle? (gates the session inversion)
  3. Fix the column-width engine limitation now, or track it?

See openspec/changes/engine-divergence-tier3/ (proposal, design, spec, tasks).

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

jedrazb and others added 11 commits June 5, 2026 09:08
…specs

Tier 1 of #696 merged (PR #706); move the change to archive and apply its 8
new core capability specs to openspec/specs/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author the engine-spine-tier2 OpenSpec change: proposal, design (engine class +
EngineHost DI seams, divergence resolutions, forced 5-step sequence), 6 capability
specs, and tasks. Grounded in a file:line map of the layout pipeline, PM view
lifecycle, transaction loop, and load/save seam in both adapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold in eng-review findings (timing-equivalence requirement+test, visual-baseline
repair pre-req, Vue scheduler consumer audit, Vue DocumentAgent prereq) and the
outside-voice premise challenge. Spike (SPIKE.md) resolves run()'s real input
contract (derives geometry from document; getPageSize/getMargins already core) and
finds load/save is a source-of-truth inversion, not a sequence lift. Recommendation:
ship the clean half (layout+scheduler+loop) first; views+session is a second
decision gated on #89.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move getColumns/columnWidthForSection/computePerBlockWidths/twipsToPixels from
React's internals/columnLayout into core layout-bridge/sectionGeometry alongside
the existing getPageSize/getMargins/resolveHeaderFooter. React delegates; this is
the geometry-resolution layer the Tier 2 engine derives from sectionProperties.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 6-step layout compute (PM doc → flow blocks → measure → HF resolve →
margin extension → layoutDocument + footnote stabilization → footnote render
items) moves out of React's useLayoutPipeline into a pure, framework-neutral
core function. React now calls computeLayout and keeps only the DOM paint +
scroll/events (where the framework timing lives). measureBlocks is the one
injected seam (each adapter passes its caching measurer). Behavior-preserving;
Vue migrates onto the same function next.

Part of the #696 Tier 2 engine spine (clean half: layout compute).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vue's inline compute pass (which lacked column / per-block-width support) is
replaced by the same core computeLayout React now uses, keeping only the paint
(renderPages + container styling + SDT focus). Vue gains column layout and
per-section measurement for free; behavior-identical for single-column docs.

Part of the #696 Tier 2 engine spine (clean half: layout compute).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lift React's rAF-coalescing layout scheduler into core/editor/layoutScheduler
(createLayoutScheduler). React wires to it via a runRef so the stable scheduler
always calls the latest pipeline; Vue routes its body doc-change relayout
through it too, so a burst of keystrokes lays out once per frame instead of
synchronously per keystroke. The selection overlay still waits via
LayoutSelectionGate. scheduleFrame is injectable so headless/test hosts run
synchronously. HF relayout stays synchronous in both (unchanged).

Part of the #696 Tier 2 engine spine (clean half: scheduler).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n edit

Lift React's UPDATED_SCROLL strip (and its drift canary) into
core/editor/scrollFlag and apply it in Vue's body dispatchTransaction too.
Vue's hidden off-screen editor previously had no strip, so an edit could yank
an ancestor's scroll to the caret; it now matches React. The dispatch
orchestration itself stays adapter-side (the reactivity models differ).

Part of the #696 Tier 2 engine spine (clean half: transaction loop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ncel)

- Lift the duplicated blockLookup build into core layout-painter
  (buildBlockLookup); both adapters call it.
- Cancel the layout scheduler in Vue destroyEditorView so a reload can't
  repaint the old document against the new one's geometry.
- Fix stale step-count in computeLayout docblock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plan

Scope the work after the #710 clean half: the silent React/Vue divergences
surfaced by routing both through one computeLayout (HF resolution, Vue render-
option gaps, intentional comment/overlay divergences, column-width engine
limitation), each with a canonical resolution + parity fixture; plus the
deferred Tier 2 half (view lifecycle, and the load/save source-of-truth
inversion gated on #89).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docx-editor Ready Ready Preview, Comment Jun 5, 2026 11:37am

Request Review

@eigenpal-release-pal

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅

Posted by the CLA bot.

Base automatically changed from feat/engine-spine-tier2 to main June 5, 2026 12:14
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