Skip to content

feat(api): REST API (server/api, Hono) を Cloudflare Workers ランタイム対応にする (#1091) - #1123

Merged
otomatty merged 11 commits into
developfrom
feature/1091
Jul 28, 2026
Merged

feat(api): REST API (server/api, Hono) を Cloudflare Workers ランタイム対応にする (#1091)#1123
otomatty merged 11 commits into
developfrom
feature/1091

Conversation

@otomatty

@otomatty otomatty commented Jul 18, 2026

Copy link
Copy Markdown
Owner

概要

Railway → Cloudflare 移行(#1088)Phase 2b として、server/api(Hono REST API)を Cloudflare Workers で実行可能にする。Node/Railway 側の挙動は不変(並行稼働維持)。DoD は「Worker ランタイム適合 + 非 DB 経路の dev 検証」— DB 依存経路の検証は #1090(D1)後、本番切替・prod ワークフローはスコープ外。

設計・要件は AI-DLC ワークフロー記録(aidlc/spaces/default/intents/260718-api-workers-migration/、本 PR に同梱)が正本。

変更点

領域 主な変更
ルート分割 src/appAgents.ts 新設。LangGraph 依存(compose-sessions / ingest / graph 登録)を隔離し、createApp({ registerAgentRoutes }) フックで index.ts のみが配線。Worker では該当ルートは 404
clientIp src/lib/clientIpNode.ts 新設(conninfo 分離 + resolver 注入)。Workers では RUNTIME var ゲートで CF-Connecting-IP 第一候補(ヘッダ存在判定はしない = Node 経路の偽装ガード)
Sentry src/lib/sentryShared.ts(SDK 非依存の scrub/判定)+ errorHandler の capture DI 化 + Worker は @sentry/cloudflarewithSentry ラッパ(waitUntil flush 保証)
KvStore ioredis アダプタを src/lib/kv/createKvStoreNode.ts の注入式に分離(workerd でモジュールロード不能だったのを解消)
articleExtractor jsdom を遅延 import 化(Worker 起動ブロッカーを解消。clip 実行時のみ評価、失敗はマスク済み 5xx)
バンドル検査 scripts/checkWorkerBundle.tsworker:bundle:check): dry-run 出力に禁止依存 5 種(@langchain / src/agents / @hono/node-server / @sentry/node / ioredis)が無いこと + gzip 10MB 予算を fail-fast。資格情報不要
workerd テスト @cloudflare/vitest-pool-workers@0.18.6(vitest 4 対応)+ vitest.config.worker.tstest:worker)。health ランタイム判定 / CORS / ルート分割 / auth マウント / エラーマスキング / KV DO 3 用途 / @aws-sdk presign の 10 テスト
CI / 設定 ci.yml に bundle check + test:worker、deploy-api-worker-dev.yml にデプロイ直前 bundle check、wrangler.jsoncRUNTIME var、.env.worker.*.example に起動必須シークレット全記載
AI-DLC 記録 13 ステージの要件・設計・レビュー判定・運用ドキュメント一式 + 承認済みチームプラクティス

変更の種類

  • ✨ 新機能 (New feature)
  • 🧪 テスト (Tests)
  • 🔧 ビルド/CI (Build/CI)
  • 📝 ドキュメント (Documentation)

テスト方法

  1. cd server/api && bun install --frozen-lockfile
  2. bunx vitest run — Node スイート 154 ファイル / 1703 テスト green(Railway 側パリティ)
  3. bun run test:worker — workerd 実機テスト 10 件 green
  4. bun run worker:bundle:check — 禁止依存ゼロ、gzip 3.06MB / 10MB(資格情報不要)
  5. bunx tsc --noEmit — 型チェック green
  6. (資格情報がある場合)bun run worker:dev でローカル workerd 起動 → /api/health"runtime":"cloudflare-workers" を返すこと

チェックリスト

  • テストがすべてパスする(server/api: 1703 + 10。※src/lib/storageAdapter/IndexedDBStorageAdapter.integration.test.ts はローカルで failing だが origin/develop でも同一再現の既存問題で本 PR と無関係)
  • Lint エラーがない(0 errors)
  • 必要に応じてドキュメントを更新した(AI-DLC 記録 + env example)
  • コミットメッセージが Conventional Commits に従っている

スクリーンショット(UI 変更がある場合)

UI 変更なし。

関連 Issue

Closes #1091
Related to #1088, #1090, #1095

補足:

  • dev 実機検証の残作業: CI の CLOUDFLARE_API_TOKEN 再発行(直近の deploy-api-worker-dev.yml は認証エラーで failure 継続中)と、リポジトリ変数 WORKER_API_BASE_URL の設定(未設定のため CI health-poll がスキップされ続けている)。いずれも本 PR のコードとは独立に対応可能。
  • 既知の副作用への対処: 現行 develop の Worker バンドルは ioredis / jsdom をモジュールロードで評価するため実リクエストで起動不能の可能性が高い(workerd テストが暴露)。本 PR で解消。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added Cloudflare Workers runtime support for the API, including runtime-aware client IP handling and Durable Object–backed KV behavior.
    • Enabled Sentry error reporting for Workers with sensitive-data scrubbing (disabled when DSN isn’t set).
    • /api/health now returns the Worker runtime identifier.
  • Bug Fixes
    • Improved error handling by consistently masking internal failures with safe 5xx responses.
  • CI/CD & Reliability
    • Added fail-fast CI checks for Worker bundles and workerd-based runtime tests before dev deploy.
  • Tests
    • Expanded automated Worker runtime coverage (health/CORS/auth routing, KV/DO, presigned URLs, error masking).
  • Documentation
    • Updated Worker secret setup and example environment files/guides.

otomatty and others added 2 commits July 18, 2026 18:41
Phase 2b of the Railway -> Cloudflare migration (#1088). Makes server/api
runtime-compatible with Workers while keeping Railway/Node behavior intact:

- Split LangGraph agent routes into appAgents.ts; the Worker entry no longer
  bundles @langchain/* / src/agents (createApp registerAgentRoutes hook)
- clientIp: CF-Connecting-IP first on Workers (gated by the explicit RUNTIME
  var, never header sniffing); Node socket fallback moved to clientIpNode.ts
- Sentry: runtime-neutral scrub helpers in sentryShared.ts, capture DI in
  errorHandler, @sentry/cloudflare withSentry wrapper on the Worker entry
- KvStore: ioredis adapter moved behind createKvStoreNode.ts injection
- articleExtractor: jsdom loaded lazily so the Worker module graph boots
- worker:bundle:check: credential-free dry-run guard (5 forbidden markers,
  gzip size budget) wired into ci.yml and the dev deploy workflow
- test:worker: workerd runtime tests via @cloudflare/vitest-pool-workers
  (health runtime field, CORS, route split, auth mount, masked errors,
  KV DO counters/one-time codes/deny-list, @aws-sdk presign)
- wrangler.jsonc: RUNTIME var; .env.worker examples list all boot-required
  secrets (auth vars and placeholder DATABASE_URL until #1090)

DB-backed success paths remain unverifiable until #1090 (D1) by design;
they must return masked 5xx instead of Worker runtime errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tices

- Intent record 260718-api-workers-migration: requirements, NFR
  requirements/design, infrastructure design, CI/CD and operation artifacts
  (13 stages, infra scope), including reviewer verdicts and stage diaries
- Promote affirmed team practices to aidlc/spaces/default/memory
  (team.md sections + project.md mandated/forbidden rules and learnings)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adapts server/api for Cloudflare Workers by separating agent and Node-only modules, adding Worker Sentry and runtime-aware storage/IP handling, introducing bundle and workerd tests, and updating CI, deployment configuration, environment examples, and migration documentation.

Changes

API Worker migration

Layer / File(s) Summary
Migration requirements and design contracts
aidlc/spaces/default/..., server/api/wrangler.jsonc
Defines Worker runtime boundaries, NFRs, bundle restrictions, deployment scope, observability, and the explicit RUNTIME variable.
Runtime-specific application wiring
server/api/src/app.ts, server/api/src/appAgents.ts, server/api/src/worker.ts, server/api/src/index.ts, server/api/src/lib/*, server/api/src/middleware/errorHandler.ts
Separates agent registration from the default Worker app, injects error capture, adds Worker Sentry wrapping, moves Node socket/KV wiring into Node-only modules, and lazy-loads jsdom.
Bundle, test, and CI validation
server/api/scripts/*, server/api/src/__tests__/*, server/api/vitest.config*, .github/workflows/*, server/api/package.json
Adds forbidden-marker and gzip-size validation, workerd Vitest execution, Worker runtime tests, package scripts, dependencies, and CI checks.
Deployment and operational records
aidlc/spaces/default/intents/260718-api-workers-migration/operation/*, server/api/.env.worker.*.example, .github/workflows/deploy-api-worker-dev.yml
Documents dev deployment ordering, environment state, health checks, rollback handling, observability, and Worker environment placeholders.

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

Possibly related PRs

  • otomatty/zedi#208 — Both changes update Worker/API authentication configuration around BETTER_AUTH_URL.
  • otomatty/zedi#908 — Shares Sentry event and request URL scrubbing logic.
  • otomatty/zedi#1118 — Shares the Redis-to-KvStore runtime wiring and Worker validation areas.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the API is being adapted for Cloudflare Workers and matches the main change set.
Linked Issues check ✅ Passed The changes cover the Workers migration, runtime-specific client IP/Sentry/KV handling, bundle checks, worker tests, and dev CI/deploy verification.
Out of Scope Changes check ✅ Passed No clear unrelated feature work stands out; the extra docs and memory logs support the migration effort.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/1091

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

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

ℹ️ 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 +1 to +2
import { describe, it, expect } from "vitest";
import { FORBIDDEN_MARKERS, findForbiddenMarkers } from "./checkWorkerBundle.lib.js";

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 Move server/api test out of scripts

For files under server/api, the root AGENTS.md placement table says tests must live under src/__tests__/ with the source tree mirrored, and colocated tests are forbidden; this new *.test.ts under server/api/scripts violates that documented layout. Please move it to the approved mirrored test location so future server/api tests remain discoverable and consistent with the project rule.

Useful? React with 👍 / 👎.

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

🧹 Nitpick comments (2)
server/api/src/__tests__/worker/kvDurableObject.test.ts (1)

23-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert that subsequent increments do not refresh the TTL.

This only verifies the count. A regression that renews the fixed-window expiry on every increment still passes; assert the remaining TTL decreases after a delayed second increment.

As per coding guidelines, “Mutation scoreを第一指標とする” and tests must serve as specifications rather than implementation mirrors.

🤖 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__/worker/kvDurableObject.test.ts` around lines 23 -
30, Extend the incrWithTtl test to verify fixed-window expiry behavior, not only
returned counts. After the first increment, record the key’s remaining TTL using
the store’s existing TTL inspection API, delay before the second increment, then
assert the remaining TTL has decreased rather than being refreshed. Keep the
existing count assertions intact.

Source: Coding guidelines

aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/monitoring-design.md (1)

17-18: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make CI assert the Worker runtime marker.

curl --fail only checks the HTTP status, so a stale or incorrect endpoint can pass without proving runtime: "cloudflare-workers". Parse the health response and fail unless that field matches; keep manual inspection as a supplement.

🤖 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
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/monitoring-design.md`
around lines 17 - 18, Update the CI health-check flow referenced by
deploy-api-worker-dev.yml so it parses the /api/health response and fails unless
the runtime field exactly equals "cloudflare-workers". Retain the existing HTTP
failure handling and manual dev verification as supplementary checks.
🤖 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 `@aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md`:
- Line 21: The tracked artifacts contain workstation-specific absolute paths
that must be removed. In
aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md:21,
replace the Project Root value with a repository-relative path or placeholder;
in
aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md:214,
225, 338, 349, 445, 520, 531, 594, and 605, redact each absolute Destination
path; and at lines 246 and 255, redact each absolute --project-dir path,
preserving the surrounding audit content.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md`:
- Line 53: Update the generated audit-log format around the Stage Start heading
so repeated headings such as Stage Completion, Stage Start, and Human Turn are
either made unique per occurrence or excluded through a narrowly scoped MD024
lint exception specific to this format.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/deployment-architecture.md`:
- Line 7: Update the fenced topology block in the deployment architecture
document by adding the text language identifier immediately after its opening
fence, preserving the block’s existing contents so it passes MD040.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/infrastructure-design-questions.md`:
- Line 15: Update the recorded answer in the infrastructure design questions
document to use plain text rather than Markdown reference-definition syntax:
replace the bracketed “Answer” label with an unbracketed label while preserving
the approval value and timestamp.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/shared-infrastructure.md`:
- Line 9: Update the BETTER_AUTH_SECRET entry in the shared infrastructure
design so deployment preflight enforces parity across api, mcp, hocuspocus, and
the Worker. Require either one shared secret-manager version or comparison of
non-reversible fingerprints, and ensure secret values are never logged.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/nfr-design-questions.md`:
- Line 21: Update the answer entry in the NFR design questions document to use
plain text rather than Markdown link-reference syntax, removing the brackets
around “Answer” while preserving the approval value and date.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/reliability-requirements.md`:
- Around line 10-13: Define one enforceable client-safe 5xx response contract in
reliability-requirements.md lines 10-13, requiring normalized sanitized
responses for all internal failures while sending detailed exceptions only to
Sentry or logging. Apply the same contract requirement in
security-requirements.md lines 10-13, and align the implementation guidance in
tech-stack-decisions.md line 43 so it does not expose err.message to clients.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/tech-stack-decisions.md`:
- Around line 36-43: Update the audit findings in the Findings section to remove
or mark resolved the RL-5 and PR-4 findings, since the requirements now
distinguish CI HTTP checks from manual runtime validation and require observing
CPU errors during dev verification. Keep an explicit unresolved finding for the
PR-1 checklist gap, identifying who performs the manual parity check and through
which path, and retain its dependency on resolving C-2 credentials.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/ci-config.md`:
- Around line 9-10: Align the Worker-test CI records with the resolved
compatibility decision in tech-stack-decisions.md: in
aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/ci-config.md
lines 9-10, remove the undecided fallback wording or document the selected
fallback; in
aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/ci-pipeline-questions.md
lines 3-5, update the “no questions” record to reflect the finalized CI
contract.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/deployment-execution-questions.md`:
- Line 5: Synchronize the deployment records with the current PR state: update
the implementation-status statement in deployment-execution-questions.md at line
5 and deployment-log.md at line 7 to remove or clearly mark the stale
“implementation not yet present” claim, so the rollout is not incorrectly
deferred.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/health-check-report.md`:
- Around line 7-9: Update the deployment health validation so a missing
WORKER_API_BASE_URL cannot silently skip the health gate or report success.
Configure the workers.dev URL before enabling the rollout, or make the
validation job fail explicitly when the variable is unset, while preserving the
/api/health polling behavior when it is configured.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/smoke-test-results.md`:
- Around line 5-12: Update the implementation status in the smoke-test results
table to indicate that the Worker implementation and validation files are
complete, while preserving the separate status that deployment and manual dev
verification have not yet occurred.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/cd-config.md`:
- Around line 7-14: Specify a language on the fenced Markdown block containing
the deployment pipeline diagram, using text (or another appropriate non-code
language) while preserving its contents unchanged.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/environment-inventory.md`:
- Around line 26-28: Update the “ローカル wrangler” entry in the environment
inventory to reflect that authentication was corrected, removing the stale
wrong-account/C-2 status. Keep the “CI トークン(CLOUDFLARE_API_TOKEN)” entry marked
as unresolved, and leave the Cloudflare MCP entry unchanged.

In
`@aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/validation-report.md`:
- Around line 9-11: Remove the operator email address and Cloudflare account
identifier from the deployment evidence in validation-report.md lines 9-11.
Apply the same redaction to the duplicated identifiers in deployment-log.md
lines 13-16, preserving the remaining validation and deployment details.

In `@server/api/src/__tests__/worker/worker.test.ts`:
- Around line 70-76: Update the test named “responds to /api/auth/get-session as
well-formed JSON (no workerd crash)” to verify its promised response shape:
assert an application/json content type and successfully parse the response body
as JSON, while retaining the existing status-range and non-rejection checks.

In `@server/api/src/lib/sentryShared.ts`:
- Around line 101-120: Update scrubSentryEvent to recursively scrub request
query strings and contexts: make the query_string handling pass object values
through scrubDeep while preserving string handling via scrubQueryStringField,
and replace shallow contexts scrubbing with scrubDeep. Keep the existing
behavior for other request, user, and extra fields unchanged.

In `@server/api/src/middleware/errorHandler.ts`:
- Line 42: Sanitize attacker-controlled request and error values before the
console.error calls in the error handler’s branches, including c.req.path,
err.message, and the thrown error at the referenced logging sites. Remove or
escape CR/LF characters, or use a structured logger that safely encodes these
fields, while preserving the existing method, path, status, and error context.
- Around line 40-49: Update the HTTPException handling branch to return the
generic “Internal server error” message for all 5xx statuses, while preserving
err.message for non-5xx responses. Keep the existing logging, capture behavior,
and status code unchanged.

In `@server/api/src/services/articleExtractor.ts`:
- Around line 12-20: The lazy loadJsdom import still allows the Node-only jsdom
dependency to be reached through the shared /api/clip Worker route. Remove this
dependency from the Worker execution path by routing extractArticleFromUrl to a
Worker-compatible parser or excluding the route from Worker deployment, and add
a contract test confirming the Worker path does not load or invoke jsdom.

---

Nitpick comments:
In
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/monitoring-design.md`:
- Around line 17-18: Update the CI health-check flow referenced by
deploy-api-worker-dev.yml so it parses the /api/health response and fails unless
the runtime field exactly equals "cloudflare-workers". Retain the existing HTTP
failure handling and manual dev verification as supplementary checks.

In `@server/api/src/__tests__/worker/kvDurableObject.test.ts`:
- Around line 23-30: Extend the incrWithTtl test to verify fixed-window expiry
behavior, not only returned counts. After the first increment, record the key’s
remaining TTL using the store’s existing TTL inspection API, delay before the
second increment, then assert the remaining TTL has decreased rather than being
refreshed. Keep the existing count assertions intact.
🪄 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: d3352752-19bc-466c-9912-f92cfeef02f7

📥 Commits

Reviewing files that changed from the base of the PR and between 079c95e and c48d6ef.

⛔ Files ignored due to path filters (1)
  • server/api/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (95)
  • .github/workflows/ci.yml
  • .github/workflows/deploy-api-worker-dev.yml
  • aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/cicd-pipeline.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/deployment-architecture.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/infrastructure-design-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/infrastructure-services.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/monitoring-design.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/shared-infrastructure.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/logical-components.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/nfr-design-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/performance-design.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/reliability-design.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/scalability-design.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-design/security-design.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/nfr-requirements-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/performance-requirements.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/reliability-requirements.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/scalability-requirements.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/security-requirements.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/nfr-requirements/tech-stack-decisions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/ci-config.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/ci-pipeline-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/ci-pipeline/quality-gates.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/infrastructure-design/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/nfr-design/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/construction/nfr-requirements/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/discovered-rules.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/evidence.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/practices-discovery-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/practices-discovery-timestamp.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/practices-discovery/team-practices.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/requirements-analysis/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/requirements-analysis/requirements-analysis-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/inception/requirements-analysis/requirements.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/deployment-execution-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/deployment-log.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/health-check-report.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-execution/smoke-test-results.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/cd-config.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/deployment-pipeline-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/deployment-strategy.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/deployment-pipeline/rollback-runbook.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/environment-inventory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/environment-provisioning-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/environment-provisioning/validation-report.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/alarms.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/anomaly-config.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/dashboards.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/log-queries.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/memory.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/observability-setup-questions.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/slo-config.md
  • aidlc/spaces/default/intents/260718-api-workers-migration/operation/observability-setup/tracing-config.md
  • aidlc/spaces/default/intents/intents.json
  • aidlc/spaces/default/memory/project.md
  • aidlc/spaces/default/memory/team.md
  • server/api/.env.worker.dev.example
  • server/api/.env.worker.production.example
  • server/api/package.json
  • server/api/scripts/checkWorkerBundle.lib.test.ts
  • server/api/scripts/checkWorkerBundle.lib.ts
  • server/api/scripts/checkWorkerBundle.ts
  • server/api/src/__tests__/app.test.ts
  • server/api/src/__tests__/lib/clientIp.test.ts
  • server/api/src/__tests__/lib/kv/createKvStore.test.ts
  • server/api/src/__tests__/lib/sentryWorkerOptions.test.ts
  • server/api/src/__tests__/middleware/errorHandler.test.ts
  • server/api/src/__tests__/services/articleExtractor.test.ts
  • server/api/src/__tests__/worker/kvDurableObject.test.ts
  • server/api/src/__tests__/worker/presign.test.ts
  • server/api/src/__tests__/worker/worker.test.ts
  • server/api/src/app.ts
  • server/api/src/appAgents.ts
  • server/api/src/index.ts
  • server/api/src/lib/clientIp.ts
  • server/api/src/lib/clientIpNode.ts
  • server/api/src/lib/kv/createKvStore.ts
  • server/api/src/lib/kv/createKvStoreNode.ts
  • server/api/src/lib/sentry.ts
  • server/api/src/lib/sentryShared.ts
  • server/api/src/lib/sentryWorker.ts
  • server/api/src/lib/sentryWorkerOptions.ts
  • server/api/src/middleware/errorHandler.ts
  • server/api/src/services/articleExtractor.ts
  • server/api/src/worker.ts
  • server/api/vitest.config.ts
  • server/api/vitest.config.worker.ts
  • server/api/wrangler.jsonc

- **Test Strategy**: Standard

## Workspace State
- **Project Root**: C:\Users\saedg\apps\zedi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Remove workstation-specific absolute paths from the tracked artifacts.

  • aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md#L21-L21: replace C:\Users\saedg\apps\zedi with a repository-relative path or placeholder.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L214-L214: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L225-L225: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L246-L246: redact the absolute --project-dir path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L255-L255: redact the absolute --project-dir path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L338-L338: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L349-L349: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L445-L445: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L520-L520: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L531-L531: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L594-L594: redact the absolute Destination path.
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L605-L605: redact the absolute Destination path.
📍 Affects 2 files
  • aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md#L21-L21 (this comment)
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L214-L214
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L225-L225
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L246-L246
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L255-L255
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L338-L338
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L349-L349
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L445-L445
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L520-L520
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L531-L531
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L594-L594
  • aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md#L605-L605
🤖 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 `@aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md` at
line 21, The tracked artifacts contain workstation-specific absolute paths that
must be removed. In
aidlc/spaces/default/intents/260718-api-workers-migration/aidlc-state.md:21,
replace the Project Root value with a repository-relative path or placeholder;
in
aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md:214,
225, 338, 349, 445, 520, 531, 594, and 605, redact each absolute Destination
path; and at lines 246 and 255, redact each absolute --project-dir path,
preserving the surrounding audit content.


---

## Stage Start

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Resolve the repeated-heading lint failures.

This generated log reuses headings such as Stage Completion, Stage Start, and Human Turn, triggering the reported MD024 warnings. Either make headings unique or add a narrowly scoped lint exception for this audit-log format.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 53-53: Multiple headings with the same content

(MD024, no-duplicate-heading)

🤖 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
`@aidlc/spaces/default/intents/260718-api-workers-migration/audit/saedgewell-353e06d8a0b4.md`
at line 53, Update the generated audit-log format around the Stage Start heading
so repeated headings such as Stage Completion, Stage Start, and Human Turn are
either made unique per occurrence or excluded through a narrowly scoped MD024
lint exception specific to this format.

Source: Linters/SAST tools


## 構成トポロジ(本 Issue 完了時点)

```

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language identifier to the fenced topology block.

Use text (or another appropriate identifier) after the opening fence so the document passes the reported MD040 check.

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 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
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/deployment-architecture.md`
at line 7, Update the fenced topology block in the deployment architecture
document by adding the text language identifier immediately after its opening
fence, preserving the block’s existing contents so it passes MD040.

Source: Linters/SAST tools


Infrastructure Design 5成果物(READY)の承認ゲート。チャットに approve / request changes をタイプしてください。

[Answer]: approve (typed, 2026-07-18)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Avoid Markdown reference-definition syntax for the recorded answer.

[Answer]: approve ... is interpreted as an unused link reference and triggers MD053. Use plain text such as Answer: approve (typed, 2026-07-18).

🧰 Tools
🪛 markdownlint-cli2 (0.23.0)

[warning] 15-15: Link and image reference definitions should be needed
Unused link or image reference definition: "answer"

(MD053, link-image-reference-definitions)

🤖 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
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/infrastructure-design-questions.md`
at line 15, Update the recorded answer in the infrastructure design questions
document to use plain text rather than Markdown reference-definition syntax:
replace the bracketed “Answer” label with an unbracketed label while preserving
the approval value and timestamp.

Source: Linters/SAST tools


| 共有物 | 共有先 | 本 Issue での扱い |
|--------|--------|------------------|
| `BETTER_AUTH_SECRET` | api ↔ mcp ↔ hocuspocus | 値の同期は運用ルールのまま(自動強制なし、既知ギャップ)。Worker へは `wrangler secret bulk` で供給 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Enforce BETTER_AUTH_SECRET parity before deployment.

This migration adds another independently managed secret copy while leaving synchronization unenforced. A drifted Worker secret can invalidate sessions or break cross-service authentication without failing deployment. Consume one secret-manager version or compare non-reversible fingerprints in a preflight; never log the secret values.

🤖 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
`@aidlc/spaces/default/intents/260718-api-workers-migration/construction/api-worker/infrastructure-design/shared-infrastructure.md`
at line 9, Update the BETTER_AUTH_SECRET entry in the shared infrastructure
design so deployment preflight enforces parity across api, mcp, hocuspocus, and
the Worker. Require either one shared secret-manager version or comparison of
non-reversible fingerprints, and ensure secret values are never logged.

Comment on lines +70 to +76
it("responds to /api/auth/get-session as well-formed JSON (no workerd crash)", async () => {
const res = await dispatch("/api/auth/get-session");
// DB 未接続のため成功は要求しない。Worker ランタイム例外(fetch reject)に
// ならず、HTTP レスポンスとして整形されていることが合格条件。
expect(res.status).toBeGreaterThanOrEqual(200);
expect(res.status).toBeLessThan(600);
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Verify the claimed JSON response shape.

The test name promises well-formed JSON, but any response with a 200–599 status passes. Assert an application/json content type and parse the body, or rename the test to describe status-only compatibility.

As per coding guidelines, tests serve as a source of truth for specifications alongside implementation documentation.

🤖 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__/worker/worker.test.ts` around lines 70 - 76, Update
the test named “responds to /api/auth/get-session as well-formed JSON (no
workerd crash)” to verify its promised response shape: assert an
application/json content type and successfully parse the response body as JSON,
while retaining the existing status-range and non-rejection checks.

Source: Coding guidelines

Comment on lines +101 to +120
export function scrubSentryEvent<T extends ScrubbableSentryEvent>(event: T): T {
if (event.request) {
event.request = {
...event.request,
url: scrubRequestUrl(event.request.url),
headers: scrubShallowRecord(event.request.headers),
data: scrubDeep(event.request.data, new WeakSet()),
query_string: scrubQueryStringField(event.request.query_string),
cookies: scrubCookies(event.request.cookies),
};
}
if (event.user) {
event.user = scrubShallowRecord(event.user) ?? event.user;
}
if (event.extra) {
event.extra = scrubDeep(event.extra, new WeakSet()) as typeof event.extra;
}
if (event.contexts) {
event.contexts = scrubShallowRecord(event.contexts) ?? event.contexts;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Recursively scrub object query strings and nested contexts.

query_string objects pass through unchanged, while contexts only receives top-level scrubbing. Both can therefore retain nested token, password, or cookie values sent to Sentry.

Proposed fix
   if (event.contexts) {
-    event.contexts = scrubShallowRecord(event.contexts) ?? event.contexts;
+    event.contexts = scrubDeep(event.contexts, new WeakSet()) as typeof event.contexts;
   }

 function scrubQueryStringField(value: unknown): unknown {
-  if (typeof value !== "string") return value;
-  return value.replace(SENSITIVE_QUERY_PARAM_RE, `$1${FILTERED}`);
+  if (typeof value === "string") {
+    return value.replace(SENSITIVE_QUERY_PARAM_RE, `$1${FILTERED}`);
+  }
+  if (value !== null && typeof value === "object") {
+    return scrubDeep(value, new WeakSet());
+  }
+  return value;
 }

Also applies to: 224-228

🤖 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/lib/sentryShared.ts` around lines 101 - 120, Update
scrubSentryEvent to recursively scrub request query strings and contexts: make
the query_string handling pass object values through scrubDeep while preserving
string handling via scrubQueryStringField, and replace shallow contexts
scrubbing with scrubDeep. Keep the existing behavior for other request, user,
and extra fields unchanged.

Comment on lines +40 to +49
if (err instanceof HTTPException) {
const status = err.status;
console.error(`[api] ${c.req.method} ${c.req.path} → ${status}`, err.message);
if (shouldCaptureApiException(status)) {
capture(err, status, {
method: c.req.method,
routePath: c.req.routePath,
});
}
return c.json({ error: err.message }, status);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Preserve 5xx masking in the HTTPException branch.

Unlike the generic branch, this returns err.message for status 500+, so new HTTPException(500, { message: ... }) can expose database, schema, or connection details. The documented contract requires { error: "Internal server error" } for every 5xx response.

Proposed fix
-      return c.json({ error: err.message }, status);
+      const responseMessage = status >= 500 ? "Internal server error" : err.message;
+      return c.json({ error: responseMessage }, status);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (err instanceof HTTPException) {
const status = err.status;
console.error(`[api] ${c.req.method} ${c.req.path}${status}`, err.message);
if (shouldCaptureApiException(status)) {
capture(err, status, {
method: c.req.method,
routePath: c.req.routePath,
});
}
return c.json({ error: err.message }, status);
if (err instanceof HTTPException) {
const status = err.status;
console.error(`[api] ${c.req.method} ${c.req.path}${status}`, err.message);
if (shouldCaptureApiException(status)) {
capture(err, status, {
method: c.req.method,
routePath: c.req.routePath,
});
}
const responseMessage = status >= 500 ? "Internal server error" : err.message;
return c.json({ error: responseMessage }, status);
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 41-41: Logging request-derived input unsanitized allows log forging (CRLF injection); strip newlines / encode the value before logging.
Context: console.error([api] ${c.req.method} ${c.req.path} → ${status}, err.message)
Note: [CWE-117] Improper Output Neutralization for Logs. OWASP A09:2021 Security Logging and Monitoring Failures.

(log-injection-typescript)

🤖 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/middleware/errorHandler.ts` around lines 40 - 49, Update the
HTTPException handling branch to return the generic “Internal server error”
message for all 5xx statuses, while preserving err.message for non-5xx
responses. Keep the existing logging, capture behavior, and status code
unchanged.

return (err, c) => {
if (err instanceof HTTPException) {
const status = err.status;
console.error(`[api] ${c.req.method} ${c.req.path} → ${status}`, err.message);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Neutralize request and error values before logging.

c.req.path, err.message, and the thrown error can contain attacker-controlled CR/LF characters. Writing them directly permits log-line injection and can corrupt downstream parsing and alerts. Escape or remove CR/LF characters in both branches, or use a structured logger that guarantees safe JSON encoding.

Also applies to: 65-65

🤖 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/middleware/errorHandler.ts` at line 42, Sanitize
attacker-controlled request and error values before the console.error calls in
the error handler’s branches, including c.req.path, err.message, and the thrown
error at the referenced logging sites. Remove or escape CR/LF characters, or use
a structured logger that safely encodes these fields, while preserving the
existing method, path, status, and error context.

Source: Linters/SAST tools

Comment on lines +12 to +20
// jsdom は Node 専用(workerd では実行不可)のため静的 import しない。
// Worker のモジュールロードを通し、clip 実行時のみ評価する(呼ばれた場合の
// 失敗は onError でマスク済み 5xx になる — #1091 RL-3)。
// jsdom is Node-only; load it lazily so the Worker module graph still boots.
type JsdomModule = typeof import("jsdom");
let _jsdom: JsdomModule | null = null;
async function loadJsdom(): Promise<JsdomModule> {
if (!_jsdom) _jsdom = await import("jsdom");
return _jsdom;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 4 'extractArticleFromUrl|app\.route\("/api/clip"' server/api/src

Repository: otomatty/zedi

Length of output: 34209


Keep jsdom out of the Worker path The lazy import only shifts the failure to first use; jsdom is still Node-only, and the shared app mounts /api/clip, which reaches extractArticleFromUrl() at runtime. Exclude this flow from the Worker graph or swap in a Worker-compatible parser, and add a Worker-path contract test.

🤖 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/services/articleExtractor.ts` around lines 12 - 20, The lazy
loadJsdom import still allows the Node-only jsdom dependency to be reached
through the shared /api/clip Worker route. Remove this dependency from the
Worker execution path by routing extractArticleFromUrl to a Worker-compatible
parser or excluding the route from Worker deployment, and add a contract test
confirming the Worker path does not load or invoke jsdom.

Source: Coding guidelines

otomatty and others added 2 commits July 20, 2026 10:01
knip misreads the cloudflare:test virtual module (vitest-pool-workers) as
an unlisted 'cloudflare' npm dependency in the worker runtime tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…al split

Deploy validation runs module-load code (auth.ts) which needs DATABASE_URL +
the Better Auth / OAuth secrets before wrangler deploy can pass startup check.
Mark those 8 as required, separate optional/no-op secrets, and note which
values come from wrangler.jsonc vars/bindings rather than secrets (#1091).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

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

🤖 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/.env.worker.production.example`:
- Around line 39-41: Remove the localhost placeholder from the production
environment configuration and provide a reachable production DATABASE_URL;
alternatively, update resolveCheckpointerForRun() so boot-time DATABASE_URL
initialization does not implicitly enable PostgresSaver checkpointing. Ensure
compose-session requests do not attempt to connect to 127.0.0.1:5432 in
production.
🪄 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: 87edc5bd-254c-439f-9eff-1bbe06c6cc68

📥 Commits

Reviewing files that changed from the base of the PR and between 316f1be and 83332a0.

📒 Files selected for processing (2)
  • server/api/.env.worker.dev.example
  • server/api/.env.worker.production.example

Comment on lines +39 to +41
# DB 接続文字列。auth.ts の drizzle アダプタがモジュールロード時に pg.Pool を
# 構築するため **起動時に必須**。D1 移行(#1090)までは到達しないプレースホルダで良い。
DATABASE_URL=postgresql://placeholder:unused@127.0.0.1:5432/unused

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not ship the localhost placeholder as an active production DATABASE_URL.

Because resolveCheckpointerForRun() enables Postgres checkpointing whenever DATABASE_URL is present (server/api/src/agents/core/checkpoint/index.ts:17-24), this value makes production attempt PostgresSaver.setup() against 127.0.0.1:5432, causing compose-session requests to fail. Provide a reachable database URL, or change the runtime contract so boot-time DB initialization does not implicitly enable checkpointing.

🤖 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/.env.worker.production.example` around lines 39 - 41, Remove the
localhost placeholder from the production environment configuration and provide
a reachable production DATABASE_URL; alternatively, update
resolveCheckpointerForRun() so boot-time DATABASE_URL initialization does not
implicitly enable PostgresSaver checkpointing. Ensure compose-session requests
do not attempt to connect to 127.0.0.1:5432 in production.

Complements secrets-template.md (lifecycle ledger) with a practical guide on
where each .env.worker value comes from — mostly copy from Railway api vars,
plus R2 access keys and OAuth console pointers (#1091).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

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

🧹 Nitpick comments (1)
.agents/skills/cloudflare-zedi/references/worker-secret-values.md (1)

85-90: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use a least-privilege Worker-specific R2 token by default.

The guide currently recommends reusing the zedi-migration token without documenting or verifying its scope, expiry, and rotation policy. Make a dedicated runtime token the default; mention the migration token only as a time-bounded fallback after its permissions are confirmed.

🤖 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 @.agents/skills/cloudflare-zedi/references/worker-secret-values.md around
lines 85 - 90, Update the STORAGE_ACCESS_KEY / STORAGE_SECRET_KEY guidance to
make a dedicated Worker-specific R2 API token the default, with only the
permissions required at runtime. Relegate the zedi-migration token to a
time-bounded fallback, and document that its scope, expiry, and rotation policy
must be confirmed before use; preserve the warning that existing Railway
STORAGE_* credentials must not be changed.
🤖 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.

Nitpick comments:
In @.agents/skills/cloudflare-zedi/references/worker-secret-values.md:
- Around line 85-90: Update the STORAGE_ACCESS_KEY / STORAGE_SECRET_KEY guidance
to make a dedicated Worker-specific R2 API token the default, with only the
permissions required at runtime. Relegate the zedi-migration token to a
time-bounded fallback, and document that its scope, expiry, and rotation policy
must be confirmed before use; preserve the warning that existing Railway
STORAGE_* credentials must not be changed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d606b9b-ef2e-41f8-a2cd-9f03644ba010

📥 Commits

Reviewing files that changed from the base of the PR and between 83332a0 and 1b87780.

📒 Files selected for processing (2)
  • .agents/skills/cloudflare-zedi/references/secrets-template.md
  • .agents/skills/cloudflare-zedi/references/worker-secret-values.md

…atty subdomain)

Not a copy-from-Railway migration: dev Worker gets freshly generated values.
Generate BETTER_AUTH_SECRET, use zedi-api-dev.otomatty.workers.dev, and add a
minimum-to-unblock-deploy path (boot check only needs non-empty values; real
OAuth/DB deferred to #1090).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

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

🤖 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 @.agents/skills/cloudflare-zedi/references/worker-secret-values.md:
- Around line 125-127: Update the guidance around worker:secrets:put --dry-run
to require verifying the exact eight required secret names, including
BETTER_AUTH_SECRET and DATABASE_URL, rather than only confirming that eight
entries are printed; alternatively, add explicit required-key validation in
putWorkerSecrets.ts.
🪄 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: 7d5bce7b-99c3-4b9c-918e-40e0256aaedd

📥 Commits

Reviewing files that changed from the base of the PR and between 1b87780 and 2e6ccc5.

📒 Files selected for processing (2)
  • .agents/skills/cloudflare-zedi/references/worker-secret-values.md
  • server/api/.env.worker.dev.example
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/api/.env.worker.dev.example

Comment on lines +125 to +127
- ⚠️ `worker:secrets:put` は **空値をスキップ**する。必須 8 項目に値(プレースホルダ含む)が
入っていないとアップロードされず、deploy が `... must be set` で失敗する。`--dry-run` で
8 項目が出ることを確認する。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate the required key names, not just the entry count.

worker:secrets:put --dry-run currently prints all non-empty parsed entries; it does not verify that the eight required keys are present. Therefore, seeing eight entries can still leave BETTER_AUTH_SECRET, DATABASE_URL, or another required key missing and cause deployment to fail.

Update this guidance to require checking the exact eight names, or add required-key validation to putWorkerSecrets.ts.

Suggested documentation correction
-- `--dry-run` で 8 項目が出ることを確認する。
+- `--dry-run` の出力に、次の必須 8 キーがすべて含まれることを確認する:
+  `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, `CORS_ORIGIN`,
+  `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`,
+  `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `DATABASE_URL`。
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- ⚠️ `worker:secrets:put`**空値をスキップ**する。必須 8 項目に値(プレースホルダ含む)が
入っていないとアップロードされず、deploy が `... must be set` で失敗する。`--dry-run`
8 項目が出ることを確認する。
- ⚠️ `worker:secrets:put`**空値をスキップ**する。必須 8 項目に値(プレースホルダ含む)が
入っていないとアップロードされず、deploy が `... must be set` で失敗する。`--dry-run` の出力に、次の必須 8 キーがすべて含まれることを確認する:
`BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, `CORS_ORIGIN`,
`GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`,
`GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `DATABASE_URL`
🤖 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 @.agents/skills/cloudflare-zedi/references/worker-secret-values.md around
lines 125 - 127, Update the guidance around worker:secrets:put --dry-run to
require verifying the exact eight required secret names, including
BETTER_AUTH_SECRET and DATABASE_URL, rather than only confirming that eight
entries are printed; alternatively, add explicit required-key validation in
putWorkerSecrets.ts.

otomatty and others added 5 commits July 25, 2026 19:59
Drop GitHub OAuth from Better Auth, sign-in/invite/extension UI, and
Worker secret examples so boot only requires Google client credentials.

Co-authored-by: Cursor <cursoragent@cursor.com>
Expanded the audit log for the saedgewell migration with additional HUMAN_TURN event entries, documenting timestamps for improved tracking and analysis.
Disable the Vitest plugin for server/api so CI root install does not try
to require @cloudflare/vitest-pool-workers via vitest.config.worker.ts.

Co-authored-by: Cursor <cursoragent@cursor.com>
@otomatty
otomatty merged commit 25eb9ae into develop Jul 28, 2026
20 checks passed
@otomatty
otomatty deleted the feature/1091 branch July 28, 2026 10:57
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.

infra(migration): REST API (server/api, Hono) → Cloudflare Workers

1 participant