Appearance
Handoff: SVC-006-public-route-guard-proof
Scope
- Dispatch ID:
SVC-006 - Service:
classroom-service - Task pack:
docs/agents/service-tasks/classroom-service.md - Next task:
SVC-006-public-route-guard-proof - Goal: prove classroom route guard behavior while keeping profile and organization references owner-local.
- Status: accepted bounded slice, not service completion.
Docs Read
docs/agents/service-agent-next-slice-queue.jsondocs/agents/service-agent-next-slice-closeout-ledger.jsondocs/agents/service-tasks/classroom-service.mddocs/agents/service-tasks/classroom-public-adapter-preflight.mddocs/agents/handoff-phase-8-classroom-adapter-preflight.mddocs/api/classroom-service.mddocs/qa/classroom-public-route-guard-proof.md
Legacy Evidence Read
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/classrooms/classroom.service.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/classrooms/classrooms.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/classrooms/classroom-workspace.spec.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.classrooms-core.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/app-data/app-data.public.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma
Legacy source was read-only evidence. No file under /Users/velikho/Desktop/WORKING/HOCTAPAZ/ was changed.
Files Changed
services/classroom-service/internal/usecase/classroom.goservices/classroom-service/internal/http/public_route_guard_test.goservices/classroom-service/internal/http/server.goservices/api-gateway/internal/gateway/gateway.goservices/api-gateway/internal/gateway/gateway_test.goapps/admin-web/src/App.test.tsxapps/admin-web/src/features/classes/admin-classes-gateway.tsapps/admin-web/src/features/classes/admin-classes-panel.tsxdocs/agents/admin-web-frontend/06-qa-and-bugfix.mddocs/qa/classroom-public-route-guard-proof.mddocs/agents/handoffs/SVC-006-public-route-guard-proof.mddocs/agents/service-agent-next-slice-closeout-ledger.jsondocs/agents/service-agent-next-slice-closeout-ledger.mddocs/agents/service-agent-accepted-handoffs.mddocs/agents/service-tasks/classroom-service.mddocs/api/classroom-service.mddocs/index.mddocs/.vitepress/config.tsdocs/qa/service-agent-goal-completion-audit.mddocs/qa/service-agent-readiness-smoke.mddeploy/gateway/routes.classroom-read-native-example.jsondeploy/gateway/routes.classroom-read-native-localhost-example.jsonscripts/test/classroom-read-route-coverage.shscripts/test/classroom-read-admin-browser-smoke.mjsscripts/test/classroom-read-runtime-smoke.shscripts/test/classroom-route-guard.shdocs/qa/classroom-read-runtime-smoke.md
Behavior Implemented
- Non-admin classroom list requests now use the actor organization as the authoritative scope; a query/header organization value cannot widen it.
- Non-admin classroom detail and manage/read checks enforce the actor organization against the classroom organization reference.
- Gateway identity is authoritative: caller-supplied
X-Actor-IdandX-Actor-Rolecannot override authenticatedX-User-*headers, and the gateway strips those compatibility headers before forwarding. - Non-admin list/detail paths fail closed when organization context is absent; query parameters cannot supply a tenant on behalf of an unauthenticated actor context.
- Added HTTP regression coverage for cross-tenant denial, PARENT denial, gateway identity headers, owner-local ids, and absence of profile/org display fields.
- Existing route guard remains the source of truth for default and non-default gateway route tables. No public route was promoted.
- Added a named non-default candidate for authenticated
ADMIN/TEACHERGET /api/classroomsandGET /api/classrooms/{classroomId}reads only. It forwards to the internal/v1/classroomsowner API and retains the broad classroom route aslegacy_proxy.
Required Evidence
- classroom route guard proof:
make test-classroom-route-guardkeeps public classroom and admin classroom prefixes onlegacy_proxy. - owner-local profile/org references: the focused HTTP test proves tenant scoping and public-id-only classroom responses without profile/org display hydration.
- rollback notes: keep
deploy/gateway/routes.jsonactive and do not promote native classroom routes from this slice. - disposable candidate runtime:
make test-classroom-read-routesvalidates route order, exact list/detail shapes, auth, organization, role, and target constraints.CLASSROOM_READ_RUNTIME_SMOKE_CONFIRM=disposable-runtime make test-classroom-read-runtimeproves tenant isolation, RBAC, response fields, and local latency sanity through the candidate Gateway. The opt-in Admin browser variant also proves canonical nested-session tenant propagation, one Gateway request, credentialed CORS/native route headers, and read-only tenant rendering.
Tests And Verification
gofmt -w services/classroom-service/internal/usecase/classroom.go services/classroom-service/internal/http/public_route_guard_test.goGOTOOLCHAIN=go1.25.11 go test ./services/classroom-service/... -count=1GOTOOLCHAIN=go1.25.11 go test ./services/api-gateway/... -count=1make test-classroom-route-guardmake test-classroom-read-routesCLASSROOM_READ_RUNTIME_SMOKE_CONFIRM=disposable-runtime CLASSROOM_READ_RUNTIME_SMOKE_ARTIFACT_DIR=output/qa/classroom-read-runtime-20260713 make test-classroom-read-runtimeCLASSROOM_READ_RUNTIME_SMOKE_CONFIRM=disposable-runtime CLASSROOM_READ_RUNTIME_SMOKE_ADMIN_BROWSER=1 CLASSROOM_READ_RUNTIME_SMOKE_ARTIFACT_DIR=output/qa/classroom-read-admin-browser-runtime-20260713 make test-classroom-read-runtime
All commands passed for this slice. The route guard covers default and route example JSON files; the focused Go test covers the service-side ownership boundary. The isolated runtime used memory-backed processes only and recorded direct and browser list latencies below five seconds; the opt-in browser candidate captured redacted request/response evidence. Both values are local sanity metrics, not production SLOs.
Route State
- Route/default state: default classroom routes remain legacy_proxy; the named list/detail candidate is non-default pending deployed and browser promotion proof.
/api/classrooms*,/api/admin/classrooms*, and/api/classrooms/:id/notifyremainlegacy_proxyin the default table.- Only candidate list/detail reads reach native
/v1/classrooms*; all other native contracts remain internal only.
Data Notes
- Classroom-service stores public ids for organization/profile/document references and classroom-local member rows.
- It does not own profile PII, IAM roles, permission-bearing organization membership, lessons, materials, tuition, exams, or attempt records.
- Snapshot lesson/material/tuition/progress fields remain counters until their owner services provide adapters.
Risks / Blockers
- This is static route/service HTTP plus disposable local Gateway and browser proof, not deployed promotion evidence.
- Public route promotion still needs legacy response parity, deployed route evidence, and rollback-after-live evidence.
- IAM/Profile default cutover gates and classroom-adjacent owner slices remain open.
Rollback Notes
These are the rollback notes for the accepted bounded slice:
- Keep
deploy/gateway/routes.jsonas the active route table. - If a rehearsal caller uses native
/v1/classrooms*, disable that caller and return traffic to the legacy/api/classrooms*route. - Do not expose purge, progress, lessons, materials, or tuition routes through the public gateway from this handoff.
Next Step
Compare the candidate list/detail response against the legacy teacher surface, then collect deployed evidence plus rollback-after-live proof before considering any default public route promotion.