Files
claude-werkzeuge/.vault-tools/workflow-pruefung/docs/plans/2026-09-24-parallelitaet/task-01-verbrauch.md
T
Pascal MichelandClaude Opus 5.5 fa1c26b407 Erste Fassung: Claude-Werkzeuge aus dem Vault
.vault-tools (ohne trading-plattform-pruefung und watermarks-remover),
.claude/agents, .claude/skills, .claude/workflows, .claude/settings.json.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:27:59 +02:00

9.9 KiB

type, title, status, summary, tags, created, updated, modell, effort, modell_begruendung
type title status summary tags created updated modell effort modell_begruendung
plan-task Task 01 — verbrauch.js: Planverbrauch lesen und Opus-Plätze rechnen draft Neuer gespeicherter Workflow, der über get_usage das 5-Stunden-Fenster und die Woche abliest und daraus die erlaubten Opus-Plätze rechnet; die Stufen stehen nur hier.
plan-task
vault-tools
codeworkflow
2026-09-24 2026-09-24 sonnet low Abschrift: jede Zeile steht wörtlich im Plan, Zieldateien und Verbote sind genannt. Sonnet low statt Haiku, weil der Code das Zeichen · (U+00B7) trägt und Haiku in der Messung vom 2026-09-23 typografische Zeichen still ersetzt hat.

Task 01 — verbrauch.js: Planverbrauch lesen und Opus-Plätze rechnen

Die globalen Vorgaben stehen in 00-uebersicht.md (Abschnitt „Globale Vorgaben“). Arbeitsverzeichnis ist der Vault-Wurzelordner D:/Claude; alle Pfade gelten von dort.

Was danach anders ist:

  • Es gibt verbrauch.js. Ein Haiku-Agent lädt mcp__ccd_session_mgmt__get_usage und überträgt die Fenster; das Skript ordnet sie ein (5-hour ..., Weekly ... all models, falls vorhanden Weekly ... Opus) und rechnet plaetze = min(wunsch, Stufe 5 Stunden, Stufe Woche, 4).
  • Ohne lesbare Ablesung gilt zwei, wie bisher im Stamm. nicht_gelesen und unbekannte_fenster sind eigene Stati, nie ein stilles Grün.
  • wunsch: 0 misst nur und bremst nie; probe ersetzt den Agenten für den Regressionslauf im echten Laufzeitsystem.

Zieldateien

  • .claude/workflows/verbrauch.js (neu)

Verbote

  • Keine andere Datei, auch nicht die übrigen Workflows unter .claude/workflows/ und nichts unter AI/Regelwerk/, .claude/agents/ oder CLAUDE.md.
  • Kein Commit, kein Push, kein Lauf eines echten Workflows.
  • Kein Umformulieren, auch nicht von Kommentaren oder Sonderzeichen; ein Widerspruch zwischen Plan und Baum wird gemeldet.

Umsetzung

.claude/workflows/verbrauch.js

Die ganze Datei ist neu:

export const meta = {
  name: 'verbrauch',
  description: 'Liest den Planverbrauch (5-Stunden-Fenster, Woche) über get_usage und rechnet daraus, wie viele Opus-Agenten gleichzeitig laufen dürfen',
  whenToUse: 'Vor jeder Opus-Staffel eines Workflows, als Kind aufgerufen: workflow({ scriptPath: "D:/Claude/.claude/workflows/verbrauch.js" }, { anlass, wunsch }). Args: {anlass, wunsch: ganze Zahl ab 0, probe?}. wunsch 0 heißt nur messen. Rückgabe {status: ok|nicht_gelesen|unbekannte_fenster, quelle, fuenf_stunden, woche, woche_opus, plaetze, gebremst, grund, fenster}. Ohne lesbare Ablesung gilt die alte Obergrenze zwei. Regel: AI/Regelwerk/codeworkflow.md, "Verbrauch und Opus-Plätze".',
  phases: [{ title: 'Verbrauch', detail: 'ein Haiku-Agent liest get_usage ab, das Skript entscheidet' }],
}

// Geltungsbereich (AI/Regelwerk/pruefwerkzeuge.md): Das Skript liest den Planverbrauch und
// rechnet daraus, wie viele Opus-Agenten gleichzeitig laufen dürfen. Es ist eine Bremse, kein
// Nachweis. Woher die Antworten kommen:
// - Welche Fenster zählen und wie sie heißen: Quelle. get_usage in der Desktop-App lieferte am
//   2026-09-24 "5-hour limit", "Weekly · all models" und "Weekly · Fable", je mit percentUsed
//   und resetsAt. Von 19 typisierten Limit-Abbrüchen im Session-Speicher (22.08. bis 20.09.)
//   waren 16 five_hour und 3 seven_day, keiner seven_day_opus. Ein Fenster "Weekly · Opus" zählt
//   trotzdem wie die Woche, falls es auftaucht, weil die Agenten, um die es geht, auf Opus laufen.
// - Die Stufen: Projektvorgabe, vorläufig (Pascal, 2026-09-24). Sie stehen nur hier und werden
//   nach der Messreihe mit .vault-tools/workflow-pruefung/verbrauch_reihe.py nachgestellt.
// - Ohne lesbare Ablesung: Projektvorgabe, die bisherige Stammregel von zwei Opus-Agenten.
// Was das Skript nicht sieht: Agenten, die nach der Ablesung in anderen Sitzungen starten;
// laufende Agenten, die nach der Ablesung weiter verbrauchen; Sitzungen ohne get_usage
// (außerhalb der Desktop-App). Das Urteil rechnet das Skript, der Haiku-Agent liest nur ab.

const HARTE_OBERGRENZE = 4
const OHNE_ABLESUNG = 2
// Je Stufe [Schwelle in Prozent, Plätze unterhalb der Schwelle]; ab der letzten Schwelle 0.
const STUFEN_FUENF_STUNDEN = [[50, 4], [70, 2], [85, 1]]
const STUFEN_WOCHE = [[70, 4], [85, 2], [95, 1]]

const FUENF_STUNDEN = /^\s*5[- ]?hour/i
const WOCHE_ALLE = /^\s*weekly\b.*\ball models\b/i
const WOCHE_OPUS = /^\s*weekly\b.*\bopus\b/i

const ABLESUNG = {
  type: 'object',
  properties: {
    verfuegbar: { type: 'boolean' },
    status: { type: 'string' },
    plan: { type: 'string' },
    fenster: {
      type: 'array',
      items: {
        type: 'object',
        properties: { label: { type: 'string' }, percentUsed: { type: 'number' }, resetsAt: { type: 'string' } },
        required: ['label', 'percentUsed', 'resetsAt'],
      },
    },
    fehler: { type: 'string' },
  },
  required: ['verfuegbar', 'status', 'plan', 'fenster', 'fehler'],
}

const AUFTRAG = `Lade mit dem Werkzeug ToolSearch das Werkzeug mcp__ccd_session_mgmt__get_usage (Abfrage "select:mcp__ccd_session_mgmt__get_usage") und rufe es einmal ohne Parameter auf.
Übertrage aus dem Ergebnis plan.status nach status und plan.plan nach plan. Übertrage jeden Eintrag aus plan.windows mit label, percentUsed und resetsAt unverändert nach fenster, auch Einträge, die dir unwichtig erscheinen. Setze verfuegbar auf true und fehler auf "".
Ist das Werkzeug nicht verfügbar oder scheitert der Aufruf, setze verfuegbar auf false, status auf "fehler", plan auf "", fenster auf [] und schreibe die Fehlermeldung wörtlich nach fehler.
Tu sonst nichts. Übertrage das Ergebnis in die Felder des StructuredOutput-Werkzeugs.`

function pruefeArgs(a) {
  if (!a || typeof a !== 'object') throw new Error('verbrauch: args fehlen')
  if (typeof a.anlass !== 'string' || !a.anlass.trim()) throw new Error('verbrauch: anlass fehlt')
  if (!Number.isInteger(a.wunsch) || a.wunsch < 0) throw new Error('verbrauch: wunsch muss eine ganze Zahl ab 0 sein, ist ' + JSON.stringify(a.wunsch))
  if (a.probe !== undefined && (a.probe === null || typeof a.probe !== 'object' || Array.isArray(a.probe))) throw new Error('verbrauch: probe muss ein Objekt sein')
}

function stufe(prozent, stufen) {
  for (const [schwelle, plaetze] of stufen) if (prozent < schwelle) return plaetze
  return 0
}

