Kontrollpflicht der Leitung: Stichproben gegen das Audit (#693)
A lead looks at the runs of the agents it leads - held against the task and against what the engine stamped, never by asking the agent. - RunInspector (Core/Inspection): the mechanical check and first profile of the inspector foundation (#204). Flags failed runs, tool errors, refusals, unfinished calls, tasks answered with bookkeeping alone, and operations the agent names but never called in the run (the 2026-09-14 pattern). - SpotChecks: picks runs per unit (rate, denser for new agents, new models and after a failure, daily cap), deterministic by run id; one task per lead. - Escalation tool: spot_checks, inspect_run, check_done, propose_measure. Measures go to a human; nothing is carried out automatically. A security suspicion is its own case, not a personnel matter. - Organisation view: settings per unit, recent checks, measures to decide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ab1bd67464
commit
68e723a9c0
@@ -2637,6 +2637,28 @@ button.org-einheit .org-name {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
/* #693: the lead's spot checks and the measures waiting for the human. */
|
||||
.org-stichproben h4 {
|
||||
margin: 14px 0 6px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.org-massnahme {
|
||||
border: 1px solid var(--warnung);
|
||||
border-left-width: 4px;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.org-massnahme.sicherheit {
|
||||
border-color: var(--gefahr);
|
||||
}
|
||||
|
||||
.org-befunde {
|
||||
margin: 0;
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
tr.org-ausreisser td {
|
||||
background: rgba(181, 130, 42, 0.08);
|
||||
}
|
||||
|
||||
@@ -105,6 +105,39 @@
|
||||
"org.doctrinePlaceholder": "Jeder Lieferantenkontakt wird in der Datenbank erfasst, bevor er weitergegeben wird.",
|
||||
"org.doctrineFrequent": "In den letzten 7 Tagen {count}-mal geändert — eine Doktrin, die oft wechselt, ist Mikromanagement mit Tokenkosten.",
|
||||
"org.doctrineRemoved": "(entfernt)",
|
||||
"org.spotChecks": "Stichproben der Leitung",
|
||||
"org.spotChecksHint": "Wie oft die Leitung dieser Einheit in die Läufe der Agenten sieht, die sie führt — gegen den Auftrag und gegen das, was die Engine aufgezeichnet hat, nie durch Nachfragen beim Agenten. Dichter bei neuen Agenten, neuen Modellen und nach einem Fehlschlag. Jede Stichprobe kostet die Leitung einen Blick: die Obergrenze niedrig halten. Maßnahmen, die die Leitung vorschlägt, entscheidest du.",
|
||||
"org.spotChecksEnabled": "Stichproben an",
|
||||
"org.spotChecksRate": "Anteil der Läufe (%)",
|
||||
"org.spotChecksDense": "Anteil bei Neuem oder nach Fehlschlag (%)",
|
||||
"org.spotChecksNewDays": "Tage, die ein Agent als neu gilt",
|
||||
"org.spotChecksPerDay": "Höchstens Stichproben je Tag",
|
||||
"org.spotChecksRecent": "Letzte Stichproben",
|
||||
"org.spotChecksNone": "Noch kein Lauf geprüft.",
|
||||
"org.spotCheckRun": "Lauf",
|
||||
"org.spotCheckWhy": "Anlass",
|
||||
"org.spotCheckFindings": "Befund im Audit",
|
||||
"org.spotCheckVerdict": "Urteil der Leitung",
|
||||
"org.why.sample": "Stichprobe",
|
||||
"org.why.new_agent": "neuer Agent",
|
||||
"org.why.new_model": "neues Modell",
|
||||
"org.why.after_a_failure": "nach Fehlschlag",
|
||||
"org.verdict.ok": "in Ordnung",
|
||||
"org.verdict.finding": "Befund",
|
||||
"org.verdictOpen": "offen bei {lead}",
|
||||
"org.measuresOpen": "Maßnahmen, die auf dich warten",
|
||||
"org.measuresDecided": "Entschiedene Maßnahmen",
|
||||
"org.measureBy": "vorgeschlagen von {lead}",
|
||||
"org.measureNote": "Notiz (optional)",
|
||||
"org.measureAccept": "Annehmen",
|
||||
"org.measureDismiss": "Ablehnen",
|
||||
"org.measure.sharpen_task": "Auftrag schärfen",
|
||||
"org.measure.change_model": "Modell wechseln",
|
||||
"org.measure.adjust_rights": "Rechte anpassen",
|
||||
"org.measure.suspend": "Agent stilllegen",
|
||||
"org.measure.security": "Sicherheitsverdacht",
|
||||
"org.measureStatus.accepted": "angenommen",
|
||||
"org.measureStatus.dismissed": "abgelehnt",
|
||||
"org.ceilingInherited": "Von oben begrenzt auf: {tools}",
|
||||
"org.settings": "Sammelwerte",
|
||||
"org.settingsHint": "Werden nach einer Vorschau in die Agenten geschrieben. Die Einheit merkt sich den Wert nur als Vorgabe für neue Mitglieder — zur Laufzeit übertrumpft nichts einen Agenten.",
|
||||
|
||||
@@ -105,6 +105,39 @@
|
||||
"org.doctrinePlaceholder": "Every supplier contact is entered in the database before it is passed on.",
|
||||
"org.doctrineFrequent": "Changed {count} times in the last 7 days — a doctrine that changes often is micro-management with token costs.",
|
||||
"org.doctrineRemoved": "(removed)",
|
||||
"org.spotChecks": "Spot checks by the lead",
|
||||
"org.spotChecksHint": "How often the lead of this unit looks at the runs of the agents it leads — held against the task and against what the engine recorded, never by asking the agent. Denser for new agents, new models and after a failure. Every check costs the lead a look: keep the cap low. Measures the lead proposes are decided by you.",
|
||||
"org.spotChecksEnabled": "Spot checks on",
|
||||
"org.spotChecksRate": "Share of runs (%)",
|
||||
"org.spotChecksDense": "Share when new or after a failure (%)",
|
||||
"org.spotChecksNewDays": "Days an agent counts as new",
|
||||
"org.spotChecksPerDay": "Checks per day at most",
|
||||
"org.spotChecksRecent": "Recent checks",
|
||||
"org.spotChecksNone": "No run checked yet.",
|
||||
"org.spotCheckRun": "Run",
|
||||
"org.spotCheckWhy": "Why",
|
||||
"org.spotCheckFindings": "Found in the audit log",
|
||||
"org.spotCheckVerdict": "Verdict of the lead",
|
||||
"org.why.sample": "sample",
|
||||
"org.why.new_agent": "new agent",
|
||||
"org.why.new_model": "new model",
|
||||
"org.why.after_a_failure": "after a failure",
|
||||
"org.verdict.ok": "in order",
|
||||
"org.verdict.finding": "finding",
|
||||
"org.verdictOpen": "open with {lead}",
|
||||
"org.measuresOpen": "Measures waiting for you",
|
||||
"org.measuresDecided": "Decided measures",
|
||||
"org.measureBy": "proposed by {lead}",
|
||||
"org.measureNote": "Note (optional)",
|
||||
"org.measureAccept": "Accept",
|
||||
"org.measureDismiss": "Dismiss",
|
||||
"org.measure.sharpen_task": "Sharpen the task",
|
||||
"org.measure.change_model": "Change the model",
|
||||
"org.measure.adjust_rights": "Adjust the rights",
|
||||
"org.measure.suspend": "Pause the agent",
|
||||
"org.measure.security": "Security suspicion",
|
||||
"org.measureStatus.accepted": "accepted",
|
||||
"org.measureStatus.dismissed": "dismissed",
|
||||
"org.ceilingInherited": "Limited from above to: {tools}",
|
||||
"org.settings": "Unit-wide values",
|
||||
"org.settingsHint": "Written into the agents after a preview. The unit keeps the value only as a default for new members — nothing overrides an agent at run time.",
|
||||
|
||||
@@ -368,6 +368,7 @@ function unitPanel(unit) {
|
||||
cards.push(ceilingCard(unit));
|
||||
cards.push(approvalsCard(unit));
|
||||
cards.push(doctrineCard(unit));
|
||||
cards.push(inspectionCard(unit));
|
||||
cards.push(settingsCard(unit));
|
||||
|
||||
// Members.
|
||||
@@ -503,6 +504,101 @@ function doctrineCard(unit) {
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* #693: The lead's spot checks. How often it looks at the runs of the agents it leads, what
|
||||
* came of the last checks, and the measures it proposed — which only a human decides. Nothing
|
||||
* here changes an agent; accepting records the decision, carrying it out stays with you.
|
||||
*/
|
||||
function inspectionCard(unit) {
|
||||
const current = unit.inspection;
|
||||
const enabled = erstelleElement("input", { type: "checkbox" });
|
||||
enabled.checked = !!current;
|
||||
const percent = (v) => String(Math.round((v ?? 0) * 100));
|
||||
const rate = erstelleElement("input", { type: "number", min: "0", max: "100", step: "1", value: percent(current?.rate ?? 0.1) });
|
||||
const dense = erstelleElement("input", { type: "number", min: "0", max: "100", step: "1", value: percent(current?.denseRate ?? 0.5) });
|
||||
const newDays = erstelleElement("input", { type: "number", min: "0", max: "365", step: "1", value: String(current?.newDays ?? 7) });
|
||||
const perDay = erstelleElement("input", { type: "number", min: "0", max: "100", step: "1", value: String(current?.perDay ?? 5) });
|
||||
const results = erstelleElement("div", { klasse: "org-stichproben" });
|
||||
|
||||
(async () => {
|
||||
let data;
|
||||
try { data = await api(`/organisation/units/${encodeURIComponent(unit.id)}/spot-checks`); } catch { return; }
|
||||
|
||||
const proposed = data.measures.filter((m) => m.status === "proposed");
|
||||
if (proposed.length) {
|
||||
results.append(erstelleElement("h4", { text: t("org.measuresOpen") }));
|
||||
for (const m of proposed) {
|
||||
const note = erstelleElement("input", { type: "text", placeholder: t("org.measureNote") });
|
||||
results.append(erstelleElement("div", { klasse: `org-massnahme${m.kind === "security" ? " sicherheit" : ""}` },
|
||||
erstelleElement("div", {},
|
||||
erstellePlakette(t(`org.measure.${m.kind}`), m.kind === "security" ? "gefahr" : "warnung"),
|
||||
erstelleElement("strong", { text: ` ${m.agentId}` }),
|
||||
erstelleElement("span", { klasse: "gedaempft text-klein", text: ` · ${t("org.measureBy", { lead: m.proposedBy })} · ${formatiereDatumZeit(m.proposedAt)}` })),
|
||||
erstelleElement("p", { klasse: "text-klein", text: m.reasoning }),
|
||||
erstelleElement("div", { klasse: "gruppe-horizontal" }, note,
|
||||
erstelleElement("button", {
|
||||
type: "button", klasse: "primaer klein", text: t("org.measureAccept"),
|
||||
onclick: () => send(`/organisation/measures/${m.id}/decide`, "POST", { accept: true, note: note.value })
|
||||
}),
|
||||
erstelleElement("button", {
|
||||
type: "button", klasse: "klein", text: t("org.measureDismiss"),
|
||||
onclick: () => send(`/organisation/measures/${m.id}/decide`, "POST", { accept: false, note: note.value })
|
||||
}))));
|
||||
}
|
||||
}
|
||||
|
||||
results.append(erstelleElement("h4", { text: t("org.spotChecksRecent") }));
|
||||
if (!data.checks.length) {
|
||||
results.append(erstelleElement("p", { klasse: "gedaempft text-klein", text: t("org.spotChecksNone") }));
|
||||
} else {
|
||||
results.append(erstelleElement("table", { klasse: "daten-tabelle" },
|
||||
erstelleElement("thead", {}, erstelleElement("tr", {},
|
||||
...[t("org.agent"), t("org.spotCheckRun"), t("org.spotCheckWhy"), t("org.spotCheckFindings"), t("org.spotCheckVerdict")]
|
||||
.map((h) => erstelleElement("th", { text: h })))),
|
||||
erstelleElement("tbody", {}, ...data.checks.map((c) => erstelleElement("tr", {},
|
||||
erstelleElement("td", { text: c.agentId }),
|
||||
erstelleElement("td", {}, erstelleElement("a", { href: `#/verbrauch/runs/${encodeURIComponent(c.runId)}`, text: formatiereDatumZeit(c.createdAt) })),
|
||||
erstelleElement("td", { text: t(`org.why.${c.why.replace(/ /g, "_")}`) }),
|
||||
erstelleElement("td", {}, c.findings.length
|
||||
? erstelleElement("ul", { klasse: "org-befunde" }, ...c.findings.map((f) => erstelleElement("li", { klasse: "text-klein", text: f.text })))
|
||||
: erstelleElement("span", { klasse: "gedaempft", text: "—" })),
|
||||
erstelleElement("td", {}, c.verdict
|
||||
? erstellePlakette(t(`org.verdict.${c.verdict}`), c.verdict === "ok" ? "erfolg" : "warnung")
|
||||
: erstelleElement("span", { klasse: "gedaempft text-klein", text: t("org.verdictOpen", { lead: c.lead }) }),
|
||||
c.note ? erstelleElement("div", { klasse: "text-klein", text: c.note }) : ""))))));
|
||||
}
|
||||
|
||||
const decided = data.measures.filter((m) => m.status !== "proposed").slice(0, 10);
|
||||
if (decided.length) {
|
||||
results.append(erstelleElement("h4", { text: t("org.measuresDecided") }));
|
||||
results.append(erstelleElement("ul", { klasse: "org-liste" }, ...decided.map((m) => erstelleElement("li", { klasse: "text-klein",
|
||||
text: `${t(`org.measure.${m.kind}`)} · ${m.agentId} · ${t(`org.measureStatus.${m.status}`)}${m.note ? " — " + m.note : ""}` }))));
|
||||
}
|
||||
})();
|
||||
|
||||
return erstelleKarte(t("org.spotChecks"), [
|
||||
erstelleElement("p", { klasse: "gedaempft text-klein", text: t("org.spotChecksHint") }),
|
||||
erstelleElement("div", { klasse: "formular-raster" },
|
||||
erstelleElement("label", { text: t("org.spotChecksEnabled") }), enabled,
|
||||
erstelleElement("label", { text: t("org.spotChecksRate") }), rate,
|
||||
erstelleElement("label", { text: t("org.spotChecksDense") }), dense,
|
||||
erstelleElement("label", { text: t("org.spotChecksNewDays") }), newDays,
|
||||
erstelleElement("label", { text: t("org.spotChecksPerDay") }), perDay),
|
||||
erstelleElement("button", {
|
||||
type: "button", klasse: "klein", text: t("common.save"),
|
||||
onclick: () => send(`/organisation/units/${encodeURIComponent(unit.id)}/inspection`, "PUT", {
|
||||
enabled: enabled.checked,
|
||||
rate: Number(rate.value) / 100,
|
||||
denseRate: Number(dense.value) / 100,
|
||||
newDays: Number(newDays.value),
|
||||
perDay: Number(perDay.value),
|
||||
reason: reason()
|
||||
})
|
||||
}),
|
||||
results
|
||||
]);
|
||||
}
|
||||
|
||||
function settingsCard(unit) {
|
||||
const setting = erstelleElement("select", {},
|
||||
...SETTINGS.map((s) => erstelleElement("option", { value: s, text: t(`org.setting.${s}`) })));
|
||||
|
||||
Reference in New Issue
Block a user