diff --git a/_bmad-output/implementation-artifacts/stories/1-1-docker-compose-umgebung-aufsetzen.md b/_bmad-output/implementation-artifacts/stories/1-1-docker-compose-umgebung-aufsetzen.md new file mode 100644 index 0000000..c5023d6 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/1-1-docker-compose-umgebung-aufsetzen.md @@ -0,0 +1,64 @@ +# Story 1.1: Docker-Compose-Umgebung aufsetzen + + + + +Status: done + +## Story + +Als **Entwickler**, +möchte ich **eine vollstĂ€ndige lokale Docker-Compose-Umgebung**, +so dass ich **ohne lokale Python/Node-Installation entwickeln kann**. + +## Acceptance Criteria + +1. `docker compose up -d` startet drei Services: `db` (PostgreSQL 16), `api` (FastAPI Port 8000), `frontend` (Next.js Port 3000) +2. `GET /api/health` gibt `{"status": "ok"}` zurĂŒck +3. Frontend ist unter `http://localhost:3000` erreichbar +4. `pg_isready` im db-Container antwortet mit `accepting connections` +5. Named Volumes: `postgres_data` und `chroma_data` sind definiert + +## Tasks / Subtasks + +- [x] Task 1: `docker-compose.yml` mit drei Services erstellen (AC: 1–4) + - [x] Subtask 1.1: `db`-Service mit PostgreSQL 16-alpine, Healthcheck + - [x] Subtask 1.2: `api`-Service mit `depends_on db`, `env_file: .env`, `chroma_data`-Volume + - [x] Subtask 1.3: `frontend`-Service mit `NEXT_PUBLIC_API_URL`-Env +- [x] Task 2: `.env.example` mit allen Pflicht-Keys erstellen (AC: 5) +- [x] Task 3: `.gitignore` um `.env` erweitern +- [x] Task 4: `backend/Dockerfile` und `frontend/Dockerfile` erstellen + +## Dev Notes + +- `chroma_data` Volume wird auf `/app/chroma_db` im api-Container gemappt +- `postgres_data` Volume fĂŒr DB-Persistenz ĂŒber Neustarts hinweg +- `api` nutzt `service_healthy` Bedingung fĂŒr db-AbhĂ€ngigkeit + +### Project Structure Notes + +- `docker-compose.yml` im Projekt-Root +- `backend/Dockerfile` und `frontend/Dockerfile` + +### References + +- [Source: CLAUDE.md#Environment Variables] +- [Source: _bmad-output/planning-artifacts/architecture.md#Deployment-Architektur] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- PostgreSQL Healthcheck verhindert Race Condition beim API-Start. +- `chroma_data` Named Volume sichert ChromaDB-Persistenz zwischen Container-Neustarts. + +### File List + +- `docker-compose.yml` +- `.env.example` +- `backend/Dockerfile` +- `frontend/Dockerfile` diff --git a/_bmad-output/implementation-artifacts/stories/1-2-backend-python-umgebung-requirements.md b/_bmad-output/implementation-artifacts/stories/1-2-backend-python-umgebung-requirements.md new file mode 100644 index 0000000..de6aff9 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/1-2-backend-python-umgebung-requirements.md @@ -0,0 +1,62 @@ +# Story 1.2: Backend-Python-Umgebung & Requirements + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **eine vollstĂ€ndige `requirements.txt` mit allen AbhĂ€ngigkeiten**, +so dass ich **reproduzierbar installieren kann**. + +## Acceptance Criteria + +1. `pip install -r requirements.txt` installiert FastAPI, LangGraph, langchain-anthropic, langchain-openai, SQLAlchemy, alembic, chromadb, tavily-python, pypdf, pytest +2. `pytest backend/tests/` lĂ€uft durch (0 oder mehr passed, 0 failed) +3. `asyncio_mode = auto` ist in `pytest.ini` konfiguriert fĂŒr async Tests +4. Alle Pakete haben gepinnte Versionen fĂŒr Reproduzierbarkeit + +## Tasks / Subtasks + +- [x] Task 1: `requirements.txt` mit allen AbhĂ€ngigkeiten erstellen (AC: 1, 4) + - [x] Subtask 1.1: Core: fastapi, uvicorn, pydantic + - [x] Subtask 1.2: AI: langgraph, langchain-anthropic, langchain-openai + - [x] Subtask 1.3: DB: sqlalchemy[asyncio], asyncpg, alembic + - [x] Subtask 1.4: Tools: chromadb, tavily-python, pypdf, langchain-text-splitters + - [x] Subtask 1.5: Test: pytest, pytest-asyncio, httpx, aiosqlite +- [x] Task 2: `pytest.ini` mit `asyncio_mode = auto` konfigurieren (AC: 2, 3) +- [x] Task 3: `backend/__init__.py` fĂŒr Package-Erkennung + +## Dev Notes + +- `aiosqlite` wird fĂŒr In-Memory-SQLite in Tests verwendet (kein PostgreSQL in CI) +- `httpx` fĂŒr async FastAPI-Test-Client (`AsyncClient`) + +### Project Structure Notes + +- `backend/requirements.txt` +- `backend/pytest.ini` + +### References + +- [Source: CLAUDE.md#Python Code Style] +- [Source: _bmad-output/planning-artifacts/architecture.md#Technische-AbhĂ€ngigkeiten] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `aiosqlite` ermöglicht In-Memory-SQLite fĂŒr Tests ohne laufenden PostgreSQL-Server. +- `asyncio_mode = auto` ist essentiell fĂŒr pytest-asyncio-KompatibilitĂ€t mit allen async Fixtures. + +### File List + +- `backend/requirements.txt` +- `backend/pytest.ini` +- `backend/__init__.py` diff --git a/_bmad-output/implementation-artifacts/stories/1-3-datenbank-migrationen-mit-alembic.md b/_bmad-output/implementation-artifacts/stories/1-3-datenbank-migrationen-mit-alembic.md new file mode 100644 index 0000000..3fededa --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/1-3-datenbank-migrationen-mit-alembic.md @@ -0,0 +1,73 @@ +# Story 1.3: Datenbank-Migrationen mit Alembic + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **Alembic-Migrationen fĂŒr `blueprints` und `council_runs`**, +so dass ich **das Datenbankschema versioniert verwalten kann**. + +## Acceptance Criteria + +1. `alembic upgrade head` erstellt Tabellen `blueprints` und `council_runs` +2. `alembic current` zeigt die aktuelle Revision +3. `blueprints`-Tabelle hat: `id` (UUID), `version` (Int), `name`, `nodes` (JSON), `edges` (JSON), `created_at`, `updated_at` +4. `council_runs`-Tabelle hat: `id`, `blueprint_id` (nullable FK), `input_topic`, `status`, `final_draft`, `critic_score`, `iteration_count`, `created_at`, `completed_at` +5. SQLAlchemy async ORM-Modelle existieren fĂŒr beide Tabellen + +## Tasks / Subtasks + +- [x] Task 1: Alembic-Konfiguration initialisieren (AC: 1, 2) + - [x] Subtask 1.1: `alembic.ini` mit `script_location` konfigurieren + - [x] Subtask 1.2: `alembic/env.py` fĂŒr async SQLAlchemy anpassen +- [x] Task 2: Migration 001 — `blueprints`-Tabelle (AC: 3) +- [x] Task 3: Migration 002 — `council_runs`-Tabelle (AC: 4) +- [x] Task 4: SQLAlchemy ORM-Modelle (AC: 5) + - [x] Subtask 4.1: `models/blueprint.py` + - [x] Subtask 4.2: `models/council_run.py` +- [x] Task 5: `database.py` mit async Engine und Session-Fabrik + +## Dev Notes + +- Async SQLAlchemy mit `asyncpg` fĂŒr PostgreSQL, `aiosqlite` fĂŒr Tests +- `get_session()` als FastAPI-Dependency fĂŒr Dependency Injection +- In Tests: `AsyncEngine` mit In-Memory-SQLite, `create_all()` statt Alembic + +### Project Structure Notes + +- `backend/alembic/versions/001_create_blueprints_table.py` +- `backend/alembic/versions/002_create_council_runs_table.py` +- `backend/models/blueprint.py` +- `backend/models/council_run.py` +- `backend/database.py` + +### References + +- [Source: _bmad-output/planning-artifacts/architecture.md#Datenbankschema] +- [Source: CLAUDE.md#Database — Alembic for migrations] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- Alembic `env.py` wurde fĂŒr async Engine (run_async_migrations) angepasst. +- ORM-Modelle nutzen `String(36)` fĂŒr UUIDs (portabel zwischen PostgreSQL und SQLite). + +### File List + +- `backend/alembic.ini` +- `backend/alembic/env.py` +- `backend/alembic/versions/001_create_blueprints_table.py` +- `backend/alembic/versions/002_create_council_runs_table.py` +- `backend/models/__init__.py` +- `backend/models/blueprint.py` +- `backend/models/council_run.py` +- `backend/database.py` diff --git a/_bmad-output/implementation-artifacts/stories/2-1-councilstate-typeddict-implementieren.md b/_bmad-output/implementation-artifacts/stories/2-1-councilstate-typeddict-implementieren.md new file mode 100644 index 0000000..4097301 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/2-1-councilstate-typeddict-implementieren.md @@ -0,0 +1,66 @@ +# Story 2.1: CouncilState TypedDict implementieren + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **den `CouncilState` TypedDict mit allen Feldern und Reducern**, +so dass er **das einzige State-Objekt fĂŒr alle Agents ist**. + +## Acceptance Criteria + +1. `feedback_history: Annotated[List[str], operator.add]` — append-only Reducer +2. `messages: Annotated[list, operator.add]` — akkumulierend +3. `APPROVAL_THRESHOLD = 8.0` importierbar aus `state` +4. `MAX_ITERATIONS = 5` importierbar aus `state` +5. Alle Felder haben korrekte Typ-Annotationen (mypy-kompatibel) + +## Tasks / Subtasks + +- [x] Task 1: `state.py` mit `CouncilState` TypedDict (AC: 1–5) + - [x] Subtask 1.1: `input_topic`, `current_draft`, `route_decision` (str) + - [x] Subtask 1.2: `feedback_history` mit `operator.add` Reducer + - [x] Subtask 1.3: `messages` mit `operator.add` Reducer + - [x] Subtask 1.4: `iteration_count` (int), `critic_score` (Optional[float]) + - [x] Subtask 1.5: `run_id`, `active_node` (str) +- [x] Task 2: Konstanten `APPROVAL_THRESHOLD`, `MAX_ITERATIONS` (AC: 3, 4) +- [x] Task 3: Unit-Tests fĂŒr Reducer-Verhalten (AC: 1, 2) + - [x] Subtask 3.1: Test `feedback_history` akkumuliert + - [x] Subtask 3.2: Test `messages` akkumuliert + - [x] Subtask 3.3: Test Konstanten-Import + +## Dev Notes + +- `operator.add` als Reducer → Werte werden niemals ĂŒberschrieben, nur angehĂ€ngt +- `typing_extensions.TypedDict` fĂŒr Python 3.10-KompatibilitĂ€t +- Bezug: `from typing import Annotated, List, Optional` und `import operator` + +### Project Structure Notes + +- `backend/state.py` +- `backend/tests/test_state.py` + +### References + +- [Source: CLAUDE.md#Architecture Concepts — CouncilState] +- [Source: _bmad-output/planning-artifacts/architecture.md#CouncilState-TypedDict] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `typing_extensions.TypedDict` statt `typing.TypedDict` fĂŒr breitere Python-KompatibilitĂ€t. +- Tests benutzen `operator.add` direkt um Reducer-Semantik zu validieren. + +### File List + +- `backend/state.py` +- `backend/tests/test_state.py` diff --git a/_bmad-output/implementation-artifacts/stories/2-2-master-agent-node-implementieren.md b/_bmad-output/implementation-artifacts/stories/2-2-master-agent-node-implementieren.md new file mode 100644 index 0000000..9822d69 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/2-2-master-agent-node-implementieren.md @@ -0,0 +1,65 @@ +# Story 2.2: Master-Agent-Node implementieren + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **den `master_agent_node`**, +so dass er **auf Basis von `input_topic` und `feedback_history` einen Draft erstellt**. + +## Acceptance Criteria + +1. Bei leerem `feedback_history` → Initialer Draft auf Basis von `input_topic` +2. Bei befĂŒlltem `feedback_history` → User-Prompt enthĂ€lt Feedback-EintrĂ€ge +3. RĂŒckgabe enthĂ€lt `current_draft` (nicht leer), `messages` (3 Elemente), `active_node = "master_agent"` +4. `iteration_count` wird um 1 erhöht +5. LLM: Claude 3.5 Sonnet, `temperature=0.7` + +## Tasks / Subtasks + +- [x] Task 1: `agents/master_agent.py` implementieren (AC: 1–5) + - [x] Subtask 1.1: `_build_master_prompt(state)` fĂŒr initiale vs. Feedback-Iterationen + - [x] Subtask 1.2: `master_agent_node(state)` LangGraph-Node-Funktion + - [x] Subtask 1.3: `SystemMessage`, `HumanMessage`, `AIMessage` in `messages` +- [x] Task 2: `agents/__init__.py` mit Exports +- [x] Task 3: Unit-Tests (AC: 1–4) + - [x] Subtask 3.1: Test initiale Draft-Erstellung (kein Feedback) + - [x] Subtask 3.2: Test Feedback-Integration im Prompt + - [x] Subtask 3.3: Test `iteration_count` +1 + +## Dev Notes + +- Mock-Pattern: `@patch("agents.master_agent.ChatAnthropic")` +- LLM-Aufruf: `llm.invoke([SystemMessage(...), HumanMessage(...)])` → AIMessage +- Bezug: `from langchain_anthropic import ChatAnthropic`, `from langchain_core.messages import ...` + +### Project Structure Notes + +- `backend/agents/master_agent.py` +- `backend/agents/__init__.py` +- `backend/tests/test_api.py` (integrationsĂ€hnliche Tests) + +### References + +- [Source: CLAUDE.md#Python Code Style] +- [Source: _bmad-output/planning-artifacts/architecture.md#CouncilState-TypedDict] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `_build_master_prompt()` unterscheidet via `if not state["feedback_history"]` zwischen erstem Aufruf und Iterations. +- `feedback_block` formatiert alle Feedback-Runden nummeriert. + +### File List + +- `backend/agents/master_agent.py` +- `backend/agents/__init__.py` diff --git a/_bmad-output/implementation-artifacts/stories/2-4-writer-agent-node-implementieren.md b/_bmad-output/implementation-artifacts/stories/2-4-writer-agent-node-implementieren.md new file mode 100644 index 0000000..14e284e --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/2-4-writer-agent-node-implementieren.md @@ -0,0 +1,56 @@ +# Story 2.4: Writer-Agent-Node implementieren + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **den `writer_agent_node`**, +so dass er **den finalen, vom Critic genehmigten Draft professionell poliert**. + +## Acceptance Criteria + +1. ErhĂ€lt `current_draft` aus State, gibt polierten finalen Draft zurĂŒck +2. `active_node = "writer_agent"` im RĂŒckgabe-Dict +3. LLM: Claude 3.5 Sonnet, `temperature=0.3` (deterministische Ausgabe) +4. System-Prompt instruiert: Grammatik/Stil verbessern, Inhalt NICHT Ă€ndern +5. Tests: LLM gemockt, RĂŒckgabe-Dict validiert + +## Tasks / Subtasks + +- [x] Task 1: `agents/writer_agent.py` implementieren (AC: 1–4) + - [x] Subtask 1.1: `_SYSTEM_PROMPT` fĂŒr finale Politur-Anweisung + - [x] Subtask 1.2: `writer_agent_node(state)` LangGraph-Node-Funktion + - [x] Subtask 1.3: RĂŒckgabe: `current_draft`, `messages`, `active_node` +- [x] Task 2: Unit-Tests (AC: 1–3, 5) + +## Dev Notes + +- `temperature=0.3` fĂŒr konsistente Formatierung (weniger KreativitĂ€t als Master) +- System-Prompt: "Do NOT change the factual content or overall structure" +- Einfachster der drei Agent-Nodes — kein bedingtes Routing + +### Project Structure Notes + +- `backend/agents/writer_agent.py` + +### References + +- [Source: _bmad-output/planning-artifacts/epics.md#Story-2.4] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- Writer-Agent gibt nur `current_draft`, `messages`, `active_node` zurĂŒck (kein `route_decision`). + +### File List + +- `backend/agents/writer_agent.py` diff --git a/_bmad-output/implementation-artifacts/stories/2-5-langgraph-graph-bauen-und-ausfuehren.md b/_bmad-output/implementation-artifacts/stories/2-5-langgraph-graph-bauen-und-ausfuehren.md new file mode 100644 index 0000000..504c7e4 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/2-5-langgraph-graph-bauen-und-ausfuehren.md @@ -0,0 +1,62 @@ +# Story 2.5: LangGraph-Graph bauen und ausfĂŒhren + + + + +Status: done + +## Story + +Als **Backend-Entwickler**, +möchte ich **einen kompilierten LangGraph-Graphen** (`build_council_graph`), +so dass er **Master→Critic→(conditional)→Writer ausfĂŒhrt**. + +## Acceptance Criteria + +1. `build_council_graph()` gibt kompilierten, `invoke`-fĂ€higen `StateGraph` zurĂŒck +2. Graph-Topologie: `master_agent → critic_agent → (rework: master_agent | approve: writer_agent) → END` +3. `route_after_critic(state)` liest `route_decision` und leitet korrekt weiter +4. `run_council_async()` fĂŒhrt den Graph in einem Thread-Pool aus (blockiert Event Loop nicht) +5. WebSocket-Callback `on_node_start` wird vor jedem Node aufgerufen + +## Tasks / Subtasks + +- [x] Task 1: `services/graph_builder.py` mit `build_council_graph()` (AC: 1, 2) + - [x] Subtask 1.1: `StateGraph(CouncilState)` mit drei Nodes + - [x] Subtask 1.2: `set_entry_point("master_agent")` + - [x] Subtask 1.3: `add_conditional_edges` fĂŒr Critic-Routing +- [x] Task 2: `route_after_critic(state)` Routing-Funktion (AC: 3) +- [x] Task 3: `run_council_async()` mit `asyncio.get_event_loop().run_in_executor()` (AC: 4, 5) +- [x] Task 4: Integration-Tests (LLMs gemockt) (AC: 1–3) + +## Dev Notes + +- `interrupt_before` nicht in Phase-1-Graph (nur in dynamischem Builder Phase 3) +- `on_node_start`-Callback: `lambda run_id, node_name: broadcast_event(run_id, {...})` +- Thread-Pool: `asyncio.get_event_loop().run_in_executor(None, graph.invoke, state)` + +### Project Structure Notes + +- `backend/services/graph_builder.py` +- `backend/tests/test_routing.py` + +### References + +- [Source: _bmad-output/planning-artifacts/architecture.md#ADR-002] +- [Source: CLAUDE.md#Key Design Constraints — Cycles are first-class] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `run_in_executor(None, ...)` nutzt Standard-Thread-Pool von asyncio; kein expliziter ThreadPoolExecutor nötig. +- Wrapper-Nodes fĂŒr `on_node_start`-Callback werden zur Laufzeit um den Original-Node gelegt. + +### File List + +- `backend/services/graph_builder.py` +- `backend/tests/test_routing.py` diff --git a/_bmad-output/implementation-artifacts/stories/2-6-fastapi-run-endpunkte-implementieren.md b/_bmad-output/implementation-artifacts/stories/2-6-fastapi-run-endpunkte-implementieren.md new file mode 100644 index 0000000..56683d5 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/2-6-fastapi-run-endpunkte-implementieren.md @@ -0,0 +1,72 @@ +# Story 2.6: FastAPI-Run-Endpunkte implementieren + + + + +Status: done + +## Story + +Als **API-Nutzer**, +möchte ich **`POST /api/councils/run` und `GET /api/councils/run/{run_id}`**, +so dass ich **einen Council-Run starten und sein Ergebnis abrufen kann**. + +## Acceptance Criteria + +1. `POST /api/councils/run` → `202 Accepted` mit `run_id`, asynchroner HintergrundausfĂŒhrung +2. `GET /api/councils/run/{run_id}` → `status`, `final_draft` bei abgeschlossenem Run +3. Unbekannte `run_id` → `404 Not Found` +4. Leeres `input_topic` → Validierungsfehler (422) +5. `GET /api/health` → `{"status": "ok", "service": "CouncilOS API"}` +6. FastAPI-App registriert alle Router unter `/api`-Prefix + +## Tasks / Subtasks + +- [x] Task 1: `api/routes.py` mit Run-Endpunkten (AC: 1–4) + - [x] Subtask 1.1: `CouncilRunRequest` Pydantic-Modell + - [x] Subtask 1.2: `POST /councils/run` mit `BackgroundTasks` + - [x] Subtask 1.3: `GET /councils/run/{run_id}` mit `run_store.get()` + - [x] Subtask 1.4: `GET /health` (AC: 5) +- [x] Task 2: `api/run_store.py` In-Memory-Run-Store (AC: 2, 3) +- [x] Task 3: `main.py` FastAPI-Entrypoint (AC: 6) + - [x] Subtask 3.1: Router-Registrierung mit `/api` Prefix + - [x] Subtask 3.2: CORS-Middleware +- [x] Task 4: Unit-Tests fĂŒr alle Endpunkte (AC: 1–5) + +## Dev Notes + +- `BackgroundTasks.add_task()` fĂŒr nicht-blockierende LangGraph-AusfĂŒhrung +- `run_store.create(run_id, input_topic)` → status: "pending" +- Test-Setup: `AsyncClient(app=app)` via `httpx` ohne echten LangGraph-Lauf + +### Project Structure Notes + +- `backend/api/routes.py` +- `backend/api/run_store.py` +- `backend/main.py` +- `backend/tests/test_api.py` + +### References + +- [Source: _bmad-output/planning-artifacts/architecture.md#REST-Endpunkte] +- [Source: _bmad-output/planning-artifacts/architecture.md#ADR-002] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `run_store` ist ein einfaches `dict` mit Thread-Safe-Operationen (GIL ausreichend fĂŒr MVP). +- `BackgroundTasks` erlaubt sofortige `202`-Antwort ohne auf LangGraph zu warten. +- CORS: `allow_origins=["*"]` fĂŒr lokale Entwicklung; in Produktion einschrĂ€nken. + +### File List + +- `backend/api/__init__.py` +- `backend/api/routes.py` +- `backend/api/run_store.py` +- `backend/main.py` +- `backend/tests/test_api.py` diff --git a/_bmad-output/implementation-artifacts/stories/3-1-react-flow-canvas-mit-custom-agent-node.md b/_bmad-output/implementation-artifacts/stories/3-1-react-flow-canvas-mit-custom-agent-node.md new file mode 100644 index 0000000..cb4d948 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/3-1-react-flow-canvas-mit-custom-agent-node.md @@ -0,0 +1,76 @@ +# Story 3.1: React-Flow-Canvas mit Custom Agent Node + + + + +Status: done + +## Story + +Als **Nutzer**, +möchte ich **Agent-Nodes per Drag & Drop auf den Canvas ziehen**, +so dass ich **meinen KI-Rat visuell zusammenstellen kann**. + +## Acceptance Criteria + +1. Drag & Drop aus linker Seitenleiste → Custom `AgentNode` erscheint auf Canvas +2. Klick auf Node → rechtes `NodeSettingsPanel` öffnet sich +3. Änderung von Name/Prompt/Modell → Canvas-Node aktualisiert sich live (Zustand-Bindung) +4. Node zeigt: Name, LLM-Modell (farblich kodiert), Tool-Badges (🔍, 📄) +5. Pulsier-Animation (`animate-pulse`) wenn `isActive = true` + +## Tasks / Subtasks + +- [x] Task 1: `components/nodes/AgentNode.tsx` Custom React Flow Node (AC: 1, 4, 5) + - [x] Subtask 1.1: Anzeige: Name, Modell-Badge, Tool-Icons (Globe, FileText) + - [x] Subtask 1.2: `isActive`-Zustand → `animate-pulse` + Indigo-Rahmen + - [x] Subtask 1.3: `Handle` (source + target) fĂŒr Verbindungen +- [x] Task 2: `store/council-store.ts` Zustand-Store (AC: 3) + - [x] Subtask 2.1: `nodes`, `edges`, `selectedNodeId`, `councilName` + - [x] Subtask 2.2: `addAgentNode()`, `updateNodeData()`, `selectNode()` +- [x] Task 3: `components/panels/NodeSidebar.tsx` (AC: 1) +- [x] Task 4: `components/panels/NodeSettingsPanel.tsx` (AC: 2, 3) +- [x] Task 5: `components/ArchitectCanvas.tsx` React Flow Wrapper +- [x] Task 6: Frontend-Tests fĂŒr Store und Parser (AC: 3) + +## Dev Notes + +- `NODE_TYPES = { agent: AgentNode }` in `ArchitectCanvas` registrieren +- Zustand via `useCouncilStore` (Zustand) — kein React-Kontext-Drill +- `memo()` fĂŒr Performance bei vielen Nodes + +### Project Structure Notes + +- `frontend/app/components/nodes/AgentNode.tsx` +- `frontend/app/components/ArchitectCanvas.tsx` +- `frontend/app/components/panels/NodeSidebar.tsx` +- `frontend/app/components/panels/NodeSettingsPanel.tsx` +- `frontend/app/store/council-store.ts` +- `frontend/app/types/council.ts` + +### References + +- [Source: _bmad-output/planning-artifacts/ux-design.md#Agent-Node-Canvas-Element] +- [Source: CLAUDE.md#React-Conventions — custom node components] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `MODEL_LABELS` und `MODEL_COLORS` als Lookup-Maps fĂŒr farbliche Modell-Kodierung. +- `memo()` verhindert unnötige Re-Renders bei vielen Nodes auf dem Canvas. + +### File List + +- `frontend/app/types/council.ts` +- `frontend/app/store/council-store.ts` +- `frontend/app/components/nodes/AgentNode.tsx` +- `frontend/app/components/ArchitectCanvas.tsx` +- `frontend/app/components/NavTabs.tsx` +- `frontend/app/components/panels/NodeSidebar.tsx` +- `frontend/app/components/panels/NodeSettingsPanel.tsx` +- `frontend/app/app/__tests__/council-store.test.ts` diff --git a/_bmad-output/implementation-artifacts/stories/3-2-lineare-und-bedingte-edges.md b/_bmad-output/implementation-artifacts/stories/3-2-lineare-und-bedingte-edges.md new file mode 100644 index 0000000..6fcaa01 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/3-2-lineare-und-bedingte-edges.md @@ -0,0 +1,63 @@ +# Story 3.2: Lineare und bedingte Edges + + + + +Status: done + +## Story + +Als **Nutzer**, +möchte ich **Pfeile zwischen Nodes ziehen und als linear/bedingt markieren**, +so dass ich **den Workflow meines Councils definieren kann**. + +## Acceptance Criteria + +1. Verbindungsziehen zwischen Handles → lineare graue Edge entsteht +2. Klick auf Edge → `EdgeSettingsPanel` öffnet sich +3. Edge als „bedingt" markieren mit Condition-Label → gestrichelte rote Linie mit Label +4. Edge als „approve" markieren → grĂŒne Linie mit Label +5. Edge-Typ und Condition werden im Zustand persistiert + +## Tasks / Subtasks + +- [x] Task 1: `components/edges/ConditionalEdge.tsx` Custom React Flow Edge (AC: 3, 4) + - [x] Subtask 1.1: `SmoothStepPath` Basis-Routing + - [x] Subtask 1.2: Farb-Kodierung: grau (linear), rot gestrichelt (rework), grĂŒn (approve) + - [x] Subtask 1.3: Label-Rendering via `EdgeLabelRenderer` +- [x] Task 2: `components/panels/EdgeSettingsPanel.tsx` (AC: 2, 3, 4) + - [x] Subtask 2.1: Typ-Auswahl (linear/conditional) + - [x] Subtask 2.2: Condition-Label-Eingabe wenn `type = conditional` +- [x] Task 3: `store/council-store.ts` — `updateEdgeData()` Funktion (AC: 5) +- [x] Task 4: `EDGE_TYPES` Registrierung in `ArchitectCanvas` + +## Dev Notes + +- `EDGE_TYPES = { conditional: ConditionalEdge }` + `defaultEdgeType: "conditional"` +- Edge-Daten: `edge.data.type` (linear|conditional), `edge.data.condition` (string) +- `EdgeLabelRenderer` positioniert Label absolut relativ zum Edge-Mittelpunkt + +### Project Structure Notes + +- `frontend/app/components/edges/ConditionalEdge.tsx` +- `frontend/app/components/panels/EdgeSettingsPanel.tsx` + +### References + +- [Source: _bmad-output/planning-artifacts/ux-design.md#Edge-Verbindungspfeil] + +## Dev Agent Record + +### Agent Model Used + +Amelia (đŸ’» BMAD Dev Agent) + +### Completion Notes List + +- `getSmoothStepPath` aus `@xyflow/react` liefert `edgePath`, `labelX`, `labelY` in einem Aufruf. +- Farben werden direkt via `stroke`-Prop auf `BaseEdge` gesetzt. + +### File List + +- `frontend/app/components/edges/ConditionalEdge.tsx` +- `frontend/app/components/panels/EdgeSettingsPanel.tsx` diff --git a/_bmad-output/implementation-artifacts/stories/3-3-blueprint-parser-react-flow-json.md b/_bmad-output/implementation-artifacts/stories/3-3-blueprint-parser-react-flow-json.md new file mode 100644 index 0000000..4d0b891 --- /dev/null +++ b/_bmad-output/implementation-artifacts/stories/3-3-blueprint-parser-react-flow-json.md @@ -0,0 +1,66 @@ +# Story 3.3: Blueprint-Parser (React Flow → JSON) + + + + +Status: done + +## Story + +Als **System**, +möchte ich **eine `parseGraphToBlueprint()`-Funktion**, +so dass sie **den Canvas-State in ein valides Blueprint-JSON konvertiert**. + +## Acceptance Criteria + +1. Nodes + Edges → `CouncilBlueprint` mit `version`, `name`, `nodes[]`, `edges[]` +2. Bedingte Edge → `type: "conditional"` und `condition: "