Repository navigation
Code Word Inspection
word.Inspect reads a bounded OOXML package into private source evidence.
It is the extraction spike for W-03, not a completed importer, document renderer,
answer recognizer or a general OOXML schema validator. It uses the Go standard
library and does not execute objects, resolve external URLs or write files.
Paragraph/run locators refer to the original package part and namespaced XML path. The caller must bind them to an immutable source revision. Parts and relationships have stable ordering independent of ZIP entry order.
The inventory retains explicit formatting, table/container properties, revision
context, drawing/equation subtrees, ancillary XML and source relationship IDs.
Run text is an evidence convenience: inspect Fragments and findings before
using it. Field instructions and deleted/hidden/alternate text are not safe
display content. Resolve adds separate, conservative mark and numbering evidence.
Likewise, table cell order is source order, not a claim about page reading order.
Assets records declared types and sizes only. The media pipeline still needs
bounded decoding, sniffing, validation and reference promotion; an inventory
entry is not evidence that an image is valid or publishable. Original source,
answer files and full inspection JSON must never be sent to a student endpoint.
word.Resolve(ctx, inspection) consumes an unmodified result from Inspect.
It never changes the raw paragraphs, text or findings. Property paths, resolved
run locators and contributing style/list locators bind derived evidence back to
the original XML. Follow the document's relationships to styles and numbering;
an unreferenced conventional filename is not an authority.
Supported semantic marks are b, i, u, strike, dstrike, vertAlign,
vanish, webHidden, caps and smallCaps for Latin-script text. The resolver
walks document defaults, paragraph basedOn chains, explicit character styles
and direct formatting. Underline styles and sub/superscript retain their values;
direct on/off formatting can clear inherited emphasis. Paragraph-mark and list
glyph properties do not become body-run marks. Complete applies only to this
subset, not colors, fonts, layout, visibility selection or learner suitability.
An absent mark is unspecified; an unresolved mark has no asserted value.
Ambiguous style toggles across multiple contributing definitions are deliberately unresolved until Word-version compatibility is characterized. Cycles, missing or duplicate styles, conditional table styling, complex-script overrides and non-Latin text also produce findings rather than guessed marks. Existing source findings for revisions, fields and alternate content still require review even when individual source-branch properties can be read.
Automatic labels stay separate from run text. The supported main-body subset
includes independent numbering instances, nine levels, explicit/default starts,
level/start overrides, restart rules, style-associated levels, numId=0, legal
numbering, decimal/zero-padded decimal, A–Z/a–z, Roman 1–3999 and Unicode bullets.
Missing start means zero and missing numFmt means decimal. A style's ilvl
does not override its abstract level's pStyle association. The suffix records
tab, space or nothing; this is source semantics, not measured geometry.
Unsupported number formats/ranges, custom formats, symbol-font/picture bullets, numbering style links and section-break restart extensions remain findings. Headers, notes and textboxes do not advance main-body counters. Revisions or alternate branches in the body leave body numbering unresolved. An ambiguous reference stops trusting subsequent counters; the resolver never silently skips an unknown item and confidently labels the next one.
Implementation references:
- Word character-style hierarchy and Word toggle compatibility.
- Numbering properties, level text, start values, formats, restart rules and start overrides.
From server/, with an authorized local file:
GOMAXPROCS=2 go run ./cmd/word-inspect /absolute/path/exam.docx
GOMAXPROCS=2 go run ./cmd/word-inspect -json /private/new-evidence.json /absolute/path/exam.docxThe command runs inspection and resolution. Default output contains counts,
source/resolution finding codes and elapsed time, not exam text or labels.
Full JSON is opt-in, created with mode 0600, and refuses an existing output
path. The command has a 30-second context deadline. Neither source files nor
private evidence belong in Git. Synthetic XML bodies under tests/testdata are
deliberately small, independently authored regression cases; the tests build
their ZIP containers at runtime.
Initial limits are 25 MiB compressed, 200 MiB declared expanded size, 4,096 ZIP
entries, 16 MiB per XML part, 32 MiB total XML, 32 MiB cumulative locator strings,
200,000 XML elements and 64 levels. Limits apply across parts where relevant.
Resolution separately caps style ancestry at 64, work at two million units,
retained locator bytes at 32 MiB and a generated label at 1,024 bytes. Context
cancellation is checked during indexing and resolution. Cycles become findings;
exhausting an expansion budget returns ErrLimit, never partial success.
XML reads verify decompression/checksum errors; assets are not decompressed by
this stage. A worker still needs an enforced memory/CPU/time boundary: Go context
cancellation and parser budgets do not replace operating-system isolation.
GOMAXPROCS=2 go test ./internal/platform/word/...
GOMAXPROCS=2 go test ./internal/platform/word/tests -run '^$' -fuzz FuzzInspection -fuzztime=15s -parallel=1
GOMAXPROCS=2 go test ./internal/platform/word/tests -run '^$' -bench BenchmarkInspect -benchmem
GOMAXPROCS=2 go test ./internal/platform/word/tests -run '^$' -bench BenchmarkResolve -benchmemThe regression corpus covers pronunciation marks, options in nested tables,
shared cloze text, tracked/hidden text, styles/numbering, textboxes, fields,
equations, compact keys and interleaved manual corrections. Security cases cover
invalid packages, active objects, XML directives, relationships, duplicate
attributes and cumulative expansion/complexity bounds. Real teacher files remain
a separate local evaluation corpus; passing these tests does not establish
recognition accuracy, .doc compatibility or teacher acceptance.
word.Extract(ctx, reader, size, sourceID, limits) builds ooxml-blocks-v1 private
source blocks from inventory and conservative resolution. The caller supplies the
immutable source record ID, never a mutable filename. Block IDs bind that identity
to the package part and XML locator; equal text in two locations stays distinct.
XML preorder intervals establish mixed paragraph/table/object order and ancestry.
Sorting XML paths would incorrectly group different element types and p[10].
Order is local to a part, not a claimed visual/page reading order.
Paragraphs retain every source fragment and resolved mark with its evidence. Offsets are Unicode code points across original paragraph fragments, not UTF-8 bytes or JavaScript UTF-16 indices. Generated numbering stays separate. Hidden, inserted/deleted/moved text, field instructions/results (including fields spanning paragraphs), ancillary parts and unresolved objects remain private evidence with review reasons. The extractor never selects a tracked-change or alternate branch, assigns correctness from formatting, or claims these blocks are learner-safe.
Nested tables keep separate grids. Cell coordinates are zero-based; column spans and supported vertical merge origins retain their source properties. Duplicate, overflowing, legacy horizontal or orphan/mismatched merge evidence remains unresolved. The initial grid bound is 128 columns. Conditional table style fidelity, visual columns, floating layout and page coordinates still require review/rendition. See Microsoft's grid spans and vertical merges.
word.ReadImage reads one selected embedded asset without following a URL. It
checks dimensions before full decoding, following Go's untrusted-image guidance,
and re-encodes static PNG/JPEG as PNG without source metadata. Initial limits are
10 MiB input, 16 million pixels, 8,192 pixels per edge and 20 MiB output. Animated
PNG and EXIF-bearing images require an explicit later normalization decision;
unsupported vector/other formats are not displayed or silently flattened. A
normalized image is still private: extraction does not create a learner asset,
choose its placement, or authorize source/answer graphics for student delivery.
Local-only evaluation:
GOMAXPROCS=2 go run ./cmd/word-extract source.docx
GOMAXPROCS=2 go run ./cmd/word-extract -json /private/new-blocks.json source.docxThe CLI derives a local identity from the source digest; its default output contains
counts/codes/timing only. Optional evidence is a new 0600 file. It is not a
committable fixture or a public API payload. Large source evidence must be stored
as private artifacts and paged for review, not placed in the queue's candidate
result envelope. Original source and extracted text remain outside Git.
Synthetic regressions cover mixed/nested order, stable identities, merges, Unicode offsets, inherited marks, automatic numbering, field/revision ambiguity, image normalization/resource limits and invalid images. A bounded fuzz check asserts distinct identities and valid ancestry. A local independent XML walk of the eight authorized DOCX files matched all 13,489 source fragments; this is text coverage evidence, not recognition accuracy or an independently reviewed answer benchmark. The private corpus/check output is not part of the repository.
Generated from server/internal/platform/word/README.md. Edit that file in the repository; changes made on this page are overwritten.
Generated from the repository's documentation on every push to develop.
Start here
Product
Implementation plan
- 00 Architecture overview
- 10 Phase 0: Scaffold and contract
- 11 Phase 1: Auth, join, shells
- 12 Phase 2: Admin authoring
- 13 Phase 3: Assign and take
- 14 Phase 4: Grade and results
- 15 Phase 5: Hardening
- 16 Phase 4.5: The student at a desk
- 17 Word import: milestone plan
- 18 Word import: teacher workflow
- 19 Versioned content contract
- 20 Data model
- 30 Risk register
- 40 Open items
- 50 Non-functional requirements
- 60 Backend architecture
- 91 September 2026 issue remediation
- 92 Student layouts and interaction rules
Redesign (Phase R)
- 70 Redesign overview
- 71 R1: Foundations (v0.7.0)
- 72 R2: Access (v0.8.0)
- 73 R3: Student console (v0.9.0)
- 74 R4: Teacher workspace (v0.10.0)
- 74d D4: Deck update (v0.11.0)
- 74e E1: Moving a question across a group
- 75 R5: Admin console and email (v0.12.0)
- 76 R6: Question types and scoring (v0.13.0)
- 77 R7: Collaboration (v0.14.0)
- 78 R8: Schedule (v0.15.0)
- 79 R9: Insights (v0.16.0)
- 80 R10: Learn (v0.17.0)
- 81 R11: Landing, leads, v1.0 (v1.0.0)
Setup and operations
- CI
- Deploying
- DNS for quizzivy.com
- Google OAuth client
- Production operations
- Cloudflare R2 bucket
- Word import worker
- R4 rehearsal on a Neon branch
Design
Team
- Agent team
- Verification environment
- Team ledger
- R4 readiness
- R4 verification strategy
- T-R4.51 browser verification
Code READMEs