Skip to content

Commit 65713e5

Browse files
authored
Merge pull request lidge-jun#1812 from lidge-jun/codex/compat-v2-docs
docs(devlog): record the codex-rs multi-agent v2 + history analysis
2 parents 2227caa + 37a9d32 commit 65713e5

15 files changed

Lines changed: 2616 additions & 0 deletions
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# 260816 — codex-rs multi-agent v2 + history performance: opencodex response
2+
3+
## Objective
4+
5+
Upstream codex-rs shipped two changes that opencodex must answer: multi-agent v2
6+
delegation now targets **every** catalog model that is not explicitly disabled
7+
(Luna included), and the local conversation-history stack was redesigned around
8+
paginated rollouts, ordinals, and a SQLite projection. This unit determines what
9+
opencodex must change, and stages it as dependency-ordered implementation phases.
10+
11+
> **Revision 2 (2026-08-16), after an adversarial A-phase audit.** An independent
12+
> reviewer returned FAIL with 11 blockers against revision 1; all were verified against
13+
> real code and folded in. The substantive corrections: the roster eligibility predicate
14+
> (`sync.ts:105`) was the actual defect and revision 1 left it untouched; the subagent
15+
> fallback chain can silently downgrade a v2 child; G5's stated impact was unreachable
16+
> and is now conditional; three phase dependencies were not real; and the research docs
17+
> carried prescriptive roadmaps that belong here. Details in each decade doc's amendment
18+
> note and in `006_audit_round1.md`.
19+
20+
## Evidence baseline
21+
22+
| Repo | Commit | Verified |
23+
| --- | --- | --- |
24+
| upstream codex-rs (조사 시점) | `9dd22890f5ff47e4af128c20e32b9758a61d78d2` | `git log -1`, 2026-08-12 |
25+
| upstream codex-rs (재검증 후, ff 완료) | `49db349ff` | 2026-08-15, +181 커밋. 두 P0 모두 유효함을 재확인 — `008`/`009` |
26+
| opencodex | `7612e4c4f81544a250c3eea9fe8ca85d8022e765` | `git log -1`, `fix(routing): source capability evidence from explicit catalog provenance (#1799)` |
27+
28+
Research documents (evidence only — no prescriptions; see LEXICO-SPLIT-01):
29+
30+
- `001_upstream_multiagent_v2_evidence.md` — v2 wire surface, catalog contract, tool schemas.
31+
- `002_upstream_history_perf_evidence.md` — rollout format, migration, pagination, proxy-visibility verdict.
32+
- `003_opencodex_subagent_catalog_inventory.md` — current opencodex catalog/subagent surface.
33+
- `004_opencodex_history_responses_inventory.md` — current opencodex rollout/Responses surface.
34+
- `005_public_web_evidence.md` — public claim ledger.
35+
- `006_audit_round1.md` — the A-phase reviewer's blockers and their disposition.
36+
37+
## The two findings that drive this unit
38+
39+
### 1. `multi_agent_version` no longer means "may I be a delegation target"
40+
41+
Before `6d4d9442c` (2026-08-04), a v2 parent could only spawn a model whose catalog
42+
value equalled `v2`. Now `model_supports_multi_agent_backend`
43+
(`codex-rs/core/src/tools/handlers/multi_agents_common.rs:36-42`) admits every model
44+
*except* explicit `Disabled`. The value instead decides whether the **child** gets
45+
collaboration tools: `collab_tools_enabled`
46+
(`codex-rs/core/src/tools/spec_plan.rs:599-610`) gives a child recursive tools only
47+
when its own catalog value is exactly `Some(V2)`.
48+
49+
| Value | Offered to a v2 parent | Child gets collab tools | Meaning |
50+
| --- | --- | --- | --- |
51+
| `"v2"` | yes | yes | recursive delegator |
52+
| `"v1"` | yes | no | **leaf worker** |
53+
| absent/null | yes | no | **leaf worker** |
54+
| `"disabled"` | no | no | ineligible |
55+
56+
opencodex encodes the OLD rule in two places, not one:
57+
58+
1. `isEligibleV2SubagentEntry` (`src/codex/catalog/sync.ts:105-108`) returns true only
59+
for `v2`/null/undefined — so an explicit `v1` pin like Luna's is **excluded from the
60+
roster entirely**. This is the load-bearing defect.
61+
2. `applyMultiAgentMode` (`src/codex/catalog/parsing.ts:382-388`) stamps every unpinned
62+
row `"v2"` when the feature flag is on, claiming every routed third-party model is a
63+
recursive delegator.
64+
65+
Both must change together: fixing only the stamp leaves Luna excluded, and fixing only
66+
the predicate leaves routed models over-claiming recursion.
67+
68+
Classification: **silent degradation** — nothing errors; the roster is simply wrong.
69+
70+
### 2. Paginated rollouts reject ordinal-less appended records
71+
72+
`6bb6e9045` + `4bb7ee347` introduce paginated rollouts: each JSONL line carries a
73+
monotonically increasing `ordinal`, `SessionMeta.history_mode` becomes `"paginated"`,
74+
and a SQLite projection materializes turns/items. `read_projection_steps`
75+
(`thread_history_materialization.rs:170-186`) returns a hard `Internal` error for a
76+
paginated line missing an ordinal.
77+
78+
opencodex's `updateSessionMeta` (`src/codex/history-provider.ts:523+`) **always**
79+
appends an ordinal-less `session_meta` line, and every thread SELECT
80+
(`history-provider.ts:589,714,722`) omits `history_mode`. Upstream's equivalent
81+
(`codex-rs/thread-store/src/local/update_thread_metadata.rs:74`) branches on `paginated` and updates only the threads table.
82+
83+
Classification: **compat-break** — corrupts a paginated thread's projection.
84+
85+
### What is explicitly NOT our problem
86+
87+
The "~98% fewer requests" figure is an N+1 elimination in *local SQLite* summary paging
88+
(`332eac4b8`): ~749 queries to ~8 for a 741-turn thread at the 100-turn page cap. It is
89+
not a reduction in `/v1/responses` calls, and `ResponsesApiRequest`
90+
(`codex-rs/codex-api/src/common.rs:252`) is unchanged by the entire history series.
91+
92+
The `27.6s → 1.7s` and 741-turn figures come from an **official OpenAI announcement**
93+
(user-confirmed). They do not appear in any public PR body — `gh api search/issues` returns
94+
`total_count: 0` for both `27.6s` and `"98% fewer"` — so treat them as an internal benchmark
95+
whose *code* is public, not as a fabricated claim. `009_gh_pr_review.md` maps the figures to
96+
the PRs that produced them (#36384 N+1 removal, #32234/#33364 pagination, #36948-36951 TUI
97+
bounded hydration, #38604 resume round-trip removal, #34361 clone avoidance). What remains
98+
opencodex-relevant is unchanged: those requests are local SQLite/app-server calls, not
99+
`/v1/responses` calls.
100+
101+
opencodex is a provider proxy, not Codex's app-server. It must **not** implement
102+
`thread/turns/list`, `thread/items/list`, or `includeTurns`.
103+
104+
## Gap matrix
105+
106+
| # | Gap | Class | Phase |
107+
| --- | --- | --- | --- |
108+
| G1 | `default` mode stamps unpinned rows `v2`, erasing leaf semantics | silent-degradation | 010 |
109+
| G2 | No typed per-model multi-agent capability, and no creation path for one | missed-opportunity | 010 |
110+
| G3 | `updateSessionMeta` appends ordinal-less lines to paginated rollouts | compat-break | 020 |
111+
| G4 | Thread SELECTs omit `history_mode`; bulk updates cannot skip unknown rows | compat-break | 020 |
112+
| G5 | `extractUserMessagePreview` misses canonical `ItemCompleted` records | **conditional** — no reachable consumer found; see 030 gate | 030 |
113+
| G6 | Rollout filename assumed to carry thread id; `4ef836f88` separates them | compat-break (latent) | 030 |
114+
| G7 | `hasAgentsMaxThreads` claims a boot refusal upstream reverted in `1f304dd1f` | stale-guard | 040 |
115+
| G8 | `/v1/responses/compact` full-field forwarding unverified | missed-opportunity | 050 |
116+
| G9 | `structure/03_catalog-and-subagents.md:133-153` documents the superseded rule | docs-drift | 010 |
117+
| **G12** | Quota fallback can rewrite a v2 child to a v1/disabled model, keeping collab tools | **compat-break** | 010 |
118+
| G13 | `collaboration.ts:349` fork-override guidance | **no-action (optional polish)** — upstream's own hint (`config/mod.rs:253`) says forks "do not accept overrides", so our text mirrors it; the implementation honors overrides but the guidance is not wrong | 010 |
119+
| **G14** | `model_messages.multi_agent` (role/mode 지시문)이 카탈로그에서 공급됨 (#38619); opencodex는 `model_messages` 를 `metadata.ts:300-308` 에서 변형하고 `upstream-models.json` 스냅샷으로 공급하는데 이 서브트리를 모름 | missed-opportunity → 잠재 silent-degradation | 060 (신규, 미작성) |
120+
| G10 | Storage cleanup refuses paginated history | no-action (keep refusal) | — |
121+
| G11 | App-server pagination protocol | no-action (out of proxy scope) | — |
122+
123+
G12 and G13 were found by the A-phase reviewer, not by the research swarm.
124+
125+
## Phase map
126+
127+
Ordering follows build order where a real dependency exists. Revision 1 asserted three
128+
dependencies that the reviewer disproved; the corrected map is mostly **parallel**, which
129+
is itself a useful finding — these are independent defects, not a chain.
130+
131+
| Phase | Doc | Outcome | Depends on |
132+
| --- | --- | --- | --- |
133+
| 1 | `010_phase1_catalog_capability_contract.md` | Leaf semantics restored end-to-end: eligibility, stamp, creation path, fallback capability class | — |
134+
| 2 | `020_phase2_history_mode_awareness.md` | Never write ordinal-less records to a paginated rollout | — |
135+
| 3 | `030_phase3_rollout_identity_and_previews.md` | Rollout-id/thread-id separation locked; G5 gated | — |
136+
| 4 | `040_phase4_config_alias_and_docs.md` | `max_threads` alias truth through its full consumer chain | — |
137+
| 5 | `050_phase5_compact_wire_verification.md` | `/v1/responses/compact` field-fidelity regression | — |
138+
139+
Recommended execution order (risk-first, not dependency-forced): 1 → 2 → 4 → 3 → 5.
140+
Phase 1 carries two compat-breaks (G12) and the headline defect; Phase 2 carries the
141+
data-corruption risk. Phases 3 and 5 may both close as NOOP with recorded evidence.
142+
143+
Each phase is one full PABCD cycle and closes with an independently verifiable gate.
144+
145+
## Environment precondition (applies to every phase)
146+
147+
This worktree has **no `node_modules/`**. Every verifier command therefore exits 1 for an
148+
environmental reason (`Cannot find module 'zod/v4'`; `TS2688: bun-types`). Each phase's B
149+
must run `bun install` first and re-record real exit codes beside its verifier table.
150+
Note `package.json:41` defines `"test": "bun scripts/test.ts"` — use `bun run test` for a
151+
full run, `bun test <file>` for focused iteration.
152+
153+
## Scope boundary
154+
155+
IN, by phase:
156+
157+
- **catalog/capability (010):** `src/codex/catalog/parsing.ts`, `src/codex/catalog/sync.ts`,
158+
`src/codex/catalog/provider-fetch.ts`, `src/codex/catalog/effort.ts` (the
159+
`applyCatalogModelMetadata` bridge), `src/codex/subagent-model-fallback.ts`,
160+
`src/server/responses/core.ts` (both fallback call sites),
161+
`src/server/responses/collaboration.ts`, `src/server/management/model-rows.ts`,
162+
`gui/src/pages/models-shared.ts`.
163+
- **history (020/030):** `src/codex/history-provider.ts`, `src/codex/sqlite-columns.ts` (new),
164+
`src/codex/history-worker.ts`, `src/codex/history-job.ts`, `src/codex/history-transition.ts`,
165+
`src/codex/history-migration-guardian.ts`, `src/codex/inject.ts`,
166+
`src/codex/convergence-types.ts` and `src/codex/transition-state.ts` (the durable
167+
reason vocabulary + its schema migration, Phase 2 Change 6),
168+
`src/storage/cleanup.ts` (read paths + the `columnExists` re-export).
169+
- **config/docs (040):** `src/codex/features.ts`, `src/cli/v2.ts`,
170+
`src/server/management/agent-settings-routes.ts` (GET and PUT), `gui/src/pages/Models.tsx`
171+
and its locale strings.
172+
- **compact (050):** `src/server/responses/compact.ts`, `src/adapters/openai-responses.ts`.
173+
- **docs:** `structure/03_catalog-and-subagents.md`, `structure/05_gui-and-management-api.md`.
174+
- matching `tests/` for each.
175+
176+
OUT: the codex-rs checkout (read-only), app-server protocol reimplementation, **other**
177+
provider adapters (`src/adapters/openai-responses.ts` is explicitly IN for Phase 5's
178+
passthrough regression; no other adapter is), GUI redesign beyond the copy/type changes
179+
listed above, release actions, any `git push`.
180+
181+
## Terminal outcome for this docs cycle
182+
183+
`DONE` when 000-007 plus every decade doc exist at diff-level precision, every audit-round
184+
blocker is folded or explicitly rebutted, and the unit is committed locally. `006` and `007`
185+
are audit history: the decade docs are canonical, and where an amendment corrected an
186+
earlier instruction the canonical text was rewritten rather than appended to.

0 commit comments

Comments
 (0)