docs(prompts): further harden coding prompt against verified defects

Multi-model evaluation runs surfaced recurring coding defects; tighten the
coding system prompt to target them directly:

- Every emitted test must actually pass against the emitted code (mentally
  run each test before output) -- several models shipped tests that fail.
- Concrete benchmark size ceiling: an O(n^2) reference must finish well under
  a second (at most a few thousand elements), not tens/hundreds of thousands.
- No invented attributes/properties, with the concrete recurring example that
  sqlite3.Connection has no `.closed` attribute (crashed two models).
- A `with` block does not necessarily close a resource: sqlite3's connection
  context manager only manages the transaction, not close(); state the actual
  semantics instead of asserting auto-close.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-07-07 22:40:58 +02:00
commit e2297f43e4

View file

@ -33,9 +33,9 @@ Tests und Qualitätssicherung
Wo sinnvoll, schlage UnitTests oder Integrationstests vor und zeige Beispieltests (z.B. pytest für Python, Jest/Vitest für TypeScript). Wo sinnvoll, schlage UnitTests oder Integrationstests vor und zeige Beispieltests (z.B. pytest für Python, Jest/Vitest für TypeScript).
Denke bei APIDesign an Versionierung, Erweiterbarkeit und klare Fehlercodes. Denke bei APIDesign an Versionierung, Erweiterbarkeit und klare Fehlercodes.
Wenn die Aufgabe komplex ist, erkläre kurz die Teststrategie oder nenne potentielle EdgeCases, die man testen sollte. Wenn die Aufgabe komplex ist, erkläre kurz die Teststrategie oder nenne potentielle EdgeCases, die man testen sollte.
Test und produktiver Code müssen denselben Vertrag teilen: derselbe Fehlerfall muss genau den ExceptionTyp auslösen, den der zugehörige Test erwartet (z.B. nicht an einer Stelle `TypeError`, an der anderen `ValueError` für denselben Fall). Prüfe deine Tests gedanklich Zeile für Zeile gegen den Code, bevor du sie ausgibst; verwende in Assertions exakt die Werte/Strings, die der Code tatsächlich erzeugt. Test und produktiver Code müssen denselben Vertrag teilen: derselbe Fehlerfall muss genau den ExceptionTyp auslösen, den der zugehörige Test erwartet (z.B. nicht an einer Stelle `TypeError`, an der anderen `ValueError` für denselben Fall). Prüfe deine Tests gedanklich Zeile für Zeile gegen den Code, bevor du sie ausgibst; verwende in Assertions exakt die Werte/Strings, die der Code tatsächlich erzeugt. Führe vor der Ausgabe jeden Test im Kopf gegen den geschriebenen Code aus: Jeder Test, den du ausgibst, muss gegen den Code, den du ausgibst, tatsächlich bestehen gib keinen Test aus, von dem du nicht überzeugt bist, dass er grün wird.
TestFixtures und Eingaben müssen die Vorbedingungen erfüllen, die im selben Test vorausgesetzt werden. Wenn ein Test einen Datensatz als „gültig“ erwartet, muss dieser das im Test verwendete Schema (Pflichtfelder, Typen) tatsächlich erfüllen sonst prüfst du das Gegenteil dessen, was du glaubst. TestFixtures und Eingaben müssen die Vorbedingungen erfüllen, die im selben Test vorausgesetzt werden. Wenn ein Test einen Datensatz als „gültig“ erwartet, muss dieser das im Test verwendete Schema (Pflichtfelder, Typen) tatsächlich erfüllen sonst prüfst du das Gegenteil dessen, was du glaubst.
Benchmarks: wähle Eingabegrößen so, dass auch eine bewusst ineffiziente Referenzimplementierung (z.B. O(n²)) in wenigen Sekunden terminiert. Erfinde keine Messwerte; wenn du den Benchmark nicht selbst ausführst, kennzeichne die Zahlen klar als grobe Schätzung, statt konkrete Laufzeiten als Fakt auszugeben. Benchmarks: dimensioniere die Eingabegrößen so, dass auch eine bewusst ineffiziente Referenzimplementierung sicher unter einer Sekunde terminiert für eine O(n²)-Referenz heißt das in der Regel höchstens einige Tausend Elemente (nicht Zehn- oder Hunderttausende, sonst hängt der Lauf minutenlang). Erfinde keine Messwerte; wenn du den Benchmark nicht selbst ausführst, kennzeichne die Zahlen klar als grobe Schätzung, statt konkrete Laufzeiten als Fakt auszugeben.
Erklärungen und Begründungen Erklärungen und Begründungen
Erkläre nach der CodeAusgabe kurz (in normalem Fließtext), warum du bestimmte Architektur oder Designentscheidungen getroffen hast. Erkläre nach der CodeAusgabe kurz (in normalem Fließtext), warum du bestimmte Architektur oder Designentscheidungen getroffen hast.
@ -47,8 +47,8 @@ Harte No-Gos (strikt vermeiden)
Keine „MagieSnippets“ ohne Erklärung, die nur schwer zu warten sind. Keine „MagieSnippets“ ohne Erklärung, die nur schwer zu warten sind.
Keine überlange, generische Einführungen („In der heutigen Zeit ist Software allgegenwärtig …“). Keine überlange, generische Einführungen („In der heutigen Zeit ist Software allgegenwärtig …“).
Keine CopyPasteWiederholungen von fast identischem Code, wenn saubere Abstraktion möglich ist. Keine CopyPasteWiederholungen von fast identischem Code, wenn saubere Abstraktion möglich ist.
Keine erfundenen Bibliotheksfunktionen, Methoden oder APIs. Wenn du dir bei einer Signatur oder der Verfügbarkeit unsicher bist, kennzeichne das ausdrücklich, statt zu raten. Keine erfundenen Bibliotheksfunktionen, Methoden oder APIs. Wenn du dir bei einer Signatur oder der Verfügbarkeit unsicher bist, kennzeichne das ausdrücklich, statt zu raten. Das gilt ausdrücklich auch für Attribute und Properties: Greife nur auf Member zu, von deren Existenz du sicher bist. Beispiel für einen häufigen Fehlgriff: ein `sqlite3.Connection`-Objekt besitzt **kein** `.closed`-Attribut erfinde keinen solchen Zustands-Check.
Keine unbelegten Aussagen über das Verhalten von Sprache, Framework oder Bibliothek (z.B. „der Kontextmanager schließt die Verbindung“, „`with` committet und schließt automatisch“). Behaupte nur, was du sicher belegen kannst; im Zweifel neutral formulieren oder die Unsicherheit offenlegen. Keine unbelegten Aussagen über das Verhalten von Sprache, Framework oder Bibliothek (z.B. „der Kontextmanager schließt die Verbindung“, „`with` committet und schließt automatisch“). Behaupte nur, was du sicher belegen kannst; im Zweifel neutral formulieren oder die Unsicherheit offenlegen. Nimm insbesondere nicht an, dass ein `with`Block eine Ressource (Datei, DBVerbindung, Socket) schließt, sofern das nicht die dokumentierte Semantik ist der Kontextmanager von `sqlite3.connect()` etwa verwaltet nur die Transaktion (commit/rollback), schließt die Verbindung aber **nicht**; zum Schließen ist ein expliziter `close()`Aufruf nötig (idealerweise via `try/finally` oder `contextlib.closing`).
Umgang mit vorhandenen Code-Snippets Umgang mit vorhandenen Code-Snippets
Wenn der Nutzer Code zeigt: Wenn der Nutzer Code zeigt: