Skip to content

Refactor specs and add tooling configuration#2

Merged
coji merged 2 commits into
mainfrom
feature/spec-review-and-cleanup
Dec 20, 2025
Merged

Refactor specs and add tooling configuration#2
coji merged 2 commits into
mainfrom
feature/spec-review-and-cleanup

Conversation

@coji
Copy link
Copy Markdown
Owner

@coji coji commented Dec 20, 2025

Summary

  • Rename docs/durably.md -> docs/spec.md for clarity
  • Add docs/spec-streaming.md for AI Agent streaming support (v2)
  • Add docs/implementation-plan.md with TDD roadmap
  • Unify package naming to @coji/durably
  • Migrate from better-sqlite3 to Turso/libsql
  • Add jobName field to Step events for consistency
  • Add name/$types to JobHandle in v2 spec

Tooling

  • Add biome for linting (pnpm lint)
  • Add prettier with organize-imports plugin (pnpm format)
  • Update README.md and CLAUDE.md to reflect changes

Test plan

  • Build passes (pnpm build)
  • Lint passes (pnpm lint)
  • Format passes (pnpm format)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added comprehensive streaming specification for AI workflows with real-time subscribe/stream semantics and checkpointing roadmap.
  • Documentation

    • Updated core specification and README with new usage examples, package naming, and LibSQL-based runtime guidance.
    • Added implementation plan and streaming extension guide with phased rollout and event model details.
  • Chores

    • Added formatting, linting, and project configuration plus ignore rules.

✏️ Tip: You can customize this high-level summary in your review settings.

## Specification changes
- Rename docs/durably.md -> docs/spec.md
- Add docs/spec-streaming.md for AI Agent streaming support
- Add docs/implementation-plan.md with TDD roadmap
- Unify package naming to @coji/durably
- Migrate from better-sqlite3 to Turso/libsql
- Add jobName field to Step events
- Add name/$types to JobHandle in v2 spec

## Tooling
- Add biome for linting
- Add prettier with organize-imports plugin
- Update README.md and CLAUDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 20, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds formatting and project configuration files, updates docs and README to rename APIs and switch SQLite dialects to LibSQL/Turso, and introduces detailed specification and implementation-planning documents for streaming and monorepo development.

Changes

Cohort / File(s) Summary
Formatting & Tooling configs
\.prettierignore, prettier.config.js, biome.json, package.json
Adds .prettierignore (ignores pnpm-lock.yaml), introduces prettier.config.js (semi:false, singleQuote:true, trailingComma:'all', printWidth:80, organize-imports plugin), adds biome.json (biome v2.3.10 config, VCS integration, linter rules), and adds devDependencies (@biomejs/biome, prettier, prettier-plugin-organize-imports) in package.json.
Serena project configuration
.serena/.gitignore, .serena/project.yml
Adds /cache to .serena/.gitignore and creates .serena/project.yml with project metadata (languages: ["typescript"], encoding utf-8, ignore_all_files_in_gitignore: true, project_name: "durably", plus initial tool/read-only settings).
Core docs & README updates
README.md, CLAUDE.md, docs/spec.md
Rewrites examples and text to use createDurably / durably instead of createClient / client, replaces BetterSqlite3Dialect with LibsqlDialect (Turso/libsql and SQLocal examples), updates import paths to @coji/durably and @libsql/kysely-libsql, and expands API behavior descriptions (job descriptor shape, run/output semantics, idempotency notes).
Streaming specification
docs/spec-streaming.md
Adds a streaming API design doc describing ctx.stream(), subscribe()/JobHandle.subscribe, event logging, checkpoints, browser tab sync, phased implementation plan (Phases A–C), and proposed public types (StreamEvent, DurablyEvent, JobContext extensions).
Implementation plan
docs/implementation-plan.md
Adds a detailed, phased TDD-driven implementation plan (Phases 0–8) with repo layout, test strategies (Node/browser Vitest), deliverables, examples, and migration notes for building the Durably monorepo.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to consistency of import paths and API names across README.md, CLAUDE.md, and docs/spec.md.
  • Review docs/spec-streaming.md and docs/implementation-plan.md for clarity on intended public API shapes and any implied runtime behavior.
  • Verify prettier.config.js, .prettierignore, biome.json, and package.json versions/plugins are compatible.

Possibly related PRs

Poem

🐰 I hopped through configs, tidy and bright,
Ignored a lockfile and set quotes just right.
From client to Durably my whiskers did steer,
Streaming dreams penned for the roadmap near.
Let's test and build — a carrot for cheer! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Refactor specs and add tooling configuration' directly summarizes the main changes: documentation/specification refactoring and addition of linting/formatting tooling (biome, Prettier).
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d07526b and 0fd52bd.

📒 Files selected for processing (1)
  • biome.json (1 hunks)

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

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
package.json (1)

22-25: Missing lint and format scripts.

The PR objectives state that pnpm lint and pnpm format pass checks, but these scripts are missing from the scripts section. Without them, users cannot run the linting and formatting tools that were added as devDependencies.

🔎 Add missing scripts
   "scripts": {
     "build": "tsup",
-    "prepublishOnly": "pnpm build"
+    "prepublishOnly": "pnpm build",
+    "lint": "biome check .",
+    "format": "prettier --write ."
   },
🧹 Nitpick comments (4)
docs/spec.md (1)

161-162: Consider documenting cleanup strategy for idempotencyKey records.

The spec states that idempotencyKey has no expiration and remains valid as long as the Run exists. While this is correct behavior, it could lead to unbounded database growth over time if old Runs are not actively managed.

Consider adding a note in the documentation about:

  • Recommended cleanup strategies for old Runs
  • TTL or archival policies applications should implement
  • Storage growth implications for high-volume scenarios
docs/spec-streaming.md (2)

176-194: Note potential confusion with column naming.

Line 193 explicitly mentions that the DB column name is created_at but will be exposed as timestamp in the event type to match the v1 pattern. While this maintains consistency with v1, it could create confusion during debugging or when writing raw SQL queries.

Consider adding a comment in the actual schema migration noting this mapping for future maintainers.


407-409: Consider addressing authorization strategy for subscribe().

Line 408 raises an important security question: "Should anyone who knows the runId be able to subscribe?" but leaves it unresolved.

For the specification document, consider adding:

  • A proposed authorization model (e.g., token-based, same-origin only, etc.)
  • Guidance on what applications should implement
  • Security implications of different approaches

This doesn't need to be implemented in v1, but having a planned approach would help guide future implementation.

.prettierignore (1)

1-1: Consider adding common ignore patterns.

While ignoring pnpm-lock.yaml is correct, consider adding other common patterns for completeness:

  • dist/ (build output)
  • node_modules/ (dependencies, though often ignored by default)
  • coverage/ (test coverage reports)
  • .git/ (version control)
🔎 Suggested additions
 pnpm-lock.yaml
+dist/
+coverage/
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 873156f and d07526b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • .prettierignore (1 hunks)
  • .serena/.gitignore (1 hunks)
  • .serena/project.yml (1 hunks)
  • CLAUDE.md (2 hunks)
  • README.md (1 hunks)
  • biome.json (1 hunks)
  • docs/implementation-plan.md (1 hunks)
  • docs/spec-streaming.md (1 hunks)
  • docs/spec.md (13 hunks)
  • package.json (1 hunks)
  • prettier.config.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Define jobs using defineJob() function that receives a context object and payload
Create steps via ctx.run(), with each step's success state and return value persisted automatically
Create job execution instances (runs) via trigger() API, which always persists runs as pending before execution
Use explicit failure handling with no automatic retry - utilize the retry() API for manual retry logic
Use dialect injection pattern by passing Kysely dialect to createClient() to abstract SQLite implementations
Extend framework behavior using the event system with events: run:start, run:complete, run:fail, step:*, log:write
Implement optional features using the plugin architecture via client.use() method, such as log persistence
Use default configuration values: pollingInterval 1000ms, heartbeatInterval 5000ms, staleThreshold 30000ms for detecting abandoned runs

