Skip to content

DOCX Formula Provenance Runtime Proof

Date: 2026-07-12

Scope

This proof covers the import-side formula provenance bridge, a bounded direct Word Equation conversion, and the durable internal MathType artifact workflow. It does not promote any public gateway route.

The read-only Go Formula runtime remains at runtimes/go-formula-docx. The writable adapter is services/docx-import-service/internal/parser/docx.

Runtime Evidence

The local runtime at http://127.0.0.1:8080 accepted POST /v1/import/docx/qas for the real test-hoctapaz corpus. The Math fixture returned QAS questions[].equations[] entries containing, among other fields:

  • source=mtef-ole, format=latex, LaTeX, and MathML;
  • oleAssetId, previewAssetId, and packagePath;
  • MTEF checksum, deterministic flag, confidence, and runtime relationship metadata.

The adapter maps these values into every import formulas[] record and carries them into approval formulaRefs[].sourceJson. Unsupported/image fallback formulas receive GO_FORMULA_DOCX_FORMULA_REVIEW_REQUIRED; deterministic OLE/WMF previews stay formula metadata and are not materialized as ordinary browser images.

The adapter regression TestMapGoFormulaQASResultIsDeterministicApartFromLatency maps the same versioned QAS payload 24 times while varying only parser latency. After removing latency_ms, the import-visible JSON must be byte-identical, including answer keys, warnings, media, formula metadata, and aggregate stats.

