feat(dev): local dev API harness — runnable createInboxApi with seeded data (HT-24) - #23
Conversation
…d data (HT-24) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a local development API server with PGlite persistence, seeded conversation scenarios, a non-delivering email sender, and a Node-to-Fetch HTTP adapter. The harness supports in-memory or file-backed databases, graceful shutdown, runtime logging, and delivery-state tests. ChangesDevelopment harness
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Developer
participant devApi
participant PGlite
participant createInboxApi
participant createHttpBridge
Developer->>devApi: start dev:api
devApi->>PGlite: create database and run migrations
devApi->>createInboxApi: construct configured API
devApi->>createHttpBridge: attach HTTP bridge
Developer->>createHttpBridge: send API request
createHttpBridge->>createInboxApi: pass Fetch Request
createInboxApi-->>createHttpBridge: return Fetch Response
createHttpBridge-->>Developer: send HTTP response
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@scripts/dev-api.ts`:
- Around line 87-91: Update the dev server setup around createHttpBridge and
server.listen to use 127.0.0.1 in both baseUrl and the listen binding, ensuring
the token-protected API is reachable only through loopback.
- Around line 114-118: Update the shutdown function to await completion of
server.close before invoking db.close, wrapping the server callback in a Promise
if needed. Preserve the existing shutdown log and process.exit flow, ensuring
in-flight HTTP requests drain before the database is closed.
In `@src/dev/seed.test.ts`:
- Around line 71-82: The seed test currently verifies only that delivery
statuses are present, allowing incorrect seeded counts to pass. Update the
assertions around deliveryStatuses to validate the exact expected count for each
status, including sent, failed, and pending, while preserving the existing
stale-pending age check for pendingThread.
In `@src/dev/seed.ts`:
- Around line 147-159: Update the seed reply calls in the relevant sent and
closed-conversation setup, including sendReply, to check their results and fail
seeding when the injected sender reports an error. Apply the same
required-result validation to the closed-conversation reply while preserving the
existing six-conversation seed flow.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bb7a1f55-74f9-466c-bca5-41e8aacdc832
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
package.jsonscripts/dev-api.tssrc/dev/dev-sender.tssrc/dev/http-adapter.tssrc/dev/seed.test.tssrc/dev/seed.ts
…strict seeding (CodeRabbit) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Purpose
HT-24: a minimal, runnable local dev server so the upcoming Agent Inbox UI (HT-23) has a real API to integrate against, and the engine gets a standing dogfood surface. Not a deployment — dev tooling only.
What it wires
npm run dev:api→tsx scripts/dev-api.ts(tsxadded as a devDependency — no native-Node TS execution path works here, since this codebase's NodeNext-style.js-suffixed imports resolve to.tsfiles, which plainnode --experimental-strip-typesdoes not remap;tsxdoes).scripts/dev-api.ts— the entry point. Deliberately outsidetsconfig.json'sinclude(src/**,tests/**only) and outsidepackage.json's package surface (nofiles/main/exportstouched) — this is the literal "dev tooling, not shipped" boundary the ticket calls for. It still typechecks cleanly under the project's strict settings (verified ad hoc against the realtsconfig.json, then reverted — see verification below).src/dev/http-adapter.ts— a small hand-rollednode:http⇄Request/Responsebridge (Node's ownReadable.toWeb/globalfetchtypes; zero new runtime dependencies). This one lives undersrc/and IS part of the normal typecheck/lint/test project, since it's genuine reusable infra, not the entry script itself.src/dev/dev-sender.ts— a dev-onlyEmailSenderthat delivers nothing and logs the fullOutboundEmail(To, Cc if present, Subject, Message-ID) to stdout. DeclaresmaxSendMs: 5_000, comfortably underDEFAULT_LEASE_MS(120_000) so it never tripsassertLeaseExceedsSenderBound— the base branch (PR feat(mail): couple the delivery lease to the sender's enforced timeout (HT-22) #22, merged) already requires everyEmailSenderto declare this bound.src/dev/seed.ts— seeds 6 conversations on every in-memory boot, reusing the realConversationStore/sendReplypaths (not raw SQL) wherever practical:sentfailed(via a one-off throwing sender, sosendReply's own failure handling produces the realfailedrow — not a hand-set status)pending(persisted viastore.appendThreadto model a crash between persist-and-send, sincesendReplyitself always resolves to sent/failed; itscreated_atis backdated with one deliberate, narrowly-scoped rawUPDATE— there's no store API for backdating a timestamp, and this is the only raw SQL in the seed script)All names/content are invented, never real data.
HT_DEV_TOKENenv var, defaulthelpthread-dev-token(see judgment call below). Port:HT_DEV_PORT, default8787. Persistence: optionalHT_DEV_DB_PATH(file-backed PGlite instead of in-memory; seeding is skipped in that mode since the point is that data survives restarts).Idempotency-Keyheader).Judgment calls
dev-token", butcreateInboxApienforces a 16-char minimum (MIN_API_TOKEN_LENGTH, fail-closed at construction).dev-token(10 chars) crashes the server at boot — caught by actually running it. Changed the default tohelpthread-dev-token(20 chars), still unambiguously dev-only.scripts/directory (new) rather thansrc/dev/for the actual executable, so it falls outsidetsconfig.json's existingincludeglobs with zero config changes needed. The reusable pieces (http-adapter.ts,dev-sender.ts,seed.ts) live undersrc/dev/and stay normally typechecked/linted/tested — only the literal "runnable script" is excluded from the checked project, per the ticket's framing.src/dev/seed.test.ts(asserts the 6-conversation count and thatsent/failed/stale-pendingall appear) since it was cheap and guards against silent seed regressions — the curl evidence below is still the primary proof per the ticket.Verification (real output, not asserted)
All three gates exit 0:
Server started for real (
npm run dev:api), then exercised with curl:(a) GET conversations, valid token → 200, seeded list (5 open by default; the closed one is excluded, confirmed separately via
?status=closed)(b) GET one conversation → 200
(c) POST reply WITH Idempotency-Key → 201, dev-sender logs the send
(d) REPLAY same Idempotency-Key, different body → 201 with the IDENTICAL original response, no second sender log line
(e) Bad token → 401
Also spot-checked the failed and stale-pending seed rows directly:
deliveryStatus":"failed"on the shipping-delay demo, anddeliveryStatus":"pending"with acreatedAtan hour in the past on the API-rate-limits demo (older than the delivery worker's 5-minutestaleAfterMs).Not a deployment
This is dev-only tooling: an in-memory (by default) PGlite database, a dev-token default, and a sender that logs instead of delivering. Nothing here is wired into any deploy path.
🤖 Generated with Claude Code
Summary by CodeRabbit
dev:apiscript and included thetsxrunner for development.