Repository navigation
fix(rca): resolve build ids for TRA and Automate tools - #559
Conversation
listBuildId / getBuildId - Find a build by SDK name, dashboard name, build number or dashboard link (TRA or Automate), searching the project's builds from the last year. - Suggest close matches and similar project names instead of a bare 404. - Fall back to Automate and App Automate builds by name for runs that never reported to Test Reporting & Analytics; return the TRA id when one exists. Automate to TRA mapping - Map an Automate or App Automate build id to its TRA build, including older runs of the same build name, confirmed before it is returned. - fetchBuildInsights, listTestIds and fetchSelfHealedSelectors accept Automate build ids and map them. Sessions and logs - listSessions accepts either id kind and either product, and explains builds whose results were uploaded and have no sessions. - getFailureLogs detects the product a session ran on and fetches the equivalent log type. - Retry dropped connections once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the walk over older runs: it costs many requests per lookup, and clients retry failed lookups. An older run now gets a direct error that points to listBuildId with the build number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each build search reads one page (the newest builds), the project lookup at most 5 pages, and the Automate name search one page per product. Clients retry failed lookups, so a wrong name now costs a few requests instead of a deep walk. Errors say what was searched and suggest the TRA build id for older runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tatus - Automate and App Automate dashboard links that name a project are matched by build name in that product, not in TRA. Several runs with the same name return candidates instead of a guess, since build numbers are not exposed. - fetchBuildInsights keeps session_type when it maps an Automate build id. - 404s are detected from the HTTP status (HttpError, axios) rather than the error text. - Shorter listBuildId description; drop date handling the builds API does not support. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Automate dashboard serves TRA build pages at /projects/<project>/builds/<name>/<number>, with TRA's dashboard name and build number, so these links resolve through the TRA lookup again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Automate builds API returns no creation date, so matches cannot be ordered across products.
sarve-shreyas
left a comment
There was a problem hiding this comment.
Reviewed the full diff against main (merge-base cd4989a6) at aa11473f.
Local checks: npm run build, tsc --noEmit and eslint on the changed files are clean, and all 2088 vitest tests pass (141 files). Prettier flags tests/tools/getFailureLogs.test.ts and tests/tools/list-session-ids.test.ts, but both were already flagged on main.
Findings outside the diff / general:
- The PR description is empty. For a +2.2k change to tool contracts (all
listBuildId/getBuildIdparams are now optional,getBuildIdfalls back to other users' builds,getFailureLogsswaps log types,listSessions/listTestIds/fetchBuildInsights/fetchSelfHealedSelectorsmap ids across products), please add a summary of the behaviour changes and how they were tested against a live account. - Questions I couldn't check from the code: (a) does
/ext/v1/projects/:id/buildstakedate_rangein epoch ms (${start},${end}usesDate.now())? (b) do the TRAbuilds/:id,quality-gates/:id, test-runs and self-heal endpoints really return 404 for an Automate hashed id? The hashed→TRA fallback inlistTestIds,fetchBuildInsightsandfetchSelfHealedSelectorsonly fires on 404, so a 400/422 there would skip it. The tests mock both of these.
The inline comments are marked blocking or non-blocking.
| .join(", ")}.` | ||
| : ""; | ||
| throw new BuildLookupError( | ||
| `No TRA project named "${projectName}" found.${hint} Use the project name shown on the dashboard, or pass the TRA build id.`, |
There was a problem hiding this comment.
Blocking: findProject stops after MAX_PROJECT_PAGES = 5 pages of /ext/v1/projects (tra-builds.ts:29), but this message says the project doesn't exist.
Scenario: an account with more projects than 5 pages hold passes a TRA link (.../projects/<p>/builds/<name>/<n>) or a project name plus buildNumber. Those paths skip /builds/latest and go straight to findProject. If the project is past page 5, the user gets No TRA project named "X" found, which is false, and the model is told to use a different name.
Suggestion: if the loop exits because of the page cap (and not because has_next is false), say the search covered only the first N projects and ask for the TRA build id. Better, use a name filter on the projects API if one exists. Please also add a test for the page-cap case.
| params: { limit: 1 }, | ||
| raise_error: false, | ||
| }); | ||
| if (!response.ok) return undefined; |
There was a problem hiding this comment.
Blocking: every non-OK response (401/403/429/5xx) is treated as "this id is not on this product". If both products fail this way, resolveTraBuildIdFromHashed throws TraBuildNotFoundError(..., unknownToAutomate = true).
Scenario: listBuildId gets an Automate build link while api.browserstack.com returns 5xx/429. resolveFromHashed (resolve-build.ts:169) returns { buildId: <hashed id> } as a success with no note, so the model passes an Automate id to listTestIds/fetchRCA as if it were a TRA id. In mapHashedToTra the same case becomes "No TRA build has this id", which is also wrong. A temporary outage looks like a bad id.
Suggestion: move on to the other product only on 404 (as listSessionIds does with UnknownBuildError). For any other status, throw an HttpError and let it surface.
| const EQUIVALENT_LOG_TYPE: Partial<Record<LogType, LogType>> = { | ||
| [AutomateLogType.SessionLogs]: AppAutomateLogType.AppiumLogs, | ||
| [AutomateLogType.ConsoleLogs]: AppAutomateLogType.DeviceLogs, | ||
| [AutomateLogType.NetworkLogs]: AppAutomateLogType.DeviceLogs, |
There was a problem hiding this comment.
Non-blocking: some of these mappings return logs the caller didn't ask for. networkLogs → deviceLogs and crashLogs → sessionLogs aren't equivalent: a HAR request answered with device logcat, or a crash request answered with Selenium logs, can lead the model to a wrong root cause. The note helps, but it comes after the content. I'd keep sessionLogs ↔ appiumLogs and return an error ("not available for sessions") for the rest.
| tools.getBuildId = server.tool( | ||
| "getBuildId", | ||
| "Get the BrowserStack build ID for a given project and build name, scoped to the current user's builds.", | ||
| "Like listBuildId, but prefers the caller's own builds. Finds a build id from project + build name, build number or link.", |
There was a problem hiding this comment.
Non-blocking: this changes getBuildId's behaviour. It used to return only the caller's builds (user_name filter, error otherwise). Now it falls back to anyone's latest run, and with buildNumber/buildUrl it ignores the user entirely (resolveInProject has no user filter). That's probably fine, but the two tools are now nearly the same. Please mention it in the PR description, or consider having getBuildId call listBuildId so the model doesn't have to choose between two near-identical tools.
- When the project list is cut off by the page cap, say only the first 50 projects were searched instead of reporting the project as missing. - Only a 404 means a build or session belongs to the other product. Other failures (429, 5xx) now surface as errors instead of returning an Automate id as if it were a TRA id. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… scope - getFailureLogs swaps only sessionLogs and appiumLogs across products. Other log types that do not exist for the session's product are reported as unavailable instead of returning a different kind of log. Notes now come before the logs. - getBuildId's description says the caller's builds are tried first only for build-name lookups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sarve-shreyas
left a comment
There was a problem hiding this comment.
Re-reviewed aa11473f..f383d32e (aa3f0a0a and f383d32e). tsc --noEmit and eslint on the changed files are clean, and all 2092 vitest tests pass.
Resolved:
- Project search page cap:
findProjectnow returnstruncated, and the error says the search was partial instead of saying the project doesn't exist. There's a test for it. - Non-404 handling:
readAutomateBuild, the session-detail lookup,resolveBuildIdFromSession, the confirmation step andmapHashedToTranow fall through only on 404. Other statuses surface asHttpError. getFailureLogsnow swaps onlysessionLogs↔appiumLogs, reports other mismatched types as errors, and puts the notes before the logs.getBuildId's description now states its user scope.
Still open:
- The PR description is still empty (
bodyis empty via the API). The behaviour-change list and the live checks (date_rangein ms, 404 for hashed ids on the TRA endpoints) haven't been added yet. PROJECTS_SEARCHED = MAX_PROJECT_PAGES * 10assumes/ext/v1/projectsreturns 10 projects per page. Was that page size checked against the live API? If it's different, the "first 50 projects" message is wrong. Counting the rows actually seen would avoid the assumption.
One inline comment below: the name-only Automate fallback still swallows errors.
| first.hashedBuildId, | ||
| first.sessionType, | ||
| config, | ||
| ).catch((): BuildLookupResult => ({ buildId: first.hashedBuildId })); |
There was a problem hiding this comment.
Blocking (same issue as the last round, on a path the fix didn't cover): this .catch swallows every error, including the HttpErrors that resolveFromHashed now throws on 5xx/429.
Scenario: listBuildId({ browserStackBuildName: "nightly" }) with no project falls through to searchAutomateBuilds. If Automate returns 503 during the mapping, this returns the hashed Automate id as a success, noted as "Found automate build ... by name". The model then passes it to listTestIds/fetchRCA as a TRA id.
Suggestion: drop the catch so HttpError propagates (resolveFromHashed already turns TraBuildNotFoundError into a result). Or catch only TraBuildNotFoundError. Separately and non-blocking: findAutomateBuildsByName (.catch(() => undefined) / !response?.ok → break) turns an outage into "None of the latest Automate builds is named X", the same kind of misleading not-found message.
…s seen - The name-only Automate fallback no longer turns a failed TRA mapping or builds-list error into a success. Products the account cannot access (401/403/404) are skipped; other failures surface. - The partial project search message reports how many projects were actually checked instead of assuming 10 per page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
listBuildId and getBuildId now make at most one lookup request: - A TRA id is returned as is; an Automate build id is mapped as before. - Project + build name returns the latest run via /builds/latest. A miss explains that the name must be the one from browserstack.yml and asks for the build number or dashboard link. - A build number, or a TRA / Automate dashboard project link, returns a ready invokeCapability call for get_build_id_from_build_coordinates, so that endpoint is defined only in the TRA capability index. Removes the project search, the project build listing, close-match lists and the Automate builds name search. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 429 or 5xx while trying the id as a TRA build is now returned as an error instead of "no build has this id". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The TRA-to-Automate step now reads only the first page of a build's test runs. If that page has no session but more pages exist, it says so and asks for the Automate build link instead of paging on. A build with no sessions at all is still reported as uploaded results. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
listBuildId and getBuildId call get_build_id_from_build_coordinates themselves, through the capability registry's invoke path, and return the build id in one tool call. The endpoint stays defined only in the TRA capability index. When the registry is disabled or its index is missing, the tools return a clear error instead of pointing at a tool the client may not have. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The helper now reads only the index of the product it calls (TRA, about 1.2 MB) instead of every product index, cached per product. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the first page of a build's test runs has no session but more pages exist, one call to the TRA capability get_build_summary_stats reads the build's session count. Zero means uploaded results; otherwise, or if the count cannot be read, the tools ask for the Automate build link. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Build ids were the most common failure point in the Automate and Test Reporting & Analytics (TRA) tools. Users have a dashboard build name, a build number, a dashboard link or an Automate build id. The tools accepted only the exact build name the SDK sent, and only for the latest run. This PR resolves what the user has into the right id, and lets each tool accept either kind of id.
Behaviour changes
listBuildId / getBuildId
Each lookup is at most one request, with no paging:
/ext/v1/builds/latest. This matches the build name from browserstack.yml. When it misses, the error explains that and asks for the build number or dashboard link, rather than searching..../projects/<project>/builds/<name>/<number>, on the TRA or the Automate dashboard): one call to the TRA capabilityget_build_id_from_build_coordinates, made by the tool itself through the capability registry, so the tool returns the build id directly and the endpoint stays defined only in the capability index. If the capability registry is disabled, the tool returns a clear error./builds/latestmatches). With a build number, it must be the name shown on the dashboard, which TRA may shorten. A dashboard link avoids the difference, and the field descriptions and errors say which name is expected.Automate build ids in TRA tools
session_type), listTestIds and fetchSelfHealedSelectors. They map the id when TRA returns 404 for it.statusis now optional.Sessions and logs
sessionLogsandappiumLogsare swapped across products, with a note. Other log types that don't exist for that product are reported as unavailable. A different kind of log is never returned.Errors and retries
HttpError, or axios errors), not the error text.Tool descriptions
Testing
Unit:
npm run buildpasses: lint, format, 142 test files / 2,087 tests, and tsc. New tests cover:Live: run against the live API through a real MCP client. All cases passed: build lookups by every input above, Automate id mapping, insights, sessions, test ids and log-type correction. API behaviours this change relies on, confirmed that way:
+. The build name the SDK sent returns 404.🤖 Generated with Claude Code