commits
single binary with subcommands:
prefect-server # server + services (default)
prefect-server --no-services # API only (horizontal scaling)
prefect-server services # background services only
kubernetes:
- k8s/ manifests for horizontal scaling deployment
- api-deployment: multiple replicas with --no-services
- services-deployment: single instance with `services` subcommand
- postgres + redis for data layer
- kustomize support
ci:
- tangled.org workflow for tests and build
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
orchestration:
- implement WaitForScheduledTime rule with unit tests
- refactor orchestration module structure (rules, transforms, types)
- add StateTypeSet bitfield for state matching
testing:
- add test-matrix script for all backend combinations
(sqlite×memory, sqlite×redis, postgres×memory, postgres×redis)
- fix postgres schema: add missing next_scheduled_start_time column
- add next_scheduled_start_time index for postgres
other:
- refactor broker and services module structure
- improve scheduler with cron support
- update benchmarks and test scripts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- rename docker-compose.yml to compose.yml
- add PREFECT_SERVER_API_HOST=0.0.0.0 for container networking
- fix env var names to match code (PREFECT_DATABASE_URL, PREFECT_REDIS_MESSAGING_HOST)
- add docker-full-test recipe for testing postgres + redis stack
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
evaluates active deployment schedules every 30 seconds, creating
SCHEDULED flow runs based on interval configuration. supports
max_scheduled_runs limits and skips paused deployments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
worker polling endpoint:
- POST /work_pools/{name}/get_scheduled_flow_runs
- returns WorkerFlowRunResponse[] (work_pool_id, work_queue_id, flow_run)
- updates queue/pool/deployment status to READY on poll
- supports work_queue_names filter and scheduled_before time filter
datetime standardization:
- unified ISO 8601 format: 2025-01-21T12:34:56.123456Z
- updated sqlite schema defaults: strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
- updated postgres schema defaults: TO_CHAR(NOW() AT TIME ZONE 'UTC', ...)
- added time utilities: parse(), lessOrEqual(), isPast(), formatMicros()
deployment work_queue_id resolution:
- resolve work_queue_id from work_pool_name on deployment create
- uses pool's default queue if work_queue_name not specified
- enables flow runs created from deployments to be found by worker polling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- default host: 127.0.0.1 (was 0.0.0.0)
- default log level: WARNING (was INFO)
- default limits: 200 for work_pools/queues/workers (was 10)
- postgres pool_size: 5 (was 10)
- move config audit to docs/scratch/ (working notes, not permanent docs)
dev mode still uses DEBUG logging via justfile.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
documents magic values in zig implementation and their python equivalents,
with recommendations for improving parity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- dockerfile: multi-stage build with zig 0.15.2, copies facil.io shared lib
- docker-compose: server, redis, postgres, test services with healthchecks
- justfile: docker-test, docker-full, bench-compare, test-client commands
- benchmark: fix avg calculation to include results with timing data
- test-serve: add uv script header for prefect dependency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
adds full deployment support including:
- deployment table with CRUD API (create, read, update, delete, filter, count)
- deployment_schedule table with CRUD API
- get_scheduled_flow_runs endpoint for runner polling
- create_flow_run from deployment
- pause/resume deployment endpoints
- .serve() now works end-to-end
also fixes event backfill to apply subscriber filters
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
adds the foundation for worker-based execution:
- work_pool table + full CRUD API
- work_queue table + CRUD (default queue auto-created with pool)
- worker table + heartbeat upsert
- status tracking (NOT_READY → READY on first heartbeat)
- reserved pool protection (prefect-* pools)
split API handlers into separate files to stay under line limits:
- src/api/work_pools.zig (pool CRUD + router)
- src/api/work_pool_queues.zig (queue handlers)
- src/api/work_pool_workers.zig (worker handlers)
tested with Python prefect client - all operations compatible.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- remove endpoint listing (see ROADMAP.md for details)
- add what works section with broad feature overview
- point to justfile for commands
- add configuration table
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
test harness:
- add variables test with full CRUD coverage
- add response validation helper for field/type checking
- validate flow, flow_run, variable, and block_type responses
api consistency:
- include variable name in conflict error message (like Python)
- fix timestamp format consistency: use ISO 8601 (with T, Z, microseconds)
instead of SQLite's datetime('now') format
- ensures create and update return consistent timestamp format
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
full CRUD for variables with endpoints:
- POST /api/variables/ (create)
- POST /api/variables/filter (list)
- POST /api/variables/count
- GET /api/variables/{id}
- GET /api/variables/name/{name}
- PATCH /api/variables/{id}
- PATCH /api/variables/name/{name}
- DELETE /api/variables/{id}
- DELETE /api/variables/name/{name}
adds variable table to both sqlite and postgres schemas with:
- id, name (unique), value (json), tags (json array)
- created/updated timestamps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- broker: document heap allocation and ephemeral group cleanup
- db: document events module functions including backfill query
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- refactor StoredMessage to heap allocation (no more 8KB truncation)
- refactor ParsedEvent to heap allocation (no more fixed buffer truncation)
- add ephemeral Redis group cleanup on unsubscribe
- implement backfill for /events/out (queries recent events before streaming)
- use std.json.Stringify.valueAlloc for proper JSON escaping
- add Col struct for documenting column indices in queryRecent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- database backends (sqlite + postgres)
- message broker (memory + redis)
- event_broadcaster service
- postgres connection pooling
- updated priority notes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
memory broker was pre-allocating 50,000 × 8KB = 400MB for message
storage. replaced with ArrayList-backed Queue that grows on demand,
matching Python's asyncio.Queue() behavior.
memory usage: 432MB → 39MB
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- parse client filter in /events/out (was always match_all)
- increase buffer sizes: persister 32KB payload, broadcaster 64KB+heap
- add subscribeEphemeral for broadcaster ($ start_id, no replay)
- join flush timer thread on shutdown
- release mutex before websocket writes (avoids blocking)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fix event pipeline: /events/in publishes to broker, event_broadcaster
subscribes with ephemeral consumer group for WS fan-out
- fix postgres backend: add bigint() method for BIGINT columns, use
int() for INTEGER (i32) columns to match pg.zig strict typing
- add redis broker backend with XADD/XREADGROUP/XAUTOCLAIM for streams
- add benchmark matrix: --matrix flag tests sqlite/postgres × memory/redis
- fix redis RESP: handle fragmented TCP with readMore() and buffer growth
- fix redis command buffer: dynamic allocation for large events (>16KB)
- rename ws_distributor → event_broadcaster (cleaner abstraction)
- add CLAUDE.md files for broker/, db/, services/ subsystems
- add docker-compose.yml for redis + postgres dev services
benchmark results (3 iterations avg):
| db | broker | time | memory |
|----------|--------|------|---------|
| sqlite | memory | 27ms | 329MB |
| sqlite | redis | 28ms | 39MB |
| postgres | memory | 67ms | 428MB |
| postgres | redis | 79ms | 34MB |
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
introduces broker module with similar pattern to database backend:
- Broker union type with memory and redis variants
- Message struct with id, topic, data, attributes, timestamp
- Publisher/Consumer interfaces for pub/sub pattern
- BoundedChannel generic for in-process message passing
- MemoryBroker implementation using bounded channels
- RedisBroker stub for future Redis Streams implementation
infrastructure changes:
- main.zig initializes broker on startup (PREFECT_BROKER_BACKEND env var)
- test script: scripts/test-broker-backends
- justfile: `just test-broker [memory|redis|all]`
the broker is initialized but not yet wired into event_persister or WS
subscribers - this sets up the foundation for incremental migration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- flow_runs.setState: only lock mutex for sqlite (postgres pool handles concurrency)
- block_types.update: use dialect-specific SQL for datetime (NOW()::TEXT vs datetime('now'))
- docs/database.md: update transaction docs to show Transaction pattern instead of stale begin/commit/execUnsafe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add Transaction type with dedicated pg connection for atomic operations
- add execWithRowCount() returning affected rows (fixes delete/trim on postgres)
- mutex now only locks for sqlite (postgres pool handles concurrency)
- dialect-specific SQL for INSERT OR IGNORE vs ON CONFLICT DO NOTHING
- dialect-specific SQL for datetime('now') vs NOW()::TEXT
- consolidate schema: sqlite.zig now uses schema/sqlite.zig
- remove deprecated begin/commit/rollback methods (use Transaction instead)
- add --workload arg to benchmark script for flexible workload selection
- simplify justfile: bench takes server/workload/iterations, test-db takes backend
- add loq exception for backend.zig (core abstraction, 600 line limit)
both sqlite and postgres integration tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add backend.zig with unified Backend, Row, Rows types
- add dialect.zig for SQL dialect helpers (placeholder rewriting)
- add pg.zig dependency for PostgreSQL support
- migrate all entity modules to use backend abstraction
- add schema/ directory with dialect-specific DDL
- add test-db-backends script for backend testing
- add hashing.zig for canonical JSON checksum generation
the backend abstraction supports:
- automatic placeholder rewriting (? → $1, $2 for postgres)
- unified row interface across both backends
- connection pooling for postgres, single conn + mutex for sqlite
- environment-based backend selection (PREFECT_DATABASE_BACKEND)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- test-api-sequence now reports per-section timing and request counts
- benchmark script shows zig vs python comparison by section
- tracks memory usage via psutil
- fix event_persister import after common.zig removal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
replaces manual bufPrint JSON templating with streaming Stringify API:
- extract common sendJson helpers to utilities/json.zig
- add writeFlowObject, writeFlowRunObject, writeTaskRunObject helpers
- handle nullable fields and nested objects properly
- use beginWriteRaw/endWriteRaw for pre-formatted JSON from db
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
implements full blocks support with:
- block_types: create, filter, get by slug, update
- block_schemas: create, filter, get by checksum/id (server-side sha256 checksum)
- block_documents: create, filter, get, update, delete
uses std.json.Stringify streaming pattern for json serialization.
health endpoint now returns "ok" (text/plain) to match python server.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- move uuid functions to utilities/uuid.zig
- move timestamp functions to utilities/time.zig
- create api/routing.zig for path extraction and run name generation
- rename extractIdSimple to extractIdAfter (actually describes what it does)
- delete common.zig
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- implement POST /flows/filter and POST /task_runs/filter endpoints
- split db/sqlite.zig into domain modules (flows, flow_runs, task_runs, events)
to respect 500 line limit per file
- move messaging.zig from events/ to utilities/ to match prefect structure
- consolidate test scripts in scripts/ with descriptive names:
- test-flow: SDK integration test
- test-api-sequence: comprehensive HTTP API test
- test-events-subscribe: websocket subscription test
- test-events-sdk: events with SDK test
- test-websocket: websocket ping/pong test
- convert all scripts to uv inline script metadata format
- update CLAUDE.md and ROADMAP.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- events/ for event system (messaging)
- orchestration/ for state transition logic
- main.zig as only root module (besides logging.zig utility)
mirrors python prefect server layout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
was an old std.http implementation, replaced by zap. not imported anywhere.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
name describes what it does, not the transport
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main.zig now just composes the server entry point (57 lines).
websocket handlers moved to src/api/websocket.zig where they belong.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
implements global orchestration transforms from python prefect server:
- SetStartTime: record when first entering RUNNING state
- SetEndTime: record when entering terminal state (COMPLETED/FAILED/etc)
- IncrementRunTime: accumulate time spent in RUNNING state
- IncrementRunCount: increment run_count when entering RUNNING
also fixes timestamp precision from seconds to microseconds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
verifies compatibility with prefect.events.clients.PrefectEventSubscriber
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- messaging.zig: subscriber management with filtering support
- main.zig: /events/out handler with auth/filter protocol
- broadcasts events to connected subscribers in real-time
- test_events_out.py: integration test verifying the flow
protocol:
1. client sends {"type": "auth", "token": "..."}
2. server responds {"type": "auth_success"}
3. client sends {"type": "filter", "filter": {...}}
4. server streams {"type": "event", "event": {...}}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- scripts/benchmark: compare zig vs python prefect servers
- justfile: bench-zig, bench-python, bench-compare commands
- src/main.zig: improved request error logging
- docs: rename prefect-zig to prefect-server throughout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fix event_persister to store resource/payload/related correctly
(was storing full raw json as resource, empty as payload)
- fix follows field to be null instead of empty string
- add truncation detection with warning logs
- expand test_flow.py with cache policy and transaction tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
concise overview of implemented components:
- web server: route dispatch, resource handlers
- database: sqlite layer, query patterns
- services: background worker registry
- messaging: bounded channel, backpressure
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- remove static buffer pattern in favor of allocator-based approach
- db functions now take Allocator param for cleaner memory management
- add task_run table and full api: create, read, set_state
- task runs support optional flow_run_id with proper NULL handling
- idempotency via task_key+dynamic_key lookup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- websocket endpoint /api/events/in for receiving client events
- event persistence with batching, deduplication, retention trimming
- messaging layer with bounded channel (50k capacity) for backpressure
- service registry pattern for background workers
- uuid format fix: store/return standard 36-char dashed format
- structured logging with level control via env vars
- GET /api/flows/{id} endpoint
- justfile for dev commands
- test scripts for python client
- CLAUDE.md and ROADMAP.md for project documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- replace raw sqlite3 C bindings with zqlite library
- add mutex-protected global connection for thread safety
- wrap state transitions in atomic transactions
- move SQL out of route handlers into db module
- add concise readme
- add uuid-zig dependency for proper uuid v4 generation
- split routes.zig into flows.zig, flow_runs.zig, admin.zig, logs.zig
- extract shared utilities to common.zig (normalizeUuid, getTimestamp, etc)
- add sqlite pragmas: WAL mode, busy_timeout, foreign_keys
- add test-flow-sequence script for integration testing
- add loq.toml config for line count limits
all flow run tests pass (success and failure cases)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
uses prek (rust pre-commit replacement) with uvx
- loq: line limit check
- zig fmt: format check
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- http server with thread pool (std.http.Server + std.Thread.Pool)
- sqlite database with flow, flow_run, flow_run_state tables
- endpoints: /health, /flows/, /flow_runs/, /flow_runs/{id}/set_state
- test script using uv shebang to verify python client compatibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
single binary with subcommands:
prefect-server # server + services (default)
prefect-server --no-services # API only (horizontal scaling)
prefect-server services # background services only
kubernetes:
- k8s/ manifests for horizontal scaling deployment
- api-deployment: multiple replicas with --no-services
- services-deployment: single instance with `services` subcommand
- postgres + redis for data layer
- kustomize support
ci:
- tangled.org workflow for tests and build
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
orchestration:
- implement WaitForScheduledTime rule with unit tests
- refactor orchestration module structure (rules, transforms, types)
- add StateTypeSet bitfield for state matching
testing:
- add test-matrix script for all backend combinations
(sqlite×memory, sqlite×redis, postgres×memory, postgres×redis)
- fix postgres schema: add missing next_scheduled_start_time column
- add next_scheduled_start_time index for postgres
other:
- refactor broker and services module structure
- improve scheduler with cron support
- update benchmarks and test scripts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- rename docker-compose.yml to compose.yml
- add PREFECT_SERVER_API_HOST=0.0.0.0 for container networking
- fix env var names to match code (PREFECT_DATABASE_URL, PREFECT_REDIS_MESSAGING_HOST)
- add docker-full-test recipe for testing postgres + redis stack
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
worker polling endpoint:
- POST /work_pools/{name}/get_scheduled_flow_runs
- returns WorkerFlowRunResponse[] (work_pool_id, work_queue_id, flow_run)
- updates queue/pool/deployment status to READY on poll
- supports work_queue_names filter and scheduled_before time filter
datetime standardization:
- unified ISO 8601 format: 2025-01-21T12:34:56.123456Z
- updated sqlite schema defaults: strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
- updated postgres schema defaults: TO_CHAR(NOW() AT TIME ZONE 'UTC', ...)
- added time utilities: parse(), lessOrEqual(), isPast(), formatMicros()
deployment work_queue_id resolution:
- resolve work_queue_id from work_pool_name on deployment create
- uses pool's default queue if work_queue_name not specified
- enables flow runs created from deployments to be found by worker polling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- default host: 127.0.0.1 (was 0.0.0.0)
- default log level: WARNING (was INFO)
- default limits: 200 for work_pools/queues/workers (was 10)
- postgres pool_size: 5 (was 10)
- move config audit to docs/scratch/ (working notes, not permanent docs)
dev mode still uses DEBUG logging via justfile.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- dockerfile: multi-stage build with zig 0.15.2, copies facil.io shared lib
- docker-compose: server, redis, postgres, test services with healthchecks
- justfile: docker-test, docker-full, bench-compare, test-client commands
- benchmark: fix avg calculation to include results with timing data
- test-serve: add uv script header for prefect dependency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
adds full deployment support including:
- deployment table with CRUD API (create, read, update, delete, filter, count)
- deployment_schedule table with CRUD API
- get_scheduled_flow_runs endpoint for runner polling
- create_flow_run from deployment
- pause/resume deployment endpoints
- .serve() now works end-to-end
also fixes event backfill to apply subscriber filters
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
adds the foundation for worker-based execution:
- work_pool table + full CRUD API
- work_queue table + CRUD (default queue auto-created with pool)
- worker table + heartbeat upsert
- status tracking (NOT_READY → READY on first heartbeat)
- reserved pool protection (prefect-* pools)
split API handlers into separate files to stay under line limits:
- src/api/work_pools.zig (pool CRUD + router)
- src/api/work_pool_queues.zig (queue handlers)
- src/api/work_pool_workers.zig (worker handlers)
tested with Python prefect client - all operations compatible.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
test harness:
- add variables test with full CRUD coverage
- add response validation helper for field/type checking
- validate flow, flow_run, variable, and block_type responses
api consistency:
- include variable name in conflict error message (like Python)
- fix timestamp format consistency: use ISO 8601 (with T, Z, microseconds)
instead of SQLite's datetime('now') format
- ensures create and update return consistent timestamp format
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
full CRUD for variables with endpoints:
- POST /api/variables/ (create)
- POST /api/variables/filter (list)
- POST /api/variables/count
- GET /api/variables/{id}
- GET /api/variables/name/{name}
- PATCH /api/variables/{id}
- PATCH /api/variables/name/{name}
- DELETE /api/variables/{id}
- DELETE /api/variables/name/{name}
adds variable table to both sqlite and postgres schemas with:
- id, name (unique), value (json), tags (json array)
- created/updated timestamps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- refactor StoredMessage to heap allocation (no more 8KB truncation)
- refactor ParsedEvent to heap allocation (no more fixed buffer truncation)
- add ephemeral Redis group cleanup on unsubscribe
- implement backfill for /events/out (queries recent events before streaming)
- use std.json.Stringify.valueAlloc for proper JSON escaping
- add Col struct for documenting column indices in queryRecent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
memory broker was pre-allocating 50,000 × 8KB = 400MB for message
storage. replaced with ArrayList-backed Queue that grows on demand,
matching Python's asyncio.Queue() behavior.
memory usage: 432MB → 39MB
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- parse client filter in /events/out (was always match_all)
- increase buffer sizes: persister 32KB payload, broadcaster 64KB+heap
- add subscribeEphemeral for broadcaster ($ start_id, no replay)
- join flush timer thread on shutdown
- release mutex before websocket writes (avoids blocking)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fix event pipeline: /events/in publishes to broker, event_broadcaster
subscribes with ephemeral consumer group for WS fan-out
- fix postgres backend: add bigint() method for BIGINT columns, use
int() for INTEGER (i32) columns to match pg.zig strict typing
- add redis broker backend with XADD/XREADGROUP/XAUTOCLAIM for streams
- add benchmark matrix: --matrix flag tests sqlite/postgres × memory/redis
- fix redis RESP: handle fragmented TCP with readMore() and buffer growth
- fix redis command buffer: dynamic allocation for large events (>16KB)
- rename ws_distributor → event_broadcaster (cleaner abstraction)
- add CLAUDE.md files for broker/, db/, services/ subsystems
- add docker-compose.yml for redis + postgres dev services
benchmark results (3 iterations avg):
| db | broker | time | memory |
|----------|--------|------|---------|
| sqlite | memory | 27ms | 329MB |
| sqlite | redis | 28ms | 39MB |
| postgres | memory | 67ms | 428MB |
| postgres | redis | 79ms | 34MB |
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
introduces broker module with similar pattern to database backend:
- Broker union type with memory and redis variants
- Message struct with id, topic, data, attributes, timestamp
- Publisher/Consumer interfaces for pub/sub pattern
- BoundedChannel generic for in-process message passing
- MemoryBroker implementation using bounded channels
- RedisBroker stub for future Redis Streams implementation
infrastructure changes:
- main.zig initializes broker on startup (PREFECT_BROKER_BACKEND env var)
- test script: scripts/test-broker-backends
- justfile: `just test-broker [memory|redis|all]`
the broker is initialized but not yet wired into event_persister or WS
subscribers - this sets up the foundation for incremental migration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- flow_runs.setState: only lock mutex for sqlite (postgres pool handles concurrency)
- block_types.update: use dialect-specific SQL for datetime (NOW()::TEXT vs datetime('now'))
- docs/database.md: update transaction docs to show Transaction pattern instead of stale begin/commit/execUnsafe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add Transaction type with dedicated pg connection for atomic operations
- add execWithRowCount() returning affected rows (fixes delete/trim on postgres)
- mutex now only locks for sqlite (postgres pool handles concurrency)
- dialect-specific SQL for INSERT OR IGNORE vs ON CONFLICT DO NOTHING
- dialect-specific SQL for datetime('now') vs NOW()::TEXT
- consolidate schema: sqlite.zig now uses schema/sqlite.zig
- remove deprecated begin/commit/rollback methods (use Transaction instead)
- add --workload arg to benchmark script for flexible workload selection
- simplify justfile: bench takes server/workload/iterations, test-db takes backend
- add loq exception for backend.zig (core abstraction, 600 line limit)
both sqlite and postgres integration tests pass
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add backend.zig with unified Backend, Row, Rows types
- add dialect.zig for SQL dialect helpers (placeholder rewriting)
- add pg.zig dependency for PostgreSQL support
- migrate all entity modules to use backend abstraction
- add schema/ directory with dialect-specific DDL
- add test-db-backends script for backend testing
- add hashing.zig for canonical JSON checksum generation
the backend abstraction supports:
- automatic placeholder rewriting (? → $1, $2 for postgres)
- unified row interface across both backends
- connection pooling for postgres, single conn + mutex for sqlite
- environment-based backend selection (PREFECT_DATABASE_BACKEND)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- test-api-sequence now reports per-section timing and request counts
- benchmark script shows zig vs python comparison by section
- tracks memory usage via psutil
- fix event_persister import after common.zig removal
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
replaces manual bufPrint JSON templating with streaming Stringify API:
- extract common sendJson helpers to utilities/json.zig
- add writeFlowObject, writeFlowRunObject, writeTaskRunObject helpers
- handle nullable fields and nested objects properly
- use beginWriteRaw/endWriteRaw for pre-formatted JSON from db
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
implements full blocks support with:
- block_types: create, filter, get by slug, update
- block_schemas: create, filter, get by checksum/id (server-side sha256 checksum)
- block_documents: create, filter, get, update, delete
uses std.json.Stringify streaming pattern for json serialization.
health endpoint now returns "ok" (text/plain) to match python server.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- move uuid functions to utilities/uuid.zig
- move timestamp functions to utilities/time.zig
- create api/routing.zig for path extraction and run name generation
- rename extractIdSimple to extractIdAfter (actually describes what it does)
- delete common.zig
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- implement POST /flows/filter and POST /task_runs/filter endpoints
- split db/sqlite.zig into domain modules (flows, flow_runs, task_runs, events)
to respect 500 line limit per file
- move messaging.zig from events/ to utilities/ to match prefect structure
- consolidate test scripts in scripts/ with descriptive names:
- test-flow: SDK integration test
- test-api-sequence: comprehensive HTTP API test
- test-events-subscribe: websocket subscription test
- test-events-sdk: events with SDK test
- test-websocket: websocket ping/pong test
- convert all scripts to uv inline script metadata format
- update CLAUDE.md and ROADMAP.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
implements global orchestration transforms from python prefect server:
- SetStartTime: record when first entering RUNNING state
- SetEndTime: record when entering terminal state (COMPLETED/FAILED/etc)
- IncrementRunTime: accumulate time spent in RUNNING state
- IncrementRunCount: increment run_count when entering RUNNING
also fixes timestamp precision from seconds to microseconds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- messaging.zig: subscriber management with filtering support
- main.zig: /events/out handler with auth/filter protocol
- broadcasts events to connected subscribers in real-time
- test_events_out.py: integration test verifying the flow
protocol:
1. client sends {"type": "auth", "token": "..."}
2. server responds {"type": "auth_success"}
3. client sends {"type": "filter", "filter": {...}}
4. server streams {"type": "event", "event": {...}}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- scripts/benchmark: compare zig vs python prefect servers
- justfile: bench-zig, bench-python, bench-compare commands
- src/main.zig: improved request error logging
- docs: rename prefect-zig to prefect-server throughout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fix event_persister to store resource/payload/related correctly
(was storing full raw json as resource, empty as payload)
- fix follows field to be null instead of empty string
- add truncation detection with warning logs
- expand test_flow.py with cache policy and transaction tests
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
concise overview of implemented components:
- web server: route dispatch, resource handlers
- database: sqlite layer, query patterns
- services: background worker registry
- messaging: bounded channel, backpressure
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- remove static buffer pattern in favor of allocator-based approach
- db functions now take Allocator param for cleaner memory management
- add task_run table and full api: create, read, set_state
- task runs support optional flow_run_id with proper NULL handling
- idempotency via task_key+dynamic_key lookup
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- websocket endpoint /api/events/in for receiving client events
- event persistence with batching, deduplication, retention trimming
- messaging layer with bounded channel (50k capacity) for backpressure
- service registry pattern for background workers
- uuid format fix: store/return standard 36-char dashed format
- structured logging with level control via env vars
- GET /api/flows/{id} endpoint
- justfile for dev commands
- test scripts for python client
- CLAUDE.md and ROADMAP.md for project documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- add uuid-zig dependency for proper uuid v4 generation
- split routes.zig into flows.zig, flow_runs.zig, admin.zig, logs.zig
- extract shared utilities to common.zig (normalizeUuid, getTimestamp, etc)
- add sqlite pragmas: WAL mode, busy_timeout, foreign_keys
- add test-flow-sequence script for integration testing
- add loq.toml config for line count limits
all flow run tests pass (success and failure cases)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- http server with thread pool (std.http.Server + std.Thread.Pool)
- sqlite database with flow, flow_run, flow_run_state tables
- endpoints: /health, /flows/, /flow_runs/, /flow_runs/{id}/set_state
- test script using uv shebang to verify python client compatibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>