The ratchet
Five record types, plus the router's own health line: one JSON object per line, appended and never edited in place.
- remind
- One line per reminder actually delivered, into the buffer of the skill it asked for, with which pattern matched and 160 characters of the prompt.
- invoke
- A tracked skill ran, and how it was triggered: typed by you, suggested by the router, or picked by the model.
- gate
- One commit decision: allow or deny, the reason, how many paths the commit carried, and which passing verify was accepted.
- run
- What the skill appends when it finishes, carrying its version. No hook writes one, so an invoke with no run beside it is what a skill that quit before finishing looks like.
- annotation
- What a later debrief says that run missed, pointing at it by id. Nothing is rewritten.
- health
- One line per session start or resume, so a week with no records can be told apart from a week the router was off.
remind, invoke, gate and run all carry the session and the prompt id, which is what makes separate buffers joinable: a remind and a later invoke of the same skill in the same session is a reminder that converted. An annotation joins to its run by reference instead, and a health line belongs to the router rather than to a session.
Detection is automatic; reinforcement is a decision, made once a week while looking at numbers. The aggregation is the deterministic half: the window runs from the last timestamp in the review watermark, else the last seven days, and the watermark moves only once the review is actually finished. Its candidates are threshold crossings, not opinions: a rule that reminded three times and never converted, a session denied three times with no run behind it, a pattern that matched nothing all window.
The loop is a ratchet: records this week, a deterministic report on Friday, a human yes, an edit to the skill, and next week's records measure whether the edit earned its keep. Twice already the records argued for an upgrade and won.