From 79314d61402ae498a670aada9b57da271b0d7feb Mon Sep 17 00:00:00 2001 From: "marcus.hinz" Date: Sat, 4 Jul 2026 19:09:13 +0200 Subject: [PATCH] =?UTF-8?q?sync:=20global=E2=86=92pool,=20pool-guard=20rau?= =?UTF-8?q?s,=20Session-Watcher-Setup,=20verbotenes=20Wort=20Ehrlich?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 08-07/.gitignore | 1 + 08-07/ai-control.json | 8 + ai-control/.gitignore | 1 + ai-control/ai-control.json | 3 + ai-control/bin/connect-test | 7 + ai-control/bin/kc-backup | 10 + ai-control/bin/kc-delete | 7 + ai-control/bin/kc-restore | 9 + ai-control/bin/kc-status | 10 + ai-control/bin/oauth-configdir-test | 31 ++ ai-control/hand.png | Bin 0 -> 13266 bytes ai-control/memory/MEMORY.md | 6 +- ai-control/memory/ai-control-pool-setup.md | 28 ++ ai-control/memory/feedback-go-pflicht.md | 14 + .../memory/feedback-terminal-test-08-07.md | 20 ++ ai-control/memory/multi-account-keychain.md | 16 +- limbach/.claude/settings.json | 18 +- limbach/.gitignore | 1 + limbach/OFFENE-PUNKTE.md | 3 + limbach/ai-control.json | 6 + limbach/memory/git-permission-scope-offen.md | 2 +- misc/.claude/settings.json | 23 ++ misc/.gitignore | 1 + misc/OFFENE-PUNKTE.md | 3 + misc/ai-control.json | 3 + {global => pool}/CLAUDE.md | 3 +- {global => pool}/bin/git-sync | 0 {global => pool}/bin/new-project | 0 {global => pool}/commands/git-sync.md | 2 +- {global => pool}/settings.json | 6 +- robotunits/.gitignore | 1 + robotunits/GEKO-16.xml | 338 ++++++++++++++++++ robotunits/ai-control.json | 3 + robotunits/bin/ghostty-wincount.swift | 15 + robotunits/bin/pool-guard | 32 ++ robotunits/memory/MEMORY.md | 4 +- .../memory/cpq-configurator-modul-registry.md | 19 + robotunits/memory/cpq-verbau-90grad-steher.md | 26 +- .../feedback-suche-nur-projektordner.md | 14 + wittgenstein/.gitignore | 1 + wittgenstein/ai-control.json | 3 + wuerth-plato/.gitignore | 1 + wuerth-plato/ai-control.json | 3 + 43 files changed, 669 insertions(+), 33 deletions(-) create mode 100644 08-07/.gitignore create mode 100644 08-07/ai-control.json create mode 100644 ai-control/.gitignore create mode 100644 ai-control/ai-control.json create mode 100755 ai-control/bin/connect-test create mode 100755 ai-control/bin/kc-backup create mode 100755 ai-control/bin/kc-delete create mode 100755 ai-control/bin/kc-restore create mode 100755 ai-control/bin/kc-status create mode 100755 ai-control/bin/oauth-configdir-test create mode 100644 ai-control/hand.png create mode 100644 ai-control/memory/ai-control-pool-setup.md create mode 100644 ai-control/memory/feedback-go-pflicht.md create mode 100644 ai-control/memory/feedback-terminal-test-08-07.md create mode 100644 limbach/.gitignore create mode 100644 limbach/OFFENE-PUNKTE.md create mode 100644 limbach/ai-control.json create mode 100644 misc/.claude/settings.json create mode 100644 misc/.gitignore create mode 100644 misc/OFFENE-PUNKTE.md create mode 100644 misc/ai-control.json rename {global => pool}/CLAUDE.md (95%) rename {global => pool}/bin/git-sync (100%) rename {global => pool}/bin/new-project (100%) rename {global => pool}/commands/git-sync.md (90%) rename {global => pool}/settings.json (83%) create mode 100644 robotunits/.gitignore create mode 100644 robotunits/GEKO-16.xml create mode 100644 robotunits/ai-control.json create mode 100644 robotunits/bin/ghostty-wincount.swift create mode 100755 robotunits/bin/pool-guard create mode 100644 robotunits/memory/cpq-configurator-modul-registry.md create mode 100644 robotunits/memory/feedback-suche-nur-projektordner.md create mode 100644 wittgenstein/.gitignore create mode 100644 wittgenstein/ai-control.json create mode 100644 wuerth-plato/.gitignore create mode 100644 wuerth-plato/ai-control.json diff --git a/08-07/.gitignore b/08-07/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/08-07/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/08-07/ai-control.json b/08-07/ai-control.json new file mode 100644 index 0000000..c50c861 --- /dev/null +++ b/08-07/ai-control.json @@ -0,0 +1,8 @@ +{ + "pool": "privateDefault", + "terminal": { + "theme": "one-dark", + "icon": "/Users/marcus.hinz/projects/claude-app-builder-4-mac/icons/08-07.png", + "title": "08-07" + } +} diff --git a/ai-control/.gitignore b/ai-control/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/ai-control/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/ai-control/ai-control.json b/ai-control/ai-control.json new file mode 100644 index 0000000..8197946 --- /dev/null +++ b/ai-control/ai-control.json @@ -0,0 +1,3 @@ +{ + "pool": "privateDefault" +} diff --git a/ai-control/bin/connect-test b/ai-control/bin/connect-test new file mode 100755 index 0000000..0c5b031 --- /dev/null +++ b/ai-control/bin/connect-test @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# connect-test — prüft, ob claude mit der aktuellen Env authentifizieren und antworten kann. +# Exit 0 = Verbindung ok. Für Negativtest mit ungültigem Key: +# ANTHROPIC_API_KEY=sk-ant-ungueltig connect-test +set -euo pipefail + +"${CLAUDE_BIN:-$HOME/.local/bin/claude}" -p 'Antworte nur mit: OK' --model claude-sonnet-5 diff --git a/ai-control/bin/kc-backup b/ai-control/bin/kc-backup new file mode 100755 index 0000000..45c21b2 --- /dev/null +++ b/ai-control/bin/kc-backup @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# kc-backup — sichert das Secret des Claude-Code-Keychain-Eintrags in eine Datei. +# Ablage außerhalb des Git-Repos (Default: ~/.ssh), damit das Secret nie committet wird. +# Usage: kc-backup [zieldatei] +set -euo pipefail + +dest=${1:-$HOME/.ssh/claude-keychain-backup.json} +security find-generic-password -s "Claude Code-credentials" -a "$USER" -w > "$dest" +chmod 600 "$dest" +echo "Keychain-Secret gesichert nach: $dest ($(wc -c < "$dest" | tr -d ' ') Bytes)" diff --git a/ai-control/bin/kc-delete b/ai-control/bin/kc-delete new file mode 100755 index 0000000..909a01c --- /dev/null +++ b/ai-control/bin/kc-delete @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +# kc-delete — löscht den Claude-Code-Keychain-Eintrag. +# Vorher kc-backup ausführen; zurückspielen mit kc-restore. +set -euo pipefail + +security delete-generic-password -s "Claude Code-credentials" -a "$USER" +echo "Keychain-Eintrag 'Claude Code-credentials' gelöscht." diff --git a/ai-control/bin/kc-restore b/ai-control/bin/kc-restore new file mode 100755 index 0000000..2ec6c04 --- /dev/null +++ b/ai-control/bin/kc-restore @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# kc-restore — spielt das mit kc-backup gesicherte Secret in den Keychain zurück. +# -U aktualisiert einen ggf. vorhandenen Eintrag statt zu scheitern. +# Usage: kc-restore [backupdatei] +set -euo pipefail + +src=${1:-$HOME/.ssh/claude-keychain-backup.json} +security add-generic-password -U -s "Claude Code-credentials" -a "$USER" -w "$(cat "$src")" +echo "Keychain-Eintrag aus $src wiederhergestellt." diff --git a/ai-control/bin/kc-status b/ai-control/bin/kc-status new file mode 100755 index 0000000..03c2e08 --- /dev/null +++ b/ai-control/bin/kc-status @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# kc-status — zeigt Zustand des Claude-Code-Keychain-Eintrags und der Auth-Env. +# mdat = letzte Änderung des Eintrags; ändert sich, wenn eine Session den Keychain beschreibt. +set -euo pipefail + +echo "== Keychain-Eintrag 'Claude Code-credentials' ==" +security find-generic-password -s "Claude Code-credentials" -a "$USER" 2>&1 | grep -E '"(mdat|cdat)"' || echo "kein Eintrag vorhanden" + +echo "== Auth-Env dieser Shell ==" +env | grep -E '^(ANTHROPIC_API_KEY|CLAUDE_CODE_OAUTH_TOKEN|CLAUDE_CONFIG_DIR)=' | sed -E 's/=(.).*/=\1…/' || echo "keine Auth-Env gesetzt" diff --git a/ai-control/bin/oauth-configdir-test b/ai-control/bin/oauth-configdir-test new file mode 100755 index 0000000..6ed749d --- /dev/null +++ b/ai-control/bin/oauth-configdir-test @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# oauth-configdir-test — prüft, ob claude auf macOS die .credentials.json aus +# CLAUDE_CONFIG_DIR liest, wenn kein Keychain-Eintrag existiert. +# Ablauf: Backup → Keychain löschen → claude mit bereinigter Auth-Env → +# Nachkontrollen → Restore. Restore läuft auch bei fehlgeschlagenem Testlauf. +# Usage: oauth-configdir-test [pool] (Default: Mx9Privat) +set -u +cd "$(dirname "$0")/.." + +pool=${1:-Mx9Privat} +pooldir="$HOME/claude-pools/$pool" + +bin/kc-backup || exit 1 +bin/kc-delete || exit 1 + +echo "== Testlauf: Env ohne ANTHROPIC_API_KEY/CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CONFIG_DIR=$pooldir ==" +env -u ANTHROPIC_API_KEY -u CLAUDE_CODE_OAUTH_TOKEN \ + CLAUDE_CONFIG_DIR="$pooldir" \ + "${CLAUDE_BIN:-$HOME/.local/bin/claude}" -p 'Antworte nur mit: OK' --model claude-sonnet-5 +echo "EXIT: $?" + +echo "== credentials-Datei nach Lauf (#10039: darf nicht gelöscht sein) ==" +ls -la "$pooldir/.credentials.json" + +echo "== Keychain nach Lauf (es darf KEIN Eintrag entstanden sein) ==" +bin/kc-status + +echo "== Restore ==" +bin/kc-restore +rm "$HOME/.ssh/claude-keychain-backup.json" +bin/kc-status diff --git a/ai-control/hand.png b/ai-control/hand.png new file mode 100644 index 0000000000000000000000000000000000000000..ff79bceabdc06e10736f7db4a3b8eb405a012cbb GIT binary patch literal 13266 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/` = CLAUDE_CONFIG_DIR. `pool.json` (name, credentialType), oauth: `.credentials.json`, apikey: `apikey` + `apiKeyHelper` in pool-`settings.json`. Pools (umbenannt 2026-07-03 abends): privateDefault (vorher Mx9Privat; oauth), apiDefault (vorher TestWürth; apikey — 2026-07-03 kurz auf oauth, am selben Tag zurück auf apikey via `apiKeyHelper` in Pool-settings.json; model claude-sonnet-5). Alle 7 Projekte → privateDefault; apiDefault derzeit ohne Projekt. Pool-Wechsel eines Projekts = nur noch `ai-control.json` ändern (einzige Quelle; zieht beim nächsten Start). Pool-Umbenennung ändert den Pfad und damit den Keychain-Suffix — alte Einträge greifen dann nicht mehr. +- `CLAUDE.md`, `commands/`, `settings.json` liegen als physische Kopien in jedem Pool — bewusst poolspezifisch, keine Symlinks. +- Zuordnung: `~/claude-projects//ai-control.json` `{ "pool": ... }`. +- Starter: `~/Applications/Ghostty-.app`, `LSEnvironment` setzt nur noch XDG_CONFIG_HOME (stabil, nie poolabhängig). Alle Ghostty-Configs starten `command = ~/.local/bin/claude-sync` (ggf. mit `--add-dir`-Args). `claude-sync` löst seit 2026-07-04 den Pool selbst auf (liest `./ai-control.json` im CWD, exportiert `CLAUDE_CONFIG_DIR`, laut scheiternd ohne Pool), macht git pull davor und commit+push danach. `claude-start` ist gelöscht — in `claude-sync` integriert. Env/plist tragen keinen Pool mehr. +- Pool-Guard (seit 2026-07-04): `claude-sync` schreibt Sentinel `.ai-control-running` in den Projekt-Root (Inhalt pool=CLAUDE_CONFIG_DIR, pid; FD offen gehalten, nach claude-Ende gelöscht, in .gitignore). Vorab-Check (Endstand 2026-07-03 spät): Fensterzahl statt Prozesszustand, ohne Apple-Events — `ghostty-wincount ` (Swift-Helfer, CGWindowListCopyWindowInfo onScreenOnly + Layer 0; Binary `~/.local/bin/ghostty-wincount`, Quelle `~/claude-projects/robotunits/bin/ghostty-wincount.swift`; App-pid via ps+grep auf den App-Pfad). Das Skript läuft selbst in Fenster 1; ≥2 → Arbeitsfenster existiert → `open -a` (Launch Services, kein Prompt) + 4 s + Exit 0; sonst → Sentinel-pid und FD-Holder kill -9, Sentinel kommentarlos löschen, normal starten. Gescheiterte Vorstufen: tty-Heuristik (Zombie behält totes tty), Schreiber-PID-Check (claude-sync-Bash überlebt Ghosttys X-Close manchmal kopflos), osascript count/activate (Automation-Prompt „App X will App Y steuern" = NOGO). Bekannte Grenze: minimierte/Space-fremde Fenster zählen als unsichtbar. Traps in claude-sync: EXIT löscht Sentinel, HUP/TERM killt claude (claude läuft als Hintergrund-Kind mit `<&0` — sonst stdin=/dev/null! — und `wait`), INT no-op; greifen aber bei Ghostty-X/Quit oft nicht (hartes Kill), Rest-Aufräumen beim nächsten Start ist der Normalfall. Alle 7 Ghostty-Configs (`~/.config-ghostty//ghostty/config`): `wait-after-command = false`; claude-sync zeigt Schlussmeldungen per sleep (2 s aktivieren, 3 s Ende, 5 s Fehler), dann schließt das Fenster selbst. SessionStart-Hook `~/claude-projects/robotunits/bin/pool-guard` in allen 7 Projekt-settings.json: Sentinel fehlt / Pool-Mismatch / Schreiber-PID tot → rot blinkender Banner auf /dev/tty + kill des claude-Elternprozesses. RAW-Nutzung außerhalb der Projekte bleibt frei; RAW-Start IM Projekt wird beendet. Achtung: greift auch bei /clear in Sessions, die noch ohne Sentinel gestartet wurden. +- Gelöscht: `~/.claude` (komplett), Keychain-Eintrag `Claude Code-credentials`, `~/claude-pools`. Unterhaltungen (90 MB) + `history.jsonl` nach Mx9Privat umgezogen — `/resume` pro Pool. +- App-Code (`~/projects/ai-control`, Tauri): Kernlogik mit injizierbarem Home, 22 Tests (`./build.sh`-Toolchain: CARGO_HOME=~/tools/.cargo; node via fnm, nur in interaktiver zsh). Commands unassign_pool/create_project/delete_project, seit 2026-07-04 restart_project (osascript-Quit → warten → open) + Modal in ProjectList: nach Umhängen eines laufenden Projekts Restart-Angebot inkl. Hinweis, dass der claude-sync-Push beim Abschießen entfällt. +- Eingebautes Terminal (2026-07-04, Prototyp, funktioniert laut Marcus): `src-tauri/src/terminal.rs` (portable-pty, Commands open_terminal/term_start/term_write/term_resize) + `terminal.html`/`src/terminal.ts` (xterm.js, Output base64-Events, Capability `term-*`). „Terminal"-Button in ProjectList öffnet pro Klick ein eigenes Fenster; PTY startet `zsh -ic claude-sync` im Projektordner — Pool/Sentinel/git-Sync unverändert bei claude-sync, App setzt kein CLAUDE_CONFIG_DIR. Fenster-X killt Kind + schließt PTY (HUP wie Ghostty-X, Abschluss-Push entfällt). Damit Weg frei, die Ghostty-Starter abzulösen ("wir haben alles in der Hand"). Offen: Scrollback-Restore, Fensterposition merken. +- Stand 2026-07-04 abends, alles committed+gepusht (bis inkl. Todo-Feature): UI komplett auf Catppuccin-Mocha-Token + JetBrains Mono (frontend-design-Skill; Plugin `frontend-design` war kaputt — installPaths in installed_plugins.json zeigten nach Pool-Umbenennung noch auf Mx9Privat, gefixt), i18n de/en (vue-i18n, Umschalter im Header). Pools: löschbar mit Zuordnungs-Auflösung, „Neu anmelden" löscht vorher den suffixierten Keychain-Eintrag (nur bei ungenutztem Pool, Warn-Dialog), Renew entfernt (Datei-Refresh-Token tot, Keychain hat Vorrang), hasCredentials (apikey-Dateicheck) mit Start-Blockierung. Verbrauch-Tab: usage_stats parst /projects/*/*.jsonl (Dedup message.id+requestId, Preistabelle Fable 10/50 Opus 5/25 Sonnet 3/15 Haiku 1/5, Cache 1.25x/0.1x), Pool-Zeilen aggregiert + Projekte aufklappbar; apiDefault hat keinerlei Transcripts (Marcus' apiKey-Abfragen vom 04.07. liefen nachweislich nicht über claude-code auf diesem Rechner — Console zeigt die Wahrheit). Projekt-Wizard (create_project_full: Scaffold nach 08-07-Muster inkl. pool-guard-Hook, Arbeitsordner anlegen/verknüpfen) + Löschen (Guard bei laufender Session, optional Arbeitsordner aus additionalDirectories mit). Todo-Feature nur Projektebene (robotunits-Muster: OFFENE-PUNKTE.md + jq-SessionStart-Hook, zuschaltbar in Wizard+Zahnrad; Pool-Ebene bewusst nicht). App heißt ai-control (Cargo-Paket umbenannt), Hand-Icon (Tray per include_bytes + Template-Icon; Dock-Icon der Terminals via NSApplication nur in RunEvent::Ready). 38 Rust-Tests. Offen/geplant: Baustein-Bibliothek (CLAUDE.md-Marker-Mechanismus, Konzept steht: ~/.config/ai-control/blocks/, verwaltete Abschnitte zwischen ai-control:block-Markern), weitere Wizard-Optionen, Split privates Repo / offizielles GitHub-Repo (Ist-Stand als Initial-Commit ohne History; OAuth-Login-Teil wegen ToS-Grauzone fürs öffentliche Repo rausnehmen — apiKey-Teil unkritisch), Vitest für UI-Tests (Vorschlag steht), Dock-Name macOS/Wayland „später überlegen". +- Alter Zwischenstand (Commit f747379 = Terminal + Start/Beenden-Button): (a) ProjectList: ein Button pro Zeile — grün „Starten" (open_terminal) / rot „Beenden" (neues Command stop_project: SIGTERM auf exakte PIDs per `pgrep -f -- "--terminal $"`, sonst Ghostty-Quit; NIE Quit über die App-Bundle-ID — träfe die Haupt-App); is_running = Ghostty ODER --terminal-Prozess; Status-Kreis ohne Prozess unsichtbar. (b) Terminal-Config-Dialog (Zahnrad): `terminal: { theme, icon, title }` in projekt-`ai-control.json` (pool-Feld jetzt Option, Datei bleibt bei unassign wenn terminal gesetzt); 5 Themes (mocha/dracula/solarized-dark/gruvbox/one-dark, Paletten in terminal.ts, Fenster-BG gespiegelt in terminal.rs theme_background — muss synchron bleiben); Icon = PNG/ICNS-Pfad via tauri-plugin-dialog-Picker, gesetzt per NSApplication.applicationIconImage (objc2, nur in RunEvent::Ready — in setup() überschreibt macOS es wieder); Titel: Fenstertitel + Header, leer → Projektname. `tauri::generate_context!` darf nur 1× expandieren (_EMBED_INFO_PLIST, fällt nur im Release-Build auf). +- Dock-Grenze macOS: Hover-Label im Dock = Bundle-Name, zur Laufzeit nicht öffentlich änderbar; Wrapper-Bundle pro Projekt von Marcus abgelehnt („verlieren zu viel"). Windows/X11: Titel+Icon greifen fensterbasiert; Wayland-Docks bräuchten .desktop pro Projekt (xdg-toplevel-icon nur KDE). → „da müssen wir was überlegen, nicht jetzt". + +**Why:** Getrennte Zugänge pro Pool, [[multi-account-keychain]]. + +**Agenda 2026-07-04:** Start/Stopp-Architektur grundsätzlich besprechen — Stand vom 2026-07-03 spät (wincount/kill-9/Sentinel-Reste) funktioniert im Test, ist aber Flickwerk um Ghosttys unzuverlässiges Kill-Verhalten herum; Marcus will eine saubere Lösung statt Workarounds. Außerdem: Pool-Guard + Kaltstart über Starter testen; App: UI aufräumen, Projekte managen, Defaults/Standardverhalten (z. B. Todolist); Linux gesondert; Open-Source-Frage („works for my workflow") entschieden nach Feinschliff; außerdem Ghostty und Zed besprechen. Stand App: beta-0.8 gepusht (Tray statt Dock, Autostart-Schalter, Fenster startet unsichtbar, Restart-Dialog). + +**How to apply — offen (Stand 2026-07-03):** (1) Sync-Konzept für `~/.config/ai-control` inkl. Unterhaltungen, Secrets-Frage (git). (2) UI kennt die drei neuen Commands nicht. (3) Erledigt durch Integration in `claude-sync` (2026-07-04): der „Flicker" betraf das gelöschte `claude-start`; `claude-sync` läuft in den Ghostty-Wrappern nachweislich interaktiv. diff --git a/ai-control/memory/feedback-go-pflicht.md b/ai-control/memory/feedback-go-pflicht.md new file mode 100644 index 0000000..e4d101f --- /dev/null +++ b/ai-control/memory/feedback-go-pflicht.md @@ -0,0 +1,14 @@ +--- +name: feedback-go-pflicht +description: Weitreichende Änderungen NIE ohne ausdrückliches Go; eine Rückfrage von Marcus ist kein Go +metadata: + node_type: memory + type: feedback + originSessionId: 6c3fc02d-bc5c-4476-9384-8062394a85ff +--- + +Weitreichende Änderungen (Verhaltensänderungen im Backend, Löschlogik, alles mit Datenverlust-Potenzial) nie umsetzen, bevor Marcus ausdrücklich Go gegeben hat. Eine Rückfrage oder Diskussionsfrage von Marcus („was machen wir dann mit X?") ist KEIN Go — Frage beantworten, dann stoppen und warten. + +**Why:** 2026-07-04: Nach Marcus' Rückfrage zur Pool-Löschung habe ich direkt implementiert; Marcus hat abgebrochen („ICH HATTE NICHT GESAGT, DASS DU DAS TUN SOLLST"). Sein „ja" galt dem Plan davor, seine Folgefrage hat die Diskussion wieder geöffnet. + +**How to apply:** Bei jedem Plan mit offener Designfrage: erst alle offenen Punkte klären, dann explizites Go für den Gesamtumfang abwarten. Antwort auf eine Frage endet mit der Antwort, nicht mit Tool-Calls. diff --git a/ai-control/memory/feedback-terminal-test-08-07.md b/ai-control/memory/feedback-terminal-test-08-07.md new file mode 100644 index 0000000..d321438 --- /dev/null +++ b/ai-control/memory/feedback-terminal-test-08-07.md @@ -0,0 +1,20 @@ +--- +name: feedback-terminal-test-08-07 +description: "Terminal-/Restart-Tests der App immer mit Projekt 08-07, nie mit ai-control" +metadata: + node_type: memory + type: feedback + originSessionId: dd1d2798-9e9a-4e95-bf63-7793e7801020 +--- + +Für Tests des eingebauten Terminals und von restart_project immer das Projekt **08-07** als Beispiel nehmen, nie **ai-control**. + +**Why:** Die Arbeitssession läuft selbst im Projekt ai-control — ein Terminal-/Restart-Test dort killt die eigene Session und der Kontext ist weg. + +**How to apply:** In Testanweisungen, Beispielaufrufen und UI-Tests (`open_terminal`, `restart_project`) als Projektname `08-07` verwenden. Siehe [[ai-control-pool-setup]]. + +**Stand 2026-07-04:** Die Session läuft selbst im eingebauten Terminal der App (xterm.js + portable-pty). Ein Neustart/Beenden der App killt die Session direkt — die 08-07-Regel schützt nur gegen `restart_project ai-control`, nicht gegen Tests auf App-Ebene. + +**Verschärfung (2026-07-04, nach erneutem Vorfall):** Alles, was den App-Prozess oder das eigene Terminal berührt, ist tabu — App-Neustart, App-Build+Relaunch, Kill von Electron-/pty-Prozessen, `pkill`/`kill` auf Prozesse, deren Zugehörigkeit nicht geprüft ist. Vor jedem Kill/Restart erst prüfen, ob der Zielprozess in der eigenen Prozesskette hängt. Solche Tests laufen nur nach Ansage von Marcus und dann in 08-07 bzw. einer separaten App-Instanz. + +**Absolutes Verbot (2026-07-04, dritter Vorfall — Kontext erneut verloren):** NIE ein Terminal für oder aus ai-control starten — kein `open_terminal ai-control`, kein Terminal-Start über die App, keine Ausnahme „nur kurz testen". Jeder Terminal-Start, der ai-control betrifft, killt diese Session. Terminal-Funktionen werden ausschließlich mit 08-07 getestet, und nur wenn Marcus es ausdrücklich anweist. diff --git a/ai-control/memory/multi-account-keychain.md b/ai-control/memory/multi-account-keychain.md index 27c9cc7..f510f68 100644 --- a/ai-control/memory/multi-account-keychain.md +++ b/ai-control/memory/multi-account-keychain.md @@ -1,14 +1,20 @@ --- name: multi-account-keychain -description: macOS-Keychain trennt Claude-Code-Credentials NICHT pro CLAUDE_CONFIG_DIR — Multi-Account nur über Env-Tokens +description: "Multi-Account auf macOS — Stand 2.1.199: pro CLAUDE_CONFIG_DIR eigener suffixierter Keychain-Eintrag; .credentials.json-Ansatz überholt" metadata: node_type: memory type: project - originSessionId: 5c4c75da-b313-4740-9988-49533ba77ed1 + originSessionId: e108ad75-f176-43b6-901d-c3ec19ec3961 --- -Verifiziert 2026-07-03: Claude Code nutzt auf macOS einen einzigen Keychain-Eintrag (Service `Claude Code-credentials`, Account = Unix-User) für alle `CLAUDE_CONFIG_DIR`-Profile. Mehrere OAuth-Logins überschreiben sich gegenseitig (Issue anthropics/claude-code#20553); der Datei-Fallback `.credentials.json` wird auf macOS gelöscht (#10039). +ÜBERHOLT (galt bis Version vor 2.1.199): claude las die `.credentials.json` direkt aus `CLAUDE_CONFIG_DIR` (verifiziert 2026-07-03 vormittags, Test `bin/oauth-configdir-test`). -**Why:** ai-control soll 3–4 Claude-Zugänge (Abo + Kunden-API-Keys) strikt getrennt auf einem Rechner betreiben. +Stand 2026-07-03 abends, Version 2.1.199 (native Installation): Neu-Login war nötig. Die `.credentials.json` im Pool `Mx9Privat` ist weg; claude speichert jetzt pro Config-Dir einen Keychain-Eintrag `Claude Code-credentials-`, Suffix = erste 8 Hex-Zeichen von SHA-256 über den `CLAUDE_CONFIG_DIR`-Pfad (Mx9Privat → `a014e206`, TestWürth → erwartet `4c0ad9a9`, ggf. NFC/NFD-Frage wegen „ü"). Ohne gesetztes `CLAUDE_CONFIG_DIR` entsteht der unsuffixierte Eintrag `Claude Code-credentials` plus `~/.claude`/`~/.claude.json`. Damit ist #20553 (Überschreiben) upstream gelöst — pro Pool ein eigener Eintrag. Verifiziert 2026-07-03 abends: Nach `/login` in ai-control (Mx9Privat) lief wuerth-plato (gleicher Pool) ohne erneutes Login — Eintrag `a014e206` wird poolweit geteilt. Keychain enthielt danach nur diesen einen Claude-Code-Eintrag (kein unsuffixierter). Zweiter Pool ebenfalls verifiziert: 08-07 (TestWürth, oauth) verlangte wie erwartet erneutes Login und erzeugte `Claude Code-credentials-4c0ad9a9` — Suffix stimmt mit der Vorab-Berechnung überein, NFC/NFD-Frage wegen „ü" damit geklärt. Keychain-Stand: genau zwei Einträge, `a014e206` (Mx9Privat) und `4c0ad9a9` (TestWürth). Multi-Account über suffixierte Keychain-Einträge funktioniert damit vollständig. Dritter Beleg: robotunits nach Umzug auf TestWürth ohne Login gestartet (Starter-Pfad hasht identisch auf `4c0ad9a9`). Mit gesetztem `CLAUDE_CONFIG_DIR` entsteht dabei kein neues `~/.claude` im Home — das gelöschte bleibt weg. -**How to apply:** Pro Instanz Credentials über Env setzen: `CLAUDE_CODE_OAUTH_TOKEN` (via `claude setup-token`, 1 Jahr gültig) für Abo-Zugänge, `ANTHROPIC_API_KEY` für Kunden-Keys. `CLAUDE_CONFIG_DIR` zusätzlich pro Zugang für getrennte settings/History/Projektzustand — nur nicht für Credentials verlassen. +Umbenennung 2026-07-03 spätabends: Mx9Privat → privateDefault, TestWürth → apiDefault. Suffixe: privateDefault → `096c4ef9`, apiDefault → `15e10251`. Die alten Einträge `a014e206`/`4c0ad9a9` wurden gelöscht; kurz darauf lag `096c4ef9` im Keychain — eine laufende Session hat die Credentials unter dem neuen Pfad selbst neu geschrieben, ohne Login-Prompt. Weitere Session-Starts erzeugten keinen zusätzlichen Eintrag. Keychain-Endstand 2026-07-03: genau ein Eintrag `096c4ef9`; für apiDefault (`15e10251`) existiert noch keiner, dort ist beim ersten Start Login nötig. + +Kaltstart-Test über die umgestellten Starter (2026-07-04): durchgeführt, funktioniert grundsätzlich (kein Login nötig), aber laut Marcus nicht in der gewünschten Qualität — worin genau der Mangel liegt, ist noch nicht festgehalten. + +**Why:** Mehrere Claude-Zugänge (Abo + Kunden-Keys) strikt getrennt auf einem Rechner. + +**How to apply:** Pro Pool ein Ordner als `CLAUDE_CONFIG_DIR` (siehe [[ai-control-pool-setup]]). OAuth: `.credentials.json` (0600) in den Pool-Ordner. API-Key: `apiKeyHelper` in der Pool-`settings.json` auf die Key-Datei. `CLAUDE_CODE_OAUTH_TOKEN`-Export ist unnötig. diff --git a/limbach/.claude/settings.json b/limbach/.claude/settings.json index 7e41934..b51f1a1 100644 --- a/limbach/.claude/settings.json +++ b/limbach/.claude/settings.json @@ -1,12 +1,24 @@ { "autoMemoryDirectory": "~/claude-projects/limbach/memory", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/limbach/OFFENE-PUNKTE.md", + "type": "command" + } + ] + } + ] + }, "permissions": { + "additionalDirectories": [ + "~/projects/limbach" + ], "allow": [ "Edit(~/projects/limbach/**)", "Edit(~/claude-projects/limbach/**)" - ], - "additionalDirectories": [ - "~/projects/limbach" ] } } diff --git a/limbach/.gitignore b/limbach/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/limbach/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/limbach/OFFENE-PUNKTE.md b/limbach/OFFENE-PUNKTE.md new file mode 100644 index 0000000..aabbff6 --- /dev/null +++ b/limbach/OFFENE-PUNKTE.md @@ -0,0 +1,3 @@ +# Offene Punkte — bei jedem Start prüfen und abhaken + +Keine offenen Punkte. diff --git a/limbach/ai-control.json b/limbach/ai-control.json new file mode 100644 index 0000000..88cf28b --- /dev/null +++ b/limbach/ai-control.json @@ -0,0 +1,6 @@ +{ + "pool": "privateDefault", + "terminal": { + "icon": "/Users/marcus.hinz/projects/claude-app-builder-4-mac/icons/limbach.png" + } +} diff --git a/limbach/memory/git-permission-scope-offen.md b/limbach/memory/git-permission-scope-offen.md index 29896c5..29b22fb 100644 --- a/limbach/memory/git-permission-scope-offen.md +++ b/limbach/memory/git-permission-scope-offen.md @@ -19,6 +19,6 @@ Plan — ein `update_scripts`-Command, der im aktuellen Projektordner die `addit 2. **cd-Aliasse** `cd-project-root` / `cd-claude-root`: exakte Bash-Regeln je Pfad statt `cd:*`, z. B. `Bash(cd ~/projects/limbach)` und `Bash(cd ~/claude-projects/limbach)`. cwd persistiert zwischen Bash-Calls, reicht zum Springen. 3. **Read/Edit/Write-Globs** je Verzeichnis: `Read(~/projects/limbach/**)`, `Edit(~/projects/limbach/**)`, `Write(~/projects/limbach/**)` und analog für `~/claude-projects/limbach/**`. Damit läuft Coden in genau diesen Dirs prompt-frei, außerhalb wird weiter gefragt. -Aufräumen: globales `Bash(cd:*)` in `~/claude-projects/global/settings.json` (Symlink-Ziel von `~/.claude/settings.json`) wieder entfernen — vom User als „Mist" bewertet. Globale git-Regeln (`git add/commit/push/…:*`) ebenfalls raus, sobald die Wrapper stehen. +Aufräumen: globales `Bash(cd:*)` in `~/claude-projects/pool/settings.json` (Symlink-Ziel von `~/.claude/settings.json`) wieder entfernen — vom User als „Mist" bewertet. Globale git-Regeln (`git add/commit/push/…:*`) ebenfalls raus, sobald die Wrapper stehen. Hintergrund: `git-sync` ist davon getrennt (synct alle Repos batch-weise, [[git-sync]]). Ursache früherer Prompts war u. a. das Bündeln von Git mit Nicht-Git-Befehlen (`cd … && ls`, `printf > datei`) in einer Zeile — Permission-Matching prüft jedes `&&`-Segment einzeln; die Wrapper umgehen das. diff --git a/misc/.claude/settings.json b/misc/.claude/settings.json new file mode 100644 index 0000000..6d02ddc --- /dev/null +++ b/misc/.claude/settings.json @@ -0,0 +1,23 @@ +{ + "autoMemoryDirectory": "~/claude-projects/misc/memory", + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "command": "jq -Rs '{systemMessage: ., hookSpecificOutput:{hookEventName:\"SessionStart\", additionalContext: .}}' /Users/marcus.hinz/claude-projects/misc/OFFENE-PUNKTE.md", + "type": "command" + } + ] + } + ] + }, + "permissions": { + "additionalDirectories": [ + "~/projects/misc" + ], + "allow": [ + "Edit(~/projects/misc/**)" + ] + } +} diff --git a/misc/.gitignore b/misc/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/misc/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/misc/OFFENE-PUNKTE.md b/misc/OFFENE-PUNKTE.md new file mode 100644 index 0000000..aabbff6 --- /dev/null +++ b/misc/OFFENE-PUNKTE.md @@ -0,0 +1,3 @@ +# Offene Punkte — bei jedem Start prüfen und abhaken + +Keine offenen Punkte. diff --git a/misc/ai-control.json b/misc/ai-control.json new file mode 100644 index 0000000..8197946 --- /dev/null +++ b/misc/ai-control.json @@ -0,0 +1,3 @@ +{ + "pool": "privateDefault" +} diff --git a/global/CLAUDE.md b/pool/CLAUDE.md similarity index 95% rename from global/CLAUDE.md rename to pool/CLAUDE.md index f20f067..e840fd4 100644 --- a/global/CLAUDE.md +++ b/pool/CLAUDE.md @@ -23,7 +23,7 @@ # Anker -- Diese Datei ist die Quelle für `~/.claude/CLAUDE.md` (Symlink). Änderungen direkt hier in `~/claude-projects/global/CLAUDE.md` vornehmen, nicht über den Symlink-Pfad. +- Diese Datei ist die Quelle für `~/.claude/CLAUDE.md` (Symlink). Änderungen direkt hier in `~/claude-projects/pool/CLAUDE.md` vornehmen, nicht über den Symlink-Pfad. - Anker für projektübergreifend Gemeinsames (Checklisten, Notizen, Memory, geteilte Dateien) ist `~/claude-projects/robotunits`. Solche Dateien gehören dorthin, nicht unter `~/projects/...`. - Projektspezifisches (z. B. SessionStart-Hooks) bleibt im jeweiligen Projekt unter dessen `.claude/settings.json` — nicht global. @@ -54,3 +54,4 @@ Nie verwenden, weder in Texten noch in Antworten: - „Der Clou" / „Der Witz" / „Der Hit" / „Die Pointe" - Sinngleiche Effekthascherei: „der Knaller", „das Highlight", „der Wow-Effekt", „die Sensation" - Englische Technikbegriffe nicht holprig eindeutschen (z. B. nicht „spritzen" für *inject*). +- „Ehrlich" als Einleitung oder Anrede (z. B. „Ehrlich gesagt", „Ehrlich zum …") — stattdessen direkt die Aussage. diff --git a/global/bin/git-sync b/pool/bin/git-sync similarity index 100% rename from global/bin/git-sync rename to pool/bin/git-sync diff --git a/global/bin/new-project b/pool/bin/new-project similarity index 100% rename from global/bin/new-project rename to pool/bin/new-project diff --git a/global/commands/git-sync.md b/pool/commands/git-sync.md similarity index 90% rename from global/commands/git-sync.md rename to pool/commands/git-sync.md index 9f22c32..3fcfeb2 100644 --- a/global/commands/git-sync.md +++ b/pool/commands/git-sync.md @@ -5,7 +5,7 @@ description: Commit + Push aller persistent konfigurierten Projekt-Repos Führe genau dieses eine Kommando aus, sonst nichts: ```bash -~/claude-projects/global/bin/git-sync +~/claude-projects/pool/bin/git-sync ``` Das Skript liest selbst die `permissions.additionalDirectories` aus den Settings und diff --git a/global/settings.json b/pool/settings.json similarity index 83% rename from global/settings.json rename to pool/settings.json index 4342d5c..5d98a44 100644 --- a/global/settings.json +++ b/pool/settings.json @@ -11,9 +11,10 @@ "Bash(git mv:*)", "Bash(git check-ignore:*)", "Bash(cd:*)", - "Bash(~/claude-projects/global/bin/git-sync:*)" + "Bash(~/claude-projects/pool/bin/git-sync:*)" ] }, + "model": "claude-fable-5[1m]", "enabledPlugins": { "frontend-design@claude-plugins-official": true, "swift-lsp@claude-plugins-official": true @@ -21,6 +22,5 @@ "promptSuggestionEnabled": false, "awaySummaryEnabled": false, "tui": "fullscreen", - "theme": "dark", - "model": "claude-fable-5[1m]" + "theme": "dark" } diff --git a/robotunits/.gitignore b/robotunits/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/robotunits/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/robotunits/GEKO-16.xml b/robotunits/GEKO-16.xml new file mode 100644 index 0000000..27b7324 --- /dev/null +++ b/robotunits/GEKO-16.xml @@ -0,0 +1,338 @@ + + + + Jira adesso Group extern + https://adesso-group-extern.atlassian.net + This file is an XML representation of an issue + en-us + 1001.0.0-SNAPSHOT + 100292 + 30-06-2026 + + + + [GEKO-16] Schutzzaun im Layout schließen/Schutzzaun zwischen zwei Punkten planen + https://adesso-group-extern.atlassian.net/browse/GEKO-16 + GEKO + <p>Dieses Ticket implementiert die interaktive Planung eines Schutzzauns zwischen zwei Punkten im 3D-Layout. Die Funktion ermöglicht das Ziehen einer Strecke, berechnet die Geometrie und generiert den Zaun als kaskadierende Baugruppe. Neu ist die intelligente Vererbungslogik: Werden PMIs als Start- oder Endpunkt genutzt, erbt der Zaun Typ und Füllung automatisch; andernfalls werden diese über ein modales Overlay abgefragt.</p> + +<h3><a name="%F0%9F%9F%A6DefinitionofReady%C2%A0"></a><b>�� Definition of Ready</b> </h3> + +<h4><a name="Wer%3F%C2%A0"></a><b>Wer?</b> </h4> + +<p>Anwender des Schutzzaun-Konfigurators im 3D-Layout.</p> + +<h4><a name="Was%3F%C2%A0"></a><b>Was?</b> </h4> + +<ul> + <li>Interaktives Setzen von Start- und Endpunkt (frei oder gesnappt an PMIs).</li> + <li>Dynamische Anzeige von 3 Bemaßungen (Länge, Diagonale, Winkel) während der Mausbewegung.</li> + <li>Intelligente Eigenschafts-Vererbung (Typ/Füllung) basierend auf Start-/End-PMIs zur Vermeidung unnötiger UI-Abfragen.</li> + <li>Filterung von PMIs: Inkompatible End-PMIs werden nach der Auswahl des Start-PMIs ignoriert und nicht gehighlighted.</li> + <li>Mathematische Berechnung von 2D-Länge (XZ-Ebene) und Ausrichtungswinkel.</li> + <li>Algorithmus-gestützte Aufteilung der Strecke in Standard- und Sonderelemente.</li> + <li>Generierung des Zauns als kaskadierender Verbund (Typ 2 Einbaubedingungen).</li> +</ul> + + +<h4><a name="Warum%3F%C2%A0"></a><b>Warum?</b> </h4> + +<p>Effiziente Planung von Schutzzäunen über größere Distanzen, ohne dass der Nutzer jedes Element einzeln platzieren muss und unter Vermeidung von fehlerhaften Typen-Mischungen.</p> + +<h4><a name="Wozu%3F%C2%A0"></a><b>Wozu?</b> </h4> + +<p>Ein strukturierter, in den Szenegraphen integrierter Schutzzaun-Zug, mit dem offene Züge geschlossen werden können.</p> + + GEKO-16 + Schutzzaun im Layout schließen/Schutzzaun zwischen zwei Punkten planen + Task + GEKO-6 + Major + To Do + + Unresolved + Marcus Hinz + Lukas Hörger + + + Wed, 8 Apr 2026 11:43:54 +0200 + Thu, 2 Jul 2026 14:28:25 +0200 + + 0 + 0 + + + + + + + Development + + + + + + Rank + + 0|i5ji9k: + + + + + T-Shirt Size - Storypoint + + + + + + + �� Definition of Ready - Formale Kriterien + + <h3><a name="%F0%9F%93%98Funktionsbeschreibung%3A"></a><b>�� Funktionsbeschreibung:</b></h3> + +<p><b>Input:</b> Klick-Events im 3D-Viewport und ggf. Auswahl-Events im modalen Overlay.</p> + +<ul> + <li><b>Verhalten / Ablauf:</b> + <ol> + <li><b>Startpunkt (Klick 1):</b> Nutzer setzt den ersten Punkt (Typ 1 Weltkoordinaten oder Typ 2 PMI).</li> + <li><b>Zeichnen &amp; Filtern:</b> Unmittelbar <b>nach dem 1. Klick</b> (sofern dieser an einem PMI erfolgte) filtert die Engine den Szenegraphen. Anhand des Typs des Start-PMIs (z. B. Basic) werden alle inkompatiblen PMIs (z. B. Allround) für den aktuellen Zeichen-Modus als ungültig markiert. Während der Nutzer nun die Gummiband-Linie zum Endpunkt zieht, werden diese inkompatiblen PMIs konsequent nicht gehighlighted und können nicht angeklickt werden.</li> + <li><b>Endpunkt (Klick 2):</b> Nutzer setzt den zweiten Punkt.</li> + <li><b>Vererbungslogik &amp; Konfiguration:</b> Die Engine entscheidet anhand folgender Logik-Matrix, woher die Parameter (Typ/Füllung) stammen: + <ul> + <li><b>Start: Frei | Ende: Frei</b> → Keine Vererbung. Das modale Auswahl-Overlay öffnet sich. Standard-Elemente werden nach Bestätigung generiert.</li> + <li><b>Start: PMI | Ende: Frei</b> → Vererbung der Properties vom Start-PMI. Das Overlay erscheint <b>nicht</b>.</li> + <li><b>Start: Frei | Ende: PMI</b> → Vererbung der Properties vom End-PMI. Das Overlay erscheint <b>nicht</b>.</li> + <li><b>Start: PMI | Ende: PMI</b> → Vererbung der Properties vom Start-PMI. (Typengleichheit ist durch das Filtern in Schritt 2 bereits sichergestellt). Das Overlay erscheint <b>nicht</b>.</li> + </ul> + </li> + <li><b>Geometrie-Berechnung:</b> Y-Offset und Rotationen werden ignoriert. Absolute Strecke über Pythagoras ({{L = √(Δx² + Δz²)}}), Winkel über <tt>atan2(Δz, Δx)</tt> (auf XZ-Ebene projiziert).</li> + <li><b>Element-Aufteilung:</b> Die Logik <tt>berechnung_aufteilung_laenge</tt> ermittelt die Rasterelemente <em>(siehe Algorithmus-Abschnitt unten)</em>.</li> + <li><b>Generierung:</b> Elemente werden kaskadierend via PMI (Typ 2) eingebaut.</li> + <li><b>Validierung:</b> Trigger der lokalen Validierungsengine.</li> + </ol> + </li> +</ul> + + +<h4><a name="Algorithmus%3AElementAufteilung%28%7B%7Bberechnungaufteilunglaenge%7D%7D%29"></a>Algorithmus: Element-Aufteilung (<tt>berechnung_aufteilung_laenge</tt>)</h4> + +<ul> + <li><b>1. Grundkonfiguration:</b> + <ul> + <li><b>Typ "Basic":</b> Der Elementabstand wird als Parameter übergeben; es gibt keinen Steherzusatz (0 mm).</li> + <li><b>Typ "Allround":</b> Fester Elementabstand von <b>11 mm</b>, Steherzusatz von <b>51 mm</b> pro Element.</li> + <li><b>Türen:</b> Ist eine Tür in der Konfiguration enthalten, wird pauschal ein Maß von <b>1124 mm</b> (zzgl. Elementabstand) von der Gesamtbreite abgezogen.</li> + </ul> + </li> + <li><b>2. Standardelemente (Greedy):</b> Der Raum wird systematisch mit den größten passenden Standardelementen gefüllt (Priorität: 1507, 1034, 776, 518 mm).</li> + <li><b>3. Dead Zone Correction (Todeszone):</b> Bleibt eine kritische Restbreite (zwischen 43 mm und Mindestbreite der Füllung) übrig, wird das kleinste bereits verplante Element downgradet (z. B. 776 mm -&gt; 518 mm) oder gelöscht, um Platz für ein produzierbares Sonderelement zu schaffen.</li> + <li><b>4. Sonderelement (Rasterung):</b> Aus der Restbreite wird im <b>43-mm-Raster</b> das finale Passstück ermittelt. Restlücken &lt; 43 mm werden als Toleranz ignoriert.</li> +</ul> + + +<p><b>Pseudocode der Aufteilungslogik:</b></p> + +<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent"> +<pre>FUNCTION calculate_width_distribution(type, has_door, gap_input, total_width, filling_type) + INITIALIZE results AS dictionary + + // --- 1. Base Setup &amp; Door Deduction --- + IF type == "Basic" THEN + gap = gap_input + post_addition = 0 + ELSE IF type == "Allround" THEN + gap = 11 + post_addition = 51 + END IF + + IF has_door == TRUE THEN + deduction = 1124 + gap + results["door_count"] = 1 + ELSE IF type != "0" THEN + deduction = 51 + results["door_count"] = 0 + ELSE + deduction = 0 + results["door_count"] = 0 + END IF + + remaining_width = total_width - deduction + test_width = remaining_width + + // --- 2. Distribute Standard Elements (Greedy Approach) --- + STANDARD_SIZES = [1507, 1034, 776, 518] + element_counts = {1507: 0, 1034: 0, 776: 0, 518: 0} + + FOR EACH size IN STANDARD_SIZES DO + required_width = size + post_addition + WHILE test_width &gt;= required_width DO + element_counts[size] = element_counts[size] + 1 + remaining_width = test_width - required_width + test_width = remaining_width - gap + END WHILE + END FOR + + // --- 3. Dead Zone Correction --- + min_special_element = (IF filling_type == 0 THEN 174 ELSE 131) + post_addition + + IF test_width &gt;= 43 AND test_width &lt;= min_special_element THEN + smallest_assigned = FIND_SMALLEST_ASSIGNED_ELEMENT(element_counts) + next_smaller = FIND_NEXT_SMALLER_SIZE(STANDARD_SIZES, smallest_assigned) + + IF smallest_assigned EXISTS THEN + element_counts[smallest_assigned] = element_counts[smallest_assigned] - 1 + + IF next_smaller EXISTS THEN + // Downgrade to next smaller standard element + element_counts[next_smaller] = element_counts[next_smaller] + 1 + difference = smallest_assigned - next_smaller + remaining_width = remaining_width + difference + test_width = test_width + difference + ELSE + // Lowest element was 518, remove it completely + freed_space = post_addition + smallest_assigned + gap + remaining_width = remaining_width + freed_space + test_width = test_width + freed_space + END IF + END IF + END IF + + SAVE element_counts TO results + test_width = test_width - post_addition + + // --- 4. Special Element (Sonderelement) Calculation --- + special_element_width = 0 + test_width_int = FLOOR(test_width) + + IF test_width_int &lt; 43 THEN + special_element_width = 0 + ELSE IF test_width_int &gt;= 518 AND test_width_int &lt; 561 THEN + results["518_count"] = results["518_count"] + 1 + remaining_width = remaining_width - 518 - gap - post_addition + ELSE IF test_width_int &gt;= 776 AND test_width_int &lt; 819 THEN + results["776_count"] = results["776_count"] + 1 + remaining_width = remaining_width - 776 - gap - post_addition + ELSE IF test_width_int &gt;= 131 AND test_width_int &lt; 776 THEN + // Calculate based on a grid step of 43 + calculated_size = ((test_width_int - 131) / 43) * 43 + 131 + + IF calculated_size == 131 AND min_special_element != 131 + post_addition THEN + special_element_width = 0 // Min size rule prevents a 131 element + ELSE + special_element_width = calculated_size + END IF + END IF + + // --- 5. Final Remainder Output --- + results["special_element"] = special_element_width + + IF special_element_width &gt; 0 THEN + remaining_width = remaining_width - special_element_width - gap - post_addition + END IF + + results["final_remainder"] = remaining_width + + RETURN results +END FUNCTION + +FUNCTION determine_parameters_and_generate(start_point, end_point) + // --- 1. Vererbungs-Weiche (Typ und Füllung ermitteln) --- + IF start_point.is_pmi == TRUE THEN + // Vererbung vom Start-PMI (gilt für PMI+Frei und PMI+PMI) + fence_type = start_point.parent.type + fence_filling = start_point.parent.filling + + ELSE IF end_point.is_pmi == TRUE THEN + // Rückwärtsvererbung vom End-PMI (gilt für Frei+PMI) + fence_type = end_point.parent.type + fence_filling = end_point.parent.filling + + ELSE + // Beide Punkte frei (Frei+Frei) -&gt; Modales UI-Overlay + ui_selection = SHOW_MODAL_OVERLAY() + IF ui_selection == CANCELLED THEN RETURN + + fence_type = ui_selection.type + fence_filling = ui_selection.filling + END IF + + // --- 2. Geometrie berechnen --- + total_width = CALCULATE_2D_DISTANCE(start_point, end_point) + angle = CALCULATE_2D_ANGLE(start_point, end_point) + + // --- 3. Aufteilung berechnen (Kern-Algorithmus bleibt unverändert) --- + distribution = calculate_width_distribution(fence_type, FALSE, 0, total_width, fence_filling) + + // --- 4. Kaskadierende Platzierung triggern --- + PLACE_FENCE_ELEMENTS(distribution, start_point, angle) +END FUNCTION</pre> +</div></div> + +<h4><a name="NichtZiele%28OutofScope%29"></a>Nicht-Ziele (Out of Scope)</h4> + +<ul> + <li><b>Live-Vorschau (Ghosting):</b> Das Rendern einer 3D-Vorschau des Zauns während der Mausbewegung.</li> + <li><b>Passstücke (Sonderbau):</b> Generierung freier Sonder-Bauteile außerhalb des 43-mm-Rasters.</li> + <li><b>Spezifikation des Overlays:</b> Das UI des modalen Overlays wird separat spezifiziert.</li> +</ul> + + +<hr /> + +<h3><a name="%F0%9F%93%91Akzeptanzkriterien%3A"></a><b>�� Akzeptanzkriterien:</b></h3> + +<ul> + <li>Die Vererbungsmatrix wird strikt eingehalten: Das Konfigurations-Overlay öffnet sich <em>ausschließlich</em>, wenn sowohl Start- als auch Endpunkt frei im Raum (Typ 1) liegen.</li> + <li>Ist der Startpunkt ein PMI, werden inkompatible PMIs im Viewer beim Hovern ignoriert und nicht als Snapping-Ziel angeboten.</li> + <li>Startet der Zaun frei und endet an einem PMI, übernimmt der Zaun die Parameter des Ziel-Elements fehlerfrei (Rückwärtsvererbung).</li> + <li>Geometrie wird flach auf der XZ-Ebene berechnet; Höhenunterschiede zwischen den Punkten führen nicht zu schiefen Zäunen.</li> + <li>Kaskadierendes Verhalten: Bei Verschiebung des Eltern-Bauteils bewegt sich der gesamte Zaun-Verbund mit.</li> +</ul> + + +<hr /> + +<h3><a name="%F0%9F%A7%AATestCases%3A"></a><b>�� Test Cases:</b></h3> + +<ul> + <li><b><span class="error">&#91;Rückwärtsvererbung&#93;</span></b>: Startpunkt ins Freie klicken -&gt; Endpunkt an PMI eines Basic-Zauns mit Polycarbonat-Füllung snappen -&gt; <em>Erwartetes Ergebnis:</em> Kein Overlay öffnet sich. Es wird sofort ein Basic-Zaun mit Polycarbonat generiert, der an das Ziel snappt.</li> + <li><b><span class="error">&#91;PMI Typen-Filter&#93;</span></b>: Startpunkt an Allround-PMI klicken -&gt; Maus über ein Basic-PMI bewegen -&gt; <em>Erwartetes Ergebnis:</em> Das Basic-PMI wird nicht gehighlighted und kann nicht als Endpunkt ausgewählt werden.</li> + <li><b><span class="error">&#91;Overlay-Trigger&#93;</span></b>: Startpunkt frei klicken -&gt; Endpunkt frei klicken -&gt;<em>Erwartetes Ergebnis:</em> Modales Overlay zur Parameterauswahl öffnet sich.</li> +</ul> + + +<hr /> + +<h3><a name="%E2%9A%A0%EF%B8%8FEdgeCases%3A%C2%A0"></a><b>⚠️ Edge Cases:</b> </h3> + +<ul> + <li><b><span class="error">&#91;Abbruch während Zeichnen&#93;</span></b>: <tt>ESC</tt> oder ein Klick außerhalb des Canvas bricht den Modus nach Klick 1 restlos ab.</li> + <li><b><span class="error">&#91;Distanz zu kurz&#93;</span></b>: Ist die Gesamtdistanz kleiner als das schmalste verfügbare Rasterelement, führt der Abschluss im Overlay zu einem No-Op (keine Generierung).</li> + <li><b><span class="error">&#91;Eltern-Löschung&#93;</span></b>: Wird das Bauteil gelöscht, an dessen PMI der Zaun beginnt, verliert das erste Zaunelement seine Typ 2 Bedingung, fällt auf Typ 1 zurück und der Zaun bleibt stehen.</li> +</ul> + + + + + + �� DoR - Checkliste + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robotunits/ai-control.json b/robotunits/ai-control.json new file mode 100644 index 0000000..8197946 --- /dev/null +++ b/robotunits/ai-control.json @@ -0,0 +1,3 @@ +{ + "pool": "privateDefault" +} diff --git a/robotunits/bin/ghostty-wincount.swift b/robotunits/bin/ghostty-wincount.swift new file mode 100644 index 0000000..6c064d2 --- /dev/null +++ b/robotunits/bin/ghostty-wincount.swift @@ -0,0 +1,15 @@ +// ghostty-wincount — Anzahl sichtbarer Normal-Fenster (Layer 0) des +// Prozesses, direkt vom Window-Server. Keine Apple-Events, kein TCC-Prompt. +import CoreGraphics +import Foundation + +guard CommandLine.arguments.count == 2, let pid = Int32(CommandLine.arguments[1]) else { + FileHandle.standardError.write(Data("usage: ghostty-wincount \n".utf8)) + exit(2) +} +let list = CGWindowListCopyWindowInfo([.optionOnScreenOnly], kCGNullWindowID) as? [[String: Any]] ?? [] +let n = list.filter { + ($0[kCGWindowOwnerPID as String] as? Int32) == pid && + ($0[kCGWindowLayer as String] as? Int) == 0 +}.count +print(n) diff --git a/robotunits/bin/pool-guard b/robotunits/bin/pool-guard new file mode 100755 index 0000000..40db4bb --- /dev/null +++ b/robotunits/bin/pool-guard @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# pool-guard — SessionStart-Hook: prüft direkt nach dem claude-Start, ob die +# Session über claude-sync (Pool-Mechanismus) läuft. claude-sync schreibt die +# Sentinel-Datei in den Projekt-Root und hält sie offen; ein RAW-Start hat +# keine lebende Sentinel dahinter und wird beendet. +set -uo pipefail + +sentinel="$CLAUDE_PROJECT_DIR/.ai-control-running" + +fail() { + # claude im Elternbaum finden und beenden, dann Banner aufs Terminal. + p=$PPID + while [ -n "$p" ] && [ "$p" -gt 1 ]; do + case "$(ps -o comm= -p "$p")" in + *claude*) kill "$p"; break ;; + esac + p=$(ps -o ppid= -p "$p" | tr -d ' ') + done + sleep 0.3 + { + printf '\n\033[1;5;97;41m POOL-GUARD: %s \033[0m\n\n' "$1" + printf '\033[1;31mDiese Session lief außerhalb des Pool-Mechanismus und wurde beendet.\nStart nur über die Ghostty-App bzw. claude-sync.\033[0m\n' + } >/dev/tty + exit 2 +} + +[ -f "$sentinel" ] || fail "keine Sentinel-Datei ($sentinel)" +pool=$(sed -n 's/^pool=//p' "$sentinel") +pid=$(sed -n 's/^pid=//p' "$sentinel") +[ "$pool" = "${CLAUDE_CONFIG_DIR:-}" ] || fail "Pool-Mismatch: Sentinel=$pool, Session=${CLAUDE_CONFIG_DIR:-}" +kill -0 "$pid" 2>/dev/null || fail "claude-sync (PID $pid) lebt nicht mehr" +exit 0 diff --git a/robotunits/memory/MEMORY.md b/robotunits/memory/MEMORY.md index 287a551..cacba98 100644 --- a/robotunits/memory/MEMORY.md +++ b/robotunits/memory/MEMORY.md @@ -8,4 +8,6 @@ - [cpq Storybook starten](cpq-storybook-start.md) — pnpm --filter @ad-cpq/common-frontend storybook (Port 6006) - [Git nur Fast-Forward](feedback-git-nur-fast-forward.md) — kein rebase/merge/force; bei Divergenz melden statt auflösen - [fire-visual Placement-Mode](fire-visual-placement-mode.md) — snap-to-point: DragPoints/DropPoints, processInput-Algorithmus, start_placement_mode-Command, PMI-DropPoints -- [cpq Verbau 90° am Steher](cpq-verbau-90grad-steher.md) — Engine kann's, cpq-GroupProcessor (translation-only) plättet die Rotation beim Replay; Punktdaten tragen die Orientierung schon +- [cpq Verbau 90° am Steher](cpq-verbau-90grad-steher.md) — Rotation ist im GroupProcessor-Replay angekommen (Edge.rot/computeLayout); fachliche 90°-Klärung läuft noch +- [cpq Configurator Modul-Registry](cpq-configurator-modul-registry.md) — GEKO-76/16: Registry in configurator/modules, Hook nach replay(), Heilung=Ableitung, Overrides=Events +- [Suche nur in Projektordnern](feedback-suche-nur-projektordner.md) — find/grep nie über ~ oder den ganzen Rechner, nur im konkreten erlaubten Verzeichnis diff --git a/robotunits/memory/cpq-configurator-modul-registry.md b/robotunits/memory/cpq-configurator-modul-registry.md new file mode 100644 index 0000000..a99a82d --- /dev/null +++ b/robotunits/memory/cpq-configurator-modul-registry.md @@ -0,0 +1,19 @@ +--- +name: cpq-configurator-modul-registry +description: "GEKO-76/16-Konzept: Modul-Registry im cpq-Configurator — Injection über DefaultCommandProcessor, Hook nach replay(), Heilung = Ableitung, Overrides = Events" +metadata: + node_type: memory + type: project + originSessionId: 8d7f85b8-4927-4426-9e04-a8be7def3b5e +--- + +Konzept-Entscheidung (2026-07-03) für die cpq/geko-Abgrenzung der Schutzzaun-Tickets GEKO-76 (Steher-Logik) und GEKO-16 (Zaun zwischen zwei Punkten): + +- **Registry-Ort:** `packages/frontend/src/configurator/` (neues Verzeichnis `modules/`), kein neues Package. cpq bleibt domänen-agnostisch; geko liefert Module (Steher, Zaun-Zug) von außen. +- **Injection:** Konstruktor des `DefaultCommandProcessor`; `Fire3D.vue` nimmt bereits `props.processor` entgegen — die geko-App baut den Processor mit ihren Modulen und reicht ihn durch. +- **Zentraler Hook:** nach `groups.replay(...)` in `DefaultCommandProcessor.replay()` — einziger Engpass für Live, Undo/Redo und Placement; Module können Live/Replay nicht auseinanderlaufen lassen. +- **Semantik:** Selbstheilung (Bauform A–G, Höhe) ist reine Ableitung aus der Topologie (`occupiedPmis()`, `Edge.rot`) und landet nie im EventStore; manuelle Detailmenü-Overrides sind echte, undo-bare Events (Modul-eigene Event-Typen). +- **Mit aufzuräumen:** hartkodierte `.name-CS_EIN_*`-Selektoren im `DefaultCommandProcessor` → Placement-Konfiguration des Moduls; Löschen ist im Processor noch unbehandelt → Kaskaden-Hook von Anfang an als Modul-Vertrag entwerfen. +- Ticket-Quellen lokal: `~/claude-projects/robotunits/GEKO-76.xml`, `GEKO-16.xml`; DrawIO-Abgrenzung: `GEKO-76-steher-cpq-geko.drawio`. + +Siehe [[cpq-verbau-architektur]], [[cpq-verbau-90grad-steher]]. diff --git a/robotunits/memory/cpq-verbau-90grad-steher.md b/robotunits/memory/cpq-verbau-90grad-steher.md index 3ff6a47..4b4e850 100644 --- a/robotunits/memory/cpq-verbau-90grad-steher.md +++ b/robotunits/memory/cpq-verbau-90grad-steher.md @@ -1,28 +1,20 @@ --- name: cpq-verbau-90grad-steher -description: "Ziel 90°-Anbau am Steher wie jetzt Links/Rechts — Engine kann es, cpq-GroupProcessor (translation-only) ist der Blocker" +description: "90°-Anbau am Steher: Rotation ist im GroupProcessor-Replay angekommen (Edge.rot, computeLayout) — fachliche 90°-Klärung läuft noch" metadata: node_type: memory type: project originSessionId: e3d54daa-eb88-474f-9e99-62bfe83821b2 --- -Ziel: an den Stehern im 90°-Winkel anbauen können, genau wie der jetzige Links/Rechts-Verbau. +Ziel: an den Stehern im 90°-Winkel anbauen können, genau wie der Links/Rechts-Verbau. -**Wer ist der Blocker: wir (cpq), nicht fire-visual.** +**Stand 2026-07-03 (verifiziert im Code):** Der frühere Blocker — translation-only-Replay im cpq-`GroupProcessor` — ist behoben. `packages/frontend/src/configurator/model/GroupProcessor.ts`: +- `Edge` trägt `rot?: Quat` (relative Rotation pro Dock-Kante). +- `computeLayout` komponiert Rotationen über den ungerichteten Kantengraphen (Quaternionen, vorwärts `R_to = R_from ∘ edge.rot`, rückwärts invers); mit Identity-Rotationen reduziert es sich exakt auf den alten translation-only-Solver. +- `dockRotation` leitet `Edge.rot` aus den PMI-Frame-Orientierungen ab (`pmiRotation` liest `properties.rotation` als Euler ZYX Grad → Quat). +- Placement nutzt die `CS_EIN_VORNE`/`CS_EIN_HINTEN`-Selektoren (hart im `DefaultCommandProcessor` — Kandidat für Modul-Konfiguration). -- **fire-visual kann es.** Das Live-Snapping im PlacementMode setzt die Subject-Matrix als `bestMatrix · dragPointTransform⁻¹` — die Rotation des Drag-Punkts ist drin, die Engine richtet also den vollen Frame aus und dreht das Teil beim Snap schon korrekt um 90°. (Vorbehalt: aus der Placement-Matrix-Notiz abgeleitet, nicht aus frischem Quell-Read; bei Bedarf `PlacementMode`-Quelle prüfen, ob DropPoints den Rotations-Frame durchreichen.) -- **cpq verliert es beim Replay.** Nach dem Snap übernehmen wir das Engine-Ergebnis nicht, sondern merken nur „Punkt A trifft Punkt B" (`group-merge` mit zwei PMI-IDs) und rechnen die Anordnung im `GroupProcessor` selbst neu — rein translatorisch. Beim nächsten `setScene` wird die 90°-Drehung auf achsparallel zurückgeplättet. +Die fachliche Klärung der 90°-Frage (GEKO-Seite) läuft laut Marcus noch separat. -**Warum schwer — das Dock-Modell ist translation-only, einachsig, gemeinsame Orientierung. Links/Rechts geht nur, weil nie rotiert wird.** Zu ändernde Schichten: -1. `Edge` = `{from,to,fromPmi,toPmi}` kennt keine Rotation → relative Rotation pro Kante nötig. -2. `computePositions` dockt mit `pos(to)=pos(from)+pmi(from)−pmi(to)` (nur Position) → Rotation entlang der Kanten komponieren. -3. `pmiLocal` liest nur `position.value`. **Die Orientierung steckt schon in den Daten** (`properties.rotation`), wird aber ignoriert — und Format passt nicht: 3 Euler-Grad (z. B. `CS_EIN_90_0_0` → `[0,-0,180]`), deklariert als „quaternion", während `transformMath` `[w,x,y,z]` erwartet. -4. Placement-Mode hartverdrahtet auf `.name-CS_LINKS`↔`.name-CS_RECHTS`; Steher-Anschlüsse leben in der `CS_EIN_*`-Familie → neue Drag-/Drop-Selektoren + Open-End-Logik (`occupiedPmis`). -5. `buildDetachEvent`/`areConnected` nehmen einen 1-achsigen Lauf an (dominante Achse, Links→Rechts-Leseordnung) → 90°-Anbau macht den Verbau zum 2D-Baum, Annahmen brechen. - -**Datenlage:** kein plain `CS_VORNE`/`CS_HINTEN` (0 Teile); `CS_LINKS`/`CS_RECHTS` nur auf 12 Teilen; Vorne/Hinten nur in `CS_EIN_*` (rotationscodiert: `CS_EIN_VORNE`, `CS_EIN_HINTEN`, `CS_EIN_STEHER_VORNE/HINTEN`, `CS_EIN_90_0_0` …). - -**Machbar:** Erweiterung unseres Modells (Rotation konsumieren + entlang Kanten propagieren + Format-Fix), keine Nachgenerierung der Teile — die nötige Orientierung liefern die Punktdaten bereits. - -Siehe [[cpq-verbau-architektur]], [[fire-visual-placement-mode]]. +Siehe [[cpq-verbau-architektur]], [[fire-visual-placement-mode]], [[cpq-configurator-modul-registry]]. diff --git a/robotunits/memory/feedback-suche-nur-projektordner.md b/robotunits/memory/feedback-suche-nur-projektordner.md new file mode 100644 index 0000000..d8468cb --- /dev/null +++ b/robotunits/memory/feedback-suche-nur-projektordner.md @@ -0,0 +1,14 @@ +--- +name: feedback-suche-nur-projektordner +description: "Suchen/find/grep nur innerhalb der erlaubten Projektverzeichnisse, nie über ~ oder den ganzen Rechner" +metadata: + node_type: memory + type: feedback + originSessionId: 8d7f85b8-4927-4426-9e04-a8be7def3b5e +--- + +Such-Befehle (find, grep, ls …) nur innerhalb der erlaubten Arbeitsverzeichnisse des jeweiligen Projekts ausführen — nicht über `/Users/marcus.hinz`, nicht über breite Pfad-Listen quer durch `~/projects`, nicht über den ganzen Rechner. + +**Why:** Marcus erlaubt Zugriff gezielt pro Projektordner; breite Sweeps über Home verletzen diese Abgrenzung, auch wenn sie technisch funktionieren. + +**How to apply:** Vor einem Such-Befehl den konkretesten bekannten Pfad wählen (z. B. das genannte Repo oder `~/claude-projects/robotunits`). Liegt das Ziel dort nicht, melden statt den Suchraum eigenmächtig auszuweiten. Gilt zusätzlich zu [[feedback-keine-intent-unterstellung]]s Grundregel „nur suchen, wenn angewiesen". diff --git a/wittgenstein/.gitignore b/wittgenstein/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/wittgenstein/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/wittgenstein/ai-control.json b/wittgenstein/ai-control.json new file mode 100644 index 0000000..8197946 --- /dev/null +++ b/wittgenstein/ai-control.json @@ -0,0 +1,3 @@ +{ + "pool": "privateDefault" +} diff --git a/wuerth-plato/.gitignore b/wuerth-plato/.gitignore new file mode 100644 index 0000000..c82ae92 --- /dev/null +++ b/wuerth-plato/.gitignore @@ -0,0 +1 @@ +.ai-control-running diff --git a/wuerth-plato/ai-control.json b/wuerth-plato/ai-control.json new file mode 100644 index 0000000..8197946 --- /dev/null +++ b/wuerth-plato/ai-control.json @@ -0,0 +1,3 @@ +{ + "pool": "privateDefault" +}