Four deployment-level fixes landed on the live Cloudflare worker + D1
instance:
1. compiler.py — populate chains table from chains.json. Pre-v1.0 the
table was never filled, which only mattered once D1 (which enforces
FKs by default, unlike SQLite) tried to insert chain_questions. The
cutover failed with FOREIGN KEY constraint failed until chains(id)
was populated.
2. types.ts (worker) — add competency_area, bloom_level, phase, and
human_review_* fields. Worker SQL was already SELECT *, so the new
columns flow through without code changes, but the TypeScript row
interface needed updating for downstream consumers.
3. rate_limit.ts — Math.max(60, …) floor on expirationTtl. Old calc
could emit values as low as 11s, which D1's KV backend rejects
(minimum 60s). Was throwing 1101 on every request after the
deployment. Tail logs showed 'Invalid expiration_ttl of 14'.
4. wrangler.toml — bump SCHEMA_FINGERPRINT to match the v1.0 vault.db
(b97218dae6354b1b…). Without this, /manifest reports
schema_fingerprint_ok: false and clients degrade.
New script:
scripts/ship_d1.py — end-to-end reload of D1 from the current YAMLs.
'vault build' → SQL dump → 'wrangler d1 execute --file'. Handles FK
ordering (chains first, then questions, then chain_questions). Used
for this cutover; repeatable for future schema bumps.
Deployment state (2026-04-22):
Worker URL: https://staffml-vault.mlsysbook-ai-account.workers.dev
D1 database: staffml-vault (254f630f-…) — 9,199 questions loaded
Release hash: 997747a8f43bbd89e03c6bb0e67865f8de35ac8316fbb0457ee0b8f955afb32f
Manifest: curl …/manifest returns 9,199 / schema_fingerprint_ok=true
GET question: /questions/cloud-0185 returns the post-Phase-2 v1.0 record
(zone=mastery, level=L6+, competency_area=latency, …)
Filtered list: /questions?track=cloud&level=L6%2B works with pagination
Site cutover is NOT in this commit. The existing hybrid path
(bundled corpus.json primary + worker /search secondary) keeps
working unchanged. To flip the site entirely to the worker:
export NEXT_PUBLIC_VAULT_API=https://staffml-vault.mlsysbook-ai-account.workers.dev
unset NEXT_PUBLIC_VAULT_FALLBACK
# then: next build && next deploy
That flip converts every caller from sync 'getQuestions()' to async
via corpus-source.ts — deferred because callers need an audit pass
to handle async correctly.