E. Codex client tool-call round trip (security-critical) #137
Labels
No labels
accessibility
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
P1
P2
P3
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Creatidy/scarcity-router#137
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Child E of #132. Security-critical. The primary interoperability blocker: client-owned tool calls on the Codex subscription source. Investigation first; implementation ONLY if semantics can be preserved.
Required semantic boundary
Hard invariants
commandExecution/fileChangetools are never confused with client-owned OpenAI function tools.Investigation (before any coding)
dynamicTools/item/tool/call(or successor mechanisms) can express: exact client tool declarations per turn; Codex pausing for a client tool; the client result returning into the SAME turn/thread; and how cancel/interrupt interacts.Use the simpler design only if semantic equivalence is proved.
If viable
tool_calls/tool_resultsevidence cells only to the level actually established.Review
Independent GLM-5.3-Flash review + mandatory Daybreak (
gpt-daybreak-blue-latest) security review when available; if unavailable:SECURITY_REVIEW_UNAVAILABLE, gate stays open, PR paused at the human decision point. UX assessment mandatory.Complexity boundary
Unknown until investigation concludes; investigation itself is bounded at 90 min wall clock. Implementation, if authorized by evidence, is a separate frozen-scope amendment of this issue before coding starts.
Non-goals
No
/v1/responsessurface; no parallel tool-call fan-out semantics beyond evidence; no executing client tools anywhere in Scarcity Router; no matrix weakening.Additive scope note (D-056, PR #147 — frozen scope text above unchanged):
D-056 pt 5 reframes what this child investigates — read "ZCode" in the frozen scope above as "the harness" throughout:
tool_calls/tool_resultsPASS/PARTIAL cells). The gap is specifically the Codex worker source, whose evidence records both cells UNSUPPORTED.Authoritative text: D-056 pt 5 in
docs/decisions.md; "Harness compatibility contract (D-056)" indocs/execution-surface.md.Tool-bridge investigation report — CURRENT runtime re-evidence (2026-09-28, UTC)
Verdict: TOOL_BRIDGE_VIABLE (conditional — constraints and unverified halves recorded below; implementation remains gated on a separate frozen-scope amendment of this issue before any coding, per the frozen scope).
Investigation only; no repository code, matrix or evidence cells were changed by this investigation. All probes were bounded, structure-only, on a fresh controlled
CODEX_HOMEunder/tmp(never the user home), with synthetic strings only: no account/auth method was sent, no turn was executed, no inference was possible (the probe home is unsigned-in and turns fail closed at upstream 401), and no quota was consumed. Output was sanitized to structure before recording.1. Current runtime established (do-not-assume-2026-09-20 satisfied)
codex-cli 0.155.0-alpha.16.3— U-001 VS Code extension discovery layout (openai.chatgpt-26.917.62051-linux-x64,bin/linux-x86_64/codex-package.jsonlayoutVersion: 1,variant: "codex", static musl ELF). This is NEWER lineage than both the 2026-09-20 probe (0.154.0-alpha.6.2) and the pinnedrust-v0.155.1schema tag.codex app-server generate-json-schema): 39 top-level + 271 v2 files. The generated bundle reflects the STABLE surface: experimental-gated fields are omitted (proved below), so schema absence alone is not evidence of removal.2. Findings against the frozen investigation questions
F1 — The client-tool mechanism exists on the current runtime and is
experimentalApi-gated (three evidence classes agree).thread/startwithdynamicToolsWITHOUT the capability → JSON-RPC error-32600, messagethread/start.dynamicTools requires experimentalApi capability(field RECOGNIZED and gated — not an unknown field; the same probe shows genuinely unknown fields are silently ignored, i.e. parsing is lenient, so this error is positive identification). WITHcapabilities.experimentalApi: true→thread/startaccepted.dynamicToolsonthread/startrequirescapabilities.experimentalApi = true; per-invocation flow:item/started(dynamicToolCall, statusinProgress) →item/tool/callserver request → client response with content items →item/completedwith final status/contentItems/success. It also states Codex persists dynamic tools in thread rollout metadata and restores them onthread/resume.ServerRequestcarriesitem/tool/call(DynamicToolCallParams:threadId,turnId,callId,tool,namespace?,arguments);DynamicToolCallResponse:{success: boolean, contentItems: [inputText | inputImage | inputAudio]};dynamicToolCallThreadItem:{id, type, tool, namespace?, arguments, status (inProgress|completed|failed), success, contentItems, durationMs}.F2 — Constraint: declarations are THREAD-scoped, not per-turn.
TurnStartParamshas nodynamicToolsmember (live probe:turn/startwithdynamicToolssilently ignores it — consistent with the lenient parse control; the stable schema omits it too). Exact per-turn tool sets are therefore expressible only when the tool set is stable for the life of the thread; a client that changes itstools[]between the declaration and the continuation cannot be honored exactly on the open thread → must be a typed fail-closed rejection, never a silent narrowing.F3 — Client-owned vs backend-native tools stay cleanly separated.
dynamicToolCallis a distinct ThreadItem type alongsidecommandExecution,fileChange,mcpToolCall,collabAgentToolCall. Nothing in the mechanism executes a client tool in Codex or in the worker: the adapter would RELAYitem/tool/callto the harness and return the harness's result.toolOutputonturn/start(stable) is now{name, namespace?, output}— name-keyed, nocallId.F4 — Implementation families. Family A (stateful suspended continuation) is the protocol-native design: the pause IS the pending
item/tool/callJSON-RPC request on the live stdio connection; the continuation IS answering it (callId-correlated) so the SAME turn/thread completes; docs confirm tools persist and are restored onthread/resume. Family B (stateless reconstruction) has two UNVERIFIED hinges: (a)turn/interruptbehavior while a server request is pending, and (b)thread/inject_itemsfidelity for assistant function-call history (the 0.154-era adapter already rejected assistant-side tool history replay). Family B is NOT proved semantically equivalent, so under the frozen rule ("use the simpler design only if semantic equivalence is proved") family A is the only selectable design on this evidence.F5 — Feature-flag surface.
experimentalFeature/list(live, 147 entries) exposes NO dynamic-tools flag: gating is purely theexperimentalApicapability. The D-019/U-011 generation-aware containment (parse per probed generation, safe disable on contract change) remains the real defense.F6 — Cancel/interrupt.
turn/interrupt→TurnStatus: interruptedis documented and schema-present. Its interaction with a PENDINGitem/tool/callis UNVERIFIED live (needs a signed-in turn). Safe resolution regardless of behavior: the worker owns the app-server process lifecycle (bounded terminate/kill/reap, already implemented) — process teardown resolves any ambiguous pending state without fabricating a tool result.3. Hard-invariant check (all preservable)
CODEX_HOMEisolation, no MCP/plugins/apps, sandbox/approval discipline: unchanged by the bridge.4. What remains unverified (honest gaps)
Live turn-level behavior — an actual pause on
item/tool/call, same-turn resume timing, interrupt-while-pending, sequential multi-tool rounds — requires a signed-in subscription home and consumes quota; it is the sameEXTERNAL_ACCEPTANCE_GATE: LIVE_CODEX_SUBSCRIPTIONclass as the existing M10 gate and stays open until that acceptance. Everything above is mechanism-level evidence (probe + official doc + binary-pinned schema), not a live round trip.5. Consequent evidence levels (for the future amendment, not changed now)
tool_calls:UNSUPPORTED (stable surface)→PARTIAL (conditional)— experimentalApi-gated dynamic tools; thread-scoped declarations (mid-round-trip tool-set changes rejected); live pause/resume pending the M10-class gate.tool_results:UNSUPPORTED→PARTIAL (conditional)under the same conditions (client result returned via theitem/tool/callresponse).D-060 implementation note — Family A landed on
gateway/codex-client-tools(READY_FOR_REVIEW pending reviews)TOOL_BRIDGE_VIABLE accepted; the Family A suspended-turn continuation is implemented. PR opens against
develop(base6fec46c, unchanged at commit time — re-verified immediately before push). Frozen scope text above is unchanged; this note is the additive implementation record.Implementation summary
execute_tool_call(worker→server) andexecute_tool_result(server→worker). Server accepts (3,2,1). v1/v2 sessions carrying v3 vocabulary = fatal; a v1/v2 worker is NEVER tool-continuation-capable; ordinary non-tool execution on old workers unchanged.item/tool/callis HELD (never refused inline) and relayed; the harness's ordinaryrole:"tool"request resumes the SAME turn. Reconstruction/replay/rerouting/restart do not exist; pre-delivery loss =404 continuation_not_found, post-delivery loss = honestambiguous_execution_state.tool_call_id= server-issuedsrct-+ 128 random bits; CodexcallId/thread/process ids never leave the worker. Exactly-once, single-subscriber claims; replay/double-delivery =409 continuation_already_resolved(client-scoped bounded tombstone ring); foreign client = indistinguishable404; any drift (model, effort, tools fingerprint, tool_choice, conversation-prefix fingerprint, assistant echo, extra tool messages, >4 MiB result) =400 continuation_mismatch. Only bounded digests stored — no prompts, arguments or results.execution_time_limit_seconds); the worker's existing deadline timer + the gateway reaper tick cancel the suspended turn at expiry; no per-leg reset, no new client-expandable policy.initializewith empty capabilities byte-for-byte (pinned by trace); ONLY tool-bearing requests opt intoexperimentalApi: trueand declaredynamicToolsverbatim (no renaming/truncation/weakening; bounded).tool_choicemodes the surface cannot enforce (required/named/none) refuse before execution;parallel_tool_callskeeps its existing refuse-not-drop treatment; sequential rounds bounded (32/turn), one pending call at a time.successmapping (recorded, D-060 pt 6): a validrole:"tool"message maps to{"success": true, "contentItems":[{"type":"inputText","text":<content>}]}— text-only v1; failure signalling stays the harness's content-level concern; nothing inferred from text, nothing fabricated.completed+ usage-freeunknowncall observation +suspended_for_client_tool; terminal leg =completed+ the turn's SINGLE usage-bearing observation +continuation_resumed, repeating the original decision/target identity. One provider call is never reported as two.Evidence/capability changes (conservative):
tool_calls/tool_resultsUNSUPPORTED → PARTIAL (dated 2026-09-28: mechanism evidence + deterministic round trip; the live signed-in acceptance stays behind theLIVE_CODEX_SUBSCRIPTION-class gate).CODEX_SURFACE_CAPABILITIES.tool_calls→ true. Tool eligibility additionally requires the live worker session to negotiate v3.Gates (full run on the exact head
97441a6): complete test suite 2303 tests OK (incl. the new full-stack Family-A acceptance: non-streaming + streaming + negative matrix; adapter/endpoint/protocol/registry/coordinator unit suites);basedpyright0 errors / 0 warnings / 0 notes; guardrails OK;git diff --checkclean; single commit overorigin/develop, no merges. #143 note: one load-sensitivetest_scenario_11cancellation flake fired once in a full run (pre-existing, documented in #143; the test passes on rerun and exercises no continuation code — not folded into this PR).Reviews: independent exact-head review + the mandatory
gpt-daybreak-blue-latestsecurity review requested on the frozen head. If Daybreak access is unavailable:SECURITY_REVIEW_BLOCKED, gate stays open at the human decision point.DEFERs: (1) live signed-in acceptance (#141-class gate) — PARTIAL, never PASS until then; (2)
required/namedtool_choicesupport would need an evidenced upstream enforcement surface — refused today; (3) dispatch-time (not pre-ranking) v3-worker eligibility — a v3-less selected worker fails closed with a typed backend failure; pre-ranking awareness would need live worker-protocol facts in the matrix (#106 territory); (4)continuation_ttl_secondsas a separate administrator knob — the admission deadline bounds the turn today; revisiting is cheap if a real workload needs a longer tool budget than its inference budget.Review round 1 remediation — new exact head
8b02bceThe independent exact-head review returned CHANGES_REQUESTED with two evidenced MERGE_BLOCKERs (both reproduced by the reviewer against the repo's own fixtures). Both accepted and fixed in one remediation commit, with regression pins:
role:"tool"result can trigger continuation/tombstone/srct--namespace logic. A conforming full-history harness's follow-up turns (which necessarily still carry the consumed tool result mid-history) route normally — a completed round can no longer 409-poison the conversation. Pinned inDetectionShapeTests.test_follow_up_turn_after_a_round_is_not_a_replay.srct-result silently re-routed (blocker). Within the delivery shape, a gateway-issued token with no live record and no tombstone (gateway restart, registry bound) now yields the typed404 continuation_not_found— never a fresh model selection. Additionally, when registration hits the pending bound, the already-suspended worker turn is cancelled immediately instead of orphaned until its deadline. Pinned intest_delivering_result_to_a_lost_suspension_is_not_found.409on very-late replays and the registry slot is freed as the round advances. Pinned.Remaining review DEFERs (left recorded, not blocking): (5) the continuation leg skips the input-context admission pre-check (bounded by the fingerprint-matched prefix + 4 MiB result cap + HTTP body limit; administrator global body/context ceilings still apply at the HTTP layer); (6)
thread/startprotocol errors on tool turns all map todynamic_tools_runtime_refused(misleading-reason-only, error path); (7) aclose()race can append an owner-less tombstone entry (honest 404 instead of 409 in that narrow race).Gates on
8b02bce: full suite 2307 tests OK;basedpyright0/0/0;git diff --checkclean;origin/develop..HEAD= 2 commits (feature + one remediation), no merges. The reviewer's narrow final verification on the new exact head is requested. Daybreak security review:SECURITY_REVIEW_BLOCKED—gpt-daybreak-blue-latestis not available in this environment; per policy no substitute model closes that gate, so the PR stays paused at the human decision point. Live acceptance:LIVE_TOOL_BRIDGE_NOT_VERIFIED(cells stay PARTIAL; note: a signed-in eligible Codex source exists, but exercising it would require either copying credentials or injecting into the production worker — both forbidden — so the owner-run acceptance remains open).Final status — head
71d0c79(decision renumbered D-060 → D-062)developcarried neither number; whole-branch textual substitution, rationale recorded in the decisions.md entry). Earlier comments above that say "D-060" refer to this same decision.8b02bce: VERDICT: APPROVED,UX impact: none(all reproductions empirically confirmed fixed; exactly-once preserved; no new issues in the remediation diff; one non-blocking note: the registration-bound cancellation path was verified manually but has no committed test pin — added to the DEFER list).71d0c79: full suite 2307 tests OK;basedpyright0/0/0; guardrails OK;git diff --checkclean;origin/develop..HEAD= 3 commits (feature, one remediation, renumbering), no merges.SECURITY_REVIEW_BLOCKED— unavailable in this environment; per policy the gate stays open at the human decision point; no substitute model closed it.LIVE_TOOL_BRIDGE_NOT_VERIFIED(cells stay PARTIAL; owner-run acceptance open).Review round 2 — remediation landed; one finding requires an OWNER DECISION (packet below)
All six findings were accepted. Five are fully remediated with regression pins; finding 1 hit the frozen STOP rule and is resolved by stopping the semantic sub-part — the round trip deliberately does not complete on this branch until you decide the packet below.
Evidence established for finding 1 (verified first-hand)
openai/codex @ 36650394,codex-rs/protocol/src/protocol.rs:/// Whether the tool call succeeded./pub success: bool— a REQUIRED plain bool, no#[serde(default)], not optional. No "unknown" encoding exists.role: "tool"messages are text-only with a closed field set (unknown fields rejected at ingress) — no success/failure fact.representative_client_fixture.py) contains no continuation leg at all — no signal there either.Conclusion: no faithful success/failure signal exists. Per the STOP rule, the previous
success: truemapping and its D-062 rationale are RETRACTED (D-062 pt 6 rewritten; the earlier text is not stated as upstream fact anywhere).Remediations (head will be frozen after gates; regressions in
tests/test_gateway_continuation_coordinator.pyand the adapter/e2e suites)tool_result_success_unresolved), the helditem/tool/callis never answered,tool_resultscell returns to UNSUPPORTED (dated),tool_callsstays PARTIAL (declarations/suspension/relay only), and the e2e pins the STOP (notool_call_answerevent ever traced).PendingContinuationretains the assistant echo as a bounded DIGEST (assistant_tool_calls_digest); raw arguments/result/prompt content are gone from registry state; retention test inspects a live record for the secret markers.worker_continuation_unavailable) gates tool-bearing requests BEFORE ranking on the owning worker's actual protocol-v3 negotiation — regression: two identical worker routes, only B v3 ⇒ B selected and dispatched, A never chosen; empty/absent fact ⇒ 503 fail-closed; non-tool requests still use the v2 worker; the D-043 matrix key is untouched (the fact rides the availability stage via aRouteRequestlive-input member, wired from the worker endpoint's dispatch-authorization path).tool_callframe or the response is produced. A bound failure cancels the suspended backend and fails typed with NO token exposed (non-streaming and streaming regressions; ordering pinned).pending_count()returns to 0 immediately on each path (tested per-case);expire_dueon a RESUMING record also cancels + tombstones instead of silently dropping.403 unauthorized_target, the record closes cancelled, and the suspended backend turn is interrupted — never rerouted. Regression: authorized → suspend → administrator blocks the resource → same client's result is NOT delivered. D-059/scarcity remain non-authoritative for live turns.MAX_FRAME_BYTEScomment no longer claims the stale 1 MiB body default and explicitly disclaims headroom over the D-058 16 MiB body limit.OWNER DECISION PACKET — the upstream
successsemantic (#137 gate)The bridge cannot complete a round trip until you choose how
successis answered. Options:success: truefor every well-formed delivered result, recorded as an EXPLICIT owner decision that consciously diverges from the upstream "tool call succeeded" semantics (rationale available: Chat Completions pipelines treat failure-signalling as content-level; the harness chose to return rather than error). Cells then return to PARTIAL and the bridge ships as-is.No code change is required to choose (b) — it is the shipped state. Choosing (a)/(c) is a one-line mapping + evidence-cell flip with your provenance.
Status: all repository gates re-run on the remediation head before the freeze; then a fresh independent normal review on the exact head. Daybreak remains
SECURITY_REVIEW_BLOCKED(unavailable in this environment); live acceptance remainsLIVE_TOOL_BRIDGE_NOT_VERIFIED;tool_calls/tool_resultsremain PARTIAL/UNSUPPORTED respectively until (a)/(c) is decided and #141's layered acceptance runs.Review round 2 — remediation pushed; branch rebased; frozen head
babf3f9All six findings accepted and resolved on the rebased branch (base is now
59538e9— D-061/#157 merged while this branch was in flight; the rebase re-applied the D-060→D-062 renumber scoped to this decision's section only, so D-061's own reference to the D-060 reservation is preserved verbatim).F1 — STOP implemented (no invented mapping). Upstream verified first-hand at
36650394(protocol.rs:/// Whether the tool call succeeded.— required plain bool, no default). The generic text-onlyrole:"tool"message and the representative capture carry no success fact. The adapter now REFUSES to fabricate the answer: a delivered result fails the attempt typedtool_result_success_unresolved, the helditem/tool/callis never answered (e2e pins: zerotool_call_answerevents traced),tool_resultsreturns to UNSUPPORTED (dated),tool_callsstays PARTIAL (declarations/suspension/relay only), and D-062 pt 6 is rewritten — the earlier interpretation is retracted as an upstream-fact claim. Owner decision packet posted above (options a/b/c); option (b) is the shipped state, no code change needed.F2 — digest-only state:
PendingContinuationstoresassistant_tool_calls_digest(bounded sha256); retention test inspects a live record and proves raw arguments/results/prompts are absent.F3 — live v3 eligibility, pre-ranking: a new availability-stage fact (
worker_continuation_unavailable) excludesworker_bridgedcandidates whose owning worker session has not negotiated v3, BEFORE ranking, driven by aRouteRequestlive-input member wired from the worker endpoint's dispatch-authorization path. Regressions: two same-identity worker routes with only B v3 ⇒ B selected and executed (A excluded at availability); empty set or absent fact ⇒ 503 fail-closed; non-tool requests still use the v2 worker; the D-043 matrix key is untouched.F4 — register-before-exposure: the adapter invokes a coordinator registration closure INSIDE the dispatch, before the streamed
tool_callframe or the response body exists; the invariant "observable ⇒ registered" now holds for racing clients by construction. Bound failure cancels the suspended backend and returns a typed failure exposing no token (non-streaming + streaming regressions).F5 — terminal cleanup on every exit: all post-claim paths (permanent, ambiguous, timeout, lost, cancelled result, failed result, unexpected, client disconnect) close the record terminal —
pending_count()returns to 0 immediately on each path (per-case tests);expire_dueon a RESUMING record also cancels + tombstones.F6 — authority recheck: the client's CURRENT grant is evaluated against the EXACT original target through the SAME M02 authorization stage admission uses, before delivery. Revocation ⇒ typed
403 unauthorized_target, record closed cancelled, suspended backend interrupted; no scarcity/campaign rerun. Regression: authorized → suspend → administrator blocks the resource → same client's result NOT delivered.Cleanups: control-server banner prints the real protocol version (was hardcoded "v1");
MAX_FRAME_BYTEScomment no longer claims the stale 1 MiB default and disclaims headroom over the D-058 16 MiB body limit;_compatibility_failurenow actually returns the sorted-duplicate-free codes its docstring always promised (two same-code failures previously produced a duplicate pair that TargetExclusion's grammar rejects).Gates on
babf3f9: full suite 2317 tests OK;basedpyright0/0/0; guardrails OK;git diff --checkclean; rebased ontoorigin/develop=59538e9(4 commits, no merges). Fresh independent normal review dispatched on this exact head. Daybreak remainsSECURITY_REVIEW_BLOCKED(unavailable); live acceptance remainsLIVE_TOOL_BRIDGE_NOT_VERIFIED.Review round 3 — remediation pushed; new exact head
e232e39The fresh independent review of
babf3f9returned CHANGES_REQUESTED with one docs-only MERGE_BLOCKER and two defers (its summary of the implementation itself: "held up against every attack I threw at it" — state machine, exactly-once delivery, register-before-exposure, terminal cleanup, authority recheck, v3 gating, and the STOP all verified, including 27 custom adversarial probes).MERGE BLOCKER (docs-only, fixed): three prose sites still claimed
tool_resultsmoved to PARTIAL while the authoritative cell table says UNSUPPORTED. Corrected: D-062 point 8 now states the split truthfully (tool_calls→ PARTIAL,tool_resultsSTAYS UNSUPPORTED per point 6), the evidence-module docstring matches its own table, and the stage1 addendum heading/intro record the SPLIT.DEFER (fixed): a failed continuation admission now releases the adapter's local suspension handle on both legs — the bounded table can no longer starve under sustained registry-full conditions. Regression added: a v3-session bound failure asserts the local table is empty and the worker received the cancel.
DEFER (fixed): the duplicated D-062 numbering rationale folded into one bullet.
Gates on
e232e39: full suite 2318 tests OK;basedpyright0/0/0; guardrails OK;git diff --checkclean; baseorigin/develop=59538e9(rebase re-applied the D-062 renumber scoped to this section, preserving D-061's reservation reference); 5 commits, no merges.Standing gates: Daybreak security review
SECURITY_REVIEW_BLOCKED(unavailable in this environment; no substitute may close it); live acceptanceLIVE_TOOL_BRIDGE_NOT_VERIFIED;tool_resultsUNSUPPORTED /tool_callsPARTIAL pending the owner decision packet (options a/b/c above — option (b) "keep the STOP" is the shipped state and requires no code change).Owner decision implemented — exact head
ffe6da2The packet is RESOLVED per your option (a). The mapping ships as an owner-accepted LOSSY protocol translation recorded as an amendment to D-062 pt 6 (no new decision number; Reconciliation records the provenance and preserves the upstream fact "Whether the tool call succeeded" as Codex's native semantic).
Implementation: the answer-point STOP is removed; after ALL continuation validation (exact client, exact continuation, digest-validated echo, exactly-once claim, current hard-authority recheck, absolute deadline), the adapter answers the held
item/tool/callwith{success: true, contentItems: [{type: inputText, text: <verbatim role:tool content>}]}. Content is never inspected/parsed/reinterpreted;success: falseis never inferred; no client-specific branch exists.Evidence:
tool_calls= PARTIAL,tool_results= PARTIAL (live acceptance outstanding; no PASS claim anywhere).Stale-prose sweep (all listed terms searched across the final tree):
tool_result_success_unresolvedsurvives ONLY in D-062 pt 6 as the recorded historical supersession ("the interim STOP ... is superseded by this decision and removed"); every other STOP-era site (cells, module docstring, surface comment, execution-surface rows + section, addendum heading/intro/mapping/cell-state, pt 8, Confidence) now tells the one story: both cells PARTIAL, owner-approved compatibility mapping, live acceptance pending, no PASS. D-056's historical "UNSUPPORTED there" wording is left as history (its record is date-stamped; D-062 pt 8 documents the move).Tests restored: the full deterministic round trip is pinned end to end for non-streaming AND streaming (tools → suspension →
tool_callsresponse →role:"tool"→{success:true, contentItems:[inputText]}onto the ORIGINAL callId → SAME thread/turn continues → terminal answer; one thread/start + one turn/start across both legs; audit: usage carried once), plus the required verbatim error-text pin:"ERROR: lookup failed"is forwarded under the SAME mapping,success: true, never parsed.Gates on
ffe6da2: full suite 2320 tests OK;basedpyright0/0/0; guardrails OK;git diff --checkclean; baseorigin/develop=59538e9(unchanged — no rebase needed); 6 commits, no merges. Fresh independent normal exact-head review dispatched now. Daybreak status follows after that review: ifgpt-daybreak-blue-latestremains unavailable, final status staysSECURITY_REVIEW_BLOCKEDand the PR is not merged. Live acceptance remainsLIVE_TOOL_BRIDGE_NOT_VERIFIED.Round 4 review: APPROVED — owner decision implemented; exact head
42a4832The fresh independent review of
ffe6da2(the owner-decision implementation) returned VERDICT: APPROVED,UX impact: none, zero MERGE_BLOCKERs. Verified by the reviewer: the mapping is emitted at exactly one site and only after the full validation chain (client equality → shape/digest echo → exactly-once claim → deadline → authority recheck); shape is exactly D-062 pt 6; content is forwarded verbatim with no inspection and nosuccess: falseinference; no client-specific branch; STOP supersession is clean (tool_result_success_unresolvedsurvives only as the historical supersession note in pt 6); the documentation tells one consistent story (both cells PARTIAL, owner-approved lossy mapping, live gate pending, no PASS claim); all restored round-trip/replay/authority/mismatch suites green; basedpyright 0/0/0.One DEFER fixed (
42a4832): D-062's Boundary list now namesrouting_core.py(the live v3 availability gate, thecontinuation_capable_resource_idsrequest input, and the compatibility reason-code dedup). A second briefing note needs no repo change: the live gate is recorded in the repo under its own nameLIVE_CODEX_SUBSCRIPTION(theLIVE_TOOL_BRIDGE_NOT_VERIFIEDwording is the task-report label for the same outstanding gate).Final state of this task:
59538e9. Exact head:42a4832(7 commits over develop, no merges, PR open and unmerged).tool_calls= PARTIAL,tool_results= PARTIAL. No PASS claim.SECURITY_REVIEW_BLOCKED—gpt-daybreak-blue-latestis not available in this environment (verified against the host's configured models); per policy no substitute model closes the security gate, so the PR stays paused at the human decision point. The recorded attack surface for that review is listed in the task and remains applicable to head42a4832.LIVE_CODEX_SUBSCRIPTIONgate; #141 owns the final layered acceptance.