On 2026-07-14, the direct MathType conversion adapter also began sorting eligible word/*.xml package paths before appending conversion warnings. This prevents the random Go-map iteration order from changing persisted fallback-warning order when a DOCX contains formulas across document, header, footer, or other Word parts. TestConvertMathTypePackageOrdersWarningsDeterministicallyAcrossWordParts uses distinct document and header fallbacks for 128 conversions per process; the focused test passed with -count=100. The change is entirely inside the writable import adapter and does not modify or promote the read-only Go Formula runtime.

The adapter also bounds its upstream request. GO_FORMULA_DOCX_TIMEOUT_MS defaults to 180 seconds. An injected http.Client with no timeout is copied with that deadline, retaining its transport without mutating the caller's client; a caller-provided positive timeout remains authoritative.

Corpus Results

make test-docx-corpus passed against /Users/velikho/Desktop/test-hoctapaz:

FixtureQuestionsRenderable imagesPer-question formulas
Physics28784
Math227125
English4000
DGNL102fallback parityfallback parity

QAS object-level stats.equationCount is intentionally not used as the import-visible formula metric: it counts source objects repeated in answer and solution rendering. The adapter sums questions[].equations[], which preserves the established Physics and Math corpus values while retaining all source data.

Current Read-Only Corpus Recheck (2026-07-13)

make test-docx-corpus passed again against /Users/velikho/Desktop/test-hoctapaz. It used the already-running read-only Go Formula DOCX parser at http://localhost:8080 and did not start, stop, or modify that legacy-runtime process. The focused integration test passed physics_28q, math_22q, english_40q, and TestGoFormulaCorpusDgnlNativeFallbackParity. This is parser parity evidence; it does not create import jobs or mutate legacy data.

Current Import Approval Contract Recheck (2026-07-13)

make test-docx-corpus now also runs TestGoFormulaCorpusApprovalContract. It uses the read-only Go Formula runtime for every fixture, a real in-process Import job/approval flow, and an httptest Question Bank boundary. It never writes to a shared Question Bank database. The contract compares the complete forwarded question JSON with the parsed Import result, including type, answer, solution, media, formulas, source metadata, warnings, and stats. It also asserts that a review-required fixture does not call Question Bank at all. It then saves a narrowed review payload and compares the forwarded Question Bank JSON with that saved review, including the recomputed question, media, and formula stats.

FixtureParsed / answeredUntouched approvalReview-safe saved subset
Math22 / 14DOCX_IMPORT_APPROVAL_INVALID; no Question Bank write13 rows: 12 single-choice and one normalized numeric short-answer row; nonnumeric Lời giải remains teacher-review-required
Physics28 / 28All 28 rows forward unchangedall 28 rows, no review filter required
English40 / 36DOCX_IMPORT_APPROVAL_INVALID; no Question Bank write34 rows with options and exactly one source answer; four missing and two conflicting rows stay excluded
DGNL102 / 41DOCX_IMPORT_APPROVAL_INVALID; no Question Bank write37 rows with options and exactly one source answer; no-option and conflicting rows stay excluded

The review-safe selection is fixture-contract evidence, not inferred answer-key generation. It models a teacher saving only rows with unambiguous source evidence; the remaining rows still need a teacher to supply or correct an answer before they can be approved. The separate isolated Compose Physics proof remains the evidence for real canonical Question Bank persistence, Exam snapshots, Word output, and AI preview workflows. It does not make the other three fixtures eligible for automatic approval.

Verification

bash
GOTOOLCHAIN=go1.25.11 go test ./services/docx-import-service/internal/parser/docx ./services/docx-import-service/internal/usecase -count=1
make test-docx-corpus
GOTOOLCHAIN=go1.25.11 go test ./services/docx-import-service/... -count=1
GOTOOLCHAIN=go1.25.11 go vet ./services/docx-import-service/...

Reviewed Question Bank And Exam Draft Proof

On 2026-07-12, the current Compose services processed the real Math fixture through the durable job, teacher review, Question Bank, and native Exam snapshot path. The parse result had 22 questions, 125 formulas, and 7 renderable images. The teacher review retained only verified q_0001, which has answer label A and one deterministic MTEF/OLE formula.

Before the valid approval, an EXAM_DRAFT payload with workflow=ONLINE was rejected as 400 DOCX_IMPORT_APPROVAL_INVALID. Question Bank row count for the import job remained zero, proving malformed Exam workflow values cannot leave canonical question rows behind. The valid payload used workflow=exam and deliveryMode=ONLINE, then created one Question Bank row and one Exam snapshot.

Question Bank persisted f(x)=5\\cos x in both question_formula_refs and the current version's formula_refs_json. Its sourceJson retained source_kind=mtef-ole, word/embeddings/oleObject1.bin, the OLE asset ID, MathML, deterministic flag, confidence, parser versions, and runtime metadata. The Exam snapshot retained the same LaTeX, deterministic flag, MathML, source-kind metadata, reviewed answer key A, and source question q_0001. This verifies Formula provenance at each database-per-service boundary; Exam does not read Question Bank storage directly.

The QA exam, canonical question, DOCX job/events, media rows, and owned object bytes were removed after the check. This is local Compose evidence, not public Gateway or browser-route promotion.

Direct MathType Conversion Smoke

The local docx-import-service was started on 127.0.0.1:18092 with the read-only Go Formula runtime at 127.0.0.1:8080. Posting the real Math fixture to POST /v1/import/mathtype/convert returned a valid DOCX attachment with:

MetricResult
Source w:object198
Converted deterministic MTEF/OLE objects117
Remaining w:object81
Output m:oMath127
Unresolved formulas0
Warnings19

unzip -t reported no ZIP errors. The 19 warnings are linear OMML fallbacks for unsupported LaTeX and require teacher review. This is a structural DOCX proof, not a Microsoft Word rendering comparison.

Current Direct Recheck (2026-07-12)

The same real Math fixture was posted to the current Compose http://127.0.0.1:8087/v1/import/mathtype/convert service. It returned a 489,407-byte Word DOCX with 117 converted deterministic formulas, 81 skipped source objects, 0 unresolved formulas, and 19 warnings. unzip -t passed; word/document.xml contained 81 remaining w:object nodes and 127 m:oMath nodes. The direct endpoint is ephemeral, so the run left no artifact, import job, or persistent media row.

Durable MathType Job Workflow

The durable workflow now uses POST /v1/import/mathtype/jobs with an organization-scoped source storageKey. It stores source payloads only while the import worker is active, then sends the converted DOCX to document-service POST /v1/internal/generated-artifacts using INTERNAL_SERVICE_TOKEN. Import-service stores only the returned media-asset metadata, conversion counters, parser versions, warnings, and scoped download API. It does not write object storage directly.

Focused regressions prove queue -> conversion -> document artifact metadata -> job detail/list filter -> scoped download -> explicit reprocess. A process that was converting when the worker restarts becomes MATHTYPE_RECOVERY_REQUIRES_RETRY, which avoids creating a second artifact without an explicit user retry.

Compose Runtime Evidence

On 2026-07-12, the current docx-import-service image and migration 000007_mathtype_artifact_jobs.sql ran against local Compose PostgreSQL, document-service, MinIO, Redis, and the Compose Go Formula runtime. The real Math fixture was first stored through document-service, then processed as docxjob-4c2e83a8f569e28ccc7b1ab7:

StepResult
Initial jobCOMPLETED, 117 converted, 81 skipped, 0 unresolved, 19 warnings, 135ms worker latency
Artifactdocument-service media asset media-ab9c2f8dea591ce1fbde2e9f, 489407 bytes, organization-scoped key
Scoped download200, DOCX attachment, ZIP check passed, 198 source OLE objects -> 81 remaining and 127 OMML nodes
Explicit reprocessreused the stored source, completed in 143ms, created distinct asset media-0cdc50b5902339c6abc38b66

The source document and resulting artifacts remain in the local QA organization for reprocess evidence. This proves service-to-service ownership and artifact lifecycle locally, not public Gateway/UI promotion or visual equivalence in Microsoft Word.

Remaining Work

  • Add usage/wallet accounting and visible review-state/UI wiring before frontend or Gateway promotion.
  • Compare representative converted output in Microsoft Word or an equivalent renderer before claiming visual-equivalence beyond the structural smoke.
  • Keep the default import route table legacy-proxied until route/live/browser proof and rollback evidence are complete.

Isolated Direct Recheck (2026-07-13)

The current Compose Import Service converted the real Math fixture through POST /v1/import/mathtype/convert. The returned DOCX was 489,407 bytes and passed unzip -t: 117 deterministic MTEF/OLE conversions, 81 skipped source objects, 0 unresolved formulas, 19 linear-OMML fallback warnings, 81 remaining w:object nodes, and 127 m:oMath nodes. The endpoint is ephemeral and did not create an import job or durable media record.

The separate Physics Question Bank approval recheck persisted 84 formula references and read each canonical question back through its owner API. This continues to prove formula provenance across Import -> Question Bank; it is not a visual-equivalence claim for Microsoft Word rendering.

Fixture Durable Workflow Recheck (2026-07-13, rerun 2026-07-14)

TestMathTypeJobRealFixtureArtifactWorkflow ran again against the real Math fixture with the read-only local Go Formula runtime. The test uses an isolated in-memory Import Service and an httptest document-service boundary: it reads the organization-scoped source, converts deterministic MathType/OLE formulas, captures exactly one generated artifact, and verifies the scoped download bytes match that artifact. The 2026-07-14 rerun passed in 0.11 seconds of test time. This is a focused local contract/latency check, not a Compose throughput measurement, a billable provider call, or public Gateway promotion evidence.

Candidate Gateway And Teacher Browser Proof (2026-07-15)

The disposable PostgreSQL proof in output/qa/mathtype-artifact-browser-local-postgres-20260715114359/ used the real Math fixture through Teacher Web, a rewritten non-default local candidate Gateway table, document-service, and import-service. The visible browser flow uploaded an opaque owner-scoped source asset, created and timer-polled the MathType job, showed the completed download control, and downloaded the DOCX attachment.

The result recorded 117 deterministic conversions, 81 remaining w:object nodes from 198 source nodes, and 127 m:oMath nodes. unzip -t passed for the 489,407-byte artifact. A second Teacher in the same organization received 404 for both download and reprocess; a candidate-Gateway request containing only a raw storageKey received MATHTYPE_SOURCE_REQUIRED. The complete contract and run command are documented in docs/qa/mathtype-artifact-browser-smoke.md.

This is isolated local candidate-route evidence, not default-route promotion or a visual-equivalence claim for Microsoft Word. The job retained 19 linear-OMML fallback warnings for teacher review.

Go-platform documentation is generated from repository Markdown.