// Ordnet die Fenster ein. Ein Fenster, das als 5 Stunden oder Woche erkannt wird, aber keine
// Prozentzahl von 0 bis 100 trägt, ist ein Befund und wird nicht still übergangen.
function einordnen(fenster) {
  if (!Array.isArray(fenster)) return { fehler: 'fenster ist keine Liste' }
  const e = { fuenf: null, woche: null, opus: null, andere: [] }
  for (const f of fenster) {
    if (!f || typeof f !== 'object' || typeof f.label !== 'string') return { fehler: 'Fenster ohne label: ' + JSON.stringify(f) }
    const art = FUENF_STUNDEN.test(f.label) ? 'fuenf' : WOCHE_ALLE.test(f.label) ? 'woche' : WOCHE_OPUS.test(f.label) ? 'opus' : null
    if (!art) { e.andere.push(f.label); continue }
    const p = f.percentUsed
    if (typeof p !== 'number' || !Number.isFinite(p) || p < 0 || p > 100) return { fehler: `${f.label}: percentUsed ${JSON.stringify(p)} ist keine Zahl von 0 bis 100` }
    const neu = { prozent: p, zuruecksetzung: typeof f.resetsAt === 'string' ? f.resetsAt : null }
    if (!e[art] || e[art].prozent < p) e[art] = neu
  }
  const fehlt = [!e.fuenf && '5-hour limit', !e.woche && 'Weekly all models'].filter(Boolean)
  if (fehlt.length) return { fehler: 'Fenster fehlt: ' + fehlt.join(', ') + (e.andere.length ? '; gelesen: ' + e.andere.join(', ') : '') }
  return e
}

pruefeArgs(args)
phase('Verbrauch')
const wunsch = args.wunsch
const deckel = wunsch > HARTE_OBERGRENZE ? `; wunsch ${wunsch} auf die harte Obergrenze ${HARTE_OBERGRENZE} gesenkt` : ''
let quelle = 'probe'
let ablesung = args.probe
if (ablesung === undefined) {
  quelle = 'agent'
  ablesung = await agent(AUFTRAG, { label: 'verbrauch:' + args.anlass, phase: 'Verbrauch', schema: ABLESUNG, model: 'haiku' })
}

function ohneAblesung(status, grund) {
  const plaetze = Math.min(wunsch, OHNE_ABLESUNG)
  log(`Verbrauch ${args.anlass}: ${status} (${grund}); ${plaetze} Plätze nach der alten Obergrenze`)
  return { status, quelle, fuenf_stunden: null, woche: null, woche_opus: null, plaetze, gebremst: false, grund: grund + deckel, fenster: ablesung && Array.isArray(ablesung.fenster) ? ablesung.fenster : null }
}

if (!ablesung || typeof ablesung !== 'object') return ohneAblesung('nicht_gelesen', 'kein Ergebnis vom Ableseagenten')
if (ablesung.verfuegbar !== true || ablesung.status !== 'ok') return ohneAblesung('nicht_gelesen', `get_usage: verfuegbar ${ablesung.verfuegbar}, status ${ablesung.status}${ablesung.fehler ? ', ' + ablesung.fehler : ''}`)
const e = einordnen(ablesung.fenster)
if (e.fehler) return ohneAblesung('unbekannte_fenster', e.fehler)

const wocheProzent = Math.max(e.woche.prozent, e.opus ? e.opus.prozent : 0)
const grenzeFuenf = stufe(e.fuenf.prozent, STUFEN_FUENF_STUNDEN)
const grenzeWoche = stufe(wocheProzent, STUFEN_WOCHE)
const plaetze = Math.min(wunsch, grenzeFuenf, grenzeWoche, HARTE_OBERGRENZE)
const gebremst = wunsch > 0 && plaetze === 0
const grund = `5 Stunden ${e.fuenf.prozent} % (bis ${grenzeFuenf} Plätze), Woche ${wocheProzent} % (bis ${grenzeWoche} Plätze)${e.opus ? ', Opus-Woche ' + e.opus.prozent + ' %' : ''}${deckel}`
log(`Verbrauch ${args.anlass}: ${grund}; ${plaetze} von ${wunsch} Plätzen${gebremst ? ', gebremst' : ''}`)
return { status: 'ok', quelle, fuenf_stunden: e.fuenf, woche: e.woche, woche_opus: e.opus, plaetze, gebremst, grund, fenster: ablesung.fenster }

Verifikation

Im Vault-Wurzelordner:

node -e "const t=require('fs').readFileSync(process.argv[1],'utf8').replace(/^export const meta = /m,'const meta = ');new (Object.getPrototypeOf(async function(){}).constructor)('agent','parallel','pipeline','phase','log','workflow','args','budget',t);console.log('PARSE OK')" .claude/workflows/verbrauch.js

Erwartet: PARSE OK, Exit 0.

Danach git status --porcelain -- .claude/workflows/verbrauch.js zeigen.