dos-class-cycle — the judge-gated plan-lifecycle tick
Garden the portfolio, but never alone. A plan's class (active / done / parked / …) should track reality — a done plan should not sit "active," a long-idle one should park.
/dos-class-cycleruns ONE tick of that gardening: it evaluates the declared triggers, asks a JUDGE-rung adjudicator (advisory, fail-to-abstain) to approve each candidate transition, applies only the gated ones, and logs the cycle. The class set, the legal transitions, and the failsafes are declared data ([lifecycle]) — a 2-class repo and a richly- classed one run the identical mechanism.
The shape: read the declared lifecycle → evaluate triggers → build candidates
(deterministic order) → judge each → enact gated transitions → log. The cycle
is domain-free mechanism; the taxonomy is [lifecycle] policy; the judge content
is a host dos.judges driver.
Inputs
--dry-run(optional) — evaluate + judge but enact nothing (a preview cycle).
Step 0 — Read the declared lifecycle + the layout
dos doctor --workspace . --json
Read lifecycle — the declared classes, transitions (each {from, to, trigger, auto}), veto_class, max_transitions_per_cycle, and
per_plan_cooldown_hours — and paths (the plan + run dirs). Use these; never
hardcode a class name, a trigger, or a cap. A repo that declared only
active/done cycles with those two; a repo with a richer taxonomy declares more.
Step 1 — Evaluate each declared trigger → candidate transitions
For each declared transition, evaluate its trigger against the portfolio (the
plan-meta classes, the run-archive history, git). A trigger that fires on a plan
proposes that plan for that from→to transition. Build the candidate list in a
DETERMINISTIC order (plan id ascending) so a replay is byte-stable.
Skip a plan that is:
- in the
veto_class(never auto-transitioned — a human moves it by hand), or - transitioned within
per_plan_cooldown_hours(the per-plan cooldown), or - already past
max_transitions_per_cyclecandidates this tick (the cap).
The trigger evaluator is the host's (an opaque trigger token like
all_phases_shipped / idle_30d — the kernel never interprets it). A natural
ground-truth trigger: a plan whose dos enumerate residual is empty AND every
unit dos verifys SHIPPED is a real all_phases_shipped → done candidate.
Step 2 — Judge each candidate (the JUDGE rung, advisory)
A class transition is a judgment call, so it goes to the JUDGE rung — a non-deterministic adjudicator that rules on the residue the deterministic checks left. Resolve the active judge by name and ask it to approve / defer each candidate:
dos judge-eval --judge <name> # the judge seam; built-in `abstain`, shipped `llm`, or a dos.judges plugin
The judge is hedged by the four disciplines (deterministic-first, advisory-only,
fail-to-abstain — a raise/bad-return becomes ABSTAIN, never APPROVE —
abstention-first). The judge content (the prompt) is a host dos.judges driver;
this skill spawns it via the seam and reads its verdict. An ABSTAIN defers the
transition to a human (the safe direction — the kernel never auto-applies on an
abstention).
Step 3 — Enact the gated transitions (auto only where declared)
For each candidate the judge APPROVED and whose declared transition has
auto = true, enact it: rewrite the plan-meta classification: to the to
class, ONE commit per cycle. Read the trunk + ship grammar from dos doctor --json's stamp; do not hardcode a commit prefix. A transition with
auto = false, or one the judge deferred/abstained on, is surfaced for a human —
never enacted.
Step 4 — Log the cycle (even a 0-transition tick)
Write a cycle record under paths.runs: the candidates, the judge verdicts, the
applied transitions, and the failsafe state (cap/cooldown/veto). A heartbeat row
every cycle — even one that applied nothing — so the gardener's history is
auditable.
What this skill deliberately does NOT do (no silent gap)
- No host class taxonomy. Classes/transitions/failsafes are
[lifecycle]data; this skill carries none. A transition naming an unknown class is a config error the kernel raises at load, not a silent skip. - No judge content. The adjudicator prompt is a host
dos.judgesdriver; the skill resolves + spawns it, fail-to-abstain. Forcing the prompt generic would re-couple the kernel. - No transition past a failsafe. The per-cycle cap, the per-plan cooldown, and the veto class are hard gates; the cycle never exceeds them.
Anti-patterns
- ❌ Applying a transition the judge ABSTAINED on — abstention defers to a human; the kernel never auto-applies on an abstention (the fail-to-abstain floor).
- ❌ Hardcoding
ACTIVE/PARK/TOMBor a trigger likeidle_14d— read the declared set fromdos doctor --json'slifecycle. - ❌ Exceeding
max_transitions_per_cyclein one tick — a runaway judge must not churn the whole portfolio; the cap is the backstop.