Skip to content

feat(protocols): preview the request path without sending (PF-03) - #5810

Closed
lidge-jun wants to merge 11 commits into
feat/pf02-protocol-tracefrom
feat/pf03-protocol-plan
Closed

lidge-jun wants to merge 11 commits into
feat/pf02-protocol-tracefrom
feat/pf03-protocol-plan

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

PF-03 of the protocol-first-class unit (devlog/_plan/260924_protocol_first_class/030_gui_and_management_api.md#pf-03-planner-and-preview). Stacked on #5809 (PF-02).

A read-only request-path preview: which wire each candidate of a model would receive for a given client API and feature set, computed with the same lane→path rule the observed trace uses. Nothing is sent, refreshed, selected or written.

  • src/protocols/plan.ts (pure leaf): planProtocol computes each candidate's request/response path, feature effects, eligibility under the unrepresentable policy (feature-unrepresentable under reject), and the guaranteed-by-all vs some-candidates-only feature split. A disabled surface blocks candidates with surface-disabled.
  • src/protocols/plan-snapshot.ts: builds the planner input from config without side effects. routeModel advances combo round-robin state and runs the policy evaluator, so combo and policy selectors are expanded from their configured targets through routeConcreteModel instead; a test pins that a preview leaves combo selection state unchanged. Messages caller-forward is reported as caller-credential-required, never assumed.
  • GET /api/protocols and POST /api/protocols/plan in src/server/management/protocol-routes.ts, lazily mounted, declared in the route registry with a deferred-verb exemption (CLI verbs land with PF-12). Input is bounded (model ≤ 200 chars, ≤ 24 features, unknown keys → 400) and never logged.
  • Dashboard: a "Request path preview" section on Integrations → API / Keys. It only calls the server on Preview, states that preview sends nothing and costs nothing, and keeps delivery mode separate from any verification verdict. An older server's 404 disables it quietly.
  • Docs: structure/ owner docs and the management API reference.

Verification

  • bun x tsc --noEmit (root): exit 0. New test files additionally typechecked with a temporary tsconfig: exit 0.

  • gui: bun x tsc -b, bun run lint:i18n: exit 0.

  • bun run structure:check, bun run privacy:scan: exit 0.

  • Tests (tests/responses/protocol-plan*.test.ts, tests/server/protocol-routes.test.ts, gui/tests/protocol-api.test.ts) were written and registered but run in the full local suite below.

  • Full local run on the stack head (feat(protocols): protocol paths as a first-class concern — PF-01..PF-12 #5820, which contains this change): bun run test — the only failures are Lab CL-03/CL-07/CL-08/SEC-02 and release helper timeouts, which fail identically on a checkout without this stack (local environment), plus service/toggle cases that pass when run alone; cd gui && bun test --isolate tests — 2398 pass, 0 fail.

  • CI on this head: all required checks pass.

Screenshots

Captured from the stack head in an isolated home (fake providers, no real credentials).

API page, Request path preview for a mixed combo

API page, Request path preview for a mixed combo

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

planProtocol turns a settled-route snapshot into a ProtocolPlanV1: each candidate's path
from path.ts, its feature effects, and whether reject-unrepresentable would refuse it,
plus the features every eligible candidate keeps versus only some. It reads no config so
the dashboard and the server can share it as a leaf.
The preview needs the route a request would settle on, but routeModel picks combo
targets and runs the policy evaluator. Combos and policies are expanded from their
configured targets through routeConcreteModel instead, so a preview never advances
round-robin state. Messages caller-forward is reported as caller-credential-required
because a preview has no caller credential to judge.
GET /api/protocols reports the contract version, surfaces, settings and policy revision;
POST /api/protocols/plan returns a preview ProtocolPlanV1. The body is bounded and
unknown keys are refused so the route cannot become a place to paste a prompt. Mounted
lazily and declared with a deferred-verb exemption owned by PF-12.
Plans are checked with the shared isProtocolPlanV1 validator and cached per target,
selector, sorted features and policy revision, so a cached preview is reused only while
the policy that produced it is still active. A 404 from an older server reads as
unavailable rather than as an error.
A "Request path preview" section after the endpoints lets an operator pick a model,
client API and features and see each candidate's path, delivery mode, fidelity, feature
effects and reasons, split into features every candidate guarantees and those only some
keep. Delivery mode is labelled as delivery, never as verification.
Structure docs name the planner's inputs, why the snapshot expands combos and policies
itself, and where the dashboard panel and its validation live; the public management API
reference lists the two read-only routes and states that a preview sends nothing.
…exists

Toward a different wire the path reason already explains the route; repeating the
ingress's cross-wire decline next to it read as a second, contradictory cause.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 25, 2026 03:29
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 762f1d62-562c-4a59-be01-6bc20b2e86aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T03:34:07.144196Z 4f7f9ce PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed.

Hygiene

✅ Deterministic PR hygiene checks passed.

@github-actions
github-actions Bot marked this pull request as draft September 25, 2026 03:30

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f7f9cedcc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/protocols/plan.ts

export function planProtocol(input: ProtocolPlanInput): ProtocolPlanV1 {
const features = orderedFeatures(input.features);
const snapshotCandidates = input.candidates.slice(0, PROTOCOL_DTO_LIMITS.candidates);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid dropping route candidates before aggregation

For a valid combo or routing profile with more than 16 targets—the existing validators impose no such maximum—this slice silently removes every later target before computing candidates, guaranteedFeatures, and partialFeatures. If an omitted target uses a different adapter or loses a requested feature, the preview can falsely report that all candidates preserve it. Either reject configurations above the DTO limit, represent truncation explicitly, or compute the aggregate over every target while bounding only the serialized detail.

AGENTS.md reference: src/AGENTS.md:L10-L10

Useful? React with 👍 / 👎.

Comment thread gui/src/protocol-api.ts
Comment on lines +93 to +95
const key = protocolPlanCacheKey(apiBase, query, info.policyRevision);
const cached = planCache.get(key);
if (cached) return { kind: "plan", plan: cached };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Key cached plans by routing configuration

After a live provider, default route, combo, or routing-profile edit, this lookup can return a stale plan without issuing the POST: policyRevision only hashes apiSurfaces, claudeCode, and protocols, even though planning also reads providers, aliases, combos, profiles, and the default provider. Include those routing inputs or a config generation in the revision/cache key, or remove this cache so the dashboard stays aligned with the active provider configuration.

AGENTS.md reference: gui/AGENTS.md:L9-L10

Useful? React with 👍 / 👎.

Comment on lines +195 to +198
} else if (result.kind === "error") {
setFailed(true);
} else {
setPlan(result.plan);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale results when a new preview fails

If a preview has already succeeded and the user changes the model, inbound API, or features, a failed subsequent request sets the error flag but retains the previous plan. The panel then renders the failure message together with an obsolete result beneath the newly selected controls, which can be mistaken for the requested preview. Clear the prior plan when starting a new request or when this error branch is reached.

Useful? React with 👍 / 👎.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed PF-03 at exact head 4f7f9cedcc3e71cff09f4a05e4fdd5bdf93c82de, against its PF-02 base. Three layer-specific blockers remain:

  1. src/protocols/plan.ts:140 truncates candidates to the 16-row DTO limit before calculating eligibility and guaranteed/partial features. A valid combo/profile with more targets can therefore hide a later incompatible route and falsely claim full preservation. Aggregate over every candidate and bound only serialized detail, reject oversized configurations, or represent truncation explicitly.
  2. gui/src/protocol-api.ts:93-95 caches by a policyRevision that excludes provider/default route/alias/combo/profile changes even though planning reads them. A live routing edit can return an obsolete plan without sending the preview POST. Include every routing input (or a config generation) in the revision/key, or remove the cache.
  3. ProtocolPlanPanel.tsx:182-199 retains the previous successful plan when the next preview fails, so stale output is rendered beneath the new controls together with the error. Clear the plan at request start or on the error branch.

Please add coverage for >16-target aggregation/truncation semantics, routing edits invalidating cached previews, and successful-preview followed by failed-preview state. PF-03 remains a draft stacked on changes-requested PF-01/PF-02 revisions.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 54 / 80

이 PR은 요청을 보내지 않고, 그 모델이 어느 길로 갈지 미리 보여 준다. API 키 화면에 "요청 경로 미리보기"가 생긴다. 모델, 클라이언트 API, 기능을 고르고 Preview를 누르면 서버가 후보마다 가는 길, 배달 방식, 기능이 살아남는지를 계산한다. 계산은 설정만 읽는다. 업스트림으로는 아무것도 나가지 않고, 콤보의 차례도 그대로다.

계산은 두 겹이다. plan.ts의 planProtocol은 이미 정해진 후보만 받아 길을 그린다. plan-snapshot.ts가 설정에서 그 후보를 만든다. 콤보와 정책은 설정된 대상을 routeConcreteModel로 푼다. 그래서 미리보기가 라운드로빈 상태를 밀지 않는다. Messages에서 호출자 키가 있어야 통과하는 경우는 caller-credential-required로만 적는다.

화면은 GET /api/protocols와 POST /api/protocols/plan만 부른다. 본문은 모델 200자, 기능 24개까지다. 모르는 키는 400이다. 옛 서버의 404는 미리보기를 끈다. 베이스는 #5809의 feat/pf02-protocol-trace다. 같은 머리의 다른 열린 PR은 없다. src/types.ts를 나누는 변경은 없다. #5811이 이 브랜치를 베이스로 앉아 있다. 테스트 샤드 1, 3, 4는 통과했다. 샤드 2와 gates, react-doctor, enforce-target은 실패했다.

라인 - src/protocols/plan.ts 140행 — 후보를 16개만 남긴 뒤에 보장 기능과 일부 기능을 센다. 콤보 대상 검사는 src/combos/types.ts 247행에서 비어 있는지만 본다. 17번째 대상이 다른 어댑터면, 미리보기는 모든 후보가 그 기능을 지킨다고 말할 수 있다. 잘렸다는 이유도 안 붙는다.

라인 - gui/src/protocol-api.ts 34행, src/protocols/settings.ts 98행 — 캐시 키는 정책 개정 해시뿐이다. 그 해시는 API 표면, Claude, protocols 설정만 넣는다. 공급자, 콤보, 기본 경로를 바꿔도 해시가 같으면 POST 없이 예전 길을 보여 준다.

라인 - gui/src/components/protocols/ProtocolPlanPanel.tsx 195행 — 두 번째 미리보기가 실패하면 에러만 켜고 이전 plan을 남긴다. 250행이 실패 문장과 예전 결과를 같이 그린다.

라인 - gui/src/pages/ApiKeys.tsx 527행 previewModels — gui/tests/apikeys-layout.test.ts 19행이 파일에 viewMode 글자가 없어야 한다고 본다. previewModels 안에 그 글자가 들어 있어 gates가 실패한다.

라인 - gui/src/i18n/fr.ts의 api.plan.routeKind("Route")와 api.plan.routeKind.combo("Combo") — 영어와 같다. gui/tests/fr-localization.test.ts 250행이 그래서 실패한다. 독일어 de.ts도 같은 두 단어를 영어 그대로 뒀다. 그 검사는 프랑스어만 빨간 상태다.

라인 - CI test 2/4, tests/cli/cli-headless-parity.test.ts 509행 — 기대한 목록은 빈 배열인데 /api/protocols와 /api/protocols/plan이 있다. 라우트 등록부의 deferred-verb 면제는 이 검사를 통과시키지 않는다. 본문은 CLI 동사를 PF-12에 맡긴다고 적었다.

메인테이너의 판단이 필요한 지점

enforce-target은 베이스가 열린 PR #5809의 머리라서 wrong_base는 넘겼다. 실패한 이유는 UI 스크린샷이 없어서다. 본문도 스크린샷을 나중에 넣겠다고 적었다. 베이스를 지금 dev로 바꾸면 #5809와 #5811 스택이 풀린다.

16개 상한을 미리보기 전체에 적용할지, 집계는 전부 하고 화면에 나가는 후보만 16개로 할지 정해 달라. 캐시는 라우팅 설정까지 키에 넣을지, 이 화면에서는 캐시를 뺄지 정해 달라.

CLI 동사는 PF-12까지 미룰 수 있다. 그때까지 패리티 검사가 빨갛다. 그 검사의 목록에 두 경로를 PF-12 소유로 넣을지, 지금 동사 껍데기를 넣을지 정해 달라.

너의 추천

방향은 유지해라. 닫을 중복 PR은 없다. 베이스를 dev로 되돌리지 마라. 합치기 전에 세 가지를 고쳐라. 후보를 자르기 전에 보장 기능을 세고, 잘렸으면 이유를 적어라. 캐시 키에 라우팅 설정을 넣거나 캐시를 빼라. 미리보기가 실패하면 이전 결과를 지워라. previewModels 이름을 바꿔 viewMode 글자가 빠지게 해라. 프랑스어 Route와 Combo를 번역해라. CLI 패리티 목록에는 PF-12 소유로 한 줄 넣어 샤드 2를 풀어라. 스크린샷은 본문에 더해라. react-doctor는 종료 코드 1인데, 잡 로그에 규칙 이름이 없다. 런 요약을 보고 그 경고만 처리하면 된다.

이 댓글은 grok-bot이 작성했습니다

The French catalog test rejects English values that carry translatable words; the
route label and its combo value were left in English.
A candidate is identified by its provider and model; the list index added nothing
and React Doctor flags index keys.
The API page layout test forbids the classic viewMode toggle by searching for the
substring, which the previous prop name contained.
The protocol routes have owed CLI verbs, recorded as deferred in the route
registry; the parity map now says so instead of leaving them uncovered.
@github-actions
github-actions Bot marked this pull request as ready for review September 25, 2026 05:23
@devin-ai-integration devin-ai-integration Bot added the priority: P3 Low: new provider/client integration, large or experimental feature (>2000 LOC or >50 files), RFC/ro label Sep 25, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

Maintainer triage: priority: P3 — protocol-first-class series PF-03.

Criteria (P3): Low: new provider/client integration, large or experimental feature (>2000 LOC or >50 files), RFC/roadmap, or long-stale branch.

Related / overlapping PRs:

lidge-jun added a commit that referenced this pull request Sep 25, 2026
…12 (#5820)

Squash of the protocol-first-class stack #5808, #5809, #5810, #5811, #5812, #5813, #5814, #5815, #5816, #5817, #5819 and #5820. Every new lane sits behind a protocols.rollout switch that defaults off.
@lidge-jun

Copy link
Copy Markdown
Owner Author

Landed in dev as part of the single squash of the protocol-first-class stack: #5820 (0f4c8d4).

@lidge-jun lidge-jun closed this Sep 25, 2026
@lidge-jun
lidge-jun deleted the feat/pf03-protocol-plan branch September 26, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: P3 Low: new provider/client integration, large or experimental feature (>2000 LOC or >50 files), RFC/ro

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants