Repository navigation
M1/PR16: review blocker set — typed-spine leaks and silent route rewriting (F01 F02 F03 F04 F06 F12) #17
Copy link
Copy link
Closed
Labels
ready-for-agentSpecified enough that the work is mechanicalSpecified enough that the work is mechanical
Description
Source: multi-agent review of PR #16 (range 3e3e058871...52a5e3b1fd), adversarially verified 2026-08-13. This issue is the canonical record; the scratchpad review artifacts are ephemeral. Fix red-test-first; closed by the PR that lands the fixes.
Checklist
- F01 — Invalid charset escapes the typed exception spine · CONFIRMED · sev 4/5 · shipped-runtime.
Pipeline.ReadBodyAsync(src/OpenCode.Sdk/Internal/Pipeline.cs:148-152) catches onlyHttpRequestException or IOException or ObjectDisposedException;HttpContent.ReadAsStringAsyncthrowsInvalidOperationException(innerArgumentException) onContent-Type: application/json; charset=not-an-encoding— verified on modern runtimes and net472. EscapesOpenCodeExceptionspine on every operation;NoThrowcannot intercept (body read at:92precedes theErrorBehaviordecision at:95-98). Fix: addInvalidOperationExceptionto the filtered catch, wrap asOpenCodeTransportException, keepOperationCanceledExceptionuntouched. Test: invalid-charset content assertingOpenCodeTransportExceptionwith IOE inner. - F02 — Unexpected 2xx enters the API-error channel · CONFIRMED · medium · both M1 operations. Written contract: "Unexpected or malformed 2xx responses are protocol failures" (
docs/superpowers/plans/2026-08-13-m1-arc-b-callable-client.md:41). Generated adapters (HealthResponseAdapter.cs:37, same inSessionMessageResponseAdapter, emitted byResponseAdapterEmitter.cs:86-119) route 201/204/299 to the error envelope instead ofOpenCodeTransportException. Fix inResponseAdapterEmitter: declared 200 = success; other2xxarm throwsOpenCodeTransportException; non-2xx stays on the API-error channel. Regenerate; test both behaviors under default andNoThrow. - F03 — Empty union marker leaks
ArgumentException, loses raw body · CONFIRMED · high · all seven unions, both adapter paths.{"_tag":""}misses the known-tag map, hitsArgumentException.ThrowIfNullOrEmpty(tag)inUnknownOpenCodeError(Models/UnknownOpenCodeError.cs:19);ReadTolerantErrorcatches onlyJsonException(ResponseAdapter.cs:93-96) → BCL exception escapes even underNoThrow,RawBodydropped. Same for a 200 payload with"type":"". Contradicts ADR-0009 tolerance. Fix: generated converters classify empty/whitespace markers as malformed →JsonException; adapter catches then yield null error + raw body (non-2xx) orOpenCodeTransportException(malformed success). Keep public ctor guards. Tests across representative unions, both paths. - F03-fold / R4 — centralize unknown-carrier construction:
ResponseAdapter.Downgrade(ResponseAdapter.cs:99-103) and generatedOpenCodeErrorJsonConverter(:53) build the carrier independently — fold a shared runtime factory into the F03 fix to kill carrier-shape drift (UNVERIFIED-FINDER R4; do it because the F03 fix touches both sites anyway). - F04 — Dot-segment ids silently rewrite the request path · CONFIRMED · medium · every generated path parameter.
Uri.EscapeDataStringleaves./..unescaped (OpenCodeRoutes.cs:35-39);new Uri(base + route)(Pipeline.cs:87) removes dot segments. Verified: session id..→/api/session/../message/m1collapses to/api/message/m1; message id..→/api/session/s/. Fix: generator-owned route-segment guard rejects exact.and..(decide whitespace-only too) before URI construction. Tests inspect actual outgoingRequestUrifor both parameter positions. Do not fightSystem.Uricanonicalization without cross-TFM proof. - F06 — Undefined
ErrorBehaviorsilently acquiresNoThrowsemantics · CONFIRMED · low · public options.Pipeline.cs:96-98testsis ErrorBehavior.Default;(ErrorBehavior)2suppresses the throw. Fix: exhaustive branch —Defaultthrows on error,NoThrowreturns, anything elseArgumentOutOfRangeException. Undefined-value test. - F12 — Send-path
ObjectDisposedExceptioncan escape under a disposal race · PLAUSIBLE · low.SendCoreAsync(Pipeline.cs:125-137) catches onlyHttpRequestExceptionwhileReadBodyAsyncwraps ODE. Fix: add ODE to the send-path filtered catch (keepOperationCanceledExceptionpassing through; pre-send disposed guard stays outside the catch). Deterministic handler-level ODE test.
Notes
- Public API names untouched — no baseline churn expected; if a fix forces a baseline change, stop and return to the maintainer.
- Full verifier text lived in
.scratchpad/ultra-review/VERIFICATION-REPORT.md(ephemeral); the mechanisms above are the preserved record.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
ready-for-agentSpecified enough that the work is mechanicalSpecified enough that the work is mechanical
Activity
Blind-Striker commentedon Aug 13, 2026
All six findings fixed red-test-first and pushed to PR #16:
082b58c— F01 (invalid charset →OpenCodeTransportException), F12 (send-pathObjectDisposedExceptionwrapped), F06 (undefinedErrorBehaviorrefused withArgumentOutOfRangeExceptionbefore sending).1de4d78— F02 (undeclared 2xx →>= 200 and < 300transport-failure arm via a newUndeclaredSuccessFailurebase helper), F03 (converters classify empty/whitespace markers as malformedJsonException; raw body retained on the error path,OpenCodeTransportExceptionon success), F04 (route builders refuse./..and whitespace values; outgoingRequestUriescaping pinned by tests). Emitter snapshots updated; 571 tests green; committed generated output regen-verified.R4 (fold) — resolved by argument, not code: a shared unknown-carrier factory would require the generic union emitter to special-case one union's construction path, which ADR-0008 forbids (emitters never branch on names). The shared contract both sites use is the public
UnknownOpenCodeErrorconstructor itself; the F03 change moves malformed-marker classification in front of both construction sites, which was the drift risk that mattered.Note for the record: ns2.0's
string.IsNullOrWhiteSpacelacks the[NotNullWhen(false)]flow annotation, so the emitted guard carries an explicitmarker is null ||disjunct — that is deliberate, not redundancy.