From 079d90a2bf4c21881d9cc3e53bad3133515ffbca Mon Sep 17 00:00:00 2001 From: jun Date: Thu, 3 Sep 2026 16:59:39 +0900 Subject: [PATCH 1/5] docs(devlog): Muse release-train roadmap and audit round 1 Plans the v2.41.0 release train: a risk-classed regression review of the 36 commits on dev ahead of main, a Meta provider mark, and the preview/main publish path. The manual release path exists because the user forbade the local full suite for this unit, and scripts/release.ts runs it in preflight. Audit round 1 established that a hand-rolled path is strictly weaker than the helper unless it also proves the version unused before bumping, uses push-event CI on the exact 40-char SHA, and re-reads the live remote head before dispatch; all three are now in 030/040. 005 records the audit synthesis, including the one finding rebutted: the Terms-of-Service acknowledgement for high-risk OAuth providers is enforced in the browser rather than at /api/oauth/login. That is real, but it predates this work and applies identically to anthropic and google-antigravity, so it is recorded as a follow-up in 050 rather than folded into a release cycle. --- .../260903_muse_release_train/000_plan.md | 77 +++++++++++++++++ .../005_audit_round1_synthesis.md | 84 +++++++++++++++++++ .../010_wp1_regression_review.md | 43 ++++++++++ .../020_wp2_muse_mark.md | 74 ++++++++++++++++ .../030_wp3_preview_release.md | 78 +++++++++++++++++ .../040_wp4_main_release.md | 42 ++++++++++ .../050_followups.md | 33 ++++++++ 7 files changed, 431 insertions(+) create mode 100644 devlog/_plan/260903_muse_release_train/000_plan.md create mode 100644 devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md create mode 100644 devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md create mode 100644 devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md create mode 100644 devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md create mode 100644 devlog/_plan/260903_muse_release_train/040_wp4_main_release.md create mode 100644 devlog/_plan/260903_muse_release_train/050_followups.md diff --git a/devlog/_plan/260903_muse_release_train/000_plan.md b/devlog/_plan/260903_muse_release_train/000_plan.md new file mode 100644 index 0000000000..7891f4c6ff --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/000_plan.md @@ -0,0 +1,77 @@ +# 260903 — Muse release train: regression review, provider mark, v2.41.0 + +## Why this unit exists + +`origin/dev` is 36 commits ahead of `origin/main`, which still carries +`v2.40.0` (published 2026-09-02). Three of those commits are the Meta/Muse +line that landed today: + +- `878f75417` (#3317) — Muse Spark 1.3 registered on the 1.2 spec. +- `ff1ac6b8c` (#3321) — the direct Meta Model API provider (`meta-model`). +- `1aa839aa8` (#3337) — the `meta-muse` provider importing the Muse Code CLI + credential behind a Terms-of-Service warning. + +The user asked for three things, in order: regression-review the 36-commit +delta against `main`, give Muse a provider mark in the dashboard, and run the +release through to a published Meta/Muse-carrying version. + +## Constraints that shape every phase + +- **No local full suite.** `bun run test` and a bare `bun test` are forbidden + for this unit. Verification is focused `bun test `, `bun run typecheck`, + and exact-SHA GitHub CI (`ci.yml` + `service-lifecycle.yml`). +- That constraint is load-bearing on the release path. `scripts/release.ts` + runs the whole suite in its preflight, so the helper cannot be used here. + The release therefore takes the manual path the helper would otherwise + automate: bump on the release branch, wait for both exact-SHA workflows, + then `gh workflow run release.yml` with `version`/`tag`/`expected-sha`. +- `--no-verify` pushes are authorized; PRs target `dev` and merge with admin + once CI is green. +- `main` and `preview` carry rulesets requiring a pull request. Promotion is + by PR, not by push. + +## Work phases + +| Phase | Doc | Deliverable | +|-------|-----|-------------| +| wp0 | this unit | roadmap + review method (docs only) | +| wp1 | `010_wp1_regression_review.md` | per-commit regression record for all 36 commits | +| wp2 | `020_wp2_muse_mark.md` | Meta/Muse SVG + `provider-icons.ts` wiring | +| wp3 | `030_wp3_preview_release.md` | `preview` dist-tag publish, exact-SHA proof | +| wp4 | `040_wp4_main_release.md` | `latest` dist-tag publish, ancestry proof | + +wp1 and wp2 are independent of each other and both gate wp3. wp4 consumes +wp3's published preview. + +## Review method (wp1) + +A 36-commit delta is too large to re-derive from scratch, and re-reading every +diff line would produce a document nobody checks. The review is risk-classed +instead, and the class decides what evidence is required: + +- **R0 docs-only** — `devlog/` or `docs-site/` only. Evidence: the diff touches + no runtime path. No test needed. +- **R1 scoped runtime** — one subsystem, covered by a focused test file that + already exists. Evidence: the focused test passes at the dev head. +- **R2 cross-cutting** — touches routing, the model catalog, release + automation, or a shared contract. Evidence: focused tests plus a read of the + seam the change crosses. +- **R3 credential/security** — auth, tokens, OAuth, keychain, workflow + permissions. Evidence: line-level read of the credential path plus + `privacy:scan`. + +The Muse commits are R2 (#3317, #3321) and R3 (#3337). + +Three more are R3, corrected after audit round 1 (`005`): `7ce0ba518` (#3262) +grants `contents: write` and `pull-requests: write` to a reusable-workflow +call, `7a529a2e8` (#3318) changes `pull_request_target` processing — a declared +trust boundary in `.github/AGENTS.md` — and `3c7c021ec` (#3296) touches +provider credential admission. A workflow-permission grant is a credential +change even when the diff reads like plumbing, which is the hole the first +draft of this table had. + +## What "done" means here + +`main` carries the reviewed dev SHA, npm `latest` resolves to the stable +version built from it, and `ocx` users installing fresh get Muse Spark 1.3 +plus both Meta providers with a real mark in the dashboard. diff --git a/devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md b/devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md new file mode 100644 index 0000000000..22124bef94 --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/005_audit_round1_synthesis.md @@ -0,0 +1,84 @@ +# Audit round 1 — synthesis + +Reviewer: delegated read-only auditor (gpt-5.6-sol, high). Verdict: **FAIL**, +seven findings. Every one was re-derived against the tree before folding; the +outcome is six folded and one rebutted-with-a-carve-out. + +## 1. "ToS enforcement is bypassable" — REBUTTED as a release blocker, RECORDED as a known limit + +The reviewer is right about the mechanism and wrong about what it means for +this release. + +The mechanism, confirmed: `loginMetaMuse` emits its warning through the +optional `ctrl.n` progress callback (`src/oauth/meta-muse.ts:128`). The CLI's +own OAuth path wires that to `console.log` (`src/oauth/login-cli.ts:87`), so +`ocx login meta-muse` prints it. The management API's flow, by contrast, +installs `n: () => {}` (`src/oauth/index.ts:1720`) and drops it on the floor — +which means `POST /api/oauth/login` and `ocx account login meta-muse`, which +goes through that same endpoint, never surface the warning text. The GUI shows +`OAuthTosWarningModal` client-side, so the acknowledgement is enforced by the +browser, not by the server. + +Why it does not block: + +- **It is not a regression and not Muse-specific.** `n: () => {}` predates this + work by a long way, and `anthropic` and `google-antigravity` — the other two + `HIGH_RISK` ids in `gui/src/oauth-tos-risk.ts:10` — have carried exactly the + same client-side-only gate since `fbac9f05e`. Shipping v2.41.0 changes the + exposure for none of them. +- **The credential path itself is clean.** The reviewer looked for a leak and + found none: no Keychain stderr surfaced, no response bodies in errors, a fixed + public error vocabulary, atomic 0600 persistence. +- **The bypass requires the user's own admin token.** `/api/oauth/login` is + behind management auth. The actor who can call it is the account holder, who + is the only party the ToS warning protects, and who has already installed and + signed into the Muse Code CLI on that machine. + +What it is: a real server-side consent gap across all three high-risk +providers, worth its own unit. It is recorded here and in +`050_followups.md` rather than folded into a release cycle, because a +backend consent boundary is a behaviour change for `anthropic` and +`google-antigravity` users too, and that does not belong in a release train +the user asked to ship today. + +## 2-5. Release-path corrections — FOLDED + +All four are correct and all four are now in the phase docs: + +- **Version availability before the bump.** `scripts/release.ts:513` checks + unused-version and channel-forward ordering BEFORE mutating anything; the + workflow's own duplicate check at `release.yml:303` runs only after dispatch. + Doing this by hand means proving the version unused first, not discovering it + from a failed publish. Live state at audit time: `latest=2.40.0`, + `preview=2.40.0-preview.20260902`, `2.41.0` unused. +- **Exact-SHA is stricter than "CI passed".** `release-dispatch-guard.cjs:14` + requires a lowercase 40-char SHA, an allowed ref, a `workflow_dispatch` + event, and equality with `GITHUB_SHA`; `release.yml:222` requires a + successful **push-event** CI run on the release branch — PR CI does not + satisfy it. +- **`dev` already carries `2.41.0`.** `package.json:3`. The main bump in the + original 040 was a no-op step; promotion carries the version with it. The + post-release workflow is `dev-version-bump.yml`, and its PR moves `dev` to + `2.42.0`. +- **Publishing is OIDC Trusted Publishing.** `release.yml:119` (`id-token: + write`), `:153` (npm >= 11.5.1), `:285`. No `NPM_TOKEN`; verify provenance + and `gitHead` after publish. + +## 6. Risk classification — FOLDED + +`7ce0ba518` (#3262) grants `contents: write` + `pull-requests: write` +(`release.yml:67`) and `7a529a2e8` (#3318) changes `pull_request_target` +processing, a declared trust boundary (`.github/AGENTS.md:16`). Both move R2 -> +R3. `3c7c021ec` (#3296) touches provider credential admission and also gets R3. + +## 7. Icon wiring — FOLDED + +The set is `MASKED_PROVIDER_ICONS` (`gui/src/provider-icons.ts:188`), not +`MASKED_MARKS`; `020` named the client-side set by mistake. `meta.svg` carries +three gradients, so the masking question does not arise — the mark is colour and +is drawn as an image. Provenance goes in the asset README, and the two ids get +explicit assertions rather than relying on the generic wiring check. + +Reviewer's own non-blocking note, confirmed: no test enumerates every registry +provider's display name, and `tests/provider-workspace-data.test.ts` does not +need changing. diff --git a/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md b/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md new file mode 100644 index 0000000000..08bbc194aa --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md @@ -0,0 +1,43 @@ +# wp1 — Regression review of the 36 dev-ahead-of-main commits + +## Input + +`git log --oneline origin/main..origin/dev` at the head recorded in +`000_plan.md`. Baseline is `v2.40.0`. + +## Method + +Each commit gets one row: SHA, PR, risk class (R0-R3 per `000_plan.md`), the +evidence actually checked, and a verdict of `clean`, `accepted`, or `blocker`. +A `blocker` must be fixed on `dev` before wp3 starts; an `accepted` row must +say why the residual risk is tolerable in a release. + +Evidence is gathered without the full suite: + +- `git show --stat ` for the touch set of every commit. +- For R1/R2, the focused test file that owns the subsystem, run individually. +- For R3, a line-level read of the credential handling plus + `bun run privacy:scan`. +- `bun run typecheck` once at the dev head covers the type-level seams that a + per-commit read would otherwise have to reason about by hand. + +## Special attention: the Meta/Muse line + +Three questions decide whether this release is safe to publish: + +1. **Does `meta-muse` ever write the imported credential anywhere a log or a + scan can see it?** #3337's follow-up (`81c1ebe8c` on the feature branch, + squashed into `1aa839aa8`) redacts scanned secrets and bounds the Keychain + read. Verify the redaction covers the error paths, not just the happy path. +2. **Can the ToS warning be bypassed?** The provider is deliberately marked + unsupported; the warning is the only thing standing between a user and an + unauthorized use of their Muse Code subscription. A silent import would be + an `UNSAFE` terminal outcome for this unit. +3. **Does Muse Spark 1.3 leak into a provider that cannot serve it?** #3317 + added 1.3 on the 1.2 spec across the resellers; the registry must not + advertise 1.3 on a provider whose upstream roster lacks it. + +## Output + +`011_review_ledger.md` — the per-commit table. Written in wp1's B phase, not +here. diff --git a/devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md b/devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md new file mode 100644 index 0000000000..e9f775bcd2 --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/020_wp2_muse_mark.md @@ -0,0 +1,74 @@ +# wp2 — A Meta/Muse mark for the provider catalog + +## Current state + +`gui/src/provider-icons.ts` maps a provider id to a file under +`gui/public/provider-icons/`. Two ids landed today with no entry: + +- `meta-model` — the direct Meta Model API provider. +- `meta-muse` — the Muse Code CLI credential import. + +Neither has a row in `PROVIDER_ICON_ALIASES` or `PROVIDER_DISPLAY_NAMES`, so +the dashboard renders them with the generic fallback and an unlabelled id. +Every other first-class provider in that file carries both. + +## Change + +1. Add `gui/public/provider-icons/meta.svg` — the Meta infinity mark, lifted + from the `aria-label="Meta symbol"` inline SVG that `dev.meta.ai` renders in + its own navigation header, read through a signed-in browser session. This is + the vendor's first-party mark on the vendor's own developer console, which is + the same provenance standard every other entry in the asset README meets. + Meta publishes no `favicon.svg` (`dev.meta.ai/favicon.svg` and + `/icon.svg` both 404; the site's declared icon is a 32x32 `.ico`), so the + rendered header mark is the best available vector. + + Normalization applied, and nothing else: the three gradient ids are renamed + from React's generated `_r_d_`/`_r_e_`/`_r_f_` to stable + `meta-mark-a`/`-b`/`-c` (a generated id collides when several documents are + inlined), the presentational `height`/`width`/`role`/`aria-label` are + dropped in favour of the `viewBox`, and `xmlns` is added so the file stands + alone. Every `d` attribute and every stop colour is verbatim. +2. Alias both ids to it: + + ```ts + "meta-model": "meta.svg", + "meta-muse": "meta.svg", + ``` + +3. Add display names: + + ```ts + "meta-model": "Meta Model API", + "meta-muse": "Muse Code", + ``` + + `meta-muse` is named for what the user recognizes — the Muse Code + subscription whose credential it imports — not for its config id. +4. The mark carries three linear gradients in Meta brand blue + (#0064E0 -> #0278F1), so it does NOT join `MASKED_PROVIDER_ICONS` + (`gui/src/provider-icons.ts:188`) — that set is for single-ink neutral + artwork that vanishes against one theme, and masking would flatten a + gradient to one ink. `gui/tests/provider-marks-assets.test.ts` enforces both + directions, so this is checked rather than asserted. + + (The first draft of this doc named `MASKED_MARKS`, which is the client-side + set in `gui/src/components/integration-marks.ts`. Audit round 1 caught it.) + +## Verification + +- `bun run typecheck` (the alias maps are typed `Record`; a + duplicate key is a type-level no-op, so the real check is the test below). +- `bun test tests/provider-icons.test.ts tests/provider-marks-assets.test.ts` + from `gui/`. The generic checks already cover a missing file and an unwired + committed asset; an explicit assertion pins the two new ids by intent, the + way the MiniMax/MiMo rows are pinned. +- Provenance recorded in `gui/public/provider-icons/README.md`. That file is + the only place a later reader can learn where a mark came from, and an + undocumented asset is indistinguishable from an invented one. +- `tests/provider-workspace-data.test.ts` needs no change: nothing enumerates + every registry provider's display name (confirmed in audit round 1). + +## Out of scope + +Re-theming the catalog, touching other marks, and any docs-site asset. diff --git a/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md b/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md new file mode 100644 index 0000000000..5a89244a4a --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md @@ -0,0 +1,78 @@ +# wp3 — Preview release (`preview` dist-tag) + +## Why the helper cannot run + +`scripts/release.ts` preflight runs `bun test --isolate tests` plus seven +isolated files. The user forbade the local suite for this unit, so invoking +the helper would violate the constraint before it reached the bump. The helper +is not broken; it is simply out of bounds here. + +## The manual path + +Everything the helper does after its preflight is reproducible by hand, and +each step keeps its own gate. Steps 0 and 4 were added after audit round 1 +(`005`); without them this path is strictly weaker than the helper it replaces. + +0. **Prove the version is available BEFORE mutating anything.** The helper does + this at `scripts/release.ts:513` — unused on npm, no existing tag or GitHub + release, and greater than what the channel currently carries. The workflow's + own duplicate check (`release.yml:303`) fires only after dispatch and never + checks channel ordering, so skipping this means learning about a collision + from a failed publish with the bump already pushed. + `npm view @bitkyc08/opencodex versions --json` plus `gh release view`. +1. Open a promotion PR from a branch **pinned to the reviewed SHA** (not the + moving `dev` ref) into `preview`, and merge it with admin. `preview` is + protected by a ruleset requiring a reviewed pull request, so promotion is by + PR; #3260/#3261 and #3123/#3125 are the precedent. Expect `enforce-target` + to flag the base — a promotion PR is exactly the case that check is not + written for — and record the admin bypass rather than waiting for green. +2. `dev` already carries `2.41.0` (`package.json:3`), so the preview channel + needs the prerelease suffix and nothing else: bump to + `2.41.0-preview.` in a second PR onto `preview`. `release.ts` + enforces the `-preview.` infix; the workflow enforces `version` equals + `package.json`. +3. Record the release SHA (`preview` head after the bump merges) as the full + lowercase 40-character hash. `release-dispatch-guard.cjs:14` rejects a short + or upper-case SHA outright. +4. Wait for `ci.yml` AND `service-lifecycle.yml` to succeed on that exact SHA, + **as push-event runs on `preview`** — `release.yml:222` will not accept the + PR-event run that produced the same tree. The bump touches `package.json`, + which is a service-lifecycle trigger path, and `release.yml`'s service gate + requires an already-successful lifecycle run for the release SHA, so + dispatching early races it. +5. Re-read the LIVE remote head (`git ls-remote origin preview`) and confirm it + still equals the release SHA. The helper does this immediately before + dispatch for a reason: `workflow_dispatch` resolves a mutable branch. +6. `gh workflow run release.yml --ref preview -f version= -f tag=preview + -f expected-sha= -f dry-run=false`. +7. Watch the run; verify `npm view @bitkyc08/opencodex dist-tags --json` moves + `preview`, and that the GitHub prerelease tag resolves to the release SHA. + +## Publishing is tokenless + +There is no `NPM_TOKEN` to supply and none may be introduced. Publication runs +under OIDC Trusted Publishing: `id-token: write` (`release.yml:119`), npm +>= 11.5.1 (`:153`), and an npm Trusted Publisher binding for this repository and +workflow (`:285`). A failure there is a registry-side configuration problem, +not something to route around with a credential. `concurrency: group: release` +is shared with the stable publish, so the two channels serialize. + +## Failure handling + +If the dispatch fails after the bump is already pushed, do not re-bump. Re-run +the failed workflow once, confirm the remote SHA did not move, and re-dispatch +with the same `expected-sha`. The `validate-dispatch` job refuses a dispatch +whose `expected-sha` does not equal `GITHUB_SHA`, which is exactly the guard +that makes a re-dispatch safe. + +That reuse is for a TRANSIENT failure — a runner fault, a flaked job, a race +with the lifecycle gate. If the publish actually reached the registry, the +version is spent: npm forbids republishing it, so the recovery is a new +version, not a retry. Check `npm view` before deciding which case you are in. + +## Note on the automatic dev bump + +`release.yml` calls `dev-version-bump.yml` after a non-dry-run publish. For a +preview publish it usually returns `changed=false` because `dev` already +carries the stable core. Expect that, and do not treat the skipped bump PR as +a failure. diff --git a/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md b/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md new file mode 100644 index 0000000000..a9ace76bdb --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md @@ -0,0 +1,42 @@ +# wp4 — Main release (`latest` dist-tag) and ancestry proof + +## Sequence + +0. Prove `2.41.0` is still unused on npm and carries no tag or GitHub release + (same check as wp3 step 0, re-run because the preview publish happened in + between). +1. Open a promotion PR from a branch **pinned to the reviewed SHA** into + `main`; `main` is protected the same way `preview` is. Merge with admin, + recording the `enforce-target` bypass. +2. **No bump is needed.** `dev` already carries `2.41.0` (`package.json:3`), so + the promotion brings the stable version with it. The original draft of this + doc prescribed a bump PR; audit round 1 established it would be a no-op that + `npm version` rejects as "Version not changed". +3. Wait for exact-SHA `ci.yml` and `service-lifecycle.yml` success on the + `main` head, as **push-event** runs (`release.yml:222`), then re-read + `git ls-remote origin main` immediately before dispatch. +4. `gh workflow run release.yml --ref main -f version=2.41.0 -f tag=latest + -f expected-sha= -f dry-run=false`. + +## Proof required before claiming DONE + +- `npm view @bitkyc08/opencodex dist-tags --json` shows `latest` at the + published stable version. +- The published version carries npm provenance and a `gitHead` matching the + release SHA. Publication is tokenless OIDC Trusted Publishing + (`release.yml:119`, `:153`, `:285`); provenance is the artifact-side proof + that the tarball came from this workflow on this repository. +- `gh release view v2.41.0` exists and its tag resolves to the release SHA. +- `git merge-base --is-ancestor origin/main` exits 0. This + is the check that distinguishes "main moved" from "main carries the work + that was reviewed" — a green release run proves neither by itself. +- The Muse code is actually in the published artifact, not merely in the tag: + the tarball must contain the `meta-muse` provider entry. A tag pointing at + the right SHA and a tarball built from it are separate facts. + +## After publish + +`dev-version-bump.yml` (called by `release.yml`'s `bump-dev-version` job) +opens a PR moving `dev` to `2.42.0`. Merge it so `dev` does not sit on an +already-published version — that stale state is what #3265 had to repair after +v2.40.0. diff --git a/devlog/_plan/260903_muse_release_train/050_followups.md b/devlog/_plan/260903_muse_release_train/050_followups.md new file mode 100644 index 0000000000..ccc4ef033e --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/050_followups.md @@ -0,0 +1,33 @@ +# Follow-ups this unit deliberately does not do + +## Server-side consent for high-risk OAuth providers + +Audit round 1 (`005` §1) established that the Terms-of-Service acknowledgement +for `HIGH_RISK` providers is enforced in the browser, not at the API boundary: + +- `gui/src/oauth-tos-risk.ts:10` lists `anthropic`, `google-antigravity`, and + `meta-muse`, and `OAuthTosWarningModal` gates the GUI button. +- `POST /api/oauth/login` performs no acknowledgement check, and the controller + it builds installs `n: () => {}` (`src/oauth/index.ts:1720`), so even the + provider's own warning text is discarded on that path. +- `ocx account login ` posts to that endpoint + (`src/cli/account-auth.ts:142`), so it inherits the gap. The older + `ocx login ` path does print the warning, because + `src/oauth/login-cli.ts:87` wires `n` to `console.log`. + +This is pre-existing and provider-wide, not introduced by the Muse work, which +is why it is not a v2.41.0 blocker. It is still a real gap and should get its +own unit: move the acknowledgement to the backend so every entry point is +covered, with the acknowledgement recorded per provider rather than per browser +session. + +The design question that unit has to answer first: an acknowledgement gate on +`/api/oauth/login` changes behaviour for `anthropic` and `google-antigravity` +logins that work today, so it needs a migration story rather than a flag flip. + +## Muse subscription usage display + +`050_wp5_passive_muse_quota.md` in the `260903_muse_spark_plan_oauth` unit +records that Meta emits subscription window usage inside streaming responses +and that OpenCodex does not yet read it. The provider note says so plainly. +Unchanged by this release. From 156188280366d471a1e1e8527aed5e4987eda3a3 Mon Sep 17 00:00:00 2001 From: jun Date: Thu, 3 Sep 2026 16:59:56 +0900 Subject: [PATCH 2/5] feat(gui): give both Meta providers their brand mark meta-model and meta-muse landed today with no icon alias and no display name, so the dashboard drew them as an initial tile with a raw config id underneath. Every other first-class provider carries both. meta.svg is the aria-label="Meta symbol" inline SVG that dev.meta.ai renders in its own navigation header, read through a signed-in session. Meta publishes no square vector at the conventional paths -- favicon.svg, icon.svg and logo.svg all 404 on that host, and the declared icon is a 32x32 .ico -- so the rendered header mark is the first-party vector, taken from the developer console these two providers actually belong to. Path data and gradient stops are verbatim. React's generated gradient ids (_r_d_, _r_e_, _r_f_) are renamed to meta-mark-a/-b/-c because a generated id collides when several marks are inlined into one document, which is the same reason minimax.svg renamed its unnamed-gradient id; the presentational height/width/role/aria-label give way to the viewBox, and xmlns is added so the file stands alone. One brand, two credentials: meta-model is Meta's own pay-as-you-go Model API, meta-muse imports the Muse Code CLI credential. Sharing one asset is the same shape as the three Alibaba plan ids. The mark is not masked -- three linear gradients in Meta brand blue, and masking flattens a gradient to one ink. The generic wiring check only fires when an asset named after the provider id is already committed, and neither id is "meta", so a dropped alias row would restore the fallback tile silently. Both are pinned explicitly instead. --- gui/public/provider-icons/README.md | 24 ++++++++++++++++++++++++ gui/public/provider-icons/meta.svg | 1 + gui/src/provider-icons.ts | 4 ++++ gui/tests/provider-icons.test.ts | 18 ++++++++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 gui/public/provider-icons/meta.svg diff --git a/gui/public/provider-icons/README.md b/gui/public/provider-icons/README.md index 43b1e1a92e..1fc7c57857 100644 --- a/gui/public/provider-icons/README.md +++ b/gui/public/provider-icons/README.md @@ -222,3 +222,27 @@ neighbouring brand's, is a misattribution that outlives the commit. `zhipu-bigmodel` and `zhipu-bigmodel-coding` share `zai.svg`: Z.AI and BigModel are the same company, and the mainland console publishes only the wordmark. + +## Meta (2026-09-03) + +- `meta.svg` — the `aria-label="Meta symbol"` inline SVG that `dev.meta.ai` + renders in its own navigation header, read 2026-09-03 through a signed-in + browser session. Meta publishes no square vector at the conventional paths: + `dev.meta.ai/favicon.svg`, `/icon.svg` and `/logo.svg` all 404, and the + site's declared icon is a 32x32 `.ico` on `static.xx.fbcdn.net`. The rendered + header mark is therefore the first-party vector, taken from the developer + console the two providers actually belong to. + + Path data and gradient stops are verbatim. Three normalizations: React's + generated gradient ids (`_r_d_`, `_r_e_`, `_r_f_`) become + `meta-mark-a/-b/-c`, because a generated id collides when several marks are + inlined into one document — the same reason `minimax.svg` renamed its + `未命名的渐变_6`; the presentational `height`/`width`/`role`/`aria-label` + are dropped in favour of the `viewBox`; and `xmlns` is added so the file + stands alone. + + Wired to both `meta-model` (the direct Meta Model API provider) and + `meta-muse` (the Muse Code credential import). One brand, two credentials — + the same shape as the three Alibaba ids sharing `alibaba-color.svg`. + **Not masked:** three linear gradients in Meta brand blue + (#0064E0 -> #0278F1), and masking flattens a gradient to a single ink. diff --git a/gui/public/provider-icons/meta.svg b/gui/public/provider-icons/meta.svg new file mode 100644 index 0000000000..59d5570e57 --- /dev/null +++ b/gui/public/provider-icons/meta.svg @@ -0,0 +1 @@ + diff --git a/gui/src/provider-icons.ts b/gui/src/provider-icons.ts index 3192862648..b99cbacd8c 100644 --- a/gui/src/provider-icons.ts +++ b/gui/src/provider-icons.ts @@ -27,6 +27,8 @@ const PROVIDER_ICON_ALIASES: Record = { "kimi-code": "kimi-color.svg", kiro: "kiro-color.svg", "lm-studio": "lm-studio-color.svg", + "meta-model": "meta.svg", + "meta-muse": "meta.svg", mistral: "mistral-color.svg", minimax: "minimax.svg", "minimax-cn": "minimax.svg", @@ -121,6 +123,8 @@ const PROVIDER_DISPLAY_NAMES: Record = { "opencode-zen": "OpenCode Zen", mistral: "Mistral", groq: "Groq", + "meta-model": "Meta Model API", + "meta-muse": "Muse Code", alibaba: "Alibaba Coding Plan", "alibaba-token-plan": "Alibaba Token Plan", "alibaba-token-plan-intl": "Alibaba Token Plan (Intl)", diff --git a/gui/tests/provider-icons.test.ts b/gui/tests/provider-icons.test.ts index 5a0495c453..b0609255b8 100644 --- a/gui/tests/provider-icons.test.ts +++ b/gui/tests/provider-icons.test.ts @@ -74,3 +74,21 @@ test("the MiniMax and Xiaomi MiMo provider ids resolve to their brand's mark", ( // The precedent that makes the two above consistent rather than novel. expect(providerIconSrc("mimo-free")).toBe("/provider-icons/xiaomi-color.svg"); }); + +/* + * One brand, two credentials. + * + * `meta-model` is Meta's own pay-as-you-go Model API and `meta-muse` imports the + * Muse Code CLI's credential. They are separate providers with separate billing + * and separate ToS risk, but they are the same company's mark -- the same shape + * as the three Alibaba plan ids sharing one asset. + * + * Pinned explicitly rather than left to the generic wiring check above, because + * that check only fires when an asset named after the id is already committed. + * Neither id is `meta`, so a dropped alias row here would restore the fallback + * tile silently. + */ +test("both Meta provider ids resolve to the Meta mark", () => { + expect(providerIconSrc("meta-model")).toBe("/provider-icons/meta.svg"); + expect(providerIconSrc("meta-muse")).toBe("/provider-icons/meta.svg"); +}); From 4ceeb2233722d7f11b45aac1d13f2f9bcde8e0bc Mon Sep 17 00:00:00 2001 From: jun Date: Thu, 3 Sep 2026 17:01:31 +0900 Subject: [PATCH 3/5] docs(devlog): screenshot of the Meta provider mark The provider rail before and after, on both tile surfaces. The fallback tile and the new mark are the same 19px box, which is the only way to see that a gradient mark actually reads at that size on #303030 and #f4f4f4 both. --- .../021_meta_mark_screenshot.png | Bin 0 -> 5984 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 devlog/_plan/260903_muse_release_train/021_meta_mark_screenshot.png diff --git a/devlog/_plan/260903_muse_release_train/021_meta_mark_screenshot.png b/devlog/_plan/260903_muse_release_train/021_meta_mark_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c0b3da692021ad9194dc9901fd4c5ff189c2da26 GIT binary patch literal 5984 zcmeAS@N?(olHy`uVBq!ia0y~yU|qn#z{tYE1QZEmU?~A|f-{|+13aCb6#|O#(=u~X z85lGs)=u2--4rO&ChyC!TGc2@DyE4|+qhn2%Zi08yo*?`efy~?wz9USPVlU5uI^t^ z-isIZ3v6ZIy2b2@3d`BO;oNIi6z`nV{$1u=^$zhrf~O0m8Z}I!=S81MKlJqN+z%hL z9or<1yPw!}+izFE4Tq0Y_aFb={7d9}aoV-_zxE5Rxpr$?k^A+{ve%j8Lt`vH$sE4^ zXu|5HN6On?+twMyyewRPdvoNOCVPc>Yk3N48)o-9|Gs=Z$0jU0cj}bWyDmSmys+}} zf@4go-)A{PJ(Jhh7ThH1E7W!OHRyq6sPtEszWqRw+%v|GDoBNe*U0lME``6R- zww}_wV`nujyHxeF)4wRTdupM(8#>b^Y$sZ!o|1oK7Hlo|;?hrGC|jgPc&7RKGH3zW z91M)@nG7sI79$W#0Wkvu(*h=#2W1YE45BTbE{-7;jBjrQ@-is! zI6FEV{*xU$;o~G{#=Y-Wm#6i)H!v_Vv2X||I6xU1+%yBpm z(J~aJW*99)QLB*AN@=u~M=ml)n@y;#!_ju=Xgd_8c|O{(M{aM9_LN4uo+#}rj?un6 vYIuzH Date: Thu, 3 Sep 2026 17:04:14 +0900 Subject: [PATCH 4/5] docs(devlog): per-commit regression review of the 36-commit release delta Every commit dev carries ahead of main, risk-classed and evidenced. Four commits get R3 treatment: the Muse credential import, the reusable-workflow permission grant, the pull_request_target attribution gate, and the provider editor's field-admission policy. All four are clean on a line-level read. 1387 focused assertions across 38 test files, zero failures, plus typecheck and privacy:scan at the dev head. The full suite was not run, at the maintainer's instruction for this unit. One accepted residual, not a blocker: the ToS acknowledgement for high-risk OAuth providers is enforced in the browser rather than at /api/oauth/login. It predates this delta and applies identically to anthropic and google-antigravity, so publishing 2.41.0 changes nobody's exposure. --- .../011_review_ledger.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 devlog/_plan/260903_muse_release_train/011_review_ledger.md diff --git a/devlog/_plan/260903_muse_release_train/011_review_ledger.md b/devlog/_plan/260903_muse_release_train/011_review_ledger.md new file mode 100644 index 0000000000..c0bc56806c --- /dev/null +++ b/devlog/_plan/260903_muse_release_train/011_review_ledger.md @@ -0,0 +1,69 @@ +# wp1 — Per-commit regression review (origin/main..origin/dev) + +Baseline `v2.40.0` (`origin/main`). 36 commits. Risk classes and method: +`000_plan.md`, as corrected by audit round 1 (`005`). + +Focused suites run for this review, all at the dev head, none of them the full +suite (forbidden for this unit): + +| Batch | Files | Result | +|-------|-------|--------| +| Meta/Muse | `meta-muse-oauth`, `meta-model-api-provider`, `privacy-scan-meta-key`, `muse-spark-web-search-compat`, `opencode-go-muse-context`, `opencode-go-muse-vision`, `command-code-provider` | 97 pass / 0 fail | +| Combos + usage | `combos`, `kiro-pool-rank`, `server-combo-failover-e2e`, `usage-aggregate-cache`, `usage-ledger-scanner`, `usage-summary`, `api-key-attribution` | 274 pass / 0 fail | +| Cursor + catalog + CI | `cursor-catalog`, `cursor-claude-id`, `cursor-effort-rows`, `cursor-effort-table`, `cursor-display-names`, `cursor-discovery`, `codex-catalog`, `provider-config-batch-management`, `ci-workflows` | 500 pass / 0 fail | +| Responses + CLI + integrations | `responses-state`, `legacy-shell-compat`, `responses-custom-tool-repair`, `chat-completions-endpoint`, `claude-cli`, `cli-status-json`, `api-keys-routes`, `remote-catalog`, `client-connect`, `integrations-writer`, `grok-sync`, `codex-desired-state` | 498 pass / 0 fail | +| GUI marks | `provider-icons`, `provider-marks-assets`, `integration-marks` | 18 pass / 0 fail | + +Total 1387 focused assertions' worth of files, zero failures. Plus +`bun run typecheck` exit 0 and `bun run privacy:scan` passed at the dev head. + +## R3 — credential and workflow-permission changes + +| SHA | PR | What it does | Evidence | Verdict | +|-----|----|--------------|----------|---------| +| `1aa839aa8` | #3337 | `meta-muse` provider importing the Muse Code CLI credential | Line-level read of `src/oauth/meta-muse.ts`. The credential never reaches an error string: Keychain stderr is discarded, the `security` child is killed on timeout, a rejected key produces `HTTP ` with no body, and the format check refuses anything that is not `LLM||`. `refreshMetaMuseToken` deliberately does not re-read the Keychain, so a `muse login` with a different account cannot silently overwrite a stored slot. 27 tests. `privacy-scan-meta-key` covers the scanner. | clean | +| `7ce0ba518` | #3262 | grants `contents: write` + `pull-requests: write` to the `bump-dev-version` call | Full diff read: 8 added lines, all inside the one job. The grant equals what `dev-version-bump.yml`'s own job already declares — a reusable-workflow call cannot give the callee more than the caller holds, which is why both v2.40.0 dispatches died at `startup_failure`. No other job in the file gains anything, and the callee is a repository-local path, not a third-party action. | clean | +| `7a529a2e8` | #3318 | `missing_coauthor_credit` gate; changes `pull_request_target` processing | The new code runs in `enforce-pr-target.yml` and `pr-hygiene.yml`, both privileged contexts. It reads `pr.title`, `pr.body` and commit messages and passes them to `resolveReferencedAuthors`, which resolves them through the GitHub API — untrusted text is used as a lookup key, never interpolated into shell. `tests/ci-workflows.test.ts` (part of the 500-pass batch) asserts no dispatch input reaches shell source. Fail-open on lookup failure, capped at five per run. | clean | +| `3c7c021ec` | #3296 | atomic dashboard provider-editor save; provider field admission | `PROVIDER_CONFIG_FIELD_POLICY` in `src/server/auth-cors.ts` classifies every `OcxProviderConfig` field as `editor`, `redacted`, or `runtime`, with `satisfies Record` so a newly added field fails typecheck until classified. `apiKey` and `apiKeyPool` are `redacted`; MCP and desktop-executor blocks are redacted whole because both carry arbitrary env and headers. This is a tightening, not a loosening: it replaces an allowlist that had been inadequate. 356 lines of new tests in `provider-config-batch-management`. | clean | + +## R2 — cross-cutting + +| SHA | PR | Seam crossed | Verdict | +|-----|----|--------------|---------| +| `878f75417` | #3317 | model catalog: Muse Spark 1.3 on the 1.2 spec across command-code and opencode-go | clean — 1.3 is registered only where the reseller actually serves it; `opencode-go` keeps only the contributor tier, matching its roster. Vision and context tests pin both. | +| `ff1ac6b8c` | #3321 | provider registry + pricing: the direct `meta-model` provider | clean — id chosen as `meta-model` specifically so it cannot capture the live `meta/` selector prefix at `router.ts`, and so it derives `META_MODEL_API_KEY` rather than the CLI's `META_API_KEY`. Parity test updated in the same commit. | +| `3d3c4fe26` | #3286 | model catalog across Antigravity, Google, sidecar | clean — closed out by its own devlog unit (`f0bbaaf6a`), catalog tests green. | +| `862e914c2` | #3274 | `/v1/models` row shape (`max_output_tokens`) | clean — contract tests updated across five files in the same commit. | +| `410a48a4f` | #3275 | Cursor Claude-id normalizer replacing three seeds | clean — 85 new assertions in `cursor-claude-id`, plus catalog and pricing tests. | +| `bc8ea072d`, `2ab9d9486`, `7ce713e8d` | #3273, #3276, #3277 | Cursor effort table: bundle read, opt-in rows, GUI provenance | clean — 314 assertions in `cursor-effort-rows`, locale parity enforced for the new strings. | +| `85d40ca35` | #3270 | usage aggregation rewritten to an incremental ledger scan | clean — the largest change in the delta (1570 lines in `usage/summary.ts`), and the one with the most new coverage: `usage-aggregate-cache` (301) and `usage-ledger-scanner` (498) are both new files. Management-API docs updated in the same commit. | +| `e9a5b0f13`, `2e74a35d4`, `6b2dfde11`, `fd324dc88` | #3298, #3302, #3294, #3256 | combo failover and quota admission, touching `server/responses/core.ts` | clean — all four land in the same subsystem and are covered together by the 274-pass combos batch. | +| `938c0136a` | #3246 | tool-bridge shape for `write_stdin` | clean — repair and undeclared-tool guards both extended. | +| `b3e205e99` | #3309 | integrations: hub clients routed through loopback | clean — a narrowing; three integration test files extended. | +| `ee24bab40` | #3269 | `service-lifecycle` triggers on `release.yml` | clean, and load-bearing for this very release: it is why a workflow-only change still trips the lifecycle gate. | +| `272ff6b11` | #3265 | moved `dev` to 2.41.0 after v2.40.0 | clean — this is the version the release train is about to publish. | + +## R1 — scoped runtime + +`38f8a8164` (#3330 Cursor picker slug), `472c785c2` (#3308 status URL), +`906511f73` (#3310 connect timeout), `eac662eb1` (#3307 rotation time), +`4cf3e9187` (#3297 liveness retry), `34c9e9802` (#3289 responses-state write +storm), `b0a42ca2f` (#3254 chat-native send budget), `fc08fc2f7` (#3290 log +panel jitter), `15b43e51c` (#3301 hermetic E2E). Each is one subsystem with its +focused test extended in the same commit; all are inside the batches above. +**clean.** + +## R0 — docs only + +`bb27c26be`, `af314b0a7`, `f0bbaaf6a`, `529639a57`, `345e2175c`, +`7424719ab`. Devlog and docs-site only; no runtime path touched. **clean.** + +## Findings + +**No blockers.** One accepted residual, carried from audit round 1 (`005` §1) +and detailed in `050_followups.md`: the Terms-of-Service acknowledgement for +`HIGH_RISK` OAuth providers is enforced client-side, so `POST +/api/oauth/login` and `ocx account login` do not surface it. Accepted for this +release because it predates the delta, applies identically to `anthropic` and +`google-antigravity`, sits behind management auth, and involves no credential +disclosure. Publishing v2.41.0 does not change that exposure for anyone. From eff903b75ddf5e0d61e03d44cc05dc3cc74e4ed5 Mon Sep 17 00:00:00 2001 From: jun Date: Thu, 3 Sep 2026 17:17:33 +0900 Subject: [PATCH 5/5] docs(devlog): fold the CodeRabbit review into the release plan Six findings, all correct. The ledger now carries one row per SHA. The grouped Cursor and combos rows hid exactly what a per-commit ledger exists to show: 2ab9d9486 touches four request paths while its neighbours touch one file each, and that is not visible from a row reading "clean, covered by the batch". R1 and R0 become tables for the same reason. The credential format example moves out of its table cell. Two pipe characters inside a five-column row are delimiters to a Markdown parser, so the verdict column was not where it claimed to be. The version preflights become blocking commands rather than commands that retrieve data. The channel-forward check has no automated equivalent: release.yml compares only for exact-version duplication, so nothing in CI stops a dist-tag being moved backwards. The ancestry proof fetches main first. git ls-remote reads the remote without updating origin/main, so the merge-base could have evaluated stale history. The artifact check covers meta-model, meta-muse and meta.svg rather than one of the three, and the ToS verdict rule is now stated as a rule instead of leaving a later reader to infer why one bypass was accepted. --- .../010_wp1_regression_review.md | 17 ++++++- .../011_review_ledger.md | 50 ++++++++++++++----- .../030_wp3_preview_release.md | 22 +++++++- .../040_wp4_main_release.md | 30 +++++++---- 4 files changed, 95 insertions(+), 24 deletions(-) diff --git a/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md b/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md index 08bbc194aa..140b37598d 100644 --- a/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md +++ b/devlog/_plan/260903_muse_release_train/010_wp1_regression_review.md @@ -31,8 +31,21 @@ Three questions decide whether this release is safe to publish: read. Verify the redaction covers the error paths, not just the happy path. 2. **Can the ToS warning be bypassed?** The provider is deliberately marked unsupported; the warning is the only thing standing between a user and an - unauthorized use of their Muse Code subscription. A silent import would be - an `UNSAFE` terminal outcome for this unit. + unauthorized use of their Muse Code subscription. + + The verdict rule, so a later reader reaches the same decision this unit + did. A bypass is `UNSAFE` and blocks the release when it is EITHER of: + + - a **new** bypass introduced by a commit in this delta, or + - any path that **discloses the credential** (a log line, an error body, a + serialized config field). + + A bypass is **accepted** only when all three hold: it predates the delta, + it applies identically to the other `HIGH_RISK` providers rather than + singling out `meta-muse`, and it is recorded in `050_followups.md` with + the file:line evidence. That is exactly one case here — the client-side-only + acknowledgement on `POST /api/oauth/login` — and `005` §1 is why it + qualifies. Anything that does not meet all three is `UNSAFE`. 3. **Does Muse Spark 1.3 leak into a provider that cannot serve it?** #3317 added 1.3 on the 1.2 spec across the resellers; the registry must not advertise 1.3 on a provider whose upstream roster lacks it. diff --git a/devlog/_plan/260903_muse_release_train/011_review_ledger.md b/devlog/_plan/260903_muse_release_train/011_review_ledger.md index c0bc56806c..94d4661b19 100644 --- a/devlog/_plan/260903_muse_release_train/011_review_ledger.md +++ b/devlog/_plan/260903_muse_release_train/011_review_ledger.md @@ -21,11 +21,18 @@ Total 1387 focused assertions' worth of files, zero failures. Plus | SHA | PR | What it does | Evidence | Verdict | |-----|----|--------------|----------|---------| -| `1aa839aa8` | #3337 | `meta-muse` provider importing the Muse Code CLI credential | Line-level read of `src/oauth/meta-muse.ts`. The credential never reaches an error string: Keychain stderr is discarded, the `security` child is killed on timeout, a rejected key produces `HTTP ` with no body, and the format check refuses anything that is not `LLM||`. `refreshMetaMuseToken` deliberately does not re-read the Keychain, so a `muse login` with a different account cannot silently overwrite a stored slot. 27 tests. `privacy-scan-meta-key` covers the scanner. | clean | +| `1aa839aa8` | #3337 | `meta-muse` provider importing the Muse Code CLI credential | Line-level read of `src/oauth/meta-muse.ts`. The credential never reaches an error string: Keychain stderr is discarded, the `security` child is killed on timeout, a rejected key produces `HTTP ` with no body, and the format check refuses anything not matching the Meta key shape (see below the table). `refreshMetaMuseToken` deliberately does not re-read the Keychain, so a `muse login` with a different account cannot silently overwrite a stored slot. 27 tests. `privacy-scan-meta-key` covers the scanner. | clean | | `7ce0ba518` | #3262 | grants `contents: write` + `pull-requests: write` to the `bump-dev-version` call | Full diff read: 8 added lines, all inside the one job. The grant equals what `dev-version-bump.yml`'s own job already declares — a reusable-workflow call cannot give the callee more than the caller holds, which is why both v2.40.0 dispatches died at `startup_failure`. No other job in the file gains anything, and the callee is a repository-local path, not a third-party action. | clean | | `7a529a2e8` | #3318 | `missing_coauthor_credit` gate; changes `pull_request_target` processing | The new code runs in `enforce-pr-target.yml` and `pr-hygiene.yml`, both privileged contexts. It reads `pr.title`, `pr.body` and commit messages and passes them to `resolveReferencedAuthors`, which resolves them through the GitHub API — untrusted text is used as a lookup key, never interpolated into shell. `tests/ci-workflows.test.ts` (part of the 500-pass batch) asserts no dispatch input reaches shell source. Fail-open on lookup failure, capped at five per run. | clean | | `3c7c021ec` | #3296 | atomic dashboard provider-editor save; provider field admission | `PROVIDER_CONFIG_FIELD_POLICY` in `src/server/auth-cors.ts` classifies every `OcxProviderConfig` field as `editor`, `redacted`, or `runtime`, with `satisfies Record` so a newly added field fails typecheck until classified. `apiKey` and `apiKeyPool` are `redacted`; MCP and desktop-executor blocks are redacted whole because both carry arbitrary env and headers. This is a tightening, not a loosening: it replaces an allowlist that had been inadequate. 356 lines of new tests in `provider-config-batch-management`. | clean | +The key-shape check named in the `1aa839aa8` row, kept out of the table because +its two pipe characters are cell delimiters to a Markdown parser: + +``` +/^LLM\|\d+\|[A-Za-z0-9_-]{10,}$/ +``` + ## R2 — cross-cutting | SHA | PR | Seam crossed | Verdict | @@ -35,9 +42,14 @@ Total 1387 focused assertions' worth of files, zero failures. Plus | `3d3c4fe26` | #3286 | model catalog across Antigravity, Google, sidecar | clean — closed out by its own devlog unit (`f0bbaaf6a`), catalog tests green. | | `862e914c2` | #3274 | `/v1/models` row shape (`max_output_tokens`) | clean — contract tests updated across five files in the same commit. | | `410a48a4f` | #3275 | Cursor Claude-id normalizer replacing three seeds | clean — 85 new assertions in `cursor-claude-id`, plus catalog and pricing tests. | -| `bc8ea072d`, `2ab9d9486`, `7ce713e8d` | #3273, #3276, #3277 | Cursor effort table: bundle read, opt-in rows, GUI provenance | clean — 314 assertions in `cursor-effort-rows`, locale parity enforced for the new strings. | +| `bc8ea072d` | #3273 | Cursor effort table read from the installed bundle; new `models-capabilities` input | clean — 117 assertions in `cursor-effort-table`, cached by bundle path, mtime and size, with a static fallback for a missing or malformed bundle. | +| `2ab9d9486` | #3276 | opt-in effort-variant rows; touches `server/index.ts`, `chat-completions.ts`, `claude-messages.ts`, `responses/core.ts` | clean — the widest seam in the Cursor group, and the one with the most new coverage: 314 assertions in `cursor-effort-rows`. Opt-in by config, so an operator who does not set it sees no row change. | +| `7ce713e8d` | #3277 | GUI Cursor tab shows effort-ladder provenance | clean — nine locales updated in the same commit and `locale-parity` extended, which is the check that would otherwise let a new string ship English-only. | | `85d40ca35` | #3270 | usage aggregation rewritten to an incremental ledger scan | clean — the largest change in the delta (1570 lines in `usage/summary.ts`), and the one with the most new coverage: `usage-aggregate-cache` (301) and `usage-ledger-scanner` (498) are both new files. Management-API docs updated in the same commit. | -| `e9a5b0f13`, `2e74a35d4`, `6b2dfde11`, `fd324dc88` | #3298, #3302, #3294, #3256 | combo failover and quota admission, touching `server/responses/core.ts` | clean — all four land in the same subsystem and are covered together by the 274-pass combos batch. | +| `e9a5b0f13` | #3298 | combos fail over on provider-scoped quota caps; adds a `responses/core.ts` call site | clean — 51 new assertions in `combos`; the failover reads a cap it previously ignored, so the change can only widen the set of requests that survive. | +| `2e74a35d4` | #3302 | combo resolution skips exhausted provider quotas | clean — covered by `combos` plus a dedicated `server-combo-failover-e2e` scenario. | +| `6b2dfde11` | #3294 | shorter request-rate cooldowns; `Retry-After` on a combo 503 | clean — 53 new assertions; the 503 now carries the header a client needs to back off correctly, which is a strict improvement on an opaque 503. | +| `fd324dc88` | #3256 | Kiro reset-aligned cooldown without `Retry-After` | clean — 110 assertions in `kiro-pool-rank`; scoped to the Kiro pool's own ranking, and shares `combos/failover.ts` with the three rows above, all four verified together in the 274-pass batch. | | `938c0136a` | #3246 | tool-bridge shape for `write_stdin` | clean — repair and undeclared-tool guards both extended. | | `b3e205e99` | #3309 | integrations: hub clients routed through loopback | clean — a narrowing; three integration test files extended. | | `ee24bab40` | #3269 | `service-lifecycle` triggers on `release.yml` | clean, and load-bearing for this very release: it is why a workflow-only change still trips the lifecycle gate. | @@ -45,18 +57,32 @@ Total 1387 focused assertions' worth of files, zero failures. Plus ## R1 — scoped runtime -`38f8a8164` (#3330 Cursor picker slug), `472c785c2` (#3308 status URL), -`906511f73` (#3310 connect timeout), `eac662eb1` (#3307 rotation time), -`4cf3e9187` (#3297 liveness retry), `34c9e9802` (#3289 responses-state write -storm), `b0a42ca2f` (#3254 chat-native send budget), `fc08fc2f7` (#3290 log -panel jitter), `15b43e51c` (#3301 hermetic E2E). Each is one subsystem with its -focused test extended in the same commit; all are inside the batches above. -**clean.** +| SHA | PR | Subsystem | Evidence | Verdict | +|-----|----|-----------|----------|---------| +| `38f8a8164` | #3330 | Cursor picker keeps the `cursor/` slug for unbranded rows | `cursor-display-names` rewritten in the same commit; in the 500-pass batch | clean | +| `472c785c2` | #3308 | `ocx status` reports a reachable dashboard URL | `cli-status-json` +35 lines; in the 498-pass batch | clean | +| `906511f73` | #3310 | `connect` uses the catalog inactivity timeout | `remote-catalog` +50, `client-connect` +8; docs and skill page updated with it | clean | +| `eac662eb1` | #3307 | rotation creation time returned by the API-key route | `api-keys-routes` +40; a one-field addition to a response | clean | +| `4cf3e9187` | #3297 | liveness probes retried before `ocx claude` spawns a proxy | `claude-cli` +17; retry only, no new spawn path | clean | +| `34c9e9802` | #3289 | stops the background write storm on `responses-state.json` | `responses-state`; 9 lines in `src`, the rest devlog. A write-frequency reduction | clean | +| `b0a42ca2f` | #3254 | chat-native shares the transient send budget across recovery | `chat-completions-endpoint` +145 | clean | +| `fc08fc2f7` | #3290 | log panel no longer jitters as rows scroll in | GUI-only; `logs-auto-refresh` and `viewport-scroll-caps` extended | clean | +| `15b43e51c` | #3301 | provider-option E2E made hermetic | test-only; removes an external dependency from a test | clean | ## R0 — docs only -`bb27c26be`, `af314b0a7`, `f0bbaaf6a`, `529639a57`, `345e2175c`, -`7424719ab`. Devlog and docs-site only; no runtime path touched. **clean.** +| SHA | PR | What | Verdict | +|-----|----|------|---------| +| `bb27c26be` | #3319 | contributor-credit unit closeout | clean — `devlog/` only | +| `af314b0a7` | #3311 | bug-drawdown campaign closeout | clean — `devlog/` only | +| `f0bbaaf6a` | #3292 | Gemini 3.8 rollout closeout | clean — `devlog/` only | +| `529639a57` | #3278 | Cursor Private Inference guide | clean — `docs-site/` only | +| `345e2175c` | #3272 | Cursor bundle effort-table roadmap | clean — `devlog/` only | +| `7424719ab` | #3267 | Windows CI repair and v2.40.0 outcome | clean — `devlog/` only | + +Each R0 diff was checked with `git show --stat` to confirm it touches no path +outside `devlog/` or `docs-site/`; nothing in the build, typecheck, or test +path reads from either. ## Findings diff --git a/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md b/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md index 5a89244a4a..f1cb0b9132 100644 --- a/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md +++ b/devlog/_plan/260903_muse_release_train/030_wp3_preview_release.md @@ -19,7 +19,27 @@ each step keeps its own gate. Steps 0 and 4 were added after audit round 1 own duplicate check (`release.yml:303`) fires only after dispatch and never checks channel ordering, so skipping this means learning about a collision from a failed publish with the bump already pushed. - `npm view @bitkyc08/opencodex versions --json` plus `gh release view`. + + Four checks, each of which must FAIL THE STEP rather than merely print. A + command that only retrieves data is not a gate: + + ```bash + V=2.41.0-preview.YYYYMMDD + # 1. the exact version is unpublished + npm view "@bitkyc08/opencodex@$V" version 2>/dev/null && { echo "published"; exit 1; } + # 2. no git tag + git ls-remote --tags origin "refs/tags/v$V" | grep -q . && { echo "tag exists"; exit 1; } + # 3. no GitHub release + gh release view "v$V" >/dev/null 2>&1 && { echo "release exists"; exit 1; } + # 4. it moves the CHANNEL forward + npm view @bitkyc08/opencodex dist-tags --json # compare against .preview + ``` + + Check 4 is the one with no automated equivalent anywhere in the workflow: + `release.yml` will happily publish a version that moves `preview` + BACKWARDS, because its only duplicate check is exact-version equality. Read + the current `preview` tag and confirm the new version sorts after it under + semver. 1. Open a promotion PR from a branch **pinned to the reviewed SHA** (not the moving `dev` ref) into `preview`, and merge it with admin. `preview` is protected by a ruleset requiring a reviewed pull request, so promotion is by diff --git a/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md b/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md index a9ace76bdb..3ee01946c5 100644 --- a/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md +++ b/devlog/_plan/260903_muse_release_train/040_wp4_main_release.md @@ -2,9 +2,13 @@ ## Sequence -0. Prove `2.41.0` is still unused on npm and carries no tag or GitHub release - (same check as wp3 step 0, re-run because the preview publish happened in - between). +0. Run the same four blocking checks as wp3 step 0 against `2.41.0`, re-run + from scratch because the preview publish happened in between: the exact + version unpublished on npm, no `v2.41.0` tag, no GitHub release, and the + version moving the `latest` dist-tag FORWARD under semver. Each must fail + the step, not merely print. The channel-forward check matters as much here + as on preview: `release.yml` compares only for exact-version duplication, + so nothing in CI would stop `latest` being moved backwards. 1. Open a promotion PR from a branch **pinned to the reviewed SHA** into `main`; `main` is protected the same way `preview` is. Merge with admin, recording the `enforce-target` bypass. @@ -27,12 +31,20 @@ (`release.yml:119`, `:153`, `:285`); provenance is the artifact-side proof that the tarball came from this workflow on this repository. - `gh release view v2.41.0` exists and its tag resolves to the release SHA. -- `git merge-base --is-ancestor origin/main` exits 0. This - is the check that distinguishes "main moved" from "main carries the work - that was reviewed" — a green release run proves neither by itself. -- The Muse code is actually in the published artifact, not merely in the tag: - the tarball must contain the `meta-muse` provider entry. A tag pointing at - the right SHA and a tarball built from it are separate facts. +- `git fetch origin main` FIRST, then + `git merge-base --is-ancestor FETCH_HEAD` exits 0, with + `FETCH_HEAD` confirmed equal to the `expected-sha` that was dispatched. + This is the check that distinguishes "main moved" from "main carries the work + that was reviewed" — a green release run proves neither by itself. The fetch + is not optional: `git ls-remote` reads the remote without updating + `origin/main`, so an ancestry test against the un-refreshed remote-tracking + ref can pass or fail on history that is minutes stale. +- The Meta work is actually in the published artifact, not merely in the tag. + Download the tarball and confirm all three: the `meta-model` provider entry, + the `meta-muse` provider entry, and `meta.svg` in the packaged GUI assets. + Checking only one of them lets a release pass with a missing alias or a + missing asset. A tag pointing at the right SHA and a tarball built from it + are separate facts. ## After publish