Repository navigation
🙌 New contributors
- @shrikargs7-cloud made their first contribution in #394
- @wanjinhao1 made their first contribution in #395
- @emiribrahimm made their first contribution in #398
- @Roshan1299 made their first contribution in #400
🔧 Fixes
Each closes a measured divergence from the vendor's real API. Those that change what a source answers are marked in bold.
Amazon S3
- Every answer carries real's
x-amz-request-idandx-amz-id-2, and an error body repeats them. A method the router does not serve answers what real answers for that method and sub-resource selector, the 405 namingBUCKET,OBJECT,SERVICEor the selector's type, the CORS 400/403 for anOPTIONS, rather than one shared JSON 405. A write isNotImplemented(501). (#319) - A refusal names what real names it with, in real's order, and a key in a bucket that does not exist or that the caller cannot see is
NoSuchBucket. (#361) - Signature Version 2, SigV4 and SigV4a are verified, in the header and in the query, and each fault is refused with real's code, message and members. (#361)
- A credential scope has to name
us-east-1, the region Backlot presents; a client signing for another region gets real'sAuthorizationHeaderMalformed, where Backlot used to read the region back out of the scope and serve any. An unsigned request is the anonymous caller's and sees no bucket. (#361) - A bucket's and an object's sub-resources answer as real answers an unconfigured one,
?versionsis ListObjectVersions, ListBuckets pages and refuses its four parameters as real does, and GetObject readspartNumber,versionId=nullandx-amz-checksum-mode. (#361) - Four writes are checked before the 501, so a malformed PutObject, DeleteObjects,
POST ?restoreorPUT ?encryptionis refused for what real refuses it for, with all ten checksum algorithms computed. AHEADis framed the way real frames it. (#361)
- Gmail's message tree is real's: a message with an attachment is
multipart/mixedover amultipart/alternative, every part carriesheaders,format=rawis built from the same tree, andformat=metadataservesmimeTypeandheadersalone.attachments.getserves{size, data},labels.listserves fifteen labels in the measured order without counts, an empty list is{"resultSizeEstimate": 0},threads.gethas nosnippet, and a snippet escapes<and>. (#400) - Gmail's
threads.getrefuses a reply's message id with the 404 a well-formed unknown id gets, rather than serving a one-message thread. (#395) - A Gmail attachment read on a missing message or attachment id is real's 400
Invalid attachment token, not a 404. (#394) - Drive and Sheets parse every repeat of a typed query parameter and refuse all the failures in one 400 with a
detailsentry each, after the credential and before the lookup. Drive'spageSizeandpageTokenare checked as real checks them, a blankfieldsanswers{}, andfiles.exportrefuses a format the file's type does not export to and serves one it does under themimeTypeexactly as sent. (#338) - A repeated query parameter is read from the end real reads it from: the first for Drive's
fields,q,pageSize,pageToken,orderByand exportmimeTypeand for Sheets'fieldsandprettyPrint, the last for the Sheets typed parameters.prettyPrintcompacts a Sheets answer atfalseand0only. (#336) - An anonymous
POSTis 401UNAUTHENTICATEDon every family, where aGETon Drive or Sheets stays the 403 unregistered caller. (#316) commentsViewModeon the Docs, Slides and Sheets get routes is an acknowledged gap. (#359)
Notion
GET /v1/commentsrefuses ablock_idit cannot use the way real does: absent, empty or not a uuid is a 400validation_error, a uuid naming nothing visible a 404, a database's id a 403. A visible block's id answers the empty list, andbacklot mcp'slist_commentsnow requiresblock_id. (#398)- A refusal carries
request_idandx-notion-request-id, and the 401 says which credential failed: a header that is not exactlyBearer <token>is refused for its format, a token that does not resolve asAPI token is invalid.. (#328) - A URL Notion does not publish is real's 400, and an operation it publishes that Backlot does not serve answers the credential first. One trailing slash is dropped and a
HEADis theGETwithout its body. (#328)
Atlassian
- A
HEADis theGETwith the body left off, and anOPTIONSis answered per product and per caller: Jira's 200 with its measuredAllow, Confluence's 404, itssearchanswering byAccept. (#330) - A path neither product serves gets each product's own answer: Jira's RFC 7807
No endpoint …, Confluence's JAX-RS 404, and the 401 for an operation the vendor publishes that Backlot does not serve. Outside the API mounts the site answers as its web app does, and a trailing slash or a run of slashes is ignored as the gateway ignores it. (#330) - Every answer carries the headers real sends beside the body,
atl-request-id,atl-traceid,x-arequestid,x-confluence-request-time, the deprecation trio and Jira's burst quota among them. (#330) - Confluence's listings read
limitandstartwith each route's own bounds,child/page,child/commentandlabelpage rather than serving the collection, and every page answers real's_links, the CQL search'scursorincluded. (#320) - Jira's
search/jqlrefuses amaxResultsoutside 1–5000, a body field it cannot coerce and an unknown body field. A negativemaxResultsused to serve the whole visible collection. (#286) setContextDefaultValuesis an acknowledged gap, with the early-access 403 the site answers recorded beside it. (#368, #399)
GitHub
- A request is refused once the caller's rate-limit window is spent, real's 403 with
usedpinned atlimit, for a caller with no credential and for a token alike, where Backlot used to let every request through. A suite sending more than ten code searches a minute now meets that refusal;BACKLOT_GITHUB_ENFORCE_RATE_LIMITS=falseturns it off. (#317) - A trailing slash means what each route makes of it: a ref keeps it and is refused,
contentsredirects one slash at a time with real'sLocationencoding, andreadme/{dir}serves that directory's README. (#326) /rate_limitanswersresourcesbeforerate, the two search windows measure a minute, and a caller with no credential has nocode_searchwindow of its own. A credential that does not resolve gets its 401 with no rate-limit headers and no count. (#289)- A path no route matches carries no version echo, and the
x-ratelimit-*headers only for a caller that sent no credential. (#288) relates_to, external installation properties and step logs are acknowledged gaps, and eighteen operations GitHub no longer publishes leave the baseline. (#360)
Slack
- A file's
useris served as a Slack id, minted from the address the corpus writes, asreactions[].usersandedited.useralready are. The corpus schema now typesfiles[].useras an address, so a corpus carrying"user": "U01"on a file stops at import. (#291) - A deactivated member drops the ten fields real never carries for one,
real_name,is_adminandtzamong them, rather than serving defaults. (#293) has_2fais served to an admin caller on every active person and to a caller on their own member, and never on a bot. (#293, #344)
Linear
AgentSession.codingHarnessandQuery.dependencyPackageMetadataare acknowledged gaps. (#358)
Tooling, packaging and CI
xxhashandcryptographyare base dependencies, for S3's checksums and SigV4a. (#361)- CONTRIBUTING's pull request checklist asks for
ruff check . && ruff format --check .besidepytest. (#396) - The
linearexample tracks@linear/sdk96.0.0; ruff 0.16.9, fastmcp 4.0.9, google-auth 2.58.1 and the actions group. (#298, #299, #300, #355, #356, #357)
Full changelog: v0.0.4...v0.0.5