Elemente: Aufgabe/Lösung-Felder entfernt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
team3
2026-06-12 07:44:39 +02:00
parent 54eaa1c89b
commit 693475128c
9 changed files with 23 additions and 72 deletions

View File

@@ -42,8 +42,6 @@ CREATE TABLE IF NOT EXISTS elements (
description TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '',
examples TEXT NOT NULL DEFAULT '[]', examples TEXT NOT NULL DEFAULT '[]',
hints TEXT NOT NULL DEFAULT '[]', hints TEXT NOT NULL DEFAULT '[]',
aufgabe TEXT NOT NULL DEFAULT '',
loesung TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
updated_at TEXT NOT NULL updated_at TEXT NOT NULL
) )
@@ -70,11 +68,6 @@ async def init_db():
await db.execute("ALTER TABLE guides ADD COLUMN step INTEGER") await db.execute("ALTER TABLE guides ADD COLUMN step INTEGER")
except aiosqlite.OperationalError: except aiosqlite.OperationalError:
pass pass
for col in ("aufgabe", "loesung"): # Migration für Elemente ohne Aufgabe/Lösung
try:
await db.execute(f"ALTER TABLE elements ADD COLUMN {col} TEXT NOT NULL DEFAULT ''")
except aiosqlite.OperationalError:
pass
await db.execute( await db.execute(
"UPDATE guides SET status = 'error', progress = NULL, error_msg = 'Server-Neustart' " "UPDATE guides SET status = 'error', progress = NULL, error_msg = 'Server-Neustart' "
"WHERE status IN ('queued', 'generating')" "WHERE status IN ('queued', 'generating')"
@@ -173,8 +166,8 @@ def _element_row(row, cursor) -> dict:
async def create_element(element: dict) -> dict: async def create_element(element: dict) -> dict:
db = await get_db() db = await get_db()
await db.execute( await db.execute(
"""INSERT INTO elements (id, topic, title, description, examples, hints, aufgabe, loesung, created_at, updated_at) """INSERT INTO elements (id, topic, title, description, examples, hints, created_at, updated_at)
VALUES (:id, :topic, :title, :description, :examples, :hints, :aufgabe, :loesung, :created_at, :updated_at)""", VALUES (:id, :topic, :title, :description, :examples, :hints, :created_at, :updated_at)""",
{**element, "examples": json.dumps(element["examples"], ensure_ascii=False), {**element, "examples": json.dumps(element["examples"], ensure_ascii=False),
"hints": json.dumps(element["hints"], ensure_ascii=False)}, "hints": json.dumps(element["hints"], ensure_ascii=False)},
) )

View File

@@ -1396,8 +1396,6 @@ def _element_fields(data: dict) -> dict | None:
"description": str(data.get("description", "")).strip(), "description": str(data.get("description", "")).strip(),
"examples": listen["examples"], "examples": listen["examples"],
"hints": listen["hints"], "hints": listen["hints"],
"aufgabe": str(data.get("aufgabe", "")).strip(),
"loesung": str(data.get("loesung", "")).strip(),
} }
@@ -1420,7 +1418,7 @@ def _topic_context(topic: str, limit: int = 12000) -> str:
async def generate_element(topic: str, hint: str, provider: str = DEFAULT_PROVIDER) -> dict: async def generate_element(topic: str, hint: str, provider: str = DEFAULT_PROVIDER) -> dict:
"""Erstellt Element-Felder per KI. Fallback: nur Titel aus dem Stichwort.""" """Erstellt Element-Felder per KI. Fallback: nur Titel aus dem Stichwort."""
fallback = {"title": hint.strip() or "Neues Element", "description": "", "examples": [], "hints": [], "aufgabe": "", "loesung": ""} fallback = {"title": hint.strip() or "Neues Element", "description": "", "examples": [], "hints": []}
try: try:
prompt = _prompt( prompt = _prompt(
"Element-Create", "Element-Create",
@@ -1456,7 +1454,7 @@ def _parse_suggestions(stdout: str) -> list[dict] | None:
text = str(s.get("text", "")).strip() text = str(s.get("text", "")).strip()
target = s.get("target") target = s.get("target")
content = str(s.get("content", "")).strip() content = str(s.get("content", "")).strip()
if text and content and target in ("description", "examples", "hints", "aufgabe", "loesung"): if text and content and target in ("description", "examples", "hints"):
if target == "examples": if target == "examples":
content = _fence(content) content = _fence(content)
suggestions.append({"text": text, "target": target, "content": content}) suggestions.append({"text": text, "target": target, "content": content})
@@ -1467,7 +1465,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list
"""Zweischrittige Prüfung auf fehlende Infos: Recherche → Verifizieren. None bei Fehler.""" """Zweischrittige Prüfung auf fehlende Infos: Recherche → Verifizieren. None bei Fehler."""
try: try:
element_json = json.dumps( element_json = json.dumps(
{k: element[k] for k in ("title", "description", "examples", "hints", "aufgabe", "loesung")}, {k: element[k] for k in ("title", "description", "examples", "hints")},
ensure_ascii=False, indent=1, ensure_ascii=False, indent=1,
) )
context = _topic_context(element["topic"]) context = _topic_context(element["topic"])
@@ -1504,7 +1502,7 @@ async def check_element(element: dict, provider: str = DEFAULT_PROVIDER) -> list
def _element_json(element: dict) -> str: def _element_json(element: dict) -> str:
return json.dumps( return json.dumps(
{k: element[k] for k in ("title", "description", "examples", "hints", "aufgabe", "loesung")}, {k: element[k] for k in ("title", "description", "examples", "hints")},
ensure_ascii=False, indent=1, ensure_ascii=False, indent=1,
) )
@@ -1520,7 +1518,7 @@ def _validate_change(c, element: dict) -> dict | None:
content = str(c.get("content", "")).strip() content = str(c.get("content", "")).strip()
if not text or action not in ("entfernen", "anpassen", "hinzufuegen"): if not text or action not in ("entfernen", "anpassen", "hinzufuegen"):
return None return None
if target not in ("title", "description", "examples", "hints", "aufgabe", "loesung"): if target not in ("title", "description", "examples", "hints"):
return None return None
if action in ("anpassen", "hinzufuegen") and not content: if action in ("anpassen", "hinzufuegen") and not content:
return None return None

View File

@@ -86,8 +86,6 @@ class ElementResponse(BaseModel):
description: str = "" description: str = ""
examples: list[str] = [] examples: list[str] = []
hints: list[str] = [] hints: list[str] = []
aufgabe: str = ""
loesung: str = ""
created_at: str created_at: str
updated_at: str updated_at: str
@@ -103,8 +101,6 @@ class ElementUpdateRequest(BaseModel):
description: str | None = None description: str | None = None
examples: list[str] | None = None examples: list[str] | None = None
hints: list[str] | None = None hints: list[str] | None = None
aufgabe: str | None = None
loesung: str | None = None
class ElementCheckRequest(BaseModel): class ElementCheckRequest(BaseModel):
@@ -113,7 +109,7 @@ class ElementCheckRequest(BaseModel):
class ElementSuggestion(BaseModel): class ElementSuggestion(BaseModel):
text: str text: str
target: Literal["description", "examples", "hints", "aufgabe", "loesung"] target: Literal["description", "examples", "hints"]
content: str content: str
@@ -124,7 +120,7 @@ class ElementCheckResponse(BaseModel):
class ElementStyleChange(BaseModel): class ElementStyleChange(BaseModel):
text: str text: str
action: Literal["entfernen", "anpassen", "hinzufuegen"] action: Literal["entfernen", "anpassen", "hinzufuegen"]
target: Literal["title", "description", "examples", "hints", "aufgabe", "loesung"] target: Literal["title", "description", "examples", "hints"]
index: int | None = None index: int | None = None
content: str = "" content: str = ""

View File

@@ -18,7 +18,7 @@ const query = ref('')
const creating = ref(false) const creating = ref(false)
const selected = ref(null) const selected = ref(null)
const tab = ref('overview') // 'overview' | 'chat' | 'edit' const tab = ref('overview') // 'overview' | 'chat' | 'edit'
const edit = ref({ title: '', description: '', examples: [], hints: [], aufgabe: '', loesung: '' }) const edit = ref({ title: '', description: '', examples: [], hints: [] })
const savingEdit = ref(false) const savingEdit = ref(false)
watch(() => props.topic, load, { immediate: true }) watch(() => props.topic, load, { immediate: true })
@@ -93,8 +93,6 @@ function loadEdit() {
description: selected.value.description, description: selected.value.description,
examples: [...selected.value.examples], examples: [...selected.value.examples],
hints: [...selected.value.hints], hints: [...selected.value.hints],
aufgabe: selected.value.aufgabe || '',
loesung: selected.value.loesung || '',
} }
} }
@@ -112,8 +110,6 @@ async function saveEdit() {
description: edit.value.description, description: edit.value.description,
examples: edit.value.examples.filter((s) => s.trim()), examples: edit.value.examples.filter((s) => s.trim()),
hints: edit.value.hints.filter((s) => s.trim()), hints: edit.value.hints.filter((s) => s.trim()),
aufgabe: edit.value.aufgabe,
loesung: edit.value.loesung,
}) })
selected.value = updated selected.value = updated
const idx = elements.value.findIndex((e) => e.id === updated.id) const idx = elements.value.findIndex((e) => e.id === updated.id)
@@ -250,19 +246,17 @@ async function applyStyleChange(i) {
const c = styleChanges.value[i] const c = styleChanges.value[i]
applyingStyle.value = true applyingStyle.value = true
try { try {
const STRING_TARGETS = ['title', 'description', 'aufgabe', 'loesung'] const STRING_TARGETS = ['title', 'description']
const fields = { const fields = {
title: selected.value.title, title: selected.value.title,
description: selected.value.description, description: selected.value.description,
examples: [...selected.value.examples], examples: [...selected.value.examples],
hints: [...selected.value.hints], hints: [...selected.value.hints],
aufgabe: selected.value.aufgabe || '',
loesung: selected.value.loesung || '',
} }
if (c.action === 'entfernen') fields[c.target].splice(c.index, 1) if (c.action === 'entfernen') fields[c.target].splice(c.index, 1)
else if (c.action === 'hinzufuegen') { else if (c.action === 'hinzufuegen') {
if (c.target === 'title') fields.title = c.content if (c.target === 'title') fields.title = c.content
else if (c.target === 'description' || c.target === 'aufgabe' || c.target === 'loesung') else if (c.target === 'description')
fields[c.target] = fields[c.target] ? fields[c.target] + '\n\n' + c.content : c.content fields[c.target] = fields[c.target] ? fields[c.target] + '\n\n' + c.content : c.content
else fields[c.target].push(c.content) else fields[c.target].push(c.content)
} else if (STRING_TARGETS.includes(c.target)) fields[c.target] = c.content } else if (STRING_TARGETS.includes(c.target)) fields[c.target] = c.content
@@ -448,25 +442,6 @@ async function send() {
/> />
</div> </div>
<div v-if="selected.aufgabe || styleAt('aufgabe').length || styleAdds('aufgabe').length" class="el-task-block">
<h4>Aufgabe</h4>
<div v-if="selected.aufgabe" class="el-entry markdown" v-html="renderMarkdown(selected.aufgabe)"></div>
<ElementSuggestion
v-for="[ci, c] in [...styleAt('aufgabe'), ...styleAdds('aufgabe')]"
:key="'sga' + ci" :change="c" :busy="suggBusy(ci)"
@apply="applyStyleChange(ci)" @dismiss="dismissStyleChange(ci)" @refine="(t) => refineChange(ci, t)"
/>
</div>
<div v-if="selected.loesung || styleAt('loesung').length || styleAdds('loesung').length" class="el-task-block">
<h4>Lösung</h4>
<div v-if="selected.loesung" class="el-entry markdown" v-html="renderMarkdown(selected.loesung)"></div>
<ElementSuggestion
v-for="[ci, c] in [...styleAt('loesung'), ...styleAdds('loesung')]"
:key="'sgl' + ci" :change="c" :busy="suggBusy(ci)"
@apply="applyStyleChange(ci)" @dismiss="dismissStyleChange(ci)" @refine="(t) => refineChange(ci, t)"
/>
</div>
<div v-if="checking || styling || statusMsg" class="el-check"> <div v-if="checking || styling || statusMsg" class="el-check">
<p v-if="checking" class="check-empty busy-text">Prüft auf fehlende Infos</p> <p v-if="checking" class="check-empty busy-text">Prüft auf fehlende Infos</p>
<p v-if="styling" class="check-empty busy-text">Prüft den Stil</p> <p v-if="styling" class="check-empty busy-text">Prüft den Stil</p>
@@ -522,12 +497,6 @@ async function send() {
<button class="edit-del" title="Entfernen" @click="edit.hints.splice(i, 1)">×</button> <button class="edit-del" title="Entfernen" @click="edit.hints.splice(i, 1)">×</button>
</div> </div>
<button class="edit-add" @click="edit.hints.push('')">+ Hinweis</button> <button class="edit-add" @click="edit.hints.push('')">+ Hinweis</button>
<label>Aufgabenstellung</label>
<textarea v-model="edit.aufgabe" placeholder="Aufgabenstellung"></textarea>
<label>Aufgabenlösung</label>
<textarea v-model="edit.loesung" placeholder="Aufgabenlösung"></textarea>
</div> </div>
</template> </template>
</aside> </aside>
@@ -786,13 +755,11 @@ async function send() {
color: var(--text); color: var(--text);
} }
.el-hints-block, .el-hints-block {
.el-task-block {
margin-top: 0.9rem; margin-top: 0.9rem;
} }
.el-hints-block h4, .el-hints-block h4 {
.el-task-block h4 {
margin: 0 0 0.35rem; margin: 0 0 0.35rem;
font-size: 0.72rem; font-size: 0.72rem;
font-weight: 700; font-weight: 700;

View File

@@ -17,8 +17,8 @@ Umfang: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH. Markdown: `inline-code` fü
Jeder Vorschlag: Jeder Vorschlag:
- text: kurz, was geändert wird (max. 12 Wörter, reiner Text) - text: kurz, was geändert wird (max. 12 Wörter, reiner Text)
- action: "entfernen" | "anpassen" | "hinzufuegen" - action: "entfernen" | "anpassen" | "hinzufuegen"
- target: "title" | "description" | "examples" | "hints" | "aufgabe" | "loesung" - target: "title" | "description" | "examples" | "hints"
- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description/aufgabe/loesung und hinzufuegen: null) - index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description und hinzufuegen: null)
- content: der neue vollständige Inhalt (bei entfernen: leer) - content: der neue vollständige Inhalt (bei entfernen: leer)
"entfernen" nur für examples/hints. Nur Vorschläge machen, die die Nutzer-Anweisung verlangt. "entfernen" nur für examples/hints. Nur Vorschläge machen, die die Nutzer-Anweisung verlangt.

View File

@@ -6,12 +6,12 @@ AKTUELLES ELEMENT (JSON):
KONTEXT (Auszüge aus dem Themen-Material): KONTEXT (Auszüge aus dem Themen-Material):
{context} {context}
RECHERCHE — sammle breit alle Kandidaten: fehlende Kernaussagen, wichtige Varianten, typische Stolperfallen, Best Practices. Fehlt eine Übungsaufgabe (aufgabe) oder deren Lösung (loesung), schlage sie vor. Lieber einen Kandidaten zu viel als einen zu wenig — die Bewertung passiert in einem zweiten Schritt. Nichts vorschlagen, was das Element schon enthält. RECHERCHE — sammle breit alle Kandidaten: fehlende Kernaussagen, wichtige Varianten, typische Stolperfallen, Best Practices. Lieber einen Kandidaten zu viel als einen zu wenig — die Bewertung passiert in einem zweiten Schritt. Nichts vorschlagen, was das Element schon enthält.
Jeder Kandidat: Jeder Kandidat:
- text: kurze Beschreibung der Lücke (max. 12 Wörter, reiner Text) - text: kurze Beschreibung der Lücke (max. 12 Wörter, reiner Text)
- target: "description" | "examples" | "hints" | "aufgabe" | "loesung" - target: "description" | "examples" | "hints"
- content: fertiger Inhalt zum Einfügen. SO KURZ WIE MÖGLICH, so lang wie nötig. Markdown: `inline-code` für Bezeichner, examples als Codeblock mit Sprachangabe (```sprache), beginnend mit kurzem Kommentar zur Variante (z. B. `<!-- Einzelner Absatz -->`), hints nur wenn WICHTIG oder NÜTZLICH, im Telegrammstil (nur Kernaussage, z. B. "Keine Blockelemente in `<p>`."). aufgabe: EINE konkrete, in Minuten lösbare Übungsaufgabe; loesung: die knappe Musterlösung dazu. Tags/Bezeichner im Fließtext IMMER in Backticks. - content: fertiger Inhalt zum Einfügen. SO KURZ WIE MÖGLICH, so lang wie nötig. Markdown: `inline-code` für Bezeichner, examples als Codeblock mit Sprachangabe (```sprache), beginnend mit kurzem Kommentar zur Variante (z. B. `<!-- Einzelner Absatz -->`), hints nur wenn WICHTIG oder NÜTZLICH, im Telegrammstil (nur Kernaussage, z. B. "Keine Blockelemente in `<p>`."). Tags/Bezeichner im Fließtext IMMER in Backticks.
Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text: Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text:
{{"suggestions": [{{"text": "...", "target": "hints", "content": "..."}}]}} {{"suggestions": [{{"text": "...", "target": "hints", "content": "..."}}]}}

View File

@@ -11,8 +11,6 @@ Erstelle GENAU EIN Element zum Stichwort:
2. description — was es ist und wozu: MAXIMAL 12 Sätze 2. description — was es ist und wozu: MAXIMAL 12 Sätze
3. examples — GENAU EIN Beispiel: KURZ und SIMPEL, wenige Zeilen Code, das Minimalbeispiel, keine Realwelt-Komplexität. Beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. `<!-- Einzelner Absatz -->`, `// Mit Default-Wert`), der die Variante benennt. 3. examples — GENAU EIN Beispiel: KURZ und SIMPEL, wenige Zeilen Code, das Minimalbeispiel, keine Realwelt-Komplexität. Beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. `<!-- Einzelner Absatz -->`, `// Mit Default-Wert`), der die Variante benennt.
4. hints — IMMER leere Liste. Hinweise ergänzt der Nutzer später selbst. (Falls je gefordert: TELEGRAMMSTIL, max. 10 Wörter.) 4. hints — IMMER leere Liste. Hinweise ergänzt der Nutzer später selbst. (Falls je gefordert: TELEGRAMMSTIL, max. 10 Wörter.)
5. aufgabe — GENAU EINE kleine Übungsaufgabe zum Konzept: konkret, in wenigen Minuten lösbar, prüft das Verständnis. Markdown, Code als Codeblock mit Sprachangabe.
6. loesung — die Musterlösung zur Aufgabe: knapp, nachvollziehbar, Schritt für Schritt nur wo nötig. Code als Codeblock mit Sprachangabe.
Das Element ist ATOMAR: allein verständlich, ohne dass der Leser etwas anderes gelesen hat. Benutzte Begriffe in einem Halbsatz auflösen. Das Element ist ATOMAR: allein verständlich, ohne dass der Leser etwas anderes gelesen hat. Benutzte Begriffe in einem Halbsatz auflösen.
@@ -23,4 +21,4 @@ Tonalität: klares Deutsch, direkt, praxisorientiert. Fachbegriffe beim ersten A
Markdown in description und examples: normale Absätze, `inline-code` für Bezeichner, Codeblöcke mit Sprachangabe (```sprache), **fett** sparsam für Kernaussagen. Keine Überschriften. Code-Beispiele IMMER als Codeblock, nie als Inline-Code. Bezeichner, Tags und Befehle (z. B. `<p>`, `git add`) im Fließtext IMMER in Backticks — nie nackt. Markdown in description und examples: normale Absätze, `inline-code` für Bezeichner, Codeblöcke mit Sprachangabe (```sprache), **fett** sparsam für Kernaussagen. Keine Überschriften. Code-Beispiele IMMER als Codeblock, nie als Inline-Code. Bezeichner, Tags und Befehle (z. B. `<p>`, `git add`) im Fließtext IMMER in Backticks — nie nackt.
Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text: Gib NUR gültiges JSON aus, ohne Code-Fence, ohne weiteren Text:
{{"title": "...", "description": "...", "examples": ["```sprache\n...\n```"], "hints": [], "aufgabe": "...", "loesung": "..."}} {{"title": "...", "description": "...", "examples": ["```sprache\n...\n```"], "hints": []}}

View File

@@ -8,7 +8,6 @@ STIL-REGELN:
2. description — was es ist und wozu: MAXIMAL 12 Sätze 2. description — was es ist und wozu: MAXIMAL 12 Sätze
3. examples — KURZ und SIMPEL: wenige Zeilen Code, Minimalbeispiel, keine Realwelt-Komplexität. Ein Beispiel pro relevanter Variante, geordnet vom Üblichen zum Speziellen. Als Codeblock mit Sprachangabe (```sprache), nie als Inline-Code. Jedes Beispiel beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. `<!-- Einzelner Absatz -->`), der die Variante benennt. 3. examples — KURZ und SIMPEL: wenige Zeilen Code, Minimalbeispiel, keine Realwelt-Komplexität. Ein Beispiel pro relevanter Variante, geordnet vom Üblichen zum Speziellen. Als Codeblock mit Sprachangabe (```sprache), nie als Inline-Code. Jedes Beispiel beginnt mit einem kurzen Kommentar in der Code-Syntax (z. B. `<!-- Einzelner Absatz -->`), der die Variante benennt.
4. hints — jeder Hinweis muss WICHTIG oder NÜTZLICH sein: Stolperfalle, Merksatz oder Best Practice mit echtem Praxiswert. Selbstverständliches, Nischenwissen und Redundantes zum Element entfernen. Telegrammstil: nur die Kernaussage, Füllverben und Herleitungen streichen. 4. hints — jeder Hinweis muss WICHTIG oder NÜTZLICH sein: Stolperfalle, Merksatz oder Best Practice mit echtem Praxiswert. Selbstverständliches, Nischenwissen und Redundantes zum Element entfernen. Telegrammstil: nur die Kernaussage, Füllverben und Herleitungen streichen.
aufgabe — EINE konkrete, in Minuten lösbare Übungsaufgabe, die das Verständnis prüft. loesung — knappe, nachvollziehbare Musterlösung dazu. Beide: Code als Codeblock mit Sprachangabe.
Vorher: "Browser fügen standardmäßig vertikalen Abstand vor und nach `<p>` ein — anpassbar mit `margin`." Vorher: "Browser fügen standardmäßig vertikalen Abstand vor und nach `<p>` ein — anpassbar mit `margin`."
Nachher: "Browser-Abstand um `<p>` per `margin` anpassbar." Nachher: "Browser-Abstand um `<p>` per `margin` anpassbar."
5. Umfang: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH. Jedes Wort muss seinen Platz verdienen — Füllwörter, Nebensätze ohne Informationswert und Selbstverständliches streichen. Aber: Kürze nie auf Kosten der Verständlichkeit oder Korrektheit. 5. Umfang: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH. Jedes Wort muss seinen Platz verdienen — Füllwörter, Nebensätze ohne Informationswert und Selbstverständliches streichen. Aber: Kürze nie auf Kosten der Verständlichkeit oder Korrektheit.
@@ -18,8 +17,8 @@ STIL-REGELN:
Schlage für jeden Stil-Verstoß GENAU EINE Änderung vor: Schlage für jeden Stil-Verstoß GENAU EINE Änderung vor:
- text: kurz, was und warum (max. 12 Wörter, reiner Text) - text: kurz, was und warum (max. 12 Wörter, reiner Text)
- action: "entfernen" | "anpassen" | "hinzufuegen" - action: "entfernen" | "anpassen" | "hinzufuegen"
- target: "title" | "description" | "examples" | "hints" | "aufgabe" | "loesung" - target: "title" | "description" | "examples" | "hints"
- index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description/aufgabe/loesung: null; bei hinzufuegen: null) - index: 0-basierte Position im AKTUELLEN examples- bzw. hints-Array (bei title/description: null; bei hinzufuegen: null)
- content: der neue/vollständige Inhalt (bei entfernen: leer) - content: der neue/vollständige Inhalt (bei entfernen: leer)
"entfernen" nur für examples/hints. "hinzufuegen" sparsam — nur wenn eine Stil-Regel es verlangt (z. B. fehlender Varianten-Kommentar gehört zu "anpassen", nicht "hinzufuegen"). Erfüllt etwas die Regeln schon: NICHT anfassen. "entfernen" nur für examples/hints. "hinzufuegen" sparsam — nur wenn eine Stil-Regel es verlangt (z. B. fehlender Varianten-Kommentar gehört zu "anpassen", nicht "hinzufuegen"). Erfüllt etwas die Regeln schon: NICHT anfassen.

View File

@@ -13,7 +13,7 @@ Prüfe JEDEN Kandidaten kritisch:
1. WICHTIG? Muss ein Lerner das wissen? Nice-to-haves und Nischenwissen ablehnen. 1. WICHTIG? Muss ein Lerner das wissen? Nice-to-haves und Nischenwissen ablehnen.
2. REDUNDANT? Steckt die Info schon im Element oder in einem anderen Kandidaten? Ablehnen bzw. Duplikate zusammenführen. 2. REDUNDANT? Steckt die Info schon im Element oder in einem anderen Kandidaten? Ablehnen bzw. Duplikate zusammenführen.
3. KORREKT? Fachlich falsch oder irreführend → ablehnen. 3. KORREKT? Fachlich falsch oder irreführend → ablehnen.
4. PASST das target ("description" | "examples" | "hints" | "aufgabe" | "loesung")? Sonst korrigieren. Höchstens EIN Vorschlag je für "aufgabe" und "loesung". 4. PASST das target ("description" | "examples" | "hints")? Sonst korrigieren.
Behalte nur Kandidaten, die alle Prüfungen bestehen. Verbessere dabei content auf die Stil-Regeln: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH; `inline-code` für Bezeichner; examples als Codeblock mit Sprachangabe und kurzem Varianten-Kommentar; hints im Telegrammstil (nur Kernaussage, Kürze nie auf Kosten der Verständlichkeit). Behalte nur Kandidaten, die alle Prüfungen bestehen. Verbessere dabei content auf die Stil-Regeln: SO LANG WIE NÖTIG und SO KURZ WIE MÖGLICH; `inline-code` für Bezeichner; examples als Codeblock mit Sprachangabe und kurzem Varianten-Kommentar; hints im Telegrammstil (nur Kernaussage, Kürze nie auf Kosten der Verständlichkeit).