Skip to content

Teacher AI Preview Question Bank Solution Runtime Proof

Date: 2026-07-12

Scope

This proof covers the reviewed AI-solution path only:

teacher-web -> exact opt-in Gateway route -> question-bank-service -> replacement question version

AI remains a preview provider. Question Bank owns the canonical question, version, explanation provenance, tenant scope, and idempotency record. The default deploy/gateway/routes.json remains unchanged: its broad /api/questions route is still legacy_proxy.

Automated Evidence

bash
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/api-gateway/internal/gateway \
  -run TestGatewayTeacherAIPreviewRoutesForwardOnlyAuthenticatedTeacherScope -count=1
pnpm --filter @hoctapaz/ui typecheck
pnpm --filter @hoctapaz/ui test -- teacher-workspace.test.tsx
make test-teacher-ai-preview-routes
make test-ai-classification-job-route-guard

All commands passed on 2026-07-12. The Gateway test verifies the exact route, authenticated actor headers, role/organization guard, and forged caller-header replacement. The UI test verifies that a logged-in teacher must supply a canonical Question Bank ID and save a reviewed preview through /api/questions/ai-preview-solutions.

Compose Runtime Fixture

The service was rebuilt with the real migration path:

bash
docker compose -f deploy/docker-compose.yml up -d --build question-bank-service

GET http://127.0.0.1:8089/readyz returned 200 with status ready. Using isolated organization org_ai_solution_runtime_20260712 and teacher teacher_ai_solution_runtime_20260712, the runtime fixture:

  1. Created canonical question q_c33a0d3cc3a04872958dc59f81d1a113 with original version qv_ab7bb62e571358706c01313e188d7ea1 and source metadata.
  2. Posted the reviewed solution_runtime_preview_1 to POST /v1/questions/ai-preview-solutions; it returned 201 and replacement version qv_3be518fe6e99965686da2752c1f81c5b.
  3. Verified persisted explanation text, type=ai_preview_solution, source=ai-preview, preview/provider/model fields, two ordered steps, and unchanged original source metadata.
  4. Replayed the exact payload; it returned 200 with the same replacement version, so no extra version was added.
  5. Retried as a different teacher in the same organization; it returned 404.
  6. Hard-deleted the unused fixture question successfully (200), which also removes the companion idempotency record.

Exact Gateway Runtime Fixture

The shared host port 8085 belongs to the local control plane, so this proof used a temporary Compose Gateway on 127.0.0.1:18085 rather than changing the default Gateway or its route table:

bash
docker compose -f deploy/docker-compose.yml build api-gateway
docker compose -f deploy/docker-compose.yml run -d --no-deps \
  --name hoctapaz-ai-preview-gateway-proof -p 18085:8080 \
  -e GATEWAY_ROUTE_TABLE=/deploy/gateway/routes.teacher-ai-preview-native-example.json \
  api-gateway

The temporary Gateway reported ready and /v1/routes exposed only the exact teacher-ai-preview-solution-write route for this write. A short-lived HS256 teacher token then submitted the reviewed solution with forged X-Actor-* headers. The actual request returned 201 with X-Hoctapaz-Gateway-Route: teacher-ai-preview-solution-write and X-Hoctapaz-Gateway-Route-State: native_write; Question Bank persisted version 2 and the expected preview/step provenance. The identical replay returned 200. The same request using a STUDENT token returned 403 AUTH_FORBIDDEN before it reached Question Bank. The fixture was hard-deleted with 200, then hoctapaz-ai-preview-gateway-proof was removed.

Teacher Browser Surface

Playwright opened the running teacher Vite app at http://127.0.0.1:5202/teacher/questions/ai-solutions. Anonymous navigation redirected to /login?next=%2Fteacher%2Fquestions%2Fai-solutions; a local teacher demo session returned to the protected route. The desktop and 390 x 844 mobile views both rendered the canonical Question Bank ID field, source selector, selected-solution preview, and review action without overlap. Mobile reported clientWidth=390 and scrollWidth=390. The only browser console error was the existing missing favicon.ico 404; no application errors were observed.

Remaining Boundary

This is a non-default, opt-in route rehearsal and direct owner-service runtime proof. It does not promote broad question APIs, does not bypass teacher review, does not let AI write Question Bank data directly, and does not claim a provider-execution cache for charged generation requests.

Go-platform documentation is generated from repository Markdown.