prefect server in zig

restore scratch docs with status annotations

keep valuable reference info, mark implementation status:
- blocks-implementation.md: API sequences and schema (implemented)
- timestamps.md: timing handling, .serve() vs workers (resolved)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+169
+2
docs/scratch/README.md
··· 4 4 5 5 ## contents 6 6 7 + - [blocks-implementation.md](./blocks-implementation.md) - blocks API sequences and schema (implemented) 7 8 - [configuration-audit.md](./configuration-audit.md) - config parity with python prefect 8 9 - [signal-handling.md](./signal-handling.md) - graceful shutdown patterns (zap/facil.io quirks) 10 + - [timestamps.md](./timestamps.md) - timestamp handling, .serve() vs workers distinction 9 11 - [zig-patterns.md](./zig-patterns.md) - zig 0.15 idioms used in this codebase
+104
docs/scratch/blocks-implementation.md
··· 1 + # blocks implementation 2 + 3 + **status**: implemented. this doc preserved as reference for API call sequences and database schema. 4 + 5 + ## api call sequence 6 + 7 + When user calls `block.save("name")`: 8 + 1. `GET /block_types/slug/{slug}` → 404 if not found 9 + 2. `POST /block_types/` → create type (or `PATCH /block_types/{id}` if exists) 10 + 3. `GET /block_schemas/checksum/{checksum}` → 404 if not found 11 + 4. `POST /block_schemas/` → create schema 12 + 5. `POST /block_documents/` → create document 13 + - if 409 conflict (name exists): `GET` + `PATCH` to update 14 + 15 + When user calls `Block.load("name")`: 16 + 1. `GET /block_types/slug/{slug}/block_documents/name/{name}` → return document with nested schema/type 17 + 18 + ## database tables 19 + 20 + ```sql 21 + block_type ( 22 + id TEXT PRIMARY KEY, 23 + created, updated, 24 + name TEXT NOT NULL, 25 + slug TEXT NOT NULL UNIQUE, 26 + logo_url, documentation_url, description, code_example TEXT, 27 + is_protected INTEGER DEFAULT 0 28 + ) 29 + 30 + block_schema ( 31 + id TEXT PRIMARY KEY, 32 + created, updated, 33 + checksum TEXT NOT NULL, 34 + fields TEXT DEFAULT '{}', -- JSON schema 35 + capabilities TEXT DEFAULT '[]', -- JSON array 36 + version TEXT DEFAULT '1', 37 + block_type_id TEXT FK, 38 + UNIQUE(checksum, version) 39 + ) 40 + 41 + block_document ( 42 + id TEXT PRIMARY KEY, 43 + created, updated, 44 + name TEXT, 45 + data TEXT DEFAULT '{}', -- JSON (encrypted in python, plain for us) 46 + is_anonymous INTEGER DEFAULT 0, 47 + block_type_id TEXT FK, 48 + block_type_name TEXT, -- denormalized 49 + block_schema_id TEXT FK, 50 + UNIQUE(block_type_id, name) 51 + ) 52 + ``` 53 + 54 + ## implementation phases 55 + 56 + ### phase 1: save() support (minimum viable) 57 + - [x] add tables to schema 58 + - [ ] `db/block_types.zig` - insert, getBySlug, update 59 + - [ ] `db/block_schemas.zig` - insert, getByChecksum 60 + - [ ] `db/block_documents.zig` - insert, getById, update 61 + - [ ] `api/block_types.zig`: 62 + - [ ] `GET /block_types/slug/{slug}` 63 + - [ ] `POST /block_types/` 64 + - [ ] `PATCH /block_types/{id}` 65 + - [ ] `api/block_schemas.zig`: 66 + - [ ] `GET /block_schemas/checksum/{checksum}` 67 + - [ ] `POST /block_schemas/` 68 + - [ ] `api/block_documents.zig`: 69 + - [ ] `POST /block_documents/` 70 + - [ ] `PATCH /block_documents/{id}` 71 + - [ ] `GET /block_documents/{id}` 72 + 73 + ### phase 2: load() support 74 + - [ ] `GET /block_types/slug/{slug}/block_documents/name/{name}` 75 + 76 + ### phase 3: filter endpoints 77 + - [ ] `POST /block_types/filter` 78 + - [ ] `POST /block_schemas/filter` 79 + - [ ] `POST /block_documents/filter` 80 + 81 + ### phase 4: nested blocks (if needed) 82 + - [ ] block_schema_reference table 83 + - [ ] block_document_reference table 84 + - [ ] recursive document hydration 85 + 86 + ## test script 87 + 88 + ```python 89 + from prefect.blocks.system import Secret 90 + 91 + # save 92 + secret = Secret(value="my-secret-value") 93 + secret.save("test-secret") 94 + 95 + # load 96 + loaded = Secret.load("test-secret") 97 + print(loaded.get()) 98 + ``` 99 + 100 + ## response formats 101 + 102 + See prefect source for exact JSON shapes: 103 + - `src/prefect/client/schemas/responses.py` 104 + - `src/prefect/server/schemas/core.py`
+63
docs/scratch/timestamps.md
··· 1 + # timestamp handling in prefect 2 + 3 + **status**: mostly resolved. `next_scheduled_start_time` added, timestamp parsing works. 4 + preserved as reference for python/zig differences and .serve() vs workers distinction. 5 + 6 + ## python implementation 7 + 8 + ### storage 9 + - **PostgreSQL**: `TIMESTAMP(timezone=True)` - native timezone-aware 10 + - **SQLite**: `DATETIME()` naive, manually converts to/from UTC 11 + 12 + ### format 13 + all timestamps are UTC timezone-aware. JSON serialization uses ISO 8601: 14 + ``` 15 + 2024-01-22T15:30:45.123456+00:00 16 + ``` 17 + 18 + ### key fields for flow_run 19 + - `expected_start_time` - when the run was originally scheduled 20 + - `next_scheduled_start_time` - **used for scheduling queries** (we're missing this!) 21 + - `start_time` - actual start 22 + - `end_time` - actual end 23 + - `state_timestamp` - when state last changed 24 + 25 + ### get_scheduled_flow_runs query 26 + ```sql 27 + WHERE fr.state_type = 'SCHEDULED' 28 + AND fr.next_scheduled_start_time <= :scheduled_before 29 + ORDER BY fr.next_scheduled_start_time ASC 30 + ``` 31 + 32 + ## our implementation 33 + 34 + ### storage 35 + - **SQLite**: `TEXT` with format `2024-01-22T15:30:45.123456Z` 36 + - using SQLite `strftime('%Y-%m-%dT%H:%M:%fZ', 'now')` for defaults 37 + 38 + ### issues (resolved) 39 + 40 + 1. ~~**missing `next_scheduled_start_time`**~~ ✓ added to flow_run table 41 + 42 + 2. ~~**string comparison is fragile**~~ ✓ timestamp parsing handles multiple formats 43 + 44 + 3. **bandaid fix** - normalizing client timestamps (space→T, +00:00→Z) works and is acceptable 45 + 46 + ## .serve() vs workers 47 + 48 + ### .serve() (Runner) 49 + - creates deployment, starts local polling loop 50 + - calls `POST /deployments/get_scheduled_flow_runs` every N seconds 51 + - executes flows locally in the same process 52 + - **NOT a worker** 53 + 54 + ### workers 55 + - standalone daemon process 56 + - connects to work pools/queues 57 + - work pool workers: `POST /work_pools/{name}/get_scheduled_flow_runs` 58 + - task workers: WebSocket `WS /task_runs/subscriptions/scheduled` 59 + 60 + ### we test 61 + - `test_cron_scheduler` - server-side scheduler creates runs (correct) 62 + - `test_worker_execution` - mislabeled! tests `.serve()` Runner, not a worker 63 + - `test_serve_with_schedule` - verifies deployment has schedule attached (correct)