Execute the first v2/main → main milestone merge and cut the release #1876
Description
Activity
Triage record: Priority High — scored 11/16 (severity 4, urgency 5, +2 for milestone and assignee) with the rubric added in #1891. Retained as an audit trail of the rubric; nothing here is needed to execute the merge.
Superseded detail: this was written while the card was in Todo and #1826 was an open flake risk. Both have since moved — the card is In Progress and #1826 shipped in this milestone.
Milestone overview — what v2.1.0 actually contains
Status 2026-08-02 — merged and built. All 14 PRs below are on
v2/mainand carried by PR #1903, which is open,MERGEABLE, and green onnpm run ci. Their issues are closed and their board cards are Done.v2/mainis now 15 commits ahead ofmain, not 14: #1904 (the.github/workflows/main.ymlsync,Closes #1902) landed after #1903's branch was cut, so it is not in this release. That's harmless — it only repairsv2/main's copy of a filemainalready has correct, and workflows aren't in the published tarball.Scope measured against
origin/main, not against the milestone label — 14 PRs, the payload #1903 carries (git rev-list --count origin/main..834fd2d7→ 14).The shape of it is unusual and worth stating plainly: this milestone is mostly repo infrastructure, not product work. Exactly one PR changes runtime behavior. The rest exists because the 2.0.0 tree swap replaced
main's tree wholesale and took a set of repo-level files with it, or because the contribution model changed underneath us in the same week.
1. Security & dependency hardening (2 PRs, both ✅ merged)
Both are dev-scope — neither ships in the tarball — and both were deferred out of the pre-2.0.0 sweep (#1837) precisely so a major upgrade wouldn't land immediately before an irreversible publish.
PR Issue What it does ✅ #1899 #1839 vitest/@vitest/browser/@vitest/browser-playwright/@vitest/coverage-v8→ 4.1.10, clearing GHSA-p63j-vcc4-9vmv (critical) plus two more. Storybook 10.2.19 → 10.5.5 to satisfy the exact peer pins.clients/webaudit 19 → 1.✅ #1900 #1838 eslint 10, clearing the brace-expansionDoS from all five lockfiles. Dropping@eslint/eslintrctookjs-yamlwith it, clearing a second high advisory for free.Two things worth carrying into the release notes: #1900 is larger than a version bump because eslint 10 surfaced 21 genuine violations — 15
preserve-caught-errorsites, 2 dead initializers, and 8react-hooks/set-state-in-effectsites — all fixed in code, with no rule disabled or scoped away. And the issue's predicted fix for #1839 does not work: even naming every vitest and Storybook package in onenpm i -DstillERESOLVEs, because npm resolves against the existing lock tree. Regeneratingclients/web/package-lock.jsonis the only mechanism, which is why that lock diff is large — it is the fix, not churn.✅ The merge-order hazard — resolved as specified. #1899 pinned
eslint-plugin-react-hooksto~7.0.1(tracked as #1897) as a deliberate hold; #1900 required 7.1.1, since 7.0.1's peer range stops at^9.0.0andERESOLVEs against eslint 10. Merging them in the wrong order would have dropped the version back and broken lint.#1899 was merged first (
23e0c39d), then #1900 (834fd2d7). As expected, #1900 wentCONFLICTINGthe moment #1899 landed; it was brought up to date with a merge fromv2/main, the~7.0.1hold removed, all five lockfiles regenerated from a clean rootnpm installrather than hand-merged, andnpm run cire-run green end to end (coverage gate, build gate, all smokes, 462 Storybook tests) before merging.Verified on
v2/mainafter the merge —clients/web/package.jsonnow declares"eslint-plugin-react-hooks": "^7.1.1"with no hold remaining, while #1899's other holds survived intact (zod: "~4.3.6", vitest4.1.10, Storybook 10.5.5 — those stay held; see v2.2.0 below). #1897 is closed, resolved by #1900 rather than worked separately.2. Tree-swap recovery — files
mainhad thatv2/maindidn't (3 PRs, all ✅ merged)The swap (#1830) replaced
main's tree with v2's, so anything living only in the old tree vanished from the released branch. Each of these is a restoration, and each is written so the next swap-like event can't repeat it.- ci: restore the Claude Code workflow and .mcp.json, lost in the v2 tree swap #1869 (Claude Code workflow (.github/workflows/claude.yml) and .mcp.json lost in the v2 tree swap — @claude no longer triggers #1850) —
.github/workflows/claude.yml+.mcp.json. This is why@claudehas been inert: workflows forissue_commentevents run from the default branch, so the restored workflow does nothing until this merge lands. - chore: restore repo files dropped by the v2 tree swap #1871 (Restore repo files dropped by the v2 tree swap (dependabot, issue template, .gitattributes, CoC) #1870) —
dependabot.yml, the legacy issue template,.gitattributes, Code of Conduct. - docs: restore SECURITY.md on v2/main, so the next tree swap can't drop it #1867 (SECURITY.md is missing from v2/main — the next milestone merge will drop it from main again #1864) —
SECURITY.md, restored onv2/mainspecifically so the next milestone merge can't drop it frommainagain.
3. Contribution model & branch policy (4 PRs, all ✅ merged)
The repo moved to Collaborators-only in the go-live, making issues the single intake channel for everyone outside the org. These make that legible.
- docs: post-swap branch model, PR policy, milestone rule, and Copilot review instructions #1866 (Post-swap docs: correct the branch model, PR policy, and stale AGENTS.md content #1873, Add .github/copilot-instructions.md so Copilot reviews against our actual conventions #1877) — post-swap branch model, PR policy, the milestone rule, and
.github/copilot-instructions.mdso Copilot reviews against our actual conventions. (docs: add .github/copilot-instructions.md and require mirroring into it #1878 was the standalone attempt; superseded and closed.) - docs: unpin the branch-pinned CONTRIBUTORS.md links in the PR template #1879 (docs: reconcile the v1 policy wording and fix branch-pinned PR-template links #1813) — reconciles the v1 policy wording and unpins branch-pinned links in the PR template.
- docs: rename CONTRIBUTORS.md to CONTRIBUTING.md so GitHub surfaces the policy #1884 (Rename CONTRIBUTORS.md → CONTRIBUTING.md so GitHub surfaces the contributing policy #1883) —
CONTRIBUTORS.md→CONTRIBUTING.md, so GitHub actually surfaces the policy in its contribution UI. - ✅ docs: replace the markdown bug template with GitHub issue forms #1894 (docs: add issue templates for the issues-only contribution model #1844) — replaces the legacy markdown bug template with GitHub issue forms (enforced required fields: client, version line, transport). Deliberately no security template — a
.ymltemplate still opens a public issue, which is the one thing a vulnerability report must not do; it wires a contact link to/security/advisories/newinstead.
4. Board & triage process (1 PR ✅ merged, plus board-only work)
- ✅ docs: add Incoming to both boards, adopt the v2 Priority field, and document the triage rubric #1892 (Boards: add an Incoming status to both boards, adopt the v2 Priority field, and document the triage rubric #1891, approved before merge) — adds Incoming to both boards, adopts the v2 Priority field, and documents the scoring rubric so triage is derived rather than asserted. Also hardens the
updateProjectV2Fieldhazard guidance and adds a snapshot/recovery recipe — that mutation full-replaces the option list and has orphaned every card on the board twice. - Triage the open issue backlog: backfill v1/v2 labels, close legacy, board the rest #1872, go-live 7/9: triage and bulk-close the v1 PR backlog #1819 closed with no PR — backlog triage and v1 PR-backlog closure were board actions, not code.
5. Protocol correctness — the only runtime change in the milestone (1 PR, ✅ merged)
- fix(core): mirror SEP-2243 x-mcp-header args to Mcp-Param-* on tools/call #1847 (tools/call omits SEP-2243 Mcp-Param-* headers (x-mcp-header mirroring), rejected by strict modern servers #1846) —
tools/callomitted the SEP-2243Mcp-Param-*headers, so strict modern servers rejected the call. The SDK only mirrors insideclient.callTool()and skips it in the browser; the Inspector routes throughclient.request()to drive MRTR manually, so it now builds the mirrored headers itself — on every client, web included.
6. Merge convergence — work whose only purpose was making this merge possible (2 PRs, ✅ merged)
- docs: converge README Test servers section with main's restructure #1880 (Converge README.md on v2/main with main's restructured Test servers section #1874) — converges the README
Test serverssection withmain's restructure. This is the PR that removes the one conflict hunk from the sequence below. - chore(deps): bring main's rc-series bumps to v2/main (@hono/node-server 2.x, vite 8.1.5) #1881 (Bring main's rc-series dependency bumps to v2/main (@hono/node-server 2.x, vite 8.1.5) #1875) — brings
main's rc-series bumps (@hono/node-server2.x, vite 8.1.5) ontov2/mainso the two branches don't disagree on dependencies at the merge point.
#1868 — the rejected alternative. A back-merge of
mainintov2/mainwould have made this merge conflict-free, but only by pulling 224 commits, 212 of them pre-swap v1-tree commits, permanently into the develop branch's ancestry. Closed unmerged. The three-line conflict is cheaper than that lineage, and it does not recur.7. Tooling reliability (1 PR, ✅ merged)
- ✅ fix(scripts): await the pack:verify web child's exit before removing its work dir #1895 (pack:verify: await the --web child's exit before removing its work dir #1826) —
pack:verifySIGTERM'd the--webchild andrmSync'd its work dir in the same breath. Extractsscripts/lib/child-cleanup.mjsso this file andsmoke-tui.mjs(fixed for the same race in fix: wait for the TUI to exit before removing its temp dir (#1801) #1814) can't drift apart again — which is the whole reason pack:verify: await the --web child's exit before removing its work dir #1826 existed as a follow-up.
Not in this milestone's payload
- docs: new modelcontextprotocol.io Inspector documentation for v2 (legacy vs. modern era, more screenshots) #1803 — the modelcontextprotocol.io docs rewrite. Lands in the
modelcontextprotocol/docsrepo, not this one; no PR exists yet. Tracked here for ownership only, so it does not gate the release. - test: cover v2/main mid-session-auth code to the ≥90% per-file gate (cliOAuth.ts, TUI App.tsx, AuthTab.tsx) #1610 / PR test: cover v2/main mid-session-auth code to the ≥90% per-file gate #1611 — carries the v2.1.0 milestone but merged 2026-07-06 and already shipped in 2.0.0 (its merge commit is an ancestor of
origin/main). A milestone-assignment artifact, not pending work. - plan: v2 go-live runbook — deprecate v1 (1.0.1), branch v1/main, swap main to v2, publish 2.0.0, close v1 backlog, restrict external PRs #1804 / go-live 7/9: triage and bulk-close the v1 PR backlog #1819 / go-live 8/9: post-swap docs, labels, boards, and integration hygiene #1821 — the go-live umbrellas. Their code shipped as 2.0.0 (chore: v1 deprecation notices in all four packages, bump to 1.0.1 #1827, chore: replace main's tree with v2 #1830); what remained here was docs and board hygiene, delivered by the PRs above.
Spun out of #1839 into v2.2.0 — not part of this release
Untangling the vitest peer knot floated two in-range dependencies that broke the gate for reasons unrelated to the security fix. Both were held rather than silently pinned, and both holds ship as-is in v2.1.0; the follow-up work is milestoned v2.2.0 and is not a candidate for this merge:
- chore(deps): zod 4.4 blows the tsc heap in clients/web — held at ~4.3.6 #1896 (v2.2.0, Medium) —
zodheld at~4.3.6inclients/weband the root; 4.4.3 blows thetsc -bheap. - chore(storybook): drop the now-redundant setProjectAnnotations call (Storybook >= 10.3 applies it automatically) #1898 (v2.2.0, Low) — Storybook 10.5.5 reports
setProjectAnnotationsas redundant. Deliberately not removed:./previewcarries the Mantine decorator and the a11y annotations that drive the play-function assertions, so if auto-provisioning missed either, the 462 stories would render unthemed and a11y-inert and still pass. A green run wouldn't prove it safe.
A third, #1897 (react-hooks 7.1), was also filed against v2.2.0 but got resolved early — #1900 was forced to take 7.1.1 regardless, so it closed with this milestone rather than waiting.
Release-note candidates
Only three items are user-visible; everything else is repo hygiene a consumer never sees:
tools/callnow sendsMcp-Param-*headers (fix(core): mirror SEP-2243 x-mcp-header args to Mcp-Param-* on tools/call #1847) — fixes rejection by strict modern servers.- Node engine floor
>=22.7.5→>=22.19.0(already called out in the pre-flight below). - Issue forms + issues-only contribution model (docs: replace the markdown bug template with GitHub issue forms #1894, docs: rename CONTRIBUTORS.md to CONTRIBUTING.md so GitHub surfaces the policy #1884) — changes how people report things.
- ci: restore the Claude Code workflow and .mcp.json, lost in the v2 tree swap #1869 (Claude Code workflow (.github/workflows/claude.yml) and .mcp.json lost in the v2 tree swap — @claude no longer triggers #1850) —
- linked a pull request that will close this issuechore: merge v2/main for the v2.1.0 milestone release #1903
on Aug 2, 2026
Execute the first
v2/main→mainmilestone merge and cut the v2.1.0 release.Everything is built and staged in PR #1903 — merge resolved, version bumped,
npm run cigreen. Only the steps below remain.Runbook
build.git push origin mainis rejected —mainis ruleset-protected (two rulesets on~DEFAULT_BRANCH; one grants bypass to nobody, admins included). Same reason nothing can be pushed tomaindirectly, including a version bump — which is why the bump already rides chore: merge v2/main for the v2.1.0 milestone release #1903 asdedee5af.2.1.0→ Create new tag on publish, Target =main→ Publish.Publishing is what publishes to npm: the
publishjob is gated ongithub.event_name == 'release', runspack:verify, asserts the tag matchespackage.json, and pushes thelatestdist-tag. (A tag push alone runsbuildbut never publishes.)>=22.7.5→>=22.19.0, fix(core): mirror SEP-2243 x-mcp-header args to Mcp-Param-* on tools/call #1847 (tools/callnow sends the SEP-2243Mcp-Param-*headers), and the issues-only contribution model (docs: replace the markdown bug template with GitHub issue forms #1894, docs: rename CONTRIBUTORS.md to CONTRIBUTING.md so GitHub surfaces the policy #1884). Those are the only user-visible changes; the rest is repo infrastructure. Full payload: milestone overview.References, notCloses.Then verify
@claudeworks again — comment@claude reviewon any open PR. Workflows forissue_commentrun from the default branch, so ci: restore the Claude Code workflow and .mcp.json, lost in the v2 tree swap #1869 was inert until this merge.v2/main, and security updates still appear (they ran whiledependabot.ymlwas missing — build(deps): bump the npm_and_yarn group across 5 directories with 12 updates #1833, build(deps): bump the npm_and_yarn group across 5 directories with 2 updates #1840 — buttarget-branchaffects security-update behavior, so confirm).Two things not to undo
Never back-merge
mainintov2/main. Built and rejected — #1868 (closed). It would pull 224 commits, 212 of them pre-swap v1-tree commits, permanently into the develop branch's ancestry. This is also why #1903's conflicts were resolved on a branch cut frommain: GitHub's web conflict editor commits to the head branch, which would have done exactly that.Never
--allow-unrelated-histories. The branches share merge base4d30d1cd, established by the go-live merge (#1830).After this, it's self-sustaining
Once
v2/main's tip is an ancestor ofmain, andmainmakes no content changes of its own, every subsequent milestone merge is conflict-free. This one was the exception: the branches had only just acquired a shared history, two PRs (#1880, #1881) existed purely to shrink the conflict set, and #1904 closed the last file wheremainheld contentv2/mainlacked. A normal milestone needs none of that.