Praxis CI
de2020eee1
feat(P02): SLICE-10 P2 integration — SPA fallback, router mount, e2e tests
...
TASK-10-01 (G-041 binding): server/__main__.py — SpaStaticFiles custom
subclass that returns index.html for non-file paths (NOT a catch-all
route). G-041 OVERRIDES the plan's catch-all approach — a catch-all
before StaticFiles would shadow asset serving. The subclass serves
assets normally + falls back to index.html for client-side routes.
Mounted at / AFTER all API routes so they take precedence.
TASK-10-02: server/__main__.py — mount operator API routers (cohort,
mastery, failure_patterns, credentials) before SPA fallback. Start
NightlyScheduler in lifespan (03:00 CT), cancel on shutdown.
TASK-10-03: tests/test_p2_aggregation_integration.py — e2e aggregation→
endpoint (12 learners non-suppressed, 5 suppressed), nightly reconcile
refreshes last_updated, freshness ≤ 24h. G-038 differencing-attack at
API layer. Requires Postgres (skips if no DSN).
TASK-10-04: tests/test_p2_spa_fallback.py — 9 assertions: / → voice UI,
/operator/* → index.html, API routes → JSON, /assets/* → StaticFiles.
R-DASH-03/05 verified. Deviation: GET to POST-only /pipecat/webrtc
falls through to SPA fallback (not 405) — acceptable, the POST route
is the real entrypoint; a GET is a client-side navigation attempt.
TASK-10-05: .ciagent/VERIFY-P2.md — REQ-ID → test mapping for all 4 P2
REQ-IDs + G-038 + G-041 + R-DASH-05.
---ci---
project: praxis
phase: 2
milestone: v0.4
status: execute
persona: backend-engineer
task: 10-01..10-05
requirements:
covered: [REQ-DASH-01, REQ-NFR-DASH-01, REQ-NFR-DASH-02, REQ-MT-02]
---/ci---
2026-08-04 04:35:29 +00:00
Praxis CI
a7f7c4e1cc
feat(P02): SLICE-08 operator API cohort endpoints — auth-gated, k-anon
...
TASK-08-01: server/operator/cohort.py — GET /api/operator/cohort practice
volume view (sessions_count, active_learners_count), k-anon display.
TASK-08-02: server/operator/mastery.py — GET /api/operator/mastery
(gate_open_rate, median_mastery_score, rubric_criterion_means).
TASK-08-03: server/operator/failure_patterns.py — GET /api/operator/failure-patterns
(failure_mode:*, branch:* metrics).
TASK-08-04: server/operator/credentials.py — GET /api/operator/credentials
+ POST /api/operator/credentials/{id}/revoke (VC management, D-057).
TASK-08-05: tests/test_operator_endpoints.py — 401/200/503, suppressed
cells value=null, last_updated=max, revoke, no per-learner data (R-DASH-02).
server/operator/_common.py — shared Cell/PathView/ViewResponse models +
all_recent_aggregates query + group_by_path helper.
All endpoints auth-gated via Depends(current_operator) (D-057). No PII
beyond aggregates (D-031, R-DASH-02).
---ci---
project: praxis
phase: 2
milestone: v0.4
status: execute
persona: backend-engineer
task: 08-01..08-05
requirements:
covered: [REQ-DASH-01, REQ-NFR-DASH-01]
---/ci---
2026-08-04 02:03:50 +00:00
Praxis CI
c396ded395
feat(P02): SLICE-07 cohort aggregation pipeline — k-anon, hook, nightly
...
TASK-07-01: server/cohort/aggregator.py — aggregate_session with k-anon
write-time suppression (D-034, K_ANON_THRESHOLD=10), idempotent upsert,
7-day rolling window, multiple metrics (sessions_count, active_learners,
gate_open_rate, median_mastery_score, rubric_criterion_means,
failure_mode_frequency, branch distribution). No PII in aggregates (D-031).
TASK-07-02: server/cohort/hook.py — on_session_end fire-and-forget (D-054),
no-op when no Postgres, failures log + nightly reconciles.
TASK-07-03: server/cohort/nightly.py — NightlyScheduler in-process asyncio
loop, 03:00 CT (America/Winnipeg approx), reconcile from mastery_gate_events,
R-DASH-04 failure handling.
TASK-07-04: session_recorder.py — chain aggregation hook after mastery flow
via asyncio.create_task (parallel, off voice path, D-054).
TASK-07-05: tests/test_cohort_aggregation.py — k-anon threshold (9/10/11),
idempotent, 7-day window, metrics, no PII.
TASK-07-06: tests/test_cohort_nightly.py — scheduler timing, reconciliation,
hook-failure+nightly recovery, R-DASH-04.
G-038 (binding): differencing-attack test — 10 learners window A, 9 in B,
verify dropped learner cannot be isolated (B suppressed, value=NULL).
---ci---
project: praxis
phase: 2
milestone: v0.4
status: execute
persona: backend-engineer
task: 07-01..07-06
requirements:
covered: [REQ-MT-02, REQ-NFR-DASH-02, REQ-NFR-DASH-01]
---/ci---
2026-08-04 02:01:06 +00:00
Praxis CI
00e39a3f85
feat(milestone): merge phase/01 operator-foundation → milestone/v0.4-operator-tier
...
Phase 1 complete — Operator Foundation:
- Postgres 16 in Docker-in-LXC (asyncpg pool, 5-table schema, PgStore, migrations)
- Operator auth (argon2id, signed stateless cookies, slowapi 5/min rate limit)
- VC issuer key migration SQLite→Postgres (archive-before-active, R-VC-MIG-01)
- Operator bootstrap CLI (create-operator.py, idempotent)
- Backup cron script + G-008 restore drill
- Graceful degradation (server starts without Postgres)
- 272 tests pass, 33 skip (Postgres-requiring), 0 fail
---ci---
project: praxis
phase: 1
milestone: v0.4
status: complete
requirements:
covered: [REQ-MT-01, REQ-AUTH-01, REQ-NFR-AUTH-01, REQ-NFR-MT-01, REQ-MT-02]
partial: []
---/ci---
2026-08-04 01:41:06 +00:00
Praxis CI
813bd586d6
docs(milestone): merge v0.3-mastery-scoring → main
...
v0.3 milestone merged to main. Mastery scoring + competency rubrics +
verifiable credentials (formative-tier) shipped. 13/13 REQ-IDs covered.
Next milestone: v0.4 (operator tier — cohort dashboard + auth + Postgres).
---ci---
project: praxis
phase: 2
milestone: v0.3
status: complete
milestone_complete: true
milestone_merged_to_main: true
---/ci---
2026-08-04 00:14:59 +00:00
Praxis CI
f04b9b3588
feat(P01): SLICE-01+02 — Dockerfile, .dockerignore, docker-compose.yml, FastAPI StaticFiles, PRAXIS_DB_PATH env (G-102 fix)
...
SLICE-01 (lead-developer): multi-stage Dockerfile (node:22-slim→python:3.12-slim),
.dockerignore (excludes secrets/node_modules/.git), docker-compose.yml
(port 8789, SQLite volume, env injection for all voice-service vars)
SLICE-02 (backend-engineer+data-engineer): FastAPI mounts client/dist as
StaticFiles at / after API routes (D-023, REQ-DEPLOY-13).
G-102 MUST fix: db/store.py + db/migrate.py now read PRAXIS_DB_PATH
from env so the Docker volume mount persists SQLite data.
G-105 FIX: Dockerfile copies pyproject.toml before source (pip install
layer cached, source changes don't invalidate).
REQ-DEPLOY-01, 02, 13, 16 covered.
---ci---
project: praxis
phase: 1
milestone: v0.2
status: execute
slice: 01-02
wave: 1
---/ci---
2026-08-01 14:16:39 +00:00
Praxis CI
fbd6602814
docs(milestone): complete v0.1 foundation
...
---ci---
phase: 0
milestone: v0.1
status: complete
---/ci---
2026-08-01 13:32:48 +00:00