Conversation
There was a problem hiding this comment.
Narrowly-scoped CI fix: the MCP Registry publish retry loop goes from 5×30s (~150s) to 20×30s (~10min), matching the measured npm propagation delays (202–287s) cited in the description. Traced the new audit test (registry-publish-retry-window.test.ts) by hand: it extracts the loop verbatim from release.yml via regex and exercises it against a stub publisher with 1000x time compression; the math checks out (worst-case 287s delay needs attempt 11, well inside the new 20-attempt budget, and the "gives up" / "first-attempt success" cases are also correct). No generated files, tool descriptions, or WORKFLOWS.md rows are touched, so those CLAUDE.md gates don't apply. No bugs or logic issues found — nothing to block on.
…the version The registry re-checks npm before it registers a version and answers 404 while npm's read replica is still catching up. The retry loop spanned 5 attempts at 30s, so it gave up at ~150s. Measured from `npm publish` returning to the registry accepting on the rerun: 246s on 0.39.1, 216s on 0.39.2, 287s on 0.39.4, 202s on 0.39.5. Four of the last five releases needed `gh run rerun --failed`. 20 attempts spans ~10 minutes, twice the worst of those. The test lifts the loop out of release.yml and runs it against a stub publisher that replays the 0.39.5 400 body until npm serves the version. It fails on main today. Fixes leadbay/product#4151 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
a1f9819 to
51dcbba
Compare
* fix(evals): the verifier authenticates from the same credentials file as the tester compose.oauth.yaml bound the credentials file into the tester service only, so a local journey run with EVAL_CLAUDE_CREDENTIALS set and CLAUDE_CODE_OAUTH_TOKEN not exported finished the product session and then exited at the verifier with "Not logged in", writing no verdict.yaml. Closes leadbay/product#4146 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(mcp): excluding a sector takes it off the include list `leadbay_adjust_audience` on prod US lens 40979, 2026-09-16: the user asked to tighten a lens to construction instead of manufacturing. The call `{sectors:["Construction"], exclude_sectors:["Manufacturing"]}` saved a filter with include `["830","731"]` and exclude `["830"]` — Manufacturing included and excluded at once, and the narrowing never happened (product#4148). `mergeFilter` unioned the new ids into the include criterion and the excluded ids into the exclude criterion, and neither pass took an id off the other list. An id the caller names on one side now comes off the other, and a criterion left with no sectors is dropped rather than written empty. Same rule both directions, so re-including a sector the lens excludes also works. Locations carry the same union in the same function and are untouched here. Proof: `adjust-audience-exclude-narrows.test.ts` replays the lens 40979 call. On main it fails with include `["830","731"]`, the exact prod payload. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * evals: adjust-audience-dirty-taxonomy declares what a live FR tenant has The case declared four sector rows — Pergola aluminium, a null name, Pergola bioclimatique and Menuiserie — and a French taxonomy. None of that is a tenant setting, so no live run could establish it and prepared-state failed on the fixture wording. Checked against prod-fr on 2026-09-16, all four /1.6/sectors/all captures: zero rows named Pergola, zero null or empty labels, and the tenant reports language en. starting_state now names what those captures show: no Pergola row in either language, menuiserie spread over several rows at different registry depths, and véranda only on the row that also covers greenhouses. Running the product's own matchSector over the live registry leaves all three trade words unresolved, which is the ambiguous_sectors outcome the case grades. outcome-1, the null-name crash, is dropped. That row never appears on a tenant, so live evidence is impossible. It stays locked deterministically in packages/core/test/unit/composite/adjust-audience-dirty-taxonomy-regression.test.ts. languages now lists en and fr. The verifier observed en; the journey resolves identically in both. Refs leadbay/product#4149 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * ci(release): wait for npm long enough for the MCP Registry to accept the version The registry re-checks npm before it registers a version and answers 404 while npm's read replica is still catching up. The retry loop spanned 5 attempts at 30s, so it gave up at ~150s. Measured from `npm publish` returning to the registry accepting on the rerun: 246s on 0.39.1, 216s on 0.39.2, 287s on 0.39.4, 202s on 0.39.5. Four of the last five releases needed `gh run rerun --failed`. 20 attempts spans ~10 minutes, twice the worst of those. The test lifts the loop out of release.yml and runs it against a stub publisher that replays the 0.39.5 400 body until npm serves the version. It fails on main today. Fixes leadbay/product#4151 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(mcp): a lead job that runs past a minute answers in time (product#4144) A qualify_leads call could block longer than the MCP host allows a tool call. The SDK cancels at 60s (DEFAULT_REQUEST_TIMEOUT_MSEC) and a host timeout is indistinguishable server-side from a user cancel, so the user sees an error while the job they approved keeps running, with no job_id to come back to. Measured on FR prod, mcp_version 0.39.1, org LEADBAY, lens 5885, 2026-09-16: the POST /mcp/qualify submit alone took about 20s, then the default 45s wait ran on top of it. One uncached lead returned 62,120ms after the call started. wait_seconds also accepted up to 180, and every still_running response suggested the follow-up poll wait exactly 60. Three bounds, one number. MAX_WAIT_SECONDS is 45, it caps what a caller may ask for, and it is what next_poll suggests. clampWaitSeconds takes the time the TOOL CALL began, so the wait gets what the quote and the submit leave of the budget instead of a further full wait_seconds on top of them. Proof: job-wait-host-ceiling.test.ts drives qualify_leads on a fake clock with the measured 20s submit. On main the call returns at 65,000ms, at 200,000ms when the caller asks for 180s, and hands back a 60s hint. After: 45,000ms, 45,000ms, and a 45s hint. Not fixed here: the submit POST itself is unbounded. It is a write that may already have charged, so it is never aborted mid-flight. A submit that alone outlasts 45s still loses the call, and no measured submit came close. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(mcp): find_new_leads gets the same whole-call budget Review found the gap: find_new_leads has the identical submit-then-wait shape through the same helper, so a slow POST /mcp/search left the wait running past the host's 60s. clampWaitSeconds now takes that call's start time too, and the test asserts it with the same 20s submit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore(mcp): release 0.39.8 Stacks five reviewed fixes into one release. - product#4144 a lead job that runs past a minute answers in time (#249) - product#4148 excluding a sector takes it off the include list (#245) - product#4151 the release waits for npm before the registry publish (#247) - product#4146 the eval verifier authenticates like the tester (#244) - product#4149 the dirty-taxonomy case declares what a live FR tenant has (#248) No file is touched by more than one of the five, so the merges carried no conflicts. On the stacked tree: build clean, 2882 tests green across 367 files, typecheck clean, prompts:build leaves the generated files unchanged, and the largest description is leadbay_import_leads at 16951 with 49 to spare. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(mcp): a city swapped out of an audience comes off the include list too The review bot on #250 spotted that the product#4148 fix covered `sector_ids` only, while `location_ids` has the identical structure. "Lyon instead of Paris" left Paris included AND excluded, exactly as manufacturing was. Not a regression from this release, but this release's CHANGELOG claims that narrowing an audience narrows it, and that claim is false while locations still contradict themselves. So it is fixed here rather than filed. `dropSectors` becomes `dropIds`, taking the criterion type. Two new tests fail on the branch without this commit and pass with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Fixes leadbay/product#4151.
The MCP Registry re-checks npm before it registers a version, and answers
version '<x>' was not found (status: 404)while npm's read replica is stillcatching up. The retry loop spanned 5 attempts at 30s, so it gave up at ~150s.
Measured from
npm publishreturning to the registry accepting the version onthe rerun:
Four of the last five releases needed
gh run rerun --failed.The loop now runs 20 attempts, ~10 minutes, twice the worst of those. The
30s sleep and the give-up behaviour are unchanged.
Proof.
packages/mcp/test/audit/registry-publish-retry-window.test.tslifts the loop out of
release.ymland runs it underbashagainst a stubmcp-publisherthat replays the verbatim 0.39.5 400 body until npm serves theversion. The sleep is compressed 1000x in the harness only, so the assertion is
about how many attempts the loop makes. On
maintoday:After:
pnpm -r test962 passed,pnpm -r typecheckclean.Not done here. The issue's last paragraph notes that a failed registry job
signals nothing, since npm, the GitHub Release and the hosted image are all
correct. No notification is added. No version bump, the release queue is
coordinated.
mcp-v0.39.6failed inPublish @leadbay/mcp to npm, which is adifferent failure and is untouched.
🤖 Generated with Claude Code