Skip to content

Commit ef71180

Browse files
committed
docs(plan): allocate 12 must-fix bugs across 5 bugfix worktrees
Campaign preparation (docs-only): five units under devlog/_plan/260802_wtN_* with 000 research + 010 implementation roadmaps, claim ledgers verified by a lunasearch fan-out (Anthropic 1M windows, Copilot mixed-wire, DeepSeek service_tier, WHATWG extension origins, POSIX rename-over-symlink). wt1 update-path: PR lidge-jun#871, issue lidge-jun#879 (star-prompt deferral leakage), lidge-jun#557 optional wt2 zero-leak: PRs lidge-jun#840 lidge-jun#841 lidge-jun#843 lidge-jun#844 lidge-jun#845 lidge-jun#847 (tracker lidge-jun#820) wt3 provider-wire: PRs lidge-jun#746 lidge-jun#860 lidge-jun#839/lidge-jun#854, issue lidge-jun#875 triage, lidge-jun#616/lidge-jun#837 optional wt4 server-config: PRs lidge-jun#850 (CORS origin confusion), lidge-jun#869 (symlink destruction) wt5 windows-service: PRs lidge-jun#868, lidge-jun#861 (issue lidge-jun#848)
1 parent 3195c71 commit ef71180

10 files changed

Lines changed: 376 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# wt1 — Update path + star-prompt leakage (research)
2+
3+
Worktree: `/Users/jun/.codex/worktrees/260802-wt1-update-path` (branch `codex/wt1-update-path`, off `dev`).
4+
This unit is docs-first preparation; the executing session re-verifies every claim at its own P before building.
5+
6+
## Scope
7+
8+
Three bugs in the install/update/prompt surface, all must-fix regardless of PR quality:
9+
10+
### Bug A — PR #871: preview installs cannot update to stable
11+
12+
- Evidence: PR #871 (`fix(update): allow stable updates from older previews`).
13+
- Root cause: an installed preview such as `2.8.2-preview.20260731` cannot be parsed by the stable-only current-version branch of the update comparator, so with npm `latest` at `2.9.1`, `isNewer()` returns `false` and the dashboard reports `already_latest`, disabling one-click update.
14+
- Grounding: `src/update/notify.ts` (`isNewer`, `readVersionCache`), consumed by `src/update/badge.ts:69` (`updateAvailable: isNewer(cache.latest_version, current, channel)`).
15+
- Severity: high — the update path silently bricks itself for every preview-channel user once a stable supersedes their build.
16+
- Fix shape (from PR, re-verify): compare an installed preview by its `major.minor.patch` core when the selected channel is `latest`; keep the latest-channel target strict (a preview registry target is never accepted as a stable update).
17+
18+
### Bug B — Issue #879: star-prompt agent deferral leaks into every editing session
19+
20+
- Evidence: issue #879, filed 2026-08-02 after user report ("shows during editing, not just install").
21+
- Root cause (three compounding decisions, verified in code):
22+
1. `src/cli/star-prompt.ts` leaves the `.star-prompted` marker unwritten on the agent path, so every agent-driven `ocx start` re-prints `printAgentDeferral()` — no deferral marker, counter, or cooldown.
23+
2. The deferral text recruits the agent as an unbounded relay ("put the same Yes/No question at the top of your next reply, unchanged"); AGENTS.md repeats the repeat-forever rule, so one `ocx start` hijacks every later reply.
24+
3. Agent PTYs pass the TTY gate, so the deferral fires on routine edit/test cycles.
25+
- Grounding: `src/cli/star-prompt.ts` (`MARKER`, `printAgentDeferral`, `maybeShowStarPrompt`), `src/cli/agent-driven.ts` (env detection), callers `src/cli/index.ts:317` and `src/service.ts:2468`, consumer of marker semantics `src/update/notify.ts:135`, tests `tests/startup-prompt.test.ts`, `tests/agent-driven.test.ts`, `tests/sidebar-routes.test.ts`.
26+
- Severity: medium — degrades every agent-assisted session and trains users to dismiss a consent question.
27+
- Constraint (must keep): an agent never answers or auto-dismisses; only the account owner stars; `403 agent_consent_required` on `POST /api/github/star` stays untouched.
28+
29+
### Bug C (should-fix, same subsystem) — PR #557: npm cache recovery hardening
30+
31+
- Maintainer-takeover draft for #533: fail closed when the npm cache is same-UID but not effectively readable/writable before any proxy stop path; sanitize persisted update-job command/log/error fields (raw home/cache paths, uid/gid).
32+
- Include only if wt1 capacity allows after A and B land; otherwise leave for maintainer review.
33+
34+
## Claim ledger
35+
36+
| # | Claim | Source | Status |
37+
|---|-------|--------|--------|
38+
| 1 | `isNewer()` stable-only parse rejects `2.8.2-preview.*` current versions | PR #871 body + `src/update/notify.ts` | code-verified |
39+
| 2 | Marker unwritten on agent path; deferral re-arms every agent start | `src/cli/star-prompt.ts` | code-verified |
40+
| 3 | npm semver/dist-tag behavior for preview vs stable channels | Luna lane (not dispatched — covered by repo comparator code) | n/a |
41+
42+
## Out of scope
43+
44+
- Changing the consent invariant (agent never stars; user-only prompt).
45+
- Pushing anything; each worktree session commits locally and asks before push.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# wt1 — Implementation roadmap (re-verify at P before building)
2+
3+
Branch `codex/wt1-update-path` off `dev`. One PABCD cycle per bug (A then B; C optional).
4+
5+
## Bug A — PR #871: preview → stable update (build first)
6+
7+
File map:
8+
9+
- MODIFY `src/update/notify.ts``isNewer()`: when channel is `latest` and the installed version is a preview (`x.y.z-preview.*`), compare by `major.minor.patch` core; a preview registry target is never accepted as a stable update target (strict target side stays).
10+
- MODIFY `src/update/badge.ts` — only if the GUI/API update-check result needs the same comparator path (it consumes `isNewer` at :69, so likely no change; verify).
11+
- MODIFY `tests/` update comparator tests near existing `notify`/`badge` coverage.
12+
13+
Acceptance + activation scenarios:
14+
15+
1. Installed `2.8.2-preview.20260731`, channel `latest`, npm latest `2.9.1``isNewer` true, GUI shows update available (was `already_latest`). Activation: unit test driving exactly this pair.
16+
2. Installed `2.9.1`, registry target `2.9.2-preview.*` on `latest` channel → still false (target-strict). Activation: regression test.
17+
3. Preview channel behavior unchanged. Activation: existing suite green.
18+
19+
## Bug B — Issue #879: star-prompt deferral bound (build second)
20+
21+
File map:
22+
23+
- MODIFY `src/cli/star-prompt.ts` — add a deferral record (e.g. `.star-deferred` with ISO timestamp) written when `printAgentDeferral()` fires; skip the deferral when the record is younger than the chosen cooldown (recommend: once per version, or 7 days — pick one and document). Bound the relay text itself: one relay per deferral event, not repeat-forever.
24+
- MODIFY `AGENTS.md` — the user-consent paragraph must match the new bound (remove "repeat unchanged forever", keep "never answer for the user").
25+
- MODIFY `tests/startup-prompt.test.ts`, `tests/agent-driven.test.ts` — regressions below.
26+
- DO NOT TOUCH: `src/server/management/sidebar-routes.ts` `403 agent_consent_required`; the human interactive prompt path; marker semantics used by `src/update/notify.ts:135`.
27+
28+
Acceptance + activation scenarios:
29+
30+
1. Second agent-driven `ocx start` within the cooldown prints nothing. Activation: test with a fresh config dir, env marker set, deferral record present.
31+
2. Hand-typed interactive run (no agent env, TTY mocked) still shows the real prompt regardless of deferral record. Activation: existing prompt test stays green + new case.
32+
3. Deferral text no longer instructs repeat-forever relay. Activation: snapshot/string assertion on `printAgentDeferral` output.
33+
4. `hasStarPromptRun()` semantics for `update/notify.ts` unchanged. Activation: existing notify tests green.
34+
35+
## Bug C — PR #557 (optional, capacity permitting)
36+
37+
Fold the two high-severity blockers (fail-closed npm cache probe before proxy stop; sanitize persisted job command/log/error fields). Draft PR exists — rebase and finish rather than rewrite.
38+
39+
## Verification gate for the worktree session
40+
41+
`bun run typecheck` + focused tests + `bun run test` before proposing merge. Commit per bug; no push without user approval.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# wt2 — Zero-leak state-store bounds (research)
2+
3+
Worktree: `/Users/jun/.codex/worktrees/260802-wt2-zero-leak` (branch `codex/wt2-zero-leak`, off `dev`).
4+
Tracker: issue #820. These six PRs are the confirmed retained-state/memory-leak family; all are must-fix regardless of PR quality.
5+
6+
## Scope
7+
8+
| PR | Defect | Bound introduced (per PR body, re-verify) |
9+
|----|--------|-------------------------------------------|
10+
| #847 | Unbounded streamed tool-argument memory (SSE records can arrive without delimiters) | 4 MiB shared SSE record cap; 8 MiB per-call / 32 MiB per-turn tool-arg cap; oversized calls fail as typed upstream errors |
11+
| #845 | Unbounded Cursor blob-store memory | 16 MiB/blob, 64 MiB total, 4096 entries; pin active-request blobs; LRU evict only unpinned; protobuf error for rejected `setBlobArgs` |
12+
| #844 | Unbounded Cursor Connect frame buffering | 32 MiB inbound payload cap; reject oversized declared length at the 5-byte header; complete one pending frame incrementally; fail turn on EOF with incomplete frame |
13+
| #843 | Unbounded Antigravity replay retention | SHA-256 fixed-size cache identities; 64 KiB/signature, 256 calls + 2 MiB/session, 32 MiB global; LRU + 1h TTL preserved |
14+
| #841 | Responses continuation state not byte-accounted | UTF-8 byte measurement before admission; reject single entries > 64 MiB store budget; same rule on snapshot load |
15+
| #840 | Windows ACL temp-path memos never released | Release memos once ephemeral file proven absent; key atomic-write timeouts by stable destination |
16+
17+
- Severity: high — all six let a long-running proxy grow RSS without bound; the Windows ACL leak (#840) was confirmed by live measurement in the prior zero-leak unit (`devlog/_plan/260801_zero_leak_state_stores/`).
18+
- Grounding entry points: `src/responses/state.ts` (#841), Cursor Connect/blob code paths (#844/#845), `src/config.ts:184-187` (`atomicWriteFileAsync` timeout memo keyed by destination, #840), SSE/tool-argument streaming in `src/adapters/openai-responses.ts` + `src/server/responses/core.ts` (#847).
19+
20+
## Key review questions for the executing session
21+
22+
- Do the caps fail as typed upstream errors without emitting a completed tool item or a clean Chat DONE marker (#847)?
23+
- Do pinned blobs leave a safe-capacity failure mode rather than deadlock (#845)?
24+
- Do the six PRs overlap in shared helpers? Order of landing matters; rebase chain per PR body cross-references (#840-#845-#847 all cite #820).
25+
- Benchmark cells: prior unit recorded 16/16 scoped PASS but competitor cells remain `UNKNOWN` — do not write superiority/RSS claims into docs or release notes.
26+
27+
## Claim ledger
28+
29+
| # | Claim | Source | Status |
30+
|---|-------|--------|--------|
31+
| 1 | Six stores are unbounded on current dev | #820 tracker + PR bodies #840-#847 | code-verified (prior unit) |
32+
| 2 | Windows ACL memo leak reproduced by measurement | `devlog/_plan/260801_zero_leak_state_stores/` | verified (prior unit) |
33+
| 3 | Proposed caps match provider payload realities (32 MiB frames etc.) | PR bodies; no external source needed | unverified — executing session must spot-check |
34+
35+
## Out of scope
36+
37+
- RSS/benchmark superiority claims.
38+
- Changing TTL semantics beyond what each PR states.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# wt2 — Implementation roadmap (re-verify at P before building)
2+
3+
Branch `codex/wt2-zero-leak` off `dev`. One PABCD cycle per PR-family cluster; all six cite tracker #820.
4+
5+
## Landing order (dependency-ordered, not effort-bucketed)
6+
7+
1. **#841 Responses state byte cap** — foundation: `src/responses/state.ts` admission accounting is shared by snapshot load; other bounds do not depend on it, but it is the smallest contract to verify the byte-accounting pattern the others reuse.
8+
2. **#847 Streamed tool-argument bounds**`src/adapters/openai-responses.ts` + `src/server/responses/core.ts` (+ Chat outbound conversion). 4 MiB SSE record cap; 8 MiB/call, 32 MiB/turn.
9+
3. **#844 Connect frame buffering** → 4. **#845 blob-store** — Cursor lane; #845's pinning assumes #844's incremental frame completion.
10+
5. **#843 Antigravity replay retention** — independent; SHA-256 identities + LRU.
11+
6. **#840 Windows ACL memo release**`src/config.ts:184-187` (`atomicWriteFileAsync` memo keyed by stable destination). LAND LAST: wt4's realpath fix (#869) also touches this function; coordinate so wt4 lands first or rebase over it.
12+
13+
## Per-PR acceptance pattern (apply to each)
14+
15+
1. Oversized input fails as a typed error without emitting a completed tool item / clean DONE marker / acknowledged store. Activation: fault-injection test feeding an over-cap payload, asserting the typed error and the absent success side-effect.
16+
2. At-cap valid input still succeeds. Activation: boundary test at exactly cap and cap−1.
17+
3. RSS-style retention proof: after N cycles of oversized + valid traffic, store size stays under the stated bound. Activation: focused harness test (reuse the prior zero-leak unit's measurement approach; `devlog/_plan/260801_zero_leak_state_stores/`).
18+
4. Eviction never removes entries pinned by an active request (#845) / prior valid mappings survive an oversized replacement (#843) / unrelated valid chains survive rejection (#841).
19+
20+
## Verification gate
21+
22+
`bun run typecheck` + `bun run test` full suite (shared stores = full suite per AGENTS.md). No RSS superiority claims in docs/release notes — comparator cells remain `UNKNOWN`.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# wt3 — Provider wire correctness (research)
2+
3+
Worktree: `/Users/jun/.codex/worktrees/260802-wt3-provider-wire` (branch `codex/wt3-provider-wire`, off `dev`).
4+
Provider-adapter/wire bugs; all must-fix regardless of PR quality.
5+
6+
## Scope
7+
8+
### Bug A — PR #746 / issue #748: Copilot Responses-only models routed to chat completions
9+
10+
- Root cause: the `github-copilot` preset configures a provider-wide `openai-chat` adapter, but Copilot fronts a mixed-wire catalog; several newer OpenAI models are served only by the Responses API (`model "gpt-5.6-sol" is not accessible via the /chat/completions endpoint`). `gpt-5.4` hides it behind a passing text-only smoke test; a real Codex request (function tools + reasoning effort) fails.
11+
- Grounding: `src/providers/registry.ts`, `src/providers/github-copilot-transport.ts`.
12+
- Severity: high — hard data-plane failure for Copilot users on current models.
13+
14+
### Bug B — PR #860 (+ issue #875): DeepSeek `service_tier` must be capability-gated
15+
16+
- Root cause: `fastMode` injects `service_tier` unconditionally on Responses routes; DeepSeek does not support the field. PR #860 adds a provider-level `supportsServiceTier` capability: canonical OpenAI Responses providers support it, DeepSeek explicitly rejects it (strip the field), unclassified custom providers keep caller-supplied values.
17+
- Fresh corroboration: issue #875 (2026-08-02) "DeepSeek V4 Flash Responses route stalls after tool calls" — same wire family; executing session must check whether #875 is the same root cause or a second defect before closing either.
18+
- Grounding: `src/adapters/openai-responses.ts`, `src/server/responses/core.ts`, `src/types.ts`.
19+
20+
### Bug C — PRs #839 / #854: Claude 4.6/4.7 1M context windows missing
21+
22+
- Root cause: `ANTHROPIC_MODEL_CONTEXT_WINDOWS` omits `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, so they advertise `max_input_tokens: null`, `shouldMarkOneMillion()` rejects them, and the `[1m]` picker row is never emitted — Claude Code accounts them at its 200k default. #854 additionally fixes generated profiles writing `[1m]` on sub-1M routes (372K route marked `[1m]`).
23+
- Grounding: `src/claude/context-windows.ts`, `src/claude/model-info.ts`, `src/providers/registry.ts`.
24+
- Note: #839 and #854 overlap; land ONE consolidated fix, credit both PRs.
25+
26+
### Optional — PRs #616 / #837: hosted image tool preferences
27+
28+
- Some gateways reserve the image tool namespace server-side; generic normalization collides with client `image_gen` declarations. #837 already integrates #616 onto current dev preserving authorship. Include if capacity allows.
29+
30+
## Claim ledger
31+
32+
| # | Claim | Source | Status |
33+
|---|-------|--------|--------|
34+
| 1 | Copilot serves some models Responses-only | gpt-5.4 verified (BerriAI/litellm#23332, exact `unsupported_api_for_model` error); gpt-5.6-sol lead only (JetBrains LLM-29711: function tools + reasoning_effort rejected on `/chat/completions`); same pattern for gpt-5-codex (opencode #2758) | verified (5.4) / lead (sol) |
35+
| 2 | DeepSeek rejects/mishandles `service_tier` | No primary evidence either way (api-docs.deepseek.com does not list the field; Anthropic-compatible API marks it "Ignored" — different endpoint) | unresolved — capability-gating is safe regardless; do not claim rejection without a live probe |
36+
| 3 | DeepSeek Responses route stalls after tool calls (hosted api.deepseek.com) | Stall reports are NIM/vLLM compatibility paths, not hosted; verified hosted failure mode is a 400 when `reasoning_content` is omitted after a tool call (official Thinking Mode docs; claude-code-router#1378) | contradicted as stated — #875 may be a `reasoning_content` echo defect, not #860's root cause; executing session must split them |
37+
| 4 | Claude Opus 4.6/4.7 + Sonnet 4.6 are documented at 1M context | Anthropic official: Opus 4.6 (1M beta, 2026-02-05), Opus 4.7 (1M, 2026-04-16, migration guide), Sonnet 4.6 (1M beta, 2026-02-17); model overview cross-check | verified |
38+
39+
## Out of scope
40+
41+
- New provider presets (covered by separate enhancement PRs).
42+
- Changing the conservative relay capability policy for unknown providers beyond what #860 states.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# wt3 — Implementation roadmap (re-verify at P before building)
2+
3+
Branch `codex/wt3-provider-wire` off `dev`. One PABCD cycle per bug; land in dependency order A → B → C (D optional).
4+
5+
## Bug A — #746/#748: Copilot mixed-wire routing
6+
7+
File map:
8+
9+
- MODIFY `src/providers/registry.ts` — per-model wire override for the `github-copilot` preset: Responses-only models route to the Responses adapter instead of the preset-wide `openai-chat`.
10+
- MODIFY `src/providers/github-copilot-transport.ts` — only if the transport needs Responses-shaped auth/headers distinct from chat (verify at P).
11+
- MODIFY provider routing tests + a mixed-wire fixture: model list containing both chat-served and Responses-only models.
12+
13+
Acceptance + activation:
14+
15+
1. `gpt-5.4` via Copilot preset issues a Responses request (never `/chat/completions`). Activation: mock-transport test asserting the wire per model — external corroboration: litellm#23332.
16+
2. Chat-served Copilot models still use chat completions (no regression). Activation: existing suite.
17+
3. `gpt-5.6-sol` tools+reasoning request takes the path that does not 400. Activation: request-shape test; note external evidence is lead-only (JetBrains LLM-29711), so gate on request shape, not a claimed upstream error string.
18+
19+
## Bug B — #860 (+#875): DeepSeek service_tier capability gate
20+
21+
File map:
22+
23+
- MODIFY `src/types.ts` + registry-enriched metadata — provider-level `supportsServiceTier` capability (`src/providers/registry.ts`), canonical OpenAI Responses providers = true, DeepSeek = explicitly false.
24+
- MODIFY `src/adapters/openai-responses.ts` / `src/server/responses/core.ts``fastMode` injects/removes `service_tier` only for supporting providers; strip for rejecting providers; preserve caller values for unclassified custom providers.
25+
- DOCS: configuration reference (EN + zh-CN) per PR.
26+
27+
Acceptance + activation:
28+
29+
1. DeepSeek Responses request never carries `service_tier`, including with `fastMode` on. Activation: serialized-payload test.
30+
2. Canonical OpenAI keeps injecting; custom unclassified preserves caller value. Activation: payload tests.
31+
3. Issue #875 triage: determine whether the stall is (a) this field, (b) missing `reasoning_content` echo after tool calls (verified DeepSeek failure mode — 400, per official Thinking Mode docs), or (c) NIM/vLLM-only. Hosted stall reports were NOT externally verified; do not close #875 on #860's evidence alone. If (b), that is a separate fix in the same lane (response-state must round-trip `reasoning_content`).
32+
33+
## Bug C — #839/#854 consolidated: Claude 1M windows
34+
35+
File map:
36+
37+
- MODIFY `src/claude/context-windows.ts` — add `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6` at 1M (externally verified: Anthropic announcements 2026-02-05 / 2026-04-16 / 2026-02-17 + model overview).
38+
- MODIFY `src/claude/model-info.ts` — generated profiles: `[1m]` marker only when the authoritative effective window ≥ 1M (fixes the 372K-route-marked-`[1m]` defect from #854); honor provider caps + case-insensitive marker spelling.
39+
- Tests: picker row emission (`[1m]` present for the three models, absent for sub-1M routes).
40+
41+
Land as ONE PR crediting #839 and #854.
42+
43+
## Bug D (optional) — #616/#837 hosted image tools
44+
45+
Rebase #837 (it already integrates #616 with authorship preserved); validate the per-model Responses wire including OpenAI API virtual-model rewrites.
46+
47+
## Verification gate
48+
49+
`bun run typecheck` + `bun run test`; wire changes need the full suite (shared adapters).

0 commit comments

Comments
 (0)