Skip to content

refactor(bridge): split bridge.ts behind a facade - #4672

Merged
lidge-jun merged 3 commits into
devfrom
codex/godfile-r5-b-bridge
Sep 15, 2026
Merged

lidge-jun merged 3 commits into
devfrom
codex/godfile-r5-b-bridge

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Summary

Stacked on #4671. src/bridge.ts was 2,206 lines, and almost all of it was two functions: bridgeToResponsesSSE at 1,387 lines and buildResponseJSONWithBudget at 562. Both move whole, neither body changes, and the facade becomes 7 lines of re-exports keeping all six public names.

leaf lines holds
src/bridge/errors.ts 34 formatErrorResponse
src/bridge/internal.ts 174 shared helpers, owned-budget state, output types
src/bridge/response-json.ts 624 buildResponseJSON and buildResponseJSONWithBudget
src/bridge/sse.ts 1,444 bridgeToResponsesSSE and its two private helpers

Placement came from counting each helper's reads per region rather than guessing. sseEvent and responseError are read only inside the SSE function, so they travel with it. adapterFailureFromEvent is read from both the SSE and the JSON path, so it goes to internal.ts.

The mutable module state needed care. ownedBudgetAbandonedMs is a module-level let that setOwnedBudgetAbandonedMsForTests mutates and that the SSE function reads when it arms the abandoned-budget watchdog. The declaration, its default constant and the setter all stay together in internal.ts, and sse.ts imports the binding instead of copying the value, so the ES live binding still shows a test-set value. Copying it into a local or re-exporting a snapshot would have frozen the delay at ten minutes with every test still green.

structure/manifest.json graces the new src/bridge/ directory. The facade path is already graced because no doc names it, the leaves inherit exactly that situation, and structure:check only sees the directory once it is tracked. structure/INDEX.md regenerated.

tests/responses/responses-undeclared-tool-guard.test.ts repoints its comment reference for declaredToolNames to the leaf that holds it.

Ratchet cap lowered from 2,206 to 7.

Verification

Run in a worktree without node_modules, so the hosted suite and typecheck are what this PR relies on for the rest.

  • bun scripts/structure-ssot.tsstructure/ SSOT checks passed
  • bun scripts/file-size-ratchet.tsfile-size ratchet passed
  • Repository-wide relative-specifier resolution audit over src and gui/src: 8,344 specifiers, no new unresolved import against the pre-change baseline.
  • Facade export surfaces compared against origin/dev with Bun.Transpiler().scan().exports: src/bridge.ts and src/adapters/openai-responses.ts both identical.
  • Pure-move proof: all 14 moved ranges here (and the 16 from the parent PR) compared byte-for-byte against git show origin/dev:<path>, normalizing only the added export keyword. 0 drift.
  • Syntactic completeness: with comments and string bodies blanked, every moved range starts and ends at brace depth 0 and never goes negative.
  • bun x tsc --noEmit --strict --skipLibCheck over the facade and leaves reports three diagnostics naming these paths: TS2591 for Buffer twice and TS2339 for Timer.unref. Running the same command against git show origin/dev:src/bridge.ts reproduces all three, so they are the missing @types/node in this worktree and not something the split introduced.

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.

Roadmap and audit record: devlog/_plan/260915_godfile_round5/.

Summary by CodeRabbit

  • New Features

    • Added support for converting adapter results into Responses API JSON and streaming SSE formats.
    • Supports messages, reasoning, function and custom tool calls, web-search results, citations, usage details, and compaction output.
    • Added heartbeats, sequence numbering, stall handling, and completed, incomplete, or failed terminal statuses.
    • Added validation for declared tools and malformed tool arguments, with standardized error responses and retry guidance.
    • Added consistent usage reporting and improved handling of truncation, upstream errors, and incomplete responses.
  • Documentation

    • Updated project structure records for the bridge components.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 15, 2026 01:34
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 9e2ba0af-1f23-4270-b45b-3ea9db9c7882

📥 Commits

Reviewing files that changed from the base of the PR and between 3c6cf21 and 3646c6e.

📒 Files selected for processing (1)
  • tests/lib/reasoning-replay-scope-source.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.


📝 Walkthrough

Walkthrough

The change extracts bridge functionality into shared utilities, a batch Responses JSON translator, and a streaming Responses SSE translator. It adds tool validation, usage normalization, budgeting, citations, terminal handling, heartbeats, cancellation, and extracted-module metadata.

Changes

Responses bridge translation

