diff --git a/docs/doctoring/gateway-route-proof-not-cli-rendering-20260930.md b/docs/doctoring/gateway-route-proof-not-cli-rendering-20260930.md new file mode 100644 index 0000000000..a09925febf --- /dev/null +++ b/docs/doctoring/gateway-route-proof-not-cli-rendering-20260930.md @@ -0,0 +1,28 @@ +# Gateway route proof and CLI rendering + +The executed route test introduced in `9c6d05374` correctly observes the +installed OpenCode CLI's request URL, but its negative case additionally +required `not found` to appear in CLI output before fixture cleanup. On a busy +host the expected `/chat/completions` request and gateway 404 occurred without +that display text. A full run therefore failed with 5,176 other tests passing, +while the same two CLI tests separately passed in 47.70 seconds. + +The fixture intentionally stops the CLI after observing a request; that is +transport evidence, not a terminal CLI error-rendering receipt. No production +request timeout, provider retry, review verdict, or sanitizer is changed. + +The repair records HTTP statuses only after the stub writes its response. +The real installed CLI must still request `/v1/chat/completions` for the shipped +configuration and `/chat/completions` for the bare-origin regression. Those +requests must respectively receive a sent 200 and 404 response. Delayed or +redacted CLI text is no longer confused with the network path. The stub retains +the pinned gateway's error body; this does not require production logs to expose +raw provider errors. + +Before repair: the unchanged negative case failed at the display-text assertion. +After repair: both real CLI cases passed in 40.56 seconds. The complete suite +and current-head hosted evidence remain separate acceptance steps. + +This is an inherited test-reliability repair, separated from the Noema +input-modality catalog change. It is not a security-finding exception or proof +that any provider HTTP 400 incident has been resolved. diff --git a/tests/test_opencode_gateway_route_integration.py b/tests/test_opencode_gateway_route_integration.py index 3893914c0e..c1560872c7 100644 --- a/tests/test_opencode_gateway_route_integration.py +++ b/tests/test_opencode_gateway_route_integration.py @@ -8,9 +8,10 @@ These tests run the installed OpenCode CLI against a stub that answers exactly like the vendored gateway — the served route succeeds, every other route 404s -with the gateway's own wording — and record which path the CLI asked for. The -tracked ``opencode.jsonc`` provider block is the input, so the proof follows -the shipped config instead of a copy of it. +with the gateway's own wording — and record the requested path and sent status. +The tracked ``opencode.jsonc`` provider block is the input, so the proof follows +the shipped config instead of a copy of it. CLI error rendering is not a +transport receipt and can lag the response on a busy host. They skip when no ``opencode`` binary is present (CI images for the quality workflows do not install it); local execution is the evidence. @@ -52,6 +53,7 @@ class _GatewayStub(BaseHTTPRequestHandler): """Answer like the vendored gateway: one served route, 404 elsewhere.""" requested_paths: list[str] = [] + responses: list[tuple[str, int]] = [] def do_POST(self) -> None: # noqa: N802 - BaseHTTPRequestHandler API """Record the requested path and answer as the gateway would.""" @@ -92,6 +94,7 @@ def _send(self, status: int, payload: dict) -> None: self.send_header("content-length", str(len(body))) self.end_headers() self.wfile.write(body) + type(self).responses.append((self.path, status)) def log_message(self, *args: object) -> None: """Silence the default stderr access log.""" @@ -101,6 +104,7 @@ def log_message(self, *args: object) -> None: def gateway_stub_fixture(): """Serve the gateway stub on a loopback port for one test.""" _GatewayStub.requested_paths = [] + _GatewayStub.responses = [] server = HTTPServer(("127.0.0.1", 0), _GatewayStub) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() @@ -190,15 +194,15 @@ def test_tracked_config_requests_the_gateway_served_route(gateway_stub, tmp_path _run_opencode(tmp_path, base_url, origin, requested_paths) assert requested_paths, "the CLI issued no request to the gateway stub" assert requested_paths[0] == GATEWAY_SERVED_ROUTE + assert _GatewayStub.responses[0] == (GATEWAY_SERVED_ROUTE, 200) def test_bare_origin_reproduces_the_route_not_found_outage(gateway_stub, tmp_path) -> None: - """Dropping /v1 must reproduce the unserved path and the gateway wording.""" + """Dropping /v1 must request an unserved path for which the stub sends 404.""" origin, requested_paths = gateway_stub bare = "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}" - stdout, stderr = _run_opencode(tmp_path, bare, origin, requested_paths) + _run_opencode(tmp_path, bare, origin, requested_paths) assert requested_paths, "the CLI issued no request to the gateway stub" assert requested_paths[0] == "/chat/completions" assert requested_paths[0] != GATEWAY_SERVED_ROUTE - combined = f"{stdout}\n{stderr}".casefold() - assert GATEWAY_ROUTE_NOT_FOUND_MESSAGE in combined + assert _GatewayStub.responses[0] == ("/chat/completions", 404)