Appearance
question-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-008
Current implementation: services/question-bank-service.
Owns: questions, answers, solutions, versions, tags, taxonomy refs, question type definitions.
Must read:
- Phase 6 question handoffs under
docs/agents/handoff-phase-6-question-*.md - legacy question modules under
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/questions/and Prisma question/question-type/taxonomy models in/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma.
Legacy source evidence:
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/questions/**/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.questions*.ts- Question, answer, question type, taxonomy, and usage Prisma models in
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma
Writable files:
services/question-bank-service/**contracts/openapi/services/question-bank-service.yamldocs/api/question-bank-service.md
Orchestrator-owned files:
- question route examples under
deploy/gateway/routes.question-*.json, question route tests underscripts/test/question-*.sh, default route table, shared deploy manifests, and frontend editor code unless explicitly delegated.
Delegated gateway verification scope:
- Only when the orchestrator assigns a question route/browser proof subtask, this service agent may update non-default
deploy/gateway/routes.question-*.jsonexamples,scripts/test/question-*.sh,docs/qa/question-*.md, and rootMakefiletest-question-*targets. - The default gateway route table and frontend editor code remain orchestrator-owned; public promotion still needs browser/runtime proof and rollback notes.
Initial tasks:
- Close native question CRUD/write gaps after read projection parity.
- Strengthen taxonomy/tag/difficulty tests and backfill verification.
- Keep import approval and AI apply contracts explicit and idempotent.
- Add canonical question create/update/archive/bulk write parity. Manual native create now exists at
POST /v1/questionswith rich content/version, answer-key, option, media/formula, taxonomy, custom type, source metadata, and tag preservation. Native update/version replacement now exists atPATCH /v1/questions/{id}: omitted fields are preserved, supplied options, tags, media refs, and formula refs replace current sets, and each successful update appends a replacementquestion_versionsrow. Conservative delete/status foundations now exist atDELETE /v1/questions/{id}andPATCH /v1/questions/bulk-status: normal delete archives rows with projected usage or clone refs and hard-deletes only unused question-bank-owned rows. Native service-local permanent-delete batch now exists atPOST /v1/questions/permanent-delete; it hard-deletes only question-bank-owned rows/projections and never removes exam/attempt/course data. Native service-local permanent-delete impact preview now exists atPOST /v1/questions/permanent-delete-impactfor selected IDs and the first scoped archived batch, usingquestion_usage_countsandquestion_exam_refsprojections. Detailed affected exam metadata now hydrates fromPUT /v1/questions/exam-refsrows published by exam-service or migration tooling. Native service-local job foundation now exists atPOST /v1/questions/permanent-delete/jobs,GET /v1/questions/permanent-delete/jobs/{id},GET /v1/questions/permanent-delete/jobs/{id}/events, andPOST /v1/questions/permanent-delete/jobs/{id}/cancel, with scoped progress snapshots and cancellation for question-bank-owned rows only. Durable broker/worker parity, browser proof, and public route promotion remain pending. - Add folder/group management parity before public question-bank cutover. Group list/create parity now exists at
GET /v1/question-groupsandPOST /v1/question-groups. Native folder list/create/update/delete now exists atGET/POST/PATCH/DELETE /v1/question-folders; manual question create acceptsfolderIdonly after question-service validates the folder in the same tenant/actor scope. Do not promote/api/question-groupsor/api/question-folderspublicly until route/browser parity is added. - Question search projection now emits JSON-compatible rebuild documents for search-service at
GET /v1/questions/search-projectionswithout importingsearch-service/internalpackages. Keep search indexing as copied data owned by search-service, never a direct cross-service DB read. - Question Bank now writes
search.projection.changed.v1envelopes to the service-ownedquestion_search_projection_outboxvia a Postgres trigger. Every question INSERT/UPDATE/DELETE shares a durable per-question revision with the canonical transaction; repeated UPSERT changes to one question in that transaction coalesce, while DELETE leaves a higher-version tombstone. The envelope contains redacted copied-index fields only, not answer keys, access secrets, or raw source metadata. With explicitQUESTION_SEARCH_PROJECTION_EVENT_TRANSPORT=nats, Question Bank leases due rows and publishes the exact persisted envelope to JetStream streamSEARCH_PROJECTIONSonsearch.projection.changed.v1. Delivery state is lease/retry/terminal-dead only and never changes canonical question writes. The publisher is disabled by default, covers Question Bank only, and adds no Gateway route; the search pull worker remains fallback. - Preserve versions, options, media/formula refs, rich JSON, and archive-vs-hard-delete behavior.
- Done: Question Bank exposes service-only
POST /v1/internal/questions/select-for-exam-blueprint. It applies normal tenant/actor scope plus matrix filters, published-only status, duplicate exclusion, grouped-question requirement, and optional seeded ordering.exam-serviceconsumes hydrated values as immutable snapshots; no service reads this database directly. - Done: exam-blueprint selection now batches its post-selection type, taxonomy, usage, and relation read-model hydration. Rule ordering, duplicate exclusion, and immutable snapshot content are unchanged while multi-rule matrix generation avoids repeated read-model lookups.
- Done: add
SVC-008-question-save-reload-route-proofcoverage that performs native question update followed by reload and verifies replacement versions, options, tags, folder, and omitted-field preservation. The bounded proof now includes a real in-memory service and non-default Gateway save/reload, Chromium API-context read/update/reload, the real teacher editor's Gateway create/reload/publish/reload workflow, fixture cleanup, and default-route rollback. Route evidence remains non-default; deployment/populated-target browser proof is still required before public promotion. - Done: add the narrow
POST /v1/questions/ai-preview-draftsowner boundary for teacher-reviewed AI generation. It forcesDRAFTandAI_GENERATEDprovenance while preserving reviewed question content/options/explanation in Question Bank. The opt-in teacher Gateway manifest exposes only the exact write path; broad/api/questionsremains legacy-proxied. The durable(organizationId, actorId, previewId)record returns the existing draft for an identical retry, rejects altered payloads, and is released only by hard delete. - Done:
POST /v1/questions/ai-preview-solutionsapplies a reviewed AI explanation only to a scoped canonical Question Bank row. It appends a replacement version, writes explanation-only AI provenance without changing original question/import provenance, and keeps a durable(organizationId, actorId, previewId)idempotency record. Exact opt-in Gateway exposure remains separate from the broad legacy questions route. - Done: P6-048 caches settled charged-generation previews inside ai-classifier-service for 24 hours, scoped by organization, actor, and the normalized request hash. It returns only the existing preview and never bypasses this Question Bank owner boundary; provider/settlement failure is deliberately not cached.
Acceptance:
- Exam/attempt snapshots are by-value consumers, not DB join consumers.
- Import drafts become canonical questions only through question-service APIs.
- Manual question-type override behavior remains compatible.
- Public write routes are not promoted until browser-visible editor save parity is proven.
Verification:
GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/...GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_CreateManualQuestion' -count=1GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_CreateQuestionManualWriteAndListScope -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run TestQuestionService_UpdateManualQuestionPreservesOmittedFieldsAndVersions -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_UpdateQuestionCreatesReplacementVersionAndPreservesOmittedFields -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run TestQuestionService_ApplyAIPreviewSolutionCreatesVersionAndIsIdempotent -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_ApplyAIPreviewSolutionWritesCanonicalVersion -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(Archive|BulkStatus)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(ArchiveQuestion|QuestionBulkStatus)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(DeleteManualQuestion|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(DeleteQuestion|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(QuestionExamRef|PermanentDeleteImpact)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(QuestionExamRef|PermanentDeleteImpact)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(PermanentDeleteJob|PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(PermanentDeleteJob|PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(QuestionFolder|CreateManualQuestion.*Folder)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(QuestionFolder|CreateQuestionManualWriteAndListScope)' -count=1 -vGOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionSearchProjection|TestQuestionSearchProjectionListsRebuildDocuments' -count=1GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_QuestionSearchProjectionBackfill -count=1 -vQUESTION_SEARCH_PROJECTION_OUTBOX_POSTGRES_CONFIRM=disposable-postgres make test-question-search-projection-outbox-postgresproves owner transaction coalescing, revision progression, rollback, and delete tombstone against a disposable PostgreSQL instance.make test-question-read-routes test-question-types-routes test-question-classification-apply-routes- Question type and read route promotion also requires
make test-question-types-live test-question-types-browserandmake test-question-read-browser test-question-read-browser-admin. - Browser-visible parity gates before public route promotion:
make test-question-read-browser test-question-read-browser-admin,make test-question-write-routes,QUESTION_WRITE_SELF_TEST=1 make test-question-write-live, andQUESTION_WRITE_BROWSER_SELF_TEST=1 make test-question-write-browser. Real editor write promotion must followdocs/qa/question-write-route-rehearsals.mdand attach live/browser artifacts plus rollback notes.