CAWLabel manages articles, packaging, label layouts and scales — then uploads everything to the shop-floor scales so they run on their own. A Delphi / VCL application, built from the first line with AI assistance.
CAWLabel verwaltet Artikel, Verpackungen, Etikettenlayouts und Waagen — und lädt alles auf die Waagen in der Produktion, damit sie eigenständig arbeiten. Eine Delphi-/VCL-Anwendung, von der ersten Zeile an mit KI-Unterstützung entwickelt.
A labelling system for production environments where scales print the labels — not the PC.
Ein Etikettiersystem für Produktionsumgebungen, in denen die Waage das Etikett druckt — nicht der PC.
Every article carries its weights, packaging, VAT rate, best-before rules and nutrition values. Free-form additional fields let each installation define what else an article needs — ingredient lists, origin declarations, storage advice — without touching the program.
The preview on the right renders the actual label for the selected article and scale, including the keyword highlighting and barcode structure described below. What you see is what the scale will print.
Zu jedem Artikel gehören Gewichte, Verpackung, Umsatzsteuersatz, Mindesthaltbarkeits-Regeln und Nährwerte. Über frei definierbare Zusatzfelder legt jede Installation fest, was ein Artikel sonst noch braucht — Zutatenlisten, Herkunftsangaben, Lagerhinweise — ohne Eingriff ins Programm.
Die Vorschau rechts rendert das tatsächliche Etikett für den gewählten Artikel und die gewählte Waage, samt Hervorhebungen und Codeaufbau. Was dort steht, druckt später die Waage.
Layouts are built in an embedded FastReport designer. Fields from articles, packaging, scale configuration and the additional fields are offered as a data tree — drag one onto the label and it prints.
A validation pass checks every layout against the physical label size and reports elements that would fall outside the printable area — or that are defined twice, bound to a data field and carrying their own placeholder as well, in which case the placeholder is silently discarded and the label prints something other than intended. All of it before anything reaches a scale.
Layouts entstehen in einem eingebetteten FastReport-Designer. Felder aus Artikeln, Verpackungen, Waagenkonfiguration und den Zusatzfeldern stehen als Datenbaum bereit — hineinziehen genügt.
Eine Prüffunktion vergleicht jedes Layout mit der physischen Etikettengröße und meldet Elemente außerhalb des Druckbereichs — ebenso doppelt definierte Elemente, die an ein Datenfeld gebunden sind und zusätzlich einen eigenen Platzhalter tragen: Der Platzhalter wird dann stillschweigend verworfen, und das Etikett zeigt etwas anderes als beabsichtigt. Alles, bevor irgendetwas auf einer Waage landet.
The part that shaped every design decision: once the data is uploaded, the scales must keep working on their own.
Der Punkt, der jede Entwurfsentscheidung geprägt hat: Nach dem Upload müssen die Waagen eigenständig weiterarbeiten.
The scales sit on the shop floor with a Zebra printer attached. CAWLabel renders each layout into ZPL — the printer’s own command language — and transfers it together with the article data over FTP.
From that moment the scales are independent. An operator selects an article, places the goods, and the scale composes and prints the label itself: the weight, the price, the best-before date, the barcode. No network, no server, no PC involved. A cable pulled out during a shift does not stop production.
This is why the data has to be complete and correct before it leaves the PC. There is no second chance to fix a typo remotely.
Die Waagen stehen in der Produktion, mit angeschlossenem Zebra-Drucker. CAWLabel rendert jedes Layout nach ZPL — der eigenen Befehlssprache des Druckers — und überträgt es zusammen mit den Artikeldaten per FTP.
Ab diesem Moment sind die Waagen unabhängig. Eine Bedienkraft wählt einen Artikel, legt die Ware auf, und die Waage setzt das Etikett selbst zusammen und druckt es: Gewicht, Preis, Mindesthaltbarkeitsdatum, Barcode. Ohne Netzwerk, ohne Server, ohne PC. Ein herausgezogenes Kabel hält die Produktion nicht an.
Deshalb müssen die Daten vollständig und richtig sein, bevor sie den PC verlassen. Einen Tippfehler kann niemand aus der Ferne nachbessern.
Before a transfer starts, each selected scale is probed over FTP. If none answers, the transfer never begins; if only some do, the operator decides whether to continue.
Vor jeder Übertragung wird jede gewählte Waage per FTP angesprochen. Antwortet keine, startet die Übertragung gar nicht; antworten nur einige, entscheidet die Bedienkraft.
What the scales recorded during the shift is collected in the same run — which article, which weight, when. The transfer screen can upload, download, or only validate.
Was die Waagen während der Schicht protokolliert haben, wird im selben Lauf abgeholt — welcher Artikel, welches Gewicht, wann. Der Bildschirm kann hochladen, abrufen oder nur prüfen.
The same transfer runs from the command line with an exit code and a log file, so a scheduled task can refresh the scales overnight.
Dieselbe Übertragung läuft auch von der Kommandozeile, mit Rückgabewert und Protokolldatei — eine geplante Aufgabe kann die Waagen nachts auffrischen.
Words that have to stand out on a label — allergens, for instance — are maintained in one place and highlighted automatically wherever they appear, in bold, italic, underlined or struck through.
Wörter, die auf dem Etikett auffallen müssen — etwa Allergene — werden an einer Stelle gepflegt und überall automatisch hervorgehoben: fett, kursiv, unterstrichen oder durchgestrichen.
Which digits carry the article number, which the weight, which the price — defined per structure and reused across articles, so a new barcode scheme does not require a code change.
Welche Stellen die Artikelnummer tragen, welche das Gewicht, welche den Preis — je Struktur definiert und artikelübergreifend wiederverwendet. Ein neues Barcode-Schema braucht keine Programmänderung.
Text, numbers, dates, checkboxes, lists, rich text, images — each installation defines the fields its articles need, and they appear in the article screen and the layout designer alike.
Text, Zahlen, Datum, Ankreuzfelder, Listen, Formattext, Grafiken — jede Installation legt die Felder fest, die ihre Artikel brauchen. Sie erscheinen im Artikelbildschirm und im Layout-Designer.
Articles, packaging, scales and VAT rates import from CSV, up to 100,000 rows per run, with a dry run that reports what would change before anything is written.
Artikel, Verpackungen, Waagen und Steuersätze lassen sich aus CSV importieren, bis zu 100.000 Zeilen je Lauf — mit Probelauf, der vorher meldet, was sich ändern würde.
Four phases, four ways of working with AI — and a different result each time. The last one closes the circle.
Vier Phasen, vier Arten mit KI zu arbeiten — und jedes Mal ein anderes Ergebnis. Die letzte schließt den Kreis.
The project started with a browser window next to the IDE. Describe a problem, read the answer, copy the code across, adjust it by hand. It worked, and for isolated questions it worked well: a parsing routine, a regular expression, the outline of a class.
What it could not do was know the project. Every question had to carry its own context, and every answer arrived without knowing what the rest of the code looked like. Names drifted apart, patterns were reinvented, and the copy-and-paste step itself introduced its own mistakes — a missing line, a stale variable name, an indentation that hid a scoping bug.
Kai moved the assistant into RAD Studio itself. The difference was immediate: no more copying. Kai reads the editor buffer, so unsaved changes are part of the picture, and it writes back into the same buffer. Compiling, opening a unit, adding a file to the project — all of that became something to ask for rather than something to do by hand.
For debugging, the practical lesson was to stay short. Kai can set breakpoints and read the call stack, but long live-debugging sessions proved fragile. The habit that emerged: read the code first, form a hypothesis, and reach for the debugger only to confirm it — one breakpoint, one look, terminate.
Claude Code added the thing the other two lacked: it can run the whole loop. Build the project, run the tests, read the output, fix what broke, run them again. A session is not a conversation about code but a sequence of verified steps.
A project file (CLAUDE.md) carries the knowledge that is not visible
in the source: which of three installed Delphi versions this project needs, why a
cached frame must restore its state in Reactivate rather than
Initialize, which build output directory actually receives the test
executable. Every hard-won insight goes in there, so the next session starts where
the last one ended instead of rediscovering the same trap.
The first three phases are about an assistant helping to write an application.
The fourth turns that around: over two sessions, Claude Code built
CAWLabelMcp.exe — a second program that exposes the labelling
system's data over the Model Context Protocol. Nineteen tools, running over standard
input/output, over HTTP, or as a Windows service. Ask it how many packs of a given
article went over scale 1 in the last two weeks and it answers from the live
database; ask it for a label and it renders the same layout the scale will print,
as a PNG.
It shares the application's own gateways rather than reimplementing them, and it writes through the same repository interface the CSV import uses — an article created from a chat is indistinguishable from an imported one. Weighings are the one thing it will never write: they are the scales' records, and there is no tool for changing them.
What began as an interface for AI turned out to be one for everything else as
well. Nine tenths of a REST API were already lying there: the tools, their parameter
schemas, the argument handling, the execution. What was missing was the way in and
the way out. The same executable now answers under /mcp for assistants
and under /api for scripts, scheduled tasks and a merchandise
management system — both through the same tools and the same permission gate,
with two levels of access read from a configuration file. A newly registered tool
appears in both worlds without a line of routing code.
What made this phase worth its own heading is what the work cost. Rendering a
label ran in one second over standard input/output and never finished over HTTP. No
debugger found that; two numbers did. The process stopped consuming processor time,
so it was waiting rather than looping, and several threads waited with the reason
EventPairLow — a call across a thread boundary. The database
connection is COM, and Windows hands such a call back to the thread that created it;
that thread was sitting in Readln. The fix was not to repair the call
but to move the work: tool calls are now queued to the thread that owns those
objects.
Two more lessons, both uncomfortable. A test double that was more forgiving than the database produced a green test suite over a real bug — doubles now reproduce the unique index and refuse to be written to outside a transaction. And the first version of the field evaluation had a food manufacturer's nutrition fields written into it, until the reminder came that the same software labels hardware just as well. It now takes the fields and their columns from the installation's own configuration and knows nothing about what they contain.
Long before any of this, checking the user interface meant screenshots and window coordinates — fragile, slow, and wrong as soon as a control moved. That problem was solved by a component of its own, TVclRestAgent: it makes a running VCL application answer. Instead of "click pixel 530/342", the caller asks which actions exist and triggers the right one by name; instead of guessing whether an input arrived, it reads the property back.
It has meanwhile grown past this project and become a product in its own right
— and it has taken the same step described above. The same component is an MCP
server as well as a REST API: one table of endpoints behind both, one permission
gate. An AI client connects to /mcp, discovers every endpoint as a tool
with its parameters, and can be asked what the screen currently shows. The
application on the other side knows nothing about being inspected; one component on
the form and Active := True is the whole of it.
Angefangen hat es mit einem Browserfenster neben der IDE. Problem beschreiben, Antwort lesen, Code herüberkopieren, von Hand anpassen. Das ging, und für abgegrenzte Fragen ging es gut: eine Parsing-Routine, ein regulärer Ausdruck, das Gerüst einer Klasse.
Was nicht ging: das Projekt kennen. Jede Frage musste ihren Kontext selbst mitbringen, und jede Antwort kam, ohne den übrigen Code zu kennen. Namen liefen auseinander, Muster wurden neu erfunden, und das Kopieren selbst brachte eigene Fehler mit — eine fehlende Zeile, ein alter Variablenname, eine Einrückung, die einen Gültigkeitsfehler verdeckte.
Kai holte den Assistenten in RAD Studio selbst. Der Unterschied war sofort spürbar: kein Kopieren mehr. Kai liest den Editorpuffer, also gehören auch ungespeicherte Änderungen zum Bild, und schreibt in denselben Puffer zurück. Kompilieren, eine Unit öffnen, eine Datei ins Projekt aufnehmen — all das wurde zu etwas, das man erbittet, statt es von Hand zu tun.
Beim Debuggen war die praktische Lehre: kurz bleiben. Kai kann Haltepunkte setzen und den Aufrufstapel lesen, aber lange Live-Sitzungen erwiesen sich als störanfällig. Daraus wurde eine Gewohnheit: erst den Code lesen, eine Vermutung bilden, und den Debugger nur zur Bestätigung bemühen — ein Haltepunkt, ein Blick, beenden.
Claude Code brachte, was den beiden anderen fehlte: Es kann den ganzen Kreis laufen. Projekt bauen, Tests ausführen, Ausgabe lesen, Gebrochenes reparieren, erneut ausführen. Eine Sitzung ist kein Gespräch über Code, sondern eine Folge nachgeprüfter Schritte.
Eine Projektdatei (CLAUDE.md) trägt das Wissen, das im Quelltext
nicht sichtbar ist: welche von drei installierten Delphi-Versionen dieses Projekt
braucht, warum ein gecachtes Frame seinen Zustand in Reactivate
wiederherstellen muss und nicht in Initialize, welches
Ausgabeverzeichnis die Test-Exe tatsächlich bekommt. Jede teuer erkaufte
Erkenntnis landet dort, damit die nächste Sitzung dort beginnt, wo die letzte
aufhörte, statt in dieselbe Falle zu laufen.
In den ersten drei Phasen hilft eine KI dabei, eine Anwendung zu schreiben. Die
vierte dreht das um: In zwei Sitzungen hat Claude Code
CAWLabelMcp.exe gebaut — ein zweites Programm, das die Daten des
Etikettiersystems über das Model Context Protocol bereitstellt. Neunzehn
Werkzeuge, wahlweise über Standardein- und -ausgabe, über HTTP oder als
Windows-Dienst. Auf die Frage, wie viele Pakete eines Artikels in den letzten zwei
Wochen über Waage 1 gelaufen sind, antwortet es aus der laufenden
Datenbank; fragt man nach einem Etikett, rendert es dasselbe Layout, das die Waage
druckt, als PNG.
Es benutzt die Gateways der Anwendung, statt sie nachzubauen, und schreibt über dieselbe Schnittstelle wie der CSV-Import — ein per Chat angelegter Artikel ist von einem importierten nicht zu unterscheiden. Eines schreibt es nie: Wiegungen. Sie sind die Aufzeichnungen der Waagen, und dafür gibt es schlicht kein Werkzeug.
Was als Schnittstelle für KI begann, war nebenbei eine für alles
andere. Neun Zehntel einer REST-Schnittstelle lagen bereits da: die Werkzeuge, ihre
Parameterschemata, die Argumentauswertung, die Ausführung. Gefehlt haben nur
der Weg hinein und der Weg hinaus. Dasselbe Programm antwortet jetzt unter
/mcp für Assistenten und unter /api für
Skripte, Aufgabenplanung und die Warenwirtschaft — beides durch dieselben
Werkzeuge und dasselbe Rechte-Tor, mit zwei Stufen aus einer Konfigurationsdatei.
Ein neu angemeldetes Werkzeug erscheint in beiden Welten, ohne eine Zeile
Routing-Code.
Eine eigene Überschrift verdient diese Phase wegen dessen, was die Arbeit
gekostet hat. Das Rendern eines Etiketts lief über die Standardausgabe in einer
Sekunde durch und über HTTP nie. Gefunden hat das kein Debugger, sondern zwei
Zahlen: Der Prozess verbrauchte keine Rechenzeit mehr, wartete also, statt zu
laufen, und mehrere Threads warteten mit dem Grund EventPairLow —
ein Aufruf über eine Thread-Grenze. Die Datenbankverbindung ist COM, und Windows
reicht einen solchen Aufruf an den Thread zurück, der sie erzeugt hat; der saß
in Readln. Die Lösung war nicht, den Aufruf zu reparieren, sondern
die Arbeit zu verschieben: Werkzeugaufrufe werden seitdem an den Thread
übergeben, dem diese Objekte gehören.
Zwei weitere Lehren, beide unbequem. Ein Test-Double, das nachgiebiger war als die Datenbank, erzeugte eine grüne Testsuite über einem echten Fehler — die Doubles bilden jetzt den eindeutigen Index nach und verweigern jedes Schreiben außerhalb einer Transaktion. Und die erste Fassung der Feldauswertung hatte die Nährwertfelder eines Lebensmittelbetriebs fest eingebaut, bis der Hinweis kam, dass dieselbe Software genauso gut Eisenwaren etikettiert. Sie nimmt die Felder und ihre Spalten jetzt aus der Konfiguration der jeweiligen Anlage und weiß nichts darüber, was darin steht.
Lange vor alledem hieß „die Oberfläche prüfen“: Screenshots und Fensterkoordinaten — zerbrechlich, langsam und falsch, sobald sich ein Bedienelement verschiebt. Gelöst hat das eine eigene Komponente, TVclRestAgent: Sie macht eine laufende VCL-Anwendung auskunftsfähig. Statt „klicke Pixel 530/342“ fragt der Aufrufer, welche Aktionen es gibt, und löst die richtige beim Namen aus; statt zu raten, ob eine Eingabe angekommen ist, liest er die Eigenschaft zurück.
Sie ist diesem Projekt inzwischen entwachsen und ein eigenes Produkt geworden
— und sie hat denselben Schritt gemacht wie oben beschrieben. Dieselbe
Komponente ist MCP-Server und REST-Schnittstelle zugleich: eine Tabelle von
Endpunkten hinter beidem, ein Rechte-Tor. Ein KI-Client verbindet sich mit
/mcp, findet jeden Endpunkt als Werkzeug samt seiner Parameter und
lässt sich fragen, was gerade auf dem Bildschirm steht. Die Anwendung auf der
anderen Seite weiß davon nichts: eine Komponente auf dem Formular,
Active := True, das ist alles.
1,927 tests across three DUnitX projects, in 87 test units. They are not run occasionally — they run after every change, and a commit without a green run does not happen.
Writing them stopped being the expensive part. A new value editor arrives with its tests; a bug fix arrives with the test that would have caught it.
1.927 Tests in drei DUnitX-Projekten, verteilt auf 87 Test-Units. Sie laufen nicht gelegentlich — sie laufen nach jeder Änderung, und ohne grünen Lauf gibt es kein Einchecken.
Sie zu schreiben ist nicht mehr der teure Teil. Ein neuer Wert-Editor kommt mit seinen Tests; eine Fehlerbehebung kommt mit dem Test, der den Fehler gefunden hätte.
A test that passes proves nothing until you have seen it fail. The habit: remove the fix, confirm that exactly the intended test goes red, put the fix back.
It has caught tests that asserted nothing, and it has saved a bug report that turned out to be wrong — the code path looked broken, the measurement said otherwise.
Ein bestandener Test beweist nichts, solange man ihn nicht hat fallen sehen. Die Gewohnheit: die Korrektur ausbauen, prüfen, dass genau der gemeinte Test rot wird, die Korrektur wieder einbauen.
Das hat Tests entlarvt, die nichts prüften — und eine Fehlermeldung verhindert, die falsch gewesen wäre: Der Codepfad sah kaputt aus, die Messung sagte etwas anderes.
Performance work is verified against the database’s own execution counters, not against intuition. One caching change looked like a clear win on paper; the first measurement showed the effect drowning in noise, and the real fix turned out to be somewhere else entirely.
Leistungsarbeit wird an den Ausführungszählern der Datenbank geprüft, nicht am Bauchgefühl. Eine Cache-Änderung sah auf dem Papier nach klarem Gewinn aus; die erste Messung zeigte den Effekt im Rauschen untergehen, und die eigentliche Ursache lag ganz woanders.
Twenty documented review rounds in a log that records what was checked, what was found, and which revision fixed it — so the next review can start from the last one instead of re-reading everything.
Zwanzig dokumentierte Review-Durchläufe in einem Protokoll, das festhält, was geprüft wurde, was gefunden wurde und welche Revision es behoben hat — damit das nächste Review beim letzten ansetzen kann.
40 pages of user documentation in Markdown, generated into HTML help with screenshots. It is written alongside the feature, not months later — which is the only reason it still matches the program.
40 Seiten Benutzerdokumentation in Markdown, daraus erzeugte HTML-Hilfe mit Bildschirmfotos. Sie entsteht mit der Funktion, nicht Monate später — nur deshalb passt sie noch zum Programm.
A component built into the application lets an automated run ask which controls exist, read their properties, trigger actions and wait for a screen to settle. It has since outgrown this project and become one of its own: TVclRestAgent.
Eine Komponente in der Anwendung erlaubt es einem automatisierten Lauf, nach vorhandenen Steuerelementen zu fragen, Eigenschaften zu lesen, Aktionen auszulösen und auf einen Bildschirmzustand zu warten. Sie ist diesem Projekt inzwischen entwachsen und ein eigenes geworden: TVclRestAgent.