@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.
- package/CHANGELOG.md +219 -0
- package/README-id.md +49 -107
- package/README.md +694 -719
- package/bin/build-agents.js +18 -11
- package/bin/cli.js +33699 -29855
- package/bin/onboarding-content.js +0 -3
- package/bin/test-run.js +491 -0
- package/bin/verify-contracts.js +4330 -3277
- package/bin/verify-package.js +76 -6
- package/bin/webui/api.js +1201 -1163
- package/bin/webui/app.html +210 -198
- package/bin/webui/css/06-responsive.css +120 -108
- package/bin/webui/css/panels/extra.css +29 -0
- package/bin/webui/css/panels/lanes.css +134 -0
- package/bin/webui/css/panels/settings.css +79 -3
- package/bin/webui/css/panels/wait.css +123 -0
- package/bin/webui/fixtures/extra.js +2036 -1934
- package/bin/webui/fixtures/flow.js +2 -2
- package/bin/webui/fixtures/index.js +510 -486
- package/bin/webui/fixtures/lanes.js +218 -0
- package/bin/webui/fixtures/settings.js +261 -34
- package/bin/webui/fixtures/stats.js +3 -3
- package/bin/webui/fixtures/wait.js +97 -0
- package/bin/webui/i18n/TERMS.md +135 -134
- package/bin/webui/i18n/en/extra.json +345 -313
- package/bin/webui/i18n/en/lanes.json +29 -0
- package/bin/webui/i18n/en/nav.json +21 -19
- package/bin/webui/i18n/en/overview.json +2 -0
- package/bin/webui/i18n/en/settings.json +16 -5
- package/bin/webui/i18n/en/wait.json +41 -0
- package/bin/webui/i18n/id/extra.json +345 -313
- package/bin/webui/i18n/id/lanes.json +29 -0
- package/bin/webui/i18n/id/nav.json +21 -19
- package/bin/webui/i18n/id/overview.json +2 -0
- package/bin/webui/i18n/id/settings.json +15 -4
- package/bin/webui/i18n/id/wait.json +41 -0
- package/bin/webui/js/01-i18n.js +151 -149
- package/bin/webui/js/panels/extra.js +3199 -3011
- package/bin/webui/js/panels/lanes.js +259 -0
- package/bin/webui/js/panels/overview.js +10 -0
- package/bin/webui/js/panels/settings.js +149 -3
- package/bin/webui/js/panels/wait.js +253 -0
- package/package.json +39 -39
- package/templates/agents/MODEL-MAPPING.md +23 -35
- package/templates/agents/orc-executor-opus-4-7-high.md +1 -1
- package/templates/agents/orc-executor-opus-4-7-med.md +1 -1
- package/templates/agents/orc-executor-opus-4-8-high.md +1 -1
- package/templates/agents/orc-executor-opus-5-high.md +1 -1
- package/templates/agents/orc-executor-opus-5-low.md +1 -1
- package/templates/agents/orc-executor-opus-5-med.md +1 -1
- package/templates/agents/orc-retro-opus-5-med.md +73 -73
- package/templates/agents/orc-retro-sonnet-5-high.md +72 -72
- package/templates/agents/orc-trace-writer-haiku-4-5.md +107 -107
- package/templates/commands/orc-route.md +1 -1
- package/templates/commands/orc-wait.md +19 -0
- package/templates/commands/orc.md +1 -1
- package/templates/hooks/orc-statusline.js +39 -0
- package/templates/skills/_shared/README.md +7 -3
- package/templates/skills/_shared/config-precedence.md +198 -0
- package/templates/skills/_shared/drift-recovery.md +1 -1
- package/templates/skills/_shared/extra-dispatch.md +159 -7
- package/templates/skills/_shared/interview.md +1 -1
- package/templates/skills/_shared/opus5-only.md +9 -7
- package/templates/skills/_shared/phases/README.md +82 -0
- package/templates/skills/{orc/references → _shared/phases}/analyst-gates.md +15 -3
- package/templates/skills/_shared/phases/execution.md +143 -0
- package/templates/skills/{orc/references → _shared/phases}/house-rules.md +13 -1
- package/templates/skills/{orc/references → _shared/phases}/intake.md +15 -2
- package/templates/skills/_shared/phases/mock-example.md +56 -0
- package/templates/skills/{orc/references → _shared/phases}/plan-handoff.md +16 -3
- package/templates/skills/_shared/phases/planning.md +135 -0
- package/templates/skills/_shared/phases/preflight.md +98 -0
- package/templates/skills/_shared/phases/review.md +53 -0
- package/templates/skills/_shared/phases/scoring.md +65 -0
- package/templates/skills/{orc/references → _shared/phases}/security-checklist.md +12 -1
- package/templates/skills/_shared/phases/security.md +55 -0
- package/templates/skills/_shared/phases/ship.md +88 -0
- package/templates/skills/{orc/references/stop-and-resume.md → _shared/phases/stop-resume.md} +169 -160
- package/templates/skills/_shared/phases/summary.md +48 -0
- package/templates/skills/_shared/phases/testgen.md +51 -0
- package/templates/skills/{orc/references/trace-protocol.md → _shared/phases/trace.md} +49 -6
- package/templates/skills/_shared/phases/verify.md +70 -0
- package/templates/skills/{orc/references → _shared/phases}/wave-grouping.md +14 -2
- package/templates/skills/{orc/references → _shared/phases}/wiki-consult.md +13 -1
- package/templates/skills/_shared/read-ladder.md +48 -0
- package/templates/skills/_shared/wait.md +240 -0
- package/templates/skills/context-combiner/SKILL.md +214 -187
- package/templates/skills/orc/README.md +148 -150
- package/templates/skills/orc/SKILL.md +75 -358
- package/templates/skills/orc/config.md +137 -389
- package/templates/skills/orc/examples/full-run-mock.md +1 -1
- package/templates/skills/orc/references/effort-and-mode.md +16 -18
- package/templates/skills/orc/references/phases/intake.md +47 -0
- package/templates/skills/orc/references/phases/integration.md +19 -0
- package/templates/skills/orc/references/preflight-report.md +4 -4
- package/templates/skills/orc/references/ultra-mode.md +117 -123
- package/templates/skills/orc/schemas/planning-output.md +279 -279
- package/templates/skills/orc/subskills/orc-planner/SKILL.md +260 -264
- package/templates/skills/orc/subskills/orc-pr/stack-gate.md +4 -1
- package/templates/skills/orc-aftermath/SKILL.md +158 -136
- package/templates/skills/orc-analyze/SKILL.md +252 -220
- package/templates/skills/orc-analyze/references/branching.md +51 -51
- package/templates/skills/orc-analyze/references/thin-input.md +1 -1
- package/templates/skills/orc-analyze-mini/SKILL.md +128 -112
- package/templates/skills/orc-boundary/SKILL.md +249 -208
- package/templates/skills/orc-brainstorm/SKILL.md +377 -339
- package/templates/skills/orc-budget/SKILL.md +248 -225
- package/templates/skills/orc-challenge/README.md +1 -1
- package/templates/skills/orc-challenge/SKILL.md +298 -255
- package/templates/skills/orc-challenge/examples/council-full-roster.md +1 -1
- package/templates/skills/orc-claude/SKILL.md +221 -199
- package/templates/skills/orc-diy/README.md +2 -1
- package/templates/skills/orc-diy/SKILL.md +132 -68
- package/templates/skills/orc-diy/references/blocks/analyze.md +1 -1
- package/templates/skills/orc-diy/references/compile.md +21 -7
- package/templates/skills/orc-diy/references/flow-schema.md +2 -2
- package/templates/skills/orc-doc/SKILL.md +49 -7
- package/templates/skills/orc-explain/SKILL.md +24 -0
- package/templates/skills/orc-export/SKILL.md +31 -5
- package/templates/skills/orc-fast/SKILL.md +218 -185
- package/templates/skills/orc-grill/SKILL.md +238 -207
- package/templates/skills/orc-handoff/SKILL.md +223 -204
- package/templates/skills/orc-learn/SKILL.md +181 -156
- package/templates/skills/orc-mini/SKILL.md +47 -24
- package/templates/skills/orc-pact/SKILL.md +255 -218
- package/templates/skills/orc-pattern/SKILL.md +154 -124
- package/templates/skills/orc-poly/SKILL.md +242 -216
- package/templates/skills/orc-poly/references/gather.md +1 -1
- package/templates/skills/orc-pr-driver/SKILL.md +196 -167
- package/templates/skills/orc-pr-driver/references/orc-run-split.md +1 -1
- package/templates/skills/orc-pr-setup/SKILL.md +212 -184
- package/templates/skills/orc-quick/README.md +2 -2
- package/templates/skills/orc-quick/SKILL.md +51 -13
- package/templates/skills/orc-quick/references/dispatch-gate.md +2 -3
- package/templates/skills/orc-retro/SKILL.md +249 -222
- package/templates/skills/orc-retro/examples/retro-mock.md +171 -171
- package/templates/skills/orc-route/SKILL.md +188 -165
- package/templates/skills/orc-verify/SKILL.md +103 -84
- package/templates/skills/orc-wait/SKILL.md +163 -0
- package/templates/skills/orc-wiki/SKILL.md +60 -206
- package/templates/skills/orc-wiki/references/phases/phase-0.md +71 -0
- package/templates/skills/orc-wiki/references/phases/phase-1.md +35 -0
- package/templates/skills/orc-wiki/references/phases/phase-2.md +52 -0
- package/templates/skills/orc-wiki/references/phases/phase-3.md +57 -0
- package/templates/skills/orc-wiki/references/phases/phase-3c.md +36 -0
- package/templates/agents/orc-advisor-fable-5.md +0 -50
- package/templates/agents/orc-analyst-fable-5.md +0 -115
- package/templates/agents/orc-judge-fable-5.md +0 -79
- package/templates/agents/orc-planner-fable-5.md +0 -152
- package/templates/agents/orc-reviewer-fable-5.md +0 -57
- package/templates/skills/_shared/fable5-override.md +0 -56
- package/templates/skills/orc-diy/references/blocks/execution.md +0 -42
- package/templates/skills/orc-diy/references/blocks/mock-example.md +0 -22
- package/templates/skills/orc-diy/references/blocks/planning.md +0 -34
- package/templates/skills/orc-diy/references/blocks/review.md +0 -18
- package/templates/skills/orc-diy/references/blocks/scoring.md +0 -16
- package/templates/skills/orc-diy/references/blocks/security.md +0 -25
- package/templates/skills/orc-diy/references/blocks/ship.md +0 -25
- package/templates/skills/orc-diy/references/blocks/summary.md +0 -18
- package/templates/skills/orc-diy/references/blocks/testgen.md +0 -17
- package/templates/skills/orc-diy/references/blocks/trace.md +0 -28
- 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",
|
|
21
|
-
const TIER_DESC_KEY = { common: "settings.tierDesc.common",
|
|
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", "
|
|
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));
|