From 8cc92e2f88ca6327a21cd7323de7c41cbe3dd028 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 23 Sep 2026 03:39:49 +0200 Subject: [PATCH] Record Soul on Cuts 12 and 13 (PA.f176-PA.f182) Co-Authored-By: Claude Opus 5 --- .../architecture/ghostlight-play-agent-cut.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/docs/architecture/ghostlight-play-agent-cut.md b/docs/architecture/ghostlight-play-agent-cut.md index 3605bfc..72b7504 100644 --- a/docs/architecture/ghostlight-play-agent-cut.md +++ b/docs/architecture/ghostlight-play-agent-cut.md @@ -3456,6 +3456,80 @@ Interpreter, the elaborator sweep and lenses. schemas rather than one, plus this file's doc-comment convention. - **Hands reported no mutations.** Soul checks every rule's call site itself, as it did after Cut 10. +- **Soul on Cuts 12 and 13** (Opus, 2026-09-23, against `a978a82`, + Windows). + - **Verdict: ready for the playtest with one preflight**, and the seed + leg cannot be judged at all. Cut 13's token rule is real, + load-bearing and better tested than Hands claimed: every mutation + killed exactly the tests that should die, and Soul's own PA.f170 + attack is refused through the route. + - Held: the token binds an answer to its question; tokens are distinct + per question; every question-opening path mints; cleared on answer and + on fault; a legacy upgrade mints for a recovered open question; the + token is opaque, appears exactly once in the served document, and is + absent from the rendered DOM and every attribute through the real + pinned lowering; the merge rule; the tests read the token from the + served surface; the strict canonical check still covers current rows; + the no-node run is **163 passed, 4 skipped**. + - **Growth judged:** the claimed ~150 lines is 93 outside the test + module, of which 29 are code. Two legacy schemas and a uuid mint for a + real fix. It earns its keep. The doc comments re-litigate the deleted + comparison, which is style, not liability. + - Triage: PA.f176 to PA.f182 are Cut 14. +- **PA.f176 (Cut 12, high for the gate, not the daemon):** + `eve_client_bridge_is_available()` checks `node` and a committed `dist/` + file, which is always present, and never checks the bridge's actual + runtime dependencies. On a clean checkout with node on PATH: **159 + passed, 4 failed** on `jsdom`, and still 4 failed on `ajv` after running + the runbook's own setup line. That is PA.f169's shape returning from the + dependency side: a prerequisite that fails the gate instead of skipping + it. **Fix:** the check proves the dependencies it needs, and skips + otherwise; the runbook's setup line installs both packages. +- **PA.f177 (Cut 12, moderate, preflight blocker):** PA.f172 fixed half the + population. `applied_keys` and `fault` were added to `PlayTurn` without + `#[serde(default)]` while the schema stayed `v1`, so an older `v1` row + fails to decode even through `upgrade_legacy_row`: "missing field + `applied_keys`". The play table then does not open, the card vanishes, + and only `playStatus` says why. + - **Ruling (Self, under the standing go):** the legacy reader is + genuinely lenient — every field added since `v1` defaults when a legacy + row is read. A row that still cannot be read is **retired to a sidecar + file and play starts fresh**, reported where the operator sees it. A + daemon must never answer an unreadable row by quietly having no play + surface. +- **PA.f178 (Cut 13, low-moderate, fix):** with no question open, + `payload.answer_token` is never read, so a stale tab's second click + opens a **new turn** with text written for a question that is gone, and + spends an inference budget doing it. **Fix:** an answer token arriving + with no open question is refused ("that question is closed"), and never + opens a turn. +- **PA.f179 (Cut 13, low, fix):** `open_question_of` and the gate both + fabricate an empty token through `unwrap_or_default()`, so a missing + token degrades to "the empty token is valid". **Fix:** compare the + `Option`s directly, so absence refuses. +- **PA.f180 (Cut 13, low, fix):** `unwrap_bindings` flattens the action + channel and the bindings channel into one map, so the server cannot tell + them apart, and a token sent as a captured binding is accepted today. + Invariant 8 then rests entirely on `eve.rs` never emitting the token as a + control. **Fix:** keep the channels distinguishable at the ingress, and + refuse a token that arrives as a binding. +- **PA.f181 (Cut 13, low, recorded):** `QuestionId`'s match is exercised by + one test that forges it, because the token refuses everything upstream. + It still covers the window between the runtime's read and `admit`. + **Kept**, with that narrowness stated where it lives. +- **PA.f182 (Cut 13, trivial, fix):** the runbook lists three node-driving + tests and warns that an unguarded fourth would break the gate. Cut 13 + added a fourth, correctly guarded, one commit after the list was + written. Add it. +- **The seed leg, plainly:** the only coverage asserts that the click + reaches the env-var read and is refused. Nothing past that line is + tested anywhere: not the vault open, not the sweep, not a model call, + not a committed patch. If the operator sets the variable and clicks + Seed, everything after that read is first contact, and a failure there + will look like a regression in this pass when it is untested ground. + Plan the playtest so a failed seed does not abort the play-loop legs. + **Also:** that refusal names a server environment variable to the player; + it becomes a player-safe message with the detail logged (invariant 8). - **PA.f77 (introduced by 7c, medium, fix):** `table_view_prints_only_a_subjects_own_granted_affordances` (`table.rs:2886`) fails about one run in four. Its unscoped