Skip to content

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.yaml
  • docs/api/question-bank-service.md

Orchestrator-owned files:

  • question route examples under deploy/gateway/routes.question-*.json, question route tests under scripts/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-*.json examples, scripts/test/question-*.sh, docs/qa/question-*.md, and root Makefile test-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/questions with rich content/version, answer-key, option, media/formula, taxonomy, custom type, source metadata, and tag preservation. Native update/version replacement now exists at PATCH /v1/questions/{id}: omitted fields are preserved, supplied options, tags, media refs, and formula refs replace current sets, and each successful update appends a replacement question_versions row. Conservative delete/status foundations now exist at DELETE /v1/questions/{id} and PATCH /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 at POST /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 at POST /v1/questions/permanent-delete-impact for selected IDs and the first scoped archived batch, using question_usage_counts and question_exam_refs projections. Detailed affected exam metadata now hydrates from PUT /v1/questions/exam-refs rows published by exam-service or migration tooling. Native service-local job foundation now exists at POST /v1/questions/permanent-delete/jobs, GET /v1/questions/permanent-delete/jobs/{id}, GET /v1/questions/permanent-delete/jobs/{id}/events, and POST /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-groups and POST /v1/question-groups. Native folder list/create/update/delete now exists at GET/POST/PATCH/DELETE /v1/question-folders; manual question create accepts folderId only after question-service validates the folder in the same tenant/actor scope. Do not promote /api/question-groups or /api/question-folders publicly until route/browser parity is added.
  • Question search projection now emits JSON-compatible rebuild documents for search-service at GET /v1/questions/search-projections without importing search-service/internal packages. Keep search indexing as copied data owned by search-service, never a direct cross-service DB read.
  • Question Bank now writes search.projection.changed.v1 envelopes to the service-owned question_search_projection_outbox via 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 explicit QUESTION_SEARCH_PROJECTION_EVENT_TRANSPORT=nats, Question Bank leases due rows and publishes the exact persisted envelope to JetStream stream SEARCH_PROJECTIONS on search.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-service consumes 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-proof coverage 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-drafts owner boundary for teacher-reviewed AI generation. It forces DRAFT and AI_GENERATED provenance while preserving reviewed question content/options/explanation in Question Bank. The opt-in teacher Gateway manifest exposes only the exact write path; broad /api/questions remains 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-solutions applies 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=1
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_CreateQuestionManualWriteAndListScope -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run TestQuestionService_UpdateManualQuestionPreservesOmittedFieldsAndVersions -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_UpdateQuestionCreatesReplacementVersionAndPreservesOmittedFields -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run TestQuestionService_ApplyAIPreviewSolutionCreatesVersionAndIsIdempotent -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_ApplyAIPreviewSolutionWritesCanonicalVersion -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(Archive|BulkStatus)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(ArchiveQuestion|QuestionBulkStatus)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(DeleteManualQuestion|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(DeleteQuestion|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(QuestionExamRef|PermanentDeleteImpact)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(QuestionExamRef|PermanentDeleteImpact)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(PermanentDeleteJob|PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(PermanentDeleteJob|PermanentDeleteImpact|PermanentDeleteQuestions)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionService_(QuestionFolder|CreateManualQuestion.*Folder)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run 'TestServer_(QuestionFolder|CreateQuestionManualWriteAndListScope)' -count=1 -v
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/usecase -run 'TestQuestionSearchProjection|TestQuestionSearchProjectionListsRebuildDocuments' -count=1
  • GOTOOLCHAIN=go1.25.11 go test ./services/question-bank-service/internal/http -run TestServer_QuestionSearchProjectionBackfill -count=1 -v
  • QUESTION_SEARCH_PROJECTION_OUTBOX_POSTGRES_CONFIRM=disposable-postgres make test-question-search-projection-outbox-postgres proves 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-browser and make 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, and QUESTION_WRITE_BROWSER_SELF_TEST=1 make test-question-write-browser. Real editor write promotion must follow docs/qa/question-write-route-rehearsals.md and attach live/browser artifacts plus rollback notes.

Go-platform documentation is generated from repository Markdown.