Appearance
Handoff: SVC-022-analytics-rebuildable-projection-proof analytics-service rebuildable projection proof
Scope
- Service/area:
analytics-service - Task ID:
SVC-022-analytics-rebuildable-projection-proof - Goal: prove analytics projections can be rebuilt from versioned event sources through explicit hydrated snapshots, with replay-safe report metadata and guarded route rehearsal.
- Non-goals: asynchronous rebuild worker, production backfill runner, owner-service database joins, canonical
StudentMasteryparity, public/default route promotion, browser/runtime parity, or changes to legacy source under/Users/velikho/Desktop/WORKING/HOCTAPAZ/.
Docs Read
docs/architecture/service-responsibility-matrix.mddocs/architecture/legacy-api-map.mddocs/agents/service-tasks/analytics-service.mddocs/agents/handoffs/SVC-022-mastery-projection-fixtures.mddocs/agents/service-agent-next-slice-queue.jsondocs/agents/service-agent-next-slice-closeout-ledger.jsondocs/agents/agent-handoff-format.mddocs/api/analytics-service.mddocs/qa/analytics-rebuildable-projection-proof.mddocs/qa/service-agent-goal-completion-audit.mddocs/qa/service-agent-readiness-smoke.md
Legacy Evidence Read
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/analytics/analytics.service.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/analytics/analytics.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.exams-analytics.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.progress-overview.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.learning-progress.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/progress/progress.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exams/exam-core.service.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/attempts/attempts.controller.ts- Legacy analytics/progress/result/dashboard inputs were read-only evidence; no legacy source or Prisma table was changed.
Files Changed
services/analytics-service/internal/usecase/analytics_rebuild_test.goservices/analytics-service/internal/usecase/analytics.goservices/analytics-service/internal/domain/analytics.goservices/analytics-service/internal/http/server.goservices/analytics-service/internal/http/server_test.godocs/api/analytics-service.mddocs/qa/analytics-rebuildable-projection-proof.mddocs/index.mddocs/.vitepress/config.tsdocs/agents/handoffs/SVC-022-analytics-rebuildable-projection-proof.mddocs/agents/service-agent-next-slice-closeout-ledger.jsondocs/agents/service-agent-next-slice-closeout-ledger.mddocs/agents/service-agent-accepted-handoffs.mddocs/qa/service-agent-goal-completion-audit.mddocs/qa/service-agent-readiness-smoke.md- No producer event contract, migration, default gateway route, or legacy file was changed.
Behavior Implemented
- Added focused proof coverage for the bounded rebuild workflow: list a versioned source event, replay it without mutation, apply an explicit hydrated result snapshot to the service-owned projection, and record/replay rebuild counters.
- The existing analytics foundation stores
sourceServiceplussourceEventIdand preserves event payloads for backfill selection. Result snapshots remain service-local upserts keyed byattemptId; analytics does not resolve owner data through database joins. - The existing rebuild-report API records
QUEUED|RUNNING|COMPLETED|FAILEDmetadata, source ids, counters, and replay identity. This is rebuild evidence, not an asynchronous worker claim. - The producer evidence is explicit: attempt, exam, course, and classroom services emit versioned event sources with snapshots or projection references. A future worker/backfill runner must supply any missing hydrated display data through an owner API or explicit snapshot before calling analytics.
- Non-admin analytics reads and event/rebuild writes now fail closed when the actor has no organization context or attempts to override it with a query or payload organization id. Admins and internal writers may select an explicit organization scope. Gateway-authenticated identity takes precedence over caller-supplied
X-Actor-*compatibility headers.
Tests And Verification
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/analytics-service/... - Result: passed with
-count=1. - Evidence: the new rebuild workflow test passed source event replay, event listing, hydrated result upsert, rebuild counters, and rebuild-report replay; existing HTTP/use-case tests also passed result, weak-topic, event, and report paths.
- Command:
make test-analytics-routes - Result: passed.
- Evidence: route guard evidence passed for the default legacy table and both non-default native examples.
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/attempt-service/... ./services/course-service/... ./services/exam-service/... ./services/classroom-service/... -count=1 - Result: passed.
- Evidence: producer source-event and analytics sink packages passed for attempt, course, exam, and classroom services.
- Command:
pnpm docs:build - Result: passed after the QA page and analytics API link were added to VitePress.
- Command:
git diff --check - Result: passed.
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/analytics-service/... -count=1 - Result: passed after the organization-scope hardening.
- Evidence: non-admin query/payload organization overrides return
ANALYTICS_FORBIDDENwithout writing or reading another tenant's data. - Command:
GOTOOLCHAIN=go1.25.11 go test -race ./services/analytics-service/internal/http ./services/analytics-service/internal/repository -count=1 - Result: passed.
- Runtime recheck: an isolated
analytics-serviceand disposable PostgreSQL database accepted one versioned attempt event, preserved the original event on a changed-payload replay, upserted one explicitly hydrated result snapshot, and preserved the original completed rebuild report on replay. The same teacher actor receivedANALYTICS_FORBIDDENwhen querying another organization. Evidence is retained inoutput/qa/analytics-rebuildable-projection-proof-20260713/. - Gateway recheck: the non-default
routes.analytics-read-native-localhost-example.jsonrouted exact results and weak-topic reads withX-Gateway-Route-State: native_read; the active default table still returnedX-Gateway-Route-State: legacy_proxyagainst a deliberately unavailable legacy target. The temporary Compose stack and Gateway were removed after capture.
Required Evidence
- rebuildable projection proof:
services/analytics-service/internal/usecase/analytics_rebuild_test.goexercises source-event selection, explicit hydrated result upsert, and replay-safe rebuild report counters without owner database joins. - event-source proof:
docs/qa/analytics-rebuildable-projection-proof.mdrecords the versioned attempt, exam, course, and classroom event contracts; producer package tests passed and analytics stores source idempotency metadata. - route guard evidence:
make test-analytics-routespassed and confirms only exact non-default result/weak-topic reads are rehearsed as native routes. - tenant boundary evidence: analytics HTTP tests cover mismatched query and event payload organization ids for a teacher actor.
- isolated runtime evidence:
output/qa/analytics-rebuildable-projection-proof-20260713/contains the event-source replay, hydrated projection, replay-safe rebuild report, tenant boundary, native-read header, and default rollback header captures.
Route State
- Legacy route:
/api/analytics*,/api/exams*,/api/classrooms*,/api/students*,/api/parents*, and fallback routes remain on the default legacy gateway table. - Native route: non-default examples rehearse only exact
GET /api/analytics/resultsandGET /api/analytics/weak-topicsreads with gateway auth,ADMIN/TEACHERrole checks, and organization scope. - Gateway state: analytics route changes require rebuildable projection/event-source proof and route guard evidence. This handoff does not approve a default route change.
- Delegated gateway scope: route proof only; default gateway route tables remain orchestrator-owned.
- Rollback notes: keep
deploy/gateway/routes.jsonactive; remove the non-default analytics route examples or disable native analytics callers if freshness, parity, access adapters, or runtime/browser proof regresses.
Data Notes
- Tables touched: the isolated analytics PostgreSQL database only; no shared or legacy database was modified.
- Migration run: analytics migrations were applied only inside the disposable
hoctapaz-analytics-proof-20260713Compose project, which was removed with its volumes after capture. - Validation report: focused service, producer, route, race, isolated runtime, docs, and diff checks passed; no production data or backfill job was executed.
Risks / Blockers
- The bounded proof does not provide an asynchronous rebuild worker or production-like backfill run report; those remain separate tasks.
- Source replay identity is still keyed by
(sourceService, sourceEventId); tenant-qualified replay keys require a coordinated schema/producer migration. - Parent/teacher/admin visibility adapters, full legacy filter parity, and browser proof remain open before public promotion. The isolated runtime and default-route rollback rehearsals are evidence only, not promotion approval.
- Canonical course mastery and profile/classroom recipient resolution remain outside analytics ownership.
- IAM/Profile production-like populated-target cutover and rollback proof remain open for the broader goal.
Next Step
- Build a named owner-service backfill runner or projection worker against disposable event fixtures, attach runtime/browser and rollback evidence, and only then review any default analytics route promotion.