@azure-id/orc 0.56.1 → 1.1.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.
Files changed (162) hide show
  1. package/CHANGELOG.md +219 -0
  2. package/README-id.md +49 -107
  3. package/README.md +694 -719
  4. package/bin/build-agents.js +18 -11
  5. package/bin/cli.js +33699 -29855
  6. package/bin/onboarding-content.js +0 -3
  7. package/bin/test-run.js +491 -0
  8. package/bin/verify-contracts.js +4330 -3277
  9. package/bin/verify-package.js +76 -6
  10. package/bin/webui/api.js +1201 -1163
  11. package/bin/webui/app.html +210 -198
  12. package/bin/webui/css/06-responsive.css +120 -108
  13. package/bin/webui/css/panels/extra.css +29 -0
  14. package/bin/webui/css/panels/lanes.css +134 -0
  15. package/bin/webui/css/panels/settings.css +79 -3
  16. package/bin/webui/css/panels/wait.css +123 -0
  17. package/bin/webui/fixtures/extra.js +2036 -1934
  18. package/bin/webui/fixtures/flow.js +2 -2
  19. package/bin/webui/fixtures/index.js +510 -486
  20. package/bin/webui/fixtures/lanes.js +218 -0
  21. package/bin/webui/fixtures/settings.js +261 -34
  22. package/bin/webui/fixtures/stats.js +3 -3
  23. package/bin/webui/fixtures/wait.js +97 -0
  24. package/bin/webui/i18n/TERMS.md +135 -134
  25. package/bin/webui/i18n/en/extra.json +345 -313
  26. package/bin/webui/i18n/en/lanes.json +29 -0
  27. package/bin/webui/i18n/en/nav.json +21 -19
  28. package/bin/webui/i18n/en/overview.json +2 -0
  29. package/bin/webui/i18n/en/settings.json +16 -5
  30. package/bin/webui/i18n/en/wait.json +41 -0
  31. package/bin/webui/i18n/id/extra.json +345 -313
  32. package/bin/webui/i18n/id/lanes.json +29 -0
  33. package/bin/webui/i18n/id/nav.json +21 -19
  34. package/bin/webui/i18n/id/overview.json +2 -0
  35. package/bin/webui/i18n/id/settings.json +15 -4
  36. package/bin/webui/i18n/id/wait.json +41 -0
  37. package/bin/webui/js/01-i18n.js +151 -149
  38. package/bin/webui/js/panels/extra.js +3199 -3011
  39. package/bin/webui/js/panels/lanes.js +259 -0
  40. package/bin/webui/js/panels/overview.js +10 -0
  41. package/bin/webui/js/panels/settings.js +149 -3
  42. package/bin/webui/js/panels/wait.js +253 -0
  43. package/package.json +39 -39
  44. package/templates/agents/MODEL-MAPPING.md +23 -35
  45. package/templates/agents/orc-executor-opus-4-7-high.md +1 -1
  46. package/templates/agents/orc-executor-opus-4-7-med.md +1 -1
  47. package/templates/agents/orc-executor-opus-4-8-high.md +1 -1
  48. package/templates/agents/orc-executor-opus-5-high.md +1 -1
  49. package/templates/agents/orc-executor-opus-5-low.md +1 -1
  50. package/templates/agents/orc-executor-opus-5-med.md +1 -1
  51. package/templates/agents/orc-retro-opus-5-med.md +73 -73
  52. package/templates/agents/orc-retro-sonnet-5-high.md +72 -72
  53. package/templates/agents/orc-trace-writer-haiku-4-5.md +107 -107
  54. package/templates/commands/orc-route.md +1 -1
  55. package/templates/commands/orc-wait.md +19 -0
  56. package/templates/commands/orc.md +1 -1
  57. package/templates/hooks/orc-statusline.js +39 -0
  58. package/templates/skills/_shared/README.md +7 -3
  59. package/templates/skills/_shared/config-precedence.md +198 -0
  60. package/templates/skills/_shared/drift-recovery.md +1 -1
  61. package/templates/skills/_shared/extra-dispatch.md +159 -7
  62. package/templates/skills/_shared/interview.md +1 -1
  63. package/templates/skills/_shared/opus5-only.md +9 -7
  64. package/templates/skills/_shared/phases/README.md +82 -0
  65. package/templates/skills/{orc/references → _shared/phases}/analyst-gates.md +15 -3
  66. package/templates/skills/_shared/phases/execution.md +143 -0
  67. package/templates/skills/{orc/references → _shared/phases}/house-rules.md +13 -1
  68. package/templates/skills/{orc/references → _shared/phases}/intake.md +15 -2
  69. package/templates/skills/_shared/phases/mock-example.md +56 -0
  70. package/templates/skills/{orc/references → _shared/phases}/plan-handoff.md +16 -3
  71. package/templates/skills/_shared/phases/planning.md +135 -0
  72. package/templates/skills/_shared/phases/preflight.md +98 -0
  73. package/templates/skills/_shared/phases/review.md +53 -0
  74. package/templates/skills/_shared/phases/scoring.md +65 -0
  75. package/templates/skills/{orc/references → _shared/phases}/security-checklist.md +12 -1
  76. package/templates/skills/_shared/phases/security.md +55 -0
  77. package/templates/skills/_shared/phases/ship.md +88 -0
  78. package/templates/skills/{orc/references/stop-and-resume.md → _shared/phases/stop-resume.md} +169 -160
  79. package/templates/skills/_shared/phases/summary.md +48 -0
  80. package/templates/skills/_shared/phases/testgen.md +51 -0
  81. package/templates/skills/{orc/references/trace-protocol.md → _shared/phases/trace.md} +49 -6
  82. package/templates/skills/_shared/phases/verify.md +70 -0
  83. package/templates/skills/{orc/references → _shared/phases}/wave-grouping.md +14 -2
  84. package/templates/skills/{orc/references → _shared/phases}/wiki-consult.md +13 -1
  85. package/templates/skills/_shared/read-ladder.md +48 -0
  86. package/templates/skills/_shared/wait.md +240 -0
  87. package/templates/skills/context-combiner/SKILL.md +214 -187
  88. package/templates/skills/orc/README.md +148 -150
  89. package/templates/skills/orc/SKILL.md +75 -358
  90. package/templates/skills/orc/config.md +137 -389
  91. package/templates/skills/orc/examples/full-run-mock.md +1 -1
  92. package/templates/skills/orc/references/effort-and-mode.md +16 -18
  93. package/templates/skills/orc/references/phases/intake.md +47 -0
  94. package/templates/skills/orc/references/phases/integration.md +19 -0
  95. package/templates/skills/orc/references/preflight-report.md +4 -4
  96. package/templates/skills/orc/references/ultra-mode.md +117 -123
  97. package/templates/skills/orc/schemas/planning-output.md +279 -279
  98. package/templates/skills/orc/subskills/orc-planner/SKILL.md +260 -264
  99. package/templates/skills/orc/subskills/orc-pr/stack-gate.md +4 -1
  100. package/templates/skills/orc-aftermath/SKILL.md +158 -136
  101. package/templates/skills/orc-analyze/SKILL.md +252 -220
  102. package/templates/skills/orc-analyze/references/branching.md +51 -51
  103. package/templates/skills/orc-analyze/references/thin-input.md +1 -1
  104. package/templates/skills/orc-analyze-mini/SKILL.md +128 -112
  105. package/templates/skills/orc-boundary/SKILL.md +249 -208
  106. package/templates/skills/orc-brainstorm/SKILL.md +377 -339
  107. package/templates/skills/orc-budget/SKILL.md +248 -225
  108. package/templates/skills/orc-challenge/README.md +1 -1
  109. package/templates/skills/orc-challenge/SKILL.md +298 -255
  110. package/templates/skills/orc-challenge/examples/council-full-roster.md +1 -1
  111. package/templates/skills/orc-claude/SKILL.md +221 -199
  112. package/templates/skills/orc-diy/README.md +2 -1
  113. package/templates/skills/orc-diy/SKILL.md +132 -68
  114. package/templates/skills/orc-diy/references/blocks/analyze.md +1 -1
  115. package/templates/skills/orc-diy/references/compile.md +21 -7
  116. package/templates/skills/orc-diy/references/flow-schema.md +2 -2
  117. package/templates/skills/orc-doc/SKILL.md +49 -7
  118. package/templates/skills/orc-explain/SKILL.md +24 -0
  119. package/templates/skills/orc-export/SKILL.md +31 -5
  120. package/templates/skills/orc-fast/SKILL.md +218 -185
  121. package/templates/skills/orc-grill/SKILL.md +238 -207
  122. package/templates/skills/orc-handoff/SKILL.md +223 -204
  123. package/templates/skills/orc-learn/SKILL.md +181 -156
  124. package/templates/skills/orc-mini/SKILL.md +47 -24
  125. package/templates/skills/orc-pact/SKILL.md +255 -218
  126. package/templates/skills/orc-pattern/SKILL.md +154 -124
  127. package/templates/skills/orc-poly/SKILL.md +242 -216
  128. package/templates/skills/orc-poly/references/gather.md +1 -1
  129. package/templates/skills/orc-pr-driver/SKILL.md +196 -167
  130. package/templates/skills/orc-pr-driver/references/orc-run-split.md +1 -1
  131. package/templates/skills/orc-pr-setup/SKILL.md +212 -184
  132. package/templates/skills/orc-quick/README.md +2 -2
  133. package/templates/skills/orc-quick/SKILL.md +51 -13
  134. package/templates/skills/orc-quick/references/dispatch-gate.md +2 -3
  135. package/templates/skills/orc-retro/SKILL.md +249 -222
  136. package/templates/skills/orc-retro/examples/retro-mock.md +171 -171
  137. package/templates/skills/orc-route/SKILL.md +188 -165
  138. package/templates/skills/orc-verify/SKILL.md +103 -84
  139. package/templates/skills/orc-wait/SKILL.md +163 -0
  140. package/templates/skills/orc-wiki/SKILL.md +60 -206
  141. package/templates/skills/orc-wiki/references/phases/phase-0.md +71 -0
  142. package/templates/skills/orc-wiki/references/phases/phase-1.md +35 -0
  143. package/templates/skills/orc-wiki/references/phases/phase-2.md +52 -0
  144. package/templates/skills/orc-wiki/references/phases/phase-3.md +57 -0
  145. package/templates/skills/orc-wiki/references/phases/phase-3c.md +36 -0
  146. package/templates/agents/orc-advisor-fable-5.md +0 -50
  147. package/templates/agents/orc-analyst-fable-5.md +0 -115
  148. package/templates/agents/orc-judge-fable-5.md +0 -79
  149. package/templates/agents/orc-planner-fable-5.md +0 -152
  150. package/templates/agents/orc-reviewer-fable-5.md +0 -57
  151. package/templates/skills/_shared/fable5-override.md +0 -56
  152. package/templates/skills/orc-diy/references/blocks/execution.md +0 -42
  153. package/templates/skills/orc-diy/references/blocks/mock-example.md +0 -22
  154. package/templates/skills/orc-diy/references/blocks/planning.md +0 -34
  155. package/templates/skills/orc-diy/references/blocks/review.md +0 -18
  156. package/templates/skills/orc-diy/references/blocks/scoring.md +0 -16
  157. package/templates/skills/orc-diy/references/blocks/security.md +0 -25
  158. package/templates/skills/orc-diy/references/blocks/ship.md +0 -25
  159. package/templates/skills/orc-diy/references/blocks/summary.md +0 -18
  160. package/templates/skills/orc-diy/references/blocks/testgen.md +0 -17
  161. package/templates/skills/orc-diy/references/blocks/trace.md +0 -28
  162. package/templates/skills/orc-diy/references/blocks/verify.md +0 -25
@@ -0,0 +1,198 @@
1
+ # Shared contract — config precedence: ranks, families, and one resolver per lane
2
+
3
+ Canonical rule for how EVERY ORC lane learns what it is configured to do.
4
+ Consumed by every lane that reads a setting; the lint pins the
5
+ `config-precedence.md` pointer into each of them.
6
+
7
+ Two sentences carry the whole file:
8
+
9
+ > **ONE resolver, and it is not you:** `orc lane config <lane> --json`.
10
+ >
11
+ > **Read a family top-down and stop at the first rank that resolves.**
12
+
13
+ ---
14
+
15
+ ## 1. The resolver
16
+
17
+ A lane never reads `.claude/orc.config.yaml`. It runs
18
+ `orc lane config <lane> --json` once, at preflight, and obeys the answer.
19
+
20
+ | field | what a lane does with it |
21
+ |---|---|
22
+ | `effective` | the flat answer. Obey it and reason about nothing. |
23
+ | `announce[]` | print every line VERBATIM at preflight, before the first dispatch |
24
+ | `stops[]` | every value that can HARD-STOP this run; honour each before wave 1 |
25
+ | `keys[]` | per key: `value`, `default`, `prio`, `family`, `state`, `is_shadowed` / `shadow_reason`, `is_inert` / `inert_reason` |
26
+ | `families{}` | which rank answered each question, and why the rest were not read |
27
+ | `not_read[]` | the keys this lane deliberately ignores |
28
+
29
+ `not_read[]` is an ANSWER, not a gap. "This lane does not read `doc_language`"
30
+ is information, and it is what makes the two-way registry lint possible.
31
+
32
+ **Never re-derive a value, a precedence, or an inertness.** A key this lane does
33
+ not read is not in the answer; a key another key shadows comes back already
34
+ marked, with the sentence already worded. A lane that reasons *"`opus5_only` is
35
+ on, so the fable block must be inert"* has forked the resolver — it reads
36
+ `is_shadowed` and prints `shadow_reason`.
37
+
38
+ ---
39
+
40
+ ## 2. Ranks, and the rule that makes them mean something
41
+
42
+ Every key declares `answers[]`: which FAMILY question it answers, at which
43
+ RANK (`P0` · `P1` · `P2` · `P3`), in which MODE.
44
+
45
+ > **Read a family top-down and stop at the first rank that resolves.**
46
+ > If the P0 key of a family resolves, P1, P2 and P3 of that family are **not
47
+ > read at all** — not consulted, not defaulted, not mentioned as a fallback. A
48
+ > rank below a resolved rank has no effect and no meaning for this decision.
49
+
50
+ **A rank compares only INSIDE its family.** `log_dir` being P2 and `opus5_only`
51
+ being P1 says nothing whatever: they never compete. A family whose keys do not
52
+ compete has every key at P2, the neutral rank, and the lint asserts it — rank
53
+ distinctness is required only where a family is declared contested.
54
+
55
+ | `mode` | resolves when | effect on the ranks below it |
56
+ |---|---|---|
57
+ | `replace` | the key is set to a truthy / non-default value | every lower rank is shadowed, entirely |
58
+ | `overlay` | the key is on **and** at least one row applies to THIS decision | lower ranks are shadowed only for the ranges or slots it covers |
59
+ | `gate` | — | it never resolves the family; it makes its own dependants **inert** (§4) |
60
+
61
+ An `overlay` resolves PARTIALLY, so the honest report is WHICH ranges and WHICH
62
+ positions it took — never a single word. The ranges it did not take fall through
63
+ to the next rank normally. That is what lets "cheap work goes to a foreign
64
+ worker, hard work stays on Opus 5" be two rows rather than a table rewrite.
65
+
66
+ **The lowest rank of a contested family is TOTAL.** Something must answer when
67
+ nothing above resolved, so that terminal row is the shipped default and it is
68
+ declared as a row, not as a key — a fall-through is not a setting.
69
+
70
+ ---
71
+
72
+ ## 3. The two contested families
73
+
74
+ Everything else is uncontested: one question, no competition, every key P2.
75
+
76
+ **`executor-band` — which model executes a SCORED task**
77
+
78
+ | rank | key | mode | |
79
+ |---|---|---|---|
80
+ | P0 | `extra_enabled` | overlay | a route row covering this score sends it off Claude |
81
+ | P1 | `opus5_only` | replace | the fixed 2-band Opus 5 ladder |
82
+ | P2 | `rubric_bands_override` | replace | hand-edited, registry-less by design; resolves on PRESENCE |
83
+ | P3 | — | terminal | the shipped score→model table |
84
+
85
+ **`fixed-role-model` — which model runs a role that has no score**
86
+
87
+ | rank | key | mode | |
88
+ |---|---|---|---|
89
+ | P0 | `extra_enabled` | overlay | a slot row holds that POSITION |
90
+ | P1 | `opus5_only` | replace | the shipped Opus 5 variant of that position's agent |
91
+ | P2 | — | terminal | the agent shipped for that position |
92
+
93
+ Said once, for both shapes: **extra decides whether a Claude agent runs at all;
94
+ `opus5_only` and the score tables only decide WHICH Claude agent runs where
95
+ extra did not take it.** Under a taken band or a taken slot, `opus5_only` is
96
+ **not consulted** — not "inert" — and it stays fully live everywhere else.
97
+
98
+ ---
99
+
100
+ ## 4. A gate is not a rank — it is inertness
101
+
102
+ `extra_enabled: false` does not lose a precedence contest. It means **no
103
+ `extra_*` key is consulted at all**. A key declares `gated_by`, inherits its
104
+ gate's row, and comes back `inert` with the reason already worded.
105
+
106
+ Lane-level inertness is a THIRD thing, with nothing in the config file involved:
107
+
108
+ | lane | inert | reason (must not be softened) |
109
+ |---|---|---|
110
+ | `/orc-quick` | `opus5_only`, `rubric_bands_override`, `extra_*` | this lane asks WHICH AGENT before every dispatch — a config that silently answered that question would break the lane's entire premise |
111
+ | `/orc-challenge` | `opus5_only` | every agent in this lane is already `claude-opus-5` — a **no-op; the lane is unaffected, not exempt** |
112
+ | `/orc-doc` | `opus5_only` | the same — unaffected, not exempt |
113
+
114
+ **"Unaffected, not exempt"** survives verbatim. A generic "not applicable" loses
115
+ the fact that turning the key on breaks nothing here.
116
+
117
+ ---
118
+
119
+ ## 5. `announce[]` — and its exact boundary
120
+
121
+ A shadowed setting must never be silent, and work must never leave Claude
122
+ without the run saying so. Those lines are computed, worded once by the CLI, and
123
+ printed by the lane verbatim.
124
+
125
+ | goes in `announce[]` | stays with the lane |
126
+ |---|---|
127
+ | anything derivable from config + disk the CLI already reads: the wiki tier, pattern-cache presence, crosslink state, extra route rows and taken slots, pact / boundary / challenge state, which keys are shadowed or inert, the resolved scan tier, a run-scoped demotion | anything needing a per-task or per-run value the CLI cannot know: a task's score, a task id, a slug, a file count, a forecast number, a wave index |
128
+
129
+ A line needing a runtime value is **not** emitted as a template with holes. Half
130
+ a sentence from the CLI and half from the model is worse than either — it is a
131
+ sentence nobody owns. Those lines stay with the lane.
132
+
133
+ ---
134
+
135
+ ## 6. The ladder can move at runtime
136
+
137
+ A profile that stalls twice in one run, or holds one live attempt quiet past
138
+ `extra_demote_stale_min`, is DEMOTED to the bottom of its families for the rest
139
+ of that run — so the next rank down becomes the effective P0. The verdict is
140
+ recomputed from disk on every read and never stored; it is run-scoped and never
141
+ touches the config file; it is never promoted back on its own; and it is
142
+ ANNOUNCED, because the mirror of sending work off Claude silently is quietly
143
+ stopping. Full rule: `extra-dispatch.md`.
144
+
145
+ ---
146
+
147
+ ## 7. When the CLI is not there
148
+
149
+ Exit ≠ 0, or the command is not found:
150
+
151
+ 1. **Say so, in one line.** Fail open with the failure stated.
152
+ 2. Use the documented defaults below.
153
+ 3. Treat **every P0 forcing mode as OFF**.
154
+ 4. Never guess a user override, and never present a default as a resolved value.
155
+
156
+ The floor is the keys a run cannot start without — `max_wave_tasks: 3` ·
157
+ `batch_pause_every: 2` · `rubric_bands: 5` · `max_scouts: 3` ·
158
+ `default_analysis_depth: standard` · `generate_tests: false` ·
159
+ `pattern_findings: ask` · `security_review: off`.
160
+
161
+ Every other key's default is the CLI's to state. **A lane never guesses one** —
162
+ if a decision needs a key not on that list and the CLI cannot answer, the lane
163
+ says which key it could not resolve and stops, rather than inventing a value the
164
+ user may have overridden.
165
+
166
+ ---
167
+
168
+ ## 8. The `## Config` section every lane carries
169
+
170
+ Verbatim, and identical in every lane but its own name:
171
+
172
+ ```markdown
173
+ ## Config
174
+
175
+ **ONE resolver, and it is not you:** `orc lane config <lane> --json`. Obey
176
+ `effective`, print every line in `announce[]` VERBATIM at preflight, and honour
177
+ `stops[]` before wave 1. Never re-derive a value, a precedence or an inertness
178
+ from `.claude/orc.config.yaml` — a key this lane does not read is not in the
179
+ answer, and a key another key shadows comes back already marked. Exit ≠ 0 → say
180
+ the CLI is unavailable and fall back to `../_shared/config-precedence.md`'s
181
+ documented defaults, out loud. Priorities and families:
182
+ `../_shared/config-precedence.md`.
183
+ ```
184
+
185
+ ---
186
+
187
+ ## 9. The file the user edits
188
+
189
+ `.claude/orc.config.yaml` holds only the keys the user changed, sits outside
190
+ `templates/` (so `orc update` never clobbers it), and is written exclusively by
191
+ `orc config set`. It is grouped by the QUESTION each key answers, ordered by
192
+ rank only inside a contested family.
193
+
194
+ `orc config set` regenerates its own group comments on every write and
195
+ **never rewrites a value it was not given**: a comment the user wrote stays with
196
+ the key it sits above, and a hand-edited multi-line block survives byte for
197
+ byte. A key written under a retired spelling is rebuilt under its current name —
198
+ a renamed mechanism must never be a silent revert.
@@ -29,7 +29,7 @@ never in `.claude/`, never in the run folder):
29
29
  **Who builds it: NOT you.** The example is DISPATCHED like any task (orc hard
30
30
  rule 1 — the orchestrator never implements, not even the smallest task). It is
31
31
  an orchestrator-SYNTHESIZED task, so it is scored from a DERIVED vector, never a
32
- judged one — see `orc/references/wave-grouping.md` "Orchestrator-synthesized
32
+ judged one — see `_shared/phases/wave-grouping.md` "Orchestrator-synthesized
33
33
  tasks".
34
34
 
35
35
  **Git: NEVER committed.** The ship phase explicitly excludes `mock-examples/`
@@ -169,7 +169,7 @@ A slot is a **point**, not an interval, so the band table's overlap refusal has
169
169
  no analogue here and none is invented. `orc extra role set` on an occupied slot
170
170
  REPLACES, and says what it replaced.
171
171
 
172
- ### The nine hold-backs, each answered by name
172
+ ### The ten hold-backs, each answered by name
173
173
 
174
174
  1. **unknown slot** — refused, exit 2, listing the six.
175
175
  2. **`extra_enabled` false** — claude. The master gate.
@@ -191,6 +191,11 @@ REPLACES, and says what it replaced.
191
191
  preflight` STOPS the run. `extra_on_failure` never covers it: that key is
192
192
  about an endpoint that failed, and a deadline you set 30 days ago deserves a
193
193
  stop.
194
+ 10. **the profile is DEMOTED in this run** (v1.0.0) — claude, named, with the
195
+ promote command. The tenth hold-back and the only one that is about the RUN
196
+ rather than about the row: two consecutive stalls, a 20-minute stale live
197
+ attempt, or a human running `orc extra demote`. The position falls through
198
+ to the SAME pinned Claude agent it always had.
194
199
 
195
200
  ### What it hands back
196
201
 
@@ -202,7 +207,7 @@ REPLACES, and says what it replaced.
202
207
  | `band` | the ROUTE row's band, not the Claude band. On a slot answer it is the string **`slot:<slot>`** — the field NAME is unchanged, so the trace parser, the eight-field dedupe and the ` :: ` tolerance keep working untouched |
203
208
  | `slot` · `lane` · `asks` | present on a slot answer only. `score` is `null` there, and **is not derived from anything** |
204
209
  | `claude` | `{via, band, agent, table}` — **always present**, on both answers. It is the fall-through target AND the fallback target, so nothing ever needs a second lookup |
205
- | `held_back` | `null` · `role` · `risk` · `boundary` · `missing-profile` · `unverified` |
210
+ | `held_back` | `null` · `role` · `risk` · `boundary` · `missing-profile` · `unverified` · `demoted` |
206
211
  | `verify_state` · `needs_reping` | `FRESH` / `STALE`. **A STALE profile still routes** — a stale check is not a failed one |
207
212
  | `model_known` | whether the routed model id was in the last ping's `models_seen`. `false` is a WARNING, never a block: the list is a cache, not an authority |
208
213
  | `credential` | `{source, key_name, present}` — never a value |
@@ -228,7 +233,7 @@ One sentence, and it is the same sentence for both shapes:
228
233
  an extra route row covering this score (only for the scores it covers)
229
234
  > opus5_only
230
235
  > rubric_bands_override
231
- > the default 8-band table
236
+ > the default 6-band table
232
237
  ```
233
238
 
234
239
  **A slot**, highest wins:
@@ -260,7 +265,7 @@ it, `orc extra route set` names the Claude band each new row displaces, and
260
265
  `orc config list --json`'s `score_table.active` can read `extra+opus5_only` —
261
266
  a composite, because the truth is a composite and a single word would be a lie.
262
267
 
263
- **`/orc-quick` is a GATED CHOICE, not a route.** `opus5_only`, `fable5_*`,
268
+ **`/orc-quick` is a GATED CHOICE, not a route.** `opus5_only`,
264
269
  `rubric_bands_override` and `extra_resume` stay INERT there — that lane asks
265
270
  *which agent* before every dispatch, so a config that silently answered that
266
271
  question, or that silently continued the previous foreign worker's half-finished
@@ -686,7 +691,7 @@ only moment anyone can act on it is now.
686
691
 
687
692
  The trace line is still yours to relay, and it still matters: it is the
688
693
  human-readable narrative of the run, it is what `/orc-retro` reads, and the
689
- trace-cadence rule in `references/trace-protocol.md` still binds every phase.
694
+ trace-cadence rule in `phases/trace.md` still binds every phase.
690
695
  What changed is that the SPEND no longer depends on it.
691
696
 
692
697
  One `EXTRA` line per foreign dispatch, plus its continuations. **Copy
@@ -1056,6 +1061,146 @@ used. A CLI ping is not a cheap ping — say so.
1056
1061
 
1057
1062
  ---
1058
1063
 
1064
+ ## The demotion — the ladder moves at runtime (v1.0.0)
1065
+
1066
+ ### `a lane that keeps paying a provider that stopped working` has broken this contract
1067
+
1068
+ `extra_stall_s` stops ONE dispatch. It has nothing to say about the second one,
1069
+ or the third. A provider that goes quiet twice in a row is not having a bad
1070
+ minute — it is costing you a wave and buying nothing — and ORC used to keep
1071
+ handing it the next task anyway, because nothing counted.
1072
+
1073
+ So it counts now. **Two consecutive `stalled` dispatches on one profile inside
1074
+ one run, or one live attempt with no observable progress for 20 minutes, drop
1075
+ that profile to the BOTTOM of its families for the rest of the run.**
1076
+ `opus5_only` — or the shipped score→model table — becomes the effective P0, and
1077
+ the work stays on Claude.
1078
+
1079
+ ### It writes no new measurement, and it is never remembered
1080
+
1081
+ Every fact the trigger reads already shipped: `EXTRA_FAILURES.stalled` and the
1082
+ `timeline` block (v0.56.1), and the extra journal written by `orc extra dispatch`
1083
+ itself before the first byte leaves the machine (v0.54.0). The demotion READS
1084
+ the journal and decides — **computed by the CLI from disk on every read, never
1085
+ stored as a verdict and never carried in your head.** That is the lesson this
1086
+ repo has now lost to four times: v0.32.0 narration, v0.49.5 the hand-back,
1087
+ v0.53.2 the spend log, v0.54.0 the journal. **A fact relayed through a model's
1088
+ memory is a fact this repo has already lost.**
1089
+
1090
+ The only thing written to disk is the HUMAN half — a promote or a manual demote,
1091
+ with its reason — under `{run_dir}/{slug}/extra-demotion.json`, beside
1092
+ `RESUME.md`. That is a decision, not a verdict, and it is deleted with the run.
1093
+
1094
+ ### The two clocks measure different things and stay two clocks
1095
+
1096
+ ```
1097
+ demote(profile, run) ⇔
1098
+ consecutive_stalls(profile, run) >= extra_demote_after (default 2)
1099
+ OR a LIVE attempt quiet for >= extra_demote_stale_min minutes (default 20)
1100
+ ```
1101
+
1102
+ - **The consecutive clock is about attempts that ENDED.** A stall is a stall
1103
+ whether its resume succeeded or not: a profile that needs a continuation twice
1104
+ in a row is costing more than it saves.
1105
+ - **The stale clock is about an attempt that has NOT ended.** `extra_stall_s`
1106
+ already stops one dispatch after 180 seconds of silence. This one is about the
1107
+ RUN — two workers in flight, both quiet, and a wave that is going nowhere.
1108
+ Different question, different budget, its own key and its own off value.
1109
+ **Neither is a simplification of the other**, and merging them "for
1110
+ simplicity" is the failure this paragraph exists to prevent.
1111
+ - **The counter is per PROFILE**, not per provider and not global. Two profiles
1112
+ on one provider can hold different keys, different models and different
1113
+ reliability.
1114
+ - **A resume of the same stalled attempt is the SAME stall.** It neither
1115
+ increments the counter nor resets it.
1116
+ - **`consecutive` resets on a completed dispatch that was not a stall** —
1117
+ including a 401.
1118
+ - **ONLY `stalled` counts.** Not `timeout` (the budget was the operator's
1119
+ choice), not `rate_limit`, not `authentication_failed`. Each of those has its
1120
+ own answer — `extra_on_failure`, the vault, `extra_resume` — and **a demotion
1121
+ triggered by a 401 would hide a credential problem behind a routing change**,
1122
+ where the user would never see the real fault.
1123
+ - **`extra_stall_s: 0` silently turns the consecutive clock off too:** no
1124
+ dispatch is ever classified `stalled`, so the counter can never move and only
1125
+ the stale clock remains. `orc extra demotion` says so rather than reporting a
1126
+ clock that cannot fire.
1127
+
1128
+ ### The four things it must never do
1129
+
1130
+ 1. **It never writes your config.** Run-scoped, like `/orc-ultra`'s
1131
+ `ultra_mode`. A run that had a bad afternoon must not silently turn off a
1132
+ subsystem you paid to set up.
1133
+ 2. **It never auto-promotes inside the run.** Two stalls is evidence, and a
1134
+ timer that re-arms a provider spends money on the evidence it already has.
1135
+ `orc extra promote <run> --reason "<why>"` is a human action and the reason is
1136
+ stored verbatim — the `/orc-pact` retirement rule. **A promote is a
1137
+ WATERMARK, not a mute:** it forgives the evidence it saw, and two fresh stalls
1138
+ after it demote again.
1139
+ 3. **It never changes WHAT.** The score does not move, `declared_files` is not
1140
+ widened, `acceptance[]` is not touched — `extra_fallback_agent`'s rule
1141
+ inherited verbatim. **A demotion changes WHERE the work runs and nothing
1142
+ else**, and the task lands on the SAME Claude agent it would have had.
1143
+ 4. **It never abandons the position.** A demoted run still goes
1144
+ `orc extra reconcile` → `orc extra resume-slice` → an ordinary Claude
1145
+ dispatch. `a lane that re-does work the worktree already contains` applies
1146
+ here exactly as it applies to the fallback, and it is the same code path.
1147
+
1148
+ ### It is ANNOUNCED, and that is not optional
1149
+
1150
+ The mirror of `a lane that sends work off Claude without saying so` is **a lane
1151
+ that quietly STOPS.** A user who connected a provider deserves to be told the
1152
+ run left it. The line is CLI-worded (`announce` on the demotion state), it
1153
+ arrives in `orc lane config`'s `announce[]`, and it must be printed **before the
1154
+ next dispatch**. `orc lane config` renders the overlay rank as `state:
1155
+ "demoted"` — its own word, keeping its slot, because rendering it as
1156
+ `extra: off` would make *"I turned this off"* and *"this run left your provider
1157
+ after two stalls"* look identical.
1158
+
1159
+ The trace verb is **CLI-composed and copied verbatim**, because **a demotion
1160
+ that leaves no line cannot be counted** (the v0.53.2 rule, restated):
1161
+
1162
+ ```
1163
+ EXTRA demote run=<slug> :: profile=<p> reason=<consecutive-stall|stale-live-attempt|manual> n=<k> → <the ladder that answers now>
1164
+ ```
1165
+
1166
+ ### The three commands
1167
+
1168
+ | command | exit codes | notes |
1169
+ |---|---|---|
1170
+ | `orc extra demotion [<run>] [--json]` | `0` armed · `1` demoted · `2` unknown run | a READ, and the exit code is the answer. `--json is not a summary`: it carries both clocks, the counter, the evidence task ids, the ladder before and after, and the announce line |
1171
+ | `orc extra promote <run> --reason "<why>"` | `0` promoted · `1` nothing was demoted · `2` unknown run | the reason is REQUIRED and stored verbatim |
1172
+ | `orc extra demote <run> --reason "<why>"` | `0` demoted · `2` unknown run | the manual half. Somebody who knows the provider is down should not have to wait for two stalls |
1173
+
1174
+ `orc extra preflight` **reports a live demotion and does not move its exit
1175
+ code** — a demotion is a finding, not a stop, the `extra-orphan-dispatch`
1176
+ precedent. `orc doctor` gains **`extra-demoted-run`**, routed to the Extra
1177
+ panel's Recovery tab, which is where `orc extra promote` lives.
1178
+
1179
+ `orc extra stats` aggregates it **per profile across every run the journal still
1180
+ holds** — a provider that demotes in every run is a pattern; one that demoted
1181
+ once is an afternoon, and the difference is only visible if somebody counts.
1182
+ There is **no rate below 3 runs**, the `extra-profile-unreliable` restraint
1183
+ verbatim.
1184
+
1185
+ ### The keys, and the two that were refused
1186
+
1187
+ | key | default | off |
1188
+ |---|---:|---|
1189
+ | `config.extra_demote_after` | `2` | `0` — never demote on consecutive stalls |
1190
+ | `config.extra_demote_stale_min` | `20` | `0` — never demote on a stale live attempt |
1191
+
1192
+ Both gated by `extra_enabled`. **Setting both to `0` is the honest off switch**,
1193
+ which is why there is no third key:
1194
+
1195
+ - **REFUSED — `extra_demote` (`on`/`off`).** The two zeros already are it, and a
1196
+ master switch over two numbers is a third spelling of one thing — the drift
1197
+ this subsystem lints for everywhere else.
1198
+ - **REFUSED — `extra_promote_after` (an auto re-promote timer).** A timer that
1199
+ re-arms a provider which stalled twice is a timer that spends money on the
1200
+ evidence it already has. Promotion is a human decision with a recorded reason.
1201
+
1202
+ ---
1203
+
1059
1204
  ## Who picks the task up — `extra_fallback_agent` (v0.56.1)
1060
1205
 
1061
1206
  `fallback_to` has always carried the band's (or the slot's) own Claude agent.
@@ -1094,19 +1239,24 @@ Three rules hold it together.
1094
1239
  slice. This is the `extra resume-slice` rule applied to the fallback.
1095
1240
 
1096
1241
  **`/orc-quick` is INERT here too,** and announces it at the agent gate — beside
1097
- `extra_enabled`, `extra_on_failure`, `extra_resume`, `opus5_only`, `fable5_*` and
1242
+ `extra_enabled`, `extra_on_failure`, `extra_resume`, `opus5_only` and
1098
1243
  `rubric_bands_override`. That lane asks which agent before every dispatch, so a
1099
1244
  config that pre-answered it would be the exact failure the gate exists to
1100
1245
  prevent.
1101
1246
 
1102
1247
  ---
1103
1248
 
1104
- ## The config surface — thirteen keys, and the count is still the point
1249
+ ## The config surface — fifteen keys, and the count is still the point
1105
1250
 
1106
1251
  The combinatorial part — providers × models × bands **× positions** — is a
1107
1252
  **ledger with a CLI and a panel** (`orc extra`), not a YAML block nobody can hold
1108
1253
  in their head.
1109
1254
 
1255
+ **v1.0.0 added TWO** — `extra_demote_after` and `extra_demote_stale_min` — and
1256
+ both are NUMBERS WITH A DOCUMENTED OFF VALUE rather than a switch, because the
1257
+ two zeros already are the off switch and a master key over them would be a third
1258
+ spelling of one thing. Two more were refused and are written down above.
1259
+
1110
1260
  **v0.56.1 added TWO**, and both come from the same observed failure: a foreign
1111
1261
  worker that goes quiet mid-task. Three more were refused and are written down so
1112
1262
  nobody proposes them again — **a stdin nudge** (`opencode run` is not an
@@ -1141,6 +1291,8 @@ of being a string nobody checked).
1141
1291
  | `config.extra_resume` | `on` | Whether a partial or crashed foreign dispatch is RESUMED rather than re-done. **Default `on`, because `off` is what is broken.** INERT in `/orc-quick`. |
1142
1292
  | `config.extra_resume_max` | `2` | Resume attempts per task before P6 takes over. The cap STOPS with an honest report naming the Claude agent — never a silent third loop, the same shape as every other bounded repair loop in ORC. |
1143
1293
  | `config.extra_stall_s` | `180` | Seconds a foreign worker may produce NOTHING before the dispatch is stopped as `stalled`. Reset by observable progress — the worker's stream, its stderr, or a declared file that changed on disk — so it never fires on a worker that is merely slow. `0` disables and the wall clock is the only stop again. **Clamped below `extra_timeout_s`**, because a budget that can never fire is worse than none. Engine `cli` only. |
1294
+ | `config.extra_demote_after` | `2` | Consecutive `stalled` dispatches on ONE profile inside one run before that profile is DEMOTED to the bottom of the ladder for the rest of the run. Only `stalled` counts; a resume of the same stall is the same stall; a completed dispatch that was not a stall resets it. Run state — it never writes your config, it is ANNOUNCED, and it is never promoted back on its own. `0` turns this clock off. |
1295
+ | `config.extra_demote_stale_min` | `20` | Minutes a LIVE attempt may show no observable progress before its profile is demoted. A different question from `extra_stall_s`, which stops ONE dispatch: this clock is about the RUN. `0` turns this clock off. |
1144
1296
  | `config.extra_fallback_agent` | `band` | WHICH Claude agent picks up a task the foreign worker could not finish. `band` is the pre-v0.56.1 behaviour. `ask` STOPS and puts the menu to the user. Any installed agent name pins one. It changes WHO, never the score, the declared files or the acceptance criteria. INERT in `/orc-quick`. |
1145
1297
 
1146
1298
  **Keys deliberately NOT added, and why each one would be a trap:**
@@ -3,7 +3,7 @@
3
3
  Canonical file: `_shared/interview.md`. THE canonical way any ORC lane
4
4
  interrogates a human until an idea is sharp enough to act on. Load it wherever a
5
5
  lane must ask more than a fixed questionnaire's worth of questions: `/orc-grill`
6
- runs it end to end, and `references/intake.md` borrows its round format when a
6
+ runs it end to end, and `phases/intake.md` borrows its round format when a
7
7
  tier's question set runs long.
8
8
 
9
9
  This is not "ask good questions". It is a specific mechanic with a termination
@@ -13,14 +13,18 @@ is inert.
13
13
 
14
14
  ## The mapping (only when `opus5_only: true`)
15
15
 
16
- **Scored executors** — one model, EFFORT as the cost dial, replacing the 8-band
16
+ **Scored executors** — one model, EFFORT as the cost dial, replacing the 6-band
17
17
  mixed-model table:
18
18
 
19
19
  | Score | Executor agent |
20
20
  |-----------|---------------------------|
21
- | `[0,40)` | `orc-executor-opus-5-low` |
22
- | `[40,80)` | `orc-executor-opus-5-med` |
23
- | `[80,100]` | `orc-executor-opus-5-high` |
21
+ | `[0,90)` | `orc-executor-opus-5-low` |
22
+ | `[90,100]` | `orc-executor-opus-5-med` |
23
+
24
+ Two bands, sharing the 90 edge with the default table's top two rows. Since
25
+ v1.0.0 the default table's high end is ALREADY Opus 5 with effort as the dial,
26
+ so this mode differs from it only below 65 — a third band here would be a
27
+ distinction the default table stopped making.
24
28
 
25
29
  **Fixed roles** — dispatch the Opus 5 variant **instead of** the default role
26
30
  agent: same task slice, same return contract, same phase.
@@ -77,12 +81,10 @@ exists to prevent. Ladder: `../orc-wiki/references/partial-refresh.md`.
77
81
  `opus5_only: true` outranks every other dispatch selector:
78
82
 
79
83
  - each role's default frontmatter pin;
80
- - **`fable5_enabled` / `fable5_roles`** — the Fable 5 role override is fully
81
- INERT while the mode is on (`../_shared/fable5-override.md` does not apply);
82
84
  - **`rubric_bands_override`** — a hand-written executor table is ignored while
83
85
  the mode is on.
84
86
 
85
- Turning the mode off restores all three. `rubric_bands` remains granularity
87
+ Turning the mode off restores both. `rubric_bands` remains granularity
86
88
  only, never a preset selector, in both modes.
87
89
 
88
90
  The forcing is flat across every lane that HONORS the mode — `orc-quick` does
@@ -0,0 +1,82 @@
1
+ # `_shared/phases/` — one copy of a phase, and a manifest per lane
2
+
3
+ This directory is NOT a skill and NOT a lane. It holds the single canonical copy
4
+ of a phase that **two or more lanes run**. A lane's spine keeps its identity, its
5
+ trigger, its own hard rules and its own phases — and for a shared phase it keeps
6
+ only a POINTER plus that lane's own deltas.
7
+
8
+ **The CLI owns the pipeline, not the prose.** `orc lane phases <lane> [--json]`
9
+ is the manifest: the ordered phase list, the file each phase lives in, the layers
10
+ that lane reads, the catalogued calls it makes, and when to read it. A skill
11
+ never derives the phase list or its order from these filenames — the same rule
12
+ the Flow stepper follows, for the same reason: *a second idea of the pipeline is
13
+ the drift this exists to make impossible.*
14
+
15
+ ## What belongs here — the rule is mechanical
16
+
17
+ > A file under `templates/skills/<lane>/` that a file in a DIFFERENT lane already
18
+ > points at belongs here. **A file with exactly one consumer stays home.**
19
+
20
+ That is auditable by grep, which is what makes it a lint rather than an opinion.
21
+ `bin/verify-contracts.js` asserts both halves: nothing under a lane folder may be
22
+ pointed at from another lane, and every file here must be claimed by **≥2 lanes**.
23
+
24
+ Centralizing a one-consumer file is centralizing for its own sake, and it costs a
25
+ lane its own wording for nothing.
26
+
27
+ ## The layer set is CLOSED
28
+
29
+ A phase file may be cut into layers with the same marker grammar `orc diy
30
+ compile` already parses (`<!-- diy:when key=value -->`):
31
+
32
+ ```markdown
33
+ <!-- orc:layer core -->
34
+ Every lane that runs this phase does this. Never optional.
35
+ <!-- /orc:layer -->
36
+
37
+ <!-- orc:layer trim -->
38
+ orc-mini / orc-fast: ONE executor, no waves. This is a REDUCTION and it is
39
+ deliberate — do not read the `full` layer here.
40
+ <!-- /orc:layer -->
41
+ ```
42
+
43
+ | Layer | Read by | Meaning |
44
+ |---|---|---|
45
+ | `core` | every lane running this phase | the invariant. Never optional |
46
+ | `full` | `/orc`, `/orc-ultra` | the complete procedure |
47
+ | `trim` | `orc-mini`, `orc-fast` | an explicit REDUCTION, stated as one |
48
+ | `composed` | `orc-diy` | what `orc diy compile` stitches |
49
+
50
+ **Four names, closed. A fifth layer is a lint failure, not a feature** — free
51
+ markers are drift with extra steps. A lane reads `core` plus at most one other
52
+ layer, and `orc lane phases` tells it which.
53
+
54
+ **Why the set exists at all:** the single biggest way this library breaks ORC is
55
+ `orc-mini` reading a shared `review.md` written for the full lane and starting to
56
+ do a full code review. Mini's product promise is that it *skips* review.
57
+ Centralizing without layers does not just cost tokens — it changes behaviour. So
58
+ **a `trim` layer must say what it drops and that dropping it is deliberate.**
59
+
60
+ **A single-layer file is a legitimate answer.** `trace.md` and `stop-resume.md`
61
+ declare `core` only: their procedure really is identical in every lane that runs
62
+ them, and what varies is DATA (the tier table, the lane token), not prose. Cutting
63
+ them into layers to look symmetrical would be inventing structure the phase does
64
+ not have.
65
+
66
+ ## Pointer discipline
67
+
68
+ Every pointer a spine adds declares `when` and `read` — the partial-read rules in
69
+ `../read-ladder.md`. `on-phase` is the default; `always` must be justified in the
70
+ release's findings; a `read:` names a HEADING and **never a line number**
71
+ (`/orc-doc` rule 2 — a stored line number is a wrong line number one edit later).
72
+
73
+ ## What is here
74
+
75
+ | File | id | Layers | Lanes |
76
+ |---|---|---|---|
77
+ | `trace.md` | `trace` | `core` | every trace-owning lane (28) |
78
+ | `preflight.md` | `preflight` | `core`, `full` | the silent-probe lanes (15) |
79
+ | `stop-resume.md` | `stop-resume` | `core` | `orc`, `orc-wiki`, `orc-diy` |
80
+
81
+ `orc lane phases --all --json` is the authoritative list; this table is a
82
+ human index of it.
@@ -1,9 +1,19 @@
1
- # Reference — Analyst, Combiner & Plan Gates (orchestrator side)
1
+ # Phase — Analyst & plan gates (id: `analyst-gates`)
2
+
3
+ > **Library file.** Canonical since v1.0.0 W12; it was under the `orc` skill's
4
+ > private `references/`, and other lanes already reached across into it. Read
5
+ > by `orc`, `orc-analyze`, `orc-mini`. Layers declared: `core` only — single-
6
+ > layer because these are the orchestrator-side gates on a returned analysis
7
+ > or plan, and a trimmed lane runs fewer of them rather than different ones.
8
+ > `orc lane phases <lane> --json` names the file and the layers to read.
9
+
10
+ <!-- orc:layer core -->
11
+ ## Reference — Analyst, Combiner & Plan Gates (orchestrator side)
2
12
 
3
13
  The orchestrator's deterministic gates around the analyst, the
4
14
  context-combiner, and the planner. The roles themselves are defined in
5
15
  `../../orc-analyze/SKILL.md`, `../../context-combiner/SKILL.md`, and
6
- `../subskills/orc-planner/SKILL.md` — this file is what YOU do around their
16
+ `../../orc/subskills/orc-planner/SKILL.md` — this file is what YOU do around their
7
17
  dispatches and returns. Load at Phase 0 (analyst dispatch) and Phase 1
8
18
  (planner return). Emit a `GATE` trace line (pass|bounce) per check when
9
19
  logging.
@@ -113,7 +123,7 @@ proceed-with-flagged.
113
123
  - `new-surface` / `behavior-change` missing `skeleton` or `given_when_then`
114
124
  → miss.
115
125
  - A disposition that DEVIATES from the derivation table in
116
- `../schemas/planning-output.md` without a `reason` → miss. The derivation
126
+ `../../orc/schemas/planning-output.md` without a `reason` → miss. The derivation
117
127
  is the default; departing from it is the thing that needs justifying.
118
128
 
119
129
  Any miss → the plan is malformed: bounce it back to the planner WITH the miss
@@ -122,3 +132,5 @@ pre-v0.7.0 plan resumed from an old checkpoint has no `grounding[]`
122
132
  (pre-v0.9.0: no `requirements[]`/`spec_invariants[]`; pre-v0.41.0: no
123
133
  `disposition`) — resume it without the
124
134
  missing checks; never bounce an old plan.
135
+
136
+ <!-- /orc:layer -->