From 404db7e5beb08d3f45a87ae1074638cab90734db Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 22 Sep 2026 02:33:39 +0200 Subject: [PATCH] Record Soul verdicts on play agent Cut 2: P2.2 falsified The port's HTTP client honours proxy environment variables and follows redirects. Loopback-only requests left the machine, and a proxy URL's userinfo was sent as proxy-authorization. PA.f16 to PA.f22 are triaged to fix in one batch after Cut 3 lands; PA.f21 is recorded. Co-Authored-By: Claude Opus 5 --- .../architecture/ghostlight-play-agent-cut.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/architecture/ghostlight-play-agent-cut.md b/docs/architecture/ghostlight-play-agent-cut.md index dd830ac..4f92709 100644 --- a/docs/architecture/ghostlight-play-agent-cut.md +++ b/docs/architecture/ghostlight-play-agent-cut.md @@ -1476,6 +1476,49 @@ Interpreter, the elaborator sweep and lenses. - `docs/architecture/ghostlight-stock-lenses.md:61` (Dungeon's elaboration pool). - The handoff line about concurrent elaboration under Dungeon's ceiling. +- **PA.f16 (introduced by Cut 2, high, fix):** `local_inference.rs:130-133`. + `reqwest::Client::builder()` keeps the environment proxy. With `HTTP_PROXY` + set, the whole request went to the proxy, and a proxy URL carrying userinfo + added `proxy-authorization`: a credential, from the environment, sent by + Ghostlight. The fix is `.no_proxy()`, plus a test that sets a proxy variable + and asserts that the endpoint is hit directly. +- **PA.f17 (introduced by Cut 2, medium, fix):** redirects are followed and + the body is re-POSTed; Soul's probe saw a 307 carry the prompt to another + port. The fix is `redirect::Policy::none()`, with a 3xx as a fault, and a + test. +- **PA.f18 (introduced by Cut 2, medium, fix):** `local_inference.rs:77-78`. + An explicit `"tool_calls": null` fails to decode and becomes + `IntegrityViolation`, which quarantines the lane. A missing `id` does the + same. The fix is to accept null and absent for both, where the reply's + shape allows it. +- **PA.f19 (introduced by Cut 2, low-medium, fix):** an empty local or SDK + prefix matches every model and silently takes the connector's lanes. Open + must refuse an empty prefix. Duplicate call ids in one reply pass the port + and the evaluator. Because the port owns the reply's integrity, the port + refuses them as `integrity_violation`. The SDK and connector ports are + outside this fix, as recorded under PA.f21. +- **PA.f20 (introduced by Cut 2, low, fix):** duplication and placement. + - `local_inference.rs:103-113` copies `call_id_is_valid` and + `tool_name_is_valid` from `sdk_inference.rs:579-590`; hoist one copy. + - Fold `two_tool_call_reply` into `tool_call_reply`. + - The loopback check lives in `open_local_port`, not in the port's + constructor, and the constructor's doc claims the opposite. Move the check + into the constructor so it holds by construction. + - A request with no tools must omit `tools` and `parallel_tool_calls` + instead of sending an empty array. +- **PA.f21 (introduced by Cut 2, low, recorded):** + - The receipt binds identity, `response_id`, finish reason and usage, not + the returned events. That is the SDK receipt's shape, as specified. + - `provider_request_sha256` hashes the prepared request, not the lowered + body that is actually sent. + - Duplicate call ids are unchecked in the SDK and connector ports. +- **PA.f22 (introduced by Cut 2, low, fix):** tests are missing for + lowering. Each of the following survived a mutation, and each gets a test + that fails under it: + - the tool declarations sent (S2); + - the stripped `model` (S3); + - the foreign-caller and expiry gates in `infer` (S4); + - the receipt digest, which must change with `response_id` (S6). - **PA.f8 (pre-existing, info):** `PreparedInference::prepare`'s doc says consumers may implement ports outside the crate; probe B shows they cannot read the request. Cut 2 removes the need for one; the comment is corrected