|
| 1 | +# 000 — v2.32.1 hotfix train: baseline, scope, and work-phase map |
| 2 | + |
| 3 | +Unit opened 2026-08-24. Session `01a0339b-4c6e-73e3-8890-23f65c5bbd46`. |
| 4 | +Goalplan slug `prepare-opencodex-dev-as-the-verified-release-ca`. |
| 5 | + |
| 6 | +## Baseline correction |
| 7 | + |
| 8 | +The planning note this unit started from was written against a v2.31 baseline. |
| 9 | +That baseline is void. Verified live on 2026-08-24: |
| 10 | + |
| 11 | +| Ref | SHA | Meaning | |
| 12 | +|-----|-----|---------| |
| 13 | +| `origin/dev` | `c44e43f00` | Merge of #2453 (wait yield_time_ms underscore) | |
| 14 | +| `origin/main` | `96e2f67c3` | `release: v2.32.0` | |
| 15 | + |
| 16 | +``` |
| 17 | +git merge-base --is-ancestor origin/dev origin/main -> exit 0 (dev IS an ancestor of main) |
| 18 | +git merge-base --is-ancestor origin/main origin/dev -> exit 1 |
| 19 | +git rev-list --count origin/dev..origin/main -> 27 |
| 20 | +git rev-list --count origin/main..origin/dev -> 0 |
| 21 | +git diff --name-status origin/dev origin/main -> M package.json |
| 22 | +git show origin/main:package.json -> "version": "2.32.0" |
| 23 | +``` |
| 24 | + |
| 25 | +Three facts follow, and they set the entire unit: |
| 26 | + |
| 27 | +1. **The next release is v2.32.1, not v2.31.1.** v2.32.0 is already published from |
| 28 | + `main` (`npm` `latest` = 2.32.0, GitHub release `v2.32.0` targets `96e2f67c3`). |
| 29 | + A 2.31.x number would move backwards over a shipped release. |
| 30 | +2. **`dev` and `main` have NOT diverged.** `dev` is an *ancestor* of `main`: |
| 31 | + 0 commits ahead, 27 behind. The 27 are main-side promotion and release commits |
| 32 | + accumulated since 2.25.0. This was recorded incorrectly in the first draft of |
| 33 | + this document — the original text read the one-way `--is-ancestor` result as |
| 34 | + divergence. Corrected here after an independent audit re-ran both directions. |
| 35 | +3. **The net tree delta is one line.** `main` carries `version: 2.32.0`; `dev` |
| 36 | + still says `2.27.0` because release bumps are made on the promotion commit and |
| 37 | + never flow back. Nothing else differs. |
| 38 | + |
| 39 | +### What wp1 therefore is |
| 40 | + |
| 41 | +Because `dev` is strictly behind `main`, `git merge origin/main` on `dev` is a |
| 42 | +**fast-forward**, not a merge commit. That is the intended operation and it is |
| 43 | +recorded as such: wp1 advances `dev` to `96e2f67c3` so the release lineage and |
| 44 | +the version line are one. `git merge-tree` confirms the only content change: |
| 45 | + |
| 46 | +``` |
| 47 | +git merge-tree $(git merge-base origin/dev origin/main) origin/dev origin/main |
| 48 | + - "version": "2.27.0", |
| 49 | + + "version": "2.32.0", |
| 50 | +``` |
| 51 | + |
| 52 | +`bun.lock`, `scripts/release.ts`, and `.github/workflows/release.yml` are |
| 53 | +untouched. **Mandatory post-condition: `dev` package.json reads exactly |
| 54 | +`2.32.0`.** Keeping `2.27.0` would regress the release ledger; bumping to |
| 55 | +`2.32.1` belongs to the promotion commit, not to wp1. |
| 56 | + |
| 57 | +## Why bugfix-only |
| 58 | + |
| 59 | +The open queue is far larger than one train can absorb: 46 open PRs, 25 of them |
| 60 | +draft, 21 `review-ready`, 11 `intake: hygiene-blocked`, plus 67 open issues. |
| 61 | +Merging by availability rather than by risk is how a hotfix release grows a |
| 62 | +regression radius it cannot verify. This train is capped at five runtime fixes |
| 63 | +plus one repository-infrastructure fix, each of which closes a defect class that |
| 64 | +is *currently user-visible on the shipped v2.32.0*. |
| 65 | + |
| 66 | +## Included units |
| 67 | + |
| 68 | +| # | PR | Defect class it closes | |
| 69 | +|---|-----|------------------------| |
| 70 | +| wp3 | #2483 | Model unusable — capitalized/dotted Claude vendor ids take the legacy `thinking.enabled` wire and get a 400 | |
| 71 | +| wp4 | #2481 | Catalog inconsistency — slash-bearing models vanish from the picker while direct calls still work | |
| 72 | +| wp5 | #2473 | Thread unrecoverable — a >16 MiB turn repeatedly dies on the WS transport with no SSE escape | |
| 73 | +| wp6 | #2477 | Tool authorization boundary — namespace aliases restored outside the caller's `tool_choice` | |
| 74 | +| wp7 | #2476 | Disk/CPU amplification — a ~24 MiB snapshot rewritten every two seconds unchanged | |
| 75 | +| wp2 | #2427 | Verification cost — the full suite reads as hung, which pushes contributors toward unverified merges | |
| 76 | + |
| 77 | +## Excluded, with reason |
| 78 | + |
| 79 | +Excluded because they widen the regression radius, not because they lack value: |
| 80 | + |
| 81 | +- **#1905** per-model compaction budgets — 27 files, `+813/-80`, touches config, |
| 82 | + management, and catalog. First candidate for v2.33.0. |
| 83 | +- **#2418** subagent scoped cooldown — 8 files, `+2044/-111`, changes routing, |
| 84 | + credential admission, quota probing, and encrypted recovery together. Needs its |
| 85 | + own security lane. |
| 86 | +- **#2470** Google thought-signature — three unrelated concerns in one PR |
| 87 | + (signature replay, output clamps, Windows fixtures). Must be split. |
| 88 | +- **#2475** Kiro tool-search priority, **#2425** xAI hosted `x_search`, |
| 89 | + **#2429** `test:changed` — not release blockers; #2429 is stacked on #2427. |
| 90 | +- **#2462** and every OAuth / remote-dashboard / hosted-SaaS / billing PR — |
| 91 | + product-direction and security-boundary changes, currently hygiene-blocked. |
| 92 | +- All 11 `intake: hygiene-blocked` PRs, by policy. |
| 93 | + |
| 94 | +## Work-phase map (dependency order) |
| 95 | + |
| 96 | +The order is a dependency chain, not a difficulty ranking. Each phase consumes |
| 97 | +the verified output of the one before it. |
| 98 | + |
| 99 | +``` |
| 100 | +wp0 docs (this unit) |
| 101 | + │ |
| 102 | + └─ wp1 dev fast-forward to main (v2.32.0) [every later head depends on it] |
| 103 | + │ |
| 104 | + ├─ wp3 #2483 anthropic ids ┐ |
| 105 | + ├─ wp4 #2481 selectedModels │ runtime fixes, merged |
| 106 | + ├─ wp5 #2473 oversized WS ├─ sequentially, each verified |
| 107 | + ├─ wp6 #2477 namespace authz [sec review] │ on the SERIAL runner |
| 108 | + ├─ wp7 #2476 snapshot writes [conditional]┘ |
| 109 | + │ │ |
| 110 | + │ └──── all of wp3..wp7 must be merged-or-deferred ────┐ |
| 111 | + │ │ |
| 112 | + └─ wp9 #2472 mixed-sequence regression │ |
| 113 | + [independent of wp3..wp7; may run any time after wp1]│ |
| 114 | + │ │ |
| 115 | + └──────────────┬───────────────────────────┘ |
| 116 | + │ |
| 117 | + wp2 #2427 test runner [LAST, or deferred] |
| 118 | + │ |
| 119 | + wp8 freeze + GO/NO-GO |
| 120 | + [requires wp3..wp7, wp9, and wp2] |
| 121 | +``` |
| 122 | + |
| 123 | +The join is explicit because the ordering rule is easy to lose in a tree |
| 124 | +drawing: **wp2 does not start until every runtime phase has a terminal |
| 125 | +outcome.** It is drawn as a sibling of nothing — it is downstream of all of |
| 126 | +them. |
| 127 | + |
| 128 | +### Why #2427 moved to the end (audit amendment) |
| 129 | + |
| 130 | +The first draft put #2427 first, reasoning that landing the verification |
| 131 | +instrument early means every later phase is verified by the same runner. The |
| 132 | +A-phase auditor argued the opposite and it is the stronger argument: #2427 |
| 133 | +switches the suite from serial isolated execution to file-parallel isolated |
| 134 | +execution (`scripts/test.ts` default becomes `bun test --isolate --parallel |
| 135 | +./tests/`), and its own PR body reports **7 failures across 902 files** on its |
| 136 | +exact head. Landing an unproven runner first makes every subsequent runtime |
| 137 | +failure ambiguous: flakiness from parallel shared-state contention would be |
| 138 | +indistinguishable from a regression introduced by the runtime PR under test. |
| 139 | + |
| 140 | +A verification instrument must be changed against a known-good baseline, not |
| 141 | +used to establish one. #2427 therefore runs LAST, immediately before freeze, and |
| 142 | +only with a pre/post gate: the runtime phases are verified on the serial runner, |
| 143 | +then #2427's head must produce a green exact-head `bun run test` plus required |
| 144 | +cross-platform CI. If it does not, it is deferred and the train proceeds on the |
| 145 | +existing runner. It is a convenience, never a blocker. |
| 146 | + |
| 147 | +wp8 depends on **every** runtime phase, not only on the phase drawn above it. |
| 148 | + |
| 149 | +## Out of scope for this unit (STRICT) |
| 150 | + |
| 151 | +No `dev` -> `main` promotion, no tag, no npm publish, no release workflow |
| 152 | +dispatch, no version bump beyond what the backmerge carries. This unit ends at a |
| 153 | +frozen, verified `dev` SHA plus a GO/NO-GO report. Promotion is a human decision. |
| 154 | + |
| 155 | +## Verification doctrine |
| 156 | + |
| 157 | +Exact-head evidence only. A remembered green run is not evidence. Every phase |
| 158 | +closes with fresh command output captured at the SHA being claimed, and every |
| 159 | +merge is proven with its merge SHA plus |
| 160 | +`git merge-base --is-ancestor <merge> origin/dev`. |
| 161 | + |
| 162 | +## Known defects already shipped in v2.32.0 (audit amendment) |
| 163 | + |
| 164 | +v2.32.0 is the v2.27.0-line tree plus a version bump, so every defect open |
| 165 | +against 2.31.0 also ships in 2.32.0. The audit was right that a hotfix train |
| 166 | +without this ledger is choosing its scope blind. Dispositions: |
| 167 | + |
| 168 | +| Issue | Defect | Fixing PR | Disposition | NO-GO? | |
| 169 | +|-------|--------|-----------|-------------|--------| |
| 170 | +| #2407 | Kiro drops tools loaded by `tool_search` | #2475 (draft, red suite) | Decide at wp2/wp8 on exact-head evidence; include only if it goes green before freeze | No | |
| 171 | +| #2458 | Gemini 3.7 Flash video input 502 — routed provider emits undeclared client tool `get_video_duration` | none | Defer: the candidate fix touches the undeclared-tool guard, the same authorization surface wp6 is hardening. Two changes to one guard in one hotfix is exactly the regression radius this train exists to avoid | No | |
| 172 | +| #2459 | Windows bare npm reinstall can leave a live proxy on a mixed old/new module graph | none | Defer: install/service surface, not a runtime defect the proxy can fix mid-session; needs its own unit | No | |
| 173 | + |
| 174 | +None forces NO-GO, but each is now a recorded decision rather than an omission. |
| 175 | +If any acquires a verified fix before freeze it may be reconsidered — the |
| 176 | +inclusion bar stays exact-head green plus review, not urgency. |
| 177 | + |
| 178 | +## Two review-ready PRs the first draft did not mention (audit amendment) |
| 179 | + |
| 180 | +- **#2474** (`fix(scripts): run ocx-run commands in the requested workdir`) — |
| 181 | + a real defect: `scripts/ocx-run:128` never enters the requested workdir. |
| 182 | + But root `package.json` excludes `scripts/` from the published artifact, so it |
| 183 | + cannot affect the shipped runtime. **This train does not use `ocx-run` in any |
| 184 | + verification step**, so it is deferred as repository-operations work rather |
| 185 | + than included. If a later phase adopts `ocx-run` for verification, this |
| 186 | + becomes a prerequisite and must be pulled in first. |
| 187 | +- **#2432** (docs, `__omit__` reasoning-effort sentinel) — currently |
| 188 | + `CHANGES_REQUESTED` with unfixed table formatting. Excluded pending its |
| 189 | + requested changes; docs-only work does not need a hotfix train. |
| 190 | + |
| 191 | +## Per-phase verifiers (audit amendment, PLAN-VERIFIER-REAL-01) |
| 192 | + |
| 193 | +The auditor ran the baseline commands and proved they pass while observing none |
| 194 | +of the planned fixes: |
| 195 | + |
| 196 | +``` |
| 197 | +bun run typecheck -> exit 0, 0.60s |
| 198 | +bun test tests/namespace-tool-compat.test.ts \ |
| 199 | + tests/selected-models.test.ts \ |
| 200 | + tests/anthropic-reasoning.test.ts -> 67 pass 0 fail, exit 0 |
| 201 | +``` |
| 202 | + |
| 203 | +Green there means nothing yet: on current `dev`, |
| 204 | +`tests/selected-models.test.ts:15` has no slash-bearing selector, |
| 205 | +`tests/anthropic-reasoning.test.ts:53` has no capitalized/dotted id, and |
| 206 | +`tests/namespace-tool-compat.test.ts:239` hand-builds an alias map without ever |
| 207 | +testing `tool_choice` authorization. That run is a **preflight**, not fix |
| 208 | +evidence. |
| 209 | + |
| 210 | +Each phase therefore names its own verifier, run at that phase's exact merge |
| 211 | +head, plus the specific assertion that must newly exist: |
| 212 | + |
| 213 | +| Phase | Verifier command | Assertion that must be present after merge | |
| 214 | +|-------|------------------|--------------------------------------------| |
| 215 | +| wp3 #2483 | `bun test tests/anthropic-reasoning.test.ts` | capitalized + dotted + dashed + date-pinned ids classify correctly, and the explicit-disable caller is covered | |
| 216 | +| wp4 #2481 | `bun test tests/selected-models.test.ts tests/codex-catalog.test.ts tests/slug-codec.test.ts` | an encoded slug in `selectedModels` keeps a slash-bearing model visible at the route/sync level, not only in the helper | |
| 217 | +| wp5 #2473 | `bun test tests/ws-upstream.test.ts tests/sse-failed-tail.test.ts` | oversized frame opens zero sockets; adjacent-byte boundary routes WS vs SSE | |
| 218 | +| wp6 #2477 | `bun test tests/namespace-tool-compat.test.ts tests/responses-parser.test.ts` | a foreign tool-type selector authorizes no alias and restores no call | |
| 219 | +| wp7 #2476 | `bun test tests/responses-state-write-amplification.test.ts tests/responses-state.test.ts` | unchanged flush does not rewrite; deleted snapshot is regenerated; eviction order unchanged | |
| 220 | +| wp2 #2427 | `bun run test` (full, exact head) + cross-platform CI | exit 0 | |
| 221 | +| wp8 | `bun run typecheck`, `bun run test`, `bun run privacy:scan` at the frozen SHA | all exit 0 | |
| 222 | + |
| 223 | +## The #2472 canary, restated (audit amendment) |
| 224 | + |
| 225 | +The original criterion — a 100-call zero-output canary — is not a feasible gate |
| 226 | +as written, and the audit demonstrated why. The proxy currently listening on |
| 227 | +:10100 is PID 922, started 2026-08-23: the **stale process from the bug report |
| 228 | +itself**, not a frozen candidate. Worse, the defect needs Cursor |
| 229 | +native-shell/host-shell interleaving with duplicate call ids; duplicates are |
| 230 | +already dropped at `src/adapters/cursor/protobuf-events.ts:1055` while the two |
| 231 | +execution paths stay separate at `src/adapters/cursor/live-transport.ts:1445`. |
| 232 | +An ordinary local prompt cannot deterministically produce that sequence, so a |
| 233 | +"100 calls, zero empty results" run would prove nothing while spending real |
| 234 | +provider credits and restarting the user's live proxy. |
| 235 | + |
| 236 | +Restated criterion: the mandatory gate is an **automated mixed-sequence |
| 237 | +regression** driving the interleaved native/host shell path with duplicate call |
| 238 | +ids, asserting a typed error or failover instead of a silent empty success. |
| 239 | +A live canary stays **optional and separately authorized**: isolated port and |
| 240 | +config, disposable workdir, the exact frozen SHA, a bounded call budget, and |
| 241 | +teardown evidence. Restarting PID 922 is not part of this unit. |
| 242 | + |
| 243 | +**That regression does not exist and no included PR writes it**, which the |
| 244 | +second audit round correctly called out: a mandatory gate with no implementing |
| 245 | +phase is a wish, not a gate. It therefore gets its own work-phase, **wp9**, |
| 246 | +documented at `090_wp9_issue2472_mixed_sequence_regression.md`. wp9 is |
| 247 | +independent of wp3–wp7 and may run any time after wp1, but it must have a |
| 248 | +terminal outcome before freeze. If wp9 concludes the sequence cannot be driven |
| 249 | +deterministically in-process, #2472 is recorded as an explicitly deferred known |
| 250 | +defect and **stops being a GO criterion** — with that finding written down, |
| 251 | +rather than left as an unmet checkbox. |
0 commit comments