You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A project has obligations that come back on a cadence: a signing-key rotation every quarter, a dependency audit every month, a certificate renewal before a date that moves each year. Each occurrence has a due date and is done once, and then the obligation is not finished — it is due again.
No record kind holds that shape today:
Kind
Due date
Completion
Recurs
objective
yes
success / stop
no
milestone
yes
exit criteria, reached once
no
work
no
criteria
no
runbook
no
stages passed, per run
yes, per run
A runbook answers in what order — the stages, the same every run (#379). It records no cadence and no next due date, so nothing can say that a run is late or that one was never started. A milestone is the only sub-objective record with a due date, which is why an agent reaching for "due by a date, done when the criteria hold" picks it — and #417's dogfooding audit found exactly that: a recurring signing-key rotation represented as a product milestone, with a target later than its parent objective. The graph then reads the rotation as product direction, and the objective's next state is driven by maintenance.
#417 handles the contract side: it will state that a milestone is reached once and that whatever comes back is not one. This issue is the record the contract has nowhere to point at yet.
Desired outcome
A routine is a recurring obligation of the project: what is owed, how often, and when it is next due. Recording one schedules nothing, dispatches nothing, and advances nothing on its own — the same stance a runbook takes. An occurrence is a work unit, so evidence, criteria, reports and done stay exactly what they are.
self routine add "<obligation>" --every <period> [--next <date>] [--runbook <id|name>] [--why w]
self routine [list] [--project <slug>]
self routine show <id|name>
self routine due <id> --next <date> --why "<why the date moved>"
self routine run <id> [--outcome "<this occurrence>"] # records a work unit linked to the routine; next due advances by --every when that unit is done
self routine retire <id> --why "<why the obligation ended>"
self routine revise <id> --every <period> --why "<what changed>"
--every takes day|week|month|quarter|year or <n> days|weeks|months. It is a period, not a cron expression: the next due date is the last occurrence's done date plus the period, or --next when a person moved it.
routine run is work add with two things on top: a link from the unit to the routine and, when the routine names a runbook, a runbook start under that unit. Nothing else on the unit is different; work done on it is what advances the next due date.
self context renders each live routine on one line under the direction block — <obligation> · next due <date> — and marks one whose next due date has passed as overdue with the run command. That line is the only place cadence is judged, and it compares two recorded dates: no clock beyond the day the render is asked for.
A routine is the third thing self help goals distinguishes from a milestone, and the runbook page's boundary paragraph gains the third side: a runbook is how, a routine is when, a work unit is this once.
Area
Goals and routines
Local-first and data implications
A routine is an entity composed from existing events and labels — the way instructions (#440) and runbooks (#379) are — so no new event type, reducer, or reserved key. The occurrence link is the member-of edge work already records toward objectives and milestones. Next due is derived from the routine's period and the linked units' done dates, so two machines replaying the same merged log render the same line. Nothing here reads the network or writes outside the store.
Boundaries
Not a scheduler: nothing fires, nothing is dispatched, nothing is written at a date. A routine that is overdue is a line a session reads, and a person or the agent they asked runs it.
Namespace: self routine beside self runbook; both are project procedures, one is the order and the other the cadence. "routine" is already the word the issue template uses for the area ("Goals and routines"). "loop" was considered and rejected: in this project it reads as an execution loop.
Verbs: add | list | show | due | run | retire | revise — run rather than start so it is not confused with runbook start, which it may call.
Entity: labels ["routine"] on an ordinary entity, period and next date as payload fields; the occurrence is a plain work unit.
listing shows the obligation and the next due date; nothing else in the store changes
2
routine run on it
a work unit exists, linked to the routine; work show names the routine; if the routine names a runbook a run under that unit is started
3
that unit is done on 2026-10-10
next due reads 2027-01-10 (done date plus the period), not 2027-01-15
4
the day of render is after the next due date and no live occurrence exists
context line reads overdue with the routine run command
5
a live occurrence exists past the due date
context line says which unit is running, no second run is suggested
6
routine due --next moves the date with a reason
the reason is on the record; the next occurrence's done recomputes from its own date plus the period
7
routine retire --why
the line leaves context; existing occurrences keep their link and history
8
the same log replayed on two machines, merged in either order
the same next due date and the same overdue verdict for the same render day
9
self help goals, the runbook page, and the dsh plugin guide
all three state the how / when / this-once boundary in the same words (docs test pins them)
10
#417's health check on a store with one routine and one product milestone
the routine appears in no direction finding and in no objective's next-or-target line
Verification stops there. It does not cover calendars, business days, time zones beyond the calendar day the store already uses, notifications, or any automatic run.
Alternatives or prior art
Keep using a milestone. What the audit found; the milestone's reach state and target date poison the objective's projection, and a reached milestone cannot come back.
Runbook with a cadence field. Rejected: a runbook is the procedure and many routines need no procedure at all (renew a certificate). Coupling them makes every runbook answer a question most of them do not have.
Work unit with a --due date. Rejected as the whole answer: it gives one occurrence a date but nothing records that another is owed after it, which is the property that makes the obligation recurring.
External scheduler or calendar. Out of scope for a local-first store; the routine states what is owed and leaves firing to people and the tools they already run.
Scope gate
One record kind, its seven verbs, the context line, and the three boundary paragraphs. Implementation starts from a case table (docs/maintainers/case-tables/<n>-routines.md) covering the verification rows and the date arithmetic, reviewed before any code.
Problem or workflow
A project has obligations that come back on a cadence: a signing-key rotation every quarter, a dependency audit every month, a certificate renewal before a date that moves each year. Each occurrence has a due date and is done once, and then the obligation is not finished — it is due again.
No record kind holds that shape today:
A runbook answers in what order — the stages, the same every run (#379). It records no cadence and no next due date, so nothing can say that a run is late or that one was never started. A milestone is the only sub-objective record with a due date, which is why an agent reaching for "due by a date, done when the criteria hold" picks it — and #417's dogfooding audit found exactly that: a recurring signing-key rotation represented as a product milestone, with a target later than its parent objective. The graph then reads the rotation as product direction, and the objective's next state is driven by maintenance.
#417 handles the contract side: it will state that a milestone is reached once and that whatever comes back is not one. This issue is the record the contract has nowhere to point at yet.
Desired outcome
A routine is a recurring obligation of the project: what is owed, how often, and when it is next due. Recording one schedules nothing, dispatches nothing, and advances nothing on its own — the same stance a runbook takes. An occurrence is a work unit, so evidence, criteria, reports and done stay exactly what they are.
--everytakesday|week|month|quarter|yearor<n> days|weeks|months. It is a period, not a cron expression: the next due date is the last occurrence's done date plus the period, or--nextwhen a person moved it.routine runiswork addwith two things on top: a link from the unit to the routine and, when the routine names a runbook, arunbook startunder that unit. Nothing else on the unit is different;work doneon it is what advances the next due date.self contextrenders each live routine on one line under the direction block —<obligation> · next due <date>— and marks one whose next due date has passed as overdue with the run command. That line is the only place cadence is judged, and it compares two recorded dates: no clock beyond the day the render is asked for.self help goalsdistinguishes from a milestone, and the runbook page's boundary paragraph gains the third side: a runbook is how, a routine is when, a work unit is this once.Area
Goals and routines
Local-first and data implications
A routine is an entity composed from existing events and labels — the way instructions (#440) and runbooks (#379) are — so no new event type, reducer, or reserved key. The occurrence link is the
member-ofedge work already records toward objectives and milestones. Next due is derived from the routine's period and the linked units' done dates, so two machines replaying the same merged log render the same line. Nothing here reads the network or writes outside the store.Boundaries
routine runstarts it, and that is the whole coupling.milestone drop --whyandroutine addare two explicit records, and [Feature]: Enforce one objective, milestone, and work-management contract across Agents #417's guidance names that pair.Naming
self routinebesideself runbook; both are project procedures, one is the order and the other the cadence. "routine" is already the word the issue template uses for the area ("Goals and routines"). "loop" was considered and rejected: in this project it reads as an execution loop.add | list | show | due | run | retire | revise—runrather thanstartso it is not confused withrunbook start, which it may call.["routine"]on an ordinary entity, period and next date as payload fields; the occurrence is a plain work unit.Verification
routine add "rotate signing keys" --every quarter --next 2026-10-15routine runon itwork shownames the routine; if the routine names a runbook a run under that unit is startedroutine runcommandroutine due --nextmoves the date with a reasonroutine retire --whyself help goals, the runbook page, and the dsh plugin guideVerification stops there. It does not cover calendars, business days, time zones beyond the calendar day the store already uses, notifications, or any automatic run.
Alternatives or prior art
--duedate. Rejected as the whole answer: it gives one occurrence a date but nothing records that another is owed after it, which is the property that makes the obligation recurring.Scope gate
One record kind, its seven verbs, the context line, and the three boundary paragraphs. Implementation starts from a case table (
docs/maintainers/case-tables/<n>-routines.md) covering the verification rows and the date arithmetic, reviewed before any code.Submission checks