@azure-id/orc 1.0.0 → 1.2.0
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.
- package/CHANGELOG.md +151 -0
- package/README.md +84 -34
- package/bin/cli.js +1110 -0
- package/bin/verify-contracts.js +112 -1
- package/bin/verify-package.js +568 -563
- package/bin/webui/api.js +15 -0
- package/bin/webui/app.html +210 -207
- package/bin/webui/css/panels/wait.css +123 -0
- package/bin/webui/fixtures/index.js +7 -0
- package/bin/webui/fixtures/wait.js +97 -0
- package/bin/webui/i18n/en/nav.json +21 -20
- package/bin/webui/i18n/en/wait.json +41 -0
- package/bin/webui/i18n/id/nav.json +21 -20
- package/bin/webui/i18n/id/wait.json +41 -0
- package/bin/webui/js/01-i18n.js +151 -150
- package/bin/webui/js/panels/wait.js +253 -0
- package/package.json +1 -1
- package/templates/commands/orc-wait.md +19 -0
- package/templates/hooks/orc-statusline.js +227 -1
- package/templates/skills/_shared/phases/execution.md +2 -0
- package/templates/skills/_shared/phases/preflight.md +22 -0
- package/templates/skills/_shared/return-validation.md +222 -145
- package/templates/skills/_shared/wait.md +240 -0
- package/templates/skills/orc/SKILL.md +247 -238
- package/templates/skills/orc-aftermath/SKILL.md +6 -1
- package/templates/skills/orc-analyze/SKILL.md +6 -1
- package/templates/skills/orc-boundary/SKILL.md +6 -1
- package/templates/skills/orc-brainstorm/SKILL.md +6 -1
- package/templates/skills/orc-budget/SKILL.md +6 -1
- package/templates/skills/orc-challenge/SKILL.md +6 -1
- package/templates/skills/orc-claude/SKILL.md +6 -1
- package/templates/skills/orc-diy/SKILL.md +6 -1
- package/templates/skills/orc-doc/SKILL.md +490 -481
- package/templates/skills/orc-explain/SKILL.md +5 -0
- package/templates/skills/orc-export/SKILL.md +5 -0
- package/templates/skills/orc-fast/SKILL.md +222 -215
- package/templates/skills/orc-grill/SKILL.md +6 -1
- package/templates/skills/orc-learn/SKILL.md +6 -1
- package/templates/skills/orc-mini/SKILL.md +252 -244
- package/templates/skills/orc-pact/SKILL.md +6 -1
- package/templates/skills/orc-pattern/SKILL.md +6 -1
- package/templates/skills/orc-poly/SKILL.md +6 -1
- package/templates/skills/orc-quick/SKILL.md +353 -346
- package/templates/skills/orc-retro/SKILL.md +6 -1
- package/templates/skills/orc-route/SKILL.md +6 -1
- package/templates/skills/orc-verify/SKILL.md +6 -1
- package/templates/skills/orc-wait/SKILL.md +163 -0
- package/templates/skills/orc-wiki/SKILL.md +180 -171
|
@@ -1,145 +1,222 @@
|
|
|
1
|
-
# Return validation (every lane, every subagent return)
|
|
2
|
-
|
|
3
|
-
Canonical procedure for validating a spawned agent's return. Every ORC lane
|
|
4
|
-
(full, mini, fast, wiki, diy) runs this on EVERY return; a malformed return is
|
|
5
|
-
a failure (requeue/re-dispatch with reason — lane sets the retry cap).
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
`
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
(`
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1
|
+
# Return validation (every lane, every subagent return)
|
|
2
|
+
|
|
3
|
+
Canonical procedure for validating a spawned agent's return. Every ORC lane
|
|
4
|
+
(full, mini, fast, wiki, diy) runs this on EVERY return; a malformed return is
|
|
5
|
+
a failure (requeue/re-dispatch with reason — lane sets the retry cap).
|
|
6
|
+
|
|
7
|
+
## 0. Is the previous attempt still ALIVE? (v1.2.0) — BEFORE anything else
|
|
8
|
+
|
|
9
|
+
> **`a lane that re-dispatches over a live attempt` has broken this contract.**
|
|
10
|
+
|
|
11
|
+
**A Task error does not kill the agent behind it.** Claude Code's tool call can
|
|
12
|
+
fail, time out, or be cut off mid-turn while the subagent it started keeps
|
|
13
|
+
running — and keeps writing files. Every rule below this line ends in
|
|
14
|
+
"re-dispatch", and every one of them silently assumed a failed call meant a dead
|
|
15
|
+
agent. It does not.
|
|
16
|
+
|
|
17
|
+
What that costs, measured: one graded `/orc-quick` entry put THREE
|
|
18
|
+
`orc-executor-opus-5-low` agents on the SAME task — 50m19s, 115m22s and
|
|
19
|
+
100m53s, **266 minutes of Opus 5 for one authorised dispatch**, all editing the
|
|
20
|
+
same files, inside a 2h04m window. The second was dispatched 4m19s after the
|
|
21
|
+
first, while the first was still working. The hook had recorded all three; no
|
|
22
|
+
lane had ever read that record.
|
|
23
|
+
|
|
24
|
+
### The rule
|
|
25
|
+
|
|
26
|
+
Before ANY re-dispatch, requeue or repair round — and before the first dispatch
|
|
27
|
+
of a resumed run — run:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
orc run inflight --json # 0 clear · 1 in-flight · 2 unknown
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| exit | meaning | what the lane does |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| 0 | provably nothing in flight | dispatch |
|
|
36
|
+
| 1 | ≥1 dispatch has not returned | **REFUSE. Name the agent, the task and its age.** Ask the user. |
|
|
37
|
+
| 2 | cannot prove either way | **REFUSE by default.** Say why, and let the USER decide. |
|
|
38
|
+
|
|
39
|
+
**Exit 2 refuses, and that is deliberate.** Everywhere else in ORC an absent
|
|
40
|
+
reading is treated as absent and never blocks — `orc usage check` exit 2 never
|
|
41
|
+
stops a run, an UNCHECKABLE pact never raises the exit code. This is the one
|
|
42
|
+
place the default inverts, because the two outcomes are not symmetrical: a
|
|
43
|
+
wrongly-refused dispatch costs one question, and a wrongly-issued one costs a
|
|
44
|
+
second Opus agent for an hour. Refusing on `unknown` is the cheap error.
|
|
45
|
+
|
|
46
|
+
### An interrupted turn is UNKNOWN, never FAILED
|
|
47
|
+
|
|
48
|
+
A usage limit, an API error, a dropped connection or a `Ctrl+C` between a
|
|
49
|
+
dispatch and its return says **nothing** about the agent. Treat it as §0 exit 2
|
|
50
|
+
and ask. A lane that classifies an interruption as a failure re-dispatches into
|
|
51
|
+
a live agent, gets interrupted again sooner because it is now paying twice, and
|
|
52
|
+
the loop tightens on itself — which is exactly how the 266-minute entry
|
|
53
|
+
happened.
|
|
54
|
+
|
|
55
|
+
### What refusing looks like
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
⛔ 1 dispatch is still in flight — not re-dispatching.
|
|
59
|
+
|
|
60
|
+
orc-executor-opus-5-low started 4m ago
|
|
61
|
+
"Fix approval flow defects"
|
|
62
|
+
|
|
63
|
+
A Task error does not kill the agent behind it. It may still be writing.
|
|
64
|
+
1. wait for it 2. dispatch anyway (2 agents on one task) 3. stop
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Option 2 must always be offered and never be the default: the user is allowed
|
|
68
|
+
to overrule this, and an unreadable sidecar must never trap a run.
|
|
69
|
+
|
|
70
|
+
### The evidence, and its one honest limit
|
|
71
|
+
|
|
72
|
+
`orc run inflight` reads the pending sidecar that `orc-trace.js` writes on every
|
|
73
|
+
`SPAWN`, cross-checked against the trace's own SPAWN/RETURN balance. It reports
|
|
74
|
+
`unknown` — never `clear` — when the sidecar is missing, unreadable, or holds
|
|
75
|
+
only records older than six hours, and when the sidecar and the trace disagree.
|
|
76
|
+
**Unknown is not zero.**
|
|
77
|
+
|
|
78
|
+
It cannot see an **ad-hoc** dispatch (`/orc-quick` recon, model+effort rather
|
|
79
|
+
than a pinned `orc-*` agent): the hook writes no `SPAWN` for one, so no record
|
|
80
|
+
exists. Those are read-only and short, so the exposure is small — but the limit
|
|
81
|
+
is stated rather than papered over, and a lane must not report `clear` as proof
|
|
82
|
+
that an ad-hoc read is finished.
|
|
83
|
+
|
|
84
|
+
## 1. Contract shape
|
|
85
|
+
|
|
86
|
+
The return must carry every field its agent contract names. Missing or extra
|
|
87
|
+
shape = malformed. Never repair a return yourself; re-dispatch.
|
|
88
|
+
|
|
89
|
+
## 2. Claimed-vs-actual model (tier-downgrade check)
|
|
90
|
+
|
|
91
|
+
Every return carries:
|
|
92
|
+
|
|
93
|
+
- `actual_model` — quoted VERBATIM from the agent's system-prompt model-id
|
|
94
|
+
line, never inferred (`unknown` when no such line exists)
|
|
95
|
+
- `actual_effort` — the agent's `$CLAUDE_EFFORT` value
|
|
96
|
+
|
|
97
|
+
Compare both against what the dispatch expected (the agent NAME encodes it).
|
|
98
|
+
Append the `VERIFY` trace line with the comparison; any mismatch is surfaced
|
|
99
|
+
to the user as a ⛔ DOWNGRADE — never silently accepted. (A subagent can't
|
|
100
|
+
exceed the MAIN session's tier, so a downgrade usually means the main session
|
|
101
|
+
is on the wrong model.)
|
|
102
|
+
|
|
103
|
+
## 2b. A FOREIGN return — the SUBSTITUTION check (v0.50.0)
|
|
104
|
+
|
|
105
|
+
A foreign worker (`orc extra dispatch`, `_shared/extra-dispatch.md`) is not a
|
|
106
|
+
Claude subagent. It has no injected system-prompt model-id line, so **it cannot
|
|
107
|
+
carry `actual_model`** — and §2 must not be faked for it. A foreign return that
|
|
108
|
+
claimed an `actual_model` would be claiming evidence that does not exist.
|
|
109
|
+
|
|
110
|
+
It carries instead, and every one of these is quoted from the wire rather than
|
|
111
|
+
assumed:
|
|
112
|
+
|
|
113
|
+
- `engine` (`api` | `claude-shim` | `cli`), `provider`, `profile`
|
|
114
|
+
- `model_requested` — what the route row asked for
|
|
115
|
+
- **`model_reported`** — the `model` field the endpoint echoed back
|
|
116
|
+
- `usage` — the four token kinds, never blended, **or `null`**
|
|
117
|
+
|
|
118
|
+
**`model_reported != model_requested` is ⛔ SUBSTITUTION**, surfaced to the user
|
|
119
|
+
exactly as ⛔ DOWNGRADE is today and never silently accepted. It is the only
|
|
120
|
+
defence against an aggregator quietly serving something else. `unknown` is a
|
|
121
|
+
valid, honest value and is reported as `unknown` — **never as a match**.
|
|
122
|
+
|
|
123
|
+
**A clean model check is not a clean answer.** An aggregator's *provider-level*
|
|
124
|
+
fallback is on by default and it PRESERVES the model id, so the substitution
|
|
125
|
+
check reads clean while the code went to a different company. Engine `api`
|
|
126
|
+
records the response's `provider` echo and reports **⚠ REROUTE**; the other two
|
|
127
|
+
engines cannot see it at all, and their `served_by_note` says so. An absent
|
|
128
|
+
measurement is never a pass.
|
|
129
|
+
|
|
130
|
+
**`usage: null` is not four zeros.** A worker that reported no token counts
|
|
131
|
+
(engine `cli` frequently) returns `null` plus a note. `{0,0,0,0}` would tell
|
|
132
|
+
`/orc-budget` the run was free. Engine `api`'s `cache_write: 0` is the opposite
|
|
133
|
+
case — a real measurement — so the two must never be normalised together.
|
|
134
|
+
|
|
135
|
+
**The fence is per-engine, and the return says which one it had.** Engine `api`
|
|
136
|
+
ENFORCES `declared_files`; engines `claude-shim` and `cli` ASK. A return
|
|
137
|
+
carrying `fence: {declared_files: false}` means the list was an instruction, not
|
|
138
|
+
a rule — treat §6 below as the only real check, and say so to the user rather
|
|
139
|
+
than reporting a constraint that was never applied.
|
|
140
|
+
|
|
141
|
+
**A RESUMED foreign dispatch owes three more fields** (v0.54.0). The dispatch
|
|
142
|
+
return sets `resume_expected: true`, so the obligation is never inferred:
|
|
143
|
+
|
|
144
|
+
- **`resume_state`** — `continued` · `restarted` · `no-op`. Absent on a slice
|
|
145
|
+
with no `resumed_from` is correct; **absent on a resume slice is MALFORMED.**
|
|
146
|
+
A return claiming `restarted` while `preexisting[]` was non-empty is a
|
|
147
|
+
FINDING, not a failure — it is how `/orc-retro` learns which providers ignore
|
|
148
|
+
a resume preamble, so surface it rather than treating it as a bad return.
|
|
149
|
+
- **`preexisting_read[]`** — which pre-existing files the worker actually
|
|
150
|
+
opened. Quoted like `wiki_used`: **what it did, never what the dispatcher
|
|
151
|
+
assumed.** An EMPTY list on a resume whose `preexisting[]` was not empty is an
|
|
152
|
+
honest and informative return — it says the worker ignored the preamble — and
|
|
153
|
+
it must be surfaced, never dropped.
|
|
154
|
+
- **`journal_fidelity`** — relayed from the dispatch return (`per-turn` |
|
|
155
|
+
`streamed-opaque`), so a validator never reports `streamed-opaque` evidence as
|
|
156
|
+
if it had per-turn tool attribution.
|
|
157
|
+
|
|
158
|
+
Everything else in this file applies to a foreign return unchanged: the
|
|
159
|
+
honest-status rules, the pattern attestation, the TDD attestation, the wiki
|
|
160
|
+
attestation, and above all **§6, the worktree delta** — which is engine-blind
|
|
161
|
+
because it reads the worktree rather than the return, and is therefore what
|
|
162
|
+
makes a foreign executor safe at all.
|
|
163
|
+
|
|
164
|
+
## 3. Honest-status rules (executor returns)
|
|
165
|
+
|
|
166
|
+
- `status=done` on a stack with a runnable build/test REQUIRES `evidence`
|
|
167
|
+
{command, exit_code, tail} quoted VERBATIM; a missing block or a false
|
|
168
|
+
`no_runner_detected` is malformed.
|
|
169
|
+
- `done` with a non-empty `unmet[]` is `partial` — treat it as such.
|
|
170
|
+
|
|
171
|
+
## 4. Pattern attestation (when a `pattern` was injected)
|
|
172
|
+
|
|
173
|
+
A task that received a `pattern` slice must return `invariants_checked: true`
|
|
174
|
+
plus the matching `pattern_version`; false/absent on a pattern task is
|
|
175
|
+
malformed.
|
|
176
|
+
|
|
177
|
+
## 5. TDD attestation (when a `tdd_spec` was injected — v0.33.0)
|
|
178
|
+
|
|
179
|
+
A task whose slice carried a `tdd_spec` must return `tdd_state: green|red` —
|
|
180
|
+
`green` only with the passing run quoted in `evidence`; `status=done` with
|
|
181
|
+
`tdd_state: red` (or an absent field) is malformed. `red` is an HONEST return:
|
|
182
|
+
the lane runs its repair loop up to `tdd_loop_max`, then STOPS with the red
|
|
183
|
+
report — never re-dispatch past the cap.
|
|
184
|
+
|
|
185
|
+
## 5b. Wiki attestation (when wiki content or page pointers were injected — v0.41.0)
|
|
186
|
+
|
|
187
|
+
A task whose slice carried wiki material must return **`wiki_used`** — the doc
|
|
188
|
+
paths it ACTUALLY read, or `none`. Quoted like `actual_model`: what the agent
|
|
189
|
+
did, never what the dispatcher assumed.
|
|
190
|
+
|
|
191
|
+
`none` is a valid and INFORMATIVE return, not a failure: it says the pages were
|
|
192
|
+
not useful or were ignored. Record it and surface it — a wiki whose pages are
|
|
193
|
+
shipped into every slice and read by nobody is the failure mode this field
|
|
194
|
+
exists to make visible, and it is invisible if `none` is quietly dropped. Absent
|
|
195
|
+
on a slice that carried wiki material is malformed. Not required otherwise.
|
|
196
|
+
|
|
197
|
+
## 6. Worktree delta (post-wave, every lane that dispatches executors)
|
|
198
|
+
|
|
199
|
+
Compare `git status --short` before and after each dispatch. A path that
|
|
200
|
+
appears, disappears, or **reverts** and is absent from that task's
|
|
201
|
+
`declared_files` is a slice violation regardless of what the return said —
|
|
202
|
+
including a file that became LESS modified, which is how a destructive `git`
|
|
203
|
+
command inside a slice disguises itself as a clean tree. `actual_files` is a
|
|
204
|
+
CLAIM; the worktree is the EVIDENCE. An unexplained delta gates the wave: name
|
|
205
|
+
it, attribute it, and get a decision before closing.
|
|
206
|
+
|
|
207
|
+
**On a RESUMED task the "before" side of the delta is the JOURNAL BASELINE**
|
|
208
|
+
(`orc extra reconcile`, `_shared/extra-dispatch.md`), not the state at the top of
|
|
209
|
+
this wave. A file the previous attempt created is already in the tree and is
|
|
210
|
+
**not** an unexplained delta — it is explained, by the journal, by name. Without
|
|
211
|
+
that the first resumed wave trips its own gate on the work it just recovered.
|
|
212
|
+
|
|
213
|
+
## 7. Gotcha capture (repair loops only — v0.40.0)
|
|
214
|
+
|
|
215
|
+
A return that closes a repair loop (`tdd_state` went red → green, a drift
|
|
216
|
+
round resolved, a reviewer P0/P1 was fixed in-run) carries
|
|
217
|
+
`gotcha_recorded` — either the entry body (`trigger`, `symptom`, `cause`,
|
|
218
|
+
`fix`, `scope`) or `none` with a one-line reason. Absent on a repair-closing
|
|
219
|
+
return is malformed. It is NOT required on a return that never repaired
|
|
220
|
+
anything, and a loop that hit its cap and STOPPED must return `none` — an
|
|
221
|
+
unsolved failure is not a gotcha. The agent RETURNS the body; the
|
|
222
|
+
orchestrator writes the file. See `gotchas.md`.
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Shared contract — the WAIT (`/orc-wait`, and the computed gate)
|
|
2
|
+
|
|
3
|
+
Canonical file: `_shared/wait.md`. THE canonical mechanic for a lane that must
|
|
4
|
+
**stop where it stands, wait for wall-clock time to pass, and carry on from the
|
|
5
|
+
same place**. Load it wherever a lane can be interrupted by a wait — which,
|
|
6
|
+
since v1.1.0, is every lane in the table below.
|
|
7
|
+
|
|
8
|
+
## Why a wait is not a suspend and not a fallback
|
|
9
|
+
|
|
10
|
+
ORC already has two shapes for leaving a run. This is a third, and conflating
|
|
11
|
+
them loses work.
|
|
12
|
+
|
|
13
|
+
| | `FALLBACK-FROM` | `RETURN-TO` | **WAIT** |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| Why it leaves | it cannot proceed | another lane must settle something | **wall-clock time must pass** |
|
|
16
|
+
| Who finishes | the receiver | the sender, after coming back | **the same lane, same run** |
|
|
17
|
+
| Another lane runs | yes | yes | **no — nothing runs** |
|
|
18
|
+
| Traces | one | two | **one** |
|
|
19
|
+
|
|
20
|
+
A wait dispatches nothing and decides nothing. It is the only ORC mechanic
|
|
21
|
+
whose entire purpose is that **no model is running**.
|
|
22
|
+
|
|
23
|
+
## The one rule
|
|
24
|
+
|
|
25
|
+
> **`a lane that waits without a hand-back` has broken this contract.**
|
|
26
|
+
|
|
27
|
+
A wait is a stop. Every stop in ORC writes its hand-back before it ends, because
|
|
28
|
+
the thing that resumes the run may not be this session — the user can close the
|
|
29
|
+
terminal, the machine can sleep, and the wake-up message is a Claude Code
|
|
30
|
+
behaviour ORC cannot promise. `RESUME.md` on disk is what makes a lost wake-up
|
|
31
|
+
cost nothing.
|
|
32
|
+
|
|
33
|
+
This is `_shared/phases/stop-resume.md` applied to a stop nobody asked a
|
|
34
|
+
question about. It does not replace that phase; it scales it.
|
|
35
|
+
|
|
36
|
+
## The three modes
|
|
37
|
+
|
|
38
|
+
A wait is requested with a mode. The modes differ in ONE thing: how much the
|
|
39
|
+
lane finishes before it stops.
|
|
40
|
+
|
|
41
|
+
| Mode | Stops at | Hand-back | Dispatches | Can lose |
|
|
42
|
+
|---|---|---|---|---|
|
|
43
|
+
| `safe` | the next **safe point** | full stop sequence | yes (checkpoint) | nothing |
|
|
44
|
+
| `soft` | the next **model turn** | full stop sequence, **forced** | yes (checkpoint) | an in-flight return |
|
|
45
|
+
| `hard` | the next **model turn** | `RESUME.md` only, best effort | **no** | an in-flight return, the checkpoint, the phase's trace packet |
|
|
46
|
+
|
|
47
|
+
**`soft` is forced.** On a lane the table below marks as checkpointing, `soft`
|
|
48
|
+
does not merely attempt the hand-back: if the checkpoint write fails, **`soft`
|
|
49
|
+
does not stop**. It reports the failure and stays in the run. That is
|
|
50
|
+
`stop-resume.md` step 2 unchanged — *stopping without a good checkpoint is the
|
|
51
|
+
one thing that loses work* — and it is the whole reason the mode exists.
|
|
52
|
+
|
|
53
|
+
**`hard` is the dispatch-free stop.** It writes only what ORC can write with its
|
|
54
|
+
own hand (`RESUME.md`, per stop-resume step 3b — never a dispatched agent). It
|
|
55
|
+
is fast BECAUSE it dispatches nothing, not in spite of it. It is the one mode
|
|
56
|
+
that can lose work, and it says so every time it runs.
|
|
57
|
+
|
|
58
|
+
### "the next model turn" is the honest promise
|
|
59
|
+
|
|
60
|
+
A typed message reaches ORC at a turn boundary. `hard` therefore **cannot**
|
|
61
|
+
interrupt a dispatch that is already in flight. What it promises is:
|
|
62
|
+
|
|
63
|
+
> stop at the first moment ORC can act, and do not wait for the current wave,
|
|
64
|
+
> phase or gate to finish.
|
|
65
|
+
|
|
66
|
+
Never write "immediately". A user who reads "immediately" and sees a wave finish
|
|
67
|
+
believes the command failed.
|
|
68
|
+
|
|
69
|
+
## Safe points
|
|
70
|
+
|
|
71
|
+
A safe point is a place where the run can stop with no loss. `safe` waits for
|
|
72
|
+
one. `soft` and `hard` do not — that is what they are for, and what they risk.
|
|
73
|
+
|
|
74
|
+
**Never begin a wait at any of these, in any mode:**
|
|
75
|
+
|
|
76
|
+
- between a dispatch and its validated return
|
|
77
|
+
- inside the stop sequence itself
|
|
78
|
+
- during a file write, a `splice`, or a wiki registration write
|
|
79
|
+
- before the smoke gate has reported
|
|
80
|
+
|
|
81
|
+
These are not a style preference. Each one leaves an artifact that no resume can
|
|
82
|
+
reconstruct.
|
|
83
|
+
|
|
84
|
+
## Which lanes support a wait
|
|
85
|
+
|
|
86
|
+
The machine-readable copy of this table is `WAIT_LANE_SHAPES` in `bin/cli.js`,
|
|
87
|
+
rendered by `orc wait lanes`. A golden test compares the two IN BOTH DIRECTIONS
|
|
88
|
+
— the `EXTRA_LANE_SHAPES` / `DIY_STEPS` precedent. A lane added to one and not
|
|
89
|
+
the other fails the suite.
|
|
90
|
+
|
|
91
|
+
| Lane | Checkpoint | Safe point |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `/orc` | full | wave or phase edge |
|
|
94
|
+
| `/orc-ultra` | full | wave or judge gate |
|
|
95
|
+
| `/orc-mini` | full | after the executor returns |
|
|
96
|
+
| `/orc-fast` | full | after the executor returns |
|
|
97
|
+
| `/orc-diy` | full | compiled phase edge |
|
|
98
|
+
| `/orc-doc` | full | wave edge |
|
|
99
|
+
| `/orc-wiki` | full | scan-task boundary |
|
|
100
|
+
| `/orc-analyze` | full | after the analyst returns |
|
|
101
|
+
| `/orc-poly` | docset | after a per-repo plan is written |
|
|
102
|
+
| `/orc-quick` | entry | after an entry closes |
|
|
103
|
+
| `/orc-challenge` | cycle | after a cycle records |
|
|
104
|
+
| `/orc-brainstorm` | snapshot | phase edge |
|
|
105
|
+
| `/orc-grill` | snapshot | round edge |
|
|
106
|
+
| `/orc-learn` | none | single dispatch |
|
|
107
|
+
| `/orc-plan` | none | single dispatch |
|
|
108
|
+
| `/orc-verify` | none | single dispatch |
|
|
109
|
+
| `/orc-pattern` | none | single dispatch |
|
|
110
|
+
| `/orc-claude` | none | single dispatch |
|
|
111
|
+
| `/orc-explain` | none | read-only, seconds long |
|
|
112
|
+
| `/orc-route` | none | read-only, seconds long |
|
|
113
|
+
| `/orc-boundary` | none | read-only, seconds long |
|
|
114
|
+
| `/orc-budget` | none | read-only, seconds long |
|
|
115
|
+
| `/orc-aftermath` | none | read-only, seconds long |
|
|
116
|
+
| `/orc-export` | none | read-only, seconds long |
|
|
117
|
+
| `/orc-retro` | none | read-only, seconds long |
|
|
118
|
+
| `/orc-pact` | none | read-only, seconds long |
|
|
119
|
+
|
|
120
|
+
**`checkpoint: none` is an ANSWER, not a gap.** A single-dispatch lane has
|
|
121
|
+
nothing to checkpoint, so a wait there is a plain wait and the message says so.
|
|
122
|
+
On such a lane `safe`, `soft` and `hard` are the SAME thing, and
|
|
123
|
+
`orc wait lanes` states that rather than pretending to a distinction. A row that
|
|
124
|
+
reads `none` must never render like a row that is missing.
|
|
125
|
+
|
|
126
|
+
## The hop loop
|
|
127
|
+
|
|
128
|
+
The lane does not sleep. A **detached** command sleeps. It costs zero tokens and
|
|
129
|
+
no model runs during it.
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
1. Write the hand-back for the mode (above).
|
|
133
|
+
2. remaining = the requested time, or resets_at - now
|
|
134
|
+
3. hop = min(wait_hop_minutes, remaining)
|
|
135
|
+
4. Run a DETACHED command that waits hop seconds.
|
|
136
|
+
5. On wake: `orc usage check --json`
|
|
137
|
+
6. Exit 0, or the requested time has elapsed → continue. Else go to 3.
|
|
138
|
+
7. wait_max_hops reached → stop, keep the hand-back, say why.
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**A hop is short on purpose.** Each wake-up is session activity, and session
|
|
142
|
+
activity is the only thing that makes the statusline run again — so each hop
|
|
143
|
+
buys a fresh reading. A single long sleep wakes into a reading as stale as the
|
|
144
|
+
sleep was long.
|
|
145
|
+
|
|
146
|
+
## After the wait — ORC does not drag a large context forward
|
|
147
|
+
|
|
148
|
+
`stop-resume.md` step 6 already requires offering both continue paths. This
|
|
149
|
+
decides which one ORC takes without asking:
|
|
150
|
+
|
|
151
|
+
- **context small** → continue here, and say so in one line.
|
|
152
|
+
- **context large** → STOP and offer both paths, recommending the fresh session.
|
|
153
|
+
|
|
154
|
+
A wait longer than one hour has already expired the prompt cache, so continuing
|
|
155
|
+
in-session re-reads the whole context at full input price — exactly when quota
|
|
156
|
+
is lowest. Auto-continuing into a bloated context is the cost the wait existed
|
|
157
|
+
to avoid.
|
|
158
|
+
|
|
159
|
+
**ORC cannot clear its own context.** `/clear` is the user's action. The wait
|
|
160
|
+
offers the swap; it never performs it.
|
|
161
|
+
|
|
162
|
+
## The computed gate (`usage_gate`)
|
|
163
|
+
|
|
164
|
+
The same engine, triggered by the CLI instead of by a typed command. It is
|
|
165
|
+
**`off` by default** — nothing below happens until the user turns it on.
|
|
166
|
+
|
|
167
|
+
Check **before a wave, never during one**: `orc usage check --json`.
|
|
168
|
+
|
|
169
|
+
| exit | state | `warn` | `stop` | `wait` |
|
|
170
|
+
|---|---|---|---|---|
|
|
171
|
+
| 0 | ok | continue | continue | continue |
|
|
172
|
+
| 1 | low | print and continue | hand back and stop | hand back, hop, come back |
|
|
173
|
+
| 2 | unknown | print and continue | print and continue | print and continue |
|
|
174
|
+
|
|
175
|
+
**Exit 2 never stops a run**, in any mode. An absent reading is absent, not low:
|
|
176
|
+
older Claude Code sends no usage headers, and a long dispatch leaves the reading
|
|
177
|
+
stale by exactly its own length. A gate that blocks on a missing number is a
|
|
178
|
+
gate people switch off.
|
|
179
|
+
|
|
180
|
+
**The worst window decides.** `orc usage check` already resolves that; never
|
|
181
|
+
re-derive it from one window.
|
|
182
|
+
|
|
183
|
+
A computed stop offers the cheaper answers before the expensive one — a lower
|
|
184
|
+
band for this wave, or `orc extra` if a profile is ready — because a wait is the
|
|
185
|
+
only one of them that costs wall-clock time.
|
|
186
|
+
|
|
187
|
+
**A typed `/orc-wait` is never suppressed by any of this**, and a computed wait
|
|
188
|
+
is suppressed entirely while a block is active.
|
|
189
|
+
|
|
190
|
+
## The block — the user's veto
|
|
191
|
+
|
|
192
|
+
`/orc-wait block <reason>` suppresses every COMPUTED wait for the rest of the
|
|
193
|
+
run. It is for the case where stopping costs more than continuing: the window
|
|
194
|
+
resets in five minutes and the task needs ten.
|
|
195
|
+
|
|
196
|
+
1. **The reason is REQUIRED.** A block with no reason is refused by name. The
|
|
197
|
+
recorded reason is what makes the risk demonstrably the user's — the same
|
|
198
|
+
`--reason` rule the run-close and doc-ship writers already use.
|
|
199
|
+
2. **Run-scoped. It NEVER writes the user's config.** The same rule the ultra
|
|
200
|
+
lane's forced run-scoped mode already follows. A veto set today must not
|
|
201
|
+
apply to a run started next month.
|
|
202
|
+
3. **It is ANNOUNCED at every gate it suppresses, with its age.** A shadowed
|
|
203
|
+
setting must never be silent. There is no auto-expiry — ORC does not decide
|
|
204
|
+
that a user's reason stopped being true — so the age is what keeps an old
|
|
205
|
+
block from applying invisibly.
|
|
206
|
+
4. **It blocks what ORC COMPUTES, never what the user TYPES.** A typed
|
|
207
|
+
`/orc-wait 30 hard` still waits while a block is active. `/orc-boundary`'s
|
|
208
|
+
rule, unchanged: a gate constrains ORC's own dispatch, never an explicit
|
|
209
|
+
instruction.
|
|
210
|
+
5. It survives a resume, and is re-announced on the first gate after it.
|
|
211
|
+
|
|
212
|
+
`orc wait cancel` is a DIFFERENT command: it ends a wait that is already
|
|
213
|
+
running. Block is before, cancel is during. Never conflate them in prose or in a
|
|
214
|
+
menu.
|
|
215
|
+
|
|
216
|
+
## What a wait never does
|
|
217
|
+
|
|
218
|
+
- It never dispatches an agent to do the waiting. An agent runs on the same
|
|
219
|
+
account and consumes the same window the wait exists to protect.
|
|
220
|
+
- It never runs another lane.
|
|
221
|
+
- It never writes the user's config.
|
|
222
|
+
- It never widens or narrows the work: the same tasks, the same slice, the same
|
|
223
|
+
agent resolve after the wait as before it.
|
|
224
|
+
- It never decides on its own that a user's block has expired.
|
|
225
|
+
|
|
226
|
+
## Trace
|
|
227
|
+
|
|
228
|
+
The wait writes CLI-composed lines into the trace that is ALREADY open, and
|
|
229
|
+
nothing when no run is active. `/orc-wait` opens no run, so it is **not a lane**
|
|
230
|
+
in the trace enum and has no `run-<lane>-<slug>` pointer — the `/orc-explain`
|
|
231
|
+
precedent, a stated blind spot rather than an oversight.
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
WAIT mode=hard requested=30m start=18:44 end=19:14 hops=1/4 trigger=user
|
|
235
|
+
WAIT block reason="window resets in 5m, task needs 10" by=user
|
|
236
|
+
WAIT unblock
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
A wait that leaves no line cannot be counted, and a block that leaves no line
|
|
240
|
+
hides the fact that a run continued through a gate on the user's authority.
|