Appearance
import-service Agent Tasks
Agent workflow: follow README.md for Audit -> Investigate -> Code -> Test -> Fix; keep this pack's writable scope and verification commands authoritative.
Dispatch type: per-service
Dispatch ID: SVC-012
Current implementation: services/docx-import-service.
Owns: import jobs, parse orchestration, preview/draft state, review save, approval saga.
Must read:
- Phase 4 and 5 import handoffs under
docs/agents/handoff-phase-4-*import*.mdandhandoff-phase-5-* docs/agents/service-tasks/import-pdf-ocr-boundary.mddocs/agents/service-tasks/import-mathtype-conversion-boundary.md- legacy import modules under
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/ai-import/,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exam-paper-templates/,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.exam-import-approval.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.exam-import-lifecycle.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.import-*.ts, and/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/formula-recognition/. - legacy PDF/OCR/MathType files:
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exam-paper-templates/mistral-document-ocr-converter.service.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exam-paper-templates/mistral-document-ocr-converter.helpers.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exam-paper-templates/go-formula-docx-import.service.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/ai-import/annotated-docx-import.helpers.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/ai-import/ai-import-exam-review.ts,/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.import-jobs.ts.
Legacy source evidence:
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/ai-import/**/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exam-paper-templates/**/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.exam-import-*.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.import-*.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/formula-recognition/**
Writable files:
services/docx-import-service/**contracts/openapi/services/docx-import-service.yamldocs/api/docx-import-service.md
Orchestrator-owned files:
- import route examples under
deploy/gateway/routes.import-*.json, import route tests underscripts/test/import-*.sh, defaultdeploy/gateway/routes.json, shared deploy manifests, and frontend routes unless explicitly delegated.
Delegated gateway verification scope:
- Only when the orchestrator assigns an import route/live/browser proof subtask, this service agent may update non-default
deploy/gateway/routes.import-*.jsonexamples,scripts/test/import-*.sh,docs/qa/import-*.md, and rootMakefiletest-import-*targets. - Single-route review/detail examples and the combined review roundtrip table are guarded by
make test-import-review-roundtrip-routes; do not leave import route examples without a named static target and rollback note. - The default gateway route table and frontend routes remain orchestrator-owned; public promotion still requires browser/runtime proof and rollback notes.
Initial tasks:
- Done: approval uses the reviewed parse result as the sole source for both Question Bank ingest and
EXAM_DRAFTsnapshots. Regression covers manual correction/filtering -> save -> approve -> matching downstream ingest and snapshot content, answer keys, explanations, and source metadata. - Done: expose owner queue/failure projection at
GET /v1/admin/import/docx/queue-summaryfor admin operationsqueue.failuresandqueue.depths. The route returns service-local job status counts, native queue depth, optional BullMQ bridge depth, recent failures, and the owner retry command route without letting admin-service or BFF read import storage directly. - Done: add DOCX Fast workflow coverage for create -> queue -> parse -> review save -> approve, including reviewed manual question-type overrides, source-location fields, and warning fields in the downstream ingest body.
- Done locally: real corpus proof now covers Math 22Q persistence through question-bank-service and
EXAM_DRAFTapproval. Imported option/media/formula reference primary keys are service-generated;correct_answer.labelsfrom Go Formula DOCX is mapped to correct options and snapshot answer keys. Seedocs/qa/docx-fixture-persistence-runtime-proof.md. Do not regress this behavior by treating source option labels as global database ids. - Done:
EXAM_DRAFTapproval persists an Import-owned operation keyed by(jobId,target). It freezes request/review digests and the outbound owner actor, checkpoints Question Bank IDs and the Exam ID, uses immutable Question Bank/Exam keys, and retries snapshot replacement without deleting owner data. A changed request or review after the first claim returns a conflict; a short lease prevents overlapping attempts. This is a checkpointed retry protocol, not a distributed transaction or default Gateway promotion. - Current four-fixture approval contract: the DGNL 102-question fixture parses into non-empty stems, but direct approval is blocked before Question Bank because nine rows have no options and only 41 rows have a usable answer. Math 22Q and English 40Q are likewise blocked when their review-required answers remain incomplete; only the fully answered Physics 28Q fixture is forwarded unchanged by the isolated contract. The same contract saves a review-safe source-evidence subset and forwards exactly 13 Math rows, 34 English rows, or 37 DGNL rows with recomputed review stats; a nonnumeric Math placeholder such as
Lời giảiis not inferred as an answer key. Do not reintroduce automatic DGNL approval without reviewed answers and options. - Done: add an internal, PDF/image-only Mistral OCR foundation at
POST /v1/import/ocr/jobsandPOST /v1/import/ocr/jobs/{id}/reprocess. It reuses the service-owned job/event/payload store, writes Mistral Markdown plus page/model/usage metadata into review state, and requires review before any question-bank approval. A direct OCR job foundPROCESSINGafter a restart is failed asOCR_RECOVERY_REQUIRES_RETRY, not automatically sent to the provider again. This is not legacy batch OCR, Word Equation conversion, gateway promotion, or browser parity. - Done: preserve the OCR editor's source-text/section review JSON, but convert it to canonical
ImportResponse.questions[]only after the teacher saves a review. The approval bridge forwards reviewed stem/options/answer labels/solution/source-span metadata to Question Bank; the original completed OCR state remains blocked because it has noReviewedAttimestamp. - Done for internal OCR only: an opt-in usage/wallet adapter checks one
ocr_pages_monthlyunit and the AZ Credit balance before a Mistral call, then settles the exact result page count through idempotent owner-service calls. It records only accounting metadata in the review payload; it does not own quota counters or wallet rows. The flag is off by default and an enabled invalid configuration fails the service closed before it accepts OCR jobs. - Preserve manual question-type overrides and source-location/warning fields for future PDF/OCR/MathType slices.
- Use
import-pdf-ocr-boundary.mdfor the PDF/OCR job slice separate from DOCX Fast: upload payload refs, page count extraction, provider/runtime selection, durable status/events, retry semantics, download/materialized-asset refs, and no default route promotion. - Use
import-mathtype-conversion-boundary.mdfor the MathType/OLE/OMML/MTEF conversion slice: adapter contract to the current Go Formula runtime or a later ADR, deterministic formula-source metadata, review-only warnings, materialized media refs, and parser-version fields. - Done locally for the provenance bridge: the import adapter calls Go Formula QAS once and preserves per-question formulas with LaTeX/MathML, MTEF/OMML source metadata, OLE/preview references, parser version, confidence, and review flags through canonical Question Bank formula refs and approval snapshots. It counts only per-question formulas and does not materialize OLE/WMF previews as browser images.
- Done: validate a new
EXAM_DRAFTworkflow (examortron-de) and delivery mode (ONLINEorOFFLINE) before the Question Bank call. Invalid values in those fields fail without creating canonical questions. - Done: when
EXAM_DRAFTsnapshot persistence fails, retain the Question Bank and Exam owner writes and resume from the durable Import checkpoint. Exam snapshot routes resolve the scoped parent Exam first; Import must not issue owner-data deletes as compensation for an ambiguous remote response. - Done locally: the isolated native PostgreSQL Physics fixture now creates a second job through the direct
EXAM_DRAFTstate machine and replays the same approval request. The replay returns the original Question Bank IDs and Exam ID withreused=true; owner reads retain one Exam and 28 canonical snapshots with the Question Bank's18/4/6type mapping and 16 true/false statements. A focused unit regression also injects one checkpoint-save failure after Question Bank ingest and after Exam create, then verifies the stable owner keys and frozen actor on retry. This validates replay/data parity, not a process-restart/PostgreSQL-outage proof or default Gateway promotion. - Done: DOCX renderable package images use document-service's token-gated
/v1/internal/media-assetsboundary whenINTERNAL_SERVICE_TOKENis set. The document owner writes bytes and metadata in one request, avoiding container access to host-only presigned upload URLs. The legacy presigned path remains a tokenless fallback; import-service never writes MinIO/S3 or document-service tables directly. - Done for the bounded internal conversion slice:
POST /v1/import/mathtype/convertreturns a direct*-word-equation.docxafter replacing deterministic MTEF/OLE formulas with OMML. It is not a job or artifact workflow: it does not persist bytes, bypass document-service ownership, create a Gateway/frontend route, or claim visual equivalence for linear OMML fallback expressions. - Done for the durable MathType slice:
POST /v1/import/mathtype/jobsreads the source only through document-service, persists generated DOCX bytes only through its token-gated artifact endpoint, and stores artifact metadata plus conversion counters in the import job. Scoped download and explicit reprocess routes preserve document-service ownership; interrupted processing requires deliberate retry to avoid duplicate artifact creation. - Done for the owner-scoped source candidate: OCR and MathType browser uploads enter
document-servicethroughPOST /v1/import-sourcesand return onlysourceAssetId. The create/reprocess paths reopen bytes through/v1/internal/import-sources/{id}/contentwith a shared internal token and exact organization/actor/purpose/source-kind checks. OCR and MathType job create reject rawstorageKey; their explicit reprocess requires the job actor, and MathType download is actor-scoped. OCR detail/status/event/review/ approve are also actor-scoped.source_media_asset_idpersists on the import job; isolated direct and opt-in batch OCR browser/PostgreSQL proofs are recorded indocs/qa/ocr-review-browser-local-postgres.mdanddocs/qa/ocr-batch-review-browser-local-postgres.md. Each saves the Teacher review, approves through candidate Gateway, and reads the canonical Question Bank editor projection back through candidate Gateway, preserving the edited stem/type/answer/solution/scoring rule. The default Gateway import route remains legacy. - Done for opt-in DOCX Question Bank approval accounting: legacy-compatible
questionsentitlement preflight runs before every Question Bank ingest, includingQUESTION_BANK,EXAM_DRAFT, the native Teacher review path, and trusted auto-approval. After the durable Question Bank checkpoint, import-service consumes the completed reviewed question count throughusage-servicewithimport-approval:usage:questions:{requestDigest}and persistsAPPROVAL_USAGE_CONSUMED. Denial calls no Question Bank API; consume retry resumes the checkpoint without re-ingest or a second counter write. This is quota parity, not a new AZ Credit wallet charge: legacy approval gatedquestionsonly. The flag remains off by default and enabled invalid usage configuration fails closed.DOCX_IMPORT_APPROVAL_USAGE_ENABLEDis the canonical flag; the older Fast-only flag remains a compatibility alias. Compose, raw K8s, and Helm declare the canonical flag asfalseand the timeout as3000, so an operator must explicitly opt into re-metering.docs/qa/docx-fast-auto-approval-usage-proof.mdrecords the bounded proof. - Add remaining usage/accounting and public-proof parity for import creation, MathType, and retry/reprocess before cutover. OCR has a bounded internal usage/wallet accounting contract, but list/detail filters must still distinguish
docx-fast,ocr-document, andmathtype-equationsources without mixing ownership. - Done: replace in-memory/limited import job list compatibility filters with organization-aware repository queries and accurate pagination totals for
GET /v1/import/docx/teacher-libraryand the SSE snapshot path. - Add quota/AZ Credit integration points for import creation, MathType, and retry/reprocess through explicit usage/wallet APIs. Keep the completed OCR adapter scoped to
ocr_pages_monthly, and keep DOCX Fast approval on the separatequestionsentitlement with its own stable key; never reuse OCR idempotency keys for another import workload. - Done for the bounded
SVC-012-import-route-proof-bundleslice: matching status/approval/detail/review route rehearsals, Teacher-native local browser creation, and the review roundtrip self-test are documented; default import routes remain legacy-proxied until deployed rollback evidence exists. - Done locally for native DOCX review media: the Teacher review surface reads an unapproved package asset only through the authenticated, job-scoped
exam-import-docx-fast-asset-contentGateway adapter and renders its Blob URL. Reviewed output retains the canonical document-service media reference for Question Bank. The same local browser proof now exposes the first selected Physics formula as literal LaTeX withsource_kind, deterministic/confidence state, and DOCX source part without injecting parser HTML; save/reload preserves its id, LaTeX, and provenance. This candidate exists only in the explicit review roundtrip route tables; local browser proof is passed, while deployment/default-route proof remains required.
Acceptance:
- Import-service stores draft/preview state, not canonical approved questions.
- Import-service owns import queue/failure projections. Admin-service/BFF may call
GET /v1/admin/import/docx/queue-summary, but must not read import-service tables, spools, Redis keys, or in-memory queues directly. - Approval writes through question-service/exam-service APIs.
- Parser/runtime failures produce durable status/events and retry-safe payload refs.
- Reviewed edits, especially
questionTypeManualOverride, are the source of truth for approval after save/reload. - Approval rejects every question that still needs an answer but has no parser/style-hint/review answer. Teachers must save a corrected review before question-bank or exam handoff; import-service must not infer an answer key.
- PDF/OCR/MathType work stays in
services/docx-import-serviceor a documented adapter; do not create duplicateservices/import-serviceorservices/formula-docx-servicedirectories. - The opt-in OCR path calls usage/wallet owner APIs for quota and AZ Credit settlement while import-service records only workload/accounting metadata and idempotency keys. The opt-in DOCX Question Bank approval path is intentionally narrower: it uses
usage-servicequestionspreflight/consume only for all Question Bank approvals, matching legacy approval and never moving a wallet ledger into import-service. Other import workloads remain future explicit integrations. - Download/content routes must serve only materialized refs produced by the job and must not bypass document-service/file-service ownership for stored bytes.
Verification:
GOTOOLCHAIN=go1.25.11 go test ./services/docx-import-service/...- Focused owner queue summary regression:
GOTOOLCHAIN=go1.25.11 go test ./services/docx-import-service/internal/http -run TestAdminImportQueueSummaryReportsOwnerCounts -count=1 make test-import-create-routes test-import-create-live test-import-create-browsermake test-import-ocr-mathtype-routesproves the owner-source candidate tables while preserving default legacy routes; it is not live or browser promotion evidence.make test-import-pdf-ocr-workflowfor the internal Mistral-compatible OCR create/reprocess workflow, owner-service accounting contract, durable review metadata, preflight denial, and settlement-failure behavior.OCR_REVIEW_BROWSER_LOCAL_POSTGRES_CONFIRM=local-postgres make test-ocr-review-browser-local-postgresandOCR_REVIEW_BROWSER_LOCAL_POSTGRES_CONFIRM=local-postgres make test-ocr-batch-review-browser-local-postgresprove the disposable direct and batch Teacher review -> candidate approval -> candidate Question Bank readback path. They are not default-route or real-provider evidence.make test-import-status-routes test-import-approval-routesmake test-import-review-roundtrip-routes test-import-review-roundtrip-liveIMPORT_REVIEW_BROWSER_SELF_TEST=1 make test-import-review-browserverifies the candidate media/formula route shape. The focused Teacher UI test verifies authenticated asset fetch, Blob rendering, and read-only formula provenance; neither is default-route proof.DOCX_PHYSICS_GATEWAY_ATTEMPT_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-physics-attempt-local-postgresstarts at the real Teacher native file input, proves source upload -> opaque asset -> DOCX Fast create -> native detail polling -> editor, records browser Resource Timing and file-selection latency, then runs formula/media review, approval, and attempt flow. It also checksexam-import-job-statusthrough the dynamically served candidate after creation and completion, retaining only redacted route/correlation/counter and asset-hash artifacts. It is local non-default evidence only.- Gateway demo route-table guard:
make test-gateway-route-examples make test-docx-corpus test-docx-materialization test-docx-warning-capture test-docx-warning-parity test-docx-payload-persistence test-docx-bullmq-bridge test-docx-bullmq-statusmake test-docx-corpususes the read-only four-fixture Go Formula runtime and then runs an in-process Import ->httptestQuestion Bank approval contract. It checks complete forwarded payload parity for the approval-safe Physics fixture and verifies Math, English, and DGNL cannot call Question Bank while review is required. It is not a shared database persistence or public-route proof.DOCX_MATERIALIZATION_SELF_TEST=1 make test-docx-materializationruns the Physics corpus through the real parser and materializer with an in-memory uploader, so the seven image-reference rewrites remain regression-covered without a user-owned import/document runtime.DOCX_MATERIALIZATION_COMPOSE_CONFIRM=isolated-runtime make test-docx-materialization-isolatedcreates a fresh Compose project with no reused host runtime, exposes only dynamically assigned loopback ports, and starts only the parser/document/import dependency slice (not Question Bank, Exam, IAM, or Classroom). It verifies the Physics 28-question parser response has seven materialized images, then reads every image through document-service and removes all project volumes. It is direct owner-runtime evidence, not Gateway/browser or default-route promotion proof.DOCX_REVIEW_GUARD_LIVE_CONFIRM=docx-review-guard-live HOCTAPAZ_DOCX_CORPUS_DIR=/Users/velikho/Desktop/test-hoctapaz make test-docx-review-guard-liveruns all four local DOCX fixtures against the live import service. It checks parser counts, type/option repair, and that unresolved or multi-label single-choice answers block approval without writing any canonical question-bank rows.- On an explicitly isolated Compose stack with the temporary BFF listener:
DOCX_PHYSICS_EXAM_BLUEPRINT_ISOLATED_CONFIRM=docx-physics-exam-blueprint-isolated make test-docx-physics-exam-blueprint-isolated. It approves the review-safe Physics fixture to canonical Question Bank rows, then verifies the BFF/Exam matrix handoff, online/offline snapshots, and Word template output without changing a default Gateway route. DOCX_PHYSICS_EXAM_BLUEPRINT_ISOLATED_SELF_TEST=1 make test-docx-physics-exam-blueprint-isolatedchecks the isolated Compose topology and dynamic loopback ports without starting the runtime.DOCX_PHYSICS_EXAM_BLUEPRINT_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-physics-exam-blueprint-local-postgresrepeats the same canonical Physics handoff without Docker: Import sends seven source media assets to the temporary Document owner before Question Bank approval, then Exam resolves those assets while rendering Word. The native cluster uses separate owner databases and is removed after the artifact is written.DOCX_CORPUS_EXAM_BLUEPRINT_LOCAL_POSTGRES_CONFIRM=local-postgres make test-docx-corpus-exam-blueprint-local-postgrescovers the remaining real Math, English, and DGNL fixtures with fresh native owner databases. It first requires the untouchedEXAM_DRAFTapproval to fail closed with no Question Bank rows, saves only the exact review-safe subset, then verifies canonical Question Bank rows, direct approval replay, BFFONLINE/OFFLINEmatrix snapshots, and a checksum-verified document-service Word export. Its fixed baselines are Math22 -> 13(12single choice,1short numeric, two media, 28 formula refs), English40 -> 34(28single choice,6passage), and DGNL102 -> 37single choice. It removes all temporary listeners and the PostgreSQL cluster after writing the artifact.docs/qa/docx-fixture-persistence-runtime-proof.mdrecords the local persistence/runtime scope and explicit OLE/WMF/manual-review limits.- The 2026-07-15 recheck artifacts are
output/qa/docx-corpus-question-bank-local-postgres-20260715081210/summary.jsonandoutput/qa/docx-physics-gateway-attempt-local-postgres-20260715081327/summary.json. The 2026-07-15 revalidation of the cross-fixtureEXAM_DRAFTworkflow isoutput/qa/docx-corpus-exam-blueprint-local-postgres-20260715113710/summary.json. It re-ran the four-fixture parser/approval contract first, then retained only Math13/22, English34/40, and DGNL37/102review-safe rows for direct replay, BFFONLINE/OFFLINEsnapshots, and Word export. It is direct owner/BFF evidence only: it does not approve excluded rows, promote a default Gateway route, or replace browser/production proof. - Focused approval regression: reviewed parse output overrides original parser output in the question-bank/exam approval payload:
GOTOOLCHAIN=go1.25.11 go test ./services/docx-import-service/internal/usecase -run TestApproveUsesReviewedParseOutput -count=1 - Browser/runtime parity before route promotion:
make test-import-review-browseror the matching live smoke named in the handoff. - Future MathType and non-OCR accounting slices must add focused status/download/accounting tests plus route guards proving default broad import routes remain legacy until browser/runtime evidence exists.