Skip to content
Merged
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
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Code, design, or tests from these pull requests shipped.
| [#3078](https://github.com/lidge-jun/opencodex/pull/3078) | [@Veritas-7](https://github.com/Veritas-7) | `0ef04e640` | "reimplements both of your production hunks on `dev`" |
| [#3142](https://github.com/lidge-jun/opencodex/pull/3142) | [@olddonkey](https://github.com/olddonkey) | `52d941640` | "That carry keeps the measurement/refusal work and ships the guard default-off" |
| [#3300](https://github.com/lidge-jun/opencodex/pull/3300) | [@S0RYUASUKA](https://github.com/S0RYUASUKA) | `15b43e51c` | the same two test files made hermetic |
| [#3284](https://github.com/lidge-jun/opencodex/pull/3284) | [@mdwsk88](https://github.com/mdwsk88) | `3d3c4fe26` | "Core implementation is already on `dev` via #3286 (`3d3c4fe26`), including the suffix wire ladder, picker collapse, Google adapter coverage" |

## Report and diagnosis

Expand Down
111 changes: 111 additions & 0 deletions devlog/_plan/260904_triage_gap_closure/000_research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# 000 — Research: three gaps the 260904 triage found but did not close

A triage pass over open PRs and issues closed #3366, #3061, and merged #2877.
Three candidates were deliberately left open because the evidence said they were
NOT done. This unit closes them properly.

## Gap 1 — PR #3293 shipped half of itself

`claude-fable-5-1` pricing is on `dev`; its metadata is not.

```text
git show origin/dev:src/usage/expected-prices.ts | grep -c claude-fable-5-1 -> 4
git show origin/dev:src/generated/model-metadata.ts | grep -o '"claude-fable-5-1"' -> (none)
git show origin/dev:scripts/model-metadata.source.json | grep -c claude-fable-5-1 -> 0
```

The pricing rows arrived through unrelated commits (`3d3c4fe26`, `ff1ac6b8c`,
`1aa839aa8`) that happened to touch `expected-prices.ts`. PR #3293 by
[@Veritas-7](https://github.com/Veritas-7) is the only source of the metadata
half, and it is still open and MERGEABLE.

That asymmetry is the actual defect: `expected-prices.ts` asserts an expected
price for a model the generated catalog does not know about. This is a
half-shipped feature, not a superseded one, which is why the triage left it open
rather than closing it as done.

## Gap 2 — issue #3431 asks for a credit the file's own rule requires

`CREDITS.md` on `dev` has 19 Carried-work rows and none for #3284.

Verified independently rather than taken from the issue text:

- `3d3c4fe26` is an ancestor of `origin/dev` and is the #3286 merge.
- `src/providers/antigravity-models.ts` on `dev` names `gemini-3.8-flash` 16
times; the suffix ladder shipped.
- #3284 by [@mdwsk88](https://github.com/mdwsk88) is CLOSED, not merged.
- Maintainer [@Ingwannu](https://github.com/Ingwannu) closed it as superseded and
wrote, in that closing comment: "Core implementation is already on `dev` via
#3286 (`3d3c4fe26`), including the suffix wire ladder, picker collapse, Google
adapter coverage, metadata/pricing, and remaining surfaces."

That last line matters procedurally. `CREDITS.md` says entries "must not be
inferred from diff similarity alone" and that every entry cites the maintainer's
own words. The closing comment IS that citation, so this row is admissible on the
file's own terms.

## Gap 3 — issue #3429 asks for a feature that was already closed once

This is the one that needs care.

PR #2994 by [@Ingwannu](https://github.com/Ingwannu) added `ultrafast` to the
pinned `gpt-5.6-sol` fallback catalog. It was closed unmerged on 2026-08-30 with
the maintainer verdict:

> Ultrafast는 프런트 노출 오류다. 피커에 칸을 올리면 고를 수는 있는데, 실제
> Ultrafast 속도가 나오지 않는다. 백엔드가 깨져서가 아니다.

Issue #2993 was closed with it. So "add ultrafast to the catalog" is a decision
that was already made and reversed, and re-landing it as a default would
reintroduce exactly the defect it was reverted for: a picker row that can be
selected but does not deliver the speed it names.

What #3429 reports is narrower and still true:

- `src/codex/catalog/parsing.ts` deletes `service_tier`, `service_tiers`,
`default_service_tier`, and `additional_speed_tiers` from these rows, so the
tier cannot survive a catalog regeneration even if the user edits
`~/.codex/opencodex-catalog.json` by hand.
- When a user forces the tier through anyway, the request completes but the
classification does not recognise it: `fastOutcome` reads `not-requested` and
the confirmation reads `unknown`, with no speed label for ultrafast.

The second half is a straight defect regardless of the first: the proxy is
carrying a tier it refuses to name.

## The shape this unit takes

Opt-in, default OFF. With the setting unset, nothing changes for anyone — the
stripping still happens, the catalog is byte-identical, and #2994's verdict
stands. With the setting on, the operator has said "I know what this is", the
tier survives end-to-end, and the logs name it honestly instead of reporting
`unknown`.

That is what makes re-landing this defensible after #2994: the reversal was about
showing everyone a row that lies. An operator-enabled flag that also fixes the
classification does not.

The user additionally asked for the Codex Set page head to be relieved of the
"한도 도달 계정 일시 중지" and "할당량 새로고침" controls, moving them below, with
the Ultra Fast toggle surfacing there. That is the same surface, so it lands in
the same phase.

## Design read

Existing dashboard, no new visual language.

DESIGN_VARIANCE: 2, MOTION_INTENSITY: 1, density D5 — operator console for
repeated expert work. The page head is currently carrying three controls plus a
title on one row, which is what the user is reacting to; moving the two
account-scoped actions down to the section they act on is a density fix, not a
restyle.

Do's: reuse the existing Switch + label + description row pattern, the existing
`btn btn-ghost btn-sm` controls, and the existing quota-refresh i18n keys.
Don'ts: no new panel, no default-on behavior, no picker row without the wire
support behind it.

## Out of scope

PR #3332 (user deferred it explicitly), release scripts, workflows, auth paths,
`gui/dist`.
70 changes: 70 additions & 0 deletions devlog/_plan/260904_triage_gap_closure/010_fable_metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# 010 — Land the missing claude-fable-5-1 metadata (PR #3293)

Work-phase `wp1`. Depends on 000.

## Problem

`src/usage/expected-prices.ts` on `dev` asserts an expected price for
`claude-fable-5-1` on four surfaces, but neither
`scripts/model-metadata.source.json` nor the `anthropic` row of
`src/generated/model-metadata.ts` knows the model exists. Pricing without
metadata is the wrong half to have.

## Diff plan

Carry PR #3293 by [@Veritas-7](https://github.com/Veritas-7). Its diff is exactly
the missing half, so this is a carry rather than a reimplementation.

### scripts/model-metadata.source.json

Insert the `claude-fable-5-1` entry into the `anthropic` provider block, in id
order between `claude-fable-5` and `claude-haiku-4-5`:

```json
"claude-fable-5-1": {
"id": "claude-fable-5-1",
"name": "Anthropic Fable 5.1",
"api": "anthropic-messages",
"provider": "anthropic",
"baseUrl": "https://api.anthropic.com",
"reasoning": true,
"input": ["text", "image"],
"cost": { "input": 10, "output": 50, "cacheRead": 0.25, "cacheWrite": 12.5 },
"contextWindow": 1000000,
"maxTokens": 128000,
"thinking": { "mode": "anthropic-adaptive", "minLevel": "minimal", "maxLevel": "xhigh" },
"compat": { "toolChoiceSupport": "auto" }
}
```

### src/generated/model-metadata.ts

This file is generated, so it is REGENERATED rather than hand-edited:
`bun run generate:model-metadata`. The expected delta is one row appended to the
`anthropic` array:

```text
["claude-fable-5-1",1000000,128000,"text,image",1,null,10,50,0.25,12.5]
```

Note the cacheRead of `0.25`, not `1`. `claude-fable-5` carries `1`; Fable 5.1's
published cache-hit rate is 0.025x base input, which the existing
`expected-prices.ts` rows already encode. Regenerating rather than typing the row
is what keeps the two files consistent.

### Attribution

`Co-authored-by: Veritas-7 <...>` in the landing commit. Per `CREDITS.md` and
`AGENTS.md`, the trailer is what GitHub reads; prose in the body is read by
nothing.

## Verification

- `bun run typecheck`
- `bun test ./tests/usage-cost.test.ts` — the PR's own test file
- Confirm the generated row exists and matches the source entry
- After merge: re-read `origin/dev` and confirm both files carry the model

## Close-out

Close #3293 citing the landing SHA and naming the author.
44 changes: 44 additions & 0 deletions devlog/_plan/260904_triage_gap_closure/020_credits_3284.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 020 — CREDITS row for #3284 (issue #3431)

Work-phase `wp2`. Depends on 000. Disjoint from every other phase.

## Why this is admissible

`CREDITS.md` sets two bars, and both are met:

1. "If you find a landing that belongs on this page, open an issue" — #3431 is
that issue, opened at maintainer [@Ingwannu](https://github.com/Ingwannu)'s
explicit request.
2. "Every entry cites the maintainer's own words... Nothing here is inferred from
a diff." — the citation is Ingwannu's closing comment on #3284.

Verified independently rather than taken from the issue body:

```text
git merge-base --is-ancestor 3d3c4fe26 origin/dev -> true
3d3c4fe26 = "feat(models): roll out Gemini 3.8 Flash ... (#3286)", author JUN
#3284 by @mdwsk88 -> CLOSED, not merged
src/providers/antigravity-models.ts on dev names gemini-3.8-flash 16 times
```

## Diff plan

One row appended to the `## Carried work` table in `CREDITS.md`, in pull-request
number order after the `#3300` row:

```markdown
| [#3284](https://github.com/lidge-jun/opencodex/pull/3284) | [@mdwsk88](https://github.com/mdwsk88) | \`3d3c4fe26\` | "Core implementation is already on \`dev\` via #3286 (\`3d3c4fe26\`), including the suffix wire ladder, picker collapse, Google adapter coverage" |
```

The quoted text is Ingwannu's, verbatim from the #3284 closing comment, matching
how every other row in the table cites its source.

## Verification

- The table renders (no broken pipe alignment)
- `bun run privacy:scan` stays green — it reads `CREDITS.md`
- After merge: `git show origin/dev:CREDITS.md | grep 3284`

## Close-out

Close #3431 citing the landing SHA.
142 changes: 142 additions & 0 deletions devlog/_plan/260904_triage_gap_closure/030_ultrafast_optin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# 030 — Ultra Fast as an explicit opt-in (issue #3429)

Work-phase `wp3`. Depends on 000. This is the phase that needs judgment, not just
diffs.

## The constraint #2994 left behind

PR #2994 added `ultrafast` to the pinned `gpt-5.6-sol` fallback catalog and was
closed unmerged:

> Ultrafast는 프런트 노출 오류다. 피커에 칸을 올리면 고를 수는 있는데, 실제
> Ultrafast 속도가 나오지 않는다.

Exploration of `origin/dev` explains WHY, and the reason is load-bearing for this
plan. `src/codex/data/upstream-models.json` advertises exactly one tier on every
row that has any:

```json
"service_tiers": [{ "id": "priority", "name": "Fast", "description": "1.5x speed, increased usage" }],
"additional_speed_tiers": ["fast"]
```

There is no upstream `ultrafast` tier to forward. So a catalog row advertising
one is fabricated metadata: the picker gains a choice the wire cannot honor. That
is precisely the defect #2994 was closed for, and adding the row back — flag or
no flag — would reproduce it.

**Therefore this phase does NOT re-add the catalog row by default, and does not
claim Ultra Fast works.** It ships the half that is true today.

## What is a real defect regardless

#3429 reports something narrower that holds independently of whether the tier is
advertised: when a caller forces `service_tier: "ultrafast"` through anyway (by
hand-editing `~/.codex/opencodex-catalog.json`, which the reporter did), the
proxy carries the value but refuses to name it.

Traced on `dev`:

- `canonicalFastTierMarker` (`src/providers/fastwire.ts:247`) folds only
`priority`/`fast`, so `ultrafast` returns `undefined`.
- `fastIntent` is therefore false, and the request is classified
`fastOutcome: "not-requested"` at `fastwire.ts:344` — not `unknown`, but
actively "no fast tier was asked for", which is false.
- `requestLogSpeedLabel` (`src/server/request-log.ts:594`) returns `undefined`
for anything but `priority`/`fast`, so the Logs page shows no speed badge.

The proxy is forwarding a tier it will not admit to. That is an observability
lie, and it is fixable without advertising anything.

## Diff plan

### 1. Config flag, default OFF

Follow the `fastRows` precedent exactly (`src/config.ts:1052`,
`src/types/config.ts:384`):

```ts
// src/config.ts — zod
ultraFastTier: z.boolean().optional().catch(false),
```

`.catch(false)` matters: a malformed hand edit degrades to off rather than
rejecting the config. Read it with the house `=== true` idiom, never truthiness.

A management-route boolean guard goes beside `showCodexSparkQuota` in
`src/server/management/config-routes.ts:408` because this flag gets a dashboard
toggle (`fastRows` has none, which is why it needs no guard).

### 2. Honest classification (the part that fixes the reported defect)

`src/providers/fastwire.ts`: widen the canonical union to
`"priority" | "ultrafast"`, fold `ultrafast` in `canonicalFastTierMarker`, add
the `canonicalToWire` entry, and generalize the two `=== "priority"`
comparisons at `:349` and `:377` to "is a recognised canonical tier".

`src/server/request-log.ts:594`: `requestLogSpeedLabel` returns `"ultrafast"`
for that tier. The existing contract — `"auto"` and `undefined` stay
`undefined` — must not move; `tests/request-log.test.ts:703` pins it.

No new `fastOutcome` literal is introduced. The four existing values still
describe what happened; what changes is that `ultrafast` now reaches them instead
of being mistaken for "not requested". That keeps
`src/usage/log.ts`'s persisted-row allowlist untouched.

### 3. Catalog gating — flag-gated, and honest about what it does

`normalizeServiceTiers` and `normalizeRoutedCatalogEntry` gain a trailing
optional options bag, defaulting to off, threaded from the `sync.ts` call sites
that already hold `config`. With the flag absent, every `delete` runs exactly as
today.

With the flag ON, the tier is PRESERVED when a catalog already carries it
(the operator's hand edit survives regeneration, which is the reporter's actual
ask). It is still NOT synthesized: `src/codex/catalog/effort.ts:160` keeps
emitting only the `priority` row, because inventing a tier upstream does not
advertise is what #2994 was reverted for.

### 4. GUI — Codex Set page head

`CodexAccountPoolPageHead` (`gui/src/components/codex-account-pool-main-card.tsx:207`)
currently carries the title, a status span, the Spark toggle, and two action
buttons on one row. Move "한도 도달 계정 일시 중지" and "할당량 새로고침" out of
the head and into the existing advanced-settings grid
(`CodexAuthAdvancedSettings`, `div#codex-auth-advanced-boxes`), which is already
the page's home for secondary controls, and put the Ultra Fast toggle there as a
`card card-row` matching `CodexAccountPickerSetting`.

Two constraints from the existing code:

- The component is dual-mode. `embedded={true}` (Providers workspace) renders a
bare `.row`; only the standalone page renders `.page-head`. The move must not
change the embedded surface.
- `gui/tests/codex-set-page-head-wrap.test.ts` throws `rule not found` if the
`.codex-auth-page-head` selectors disappear, and
`codex-account-pool-toast-tone.test.tsx` queries
`.codex-auth-page-head__feedback`. Keep those class names alive.

The toggle's description states plainly that upstream advertises no Ultra Fast
tier today and that the setting only preserves a tier the operator supplies. A
switch that implies a speed it cannot deliver is the #2994 defect in a new place.

## Verification

- `bun run typecheck`, `bun run lint:gui`
- Flag OFF regression set, which is the proof the default did not move:
`tests/codex-catalog.test.ts`, `tests/fastwire-characterization-routing.test.ts`
(byte golden), `tests/service-tier-capability.test.ts`,
`tests/request-log.test.ts`, `tests/fastwire-observability.test.ts`
- New focused tests: flag OFF strips exactly as before; flag ON preserves a
supplied tier; `ultrafast` classifies instead of reading `not-requested`;
`requestLogSpeedLabel` maps it while `"auto"` still returns `undefined`
- `gui/tests/codex-set-page-head-wrap.test.ts` and the toast-tone suite stay green
- Live: the Codex Set page head no longer carries the two buttons; they and the
toggle are reachable below

## What this phase will NOT claim

It will not claim Ultra Fast delivers Ultra Fast speed. If live evidence shows an
opted-in request still cannot get the tier honored end-to-end, that is reported
on #3429 rather than papered over — the same call #2994 made, and the reason it
is closable honestly either way.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading