diff --git a/devlog/_plan/260806_stacked_bug_campaign/000_plan.md b/devlog/_plan/260806_stacked_bug_campaign/000_plan.md new file mode 100644 index 000000000..733827a31 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/000_plan.md @@ -0,0 +1,120 @@ +# 000 — Plan: stacked bug campaign with contributor attribution (2026-08-06) + +## Objective + +Land a maintainer-authored **stacked PR chain** (`stack 01..N`) against `dev` +that covers the great majority of open bug issues and open contributor bug/fix +PRs at the 2026-08-06 cutoff, **crediting every original contributor by name**. + +The campaign explicitly does *not* merge contributor PRs. Each contributor's +work is either cherry-picked with authorship preserved or reimplemented with a +`Co-authored-by:` trailer plus a named citation of their PR/issue. Their PR then +receives a comment saying where their work landed and that their name went with +it. + +## Base + +| Fact | Value | +|------|-------| +| Worktree | `/Users/jun/.codex/worktrees/42d5/opencodex` | +| Cutoff snapshot | 2026-08-06T13:49:07Z (`.snapshot_issues.json`, `.snapshot_prs.json`) | +| Snapshot base | `origin/dev` = `43a1fdc45` | +| Working base | `origin/dev` = `e9d957bf6` (advanced during triage when #1129 merged) | +| Stack branch prefix | `codex/260806-stackNN-` | +| Open at cutoff | 49 issues, 34 PRs | + +`e9d957bf6` is the working base because #1129 merged mid-triage; the two extra +commits are the maintainer's own restore-watchdog test margin and its merge. +No in-scope item depends on the difference. + +## Scope rules + +In scope: + +- Bugs and code-level bug-like defects open at the cutoff. +- Contributor PRs that fix such a defect, whatever their state (draft, + conflicting, over-scoped) — the *fix* is what matters, not the packaging. + +Out of scope, recorded with a reason rather than silently dropped: + +- Items opened after the cutoff. +- Feature programs (provider onboarding, cost overlay, remote E2EE, OMP, + account-picker slices, localization mega-PRs). +- Roadmap/tracking issues and upstream-owned defects. +- `needs-info` issues with no reproduction on the current tree. +- Maintainer PRs already in flight (`#557`, `#1008`) — left alone deliberately. +- Release automation, version bumps, and any publish action. + +## Method + +`cxc-loop` HOTL over `cxc-pabcd`. Work-phase 1 (this unit) is docs-only: freeze +the snapshot, triage everything, and write every implementation phase's decade +doc to diff-level precision. Implementation begins at work-phase 2, one decade +doc per PABCD cycle, one stack PR per cycle. + +Triage was executed by four parallel sol-medium lanes (two issue lanes, two PR +lanes), each required to confirm the defect against the current tree with +`path:line` citations rather than restating the reporter's claim. Their verdicts +are recorded in `001_issue_triage.md` and `002_pr_triage.md`. + +## Attribution contract (the point of this campaign) + +Every stack commit that derives from contributor work carries: + +1. `Co-authored-by: ` using the identity from the + contributor's own commits on their PR head. +2. A commit body naming the source PR and, where applicable, the issue. +3. A PR description crediting the contributor in prose, not only in a trailer. + +Where a fix is reimplemented rather than cherry-picked, the contributor is still +credited — they found the defect and proved the code path, which is the +expensive part. The reimplementation reason is stated plainly in the PR body so +the record is honest in both directions. + +## Work-phase map + +Dependency-ordered. Each row is one PABCD cycle and one stacked PR; each stacks +on the previous head, and stack 01 targets `dev`. + +| Phase | Doc | Subject | Issue | Source PR(s) | Credited | +|-------|-----|---------|-------|--------------|----------| +| 1 | (this unit) | Triage + roadmap | — | — | — | +| 2 | `010` | Bounded translated-SSE inspection | #1112 | #1114 | ingwannu | +| 3 | `020` | Empty native-profile stage sweep | #1120 | #1124 | ingwannu | +| 4 | `030` | Native-main ACL timeout retry | — | #1130 | luvs01 | +| 5 | `040` | Bounded rollout inspection | — | #1115 | Simon | +| 6 | `050` | Anthropic response-model identity | #1117 | #1122, #1121 | Giulio Leone, ingwannu | +| 7 | `060` | GitHub Copilot Responses normalization | #1110 | #1111 | Simon | +| 8 | `070` | Darwin eager rewrite relay gate | #1127 | #947 | 0xWinner98, biao | +| 9 | `080` | Vision raw-body image synchronization | — | #1047 | Bailey | +| 10 | `090` | Gemini/CCA reasoning-effort documentation + coverage | — | #978 | Pranav Yerramaneni | +| 11 | `100` | Routed structured-output schema preservation | — | #985 | Pranav Yerramaneni | +| 12 | `110` | Cursor structured-edit conversion | #1017 | #1036 | NexusCore | +| 13 | `120` | Reasoning-replay empty-delta handoff | — | #1126 | NexusCore | +| 14 | `130` | Usage-log attempt persistence | — | #1093 | Takashi Yamashiro | +| 15 | `140` | Effort-picker fail-closed + Pi loopback export | — | #1092, #1085 | Eachann, n3wr1ch | +| 16 | `150` | Test-home isolation + Desktop allowlist docs | #241 | #997, #999 | Yuxin Qiao | +| 17 | `160` | Closeout: attribution comments, full suite, dispositions | — | — | — | + +Phase order puts small merge-clean adoptions first so the stack has a stable +base, then the larger adapted reimplementations. Phases 2-5 are near-verbatim +adoptions; 6-15 are adapted or reimplemented. + +## Verification floor per implementation phase + +- `bun run typecheck` exit 0 +- the phase's focused test files, 0 failures +- `bun run privacy:scan` green +- `bun run lint:gui` + `bun run build:gui` when the phase touches `gui/` +- one full `bun run test` near the end of the campaign + +A remembered pass is not evidence; each phase records its command output. + +## Authorization boundary + +Authorized for this campaign: pushing `codex/260806-stack*` branches, creating +the stack PRs, and commenting on superseded contributor PRs. + +Not authorized without a fresh request: merging anything, closing any PR or +issue, force-pushing contributor branches, pushing `dev`/`main`/`preview`, +deleting branches, npm publish, releases. diff --git a/devlog/_plan/260806_stacked_bug_campaign/001_issue_triage.md b/devlog/_plan/260806_stacked_bug_campaign/001_issue_triage.md new file mode 100644 index 000000000..6775935c7 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/001_issue_triage.md @@ -0,0 +1,75 @@ +# 001 — Issue triage (49 open at the 2026-08-06T13:49Z cutoff) + +Two sol-medium lanes classified every open issue against the working tree. +Rule applied: a reporter's claim is not evidence — each verdict cites the code +path that proves or refutes it. Snapshot: `.snapshot_issues.json`. + +## Classification key + +| Class | Meaning | +|-------|---------| +| `FIXABLE` | Real defect with a bounded fix; enters the stack | +| `LARGE` | Real defect, but program-scale; deferred with reason | +| `UPSTREAM` | Not fixable in this repository | +| `NEEDS_INFO` | Cannot be confirmed on the current tree | +| `FEATURE` | Enhancement, not a bug | + +## In-scope defects (enter the stack) + +| # | Reporter | Class | Proof on the current tree | Phase | +|---|----------|-------|---------------------------|-------| +| 1112 | lidge-jun | FIXABLE | `trackSseForRequestLog` (`src/server/relay.ts:353`) appends into `buffer` inside `inspectChunk` at `:382` with no cap and re-parses each payload through three string helpers; `relaySseWithHeartbeat` (`:498`) repeats the same unbounded append at `:540` | 010 | +| 1120 | MarcusNeufeldt | FIXABLE | `runOwnedStageSweep` (`src/codex/native-profile-startup.ts:131`) always calls `entry.manager.sweepStages()`, and `sweepStages` (`src/codex/native-profile-manager.ts:916`) enters the locked path (`sweepStagesLocked`, `:828`) even with zero artifacts | 020 | +| 1117 | giulioleone097 | FIXABLE | `applyFinalRouteRequestNormalization` (`src/server/responses/core.ts:856`, called at `:1532`) overwrites `parsed.modelId` with the bare upstream id; the image loop then emits that value (`src/images/loop.ts:903`), as do the JSON/streaming and web-search paths | 050 | +| 1110 | Simon-Opopeee | FIXABLE | The `github-copilot` entry (`src/providers/registry.ts:2043`) declares `adapter: "openai-chat"` at `:2046`; Responses is forced per model by `modelWireDefaults` at `:2060-2067`. Those responses reach the Responses relay, which composes only generic image/id/snapshot repairs — no Copilot-specific normalization exists | 060 | +| 1127 | 0xWinner98 | FIXABLE | `selectEagerPath` (`src/lib/bun-stream-caps.ts:99`) returns null whenever `needsClientRewrite` is set (`:106`), and its Darwin tail admits only `config-eager` (`:112`); inline payload rewrite and budget remain Win32-only in `core.ts` | 070 | +| 1017 | Vincent-HD | FIXABLE | Codex's freeform custom tool is exposed as a single-string `input` parameter (`src/responses/parser.ts:167-173`); the Cursor adapter emits normalized arguments with no structured-edit conversion (`src/adapters/cursor/protobuf-events.ts`, tool-call emission path) | 110 | +| 241 | Lingchen97 | UPSTREAM (docs) | Routed rows are emitted with `visibility = "list"` (`src/codex/catalog/sync.ts:240`); the Desktop allowlist is outside this repo. Documented workaround only | 150 | + +## Real but program-scale (deferred, reason recorded) + +| # | Reporter | Why deferred | +|---|----------|--------------| +| 1102 | comfuture | Non-loopback requires credentials (`src/server/auth-cors.ts:211`) and Responses accepts only the dedicated header (`auth-cors.ts:369`); token inheritance exists only when the shim is the parent (`src/codex/shim.ts:381`). Transparent hand-off to a directly spawned binary needs a new trust/admission design | +| 1059 | lidge-jun | Windows suite failures cross temp-write, ACL, replacement, journal, catalog, and coordinator paths (`src/config.ts:198-219`, `src/codex/internal/catalog-writer.ts:47`). Run 31095755263 failed all four shards. No subset can be honestly proven fixed from macOS — injected I/O tests cannot establish Windows ACL inheritance, replacement semantics, or real cross-process SQLite locking | +| 1049 | lidge-jun | Pre-substrate homes are classified `legacy-uncoordinated` (`src/codex/inject-coordination.ts:24`) and bypass the lock (`src/codex/inject.ts:870`). Crash-safe adoption is migration-grade work needing its own recovery matrix | + +## Upstream-owned (no repo-side fix exists) + +| # | Reporter | Evidence | +|---|----------|----------| +| 92 | webmastertorch | The proxy recognizes valid Fernet task content but holds no key (`src/server/responses/encrypted-payload.ts:204`) and now rejects routed delivery cleanly (`src/server/responses/core.ts:1507`). Plaintext must be retained upstream — openai/codex#33551 | +| 417 | lidge-jun | The relay forwards bytes without decode/re-encode (`src/server/index.ts:305`); the Korean/1.3 MB regression is an ancestor of HEAD. Upstream openai/codex#35161 | +| 1100 | c3right | OpenCodex advertises effort levels but strips inherited summary capability conservatively (`src/codex/catalog/parsing.ts:341`); explicit per-model opt-in works (`src/codex/catalog/effort.ts:139`). Codex gates its whole reasoning object on the summary flag before ingress | + +## Needs reproduction (cannot be confirmed on the current tree) + +| # | Reporter | What is missing | +|---|----------|-----------------| +| 1128 | c040340 | The asserted missing policy is not borne out: DeepSeek disables upstream SSE (`src/providers/registry.ts:1310`), `core.ts:874` applies it, and routed `/responses/compact` re-enters `handleResponses` (`src/server/responses/compact.ts:553`) | +| 1024 | brunoflma | NVIDIA Nemotron is text-only by classification (`src/providers/registry.ts:658`); MiMo is excluded because the measured endpoint accepts images (`registry.ts:363`). The remaining custom route needs its provider config | +| 994 | hamzasoussi53 | The suspected zen DeepSeek path is fixed with coverage (`src/providers/registry.ts:1967`, `tests/opencode-zen-deepseek-reasoning.test.ts:55`), but the report never identifies provider/model | +| 904 | lidge-jun | Relay paths are byte-clean (`src/server/index.ts:305`); needs a failing client/provider capture | +| 796 | hooliy-01 | Ark hosts receive the structured placeholder (`src/adapters/openai-chat.ts:546`), but no live Ark endpoint verifies acceptance (`tests/volcengine-ark-assistant-content.test.ts:16`) | +| 418 | brunoflma | The latest trace completed on an inherited native model because `model` was omitted; it did not reproduce the original custom-parent→custom-child failure | + +## Features / roadmap (out of scope for a bug campaign) + +`#1125`, `#1107`, `#1091`, `#1086`, `#1082`, `#1076`, `#1073`, `#1062`, `#1060`, +`#1058`, `#1048`, `#974`, `#823`, `#822`, `#821`, `#820`, `#809`, `#755`, `#695`, +`#657`, `#572`, `#561`, `#540`, `#415`, `#414`, `#386`, `#201`, `#178`, `#177`, +`#95`. + +Spot-checked rather than assumed: `#1086` (tri-state `fastMode` already applied +at `src/server/responses/core.ts:903`; only the per-model map is missing), +`#1073` (runtime already resolves `modelContextWindows[id]` then provider +`contextWindow` at `src/codex/catalog/provider-fetch.ts:528`), `#809` (every +`/api/*` route is management-authenticated at `src/server/index.ts:676`, so a +data-plane catalog route is a new security-sensitive API), and `#1048` (reduced +scope already merged in #1106). + +## Coverage + +7 issues enter the stack, 3 are real-but-large, 3 upstream, 6 need +reproduction, 30 are features/roadmap. Total 49 — every open issue accounted +for. diff --git a/devlog/_plan/260806_stacked_bug_campaign/002_pr_triage.md b/devlog/_plan/260806_stacked_bug_campaign/002_pr_triage.md new file mode 100644 index 000000000..48bac91b2 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/002_pr_triage.md @@ -0,0 +1,119 @@ +# 002 — PR triage and contributor attribution (32 open at the cutoff) + +Live count at the cutoff, verified with `gh pr list --state open --limit 200`: +**32** open PRs — `557, 581, 811, 812, 947, 978, 985, 997, 999, 1002, 1008, +1010, 1036, 1039, 1047, 1056, 1085, 1092, 1093, 1095, 1096, 1109, 1111, 1114, +1115, 1119, 1121, 1122, 1124, 1126, 1130, 1131`. The tables below also carry +**#1129**, which merged during triage (`e9d957bf6`) and is listed only to record +that it needs no action; it is not counted in the 32. + +Two sol-medium lanes fetched every PR head, read the real diff, checked +mergeability against live `dev`, and recorded the **commit author identity** — +that identity is what the stack's `Co-authored-by:` trailers must use. +Snapshot: `.snapshot_prs.json`. + +## Attribution table (identity used in stack commits) + +Email addresses are redacted here as ``: this devlog is a +public directory and `bun run privacy:scan` fails the build on a committed +contributor address. The **real** identity used for each `Co-authored-by:` +trailer is read at implementation time from the contributor's own commits: + +``` +git fetch origin pull//head:pr- +git log -1 --format='%an <%ae>' pr- +``` + +That keeps attribution exact in git history — where it belongs and where GitHub +renders it — without publishing addresses in a documentation file. + +| PR | GitHub login | Credit as | Commit identity for `Co-authored-by:` | +|----|--------------|-----------|----------------------------------------| +| 1131, 1130, 1039 | `luvs01` | luvs01 | `luvs01 ` | +| 1124, 1121, 1114, 811 | `Ingwannu` | ingwannu | `Ingwannu ` | +| 1126, 1036 | `ZachDreamZ` | NexusCore | `Agent59353 ` (#1126) | +| 1122 | `giulioleone097` | Giulio Leone | `Giulio Leone ` | +| 1115, 1111 | `Simon-Opopeee` | Simon | `Simon ` | +| 1109, 1085 | `n3wr1ch` | n3wr1ch | `n3wr1ch ` | +| 1096 | `chrisae9` | Chris Alves | `chrisae9 ` | +| 1095, 1047 | `baileyh8` | Bailey | `baileyh8 ` | +| 1093 | `yamashirotakashi` | Takashi Yamashiro | `Takashi Yamashiro ` | +| 1092 | `eachann1024` | Eachann | `关俊江 ` | +| 1056, 947 | `WZBbiao` | biao | `WZBbiao ` | +| 1010 | `harryzhou2000` | Harry Zhou | `HarryZhou ` | +| 1002 | `hanjianjun` | hanjianjun | `hanjianjun ` | +| 999, 997 | `Yuxin-Qiao` | Yuxin Qiao | `Yuxin Qiao ` | +| 985, 978 | `DevMello` | Pranav Yerramaneni | `devmello ` | +| 812 | `theQuert` | Quert | `theQuert ` | +| 581 | `letr1n1ty` | k0 | `letr1n1ty ` | + +`#1036`'s head commits carry a maintainer-adjacent committer identity; the +PR author `ZachDreamZ` (NexusCore) is credited, since authorship of the idea +and the patch is what the attribution is for. + +## Adopt near-verbatim (merge-clean, right code path, tested) + +| PR | Contributor | Subject | Phase | Note | +|----|-------------|---------|-------|------| +| 1114 | ingwannu | Bounded translated SSE inspection (#1112) | 010 | Reuses the existing byte-bounded inspector; small and correctly layered | +| 1124 | ingwannu | Skip empty native-profile stage sweeps (#1120) | 020 | Skips locking only when both stage locations are provably absent; uncertainty stays fail-closed | +| 1130 | luvs01 | Native-main ACL timeout retry | 030 | Fail-closed with coded errors and one bounded retry | +| 1115 | Simon | Bounded oversized rollout inspection | 040 | fd/path re-stat checks, chunk-safe UTF-8, malformed/truncated cases | +| 997 | Yuxin Qiao | Isolate usage-log fixtures from the real home | 150 | Scratch-home isolation with explicit target/content assertions | +| 999 | Yuxin Qiao | Document the Desktop remote allowlist limit (#241) | 150 | Docs-only, accurate, merge-clean | + +## Adopt adapted (idea correct, packaging narrowed) + +| PR | Contributor | Keep | Drop | Phase | +|----|-------------|------|------|-------| +| 1122 | Giulio Leone | Anthropic client-facing selector captured before the upstream rewrite in `applyFinalRouteRequestNormalization`, then used in the bridge and passthrough JSON/SSE builders (anchors located by symbol in `src/server/responses/core.ts`, not by line — four phases edit that file) | Generalizing response identity and catalog lifecycle to every provider; the hidden compatibility alias rows | 050 | +| 1111 | Simon | `src/server/github-copilot-responses-repair.ts` (+338) with its `src/server/relay.ts`, `src/server/responses/core.ts`, and `src/server/sse-payload-rewrite.ts` wiring, plus `tests/github-copilot-sse-rewrite.test.ts` and `tests/github-copilot-stream-contract.test.ts` | Commit `6247d3932` — the `tests/ci-workflows.test.ts` permission assertion that causes the only merge conflict | 060 | +| 1047 | Bailey | The new `syncRawBodyImageDescriptions` (added after `renderDescription`, `src/vision/index.ts:269`) and its calls at the end of `describeImagesInPlace` (`:347`) and `stripImagesInPlace` (`:453`) | Its `descriptions.length === 0` early return and its raw-part fallback when a description is missing — both leave images in `_rawBody` | 080 | +| 978 | Pranav Yerramaneni | Adapter gate at `src/adapters/google.ts:343-360` | Nothing; add the missing provider-wide positive case and correct the docs | 090 | +| 985 | Pranav Yerramaneni | `parseTextFormat` replacing the boolean `detectStructuredOutput` (`src/responses/parser.ts:671`), `options.textFormat` on `src/types.ts`, and the new `response_format` mapping in `src/adapters/openai-chat.ts` (no such handling exists on dev) | Its `textFormat.schema !== undefined` guard, which drops a schema-less `json_schema` entirely | 100 | +| 1036 | NexusCore | Translator and tests | Provenance derived from pre-filter `request.tools` (`src/adapters/cursor/live-transport.ts:543-560`) — derive from the final catalog | 110 | +| 1126 | NexusCore | Empty-delta replay preservation (`src/bridge.ts:826,1558`) | Persisted chain-of-thought cache, exit hooks, global counters — the memory-only privacy contract stays | 120 | +| 1093 | Takashi Yamashiro | Ordinary-attempt recording, explicit empty arrays (`src/usage/log.ts:323`) | Ingress-span persistence: any admitted client could forge a regex-shaped "guard-issued" span | 130 | +| 1092 | Eachann | Effort-picker fail-closed fix (`gui/src/combo-workspace-data.ts:12`) | Catalog fallback synthesis, copy redesign, `imageInput`, locale churn | 140 | +| 1085 | n3wr1ch | Pi loopback placeholder and no-key UX (`src/clients/config-export.ts:704,948`) | Combo/direct-mode filtering and generalized export-contract changes | 140 | + +## Reimplement (defect real, patch unusable as written) + +| PR | Contributor | Why | Phase | +|----|-------------|-----|-------| +| 947 | biao | Darwin predicate idea is sound but the branch conflicts with dev and its tests use fixed `settle()` waits (`tests/relay-eager.test.ts:269-284`). Port `requiresEagerRewriteRelay()` onto current transport code with deterministic waits | 070 | +| 1095 | Bailey | Unsafe: a five-second idle gap after opened items finish is not proof no later item arrives, so it can synthesize `response.completed` and truncate a slow valid response. Correct approach needs authoritative EOF/`[DONE]` | deferred to 070 follow-up, not landed | +| 1056 | biao | 54 files with backup, convergence, multi-agent-pin, and incomplete GUI problems (`src/codex/catalog/sync.ts:498-503`, `src/codex/convergence.ts:194-207`) | deferred | + +## Defer (out of scope, reason recorded) + +| PR | Contributor | Reason | +|----|-------------|--------| +| 1131 | luvs01 | Strong C4 lifecycle/auth patch, 30 files — needs maintainer security review as its own unit, not a stack slice | +| 1121 | ingwannu | Duplicate of #1122; #1122 covers native passthrough metadata and alias collisions that #1121 misses | +| 1109 | n3wr1ch | OMP feature program across runtime, GUI, docs, localization, credential destinations | +| 1096 | Chris Alves | First slice of an unfinished account-picker feature (#425) | +| 1039 | luvs01 | Circuit-breaker feature program needing its own contract cycle | +| 1010 | Harry Zhou | Cost-overlay feature program, actively reviewed elsewhere | +| 1002 | hanjianjun | Vision-reasoning feature, conflicting, UI/localization-heavy | +| 812 | Quert | Provider onboarding with an external authorization gate | +| 811 | ingwannu | 15k-line E2EE/release/platform program, CI red and conflicting | +| 581 | k0 | 8k-line zh-TW localization program, stale and conflicting | +| 557, 1008 | JUN (maintainer) | Already in flight and maintainer-owned; deliberately untouched | +| 1129, 1119 | JUN (maintainer) | #1129 already merged as `e9d957bf6`; #1119 is the maintainer's own contract-test PR | + +## Duplicate resolution + +`#1122` beats `#1121` (both target #1117): #1122 also covers native passthrough +response metadata and deterministic alias collisions. `#1121`'s author +(ingwannu) is credited in the phase-050 PR anyway — they filed a correct +independent diagnosis of the same defect. + +## Coverage + +6 adopt-verbatim, 10 adopt-adapted, 3 reimplement (1 **planned for** phase 070, +2 deferred), 13 defer. Total **32** open PRs, every one accounted for, plus the +already-merged #1129 recorded for completeness. + +Nothing in this document is landed at the time of writing — it is a roadmap. +Landed state is recorded only in `170_dispositions.md`, with commit evidence. diff --git a/devlog/_plan/260806_stacked_bug_campaign/010_phase2_bounded_sse_inspection.md b/devlog/_plan/260806_stacked_bug_campaign/010_phase2_bounded_sse_inspection.md new file mode 100644 index 000000000..032f2d9fa --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/010_phase2_bounded_sse_inspection.md @@ -0,0 +1,51 @@ +# 010 — Phase 2: bounded translated-SSE inspection (#1112, PR #1114) + +Credit: **ingwannu** (`Ingwannu `), PR #1114. +Adoption: near-verbatim cherry-pick. + +## Defect + +`trackSseForRequestLog` retains an unterminated translated-path frame +indefinitely and reparses each complete payload through three separate string +helpers (`src/server/relay.ts:373`). `relaySseWithHeartbeat` repeats the same +unbounded partial-frame state (`src/server/relay.ts:539`). 64 MiB of +delimiterless upstream bytes therefore pin memory and triple parse cost. + +`createSseInspector` already implements the correct behavior: cap one candidate +frame, discard only the oversized frame from *inspection*, resynchronize at the +next SSE delimiter, and leave downstream bytes byte-for-byte untouched. + +## Change + +Source commit `73706d3b2` (single commit, clean tree): + +| Path | Op | Content | +|------|----|---------| +| `src/server/relay.ts` | MODIFY | Replace both string-retaining scanners with `createSseInspector`; parse each complete payload once and share the parsed object across the request-log, first-output, and terminal observers (−44/+16) | +| `tests/sse-inspector-bounds.test.ts` | MODIFY | +65 lines: over-cap delimiterless input, recovery at the next valid frame, parse-count assertion, byte-for-byte output preservation | +| `structure/04_transports-and-sidecars.md` | MODIFY | +5 lines recording the bounded-inspection invariant | + +No behavior change for well-formed streams: the relay's output bytes are +asserted identical. + +## Execution + +``` +git checkout -b codex/260806-stack01-bounded-sse origin/dev +git cherry-pick 73706d3b2 +``` + +The cherry-pick preserves `Ingwannu` as commit author. Amend the message to add +the source citation and `Closes #1112`; authorship stays with the contributor, +so no `Co-authored-by:` trailer is needed for a verbatim pick. + +## Verification + +- `bun test tests/sse-inspector-bounds.test.ts tests/relay-eager.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 01, base `dev`. Body cites #1112, credits ingwannu, and states that #1114 +remains open for them to see the landing. diff --git a/devlog/_plan/260806_stacked_bug_campaign/020_phase3_native_profile_empty_sweep.md b/devlog/_plan/260806_stacked_bug_campaign/020_phase3_native_profile_empty_sweep.md new file mode 100644 index 000000000..96d9e1ad4 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/020_phase3_native_profile_empty_sweep.md @@ -0,0 +1,56 @@ +# 020 — Phase 3: skip empty native-profile stage sweeps (#1120, PR #1124) + +Credit: **ingwannu** (`Ingwannu `), PR #1124. +Reporter: **MarcusNeufeldt** (#1120). Adoption: near-verbatim cherry-pick. + +## Defect + +`startNativeMainStartupLifecycle` always runs `manager.sweepStages()` under the +native-profile SQLite transaction (`src/codex/native-profile-startup.ts:131`), +and `sweepStages()` takes the profile lock unconditionally +(`src/codex/native-profile-manager.ts:916`) — even when there is no stage +registry and no staging tree at all. + +A transient lock or Windows ACL failure then leaves the native-main gate in +`stage-cleanup-required`, native requests surface a misleading 503 that reads +like upstream capacity pressure, and `doctor`/`recover` contend on the same +transaction. That is the reported catch-22 on an installation with zero +profiles. + +## Change + +Source commit `dd2078763`: + +| Path | Op | Content | +|------|----|---------| +| `src/codex/native-profile-manager.ts` | MODIFY | +19: absence-only preflight — skip the locked sweep when both the stage registry and this instance's staging tree are provably absent | +| `src/codex/native-profile-startup.ts` | MODIFY | +8: call the preflight before `runOwnedStageSweep` | +| `src/codex/auth-context.ts` | MODIFY | +7/−1: 503 identifies local native-profile maintenance instead of upstream capacity | +| `src/server/claude-messages.ts` | MODIFY | +3/−1: same diagnostic on the Claude path | +| `tests/native-profile-stage-lifecycle.test.ts` | MODIFY | +62: zero-profile/zero-stage install cannot be fenced by an unavailable lock; present or unreadable stage state still fails closed | +| `tests/codex-auth-context.test.ts` | MODIFY | +14: 503 message identity | +| `tests/native-profile-drain-server.test.ts` | MODIFY | +3/−1: message alignment | +| `structure/02_config-and-codex-home.md` | MODIFY | +14: absence-only fast-path invariant | + +**Safety property preserved:** any *present* artifact, or any path that cannot +be proven absent, still enters the existing locked fail-closed cleanup. +Owner/claim protection for the physical native-main credential is untouched. +This is a C4-adjacent credential path, so the absence proof — not the happy +path — is what the regression test pins. + +## Execution + +``` +git cherry-pick dd2078763 +``` + +## Verification + +- `bun test tests/native-profile-stage-lifecycle.test.ts tests/codex-auth-context.test.ts tests/native-profile-drain-server.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 02, base = stack 01 head. `Closes #1120`, credits ingwannu and the +reporter. diff --git a/devlog/_plan/260806_stacked_bug_campaign/030_phase4_native_main_acl_timeout.md b/devlog/_plan/260806_stacked_bug_campaign/030_phase4_native_main_acl_timeout.md new file mode 100644 index 000000000..c9b8a0419 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/030_phase4_native_main_acl_timeout.md @@ -0,0 +1,48 @@ +# 030 — Phase 4: native-main ACL timeout recovery (PR #1130) + +Credit: **luvs01** (`luvs01 `), PR #1130. +Adoption: near-verbatim cherry-pick. + +## Defect + +The Windows secret-ACL helper treats a timeout the same as a permanent failure, +so a transient ACL call stall permanently fences the native-main owner path. +There is no bounded retry and no coded error to distinguish "the ACL tool timed +out" from "the ACL was refused". + +## Change + +Source commit `004a6c12c`: + +| Path | Op | Content | +|------|----|---------| +| `src/lib/windows-secret-acl.ts` | MODIFY | +88/−~20: coded error taxonomy, timeout classified separately from permanent denial, one bounded retry | +| `src/codex/native-main-owner.ts` | MODIFY | +20: consume the coded result; retry once on timeout, fail closed otherwise | +| `src/codex/native-main-lock-file.ts` | MODIFY | +6/−~2: propagate the coded failure | +| `tests/windows-secret-acl.test.ts` | MODIFY | +135: permanent-error, timeout, and retry-exhaustion cases | +| `tests/native-main-owner-lifetime.test.ts` | MODIFY | +78: owner path behavior under each failure class | + +**Fail-closed is preserved.** The retry applies only to the timeout class; a +refused ACL still denies. This matters because the path guards a physical +credential — a retry that swallowed a denial would be a security regression, +and the tests pin that it does not. + +## Execution + +``` +git cherry-pick 004a6c12c +``` + +## Verification + +- `bun test tests/windows-secret-acl.test.ts tests/native-main-owner-lifetime.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +Note: the Windows-specific code path cannot be exercised natively on macOS; the +tests inject the ACL boundary. That limitation is stated in the PR rather than +claimed as full platform proof. + +## PR + +Stack 03, base = stack 02 head. No issue link (#1130 has none); credits luvs01. diff --git a/devlog/_plan/260806_stacked_bug_campaign/040_phase5_bounded_rollout_inspection.md b/devlog/_plan/260806_stacked_bug_campaign/040_phase5_bounded_rollout_inspection.md new file mode 100644 index 000000000..ad3dbed40 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/040_phase5_bounded_rollout_inspection.md @@ -0,0 +1,46 @@ +# 040 — Phase 5: bounded oversized rollout inspection (PR #1115) + +Credit: **Simon** (`Simon `), PR #1115. +Adoption: near-verbatim cherry-pick of 5 commits. + +## Defect + +`src/codex/native-residue.ts` reads a rollout file without bounding it to the +observed size, decodes the whole buffer at once, and can mis-handle a pathname +that changed between stat and read. An oversized rollout therefore drives an +unbounded read during residue inspection. + +## Change + +Source commits, oldest first — `f86e7a783`, `37fada88d`, `d348308c3`, +`926621db5`, `b5452a0a5`: + +| Path | Op | Content | +|------|----|---------| +| `src/codex/native-residue.ts` | MODIFY | +167/−27: bound reads to the observed size, decode UTF-8 incrementally, parse JSONL from bounded chunks, re-stat the pathname, scan lines once, preserve BOM | +| `tests/codex-native-residue.test.ts` | MODIFY | +69: oversized, malformed, truncated, and BOM cases | + +**Residue classification is not weakened** — the bound applies to how much is +read, not to what counts as residue. That distinction is the review-critical +part: a bounded reader that silently reclassified a truncated rollout as +"clean" would hide real residue. + +## Execution + +``` +git cherry-pick f86e7a783 37fada88d d348308c3 926621db5 b5452a0a5 +``` + +All five carry Simon's authorship; the chain is kept intact rather than +squashed so the review history stays legible. + +## Verification + +- `bun test tests/codex-native-residue.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 04, base = stack 03 head. Credits Simon; notes that the unrelated +native-history bound was already split out by the contributor. diff --git a/devlog/_plan/260806_stacked_bug_campaign/050_phase6_anthropic_response_identity.md b/devlog/_plan/260806_stacked_bug_campaign/050_phase6_anthropic_response_identity.md new file mode 100644 index 000000000..8564a9926 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/050_phase6_anthropic_response_identity.md @@ -0,0 +1,71 @@ +# 050 — Phase 6: Anthropic response-model identity (#1117, PRs #1122/#1121) + +Credit: **Giulio Leone** (`Giulio Leone `, PR #1122, +also the reporter of #1117) and **ingwannu** +(`Ingwannu `, PR #1121 — independent correct +diagnosis of the same defect). Adoption: **adapted**, narrowed to Anthropic. + +## Defect + +`applyFinalRouteRequestNormalization` rewrites `parsed.modelId` from the +Codex-facing selector to the bare upstream Anthropic id +(`src/server/responses/core.ts:867`). Every downstream consumer then builds +`response.model` from the mutated value: + +- normal Responses bridge — `src/server/responses/core.ts:2562` +- image loop — `src/images/loop.ts:902` +- web-search loop — `src/web-search/loop.ts:772` + +So a request for `anthropic/claude-sonnet-5` comes back as `claude-sonnet-5`, +and a client that round-trips `response.model` loses the provider routing. + +## Why adapted rather than cherry-picked + +#1122 is the stronger of the duplicate pair — it covers passthrough JSON/SSE and +alias collisions that #1121 misses — but it grows a narrow Anthropic identity +bug into a generalized cross-provider response-identity and catalog-lifecycle +rewrite across 25 files. A bug fix that changes response identity for every +provider is a contract change wearing a fix's clothing. The stack keeps the +correct mechanism and drops the generalization. + +## Change + +| Path | Op | Content | +|------|----|---------| +| `src/server/responses/core.ts` | MODIFY | Inside `applyFinalRouteRequestNormalization`, capture the pre-rewrite Codex-facing selector into a dedicated field (`clientFacingModelId`) **only on the Anthropic branch that performs the rewrite**; leave `parsed.modelId` as the upstream wire model so the Anthropic request body is unchanged | +| `src/server/responses/core.ts` | MODIFY | At the streaming and JSON builders emit `clientFacingModelId ?? modelId`. Because the field is set only where the Anthropic rewrite happens, providers that never rewrite are byte-identical — the `??` fallback is what keeps this a fix rather than a cross-provider contract change | +| `src/images/loop.ts` | MODIFY | Thread the selector through the loop's dependencies instead of reading mutated `parsed.modelId` (~`:902`) | +| `src/web-search/loop.ts` | MODIFY | Same (~`:772`) | +| `src/server/request-log.ts` | MODIFY | Keep the physical routed model in request logs — observability must still show what was actually called | +| `tests/anthropic-response-model.test.ts` | NEW | Qualified and legacy bare selectors; streaming and JSON; assert the upstream request body still carries the bare model | +| `tests/images/*`, `tests/web-search*.test.ts` | MODIFY | Bridge cases for both loops | + +**Explicitly not done here:** hidden bare-selector compatibility catalog rows. +#1122 generates them with an ownership marker, but catalog row generation with +restore/removal semantics is a separate contract with its own failure modes +(user-owned row collision, restore deleting a generated row). If the bare +selector needs to keep resolving, that is its own unit — noted in the PR so the +decision is visible rather than silently dropped. + +**Scope guard (audit finding 4).** A regression test asserts that a non-Anthropic +routed provider whose public and wire model differ emits exactly the same +`response.model` as before this phase. If that test cannot be written without +the field being set for that provider, the narrowing has failed and the phase +returns to P rather than shipping a silent contract change. + +Exact line numbers are deliberately omitted here: `core.ts` is edited by phases +050, 060, 070, and 130, so each phase re-locates its anchor by symbol +(`applyFinalRouteRequestNormalization`, the relay rewrite composition point) +at its own P rather than trusting a line number recorded before four edits. + +## Verification + +- `bun test tests/anthropic-response-model.test.ts` plus the image/web-search bridge suites +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 05, base = stack 04 head. `Closes #1117`. Credits Giulio Leone as the +reporter and primary implementer and ingwannu for the independent diagnosis, +and states plainly which parts of #1122 were intentionally not carried over. diff --git a/devlog/_plan/260806_stacked_bug_campaign/060_phase7_copilot_responses_normalization.md b/devlog/_plan/260806_stacked_bug_campaign/060_phase7_copilot_responses_normalization.md new file mode 100644 index 000000000..d0489aadd --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/060_phase7_copilot_responses_normalization.md @@ -0,0 +1,64 @@ +# 060 — Phase 7: GitHub Copilot Responses normalization (#1110, PR #1111) + +Credit: **Simon** (`Simon `), PR #1111 and issue +#1110. Adoption: **adapted** — the provider fix is kept, one unrelated commit +is dropped. + +## Defect (verified on `dev` = e9d957bf6) + +The `github-copilot` provider entry (`src/providers/registry.ts:2043`) declares +`adapter: "openai-chat"` but pins every inbound wire to Responses through +`modelWireDefaults` (`:2060`) — its models reject `/chat/completions` for real +Codex-agent traffic. So Copilot responses reach the Responses relay, which +composes only the generic image/id/snapshot repairs. No Copilot-specific +normalization exists, and Codex clients receive Copilot's dialect: non-canonical +ids, provider-only encrypted/obfuscation fields, and tool-call frames that never +form a valid Responses lifecycle. + +## Why adapted + +#1111's provider repair is sound: it keeps raw upstream frames for inspection +and continuation while normalizing only the Codex-facing relay. The single +merge conflict against `dev` comes from commit `6247d3932`, which edits an +unrelated CI permission assertion (now `tests/ci-workflows.test.ts:4399`). +Dropping that one commit removes the conflict without touching the fix. + +## Change + +Paths below are the contributor's actual files, read from `gh pr diff 1111` +against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `src/server/github-copilot-responses-repair.ts` | ADOPT (NEW, +338) | Provider-scoped client-facing block rewrite: stable response/item ids, strip Copilot-only encrypted/obfuscation fields, buffer tool input until the authoritative function/custom `.done` payload, then emit canonical lifecycle blocks | +| `src/server/responses/core.ts` | MODIFY (+33/−~4) | Compose the Copilot rewrite with the existing generic rewrites at the relay rewrite composition point (located by symbol — phases 050/070/130 also edit this file), on both the eager and tee paths, under the existing translator budget | +| `src/server/sse-payload-rewrite.ts` | MODIFY (+39/−~32) | Rewrite hook shape the Copilot repair plugs into | +| `src/server/relay.ts` | MODIFY (+28) | Wire the rewrite through the relay path | +| `tests/github-copilot-sse-rewrite.test.ts` | ADOPT (NEW, +281) | Provider-dialect fixtures: id normalization, field stripping, tool-call reconstruction | +| `tests/github-copilot-stream-contract.test.ts` | ADOPT (NEW, +248) | Endpoint-level stream contract | +| `tests/sse-payload-rewrite.test.ts`, `tests/sse-inspector-bounds.test.ts`, `tests/passthrough-abort.test.ts` | MODIFY (+34/+37/+11) | Supporting coverage. **Conflict note:** phase 010 already changed `sse-inspector-bounds.test.ts`; this phase rebases onto that result rather than reverting it | +| `tests/ci-workflows.test.ts` | UNTOUCHED | Commit `6247d3932` is dropped; this file must show **no diff** in the phase | + +Raw upstream frames stay untouched for diagnostics and continuation — the +rewrite is client-facing only. That boundary is what keeps request-log fidelity +intact. + +## Execution + +Cherry-pick the PR's commits except `6247d3932`, resolving against current +`dev`. If the range does not apply cleanly, reimplement the module from the +contributor's design and keep the `Co-authored-by: Simon` trailer. + +## Verification + +- `bun test tests/github-copilot-*.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +Live Copilot traffic is not available in this environment; the tests are +fixture-driven and the PR says so instead of claiming live verification. + +## PR + +Stack 06, base = stack 05 head. `Closes #1110`. Credits Simon and names the +dropped CI commit explicitly. diff --git a/devlog/_plan/260806_stacked_bug_campaign/070_phase8_darwin_eager_rewrite.md b/devlog/_plan/260806_stacked_bug_campaign/070_phase8_darwin_eager_rewrite.md new file mode 100644 index 000000000..81cbf3d50 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/070_phase8_darwin_eager_rewrite.md @@ -0,0 +1,53 @@ +# 070 — Phase 8: Darwin eager rewrite relay gate (#1127, PR #947) + +Credit: **0xWinner98** (reporter, #1127) and **biao** +(`WZBbiao `, PR #947 — the Darwin +predicate idea). Adoption: **reimplement** on current transport code. + +## Defect (verified on `dev` = e9d957bf6) + +`selectEagerPath` (`src/lib/bun-stream-caps.ts:99`) returns `null` for **any** +caller with `needsClientRewrite` set (`:106`), before platform logic runs at +all. Its Darwin tail then admits only a `config-eager` decision (`:112`). So a +macOS client that needs a rewrite always falls back to tee, and the inline +payload rewrite plus `rewriteBudget` in `core.ts` are wired only for Win32. + +#1025 repaired the snapshots, but the transport gate closed with #928 was never +reopened for macOS — so the macOS half of #893 is still broken. + +## Why reimplement + +PR #947 has the right predicate but conflicts with current `dev` across 14 +files, and its tests assert through fixed `settle()` waits +(`tests/relay-eager.test.ts:269-284`) — timing-based assertions that pass for +the wrong reason. The predicate is ported; the test shape is replaced with a +deterministic `onDone` promise plus a bounded failure timeout. + +## Change + +| Path | Op | Content | +|------|----|---------| +| `src/lib/bun-stream-caps.ts` | MODIFY | In `selectEagerPath`, stop rejecting on `needsClientRewrite` alone (`:106`): when the mode resolves to `config-eager` on Darwin, the explicit selection stays eligible. Darwin `auto` is **not** broadened, and the non-darwin/non-win32 rejection is untouched | +| `src/server/responses/core.ts` | MODIFY | Locate the `win32EagerRewrite` composition point by symbol (not line — phases 050/060/130 also edit this file). Compose payload/block rewrites when either Win32 forced rewrite **or** a Darwin-selected eager path is active; pass `rewriteBudget` whenever inline rewriting is active | +| `tests/bun-stream-caps.test.ts` | MODIFY | Full platform × mode × rewrite-required policy matrix | +| `tests/relay-eager.test.ts` | MODIFY | Replace fixed `settle()` waits with an `onDone` promise and a bounded failure timeout | +| `tests/responses-snapshot-repair-server.test.ts` | MODIFY | Darwin rewrite activation reaches the client | + +**Win32 behavior is unchanged** — its forced-rewrite path is separate and stays +as is. Broadening Darwin `auto` is deliberately avoided: the default path should +not change transport strategy as a side effect of a bug fix. + +## Verification + +- `bun test tests/bun-stream-caps.test.ts tests/relay-eager.test.ts tests/responses-snapshot-repair-server.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +macOS is the host here, so the Darwin path is exercised natively — this is the +one platform phase with real local coverage. + +## PR + +Stack 07, base = stack 06 head. `Closes #1127`. Credits 0xWinner98 for the +macOS-specific report and biao for the predicate from #947, and states why the +patch was rebuilt rather than picked. diff --git a/devlog/_plan/260806_stacked_bug_campaign/080_phase9_vision_rawbody_sync.md b/devlog/_plan/260806_stacked_bug_campaign/080_phase9_vision_rawbody_sync.md new file mode 100644 index 000000000..961b329f9 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/080_phase9_vision_rawbody_sync.md @@ -0,0 +1,53 @@ +# 080 — Phase 9: vision raw-body image synchronization (PR #1047) + +Credit: **Bailey** (`baileyh8 `), PR #1047. +Adoption: **adapted** — one remaining leak closed. + +## Defect (verified on `dev` = e9d957bf6) + +`describeImagesInPlace` (`src/vision/index.ts:347`) rebuilds each message, +replacing image parts with their descriptions in `parsed.context.messages` +(`:426`), and `stripImagesInPlace` (`:453`) does the same with an +"[image omitted…]" placeholder. + +Neither touches `parsed._rawBody`. The native Responses passthrough adapter +serializes `_rawBody`, while translated adapters serialize `context.messages` — +so on the passthrough path the original pixels are still sent to a text-only +upstream *after* the sidecar produced a caption. There is no +`syncRawBodyImageDescriptions` on `dev` (`rg` returns no matches); the whole +function is new in #1047. + +## Why adapted + +#1047 adds `syncRawBodyImageDescriptions` and calls it at the end of both +`describeImagesInPlace` and `stripImagesInPlace`. The mechanism is correct and +the E2E coverage is strong. Two conditions in the contributor's function make it +a partial fix: + +- It returns early when `descriptions.length === 0`, so a caption pass that + produced nothing leaves the raw images in `_rawBody` — the failure path is the + one that leaks. +- When `descriptions[nextDescription++]` is `undefined` (fewer captions than + images) it returns the original `input_image` part unchanged, so a partial + caption pass still forwards raw pixels. + +## Change + +Cherry-pick #1047, then close both failure paths. + +| Path | Op | Content | +|------|----|---------| +| `src/vision/index.ts` | ADOPT | `syncRawBodyImageDescriptions` (+ `isPlainRecord`) after `renderDescription` (~`:275`), called at the end of `describeImagesInPlace` (~`:506`) and `stripImagesInPlace` (~`:535`) | +| `src/vision/index.ts` | ADAPT | **Change from #1047:** drop the `descriptions.length === 0` early return so normalization runs regardless; when no description is available for an image, substitute the same "[image omitted…]" text `stripImagesInPlace` uses rather than returning the raw part | +| `tests/vision-sidecar-e2e.test.ts` | ADAPT | Bailey's +195 lines plus: zero-caption path and fewer-captions-than-images path, each asserting no `input_image` survives in `_rawBody` | + +## Verification + +- `bun test tests/vision-*.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 08, base = stack 07 head. Credits Bailey; names the two failure-path gaps +added on top of their patch. diff --git a/devlog/_plan/260806_stacked_bug_campaign/090_phase10_gemini_effort_docs.md b/devlog/_plan/260806_stacked_bug_campaign/090_phase10_gemini_effort_docs.md new file mode 100644 index 000000000..6513a6e6c --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/090_phase10_gemini_effort_docs.md @@ -0,0 +1,35 @@ +# 090 — Phase 10: Gemini/CCA reasoning-effort docs and coverage (PR #978) + +Credit: **Pranav Yerramaneni** (`devmello `), PR #978. +Adoption: **adapted** — runtime kept, documentation and coverage corrected. + +## Defect + +`thinkingLevel` was sent only for a narrow model set, so any Google model with a +declared effort ladder silently ignored the selected reasoning effort. + +## Why adapted + +The runtime gate at `src/adapters/google.ts:343-360` is sound and stays. What +lags is the contract: the docs do not say that **both** `reasoningEfforts` and +`modelReasoningEfforts` assert capability, nor that CCA uses its own +envelope-specific path — and the test suite has no provider-wide positive case, +only the image-adjacent one. + +## Change + +| Path | Op | Content | +|------|----|---------| +| `src/adapters/google.ts` | KEEP | `:343-360` as authored | +| `docs-site/src/content/docs/reference/configuration/providers.md` | MODIFY | `:83-84` — both ladder sources assert capability; CCA uses the envelope-specific path | +| `tests/google-hardening.test.ts` | MODIFY | Add the provider-wide non-image positive case beside the existing direct-AI-Studio `thinkingLevel` test at `:284` (the file ends at `:346`) | + +## Verification + +- `bun test tests/google-hardening.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 09, base = stack 08 head. Credits Pranav Yerramaneni. diff --git a/devlog/_plan/260806_stacked_bug_campaign/100_phase11_structured_output_schema.md b/devlog/_plan/260806_stacked_bug_campaign/100_phase11_structured_output_schema.md new file mode 100644 index 000000000..e487724ff --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/100_phase11_structured_output_schema.md @@ -0,0 +1,52 @@ +# 100 — Phase 11: routed structured-output schema preservation (PR #985) + +Credit: **Pranav Yerramaneni** (`devmello `), PR #985. +Adoption: **adapted** — one schema-loss bug closed. + +## Defect (verified on `dev` = e9d957bf6) + +The Responses `text.format` request is reduced to a boolean at the parser: +`parseRequest` calls `detectStructuredOutput(data.text)` +(`src/responses/parser.ts:671`) and keeps only `_structuredOutput: true` +(`:685`). The format object — schema, name, strict — is discarded there, so no +adapter can forward it. + +`src/adapters/openai-chat.ts` has no `response_format` handling at all on `dev` +(`rg response_format src/adapters/openai-chat.ts` returns no matches); body +assembly ends at the `promptCacheKey` block near `:818`. A client routed to an +`openai-chat` provider therefore asks for a JSON schema and receives prose. + +## Why adapted + +#985's architecture is right: carry the parsed format on `options.textFormat`, +keep `_structuredOutput` for the web-search sidecar, and re-nest the flattened +Responses fields under `json_schema` — the inverse of `responseFormatToText`. + +One gap remains in the contributor's adapter code: the `json_schema` branch is +guarded by `textFormat.schema !== undefined`, so a schema-less `json_schema` +falls through and **no** `response_format` is sent. That silently reproduces the +original defect on a narrower input. + +## Change + +Cherry-pick #985's commits, then correct the adapter guard. + +| Path | Op | Content | +|------|----|---------| +| `src/responses/parser.ts` | ADOPT | Replace `detectStructuredOutput` with `parseTextFormat` returning the format object; set `options.textFormat`; derive `_structuredOutput` from it (~`:668-711` as authored) | +| `src/types.ts` | ADOPT | `textFormat` on `OcxRequestOptions` (+14 as authored) | +| `src/adapters/openai-chat.ts` | ADAPT | Add the `response_format` mapping after the `promptCacheKey` block (~`:820`). **Change from #985:** enter the `json_schema` branch whenever `type === "json_schema"`, adding the `schema` member conditionally instead of gating the whole branch on it | +| `src/adapters/openai-responses.ts`, `src/server/responses/core.ts`, `src/server/chat-completions.ts` | ADOPT | As authored | +| `tests/openai-chat-hardening.test.ts` | ADAPT | Authored cases plus a schema-less `json_schema` case asserting `response_format.type === "json_schema"` is still sent | +| `tests/responses-parser.test.ts`, `tests/chat-completions-endpoint.test.ts`, `tests/responses-compaction-routing.test.ts`, `tests/kiro-adapter.test.ts`, `tests/server-kiro-completion-e2e.test.ts` | ADOPT | As authored | +| `docs-site/src/content/docs/reference/proxy-formats.md` | ADOPT | +7 as authored | + +## Verification + +- `bun test` on the structured-output and openai-chat adapter suites +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 10, base = stack 09 head. Credits Pranav Yerramaneni. diff --git a/devlog/_plan/260806_stacked_bug_campaign/110_phase12_cursor_structured_edits.md b/devlog/_plan/260806_stacked_bug_campaign/110_phase12_cursor_structured_edits.md new file mode 100644 index 000000000..d131e9cb7 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/110_phase12_cursor_structured_edits.md @@ -0,0 +1,47 @@ +# 110 — Phase 12: Cursor structured-edit conversion (#1017, PR #1036) + +Credit: **NexusCore** (`@ZachDreamZ`), PR #1036. Reporter: **Vincent-HD** +(#1017). Adoption: **adapted** — provenance derivation corrected. + +## Defect + +Codex advertises `apply_patch` as a single-string function +(`src/responses/parser.ts:166`). The Cursor adapter emits calls with normalized +arguments but never converts structured edits into a valid `apply_patch` +envelope (`src/adapters/cursor/protobuf-events.ts:388`), so Cursor consistently +produces invalid payloads. + +## Why adapted + +#1036's translator and tests are good. The remaining defect is provenance: +`src/adapters/cursor/live-transport.ts:543-560` computes visibility from +`cursorVisibleTools` but then derives structured-edit availability and tool +names from the *earlier* `request.tools`. After filtering or budgeting removes a +tool, the two disagree — and a synthetic edit identified by wire name alone can +collide with a real client tool. + +## Change + +File list read from `gh pr diff 1036` against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `src/adapters/cursor/tool-definitions.ts` | ADOPT (+111) | Cursor-compatible `edit_file`/`multi_edit` definitions, injected only when Codex exposed `apply_patch`, tagged with internal provenance | +| `src/adapters/cursor/protobuf-events.ts` | ADOPT (+180) | Convert exact-match replacements into a valid Codex freeform patch envelope, emit as `apply_patch` preserving the original call id, and reject malformed/ambiguous replacements with an explicit bridge error rather than forwarding invalid patch text | +| `src/adapters/cursor/request-builder.ts` | ADOPT (+15/−~4) | Produce the final filtered/budgeted catalog that provenance derives from | +| `src/adapters/cursor/live-transport.ts` | ADAPT (+15) | **Change from #1036:** derive the structured-edit set from the final filtered catalog produced by `request-builder.ts`, not from the earlier `request.tools` — after filtering or budgeting drops a tool the two disagree | +| `src/adapters/cursor/native-exec.ts`, `src/adapters/cursor/native-exec-fs.ts` | ADOPT (+6/−~2, +15/−~7) | Supporting wiring as authored | +| `tests/cursor-structured-edit.test.ts` | ADAPT (NEW, +449) | Authored conversion and collision cases, plus a post-filter provenance case proving the set is derived after filtering | + +Injection happens **only** when Codex exposed `apply_patch` — the adapter must +not invent an editing capability the client never offered. + +## Verification + +- `bun test tests/cursor-structured-edit.test.ts` and the Cursor adapter suites +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 11, base = stack 10 head. `Closes #1017`. Credits NexusCore and Vincent-HD. diff --git a/devlog/_plan/260806_stacked_bug_campaign/120_phase13_reasoning_replay_empty_delta.md b/devlog/_plan/260806_stacked_bug_campaign/120_phase13_reasoning_replay_empty_delta.md new file mode 100644 index 000000000..d619620b2 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/120_phase13_reasoning_replay_empty_delta.md @@ -0,0 +1,48 @@ +# 120 — Phase 13: reasoning-replay empty-delta handoff (PR #1126) + +Credit: **NexusCore** (`@ZachDreamZ`, +`Agent59353 `), PR #1126. +Adoption: **adapted** — the bug fix is taken, the persistence feature is not. + +## Defect + +Replay candidates are lost when a provider emits empty `text_delta` / +`thinking_delta` events, so reasoning replay restarts cannot reconstruct the +turn. + +## Why adapted + +A real empty-delta bug sits inside a much larger optional feature: persisting +chain-of-thought to disk, shutdown hooks, global counters, and config plumbing. +Writing model reasoning to disk is a privacy-surface change, not a bug fix — +`src/responses/reasoning-replay-cache.ts:17` documents a memory-only contract +deliberately. Changing that contract needs its own decision, not a ride along a +defect repair. + +## Change + +File list read from `gh pr diff 1126` against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `src/bridge.ts` | ADOPT (+14/−~4) | Preserve replay candidates across empty `text_delta`/`thinking_delta` events in the streaming and batch builders | +| `src/responses/reasoning-replay-cache.ts` | ADAPT (#1126: +244) | Take only the empty-delta handling. **Dropped:** disk persistence, exit hooks, and global warning counters — the memory-only contract at `:17` stays | +| `src/config.ts`, `src/lib/config-dir.ts`, `src/adapters/openai-chat.ts` | DROP | #1126 edits these only to plumb the persistence feature; none is needed for the defect | +| `tests/bridge-reasoning-replay-batch.test.ts` | ADOPT (NEW, +92) | Batch-builder empty-delta sequences | +| `tests/reasoning-replay-robustness.test.ts` | ADAPT (NEW, +240) | Streaming empty-delta sequences; drop the persistence cases and add an assertion that nothing is written to disk | + +**Dropped:** disk persistence, exit hooks, global warning metrics, and the +associated config surface. Stated in the PR so the contributor can see exactly +what was kept and why. + +## Verification + +- `bun test` on the reasoning-replay and bridge suites +- `bun run typecheck` +- `bun run privacy:scan` (load-bearing here — it is the gate that would catch a + reasoning-to-disk regression) + +## PR + +Stack 12, base = stack 11 head. Credits NexusCore. #950 is already closed by +#971, so no issue link. diff --git a/devlog/_plan/260806_stacked_bug_campaign/130_phase14_usage_attempt_persistence.md b/devlog/_plan/260806_stacked_bug_campaign/130_phase14_usage_attempt_persistence.md new file mode 100644 index 000000000..4af0d1d27 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/130_phase14_usage_attempt_persistence.md @@ -0,0 +1,45 @@ +# 130 — Phase 14: usage-log attempt persistence (PR #1093) + +Credit: **Takashi Yamashiro** +(`Takashi Yamashiro `), PR #1093. +Adoption: **adapted** — attempt recording kept, forgeable ingress spans dropped. + +## Defect + +Ordinary request attempts are not persisted, so the usage log cannot show what +was actually attempted; normalization also collapses explicit empty arrays, +losing the distinction between "no attempts" and "not recorded". + +## Why adapted + +The attempt-persistence half is useful and correct. The ingress-span half is +not safe as written: the endpoint reads a client-supplied correlation header at +the public admitted surface (`src/server/index.ts:957`), so **any admitted +client can forge a regex-shaped "guard-issued" ingress span**. Persisted +telemetry that an untrusted caller can shape is worse than absent telemetry — +it looks authoritative. That half waits for a trusted producer boundary. + +## Change + +File list read from `gh pr diff 1093` against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `src/server/responses/core.ts` | ADOPT (+13) | Create the ordinary request attempt after final adapter resolution, using the existing attempt owner (anchor located by symbol — phases 050/060/070 also edit this file) | +| `src/server/request-log.ts` | ADOPT (+24/−~3) | Use the existing attempt owner; no new writer | +| `src/usage/log.ts` | ADOPT (+15/−~1) | Preserve explicit empty arrays through normalization | +| `src/server/index.ts` | DROP (#1093: +3) | The client-supplied correlation-header read at the public admitted endpoint — this is the forgeable-span surface | +| `tests/request-log.test.ts` | ADOPT (+41) | Attempt row presence after resolution | +| `tests/usage-log.test.ts` | ADOPT (+37) | Empty-array preservation | +| `tests/server-auth.test.ts` | DROP (#1093: +14/−~1) | Covers the dropped ingress-span header path | + +## Verification + +- `bun test` on the usage-log and request-log suites +- `bun run typecheck` +- `bun run privacy:scan` (must stay green — no account identifiers in the log) + +## PR + +Stack 13, base = stack 12 head. Credits Takashi Yamashiro and explains the +security reason the ingress-span portion was withheld. diff --git a/devlog/_plan/260806_stacked_bug_campaign/140_phase15_effort_picker_and_pi_export.md b/devlog/_plan/260806_stacked_bug_campaign/140_phase15_effort_picker_and_pi_export.md new file mode 100644 index 000000000..737dbb66f --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/140_phase15_effort_picker_and_pi_export.md @@ -0,0 +1,64 @@ +# 140 — Phase 15: effort-picker fail-closed + Pi loopback export (PRs #1092, #1085) + +Credit: **Eachann** (`关俊江 `, PR #1092) and **n3wr1ch** +(`n3wr1ch `, PR #1085). +Adoption: **adapted** — the two bug cores are extracted from two large PRs. + +Both slices are small, independent, and touch different files, so they share one +stack phase rather than splitting into two near-empty PRs. If review prefers +them separate, the phase splits cleanly at the file boundary. + +## Defect A — effort picker (Eachann, #1092) + +A model whose capability ladder is unknown disappears from the effort picker +entirely, instead of being offered with no forced default. + +File list read from `gh pr diff 1092` against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `gui/src/combo-workspace-data.ts` | ADAPT | Treat an unknown capability ladder as a wildcard **for picker availability only** | +| `src/combos/request.ts` | ADOPT (+14) | Continue omitting the runtime default when support is unknown — the runtime stays fail-closed | +| `src/combos/types.ts`, `src/combos/index.ts`, `src/types.ts` | ADOPT (+6/+1/+6) | Supporting types | +| `src/server/management/combo-routes.ts` | ADOPT (+13/−~2) | Route plumbing for the picker contract | +| `src/server/responses/core.ts` | ADOPT (+4) | Runtime guard | +| `tests/combo-workspace-data.test.ts` | ADOPT (+47/−~4) | Unknown-ladder model appears in the picker | +| `tests/combos.test.ts`, `tests/combo-management-api.test.ts`, `tests/server-combo-failover-e2e.test.ts` | ADOPT (+26/+27/+17) | Runtime fail-closed behavior end to end | +| `tests/codex-catalog.test.ts` (#1092: +211) | DROP | Belongs to the dropped catalog fallback synthesis | +| The #1092 copy/`imageInput`/locale files | DROP | Feature work, not this defect | + +**Dropped from #1092:** catalog fallback synthesis, public-name copy redesign, +`imageInput` policy, and locale churn. The PR also fails `git diff --check` on +an added EOF blank line, which the extraction avoids. + +## Defect B — Pi loopback export (n3wr1ch, #1085) + +Pi's exported client config references an unresolved environment variable, so +loopback models vanish for a user with no API key set. + +File list read from `gh pr diff 1085` against `dev` = `e9d957bf6`. + +| Path | Op | Content | +|------|----|---------| +| `src/clients/config-export.ts` | ADAPT (#1085: +46/−~29) | Use the existing non-secret loopback placeholder instead of the unresolved env reference, and declare no required environment variable for Pi. **Dropped** from the same file: the generalized export-policy rewrite | +| `tests/client-config-export.test.ts` | ADOPT (+19/−~11) | Serializer output for Pi with no key set | +| `tests/management-client-config-route.test.ts` | ADOPT (+76/−~2) | Route-level output | +| `tests/client-config-export-new-clients.test.ts`, `tests/client-config-new-clients.test.ts` | ADOPT (+4 each) | Client-matrix alignment | +| `src/cli/export-command.ts` (#1085: +20/−~11), `src/cli/opencode.ts` (+13/−~4), `src/server/management/model-rows.ts` (+25/−~10), `tests/cli-export-command.test.ts` (+87/−~2) | DROP | Combo/direct-mode filtering and cross-client contract churn | +| `src/combos/index.ts` (#1085: +1), `src/combos/types.ts` (#1085: +15) | DROP **#1085 hunks only** | These two files are also touched by #1092 in slice A above, and **those hunks are adopted**. Only #1085's additions — its direct-mode combo filtering — are excluded. When implementing, take the #1092 hunks and skip the #1085 ones rather than reverting the file | + +**Dropped from #1085:** combo/direct-mode filtering, cross-client contract +changes, and generalized export-policy churn across 31 files. + +## Verification + +- `bun test` on the combo/config-export suites +- `bun run typecheck` +- `bun run lint:gui` and `bun run build:gui` (this phase touches `gui/`) +- `bun run privacy:scan` +- GUI screenshot required in the PR description by repository policy + +## PR + +Stack 14, base = stack 13 head. Credits Eachann and n3wr1ch, and lists what was +intentionally left in their original PRs. diff --git a/devlog/_plan/260806_stacked_bug_campaign/150_phase16_test_isolation_and_allowlist_docs.md b/devlog/_plan/260806_stacked_bug_campaign/150_phase16_test_isolation_and_allowlist_docs.md new file mode 100644 index 000000000..9b6c077be --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/150_phase16_test_isolation_and_allowlist_docs.md @@ -0,0 +1,56 @@ +# 150 — Phase 16: test-home isolation + Desktop allowlist docs (PRs #997, #999) + +Credit: **Yuxin Qiao** +(`Yuxin Qiao `), PRs #997 and +#999. Adoption: near-verbatim cherry-pick of both. + +Two small, independent, merge-clean contributions from the same author, landed +together as the stack's last content phase. + +## Slice A — usage-log fixture isolation (#997) + +`tests/management-api-logs-metrics.test.ts` wrote fixtures into the real +OpenCodex home, so running the suite could touch a developer's actual usage log. + +Source commits `3304d5c83`, `cdebe0e13`: + +| Path | Op | Content | +|------|----|---------| +| `tests/management-api-logs-metrics.test.ts` | MODIFY | +51/−2: pin fixture writes to the scratch home and assert the target path and content explicitly, so the isolation is proven rather than assumed | + +The explicit target assertion is what makes this non-vacuous — a test that +merely stopped writing would pass silently even if isolation broke again. + +## Slice B — Desktop remote allowlist documentation (#999, issue #241) + +Routed rows are emitted with `visibility = "list"` +(`src/codex/catalog/sync.ts:240`), but Codex Desktop applies its own allowlist +downstream — outside this repository (upstream openai/codex#19694). The +limitation and its workaround were undocumented. + +Source commit `d849dc631`: + +| Path | Op | Content | +|------|----|---------| +| `docs-site/src/content/docs/guides/codex-app-models.md` | MODIFY | +17: the limitation and the workaround | +| `docs-site/.../{ja,ko,ru,zh-cn}/guides/codex-app-models.md` | MODIFY | +52 total: matching localized notes | + +Docs-only; #241 stays **open** as an upstream tracker — documenting a +limitation is not fixing it. + +## Execution + +``` +git cherry-pick 3304d5c83 cdebe0e13 d849dc631 +``` + +## Verification + +- `bun test tests/management-api-logs-metrics.test.ts` +- `bun run typecheck` +- `bun run privacy:scan` + +## PR + +Stack 15, base = stack 14 head. Credits Yuxin Qiao for both slices. References +#241 without closing it. diff --git a/devlog/_plan/260806_stacked_bug_campaign/160_phase17_closeout.md b/devlog/_plan/260806_stacked_bug_campaign/160_phase17_closeout.md new file mode 100644 index 000000000..6525fda94 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/160_phase17_closeout.md @@ -0,0 +1,66 @@ +# 160 — Phase 17: closeout (attribution comments, full suite, dispositions) + +Terminal phase. No source changes beyond this unit's own records. + +## 1. Attribution comments on superseded contributor PRs + +One comment per in-scope contributor PR, posted with `gh pr comment`. The +comment must state, in this order: + +1. Which stack PR carries their work. +2. That their authorship is preserved — cherry-picked authorship or a + `Co-authored-by:` trailer, named explicitly. +3. What was adapted or dropped, and why, when the adoption was not verbatim. +4. That their PR is left **open** for them to close or continue — the campaign + does not close contributor PRs. + +Target PRs: #1114, #1124, #1130, #1115, #1122, #1121, #1111, #947, #1047, #978, +#985, #1036, #1126, #1093, #1092, #1085, #997, #999. + +No comment is posted on deferred feature programs (#1131, #1109, #1096, #1039, +#1010, #1002, #812, #811, #581) — they were never superseded, and a comment +implying otherwise would be misleading. + +## 2. Full suite on the final stack head + +`bun run test` on the top of the stack. The result is reported as measured — +including any load-sensitive failures, with the isolated re-run result stated +separately rather than folded into a "green" claim. + +## 3. Disposition matrix + +Written to `devlog/_plan/260806_stacked_bug_campaign/170_dispositions.md` with +this exact schema — one row per item in `001` and `002`, no item omitted: + +``` +| Item | Kind | Final state | Carrier | Credited | Evidence | +``` + +- **Item** — `#` (issue or PR). +- **Kind** — `issue` or `pr`. +- **Final state** — one of `landed-stackNN`, `deferred`, `upstream`, + `needs-info`, `feature`, `already-merged`. +- **Carrier** — the stack PR number that carries the work, or `—`. +- **Credited** — the contributor display name, or `—`. +- **Evidence** — the commit SHA on the stack branch, or the reason string for a + non-landed state. + +A row whose Final state is `landed-stackNN` and whose Evidence is empty is a +FAIL of this phase: it claims a landing with no proof. + +The matrix must reconcile against the campaign totals: 49 issues and 32 open +PRs (+ #1129 already merged). A total mismatch is a FAIL, not a rounding note. + +## 4. What is deliberately NOT done + +- No PR is merged. +- No issue or PR is closed — including issues whose fix is in the stack. They + close when the stack merges, which is the maintainer's decision. +- No branch is deleted, no release is cut, no version is bumped. + +## Terminal outcome + +Reported honestly against the goalplan criteria: `DONE` only if the stack PRs +exist against `dev` with their CI state reported and every in-scope contributor +PR carries its attribution comment. Otherwise the real outcome +(`BUDGET_EXHAUSTED`, `BLOCKED`, `NEEDS_HUMAN`) with evidence. diff --git a/devlog/_plan/260806_stacked_bug_campaign/170_dispositions.md b/devlog/_plan/260806_stacked_bug_campaign/170_dispositions.md new file mode 100644 index 000000000..6dcc50bde --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/170_dispositions.md @@ -0,0 +1,150 @@ +# 170 — Disposition matrix (final) + +Schema per `160_phase17_closeout.md`. Reconciles against the frozen cutoff: +**49 open issues, 32 open PRs** (+ #1129 merged mid-triage). + +## Stack PRs created + +| Stack | PR | Base | Subject | Credited | +|-------|----|------|---------|----------| +| 01 | [#1133](https://github.com/lidge-jun/opencodex/pull/1133) | `dev` | Bounded translated SSE inspection | ingwannu | +| 02 | [#1134](https://github.com/lidge-jun/opencodex/pull/1134) | stack 01 | Skip empty native-profile stage sweeps | ingwannu | +| 03 | [#1135](https://github.com/lidge-jun/opencodex/pull/1135) | stack 02 | Native-main ACL timeout retry | luvs01 | +| 04 | [#1136](https://github.com/lidge-jun/opencodex/pull/1136) | stack 03 | Bounded rollout inspection | Simon | +| 05 | [#1137](https://github.com/lidge-jun/opencodex/pull/1137) | stack 04 | Structured output + Gemini effort ladders | Pranav Yerramaneni | +| 06 | [#1138](https://github.com/lidge-jun/opencodex/pull/1138) | stack 05 | Anthropic response-model identity | Giulio Leone, ingwannu | +| 07 | [#1139](https://github.com/lidge-jun/opencodex/pull/1139) | stack 06 | Vision raw-body sync + usage attempts | Bailey, Takashi Yamashiro | +| 08 | [#1141](https://github.com/lidge-jun/opencodex/pull/1141) | stack 07 | GitHub Copilot Responses normalization | Simon | +| 09 | [#1142](https://github.com/lidge-jun/opencodex/pull/1142) | stack 08 | Darwin eager rewrite relay gate | biao, 0xWinner98 | +| 10 | [#1144](https://github.com/lidge-jun/opencodex/pull/1144) | stack 09 | Cursor structured edits + reasoning replay | NexusCore, Vincent-HD | +| 11 | [#1150](https://github.com/lidge-jun/opencodex/pull/1150) | stack 10 | Test-home isolation + Desktop allowlist docs | Yuxin Qiao | +| 12 | [#1151](https://github.com/lidge-jun/opencodex/pull/1151) | stack 11 | Effort picker + Pi loopback export | Eachann, n3wr1ch | + +The chain is linear: each PR targets the previous head, and stack 01 targets +`dev`. Merging bottom-up collapses the whole chain onto `dev`. + +## Issues + +| Item | Kind | Final state | Carrier | Credited | Evidence | +|------|------|-------------|---------|----------|----------| +| #1112 | issue | landed-stack01 | #1133 | ingwannu | `b2d1bb85d`, sse-inspector-bounds 23 pass | +| #1120 | issue | landed-stack02 | #1134 | ingwannu | `94c5ccf59`, native-profile trio 51 pass | +| #1117 | issue | landed-stack06 | #1138 | Giulio Leone | `1879bd0df`, identity suites 132 pass, red-green confirmed | +| #1110 | issue | landed-stack08 | #1141 | Simon | `8369eb949`, `58500d277`, copilot suites 50 pass | +| #1127 | issue | landed-stack09 | #1142 | biao, 0xWinner98 | `b6d325079`, 70 pass, red-green confirmed | +| #1017 | issue | landed-stack10 | #1144 | NexusCore | cursor+replay suites 32 pass | +| #241 | issue | upstream | — | Yuxin Qiao | Desktop allowlist is outside this repo (openai/codex#19694); #999 documents it, phase 150 deferred | +| #1102 | issue | deferred | — | — | Transparent credential hand-off to a spawned binary needs a new trust/admission design | +| #1059 | issue | deferred | — | — | Windows suite failures cross six subsystems; no subset provable from macOS | +| #1049 | issue | deferred | — | — | Crash-safe adoption is migration-grade work with its own recovery matrix | +| #92, #417, #1100 | issue | upstream | — | — | Fernet task body, Korean realtime U+FFFD, Codex summary-flag gating — all client-side | +| #1128, #1024, #994, #904, #796, #418 | issue | needs-info | — | — | Not reproducible on the current tree; each row in `001` cites the code that refutes or cannot confirm the report | +| 30 further issues | issue | feature | — | — | Enumerated in `001_issue_triage.md`; enhancement or roadmap, not bugs | + +**Issue total: 6 landed + 1 upstream-documented + 3 deferred-large + 3 upstream ++ 6 needs-info + 30 feature = 49.** + +## Pull requests + +| Item | Kind | Final state | Carrier | Credited | Evidence | +|------|------|-------------|---------|----------|----------| +| #1114 | pr | landed-stack01 | #1133 | ingwannu | cherry-picked, authorship preserved; comment 5206431175 | +| #1124 | pr | landed-stack02 | #1134 | ingwannu | cherry-picked; comment 5206431146 | +| #1130 | pr | landed-stack03 | #1135 | luvs01 | cherry-picked; comment 5206434180 | +| #1115 | pr | landed-stack04 | #1136 | Simon | 5 commits cherry-picked; comment 5206434197. **Closed by the author** (`Simon-Opopeee`, 2026-08-06T15:15:19Z, not merged) after the attribution comment — their decision, not a campaign action | +| #985 | pr | landed-stack05 | #1137 | Pranav Yerramaneni | adapted (schema-less `json_schema` guard); comment 5206933287 | +| #978 | pr | landed-stack05 | #1137 | Pranav Yerramaneni | adapted (docs + provider-wide case); comment 5206933565 | +| #1122 | pr | landed-stack06 | #1138 | Giulio Leone | adapted, narrowed to Anthropic; comment 5206933313 | +| #1121 | pr | credited-only | #1138 | ingwannu | duplicate of #1122; diagnosis credited; comment 5206933787 | +| #1047 | pr | landed-stack07 | #1139 | Bailey | adapted (failure-path leaks closed); comment 5206941034 | +| #1093 | pr | landed-stack07 (partial) | #1139 | Takashi Yamashiro | attempts adopted; ingress spans withheld as forgeable; comment 5206941258 | +| #1111 | pr | landed-stack08 | #1141 | Simon | adapted, dropped `6247d3932`; comment 5207281602 | +| #947 | pr | reimplemented-stack09 | #1142 | biao | predicate ported, timing-based tests rewritten; comment 5207281733 | +| #1036 | pr | landed-stack10 | #1144 | NexusCore | adapted (post-filter provenance); comment 5207281718 | +| #1126 | pr | landed-stack10 (partial) | #1144 | NexusCore | empty-delta adopted; disk persistence withheld; comment 5207281904 | +| #1092 | pr | landed-stack12 | #1151 | Eachann | `aff0bb0b3`, picker fix extracted; catalog/copy/`imageInput`/locales dropped; comment 5207733645 | +| #1085 | pr | landed-stack12 | #1151 | n3wr1ch | `bc796a086`, Pi loopback placeholder; cli/model-rows churn dropped; comment 5207733762 | +| #997 | pr | landed-stack11 | #1150 | Yuxin Qiao | `5b32a5e19`, `64be20518` cherry-picked; comment 5207733648 | +| #999 | pr | landed-stack11 | #1150 | Yuxin Qiao | `5bf99550c` cherry-picked; #241 not closed; comment 5207733780 | +| #1095 | pr | rejected-unsafe | — | Bailey | Idle-timer completion can truncate a slow valid response; needs authoritative EOF | +| #1056 | pr | deferred | — | biao | 54 files with backup/convergence/GUI gaps | +| #1131, #1109, #1096, #1039, #1010, #1002, #812, #811, #581 | pr | deferred | — | various | Feature programs / security-review units, enumerated in `002` | +| #557, #1008 | pr | deferred | — | JUN | Maintainer-owned, already in flight, deliberately untouched | +| #1119 | pr | deferred | — | JUN | Maintainer's own contract-test PR | +| #1129 | pr | already-merged | — | JUN | `e9d957bf6`, merged mid-triage | + +**PR total: 16 landed + 1 credited-only + 1 rejected + 14 deferred = 32 open, ++ #1129 already merged.** + +With phases 140 and 150 executed, every phase in the roadmap has run. The +remaining 14 deferrals are feature programs, maintainer-owned in-flight PRs, +and the two reimplementations judged not worth landing as written — each with +its reason in `002_pr_triage.md`. + +## Contributors credited + +ingwannu, luvs01, Simon (Simon-Opopeee), Pranav Yerramaneni (DevMello), +Giulio Leone, Bailey (baileyh8), Takashi Yamashiro, biao (WZBbiao), +NexusCore (ZachDreamZ) — plus reporters MarcusNeufeldt, 0xWinner98, Vincent-HD. + +Every landed commit derived from contributor work carries either their original +authorship (cherry-pick) or a `Co-authored-by:` trailer. No contributor PR was +merged, closed, or force-pushed. + +## Not done, deliberately + +- No PR merged. No issue or PR closed — including issues the stack fixes; they + close when the stack merges, which is the maintainer's call. +- No branch deleted, no release, no version bump. + +## One in-scope deviation, recorded + +Phase 140 marked `tests/cli-export-command.test.ts` as DROP, but the Pi change +made two of its assertions stale: they expected the `export OPENCODEX_API_KEY=` +output that #1085 reports as the defect. Honoring the scope line would have +shipped a red suite, so the assertions were updated to the new contract +(`304d0d883`). The security property they exist for — no `ocx_` token in +stdout — is unchanged and still asserted. Recorded here because a plan +deviation that nobody writes down is how a roadmap stops being trustworthy. + +## CI state at close + +Stacks 01-06 fully green. Stacks 07-10 were still completing or hit a GitHub +Actions outage (`Failed to resolve action download info: Service Unavailable`) +that was rerun. One earlier `test 3/4` failure on #1133 was a Bun runtime +`EEXIST: epoll_ctl` between tests with **no assertion failure**, proven +incidental by #1134 — which contains the same commit — passing that shard; it +is green after rerun. + +## Final audit (independent, terra) and the two defects it found + +A read-only adversarial audit of the finished stack returned **FAIL** on two +points. Both are recorded here rather than smoothed over. + +**1. #1144 credited NexusCore in prose but not in git.** The carried commits +are authored by `Agent59353 ` — the identity on +#1126's head — and nothing in the commit metadata named ZachDreamZ/NexusCore. +A PR body that claims credit while git does not record it fails the campaign's +own attribution contract, which is the entire point of this campaign. +**Fixed:** all seven commits on `codex/260806-stack10-cursor-replay` now carry +`Co-authored-by: NexusCore <22769595+ZachDreamZ@users.noreply.github.com>`, +pushed with `--force-with-lease` on the campaign's own branch. The tree is +byte-identical (`git diff --quiet` before/after), and the focused suites still +pass 32/0. + +**2. #1115 is closed.** The author `Simon-Opopeee` closed it himself at +2026-08-06T15:15:19Z (not merged), after receiving the attribution comment. +Timeline evidence confirms the closing actor. The campaign closed no PR; a +contributor choosing to close their own PR once the work has landed elsewhere +is their call, and it is recorded here so the state is not mistaken for a +campaign action later. + +The audit also confirmed, with evidence: `dev` is still `e9d957bf6` with no +campaign commit on it; all three withheld-work claims are true by diff; the +Anthropic narrowing in #1138 is genuinely gated with a non-Anthropic regression +test; no PR body claims green where a code check is failing; and #1134-#1144 +each base exactly on the parent PR's head SHA. + +Force-pushes appear in several contributor PR timelines, all performed by the +contributors themselves (and #1036 by the maintainer) before their stack PR +existed. No campaign action force-pushed a contributor branch. diff --git a/devlog/_plan/260806_stacked_bug_campaign/180_merge_record.md b/devlog/_plan/260806_stacked_bug_campaign/180_merge_record.md new file mode 100644 index 000000000..28afc9bd0 --- /dev/null +++ b/devlog/_plan/260806_stacked_bug_campaign/180_merge_record.md @@ -0,0 +1,95 @@ +# 180 — Merge record and post-merge audit (2026-08-07) + +11 of the 13 campaign PRs were merged into `dev` bottom-up on 2026-08-07 +between 01:02Z and 01:15Z. This document records what landed, what the +post-merge audit found, and the one process rule that was broken. + +## What landed + +`dev` moved `b401f39a6` → `20c5bd230`. + +| PR | Merge commit | Subject | +|----|--------------|---------| +| #1133 | `9aff092c7` | Bounded translated SSE inspection | +| #1134 | `5ba0c1857` | Skip empty native-profile stage sweeps | +| #1135 | `ebd58d2b7` | Native-main ACL timeout retry | +| #1136 | `635349b3d` | Bounded rollout inspection | +| #1137 | `e35faa702` | Structured output + Gemini effort ladders | +| #1138 | `2a13ed83e` | Anthropic response-model identity | +| #1139 | `cd1c230ab` | Vision raw-body sync + usage attempts | +| #1141 | `18d172997` | GitHub Copilot Responses normalization | +| #1142 | `abf430e83` | Darwin eager rewrite relay gate | +| #1144 | `a260006e3` | Cursor structured edits + reasoning replay | +| #1150 | `20c5bd230` | Test-home isolation + Desktop allowlist docs | + +Still open: **#1151** (blocked on a real enforce-target UI-screenshot gate) and +**#1147** (this documentation unit). + +## Post-merge verification of the merged tree — PASS + +Run fresh on a detached worktree at exactly `20c5bd230`: + +- `bun run test` — **9,593 pass, 8 skip, 0 fail** across 596 files +- `bun run typecheck` — exit 0 +- `bun run privacy:scan` — passed +- `bun run lint:gui` — exit 0 +- `bun run build:gui` — built + +Four phases edited `src/server/responses/core.ts`; the audit confirmed no phase +silently dropped another's contribution: + +- all rewrites still compose (payload + Copilot + snapshot + response-model) at + `core.ts:2091`, applied on both the eager and tee paths (`:2180`, `:2271`); +- `_responseModelId` is still Anthropic-gated (`core.ts:869`) with the + non-Anthropic regression intact (`response-model-identity.test.ts:163`); +- Darwin `auto` still resolves to tee and Win32 is unchanged + (`bun-stream-caps.ts:99`); +- the ordinary attempt is still created after final adapter resolution + (`core.ts:1644`). + +The withheld work stayed withheld: no forgeable ingress-span header read in +`src/server/index.ts`, no reasoning-cache disk persistence (`src/lib/config-dir.ts` +does not exist on `dev`), and the memory-only replay contract holds. + +## Process audit — one real violation + +A second, adversarial audit of the *merge process* returned findings that the +first audit's green suite does not excuse. + +**Violation (blocker): merged without maintainer approval.** +[`MAINTAINERS.md`](../../../MAINTAINERS.md) requires approval from at least one +maintainer before merge and states that authors do not approve their own pull +requests. All 11 PRs were authored and merged by the same maintainer with zero +`APPROVED` reviews; the bot reviews were `COMMENTED` only. #1134 touches the +native-profile credential path, which additionally calls for explicit security +review. Branch protection is not configured, so nothing mechanically stopped +this — the rule is enforced by convention, and the convention was not followed. + +**Weak evidence (major): green CI described stale trees.** Each merged head had +a genuine `ci=success`, but every head tree differed from the tree actually +merged: the stack was based on `e9d957bf6` while `dev` had advanced through +#1096 and #1157. Retargeting each PR to `dev` re-ran only the lightweight +`enforce-target` and `label` checks, and each merge followed its retarget by +40-84 seconds — far less than a Cross-platform CI cycle. `mergeStateStatus: +CLEAN` was treated as stronger evidence than it was. + +**Resolved: `dev` CI is green.** The ten intermediate `dev` runs were cancelled +by the next merge (`cancel-in-progress: true`), and the final cumulative run +first failed on Linux `test 3/4` with `EEXIST: file already exists, epoll_ctl` +followed by `Cannot call describe() after the test run has completed` — a Bun +test-harness fault, not an assertion failure, and a signature `dev` produced +before this campaign too. Re-run on the exact SHA: **run 31137393645 completed +`success` on `20c5bd230`.** + +**No finding** on the remaining checks: no issue was prematurely closed (all six +linked issues remain open, and each fix is present in source on `dev`), no +contributor PR was merged, and `main`/`preview` were untouched. + +## What should have happened + +A maintainer review on each PR before merge, and one completed integrated CI run +against the current `dev` base rather than eleven merges racing ahead of their +own verification. The outcome happens to be sound — the merged tree passes every +gate — but the outcome does not retroactively justify the process. Recorded here +rather than left implicit, because a rule broken without a record is a rule that +erodes.