From 2d55b7eff3813419c7fef91304b9da2775a2b962 Mon Sep 17 00:00:00 2001 From: devshift-stack Date: Mon, 29 Dec 2025 21:14:40 +0100 Subject: [PATCH 1/5] fix: Remove hardcoded Gemini API key from gemini_service.dart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use String.fromEnvironment to read API key from dart-define flag instead of hardcoding. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- lib/services/gemini_service.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/services/gemini_service.dart b/lib/services/gemini_service.dart index 61afb15..a00fa31 100644 --- a/lib/services/gemini_service.dart +++ b/lib/services/gemini_service.dart @@ -4,8 +4,8 @@ import 'package:google_generative_ai/google_generative_ai.dart'; import 'user_profile_service.dart'; class GeminiService { - // Free tier API key - 15 requests/minute, 1500/day - static const String _apiKey = 'AIzaSyD5jBRl-Ti0r_uSyx5JW24H3CySQ8RWrS8'; + // API key from environment variable (dart-define) + static const String _apiKey = String.fromEnvironment('GEMINI_API_KEY', defaultValue: ''); GenerativeModel? _model; ChatSession? _chat; From 1394d78a07041fd2df4bfacef90413e1097aa399 Mon Sep 17 00:00:00 2001 From: devshift-stack Date: Mon, 29 Dec 2025 22:13:14 +0100 Subject: [PATCH 2/5] fix: Update Flutter version to 3.38.5 for SDK compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The project requires Dart SDK ^3.10.1, which is included in Flutter 3.38.5. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8635907..e92fe04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.0" + flutter-version: "3.38.5" channel: stable cache: true @@ -51,7 +51,7 @@ jobs: - name: Setup Flutter uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.0" + flutter-version: "3.38.5" channel: stable cache: true From 47ae09277c9a9eb60c82f3b081d743fce515f279 Mon Sep 17 00:00:00 2001 From: devshift-stack Date: Mon, 29 Dec 2025 22:29:30 +0100 Subject: [PATCH 3/5] fix: Add missing R class import in AlankoWidgetProvider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added import for R class to resolve 'Unresolved reference R' build error. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt index 2bdcacf..1dbb354 100644 --- a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt +++ b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.SharedPreferences import android.widget.RemoteViews import es.antonborri.home_widget.HomeWidgetProvider +import com.alanko.ai.alanko_ai.R class AlankoWidgetProvider : HomeWidgetProvider() { From a1a0406b61d7b7b03df5249e0db0c52158786795 Mon Sep 17 00:00:00 2001 From: devshift-stack Date: Mon, 29 Dec 2025 23:08:18 +0100 Subject: [PATCH 4/5] fix: Remove unnecessary R class import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R class is automatically available in the same package without import. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt index 1dbb354..2bdcacf 100644 --- a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt +++ b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt @@ -5,7 +5,6 @@ import android.content.Context import android.content.SharedPreferences import android.widget.RemoteViews import es.antonborri.home_widget.HomeWidgetProvider -import com.alanko.ai.alanko_ai.R class AlankoWidgetProvider : HomeWidgetProvider() { From ced4994c8562b1c9dc430050632bb4f4a4343212 Mon Sep 17 00:00:00 2001 From: devshift-stack Date: Mon, 29 Dec 2025 23:54:11 +0100 Subject: [PATCH 5/5] fix: Use correct R class import from namespace package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit namespace is com.alanko.ai, so R is in com.alanko.ai.R 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .github/workflows/otop-static-checks.yml | 40 +++++++ AGENTS.md | 6 + .../ai/alanko_ai/AlankoWidgetProvider.kt | 1 + docs/otop-standard.md | 107 ++++++++++++++++++ docs/prompts/01_blueprint_from_openapi.txt | 21 ++++ docs/prompts/02_v0_prompt.txt | 14 +++ docs/prompts/03_wiring_plan.txt | 17 +++ docs/prompts/04_checklist.txt | 15 +++ docs/retrofit-guide.md | 91 +++++++++++++++ linux/flutter/generated_plugin_registrant.cc | 12 +- linux/flutter/generated_plugins.cmake | 3 +- macos/Flutter/GeneratedPluginRegistrant.swift | 4 + otop.config.json | 14 +++ rules/spectral-otop.yml | 20 ++++ scripts/otop-add-operationid.py | 72 ++++++++++++ scripts/otop-env-audit.sh | 18 +++ scripts/otop-install.sh | 70 ++++++++++++ scripts/otop-openapi-lint.sh | 19 ++++ scripts/otop-scan.sh | 98 ++++++++++++++++ scripts/otop-uiid-audit.sh | 25 ++++ .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 22 files changed, 661 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/otop-static-checks.yml create mode 100644 docs/otop-standard.md create mode 100644 docs/prompts/01_blueprint_from_openapi.txt create mode 100644 docs/prompts/02_v0_prompt.txt create mode 100644 docs/prompts/03_wiring_plan.txt create mode 100644 docs/prompts/04_checklist.txt create mode 100644 docs/retrofit-guide.md create mode 100644 otop.config.json create mode 100644 rules/spectral-otop.yml create mode 100644 scripts/otop-add-operationid.py create mode 100644 scripts/otop-env-audit.sh create mode 100644 scripts/otop-install.sh create mode 100644 scripts/otop-openapi-lint.sh create mode 100644 scripts/otop-scan.sh create mode 100644 scripts/otop-uiid-audit.sh diff --git a/.github/workflows/otop-static-checks.yml b/.github/workflows/otop-static-checks.yml new file mode 100644 index 0000000..062771b --- /dev/null +++ b/.github/workflows/otop-static-checks.yml @@ -0,0 +1,40 @@ +name: OTOP Static Checks +on: + pull_request: + push: + +jobs: + otop: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: UI ID audit (non-blocking report) + run: | + if [ -f scripts/otop-uiid-audit.sh ]; then + bash scripts/otop-uiid-audit.sh || true + cat otop.audit.uiids.md || true + else + echo "No UI audit script." + fi + + - name: Hardcoded URL audit (block on critical findings) + run: | + if [ -f scripts/otop-env-audit.sh ]; then + bash scripts/otop-env-audit.sh || true + # fail if we find hardcoded localhost or obvious prod domains in src/ (tune as needed) + if rg -n "http://localhost:|https://api\." . -g'!**/node_modules/**' -g'!**/dist/**' -g'!**/build/**' -g'!**/.next/**' -g'!**/.venv/**' ; then + echo "Hardcoded URL found. Move to ENV/proxy." + exit 1 + fi + else + echo "No env audit script." + fi + + - name: OpenAPI lint (block if spec exists and fails) + run: | + if [ -f scripts/otop-openapi-lint.sh ]; then + bash scripts/otop-openapi-lint.sh + else + echo "No OpenAPI lint script." + fi diff --git a/AGENTS.md b/AGENTS.md index 2963588..5e7e742 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -506,3 +506,9 @@ firebase login Bei Fragen: Pull Request mit Frage erstellen oder Issue auf GitHub. **Owner:** devshift-stack (dsactivi) + +## OTOP Rules (MUST) +- Add `data-otop-id` + `data-testid` to every interactive UI component (Web). +- React Native: add `testID` + `accessibilityLabel="otop:"`. +- Do not hardcode API URLs; use ENV/proxy. +- Backend APIs must have OpenAPI with unique `operationId` + structured `tags`. diff --git a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt index 2bdcacf..18afa18 100644 --- a/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt +++ b/android/app/src/main/kotlin/com/alanko/ai/alanko_ai/AlankoWidgetProvider.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.SharedPreferences import android.widget.RemoteViews import es.antonborri.home_widget.HomeWidgetProvider +import com.alanko.ai.R class AlankoWidgetProvider : HomeWidgetProvider() { diff --git a/docs/otop-standard.md b/docs/otop-standard.md new file mode 100644 index 0000000..1aed462 --- /dev/null +++ b/docs/otop-standard.md @@ -0,0 +1,107 @@ +# OTOP Standard (Repo-weit) + +## Ziel +1) **Backend-Funktionen** sind eindeutig & stabil referenzierbar (OpenAPI `operationId`). +2) **UI-Elemente** sind eindeutig & stabil referenzierbar (`data-otop-id` / `data-testid`). +3) **Verbindung** Frontend↔Backend ist robust (keine hardcoded URLs). + +--- + +## 1) Backend Standard (OpenAPI = Function Registry) + +### Pflicht +- Jede REST-Operation MUSS besitzen: + - `operationId` (eindeutig innerhalb der Spec) + - `tags` (mind. 1 Tag; dient als Gruppierung im Tool) + +### operationId Konvention (deterministisch) +**Format:** `{tagSlug}_{method}_{pathSlug}` + +Beispiele: +- Tag: `Tasks`, Methode: `POST`, Pfad: `/tasks` + → `tasks_post_tasks` +- Tag: `Candidates`, Methode: `GET`, Pfad: `/candidates/{id}` + → `candidates_get_candidates_id` + +**Regeln:** +- `tagSlug` = lower + `_` statt Leerzeichen +- `pathSlug` = path ohne führenden `/`, `/`→`_`, `{id}`→`id` +- Keine Sonderzeichen, nur `[a-z0-9_]` + +### Tags Konvention +- Tags sollten “Menü-Struktur” im OTOP Tool abbilden: + - `Auth`, `Agents`, `Tasks`, `CRM`, `Telephony`, `Admin`, `Utils` +- Optional: “Substruktur” im Tag-Name: `CRM/Candidates`, `CRM/Jobs` + +### Health/Ready (empfohlen) +- `/health` (liveness) +- `/ready` (readiness; z.B. DB/Queue ready) + +--- + +## 2) Frontend Standard (UI IDs = Link Targets) + +### Web (React/Vite/Next) +Jede interaktive Komponente MUSS haben: +- `data-testid` +- `data-otop-id` + +**ID Schema:** `{domain}.{entity}.{screen}.{component}.{action}` + +Beispiele: +- `crm.candidate.list.search.input` +- `crm.candidate.list.create.button` +- `crm.candidate.form.save.button` +- `agents.task.detail.disable.toggle` + +**Beispiel:** +```tsx + +``` + +### React Native (Expo) +React Native nutzt kein `data-*`, darum: +- `testID` (Tests & Tool-Anker) +- `accessibilityLabel` (OTOP-Label, stabil) + +```tsx + + Create + +``` + +### Flutter +```dart +ElevatedButton( + key: const Key('crm.candidate.list.create.button'), + onPressed: () {}, + child: const Text('Create'), +) +``` + +--- + +## 3) Verbindung Frontend ↔ Backend (keine hardcoded URLs) +Erlaubt: +- Proxy `/api/*` (best) +- ENV `*_API_BASE_URL` (ok) + +Verboten: +- Hardcoded Domains/Ports im Code (`http://localhost:...`, `https://api...`) + +--- + +## 4) Definition of Done (für “Fertigstellung”) +Ein UI gilt als „fertig“, wenn: +- alle benötigten Backend-Funktionen entweder + - **verlinkt** sind (UI-ID → operationId), oder + - bewusst als **deaktiviert** markiert sind +- Delete/Disable/Unlink erzeugt Warnung über Auswirkungen (Dependencies) diff --git a/docs/prompts/01_blueprint_from_openapi.txt b/docs/prompts/01_blueprint_from_openapi.txt new file mode 100644 index 0000000..f1be793 --- /dev/null +++ b/docs/prompts/01_blueprint_from_openapi.txt @@ -0,0 +1,21 @@ +Du bist Produkt+Frontend-Architekt. + +INPUT: +- OpenAPI Spec (JSON/YAML) oder OpenAPI URL +- Ziel: UI Blueprint aus OpenAPI ableiten (Screens/Buttons/Felder/States) +- Regeln: + - Gruppiere nach Tags. + - Für jede Entity: List + Detail + Create + Edit + Delete Confirm. + - Aus Request Schemas: required/optional Felder + enums ableiten. + - Jede Action referenziert operationId + method + path. + - Jeder Screen hat Loading/Error/Empty/Success. + +OUTPUT FORMAT: +- SECTION: + - ENTITY: + - SCREEN: + - PURPOSE: + - UI ELEMENTS: + - BUTTONS (mit UI-ID Schema): + - API LINKS (operationId → method path): + - STATES: diff --git a/docs/prompts/02_v0_prompt.txt b/docs/prompts/02_v0_prompt.txt new file mode 100644 index 0000000..a32fc94 --- /dev/null +++ b/docs/prompts/02_v0_prompt.txt @@ -0,0 +1,14 @@ +Du bist v0 Prompt-Writer (React + Tailwind + shadcn/ui). + +INPUT: +- UI BLUEPRINT (aus 01) +- Regeln: + - shadcn/ui verwenden (Button, Dialog, Table, Input, Select, Tabs, Toast) + - Jeder interaktive Control bekommt data-otop-id + data-testid + - Delete immer mit Confirm Dialog + - List: Search + Filter + Pagination + Empty/Loading/Error + - Forms: required validation + disabled submit + success toast + - API Calls als Platzhalter: api.(params) + +OUTPUT: +- Gib nur den fertigen v0 Prompt aus. diff --git a/docs/prompts/03_wiring_plan.txt b/docs/prompts/03_wiring_plan.txt new file mode 100644 index 0000000..6b75e07 --- /dev/null +++ b/docs/prompts/03_wiring_plan.txt @@ -0,0 +1,17 @@ +Du bist Frontend-Integrator. + +INPUT: +- UI BLUEPRINT +- Generated client: Orval (React Query) oder openapi-fetch oder OpenAPI Generator SDK +- Regeln: + - Verwende ausschließlich generated client/hooks. + - Keine hardcoded URLs. + - Jede Mutation invalidiert betroffene Lists (z.B. create invalidiert list). + +OUTPUT: +- SCREEN: + - READS: + - MUTATIONS: + - PARAM MAPPING: + - UI STATES: + - CACHE/INVALIDATION: diff --git a/docs/prompts/04_checklist.txt b/docs/prompts/04_checklist.txt new file mode 100644 index 0000000..61050ef --- /dev/null +++ b/docs/prompts/04_checklist.txt @@ -0,0 +1,15 @@ +Du bist QA/Reviewer. + +INPUT: +- UI BLUEPRINT +- Regeln: + - Buttons: Create/Edit/Delete/Save/Cancel vorhanden + - Delete Confirm vorhanden + - required validation vorhanden + - Loading/Error/Empty/Success vorhanden + - data-otop-id + data-testid überall + - Buttons referenzieren die richtige operationId + +OUTPUT: +- SCREEN: ... + - [ ] ... diff --git a/docs/retrofit-guide.md b/docs/retrofit-guide.md new file mode 100644 index 0000000..f685407 --- /dev/null +++ b/docs/retrofit-guide.md @@ -0,0 +1,91 @@ +# Retrofit Guide (rückwirkend umstellen) + +## Warum rückwirkend in Schritten? +Wenn du „alles auf einmal“ in 22 Repos umstellst, entsteht Chaos (Merge-Konflikte, UI-Brüche). Darum: + +1) **Standards + Checks überall einführen** (OTOP Pack) +2) **Baselines/Reports erzeugen** (Audit) +3) **Gezielt nachziehen** (repoweise, screenweise) + +--- + +## Schritt 1: Pack in alle Repos übernehmen +- `bash otop-pack-v1/scripts/otop-install.sh ` + +Das kopiert: +- `docs/otop-standard.md` +- `docs/prompts/*` +- `rules/spectral-otop.yml` +- `scripts/*` +- `.github/workflows/otop-*.yml` (statische Checks) +- Ergänzt optional `AGENTS.md` + +--- + +## Schritt 2: Baseline pro Repo erzeugen +In jedem Repo: +```bash +bash scripts/otop-scan.sh +bash scripts/otop-uiid-audit.sh +bash scripts/otop-openapi-lint.sh +bash scripts/otop-env-audit.sh +``` + +Ergebnis: +- `otop.config.json` (Scan) +- `otop.audit.uiids.md` (UI-ID Coverage) +- `otop.audit.env.md` (Hardcoded URL Findings) +- Lint-Output für OpenAPI + +--- + +## Schritt 3: Priorisierung (empfohlen) +Basierend auf deinem aktuellen Report: +- **partner**: Hardcoded URLs → ENV (kritisch, sonst nie sauber deploybar) +- **CRM-activi**: OpenAPI ohne operationId → nicht verlinkbar +- **code-cloud-agents / Optimizecodecloudagents**: OpenAPI+Health ok, aber UI-IDs fehlen komplett + +--- + +## Schritt 4: UI IDs rückwirkend einführen (Web) +### 4.1 Schnellster Hebel: “Design System Wrapper” +Lege zentral Komponenten an, die IDs erzwingen: +- `OButton`, `OInput`, `OSelect`, `OLink` +- Props: `otopId` (string), setzt automatisch beide Attribute + +Dann ersetzt du schrittweise: +- `