Repository navigation
v0.0.4
🤖 The agent loop
We're applying an agent-based automation loop to Backlot's own maintenance: a Claude Code routine takes an issue labelled agent, measures the real vendor API, fixes the divergence and opens a pull request that two reviewer agents have passed, leaving the merge to a person. It is still at an early stage, and we're looking forward to stabilising it in the near future. How to drive it is in docs/loop.md.
⚠️ Breaking changes
- A corpus imported before this release has to be re-imported. Drive files gained a folded title and Slack a table for deactivated members; the server names both at startup. A record needs no change unless it is one below. (#201, #259)
- A Slack reaction or edit names its people by address.
reactions[].usersis a non-empty list of addresses with nocount, andeditedis{"user": <the author's address>, "ts": <a later second>}; the router mints the Slack ids. A corpus carrying"users": ["U01"]stops at import. (#164, #257) - Notion requires
Notion-Version. A missing header or an unpublished version is refused with real's message; the seven published versions pass.2025-09-03is no longer assumed for a caller that sent nothing. (#226)
🔧 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
- A bare bucket
GETis ListObjects andlist-type=2is ListObjectsV2, each reading its own parameters and refusing the other's. Backlot answered the V2 shape regardless. A delimited V1 walk now terminates. (#204) encoding-type=urlis applied, which boto3 sends on every listing; a repeated parameter reads its first value;max-keys=-1is real's 400 rather than a bare 500. (#204)- An undecodable
continuation-token, or an empty one, is refused instead of answered with page one. (#281) - A
HEADon a bucket sub-resource carriesAllow: GETwhere the same path serves aGET. (#241)
Notion
Notion-Versionselects the database query path:databases/{id}/querybefore2025-09-03,data_sources/{id}/queryandGET data_sources/{id}from it, eachinvalid_request_urloutside its range. Every route declares the header, sobacklot mcpsends it. (#226)- The AI-plugins and AI-skills directory is an acknowledged gap. (#252)
- Drive's
files.listparsesqas the reference's grammar —and,or,not, parentheses, every operator per field — and refuses a clause it cannot evaluate rather than dropping it and answering the whole listing. Six of the issue's seven queries answered every file before. (#201) - A Sheets values read accepts R1C1, which LlamaIndex's
GoogleSheetsReadersends;point_sheets_atredirects that reader, with an example. (#201) - Every Google error body is rendered the way real writes one: indented,
charset=UTF-8, real's 209 escaped characters.callback=on a GET answers the error as JSONP at 200;altis read case-insensitively. (#220) $.xgafvis honoured on every route:1adds the legacyerrors[]array where each family's own rule says so, and any other value is refused ahead of everything else. (#202)backlot mcp --source gdriveoffers Docs, Sheets and Slides beside Drive — thirteen tools where there were six. (#199)backlot diffcompares Google's batch endpoint against thebatchPathits documents declare. (#283)
Atlassian
- A query parameter is read the way each product's own binder reads it: an empty value is the default, whitespace is removed, a repeated integer takes its first value,
startAtis along, Confluence refuses a negative where Jira clamps.?limit=-1no longer reaches SQLite as "no limit". A conversion failure carries Jira's RFC 7807 body or Confluence's Spring pair. (#206) - Jira's
search/jqlreads the query string on GET and the body on POST, nothing else. A POST with no body, a wrong media type, an undecodablenextPageTokenor a missingjqlis refused the way real refuses it. (#213) - Confluence's space listing answers a page:
limitandstartare read and_linkscarriesnext/prevspelled as real spells them, cut from the caller's own reachable spaces. (#271) - The space permission roster is
?expand=permissions, one entry per grant, andGET space/{key}/permissionis real's 405. A wrong method carries each product's own body, Jira's withAllow. (#245) - A Jira JSON body is
application/json;charset=UTF-8; Confluence stays bare. (#284) - The Forge panel pin status and workflow copy are acknowledged gaps. (#228)
Slack
- A deactivated member is
deleted: true, absent fromconversations.members, andaccount_inactiveon their own token, while their messages stay. A roster entry statesdeactivated: true. (#259) - A reaction's
usersand an edit'suserare rendered from addresses, so no invented Slack id is left in the bundled corpora. (#164, #257)
GitHub
- A trailing slash is a 404, not a 307, real having no slash redirect;
/contents/stays the root listing's 200. The 404 wins over a bad bearer. (#250) - A bad bearer is answered ahead of an unsupported
X-GitHub-Api-Version; a caller with no credential still meets the version check first. (#243) - The actions-policies family is an acknowledged gap. (#253)
Linear
IntegrationServicecarriesdatadog;IssueLabel.groupTypeis served, which@linear/sdk95.1 selects; four other new fields are acknowledged gaps. (#227, #275)
Tooling, packaging and CI
scripts/gen_docs.pyrenders from the checkout it runs in, not whicheverbacklotis installed. (#282)google-auth>=2.55in theofficial-sdkextra; thelinearexample tracks@linear/sdk95.1. (#211, #209, #275)- ruff 0.16.7, mcp 2.2.0, the actions group. (#210, #211)
Full changelog: v0.0.3...v0.0.4