Follow the gateway's whole-API contract: no redirects, one pinned policy (0.3.3) #9
Loading…
Reference in a new issue
No description provided.
Delete branch "oss-oo/gateway-contract-align"
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?
What
The laboratory's MCP gateway is getting this plugin's whole-API contract (
api_spec,api_read,api_write) with a copy of this guard inagent-gateway/api_guard.py. This change brings the plugin in line with that copy and hardens what the review found on the way.No redirect is followed, for any method or 3xx status. Up to 0.3.2, urllib followed same-origin redirects. That had four effects:
POSTturned into aGETthat was reported asHTTP 200 POST;Now a resource the laboratory moved is reported with its new API path ("call again with that path or the new repository name"), and any other redirect only as a fact. urllib does not even read the
Locationany more, and a malformed one is reported, not raised.One pinned policy.
policy_sha256()digests the decision tables the way the gateway does.test_the_guard_policy_is_the_gatewayspinsb4e86f74…, the value the gateway pins.lab_api_specreports it. A further test pins that no write route carries regex flags, which the digest does not cover.The token never shows. Should the laboratory send it back, the core replaces it as the answer arrives, before anything is cut or printed. That covers bodies, titles, errors and redirect notes, and a token spelled with JSON escapes too.
Clean errors, never a traceback, for:
Query values without a JSON spelling (NaN, infinities, numbers too long to print) are refused before anything is sent, by
api_callitself.Version 0.3.3. The README and the skill say what a moved resource looks like and what the digest covers.
Evidence
sh scripts/check.sh): exit 0. It runs 460 Python tests, the asset check,claude plugin validateand 18 mod tests. CI is green on every head.Locationtraceback that could carry the token, the token surviving a cut, deep or odd answers, and test gaps.Location;claudeagainst https://oss-oo.io on 2026-10-27. Three paths: plugin 0.3.2, this branch, and the gateway's own handler, run locally from its branch.PATCHofprivateare refused by all three without a request.PUTof the same topics answers 204 on all three paths, and the topics are unchanged afterwards.OSS-OO/forgewas renamed and answers 301.GET /repos/OSS-OO/forge.POST /repos/OSS-OO/forge/issues(empty body) asHTTP 200, with an issue list./userall answered 200.Not in this change
Once the gateway is deployed, this plugin's
lab_api_*and the gateway'sapi_*overlap. Both enforce the same policy (same digest). The owner decides whether to keep both, prefer the gateway, or retirelab_api_*; nothing is removed here.🤖 Generated with Claude Code
- urllib no longer reads a redirect's Location (http_error_30x return None), and the redirect note survives a Location that no URL parser reads. A malformed or crafted Location was a traceback before, one that could carry the token. - A plain-text error body loses the token before it is cut to 200 bytes, so no part of a token survives the cut. - main() replaces the token in everything it prints (bodies, titles, errors), should the laboratory ever send it back. The README now says exactly that and no more. - api_call converts query values itself (JSON spelling; NaN, infinities and over-long numbers refused), so the library path matches the CLI and the gateway. - A cut answer (IncompleteRead), JSON nested too deeply in an answer or an error body, and an API description that is not a JSON document with paths are clean errors, not tracebacks. The description's error text is bounded and printable. - The moved note fits the CI tools too ("call again with that path or the new repository name"). A test pins that no write route carries regex flags, which the shared digest does not cover, and the README says what the digest covers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>- keyring_token refuses an entry that is no API token (under 16 characters, a space, a line break, non-ASCII) without quoting it. Replacing the token in every answer garbles nothing only for a token that cannot be ordinary text, and a line break would otherwise reach the Authorization header. - A RecursionError is reported neutrally, as an answer or a body nested too deeply to handle. - Tests pin what the review's mutations showed unpinned: every occurrence of the token is replaced, as the answer arrives and before either cut; the 300-character bound; line, control and direction characters; a numeric operationId; a cached {} or paths list read again; non-ASCII in the compact view. The deep-answer test no longer depends on where CPython's indenting encoder stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>