Chapter card → reviewed spec
A chapter is a contract.
Testkenn turns it into one.
Every Breathline Federation title carries the same five fields on its card: title, promise, beats, keywords, BISAC. Testkenn treats those five as the source of truth for the backend that ships alongside the book — Gemini drafts the spec, Claude scores it against the series rubric, and no spec reaches the encoder without a human pressing Approve.
01 — INTAKE
Paste the card, keep the fields honest
The form below is the exact shape of the POST /api/cards body. Beats are one per line — the drafter maps each beat to at least one endpoint or state transition, so a vague beat produces a vague spec. BISAC codes are validated against the subject list before the card is written to SQLite.
02 — TWO-STAGE PIPELINE
Draft on the left, criticism on the right
Stage one asks Gemini for a backend spec in YAML: entities, endpoints, state machine, and the beat-to-endpoint map. Stage two hands that YAML to Claude with the series rubric and no ability to edit it — the reviewer only scores and lists blockers. Same separation BeardSign uses: the model that writes is never the model that signs off.
Gemini draft — spec.yaml
Claude review — score & blockers
03 — HUMAN PANEL
The gate is a person, not a threshold
A score of 88 does not ship anything. Approve writes the spec to specs with status='approved' and stamps the reviewer; Fix re-runs stage one with your note appended as a constraint; Regenerate discards the draft and starts from the card with a fresh seed. Every press lands in review_events — the history below is that table.
Decision
awaiting draftreview_events
- Nothing logged in this session yet.
04 — SERIES TRACKER
S0 through S13, and where each one stopped
Fourteen stages, one row each. Status moves in one direction — draft, review, approved, encoded — except when a reviewer sends a spec back, which resets it to draft and keeps the old revision in history. Timestamps are the last state change, not the last read.
| Stage | Title | Status | Score | BISAC | Last change |
|---|
05 — DATA MODEL
Four tables, no ORM
SQLite through better-sqlite3, synchronous and single-file. Cards are immutable once drafted — an edit writes a new revision rather than mutating the row, so a spec always points at the exact card text it was generated from.
schema.sql
sqlitecards
The five card fields plus stage, revision and author. Beats stored as a JSON array; keywords normalised to lowercase and deduplicated on insert.
specs
Raw YAML from stage one, the model and prompt hash that produced it, and status. One card can hold many specs; only one carries approved.
reviews
Stage two output: four criterion scores, the blocker list, and the total. Stored verbatim so a rubric change never rewrites past verdicts.
review_events
Append-only. Every Fix, Regenerate and Approve with actor, note and timestamp — this is what the tracker's "last change" column reads from.