Files:

  • prettier.config.js
🧠 Learnings (11)
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use default configuration values: `pollingInterval` 1000ms, `heartbeatInterval` 5000ms, `staleThreshold` 30000ms for detecting abandoned runs

Applied to files:

  • prettier.config.js
  • biome.json
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Durably is a step-oriented batch execution framework for Node.js and browsers with resumable batch processing using only SQLite for persistence

Applied to files:

  • CLAUDE.md
  • docs/spec.md
  • README.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: The same job definition code must run in both Node.js and browser environments - Node.js uses better-sqlite3/libsql, browsers use SQLite WASM with OPFS backend

Applied to files:

  • CLAUDE.md
  • docs/spec.md
  • README.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*migration*.{ts,tsx,js,jsx,sql} : Database schema contains four tables: `runs`, `steps`, `logs`, `schema_versions`. Runs have fields: `status` (pending/running/completed/failed), `idempotency_key`, `concurrency_key`, `heartbeat_at`. Steps have fields: `status` (completed/failed), `output` (JSON), indexed by `run_id` and `index`

Applied to files:

  • CLAUDE.md
  • docs/spec.md
  • README.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Create job execution instances (runs) via `trigger()` API, which always persists runs as `pending` before execution

Applied to files:

  • CLAUDE.md
  • docs/spec.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Define jobs using `defineJob()` function that receives a context object and payload

Applied to files:

  • CLAUDE.md
  • docs/spec.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Create steps via `ctx.run()`, with each step's success state and return value persisted automatically

Applied to files:

  • CLAUDE.md
  • docs/spec.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Database persistence uses SQLite with dialect abstraction through Kysely for both Node.js (better-sqlite3/libsql) and browser (SQLite WASM) implementations

Applied to files:

  • CLAUDE.md
  • docs/spec.md
  • README.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use dialect injection pattern by passing Kysely dialect to `createClient()` to abstract SQLite implementations

Applied to files:

  • CLAUDE.md
  • docs/spec.md
  • README.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Extend framework behavior using the event system with events: `run:start`, `run:complete`, `run:fail`, `step:*`, `log:write`

Applied to files:

  • docs/spec.md
📚 Learning: 2025-12-09T04:34:39.625Z
Learnt from: CR
Repo: coji/durably PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-09T04:34:39.625Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Implement optional features using the plugin architecture via `client.use()` method, such as log persistence

Applied to files:

  • docs/spec.md
🔇 Additional comments (17)
.serena/.gitignore (1)

1-1: LGTM!

Standard ignore pattern for cache directory.

CLAUDE.md (2)

7-15: LGTM! Documentation accurately reflects the architectural updates.

The updates to runtime libraries (Turso/libsql, SQLocal) and documentation structure are clearly documented and consistent with the PR objectives.


19-30: LGTM! API naming updates are consistent.

The changes from standalone functions to method-based API (durably.defineJob(), createDurably(), durably.use()) improve discoverability and namespace clarity.

docs/implementation-plan.md (3)

869-879: LGTM! Proper consideration of OPFS cleanup challenges.

The documentation correctly identifies that OPFS database files may require explicit deletion and notes this as a consideration for browser testing. This is an important operational concern for test cleanup.


328-364: LGTM! Storage abstraction supports v2 extensibility.

The Storage interface design with optional methods (e.g., createLog?, getLogs?) and clear separation of Run, Step, and Log operations provides a solid foundation for future v2 features while maintaining clean boundaries.


198-206: No changes needed—LibSQL supports ':memory:' syntax for in-memory databases.

LibSQL supports ':memory:' for in-memory databases, consistent with SQLite's API. The libsql C API documentation explicitly shows ':memory:' as valid syntax, and the libsql npm package includes examples using ':memory:'. The code syntax is correct.

Note: There are known issues with transactions in ':memory:' databases in the libsql-client-ts library where in-memory databases can be discarded after transaction creation, but this may not affect simple test scenarios.

.serena/project.yml (1)

16-84: LGTM! Well-documented project configuration.

The configuration appropriately sets TypeScript as the primary language, enables gitignore integration, and includes helpful inline documentation for all available options.

README.md (1)

11-54: LGTM! README accurately reflects the new API surface.

The migration from better-sqlite3 to LibSQL/Turso is clearly documented, and the API examples correctly show:

  • New import paths (@libsql/kysely-libsql)
  • Object-based job definition syntax
  • Environment-based configuration pattern
docs/spec.md (4)

327-336: LGTM! Addition of jobName to step events improves consistency.

Including jobName in step events (lines 327-336) ensures all events carry sufficient context for filtering and type-safe event handling, which aligns well with the Discriminated Union pattern described later.


341-427: LGTM! Well-structured event type definitions.

The formal event type definitions with:

  • Discriminated Union pattern via type field
  • sequence field for ordering guarantees
  • Comprehensive interface definitions

This provides a solid foundation for type-safe event handling and future v2 streaming features.


839-967: LGTM! Comprehensive v2 preparation guidance.

The internal design guidelines section provides clear, actionable guidance for:

  • JobContext extensibility (class/factory pattern)
  • Storage abstraction
  • Event sequencing

This forward-thinking approach will minimize breaking changes when v2 features are added.


667-668: LGTM! Appropriate ULID implementation choice.

Using ulidx is a good choice as it's lightweight, supports both Node.js and browsers, and provides the needed sortability and uniqueness properties.

docs/spec-streaming.md (3)

82-141: LGTM! Excellent use of Web Streams API for cross-platform streaming.

Using ReadableStream as the return type for subscribe() is a smart choice because:

  • Native browser API (no additional dependencies)
  • Supports backpressure
  • Works in both Node.js and browsers
  • Enables for await...of consumption

The implementation pattern with start() for initial events and pull() for new events is well-designed.


354-398: LGTM! Well-justified trade-off for event persistence strategy.

The decision to persist only coarse-grained events (step:, run:) while keeping token-level emits in-memory is pragmatic and well-reasoned:

  • Dramatically reduces DB write load
  • Token-level data is regenerable (LLM responses)
  • Reconnection still works at step boundaries
  • Documented trade-offs are acceptable

The implementation rationale and reconnection behavior are clearly explained.


416-514: LGTM! Comprehensive and realistic use case example.

The AI Coding Assistant example effectively demonstrates:

  • Mixing ctx.run() and ctx.stream()
  • Different emit types for various streaming data
  • Progress tracking integration
  • Client-side stream consumption patterns

This provides clear guidance for implementers and users of the streaming API.

prettier.config.js (1)

1-7: LGTM!

The Prettier configuration is well-structured with sensible defaults:

  • No semicolons and single quotes align with modern JavaScript conventions
  • Trailing commas improve git diffs
  • 80-character print width is a reasonable default
  • The organize-imports plugin integration will help maintain consistent import ordering
package.json (1)

55-58: No action needed. prettier-plugin-organize-imports@4.3.0 has peer dependency prettier>=2.0, and prettier@3.7.4 satisfies this requirement.

Comment thread biome.json Outdated
"files": {
"includes": ["**", "!worker-configuration.d.ts", "!app/styles/globals.css"]
},
"assist": { "actions": { "source": { "organizeImports": "off" } } },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify responsibility for import organization.

Biome's organizeImports is disabled here, but according to the AI summary, prettier-plugin-organize-imports is enabled in the Prettier config. This creates ambiguity about which tool manages import organization.

Consider documenting which tool is responsible or removing one of the conflicting configurations to avoid confusion.

🤖 Prompt for AI Agents
In biome.json around line 6, the project disables Biome's organizeImports while
Prettier enables prettier-plugin-organize-imports, causing ambiguity about which
tool should manage imports; either remove/disable one of the conflicting
settings or add a clear comment and documentation indicating the chosen tool for
import organization (e.g., keep Prettier plugin enabled and re-enable/clarify
Biome setting or leave Biome off and document that Prettier handles imports) and
update config files/readme accordingly so only one tool is authoritative.

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