Layer / File(s) Summary
Shared bridge support and extracted-module metadata
src/bridge/errors.ts, src/bridge/internal.ts, structure/INDEX.md, structure/manifest.json, tests/fixtures/file-size-baseline.json, tests/responses/responses-undeclared-tool-guard.test.ts, tests/lib/reasoning-replay-scope-source.test.ts
The bridge now formats errors, normalizes usage, validates tool arguments, builds web-search actions, generates UUIDs, and accumulates string fragments. Metadata, size baselines, comments, and source-scope tests reference the extracted modules.
Batch Responses JSON translation
src/bridge/response-json.ts
buildResponseJSON translates adapter events into output items, tracks translator budget, validates tools and arguments, attaches citations, normalizes truncation reasons, and returns completed, incomplete, or failed responses.
Streaming Responses SSE translation
src/bridge/sse.ts
bridgeToResponsesSSE converts adapter events into typed SSE frames for text, reasoning, tool calls, web search, citations, compaction, malformed input, undeclared tools, and translation-buffer overflow.
Stream lifecycle and terminal handling
src/bridge/sse.ts
The stream emits creation and progress frames, sends heartbeats, detects upstream stalls, synthesizes EOF incompleteness, supports cancellation, emits one terminal event plus [DONE], invokes callbacks, and disposes owned budgets.

Priority: ➖ Normal

Estimated code review effort: 5 (Critical) | ~90 minutes

Change: Refactor

Sequence Diagram(s)

sequenceDiagram
  participant Adapter as Adapter event iterable
  participant Bridge as bridgeToResponsesSSE
  participant Budget as Translator budget
  participant Client as ReadableStream client
  Adapter->>Bridge: Deliver adapter events
  Bridge->>Budget: Charge retained and transient bytes
  Bridge->>Client: Emit Responses SSE frames
  Bridge->>Client: Emit terminal event and [DONE]
Loading

Merge Risk: ⚪ Minimal · up to 3646c

The bridge extraction has no established current-head failure requiring a merge hold.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.38% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 13 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: splitting the large bridge implementation into leaf modules while retaining a facade in src/bridge.ts.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/godfile-r5-b-bridge

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 15, 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-15T01:38:03.799246Z ba445be 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 added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Sep 15, 2026
Base automatically changed from codex/godfile-r5-a-openai-responses to dev September 15, 2026 01:35

@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: ba445bec4d

ℹ️ 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/bridge.ts
Comment on lines +5 to +6
export { buildResponseJSON } from "./bridge/response-json";
export { bridgeToResponsesSSE } from "./bridge/sse";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Retarget the source-oracle after moving both bridge bodies

When bun test tests/lib/reasoning-replay-scope-source.test.ts runs, the test still reads only src/bridge.ts and expects two const replayCacheScope = options?.replayCacheScope; declarations. This facade now contains neither declaration, so .match() returns null and toHaveLength(2) fails. Update the test to inspect src/bridge/sse.ts and src/bridge/response-json.ts, or aggregate those leaves, so the required test suite can pass.

AGENTS.md reference: src/AGENTS.md:L24-L26

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 73 / 80

