File size: 16,942 Bytes
7005700 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | const stages = {
owner: {
kicker: "01 / Human Ownership",
title: "Der Mensch setzt Ziel, Risiko und Abbruchlinie.",
body: "Agenten arbeiten erst sinnvoll, wenn Ziel, Nicht-Ziel und Human-only Entscheidungen sichtbar sind.",
active: 0,
},
scope: {
kicker: "02 / Scope Boundary",
title: "Der Auftrag wird klein genug, dass Erfolg pruefbar ist.",
body: "Das Harness trennt Wunsch, Kontext, Nicht-Ziel und erlaubte Aktionen, bevor der Agent arbeitet.",
active: 1,
},
delegate: {
kicker: "03 / Delegation Card",
title: "Das Tool bekommt Rolle, Grenze und Artefaktpflicht.",
body: "Die Delegation Card ersetzt den diffusen Prompt durch Ziel, Scope, Human Gates und Verifikation.",
active: 2,
},
verify: {
kicker: "04 / Verifier Loop",
title: "Done ist erst wahr, wenn Belege danebenliegen.",
body: "Tests, Diffs, Dateien, Screenshots oder Reports schlagen die Erfolgserzaehlung des Agenten.",
active: 3,
},
handoff: {
kicker: "05 / Next Action",
title: "Am Ende steht eine Entscheidung, kein Rohlog.",
body: "Der Mensch sieht, was geaendert wurde, welche Belege existieren, was offen bleibt und was als naechstes passiert.",
active: 4,
},
};
const taskData = {
docs: {
goal: "Baue oder verbessere ein Tutorial-Artefakt.",
artifact: "Markdown-Dokumente, Quellenliste, Completion Checklist",
verify: "rg-Coverage, Link-Check, git diff --check",
},
bug: {
goal: "Reproduziere und behebe einen konkreten Fehler.",
artifact: "kleiner Diff, fokussierter Test oder Repro-Log",
verify: "Fehler reproduziert, Fix ausgefuehrt, Test/Smoke-Check gruen",
},
ui: {
goal: "Verbessere eine konkrete UI-Ansicht.",
artifact: "HTML/CSS/JS-Diff, Screenshot, responsive Check",
verify: "Headless Screenshot, mobile/desktop Sichtpruefung, keine Overlaps",
},
audit: {
goal: "Pruefe ein Repo oder Ergebnis read-only.",
artifact: "Audit-Report mit Findings, Belegen und Next Action",
verify: "Datei-/Zeilenbelege, keine Schreiboperationen, offene Risiken markiert",
},
};
const toolData = {
codex: {
name: "Codex",
source: "source: official OpenAI docs + local tutorial",
role: "Lokaler Coding-Agent fuer Repo-Arbeit, Review, Tests und sichtbare Diffs.",
use: "bounded implementation, repo audit, tests, docs",
needs: "AGENTS.md, konkrete Aufgabe, Done when, Verifikation",
artifact: "Diff, Tests, Verification Report, Next Action",
gate: "Scope drift, dependencies, destructive actions, deploy",
promptHandle: "Codex arbeitet im aktuellen Repo und liefert Diffs, Checks und Handoff.",
},
claude: {
name: "Claude Code",
source: "source: local HAI notes + Claude Code practice layer",
role: "Tiefer Codebase-Arbeiter fuer Planung, Implementierung und Review gegen Repo-Kontext.",
use: "komplexe Codebase-Arbeit, Architekturfragen, laengere Debug-Loops",
needs: "CLAUDE.md/Repo-Regeln, kleine Tasks, klare Tests, sichtbarer Diff",
artifact: "Plan, Patch, Tests, Review Notes",
gate: "Engine-Grenzen ehrlich markieren, breite Refactors und Tool-Rechte freigeben lassen",
promptHandle: "Claude Code nutzt Repo-Kontext, soll aber mit klarer Human Gate- und Testpflicht arbeiten.",
},
hermes: {
name: "Hermes",
source: "source: local Zustand-Hermes",
role: "Control Plane mit Profilen, Skills, Memory, Verifiern und Owner-Schutz.",
use: "Projektzustand laden, Profil waehlen, kleine naechste Schritte fuehren",
needs: "explizites Profil, Scope, Artefaktpflicht, Verifier oder Decision-Control",
artifact: "Owner Packet, Delegation Card, Verifier Report, Resume State",
gate: "Samuel entscheidet Produkt, Scope, Architektur, irreversible Freigaben",
promptHandle: "Hermes soll als Control Plane arbeiten: Profil waehlen, Briefing bauen, bounded delegieren.",
},
openclaw: {
name: "OpenClaw",
source: "source: local OpenClaw commands + architecture notes",
role: "Router/Gateway-System fuer Agenten, Sessions, Devices und komplexe Scout-Build-Verify-Flows.",
use: "Frontdoor-Routing, Gateway-Status, Agent-Sessions, komplexe Projektpipeline",
needs: "router/hermes/specialist topology, gateway health, session choice, explicit approvals",
artifact: "Routing Decision, Session State, Scout-Build-Verify Trace",
gate: "gateway/config/devices/auth commands and new specialists need explicit human approval",
promptHandle: "OpenClaw soll Routing und Sessions kontrolliert fuehren; komplexe Aufgaben gehen Scout -> Build -> Verify.",
},
opencode: {
name: "OpenCode",
source: "source: local OpenCode tutorials + handoff docs",
role: "Schneller Builder und Reality-Check-Agent mit strenger Scope-Anweisung.",
use: "one-shot Drafts, Gegenentwurf, schnelle Dateistrukturen, Implementierung",
needs: "vollstaendiger Kontext pro Run, Output-Pfad, Nicht-Ziele, Tests",
artifact: "kleiner Diff, Risiken, bewusst weggelassene Features",
gate: "wenn der erste Run falsch scoped ist: neue Session mit besserem Briefing statt Reparatur-Chat",
promptHandle: "OpenCode bekommt einen engen one-shot Auftrag mit Output-Pfad und expliziten Nicht-Zielen.",
},
pi: {
name: "Pi",
source: "source: generic HAI profile; no canonical local tool source found",
role: "Persoenlicher Intake- und Denkdialog fuer unstrukturierte menschliche Ausgangslage.",
use: "Wunsch klaeren, emotionale/kognitive Lage sortieren, Owner Packet vorbereiten",
needs: "keine Writes, keine Done-Claims, klare Uebergabe an Arbeitsagent",
artifact: "Problemklassifikation, Human-only Liste, naechste Frage",
gate: "Reflexion ist kein Beleg; Umsetzung startet erst in einem anderen bounded Tool",
promptHandle: "Pi soll nur klaeren und verdichten, nicht implementieren oder Erfolg behaupten.",
},
conductor: {
name: "Conductor",
source: "source: generic HAI orchestration profile; no canonical local tool source found",
role: "Orchestrierungsrolle fuer mehrere Worker, ohne selbst die Ownership zu uebernehmen.",
use: "Run-Leitung, Task-Slicing, Worker-Reihenfolge, Handoff und Integration",
needs: "ein Lead, getrennte Write Scopes, Stop Rules, gemeinsamer Verifier",
artifact: "Run Plan, Worker Contracts, Integration Checklist",
gate: "kein Fanout ohne disjunkte Schreibbereiche und klares Integrationsartefakt",
promptHandle: "Conductor koordiniert nur bounded Worker und muss Human Gates und Verifier sichtbar halten.",
},
};
const riskData = {
low: {
label: "bounded",
meter: "74%",
gate: "Stoppe bei Scope Drift oder fehlenden Belegen.",
},
medium: {
label: "gated",
meter: "58%",
gate: "Frage vor Dependencies, breiteren Datei-Aenderungen und unklaren Tests.",
},
high: {
label: "human gate required",
meter: "34%",
gate: "Keine Ausfuehrung ohne menschliche Freigabe fuer Risiko, Auth, Deploy oder destruktive Aktionen.",
},
};
const canvas = document.querySelector("#harnessCanvas");
const ctx = canvas.getContext("2d");
const stageButtons = Array.from(document.querySelectorAll("[data-stage]"));
const flowNodes = Array.from(document.querySelectorAll(".flow-node"));
const stageKicker = document.querySelector("#stageKicker");
const stageTitle = document.querySelector("#stageTitle");
const stageBody = document.querySelector("#stageBody");
const promptOutput = document.querySelector("#promptOutput");
const readinessMeter = document.querySelector("#readinessMeter");
const readinessLabel = document.querySelector("#readinessLabel");
const copyPrompt = document.querySelector("#copyPrompt");
const toolSelect = document.querySelector("#toolSelect");
const toolTabs = Array.from(document.querySelectorAll("[data-tool]"));
const toolSource = document.querySelector("#toolSource");
const toolName = document.querySelector("#toolName");
const toolRole = document.querySelector("#toolRole");
const toolUse = document.querySelector("#toolUse");
const toolNeeds = document.querySelector("#toolNeeds");
const toolArtifact = document.querySelector("#toolArtifact");
const toolGate = document.querySelector("#toolGate");
const gateInputs = Array.from(document.querySelectorAll(".gate-list input"));
const gateVerdict = document.querySelector(".gate-verdict");
const gateMode = document.querySelector("#gateMode");
const gateTitle = document.querySelector("#gateTitle");
const gateBody = document.querySelector("#gateBody");
let currentStage = "owner";
let currentTool = "codex";
let currentTask = "docs";
let currentRisk = "low";
let pointer = { x: 0.72, y: 0.38 };
function setStage(stageId) {
const stage = stages[stageId];
if (!stage) return;
currentStage = stageId;
stageKicker.textContent = stage.kicker;
stageTitle.textContent = stage.title;
stageBody.textContent = stage.body;
stageButtons.forEach((button) => {
button.classList.toggle("is-active", button.dataset.stage === stageId);
});
flowNodes.forEach((node) => {
node.classList.toggle("is-active", node.dataset.stage === stageId);
});
}
function buildPrompt() {
const task = taskData[currentTask];
const risk = riskData[currentRisk];
const tool = toolData[currentTool];
readinessMeter.style.width = risk.meter;
readinessLabel.textContent = risk.label;
promptOutput.textContent = [
"Tool:",
tool.name,
"",
"Ziel:",
task.goal,
"",
"Tool-Rolle:",
"- " + tool.promptHandle,
"",
"Kontext:",
"- Nutze das bestehende Repo und lies betroffene Dateien zuerst.",
"- Behandle Chat als Steuerpult, nicht als Arbeitsflaeche.",
"- Tool braucht: " + tool.needs + ".",
"",
"Scope:",
"- Arbeite nur an dieser Aufgabe.",
"- Erzeuge ein pruefbares Artefakt: " + task.artifact + ".",
"- Tool-Artefakt: " + tool.artifact + ".",
"",
"Nicht tun:",
"- keine Commits ohne Freigabe",
"- keine neuen Dependencies ohne Gate",
"- keine Scope-Erweiterung ohne Rueckfrage",
"",
"Human Gates:",
"- " + tool.gate + ".",
"- " + risk.gate,
"- Stoppe bei destruktiven Aktionen, Secrets/Auth/Deploy oder unklarem Arbeitsordner.",
"",
"Verifikation:",
"- " + task.verify,
"- Liefere einen Verification Report mit Belegen.",
"",
"Abschluss:",
"- Geaendert",
"- Belege",
"- Checks",
"- Offen",
"- Naechste Entscheidung",
].join("\n");
}
function setTool(toolId) {
const tool = toolData[toolId];
if (!tool) return;
currentTool = toolId;
toolSource.textContent = tool.source;
toolName.textContent = tool.name;
toolRole.textContent = tool.role;
toolUse.textContent = tool.use;
toolNeeds.textContent = tool.needs;
toolArtifact.textContent = tool.artifact;
toolGate.textContent = tool.gate;
toolSelect.value = toolId;
toolTabs.forEach((tab) => {
tab.classList.toggle("is-active", tab.dataset.tool === toolId);
});
buildPrompt();
}
function updateGate() {
const active = gateInputs.filter((input) => input.checked).map((input) => input.value);
const hardStop = active.some((value) => ["dependency", "destructive", "secret", "scope"].includes(value));
gateVerdict.classList.toggle("is-stop", hardStop);
if (hardStop) {
gateMode.textContent = "STOP";
gateTitle.textContent = "Erst Human Gate, dann Codex.";
gateBody.textContent = "Mindestens ein harter Gate-Faktor ist aktiv. Der Mensch entscheidet Scope, Risiko oder Freigabe, bevor der Agent weiterarbeitet.";
} else if (active.includes("unknown")) {
gateMode.textContent = "WARN";
gateTitle.textContent = "Der Agent darf nur diagnostizieren.";
gateBody.textContent = "Unklare Tests oder unbekannte Fehler brauchen erst Reproduktion und Beleg, nicht direkt einen Fix.";
} else {
gateMode.textContent = "RUN";
gateTitle.textContent = "Das Tool darf bounded arbeiten.";
gateBody.textContent = "Kein harter Human Gate aktiv. Der Auftrag braucht trotzdem Artefaktpflicht und Verifikation.";
}
}
function resizeCanvas() {
const ratio = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = Math.max(1, Math.floor(rect.width * ratio));
canvas.height = Math.max(1, Math.floor(rect.height * ratio));
ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
}
function drawCanvas(time) {
const rect = canvas.getBoundingClientRect();
const width = rect.width;
const height = rect.height;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#0d100c";
ctx.fillRect(0, 0, width, height);
const grid = 46;
ctx.strokeStyle = "rgba(238, 228, 207, 0.05)";
ctx.lineWidth = 1;
for (let x = -grid; x < width + grid; x += grid) {
ctx.beginPath();
ctx.moveTo(x + ((time / 80) % grid), 0);
ctx.lineTo(x + ((time / 80) % grid), height);
ctx.stroke();
}
for (let y = 0; y < height; y += grid) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
const nodes = [
{ label: "OWNER", x: 0.62, y: 0.24, color: "#a4d65e" },
{ label: "SCOPE", x: 0.78, y: 0.36, color: "#4fa997" },
{ label: toolData[currentTool].name.toUpperCase().slice(0, 9), x: 0.68, y: 0.54, color: "#547aa5" },
{ label: "VERIFY", x: 0.84, y: 0.66, color: "#c76b42" },
{ label: "NEXT", x: 0.55, y: 0.72, color: "#9b82c7" },
];
const activeIndex = stages[currentStage].active;
ctx.lineWidth = 2;
for (let i = 0; i < nodes.length - 1; i += 1) {
const from = nodes[i];
const to = nodes[i + 1];
const fx = from.x * width;
const fy = from.y * height;
const tx = to.x * width;
const ty = to.y * height;
ctx.strokeStyle = i < activeIndex ? "rgba(164, 214, 94, 0.7)" : "rgba(238, 228, 207, 0.18)";
ctx.beginPath();
ctx.moveTo(fx, fy);
ctx.lineTo((fx + tx) / 2, fy);
ctx.lineTo((fx + tx) / 2, ty);
ctx.lineTo(tx, ty);
ctx.stroke();
}
nodes.forEach((node, index) => {
const x = node.x * width + (pointer.x - 0.5) * 16;
const y = node.y * height + (pointer.y - 0.5) * 16;
const active = index === activeIndex;
const size = active ? 88 : 72;
ctx.save();
ctx.translate(x, y);
ctx.rotate(Math.PI / 4);
ctx.fillStyle = active ? node.color : "rgba(238, 228, 207, 0.08)";
ctx.strokeStyle = active ? "rgba(255,255,255,0.62)" : "rgba(238, 228, 207, 0.22)";
ctx.lineWidth = active ? 2 : 1;
ctx.fillRect(-size / 2, -size / 2, size, size);
ctx.strokeRect(-size / 2, -size / 2, size, size);
ctx.restore();
ctx.fillStyle = active ? "#0d100c" : "rgba(238, 228, 207, 0.72)";
ctx.font = "12px IBM Plex Mono, Consolas, monospace";
ctx.textAlign = "center";
ctx.fillText(node.label, x, y + 4);
});
requestAnimationFrame(drawCanvas);
}
stageButtons.forEach((button) => {
button.addEventListener("click", () => setStage(button.dataset.stage));
});
toolTabs.forEach((button) => {
button.addEventListener("click", () => setTool(button.dataset.tool));
});
toolSelect.addEventListener("change", () => setTool(toolSelect.value));
document.querySelectorAll("[data-task]").forEach((button) => {
button.addEventListener("click", () => {
currentTask = button.dataset.task;
document.querySelectorAll("[data-task]").forEach((item) => item.classList.toggle("is-active", item === button));
buildPrompt();
});
});
document.querySelectorAll("[data-risk]").forEach((button) => {
button.addEventListener("click", () => {
currentRisk = button.dataset.risk;
document.querySelectorAll("[data-risk]").forEach((item) => item.classList.toggle("is-active", item === button));
buildPrompt();
});
});
copyPrompt.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText(promptOutput.textContent);
copyPrompt.textContent = "Copied";
setTimeout(() => {
copyPrompt.textContent = "Copy";
}, 1200);
} catch {
copyPrompt.textContent = "Select";
setTimeout(() => {
copyPrompt.textContent = "Copy";
}, 1200);
}
});
gateInputs.forEach((input) => input.addEventListener("change", updateGate));
document.querySelectorAll(".run-step").forEach((button) => {
button.addEventListener("click", () => {
const detail = button.nextElementSibling;
const open = button.classList.contains("is-open");
document.querySelectorAll(".run-step").forEach((item) => item.classList.remove("is-open"));
document.querySelectorAll(".run-detail").forEach((item) => item.classList.remove("is-open"));
if (!open) {
button.classList.add("is-open");
detail.classList.add("is-open");
}
});
});
window.addEventListener("resize", resizeCanvas);
window.addEventListener("pointermove", (event) => {
pointer = {
x: event.clientX / Math.max(1, window.innerWidth),
y: event.clientY / Math.max(1, window.innerHeight),
};
});
resizeCanvas();
setStage("owner");
setTool("codex");
updateGate();
requestAnimationFrame(drawCanvas);
|