Skip to content

fix(api): address PR #972 review findings for release readiness#974

Merged
otomatty merged 3 commits into
developfrom
fix/pr-972-release-readiness
May 25, 2026
Merged

fix(api): address PR #972 review findings for release readiness#974
otomatty merged 3 commits into
developfrom
fix/pr-972-release-readiness

Conversation

@otomatty

@otomatty otomatty commented May 25, 2026

Copy link
Copy Markdown
Owner

概要

PR #972(develop → main リリース)のレビュー・CI で検出された正当な問題を修正します。主因はマイグレーション FK が存在しない "users" テーブルを参照していたことによる deploy-dev 失敗です。

変更点

server/api/drizzle/

  • 0031 / 0032 の FK を "users"("id")"user"("id") に修正(Better Auth の実テーブル名に合わせる)

server/api/src/agents/core/

  • BYOK 検証: 静的 env モデル provider との事前照合を削除し、credential 存在チェックのみに簡素化。model-less graph(wiki-maintenance)はスキップ
  • web search model 選択: 最安コスト行の中でのみ OpenAI を優先(常に OpenAI を選んでしまうバグを修正)
  • Postgres checkpointer: setup() 失敗時に setupOnce をリセットして再試行可能に
  • zediChatModel: ストリーム abort 時も usage 記録するよう try/finally
  • SSE types: SseComposePhaseEvent / SseComposeSectionEvent を barrel export に追加

server/api/src/agents/runner/

  • mapComposePhase"conflict" フェーズを追加

server/api/src/routes/

  • composeSessionProjection: checkpoint phase: "completed" を正しく "completed" に投影

server/api/src/agents/subgraphs/research/

  • researchResumeSchema: approvedSourceIds を必須化、approved/rejected の重複を拒否

server/api/src/services/

  • wikiSearchService: limit の NaN ガード、updated_at の Date 正規化

変更の種類

  • 🐛 バグ修正 (Bug fix)
  • ✨ 新機能 (New feature)
  • 💥 破壊的変更 (Breaking change)
  • 📝 ドキュメント (Documentation)
  • 🎨 スタイル/リファクタリング (Style/Refactor)
  • 🧪 テスト (Tests)
  • 🔧 ビルド/CI (Build/CI)

テスト方法

  • cd server/api && bun run test:run -- src/__tests__/agents/core/composeBackendValidation.test.ts src/__tests__/routes/composeSessionProjection.test.ts src/__tests__/agents/runner/sseMapper.test.ts src/__tests__/services/wikiSearchService.test.ts src/__tests__/agents/subgraphs/research/researchGraph.resume.test.ts src/__tests__/agents/core/llm/zediChatModel.test.ts
  • CI green を確認
  • マージ後、develop へ取り込み → PR Release: develop を main にマージ #972 を更新

チェックリスト

  • テストがすべてパスする(対象ファイル)
  • Lint エラーがない(pre-commit 通過)
  • 必要に応じてドキュメントを更新した
  • コミットメッセージが Conventional Commits に従っている

関連 Issue / PR

Related to #972

レビューコメント対応メモ(対応しないもの)

コメント 判断
CI action SHA pin CI ワークフロー変更は本 PR スコープ外(babysit 方針)
E2E waitForTimeout 置換 大規模 refactor。現状 CI E2E は green
SECURITY.ja.md placeholder 実メールアドレスは運用判断が必要
ingest タイムアウト アーキテクチャ改善。別 Issue 推奨

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added wiki compose sessions tracking.
    • Enabled user AI credential storage for BYOK.
    • Accept "conflict" as a valid compose-phase event.
  • Bug Fixes

    • Prevented checkpointer setup from permanently failing after an error.
    • Ensured streaming path records usage/costs correctly and skips billing on incomplete streams.
  • Improvements

    • Stricter web-search model selection for cheapest cost tie and improved wiki search normalization.
    • Added cross-field validation to research resume inputs.
  • Tests

    • Expanded tests for streaming usage, credential checks, SSE mapping, and compose projections.

Review Change Stack

Correct migration FK targets (`user` not `users`), unblock BYOK session
creation, and harden compose projection, SSE mapping, and resume validation.

Co-authored-by: Cursor <cursoragent@cursor.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@otomatty, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 5 reviews of capacity. Refill in 48 minutes and 26 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4ab2d4cd-3409-4010-aa8f-0314dd1068cd

📥 Commits

Reviewing files that changed from the base of the PR and between d7e7488 and 37ee8b7.

📒 Files selected for processing (1)
  • server/api/src/agents/core/tools/resolveWebSearchModel.ts
📝 Walkthrough

Walkthrough

