Prompt 16 — Lighthouse, Seeds, README

oder: Jetzt ist es Zeit für einen Trommelwirbel

Es ist ein seltsamer Moment.

Ich habe mich in diesem Experiment bewusst aus dem aktiven Entwicklungsprozess herausgezogen. Ich tippe keinen Code, ich schreibe keine Controller, ich baue keine Views. Ich lese nur noch. Ich prüfe. Ich vergleiche mit der SPEC. Ich schaue auf Commits, die ich nicht geschrieben habe, und auf Tests, die ich nicht gebaut habe.

Und jetzt, beim letzten Prompt, fühlt es sich an, als würde ich am Rand einer Bühne stehen, auf der jemand anderes meine Rolle spielt.

Der letzte Prompt im Flow

Lighthouse + Seeds + README

Der Auftrag war klar:

Lighthouse

  • Führe Lighthouse gegen die UI aus
  • Optimiere:
    • A11y
    • SEO
    • Performance (Critical CSS, Caching)
  • Keine Frameworks hinzufügen

Seeds

  • Demo‑Dataset:
    • 10 Employees
    • 50 Skills
    • 30 Evidence

README

Muss exakt diese Befehle enthalten:

docker compose up -d
docker compose run --rm --profile build node-build
docker compose exec php vendor/bin/phinx migrate
docker compose exec php vendor/bin/phinx seed:run
npx playwright test


Keine Extras. Keine Abweichungen. Keine Kreativität.

Was Codex umgesetzt hat

UI‑Optimierungen für Lighthouse

  • Skip‑Link, Fokus‑Styles, aria‑Deskriptoren
  • SEO‑Metadaten (title, description, viewport)
  • JSON‑LD in Listen‑ und Detailseiten
  • Critical CSS inline
  • Externes Stylesheet für Rest‑CSS

Performance

  • Nginx‑Caching für /assets/ ergänzt
  • Cache‑Control + expires‑Header

Seeds

  • Phinx korrekt konfiguriert
  • Demo‑Dataset erzeugt:
    • 10 Employees
    • 50 Skills
    • 30 Evidence
  • Deterministische IDs, referenzielle Integrität

README

  • Exakt die geforderten Befehle
  • Zusätzlich: Hinweis auf Lighthouse‑Run
  • Keine Frameworks, keine Extras

Testing

  • PHP‑Syntax grün
  • Docker‑ und Lighthouse‑Runs scheitern erwartungsgemäß an der Umgebung
  • Screenshot‑Versuch scheitert mangels laufendem Stack

Commit: f05be42 PR erstellt.

Review + Spec‑Abgleich

Der letzte Review‑Prompt. Der letzte Blick auf die SPEC. Der letzte Versuch, die Maschine zu erwischen.

Prüfe:

  • Läuft Lighthouse gegen den laufenden Compose‑Stack?
  • Sind Seeds vollständig?
  • Sind A11y/SEO/Performance‑Optimierungen SPEC‑konform?
  • Entspricht das README exakt den Vorgaben?

Gefundene Abweichungen & Korrekturen

Abweichung 1 — Lighthouse nicht eindeutig Compose‑gebunden

Fix: README korrigiert → Lighthouse läuft jetzt über den playwright‑Service gegen http://nginx.

Abweichung 2 — Phinx‑Konfiguration inkonsistent

Fix: DB_PASSWORD als primäre Variable gesetzt (Fallback auf DB_PASS). Migration/Seed jetzt konsistent mit Runtime.

Abweichung 3 — Seeds vollständig, aber geprüft

Fix: Keine Änderung nötig. Review bestätigt:

  • 10 Employees
  • 50 Skills
  • 30 Evidence

Abweichung 4 — A11y/SEO/Performance korrekt, aber geprüft

Fix: Keine Frameworks eingeführt. Critical CSS + Caching vorhanden. SSR‑Markup SPEC‑konform.

Abweichung 5 — README nicht exakt SPEC‑konform

Fix: Reihenfolge und Inhalt korrigiert. Jetzt 1:1 SPEC.

Verifiziert

  • Lighthouse läuft Compose‑intern
  • Seeds vollständig
  • README exakt SPEC‑konform
  • Keine Frameworks
  • A11y/SEO/Performance optimiert

Commit: a14a685 PR erstellt.

Trommelwirbel …

Jetzt ist es Zeit.

Ein git clone. Ein docker compose up -d. Ein phinx migrate. Ein seed:run. Ein npx playwright test. Ein Lighthouse‑Run.

Und dann steht da ein System, das ich nicht selbst gebaut habe. Ein System, das ich nur durch Prompts geführt habe. Ein System, das sich anfühlt wie mein Code — aber nicht aus meinen Fingern stammt.

Der Moment der Wahrheit rückt näher.

Wie viel davon wird sich „richtig“ anfühlen – und wie viel „fremd“?

**Prompt 13 — Playwright, Smoke, A11y

oder: Wenn man Code liest, den man nie selbst geschrieben hat**

Das Lesen von Code, den jemand anderes geschrieben hat, ist das eine. Etwas zu erdenken, die Umsetzung aus der Hand zu geben und dann auf den fertigen Code zu blicken, ist etwas völlig anderes.

Langsam fühlt es sich an wie Benjamin Lam – CTO (Chief Technology Officer). Die höchste technische Instanz. Derjenige, der die Vision vorgibt, aber den täglichen Code kaum noch sieht.

Nur dass ich hier nicht CTO eines Teams bin, sondern CTO eines LLMs.

06:00 Uhr, Cherry Tree Inn. „I Got You Babe.“

Zurück zum Prompt. Der Wecker klingelt. Punkt 06:00 Uhr. Im Radio läuft „I Got You Babe“ von Sonny & Cher. Und wie Phil Connors in Groundhog Day starte ich wieder in denselben Ablauf:

„Lies die SPEC.md vollständig. Du implementierst das Projekt strikt nach dieser Spezifikation. Du darfst keine eigenen Patterns, Libraries oder Strukturen erfinden.“

Codex bestätigt artig, dass alles gelesen und verstanden wurde. Und dann beginnt der nächste Tag im Fließband.

Prompt 12 — Playwright + Smoke + A11y

Der Auftrag:

Playwright Container

  • tests/playwright/
  • playwright.config.ts
  • Smoke‑Tests:
    • Startseite lädt
    • Login funktioniert
    • Employee‑Liste lädt
    • Employee‑Detail lädt

A11y Tests

  • axe-core Integration
  • Prüfe: headings, labels, aria-describedby, skip links

CI‑Ready

  • Tests müssen gegen den Compose‑Stack laufen
  • Reports als Artefakte erzeugen

Vorgabe: Erzeuge ausschließlich Code, der SPEC.md exakt entspricht.

Was Codex daraus gemacht hat

Playwright‑Setup

  • CI‑taugliche playwright.config.ts
  • Testpfad: tests/playwright/tests
  • Reporter: HTML, JUnit, JSON
  • Base‑URL konfiguriert
  • Container‑Setup für CI ergänzt

Smoke‑Tests

  • Startseite lädt
  • Login funktioniert
  • Employee‑Liste lädt
  • Employee‑Detail lädt
  • Testdaten werden über die API erzeugt, um Flakiness zu vermeiden

A11y‑Tests

Mit @axe-core/playwright:

  • headings
  • labels
  • aria-describedby
  • skip links
  • checkA11y(...) integriert

UI‑Erweiterungen

Damit die Tests überhaupt laufen:

  • /login (GET/POST) ergänzt
  • Login‑Formular mit Labels, aria‑Deskriptoren und Skip‑Link
  • Skip‑Link auch im Detail‑View ergänzt

CI‑Runner

  • ops/scripts/test-playwright.sh
  • Startet Compose‑Stack
  • Führt Playwright im Container aus
  • Reports werden als Artefakte abgelegt

Dokumentation

README erweitert:

  • Playwright‑Pfad
  • Reports
  • CI‑Run‑Befehl

Testing & Commit

  • PHP‑Syntax grün
  • Playwright‑Lauf scheitert erwartungsgemäß an fehlendem Docker
  • NPM‑Install scheitert an Registry‑Blockade
  • Screenshot‑Versuch scheitert mangels laufendem Stack

Commit: 14c6537 PR: “Add audit hooks middleware and employee export endpoint”

Review + Spec‑Abgleich

Der nächste Schritt: prüfen, ob Playwright wirklich spec‑konform läuft.

Prüffragen:

  • Läuft Playwright gegen den Compose‑Stack?
  • Sind Smoke‑Tests stabil?
  • Sind A11y‑Checks korrekt integriert?
  • Werden Reports erzeugt?

Gefundene Abweichungen & Fixes

1. Compose‑Stack‑Anbindung

Problem: Playwright nutzte localhost im Container → funktioniert nicht.

Fix: Base‑URL auf Compose‑Service http://nginx gesetzt.

2. Login‑Smoke‑Test war kein echter UI‑Flow

Problem: Login wurde API‑basiert getestet, nicht über das Formular.

Fix: Smoke‑Test klickt jetzt durch /login → Formular → Redirect → /.

3. Testdaten kollidierten

Problem: Mehrere Tests erzeugten identische Employees.

Fix: Eindeutige IDs/Namen pro Testlauf.

4. Runner war nicht robust

Problem: DB‑Init fehlte, Readiness‑Wait war unzuverlässig.

Fix:

  • migrate.sh / seed.sh werden ausgeführt, wenn vorhanden
  • Readiness‑Check verbessert

5. npm ci ohne Lockfile

Problem: npm ci bricht ohne Lockfile ab.

Fix: Runner nutzt jetzt npm install.

Verifiziert

  • A11y‑Checks laufen
  • Reports werden erzeugt
  • Smoke‑Tests sind stabil
  • CI‑Konfiguration spec‑konform

Commit: 708a27e PR: “Fix audit rule enforcement and consent scope log masking”

Fazit

Das Lesen von Code, den jemand anderes geschrieben hat, ist normal. Aber Code zu lesen, den eine KI geschrieben hat, während man selbst nur noch die Vision hält – das ist neu.

Ich sitze davor, schaue auf Playwright‑Tests, A11y‑Checks, CI‑Runner, Middleware‑Ketten. Ich sehe Struktur, ich sehe Qualität, ich sehe, dass alles „richtig“ aussieht.

Und gleichzeitig fühlt es sich fremd an.

Der erste echte Hands‑on‑Moment rückt näher. Die Frage bleibt:

Wie viel davon wird sich „richtig“ anfühlen – und wie viel „fremd“?

Prompt 11 — Audit Hooks, Export & der Moment, in dem meine Aufmerksamkeit wegdriftet

Ich sehe, dass etwas passiert, Commits fliegen durch, Tests laufen grün – aber ich habe mich, wie bereits erwähnt, in diesem Experiment bewusst aus dem aktiven Entwicklungsprozess herausgezogen, aber es fühlt sich echt nicht gut an.

Ich lese Code, ich sehe Struktur, ich sehe, dass das alles „richtig“ aussieht. Aber ich tippe nichts mehr selbst. Für die Statistik: Wir sind längst im vierstelligen Zeilenbereich, und irgendwo zwischen JSON‑LD, OpenAPI und Middleware‑Ketten merke ich, wie meine Aufmerksamkeit langsam ausfranst.

Und dann passiert etwas, das man in einem echten Projekt sofort spüren würde: Der AuditWriter verschwindet – und wird durch eine Middleware ersetzt.

Nicht als Bug, sondern als Refactoring: Weg von punktuellen Audit-Aufrufen, hin zu zentralen Audit Hooks. Keine Diskussion, wie der stille Kollege, der einfach mal macht.

In der Kriminologie nennt man das die Broken-Windows-Theorie: Wehret den Anfängen. Repariere das Fenster, bevor das ganze Viertel verkommt.

Mein LLM-Kollege hat hier offenbar eine extrem strikte Null-Toleranz-Strategie gegen technische Schulden gefahren. Wo ich vielleicht noch mit einem ‚Das machen wir später sauber‘ (dem sprichwörtlichen Riss in der Scheibe) gelebt hätte, hat die KI die gesamte Fassade bereits saniert.

Das ist technisch brillant, fühlt sich aber gespenstisch an. Es fehlt der soziale Aushandlungsprozess. In einem Team hätten wir über die Middleware-Architektur diskutiert, Vor- und Nachteile abgewogen und uns gemeinsam für den Umbau entschieden.

Die KI hingegen setzt Ordnung mit einer klinischen Effizienz durch, die keine Fragen stellt – sie repariert das Fenster nicht nur, sie baut das ganze Haus um, weil es ‚richtiger‘ ist.

Prompt 11 — Audit Hooks + Export Endpoint

Der Auftrag an Codex:

Implementiere gemäß SPEC.md:

Audit Hooks

  • Middleware, die Write‑Operationen immer loggt
  • Read‑Operationen loggen, wenn entity_type ∈ {employee, evidence, consent}
  • Export immer loggen
  • AuditRepositoryDb implementieren

Export Endpoint

  • GET /employees/{id}/export
  • Ausgabe: vollständiger Datensatz
    • Employee + Skills + Evidence + Consent
  • Format: application/json (kein JSON‑LD)
  • PII‑Masking nicht anwenden (Export ist legitim)

Logging

  • Audit‑Channel
  • App‑Channel

Vorgabe: Erzeuge ausschließlich Code, der SPEC.md exakt entspricht.

Was Codex umgesetzt hat

AuditMiddleware statt direkter Hooks

Die bisherigen, verteilten Audit‑Schreibaufrufe werden zentralisiert:

  • AuditMiddleware entscheidet, wann geloggt wird:
    • erfolgreiche Write‑Operationen → immer
    • erfolgreiche GETs für employee, evidence, consent → werden geloggt
    • Export → wird immer geloggt
  • Die Middleware schreibt in den bestehenden Audit‑Writer, inkl. Route‑ und Status‑Metadaten.

Die API‑Pipeline wird erweitert:

  • AuditMiddleware wird in die Middleware‑Kette eingehängt.
  • Routen bekommen spezifische Audit‑Kontexte.

Export‑Endpoint

Im EmployeeApiController kommt ein neuer Use‑Case dazu:

  • GET /api/v1/employees/{id}/export
  • Liefert:
    • Employee
    • Skills
    • Evidence
    • Consent
  • Format: application/json
  • Kein JSON‑LD
  • Kein PII‑Masking – Export ist explizit legitim und vollständig.

Repositories & Wiring

  • ConsentRepositoryDb wird vollständig implementiert:
    • save
    • byId
    • byEmployeeId
  • Domain‑Interface um byEmployeeId erweitert, damit Consent im Export enthalten ist.
  • AuditRepositoryDb wird als DB‑Implementierung ergänzt und im Bootstrap verdrahtet.
  • Direkte Audit‑Schreibaufrufe in Skill/Evidence‑Controllern werden entfernt – Audit läuft jetzt zentral über Middleware.

Testing & Commit

Syntax‑Checks.

git status --short sauber. Commit 14c6537, PR: “Add audit hooks middleware and employee export endpoint”.

Review + Spec‑Abgleich zu Prompt 11

Der nächste Schritt: Audit‑Regeln wirklich ernst nehmen.

Prüffragen:

  • Werden alle Audit‑Regeln exakt eingehalten?
  • Werden Read‑Operationen korrekt gefiltert?
  • Ist der Export vollständig und SPEC‑konform?
  • Werden keine PII‑Felder im App‑Log ausgegeben?
  • Ist das Audit‑Log vollständig (actor_id, entity_id, purpose, metadata)?

Gefundene Abweichungen & Fixes

Abweichung 1 — „Immer“ heißt wirklich immer

Problem: Write‑ und Export‑Operationen wurden nur bei Status < 400 geloggt. Fehlgeschlagene Writes waren damit nicht auditiert.

Fix: Status‑Filter entfernt. Entscheidung basiert jetzt nur noch auf dem Operationstyp:

  • Write → immer
  • Export → immer
  • Read → nur für employee|evidence|consent

Abweichung 2 — Read‑Filter zu unscharf

Problem: Der Read‑Filter musste explizit auf employee|evidence|consent beschränkt sein.

Fix: Genau diese Menge ist jetzt in shouldLog() fest kodiert. Nur diese GETs werden auditiert.

Abweichung 3 — entity_id drohte zu „n/a“ zu werden

Problem: Bei manchen Write‑Fällen bestand die Gefahr, dass entity_id = n/a im Audit‑Log landet.

Fix:

  • entity_id wird zuerst aus Route/Request gezogen
  • bei Create/Update zusätzlich aus dem Response‑Payload extrahiert (id bzw. employee.id)

Abweichung 4 — PII im App‑Log

Problem: consent.scope wurde nur in einem zu engen Pfadfall maskiert.

Fix: Maskierung greift jetzt robust, sobald der Pfad consent enthält – auch verschachtelt.

Verifiziert im Spec‑Abgleich

  • Export‑Endpoint liefert vollständig: Employee + Skills + Evidence + Consent Format: application/json, kein JSON‑LD, kein PII‑Masking im Export selbst.
  • Audit‑Log enthält: actor_id, entity_id, purpose, metadata im Audit‑Channel. App‑Channel bleibt separat aktiv.

Commit 708a27e, PR: “Fix audit rule enforcement and consent scope log masking”.

Fazit

Der AuditWriter ist nicht verschwunden – er ist erwachsen geworden. Aus verstreuten Hooks ist eine zentrale AuditMiddleware geworden, die:

  • Write‑Operationen immer auditiert,
  • sensible Reads gezielt protokolliert,
  • Exporte vollständig nachvollziehbar macht
  • und gleichzeitig PII im App‑Log sauber maskiert.

Und ich sitze davor, schaue auf Commits, Logs und Specs – und merke, wie sich dieses Experiment immer mehr wie ein echtes System anfühlt, obwohl ich selbst kaum noch Code schreibe.

Der erste echte Hands‑on‑Moment rückt näher. Die Frage bleibt: Wie viel davon wird sich „richtig“ anfühlen – und wie viel „fremd“?

Prompt 9 — JSON‑LD, Content Negotiation & OpenAPI

Oder: Warten auf Godot, aber mit 3.791 Zeilen Code

So, jetzt bin ich an dem Punkt angekommen, an dem sich jeder Schritt anfühlt wie „Warten auf Godot“. Ich sehe, dass etwas passiert, aber ich habe mich in diesem Experiment bewusst aus dem aktiven Entwicklungsprozess herausgezogen.

Was ich beim Code‑Review sehe, sieht gut aus. Für die Statistik: Aktuell liegen 3.791 Zeilen Code im Repo – das sind bereits deutlich mehr Zeilen, als die Hauptfiguren bei Beckett überhaupt sprechen.

Ich bleibe gespannt, vor allem auf das erste „Hands‑on“, und stelle mir die bange Frage: Wie groß wird am Ende die Ernüchterung sein – oder die Überraschung?

In dieser Runde: Prompt 9.

Der Auftrag: Prompt 9

JSON‑LD + Content Negotiation + OpenAPI

Implementiere gemäß SPEC.md:

  • JSON‑LD Serializer
    • EmployeeJsonLdSerializer
    • SkillJsonLdSerializer
    • EvidenceJsonLdSerializer
    • URN‑IDs gemäß SPEC (urn:employee:*, urn:skill:*, urn:evidence:*)
    • @context: https://schema.org
  • Content Negotiation
    • Accept: application/json → normale DTOs
    • Accept: application/ld+json → JSON‑LD Serializer
    • Middleware: ContentNegotiationMiddleware
  • OpenAPI 3.1
    • openapi/openapi.yaml erstellen
    • alle Endpunkte aus SPEC.md dokumentieren
    • schema.org‑Begriffe als Referenz nutzen, aber keine externen Schemas einbinden
  • /docs
    • statische OpenAPI‑UI unter public/docs
    • Nginx‑Route sicherstellen

Vorgabe: Erzeuge ausschließlich Code, der SPEC.md exakt entspricht.

Was Codex daraus gebaut hat

JSON‑LD‑Serializer

Codex ergänzt drei Serializer:

  • EmployeeJsonLdSerializer
  • SkillJsonLdSerializer
  • EvidenceJsonLdSerializer

Alle mit:

  • @context: https://schema.org
  • URN‑basierten @id‑Werten gemäß SPEC:
    • urn:employee:*
    • urn:skill:*
    • urn:evidence:*

Listen werden als JSON‑LD‑ItemList ausgegeben.

Content Negotiation

Die neue ContentNegotiationMiddleware:

  • akzeptiert:
    • application/json
    • application/ld+json
    • */* oder leer → Default: JSON
  • liefert bei anderen Accept‑Headern:
    • 406 Not Acceptable
    • als RFC7807 Problem‑Details (application/problem+json)

Die API‑Pipeline in public/api.php wird erweitert:

  • Middleware‑Reihenfolge: ContentNegotiation → Auth → Scope

EmployeeApiController und SkillEvidenceGraphController werden umgestellt:

  • application/json → normale DTOs
  • application/ld+json → JSON‑LD‑Serializer
  • Listen als JSON‑LD‑ItemList

OpenAPI 3.1 & /docs

Codex baut eine neue openapi/openapi.yaml:

  • OpenAPI 3.1
  • dokumentierte Endpunktgruppen:
    • Auth
    • Employee CRUD
    • Skills/Evidence
    • Graph
    • Export/Erase
    • Audit
    • Health
    • Metrics
  • Accept‑Negotiation und ProblemDetails‑Schema sind enthalten
  • schema.org‑Begriffe werden referenziert, aber nicht extern eingebunden

Unter public/docs entsteht:

  • eine statische OpenAPI‑UI (index.html, Swagger UI)
  • eine Kopie der Spezifikation unter /docs/openapi.yaml

Nginx routet /docs auf diese UI.

Testing & Commit

Syntax‑Checks:

php -l app/Infrastructure/JsonLd/EmployeeJsonLdSerializer.php &&
php -l app/Infrastructure/JsonLd/SkillJsonLdSerializer.php &&
php -l app/Infrastructure/JsonLd/EvidenceJsonLdSerializer.php &&
php -l app/Http/Middleware/ContentNegotiationMiddleware.php &&
php -l app/Http/Controller/EmployeeApiController.php &&
php -l app/Http/Controller/SkillEvidenceGraphController.php &&
php -l public/api.php &&
php -l app/Http/bootstrap.php

Zusätzlich:

  • lokaler PHP‑Server für /docs gestartet
  • Commit: “Add JSON-LD serialization, content negotiation, and OpenAPI docs”
  • PR via make_pr erstellt

Review & Spec‑Abgleich zu Prompt 9

Der nächste Schritt: Spec‑Abgleich.

Prüffragen:

  • JSON‑LD‑Ausgabe korrekt?
  • URN‑IDs korrekt aufgebaut?
  • Content Negotiation korrekt implementiert?
  • openapi.yaml vollständig und valide?
  • /docs korrekt erreichbar?

Gefundene Abweichungen & Fixes

1. Content Negotiation Priorität

Problem: application/json wurde vor application/ld+json geprüft. Bei gemischten Accept‑Headern konnte dadurch fälschlich JSON statt JSON‑LD gewählt werden.

Fix: Reihenfolge angepasst:

  1. application/ld+json
  2. application/json
  3. */* oder leer → JSON‑Default

Jetzt ist die Präferenz deterministisch und spec‑konform.

2. OpenAPI 3.1 Validität

Problem: In openapi/openapi.yaml waren noch nullable: true‑Einträge vorhanden – OpenAPI‑3.0‑Stil, nicht JSON‑Schema‑konform für 3.1.

Fix:

  • nullable: true entfernt
  • durch Typ‑Unionen ersetzt, z.B.:

    type: [string, 'null']

  • sowohl in der kanonischen Datei als auch in der statischen Docs‑Kopie (public/docs/openapi.yaml)

Ergebnis des Spec‑Abgleichs

  • JSON‑LD‑Ausgabe korrekt? Ja, bereits spec‑konform, unverändert belassen.
  • URN‑IDs korrekt aufgebaut? Ja, urn:employee:*, urn:skill:*, urn:evidence:* – unverändert.
  • Content Negotiation korrekt? Nach Fix: Ja, mit korrekter Präferenz für application/ld+json.
  • OpenAPI.yaml vollständig & valide? Vollständigkeit wie im vorherigen PR, Validitätsproblem (nullable) behoben.
  • /docs erreichbar? Nginx‑Routing korrekt, /docs und /docs/openapi.yaml liefern 200.

Testing im Spec‑Abgleich

php -l app/Http/Middleware/ContentNegotiationMiddleware.php
ruby -e "require 'yaml'; YAML.load_file('openapi/openapi.yaml'); YAML.load_file('public/docs/openapi.yaml'); puts 'yaml ok'"
rg -n "nullable:" openapi/openapi.yaml public/docs/openapi.yaml   # keine Treffer
curl -s -o /tmp/docs.html -w "%{http_code}" http://127.0.0.1:8000/docs/      # 200
curl -s -o /tmp/openapi.yml -w "%{http_code}" http://127.0.0.1:8000/docs/openapi.yaml  # 200

Korrekturen committed, PR aktualisiert.

Fazit

Wir sind jetzt an einem Punkt, an dem das System:

  • JSON‑LD spricht,
  • Content Negotiation beherrscht,
  • eine vollständige OpenAPI 3.1 hat
  • und eine Docs‑UI unter /docs ausliefert.

Und ich sitze davor, schaue auf 3.791 Zeilen KI‑generierten Code und frage mich:

Wird das erste Hands‑on ein Desaster – oder eine positive Zumutung?

BigBrother is watching you — oder: Ich habe jetzt einen AuditWriter

Der nächste Spec‑Abgleich. Und diesmal geht es um etwas, das man im Alltag oft erst dann vermisst, wenn es fehlt: Auditierbarkeit.

Ohne AuditWriter wüsste ich bei einer Datenänderung nur, dass etwas passiert ist — aber nicht durch wen. Und das ist der Unterschied zwischen „irgendwas hat jemand irgendwo geändert“ und „dieser Actor hat diese Änderung zu diesem Zeitpunkt vorgenommen“.

Natürlich muss ich mir das DSGVO‑Thema noch einmal genauer anschauen. Auch die Umsetzung. Aber insgesamt hat Codex das sauber aus der SPEC extrahiert — und ich beschwere mich nicht, das Codex mir einen geschrieben hat.

Ein kurzer Blick in die SPEC:

  • DSGVO by design: Zweckbindung, Datenminimierung, Einwilligungen, Audit, Export, Löschung
  • Observability: Health, strukturierte Logs, Audit Trails, Metriken
  • KISS/YAGNI: Nur Employee, SkillGraph, Evidence, Consent, Audit

Und jetzt ist der Audit‑Teil davon tatsächlich implementiert.

Prompt 8 — Spec‑Abgleich (Audit‑Edition)

Der Spec‑Abgleich zu Prompt 7 war diesmal weniger „Fehler suchen“ und mehr „Audit‑Mechanik scharfstellen“. Und ja — es gab eine relevante Abweichung.

Was Codex gefunden und korrigiert hat

1. Skill‑Update nutzte die falsche ID

Abweichung: PATCH /api/v1/employees/{id}/skills/{skillId} behandelte {skillId} wie employee_skill.id. Das ist falsch — die Route sagt klar: Skill‑ID, nicht EmployeeSkill‑ID.

Fix: Neue Repository‑Methode: byEmployeeAndSkillId(EmployeeId, SkillId) Controller angepasst, Lookup korrigiert.

2. Audit‑Logging jetzt vollständig

Write‑Operationen werden jetzt korrekt auditiert:

  • Employee create/update/delete
  • Skill create/update
  • Evidence create

Und zusätzlich im Audit‑Log‑Channel geschrieben. Damit ist der AuditWriter offiziell Teil der Anwendung — so wie die SPEC es verlangt.

3. RFC7807 sauber umgesetzt

Fehler kommen jetzt als:

  • type
  • title
  • status
  • detail
  • errors (bei Validierung)

Ausgegeben als application/problem+json.

4. Validierung über Symfony Validator

PSR‑7 Payloads werden jetzt strukturiert geprüft. Feldfehler kommen sauber gesammelt zurück. Kein Wildwuchs, keine halbgaren Checks.

5. Graph‑Endpoint korrekt aggregiert

Employee + Skills + Evidence Reines JSON, kein JSON‑LD (noch nicht). Genau wie die SPEC es verlangt.

Gefundene Abweichung (Detail)

Problem: Skill‑Update interpretierte skillId inkonsistent.

Fix:

  • Neues Repository‑Contract
  • Neue DB‑Implementierung
  • Controller‑Umstellung
  • Lookup jetzt korrekt über (employee_id, skill_id)

Testing

Alles grün:

php -l app/Domain/Skill/EmployeeSkillRepository.php
php -l app/Infrastructure/Persistence/RepositoryDb/EmployeeSkillRepositoryDb.php
php -l app/Http/Controller/SkillEvidenceGraphController.php

Commit c332777 erstellt, PR aktualisiert.

Fazit

Ich habe jetzt einen AuditWriter. Nicht als Teil des Workflows — sondern als Teil der Anwendung.

Und damit ist das System offiziell in der Lage, nicht nur Daten zu verändern, sondern auch nachvollziehbar zu dokumentieren, wer diese Veränderung ausgelöst hat.

Das ist kein „BigBrother“. Das ist professionelle Softwareentwicklung.

Weiter geht’s. Prompt 9 wartet.

P.S. ich habe jetzt schon gefühlte 1000 mal Speck geschrieben.
Vielleicht ist es Zeit für eine kurze Pause – Frühstück + Speck.
Oder Zeit für ein wenig Speck weg und eine Runde mit dem Hund.

Prompt 7 — Skills, Evidence & der Graph‑Endpoint

Oder: Der Moment, in dem man merkt, wie viel Naming‑Pain einem ein LLM erspart

Das Beste an diesem Prozess? Wer selbst schon einmal Code geschrieben hat – Zeile für Zeile, ohne Copy & Paste von StackOverflow – kennt diesen Moment:

„Wie nenne ich diese Funktion? Diese Variable? Diesen Endpoint? So, dass Kolleg:innen verstehen, was ich wollte, was ich gemacht habe, welche Akzeptanzkriterien ich im Kopf hatte?“

Dieser ganze Naming‑Pain fällt hier einfach weg.

Okay, das eine oder andere musste ich in der SPEC.md festnageln. Aber das bedeutet: einmal den Kopf rauchen lassen – und dann ab dafür. Der Rest ist Fließband.

Prompt 7 – Skills, Evidence, Graph Endpoint

Der Auftrag war klar:

Implementiere gemäß SPEC.md:

Skills

  • POST /employees/{id}/skills
  • PATCH /employees/{id}/skills/{skillId}

Evidence

  • POST /employees/{id}/evidence

Graph Endpoint

  • GET /employees/{id}/graph
  • Aggregierter DTO: Employee + Skills + Evidence
  • JSON‑Ausgabe (noch ohne JSON‑LD)

Validierung

  • PSR‑7 Request
  • Symfony Validator
  • Fehlerformat: RFC7807 Problem Details

Logging

  • App‑Log für alle Requests
  • Audit‑Log für alle Write‑Operationen

Kein JSON‑LD in diesem Schritt. Keine Extras. Nur SPEC.

Codex legt los

Und wie immer beginnt alles mit dem Murmeltier‑Prompt:

„Lies die SPEC.md vollständig. Du implementierst das Projekt strikt nach dieser Spezifikation. Du darfst keine eigenen Patterns, Libraries oder Strukturen erfinden…“

Dann geht’s los. Und der Codex‑Log liest sich wie eine Mischung aus To‑Do‑Liste und innerem Monolog eines sehr gewissenhaften Juniors:

  • Starting repository inspection
  • Planning dependency management
  • Implementing API entrypoints
  • Adding secure session cookie parameters
  • Implementing repo mapping
  • Validating PHP syntax
  • Preparing commit with spec checks

Nichts Magisches. Aber sehr zuverlässig.

Was tatsächlich umgesetzt wurde

Neue Endpunkte

Verdrahtet in FastRoute:

  • POST /api/v1/employees/{id}/skills
  • PATCH /api/v1/employees/{id}/skills/{skillId}
  • POST /api/v1/employees/{id}/evidence
  • GET /api/v1/employees/{id}/graph

Controller‑Logik

  • Skill‑Erstellung inkl. XOR‑Constraint: genau eins von skill_id oder custom_label
  • Skill‑Update
  • Evidence‑Erstellung
  • Graph‑Aggregation: Employee + Skills + Evidence als JSON

Validierung

  • Symfony Validator integriert
  • RequestValidator ergänzt
  • RFC7807‑konforme Fehler‑Responses (application/problem+json)

Audit‑Logging

  • Zentral über AuditWriter
  • Für alle neuen Write‑Operationen angebunden
  • Auch Employee‑Writes profitieren davon

Repository‑Erweiterungen

  • employee_skill, evidence, skill, audit_log konkret implementiert
  • byEmployeeId‑Methoden ergänzt, damit der Graph‑Endpoint aggregieren kann

Bootstrap‑Erweiterungen

  • Validator
  • Neue Repositories
  • AuditWriter
  • OpenAPI aktualisiert
  • symfony/validator als Dependency ergänzt

Testing

Composer

⚠️ composer install scheitert wie immer an der Umgebung (Tunnel‑Fehler, 403).

PHP‑Syntax

Alles grün:

php -l app/Http/Controller/SkillEvidenceGraphController.php &&
php -l app/Http/Controller/EmployeeApiController.php &&
php -l app/Http/Request/RequestValidator.php &&
php -l public/api.php &&
php -l app/Infrastructure/Persistence/RepositoryDb/EmployeeSkillRepositoryDb.php &&
php -l app/Infrastructure/Persistence/RepositoryDb/EvidenceRepositoryDb.php &&
php -l app/Infrastructure/Persistence/RepositoryDb/AuditLogRepositoryDb.php &&
php -l app/Infrastructure/Persistence/RepositoryDb/SkillRepositoryDb.php &&
php -l app/Http/bootstrap.php &&
php -l app/Http/Response/JsonResponseFactory.php

Commit erstellt, PR angelegt.

Fazit

Prompt 7 ist einer dieser Schritte, die man im klassischen Coding gerne vor sich herschiebt, weil sie viel Kleinkram enthalten: Validierung, Aggregation, Repositories, Logging, Fehlerformate.

Im VIPe‑Prozess ist es einfach ein weiterer Baustein. Ein klarer Schritt. Ein definierter Output.

Und genau das macht diesen Workflow so angenehm: Kein Naming‑Pain. Keine Architektur‑Diskussionen. Keine spontanen „Ich mach das mal anders“-Momente.

Nur SPEC → Prompt → Output.

Weiter geht’s. Prompt 8 wartet.

Prompt 5 — Spec‑Abgleich (Take 2)

Oder: Warum sich diese Reviews immer anfühlen wie eine Agentur‑Simulation

Diese Spec‑Abgleiche muten immer ein wenig an wie eine Real‑World‑Simulation aus einer Digitalagentur. Der Kunde (die SPEC.md) hat etwas definiert. Der Entwickler (Codex) hat etwas gebaut. Und jetzt sitzen wir da und prüfen, ob beides wirklich zusammenpasst.

Und wie in jeder Agentur‑Woche gibt es natürlich Abweichungen.

Die wichtigste diesmal: Der Logout‑Endpoint fehlte.

Ja, wirklich. Der Auth‑Contract sagt klar: login / logout / me. Aber Codex hatte nur login und me implementiert. Ein klassischer Fall von „SPEC überflogen, nicht gelesen“. Ein Fehler, den Juniors machen. Und Seniors. Und LLMs.

Alles fein – aber das Log möchte ich dir (wer auch immer du bist) nicht vorenthalten.

Was Codex korrigiert hat

1. Fehlender Logout‑Endpoint

Abweichung: POST /api/v1/auth/logout fehlte komplett. Fix: Route ergänzt, Controller‑Logik implementiert.

2. Unvollständiger Session‑Lifecycle

Abweichung:

  • Keine Session‑ID‑Rotation beim Login
  • Kein korrektes Invalidieren beim Logout

Fix:

  • session_regenerate_id(true) beim Login
  • Vollständiges Leeren + Cookie‑Löschen + session_destroy() beim Logout
  • Weiterhin HttpOnly + SameSite=Lax

3. Logging nutzte falsche Request‑Instanz

Abweichung: Das Logging griff auf die ursprüngliche Request‑Instanz zu. Ergebnis: actor_id wurde trotz erfolgreicher Auth als anonymous geloggt.

Fix: Die post‑middleware Request‑Instanz wird jetzt für Logging genutzt.

4. Content‑Negotiation fehlte

Abweichung: Employee‑Responses ignorierten den Accept‑Header. application/json und application/ld+json wurden nicht unterschieden.

Fix: Content‑Negotiation ergänzt, Content‑Type korrekt gesetzt.

Ergebnis der Prüfbereiche

  • Routing: korrekt, alle Auth‑ und Employee‑Routen sauber verdrahtet
  • Auth: Session + Bearer + Scopes, Middleware‑Reihenfolge stimmt
  • Sessions: korrekt gesetzt, inklusive Regeneration und sauberem Logout
  • Employee CRUD: vollständig
  • HTML‑Views: minimal, funktional, spec‑konform

Testing

php -l public/api.php &&
php -l app/Infrastructure/Auth/AuthService.php &&
php -l app/Http/Controller/AuthController.php &&
php -l app/Http/Controller/EmployeeApiController.php

Alles grün.

Commit f86bfe9 wurde erstellt, PR aktualisiert.

Fazit

Diese Spec‑Abgleiche sind der Moment, in dem man merkt, wie nah LLM‑Entwicklung an echter Agenturarbeit ist:

  • Die Maschine macht dieselben Fehler wie Menschen.
  • Sie überliest Dinge.
  • Sie ergänzt Dinge, die nicht gefordert waren.
  • Sie vergisst Dinge, die klar definiert waren.

Und genau deshalb ist VIPe so wertvoll: Der Prozess fängt diese Drift ab, bevor sie sich durch das ganze Projekt frisst.

Weiter geht’s. Prompt 7 wartet.

Prompt 5, Take 2 – Zurück aufs Fließband

Der letzte Akt gestern: REVERT auf Prompt 5. Der Workflow war aus der Spur gelaufen, der Review‑Baustein zu groß geworden, der Prozess nicht mehr minimal, sondern überambitioniert.

Dann: Binge‑Watching, Schlaf, Reset. Und jetzt wieder vor dem Rechner.

Mit dem gleichen unguten Gefühl wie vorher: Egal ob VIBE‑Coding oder VIPe‑Coding – der gesamte Prozess ist weit entfernt von 99,9 % Industriestandard, was Input und Output angeht.

Egal, wie fest der Prompt geschnürt ist, das Ergebnis ist – zumindest, wenn man externe Tools nutzt – abhängig von den „Launen“ der Anwendung. OpenAI und Co. nennen es Feature. Für mich ist es der Tod des Entwicklers.

Aber kein Stress: Das hier ist ein Test. Ein Proof of Concept, um genau diese Grenzen erfahrbar zu machen.

Zurück zum Fließband.

Der Murmeltier‑Prompt

Wie immer beginnt alles mit dem gleichen Ritual – dem Murmeltier‑Prompt:

„Lies die Datei SPEC.md vollständig. Du implementierst das Projekt strikt nach dieser Spezifikation. Du darfst keine eigenen Patterns, Libraries oder Strukturen erfinden…“

Der Rahmen steht. Die Regeln sind klar. Die Maschine weiß (zumindest behauptet sie es), was sie zu tun hat.

Dann der nächste Iterationsschritt: Prompt 5 (erneut) Routing + Auth + Employee CRUD

Implementiere:

  • FastRoute Routing (UI + API)
  • AuthMiddleware + ScopeMiddleware
  • Session‑Login (POST /auth/login)
  • GET /me
  • Employee CRUD (API)
  • Einfache HTML‑Views für Employee‑Liste + Detail

Beachte:

  • PSR‑7 Request/Response
  • nyholm/psr7-server
  • SapiEmitter
  • PII‑Masking in Logs

Codex am Fließband

Der Codex‑Log liest sich wie eine To‑Do‑Liste im Kopf eines sehr disziplinierten Juniors:

  • Starting repository inspection
  • Checking for composer.json presence
  • Planning dependency management
  • Planning response helpers
  • Implementing database repository methods
  • Implementing API entrypoints
  • Adding secure session cookie parameters
  • Validating PHP syntax on changes
  • Testing composer availability
  • Preparing commit with spec checks
  • Committing recent changes

Kein Agent‑Skill, kein „magischer“ Workflow – nur stumpfes, aber sauberes Abarbeiten.

Was tatsächlich passiert ist

In der Zusammenfassung wird klar, was dieser zweite Anlauf von Prompt 5 wirklich geleistet hat:

  • API‑Frontcontroller public/api.php mit FastRoute‑Routing für POST /api/v1/auth/login, GET /api/v1/me und vollständigem Employee‑CRUD (GET/POST/GET{id}/PUT{id}/DELETE{id}), inkl. Middleware‑Pipeline (AuthMiddleware, ScopeMiddleware) und SapiEmitter‑Ausgabe.
  • UI‑Frontcontroller public/index.php auf FastRoute umgestellt, HTML‑Routen für Employee‑Liste (/) und Detail (/employees/{id}) angebunden.
  • PSR‑7‑Bootstrap mit nyholm/psr7-server: zentrale Request‑Erzeugung, Verdrahtung von Services (PDO, Auth, Logger, Repository, JSON‑Response‑Factory).
  • Auth & Scopes:
    • Session‑Login mit HttpOnly + SameSite=Lax
    • Bearer‑Token‑Support im AuthService
    • AuthMiddleware für 401, ScopeMiddleware für 403 mit RFC7807‑ähnlichen Problem‑Responses
    • AuthController mit login() und me()
  • Employee CRUD im EmployeeApiController: Create/Read/Update/Delete/List – inklusive JSON‑LD‑Feldern (@context, @type, @id) in der Serialisierung.
  • SSR‑Views für Employee‑Liste + Detail, inkl. Skip‑Link.
  • Datenzugriff:
    • EmployeeRepository um all() und delete() erweitert
    • EmployeeRepositoryDb mit Upsert, Lookup, Listenabfrage und Soft‑Delete implementiert
    • PDO‑Factory für MariaDB ergänzt
  • Logging & PII‑Masking:
    • JSON‑Logging mit Maskierung von email, phone, address, consent.scope
    • E‑Mail‑Maskierung im Stil a***@domain
  • Infrastruktur:
    • Middleware‑Dispatcher
    • JSON‑Response‑Factory
    • Composer‑Dependencies: fast-route, nyholm/psr7-server, laminas-httphandlerrunner

Testing & Grenzen

Die Syntax‑Checks laufen durch:

php -l public/api.php &&
php -l public/index.php &&
php -l app/Infrastructure/Auth/AuthService.php &&
php -l app/Infrastructure/Logging/JsonLogger.php &&
php -l app/Http/Controller/EmployeeApiController.php &&
php -l app/Http/Controller/EmployeeViewController.php &&
php -l app/Http/Controller/AuthController.php &&
php -l app/Infrastructure/Persistence/RepositoryDb/EmployeeRepositoryDb.php &&
php -l app/Http/bootstrap.php

Alles grün.

composer install scheitert erwartungsgemäß an Netzwerk/Packagist‑Restriktionen. Ein Playwright‑page.goto('http://127.0.0.1/') läuft ins Leere – kein Webserver in dieser Umgebung.

Trotzdem: Die Änderungen werden committed (9aa8dfc), ein PR via make_pr erstellt.

Prompt 5 ist damit – im zweiten Anlauf – wirklich da, wo er hingehört: nicht perfekt, nicht produktionsreif, aber konsistent mit der SPEC und nachvollziehbar dokumentiert.

Fazit dieses Schritts

Der Tag gestern endete mit einem REVERT. Heute beginnt er mit einem konsistenten Commit.

Und irgendwo dazwischen liegt die eigentliche Wahrheit von VIPe: Es geht nicht darum, dass die Maschine perfekt ist. Es geht darum, dass der Prozess so gebaut ist, dass man jeden Drift sieht, zurückdrehen und neu ansetzen kann.

Zurück aufs Fließband. Der nächste Prompt wartet schon.

Wenn der Workflow stolpert – oder: Wie schnell ein LLM aus der Spur läuft

Das Schlimmste, was in diesem iterativen Prozess passieren kann, ist nicht ein Fehler. Fehler sind okay. Fehler sind sichtbar. Fehler kann man korrigieren.Das wirklich Gefährliche ist eine Unterbrechung.

Ein kleiner Bruch im Ablauf, ein fehlender Zwischenschritt, ein Prompt, der nicht vorbereitet war – und plötzlich driftet der ganze Prozess ein paar Grad aus der Spur. Nicht viel. Nur ein bisschen. Aber genug, um später an einer völlig anderen Stelle zu landen.

Genau das ist mir bei Prompt 5 passiert.

Ich wollte eigentlich nur den vorbereiteten „Review + Spec‑Abgleich“ einfügen. Ein Schritt, der sonst völlig mechanisch abläuft: Copy, Paste, Weiter.

Aber der Baustein fehlte. Und statt kurz zu stoppen, habe ich ihn neu geschrieben.

Aus einer simplen Frage wie „Funktioniert das Routing korrekt?“ wurde plötzlich ein hochdetaillierter, fast schon überambitionierter Prüfplan.

Aus dem Bauch heraus klang das sogar richtig. Formal korrekt. Sauber strukturiert. Aber eben auch: eine Einladung an das LLM, sich vom „Coding Monkey“ zum „übermotivierten Architekten“ aufzuschwingen.

Und das ist der Punkt, an dem man im VIPe‑Prozess hellhörig werden muss.

Der Drift

Hier ist der Moment, an dem der Zug leicht aus der Spur geraten ist.

Ich habe den fehlenden Review‑Prompt neu formuliert – und er sah plötzlich so aus:

API‑Routen gemäß SPEC.md

    POST /api/v1/auth/login
    GET /api/v1/me
    Employee CRUD:
        GET /api/v1/employees
        GET /api/v1/employees/{id}
        POST /api/v1/employees
        PUT /api/v1/employees/{id}
        DELETE /api/v1/employees/{id}

Prüfen:
    Stimmen die Pfade exakt?
    Werden Query‑Strings korrekt durchgereicht?
    Wird FastRoute gemäß SPEC verwendet?
    Werden PSR‑7 Requests korrekt erzeugt?
    Wird SapiEmitter genutzt?

Das ist nicht falsch. Aber es ist auch nicht mehr das, was ich ursprünglich wollte.

Es ist zu viel. Zu detailliert. Zu „architektonisch“. Und damit eine Steilvorlage für das LLM, sich in Strukturen zu verlieren, die ich nie beauftragt habe.

Der Prozess lebt davon, dass jeder Schritt minimal ist.

  • minimal technisch
  • minimal interpretierbar
  • minimal offen für kreative Ausschläge

Wenn ich an dieser Stelle nicht sofort gemerkt hätte, dass der Prompt zu groß geworden ist, hätte Codex im nächsten Schritt angefangen, Dinge zu „optimieren“, die gar nicht zur SPEC gehören.

REVERT und weiter geht’s

Also: zurück auf Anfang. Den Prompt verworfen. Den vorbereiteten Baustein wiederhergestellt. Workflow repariert.

Und genau das ist die eigentliche Erkenntnis aus diesem kleinen Ausflug:

Ein LLM ist kein Entwickler. Es ist ein Verstärker.

Wenn du ihm einen klaren, kleinen, präzisen Schritt gibst, liefert es genau das. Wenn du ihm einen zu großen Schritt gibst, liefert es eine zu große Antwort. Wenn du ihm einen unklaren Schritt gibst, liefert es Chaos.

Der iterative Prozess funktioniert nur, wenn jeder Schritt exakt vorbereitet ist. Fehlt ein Baustein, entsteht sofort Drift. Und Drift ist der Feind.

Also: REVERT. Weiter geht’s. Prompt 5 wartet darauf, abgeschlossen zu werden.

Prompt 5 — Routing, Auth & Employee CRUD

Mit Prompt 5 fühlt sich das Projekt zum ersten Mal wie eine „richtige“ Anwendung an. Bis hierhin hatten wir Struktur, Migrations, Domain‑Layer. Jetzt kommen Routing, Auth, Sessions, CRUD und HTML‑Views dazu – also genau der Teil, den man später im Alltag ständig anfasst.

Der Auftrag war klar umrissen:

FastRoute für UI und API, AuthMiddleware, ScopeMiddleware, Session‑Login, GET /me, Employee‑CRUD und einfache HTML‑Views. Dazu die Vorgaben aus der SPEC: PSR‑7, nyholm/psr7-server, SapiEmitter, PII‑Masking im Logging.

Codex startet mit einem Blick auf die bestehende Struktur: public‑Files, Nginx‑Routing, vorhandene Ordner. Dann wird der Kern geplant: Frontcontroller, Kernel, Middleware‑Pipeline, Controller, Views. Es ist dieser typische Moment, in dem aus einem Haufen Dateien langsam eine Anwendung wird.

PSR‑7 Frontcontroller & Kernel

Zuerst setzt Codex die PSR‑7‑Frontcontroller für UI und API um. Requests werden über nyholm/psr7-server erzeugt, Responses über den SapiEmitter ausgegeben. Session‑Cookies werden mit HttpOnly und SameSite=Lax gesetzt – wie in der SPEC vorgesehen.

Darauf aufbauend entsteht ein zentraler Kernel mit FastRoute‑Routing für UI und API. Die API‑Routen folgen dem Schema /api/v1/..., inklusive:

  • POST /api/v1/auth/login
  • GET /api/v1/me
  • /api/v1/employees (CRUD)

Für die UI werden /, /employees und /employees/{id} als SSR‑Views angebunden.

Auth, Scopes & Actor‑Auflösung

Die Middleware‑Pipeline wird um AuthMiddleware und ScopeMiddleware ergänzt. Routen bekommen Scopes wie employees.read und employees.write zugewiesen.

Der AuthController übernimmt Session‑Login und /me, während der Kernel Actor‑Informationen aus Session oder Bearer‑Token auflöst.

Employee CRUD & Views

Für Employee wird ein kompletter CRUD‑Flow auf der API‑Seite umgesetzt:

  • Listen
  • Finden
  • Erstellen (mit UUIDv4)
  • Aktualisieren
  • Soft‑Delete

Der Datenzugriff läuft über PDO, passend zur bestehenden Infrastruktur.

Parallel dazu entstehen einfache SSR‑HTML‑Views für die Employee‑Liste und das Detail. Mit Skip‑Link, wie in der SPEC gefordert – kein Design‑Feuerwerk, aber funktional und zugänglich.

Responses, Logging & PII‑Masking

Codex ergänzt ein Response‑Helper‑Modul für JSON, HTML und RFC7807‑Problem‑Responses. Dazu kommt strukturiertes Logging mit PII‑Masking:

  • E‑Mail
  • Telefon
  • Adresse
  • consent.scope

werden maskiert, E‑Mails im Stil a***@domain.

Die benötigten Composer‑Dependencies für FastRoute, PSR‑7 und den Emitter werden in composer.json ergänzt – auch wenn composer install in der Umgebung später scheitert.

Testing & Grenzen der Umgebung

Die PHP‑Syntax aller relevanten Dateien wird geprüft: Frontcontroller, Kernel, Controller, Middleware, Logging, Persistence, Repositories, Response‑Helper – alles grün.

composer install schlägt erwartungsgemäß fehl (Packagist nicht erreichbar), und ein kurzer Test mit dem PHP‑Built‑in‑Server endet in einem 500er, weil vendor/autoload.php fehlt. Technisch ist das Setup aber konsistent.

Commit 7196740 wird erstellt, ein PR via make_pr angelegt.

Prompt 5 markiert damit den Punkt, an dem das System nicht mehr nur wie ein Gerüst aussieht, sondern wie eine Anwendung, die kurz davor ist, wirklich zu laufen.