smlflg's picture
Initial public upload from Projekte/CodexMaxHarnessHowTo
0ef38b4 verified
Raw History Blame Contribute Delete
12.6 kB
const rateCard = {
"GPT-5.4": { input: 62.5, cached: 6.25, output: 375 },
"GPT-5.4-Mini": { input: 18.75, cached: 1.875, output: 113 },
"GPT-5.3-Codex": { input: 43.75, cached: 4.375, output: 350 },
"GPT-5.2-Codex": { input: 43.75, cached: 4.375, output: 350 },
};
const days = [
{
title: "Inventar und Ziele",
mode: "Scout",
output: "Repo-Liste, Top-20 Task-Kandidaten, Risiko-Matrix",
items: ["Codex Usage Panel pruefen", "beste Repos nach Impact sortieren", "keine Endlosjobs starten"],
},
{
title: "Harness-Benchmark",
mode: "Scout",
output: "Messbare Eval-Suite fuer Codex, Hermes, OpenCode",
items: ["identische Tasksheets", "Zeit, Kosten, Fehlerklasse erfassen", "ein Scoreboard schreiben"],
},
{
title: "Testgenerator-Welle",
mode: "Build",
output: "Tests fuer riskante Module und fehlende Regressionen",
items: ["erst failing tests", "dann Fixes", "Coverage-Luecken dokumentieren"],
},
{
title: "Refactor-Welle",
mode: "Build",
output: "kleinere APIs, weniger Duplication, bessere Grenzen",
items: ["keine Big-Bang-Refactors", "jede Aenderung testbar", "Diffs nach Modul trennen"],
},
{
title: "Frontend-Finish",
mode: "Ship",
output: "visuell fertige lokale Tools mit Screenshot-Check",
items: ["mobile und desktop pruefen", "Textueberlauf fixen", "leere States bauen"],
},
{
title: "Doku und Runbooks",
mode: "Ship",
output: "README, Setup, Troubleshooting, Agent-Prompts",
items: ["nur operative Doku", "keine Markdown-Flut", "Prompts als wiederverwendbare Vorlagen"],
},
{
title: "Review-Tag",
mode: "Verify",
output: "Codex-Reviews, Security-Checks, CI-Fixes",
items: ["codex review nutzen", "Findings zuerst", "nur echte Risiken fixen"],
},
{
title: "Final Sweep",
mode: "Verify",
output: "Archiv, offene Risiken, naechster Monatsplan",
items: ["Zombie-Sessions schliessen", "Artefakte verlinken", "Lessons in Harness-Guide uebernehmen"],
},
];
const prompts = [
{
title: "Deep Repo Audit",
command: `codex exec --full-auto -m gpt-5.4 -C /home/smlflg/Projekte/DEIN_REPO \\
"Lies das Repo, finde die 10 hoechsten Hebel fuer Tests, Sicherheit und Wartbarkeit. Aendere noch nichts. Gib eine priorisierte, belegte Liste mit Dateien und konkreten naechsten Commands aus."`,
},
{
title: "Harness Eval Run",
command: `codex exec --full-auto -m gpt-5.4 -C /home/smlflg/Projekte/HarnessEcosystem \\
"Baue eine kleine reproduzierbare Eval fuer Codex-Harness-Verhalten: Tasksheet, Messkriterien, Beispieltask, Ergebnis-Tabelle. Halte die Aenderung eng und teste lokal."`,
},
{
title: "Test-First Fix",
command: `codex exec --full-auto -m gpt-5.4 -c model_reasoning_effort="high" -C /home/smlflg/Projekte/DEIN_REPO \\
"Finde eine reale, riskante Bugklasse. Schreibe zuerst einen failing Test, implementiere den kleinsten Fix, fuehre relevante Tests aus und erklaere den Diff."`,
},
{
title: "Codex Review",
command: `codex review --base main "Priorisiere Bugs, Regressionen, Security-Risiken und fehlende Tests. Keine Stil-Nitpicks ohne Risiko."`,
},
{
title: "OpenAI Docs MCP Check",
command: `codex exec --search -C /home/smlflg/Projekte/DEIN_REPO \\
"Nutze openaiDeveloperDocs, pruefe ob unsere OpenAI-API-Nutzung aktuell ist, und schlage gezielte Code-Aenderungen vor. Nur offizielle OpenAI-Doku verwenden."`,
},
{
title: "Finish Pass",
command: `codex exec --full-auto -m gpt-5.4 -C /home/smlflg/Projekte/DEIN_REPO \\
"Mache aus diesem lokalen Tool ein fertiges Artefakt: README, Startanleitung, leere States, Fehlertexte, kleine UI-Politur und abschliessende Verifikation."`,
},
];
const lanes = ["Scout", "Build", "Verify", "Ship"];
const defaultTasks = [
{ id: "seed-mcp-docs", title: "Codex MCP Docs in bestehende OpenAI-Projekte einbauen", lane: "Scout", model: "GPT-5.4" },
{ id: "seed-promptgarage-eval", title: "PromptGarage: Eval-DB pruefen und Judge-Runs automatisieren", lane: "Build", model: "GPT-5.3-Codex" },
{ id: "seed-token-diaet", title: "HarnessEcosystem: TOKEN_DIAET mit neuen Codex-Rate-Card-Daten aktualisieren", lane: "Ship", model: "GPT-5.4" },
{ id: "seed-static-sites", title: "Alle privaten Static-Sites auf mobile Textueberlaeufe pruefen", lane: "Verify", model: "GPT-5.4-Mini" },
];
const formatter = new Intl.NumberFormat("de-DE", { maximumFractionDigits: 0 });
const preciseFormatter = new Intl.NumberFormat("de-DE", { maximumFractionDigits: 2 });
let taskState = loadTasks();
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function createId() {
if (globalThis.crypto?.randomUUID) return crypto.randomUUID();
return `task-${Date.now()}-${Math.random().toString(16).slice(2)}`;
}
function normalizeTask(task) {
const lane = lanes.includes(task?.lane) ? task.lane : "Scout";
const model = Object.hasOwn(rateCard, task?.model) ? task.model : "GPT-5.4";
return {
id: String(task?.id || createId()),
title: String(task?.title || "Unbenannter Task").slice(0, 240),
lane,
model,
};
}
function loadTasks() {
try {
const saved = localStorage.getItem("codex-max-harness-tasks");
if (!saved) return defaultTasks.map(normalizeTask);
const parsed = JSON.parse(saved);
return Array.isArray(parsed) ? parsed.map(normalizeTask) : defaultTasks.map(normalizeTask);
} catch {
return defaultTasks.map(normalizeTask);
}
}
function numberFrom(id) {
return Number(document.getElementById(id).value || 0);
}
function renderTimeline() {
document.getElementById("timeline").innerHTML = days
.map(
(day, index) => `
<article class="day-card">
<div class="day-kicker"><span class="dot"></span>Tag ${index + 1} · ${escapeHtml(day.mode)}</div>
<h3>${escapeHtml(day.title)}</h3>
<p><strong>Output:</strong> ${escapeHtml(day.output)}</p>
<ul>${day.items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>
</article>
`,
)
.join("");
}
function renderModels() {
const select = document.getElementById("modelSelect");
select.innerHTML = Object.keys(rateCard)
.map((model) => `<option value="${model}">${model}</option>`)
.join("");
}
function updateBudget() {
const remaining = numberFrom("remainingCredits");
const daysLeft = Math.max(1, numberFrom("remainingDays"));
const buffer = Math.min(40, Math.max(0, numberFrom("bufferPercent"))) / 100;
const blocks = Math.max(1, numberFrom("blocksPerDay"));
const usable = remaining * (1 - buffer);
const daily = usable / daysLeft;
const block = daily / blocks;
document.getElementById("usableCredits").textContent = formatter.format(usable);
document.getElementById("dailyCredits").textContent = formatter.format(daily);
document.getElementById("blockCredits").textContent = formatter.format(block);
}
function updateEstimate() {
const selected = document.getElementById("modelSelect").value;
const rate = rateCard[selected];
const input = numberFrom("inputTokens") * rate.input;
const cached = numberFrom("cachedTokens") * rate.cached;
const output = numberFrom("outputTokens") * rate.output;
const multiplier = document.getElementById("fastMode").checked ? 2 : 1;
const total = (input + cached + output) * multiplier;
document.getElementById("estimatedCredits").textContent = preciseFormatter.format(total);
document.getElementById("estimateDetail").textContent =
`${selected}: Input ${rate.input}, Cached ${rate.cached}, Output ${rate.output} Credits pro 1M Tokens`;
}
function getTasks() {
return taskState;
}
function saveTasks(tasks) {
taskState = tasks.map(normalizeTask);
try {
localStorage.setItem("codex-max-harness-tasks", JSON.stringify(taskState));
} catch {
// Keep the in-memory board working when file:// or private mode blocks storage.
}
}
function moveTask(id, direction) {
const tasks = getTasks();
const task = tasks.find((item) => item.id === id);
if (!task) return;
const index = lanes.indexOf(task.lane);
const nextIndex = Math.min(lanes.length - 1, Math.max(0, index + direction));
task.lane = lanes[nextIndex];
saveTasks(tasks);
renderKanban();
}
function deleteTask(id) {
saveTasks(getTasks().filter((task) => task.id !== id));
renderKanban();
}
function renderKanban() {
const tasks = getTasks();
document.getElementById("kanban").innerHTML = lanes
.map((lane) => {
const laneTasks = tasks.filter((task) => task.lane === lane);
return `
<section class="lane" aria-label="${escapeHtml(lane)}">
<h3>${escapeHtml(lane)}<span class="lane-count">${laneTasks.length}</span></h3>
${laneTasks
.map(
(task) => `
<article class="task-card">
<strong>${escapeHtml(task.title)}</strong>
<div class="task-meta">
<span class="tag">${escapeHtml(task.model)}</span>
<span class="tag alt">${escapeHtml(task.lane)}</span>
</div>
<div class="task-actions">
<button type="button" data-task-move="${escapeHtml(task.id)}" data-direction="-1">Zurueck</button>
<button type="button" data-task-move="${escapeHtml(task.id)}" data-direction="1">Weiter</button>
<button type="button" data-task-delete="${escapeHtml(task.id)}">Loeschen</button>
</div>
</article>
`,
)
.join("")}
</section>
`;
})
.join("");
}
function renderPrompts() {
document.getElementById("promptGrid").innerHTML = prompts
.map(
(prompt, index) => `
<article class="prompt-card">
<h3>${escapeHtml(prompt.title)}</h3>
<pre><code>${escapeHtml(prompt.command)}</code></pre>
<button class="button ghost small" type="button" data-copy="${index}">Kopieren</button>
</article>
`,
)
.join("");
}
function fallbackCopy(text) {
const area = document.createElement("textarea");
area.value = text;
area.setAttribute("readonly", "");
area.style.position = "fixed";
area.style.left = "-9999px";
document.body.append(area);
area.select();
try {
return document.execCommand("copy");
} finally {
area.remove();
}
}
async function copyText(text) {
try {
if (navigator.clipboard?.writeText) {
await navigator.clipboard.writeText(text);
return true;
}
} catch {
// Fall through to the textarea fallback for file:// contexts.
}
return fallbackCopy(text);
}
function attachEvents() {
document.querySelectorAll("#budgetForm input").forEach((input) => {
input.addEventListener("input", updateBudget);
});
document.querySelectorAll("#costForm input, #costForm select").forEach((input) => {
input.addEventListener("input", updateEstimate);
input.addEventListener("change", updateEstimate);
});
document.getElementById("taskForm").addEventListener("submit", (event) => {
event.preventDefault();
const title = document.getElementById("taskTitle").value.trim();
if (!title) return;
const tasks = getTasks();
tasks.push({
id: createId(),
title,
lane: document.getElementById("taskLane").value,
model: document.getElementById("taskModel").value,
});
saveTasks(tasks);
event.currentTarget.reset();
renderKanban();
});
document.getElementById("kanban").addEventListener("click", (event) => {
const moveButton = event.target.closest("[data-task-move]");
if (moveButton) {
moveTask(moveButton.dataset.taskMove, Number(moveButton.dataset.direction));
return;
}
const deleteButton = event.target.closest("[data-task-delete]");
if (deleteButton) {
deleteTask(deleteButton.dataset.taskDelete);
}
});
document.getElementById("promptGrid").addEventListener("click", async (event) => {
const button = event.target.closest("[data-copy]");
if (!button) return;
const prompt = prompts[Number(button.dataset.copy)];
const copied = await copyText(prompt.command);
button.textContent = copied ? "Kopiert" : "Nicht kopiert";
window.setTimeout(() => {
button.textContent = "Kopieren";
}, 1200);
});
}
renderTimeline();
renderModels();
renderKanban();
renderPrompts();
attachEvents();
updateBudget();
updateEstimate();