Refactors compose backend pre-flight checks to use stored BYOK credentials rather than per-model provider validation; allows compose-phase "conflict" SSE events; changes streaming usage accounting to post-process accumulated text; updates schema FKs and supporting validation/service normalization.

Changes

Compose Validation & Event Handling

Layer / File(s) Summary
Database Schema Migration
server/api/drizzle/0031_add_wiki_compose_sessions.sql, server/api/drizzle/0032_add_user_ai_credentials.sql
Foreign key references updated from "users"("id")"user"("id").
Backend Validation & Credential Handling
server/api/src/agents/core/composeBackendValidation.ts, server/api/src/__tests__/agents/core/composeBackendValidation.test.ts
assertComposeBackendReady removed per-model validateModelAccess checks; it now fetches modelIds, returns early for model-less graphs, derives expected provider for BYOK backends, and checks stored credentials. Tests updated to remove provider-mismatch scenario and to assert credential lookup behavior and model-less skip.
SSE Conflict Phase Support
server/api/src/agents/runner/sseMapper.ts, server/api/src/agents/core/types/index.ts, server/api/src/__tests__/agents/runner/sseMapper.test.ts
mapComposePhase now accepts phase: "conflict". Barrel exports SseComposePhaseEvent and SseComposeSectionEvent. New test verifies mapping of compose_phase conflict events to typed SSE payload.
Stream Accounting & Tests
server/api/src/agents/core/llm/zediChatModel.ts, server/api/src/__tests__/agents/core/llm/zediChatModel.test.ts
Streaming post-processing moved after provider iteration: token estimation from accumulated text, conditional DB usage recording only when stream ended with done=true, fallback to local cost calc on billing failure, and tests asserting no usage is recorded for incomplete or errored streams.
Projection, Schema & Service Fixes
server/api/src/routes/composeSessionProjection.ts, server/api/src/__tests__/routes/composeSessionProjection.test.ts, server/api/src/agents/subgraphs/research/resumeSchema.ts, server/api/src/agents/core/checkpoint/postgresCheckpointer.ts, server/api/src/agents/core/tools/resolveWebSearchModel.ts, server/api/src/services/wikiSearchService.ts
projectComposeStateValues special-cases "completed" prefixes for interrupted-phase fallback. researchResumeSchema adds superRefine to reject overlapping approved/rejected IDs and makes approvedSourceIds required. Postgres checkpointer clears cached setup on failure to allow retries. Web search model selection filters to cheapest cost tie. Wiki search normalizes limit and serializes updated_at to ISO strings.

Sequence Diagram

sequenceDiagram
  participant Client
  participant assertComposeBackendReady
  participant FetchModelIDs
  participant UserAIStore
  Client->>assertComposeBackendReady: request pre-flight check for graphId
  assertComposeBackendReady->>FetchModelIDs: fetch modelIds for graphId
  alt No models configured
    FetchModelIDs-->>assertComposeBackendReady: empty -> return early
  else Models present
    assertComposeBackendReady->>UserAIStore: derive expectedProvider & fetch credential for user
    UserAIStore-->>assertComposeBackendReady: credential or null
    alt Credential missing
      UserAIStore-->>assertComposeBackendReady: respond with HTTPException(400)
    else Credential found
      UserAIStore-->>assertComposeBackendReady: success -> allow compose
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • otomatty/zedi#959: Related to compose_phase SSE event handling and mapper changes.
  • otomatty/zedi#956: Related to wiki-compose research loop and resume schema interactions.

Poem

A rabbit taps the migration trace,
Swaps users to user in place,
BYOK checks keep the compose race,
Streams tally tokens, finish with grace,
Conflicts now hop into place. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. 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 clearly relates to the main objective: fixing issues from PR #972 review findings for release readiness, addressing database migration and validation problems.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pr-972-release-readiness

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request modifies database foreign keys, refines BYOK backend validation, and wraps chat model streaming in a try...finally block for reliable usage recording. It also improves web search model selection, adds schema validation for research sources, and updates SSE mapping. Feedback highlights a risk of error masking in the usage recording logic and identifies redundant conditional checks in model resolution and session projection.

Comment on lines +323 to +332
const usage = await recordZediUsage({
db: this.db,
userId: this.userId,
modelId: this.modelRowId,
feature: this.feature,
usage: { inputTokens, outputTokens },
inputCostUnits: this.inputCostUnits,
outputCostUnits: this.outputCostUnits,
apiMode: this.apiMode,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

finally ブロック内で recordZediUsage を await していますが、この処理が失敗した場合に try ブロックで発生した元のエラーが上書き(マスク)されてしまう可能性があります。利用状況の記録は重要ですが、ここでの例外が原因で最終的なチャンクの yield や後続のクリーンアップが阻害されるのを防ぐため、recordZediUsage を try-catch で囲んでエラーをハンドル(ログ出力など)することを検討してください。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

対応しました。finally を廃止し、done === true 時のみ recordZediUsage を呼びます。recordZediUsage 自体は try/catch で囲み、失敗時は console.error のうえ推定トークンで最終チャンクを返します(成功ストリームの例外マスクを防止)。

Comment on lines +96 to +97
const first = rows[0];
if (!first) return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

直前の 94 行目で rows.length === 0 の場合に null を返しているため、この if (!first) チェックは冗長です。コードを簡潔にするために削除を検討してください。

References
  1. 簡潔で意図が明確なコードを推奨します。 (link)
  2. 不要な条件分岐や到達不能なコードを削除することで、コードの可読性と保守性を向上させます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

対応しました。rows.length === 0 の直後に rows[0] を直接参照する形に整理し、冗長な if (!first) を削除しました。

if (typeof state.phase === "string" && projection.phase === undefined) {
projection.phase = phaseFromSessionRow(state.phase, "interrupted");
projection.phase =
state.phase.startsWith("completed") || state.phase === "completed"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

state.phase.startsWith("completed") が真であれば state.phase === "completed" の条件も満たされるため、後者のチェックは冗長です。コードを簡潔にするために削除を検討してください。

References
  1. 冗長な論理演算を避けることを推奨します。 (link)
  2. 論理的に冗長な条件式を整理することで、コードの意図をより明確にします。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

対応しました。startsWith("completed") のみに簡略化しました。

@otomatty otomatty self-assigned this May 25, 2026

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

ℹ️ 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 on lines +323 to +327
const usage = await recordZediUsage({
db: this.db,
userId: this.userId,
modelId: this.modelRowId,
feature: this.feature,

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 Skip usage billing when streaming call errors

recordZediUsage now runs unconditionally in finally, so it executes even when the provider stream throws before completion; this is reachable because streamOpenAI/streamAnthropic/streamGoogle throw on non-2xx responses in server/api/src/services/aiProviders.ts. In those failure paths the request fails but we still charge estimated prompt tokens, which can inflate usage/budget accounting and bill failed calls; billing should be limited to successful completion (or a narrowly-detected abort case).

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

対応しました。finally による無条件課金をやめ、routes/ai/chat.ts と同様に done === true のときだけ recordZediUsage を呼ぶようにしました。プロバイダ例外は for await からそのまま伝播するため課金コードに到達しません。incomplete 終了は usage メタデータのみ返し DB 課金しません。テスト does not record usage when the provider stream throws を追加済みです。

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
server/api/src/__tests__/routes/composeSessionProjection.test.ts (1)

66-85: ⚡ Quick win

Add a spec case for prefixed completed phases.

The implementation now treats completed* values as completed, but this test only locks in the exact "completed" string. Add one case for a prefixed phase so that behavior is part of the spec too.

🧪 Suggested test
   it("projects completion markdown from checkpoint values", () => {
     const projection = projectComposeStateValues({
       phase: "completed",
       completion: {
         markdown: "## A\n\nBody",
@@
     expect(projection.completedMarkdown).toBe("## A\n\nBody");
     expect(projection.draftedSections).toHaveLength(1);
     expect(projection.phase).toBe("completed");
   });
+
+  it("maps prefixed completed phases to completed", () => {
+    const projection = projectComposeStateValues({
+      phase: "completed:await_persist",
+    });
+    expect(projection.phase).toBe("completed");
+  });

As per coding guidelines, **/*.{test,spec}.{ts,tsx,js,jsx}: Tests serve as a source of truth for specifications alongside implementation code TSDoc/JSDoc.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/api/src/__tests__/routes/composeSessionProjection.test.ts` around
lines 66 - 85, Add a test asserting that phases that start with "completed" are
treated as completed: update the test using projectComposeStateValues by adding
a case where the input phase is a prefixed string (e.g., "completed-foo" or
"completedWithNotes") with the same completion object and assert
projection.completedMarkdown, projection.draftedSections.length, and
projection.phase are the expected completed values (use the same assertions as
the existing "projects completion markdown from checkpoint values" test but with
the prefixed phase).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/api/src/agents/core/composeBackendValidation.ts`:
- Around line 29-31: Update the TSDoc/JSDoc in composeBackendValidation.ts (the
top-level doc for the composeBackendValidation function / createZediChatModel
related functions) so it no longer claims static model-provider compatibility is
validated here; instead state in English and Japanese that this module only
checks credential existence and that provider matching for BYOK runs is enforced
at runtime via resolveComposeModelId, and add a Japanese translation of the
nearby inline comment ("Model-less graphs... Provider matching...") so both
languages mirror the inline note.

In `@server/api/src/agents/core/llm/zediChatModel.ts`:
- Around line 294-296: The new inline comments (e.g., the one starting "Surface
incremental tokens to LangChain callback consumers..." and the other comments
around the same block referenced at lines ~316 and ~334) are English-only;
update them to be bilingual by adding equivalent Japanese translations alongside
the existing English text while preserving meaning and tone and keeping the
comments adjacent to the same code (references: the comment that mentions
`streamEvents` and "LangChain callback consumers" and the nearby comment
blocks). Ensure formatting matches existing TS comment style used across the
repo (both languages in the same comment block).
- Around line 315-332: The finally block currently awaits recordZediUsage(...)
which can reject and interfere with the primary stream error; wrap the
recordZediUsage call in its own try/catch inside the finally so any rejection is
caught and only logged (e.g., this.logger.error(...) or console.error) and not
re-thrown, ensuring the original generator error propagates; keep using
promptLength/zediMessages/accumulated/inputTokens/outputTokens as computed but
move the await into the guarded try/catch. Also update the inline comments
around the finally/usage calculation (references to lines near zediMessages,
promptLength and recordZediUsage) to include both English and Japanese
translations per the bilingual guideline.

---

Nitpick comments:
In `@server/api/src/__tests__/routes/composeSessionProjection.test.ts`:
- Around line 66-85: Add a test asserting that phases that start with
"completed" are treated as completed: update the test using
projectComposeStateValues by adding a case where the input phase is a prefixed
string (e.g., "completed-foo" or "completedWithNotes") with the same completion
object and assert projection.completedMarkdown,
projection.draftedSections.length, and projection.phase are the expected
completed values (use the same assertions as the existing "projects completion
markdown from checkpoint values" test but with the prefixed phase).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dcfcf33d-df4b-4a51-9c7e-32dabcd47fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 71e2b62 and e422fe5.

📒 Files selected for processing (14)
  • server/api/drizzle/0031_add_wiki_compose_sessions.sql
  • server/api/drizzle/0032_add_user_ai_credentials.sql
  • server/api/src/__tests__/agents/core/composeBackendValidation.test.ts
  • server/api/src/__tests__/agents/runner/sseMapper.test.ts
  • server/api/src/__tests__/routes/composeSessionProjection.test.ts
  • server/api/src/agents/core/checkpoint/postgresCheckpointer.ts
  • server/api/src/agents/core/composeBackendValidation.ts
  • server/api/src/agents/core/llm/zediChatModel.ts
  • server/api/src/agents/core/tools/resolveWebSearchModel.ts
  • server/api/src/agents/core/types/index.ts
  • server/api/src/agents/runner/sseMapper.ts
  • server/api/src/agents/subgraphs/research/resumeSchema.ts
  • server/api/src/routes/composeSessionProjection.ts
  • server/api/src/services/wikiSearchService.ts

Comment thread server/api/src/agents/core/composeBackendValidation.ts
Comment thread server/api/src/agents/core/llm/zediChatModel.ts Outdated
Comment thread server/api/src/agents/core/llm/zediChatModel.ts Outdated
Bill streaming usage only on successful done chunks, skip DB writes on
provider errors and incomplete streams, and sync bilingual TSDoc.

Co-authored-by: Cursor <cursoragent@cursor.com>
@otomatty

Copy link
Copy Markdown
Owner Author

レビューコメントへの対応をコミットしました(d7e7488a)。

  • ストリーミング課金: done 到達時のみ DB 課金。プロバイダ例外時は課金せず、incomplete はメタデータのみ
  • recordZediUsage 失敗は console.error で握り、成功ストリームをマスクしない
  • TSDoc / インラインコメントを日英併記に更新
  • 冗長な条件分岐を整理

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown

@otomatty ありがとうございます!コミット d7e7488 の変更内容を確認しました。以下の対応が適切に行われていると認識しています:

  • 課金ロジックの修正: done 到達時のみ DB 課金、プロバイダ例外時は課金しない、incomplete はメタデータのみ記録
  • エラーハンドリング: recordZediUsage 失敗を console.error で握り、成功ストリームをマスクしない設計
  • コードの整理: TSDoc・インラインコメントの日英併記対応、冗長な条件分岐の削除

PR全体のレビューを実施します。

🐇 ✨

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Co-authored-by: Cursor <cursoragent@cursor.com>
@otomatty otomatty merged commit eeb934c into develop May 25, 2026
18 checks passed
@otomatty otomatty deleted the fix/pr-972-release-readiness branch May 25, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant