@azure-id/orc 0.56.1 → 1.0.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 (153) hide show
  1. package/CHANGELOG.md +2367 -2218
  2. package/README-id.md +49 -107
  3. package/README.md +45 -133
  4. package/bin/build-agents.js +18 -11
  5. package/bin/cli.js +33060 -29855
  6. package/bin/onboarding-content.js +0 -3
  7. package/bin/test-run.js +491 -0
  8. package/bin/verify-contracts.js +4256 -3277
  9. package/bin/verify-package.js +563 -498
  10. package/bin/webui/api.js +1186 -1163
  11. package/bin/webui/app.html +9 -0
  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/fixtures/extra.js +2036 -1934
  17. package/bin/webui/fixtures/flow.js +2 -2
  18. package/bin/webui/fixtures/index.js +503 -486
  19. package/bin/webui/fixtures/lanes.js +218 -0
  20. package/bin/webui/fixtures/settings.js +261 -34
  21. package/bin/webui/fixtures/stats.js +3 -3
  22. package/bin/webui/i18n/TERMS.md +135 -134
  23. package/bin/webui/i18n/en/extra.json +345 -313
  24. package/bin/webui/i18n/en/lanes.json +29 -0
  25. package/bin/webui/i18n/en/nav.json +1 -0
  26. package/bin/webui/i18n/en/overview.json +2 -0
  27. package/bin/webui/i18n/en/settings.json +16 -5
  28. package/bin/webui/i18n/id/extra.json +345 -313
  29. package/bin/webui/i18n/id/lanes.json +29 -0
  30. package/bin/webui/i18n/id/nav.json +1 -0
  31. package/bin/webui/i18n/id/overview.json +2 -0
  32. package/bin/webui/i18n/id/settings.json +15 -4
  33. package/bin/webui/js/01-i18n.js +1 -0
  34. package/bin/webui/js/panels/extra.js +3199 -3011
  35. package/bin/webui/js/panels/lanes.js +259 -0
  36. package/bin/webui/js/panels/overview.js +10 -0
  37. package/bin/webui/js/panels/settings.js +149 -3
  38. package/package.json +39 -39
  39. package/templates/agents/MODEL-MAPPING.md +23 -35
  40. package/templates/agents/orc-executor-opus-4-7-high.md +1 -1
  41. package/templates/agents/orc-executor-opus-4-7-med.md +1 -1
  42. package/templates/agents/orc-executor-opus-4-8-high.md +1 -1
  43. package/templates/agents/orc-executor-opus-5-high.md +1 -1
  44. package/templates/agents/orc-executor-opus-5-low.md +1 -1
  45. package/templates/agents/orc-executor-opus-5-med.md +1 -1
  46. package/templates/agents/orc-retro-opus-5-med.md +73 -73
  47. package/templates/agents/orc-retro-sonnet-5-high.md +72 -72
  48. package/templates/agents/orc-trace-writer-haiku-4-5.md +107 -107
  49. package/templates/commands/orc-route.md +1 -1
  50. package/templates/commands/orc.md +1 -1
  51. package/templates/skills/_shared/README.md +7 -3
  52. package/templates/skills/_shared/config-precedence.md +198 -0
  53. package/templates/skills/_shared/drift-recovery.md +1 -1
  54. package/templates/skills/_shared/extra-dispatch.md +159 -7
  55. package/templates/skills/_shared/interview.md +1 -1
  56. package/templates/skills/_shared/opus5-only.md +9 -7
  57. package/templates/skills/_shared/phases/README.md +82 -0
  58. package/templates/skills/{orc/references → _shared/phases}/analyst-gates.md +15 -3
  59. package/templates/skills/_shared/phases/execution.md +143 -0
  60. package/templates/skills/{orc/references → _shared/phases}/house-rules.md +13 -1
  61. package/templates/skills/{orc/references → _shared/phases}/intake.md +15 -2
  62. package/templates/skills/_shared/phases/mock-example.md +56 -0
  63. package/templates/skills/{orc/references → _shared/phases}/plan-handoff.md +16 -3
  64. package/templates/skills/_shared/phases/planning.md +135 -0
  65. package/templates/skills/_shared/phases/preflight.md +76 -0
  66. package/templates/skills/_shared/phases/review.md +53 -0
  67. package/templates/skills/_shared/phases/scoring.md +65 -0
  68. package/templates/skills/{orc/references → _shared/phases}/security-checklist.md +12 -1
  69. package/templates/skills/_shared/phases/security.md +55 -0
  70. package/templates/skills/_shared/phases/ship.md +88 -0
  71. package/templates/skills/{orc/references/stop-and-resume.md → _shared/phases/stop-resume.md} +169 -160
  72. package/templates/skills/_shared/phases/summary.md +48 -0
  73. package/templates/skills/_shared/phases/testgen.md +51 -0
  74. package/templates/skills/{orc/references/trace-protocol.md → _shared/phases/trace.md} +49 -6
  75. package/templates/skills/_shared/phases/verify.md +70 -0
  76. package/templates/skills/{orc/references → _shared/phases}/wave-grouping.md +14 -2
  77. package/templates/skills/{orc/references → _shared/phases}/wiki-consult.md +13 -1
  78. package/templates/skills/_shared/read-ladder.md +48 -0
  79. package/templates/skills/context-combiner/SKILL.md +214 -187
  80. package/templates/skills/orc/README.md +148 -150
  81. package/templates/skills/orc/SKILL.md +70 -358
  82. package/templates/skills/orc/config.md +137 -389
  83. package/templates/skills/orc/examples/full-run-mock.md +1 -1
  84. package/templates/skills/orc/references/effort-and-mode.md +16 -18
  85. package/templates/skills/orc/references/phases/intake.md +47 -0
  86. package/templates/skills/orc/references/phases/integration.md +19 -0
  87. package/templates/skills/orc/references/preflight-report.md +4 -4
  88. package/templates/skills/orc/references/ultra-mode.md +117 -123
  89. package/templates/skills/orc/schemas/planning-output.md +279 -279
  90. package/templates/skills/orc/subskills/orc-planner/SKILL.md +260 -264
  91. package/templates/skills/orc/subskills/orc-pr/stack-gate.md +4 -1
  92. package/templates/skills/orc-aftermath/SKILL.md +153 -136
  93. package/templates/skills/orc-analyze/SKILL.md +247 -220
  94. package/templates/skills/orc-analyze/references/branching.md +51 -51
  95. package/templates/skills/orc-analyze/references/thin-input.md +1 -1
  96. package/templates/skills/orc-analyze-mini/SKILL.md +128 -112
  97. package/templates/skills/orc-boundary/SKILL.md +244 -208
  98. package/templates/skills/orc-brainstorm/SKILL.md +372 -339
  99. package/templates/skills/orc-budget/SKILL.md +243 -225
  100. package/templates/skills/orc-challenge/README.md +1 -1
  101. package/templates/skills/orc-challenge/SKILL.md +293 -255
  102. package/templates/skills/orc-challenge/examples/council-full-roster.md +1 -1
  103. package/templates/skills/orc-claude/SKILL.md +216 -199
  104. package/templates/skills/orc-diy/README.md +2 -1
  105. package/templates/skills/orc-diy/SKILL.md +129 -70
  106. package/templates/skills/orc-diy/references/blocks/analyze.md +1 -1
  107. package/templates/skills/orc-diy/references/compile.md +21 -7
  108. package/templates/skills/orc-diy/references/flow-schema.md +2 -2
  109. package/templates/skills/orc-doc/SKILL.md +44 -7
  110. package/templates/skills/orc-explain/SKILL.md +19 -0
  111. package/templates/skills/orc-export/SKILL.md +26 -5
  112. package/templates/skills/orc-fast/SKILL.md +215 -187
  113. package/templates/skills/orc-grill/SKILL.md +233 -207
  114. package/templates/skills/orc-handoff/SKILL.md +223 -204
  115. package/templates/skills/orc-learn/SKILL.md +176 -156
  116. package/templates/skills/orc-mini/SKILL.md +41 -23
  117. package/templates/skills/orc-pact/SKILL.md +250 -218
  118. package/templates/skills/orc-pattern/SKILL.md +149 -124
  119. package/templates/skills/orc-poly/SKILL.md +237 -216
  120. package/templates/skills/orc-poly/references/gather.md +1 -1
  121. package/templates/skills/orc-pr-driver/SKILL.md +196 -167
  122. package/templates/skills/orc-pr-driver/references/orc-run-split.md +1 -1
  123. package/templates/skills/orc-pr-setup/SKILL.md +212 -184
  124. package/templates/skills/orc-quick/README.md +2 -2
  125. package/templates/skills/orc-quick/SKILL.md +45 -12
  126. package/templates/skills/orc-quick/references/dispatch-gate.md +2 -3
  127. package/templates/skills/orc-retro/SKILL.md +244 -222
  128. package/templates/skills/orc-retro/examples/retro-mock.md +171 -171
  129. package/templates/skills/orc-route/SKILL.md +183 -165
  130. package/templates/skills/orc-verify/SKILL.md +100 -86
  131. package/templates/skills/orc-wiki/SKILL.md +55 -206
  132. package/templates/skills/orc-wiki/references/phases/phase-0.md +71 -0
  133. package/templates/skills/orc-wiki/references/phases/phase-1.md +35 -0
  134. package/templates/skills/orc-wiki/references/phases/phase-2.md +52 -0
  135. package/templates/skills/orc-wiki/references/phases/phase-3.md +57 -0
  136. package/templates/skills/orc-wiki/references/phases/phase-3c.md +36 -0
  137. package/templates/agents/orc-advisor-fable-5.md +0 -50
  138. package/templates/agents/orc-analyst-fable-5.md +0 -115
  139. package/templates/agents/orc-judge-fable-5.md +0 -79
  140. package/templates/agents/orc-planner-fable-5.md +0 -152
  141. package/templates/agents/orc-reviewer-fable-5.md +0 -57
  142. package/templates/skills/_shared/fable5-override.md +0 -56
  143. package/templates/skills/orc-diy/references/blocks/execution.md +0 -42
  144. package/templates/skills/orc-diy/references/blocks/mock-example.md +0 -22
  145. package/templates/skills/orc-diy/references/blocks/planning.md +0 -34
  146. package/templates/skills/orc-diy/references/blocks/review.md +0 -18
  147. package/templates/skills/orc-diy/references/blocks/scoring.md +0 -16
  148. package/templates/skills/orc-diy/references/blocks/security.md +0 -25
  149. package/templates/skills/orc-diy/references/blocks/ship.md +0 -25
  150. package/templates/skills/orc-diy/references/blocks/summary.md +0 -18
  151. package/templates/skills/orc-diy/references/blocks/testgen.md +0 -17
  152. package/templates/skills/orc-diy/references/blocks/trace.md +0 -28
  153. package/templates/skills/orc-diy/references/blocks/verify.md +0 -25
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ /* panels/lanes.js — orc ui client
3
+ The `orc lane` noun, rendered: which lanes exist, which SHARED phases each
4
+ one runs, and the whole call catalogue.
5
+
6
+ Loaded by app.html in the order its numeric prefix names. Classic
7
+ script, no import/export: an ES module import carries no query string,
8
+ and every static request here needs the per-launch session token. */
9
+
10
+
11
+ /* =================================================================== LANES == */
12
+
13
+ // TWO TABS, on this panel's own precedent (Crosslink's two, Knowledge's five,
14
+ // Extra's six). Phases and Calls answer two different questions about the same
15
+ // noun — what a lane READS, in order, and what a lane RUNS — and stacking both
16
+ // down one column made the second one something you had to know was there.
17
+ //
18
+ // The tab survives a re-render, exactly as `KN_TAB` and `EX_TAB` do: losing
19
+ // your place because a fetch resolved is the small rudeness that makes a panel
20
+ // feel unreliable.
21
+ let LN_TAB = "phases";
22
+ // The selected lane likewise. A lane picker that snaps back to `orc` every time
23
+ // the panel repaints is a picker nobody uses twice.
24
+ let LN_LANE = "orc";
25
+
26
+ PANELS.lanes = function (host) {
27
+ head(host, t("lanes.title"), t("lanes.sub"));
28
+ section(
29
+ host,
30
+ () => read("/api/lanes").then((r) => r.data),
31
+ (d) => {
32
+ const out = frag();
33
+ const lanes = d.lanes || [];
34
+ if (!lanes.length) {
35
+ out.append(el("div", "empty", t("lanes.none")));
36
+ return out;
37
+ }
38
+ if (!lanes.some((l) => l.lane === LN_LANE)) LN_LANE = lanes[0].lane;
39
+
40
+ const body = el("div", "stack");
41
+ const paint = () => {
42
+ body.replaceChildren();
43
+ if (LN_TAB === "phases") phasesTab(body, lanes);
44
+ else callsTab(body);
45
+ };
46
+
47
+ // The panel's own `.tabs` convention: a flat strip driven by
48
+ // `aria-selected`, exactly as Knowledge and Runs build theirs. Keys are
49
+ // written out IN FULL rather than assembled from the tab id — a key built
50
+ // from a fragment is invisible to every check that looks for one.
51
+ const strip = el("div", "tabs");
52
+ const select = (which) => {
53
+ LN_TAB = which;
54
+ for (const b of strip.children) b.setAttribute("aria-selected", String(b.dataset.tab === which));
55
+ paint();
56
+ };
57
+ for (const [which, label] of [
58
+ ["phases", t("lanes.tab.phases")],
59
+ ["calls", t("lanes.tab.calls")],
60
+ ]) {
61
+ const b = el("button", null, label);
62
+ b.type = "button";
63
+ b.dataset.tab = which;
64
+ b.setAttribute("aria-selected", String(which === LN_TAB));
65
+ b.addEventListener("click", () => select(which));
66
+ strip.append(b);
67
+ }
68
+ out.append(strip, body);
69
+ paint();
70
+ return out;
71
+ }
72
+ );
73
+ };
74
+
75
+ /* ------------------------------------------------------------------ phases -- */
76
+
77
+ function phasesTab(host, lanes) {
78
+ const picker = card(t("lanes.picker.title"));
79
+ picker.append(el("div", "note", t("lanes.picker.note")));
80
+
81
+ const sel = el("select", "text-input");
82
+ sel.setAttribute("aria-label", t("lanes.picker.aria"));
83
+ for (const l of lanes) {
84
+ const o = el("option", null, l.lane + (l.command ? " " + l.command : ""));
85
+ o.value = l.lane;
86
+ if (l.lane === LN_LANE) o.selected = true;
87
+ sel.append(o);
88
+ }
89
+ picker.append(sel);
90
+ host.append(picker);
91
+
92
+ const slot = el("div", "stack");
93
+ host.append(slot);
94
+
95
+ const load = () => {
96
+ slot.replaceChildren(skeleton(3));
97
+ read("/api/lane/phases?lane=" + encodeURIComponent(LN_LANE))
98
+ .then((r) => slot.replaceChildren(phasesCard(r.data)))
99
+ .catch((e) => slot.replaceChildren(failBox(e)));
100
+ };
101
+ sel.addEventListener("change", () => {
102
+ LN_LANE = sel.value;
103
+ load();
104
+ });
105
+ load();
106
+ }
107
+
108
+ function phasesCard(d) {
109
+ const one = (d.lanes || [])[0];
110
+ const c = card(t("lanes.phases.title"));
111
+ if (!one) {
112
+ c.append(el("div", "empty", t("lanes.phases.unknown")));
113
+ return c;
114
+ }
115
+
116
+ // The trace tier and token are the CLI's words for how this lane narrates.
117
+ // They belong at the top because every phase below closes into one of them.
118
+ const meta = el("div", "lane-meta");
119
+ meta.append(el("span", "lane-meta-label", t("lanes.phases.trace")));
120
+ if (one.trace_tier) meta.append(chip(one.trace_tier));
121
+ if (one.trace_token) meta.append(chip("lane token " + one.trace_token, "lane"));
122
+ c.append(meta);
123
+
124
+ const phases = one.phases || [];
125
+ if (!phases.length) {
126
+ // AN EMPTY ANSWER IS AN ANSWER (v0.43.0). Five lanes keep their pipeline in
127
+ // their own spine on purpose, so "no shared phases" is a fact about this
128
+ // lane and not a fetch that failed. Saying nothing here would make the two
129
+ // look identical.
130
+ c.append(el("div", "empty", t("lanes.phases.inSpine")));
131
+ return c;
132
+ }
133
+
134
+ for (const p of phases) {
135
+ const row = el("div", "phase-row");
136
+ row.append(el("span", "phase-ord", String(p.ord)));
137
+
138
+ const mid = el("div", "phase-mid");
139
+ mid.append(el("div", "phase-id", p.id));
140
+ // A row names a FILE or a HEADING, never both and never neither — the
141
+ // manifest rule the two-way lint enforces. Whichever it is, it is a CLI
142
+ // string and is printed verbatim.
143
+ if (p.file) mid.append(el("div", "phase-file", p.file));
144
+ if (p.heading) mid.append(el("div", "phase-file", p.heading));
145
+ if ((p.calls || []).length) {
146
+ const calls = el("div", "phase-calls");
147
+ calls.append(el("span", "phase-calls-label", t("lanes.phases.calls")));
148
+ for (const id of p.calls) calls.append(chip(id, "lane"));
149
+ mid.append(calls);
150
+ }
151
+ row.append(mid);
152
+
153
+ const right = el("div", "phase-right");
154
+ // `when` is one of the CLI's own words (`always`, `on-phase`,
155
+ // `compile-time`, …). It is never softened into "sometimes".
156
+ if (p.when) right.append(chip(p.when, p.when === "always" ? "ok" : null));
157
+ if (p.read) right.append(el("div", "phase-read", p.read));
158
+ if ((p.layers || []).length) right.append(el("div", "phase-layers", p.layers.join(" · ")));
159
+ row.append(right);
160
+
161
+ if (p.optional_when) row.append(el("div", "phase-why", p.optional_when));
162
+ c.append(row);
163
+ }
164
+
165
+ if ((d.phase_files || []).length || (d.layer_set || []).length) {
166
+ const foot = el("div", "note");
167
+ foot.append(document.createTextNode(t("lanes.phases.foot")));
168
+ c.append(foot);
169
+ }
170
+ return c;
171
+ }
172
+
173
+ /* ------------------------------------------------------------------- calls -- */
174
+
175
+ function callsTab(host) {
176
+ const slot = el("div", "stack");
177
+ host.append(slot);
178
+ slot.replaceChildren(skeleton(4));
179
+ read("/api/lane/calls")
180
+ .then((r) => slot.replaceChildren(callsCard(r.data)))
181
+ .catch((e) => slot.replaceChildren(failBox(e)));
182
+ }
183
+
184
+ function callsCard(d) {
185
+ const calls = d.calls || [];
186
+ const c = card(t("lanes.calls.title", { n: calls.length }));
187
+ c.append(el("div", "note", t("lanes.calls.note")));
188
+ if (!calls.length) {
189
+ c.append(el("div", "empty", t("lanes.calls.none")));
190
+ return c;
191
+ }
192
+
193
+ for (const call of calls) {
194
+ // EXPANDS IN PLACE, one row at a time — the Runs-row rule (v0.43.6). A
195
+ // detail box below a list means reading a row and then looking somewhere
196
+ // else for what it said.
197
+ const row = el("div", "call-row");
198
+ const h = el("button", "call-head");
199
+ h.type = "button";
200
+ h.setAttribute("aria-expanded", "false");
201
+ h.append(el("span", "call-caret", "▸"));
202
+ h.append(el("span", "call-cmd", call.cmd));
203
+ if (call.cost) h.append(chip(call.cost, call.cost === "free" ? "ok" : "warn"));
204
+ h.append(el("span", "call-lanes-n", tn((call.lanes || []).length, "lanes.calls.lanes")));
205
+
206
+ const detail = el("div", "call-detail");
207
+ detail.hidden = true;
208
+ // Every string below is the CLI's registry text and is never translated: it
209
+ // names commands, exit codes and reference files by their real ids.
210
+ const rows = [];
211
+ if (call.what) rows.push([t("lanes.calls.what"), call.what]);
212
+ if (call.when) rows.push([t("lanes.calls.when"), call.when]);
213
+ if (call.on_absent) rows.push([t("lanes.calls.onAbsent"), call.on_absent]);
214
+ if (call.never) rows.push([t("lanes.calls.never"), call.never]);
215
+ if (call.canonical) rows.push([t("lanes.calls.canonical"), call.canonical]);
216
+ detail.append(kvList(rows));
217
+
218
+ // The exit codes are a CONTRACT, so they are drawn as a table rather than
219
+ // folded into a sentence — a lane branches on these numbers.
220
+ const exits = call.exits || {};
221
+ const codes = Object.keys(exits);
222
+ if (codes.length) {
223
+ const ex = el("div", "call-exits");
224
+ ex.append(el("span", "call-exits-label", t("lanes.calls.exits")));
225
+ for (const code of codes) ex.append(el("span", "call-exit", code + " = " + exits[code]));
226
+ detail.append(ex);
227
+ }
228
+ if ((call.states || []).length) {
229
+ const st = el("div", "call-exits");
230
+ st.append(el("span", "call-exits-label", t("lanes.calls.states")));
231
+ for (const s of call.states) st.append(chip(s));
232
+ detail.append(st);
233
+ }
234
+ if ((call.lanes || []).length) {
235
+ const ls = el("div", "call-lane-chips");
236
+ ls.append(el("span", "call-exits-label", t("lanes.calls.calledBy")));
237
+ for (const l of call.lanes) ls.append(chip(l, "lane"));
238
+ detail.append(ls);
239
+ }
240
+
241
+ h.addEventListener("click", () => {
242
+ const open = h.getAttribute("aria-expanded") === "true";
243
+ // ONE ROW OPEN AT A TIME, the Runs-row rule again: thirty open rows is
244
+ // the wall this panel replaced.
245
+ for (const other of c.querySelectorAll(".call-head[aria-expanded='true']")) {
246
+ other.setAttribute("aria-expanded", "false");
247
+ other.parentElement.querySelector(".call-detail").hidden = true;
248
+ }
249
+ if (!open) {
250
+ h.setAttribute("aria-expanded", "true");
251
+ detail.hidden = false;
252
+ }
253
+ });
254
+
255
+ row.append(h, detail);
256
+ c.append(row);
257
+ }
258
+ return c;
259
+ }
@@ -54,6 +54,16 @@ const FINDING_ROUTE = {
54
54
  // evicts the old package first), and `upgrade` is an action row on
55
55
  // MAINTENANCE — so that is genuinely the panel that can clear it.
56
56
  "legacy-global-package": { panel: "maintenance", cta: "overview.item.legacyGlobalPackage.cta" },
57
+ // v1.0.0 W5. The rule again: a demoted run is cleared by `orc extra promote`,
58
+ // and Recovery is the Extra tab where every other journal-shaped state is
59
+ // already rendered — so that is genuinely the panel that can clear it.
60
+ "extra-demoted-run": { panel: "extra", cta: "overview.item.extraDemotedRun.cta" },
61
+ // v1.0.0 W16. An INSTALL-FOOTPRINT finding, so it takes the documented
62
+ // default rather than an interesting exception: the thing that clears it is
63
+ // `orc update`, which is an action row on MAINTENANCE. It is spelled out
64
+ // rather than left to DEFAULT_FINDING_ROUTE because the CTA is specific --
65
+ // a reader who sees "lane config drifted" should be told which button.
66
+ "lane-keys-drifted": { panel: "maintenance", cta: "overview.item.laneKeysDrifted.cta" },
57
67
  };
58
68
  const DEFAULT_FINDING_ROUTE = { panel: "maintenance", cta: "overview.item.doctor.cta" };
59
69
  const findingRoute = (id) => FINDING_ROUTE[id] || DEFAULT_FINDING_ROUTE;
@@ -17,8 +17,8 @@
17
17
  // keys stay GREPPABLE: a build of a string key from a fragment is a key no
18
18
  // coverage check can see, and the check is the only thing standing between a
19
19
  // renamed key and a raw dotted string on somebody's screen.
20
- const TIER_LABEL_KEY = { common: "settings.tier.common", fable5: "settings.tier.fable5", advanced: "settings.tier.advanced" };
21
- const TIER_DESC_KEY = { common: "settings.tierDesc.common", fable5: "settings.tierDesc.fable5", advanced: "settings.tierDesc.advanced" };
20
+ const TIER_LABEL_KEY = { common: "settings.tier.common", advanced: "settings.tier.advanced" };
21
+ const TIER_DESC_KEY = { common: "settings.tierDesc.common", advanced: "settings.tierDesc.advanced" };
22
22
  const TIER_LABEL = (tier) => (TIER_LABEL_KEY[tier] ? t(TIER_LABEL_KEY[tier]) : tier);
23
23
  const TIER_DESC = (tier) => (TIER_DESC_KEY[tier] ? t(TIER_DESC_KEY[tier]) : "");
24
24
 
@@ -162,6 +162,12 @@ async function renderSettings(body) {
162
162
  // flips — which is how the precedence rule gets taught rather than described.
163
163
  out.append(ladderCard(d.score_table));
164
164
 
165
+ // WHO DECIDES WHAT (v1.0.0 W16). The ladder above answers one family;
166
+ // this answers all of them, and it is the picture a flat key list cannot
167
+ // draw: a contested setting is not decided by the key you are looking at,
168
+ // it is decided by the highest rank that resolved.
169
+ out.append(ranksCard(d.families, d.families_resolved, d.rank_states));
170
+
165
171
  // 36 keys in three flat lists is a wall, and the answer to "where is the one
166
172
  // I want" was scrolling. Each tier is now its own collapsible card, and the
167
173
  // toolbar filters across ALL of them at once — so finding a key never depends
@@ -169,7 +175,7 @@ async function renderSettings(body) {
169
175
  const tiers = [];
170
176
  out.append(settingsToolbar(d, tiers));
171
177
 
172
- for (const tier of ["common", "fable5", "advanced"]) {
178
+ for (const tier of ["common", "advanced"]) {
173
179
  const keys = d.keys.filter((k) => k.tier === tier);
174
180
  if (!keys.length) continue;
175
181
 
@@ -234,6 +240,32 @@ async function renderSettings(body) {
234
240
  out.append(c);
235
241
  }
236
242
 
243
+ // A key ORC REMOVED, still sitting in the user's file. It is NOT an editable
244
+ // setting and it is NOT a hand-edited override, so it gets its own block —
245
+ // rendering it as either would tell the user a dead line still does
246
+ // something. The CLI computed the row; this only draws it.
247
+ if ((d.retired_keys || []).length) {
248
+ const c = card(t("settings.retired.title"));
249
+ c.append(el("div", "note", t("settings.retired.note")));
250
+ for (const r of d.retired_keys) {
251
+ const row = el("div", "setting shadowed");
252
+ const left = el("div");
253
+ const name = el("div", "setting-name");
254
+ name.append(document.createTextNode(r.key));
255
+ const lock = el("span", "lock");
256
+ lock.append(document.createTextNode("🔒 " + t("settings.retired.badge")));
257
+ name.append(lock);
258
+ left.append(name);
259
+ left.append(el("div", "setting-desc", t("settings.retired.readonly")));
260
+ left.append(el("div", "shadow-why", `${t("settings.retired.removedIn")} ${r.removed_in} — ${r.why}`));
261
+ const right = el("div", "setting-control");
262
+ right.append(el("div", "readonly-value", String(r.value)));
263
+ row.append(left, right);
264
+ c.append(row);
265
+ }
266
+ out.append(c);
267
+ }
268
+
237
269
  // Last block on the panel, and it sticks to the bottom of the viewport once
238
270
  // something is pending: with 36 keys across three tiers, an Apply you have to
239
271
  // go looking for is an Apply that gets forgotten.
@@ -243,6 +275,99 @@ async function renderSettings(body) {
243
275
  body.replaceChildren(out);
244
276
  }
245
277
 
278
+ // WHO DECIDES WHAT (v1.0.0 W16) — the contested families, resolved.
279
+ //
280
+ // THE PANEL DECIDES NOTHING HERE. `families` is the CLI's static registry (which
281
+ // ranks exist, in which order, and the question each family answers) and
282
+ // `families_resolved` is what those ranks actually did against the config on
283
+ // disk right now. Re-deriving a precedence in the browser is the exact drift
284
+ // this panel exists to make impossible — the Flow-stepper rule, applied to
285
+ // config.
286
+ //
287
+ // Only CONTESTED families are drawn. A family with one rank has no precedence
288
+ // to teach, and a card that lists thirty uncontested rows to show two
289
+ // interesting ones is a card nobody reads to the bottom of.
290
+ //
291
+ // Every state word is the CLI's own, from the closed set it publishes as
292
+ // `rank_states` — never a friendlier synonym (`resolved`, `not-read`, `inert`,
293
+ // `demoted`, `absent`, `partly-resolved`). A `why` is CLI prose and is never
294
+ // translated: it names keys, runs, profiles and commands by their real ids.
295
+ const RANK_STATE_KIND = {
296
+ resolved: "ok",
297
+ "partly-resolved": "ok",
298
+ "not-read": null,
299
+ inert: "warn",
300
+ demoted: "warn",
301
+ absent: null,
302
+ };
303
+
304
+ function ranksCard(families, resolved, states) {
305
+ const c = card(t("settings.ranks.title"));
306
+ c.id = "ranks-card";
307
+ c.append(el("div", "note", t("settings.ranks.note")));
308
+
309
+ const names = Object.keys(families || {}).filter((n) => (families[n] || {}).contested);
310
+ if (!names.length) {
311
+ c.append(el("div", "empty", t("settings.ranks.none")));
312
+ return c;
313
+ }
314
+
315
+ for (const name of names) {
316
+ const fam = families[name] || {};
317
+ const res = (resolved || {})[name] || {};
318
+ const block = el("div", "rank-family");
319
+
320
+ const h = el("div", "rank-family-head");
321
+ // The family id is a CLI id. The QUESTION beside it is the CLI's too —
322
+ // it is the whole reason the grouping means anything.
323
+ h.append(el("span", "rank-family-name", name));
324
+ h.append(el("span", "rank-family-q", fam.question || ""));
325
+ block.append(h);
326
+
327
+ // WHAT ANSWERED, stated before the ladder rather than left to be inferred
328
+ // from which row happens to be green. `resolved_by` is null on a terminal
329
+ // rank, which is not a gap: it means nothing above the floor resolved.
330
+ if (res.resolved_at) {
331
+ const by = el("div", "rank-answer");
332
+ by.append(
333
+ document.createTextNode(
334
+ res.resolved_by
335
+ ? t("settings.ranks.answeredBy", { prio: res.resolved_at, key: res.resolved_by })
336
+ : t("settings.ranks.answeredByDefault", { prio: res.resolved_at })
337
+ )
338
+ );
339
+ block.append(by);
340
+ }
341
+
342
+ for (const r of res.ranks || fam.ranks || []) {
343
+ const row = el("div", "rank-row" + (r.state === "resolved" ? " is-resolved" : ""));
344
+ row.append(el("span", "rank-prio", r.prio));
345
+ // A rank is either a KEY or the family's terminal floor. The floor has no
346
+ // key on purpose — it is "the thing that happens when nobody chose" — and
347
+ // rendering it as a key would invite somebody to look for it in the file.
348
+ row.append(el("span", "rank-key" + (r.key ? "" : " is-terminal"), r.key || r.terminal || ""));
349
+ const st = r.state || "absent";
350
+ // An unknown state is RENDERED, not swallowed: the CLI owns this set, and
351
+ // a panel that silently drops a word it does not recognise is a panel that
352
+ // hides the next state somebody adds.
353
+ row.append(chip(st, RANK_STATE_KIND[st] === undefined ? null : RANK_STATE_KIND[st]));
354
+ if (r.why) row.append(el("div", "rank-why", r.why));
355
+ block.append(row);
356
+ }
357
+ c.append(block);
358
+ }
359
+
360
+ // The closed set, once, at the bottom. It is what makes an unfamiliar chip
361
+ // above readable without leaving the page.
362
+ if ((states || []).length) {
363
+ const legend = el("div", "rank-legend");
364
+ legend.append(el("span", "rank-legend-label", t("settings.ranks.legend")));
365
+ for (const s of states) legend.append(chip(s, RANK_STATE_KIND[s] === undefined ? null : RANK_STATE_KIND[s]));
366
+ c.append(legend);
367
+ }
368
+ return c;
369
+ }
370
+
246
371
  function ladderCard(table) {
247
372
  const c = card(t("settings.ladder.title"));
248
373
  c.id = "ladder-card"; // the FLIP morph finds it by id, never by a :has() query
@@ -324,6 +449,27 @@ function settingRow(k, panelBody, edits) {
324
449
  // translated. They name keys, values and precedence rules by their real ids.
325
450
  left.append(el("div", "setting-desc", k.desc));
326
451
  if (k.shadow_reason) left.append(el("div", "shadow-why", k.shadow_reason));
452
+ // WHICH LANES READ THIS (v1.0.0 W16). `k.lanes[]` is the CLI's own
453
+ // `CONFIG_META[].lanes[]` — the same list `orc lane config` filters on, and
454
+ // the same list a two-way lint already refuses to let drift. It answers the
455
+ // question a settings screen otherwise leaves you guessing at: I changed
456
+ // this, so what did I just change? A lane name is a CLI id and is never
457
+ // translated.
458
+ //
459
+ // AN EMPTY LIST IS AN ANSWER AND KEEPS ITS ROW. Ten keys are permanently
460
+ // empty and the lint has an allowlist naming every one of them: they are
461
+ // OPERATING keys of the `orc extra` bridge — a lane calls `orc extra
462
+ // dispatch` and the CLI reads `extra_timeout_s`, so no lane ever names it.
463
+ // Skipping the line would make "no lane reads this" look identical to "we
464
+ // did not render it", which is the distinction this whole row exists to
465
+ // draw.
466
+ {
467
+ const lanes = el("div", "setting-lanes");
468
+ lanes.append(el("span", "setting-lanes-label", t("settings.lanes.label")));
469
+ if ((k.lanes || []).length) for (const lane of k.lanes) lanes.append(chip(lane, "lane"));
470
+ else lanes.append(el("span", "setting-lanes-none", t("settings.lanes.none")));
471
+ left.append(lanes);
472
+ }
327
473
 
328
474
  const right = el("div", "setting-control");
329
475
  right.append(controlFor(k, edits));
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
- {
2
- "name": "@azure-id/orc",
3
- "version": "0.56.1",
4
- "description": "ORC — an orchestrator skill constellation for Claude Code: intake, planning, scored parallel subagents, code-pattern matching, review, verify, ship, plus a project knowledge-base wiki.",
5
- "bin": {
6
- "orc": "bin/cli.js"
7
- },
8
- "files": [
9
- "README.md",
10
- "README-id.md",
11
- "CHANGELOG.md",
12
- "bin/",
13
- "mock-run/",
14
- "templates/"
15
- ],
16
- "scripts": {
17
- "postinstall": "node -e \"try{require('fs').accessSync(require('path').join(__dirname,'bin','cli.js'));console.log('\\nORC installed. Run: orc init (or orc init --global)\\n')}catch(e){console.error('\\n[orc] WARNING: bin/cli.js missing from this install — the repo/publish was incomplete. Reinstall from a complete source.\\n')}\"",
18
- "build:agents": "node bin/build-agents.js",
19
- "test": "node --test --test-concurrency=6 test/",
20
- "verify": "node bin/build-agents.js --check && node bin/verify-package.js && node bin/verify-contracts.js",
21
- "prepack": "node bin/build-agents.js --check && node bin/verify-package.js && node bin/verify-contracts.js && node --test --test-concurrency=6 test/"
22
- },
23
- "keywords": [
24
- "claude",
25
- "claude-code",
26
- "orchestrator",
27
- "skills",
28
- "agents",
29
- "ai"
30
- ],
31
- "license": "MIT",
32
- "engines": {
33
- "node": ">=18"
34
- },
35
- "repository": {
36
- "type": "git",
37
- "url": "github:azure-id/orc"
38
- }
39
- }
1
+ {
2
+ "name": "@azure-id/orc",
3
+ "version": "1.0.0",
4
+ "description": "ORC — an orchestrator skill constellation for Claude Code: intake, planning, scored parallel subagents, code-pattern matching, review, verify, ship, plus a project knowledge-base wiki.",
5
+ "bin": {
6
+ "orc": "bin/cli.js"
7
+ },
8
+ "files": [
9
+ "README.md",
10
+ "README-id.md",
11
+ "CHANGELOG.md",
12
+ "bin/",
13
+ "mock-run/",
14
+ "templates/"
15
+ ],
16
+ "scripts": {
17
+ "postinstall": "node -e \"try{require('fs').accessSync(require('path').join(__dirname,'bin','cli.js'));console.log('\\nORC installed. Run: orc init (or orc init --global)\\n')}catch(e){console.error('\\n[orc] WARNING: bin/cli.js missing from this install — the repo/publish was incomplete. Reinstall from a complete source.\\n')}\"",
18
+ "build:agents": "node bin/build-agents.js",
19
+ "test": "node bin/test-run.js",
20
+ "verify": "node bin/build-agents.js --check && node bin/verify-package.js && node bin/verify-contracts.js",
21
+ "prepack": "node bin/build-agents.js --check && node bin/verify-package.js && node bin/verify-contracts.js && node bin/test-run.js"
22
+ },
23
+ "keywords": [
24
+ "claude",
25
+ "claude-code",
26
+ "orchestrator",
27
+ "skills",
28
+ "agents",
29
+ "ai"
30
+ ],
31
+ "license": "MIT",
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "github:azure-id/orc"
38
+ }
39
+ }
@@ -3,7 +3,7 @@
3
3
  Single-role, model-specific agents. The orchestrator dispatches BY AGENT NAME —
4
4
  model is pinned in frontmatter, not requested in prose. No agent is multi-role.
5
5
 
6
- ## Executors (score-mapped by the single 8-band table in config.md)
6
+ ## Executors (score-mapped by the single 6-band table in config.md)
7
7
 
8
8
  | Score band | Agent | Model | Effort |
9
9
  |-----------|-------|-------|--------|
@@ -11,14 +11,20 @@ model is pinned in frontmatter, not requested in prose. No agent is multi-role.
11
11
  | [30,40) | orc-executor-sonnet-4-6-med | claude-sonnet-4-6 | medium |
12
12
  | [40,55) | orc-executor-sonnet-4-6-high | claude-sonnet-4-6 | high |
13
13
  | [55,65) | orc-executor-sonnet-5-high | claude-sonnet-5 | high |
14
- | [65,70) | orc-executor-opus-4-7-med | claude-opus-4-7 | medium |
15
- | [70,80) | orc-executor-opus-4-7-high | claude-opus-4-7 | high |
16
- | [80,90) | orc-executor-opus-4-8-high | claude-opus-4-8 | high |
17
- | [90,100] | orc-executor-opus-5-high | claude-opus-5 | high |
14
+ | [65,90) | orc-executor-opus-5-low | claude-opus-5 | low |
15
+ | [90,100] | orc-executor-opus-5-med | claude-opus-5 | medium |
18
16
 
19
- Score→executor mapping lives in config.md (one canonical 8-band table;
17
+ Score→executor mapping lives in config.md (one canonical 6-band table;
20
18
  `rubric_bands` is granularity only, not a preset selector).
21
19
 
20
+ **Executors no band names — they still ship.**
21
+ `orc-executor-opus-4-7-med`, `orc-executor-opus-4-7-high`,
22
+ `orc-executor-opus-4-8-high` and `orc-executor-opus-5-high` are still generated
23
+ and still installed. Since v1.0.0 they are reachable only when a user names one
24
+ explicitly: `rubric_bands_override`, `orc diy`'s `fixed_executor`, or
25
+ `extra_fallback_agent`. They are not deleted because a table change is not a
26
+ model change, and an agent's model change is always a RENAME.
27
+
22
28
  ## Fixed-role agents
23
29
 
24
30
  | Agent | Model | Effort | Role |
@@ -36,8 +42,8 @@ Score→executor mapping lives in config.md (one canonical 8-band table;
36
42
  | orc-retro-sonnet-5-high | claude-sonnet-5 | high | mine behavior traces → calibration report (/orc-retro; read-only) |
37
43
  | orc-wiki-scanner-opus-4-8-high | claude-opus-4-8 | high | scan ONE wiki coverage area → evidence-anchored doc body + crosslink tags (/orc-wiki only; read-only against the project). The DEEP half of the v0.46.0 tier ladder: first scan · STRUCTURAL · wide delta · a new exported symbol |
38
44
  | orc-wiki-scanner-sonnet-5-high | claude-sonnet-5 | high | the LIGHT half of the same ladder — an existing doc whose covered files moved by a small, no-new-surface delta. IDENTICAL return contract; it escalates with `needs_context` rather than under-delivering. Never used for a first scan |
39
- | orc-executor-opus-5-med | claude-opus-5 | medium | `opus5_only` ladder ONLY — the [40,80) band (never dispatched by the default 8-band table) |
40
- | orc-executor-opus-5-low | claude-opus-5 | low | `opus5_only` ladder ONLY the [0,40) band, and the forced mini/fast executor (never dispatched by the default 8-band table) |
45
+ | orc-executor-opus-5-med | claude-opus-5 | medium | the `[90,100]` band in BOTH tables |
46
+ | orc-executor-opus-5-low | claude-opus-5 | low | the default table's `[65,90)`, `opus5_only`'s `[0,90)`, and the forced mini/fast executor |
41
47
  | orc-advisor-opus-5-xhigh | claude-opus-5 | xhigh | ultra Phase U0 advisory brief + rubric + clarification questions (read-only; /orc-ultra only) |
42
48
  | orc-judge-opus-5-xhigh | claude-opus-5 | xhigh | ultra judgment gates — analysis / plan / implementation (read-only; /orc-ultra only) |
43
49
  | orc-learn-writer-opus-5-low | claude-opus-5 | low | deepen ONE feature → learning-docs/<slug>/ (/orc-learn only; git-ignored output) |
@@ -58,15 +64,14 @@ Score→executor mapping lives in config.md (one canonical 8-band table;
58
64
 
59
65
  `opus5_only` FORCES every dispatched role onto `claude-opus-5`, with effort as
60
66
  the only cost dial. Each agent below replaces its default with the same slice
61
- and the same return contract. Effort is PINNED per role (unlike `fable5_effort`,
62
- no CLI rewrites these). Full mapping + precedence:
67
+ and the same return contract. Effort is PINNED per role no CLI rewrites
68
+ these. Full mapping + precedence:
63
69
  `../skills/_shared/opus5-only.md`.
64
70
 
65
71
  | Agent | Model | Effort | Replaces |
66
72
  |-------|-------|--------|----------|
67
- | orc-executor-opus-5-low | claude-opus-5 | low | the `[0,40)` band · orc-mini's and orc-fast's fixed executor |
68
- | orc-executor-opus-5-med | claude-opus-5 | medium | the `[40,80)` band |
69
- | orc-executor-opus-5-high | claude-opus-5 | high | the `[80,100]` band |
73
+ | orc-executor-opus-5-low | claude-opus-5 | low | the `[0,90)` band · orc-mini's and orc-fast's fixed executor |
74
+ | orc-executor-opus-5-med | claude-opus-5 | medium | the `[90,100]` band |
70
75
  | orc-analyze-mini-opus-5-med | claude-opus-5 | medium | orc-analyze-mini-sonnet-5-high |
71
76
  | orc-planner-mini-opus-5-med | claude-opus-5 | medium | orc-planner-mini-sonnet-5-high |
72
77
  | orc-scout-opus-5-low | claude-opus-5 | low | orc-scout-sonnet-4-6-high |
@@ -80,22 +85,6 @@ verifier, test-author, combiner, learn-writer, advisor, judge — dispatch
80
85
  unchanged under this mode. **Never forced:** `orc-trace-writer-haiku-4-5` (it
81
86
  transcribes a packet, no reasoning) and orc-diy (its table is compile-owned).
82
87
 
83
- ## Fable 5 role-override agents (hard-gated; dispatched only when configured)
84
-
85
- *(Entirely INERT while `opus5_only: true` — that mode outranks this one.)*
86
-
87
- Used ONLY when `fable5_enabled: true` and the role is in `fable5_roles` — each
88
- replaces its default role agent with the same slice/contract. Effort defaults to
89
- `medium` and is rewritten in-place by `orc config set fable5_effort <v>`.
90
-
91
- | Agent | Model | Effort | Replaces |
92
- |-------|-------|--------|----------|
93
- | orc-analyst-fable-5 | claude-fable-5 | fable5_effort | orc-system-analyst-opus-5-high |
94
- | orc-planner-fable-5 | claude-fable-5 | fable5_effort | orc-planner-opus-5-med |
95
- | orc-advisor-fable-5 | claude-fable-5 | fable5_effort | orc-advisor-opus-5-xhigh (ultra) |
96
- | orc-judge-fable-5 | claude-fable-5 | fable5_effort | orc-judge-opus-5-xhigh (ultra) |
97
- | orc-reviewer-fable-5 | claude-fable-5 | fable5_effort | orc-reviewer-opus-5-med |
98
-
99
88
  Mini execution reuses orc-executor-sonnet-5-high. Fast-lane (orc-fast)
100
89
  execution reuses orc-executor-sonnet-4-6-high — no dedicated agent. Under
101
90
  `opus5_only` both reuse orc-executor-opus-5-low.
@@ -114,8 +103,7 @@ agent to spawn before EVERY dispatch, and reuses shipped agents:
114
103
  The only dispatch it does not re-ask is build-repair rounds 1–2, which reuse the
115
104
  executor the user already chose for that entry; round 3 asks again.
116
105
 
117
- **`opus5_only`, `fable5_enabled`/`fable5_roles`, and `rubric_bands_override` are
118
- all INERT in this lane** — they would silently collapse the user's choice. It is
106
+ **`opus5_only` and `rubric_bands_override` are both INERT in this lane** — they would silently collapse the user's choice. It is
119
107
  the one exception to `opus5_only`'s otherwise flat precedence. See
120
108
  `skills/_shared/opus5-only.md` and `skills/orc-quick/references/dispatch-gate.md`.
121
109
 
@@ -136,12 +124,12 @@ The orchestrator (main session) is NOT an agent file.
136
124
  Model IDs use the Platform/API dateless format (confirmed at
137
125
  platform.claude.com/docs/en/about-claude/models/model-ids-and-versions):
138
126
  claude-haiku-4-5, claude-sonnet-4-6, claude-sonnet-5, claude-opus-4-7,
139
- claude-opus-4-8, claude-opus-5 (the top executor band + the core fixed roles),
140
- and claude-fable-5 (the Fable 5 role-override agents).
127
+ claude-opus-4-8 and claude-opus-5 (the top executor band + the core fixed
128
+ roles).
141
129
 
142
130
  1. **Run `/agents`** to confirm Claude Code accepts these full IDs in agent
143
- frontmatter — in particular `claude-haiku-4-5` and `claude-fable-5`, the two
144
- newest ids. If it wants short aliases (opus/sonnet/haiku) or dated IDs, adjust
131
+ frontmatter — in particular `claude-haiku-4-5` and `claude-opus-5`. If it
132
+ wants short aliases (opus/sonnet/haiku) or dated IDs, adjust
145
133
  each `model:` field. The full IDs are valid at the API level but Claude Code
146
134
  may normalize differently.
147
135
  2. **Confirm `effort:` is a valid CLI frontmatter field.** If the CLI ignores