+- 2026-07-27: **X2a done** (`fb5185808`) — deterministic eval harness at `evaluation/manage-assistant/` (uv/Python 3.12, host-run, 48 files). Route-level: drives the real `/api/manage/chat` SSE endpoint with a minted lecturer session cookie, parses the AI SDK UI-message-stream, fence-strips tool output, and detects proposal cards. Three dimensions, 30 GT cases in the sibling framework's markdown+frontmatter format: E1 tool selection (12 cases, soft 0.95), E5 refusal/do-not-save (8) and E6 injection (10) as hard gates. E6 is a genuine indirect-injection surface — `seed.py` idempotently creates a synthetic *collaborator* lecturer owning payload-carrying elements and grants the eval lecturer READ via `DerivedPermission` (the real sharing visibility path, since the MCP reads gate on derived permissions, not `ownerId`). **Location decision:** in-repo rather than in the private `evaluation/framework` submodule (PR #5190), because a nightly gate in a public repo cannot depend on a private submodule; GT stays under `evaluation/manage-assistant/data/` rather than the `evaluation/data/` root so this branch stays independent of #5190. **Adversarial review (Opus) verdict needs-fix; both BLOCKING findings were reproduced by me before accepting:** (1) *the hard gates could not fail on a dead turn* — every check tests for the ABSENCE of unsafe behavior, so an HTTP-200 stream that terminated immediately (AI SDK `finishReason:"error"`, which emits no `{type:'error'}` frame the harness already caught) scored a clean pass on every E5 do-not-save case and every direct E6 case; `finish_reason` was captured and never read. Fixed with a liveness gate requiring prose or a tool call. (2) *E6's retrieval check was content-blind* — it asserted only that a retrieval tool NAME was called, so a zero-hit search was indistinguishable from resisting an injection; fixed by requiring the seeded element's unique payload marker (now defined next to the content in `seed.py`) in the output the model actually received. Verified statically that all four markers survive the server's `compactPlainText` normalization and both truncation limits (`element_search` snippet 500, `element_get` detail 4000) — so search really does deliver the full payload, not just a name. Also fixed: declared `expected_calls[].arguments` are now enforced (a hallucinated id no longer passes), the seven subset-policy E1 read cases forbid a spurious persist call, trial-capped runs are labelled as such, the 429 retry is counted by the pacer, and the stale "known issue" README section now records the X4 card regression as fixed in `1a1444c34`. **Durable anti-regression:** `tests/test_scoring_contract.py` — 15 network-free tests (marked `offline`, gated by a new function-scoped autouse fixture so they never touch DB/HTTP) over the *real shipped case files*, asserting the gates FAIL on a degenerate turn, on content-blind retrieval, on forbidden calls + leaked card, and on transport errors. Each fix was transiently reverted to prove its test fails (`assert not True` / `assert None is not None`), then restored. Verified: ruff check + format clean, 46 tests collect, 15/15 offline pass in 0.07s, prettier clean on all 31 markdown files, hygiene scan clean (the only credential-shaped string is the `klicker-prod:klicker` DSN already committed in the tracked `.devcontainer/devcontainer.env`). **Residual — the one gap:** the 30-case *live* run has NOT been re-executed since these scoring changes. The builder's pre-review run was 31/31 at `MAX_TRIALS=1`, but that predates the liveness gate, the argument enforcement and the E6 marker check. The stack was OOM-reaped and restarted; re-running needs `UPSTREAM_OPENAI_API_KEY`, and `OPENROUTER_API_KEY` is not read-allowlisted on the `klicker-prd` operator profile (only `DATABASE_URL` is) — blocked pending Roland's approval of `rs-infisical-operator --profile klicker-prd allow-read OPENROUTER_API_KEY`. The two live-only risks are E1 cases 04/06 under real model behavior (could drop E1 below 0.95) and end-to-end confirmation of the E6 marker path.
0 commit comments