Files
skills/orchestrator/templates/page-agent-order.js
T
Kevin Veen-BirkenbachandClaude Opus 5.5 c035373518 feat(orchestrator)!: S0 English everywhere, Sécurité, NATO hashes, ICAO digits
Requirement 001 phase 6, S0:

- Speech: the security class shows as "Sécurité" and is spoken as in
  maritime radio, "say-cure-ee-tay" three times. A hex run of 7 or more
  characters with a letter and a digit (commit, session, message ids) is
  spelled in the NATO alphabet; every other digit is spoken on its own
  in ICAO pronunciation (42305: four two tree zero fife). The spelling
  runs inside clip() before the length cut, so a phrase still fits
  SPEECH_LIMIT; the displayed text keeps its digits and hashes.
- English: every orchestrator label, tooltip, message, error, script
  output, document and test string is English (Regeln -> Firewall,
  Hierarchie -> Hierarchy, Steuerung -> Controls, Ausführen -> Run, ...);
  the page declares lang="en" and formats dates as en-GB. Task filter
  ids are English. German stays only in the skill trigger phrases that
  quote the operator, and as identifiers: the view keys (view=) and the
  leitstand id.
- tests/test_english.py scans every tracked text file for umlauts and
  German words, with an allowlist of the quoted trigger phrases.

BREAKING CHANGE: the Stream search tokens art: and seit: are now kind:
and since:; an old art: or seit: token counts as a plain word.

Evidence: make test 657 OK, ruff and markdownlint clean
(/tmp/s0-make-test-1.log); e2e 17 OK, none skipped (/tmp/s0-e2e-1.log);
MEDUSA 86 files, 24 findings on reworded strings accepted after an
independent skeptic (21 path:line, 0 rejected, no behaviour change in
signing, approving, rules, denying, offices, events), rescan 0 open.

🎯 Fix: 93 %
💥 Break: 4 %
💸 Cost: 100 %
⏱️ Time: –
🛡️ Security: 100 %
🏆 Score: 89
🚦 Gate: ✅ 90/5, cost and time <= 100, security 100 %

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 17:26:51 +00:00

103 lines
4.2 KiB
JavaScript

const orderDom = {box: null, dragged: null, focus: null};
function openAgentOrder() {
go("agents", "hierarchy");
window.setTimeout(() => {
const title = document.getElementById("agent-order-title");
if (title) {
title.scrollIntoView({block: "nearest"});
title.focus();
}
}, 50);
}
function syncOrder() {
const stored = state.agent_order;
if (ui.order === null || ui.order.length !== stored.length || ui.order.some((name) => !stored.includes(name))) {
ui.order = [...stored];
}
}
function placeAgent(next, focus) {
ui.order = next;
orderDom.focus = focus;
drawAgentOrder();
}
function moveAgent(name, step) {
const from = ui.order.indexOf(name);
const to = from + step;
if (from < 0 || to < 0 || to >= ui.order.length) {
return;
}
const next = ui.order.filter((entry) => entry !== name);
next.splice(to, 0, name);
placeAgent(next, `${step < 0 ? "up" : "down"}:${name}`);
}
function dropAgent(name) {
const dragged = orderDom.dragged;
orderDom.dragged = null;
if (dragged === null || dragged === name) {
return;
}
const next = ui.order.filter((entry) => entry !== dragged);
next.splice(next.indexOf(name) + (ui.order.indexOf(dragged) < ui.order.indexOf(name) ? 1 : 0), 0, dragged);
placeAgent(next, null);
}
function orderItem(name, index) {
const up = officeBtn("bi-arrow-up", `Move ${name} up`, `Move ${name} up: higher priority`, () => moveAgent(name, -1), `up:${name}`);
const down = officeBtn("bi-arrow-down", `Move ${name} down`, `Move ${name} down: lower priority`, () => moveAgent(name, 1), `down:${name}`);
up.disabled = index === 0;
down.disabled = index === ui.order.length - 1;
const item = h("li", {draggable: "true", "data-name": name}, icon("bi-grip-vertical", "grip"), h("span", {class: "rank"}, `${index + 1}.`), face(name, false), person(name), h("span", {class: "spacer"}), up, down);
item.addEventListener("dragstart", (event) => {
orderDom.dragged = name;
item.classList.add("dragging");
event.dataTransfer.effectAllowed = "move";
event.dataTransfer.setData("text/plain", name);
});
item.addEventListener("dragend", () => item.classList.remove("dragging"));
item.addEventListener("dragover", (event) => event.preventDefault());
item.addEventListener("drop", (event) => {
event.preventDefault();
dropAgent(name);
});
return item;
}
async function saveAgentOrder() {
if (await act("order", "Priority", {agents: ui.order})) {
ui.order = null;
drawAgentOrder();
}
}
function drawAgentOrder() {
const box = orderDom.box;
if (!box || !box.isConnected) {
orderDom.box = null;
return;
}
syncOrder();
const key = orderDom.focus || focusKey(box);
orderDom.focus = null;
const changed = JSON.stringify(ui.order) !== JSON.stringify(state.agent_order);
box.replaceChildren(
h("div", {class: "inbox-head"}, h("h2", {class: "group-title", id: "agent-order-title", tabindex: "-1", "data-focus": "order:title"}, "Agent priority", counter(ui.order.length))),
h("p", {class: "muted"}, "Orders the chair's grants and decides only between equal task priorities; the tasks' --prio pins stay unchanged."),
ui.order.length ? h("ol", {class: "order-list", "aria-label": "Agent order"}, ui.order.map(orderItem)) : h("p", {class: "muted"}, "No agent with an office or an open task."),
h("div", {class: "actions"},
h("span", {class: "muted"}, "Drag or use the arrows to reorder"),
h("span", {class: "spacer"}),
h("button", {type: "button", class: "btn ghost", "data-focus": "order:reset", disabled: !changed, on: {click: () => placeAgent([...state.agent_order], "order:reset")}}, "Reset"),
h("button", {type: "button", class: "btn primary", "data-focus": "order:save", disabled: !changed, on: {click: saveAgentOrder}}, "Save order")));
const target = key && [...box.querySelectorAll("[data-focus]")].find((element) => element.dataset.focus === key);
if (target && target.disabled) {
refocus(box, key.startsWith("up:") ? `down:${key.slice(3)}` : key.startsWith("down:") ? `up:${key.slice(5)}` : "order:title");
} else {
refocus(box, key);
}
}