feat(platform): pagination, admission control, cohort roles, version endpoint, structured assessment editor

- Real server-side pagination (platform/api/src/pagination.js) on the audit
  log (was a flat LIMIT 200), student attempt history (was LIMIT 100), and
  assessment session history (was fully unbounded - the one genuine growth
  risk found). Frontend updated (AuditView, AssessmentsView, StudentsView).
- Admission control: resource-policy.yaml's new (disabled-by-default)
  admission_control.max_concurrent_labs, checked before touching Docker at
  all, returns a clean 503 CAPACITY_EXCEEDED instead of failing mid-provision.
  Relayed as a clean student-facing message, never a raw 500. New
  GET /capacity endpoint feeds the instructor dashboard.
- cohort_instructors.role_in_cohort (existed since Phase 2B, never read) is
  now enforced: teacher vs viewer, rbac.js's
  requireCohortWriteAccess/requireStudentCohortWriteAccess.
- GET /api/version - app version, deployed Git SHA, latest migration as a
  schema-version stand-in.
- Structured assessment authoring UI (AssessmentsView.jsx's PoolSpecEditor)
  replaces the raw-JSON pool_spec textarea, backed by a new
  GET /api/admin/scenario-catalog endpoint. Owner-only raw-JSON fallback
  remains for anything the structured editor doesn't cover.
- NEEDS_ATTENTION_FAIL_THRESHOLD/STALE_DAYS moved to env-var configuration.

Full regression suite (e2e-phase2a/2b/3a/3b, 67/67) re-run clean after
these changes; frontend production build confirmed clean.
This commit is contained in:
2026-08-24 20:15:39 -05:00
parent addf474b70
commit a8e543234c
13 changed files with 369 additions and 60 deletions
+4 -3
View File
@@ -52,7 +52,7 @@ export const adminApi = {
createStudent: (display_name, cohort_id) => post('/api/admin/students', { display_name, cohort_id }),
updateStudent: (id, fields) => patch(`/api/admin/students/${id}`, fields),
studentProgress: (id) => get(`/api/admin/students/${id}/progress`),
studentAttempts: (id) => get(`/api/admin/students/${id}/attempts`),
studentAttempts: (id, page = 1) => get(`/api/admin/students/${id}/attempts?page=${page}`), // Phase 4.1 §16 — paginated, see pagination.js
studentEnvironment: (id) => get(`/api/admin/students/${id}/environment`),
provisionStudentLab: (id) => post(`/api/admin/students/${id}/environment/provision`),
assignStudentCohort: (id, cohort_id) => post(`/api/admin/students/${id}/cohort`, { cohort_id }),
@@ -79,19 +79,20 @@ export const adminApi = {
revokeInstructorSessions: (id) => post(`/api/admin/instructors/${id}/revoke-sessions`),
resetInstructorPassword: (id, new_password) => post(`/api/admin/instructors/${id}/reset-password`, { new_password }),
auditEvents: () => get('/api/admin/audit-events'),
auditEvents: (page = 1) => get(`/api/admin/audit-events?page=${page}`), // Phase 4.1 §16 — paginated, see pagination.js
// ── Phase 3B: learning analytics ────────────────────────────────────────────────────────
cohortLearning: (id) => get(`/api/admin/cohorts/${id}/learning`),
studentLearning: (id) => get(`/api/admin/students/${id}/learning`),
scenarioAnalytics: (cohortId) => get(`/api/admin/scenario-analytics${cohortId ? `?cohort_id=${cohortId}` : ''}`),
needsAttention: (cohortId) => get(`/api/admin/cohorts/${cohortId}/needs-attention`),
scenarioCatalog: () => get('/api/admin/scenario-catalog'), // Phase 4.1 §18 — structured assessment editor
// ── Phase 3B: assessment (exam + capstone) management ───────────────────────────────────
assessments: () => get('/api/admin/assessments'),
createAssessment: (fields) => post('/api/admin/assessments', fields),
updateAssessment: (id, fields) => patch(`/api/admin/assessments/${id}`, fields),
assignAssessment: (id, cohort_id) => post(`/api/admin/assessments/${id}/assign`, { cohort_id }),
assessmentSessions: (id) => get(`/api/admin/assessments/${id}/sessions`),
assessmentSessions: (id, page = 1) => get(`/api/admin/assessments/${id}/sessions?page=${page}`), // Phase 4.1 §16 — paginated, see pagination.js
cancelAssessmentSession: (sessionId) => post(`/api/admin/assessment-sessions/${sessionId}/cancel`),
}