이 PR은 지금 dev HEAD(9b711073a, 방금 합쳐진 #4671 openai-responses 파사드) 바로 다음에 올 godfile round5의 bridge 분해다. 현재 dev에 아직 있는 src/bridge.ts는 2,206줄짜리 모노리스이고, 거의 전부가 bridgeToResponsesSSE(약 1,387줄)와 buildResponseJSONWithBudget(약 562줄) 두 함수다. 이 PR은 그 본문을 한 줄도 고치지 않고 네 리프로 옮긴다. src/bridge/errors.ts(formatErrorResponse), src/bridge/internal.ts(공유 헬퍼·owned-budget 상태·출력 타입), src/bridge/response-json.ts(buildResponseJSON / WithBudget), src/bridge/sse.ts(SSE 본체). 파사드 src/bridge.ts는 7줄 재export로 origin/dev와 같은 공개 이름 여섯 개(adapterFailureFromMessage, formatErrorResponse, setOwnedBudgetAbandonedMsForTests, buildResponseJSON, bridgeToResponsesSSE, ResponsesTerminalStatus)를 유지한다. 소비자는 계속 from "../bridge" / from "../../bridge"만 보면 되고, src/server/responses/core.ts·collaboration·compact 같은 경로의 import는 그대로다.

왜 지금 점수이냐면, round5 로드맵(devlog/_plan/260915_godfile_round5/)이 이미 dev에 깔려 있고 첫 타깃(#4671)은 착지했다. 남은 oversized는 bridge와 src/server/index.ts(약 3,400줄)다. bridge를 먼저 파사드 뒤로 밀어 두는 게 다음 스택(activation_guard / server index)과 파일크기 래칫을 동시에 편하게 만든다. 검증도 방향이 맞다. structure SSOT, file-size ratchet, 상대경로 해석 감사, Bun.Transpiler export 표면 비교, 이동 구간 byte-for-byte(추가된 export 만 정규화) 0 drift. 본문 동작 변경이 아니라 구조 이동이라 제품 리스크는 낮다.

다만 지금 GitHub 상태는 바로 머지하면 안 된다. #4671이 dev에 들어가면서 tests/fixtures/file-size-baseline.json이 양쪽에서 바뀌었고, merge-tree 기준 충돌은 그 한 줄이다다. dev 쪽은 아직 "src/bridge.ts": 2206, 이 브랜치는 7. openai-responses 쪽 6줄 캡은 이미 양쪽에 맞춰져 있다. 해결은 단순하다. dev 위로 rebase(또는 merge)한 뒤 baseline의 bridge 값만 7로 맞추면 된다. 브랜치 커밋 히스토리에는 부모 PR의 openai-responses 분해도 들어 있지만, 현재 tip 대비 실질 diff는 bridge·structure grace·undeclared-tool 주석·baseline 쪽이다. rebase 후 그 중복 커밋은 정리되거나 empty에 가까워질 수 있다.

한 가지 설계 선택은 계획서와 다르다. 020_bridge.md 표는 ownedBudgetAbandonedMs / setOwnedBudgetAbandonedMsForTests를 sse 리프로 보냈는데, 구현은 internal.tsexport let으로 두고 sse.ts가 그 바인딩을 import한다. 이건 버그가 아니라 맞는 쪽이다. 값을 복사하거나 스냅샷 re-export하면 테스트가 세터로 줄인 지연이 SSE 쪽에 안 보이고, 워치독이 항상 10분에 고정된 채로 테스트만 통과할 수 있다. ES live binding을 쓰는 이유가 PR 본문에 분명히 적혀 있다. 계획서 숫자 표만 sse로 남아 있으니, 머지 전에 계획서 한 줄을 구현에 맞추거나 “의도적 이탈”로 짧게 남기면 된다.

CI는 gates·hygiene·docker smoke 등은 통과했고, 리뷰 시점 기준 test 2/4가 실패로 찍혀 있었다. 스택이 CONFLICTING인 동안의 샤드 실패일 수 있으니, baseline 충돌을 푼 뒤 전체 샤드를 다시 보고 초록일 때 합치는 편이 안전하다. sse.ts가 1,444줄로 큰 것은 이번 PR 목표가 “함수 통째 이동”이라서 예상된 결과다. 더 쪼개는 일은 round5 다음 카드로 미뤄도 된다.

라인 - tests/fixtures/file-size-baseline.json ("src/bridge.ts") - #4671 착지 후 dev와 충돌. 값은 7로 맞추고 rebase 필요.
devlog/_plan/260915_godfile_round5/020_bridge.md 이동표(ownedBudget → sse) - 구현은 src/bridge/internal.tsexport let ownedBudgetAbandonedMs + live import. 동작은 맞음. 문서만 어긋남.
src/bridge/internal.ts (export let ownedBudgetAbandonedMs) - 공개 mutable 바인딩. 테스트 세터·SSE 워치독용이라 필요하지만, 앞으로 이 심볼을 다른 리프가 복사 import하지 않도록 유지해야 함.
src/bridge/sse.ts (전체 ~1444줄) - 순수 이동 결과로 여전히 큼. 이번 머지 차단 사유는 아님. 후속 분해 후보로만 기억.
mergeStateStatus CONFLICTING / DIRTY - 충돌 해소 전 merge 버튼 금지. 공개 export 표면은 origin/dev와 동일해서 소비자 쪽 추가 수정은 없어 보임.

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

너의 추천
close-don't-rebase 대상이 아니다. #4671 다음 정답 카드다. dev에 rebase해서 baseline의 src/bridge.ts만 7로 맞추고, CI 전체(특히 실패했던 test shard)가 초록이면 머지. 계획서 ownedBudget 줄은 짧게 고치거나 이탈 메모만 남겨도 충분하다. 공개 import 경로는 건드리지 말 것.

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

src/bridge.ts was 2,206 lines, and almost all of it was two functions:
bridgeToResponsesSSE at 1,387 lines and buildResponseJSONWithBudget at 562.
Both move whole; neither body changes.

Four leaves under src/bridge/:

  errors.ts           34  formatErrorResponse
  internal.ts        174  shared helpers, the owned-budget state, output types
  response-json.ts   624  buildResponseJSON and buildResponseJSONWithBudget
  sse.ts           1,444  bridgeToResponsesSSE plus its two private helpers

The facade is 7 lines of re-exports and keeps all six public names.

Placement came from counting each helper's uses per region rather than guessing.
sseEvent and responseError are read only inside the SSE function, so they travel
with it. adapterFailureFromEvent is read from both the SSE and JSON paths, so it
goes to internal.ts.

The mutable module state needed care. `ownedBudgetAbandonedMs` is a module-level
`let` mutated by setOwnedBudgetAbandonedMsForTests and read from inside the SSE
function, which now lives in a different file. The declaration, its default
constant and the setter all stay in internal.ts, and sse.ts imports the binding
rather than copying the value, so the ES live binding still shows a test-set
value. Copying it into a local or re-exporting a snapshot would have silently
frozen the watchdog delay at ten minutes.

tests/responses/responses-undeclared-tool-guard.test.ts repoints its comment
reference for declaredToolNames to the leaf that holds it.

Ratchet cap lowered from 2,206 to 7.
structure/manifest.json graces src/bridge.ts because no doc names that path; the
leaves moved out of it inherit exactly that situation, and structure:check only
saw the new directory once it was tracked. Regenerated structure/INDEX.md.
@lidge-jun
lidge-jun force-pushed the codex/godfile-r5-b-bridge branch from ba445be to 3c6cf21 Compare September 15, 2026 01:49
…aves

tests/lib/reasoning-replay-scope-source.test.ts reads bridge source as text and
pins two declarations of `const replayCacheScope = options?.replayCacheScope;`.
After the facade split one lives in src/bridge/sse.ts and the other in
src/bridge/response-json.ts, so reading the facade matched nothing and the
assertion failed on null. Read both leaves and keep the count at 2.

This oracle was missed when the split was planned. The audit searched tests/ for
the literal `src/bridge.ts`, but this test composes the path from a relative
fragment: `repoPath("src", ...relative.split("/"))` called with `"bridge.ts"`.
A literal search cannot see that. The replacement check resolves every string
literal in a test that reads files, against the real src tree, which finds the
composed form too.
@lidge-jun

Copy link
Copy Markdown
Owner Author

Maintainer integration record

Integrating into dev under the MAINTAINERS.md maintainer-integration clause (lines 59-64), recording the choice and the exact-head verification as that clause requires. This is maintainer integration, not a self-approval or an independent review.

Exact head verified: 3646c6ece0e3cbc5b18c7b823dc14f5cab37edd3

CI at that head: every non-skipped check reports SUCCESS, including test 1/4 through 4/4, gates, macos 1/2 and 2/2, enforce-target, hygiene, react-doctor, storage policy, api usage, keyring on all three platforms, docker smoke and npm-global on all three. mergeable: MERGEABLE.

One real failure was found and fixed on this branch, which is worth recording. The first push failed test 2/4 and macos 2/2 on reasoning replay scope propagation > bridge, adapter, and cache contain no process-wide fallback with Received value does not have a length property: null. tests/lib/reasoning-replay-scope-source.test.ts reads bridge source as text and pins two occurrences of const replayCacheScope = options?.replayCacheScope;; after the split one lives in src/bridge/sse.ts and the other in src/bridge/response-json.ts, so reading the facade matched nothing.

The planning audit missed it because it searched tests/ for the literal src/bridge.ts, and that test composes the path: repoPath("src", ...relative.split("/")) called with "bridge.ts". A literal search cannot see a composed path. The check that replaced it resolves every string literal in a file-reading test against the real src tree, which finds the composed form; run against this branch it reports exactly the two leaf paths the fixed oracle now reads, and nothing else for src/bridge.

That is the failure mode this round was most worried about — an oracle that reads source as text and goes quiet when the content moves — and it is why the split PRs are verified against hosted CI rather than a local proof alone.

Security review: not applicable. No authentication, credential, OAuth, workflow, release-automation or dependency-installation path is touched.

Outstanding maintainer change requests: none.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant