Skip to content

feat(seq-fields): import, resolve, and export SEQ sequence fields (SD-3018)#3636

Open
luccas-harbour wants to merge 13 commits into
luccas/sd-3007-feature-page-referencesfrom
luccas/sd-3018-feature-sequence-fields
Open

feat(seq-fields): import, resolve, and export SEQ sequence fields (SD-3018)#3636
luccas-harbour wants to merge 13 commits into
luccas/sd-3007-feature-page-referencesfrom
luccas/sd-3018-feature-sequence-fields

Conversation

@luccas-harbour
Copy link
Copy Markdown
Contributor

Summary

Adds end-to-end support for Word SEQ (sequence) fields — the mechanism behind caption numbering (Figure 1, Table 2, …) and other auto-incrementing counters. SEQ numbers are now parsed from OOXML on import, resolved live during layout in document order, recomputed through the Document API and F9, and written back on export.

The design centers on two shared, framework-agnostic primitives (an instruction parser and a stateful evaluator) that every code path — import, layout, Document API, export — reuses, so numbering semantics live in exactly one place.

What changed

Shared SEQ primitives (super-converter/field-references/shared/)

  • seq-instruction.js — tokenizer + parser for SEQ <id> <switches>. Handles \n (next), \c (current), \h (hide), \r N (restart-at), \s N (restart-at-heading-level), \* FORMAT (general format), \# picture (numeric picture), attached numeric switches (\r0, \s1), quoted/escaped tokens, and general-format mapping. Exposes isSeqInstruction, normalizeSeqIdentifier, and sequenceFieldAttrsFromParsed (projects parsed metadata into PM node attrs).
  • seq-evaluator.jsSequenceFieldEvaluator: one instance per linear pass over a story. Maintains per-identifier counters, heading-level serials for \s resets, restart-number/level handling, current-vs-next mode, \h suppression, and value formatting (numeric picture / page-number format / plain). Field-argument (bookmark-reference) SEQ is conservatively stubbed pending bookmark resolution.

Import (v2/importer/, v3/handlers/sd/sequenceField/)

  • Registered sequenceFieldEntity in docxImporter so SEQ fields import via the v3 translator.
  • SEQ keyword detection is now case-insensitive (extractFieldKeyword), replacing the brittle startsWith('SEQ ') checks.
  • The translator now stores full parsed metadata on the node (identifier, mode, switches, formats) while keeping the raw instruction as the export source of truth.

Layout resolution (layout-adapter/)

  • New TextRun contract fields: token: 'seq' and seqMetadata.
  • sequence-field.ts converter emits a SEQ token (with cached text as fallback) instead of baking in a number at conversion time.
  • resolve-sequence-fields.ts runs a single linear pass over fully-assembled FlowBlock[] (paragraphs, tables, lists) after block assembly, resolving each SEQ token in document order. This is cache-safe: cached paragraphs still contribute their preserved token metadata to the pass.

Document API (document-api-adapters/)

  • sequence-field-updater.tsupdateSequenceFieldsInTransaction recomputes SEQ nodes within a transaction, scoped to all / range / identifier, resolving heading levels via the converter context. Body-story only by design.
  • Wired into caption insert/configure, field insert/rebuild, and field-resolver (returns resolvedNumber for sequence fields). caption-resolver now uses isSeqInstruction.

Export

  • The translator emits the current resolved SEQ result (resolvedNumberIsCurrent) when present, falling back to existing content/cached number, with marks preserved.

F9 / field-update (extensions/field-update/)

  • F9 now recomputes SEQ fields, including after TOC edits change document state; SEQ selection is preserved across the TOC update path.

Schema (extensions/sequence-field/)

  • Added attrs: fieldArgument, sequenceMode, hideResult, restartNumber, hasGeneralFormat, pageNumberFieldFormat, numericPictureFormat, resolvedNumberIsCurrent. Empty SEQ now renders as '' rather than '0'.

Testing

New unit + integration coverage across the stack: seq-instruction, seq-evaluator, resolve-sequence-fields, sequenceFieldImporter.integration, sequence-field-export-routing, sequence-field-updater, caption/field plan-engine wrappers (*.seq-fields.test.ts), field-update, caption-resolver, and field-resolver. (~1,300 lines of tests added.)

@luccas-harbour luccas-harbour requested a review from a team as a code owner June 4, 2026 12:58
@linear-code
Copy link
Copy Markdown

linear-code Bot commented Jun 4, 2026

SD-3018

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

I'm unable to verify against ECMA-376 because the mcp__ecma-spec__* tool calls are being denied at the permission prompt. The task explicitly requires verifying elements/attributes against the spec using these tools, so I need them approved before I can produce a reliable review.

Could you approve the mcp__ecma-spec__* tools (or run in a mode that allows them)? Once approved, I'll verify:

  • w:fldChar / w:fldCharType enum values (begin / separate / end) emitted by the translator's decode
  • w:instrText, w:fldSimple / w:instr, w:r / w:rPr / w:t element shapes
  • The SEQ field switches the parser handles (\n, \c, \h, \r, \s, \*, \#) and their general-format mappings against the field-code definitions

Let me know once the permission is granted and I'll run the verification and write up the review.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c379b2c4c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Add a shared SEQ instruction parser for later import, layout, API, rebuild, and F9 paths. The parser preserves raw instruction text, normalizes only field dispatch, handles quoted and unquoted arguments, parses SEQ-specific switches, records unknown switches, and reuses the existing page-number general-format mapping instead of copying format tables.

Parser tests cover the Phase 1 required cases plus pair-reviewer fixes for raw instruction preservation, shared keyword dispatch, and quoted-only unescaping.
Route SEQ field preprocessing case-insensitively and add a v2 sequenceField importer before passthrough so synthetic sd:sequenceField nodes become PM sequenceField nodes instead of being dropped.

Reuse the shared SEQ instruction parser in the sequence field translator, carry parsed import attrs onto the PM node, mark imported cached results stale, and keep raw instruction export behavior unchanged.

Add focused preprocessing/import/translator tests covering uppercase and lowercase complex and fldSimple SEQ fields plus cached result preservation.
Add a pure SequenceFieldEvaluator next to the shared SEQ parser so layout, API, and update paths can share counter semantics without importing editor or rendering internals.

The evaluator owns per-identifier counters, heading serial tracking for \s resets, explicit \r reset precedence, conservative field-argument fallback behavior, hidden-result handling, and shared contract-based number formatting. Add focused coverage for the required Phase 3 numbering, reset, repeat-current, hidden, formatting, empty-identifier, field-argument, and initial-counter cases.
Add SEQ token metadata to TextRun and emit sequence-field layout tokens without relying on a fake zero placeholder.

Resolve SEQ display in a post-assembly toFlowBlocks pass using the shared SequenceFieldEvaluator so paragraph, table, list-compatible, and cache-hit blocks are renumbered in document order before layout measurement.

Cover numbering modes, formatting, heading-level restarts, story isolation, table traversal, and FlowBlockCache renumbering with focused layout-adapter tests.
Add a shared transaction helper that recomputes body SEQ fields in ProseMirror state using the shared parser/evaluator and style-aware heading resolution from the layout adapter when converter style data is available.

Wire raw field insertion, field rebuild, caption insertion/configuration, and F9 updates through the helper while preserving existing TOC and document-stat dispatch behavior. Field discovery now reads sequenceField.resolvedNumber as the resolved display text.

Cover updater scopes, caption/field wrapper recomputation, field resolver readback, and F9 SEQ refresh behavior with focused tests.
Update sequenceField export so current evaluated resolvedNumber values are authoritative, including empty current results for hidden fields. Preserve imported cached child content when results are not current, with a fallback to non-current resolvedNumber only when no cached child content exists.

Add export-routing tests for current result runs, hidden empty output, cached-content preservation, resolvedNumber fallback, and raw instructionTokens preservation. Add PM-updater coverage documenting Phase 7 conservative field-argument behavior: cached text wins, no-cache references repeat the previous counter, and counters do not advance.
Handle Word-emitted SEQ switches such as \r0 and \s1 by normalizing attached numeric values through the existing restart parser path.

This fixes the NDA browser-test case where hidden level reset fields imported with restartNumber null, causing the first visible level2 SEQ field to render as 2 instead of 1.

Adds parser coverage for attached restart switches and a layout regression for hidden restart-zero fields seeding the next visible value.
Build the stat/SEQ update transaction from the current editor state after TOC updates dispatch their own transactions. This preserves TOC edits and shifted SEQ positions when F9 updates both TOC and SEQ fields.
Use the shared case-insensitive SEQ instruction check when caption discovery falls back to sequenceField nodes. This keeps imported lowercase seq captions discoverable by the API.
Keep the sequenceField schema default at ARABIC for backward compatibility while the shared parser continues to default parsed instructions to Arabic.
Remember whether the original F9 selection contained a SEQ field before TOC updates dispatch their own transactions. This lets the fresh post-TOC transaction recompute SEQ fields even when TOC edits shift the selected field position.
Add sequenceFieldAttrsFromParsed beside the SEQ parser and use it from import, raw field insertion, caption insertion/configuration, and PM recompute. This keeps parsed instruction attrs and null/default normalization in one place.
@luccas-harbour luccas-harbour force-pushed the luccas/sd-3018-feature-sequence-fields branch from e156fa7 to 12f92a2 Compare June 4, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant