← Code-Übersicht

operations-playbook.yaml

Pfad: doku/operations-playbook.yaml

Ext: yaml

Größe: 8775 Bytes

Geändert: 2026-07-16 12:00:06+02

version: 1
project: demo.karlkratz.com
language: de
last_reviewed: 2026-07-16

storage:
  dokumentation:
    canonical_store:
      type: postgres-table
      table: public.dokumentation
      repo: Demo\Infrastructure\Persistence\PostgresDokumentationRepository
      route_read_list: "GET /doku"
      route_read_item: "GET /doku/{slug}"
      route_save: "POST /doku/{slug}"
      route_history: "GET /doku/{slug}/{version}"
      write_mode:
        edit_only_via_route: true
        create_only_via_sql: true
    create_flow:
      required: "INSERT INTO dokumentation (slug, titel, body, tags) ... ON CONFLICT (slug) DO NOTHING;"
      migration_entry_points:
        - migrations/0002_dokumentation.sql
        - migrations/0003_dokumentation_seed.sql
        - migrations/0008_system_seed.sql
    edit_flow:
      controller: Demo\Infrastructure\Http\DokuSpeichernAction
      use_case: Demo\Application\Dokumentation\SpeichereInhalt
      converter: Demo\Infrastructure\Content\HtmlToMarkdown
      repository_call: speichereInhalt($slug, $markdown)
      persistence_query: UPDATE dokumentation SET body = :body WHERE slug = :slug
      redirect: "/doku/{slug}"
      side_effect: audit.fn_write_history auf public.history_dokumentation
    query_flow:
      controller: Demo\Infrastructure\Http\DokuDetailAction
      use_cases:
        - Demo\Application\Dokumentation\ZeigeDokumentation
        - Demo\Application\Dokumentation\ListeDokumentationVersionen
        - Demo\Application\Dokumentation\ZeigeDokumentationVersion
      renderer:
        current_and_edit: Demo\Infrastructure\Content\MarkdownToHtml
        view: src/Infrastructure/Http/view/doku-detail.php

  prozesse:
    canonical_store:
      type: postgres-table
      table: public.prozesse
      detail_table: public.prozess_schritt
      repo: Demo\Infrastructure\Persistence\PostgresProzessRepository
      ports: Demo\Domain\Prozess\ProzessRepository
      route_list: "GET /prozesse"
      route_detail: "GET /prozesse/{slug}"
      scope: read_only_in_ui
    update_flow:
      direct_ui_edit: false
      persistence_via_db:
        prefer_migration_files:
          - migrations/0005_prozesse.sql
          - migrations/0006_prozesse_seed.sql
          - migrations/0008_system_seed.sql
        min_verification: sql constraints and trigger checks

  code_spiegel:
    canonical_store:
      type: postgres-table
      table: public.code_datei
      history_table: public.history_code_datei
      repo: Demo\Infrastructure\Persistence\PostgresCodeDateiRepository
      repo_use_cases:
        - Demo\Application\Code\ListeCodeDateien
        - Demo\Application\Code\ZeigeCodeDatei
        - Demo\Application\Code\SynchronisiereCodeDateien
        - Demo\Application\Code\ListeCodeDateiVersionen
        - Demo\Application\Code\ZeigeCodeDateiVersion
    refresh_flow:
      on_routes:
        - "GET /code"
        - "GET /code/{id}"
        - "GET /code/{id}/{version}"
      scanner: Demo\Infrastructure\Code\CodeDateiScanner
      sync_use_case: Demo\Application\Code\SynchronisiereCodeDateien
      scanner_extensions: ['php','js','css','sql','sh','yml','yaml','json']
      scanner_ignores: ['.git','credentials','node_modules','vendor','tmp']
      sync_policy:
        existing_path_equal_hash: no_update
        existing_path_changed_hash: update
        missing_in_scan: delete
        new_in_scan: insert
      sync_sequence:
        1: sync->execute(scanner->scan())
        2: list/view use case reads from DB
      history:
        trigger_mode: automatic via audit.fn_write_history
        source_table: public.code_datei

operations:
  doku_templates:
    check_if_exists:
      description: "Vor jedem Write prüfen, ob Slug schon existiert."
      sql: |
        SELECT id
          FROM public.dokumentation
         WHERE slug = :slug;
    create_new_dokumentation:
      description: "Neue Doku nur per SQL anlegen. Anschließend via UI editierbar."
      example_rows:
        - slug: "neue-doku"
          titel: "Neue Dokumentation"
          body: |
            # Neue Dokumentation

            Kurze Einordnung, Zielbild und Ablaufbeschreibung.

            - Erstes Kapitel: Kontext
            - Zweites Kapitel: Umsetzung
          tags:
            - dokumentation
            - inhalt
        - slug: "projekt-ueberblick"
          titel: "Projekt-Überblick"
          body: |
            # Projekt-Überblick

            Diese Seite beschreibt Zweck, Architektur und Betriebsweise.

            ## Architektur

            Hexagonal aufgebaut mit klarer Schichtentrennung.
          tags:
            - system
            - ueberblick
      sql: |
        INSERT INTO public.dokumentation (slug, titel, body, tags)
        VALUES (:slug, :titel, :body_markdown, :tags)
        ON CONFLICT (slug) DO UPDATE
            SET body = EXCLUDED.body,
                titel = EXCLUDED.titel,
                tags = EXCLUDED.tags;
      note: >
        `ON CONFLICT` verhindert Fehler bei Wiederholung und hält den Flow robust.
        Für reine Neuanlage kann `DO NOTHING` genutzt werden.
      optional_followup:
        - "INSERT INTO public.doku_prozess (doku_id, prozess_id) ... optional"
        - "Link zu Prozessdoku per `SELECT id ...` + `INSERT` über prozess_id möglich"
  create_or_update_via_ui:
    description: "Update bestehender Slugs über den definierten System-Flow."
    steps:
      - "GET /doku/{slug}"
      - "Inhalt ändern und abschicken"
      - "POST /doku/{slug}"
    route_call: "POST /doku/{slug}"
    form_field: "inhalt"
    request_payload_hint: |
      inhalt=<html-sanitized-content>
    success_observable:
      - "Redirect auf /doku/{slug}"
      - "Neue Zeile in public.history_dokumentation nach dem ersten Update"
    latest_history_probe:
      sql: |
        SELECT history_id, operation, geaendert_am
        FROM public.history_dokumentation
       WHERE daten->>'slug' = :slug
       ORDER BY geaendert_am DESC, history_id DESC
       LIMIT 1;
    read_version_snapshot:
      sql: |
        SELECT daten
        FROM public.history_dokumentation
       WHERE history_id = :history_id
         AND daten->>'slug' = :slug;
    verify_ui_after_change:
      steps:
        - "GET /doku/{slug} -> neuer Inhalt in HTML"
        - "GET /doku/{slug}/{latest_history_id} -> unveränderter Snapshot"

  prozesse_templates:
    description: "Prozesse werden als Lesedaten gepflegt; Änderungen gehen über SQL/Migrations."
    read:
      list_sql: |
        SELECT p.slug, p.titel, p.tags, p.updated_at
        FROM public.prozesse p
        ORDER BY p.updated_at DESC;
      detail_sql: |
        SELECT p.slug, p.titel, p.beschreibung, ps.name, ps.beschreibung
        FROM public.prozesse p
        JOIN public.prozess_schritt ps ON ps.prozess_id = p.id
       WHERE p.slug = :slug
       ORDER BY ps.id;
    seed_pattern:
      note: >
        Neue Prozesse immer als neue Migration anlegen (z. B. 000X_*.sql),
        bestehende Seeds über 0008_system_seed.sql/0006_prozesse_seed.sql ersetzen,
        danach Reihenfolge/Constraints prüfen.
      migration_snippet: |
        INSERT INTO public.prozesse (slug, titel, beschreibung, tags)
        VALUES (:slug, :titel, :beschreibung, :tags);

  code_templates:
    post_change_verify:
      path_sql: |
        SELECT id, relativer_pfad, code_hash, datei_mtime, updated_at
        FROM public.code_datei
       WHERE relativer_pfad = :relativer_pfad;
      history_sql: |
        SELECT history_id, operation, geaendert_am, daten->>'code_hash' AS code_hash
        FROM public.history_code_datei
       WHERE (daten->>'relativer_pfad') = :relativer_pfad
       ORDER BY geaendert_am DESC, history_id DESC
       LIMIT 5;
      sync_effect:
        - "Route aufrufen: GET /code/{id} (oder GET /code) nach Dateiaenderung"
        - "Repository-Sync: Insert/Update/Delete via PostgresCodeDateiRepository::synchronisiere()"

  when_user_says_create_doku:
    - check if slug exists in public.dokumentation
    - if exists: edit through POST /doku/{slug}
    - if not exists: add row in public.dokumentation via migration/SQL before using UI
  when_user_says_update_doku:
    - open route GET /doku/{slug}
    - edit content in editor
    - POST /doku/{slug}
    - verify with GET /doku/{slug} and GET /doku/{slug}/{latest_version}
  when_user_says_create_or_update_prozess:
    - do migration change in src/migrations first (0005/0006/0008 family)
    - verify in DB and UI via /prozesse and /prozesse/{slug}
  when_user_says_update_code_after_file_change:
    - ensure changed files are in scanner scope (extensions/ignores)
    - call /code route once (or /code/{id}) to trigger sync
    - verify row changed in public.code_datei and snapshots in history_code_datei