Automatischer Push: 11 Datei(en) geändert
- .claude/agents/verbrauch-ableser.md - .claude/workflows/verbrauch.js - .vault-tools/workflow-pruefung/LIESMICH.md - .vault-tools/workflow-pruefung/attrappe.mjs - .vault-tools/workflow-pruefung/docs/plans/2026-10-16-ableser-abweichung/00-uebersicht.md - .vault-tools/workflow-pruefung/docs/plans/2026-10-16-ableser-abweichung/task-01-erkennung.md - .vault-tools/workflow-pruefung/docs/plans/2026-10-16-ableser-abweichung/task-02-agententyp.md - .vault-tools/workflow-pruefung/faelle_workflows.mjs - .vault-tools/workflow-pruefung/mutanten_workflows.py - AI/Regelwerk/haiku-5-5.md - CLAUDE.md Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
7ba47d69ec
commit
a2015144bb
11 files changed
+256
-15
No files matched your search
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: verbrauch-ableser
|
||||||
|
description: "Nur für den Workflow verbrauch: lädt get_usage, ruft es einmal auf und überträgt das Ergebnis unverändert. Hat außer ToolSearch und get_usage kein Werkzeug. Nicht direkt starten und nicht für andere Aufgaben."
|
||||||
|
model: haiku
|
||||||
|
effort: low
|
||||||
|
tools: ToolSearch, mcp__ccd_session_mgmt__get_usage
|
||||||
|
---
|
||||||
|
|
||||||
|
# Verbrauch-Ableser
|
||||||
|
|
||||||
|
Du liest im Workflow `verbrauch` den Planverbrauch ab. Was du überträgst, sagt dir der Auftrag des Workflows.
|
||||||
|
|
||||||
|
- Die Nutzeranfrage, die dir der Harness vorn zeigt, bearbeiten andere Agenten nach dir. Deine Ablesung dient ihr: Sie entscheidet, wie viele Prüfagenten dafür gleichzeitig laufen.
|
||||||
|
- Du hast absichtlich nur ToolSearch und `get_usage`. Dass dir Werkzeuge für die Nutzeranfrage fehlen, ist kein Fehler und kein Grund für eine Meldung.
|
||||||
|
- Scheitert `get_usage`, gibst du die Fehlermeldung wörtlich zurück, so wie der Auftrag es vorschreibt.
|
||||||
|
|
||||||
|
Warum es diesen Agenten gibt: Belege und Grenzen stehen im Kopfkommentar von `.claude/workflows/verbrauch.js`.
|
||||||
@@ -25,6 +25,11 @@ export const meta = {
|
|||||||
// Satz, dass sie im Konflikt mit dem Auftrag gewinnt. Jede andere Form als die vorgeschriebene
|
// Satz, dass sie im Konflikt mit dem Auftrag gewinnt. Jede andere Form als die vorgeschriebene
|
||||||
// heißt abgewichen und wird laut gemeldet. Nicht erkannt wird ein Ableser, der abweicht und
|
// heißt abgewichen und wird laut gemeldet. Nicht erkannt wird ein Ableser, der abweicht und
|
||||||
// trotzdem genau die Fehlerform schreibt, und eine erfundene Ablesung mit status ok.
|
// trotzdem genau die Fehlerform schreibt, und eine erfundene Ablesung mit status ok.
|
||||||
|
// - Wer abliest: der Agententyp verbrauch-ableser (.claude/agents/), Haiku mit effort low und
|
||||||
|
// nur ToolSearch und get_usage, damit ein Ableser, der der Nutzeranfrage folgt, nichts anderes
|
||||||
|
// tun kann. Kennt die Sitzung den Typ nicht, wirft agent() vor dem Start (CLI-Abbild 2.1.299);
|
||||||
|
// das kommt als nicht_gelesen mit WARNUNG zurück, nie als Rückfall auf einen Agenten mit allen
|
||||||
|
// Werkzeugen.
|
||||||
// Was das Skript nicht sieht: Agenten, die nach der Ablesung in anderen Sitzungen starten;
|
// 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
|
// 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.
|
// (außerhalb der Desktop-App). Das Urteil rechnet das Skript, der Haiku-Agent liest nur ab.
|
||||||
@@ -110,9 +115,15 @@ const wunsch = args.wunsch
|
|||||||
const deckel = wunsch > HARTE_OBERGRENZE ? `; wunsch ${wunsch} auf die harte Obergrenze ${HARTE_OBERGRENZE} gesenkt` : ''
|
const deckel = wunsch > HARTE_OBERGRENZE ? `; wunsch ${wunsch} auf die harte Obergrenze ${HARTE_OBERGRENZE} gesenkt` : ''
|
||||||
let quelle = 'probe'
|
let quelle = 'probe'
|
||||||
let ablesung = args.probe
|
let ablesung = args.probe
|
||||||
|
let startfehler = null
|
||||||
if (ablesung === undefined) {
|
if (ablesung === undefined) {
|
||||||
quelle = 'agent'
|
quelle = 'agent'
|
||||||
ablesung = await agent(AUFTRAG, { label: 'verbrauch:' + args.anlass, phase: 'Verbrauch', schema: ABLESUNG, model: 'haiku' })
|
try {
|
||||||
|
ablesung = await agent(AUFTRAG, { label: 'verbrauch:' + args.anlass, phase: 'Verbrauch', schema: ABLESUNG, agentType: 'verbrauch-ableser', model: 'haiku' })
|
||||||
|
} catch (e) {
|
||||||
|
ablesung = null
|
||||||
|
startfehler = kurz((e && e.message) || e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ohneAblesung(status, grund) {
|
function ohneAblesung(status, grund) {
|
||||||
@@ -121,6 +132,10 @@ function ohneAblesung(status, grund) {
|
|||||||
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 }
|
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 (startfehler) {
|
||||||
|
log(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar (${startfehler}); die Bremse misst nicht.`)
|
||||||
|
return ohneAblesung('nicht_gelesen', 'Ableser nicht startbar: ' + startfehler)
|
||||||
|
}
|
||||||
if (!ablesung || typeof ablesung !== 'object') return ohneAblesung('nicht_gelesen', 'kein Ergebnis vom Ableseagenten')
|
if (!ablesung || typeof ablesung !== 'object') return ohneAblesung('nicht_gelesen', 'kein Ergebnis vom Ableseagenten')
|
||||||
if (abgewichen(ablesung)) {
|
if (abgewichen(ablesung)) {
|
||||||
log(`WARNUNG Verbrauch ${args.anlass}: Der Ableser hat get_usage nicht gelesen und den Auftrag verlassen; die Bremse misst nicht. Seine Werkzeugaufrufe im Journal unter ▸ verbrauch ansehen.`)
|
log(`WARNUNG Verbrauch ${args.anlass}: Der Ableser hat get_usage nicht gelesen und den Auftrag verlassen; die Bremse misst nicht. Seine Werkzeugaufrufe im Journal unter ▸ verbrauch ansehen.`)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ title: "Workflow-Prüfung: Attrappe, Fälle und Messreihe zum Verbrauch"
|
|||||||
summary: "Node-Attrappe für die gespeicherten Workflows des Vaults mit Fällen je Workflow, dazu verbrauch_reihe.py, das aus den Workflow-Protokollen rechnet, wie viel vom 5-Stunden-Fenster und von der Woche zwischen zwei Verbrauchsablesungen verbraucht wurde. Entstanden 2026-09-24."
|
summary: "Node-Attrappe für die gespeicherten Workflows des Vaults mit Fällen je Workflow, dazu verbrauch_reihe.py, das aus den Workflow-Protokollen rechnet, wie viel vom 5-Stunden-Fenster und von der Woche zwischen zwei Verbrauchsablesungen verbraucht wurde. Entstanden 2026-09-24."
|
||||||
tags: [vault-tools, codeworkflow]
|
tags: [vault-tools, codeworkflow]
|
||||||
created: 2026-09-24
|
created: 2026-09-24
|
||||||
updated: 2026-10-16
|
updated: 2026-10-17
|
||||||
---
|
---
|
||||||
|
|
||||||
# Workflow-Prüfung
|
# Workflow-Prüfung
|
||||||
@@ -21,7 +21,7 @@ Die Regel dazu steht in [[AI/Regelwerk/codeworkflow|codeworkflow.md]], Abschnitt
|
|||||||
|
|
||||||
## Was die Attrappe nachbildet
|
## Was die Attrappe nachbildet
|
||||||
|
|
||||||
`attrappe.mjs` führt ein Workflow-Skript mit nachgebauten Globals aus, genauso streng wie das Laufzeitsystem: `workflow()` im Kind wirft, im Kind ist `phase()` wirkungslos und jeder Agent trägt die Phase `▸ <name>` (ab dem zweiten Aufruf desselben Kindes `▸ <name> #<n>`), ein unbekannter Workflow wirft, `Date.now()`, `new Date()` ohne Argument und `Math.random()` werfen. Ein Schema ohne `properties` wirft, ein Ergebnis ohne die `required`-Felder wird zu `null`, und ein `agent()` ohne `model` ist ein Verstoß. Aufrufe über `D:/Claude/.claude/workflows/...` leitet sie in den Ordner unter Test um. Sie zählt, wie viele Agenten je Phase und Modell gleichzeitig laufen.
|
`attrappe.mjs` führt ein Workflow-Skript mit nachgebauten Globals aus, genauso streng wie das Laufzeitsystem: `workflow()` im Kind wirft, im Kind ist `phase()` wirkungslos und jeder Agent trägt die Phase `▸ <name>` (ab dem zweiten Aufruf desselben Kindes `▸ <name> #<n>`), ein unbekannter Workflow wirft, `Date.now()`, `new Date()` ohne Argument und `Math.random()` werfen. Ein Schema ohne `properties` wirft, ein Ergebnis ohne die `required`-Felder wird zu `null`, und ein `agent()` ohne `model` ist ein Verstoß. Ein `agentType` ohne Agentendatei unter `D:/Claude/.claude/agents/` wirft vor dem Start, wie im Ziel (Echtlauf 2026-10-17); Fälle können die Liste über `optionen.agenten` vorgeben. Aufrufe über `D:/Claude/.claude/workflows/...` leitet sie in den Ordner unter Test um. Sie zählt, wie viele Agenten je Phase und Modell gleichzeitig laufen.
|
||||||
|
|
||||||
**Was sie nicht sieht:** die Obergrenze gleichzeitiger Agenten je Lauf, Budget, Resume und das Verhalten echter Modelle. Wie lange echte Linsen bei drei gleichzeitigen Opus-Agenten laufen und wie viel Verbrauch sie erzeugen, zeigt nur ein echter Lauf.
|
**Was sie nicht sieht:** die Obergrenze gleichzeitiger Agenten je Lauf, Budget, Resume und das Verhalten echter Modelle. Wie lange echte Linsen bei drei gleichzeitigen Opus-Agenten laufen und wie viel Verbrauch sie erzeugen, zeigt nur ein echter Lauf.
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ Stand 2026-09-24 nach Plankritik, Prüfung und Nachbesserung. Die Zahlen gelten
|
|||||||
- **Erster Eichwert** am Abend des 2026-09-24, Plan laut App „Pro“: `verbrauch_reihe.py --seit 2026-09-24` ergab 8,1 % des 5-Stunden-Fensters je Opus-Agentenstunde (5 Paare, 1,7 Opus-Stunden, darunter ein Plankritik-Lauf der Trading-Sitzung). Die Woche stieg in denselben Paaren um 5 Punkte. Der Verbrauch anderer Sitzungen und der Hauptsitzung steckt mit drin; die Zahl ist also eher zu hoch. Für das Nachstellen der Stufen reicht ein Abend nicht.
|
- **Erster Eichwert** am Abend des 2026-09-24, Plan laut App „Pro“: `verbrauch_reihe.py --seit 2026-09-24` ergab 8,1 % des 5-Stunden-Fensters je Opus-Agentenstunde (5 Paare, 1,7 Opus-Stunden, darunter ein Plankritik-Lauf der Trading-Sitzung). Die Woche stieg in denselben Paaren um 5 Punkte. Der Verbrauch anderer Sitzungen und der Hauptsitzung steckt mit drin; die Zahl ist also eher zu hoch. Für das Nachstellen der Stufen reicht ein Abend nicht.
|
||||||
|
|
||||||
- **Ableser verlässt den Auftrag** (2026-10-16, Plan `docs/plans/2026-10-16-ableser-abweichung/`): Zweimal bearbeitete der Haiku-Ableser die Nutzeranfrage statt `get_usage` zu lesen und schrieb seine Antwort in `status` und `plan` (wf_7ca17029-421, wf_80616e8e-0e3). `verbrauch.js` meldet diese Form jetzt als `status: abgewichen` mit einer Logzeile, die mit `WARNUNG` beginnt; Plätze wie ohne Ablesung. Neun neue Fälle, sieben davon gegen den alten Stand rot; die zwei Gegenproben (ehrlicher Fehlschlag, `not_applicable`) bleiben auf beiden Ständen grün. Acht neue Mutanten, alle gefangen; der volle Mutantenlauf brauchte knapp 50 Minuten. Die drei echten Antworten aus den Journalen ergaben `abgewichen`, `abgewichen` und `nicht_gelesen`. **Ungemessen:** ob der neue Brückensatz im Auftrag einen echten Ableser vom Abweichen abhält; der Fall misst nur, dass der Satz dasteht. Ein abweichender Ableser hat weiterhin alle Werkzeuge.
|
- **Ableser verlässt den Auftrag** (2026-10-16, Plan `docs/plans/2026-10-16-ableser-abweichung/`): Zweimal bearbeitete der Haiku-Ableser die Nutzeranfrage statt `get_usage` zu lesen und schrieb seine Antwort in `status` und `plan` (wf_7ca17029-421, wf_80616e8e-0e3). `verbrauch.js` meldet diese Form jetzt als `status: abgewichen` mit einer Logzeile, die mit `WARNUNG` beginnt; Plätze wie ohne Ablesung. Neun neue Fälle, sieben davon gegen den alten Stand rot; die zwei Gegenproben (ehrlicher Fehlschlag, `not_applicable`) bleiben auf beiden Ständen grün. Acht neue Mutanten, alle gefangen; der volle Mutantenlauf brauchte knapp 50 Minuten. Die drei echten Antworten aus den Journalen ergaben `abgewichen`, `abgewichen` und `nicht_gelesen`. **Ungemessen:** ob der neue Brückensatz im Auftrag einen echten Ableser vom Abweichen abhält; der Fall misst nur, dass der Satz dasteht. Ein abweichender Ableser hat weiterhin alle Werkzeuge.
|
||||||
|
- **Ableser als Agententyp** (2026-10-17, Task 02 desselben Plans): Der Ableser läuft als `agentType: verbrauch-ableser` (Haiku, `effort: low`, nur ToolSearch und `get_usage`). Ein Startfehler, etwa ein in der Sitzung noch unbekannter Typ, kommt als `nicht_gelesen` mit `WARNUNG` zurück, nie als Rückfall auf einen Agenten mit allen Werkzeugen. Echtläufe: Vor der Registrierung warf `agent()` „agent type 'verbrauch-ableser' not found“ ohne Agent (`wf_63cb007d-71b`); danach lasen vier von vier Ablesern richtig, mit Effort `low` und nur ToolSearch, `get_usage` und StructuredOutput (`wf_1901f51f-dd3`, `wf_4d8a69f9-51a`). Die Sitzung kannte die neue Agentendatei nach etwa zehn Minuten. 261 Fälle grün, drei neue gegen den alten Stand rot; 157 Mutanten, alle gefangen oder als wirkungslos geführt. **Ungemessen:** ein Ableser, der eine abweichende Nutzeranfrage sieht. Alle vier Echtläufe starteten in Zügen ohne Nutzernachricht, der Harness reichte keine Anfrage durch.
|
||||||
|
|
||||||
**Offen:** Die Trading-Sitzung hat `plankritik.js` am 2026-09-24 nach einem echten Lauf nachgebessert (Ausgabe der Planwerkzeuge nur bei `EXIT 0`). Für diese Stelle gibt es noch keinen Fall.
|
**Offen:** Die Trading-Sitzung hat `plankritik.js` am 2026-09-24 nach einem echten Lauf nachgebessert (Ausgabe der Planwerkzeuge nur bei `EXIT 0`). Für diese Stelle gibt es noch keinen Fall.
|
||||||
|
|
||||||
|
|||||||
@@ -14,16 +14,25 @@
|
|||||||
// - agent() mit schema braucht type object und properties, required muss in properties stehen.
|
// - agent() mit schema braucht type object und properties, required muss in properties stehen.
|
||||||
// Ein Ergebnis ohne die required-Felder wird wie ein gescheiterter Agent zu null.
|
// Ein Ergebnis ohne die required-Felder wird wie ein gescheiterter Agent zu null.
|
||||||
// - parallel() ist eine Barriere; ein werfender Thunk wird zu null.
|
// - parallel() ist eine Barriere; ein werfender Thunk wird zu null.
|
||||||
|
// - agent() mit einem agentType, den die Sitzung nicht kennt, wirft vor dem Start (CLI-Abbild
|
||||||
|
// 2.1.299). Bekannt sind die Agentendateien unter .claude/agents/ des Vaults oder optionen.agenten.
|
||||||
// Zusätzlich als Verstoß protokolliert (Stamm, "Agenten & Modelle"): agent() ohne model.
|
// Zusätzlich als Verstoß protokolliert (Stamm, "Agenten & Modelle"): agent() ohne model.
|
||||||
// Was die Attrappe nicht nachbildet: die Obergrenze gleichzeitiger Agenten je Lauf, Budget,
|
// Was die Attrappe nicht nachbildet: die Obergrenze gleichzeitiger Agenten je Lauf, Budget,
|
||||||
// Resume und das Verhalten echter Modelle. Sie ist ein Fangnetz für die Steuerlogik der
|
// Resume und das Verhalten echter Modelle. Sie ist ein Fangnetz für die Steuerlogik der
|
||||||
// Skripte, kein Nachweis für einen echten Lauf.
|
// Skripte, kein Nachweis für einen echten Lauf.
|
||||||
import { readFileSync, existsSync } from 'node:fs'
|
import { readFileSync, existsSync, readdirSync } from 'node:fs'
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
|
||||||
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor
|
||||||
// Aufrufe über den echten Pfad werden in den Ordner unter Test umgeleitet.
|
// Aufrufe über den echten Pfad werden in den Ordner unter Test umgeleitet.
|
||||||
const ECHTER_ORDNER = 'D:/Claude/.claude/workflows/'
|
const ECHTER_ORDNER = 'D:/Claude/.claude/workflows/'
|
||||||
|
// Agententypen wie in der Sitzung: die Agentendateien des Vaults neben den Workflows. Fehlt der
|
||||||
|
// Ordner, ist jeder agentType unbekannt und jeder Fall damit rot: ein Befund, kein Durchlass.
|
||||||
|
const AGENTEN_ORDNER = path.join(ECHTER_ORDNER, '..', 'agents')
|
||||||
|
function agentenDesVaults() {
|
||||||
|
if (!existsSync(AGENTEN_ORDNER)) return []
|
||||||
|
return readdirSync(AGENTEN_ORDNER).filter(n => n.endsWith('.md')).map(n => n.slice(0, -3))
|
||||||
|
}
|
||||||
|
|
||||||
class DatumAttrappe extends Date {
|
class DatumAttrappe extends Date {
|
||||||
constructor(...a) {
|
constructor(...a) {
|
||||||
@@ -51,6 +60,7 @@ const warte = ms => new Promise(r => setTimeout(r, ms))
|
|||||||
// aufruf trägt label, phase, modell, effort, agentType, prompt, tiefe und schema.
|
// aufruf trägt label, phase, modell, effort, agentType, prompt, tiefe und schema.
|
||||||
export async function fuehreAus(datei, args, antworte, optionen = {}) {
|
export async function fuehreAus(datei, args, antworte, optionen = {}) {
|
||||||
const ordner = optionen.ordner || path.dirname(datei)
|
const ordner = optionen.ordner || path.dirname(datei)
|
||||||
|
const agenten = optionen.agenten || agentenDesVaults()
|
||||||
const protokoll = { agents: [], logs: [], phasen: [], verstoesse: [], spitze: {}, spitzeJePhase: {}, workflows: [] }
|
const protokoll = { agents: [], logs: [], phasen: [], verstoesse: [], spitze: {}, spitzeJePhase: {}, workflows: [] }
|
||||||
const laufend = {}
|
const laufend = {}
|
||||||
const laufendJePhase = {}
|
const laufendJePhase = {}
|
||||||
@@ -82,6 +92,7 @@ export async function fuehreAus(datei, args, antworte, optionen = {}) {
|
|||||||
if (!opts.schema || opts.schema.type !== 'object' || !opts.schema.properties) throw new Error('Schema braucht type object und properties: ' + opts.label)
|
if (!opts.schema || opts.schema.type !== 'object' || !opts.schema.properties) throw new Error('Schema braucht type object und properties: ' + opts.label)
|
||||||
if ((opts.schema.required || []).some(k => !(k in opts.schema.properties))) throw new Error('required nicht in properties: ' + opts.label)
|
if ((opts.schema.required || []).some(k => !(k in opts.schema.properties))) throw new Error('required nicht in properties: ' + opts.label)
|
||||||
}
|
}
|
||||||
|
if (opts.agentType != null && !agenten.includes(String(opts.agentType))) throw new Error(`agent({agentType}): agent type '${opts.agentType}' not found. Available agents: ${agenten.join(', ')}`)
|
||||||
const modell = opts.model || '(erbt)'
|
const modell = opts.model || '(erbt)'
|
||||||
const phase = kindPhase || opts.phase || aktuellePhase || '(ohne)'
|
const phase = kindPhase || opts.phase || aktuellePhase || '(ohne)'
|
||||||
const eintrag = { label: opts.label || null, phase, modell, effort: opts.effort, agentType: opts.agentType, prompt, tiefe, start: ++uhr, ende: null, ergebnis: undefined }
|
const eintrag = { label: opts.label || null, phase, modell, effort: opts.effort, agentType: opts.agentType, prompt, tiefe, start: ++uhr, ende: null, ergebnis: undefined }
|
||||||
|
|||||||
+3
-2
@@ -5,7 +5,7 @@ status: active
|
|||||||
summary: "Nach dem Plan erkennt verbrauch.js an status und plan, dass der Haiku-Ableser statt get_usage die Nutzeranfrage bearbeitet hat, und meldet das als status abgewichen mit Warnzeile; der Auftrag ordnet die Ablesung ausdrücklich der Nutzeranfrage zu. Pascals Wahl vom 2026-10-16: Möglichkeiten 1 und 2."
|
summary: "Nach dem Plan erkennt verbrauch.js an status und plan, dass der Haiku-Ableser statt get_usage die Nutzeranfrage bearbeitet hat, und meldet das als status abgewichen mit Warnzeile; der Auftrag ordnet die Ablesung ausdrücklich der Nutzeranfrage zu. Pascals Wahl vom 2026-10-16: Möglichkeiten 1 und 2."
|
||||||
tags: [plan, vault-tools, codeworkflow]
|
tags: [plan, vault-tools, codeworkflow]
|
||||||
created: 2026-10-16
|
created: 2026-10-16
|
||||||
updated: 2026-10-16
|
updated: 2026-10-17
|
||||||
---
|
---
|
||||||
|
|
||||||
Projekt: [LIESMICH](../../../LIESMICH.md)
|
Projekt: [LIESMICH](../../../LIESMICH.md)
|
||||||
@@ -42,9 +42,10 @@ Projekt: [LIESMICH](../../../LIESMICH.md)
|
|||||||
| Nr. | Datei | Thema | Modell |
|
| Nr. | Datei | Thema | Modell |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 01 | [task-01-erkennung.md](task-01-erkennung.md) | Erkennung, Brückensatz, Fälle, Mutanten | `sonnet` |
|
| 01 | [task-01-erkennung.md](task-01-erkennung.md) | Erkennung, Brückensatz, Fälle, Mutanten | `sonnet` |
|
||||||
|
| 02 | [task-02-agententyp.md](task-02-agententyp.md) | Ableser als Agententyp `verbrauch-ableser`, Startfehler abfangen, Attrappe prüft `agentType` | `sonnet` |
|
||||||
|
|
||||||
## Nicht im Umfang
|
## Nicht im Umfang
|
||||||
|
|
||||||
- Eigener Agententyp für den Ableser mit nur ToolSearch und `get_usage`: eigener Schritt nach einem Echtlauf.
|
- Task 02 (Möglichkeit 5, Agententyp mit nur ToolSearch und `get_usage`) kam am 2026-10-17 auf Pascals Auftrag dazu. Die Agentendatei und die Zeile in der Rollentabelle legt der Koordinator an, und vor Task 02 läuft ein Echtlauf über das Workflow-Werkzeug.
|
||||||
- `LIESMICH.md`, Erinnerung und Sitzungsprotokoll schreibt der Koordinator.
|
- `LIESMICH.md`, Erinnerung und Sitzungsprotokoll schreibt der Koordinator.
|
||||||
- Kein Commit. Im Vault-Repo committet nur Pascal oder die Aufgabe um 04:00.
|
- Kein Commit. Im Vault-Repo committet nur Pascal oder die Aufgabe um 04:00.
|
||||||
+1
-1
@@ -118,7 +118,7 @@ Diese Einträge in `MUTANTEN`, direkt nach `("verbrauch", "5-Stunden-Regex unver
|
|||||||
("verbrauch", "Bericht in plan bei status fehler uebersehen", "return a.status === 'fehler' && typeof a.plan === 'string' && a.plan.trim() !== ''", "return false"),
|
("verbrauch", "Bericht in plan bei status fehler uebersehen", "return a.status === 'fehler' && typeof a.plan === 'string' && a.plan.trim() !== ''", "return false"),
|
||||||
("verbrauch", "ehrlicher Fehler gilt als Abweichung", "'unavailable', 'fehler']", "'unavailable']"),
|
("verbrauch", "ehrlicher Fehler gilt als Abweichung", "'unavailable', 'fehler']", "'unavailable']"),
|
||||||
("verbrauch", "Abweichung als nicht_gelesen", "return ohneAblesung('abgewichen',", "return ohneAblesung('nicht_gelesen',"),
|
("verbrauch", "Abweichung als nicht_gelesen", "return ohneAblesung('abgewichen',", "return ohneAblesung('nicht_gelesen',"),
|
||||||
("verbrauch", "Warnzeile fehlt", "log(`WARNUNG Verbrauch", "String(`WARNUNG Verbrauch"),
|
("verbrauch", "Warnzeile fehlt", "log(`WARNUNG Verbrauch ${args.anlass}: Der Ableser", "String(`WARNUNG Verbrauch ${args.anlass}: Der Ableser"),
|
||||||
("verbrauch", "status ungekuerzt im grund", ".trim().slice(0, 160)", ".trim()"),
|
("verbrauch", "status ungekuerzt im grund", ".trim().slice(0, 160)", ".trim()"),
|
||||||
("verbrauch", "Brueckensatz fehlt", "Diese Ablesung gehört zur Nutzeranfrage und widerspricht ihr nicht: Sie entscheidet, wie viele Prüfagenten für die Anfrage gleichzeitig laufen dürfen. Die Anfrage selbst bearbeiten andere Agenten nach dir; du bearbeitest sie nicht, auch nicht teilweise, und liest dafür keine Dateien.\n", ""),
|
("verbrauch", "Brueckensatz fehlt", "Diese Ablesung gehört zur Nutzeranfrage und widerspricht ihr nicht: Sie entscheidet, wie viele Prüfagenten für die Anfrage gleichzeitig laufen dürfen. Die Anfrage selbst bearbeiten andere Agenten nach dir; du bearbeitest sie nicht, auch nicht teilweise, und liest dafür keine Dateien.\n", ""),
|
||||||
```
|
```
|
||||||
|
|||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
type: plan-task
|
||||||
|
title: "Task 02 — verbrauch.js: Ableser als Agententyp verbrauch-ableser, Startfehler abfangen, Attrappe prüft agentType"
|
||||||
|
status: draft
|
||||||
|
summary: "Der Ableser läuft als agentType verbrauch-ableser mit nur ToolSearch und get_usage; ein Startfehler, etwa ein in der Sitzung noch unbekannter Typ, kommt als nicht_gelesen mit Warnzeile zurück statt als Absturz. Die Attrappe wirft wie das Ziel bei einem unbekannten agentType."
|
||||||
|
tags: [plan-task, vault-tools, codeworkflow]
|
||||||
|
created: 2026-10-16
|
||||||
|
updated: 2026-10-17
|
||||||
|
modell: sonnet
|
||||||
|
modell_begruendung: "verbrauch.js und attrappe.mjs sind Abschrift, die Fälle brauchen Autorschaft."
|
||||||
|
---
|
||||||
|
|
||||||
|
Übersicht: [00-uebersicht.md](00-uebersicht.md)
|
||||||
|
|
||||||
|
# Task 02 — Agententyp verbrauch-ableser
|
||||||
|
|
||||||
|
Pascals Auftrag vom 2026-10-17: Möglichkeit 5 umsetzen. Die Agentendatei `D:/Claude/.claude/agents/verbrauch-ableser.md` und die Zeile in der Rollentabelle des Stamms hat der Koordinator angelegt. Voraussetzung ist der Stand von `verbrauch.js` nach Task 01 (Erkennung `abgewichen`, Brückensatz, `kurz`); er ist seit dem 2026-10-17 eingespielt.
|
||||||
|
|
||||||
|
## Arbeitsorte
|
||||||
|
|
||||||
|
Wie Task 01, `S` gleich. Du schreibst **nur**:
|
||||||
|
|
||||||
|
- `S/wf-neu/verbrauch.js`
|
||||||
|
- `S/wp-neu/attrappe.mjs`
|
||||||
|
- `S/wp-neu/faelle_workflows.mjs`
|
||||||
|
- `S/wp-neu/mutanten_workflows.py`
|
||||||
|
|
||||||
|
Nichts unter `D:/Claude/` anfassen. Keine Hilfsdateien. Kein Commit.
|
||||||
|
|
||||||
|
## Belege
|
||||||
|
|
||||||
|
- Ziel, CLI-Abbild 2.1.299 (gelesen, nicht gelaufen): `agent()` mit einem `agentType`, den die Sitzung nicht kennt, wirft `agent({agentType}): agent type '<typ>' not found. Available agents: ...`, bevor ein Agent startet.
|
||||||
|
- Stamm, „Agenten & Modelle": Mit `agentType` liefert dessen Frontmatter den Effort; ein `effort`-Parameter daneben steht nur für eine bewusste Abweichung.
|
||||||
|
- Echtlauf 2026-10-17, `wf_63cb007d-71b`: Solange die Sitzung den Typ nicht kannte, warf `agent()` genau diese Meldung, nach 145 ms und ohne Agent.
|
||||||
|
- Echtlauf 2026-10-17, `wf_1901f51f-dd3`, drei Ablesungen mit `agentType: 'verbrauch-ableser'`: Alle drei liefen als `verbrauch-ableser` auf `claude-haiku-5-5` mit Effort `low` und riefen nur ToolSearch, `get_usage` und StructuredOutput auf. Als nachladbares Werkzeug war nur `get_usage` angeboten. Die Werte stimmten mit der eigenen Ablesung des Koordinators überein (36 % und 53 %). Der Lauf startete in einem Zug ohne Nutzernachricht, der Ableser sah also keine abweichende Nutzeranfrage.
|
||||||
|
|
||||||
|
## 1 verbrauch.js
|
||||||
|
|
||||||
|
**1a, Kopfkommentar:** Vor der Zeile `// Was das Skript nicht sieht: Agenten, die nach der Ablesung in anderen Sitzungen starten;` kommen diese Zeilen:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// - Wer abliest: der Agententyp verbrauch-ableser (.claude/agents/), Haiku mit effort low und
|
||||||
|
// nur ToolSearch und get_usage, damit ein Ableser, der der Nutzeranfrage folgt, nichts anderes
|
||||||
|
// tun kann. Kennt die Sitzung den Typ nicht, wirft agent() vor dem Start (CLI-Abbild 2.1.299);
|
||||||
|
// das kommt als nicht_gelesen mit WARNUNG zurück, nie als Rückfall auf einen Agenten mit allen
|
||||||
|
// Werkzeugen.
|
||||||
|
```
|
||||||
|
|
||||||
|
**1b, Aufruf:** Die Zeile
|
||||||
|
|
||||||
|
```js
|
||||||
|
ablesung = await agent(AUFTRAG, { label: 'verbrauch:' + args.anlass, phase: 'Verbrauch', schema: ABLESUNG, model: 'haiku' })
|
||||||
|
```
|
||||||
|
|
||||||
|
wird zu
|
||||||
|
|
||||||
|
```js
|
||||||
|
try {
|
||||||
|
ablesung = await agent(AUFTRAG, { label: 'verbrauch:' + args.anlass, phase: 'Verbrauch', schema: ABLESUNG, agentType: 'verbrauch-ableser', model: 'haiku' })
|
||||||
|
} catch (e) {
|
||||||
|
ablesung = null
|
||||||
|
startfehler = kurz((e && e.message) || e)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**1c, Variable:** Die Zeile `let ablesung = args.probe` wird zu zwei Zeilen:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let ablesung = args.probe
|
||||||
|
let startfehler = null
|
||||||
|
```
|
||||||
|
|
||||||
|
**1d, Rückgabe:** Vor der Zeile `if (!ablesung || typeof ablesung !== 'object') return ohneAblesung('nicht_gelesen', 'kein Ergebnis vom Ableseagenten')` kommt:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (startfehler) {
|
||||||
|
log(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar (${startfehler}); die Bremse misst nicht.`)
|
||||||
|
return ohneAblesung('nicht_gelesen', 'Ableser nicht startbar: ' + startfehler)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`kurz` ist in Task 01 vor `pruefeArgs(args)` angelegt und damit vor dem Aufruf definiert. Sonst änderst du an `verbrauch.js` nichts.
|
||||||
|
|
||||||
|
## 2 attrappe.mjs
|
||||||
|
|
||||||
|
Die Attrappe wirft wie das Ziel bei einem unbekannten `agentType`. Bekannt sind die Namen der Dateien `*.md` in `D:/Claude/.claude/agents/`, abgeleitet aus `ECHTER_ORDNER`, oder eine Liste aus `optionen.agenten`. Nicht relativ zur Attrappe, weil sie im Prüfstand auch als Kopie im Scratchpad läuft.
|
||||||
|
|
||||||
|
**2a, Kopfkommentar:** Nach der Zeile `// - parallel() ist eine Barriere; ein werfender Thunk wird zu null.` kommt:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// - agent() mit einem agentType, den die Sitzung nicht kennt, wirft vor dem Start (CLI-Abbild
|
||||||
|
// 2.1.299). Bekannt sind die Agentendateien unter .claude/agents/ des Vaults oder optionen.agenten.
|
||||||
|
```
|
||||||
|
|
||||||
|
**2b, Import:** `import { readFileSync, existsSync } from 'node:fs'` wird zu `import { readFileSync, existsSync, readdirSync } from 'node:fs'`.
|
||||||
|
|
||||||
|
**2c, Liste:** Nach der Zeile `const ECHTER_ORDNER = 'D:/Claude/.claude/workflows/'` kommt:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Agententypen wie in der Sitzung: die Agentendateien des Vaults neben den Workflows. Fehlt der
|
||||||
|
// Ordner, ist jeder agentType unbekannt und jeder Fall damit rot: ein Befund, kein Durchlass.
|
||||||
|
const AGENTEN_ORDNER = path.join(ECHTER_ORDNER, '..', 'agents')
|
||||||
|
function agentenDesVaults() {
|
||||||
|
if (!existsSync(AGENTEN_ORDNER)) return []
|
||||||
|
return readdirSync(AGENTEN_ORDNER).filter(n => n.endsWith('.md')).map(n => n.slice(0, -3))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2d, in `fuehreAus`:** Nach der Zeile `const ordner = optionen.ordner || path.dirname(datei)` kommt `const agenten = optionen.agenten || agentenDesVaults()`.
|
||||||
|
|
||||||
|
**2e, in `agent()`:** Nach dem Block, der das Schema prüft (er endet mit der schließenden `}` nach `throw new Error('required nicht in properties: ' + opts.label)`), kommt:
|
||||||
|
|
||||||
|
```js
|
||||||
|
if (opts.agentType != null && !agenten.includes(String(opts.agentType))) throw new Error(`agent({agentType}): agent type '${opts.agentType}' not found. Available agents: ${agenten.join(', ')}`)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3 faelle_workflows.mjs
|
||||||
|
|
||||||
|
| Abschnitt | Name | Eingabe | Erwartung |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `attrappe` | `unbekannter agentType wirft vor dem Start` | Skript mit `agent('x', { label: 'werkzeug:planwerkzeuge', model: 'sonnet', agentType: 'gibt-es-nicht' })` | Fehler passt auf `/agent type 'gibt-es-nicht' not found/`, `p.agents.length === 0` |
|
||||||
|
| `attrappe` | `bekannter agentType aus optionen.agenten läuft` | dasselbe mit `agentType: 'eigen'` und `fuehreAus(..., { ordner: TMP, agenten: ['eigen'] })` | kein Fehler, ein Agent mit `agentType 'eigen'` |
|
||||||
|
| `verbrauch` | Grundlinie ergänzen | bestehender Fall `Grundlinie: Agent liest 10/10, ...` | zusätzlich `p.agents[0].agentType === 'verbrauch-ableser'` und `p.agents[0].effort === undefined` |
|
||||||
|
| `verbrauch` | `Ableser-Typ unbekannt: nicht_gelesen mit Warnung, kein Absturz` | `fuehreAus(WF('verbrauch'), { anlass: 't', wunsch: 3 }, antworten({}), { ordner: ORDNER, agenten: ['planer'] })` | `fehler === null`, `status 'nicht_gelesen'`, `plaetze 2`, `grund` passt auf `/nicht startbar/` und auf `/not found/`, genau eine Warnzeile, kein Agent gestartet |
|
||||||
|
| `plankritik` | `Ableser-Typ unbekannt: Linsen zu zweit, Plankritik läuft` | `PK(PK_ARGS, {}, { agenten: ['pruefer'] })` | `e.status 'fertig'`, Spitze `Plankritik`/`opus` gleich 2, `e.verbrauch.every(v => v.status === 'nicht_gelesen')`, drei Warnzeilen |
|
||||||
|
|
||||||
|
Für den letzten Fall nimmt `PK` die Optionen schon als drittes Argument entgegen.
|
||||||
|
|
||||||
|
## 4 mutanten_workflows.py
|
||||||
|
|
||||||
|
Nach den Einträgen aus Task 01:
|
||||||
|
|
||||||
|
```python
|
||||||
|
("verbrauch", "Ableser ohne Agententyp", "agentType: 'verbrauch-ableser', ", ""),
|
||||||
|
("verbrauch", "Startfehler nicht abgefangen", " ablesung = null\n startfehler = kurz((e && e.message) || e)\n", " throw e\n"),
|
||||||
|
("verbrauch", "Startfehler ohne Warnzeile", "log(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar", "String(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar"),
|
||||||
|
("verbrauch", "Ableser erbt Sitzungseffort statt Frontmatter", "agentType: 'verbrauch-ableser', model: 'haiku' }", "agentType: 'verbrauch-ableser', model: 'haiku', effort: 'max' }"),
|
||||||
|
```
|
||||||
|
|
||||||
|
**Korrektur nach dem Lauf (2026-10-17):** Der Block 1d beginnt wie die Warnzeile aus Task 01, das Muster des Mutanten „Warnzeile fehlt“ kam dadurch zweimal vor (VERALTET). Es lautet jetzt ``log(`WARNUNG Verbrauch ${args.anlass}: Der Ableser``; Task 01 ist mitgezogen.
|
||||||
|
|
||||||
|
## Prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Fälle gegen den neuen Stand
|
||||||
|
node "$S/wp-neu/faelle_workflows.mjs" "$S/wf-neu"; echo "EXIT $?"
|
||||||
|
# Rotlauf der neuen verbrauch- und plankritik-Fälle gegen den Stand nach Task 01 ist nicht nötig; Rotlauf gegen den alten Stand
|
||||||
|
node "$S/wp-neu/faelle_workflows.mjs" "D:/Claude/.claude/workflows" | grep -v "^OK"; echo "EXIT ${PIPESTATUS[0]}"
|
||||||
|
# Mutanten: kein UEBERLEBT, kein VERALTET
|
||||||
|
python "$S/wp-neu/mutanten_workflows.py" "$S/wf-neu" | grep -v "^GEFANGEN"; echo "EXIT ${PIPESTATUS[0]}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Melde am Ende: geänderte Dateien, die drei Ausgaben, und **was du anders gemacht hast als beauftragt und warum**.
|
||||||
|
|
||||||
@@ -146,6 +146,22 @@ fall('attrappe', 'agent() ohne model ist ein Verstoß', async () => {
|
|||||||
return fehler
|
return fehler
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fall('attrappe', 'unbekannter agentType wirft vor dem Start', async () => {
|
||||||
|
const { fehler, soll } = pruefe()
|
||||||
|
const { fehler: f, protokoll: p } = await A(skript('typ-unbekannt', "return await agent('x', { label: 'werkzeug:planwerkzeuge', model: 'sonnet', agentType: 'gibt-es-nicht' })"))
|
||||||
|
soll(f && /agent type 'gibt-es-nicht' not found/.test(f), 'Fehler: ' + f)
|
||||||
|
soll(p.agents.length === 0, 'Agent gestartet trotz unbekanntem Typ: ' + p.agents.length)
|
||||||
|
return fehler
|
||||||
|
})
|
||||||
|
|
||||||
|
fall('attrappe', 'bekannter agentType aus optionen.agenten läuft', async () => {
|
||||||
|
const { fehler, soll } = pruefe()
|
||||||
|
const { fehler: f, protokoll: p } = await fuehreAus(skript('typ-bekannt', "return await agent('x', { label: 'werkzeug:planwerkzeuge', model: 'sonnet', agentType: 'eigen' })"), {}, antworten({}), { ordner: TMP, agenten: ['eigen'] })
|
||||||
|
soll(f === null, 'Fehler: ' + f)
|
||||||
|
soll(p.agents.length === 1 && p.agents[0].agentType === 'eigen', 'Agenten: ' + JSON.stringify(p.agents.map(x => x.agentType)))
|
||||||
|
return fehler
|
||||||
|
})
|
||||||
|
|
||||||
fall('attrappe', 'Ergebnis ohne required-Feld wird null und ist ein Verstoß', async () => {
|
fall('attrappe', 'Ergebnis ohne required-Feld wird null und ist ein Verstoß', async () => {
|
||||||
const { fehler, soll } = pruefe()
|
const { fehler, soll } = pruefe()
|
||||||
const schema = "{ type: 'object', properties: { a: { type: 'string' }, b: { type: 'string' } }, required: ['a', 'b'] }"
|
const schema = "{ type: 'object', properties: { a: { type: 'string' }, b: { type: 'string' } }, required: ['a', 'b'] }"
|
||||||
@@ -171,6 +187,7 @@ fall('verbrauch', 'Grundlinie: Agent liest 10/10, wunsch 3 ergibt 3 Plätze', as
|
|||||||
soll(f === null, 'Fehler: ' + f)
|
soll(f === null, 'Fehler: ' + f)
|
||||||
soll(e && e.status === 'ok' && e.plaetze === 3 && e.gebremst === false && e.quelle === 'agent', 'Ergebnis: ' + JSON.stringify(e))
|
soll(e && e.status === 'ok' && e.plaetze === 3 && e.gebremst === false && e.quelle === 'agent', 'Ergebnis: ' + JSON.stringify(e))
|
||||||
soll(p.agents.length === 1 && p.agents[0].label === 'verbrauch:test' && p.agents[0].modell === 'haiku', 'Agent: ' + JSON.stringify(p.agents.map(x => [x.label, x.modell])))
|
soll(p.agents.length === 1 && p.agents[0].label === 'verbrauch:test' && p.agents[0].modell === 'haiku', 'Agent: ' + JSON.stringify(p.agents.map(x => [x.label, x.modell])))
|
||||||
|
soll(p.agents[0].agentType === 'verbrauch-ableser' && p.agents[0].effort === undefined, 'Typ/Effort: ' + JSON.stringify([p.agents[0].agentType, p.agents[0].effort]))
|
||||||
ohneVerstoss(p, soll)
|
ohneVerstoss(p, soll)
|
||||||
return fehler
|
return fehler
|
||||||
})
|
})
|
||||||
@@ -313,6 +330,16 @@ fall('verbrauch', 'Auftrag ordnet die Ablesung der Nutzeranfrage zu', async () =
|
|||||||
return fehler
|
return fehler
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fall('verbrauch', 'Ableser-Typ unbekannt: nicht_gelesen mit Warnung, kein Absturz', async () => {
|
||||||
|
const { fehler, soll } = pruefe()
|
||||||
|
const { ergebnis: e, fehler: f, protokoll: p } = await fuehreAus(WF('verbrauch'), { anlass: 't', wunsch: 3 }, antworten({}), { ordner: ORDNER, agenten: ['planer'] })
|
||||||
|
soll(f === null, 'Fehler: ' + f)
|
||||||
|
soll(e && e.status === 'nicht_gelesen' && e.plaetze === 2 && /nicht startbar/.test(e.grund) && /not found/.test(e.grund), JSON.stringify(e))
|
||||||
|
soll(warnung(p).length === 1, 'Warnzeilen: ' + JSON.stringify(warnung(p)))
|
||||||
|
soll(p.agents.length === 0, 'Agenten gestartet: ' + p.agents.length)
|
||||||
|
return fehler
|
||||||
|
})
|
||||||
|
|
||||||
for (const [name, probe] of [
|
for (const [name, probe] of [
|
||||||
['status unavailable', { verfuegbar: true, status: 'unavailable', plan: '', fenster: [], fehler: '' }],
|
['status unavailable', { verfuegbar: true, status: 'unavailable', plan: '', fenster: [], fehler: '' }],
|
||||||
['verfuegbar false', { verfuegbar: false, status: 'fehler', plan: '', fenster: [], fehler: 'Werkzeug fehlt' }],
|
['verfuegbar false', { verfuegbar: false, status: 'fehler', plan: '', fenster: [], fehler: 'Werkzeug fehlt' }],
|
||||||
@@ -479,6 +506,16 @@ fall('plankritik', 'Ablesung ohne Ergebnis: Linsen zu zweit (alte Obergrenze)',
|
|||||||
return fehler
|
return fehler
|
||||||
})
|
})
|
||||||
|
|
||||||
|
fall('plankritik', 'Ableser-Typ unbekannt: Linsen zu zweit, Plankritik läuft', async () => {
|
||||||
|
const { fehler, soll } = pruefe()
|
||||||
|
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, {}, { agenten: ['pruefer'] })
|
||||||
|
soll(e && e.status === 'fertig', 'status ' + (e && e.status))
|
||||||
|
soll(spitze(p, 'Plankritik', 'opus') === 2, 'Linsen gleichzeitig: ' + spitze(p, 'Plankritik', 'opus'))
|
||||||
|
soll(e && e.verbrauch.every(v => v.status === 'nicht_gelesen'), JSON.stringify(e && e.verbrauch.map(v => v.status)))
|
||||||
|
soll(warnung(p).length === 3, 'Warnzeilen: ' + JSON.stringify(warnung(p)))
|
||||||
|
return fehler
|
||||||
|
})
|
||||||
|
|
||||||
fall('plankritik', 'Abweichung bei der ersten Ablesung: Linsen zu zweit, Status abgewichen im Ergebnis', async () => {
|
fall('plankritik', 'Abweichung bei der ersten Ablesung: Linsen zu zweit, Status abgewichen im Ergebnis', async () => {
|
||||||
const { fehler, soll } = pruefe()
|
const { fehler, soll } = pruefe()
|
||||||
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, { ablesungen: [BEANTWORTET] })
|
const { ergebnis: e, protokoll: p } = await PK(PK_ARGS, { ablesungen: [BEANTWORTET] })
|
||||||
|
|||||||
@@ -82,9 +82,13 @@ MUTANTEN = [
|
|||||||
("verbrauch", "Bericht in plan bei status fehler uebersehen", "return a.status === 'fehler' && typeof a.plan === 'string' && a.plan.trim() !== ''", "return false"),
|
("verbrauch", "Bericht in plan bei status fehler uebersehen", "return a.status === 'fehler' && typeof a.plan === 'string' && a.plan.trim() !== ''", "return false"),
|
||||||
("verbrauch", "ehrlicher Fehler gilt als Abweichung", "'unavailable', 'fehler']", "'unavailable']"),
|
("verbrauch", "ehrlicher Fehler gilt als Abweichung", "'unavailable', 'fehler']", "'unavailable']"),
|
||||||
("verbrauch", "Abweichung als nicht_gelesen", "return ohneAblesung('abgewichen',", "return ohneAblesung('nicht_gelesen',"),
|
("verbrauch", "Abweichung als nicht_gelesen", "return ohneAblesung('abgewichen',", "return ohneAblesung('nicht_gelesen',"),
|
||||||
("verbrauch", "Warnzeile fehlt", "log(`WARNUNG Verbrauch", "String(`WARNUNG Verbrauch"),
|
("verbrauch", "Warnzeile fehlt", "log(`WARNUNG Verbrauch ${args.anlass}: Der Ableser", "String(`WARNUNG Verbrauch ${args.anlass}: Der Ableser"),
|
||||||
("verbrauch", "status ungekuerzt im grund", ".trim().slice(0, 160)", ".trim()"),
|
("verbrauch", "status ungekuerzt im grund", ".trim().slice(0, 160)", ".trim()"),
|
||||||
("verbrauch", "Brueckensatz fehlt", "Diese Ablesung gehört zur Nutzeranfrage und widerspricht ihr nicht: Sie entscheidet, wie viele Prüfagenten für die Anfrage gleichzeitig laufen dürfen. Die Anfrage selbst bearbeiten andere Agenten nach dir; du bearbeitest sie nicht, auch nicht teilweise, und liest dafür keine Dateien.\n", ""),
|
("verbrauch", "Brueckensatz fehlt", "Diese Ablesung gehört zur Nutzeranfrage und widerspricht ihr nicht: Sie entscheidet, wie viele Prüfagenten für die Anfrage gleichzeitig laufen dürfen. Die Anfrage selbst bearbeiten andere Agenten nach dir; du bearbeitest sie nicht, auch nicht teilweise, und liest dafür keine Dateien.\n", ""),
|
||||||
|
("verbrauch", "Ableser ohne Agententyp", "agentType: 'verbrauch-ableser', ", ""),
|
||||||
|
("verbrauch", "Startfehler nicht abgefangen", " ablesung = null\n startfehler = kurz((e && e.message) || e)\n", " throw e\n"),
|
||||||
|
("verbrauch", "Startfehler ohne Warnzeile", "log(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar", "String(`WARNUNG Verbrauch ${args.anlass}: Ableser nicht startbar"),
|
||||||
|
("verbrauch", "Ableser erbt Sitzungseffort statt Frontmatter", "agentType: 'verbrauch-ableser', model: 'haiku' }", "agentType: 'verbrauch-ableser', model: 'haiku', effort: 'max' }"),
|
||||||
("plankritik", "Muster-Satz fehlt (N5)", "musterModus\n ? ' Kein Mutant ist gefahren; OK heißt hier nur, dass das Muster genau einmal in der Datei steht; ob ein Test fällt, ist unbelegt.'\n : ", ""),
|
("plankritik", "Muster-Satz fehlt (N5)", "musterModus\n ? ' Kein Mutant ist gefahren; OK heißt hier nur, dass das Muster genau einmal in der Datei steht; ob ein Test fällt, ist unbelegt.'\n : ", ""),
|
||||||
("plankritik", "Exit 2 wird wie 0 behandelt (N7)", "exitCode === 2 || exitCode === 3", "false"),
|
("plankritik", "Exit 2 wird wie 0 behandelt (N7)", "exitCode === 2 || exitCode === 3", "false"),
|
||||||
("quorum", "Mehrheit schon bei einer Stimme", "mehrheit: beteiligt.length >= 2", "mehrheit: beteiligt.length >= 1"),
|
("quorum", "Mehrheit schon bei einer Stimme", "mehrheit: beteiligt.length >= 2", "mehrheit: beteiligt.length >= 1"),
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
type: resource
|
type: resource
|
||||||
title: "Arbeiten mit Claude Haiku 5.5"
|
title: "Arbeiten mit Claude Haiku 5.5"
|
||||||
status: reference
|
status: pending-review
|
||||||
summary: "Haiku 5.5 als Abschrift-Executer, Quorum-Ersatzstimme, Verbrauchsablesung und Sitzungsmodell: Effort je Rolle nach Messung vom 2026-10-09, Prüfpunkte, offene Entscheidungen, Verhalten und Prompt-Muster, Spezifikation und API-Brüche."
|
summary: "Haiku 5.5 als Abschrift-Executer, Quorum-Ersatzstimme, Verbrauchsablesung und Sitzungsmodell: Effort je Rolle nach Messung vom 2026-10-09, Prüfpunkte, offene Entscheidungen, Verhalten und Prompt-Muster, Spezifikation und API-Brüche."
|
||||||
tags: [regelwerk, modelle, haiku, prompting]
|
tags: [regelwerk, modelle, haiku, prompting]
|
||||||
created: 2026-10-09
|
created: 2026-10-09
|
||||||
updated: 2026-10-09
|
updated: 2026-10-17
|
||||||
source: "https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-haiku-5-5"
|
source: "https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-haiku-5-5"
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ source: "https://platform.claude.com/docs/en/build-with-claude/prompt-engineerin
|
|||||||
> **Geltungsbereich — zuerst lesen.**
|
> **Geltungsbereich — zuerst lesen.**
|
||||||
> - Diese Datei gilt für **Claude Haiku 5.5** (`claude-haiku-5-5`). Läufst du selbst als Haiku 5.5, ob als Hauptsitzung oder als Subagent, sind Teil A und Teil B deine Arbeitsweise.
|
> - Diese Datei gilt für **Claude Haiku 5.5** (`claude-haiku-5-5`). Läufst du selbst als Haiku 5.5, ob als Hauptsitzung oder als Subagent, sind Teil A und Teil B deine Arbeitsweise.
|
||||||
> - Schreibt ein Koordinator einen Auftrag an einen Haiku-Subagenten, nimmt er Teil A. Für Fable 5.1 als Koordinator ist das verbindlich, Opus 5.5 entscheidet selbst.
|
> - Schreibt ein Koordinator einen Auftrag an einen Haiku-Subagenten, nimmt er Teil A. Für Fable 5.1 als Koordinator ist das verbindlich, Opus 5.5 entscheidet selbst.
|
||||||
> - Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]], „Agenten & Modelle") nennt Haiku 5.5 für Abschrift, Quorum-Ersatz und die Haiku-Sitzung (A5); die Zuordnung der übrigen Rollen ändert sich nicht. Haiku 4.5 beschreibt [[AI/Regelwerk/sonnet-5|sonnet-5.md]], C3.
|
> - Die Rollentabelle im Stamm ([[CLAUDE|Claude.md]], „Agenten & Modelle") nennt Haiku 5.5 für Abschrift, Quorum-Ersatz, die Verbrauchsablesung (`verbrauch-ableser`) und die Haiku-Sitzung (A5); die Zuordnung der übrigen Rollen ändert sich nicht. Haiku 4.5 beschreibt [[AI/Regelwerk/sonnet-5|sonnet-5.md]], C3.
|
||||||
> - Angeordnet von Pascal am 2026-10-09. Quellen: Anthropic-Doku (Rohtext am 2026-10-09 per curl abgerufen), Modellkatalog und Systemprompt-Bausteine im Programmabbild von Claude Code 2.1.296 (gelesen, nicht gelaufen), die Transkripte des Vaults und eine eigene Messung (A3). Wo die Doku eine Bedingung nennt, steht sie hier mit.
|
> - Angeordnet von Pascal am 2026-10-09. Quellen: Anthropic-Doku (Rohtext am 2026-10-09 per curl abgerufen), Modellkatalog und Systemprompt-Bausteine im Programmabbild von Claude Code 2.1.296 (gelesen, nicht gelaufen), die Transkripte des Vaults und eine eigene Messung (A3). Wo die Doku eine Bedingung nennt, steht sie hier mit.
|
||||||
|
|
||||||
## Wozu diese Datei
|
## Wozu diese Datei
|
||||||
@@ -24,7 +24,7 @@ Seit dem 2026-10-07 läuft alles, was im Vault `haiku` heißt, auf Haiku 5.5 —
|
|||||||
| Beobachtung | Abschnitt |
|
| Beobachtung | Abschnitt |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Unklar, mit welchem Effort ein Haiku-Agent läuft oder laufen soll | A1, A2 |
|
| Unklar, mit welchem Effort ein Haiku-Agent läuft oder laufen soll | A1, A2 |
|
||||||
| Die Ablesung in `verbrauch` läuft mit `max` | A1, A7 |
|
| Die Ablesung in `verbrauch` lief bis 2026-10-17 mit `max` | A1, A7 |
|
||||||
| Task mit `modell: haiku` und typografischen Zeichen | A3, A4 |
|
| Task mit `modell: haiku` und typografischen Zeichen | A3, A4 |
|
||||||
| Pascal startet eine Sitzung auf Haiku | A5 |
|
| Pascal startet eine Sitzung auf Haiku | A5 |
|
||||||
| Haiku-Executer meldet „fertig" ohne echte Prüfung | B3 |
|
| Haiku-Executer meldet „fertig" ohne echte Prüfung | B3 |
|
||||||
@@ -53,7 +53,7 @@ Zwei Sorten von Punkten sind gemischt: Manche greifen **in dieser Claude-Code-Si
|
|||||||
| `executer` bei `modell: haiku` (Abschrift) | `executer-welle`, `agentType: executer`, ohne `effort` | `medium` aus dem Frontmatter des Executers | Transkripte 2026-10-09; Messung A3 |
|
| `executer` bei `modell: haiku` (Abschrift) | `executer-welle`, `agentType: executer`, ohne `effort` | `medium` aus dem Frontmatter des Executers | Transkripte 2026-10-09; Messung A3 |
|
||||||
| Task mit `modell: haiku` und eigenem `effort:` | `executer-welle` | trotzdem `medium`: `effortFuer()` verwirft den Task-Effort und loggt ihn als „wirkungslos" | `.claude/workflows/executer-welle.js` |
|
| Task mit `modell: haiku` und eigenem `effort:` | `executer-welle` | trotzdem `medium`: `effortFuer()` verwirft den Task-Effort und loggt ihn als „wirkungslos" | `.claude/workflows/executer-welle.js` |
|
||||||
| `quorum-stimme` als Ersatz für Codex | `quorum`, `agentType: quorum-stimme`, ohne `effort` | `high` aus dem Frontmatter | Messung A3 |
|
| `quorum-stimme` als Ersatz für Codex | `quorum`, `agentType: quorum-stimme`, ohne `effort` | `high` aus dem Frontmatter | Messung A3 |
|
||||||
| Ablesung in `verbrauch` | `verbrauch.js`, ohne `agentType` und ohne `effort` | der Effort der aufrufenden Sitzung, am 2026-10-09 `max` oder `xhigh` | Transkripte 2026-10-09 |
|
| Ablesung in `verbrauch` | `verbrauch.js`, `agentType: verbrauch-ableser`, ohne `effort` | `low` aus dem Frontmatter; bis 2026-10-17 ohne `agentType` der Effort der aufrufenden Sitzung, am 2026-10-09 `max` oder `xhigh` | Echtläufe 2026-10-17 (`wf_1901f51f-dd3`, `wf_4d8a69f9-51a`); Transkripte 2026-10-09 |
|
||||||
| Hauptsitzung | Modellwahl in der App | Sitzungseinstellung, Standard `medium` | Transkripte 2026-10-09; Katalog |
|
| Hauptsitzung | Modellwahl in der App | Sitzungseinstellung, Standard `medium` | Transkripte 2026-10-09; Katalog |
|
||||||
|
|
||||||
## A2 Effort je Rolle
|
## A2 Effort je Rolle
|
||||||
@@ -62,7 +62,7 @@ Zwei Sorten von Punkten sind gemischt: Manche greifen **in dieser Claude-Code-Si
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `executer`, Abschrift | `medium` (Frontmatter, unverändert) | Messung: `low`, `medium` und `high` gleich treu, je Lauf 0,008–0,009 $; `low` sparte keine Ausgabe. Doku: `medium` ist der Start für agentisches Coding; auf `low` hört Haiku in langen Agent-Prompts eher früh auf, auf `low` und `medium` meldet es Änderungen teils ohne Prüfung als fertig (B2, B3). |
|
| `executer`, Abschrift | `medium` (Frontmatter, unverändert) | Messung: `low`, `medium` und `high` gleich treu, je Lauf 0,008–0,009 $; `low` sparte keine Ausgabe. Doku: `medium` ist der Start für agentisches Coding; auf `low` hört Haiku in langen Agent-Prompts eher früh auf, auf `low` und `medium` meldet es Änderungen teils ohne Prüfung als fertig (B2, B3). |
|
||||||
| `quorum-stimme`, Ersatz | `high` (Frontmatter, unverändert) | Messung: 9 von 9 auf jeder Stufe; `high` brachte je Lauf zwei berechtigte Zusatzfunde, `low` einen, bei höchstens 0,010 $ je Lauf. Doku: `high` für strenges Befolgen von Anweisungen. |
|
| `quorum-stimme`, Ersatz | `high` (Frontmatter, unverändert) | Messung: 9 von 9 auf jeder Stufe; `high` brachte je Lauf zwei berechtigte Zusatzfunde, `low` einen, bei höchstens 0,010 $ je Lauf. Doku: `high` für strenges Befolgen von Anweisungen. |
|
||||||
| Ablesung in `verbrauch` | `low` (angenommen, Umsetzung offen, A7) | Feste Ablesung ohne Urteil; Doku: `low` für kurze Werkzeugaufgaben. Nicht gemessen. |
|
| Ablesung in `verbrauch` | `low` (Frontmatter von `verbrauch-ableser`, seit 2026-10-17) | Feste Ablesung ohne Urteil; Doku: `low` für kurze Werkzeugaufgaben. Echtlauf 2026-10-17: vier von vier Ablesungen richtig, je drei Werkzeugaufrufe, Werte gleich der eigenen Ablesung. |
|
||||||
| Hauptsitzung | `medium` (Standard) | Doku: Startpunkt für die meiste Arbeit; `xhigh` und `max` nur mit Gewinn in eigenen Evals, dann auch Sonnet 5.5 vergleichen. Nicht gemessen. |
|
| Hauptsitzung | `medium` (Standard) | Doku: Startpunkt für die meiste Arbeit; `xhigh` und `max` nur mit Gewinn in eigenen Evals, dann auch Sonnet 5.5 vergleichen. Nicht gemessen. |
|
||||||
|
|
||||||
## A3 Messung vom 2026-10-09
|
## A3 Messung vom 2026-10-09
|
||||||
@@ -168,7 +168,7 @@ Pascal hat die Vorschläge am 2026-10-09 angenommen; umgesetzt werden sie als ei
|
|||||||
|
|
||||||
| Stelle | Befund | Umsetzung |
|
| Stelle | Befund | Umsetzung |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `verbrauch.js`: Haiku-Ablesung ohne `effort` | läuft mit dem Effort der Sitzung, am 2026-10-09 `max` | `effort: 'low'` setzen und die Fälle in `faelle_workflows.mjs` um den Effort ergänzen; als eigene Aufgabe angelegt |
|
| `verbrauch.js`: Haiku-Ablesung ohne `effort` | lief mit dem Effort der Sitzung, am 2026-10-09 `max` | **Erledigt am 2026-10-17:** Der Ableser läuft als `agentType: verbrauch-ableser` mit `effort: low` im Frontmatter; die Grundlinie in `faelle_workflows.mjs` prüft Typ und fehlenden `effort`-Parameter |
|
||||||
| `executer-welle.js`: Task-Effort bei `haiku` verworfen, Logzeile „wirkungslos"; Kommentare „etwa low für Abschrift mit typografischen Zeichen" und „Haiku kennt keinen Effort" | stimmt seit Haiku 5.5 nicht mehr | Effort durchreichen wie bei Sonnet, Kommentar und Logzeile berichtigen; dieselbe Aufgabe |
|
| `executer-welle.js`: Task-Effort bei `haiku` verworfen, Logzeile „wirkungslos"; Kommentare „etwa low für Abschrift mit typografischen Zeichen" und „Haiku kennt keinen Effort" | stimmt seit Haiku 5.5 nicht mehr | Effort durchreichen wie bei Sonnet, Kommentar und Logzeile berichtigen; dieselbe Aufgabe |
|
||||||
| `plankritik.js`, Linse „abdeckung": verlangt für Abschrift mit typografischen Zeichen `modell sonnet` und `effort low` statt `haiku` | würde jeden Plan nach der neuen Regel in Schritt 3 als Fehler melden | Linsentext an die Entscheidung vom 2026-10-09 anpassen, Fälle der Plankritik mitziehen; dieselbe Aufgabe |
|
| `plankritik.js`, Linse „abdeckung": verlangt für Abschrift mit typografischen Zeichen `modell sonnet` und `effort low` statt `haiku` | würde jeden Plan nach der neuen Regel in Schritt 3 als Fehler melden | Linsentext an die Entscheidung vom 2026-10-09 anpassen, Fälle der Plankritik mitziehen; dieselbe Aufgabe |
|
||||||
| Codex-Hilfsagenten im Quorum auf `sonnet` statt `haiku` (Kommentar in `quorum.js`) | Die Begründung beruht auf Haiku 4.5; die Ausnahme ist für Haiku 5.5 seit 2026-10-09 aufgehoben | Kommentar berichtigen; ob die Hilfsagenten auf `haiku` wechseln, gehört in dieselbe Aufgabe |
|
| Codex-Hilfsagenten im Quorum auf `sonnet` statt `haiku` (Kommentar in `quorum.js`) | Die Begründung beruht auf Haiku 4.5; die Ausnahme ist für Haiku 5.5 seit 2026-10-09 aufgehoben | Kommentar berichtigen; ob die Hilfsagenten auf `haiku` wechseln, gehört in dieselbe Aufgabe |
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ Rollen sind fest auf Modelle verteilt. **Beim Starten eines Subagenten immer das
|
|||||||
| Quorum am Meilenstein, nur lesend | `quorum-stimme` | Opus 5.5 (`opus`), im Workflow `quorum` auch `sonnet`, Ersatz `haiku` | `high` (Frontmatter), auch für Haiku 5.5 |
|
| Quorum am Meilenstein, nur lesend | `quorum-stimme` | Opus 5.5 (`opus`), im Workflow `quorum` auch `sonnet`, Ersatz `haiku` | `high` (Frontmatter), auch für Haiku 5.5 |
|
||||||
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5.5 (`sonnet`), bei reiner Abschrift Haiku 5.5 (`haiku`) | `medium` (Frontmatter), auch für Haiku 5.5 |
|
| Umsetzen (Code schreiben, ändern, testen) | `executer` | Sonnet 5.5 (`sonnet`), bei reiner Abschrift Haiku 5.5 (`haiku`) | `medium` (Frontmatter), auch für Haiku 5.5 |
|
||||||
| Recherche, Suche, Erkundung | `recherche` | Sonnet 5.5 (`sonnet`) | `medium` (Frontmatter) |
|
| Recherche, Suche, Erkundung | `recherche` | Sonnet 5.5 (`sonnet`) | `medium` (Frontmatter) |
|
||||||
|
| Verbrauch ablesen, nur im Workflow `verbrauch` | `verbrauch-ableser` | Haiku 5.5 (`haiku`); Werkzeuge nur ToolSearch und `get_usage` | `low` (Frontmatter) |
|
||||||
|
|
||||||
Die Aliasse `opus` und `sonnet` lösen in Claude Code 2.1.285 auf `claude-opus-5-5` bzw. `claude-sonnet-5-5` auf (Modellkatalog im Programm, beide nachgelesen 2026-09-29). `haiku` löst laut Transkripten seit 2.1.293 auf `claude-haiku-5-5` auf, das anders als Haiku 4.5 Effort kennt und ihn aus dem Frontmatter oder der Sitzung bekommt (nachgelesen 2026-10-09 in 2.1.296): [[AI/Regelwerk/haiku-5-5|haiku-5-5.md]]. Effort `medium` für den `executer` auf Sonnet 5.5 ist am 2026-09-29 gemessen, für `recherche` nicht: [[AI/Regelwerk/sonnet-5-5|sonnet-5-5.md]], A1. Der eingebaute Agent `Explore` lädt diese Datei nicht und kennt die Vault-Regeln deshalb nicht — nur für reine Dateisuche; `general-purpose` erbt den Sitzungs-Effort und ist durch `recherche` ersetzt. Wann Haiku statt Sonnet, die Belege dazu und die Zuordnung der Superpowers-Skills: [[AI/Regelwerk/codeworkflow|codeworkflow.md]].
|
Die Aliasse `opus` und `sonnet` lösen in Claude Code 2.1.285 auf `claude-opus-5-5` bzw. `claude-sonnet-5-5` auf (Modellkatalog im Programm, beide nachgelesen 2026-09-29). `haiku` löst laut Transkripten seit 2.1.293 auf `claude-haiku-5-5` auf, das anders als Haiku 4.5 Effort kennt und ihn aus dem Frontmatter oder der Sitzung bekommt (nachgelesen 2026-10-09 in 2.1.296): [[AI/Regelwerk/haiku-5-5|haiku-5-5.md]]. Effort `medium` für den `executer` auf Sonnet 5.5 ist am 2026-09-29 gemessen, für `recherche` nicht: [[AI/Regelwerk/sonnet-5-5|sonnet-5-5.md]], A1. Der eingebaute Agent `Explore` lädt diese Datei nicht und kennt die Vault-Regeln deshalb nicht — nur für reine Dateisuche; `general-purpose` erbt den Sitzungs-Effort und ist durch `recherche` ersetzt. Wann Haiku statt Sonnet, die Belege dazu und die Zuordnung der Superpowers-Skills: [[AI/Regelwerk/codeworkflow|codeworkflow.md]].
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user