Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions devlog/_plan/260905_bug_triage_stack/005_stack_order.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ possible diff relative to its base.
| 1 | 010 | `codex/3467-google-location-error` | `dev` | #3467 | agentHits |
| 2 | 020 | `codex/3462-mihomo-ipv6-fakeip` | pos 1 head | #3462 | — |
| 3 | 030 | `codex/3464-launchd-stable-launcher` | pos 2 head | Refs #3464 (does not close; running-process mismatch remains) | — |
| 4 | 040 | `codex/3522-spill-write-health` | pos 3 head | (references #3522, does not close) | Ingwannu |
| 5 | 050 | `codex/3406-codex-toggle-truth` | pos 4 head | #3406 | turin |
| 4 | 040 | — (superseded: #3542 merged 7eddfb3eb) | — | refs #3522 | Ingwannu (in #3542) |
| 5 | 050 | `codex/3406-codex-toggle-truth` | pos 3 head (#3554) | #3406 | turin |

Per-PR contract (each PR):

Expand Down
26 changes: 0 additions & 26 deletions devlog/_plan/260905_bug_triage_stack/020_wp3_mihomo_ipv6_fakeip.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,6 @@ against the current tree at this cycle's P before implementing.

---

## wp3 plan amendments (audit rounds 1–2; binding over the lane text below)

Implementation shape (see the PR diff for the authoritative change):

1. `src/lib/proxy-env.ts`: `effectiveProxyFor(url, env)` selects the proxy variable that
matches the URL scheme (`HTTPS_PROXY` for `https:`, `HTTP_PROXY` for `http:`); returns null
when none is set. `ALL_PROXY` is not consulted.
2. `src/lib/provider-outbound.ts`: `allowMihomoIpv6FakeIp` is derived from that selection plus
`!noProxyMatches(parsed)`, and when the flag admitted the answer the request is issued with
an explicit `proxy:` option bound to the same selection (Bun `BunFetchRequestInit.proxy`,
bun-types 1.4.0 `globals.d.ts:1944`). Snapshot both before the DNS await; no direct retry
after a proxy failure. All other branches are unchanged.
3. `src/lib/destination-policy.ts`: separate `allowMihomoIpv6FakeIp` option (not a relabel of
the `benchmark address` detail), so config-time validation in `provider-routes.ts` is untouched.
4. Tests: `tests/providers/provider-outbound.test.ts` (scheme-matched proxy selection matrix,
captured `proxy` option, NO_PROXY) and `tests/routing/destination-policy-resolved.test.ts`
(accept under flag / reject without / literal / adjacent prefix / benchmark-only flag /
config-validation path).
5. Docs: English `providers.md` §"Provider diagnostic outbound safety" (:333) and the seven
locale pages (ko:129 ja:129 zh-cn:129 zh-tw:103 fr:143 ru:145 tr:151).

Review notes and the reviewer's transport measurements live in scratch (`.tmp/triage/`), per
AGENTS.md §Security working notes; they are summarized publicly once the PR is merged.

---

1) VERDICT: FIXABLE

Neither dev nor [PR #3489](https://github.com/lidge-jun/opencodex/pull/3489) fixes #3462. Implement a separate, proxy-only IPv6 fake-IP exception; do not carry #3489 as this issue’s fix.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,6 @@ against the current tree at this cycle's P before implementing.

---

## wp4 P stale-check + amendments (binding over the lane text below)

Stale-check at 37622b92d (stack pos 2 head): `cliEntry(runtime)` :66, `writeServiceInstallState(backend, launcherPath)` :238, `buildPlist` :489-527, `installLaunchd` :2244-2272, `buildServiceLauncherShellCommand` :567, `buildUnit` launcher/env shape :3268-3301, `bakedServicePathsDiagnostic` launcher-aware :3236 — all as the lane text assumed.

Amendment (missed by the lane): `launchdStart` (:2281-2300) decides "already loaded from the current plist" by matching the live `launchctl print` output against `buildServiceShellCommand(entry.bun, entry.cli)`. After this change a launcher-baked plist would never match that string, so every healthy `ocx service start` on macOS would fall through to the failure branch. Fix: derive the expected command from the recorded install state — `readServiceInstallState()?.launcherPath` present ⇒ `buildServiceLauncherShellCommand(launcherPath)`, else the Bun+CLI pair — and pin it with a test in the existing `launchdStart` describe.

`ServiceInstallState.launcherPath` doc comment says "Linux only"; update to "launchd and systemd".

Docs: lifecycle.md :245/:256 as the lane text says; structure/04 :36 generalised. Optional `version-skew.ts` wording change is **deferred** (separate concern, keeps this PR to launcher parity).

---

1) VERDICT: FIXABLE

Launchd parity is **not fixed**. The stable launcher fixes which version starts next; it does **not** replace an already-running proxy after `mise upgrade`.
Expand Down Expand Up @@ -175,10 +163,3 @@ Risk: explicit security review is warranted under [MAINTAINERS.md:60](/Users/jun
- Stable-launcher parity does **not** satisfy automatic repair/request refusal during an already-running version mismatch. That remains a separate product decision.
- Actual mise shim selection under launchd’s login-shell environment needs a macOS smoke check; no live service or real upgrade was exercised.
- Read-only investigation only: no files, Git state, GitHub comments, service state or tests were changed/run.

Audit round 1 fold (GO-WITH-FIXES, 1 blocker): `serviceStatusReport()` (:4243) compares the live
job against `buildServiceShellCommand(entry.bun, entry.cli, installedServiceListenPort())` too —
same fix as `launchdStart`: one helper `expectedLaunchdCommand(port)` that reads
`readServiceInstallState()?.launcherPath` and returns the launcher command when present, else the
Bun+CLI pair (never rediscovering PATH at start/status). `launchdStart` also gets the installed port.

12 changes: 12 additions & 0 deletions devlog/_plan/260905_bug_triage_stack/040_wp5_spill_write_health.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,15 @@ Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com>
- PR tests were inspected, not executed. No files, Git state, or GitHub state were changed.



## wp5 outcome — NOOP (superseded by merged #3542)

At this cycle's P (2026-09-04T23:45Z) PR #3542 — the maintainer carry of #3525 opened by a
parallel session — was already **MERGED** into `dev` (merge 7eddfb3eb 2026-09-04T23:07:34Z) with exact-head CI 28 pass /
0 fail. It carries every hunk this doc planned (spill write health projection, privacy-safe
error class, `/api/system/memory` fields, tests, docs) with the `Co-authored-by: Ingwannu`
trailer. Opening a second PR would duplicate landed work, so wp5 closes as **NOOP**.

#3522 stays open per the original author's plan (observability fix; the current-version
persistence defect is still unreproduced).

4 changes: 2 additions & 2 deletions devlog/_plan/260905_bug_triage_stack/060_ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ Append-only. One row per external action or verification.
| 2026-09-04T22:28Z | manifest | live scan | 12 open bug issues, exactly the snapshot set |

| 2026-09-04T22:53Z | #3467 / PR #3547 | pushed fd1dbbedb (--no-verify), PR opened base dev | typecheck 0; focused 48 pass; privacy ok; CI trailing |
| 2026-09-04T23:16Z | #3462 / PR #3551 | pushed 9611cdf4b (--no-verify), PR opened base #3547 head | typecheck 0; focused 71 pass; privacy ok; CI trailing |
| 2026-09-04T23:36Z | #3464 / PR #3554 | pushed b4fbd293f (--no-verify), PR opened base #3551 head, Refs not Closes | typecheck 0; service.test.ts 199 pass; privacy ok; CI trailing |
| 2026-09-04T23:46Z | #3522 / PR #3542 (merged 7eddfb3eb) | NOOP: parallel-session carry already on dev, CI 28/0 | wp5 closed without a new PR |
| 2026-09-04T23:49Z | #3406 / PR #3556 | pushed 68f836a9f (--no-verify), PR opened base #3554 head, screenshot in body | typecheck 0; root 7 pass; gui 51 pass; lint/i18n/build ok; privacy ok; CI trailing |
8 changes: 8 additions & 0 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ opencodex makes Codex route through the proxy by editing two things Codex reads:
(`$CODEX_HOME/config.toml`, default `~/.codex/config.toml`) and its model catalog. Every edit is
idempotent and reversible.

The **Integrations** overview has a Codex switch for this native integration. Its switch shows
the desired state from OpenCodex's configuration, while the badge reports whether Codex is
currently observed using the proxy; during cleanup those can briefly differ while the badge
continues to report the observed state. Disabling names the effective Codex config
file, removes OpenCodex's generated routing artifacts, and leaves the proxy running for other
clients. Re-enabling rebuilds the catalog from the models available at that time, so it does not
restore the Codex files byte for byte.

The proxy exposes one bare `openai` Codex-login route with Pool(default) and Direct account modes,
plus `openai-apikey/<model>` for the configured API key. Pool includes main plus added accounts;
Direct uses only the caller/main bearer. The routes do not fall back to one another. Shipped v1
Expand Down
Binary file added docs/pr-assets/3407-codex-disable-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,12 @@ export const de: Record<TKey, string> = {
"integrations.native.msg.desktopEnabled": "Claude-Desktop-Integration aktiviert.",
"integrations.detail.grokModels": "{count} Modell(e) verbunden",
"integrations.detail.grokAbsent": "Kein opencodex-Block in der Konfiguration",
"integrations.dialog.codex.title": "Codex-Integration deaktivieren?",
"integrations.dialog.codex.changes": "opencodex entfernt seine Weiterleitung aus {path}, entfernt sein generiertes Profil, stellt den nativen Modellkatalog wieder her und kennzeichnet fortsetzbare Threads wieder für natives Codex.",
"integrations.dialog.codex.breakage": "Normales Codex verbindet sich direkt mit OpenAI; Modelle, die von anderen Providern geroutet wurden, verschwinden aus Codex. Proxy und /v1/responses bleiben für andere Clients aktiv.",
"integrations.dialog.codex.undo": "Beim erneuten Aktivieren wird der geroutete Katalog aus den dann verfügbaren Modellen neu erstellt und Codex wieder injiziert. Der Verlauf fortsetzbarer Threads wird in die passende Richtung nutzbar gemacht, aber die Dateien werden nicht Byte für Byte wiederhergestellt.",
"integrations.dialog.codex.sideEffect": "Wenn du nach der Injektion durch opencodex ein geroutetes Root-Modell ausgewählt hast, entfernt das Deaktivieren diese Auswahl; beim erneuten Aktivieren kann sie nicht rekonstruiert werden — wähle das Modell erneut. Wenn ein externer model_provider Codex besitzt, entfernt opencodex nur sein veraltetes Journal und lässt Konfiguration, Katalog und Verlauf unverändert.",
"integrations.dialog.codex.confirm": "Deaktivieren",
"integrations.dialog.grok.title": "Grok-Build-Integration deaktivieren?",
"integrations.dialog.grok.changes": "Aus {path} wird nur der von opencodex markierte Block entfernt. Manuell geschriebener Inhalt außerhalb des Blocks bleibt unverändert.",
"integrations.dialog.grok.breakage": "Nach dem Deaktivieren verschwinden die opencodex-Modellaliase aus Grok Build. Modelle, die mit dem xAI-Konto verwendet wurden, bleiben erhalten.",
Expand Down
6 changes: 6 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1597,6 +1597,12 @@ export const en = {
"integrations.cursor.colReasoning": "Reasoning",
"integrations.cursor.colContext": "Context",
"integrations.cursor.guide": "Open the Cursor Private Inference guide",
"integrations.dialog.codex.title": "Disable the Codex integration?",
"integrations.dialog.codex.changes": "opencodex will remove its routing from {path}, remove its generated profile, restore the native model catalog, and retag resumable threads for native Codex.",
"integrations.dialog.codex.breakage": "Plain codex will connect directly to OpenAI, and models routed from other providers will disappear from Codex. The proxy and /v1/responses stay running for other clients.",
"integrations.dialog.codex.undo": "Turning this back on rebuilds the routed catalog from the models available then and injects Codex again. Resume history is made usable in the matching direction, but its files are not restored byte for byte.",
"integrations.dialog.codex.sideEffect": "If you selected a routed root model after opencodex injected the config, disabling removes that model selection and turning the integration back on cannot reconstruct it; select the model again. If an external model_provider owns Codex, opencodex removes only its stale journal and leaves the config, catalog, and history unchanged.",
"integrations.dialog.codex.confirm": "Disable",
"integrations.dialog.grok.title": "Disable the Grok Build integration?",
"integrations.dialog.grok.changes": "Only the block marked by opencodex will be removed from {path}. Content written outside the block will remain unchanged.",
"integrations.dialog.grok.breakage": "Disabling removes the opencodex model aliases from Grok Build. Models used with your xAI account remain available.",
Expand Down
6 changes: 6 additions & 0 deletions gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1529,6 +1529,12 @@ export const fr: Record<TKey, string> = {
"integrations.detail.desktopNotInstalled": "La bibliothèque de configuration de Claude Desktop n’est pas installée",
"integrations.detail.grokModels": "{count} modèle(s) câblés",
"integrations.detail.grokAbsent": "Aucun bloc opencodex dans la configuration",
"integrations.dialog.codex.title": "Désactiver l’intégration Codex ?",
"integrations.dialog.codex.changes": "opencodex supprimera son routage de {path}, supprimera son profil généré, restaurera le catalogue de modèles natif et réattribuera les fils reprenables à Codex natif.",
"integrations.dialog.codex.breakage": "codex se connectera directement à OpenAI et les modèles routés depuis d’autres fournisseurs disparaîtront de Codex. Le proxy et /v1/responses resteront actifs pour les autres clients.",
"integrations.dialog.codex.undo": "La réactivation reconstruit le catalogue routé avec les modèles alors disponibles et réinjecte Codex. L’historique reprenable redevient utilisable dans la direction correspondante, mais ses fichiers ne sont pas restaurés octet par octet.",
"integrations.dialog.codex.sideEffect": "Si vous avez sélectionné un modèle racine routé après l’injection de la configuration par opencodex, sa désactivation supprime cette sélection et la réactivation ne peut pas la reconstituer ; sélectionnez à nouveau le modèle. Si un model_provider externe possède Codex, opencodex supprime uniquement son journal obsolète et laisse la configuration, le catalogue et l’historique inchangés.",
"integrations.dialog.codex.confirm": "Désactiver",
"integrations.dialog.grok.title": "Désactiver l’intégration Grok Build ?",
"integrations.dialog.grok.changes": "Seul le bloc marqué par opencodex sera supprimé de {path}. Le contenu écrit en dehors du bloc restera inchangé.",
"integrations.dialog.grok.breakage": "La désactivation supprime les alias de modèles opencodex de Grok Build. Les modèles utilisés avec votre compte xAI restent disponibles.",
Expand Down
6 changes: 6 additions & 0 deletions gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,12 @@ export const ja: Record<TKey, string> = {
"integrations.native.msg.desktopEnabled": "Claude Desktop 連携を有効にしました。",
"integrations.detail.grokModels": "モデル {count} 個を接続済み",
"integrations.detail.grokAbsent": "設定に opencodex ブロックがありません",
"integrations.dialog.codex.title": "Codex 連携を解除しますか?",
"integrations.dialog.codex.changes": "opencodex は {path} から自身のルーティングを削除し、生成したプロファイルを削除し、ネイティブのモデルカタログを復元し、再開可能なスレッドをネイティブ Codex 用に再タグ付けします。",
"integrations.dialog.codex.breakage": "通常の codex は OpenAI に直接接続し、他のプロバイダー経由でルーティングされていたモデルは Codex から消えます。プロキシと /v1/responses は他のクライアント向けに動作し続けます。",
"integrations.dialog.codex.undo": "再び有効にすると、その時点で利用できるモデルからルーティングカタログを再構築し、Codex を再注入します。再開可能な履歴は対応する方向で利用できるようになりますが、ファイルはバイト単位では復元されません。",
"integrations.dialog.codex.sideEffect": "opencodex が設定を注入した後にルートのルーティングモデルを選択していた場合、解除するとその選択も削除され、再有効化しても復元できません。モデルをもう一度選択してください。外部の model_provider が Codex を所有している場合、opencodex は古いジャーナルだけを削除し、設定・カタログ・履歴は変更しません。",
"integrations.dialog.codex.confirm": "解除",
"integrations.dialog.grok.title": "Grok Build 連携を解除しますか?",
"integrations.dialog.grok.changes": "{path} から、opencodex が印を付けたブロックだけを削除します。ブロック外に直接書いた内容はそのまま残します。",
"integrations.dialog.grok.breakage": "解除すると、Grok Build から opencodex のモデルエイリアスが消えます。xAI アカウントで使用していたモデルはそのままです。",
Expand Down
6 changes: 6 additions & 0 deletions gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,12 @@ export const ko: Record<TKey, string> = {
"integrations.native.msg.desktopEnabled": "Claude Desktop 통합을 켰습니다.",
"integrations.detail.grokModels": "모델 {count}개 연결됨",
"integrations.detail.grokAbsent": "설정에 opencodex 블록이 없습니다",
"integrations.dialog.codex.title": "Codex 통합을 끌까요?",
"integrations.dialog.codex.changes": "{path}에서 opencodex 라우팅을 제거하고 생성한 프로필을 삭제하며, 기본 Codex 모델 카탈로그를 복원하고, 재개 가능한 스레드에 기본 Codex 태그를 다시 붙입니다.",
"integrations.dialog.codex.breakage": "일반 codex는 OpenAI에 직접 연결되고, 다른 프로바이더로 라우팅되던 모델은 Codex에서 사라집니다. 다른 클라이언트를 위한 프록시와 /v1/responses는 계속 실행됩니다.",
"integrations.dialog.codex.undo": "다시 켜면 당시 사용 가능한 모델로 라우팅 카탈로그를 다시 만들고 Codex를 다시 주입합니다. 재개 기록은 맞는 방향으로 사용할 수 있게 되지만 파일이 바이트 단위로 복원되지는 않습니다.",
"integrations.dialog.codex.sideEffect": "opencodex가 구성을 주입한 뒤 라우팅된 루트 모델을 선택했다면, 해제할 때 그 모델 선택도 제거되며 다시 켜도 복원할 수 없습니다. 모델을 다시 선택하세요. 외부 model_provider가 Codex를 소유하면 opencodex는 오래된 저널만 제거하고 구성, 카탈로그, 기록은 그대로 둡니다.",
"integrations.dialog.codex.confirm": "해제",
"integrations.dialog.grok.title": "Grok Build 연동을 해제할까요?",
"integrations.dialog.grok.changes": "{path}에서 opencodex가 표시해 둔 블록만 제거합니다. 블록 바깥에 직접 쓴 내용은 그대로 둡니다.",
"integrations.dialog.grok.breakage": "해제하면 Grok Build에서 opencodex 모델 별칭이 사라집니다. xAI 계정으로 쓰던 모델은 그대로입니다.",
Expand Down
Loading
Loading