docs/plans/2026-09-04-preserve-docx-equation-output.md
Objective: Preserve supported DOCX equation output while ignoring unrelated XML; done when regression, package, repository, review, changeset, and task PR gates pass.
Flow mode: one-shot execution
Goal plan: docs/plans/2026-09-04-preserve-docx-equation-output.md
Template: docs/plans/templates/task.md
Primary template: docs/plans/templates/task.md
Applied packs:
Task source:
Timed checkpoint:
Completion threshold:
htmlToDocxBlob regressions fail before the fix and pass afterward.check pass.Verification surface:
htmlToDocxBlob equation tests and the full slow DOCX conversion suite.word/document.xml inspection for accepted and ignored input.@platejs/docx-io typecheck/build, pnpm lint:fix, and pnpm check.Constraints:
Boundaries:
htmlToDocxBlob, both equation rendering paths, OOXML namespace definitions, and generated word/document.xml.Output budget strategy: Read exact owner/test/type paths, cap searches and command output, and avoid generated/build trees except for named artifact verification.
Blocked condition: Stop only if valid equation output regresses, package or repository checks cannot be repaired in scope, or the temporary review PR cannot be created after verification.
Task state:
Current verdict:
Pre-solution issue challenge:
htmlToDocxBlob proof reproduced unrelated block and inline content before product edits.Completion rule:
Start Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Timed checkpoint parsed | no | N/A: none requested |
| Skill analysis before edits | yes | task, autogoal, security-triage, tdd, changeset, and autoreview selected |
| Active goal checked or created | yes | Exact work goal is active; confidential detail is kept outside the committable bundle |
| Source of truth read before edits | yes | Advisory, public API, both XML sinks, namespace owner, tests, package metadata, and upstream parser behavior read |
| Tracker comments and attachments read | yes | Repository report has zero comments and no attachments |
| Video transcript evidence required | no | N/A: no video |
| Pre-solution issue challenge required | yes | Validity, repro, and owner-boundary verdict recorded before code edits |
| Reproduction verdict before implementation | yes | Source and shipped-package public API repros both imported unrelated XML |
| Repro escalation ladder selected | yes | Generated archive XML is authoritative; browser layers are N/A |
| Suggested fix reviewed against durable boundary | yes | Shared parser fixes both sinks without duplicating caller policy |
docs/solutions checked for existing work | yes | Narrow search found no applicable prior solution |
| TDD decision before behavior change | yes | Root, descendant, and attribute regressions were observed red then green |
| Branch decision for code-changing task | yes | Dedicated codex/preserve-docx-equation-output branch starts at current origin/main |
| Release artifact decision | yes | One patch changeset for @platejs/docx-io |
| Browser tool decision | no | N/A: no browser surface |
| PR expectation decision | yes | task requires a verified temporary review PR |
| Dedicated task plan selected | yes | This plan owns temporary review repository PR #1 only |
| Tracker sync expectation decision | no | N/A: no public tracker |
| Output budget strategy recorded | yes | Exact paths and capped output recorded above |
| Package/API pack selected | yes | Published @platejs/docx-io runtime behavior changes |
| Public surface identified | yes | htmlToDocxBlob is the tested public boundary; its signature and exports stay unchanged |
| Release artifact selected | yes | .changeset/preserve-docx-equation-output.md |
changeset skill loaded | yes | Full skill read; patch level and imperative user-impact prose used |
| Barrel impact decided | yes | New file is internal and unexported; no barrel generation needed |
Work Checklist:
/Users/zbeyens/git/plate; all proof runs there.Completion Gates:
| Gate | Applies | Evidence |
|---|---|---|
| Named verification threshold | yes | Focused/package tests and exact pnpm check pass |
| Bug reproduced before fix | yes | Public API proof imported two unrelated nodes before product edits |
| Targeted behavior verification | yes | Six focused equation tests pass with nineteen assertions |
| Typed code changed | yes | Package source-first typecheck passes |
| Package exports changed | no | N/A: internal unexported utility only |
| Install graph changed | no | N/A: no manifest or lockfile change |
| Browser surface changed | no | N/A: deterministic archive XML only |
| CI template output changed | no | N/A: no template files changed |
| Package behavior changed | yes | Patch changeset added for @platejs/docx-io |
| Registry output changed | no | N/A: no registry surface |
| Docs or content changed | yes | Internal task plan only; no public reference docs change |
| High-risk mini gate | yes | Invalid XML must disappear while valid math remains; focused archive assertions cover both sides |
| Local install corruption suspected | no | N/A: type errors matched the new code and were fixed directly |
| Autoreview | yes | Final local review of the complete code patch reports zero findings and correctness 0.86; later sync is plan-only |
| PR create or update | yes | Exact pnpm check passed before temporary review repository PR #1 was created |
| Per-PR task ownership | yes | PR #1 has one task-plan line and this final plan commit is pushed to its head |
| Final lint | yes | pnpm lint:fix and repository lint passed with one pre-existing warning |
| Goal plan complete | yes | Resolved plan passes the mechanical checker |
| Package typecheck/build/test | yes | Focused/full conversion tests, typecheck, build, lint, and repository check required |
| Release and disclosure | no | N/A: merge, package publish, advisory metadata, CVE, and disclosure are separate maintainer actions |
Phase / pass table:
| Phase | Status | Evidence | Next |
|---|---|---|---|
| Intake and source read | complete | Report, public API, sinks, dependencies, namespaces, and tests inspected | implementation |
| Reproduction | complete | Current source and exact shipped package both reproduced the invalid import | implementation |
| Implementation | complete | Shared parser validates the root and removes non-math content and attributes | verification |
| Verification | complete | Focused/package tests, build/typecheck, lint, repository check, and local autoreview pass | PR sync |
| PR / tracker sync | complete | Temporary review repository PR #1 is open, non-draft, and its body readback is correct | closeout |
| Closeout | complete | All task gates are resolved; merge, release, and disclosure remain separate maintainer actions | final response |
Findings:
htmlToDocxBlob.Decisions and tradeoffs:
oMath or oMathPara root in the Office Math namespace.Implementation notes:
Error attempts:
| Error / failed attempt | Count | Next different move | Resolution |
|---|---|---|---|
| Broad dependency search streamed minified output | 1 | Read exact type and source files with capped output | Owner and types were found without repeating the broad search |
| Initial package archive filename assumption was wrong | 1 | Inspect the produced archive name | Exact shipped package repro completed |
| Node ESM could not resolve an existing extensionless dependency import | 1 | Run the same repro with the repository's Bun runtime | Shipped-package repro completed |
| Bun test path lacked the explicit relative prefix | 1 | Use ./packages/... | Focused suite ran normally |
| First utility draft used browser DOM globals instead of xmlbuilder node types | 1 | Derive narrow structural types from XMLBuilder['node'] | Package typecheck passes without adding a dependency |
| First attribute-preservation assertion depended on serializer normalization | 1 | Assert preserved math attributes and observable text instead | Correct contract passes |
| Extra unit-test filename from an earlier note did not exist | 1 | Use the owning slow conversion suite and repository discovery | No product failure; exact 33-test owner suite and repository test batches pass |
Verification evidence:
/Users/zbeyens/git/plate: source public-API repro before fix -> two unrelated XML nodes imported.@platejs/docx-io package: same public-API repro -> two unrelated XML nodes imported./Users/zbeyens/git/plate: focused equation suite -> 6 pass, 0 fail, 19 assertions./Users/zbeyens/git/plate: full html-to-docx.slow.ts -> 33 pass, 0 fail, 68 assertions./Users/zbeyens/git/plate: package source-first typecheck -> 8/8 tasks pass./Users/zbeyens/git/plate: package build -> pass./Users/zbeyens/git/plate: pnpm lint:fix -> pass; one file formatted./Users/zbeyens/git/plate: structured local autoreview with the focused suite in parallel -> zero findings, correctness 0.86./Users/zbeyens/git/plate: exact pnpm check -> pass: lint, 54 builds, 54 typechecks, 3,463 fast tests, 358 slow tests, all additional batches, and timing enforcement./Users/zbeyens/git/plate: exact public-API repro after fix -> zero unrelated XML nodes imported.main, and owns this branch.pnpm check is the full repository proof.Final handoff contract:
pnpm check passed before PR creation.Task-style PR body contract:
๐ Fixes โ N/A, then exactly one task-plan line, then ๐ข 95-100% confidence.| Phase | ๐งช Tests | ๐ Browser | header with Reproduced and Verified rows.Timeline:
Reboot status:
| Question | Answer |
|---|---|
| Where am I? | Verified private task PR closeout |
| Where am I going? | Maintainer review; merge, release, and disclosure are separate work |
| What is the goal? | Preserve supported DOCX equations while rejecting unrelated XML |
| What have I learned? | Both sinks shared one unrestricted import contract, and Office Math can be preserved by namespace |
| What have I done? | Reproduced, fixed, added six focused tests and a patch changeset, passed all gates, and opened verified private PR #1 |
Open risks:
pnpm check is the CI-equivalent proof; merge, release, advisory metadata/CVE work, and disclosure are separate maintainer actions.