---ci---
phase: 7
milestone: v0.3
status: review
lessons:
- P0 CORS: allow_methods lacked PUT while the build surface writes files with PUT — every cross-origin Save failed preflight; pinned with tests/api/test_cors.py
- P0 ingest leak: queue-overflow flood path returned without the disconnect sentinel, parking the drainer forever (one leaked task-set per flooded trace); sentinel now always enqueued, real-server regression test added
- P1 perf: flood cap counted rows via len(get_trace(...)) — O(trace) per append, O(n²) per session; TraceStore.count() (COUNT(*)) added and wired
- P0 security: file routes followed exec-planted symlinks out of the workspace bind; _resolve_in_workspace refuses escapes (422), read/write now 404 on unknown sandboxes (was 500)
- P1 security: WS ingest accepted any browser Origin (CORS middleware does not cover WS); localhost dev origins + no-Origin (capture agent) allowed, others 1008
- P1 correctness: use-sandbox-session leaked a created sandbox on any mid-start failure (per-learner cap 1 → all retries 429 forever); failed starts now destroy what they created
- P2 testing: reconnect-flush test killed mid-burst (nondeterministic under load, reproduced on pre-change code); now waits for server-side observation of the pre-kill burst — the underlying one-line replay-margin/ACK gap is documented for v0.4
- maintainability: grading-store/templates/grading.ts docstrings claimed grading is variant-blind (stale pre-P4 text) — updated; ARCHITECTURE.md referenced nonexistent voice/openai_audio.py; dead if TYPE_CHECKING: pass blocks removed
---/ci---
Trivial P0 fixes (this commit): two docstrings claimed things the wiring
does not do. variants/__init__.py said the package 'never imports agents/'
— false since Wave 2: generator.py holds the module-direct
agents.structured import (the sanctioned D-020 shared defense, same
exception as grading/engine.py); docstring now states the real boundary.
templates.py (header + RubricAnchors) claimed rubric anchors are 'used by
grading context' / 'shipped to the grader' — false in the current wiring:
GradingEngine is variant-blind (variant_seed=None; no variant lookup; no
anchor consumption; render_trace_digest takes only the digest). Docstrings
now tell the truth and name the follow-up.
Verification (four layers, evidence in the phase report):
- Structural: AST boundary audit clean — variants/ has zero api/fastapi
imports; only sanctioned agents.structured + llm/prompts/config/store.
- Behavioral: variants slice 41/41 green; full suite 324/324 green;
pnpm typecheck 7/7 green (forced, no cache). Live app probes confirmed
distinct learners -> distinct statements/seeds/task_ids at the API
level, cache hit = zero LLM calls, deterministic fallback (calls==2),
a-5 fairness envelope test green, sha256(template|learner|milestone)
seed derivation verified byte-exact against the spec formula.
- Security: secrets scan over the P04 diff (b52bef9..4acffac, 17 files
+2100/-1) clean — no key/token/password assignments, no URLs, no key
shapes. Prompt-injection surface bounded: the variant prompt carries
only template skeleton/title/id + seeded slot values — no learner id
or user-controlled content reaches the LLM. Empty learner_id -> 422.
- Quality: ruff clean; AI_MODEL env override verified live; tests are
mock-only (MockProvider family, zero network imports).
Must-Haves: 5 of 6 SATISFIED. NOT satisfied: MH#4 half — anchors are
present per template and a-5-testable, but NOT shipped to the grader
prompt context (non-trivial cross-module wiring: engine + prompt
signature + lifespan ordering — grading engine is built before the
variant store exists; reported as P1, not fixed here).
---ci---
phase: 4
milestone: v0.3
status: verify
requirements:
covered: [REQ-3-005]
partial: [REQ-3-005]
lessons:
- Docstrings that describe a must-have's target state ('shipped to the
grader prompt') read as done in review — verify wiring, not words:
grep the consumer side (grading/) before believing the producer side.
- main.py builds GradingEngine before variant_store exists; any P4/P6
anchor-shipment fix must reorder lifespan construction or inject the
variant store into the engine after the fact.
- Params distinctness is parametric (280-64908 combos per template) —
two learners CAN draw identical params (~15% at 10 learners on the
tightest template); distinctness is proven via seeds/task_ids/statement
embeddings, which is what the must-have actually requires.
---/ci---