dos-kernel 0.22.0__py3-none-win_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dos/__init__.py +261 -0
- dos/_bin/dos-hook.exe +0 -0
- dos/_filelock.py +255 -0
- dos/_job_policy.py +97 -0
- dos/_tree.py +145 -0
- dos/admission.py +433 -0
- dos/answer_shape.py +299 -0
- dos/arbiter.py +859 -0
- dos/archive_lock.py +266 -0
- dos/arg_provenance.py +814 -0
- dos/attest.py +472 -0
- dos/breaker.py +311 -0
- dos/churn.py +226 -0
- dos/claim_extract.py +229 -0
- dos/claim_ttl.py +150 -0
- dos/cli.py +8721 -0
- dos/commit_audit.py +666 -0
- dos/completion.py +466 -0
- dos/concurrency_class.py +154 -0
- dos/config.py +1380 -0
- dos/config_lint.py +464 -0
- dos/cooldown.py +390 -0
- dos/coverage.py +387 -0
- dos/dangling_intent.py +287 -0
- dos/data_class.py +397 -0
- dos/decisions.py +1274 -0
- dos/decisions_tui.py +251 -0
- dos/dispatch_top.py +740 -0
- dos/dispatch_top_tui.py +116 -0
- dos/drivers/__init__.py +40 -0
- dos/drivers/ci_status.py +630 -0
- dos/drivers/citation_resolve.py +703 -0
- dos/drivers/decision_stop.py +98 -0
- dos/drivers/export_file.py +173 -0
- dos/drivers/export_otlp.py +275 -0
- dos/drivers/export_statsd.py +242 -0
- dos/drivers/hook_dialects.py +391 -0
- dos/drivers/job.py +47 -0
- dos/drivers/llm_judge.py +360 -0
- dos/drivers/memory_recall.py +1231 -0
- dos/drivers/notify_slack.py +373 -0
- dos/drivers/notify_webhook.py +251 -0
- dos/drivers/operator_judge.py +114 -0
- dos/drivers/os_acceptance.py +228 -0
- dos/drivers/paste_log.py +132 -0
- dos/drivers/plan_scope.py +133 -0
- dos/drivers/self_improve.py +375 -0
- dos/drivers/similarity_judge.py +249 -0
- dos/drivers/state_diff.py +274 -0
- dos/drivers/supervisor.py +347 -0
- dos/drivers/watchdog.py +363 -0
- dos/drivers/workshop.py +160 -0
- dos/durable_schema.py +344 -0
- dos/effect_witness.py +393 -0
- dos/efficiency.py +318 -0
- dos/enforce.py +414 -0
- dos/enumerate.py +776 -0
- dos/env_print.py +378 -0
- dos/event_severity.py +258 -0
- dos/evidence.py +692 -0
- dos/exec_capability.py +256 -0
- dos/export_cursor.py +143 -0
- dos/exporter.py +320 -0
- dos/firing_label.py +353 -0
- dos/fleet_roll.py +226 -0
- dos/gate_classify.py +827 -0
- dos/gh4_coverage.py +179 -0
- dos/git_delta.py +122 -0
- dos/guard.py +215 -0
- dos/health.py +552 -0
- dos/help_summary.py +519 -0
- dos/home.py +934 -0
- dos/hook_binary.py +194 -0
- dos/hook_dialect.py +271 -0
- dos/hook_exit.py +191 -0
- dos/hook_install.py +437 -0
- dos/id_alloc.py +304 -0
- dos/improve.py +499 -0
- dos/intent_ledger.py +635 -0
- dos/interpret.py +176 -0
- dos/intervention.py +769 -0
- dos/intervention_eval.py +371 -0
- dos/journal_delta.py +308 -0
- dos/judge_eval.py +328 -0
- dos/judges.py +366 -0
- dos/lane_infer.py +127 -0
- dos/lane_journal.py +1001 -0
- dos/lane_lease.py +952 -0
- dos/lane_overlap.py +228 -0
- dos/lease_health.py +282 -0
- dos/lifecycle.py +211 -0
- dos/liveness.py +352 -0
- dos/lock_modes.py +185 -0
- dos/log_source.py +395 -0
- dos/loop_decide.py +1746 -0
- dos/marker_gate.py +254 -0
- dos/marker_sensor.py +396 -0
- dos/noop_streak.py +280 -0
- dos/notify.py +479 -0
- dos/observe.py +175 -0
- dos/oracle.py +1661 -0
- dos/overlap_eval.py +214 -0
- dos/overlap_policy.py +342 -0
- dos/packet_sidecar.py +267 -0
- dos/phase_shipped.py +1985 -0
- dos/pick_priority.py +225 -0
- dos/pickable.py +369 -0
- dos/picker_oracle.py +1037 -0
- dos/plan_board.py +513 -0
- dos/plan_board_tui.py +113 -0
- dos/plan_source.py +455 -0
- dos/posttool_sensor.py +528 -0
- dos/precursor_gate.py +499 -0
- dos/precursor_gate_eval.py +239 -0
- dos/preflight.py +825 -0
- dos/pretool_sensor.py +490 -0
- dos/proc_delta.py +181 -0
- dos/productivity.py +296 -0
- dos/provider_limit.py +242 -0
- dos/py.typed +4 -0
- dos/reason_morphology.py +299 -0
- dos/reasons.py +449 -0
- dos/reconcile.py +173 -0
- dos/recurring_wedge.py +206 -0
- dos/render.py +393 -0
- dos/result_state.py +468 -0
- dos/resume.py +578 -0
- dos/resume_evidence.py +293 -0
- dos/retention.py +344 -0
- dos/reward.py +372 -0
- dos/rewind.py +587 -0
- dos/rewind_evidence.py +168 -0
- dos/rewind_tokens.py +252 -0
- dos/run_id.py +342 -0
- dos/scope.py +520 -0
- dos/scope_source.py +382 -0
- dos/scout.py +982 -0
- dos/self_modify.py +209 -0
- dos/sibling_scan.py +569 -0
- dos/skills/EXAMPLES.md +584 -0
- dos/skills/dos-class-cycle/SKILL.md +107 -0
- dos/skills/dos-dispatch/SKILL.md +177 -0
- dos/skills/dos-dispatch-loop/SKILL.md +254 -0
- dos/skills/dos-goal-gate/SKILL.md +269 -0
- dos/skills/dos-next-up/SKILL.md +231 -0
- dos/skills/dos-promote/SKILL.md +114 -0
- dos/skills/dos-replan/SKILL.md +159 -0
- dos/skills/dos-replan-loop/SKILL.md +114 -0
- dos/skills/dos-self-improve/SKILL.md +213 -0
- dos/skills/dos-supervise-loop/SKILL.md +180 -0
- dos/skills/dos-unstick/SKILL.md +108 -0
- dos/skills/dos-witness-claim/SKILL.md +251 -0
- dos/stamp.py +1002 -0
- dos/state_health.py +387 -0
- dos/status.py +114 -0
- dos/stop_policy.py +334 -0
- dos/supervise.py +1014 -0
- dos/testwitness.py +392 -0
- dos/timeline.py +1027 -0
- dos/tokens.py +485 -0
- dos/tool_stream.py +393 -0
- dos/tool_stream_eval.py +226 -0
- dos/trace.py +524 -0
- dos/verdict.py +140 -0
- dos/verdict_cli.py +189 -0
- dos/verdict_journal.py +497 -0
- dos/verdict_rollup.py +217 -0
- dos/verdicts.py +181 -0
- dos/wedge_reason.py +282 -0
- dos_kernel-0.22.0.dist-info/METADATA +859 -0
- dos_kernel-0.22.0.dist-info/RECORD +178 -0
- dos_kernel-0.22.0.dist-info/WHEEL +5 -0
- dos_kernel-0.22.0.dist-info/entry_points.txt +39 -0
- dos_kernel-0.22.0.dist-info/licenses/LICENSE +21 -0
- dos_kernel-0.22.0.dist-info/top_level.txt +2 -0
- dos_mcp/__init__.py +52 -0
- dos_mcp/py.typed +2 -0
- dos_mcp/server.py +779 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dos-class-cycle
|
|
3
|
+
description: One automatic plan-class lifecycle tick. Reads the DECLARED class set + transition list from the workspace `[lifecycle]` table (not a hardcoded taxonomy), evaluates each trigger, spawns a read-only JUDGE-rung adjudicator (the `dos.judges` seam — advisory, fail-to-abstain) to approve/defer each candidate transition, applies the gated transitions as plan-meta edits + one commit per cycle, and logs to the run archive. Failsafes (per-cycle cap, per-plan cooldown, a veto class) are `[lifecycle]` data; the judge content is a host `dos.judges` driver. Every path/class comes from `dos doctor --json`. Use to garden a plan portfolio's lifecycle automatically, judge-gated. The DOS lifecycle gardener (SKP Axis 5, docs/207 Phase 5c).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dos-class-cycle — the judge-gated plan-lifecycle tick
|
|
7
|
+
|
|
8
|
+
> **Garden the portfolio, but never alone.** A plan's class (active / done /
|
|
9
|
+
> parked / …) should track reality — a done plan should not sit "active," a
|
|
10
|
+
> long-idle one should park. `/dos-class-cycle` runs ONE tick of that gardening:
|
|
11
|
+
> it evaluates the declared triggers, asks a **JUDGE-rung adjudicator** (advisory,
|
|
12
|
+
> fail-to-abstain) to approve each candidate transition, applies only the gated
|
|
13
|
+
> ones, and logs the cycle. The class set, the legal transitions, and the
|
|
14
|
+
> failsafes are **declared data** (`[lifecycle]`) — a 2-class repo and a richly-
|
|
15
|
+
> classed one run the identical mechanism.
|
|
16
|
+
|
|
17
|
+
The shape: **read the declared lifecycle → evaluate triggers → build candidates
|
|
18
|
+
(deterministic order) → judge each → enact gated transitions → log.** The cycle
|
|
19
|
+
is domain-free mechanism; the taxonomy is `[lifecycle]` policy; the judge content
|
|
20
|
+
is a host `dos.judges` driver.
|
|
21
|
+
|
|
22
|
+
## Inputs
|
|
23
|
+
|
|
24
|
+
- `--dry-run` (optional) — evaluate + judge but enact nothing (a preview cycle).
|
|
25
|
+
|
|
26
|
+
## Step 0 — Read the declared lifecycle + the layout
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
dos doctor --workspace . --json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Read `lifecycle` — the declared `classes`, `transitions` (each `{from, to,
|
|
33
|
+
trigger, auto}`), `veto_class`, `max_transitions_per_cycle`, and
|
|
34
|
+
`per_plan_cooldown_hours` — and `paths` (the plan + run dirs). **Use these; never
|
|
35
|
+
hardcode a class name, a trigger, or a cap.** A repo that declared only
|
|
36
|
+
`active`/`done` cycles with those two; a repo with a richer taxonomy declares more.
|
|
37
|
+
|
|
38
|
+
## Step 1 — Evaluate each declared trigger → candidate transitions
|
|
39
|
+
|
|
40
|
+
For each declared `transition`, evaluate its `trigger` against the portfolio (the
|
|
41
|
+
plan-meta classes, the run-archive history, git). A trigger that fires on a plan
|
|
42
|
+
proposes that plan for that `from→to` transition. Build the candidate list in a
|
|
43
|
+
DETERMINISTIC order (plan id ascending) so a replay is byte-stable.
|
|
44
|
+
|
|
45
|
+
Skip a plan that is:
|
|
46
|
+
- in the `veto_class` (never auto-transitioned — a human moves it by hand), or
|
|
47
|
+
- transitioned within `per_plan_cooldown_hours` (the per-plan cooldown), or
|
|
48
|
+
- already past `max_transitions_per_cycle` candidates this tick (the cap).
|
|
49
|
+
|
|
50
|
+
The trigger evaluator is the host's (an opaque trigger token like
|
|
51
|
+
`all_phases_shipped` / `idle_30d` — the kernel never interprets it). A natural
|
|
52
|
+
ground-truth trigger: a plan whose `dos enumerate` residual is empty AND every
|
|
53
|
+
unit `dos verify`s SHIPPED is a real `all_phases_shipped` → done candidate.
|
|
54
|
+
|
|
55
|
+
## Step 2 — Judge each candidate (the JUDGE rung, advisory)
|
|
56
|
+
|
|
57
|
+
A class transition is a judgment call, so it goes to the **JUDGE rung** — a
|
|
58
|
+
non-deterministic adjudicator that rules on the residue the deterministic checks
|
|
59
|
+
left. Resolve the active judge by name and ask it to approve / defer each
|
|
60
|
+
candidate:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
dos judge-eval --judge <name> # the judge seam; built-in `abstain`, shipped `llm`, or a dos.judges plugin
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The judge is hedged by the four disciplines (deterministic-first, advisory-only,
|
|
67
|
+
**fail-to-abstain** — a raise/bad-return becomes ABSTAIN, never APPROVE —
|
|
68
|
+
abstention-first). The judge *content* (the prompt) is a host `dos.judges` driver;
|
|
69
|
+
this skill spawns it via the seam and reads its verdict. An ABSTAIN defers the
|
|
70
|
+
transition to a human (the safe direction — the kernel never auto-applies on an
|
|
71
|
+
abstention).
|
|
72
|
+
|
|
73
|
+
## Step 3 — Enact the gated transitions (auto only where declared)
|
|
74
|
+
|
|
75
|
+
For each candidate the judge APPROVED **and** whose declared transition has
|
|
76
|
+
`auto = true`, enact it: rewrite the plan-meta `classification:` to the `to`
|
|
77
|
+
class, ONE commit per cycle. Read the trunk + ship grammar from `dos doctor
|
|
78
|
+
--json`'s `stamp`; **do not hardcode a commit prefix.** A transition with
|
|
79
|
+
`auto = false`, or one the judge deferred/abstained on, is surfaced for a human —
|
|
80
|
+
never enacted.
|
|
81
|
+
|
|
82
|
+
## Step 4 — Log the cycle (even a 0-transition tick)
|
|
83
|
+
|
|
84
|
+
Write a cycle record under `paths.runs`: the candidates, the judge verdicts, the
|
|
85
|
+
applied transitions, and the failsafe state (cap/cooldown/veto). A heartbeat row
|
|
86
|
+
every cycle — even one that applied nothing — so the gardener's history is
|
|
87
|
+
auditable.
|
|
88
|
+
|
|
89
|
+
## What this skill deliberately does NOT do (no silent gap)
|
|
90
|
+
|
|
91
|
+
- **No host class taxonomy.** Classes/transitions/failsafes are `[lifecycle]`
|
|
92
|
+
data; this skill carries none. A transition naming an unknown class is a config
|
|
93
|
+
error the kernel raises at load, not a silent skip.
|
|
94
|
+
- **No judge content.** The adjudicator prompt is a host `dos.judges` driver; the
|
|
95
|
+
skill resolves + spawns it, fail-to-abstain. Forcing the prompt generic would
|
|
96
|
+
re-couple the kernel.
|
|
97
|
+
- **No transition past a failsafe.** The per-cycle cap, the per-plan cooldown, and
|
|
98
|
+
the veto class are hard gates; the cycle never exceeds them.
|
|
99
|
+
|
|
100
|
+
## Anti-patterns
|
|
101
|
+
|
|
102
|
+
- ❌ Applying a transition the judge ABSTAINED on — abstention defers to a human;
|
|
103
|
+
the kernel never auto-applies on an abstention (the fail-to-abstain floor).
|
|
104
|
+
- ❌ Hardcoding `ACTIVE`/`PARK`/`TOMB` or a trigger like `idle_14d` — read the
|
|
105
|
+
declared set from `dos doctor --json`'s `lifecycle`.
|
|
106
|
+
- ❌ Exceeding `max_transitions_per_cycle` in one tick — a runaway judge must not
|
|
107
|
+
churn the whole portfolio; the cap is the backstop.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dos-dispatch
|
|
3
|
+
description: End-to-end plan-and-ship for one lane — snapshot the portfolio with /dos-next-up, take a lane lease via `dos arbitrate` so parallel dispatches don't collide, gate the empty case via `dos gate`, ship the packet, and archive the run under the configured run dir. Driven entirely by `dos` verbs + the workspace's `dos.toml`; names no host path, lane, or commit convention. Use when you want to plan and ship the next batch on one lane in a single command, with concurrency safety. The DOS reference dispatch workflow (SKP Axis 5).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dos-dispatch — the generic chained snapshot→ship cycle
|
|
7
|
+
|
|
8
|
+
> **The concurrency-safe dispatch.** It chains `/dos-next-up` (the packet) to a
|
|
9
|
+
> ship, but first takes a **lane lease** through the admission kernel so several
|
|
10
|
+
> dispatches on disjoint lanes run in parallel without editing the same files.
|
|
11
|
+
> The "may I run on this lane" decision is the kernel's (`dos arbitrate`), not
|
|
12
|
+
> inline prose. Every path/lane comes from `dos doctor --json`; nothing is
|
|
13
|
+
> hardcoded.
|
|
14
|
+
|
|
15
|
+
The shape: **discover → take a lane → snapshot → gate → ship → archive.** The
|
|
16
|
+
lane taxonomy and the run-dir location are data (`[lanes]`, `[paths]`); the
|
|
17
|
+
admission and the gate verdict are kernel syscalls.
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `--lane <name>` (optional) — the lane to dispatch on (a name from the active
|
|
22
|
+
`[lanes]`). Omitted = a bare auto-pick: the arbiter picks a free lane from the
|
|
23
|
+
`autopick` ladder.
|
|
24
|
+
- `--leases <json>` (optional) — the live leases other dispatches hold, as a JSON
|
|
25
|
+
list of `{lane, lane_kind, tree}` (the arbiter keys exclusivity on `lane_kind`,
|
|
26
|
+
so include it — `cluster`/`keyword`/`global`). In a real loop these come from a
|
|
27
|
+
status query; for a single dispatch this is usually `[]`.
|
|
28
|
+
|
|
29
|
+
## Step 0 — Discover the layout + the lane taxonomy
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
dos doctor --workspace . --json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Read `lanes` (the taxonomy), `paths.next_packets` (packet output), and
|
|
36
|
+
`paths.runs` (the run dir to archive under). **Use these; never hardcode a lane
|
|
37
|
+
name or a run path.**
|
|
38
|
+
|
|
39
|
+
## Step 1 — Take a lane lease (the admission kernel)
|
|
40
|
+
|
|
41
|
+
Ask the kernel whether this dispatch may run on the requested lane, given the
|
|
42
|
+
live leases. The arbiter runs the tree-disjointness algebra over the lanes'
|
|
43
|
+
declared trees — two dispatches on disjoint trees both ADMIT; overlapping trees
|
|
44
|
+
COLLIDE.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
dos arbitrate --workspace . --lane <LANE> --kind cluster --leases '<LIVE_LEASES>'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Read the `LaneDecision` JSON: `{outcome, lane, tree, reason, free_clusters, …}`.
|
|
51
|
+
|
|
52
|
+
- `outcome: "acquire"` → admitted. `lane` is the lane to run on (may differ from
|
|
53
|
+
the request when auto-pick reassigned it); `tree` is its file tree. Proceed.
|
|
54
|
+
- `outcome: "refuse"` → not admitted. `reason` explains why; `free_clusters`
|
|
55
|
+
lists lanes you could pick instead. **Stop** (or retry on a free lane). Do not
|
|
56
|
+
force — `--force` is an operator-only override, not an automation default.
|
|
57
|
+
|
|
58
|
+
The exit code mirrors the outcome (0 = acquire, 1 = refuse), so the screenplay
|
|
59
|
+
can branch on it directly.
|
|
60
|
+
|
|
61
|
+
## Step 2 — Snapshot the portfolio (the packet)
|
|
62
|
+
|
|
63
|
+
Run `/dos-next-up` scoped to the acquired lane:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
/dos-next-up --scope <LANE>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
It writes a packet under `paths.next_packets` and returns its path + a gate
|
|
70
|
+
verdict. Capture the packet path and its `.dispositions-<tag>.json` sidecar.
|
|
71
|
+
|
|
72
|
+
## Step 3 — Gate the empty case (typed verdict)
|
|
73
|
+
|
|
74
|
+
Before shipping, classify the packet so an empty packet doesn't launch a no-op
|
|
75
|
+
ship:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
dos gate --workspace . <paths.next_packets>/.dispositions-<tag>.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Branch on the exit code (the verdict IS the code):
|
|
82
|
+
|
|
83
|
+
- `0` **LIVE** → there is dispatchable work; proceed to Step 4 (ship).
|
|
84
|
+
- `3` **DRAIN** → empty backlog; **skip the ship**, archive a no-op, report drained.
|
|
85
|
+
- `4` **STALE-STAMP** → shipped-but-unstamped drift; skip the ship, surface the
|
|
86
|
+
drift for reconciliation (a `/dos-replan` can stamp it).
|
|
87
|
+
- `5` **BLOCKED** → picks blocked; skip, surface.
|
|
88
|
+
- `6` **RACE** → lost a render race; retry the snapshot once.
|
|
89
|
+
|
|
90
|
+
## Step 4 — Ship the packet (LIVE only)
|
|
91
|
+
|
|
92
|
+
Launch the packet's dispatch list (the per-pick prompts `/dos-next-up` rendered).
|
|
93
|
+
How you ship is host-shaped — the generic baseline launches each pick's prompt as
|
|
94
|
+
its own agent. Record what shipped.
|
|
95
|
+
|
|
96
|
+
## Step 5 — Archive the run
|
|
97
|
+
|
|
98
|
+
Write a run record under `paths.runs` (the run dir from `dos doctor --json`): the
|
|
99
|
+
lane, the packet path, the gate verdict, and what shipped. Commit it with a
|
|
100
|
+
generic subject — **read your trunk and ship-grammar from config; do not hardcode
|
|
101
|
+
a commit prefix.** (`dos doctor --json`'s `stamp` names the active grammar.)
|
|
102
|
+
|
|
103
|
+
## Step 6 — Release the lane lease
|
|
104
|
+
|
|
105
|
+
If you took a cross-process `dos lease` for the archive, release it:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
dos lease --workspace . release <owner>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The lane lease itself is advisory state in `live_leases`; a real loop
|
|
112
|
+
(`/dos-dispatch-loop`) threads it forward. A single dispatch simply finishes.
|
|
113
|
+
|
|
114
|
+
## What this skill deliberately does NOT do (no silent gap)
|
|
115
|
+
|
|
116
|
+
- **No per-pick soft-claim leasing.** It takes a *lane* lease (`dos arbitrate`),
|
|
117
|
+
not the heavy per-pick soft-claim core (`CLAUDE.md` heavy tier). Two loops on
|
|
118
|
+
the same lane serialize on the lane, not on individual picks.
|
|
119
|
+
- **No rate-limit resume / focus scheduler.** Those are the host's heavy tier; a
|
|
120
|
+
generic dispatch ships once and archives. `/dos-dispatch-loop` adds the cadence.
|
|
121
|
+
- **No host packet template / commit subject.** It reads the ship grammar from
|
|
122
|
+
`[stamp]` and assembles a generic archive record.
|
|
123
|
+
|
|
124
|
+
**Log the gap, never silently skip it.** The first time the skill would have
|
|
125
|
+
reached for one of these (a per-pick soft-claim, a focus-scheduler pick, a
|
|
126
|
+
rate-limit resume), emit a one-line `log` naming what it is not doing — so the
|
|
127
|
+
capability gap is surfaced at runtime, matching `/dos-dispatch-loop`.
|
|
128
|
+
|
|
129
|
+
## Worked example (live transcript)
|
|
130
|
+
|
|
131
|
+
> **The shape, run for real.** `doctor → arbitrate → gate → ship → verify`,
|
|
132
|
+
> with the actual `LaneDecision` JSON and the exit codes that branch it. Captured
|
|
133
|
+
> against a live DOS workspace (`dos 0.22.0`); copy-paste, then read the RUNG.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
$ dos doctor --workspace . --json
|
|
137
|
+
{ ... "lanes": {"concurrent": ["benchmark","docs","examples","scripts","spikes","src","tests"],
|
|
138
|
+
"exclusive": ["global"], "autopick": ["benchmark","docs",...,"tests"]},
|
|
139
|
+
"paths": {"plans_glob":"docs/**/*-plan.md","next_packets":".dos/verdicts","runs":".dos/runs"},
|
|
140
|
+
"stamp": {"style":"grep"}, "overlap_policy": {"active":"prefix"} }
|
|
141
|
+
```
|
|
142
|
+
The WCR on-ramp — lanes/paths/stamp are DATA. Nothing below is hardcoded.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
$ dos arbitrate --workspace . --lane src
|
|
146
|
+
{"auto_picked":true,"free_clusters":[],"lane":"benchmark","lane_kind":"cluster","outcome":"acquire","pick_count":null,"reason":"auto-picked free cluster lane benchmark (requested src was busy).","tree":["benchmark/**"]}
|
|
147
|
+
```
|
|
148
|
+
exit 0 (**acquire**) — but you asked for `src` and got `benchmark`: a live lease made
|
|
149
|
+
`src` contended, so the admission kernel redirected rather than double-book. Run on
|
|
150
|
+
`lane` (= `benchmark`), not your request. exit 1 (**refuse**) would mean stop / pick from `free_clusters`.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
$ dos gate --workspace . .dos/verdicts/.dispositions-benchmark.json
|
|
154
|
+
```
|
|
155
|
+
The verdict IS the code: `0` LIVE → ship; `3` DRAIN → skip + archive no-op; `4`
|
|
156
|
+
STALE-STAMP / `5` BLOCKED → skip + surface; `6` RACE → retry the snapshot once.
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
$ dos verify --workspace . docs/82_liveness-oracle-plan liveness --json
|
|
160
|
+
{"phase":"liveness","plan":"docs/82_liveness-oracle-plan","rung":"direct","sha":"80d4f30","shipped":true,"source":"grep-subject","summary":"80d4f30 liveness: exclude the BIRTH acquire from the ADVANCING event count"}
|
|
161
|
+
```
|
|
162
|
+
exit 0 SHIPPED — but `source` is **`grep-subject`**, not `registry`: a commit *subject*
|
|
163
|
+
carrying the phase token flips this true even if little was built. Read the rung.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
$ dos verify --workspace . docs/99_runtime-validation-and-the-actuation-boundary halt --json
|
|
167
|
+
{"phase":"halt","plan":"docs/99_runtime-validation-and-the-actuation-boundary","shipped":false,"source":"none"}
|
|
168
|
+
```
|
|
169
|
+
exit 1 NOT_SHIPPED `(source=none)` — git ancestry never stamped it. The oracle closes
|
|
170
|
+
the phase from git, never from "I'm done." Then `/release` cuts the version.
|
|
171
|
+
|
|
172
|
+
## Anti-patterns
|
|
173
|
+
|
|
174
|
+
- ❌ Launching a ship on a 0-pick packet — gate first; DRAIN/STALE-STAMP skip.
|
|
175
|
+
- ❌ `--force`-ing past a refuse in automation — a refuse means a real collision;
|
|
176
|
+
pick a free lane from `free_clusters` or stop.
|
|
177
|
+
- ❌ Hardcoding a run dir or a commit prefix — read them from `dos doctor --json`.
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dos-dispatch-loop
|
|
3
|
+
description: Run /dos-dispatch on a recurring cadence, alternating with /dos-replan when the backlog drains — the dispatch→replan→dispatch cycle. The continue/stop/next-mode decision is the kernel's typed loop decision, not inline prose: each iteration is classified (`dos gate`) into a verdict and the loop's counters (drained-twice, the unclear/dirty-zero breakers, the iteration cap) drive the next step. Several loops on disjoint lanes run concurrently, each taking its own lane lease via `dos arbitrate`. Driven entirely by `dos` verbs + the workspace's `dos.toml`. The DOS reference loop workflow (SKP Axis 5).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# dos-dispatch-loop — the generic dispatch⇄replan cadence
|
|
7
|
+
|
|
8
|
+
> **The unattended plan-and-ship loop.** It runs `/dos-dispatch` repeatedly and
|
|
9
|
+
> falls to `/dos-replan` when a lane drains, stopping on a typed, kernel-decided
|
|
10
|
+
> condition (not a prose guess). The stop/continue logic is the kernel's
|
|
11
|
+
> `loop_decide.decide` — the loop carries counters, the kernel decides. Several
|
|
12
|
+
> loops on disjoint lanes run in parallel, each holding its own lane lease.
|
|
13
|
+
|
|
14
|
+
The stop conditions are the kernel's, in one place:
|
|
15
|
+
|
|
16
|
+
1. **iteration cap** — reached `max_iterations` (default 10).
|
|
17
|
+
2. **drained-twice** — a DRAIN after a *productive* `/dos-replan` that itself
|
|
18
|
+
followed a DRAIN (the lane is genuinely exhausted).
|
|
19
|
+
3. **consecutive-unclear** — the dispatch subprocess is failing systematically.
|
|
20
|
+
4. **rate-limited** — a usage window is exhausted (don't burn launches).
|
|
21
|
+
5. **launch-failed** — a subprocess never started.
|
|
22
|
+
6. **pick-held-invariant** — the next unit is held ONLY by a reason a re-dispatch
|
|
23
|
+
cannot change (draft-class / operator-gated / soak-open / dependency-unmet);
|
|
24
|
+
re-dispatching it would re-block identically, so honest-STOP + surface the hold.
|
|
25
|
+
7. **pick-cooldown** — the next unit was attempted-and-didn't-move inside its
|
|
26
|
+
cooldown window AND nothing fresher is offerable; re-dispatching it would
|
|
27
|
+
re-storm a known drain (the ~5%-shipping re-pick loop the bare loop hit).
|
|
28
|
+
|
|
29
|
+
The last two are the docs/207 anti-churn rungs: the loop stops re-picking work it
|
|
30
|
+
cannot move, instead of burning the cap re-confirming a known hold.
|
|
31
|
+
|
|
32
|
+
## Inputs
|
|
33
|
+
|
|
34
|
+
- `--lane <name>` (optional) — focus the whole loop on one lane (fixed for the
|
|
35
|
+
run; a bare loop auto-picks a free lane at Step 0).
|
|
36
|
+
- `--gate hard|soft|drive` (default `hard`) — the verdict policy. `hard` routes a
|
|
37
|
+
non-LIVE verdict through `/dos-replan`; `soft`/`drive` stop on a true DRAIN;
|
|
38
|
+
`drive` self-heals a STALE-STAMP inline.
|
|
39
|
+
- `--max-iterations <N>` (default 10).
|
|
40
|
+
|
|
41
|
+
## Step 0 — Pre-flight: take the lane, read the taxonomy
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
dos doctor --workspace . --json
|
|
45
|
+
dos arbitrate --workspace . --lane <LANE> --kind cluster --leases '<SIBLING_LEASES>'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The arbiter ADMITs a free lane (or auto-picks one); a REFUSE means a sibling loop
|
|
49
|
+
already holds an overlapping lane — pick a free one from `free_clusters` or exit.
|
|
50
|
+
Initialise the loop counters (iteration=1, the breakers at 0).
|
|
51
|
+
|
|
52
|
+
## Step 1 — Pick-selection: skip held + cooled units (the anti-churn gate)
|
|
53
|
+
|
|
54
|
+
Before a `dispatch` iteration offers a unit, screen the candidate unit set so the
|
|
55
|
+
loop never re-storms work it cannot move (the docs/207 §6 throughline). For each
|
|
56
|
+
candidate, in order:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
dos pickable <UNIT> --state '<host-gathered state>' # OFFERABLE=0; HELD=per-reason code
|
|
60
|
+
dos cooldown <UNIT> # CLEAR=0; RECENTLY_ATTEMPTED=3
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- A `pickable` exit of **0** (OFFERABLE) **and** a `cooldown` exit of **0** (CLEAR)
|
|
64
|
+
→ this unit is dispatchable; offer it and proceed to the iteration.
|
|
65
|
+
- A `pickable` HELD by a re-dispatch-CURABLE reason (IN_FLIGHT / SOFT_CLAIMED /
|
|
66
|
+
STALE_CLAIM / UNPARSEABLE) or a `cooldown` of **3** (RECENTLY_ATTEMPTED) → **skip
|
|
67
|
+
this unit, try the next candidate** (the skip-to-next is pick-selection's job).
|
|
68
|
+
- A `pickable` HELD by a re-dispatch-INVARIANT reason (DRAFT_CLASS=10 /
|
|
69
|
+
OPERATOR_GATED=11 / SOAK_OPEN=12 / DEPENDENCY_UNMET=13) → carry that verdict into
|
|
70
|
+
Step 2; the kernel will honest-STOP on it (don't re-dispatch a unit a re-dispatch
|
|
71
|
+
cannot un-gate).
|
|
72
|
+
|
|
73
|
+
When EVERY remaining candidate is skipped (all cooled / curably-held), carry the
|
|
74
|
+
last `cooldown` RECENTLY_ATTEMPTED (or the invariant `pickable` hold) into Step 2
|
|
75
|
+
as the loop's pre-dispatch evidence — the kernel turns "nothing fresh is offerable"
|
|
76
|
+
into the `pick-cooldown` / `pick-held-invariant` honest-STOP. **Do not re-dispatch
|
|
77
|
+
the cooled/held unit yourself** — that is the re-pick storm this gate prevents.
|
|
78
|
+
|
|
79
|
+
## Step 1b — Run one iteration
|
|
80
|
+
|
|
81
|
+
For a `dispatch` iteration, invoke `/dos-dispatch --lane <LANE>` (it snapshots,
|
|
82
|
+
gates, and ships). For a `replan` iteration, invoke `/dos-replan`. Capture the
|
|
83
|
+
iteration's outcome:
|
|
84
|
+
|
|
85
|
+
- a **dispatch** iteration that reached the gate carries a typed verdict — get it
|
|
86
|
+
from `dos gate` over the packet's dispositions sidecar (LIVE/DRAIN/STALE-STAMP/
|
|
87
|
+
BLOCKED/RACE).
|
|
88
|
+
- a **replan** iteration carries a productivity signal (did it refill/garden?).
|
|
89
|
+
|
|
90
|
+
## Step 2 — Decide continue / replan / stop (the kernel decides)
|
|
91
|
+
|
|
92
|
+
This is the load-bearing step: **the decision is a kernel mechanism, not prose.**
|
|
93
|
+
Feed the iteration outcome + the carried counters to the loop decider. In code a
|
|
94
|
+
host calls `dos.loop_decide.decide(state, outcome)`; the screenplay's job is to
|
|
95
|
+
construct the typed `IterationOutcome` and read the returned `LoopDecision`:
|
|
96
|
+
|
|
97
|
+
- `action: "continue"` → run the next iteration in `next_mode` (`dispatch` or
|
|
98
|
+
`replan`); if `reconcile` is set (a soft/drive STALE-STAMP), run an inline
|
|
99
|
+
stamp-reconcile pass first. Carry `next_state` forward (the updated counters).
|
|
100
|
+
- `action: "stop"` → the loop ends; report `stop_reason` (one of the five above)
|
|
101
|
+
and `surface` (whether it needs operator attention).
|
|
102
|
+
- `action: "retry-same-iter"` → a transient overload; sleep `backoff_seconds`
|
|
103
|
+
and re-run the SAME iteration.
|
|
104
|
+
|
|
105
|
+
The drained-twice rule is the kernel's: a DRAIN counts toward an early stop ONLY
|
|
106
|
+
after a *productive* `/dos-replan`. A STALE-STAMP or BLOCKED gate routes to
|
|
107
|
+
`/dos-replan` (under `hard`) but never arms a false drained-twice stop — that is
|
|
108
|
+
the structural fix the typed gate exists for.
|
|
109
|
+
|
|
110
|
+
The pre-dispatch evidence from Step 1 rides into the decision too: the kernel reads
|
|
111
|
+
the carried `Pickability` (→ `pick-held-invariant` stop) and `Cooldown` (→
|
|
112
|
+
`pick-cooldown` stop). So the loop's continue/stop is driven END-TO-END by kernel
|
|
113
|
+
rungs — the honest-STOP that used to be a per-run human override is now a kernel
|
|
114
|
+
rule, not prose the loop re-applies each iteration.
|
|
115
|
+
|
|
116
|
+
## Step 3 — Reconcile each claimed pick (the cross-run KEEP)
|
|
117
|
+
|
|
118
|
+
A `dispatch` iteration that SHIPPED claims picks done — but a claim is a
|
|
119
|
+
self-report. Before the archive drops a claimed pick from the residual, reconcile
|
|
120
|
+
its claim against ground truth so a quietly-incomplete pick re-enters the pickable
|
|
121
|
+
set next iteration, flagged:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
dos reconcile <UNIT> --claimed-done --plan <PLAN> --phase <PHASE> # oracle from git
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Branch on the exit code (the verdict IS the code):
|
|
128
|
+
|
|
129
|
+
- `0` **VERIFIED** → the oracle confirms it shipped; it leaves the residual.
|
|
130
|
+
- `3` **QUIET_INCOMPLETE** → CLAIMED done but the oracle says NOT_SHIPPED — KEEP it
|
|
131
|
+
in the residual, flagged; it re-enters the pickable set next iteration so the
|
|
132
|
+
host routes it (a verifier pass / `/dos-replan` / a finding). **Do NOT believe
|
|
133
|
+
the claim** — only ground truth removes work (the FQ-336 touch-counts-as-ship
|
|
134
|
+
false-DRAIN is exactly what this catches).
|
|
135
|
+
- `4` **HONEST_OPEN** → not claimed, not shipped; honest open work, stays in the
|
|
136
|
+
residual.
|
|
137
|
+
|
|
138
|
+
This is the cross-run KEEP wired at the boundary that runs the write (the
|
|
139
|
+
`CLAUDE.md` "wire the contract into the step that runs the write" rule).
|
|
140
|
+
|
|
141
|
+
## Step 4 — Archive + release (and leave the lane's tree clean)
|
|
142
|
+
|
|
143
|
+
When the loop stops, write a loop record under `paths.runs` (the run dir from
|
|
144
|
+
`dos doctor --json`: the per-iteration verdicts, the reconcile flags, the stop
|
|
145
|
+
reason) and release the lane lease. Commit with a generic subject read from config
|
|
146
|
+
— no hardcoded prefix.
|
|
147
|
+
|
|
148
|
+
**Leave the tree clean for the lane you held — an unattended loop must not strand
|
|
149
|
+
its own writes.** A loop that ships work but leaves it uncommitted is the bug the
|
|
150
|
+
oracle catches next session: `dos verify` answers from git ancestry, so an
|
|
151
|
+
uncommitted change is a phase the kernel reports `NOT_SHIPPED` (the "a commit IS the
|
|
152
|
+
ship-stamp" contract). So at close-out, commit your lane's writes — driven by the
|
|
153
|
+
same `dos` verbs the loop already uses, with generic git:
|
|
154
|
+
|
|
155
|
+
- **Commit your lane's writes by explicit pathspec** — stage exactly the files
|
|
156
|
+
under the lane region you leased (the `tree` globs `dos arbitrate` handed back),
|
|
157
|
+
then commit naming those paths: `git add <lane paths>`; `git commit -m "<subject>"
|
|
158
|
+
-- <lane paths>`. **Never a bare `git add -A`** — when sibling loops hold disjoint
|
|
159
|
+
lanes on the same tree, a blanket add sweeps another loop's in-flight edits into
|
|
160
|
+
your commit. The lane lease you held names exactly which paths are yours; commit
|
|
161
|
+
only those.
|
|
162
|
+
- **Confirm the tree is clean for your lane before you exit.** `git status
|
|
163
|
+
--porcelain -- <lane paths>` over the region you leased should come back empty once
|
|
164
|
+
you have committed. If it does not, you stranded durable work — `log` it and commit
|
|
165
|
+
it (or, if a path turns out to belong to a *still-live* sibling lease — check
|
|
166
|
+
`dos arbitrate`/the lane journal — leave it for that loop). Either way the loop
|
|
167
|
+
must not exit leaving its own lane dirty off a self-reported "done".
|
|
168
|
+
- **Scratch is not stranded work.** Short-lived probe output (a host's scratch
|
|
169
|
+
convention — temp dirs, `*.err`, leading-underscore probes) is deletable noise, not
|
|
170
|
+
a phase to ship; `rm` it or leave it gitignored, don't commit it into the lane.
|
|
171
|
+
|
|
172
|
+
This close-out is what keeps an unattended fleet's tree **clean and well-organized
|
|
173
|
+
across runs**: each loop commits its own lane and confirms it left nothing behind, so
|
|
174
|
+
the trunk never accumulates anonymous WIP from a loop that stopped mid-write.
|
|
175
|
+
|
|
176
|
+
> **DOS-repo note (not part of the generic skill):** when this loop runs *in the DOS
|
|
177
|
+
> kernel repo itself*, that repo ships an advisory `scripts/git_hygiene.py --strict`
|
|
178
|
+
> that mechanizes the "is my lane clean?" check above (exit 1 on stranded durable
|
|
179
|
+
> work, lease-aware). It is a DOS-repo convenience, not a `dos` verb — a foreign
|
|
180
|
+
> workspace uses the generic `git status` form above. Both express the same
|
|
181
|
+
> discipline.
|
|
182
|
+
|
|
183
|
+
## What this skill deliberately does NOT do (no silent gap, `CLAUDE.md` heavy tier)
|
|
184
|
+
|
|
185
|
+
- **No soft-claim lease core.** It coordinates loops by *lane* lease
|
|
186
|
+
(`dos arbitrate`), not the per-pick soft-claim machinery that stays host-side.
|
|
187
|
+
`log` this when a sibling lane is busy rather than waiting on a soft-claim.
|
|
188
|
+
- **No value-greedy focus scheduler.** It picks by lane order + the gate verdict,
|
|
189
|
+
not a host's per-iteration focus ranking. That scheduler is the heavy tier.
|
|
190
|
+
- **No rate-limit predictive monitor / resume manifest.** It stops on a
|
|
191
|
+
RATE_LIMITED outcome and reports it; it does not pre-empt or auto-resume. A
|
|
192
|
+
host adds that as a driver concern.
|
|
193
|
+
|
|
194
|
+
It `log`s each of these the first time it would have used them, so the capability
|
|
195
|
+
gap is named, never silent.
|
|
196
|
+
|
|
197
|
+
## Worked example (live transcript)
|
|
198
|
+
|
|
199
|
+
> **The cadence loop, by hand.** Take a lane, keep the WAL beat alive, ask the
|
|
200
|
+
> kernel if the run is moving. Every line is a real `dos` verb; the heartbeat is
|
|
201
|
+
> the `HEARTBEAT` op the trajectory-audit cross-signal reads.
|
|
202
|
+
|
|
203
|
+
Step 0 — take a lane. You ask for `src`; a live lease made it contended, so the
|
|
204
|
+
arbiter auto-picks a free cluster lane instead (it never double-books a region):
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
$ dos arbitrate --workspace . --lane src
|
|
208
|
+
{"auto_picked":true,"free_clusters":[],"lane":"benchmark","lane_kind":"cluster","outcome":"acquire","pick_count":null,"reason":"auto-picked free cluster lane benchmark (requested src was busy).","tree":["benchmark/**"]}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
→ exit `0` (`acquire`); the redirect IS the admission kernel refusing a collision.
|
|
212
|
+
|
|
213
|
+
Keep the beat alive across iterations — `acquire` once, `heartbeat` each pass,
|
|
214
|
+
`release` at the end. The `HEARTBEAT` op is what makes `SPINNING` reachable from
|
|
215
|
+
real evidence (a beat is not an event):
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
$ dos lease-lane acquire --workspace . --lane benchmark # writes ACQUIRE to the WAL
|
|
219
|
+
$ dos lease-lane heartbeat --workspace . --lane benchmark # writes HEARTBEAT each iteration
|
|
220
|
+
$ dos lease-lane release --workspace . --lane benchmark # writes RELEASE when the loop stops
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Ask the kernel if the run is moving (the temporal verdict, from git delta — never
|
|
224
|
+
the loop's "making progress" self-report):
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
$ dos liveness --workspace . --run-id R --start-sha 80d4f30
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
→ exit `0` = `ADVANCING` · `3` = `SPINNING` · `4` = `STALLED` (`2` contract-error).
|
|
231
|
+
Drive the loop off the exit code, not stdout prose.
|
|
232
|
+
|
|
233
|
+
Read the beat back from the WAL — an `ACQUIRE`/`HEARTBEAT`/`REFUSE`/`RELEASE`
|
|
234
|
+
sequence on generic lanes (ILLUSTRATIVE shape; `dos journal replay` folds it):
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
$ dos journal --workspace . tail 4
|
|
238
|
+
{"op":"ACQUIRE","lane":"benchmark","tree":["benchmark/**"]}
|
|
239
|
+
{"op":"HEARTBEAT","lane":"benchmark"}
|
|
240
|
+
{"op":"REFUSE","lane":"benchmark","reason":"lane benchmark is already held by a live loop — pick a different --lane or wait."}
|
|
241
|
+
{"op":"RELEASE","lane":"benchmark"}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
→ the `REFUSE` row is the WAL recording the arbiter's "no" — a sibling loop's
|
|
245
|
+
overlapping lane, fossilized for `dos decisions` to surface.
|
|
246
|
+
|
|
247
|
+
## Anti-patterns
|
|
248
|
+
|
|
249
|
+
- ❌ Re-implementing the stop conditions in prose — route every outcome through
|
|
250
|
+
the kernel loop decision; the counters/breakers/cap are the kernel's.
|
|
251
|
+
- ❌ Counting a STALE-STAMP/BLOCKED toward drained-twice — only a true DRAIN after
|
|
252
|
+
a productive replan counts (the kernel enforces this; don't second-guess it).
|
|
253
|
+
- ❌ Running two loops on overlapping lanes — the arbiter REFUSEs the second;
|
|
254
|
+
honor it, don't `--force`.
|