Appearance
DOCX Fixture Persistence And Runtime Proof
Scope
This proof records a local Docker Compose run against the real DOCX fixtures in /Users/velikho/Desktop/test-hoctapaz. It covers native parser output, document storage, question-bank persistence, EXAM_DRAFT snapshots, IAM membership verification, and online/offline runtime decisions. It does not promote any default gateway route.
Preconditions
Apply the service migrations and run current images before a live run:
bash
docker compose -f deploy/docker-compose.yml up --build --force-recreate \
iam-service-migrations document-service-migrations \
question-bank-service-migrations docx-import-service-migrations \
exam-service-migrations
S3_PRESIGNED_UPLOAD_ENDPOINT=http://minio:9000 \
docker compose -f deploy/docker-compose.yml up -d --build \
go-formula-docx iam-service document-service question-bank-service \
docx-import-service exam-servicehttp://localhost:9902 remains the host-facing MinIO endpoint. The container-facing presigned endpoint is required only while docx-import-service materializes media itself.
Parser And Media Evidence
bash
GO_FORMULA_DOCX_URL=http://127.0.0.1:8095 \
HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz \
make test-docx-corpus
GO_FORMULA_DOCX_URL=http://127.0.0.1:8095 \
DOCX_IMPORT_SERVICE_URL=http://127.0.0.1:8087 \
DOCUMENT_SERVICE_URL=http://127.0.0.1:8088 \
HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz \
make test-docx-materializationThe 2026-07-10 run passed the Physics 28-question materialization check with seven read-back image assets. Corpus integration also passed Physics 28Q, Math 22Q, English 40Q, and the DGNL 102Q native fallback.
Read-Only Materialization Recheck (2026-07-14)
DOCX_MATERIALIZATION_SELF_TEST=1 make test-docx-materialization passed again against the read-only local Go Formula adapter and the real Physics fixture in 0.06 seconds. It parsed all 28 questions, materialized exactly seven renderable images through the in-memory uploader, and verified stable owner scope, image content types, and rewritten media URLs. This is an adapter and materializer regression proof: it does not create document-service media rows, Question Bank questions, an Exam snapshot, or a billable Mistral provider request.
For repeatable proof without a shared local import/document runtime, use the opt-in disposable Compose harness:
bash
DOCX_MATERIALIZATION_COMPOSE_CONFIRM=isolated-runtime \
HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz \
make test-docx-materialization-isolatedIt starts a new project from deploy/docker-compose.docx-materialization-proof.yml, exposes only Go Formula DOCX, docx-import-service, and document-service on three fresh loopback ports. Its narrowed dependency graph intentionally excludes Question Bank, Exam, IAM, and Classroom because this materialization endpoint does not invoke their owner APIs. It then removes its containers, network, and volumes unless the explicit debug-only DOCX_MATERIALIZATION_COMPOSE_KEEP=1 is set. This remains a direct owner-service proof and does not involve Gateway, browser, or default-route promotion.
Fresh Isolated Revalidation (2026-07-14)
DOCX_MATERIALIZATION_COMPOSE_CONFIRM=isolated-runtime make test-docx-materialization-isolated built and ran a fresh hoctapaz-docx-materialization-20260714063322 Compose project. It imported the real Physics fixture, read all seven materialized assets back through document-service, and verified 28 questions plus seven MediaAsset references. The cleanup check found no project containers, volumes, or listeners on the dynamically assigned loopback ports.
In the same revalidation window, make test-docx-corpus passed the actual Physics 28Q, Math 22Q, English 40Q, and DGNL native-fallback fixtures and the in-process Import-to-Question-Bank approval contract. The corpus check invokes the read-only Go Formula endpoint and httptest downstream contract only; it does not turn the isolated materialization run into shared database persistence, Mistral provider, browser, or default Gateway evidence.
Four-fixture Isolated Question Bank Revalidation (2026-07-14)
The opt-in command below starts a fresh Compose project and dynamically assigns loopback ports only to docx-import-service and question-bank-service:
bash
DOCX_CORPUS_QUESTION_BANK_ISOLATED_CONFIRM=docx-corpus-question-bank-isolated \
HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz \
make test-docx-corpus-question-bank-isolatedBefore Docker is started, the same harness can validate its Compose boundary with DOCX_CORPUS_QUESTION_BANK_ISOLATED_SELF_TEST=1 make test-docx-corpus-question-bank-isolated. It verifies the two assigned public ports, the required internal dependency closure, and that the storage, database, parser, document, and Exam services are not published to the host.
The runner imports each real fixture, attempts the untouched approval first, saves only the contract-defined review-safe subset where approval is closed, and reads every canonical row back through the Question Bank owner API. The original Compose artifact is output/qa/docx-corpus-question-bank-runtime-20260714020439167/summary.json. Its Math selection below is superseded by the numeric semantic guard recorded in the current local PostgreSQL revalidation.
| Fixture | Parsed | Approval guard | Review-safe rows persisted | Canonical result |
|---|---|---|---|---|
| Math | 22 | untouched approval blocked | 14 (superseded) | Original run: 12 SINGLE_CHOICE, 2 SHORT_NUMERIC_ANSWER; current selector accepts 13 rows only |
| Physics | 28 | no review required | 28 | 18 SINGLE_CHOICE, 6 SHORT_NUMERIC_ANSWER, 4 TRUE_FALSE_GROUP, 28 answer keys, 7 media refs, 84 formula refs, 5 source solutions |
| English | 40 | untouched approval blocked | 34 | 28 SINGLE_CHOICE, 6 PASSAGE_READING, 34 answer keys |
| DGNL | 102 | untouched approval blocked | 37 | 37 SINGLE_CHOICE, 37 answer keys |
For every fixture, the read-back source-question IDs matched the selected review payload exactly. Canonical single-choice and passage rows retained exactly one correct option; non-empty parser solutions matched their Question Bank counterparts. The Compose cleanup removed project containers, network, and volumes. This is isolated Import -> Document -> Question Bank owner-service evidence only: it does not write shared data, promote a default Gateway route, or prove billable Mistral OCR, AI-provider quality, browser delivery, exam generation, or Word visual parity.
Four-fixture Local Postgres Revalidation (2026-07-15)
When Docker Desktop is unavailable, the same owner-service persistence proof can run without Docker:
bash
DOCX_CORPUS_QUESTION_BANK_LOCAL_POSTGRES_CONFIRM=local-postgres \
HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz \
make test-docx-corpus-question-bank-local-postgresThe runner creates a new PostgreSQL cluster under /tmp, creates separate docx_import and question_bank databases, applies the current migration Up blocks, starts only the native import and Question Bank services on fresh loopback ports, and removes both service processes and the cluster on exit. The Go Formula adapter remains read-only. It does not read or write any configured local/shared database and does not start Docker, Gateway, Exam, AI, or browser processes.
The latest 2026-07-15 run passed all four fixtures and ended with four persisted import jobs and 112 persisted canonical Question Bank rows: Math 13, Physics 28, English 34, and DGNL 37. Math retains 12 SINGLE_CHOICE rows and one SHORT_NUMERIC_ANSWER, 13 answer keys, two media references, 28 formula references, and no source solution. The parser exposes 14 non-empty Math answer candidates, but the short-answer value Lời giải is a nonnumeric placeholder, so it and the other eight unresolved Math rows remain teacher review-required. The runtime verifier also confirmed the closed untouched approvals, exact type counts, source-question IDs, canonical correct options, source-label-to-option-ID mappings, numeric answer values, Physics true/false statement booleans, and source solutions. Its parser response latency was 68 ms for Math, 82 ms for Physics, 23 ms for English, and 196 ms for DGNL; these are isolated local observations, not production SLOs. The generated artifact is output/qa/docx-corpus-question-bank-local-postgres-20260715102732/, with per-job, per-review, per-approval, per-Question-Bank read-back JSON, and summary.json.
This is fresh isolated native persistence evidence for Import -> Question Bank only. It does not prove default Gateway behavior, production storage, billable Mistral OCR, AI quality or settlement, Exam snapshots, Word rendering, or browser delivery.
Physics Owner Workflow Local Postgres Recheck (2026-07-15)
The latest Docker-free owner/BFF artifact is output/qa/docx-physics-exam-blueprint-local-postgres-20260715114605/summary.json. It used fresh document, import, Question Bank, Exam, AI, and BFF owner stores and approved all 28 Physics questions. The Question Bank retained the 18/6/4 type split, 28 answer keys, seven media references, 84 formula references, 16 true/false statements, and five source solutions. A three-rule BFF blueprint produced one ONLINE and one OFFLINE immutable snapshot, while a service-owned Word template produced a valid 933,273-byte DOCX with seven embedded media parts, 62 OMML nodes, 22 visible formula review fallbacks, and five solutions. Mock AI similar/explanation responses remained previews until the explicit teacher-reviewed Question Bank handoff. The runner removed its native services and temporary PostgreSQL cluster after writing the artifact.
The matching non-default Gateway candidate artifact is output/qa/docx-physics-gateway-attempt-local-postgres-20260715-current/. It used separate disposable IAM, Document, Classroom, Import, Question Bank, Exam, and Attempt databases; registered one teacher and one student; then completed the real Teacher native import surface in 1,966 ms before running Physics import, blueprint generation, print export, attempt start/save/submit, and result read to GRADED. Browser Resource Timing captured only four Gateway fetches for the source upload, create, and two poll requests, with no direct service origin. Its final stores contain two identities, 25 media assets, three import jobs, 28 Question Bank rows, one exam, and one attempt. Both artifacts are local/disposable proof only: they do not promote default Gateway routes, prove Mistral credentials, durable object storage, or production behavior.
Compose Media Boundary Repair (2026-07-12)
The current Compose materialization check initially exposed a topology defect: document-service correctly issued browser-facing presigned URLs at http://localhost:9902, but a docx-import-service container could not upload to that host address. The failure was visible as GO_FORMULA_DOCX_IMAGE_MATERIALIZE_FAILED with connect: connection refused.
The repair adds token-gated POST /v1/internal/media-assets. With INTERNAL_SERVICE_TOKEN configured, docx-import-service uploads each extracted image once to document-service, which writes the object and creates the media row without exposing a presigned URL to the worker. The legacy presigned path remains the fallback where no internal token is present.
After rebuilding the two Compose services, the real Physics 28Q fixture passed make test-docx-materialization: all seven renderable images had materialized media asset IDs, image/* content types, and successful scoped read-back from /v1/storage/media-assets/{id}/content. Cleanup deleted all seven object bytes through document-service and the exact metadata rows; final verification was media_assets=0 for org_docx_materialization_20260712.
For the Math fixture, native persistence produced 22 question rows with this normalized type distribution:
| Parser type | Stored question-bank type | Count |
|---|---|---|
THPT_MATH_SINGLE_CHOICE_4 and IMAGE_BASED | SINGLE_CHOICE | 12 |
THPT_MATH_TRUE_FALSE_4 | TRUE_FALSE_GROUP | 4 |
THPT_MATH_SHORT_NUMERIC | SHORT_NUMERIC_ANSWER | 6 |
The run matched all 12 source answer labels that had an unambiguous marker to stored options.isCorrect; four source explanations were persisted; and all seven PNG/JPEG media assets had non-empty content at /v1/storage/media-assets/{id}/content.
Historical DGNL Persistence Evidence
The early 2026-07-10 artifact output/qa/docx-dgnl-persistence-20260710T164155 recorded a DOCX Fast compatibility run with total_questions=102, imported=102, and target QUESTION_BANK. It predates the current answer-review guard and must not be used as approval evidence. Current behavior rejects the untouched fixture before any Question Bank write; only a teacher-saved review subset with options and one unambiguous source answer is eligible for approval.
The native fallback now ignores A./B./C./D. lines inside Loại đáp án when there is no following question stem. This prevents answer explanations from being parsed as a new question. Persisted source questions 34, 35, and 36 now have non-empty content and four options each; their original DOCX stems are preserved in question-bank data. The regression test and corpus gate reject an empty DGNL stem.
This is persistence proof, not answer-key proof: 53 DGNL questions do not have a clear source answer marker and remain REVIEW_REQUIRED.
Internal Mistral OCR Runtime Proof
The internal OCR route was exercised against a real PDF object stored through MinIO/document-service and a local HTTP Mistral-compatible test provider. The provider received the same POST /v1/ocr data-URL contract as the real adapter, then returned one page of Markdown. The artifact is output/qa/ocr-runtime-verified-20260710T170249.
The completed ocr-document job preserved provider=mistral, model, one page with confidence, usage metadata, source kind, and the re-parsed review question. It stayed REVIEW_REQUIRED. An explicit OCR reprocess reused the same job ID and document-service source, then recorded an OCR document reprocess queued event before completing again.
The Compose process was restored with an empty MISTRAL_API_KEY and the local test provider was stopped after verification. This proves the native HTTP/job workflow, not a billable request to Mistral's public API.
make test-import-pdf-ocr-workflow now repeats the same bounded contract in a hermetic workflow test: it exercises the Mistral-compatible data-URL adapter, document-service source read, durable ocr-document review metadata, and an explicit reprocess. It complements the Compose evidence but does not replace a billable-provider or public Gateway/browser proof.
The disabled-by-default OCR usage/wallet adapter is verified separately through the same hermetic workflow target. It proves the owner-service request contract and job-scoped idempotency only; it is not a live wallet/entitlement or billable Mistral proof. See OCR Usage Wallet Contract Proof.
Question And Snapshot Invariants
question-bank-servicegenerates service-owned primary keys for imported option, media-reference, and formula-reference rows. Source labels such asAandBremain labels, not globally shared primary keys.- Both question-bank ingest and exam snapshot generation accept
correct_answer.labels, the shape emitted by Go Formula DOCX native style hints. Persisted answer keys then point to the stored option/snapshot IDs. EXAM_DRAFTapproval creates the exam and its question snapshots without reading the question-bank database directly.
Review Guard Runtime Evidence
On 2026-07-12, the English 40-question fixture was run through the current Compose docx-import-service. The adapter recovered six QAS responses where a leading A. option had been folded into the stem and normalized two formula-less MATH_LATEX responses to SINGLE_CHOICE. Four questions (5, 6, 12, and 29) have no answer marker. Two more source rows, q_0026 and q_0028, are single-choice questions with multiple answer labels. They now receive GO_FORMULA_DOCX_AMBIGUOUS_SINGLE_CHOICE_ANSWER and require teacher review; the import path does not silently reclassify them as multiple-choice.
Approval of the untouched job returned 400 DOCX_IMPORT_APPROVAL_INVALID. A teacher review retaining only the verified first question was then approved to QUESTION_BANK: the persisted option labelled A is isCorrect=true, and the question version stores the service-owned option ID in answer_keys_json. This proves that review is the approval boundary and that source labels are translated only at canonical persistence time.
Current Four-fixture Review-safe Question Bank Selection (2026-07-14)
After rebuilding docx-import-service and question-bank-service, all four real fixtures were imported as scoped jobs. Each review payload retained only rows with an answer key that was safe for its declared type. Every persisted row was read back from Question Bank and required to retain service provenance, a current version, and a canonical answer key. Single-choice and passage rows also required exactly one isCorrect option.
| Fixture | Parsed | Review-safe rows persisted | Canonical type/result summary | Media refs | Formula refs |
|---|---|---|---|---|---|
| Math | 22 | 13 | 12 SINGLE_CHOICE, 1 SHORT_NUMERIC_ANSWER; 12 correct options | 2 | 28 |
| Physics | 28 | 28 | 18 SINGLE_CHOICE, 6 SHORT_NUMERIC_ANSWER, 4 TRUE_FALSE_GROUP; 18 correct options | 7 | 84 |
| English | 40 | 34 | 28 SINGLE_CHOICE, 6 PASSAGE_READING; 34 correct options | 0 | 0 |
| DGNL | 102 | 37 | 37 SINGLE_CHOICE; 37 correct options | 0 | 0 |
The current run removed all 112 Question Bank rows through the Question Bank owner API. It then deleted the four scoped import jobs (and cascading events) from the local import-service database; the final job count for the test organization was zero. This is local Compose evidence only and does not promote a default Gateway route.
Reviewed EXAM_DRAFT Snapshot Proof (2026-07-12)
The same English fixture was rerun after the review/snapshot regression repair. The teacher saved a review payload containing only verified source question q_0001; the original parse still had 40 rows. EXAM_DRAFT approval then created exactly one Question Bank row and one native Exam snapshot. The snapshot retained Question 1:, answer key A, source question q_0001, and the reviewed document ID. It did not include any of the 39 excluded parser rows.
Cleanup removed the QA exam through Exam Service, the canonical question through Question Bank, and the import job with its event rows. PostgreSQL verification returned import_jobs=0, import_events=0, exams=0, snapshots=0, questions=0, and question_groups=0 for org_docx_reviewed_exam_current_20260712.
Current Physics EXAM_DRAFT Proof (2026-07-12)
A new Physics 28-question job was reviewed down to source q_0001 and approved to EXAM_DRAFT. Import Service created one canonical Question Bank row and one draft Exam snapshot. The snapshot retained the Physics single-choice type, answer key A, and sourceSnapshotJson values for the import job, document, and sourceQuestionId=q_0001. The draft Exam was deleted through Exam Service, the Question Bank row through Question Bank Service, and the scoped import job through the import database; the final import-job count was zero.
Legacy EXAM_DRAFT Failure Compensation Proof (2026-07-12)
This records behavior before the durable approval-operation checkpoint was introduced. It is not current retry behavior. The Math fixture was parsed again as 22 questions, 125 formulas, and 7 renderable images. A teacher review retained only answered formula question q_0001, then approved it with a non-existent examId. Exam Service rejected the snapshot request as 404 EXAM_NOT_FOUND; Import Service returned the expected DOCX_IMPORT_APPROVAL_DEPENDENCY_FAILED response and called the Question Bank owner API to compensate the newly ingested row.
Before fixture cleanup, PostgreSQL counts were question_rows=0, question_groups=0, missing_exam_snapshots=0, and org_exams=0. The job, events, 7 materialized media objects/rows, and payload spool were then removed, leaving all tenant-scoped cleanup counts at zero. A focused regression also forces snapshot persistence failure after a newly created draft and verifies the compensation order: delete draft Exam first, then delete Question Bank rows.
Current behavior keeps owner writes after an ambiguous snapshot failure and retries the saved Question Bank/Exam checkpoint with immutable owner keys; see the approval contract and focused use-case regression. No new live migration or default Gateway promotion is claimed by this historical proof.
make test-docx-review-guard-live is an explicit opt-in Compose smoke for the Math 22Q, Physics 28Q, English 40Q, and DGNL 102Q fixtures. It creates organization-scoped QA jobs, verifies parser counts and repair invariants, and asserts that any unresolved answer blocks approval. It never approves a job, so it does not write question-bank or exam data.
Runtime Evidence
The same Math import was approved twice into separate published exams under an IAM organization with a registered teacher and student membership:
| Delivery mode | Snapshot count | Student runtime result |
|---|---|---|
ONLINE | 22 | allowed=true; 12 questions with unambiguous answer markers exposed matching correct options and answer keys |
OFFLINE | 22 | allowed=false, denialCode=EXAM_OFFLINE |
The runtime path called IAM's internal organization-membership decision before returning the exam decision. The data path therefore proves the IAM -> import -> question-bank -> exam snapshot -> student runtime boundary locally.
Known Limits
- The Math fixture contains 236 OLE/WMF artifacts that are intentionally reported as unsupported media. They are retained as warning metadata, not silently treated as rendered images.
- Eight Math questions have no clear source answer marker and require teacher review. They must not be counted as automatically verified answers.
- The English fixture has four questions without a source answer marker and two single-choice rows with conflicting labels (
q_0026,q_0028). All six require teacher review before approval. - The DGNL fixture reports 647 OLE and 648 WMF/EMF artifacts as review metadata. It does not render those artifacts as normal media. Sixty-one DGNL questions have no clear answer marker; four single-choice rows have conflicting labels (
native-dgnl-093,native-dgnl-096,native-dgnl-099, andnative-dgnl-102). All 65 require teacher review. - Direct Mistral OCR remains the default. Opt-in
ocrMode=mistral-batchpersists the source/input/output file IDs, provider job ID, lease, attempts, andnextPollAt; it submits the provider JSONL batch and resumes only a durable provider poll after restart. The batch queue exposes delayed poll work separately from active and waiting jobs. A crash before the provider job ID is persisted fails closed for explicit retry to avoid an automatic duplicate provider charge. This does not provide public Gateway/browser promotion evidence. - This proof does not cover public/default gateway routes, a billable Mistral credential, authenticated browser delivery, or production deployment.
Regression Commands
bash
GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/... \
./services/docx-import-service/... \
./services/iam-service/... \
./services/exam-service/... -count=1
make test-service-readiness
make test-runtime-foundation
make test-import-pdf-ocr-workflow
GOTOOLCHAIN=go1.25.11 go test -race ./services/docx-import-service/internal/usecase \
-run '^TestImportJobServiceSkipsSupersededOCRBatchPollTimer$' -count=10The memory import repository returns independent aggregate snapshots for jobs, events, and approval checkpoints. This keeps a superseded OCR batch poll timer from sharing mutable OCRBatch state with the worker that completes a newer poll. The command above was run locally on 2026-07-14; it is race-regression evidence only and does not promote a public Gateway route.
Isolated Compose Recheck (2026-07-13)
An independently named local Compose project rebuilt the current Import, Question Bank, Exam, IAM, Document, and Go Formula images. It used only fresh project-owned volumes and a non-default route configuration; the shared cluster and deploy/gateway/routes.json were not modified.
make test-docx-corpus again parsed the real fixture corpus as Physics 28, Math 22, English 40, and DGNL 102 questions. The live review guard then kept approval closed for every unresolved source answer:
| Fixture | Parsed | Review state that blocks approval |
|---|---|---|
| Math | 22 | 8 questions without a source answer, plus one nonnumeric Lời giải placeholder |
| Physics | 28 | none; all rows are review-safe |
| English | 40 | 4 missing answers and 2 conflicting single-choice labels |
| DGNL | 102 | 61 missing answers and 4 conflicting single-choice labels |
The complete Physics job was approved to QUESTION_BANK. Owner-list and direct-detail reads returned the same 28 canonical IDs: 18 SINGLE_CHOICE, 6 SHORT_NUMERIC_ANSWER, and 4 TRUE_FALSE_GROUP. The verifier confirmed canonical answer-key integrity, 6 stored solutions, 84 formula references with source provenance, and 7 materialized media references. Source option labels are translated to service-owned option IDs only at approval time.
Two complete Physics jobs were also approved to published EXAM_DRAFT exams in the IAM-owned local organization. Each persisted 28 immutable snapshots. The member student received allowed=true with 28 questions for ONLINE; the same student received allowed=false and EXAM_OFFLINE for OFFLINE. This rechecks IAM membership -> import -> Question Bank -> Exam snapshot -> student runtime without direct cross-service database access.
Physics Blueprint And Word Template Recheck (2026-07-13)
An independently named, fresh Compose project exercised the owner-service and non-default BFF boundary with the real Physics fixture. The artifact is output/qa/docx-physics-exam-blueprint-runtime-20260713-210106.
The import approved all 28 rows to Question Bank and retained the expected 18 SINGLE_CHOICE, 6 SHORT_NUMERIC_ANSWER, and 4 TRUE_FALSE_GROUP split, all 28 answer keys, 7 media references, and 84 formula references. Through the BFF, a three-rule blueprint generated one ONLINE and one OFFLINE draft; each contains 28 immutable snapshots with the same type, answer-key, media, and formula totals. A service-owned Word template rendered the offline draft to a valid non-empty OpenXML DOCX. An intentionally unavailable blueprint rule returned 400 before a draft was created.
The project used fresh project-owned volumes and was removed with its containers, images, volumes, network, and temporary listeners after the run. This is local owner/BFF evidence only. It does not promote a default Gateway route, prove an authenticated browser download, or alter legacy data.
Physics Rich Word Package Recheck (2026-07-13)
After the renderer added the scoped document-service media-read contract, a second fresh Compose project reran the same fixture. The artifact is output/qa/docx-physics-rich-print-runtime-20260713-213402. The 28-question OFFLINE snapshot retained seven unique mediaAssetId values and 84 formula references. Its 933,058-byte DOCX passed unzip -t, contained exactly seven word/media/* parts and seven drawings, and preserved every formula reference as either 62 supported OMML entries or 22 visible review fallbacks. The smoke asserts that those counts equal the snapshot totals before completing.
The follow-up fresh artifact output/qa/docx-physics-solution-parity-runtime-20260713 adds a source-keyed solution invariant: all five non-empty Physics solution_html values produced by the current parser were stored in Question Bank, copied into both 28-item ONLINE and OFFLINE snapshots, and found in the rendered Word XML. This is data-parity evidence for the actual current parser output, not a claim that every question in every corpus fixture has a source solution.
Physics AI Review Ownership Recheck (2026-07-13)
The fresh artifact output/qa/docx-physics-ai-runtime-20260713 reused the real 28-question Physics import after its matrix, snapshot, and Word checks. It then sent one imported SINGLE_CHOICE Question Bank ID to POST /v1/ai/generation/preview in similar mode. The deterministic mock returned one PREVIEW payload with previewOnly=true, its imported reference ID, and the explicit question-service handoff; the Question Bank count stayed at 28.
The teacher-reviewed payload was then sent to Question Bank's POST /v1/questions/ai-preview-drafts. It created exactly one AI_GENERATED DRAFT with the reference-question provenance, and replaying the same preview ID returned that same draft. An AI explanation preview for the same imported question likewise remained PREVIEW and did not consume usage or wallet credit. Its reviewed result went through POST /v1/questions/ai-preview-solutions, appended exactly one canonical Question Bank version, retained the original DOCX importJobId, and replayed idempotently without adding a Question Bank row.
This proves the local ownership boundary: AI creates only mock preview data; Question Bank owns the reviewed draft/version writes. The project used fresh volumes and was removed after the run. It is not provider-credential, usage/wallet-settlement, browser, default Gateway, or production evidence.
All reads remained on the internal token- and organization-scoped document-service contract; the renderer received no object key or browser URL. The project volumes and listeners were removed after the run. This validates owner-service data flow locally, not full source-DOCX visual layout, browser delivery, default Gateway behavior, or production promotion.
make test-import-pdf-ocr-workflow passed its Mistral-compatible OCR protocol workflow. It is hermetic provider-contract coverage, not a request to a billable Mistral credential. Browser/default-gateway promotion and production deployment remain outside this local proof.
Physics Owner Idempotency And True/False Recheck (2026-07-13)
The fresh isolated Compose artifact output/qa/docx-physics-exam-blueprint-runtime-20260713172705384 reran the real 28-question Physics DOCX after the Question Bank owner ingest ledger and true/false normalization were added. Import sent a stable Idempotency-Key to Question Bank only. All 28 canonical rows persisted, including four TRUE_FALSE_GROUP rows with 16 ordered statements carrying content and correctBoolean; both the ONLINE and OFFLINE snapshots retained all 16. The rendered Word file contained one Đúng/Sai response pair for every statement, as well as the existing seven embedded media, 84 formula references (62 OMML and 22 visible review fallbacks), and five source solutions.
A separate owner-only retry on the same isolated stack posted the same Idempotency-Key and source job twice, received the same canonical question ID, and listed exactly one stored row. Reusing that key with a changed payload returned 409 QUESTION_IMPORT_IDEMPOTENCY_CONFLICT. Migration 000014_question_docx_import_ingests.sql ran only against this temporary project volume. This is local owner/BFF proof, not default Gateway, browser, production, or whole-EXAM_DRAFT saga exactly-once evidence.
Direct Physics EXAM_DRAFT Replay Recheck (2026-07-14)
The native PostgreSQL harness now creates a second fresh Physics 28-question import job and sends it directly to Import Service's EXAM_DRAFT approval state machine. It does not infer this result from the separate Question Bank then BFF blueprint flow. The artifact output/qa/docx-physics-exam-blueprint-local-postgres-20260714171146/summary.json records the first approval and an immediate replay of the identical request.
The initial approval created 28 canonical Question Bank rows, one private ONLINE Exam draft, and 28 snapshots. The replay returned reused=true with the same Exam ID, snapshot count, and ordered canonical Question Bank IDs. Direct owner reads found exactly one row for that Exam ID, exactly 28 Question Bank rows scoped to the second import job, and exactly 28 snapshots. Snapshot read-back retained the canonical 18 SINGLE_CHOICE, 4TRUE_FALSE_GROUP, and 6 SHORT_NUMERIC_ANSWER split, 28 answer keys, seven media references, 84 formula references, 16 ordered true/false statements, 28 source question IDs, and all five non-empty Physics solutions.
This rerun also caught and fixed a real direct-approval gap: snapshot creation now normalizes THPT/DGNL parser type codes with the same canonical mapping used by Question Bank, and derives true/false sub-items from the reviewed output. Import still does not read Question Bank storage to build snapshots.
TestApprovalExamDraftRetriesStableOwnerKeysAfterCheckpointSaveFailure also injects one Import checkpoint-save failure immediately after either the Question Bank ingest or Exam create response. The next call reuses the frozen actor and stable owner key, then the completed replay makes no more owner calls. This is deterministic unit coverage of the lost-local-checkpoint branch.
The check still does not simulate an actual process stop or PostgreSQL outage between an owner write and checkpoint save, so it is not full crash-window exactly-once proof. It is also not Gateway, browser, production, or default route evidence.
Local Host Runtime Recheck (2026-07-15)
After the read-only Formula runtime host default was aligned with local Compose at 127.0.0.1:8095, the same disposable owner/BFF proof passed without a Formula URL override. Its artifact is output/qa/docx-physics-exam-blueprint-local-postgres-20260715114605/. The 28 Physics questions retained the canonical 18 SINGLE_CHOICE, 6SHORT_NUMERIC_ANSWER, and 4 TRUE_FALSE_GROUP distribution across Question Bank, online and offline snapshots, and direct EXAM_DRAFT approval. It also recorded 28 answer keys, seven media references, 84 formula references, 16 true/false statements, five source solutions through Word rendering, an idempotent direct approval replay, one AI-generated draft from a similar preview, and one idempotent AI solution version. The isolated runtime removed its temporary databases and native processes after completion.
Current Corpus Recheck (2026-07-15)
The current recheck used the same real files under /Users/velikho/Desktop/test-hoctapaz, fresh owner databases, and the read-only Formula runtime at 127.0.0.1:8095:
make test-docx-corpuspassed parser, import-visible determinism, and the approval guard for Math 22Q, Physics 28Q, English 40Q, and DGNL 102Q.DOCX_CORPUS_QUESTION_BANK_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-corpus-question-bank-local-postgrespersisted the teacher-reviewed subsets as 13 Math, 28 Physics, 34 English, and 37 DGNL canonical Question Bank rows. It retained answer keys for every persisted row; Physics retained seven media, 84 formula references, and five solutions. Artifact:output/qa/docx-corpus-question-bank-local-postgres-20260715081210/.DOCX_PHYSICS_EXAM_BLUEPRINT_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-physics-exam-blueprint-local-postgresrecreated the complete Physics owner/BFF path:18/6/4Question Bank type split, directEXAM_DRAFTreplay, immutableONLINE/OFFLINEsnapshots, five source solutions in snapshots and Word export, mock AI preview-to-reviewed write, and a 933,275-byte DOCX. Artifact:output/qa/docx-physics-exam-blueprint-local-postgres-20260715081210/.DOCX_CORPUS_EXAM_BLUEPRINT_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-corpus-exam-blueprint-local-postgrespassed withoutput/qa/docx-corpus-exam-blueprint-local-postgres-20260715113710/summary.json. It required the untouchedEXAM_DRAFTapproval to returnDOCX_IMPORT_APPROVAL_INVALIDwith zero Question Bank rows, then saved and approved the exact review-safe Math13/22, English34/40, and DGNL37/102subsets. For each fixture, direct approval replay returned the same owner rows and Exam ID; BFF-createdONLINEandOFFLINEmatrices retained every selected canonical ID, type, answer key, media/formula ref, and source provenance. The three completed Word exports passed ZIP, content-type, checksum, document-owner metadata, answer-sheet, and answer/solution XML checks. Before cleanup the isolated databases held 84 canonical questions, nine exams, 252 snapshots, and three print exports; the runner then removed all temporary listeners and its PostgreSQL cluster. Immediately before that owner/BFF proof,make test-docx-corpuspassed the real Physics 28Q, Math 22Q, English 40Q, and DGNL 102Q parser checks, deterministic Math/Physics import-visible output, and the in-process fail-closed/review-safe approval contract.
This closes the reviewed-subset owner/BFF gap for Math, English, and DGNL. It does not approve excluded source rows, prove visual Word equivalence, promote a default Gateway route, or prove browser, billed-provider, durable object-store, or production behavior.
Latest Refresh (2026-07-15)
The four real files were rerun with the read-only Formula host at 127.0.0.1:8095. make test-docx-corpus passed parser parity, deterministic visible Formula output, and the fail-closed approval contract. The fresh Question Bank artifact output/qa/docx-corpus-question-bank-local-postgres-20260715122623/summary.json recorded 112 canonical reviewed rows: Math 13/22, Physics 28/28, English 34/40, and DGNL 37/102. It preserved one answer key per persisted row; Physics retained 18/4/6 types, seven media refs, 84 formula refs, and five source solutions. Parser latencies were 69 ms, 84 ms, 25 ms, and 183 ms. Rows requiring teacher review remained outside the canonical write.
The companion owner/BFF artifact output/qa/docx-corpus-exam-blueprint-local-postgres-20260715122636/summary.json again fail-closed untouched approvals, created review-safe online/offline blueprints and checksum-verified Word exports for Math 13, English 34, and DGNL 37. Its temporary PostgreSQL cluster and native child processes were removed on exit. This remains local owner/BFF evidence, not default Gateway or production proof.