| name | law-review-docx |
| description | Use this skill to BUILD a formatted Word document from law review / legal MARKDOWN drafts via the law_review_template + pandoc (footnotes, TOC, styled tables) — NOT the generic 'docx' skill (which edits docx content) and NOT 'docx-render' (which only converts an existing .docx to PDF). Triggers: 'generate a docx', 'create the Word file', 'export to docx', 'build the document', 'compile/finalize the draft', 'build the law review document', 'make a Word version', 'turn my markdown draft into Word', 'make the submission docx', 'apply the law review template'. |
| user-invocable | true |
Law Review DOCX Export
Convert markdown drafts into a properly formatted Word document using the law review template via pandoc.
This is the ONLY correct way to build a law-review .docx — never hand-roll
pandoc/soffice; the template, footnote handling, TOC, and table styling all
live in build_docx.py. Agents without the Skill tool (most workflow
subagents) can't invoke this skill — run the script below directly:
Usage
uv run python3 ${CLAUDE_SKILL_DIR}/scripts/build_docx.py PROJECT_DIR [--output PATH] [--fix-footnotes]
The script:
- Detects title/author from
.planning/ACTIVE_WORKFLOW.md or PRECIS.md
- Combines all
drafts/*Draft*.md files in section order (Introduction → Parts → Conclusion → Appendix)
- Strips YAML frontmatter and prefixes footnote labels to avoid cross-section collisions
- Resolves
<!-- include: PATH --> sentinels by inlining file contents (paths must be absolute or ~-expanded)
- Runs pandoc with
--reference-doc pointing to the law review template
- Optionally runs the docx-repair repair script (
--fix-footnotes)
Compile-Time Includes
To embed externally generated tables or fragments at build time, place a sentinel in the draft:
<!-- include: ~/projects/mirror/data/tables/paper/table2_body.md -->
The preprocessor expands ~, reads the file, and splices its contents inline before pandoc runs. Missing or non-absolute paths emit a visible <!-- MISSING: ... --> placeholder instead of failing silently. For images, use plain pandoc markdown () — no sentinel needed.
Detecting the Project Directory
If the user doesn't specify a path, detect it from context:
- Check if current working directory has
drafts/ and .planning/
- Check if
.planning/ACTIVE_WORKFLOW.md exists and read project_dir from it
- Follow symlinks (e.g.,
paper → actual project directory)
Template
The reference template lives at:
${CLAUDE_SKILL_DIR}/../writing-legal/templates/law_review_template.docx
This template defines all styles that pandoc applies:
| Style | Use | Formatting |
|---|
| Title | Article title | Bold, small caps, centered |
| Heading 1 | Part titles (I., II., III.) | Bold, left-aligned |
| Heading 2 | Sections (A., B., C.) | Bold, left-aligned |
| Heading 3 | Subsections (1., 2., 3.) | Italic, left-aligned |
| Body Text | All body paragraphs | First-line indent |
| First Paragraph | After headings | No indent |
| Footnote Text | Footnotes | 10pt, single-spaced |
After Export
Report the output path, section count, footnote count, and approximate word count. If the user needs further formatting (NOTEREF cross-references, footnote repair from cloud editing), suggest --fix-footnotes or the docx-repair skill.
Rendering to PDF (Word fidelity, incl. from background jobs)
build_docx.convert_to_pdf() delegates to doc_render.convert(renderer="word"),
which uses Microsoft Word's engine for line-exact layout (best for widow detection)
and faithful tables.
Note on table fidelity: for tables this skill builds, the wrap_cell pass (see
style_tables) already pre-breaks cells with explicit <w:br/> so soffice and
x2t render the grid too (commit ec349c5), and x2t kerning is corrected by
doc_render's GPOS/kern injection + EB-Garamond substitution. So all three
engines are grid-faithful for build-generated tables; Word is preferred for
polish, not required for table integrity. Word matters most for hand-authored
docx whose tables never pass through wrap_cell — LibreOffice collapses such a
table to a single stacked column whenever a cell must auto-wrap (Word/x2t keep the
grid).
Word is GUI-driven, so a detached Claude background job can't drive it directly
(AppleEvents fail with -600 — it's in a non-console GUI session without Word's TCC
grant). doc_render handles this transparently by dispatching the render into a
cmux pane (which lives in the console GUI session and is TCC-granted). One-time
host prerequisites:
- cmux socket control enabled:
automation.socketControlMode ≠ "cmuxOnly" in
~/.config/cmux/cmux.json, then cmux reload-config.
- Microsoft Word granted to cmux under System Settings → Privacy & Security →
Automation, and Word in Full Disk Access.
Set $DOC_RENDER_NO_CMUX=1 to disable the cmux path (then background jobs fall
back to x2t/LibreOffice). See docs/investigations/2026-06-22_word-render-cmux-dispatch.md.
Known Gotcha: Pandoc-Citeproc Paren-Wrap Inside Footnotes
Symptom. In the compiled DOCX, some footnotes read with a doubled space
and wrapping parens around a citation:
see (Griffin, supra note 12; Macey, supra note 12). For proponents...
(note the two spaces before ().
Root cause. Pandoc-citeproc wraps any bracketed parenthetical citation
[@key] or [signal @key] in parens with a leading space when it appears
mid-paragraph inside a footnote body. At the paragraph start the wrap is
suppressed; mid-paragraph it is not. This is native pandoc behavior for
note-style CSLs and cannot be fixed at the CSL level.
Why the natural-looking fix doesn't work. Rewriting source to bare
textual form (@key without brackets) renders cleanly only if every
citation has a locator. For bib entries without locators (books, misc,
many articles), pandoc-citeproc with a note-style CSL emits just a stray
number (1.) because the full cite is supposed to go into a footnote and
there is no footnote to host it (we're already inside one).
Fix. The docx-repair skill's fix_footnotes.py detects and strips
these wraps post-compile. The detector keys on the distinctive XML
signature:
<w:r><w:t xml:space="preserve"> </w:t></w:r>
<w:r><w:t xml:space="preserve"> </w:t></w:r>
<w:r>…<w:t>(Author,</w:t></w:r>
… citation content …
<w:r>…<w:t>)</w:t></w:r>
Author-written explanatory parentheticals ((describing X), (documenting Y))
appear as a single <w:t> (…)</w:t> run and lack the double-whitespace
signature, so they are preserved. build_docx.py runs fix_footnotes.py
automatically when --fix-footnotes is set (the default).
Red Flags
| Action | Why Wrong | Do Instead |
|---|
Running pandoc -o output.docx without --reference-doc | Produces default Calibri formatting that violates journal requirements | Always use the template |
| Manually constructing the DOCX with python-docx or docx-js | Reinvents what the template + pandoc already handle | Run the script |
| Combining markdown without prefixing footnote labels | Causes footnote collisions when multiple sections use [^1] | The script handles this automatically |