diff --git a/.dockerignore b/.dockerignore
index 7eeeb1ff8..468882824 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -18,9 +18,8 @@ lamb-kb-server-stable/
Documentation/
scripts/
-frontend/svelte-app/node_modules/
-frontend/svelte-app/.svelte-kit/
-frontend/svelte-app/.vite/
+frontend/packages/**/node_modules/
+frontend/packages/**/.svelte-kit/
frontend/build/
backend/test_output/
diff --git a/.github/recipes/bump-dev-version.instructions.md b/.github/recipes/bump-dev-version.instructions.md
index 0d856e408..a73ce701c 100644
--- a/.github/recipes/bump-dev-version.instructions.md
+++ b/.github/recipes/bump-dev-version.instructions.md
@@ -12,8 +12,8 @@ Short, repeatable instructions to bump the `dev` version used by the frontend bu
## Files involved 🔧
-- `frontend/svelte-app/scripts/generate-version.js` (source of record for the dev version)
-- `frontend/svelte-app/src/lib/version.js` (auto-generated by the script; usually git-ignored)
+- `frontend/packages/creator-app/scripts/generate-version.js` (source of record for the dev version)
+- `frontend/packages/creator-app/src/lib/version.js` (auto-generated by the script; usually git-ignored)
> Note: the generated file (`src/lib/version.js`) is normally ignored by git. You generally commit only the change to the generator script.
@@ -25,27 +25,27 @@ Short, repeatable instructions to bump the `dev` version used by the frontend bu
```bash
# Edit the file and change the version string
-sed -n '1,120p' frontend/svelte-app/scripts/generate-version.js
+sed -n '1,120p' frontend/packages/creator-app/scripts/generate-version.js
# Change: version: '0.3' -> version: '0.4'
```
2. Run the generator to refresh the generated file:
```bash
-node frontend/svelte-app/scripts/generate-version.js
-# This writes frontend/svelte-app/src/lib/version.js with current commit/branch/date.
+node frontend/packages/creator-app/scripts/generate-version.js
+# This writes frontend/packages/creator-app/src/lib/version.js with current commit/branch/date.
```
3. Verify the generated file contains the new version and a recent commit hash:
```bash
-cat frontend/svelte-app/src/lib/version.js
+cat frontend/packages/creator-app/src/lib/version.js
```
4. Commit the *script* change (do not add the generated file unless you intentionally want it tracked):
```bash
-git add frontend/svelte-app/scripts/generate-version.js
+git add frontend/packages/creator-app/scripts/generate-version.js
git commit -m "chore: bump dev version to 0.4"
```
diff --git a/.github/skills/release-sync/SKILL.md b/.github/skills/release-sync/SKILL.md
index 51a677d6f..a308705bb 100644
--- a/.github/skills/release-sync/SKILL.md
+++ b/.github/skills/release-sync/SKILL.md
@@ -105,25 +105,25 @@ git push origin v0.X
Update the version number displayed in the LAMB UI banner.
**Important Version Bumping Rules:**
-- Version is defined in `frontend/svelte-app/scripts/generate-version.js`
+- Version is defined in `frontend/packages/creator-app/scripts/generate-version.js`
- Run the generator script to create `src/lib/version.js`
- **Only commit the generator script**, NOT the generated `version.js` file
- The generated file is built during deployment
**Update version number:**
-1. Edit `frontend/svelte-app/scripts/generate-version.js`
+1. Edit `frontend/packages/creator-app/scripts/generate-version.js`
2. Change the version line: `version: '0.4'` → `version: '0.5'`
3. Regenerate the version file:
```bash
-node frontend/svelte-app/scripts/generate-version.js
+node frontend/packages/creator-app/scripts/generate-version.js
```
4. Stage and commit only the generator script:
```bash
-git add frontend/svelte-app/scripts/generate-version.js
+git add frontend/packages/creator-app/scripts/generate-version.js
git commit -m "chore: bump version to 0.X"
git push origin main
```
@@ -200,9 +200,9 @@ git checkout main && git pull origin main && \
git tag -a v0.X -m "Release v0.X" && git push origin v0.X
# Step 5: Bump version
-# Edit: frontend/svelte-app/scripts/generate-version.js (version: '0.X')
-node frontend/svelte-app/scripts/generate-version.js
-git add frontend/svelte-app/scripts/generate-version.js
+# Edit: frontend/packages/creator-app/scripts/generate-version.js (version: '0.X')
+node frontend/packages/creator-app/scripts/generate-version.js
+git add frontend/packages/creator-app/scripts/generate-version.js
git commit -m "chore: bump version to 0.X" && git push origin main
# Step 6: Move tag
@@ -232,9 +232,9 @@ git tag --sort=-v:refname | head -5
## Related Files
-- **Version generator:** `frontend/svelte-app/scripts/generate-version.js`
+- **Version generator:** `frontend/packages/creator-app/scripts/generate-version.js`
- **Version documentation:** `CLAUDE.md` (Version Bumping section)
-- **Generated version file:** `frontend/svelte-app/src/lib/version.js` (auto-generated, do not commit)
+- **Generated version file:** `frontend/packages/creator-app/src/lib/version.js` (auto-generated, do not commit)
## Resources
diff --git a/.gitignore b/.gitignore
index 6676206d8..2efa42b1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,6 +24,10 @@ wheels/
# Virtual Environments
.env
+.env.local
+.env.*.local
+**/.env.local
+**/.env.*.local
.venv
env/
venv/
@@ -31,6 +35,18 @@ ENV/
env.bak/
venv.bak/
+# Secrets / credentials (any depth)
+*.pem
+*.key
+secrets.json
+credentials.json
+
+# Playwright / vitest artifacts
+**/test-results/.last-run.json
+**/test-results/
+playwright-report/
+.playwright/
+
# Backend specific
backend/__pycache__/
backend/.env
@@ -44,6 +60,12 @@ backend/pipelines/*
backend/lamb_assistants/*
backend/static/public
backend/timelog.md
+# File evaluation module: student submission files (runtime; do not commit)
+# Ignores this directory and everything under it recursively.
+backend/uploads/file-eval/
+# Local DB dump and module dev snapshot (do not commit)
+lamb_v4_dump.sql
+backend/file_eval_snapshot.txt
# Database
*.db
@@ -68,6 +90,10 @@ config.json
/frontend/node_modules
/frontend/build
/frontend/.svelte-kit
+frontend/svelte-app/.svelte-kit/
+frontend/svelte-app/package-lock.json
+frontend/svelte-app/src/lib/version.js
+/frontend/svelte-app
/frontend/package
/frontend/.env
/frontend/.env.*
@@ -75,9 +101,28 @@ config.json
/frontend/vite.config.js.timestamp-*
/frontend/vite.config.ts.timestamp-*
-# Allow committing Svelte/Vite config for the frontend app
-!frontend/svelte-app/svelte.config.js
-!frontend/svelte-app/vite.config.js
+# pnpm store and lockfiles
+.pnpm-store/
+frontend/.pnpm-store/
+**/.pnpm-store/
+pnpm-lock.yaml
+frontend/pnpm-lock.yaml
+frontend/packages/**/pnpm-lock.yaml
+
+# SvelteKit generated files (all packages)
+frontend/packages/**/.svelte-kit/
+
+# package-lock.json files (using pnpm instead)
+frontend/package-lock.json
+frontend/packages/**/package-lock.json
+
+# Local tooling / scratch (not product source)
+aac_logs/
+
+# Generated dev version stamps — regenerate with each package's scripts/generate-version.js
+# (Do not commit local timestamps; build/dev runs the generator.)
+frontend/packages/creator-app/src/lib/version.js
+frontend/packages/module-chat/src/lib/version.js
# IDE and editors
.idea/
@@ -129,7 +174,6 @@ out/
build/
Documentation/human-context.md
testcurls.md
-frontend/svelte-app/static/config.js
# Open-WebUI specific ignores
open-webui/backend/data/
@@ -166,14 +210,20 @@ static/
data/
Documentation/test-scripts/package-lock.json
testing/package-lock.json
-testing/playwright/test-results/.last-run.json
+test-results/
+.last-run.json
backend/static/cache
docker-compose.yaml
.pnpm-store/
backend/testing/
backend/uploads/
-frontend/packages/
-.opencode
+
+#OPENCODE
+.opencode/ # Carpeta de skills y caché local
+opencode.json # Tu configuración con paths absolutos
+.env # SIEMPRE ignora esto (aquí van las API keys)
+AGENTS.md # Tu archivo de contexto (si quieres que sea privado)
+CONTEXT.md
./clinerules
@@ -184,9 +234,52 @@ Documentation/tfg-structure-guide.md
diagram-17-clean.md
docs/
frontend-architecture-diagrams.md
-opencode.json
package-lock.json
+.playwright-mcp
+
+aac_logs/
+.env.old
+
+# Worktrees
+.worktrees/
+.agents/
+skills-lock.json
+test-results/
+testing/load/document-cache-test/
+testing/load/kv-cache-classroom/
+backend/testing/context_dumps/
+
+# Documentación de trabajo y planes
+Documentation/
+docs/superpowers/plans/
+
+# Notas de trabajo de frontend
+frontend/DOCKER_CI_UPDATES.md
+frontend/FILES_CREATED.md
+frontend/MERGE_DEV_TO_PHASE4.md
+frontend/MIGRATION_MONOREPO.md
+frontend/PHASE2_BUGS.md
+frontend/PHASE2_COMMIT_GUIDE.md
+frontend/PHASE2_COMPLETION_SUMMARY.md
+frontend/PHASE2_EXECUTION_PLAN.md
+frontend/README_PHASE2_COMPLETE.md
+fix5_axios_interceptor_plan.md
+
+# Dumps y resultados de testing
+**/context_dumps/
+.last-run.json
+
+
+./clinerules
+Documentation/assistantform-refactoring-log.md
+Documentation/tfg-capitol-5-prototipado.md
+Documentation/tfg-structure-guide.md
+diagram-17-clean.md
+docs/
+frontend-architecture-diagrams.md
+opencode.json
+package-lock.json
.playwright-mcp
aac_logs/
@@ -195,4 +288,7 @@ aac_logs/
# Worktrees
.worktrees/
.agents/
-skills-lock.json
\ No newline at end of file
+skills-lock.json
+test-results/
+testing/load/document-cache-test/
+testing/load/kv-cache-classroom/
diff --git a/.opencode/plans/pr-description-lamba-refactor-merge-kvcache-v2.md b/.opencode/plans/pr-description-lamba-refactor-merge-kvcache-v2.md
new file mode 100644
index 000000000..a85f4dcf3
--- /dev/null
+++ b/.opencode/plans/pr-description-lamba-refactor-merge-kvcache-v2.md
@@ -0,0 +1,930 @@
+# Pull Request: `lamba_refactor_merge_kvcache_v2`
+
+**Branch:** `lamba_refactor_merge_kvcache_v2` → `dev`
+**Commits:** ~332 commits (vs `dev`)
+**Stats:** 586 files changed, +75,209 / -35,014 lines
+
+---
+
+## Summary
+
+Integration branch consolidating LAMB's main development lines: the completions pipeline with a two-plugin architecture (KV-cache friendly), Knowledge Stores (new KB Server), Library Manager, LTI modules (Phase 4), and the frontend migration to a pnpm monorepo. Includes post-merge fixes, design system restoration, and pre-merge validation hardening.
+
+---
+
+## Branch lineage and merge history
+
+This branch is an **integration line**, not a single feature. It merges several upstream development tracks into one testable whole before landing on `dev`.
+
+```mermaid
+flowchart TD
+ dev[dev]
+ phase4["feature/issue#277/phase4_lamba_port"]
+ kbInt["projects/refactor/kbserver-lamb-integration"]
+ kvCache["library_manager_and_kv_cache_final_refactor"]
+ target["lamba_refactor_merge_kvcache_v2"]
+
+ phase4 --> target
+ kbInt --> target
+ kvCache --> target
+ dev --> phase4
+ dev --> kbInt
+ dev --> kvCache
+ target --> devPR["PR → dev"]
+```
+
+### Primary merge: `feature/issue#277/phase4_lamba_port` (Issue #277)
+
+**Commit:** `f1fb71b6` — *Merge phase4_lamba_port: frontend monorepo + LTI modules + Knowledge Stores (temporal)*
+
+This is the **original Phase 4 branch** (`feature/issue#277/phase4_lamba_port`). Merging it into `lamba_refactor_merge_kvcache_v2` brings:
+
+| Area | What landed from Phase 4 |
+|------|---------------------------|
+| **Frontend monorepo** | pnpm workspace: `@lamb/ui`, `creator-app`, `module-chat`, `module-file-eval`; removal of `frontend/svelte-app/` |
+| **LTI Activity Module System** | `backend/lamb/modules/` — chat + file evaluation modules, JWT sessions, `LTIContext` hooks |
+| **File evaluation** | Upload, AI grading, Moodle LTI passback (`lti_passback.py`), group work |
+| **LTI core refactor** | `lti_router.py`, `lti_activity_manager.py`, `auth_context.py` — module dispatch, dead template removal |
+| **Backend packaging** | `requirements-base.txt` + `requirements-ml.txt`, Docker/Caddy updates |
+| **Shared UI package** | Nav, Footer, authService, sessionManager, i18n (4 locales), sanitize utils in `@lamb/ui` |
+| **Post-merge consolidation** | Import path fixes (`@lamb/ui`), AAC terminal, org-admin, assistant sharing — see `frontend/MERGE_DEV_TO_PHASE4.md` |
+
+**Post-Phase-4 fixes on this branch** (same PR, after `f1fb71b6`):
+
+- `81546152` — restore AssistantForm + imports after merge conflicts
+- `c9d622ec` — LTI configure endpoint with existing activities
+- `554b6539`, `0b04eb1a`, `756faf82` — Playwright regressions
+- `ab442b79` — design system tokens, logo, version
+- `d728c491` — DB init once per process; LTI module `config.js` samples
+- `eb0c8718` — pre-merge PPS/RAG + document RAG validation (see §5)
+- **Create PPS default (post-merge):** `resolveCreatePromptProcessor()` forces `kvcache_augment` on create reset/submit; both PPS visible in Advanced create; removed temporary `LAMB_LEGACY_PPS_DEFAULT` / `apply_defaults_pps_policy` served-defaults hack
+
+### Other integration merges (same branch)
+
+| Merge | Content |
+|-------|---------|
+| `6c3968c2` | KB Server + LAMB integration (`projects/refactor/kbserver-lamb-integration`) — Knowledge Stores, `lamb-kb-server/`, Creator Interface KS routes |
+| KV-cache / Library Manager line | `kvcache_augment`, `library_file_rag`, query rewriting KS RAG, Library Manager folders/capabilities |
+| `7a3103cb` | `dev` synced into cost-management / library refactor line |
+
+**Review note:** Phase 4–specific bugs (monorepo auth, dual `apiClient`, raw `fetch()` in LTI modules, XSS/marked migration) are documented in [`frontend/MERGE_DEV_TO_PHASE4.md`](frontend/MERGE_DEV_TO_PHASE4.md). A dedicated review of that surface is **deferred** until after this PR description is finalized.
+
+---
+
+## 1. Completions Pipeline — Two-Plugin Architecture
+
+**Goal:** Separate the legacy pipeline (`simple_augment` + KB-based RAGs) from the new KV-cache friendly pipeline (`kvcache_augment` + Knowledge Stores / Library Manager), without modifying legacy code.
+
+### 1.1 New PPS: `kvcache_augment.py`
+
+- Injects `document_context` at the **beginning** of the system prompt (KV-cache friendly: stable prefix = cache hits ~97-98%).
+- `COMPATIBLE_RAG` declares compatible RAGs: `library_file_rag`, `knowledge_store_rag`, `query_rewriting_ks_rag`, `rubric_rag`, `no_rag`.
+- `DEFAULT_RAG_PROMPT_TEMPLATE` as D3 fallback when no `prompt_template` exists but RAG context is available.
+- **Labeled doc wrapper:** wraps the document with a "REFERENCE DOCUMENT" header + creator selection note + recency-bias reminder.
+- `COMPATIBLE_RAG` validation in `main.py` (`load_and_validate_plugins`): rejects invalid PPS-RAG combinations at **completion** time.
+- `metadata_validators.py` + Creator Interface create/update: rejects incompatible combos at **save** time (see §5, commit `eb0c8718`).
+- **Default PPS changed:** `kvcache_augment` is now the default PPS for new assistants (was `simple_augment`). Create mode uses `resolveCreatePromptProcessor()` so the form always selects `kvcache_augment` when available, regardless of stale org defaults or localStorage cache. Both PPS are visible in Advanced Mode create; legacy `simple_augment` remains disabled (read-only) in edit mode only.
+
+**Key files:**
+- `backend/lamb/completions/pps/kvcache_augment.py` (new, 197 lines)
+- `backend/lamb/completions/main.py` (COMPATIBLE_RAG validation)
+- `backend/lamb/completions/org_config_resolver.py` (new)
+- `backend/lamb/completions/plugin_config.py` (new)
+
+### 1.2 New RAG: `library_file_rag.py`
+
+- Fetches documents from Library Manager via HTTP (`/libraries/{id}/items/{id}/content`).
+- Replaces logic previously embedded in `single_file_rag.py`.
+- `single_file_rag.py` remains untouched as legacy (static `file_path` only).
+
+### 1.3 Query Rewriting KS RAG
+
+- New RAG processor `query_rewriting_ks_rag`: rewrites the user query using a small-fast-model over conversation history, then queries Knowledge Stores via KB Server v2 (port 9092).
+- Shared modules: `_ks_query_helpers.py` (used by `knowledge_store_rag` and `query_rewriting_ks_rag`) and `_query_rewriting_helper.py` (SFM rewriting).
+- **Note:** legacy `context_aware_rag` still targets KB Server v1 (9090); it was **not** refactored onto `_ks_query_helpers`.
+- Frontend display name: `query_rewriting_ks_rag` → **"Context Aware Rag"**. Legacy `context_aware_rag` uses the same label today (suffix "(Old)" planned but not yet applied — see §11).
+
+**Key files:**
+- `backend/lamb/completions/rag/query_rewriting_ks_rag.py` (new, 122 lines)
+- `backend/lamb/completions/rag/_ks_query_helpers.py` (new, 112 lines)
+- `backend/lamb/completions/rag/_query_rewriting_helper.py` (new, 116 lines)
+- `backend/lamb/completions/rag/knowledge_store_rag.py` (new, 129 lines)
+
+### 1.4 Frontend — PPS/RAG Compatibility Filtering
+
+- `ragProcessorHelpers.js`: `PPS_COMPATIBLE_RAG` backend mirror, `getCompatibleRagForPps()`, `ppsSupportsDocumentRag()`, `isHiddenInCreate()`, `isDocumentRag()`, `isLegacyPps()`, `resolveCreatePromptProcessor()`.
+- `ConfigurationPanel.svelte`: RAG dropdown filtered by PPS compatibility; PPS dropdown shows both processors in create Advanced Mode.
+- **Legacy PPS lock in edit mode:** All assistants using `simple_augment` show a locked read-only UI in edit mode with an i18n'd amber notice banner. KB selectors, KS selectors, Top-K input, and document toggle are all disabled. The user is directed to create a new assistant for changes.
+- `LibraryItemSelector.svelte`: library + item selector for Document RAG.
+- `AssistantForm` refactored: from ~1,747 LOC monolith to ~535 LOC orchestrator + logic modules + UI subcomponents.
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `backend/lamb/completions/pps/kvcache_augment.py` | New KV-cache friendly PPS (197 lines) |
+| `backend/lamb/completions/rag/library_file_rag.py` | RAG via Library Manager HTTP (72 lines) |
+| `backend/lamb/completions/rag/query_rewriting_ks_rag.py` | Query rewriting + KS retrieval (122 lines) |
+| `backend/lamb/completions/rag/knowledge_store_rag.py` | Knowledge Store RAG processor (129 lines) |
+| `backend/lamb/completions/rag/_ks_query_helpers.py` | Shared KS query helpers (112 lines) |
+| `backend/lamb/completions/rag/_query_rewriting_helper.py` | SFM query rewriting (116 lines) |
+| `backend/lamb/completions/org_config_resolver.py` | Org-level config resolution (80 lines) |
+| `backend/lamb/completions/plugin_config.py` | Plugin configuration loader (61 lines) |
+| `backend/creator_interface/metadata_validators.py` | Metadata validation: library refs, **COMPATIBLE_RAG on save** |
+| `frontend/.../components/ConfigurationPanel.svelte` | Extracted configuration panel (363 lines) |
+| `frontend/.../components/RagOptionsPanel.svelte` | Extracted RAG options panel (104 lines) |
+| `frontend/.../components/KnowledgeBaseSelector.svelte` | KB selector component (103 lines) |
+| `frontend/.../components/KnowledgeStoreSelector.svelte` | KS selector component (72 lines) |
+| `frontend/.../components/LibraryItemSelector.svelte` | Library item selector (111 lines) |
+| `frontend/.../components/FormActions.svelte` | Form action buttons (50 lines) |
+| `frontend/.../logic/assistantFormFetchers.js` | Extracted fetch logic (176 lines) |
+| `frontend/.../logic/assistantFormSubmit.js` | Extracted submit logic |
+| `frontend/.../logic/importAssistantValidator.js` | Import validation (141 lines) |
+| `frontend/.../utils/ragProcessorHelpers.js` | PPS/RAG compatibility + helpers (183 lines) |
+| `frontend/.../components/assistants/ConfigurationPanel.svelte.test.js` | Both PPS in create dropdown + legacy edit lock tests |
+| `frontend/.../components/assistants/RagOptionsPanel.svelte.test.js` | Legacy banner + disabled selectors tests |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `backend/static/json/defaults.json` | Default PPS: `simple_augment` → `kvcache_augment` |
+| `backend/creator_interface/assistant_router.py` | Metadata default PPS: `simple_augment` → `kvcache_augment`; removed served-defaults PPS override |
+| `backend/lamb/assistant_default_pps.py` | Simplified: `default_prompt_processor()` + `load_defaults_json_document()` (no env policy) |
+| `backend/config.py` | Removed `LAMB_LEGACY_PPS_DEFAULT` env flag |
+| `backend/.env.example` | Removed `LAMB_LEGACY_PPS_DEFAULT` docs |
+| `testing/playwright/.env.sample` | Removed `LAMB_LEGACY_PPS_DEFAULT` docs |
+| `backend/lamb/completions/pps/simple_augment.py` | Cleanup: removed document_context, added COMPATIBLE_RAG |
+| `backend/lamb/completions/rag/context_aware_rag.py` | Legacy KB v1 path unchanged; query rewriting via `_query_rewriting_helper` only |
+| `backend/lamb/completions/main.py` | COMPATIBLE_RAG at completion time; `_require_document_context` HTTP 502 on document load failure |
+| `backend/creator_interface/assistant_router.py` | Metadata validation on create **and** update |
+| `frontend/.../assistants/AssistantForm.svelte` | Refactor: ~1747→~535 LOC; `resolveCreatePromptProcessor()` on non-advanced submit |
+| `frontend/.../assistants/logic/assistantFormState.svelte.js` | Document RAG fields; create reset uses `resolveCreatePromptProcessor()` |
+| `frontend/.../assistants/logic/assistantFormSubmit.js` | `document_rag: 'library_file_rag'`; submit validation for PPS/RAG/refs |
+| `frontend/.../assistants/logic/importAssistantValidator.js` | Import checks aligned with backend metadata rules |
+| `frontend/.../stores/assistantConfigStore.js` | Fallback PPS + capabilities; added new RAGs to store |
+| `frontend/.../utils/ragProcessorHelpers.js` | Added `isLegacyPps()` + `resolveCreatePromptProcessor()` |
+| `frontend/.../components/assistants/components/ConfigurationPanel.svelte` | Both PPS in create Advanced Mode, `isLegacyEdit` wiring, document toggle disabled |
+| `frontend/.../components/assistants/components/RagOptionsPanel.svelte` | Generalized legacy banner for all `simple_augment` RAGs, disabled selectors |
+| `frontend/.../components/assistants/components/KnowledgeBaseSelector.svelte` | Added `disabled` prop |
+| `frontend/.../components/assistants/components/KnowledgeStoreSelector.svelte` | Added `disabled` prop |
+| `frontend/packages/ui/src/lib/locales/*.json` | Added `legacyPpsNotice` key in 4 locales |
+
+---
+
+## 2. Knowledge Stores — New KB Server (port 9092)
+
+**Goal:** Vectorization microservice with plugin architecture, replacing the legacy KB Server (port 9090) for new assistants.
+
+### 2.1 Backend KB Server (`lamb-kb-server/`)
+
+- **Plugin architecture:** 3 vector DBs (ChromaDB, Qdrant), 4 chunking strategies (simple, hierarchical/parent-child, by_page, by_section), 3 embedding vendors (OpenAI, Ollama, local).
+- **Async ingestion:** SQLite-backed job queue with polling.
+- **Per-org filesystem isolation:** vectors at `data/storage/{org_id}/{collection_id}/`.
+- **Locked store setup:** chunking strategy, embedding vendor/model, and vector DB backend are immutable after creation.
+- **FR-10:** Library items referenced by active Knowledge Stores cannot be deleted.
+
+### 2.2 LAMB Integration
+
+- **Creator Interface endpoints:** `/creator/knowledge-stores/*` with ACL + proxy to KB Server.
+- **DB tables:** `knowledge_stores` + `kb_content_links` in LAMB DB.
+- **HTTP client:** `backend/creator_interface/knowledge_store_client.py` (608 lines).
+- **Router:** `backend/creator_interface/knowledge_store_router.py` (753 lines).
+- **RAG processor:** `knowledge_store_rag.py` + `query_rewriting_ks_rag.py` in the completions pipeline.
+- **CLI:** `lamb ks ...` / `lamb knowledge-store ...` in `lamb-cli/`.
+
+### 2.3 Frontend
+
+- **Components:** `KnowledgeStoresList.svelte` (895 lines), `KnowledgeStoreDetail.svelte` (1146 lines), `AddContentToKSModal.svelte` (641 lines), `IngestionProgressModal.svelte` (323 lines).
+- **Unified wizard:** `CreateKnowledgeWizard.svelte` with stepper for Library + Knowledge Store.
+- **Cache store:** `ksCache.js` (stale-while-revalidate).
+- **Route:** `/knowledge-stores` redirects to `/libraries?section=knowledge-stores`.
+- **i18n:** Knowledge Bases/Stores keys across 4 locales (en, es, ca, eu).
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `lamb-kb-server/` | Complete microservice (new, ~5000+ lines) |
+| `lamb-kb-server/backend/main.py` | FastAPI app + lifespan (184 lines) |
+| `lamb-kb-server/backend/config.py` | Centralized config (77 lines) |
+| `lamb-kb-server/backend/database/` | Connection + models (114 + 139 lines) |
+| `lamb-kb-server/backend/plugins/base.py` | Plugin base class (394 lines) |
+| `lamb-kb-server/backend/plugins/chunking/` | 4 strategies: simple, hierarchical, by_page, by_section |
+| `lamb-kb-server/backend/plugins/embedding/` | 3 vendors: openai, ollama, local |
+| `lamb-kb-server/backend/plugins/vector_db/` | 2 backends: chromadb, qdrant |
+| `lamb-kb-server/backend/routers/` | collections, content, jobs, query, system |
+| `lamb-kb-server/backend/services/` | collection, ingestion, query services |
+| `lamb-kb-server/backend/tasks/worker.py` | Async job worker (320 lines) |
+| `lamb-kb-server/backend/schemas/` | Pydantic schemas (collection, content, query, jobs) |
+| `lamb-kb-server/tests/` | 29 test files (unit + integration + e2e) |
+| `backend/creator_interface/knowledge_store_client.py` | HTTP client for KB Server v2 (608 lines) |
+| `backend/creator_interface/knowledge_store_router.py` | Creator Interface KS endpoints (753 lines) |
+| `frontend/.../knowledgeStores/KnowledgeStoresList.svelte` | KS list view (895 lines) |
+| `frontend/.../knowledgeStores/KnowledgeStoreDetail.svelte` | KS detail view (1146 lines) |
+| `frontend/.../knowledgeStores/AddContentToKSModal.svelte` | Add content modal (641 lines) |
+| `frontend/.../knowledgeStores/IngestionProgressModal.svelte` | Ingestion progress (323 lines) |
+| `frontend/.../knowledge/CreateKnowledgeWizard.svelte` | Unified creation wizard (580 lines) |
+| `frontend/.../knowledge/wizard/StepKSSetup.svelte` | KS setup step (784 lines) |
+| `frontend/.../knowledge/wizard/StepKSContent.svelte` | KS content step (262 lines) |
+| `frontend/.../knowledge/wizard/StepLibrarySetup.svelte` | Library setup step (418 lines) |
+| `frontend/.../knowledge/wizard/StepLibraryContent.svelte` | Library content step (503 lines) |
+| `frontend/.../knowledge/wizard/Step8_ReviewCreate.svelte` | Review + create step (752 lines) |
+| `frontend/.../knowledge/wizard/Step9_Done.svelte` | Done step (113 lines) |
+| `frontend/.../modals/CreateKnowledgeStoreModal.svelte` | Create KS modal (641 lines) |
+| `frontend/.../services/knowledgeStoreService.js` | KS API service (403 lines) |
+| `frontend/.../stores/ksCache.js` | KS cache store (119 lines) |
+| `frontend/.../routes/knowledge-stores/+page.svelte` | KS route redirect (20 lines) |
+| `lamb-cli/src/lamb_cli/commands/knowledge_store.py` | CLI commands (425 lines) |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `backend/lamb/database_manager.py` | New tables knowledge_stores + kb_content_links (+837 lines) |
+| `backend/creator_interface/knowledges_router.py` | Refactored for legacy KB + new KS coexistence |
+| `backend/creator_interface/kb_server_manager.py` | Dual KB Server legacy + v2 support |
+| `backend/creator_interface/main.py` | KS router registration |
+| `frontend/.../components/KnowledgeBaseDetail.svelte` | KB/KS coexistence adjustments |
+| `frontend/.../components/KnowledgeBasesList.svelte` | KB/KS coexistence adjustments |
+| `frontend/.../routes/knowledgebases/+page.svelte` | KB/KS coexistence adjustments |
+| `frontend/packages/ui/src/lib/locales/*.json` | New KS i18n keys across 4 locales |
+| `lamb-cli/src/lamb_cli/commands/assistant.py` | KS support in assistant commands |
+
+---
+
+## 3. Library Manager — Document Repository
+
+**Goal:** Independent microservice (port 9091) for importing and structuring documents in markdown format with permalinks.
+
+### 3.1 Improvements in this branch
+
+- **Folders:** folder system with CRUD, move, tree view (`folder_service.py`, `folders router`).
+- **Capabilities:** structured content (text, images, pages) served via API (`capabilities router`, `content_handlers/`).
+- **File tree UI:** `FileTreeModal.svelte` (1055 lines), `FileTreeNode.svelte` (283 lines), `MoveToFolderPicker.svelte`, `TreePreviewPane.svelte`.
+- **Content renderers:** `ImagesRenderer`, `PagesRenderer`, `TextRenderer`.
+- **Plugin improvements:** markitdown error handling, shared MIME detection, improved URL import, YouTube transcript with titles.
+- **FR-10 interlock:** items referenced by Knowledge Stores cannot be deleted.
+
+### 3.2 LAMB Integration
+
+- **Creator Interface:** `/creator/libraries/*` endpoints with ACL + proxy.
+- **Library Manager client:** `library_manager_client.py` (164 lines updated).
+- **Library router:** `library_router.py` (560 lines updated).
+- **Frontend:** `LibrariesList.svelte`, `LibraryDetail.svelte`, `ItemContentModal.svelte`, `ItemContentTabs.svelte`, `PluginPickerModal.svelte`.
+- **Cache:** `librariesCache.js` (stale-while-revalidate).
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `library-manager/backend/routers/folders.py` | Folder CRUD router (159 lines) |
+| `library-manager/backend/routers/capabilities.py` | Content capabilities router (225 lines) |
+| `library-manager/backend/services/folder_service.py` | Folder service (398 lines) |
+| `library-manager/backend/plugins/content_handlers/` | capability, images, pages, text handlers |
+| `library-manager/backend/plugins/_markitdown_errors.py` | Markitdown error handling (88 lines) |
+| `library-manager/backend/plugins/_mime.py` | Shared MIME detection (32 lines) |
+| `library-manager/backend/schemas/folders.py` | Folder schemas (87 lines) |
+| `library-manager/backend/database/models.py` | Folder model added (+45 lines) |
+| `frontend/.../libraries/ItemContentModal.svelte` | Item content viewer (75 lines) |
+| `frontend/.../libraries/ItemContentTabs.svelte` | Content tabs (308 lines) |
+| `frontend/.../libraries/PluginPickerModal.svelte` | Plugin picker (129 lines) |
+| `frontend/.../libraries/fileTree/FileTreeModal.svelte` | File tree modal (1055 lines) |
+| `frontend/.../libraries/fileTree/FileTreeNode.svelte` | Tree node component (283 lines) |
+| `frontend/.../libraries/fileTree/MoveToFolderPicker.svelte` | Move picker (98 lines) |
+| `frontend/.../libraries/fileTree/TreePreviewPane.svelte` | Preview pane (174 lines) |
+| `frontend/.../libraries/fileTree/treeOps.js` | Tree operations logic (352 lines) |
+| `frontend/.../libraries/capabilities/ImagesRenderer.svelte` | Images renderer (174 lines) |
+| `frontend/.../libraries/capabilities/PagesRenderer.svelte` | Pages renderer (86 lines) |
+| `frontend/.../libraries/capabilities/TextRenderer.svelte` | Text renderer (41 lines) |
+| `frontend/.../libraries/capabilities/index.js` | Capabilities barrel (53 lines) |
+| `frontend/.../stores/librariesCache.js` | Libraries cache store (117 lines) |
+| `frontend/.../routes/libraries/+page.svelte` | Libraries unified page (270 lines) |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `library-manager/backend/main.py` | Registered folders + capabilities routers (+87 lines) |
+| `library-manager/backend/services/import_service.py` | Refactored import flow (+196 lines) |
+| `library-manager/backend/services/content_service.py` | Added content serving (+138 lines) |
+| `library-manager/backend/routers/content.py` | Content endpoint updated |
+| `library-manager/backend/routers/importing.py` | Import router updated (+84 lines) |
+| `library-manager/backend/plugins/base.py` | Improved plugin base (+112 lines) |
+| `library-manager/backend/plugins/markitdown_import.py` | Improved error handling |
+| `library-manager/backend/plugins/markitdown_plus_import.py` | Improvements (+244 lines) |
+| `library-manager/backend/plugins/url_import.py` | Improved URL import (+216 lines) |
+| `library-manager/backend/plugins/youtube_transcript_import.py` | Titles + improvements (+243 lines) |
+| `library-manager/backend/plugins/simple_import.py` | Minor adjustments |
+| `library-manager/backend/database/connection.py` | Folder support (+33 lines) |
+| `backend/creator_interface/library_manager_client.py` | Updated client (+164 lines) |
+| `backend/creator_interface/library_router.py` | Expanded router: folders, capabilities (+560 lines) |
+| `frontend/.../libraries/LibrariesList.svelte` | UI adjustments |
+| `frontend/.../libraries/LibraryDetail.svelte` | Added file tree + capabilities (+141 lines) |
+| `frontend/.../services/libraryService.js` | New endpoints: folders, capabilities |
+| `lamb-cli/src/lamb_cli/commands/library.py` | New CLI commands: folders (+356 lines) |
+
+---
+
+## 4. Phase 4 — LTI Activity Module System + Frontend Monorepo
+
+**Source branch:** `feature/issue#277/phase4_lamba_port` (Issue #277), merged at `f1fb71b6`. Section 4 describes the Phase 4 surface area; post-merge fixes for this line are in §5.
+
+### 4.0 Phase 4 merge scope (from `feature/issue#277/phase4_lamba_port`)
+
+Beyond the file lists below, the Phase 4 merge introduced:
+
+- **Monorepo auth/session:** unified `sessionManager` in `@lamb/ui` with `registerOnClearSession()` (logout clears creator-app stores).
+- **Module SPAs:** `/m/chat/` and `/m/file-eval/` built as separate Vite apps, served by backend static routes.
+- **Removed legacy templates:** `lti_activity_setup.html`, `lti_dashboard.html` replaced by Svelte module UIs.
+- **Documentation relocation:** old root `Documentation/` tree trimmed; operational docs moved to `docs/`, `environment_data/`, service READMEs.
+- **Known Phase 4 follow-ups (not blocking this PR):** dual `apiClient.js`, raw `fetch()` in LTI modules, marked→`renderMarkdownSafe` migration — see [`frontend/MERGE_DEV_TO_PHASE4.md`](frontend/MERGE_DEV_TO_PHASE4.md).
+
+### 4.1 LTI Activity Module System
+
+- **Chat Module (`modules/chat/`):** LTI launch, JWT session management, instructor dashboard, student flow.
+- **File Evaluation Module (`modules/file_evaluation/`):** AI-powered file evaluation, grading, group work, Moodle grade passback.
+ - `evaluation_service.py` (255 lines): AI evaluation via assistant.
+ - `grade_service.py` (144 lines): grade management.
+ - `lti_passback.py` (269 lines): Moodle grade sync via LTI outcomes.
+ - `document_extractor.py` (107 lines): document content extraction.
+ - `storage_service.py` (86 lines): submission storage.
+- **Base module (`modules/base.py`):** `LTIContext` class, `on_student_launch()` / `on_instructor_launch()` hooks.
+- **JWT tokens:** replaced in-memory LTI tokens with persistent JWTs.
+- **Security:** auth guards, disabled user handling, session reconciliation.
+
+### 4.2 Frontend Monorepo Migration
+
+- **pnpm workspace:** `@lamb/ui` (shared), `creator-app` (main SPA), `module-chat` (LTI chat), `module-file-eval` (LTI file evaluation).
+- **`@lamb/ui`:** Nav, Footer, LanguageSelector, ConfirmationModal, userStore, configStore, authService, configService, i18n (4 locales), sanitize utils.
+- **`creator-app`:** full creator interface UI with design system (Button, Badge, Modal, Card, Tabs, etc.).
+- **`module-chat`:** independent SPA for `/m/chat/` (setup + dashboard).
+- **`module-file-eval`:** independent SPA for `/m/file-eval/` (upload + grading).
+- **`svelte-app/` removal:** complete migration to monorepo.
+
+### 4.3 Design System
+
+- **UI primitives:** 20+ components in `frontend/packages/creator-app/src/lib/components/ui/` (Button, IconButton, Modal, Badge, Card, Toast, Tabs, FormField, Dropdown, OverflowMenu, Dropzone, Stepper, Banner, Collapsible, Checkbox, EmptyState, Skeleton*).
+- **CSS tokens in `app.css`:** semantic colors (brand, success, warning, danger, info), shadows, radii, motion, typography.
+- **Icons:** barrel in `icons.js` (re-export of lucide-svelte). `flowbite-svelte` removed.
+- **Status badges:** `statusBadge.js` with locked mapping (ready=success, processing=info, failed=danger, etc.).
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `backend/lamb/modules/__init__.py` | Module registry (23 lines) |
+| `backend/lamb/modules/base.py` | LTIContext + base module class (109 lines) |
+| `backend/lamb/modules/chat/__init__.py` | Chat module (149 lines) |
+| `backend/lamb/modules/chat/service.py` | Chat service: dashboard, sessions (484 lines) |
+| `backend/lamb/modules/file_evaluation/__init__.py` | File eval module (268 lines) |
+| `backend/lamb/modules/file_evaluation/service.py` | File eval service (441 lines) |
+| `backend/lamb/modules/file_evaluation/evaluation_service.py` | AI evaluation (255 lines) |
+| `backend/lamb/modules/file_evaluation/evaluator_client.py` | Evaluator HTTP client (234 lines) |
+| `backend/lamb/modules/file_evaluation/grade_service.py` | Grade management (144 lines) |
+| `backend/lamb/modules/file_evaluation/lti_passback.py` | Moodle grade passback (269 lines) |
+| `backend/lamb/modules/file_evaluation/document_extractor.py` | Document extraction (107 lines) |
+| `backend/lamb/modules/file_evaluation/storage_service.py` | Submission storage (86 lines) |
+| `backend/lamb/modules/file_evaluation/router.py` | File eval API router (375 lines) |
+| `backend/lamb/modules/file_evaluation/schemas.py` | Pydantic schemas (147 lines) |
+| `backend/lamb/modules/file_evaluation/migrations.py` | DB migrations (121 lines) |
+| `frontend/packages/ui/` | Complete shared package (new) |
+| `frontend/packages/ui/src/lib/components/Nav.svelte` | Navigation component |
+| `frontend/packages/ui/src/lib/components/Footer.svelte` | Footer component |
+| `frontend/packages/ui/src/lib/components/modals/ConfirmationModal.svelte` | Shared confirmation modal |
+| `frontend/packages/ui/src/lib/services/authService.js` | Auth service (225 lines) |
+| `frontend/packages/ui/src/lib/services/configService.js` | Config service (53 lines) |
+| `frontend/packages/ui/src/lib/session/sessionManager.js` | Session manager (57 lines) |
+| `frontend/packages/ui/src/lib/stores/userStore.js` | User store |
+| `frontend/packages/ui/src/lib/stores/configStore.js` | Config store (28 lines) |
+| `frontend/packages/ui/src/lib/i18n/` | i18n setup + 4 locales |
+| `frontend/packages/ui/src/lib/utils/sanitize.js` | DOMPurify sanitization |
+| `frontend/packages/ui/src/lib/styles/theme.css` | Theme CSS (59 lines) |
+| `frontend/packages/module-chat/` | LTI chat package (new) |
+| `frontend/packages/module-chat/src/routes/setup/+page.svelte` | Chat setup page (479 lines) |
+| `frontend/packages/module-chat/src/routes/dashboard/+page.svelte` | Chat dashboard (352 lines) |
+| `frontend/packages/module-file-eval/` | LTI file evaluation package (new) |
+| `frontend/packages/module-file-eval/src/routes/upload/+page.svelte` | Student upload (719 lines) |
+| `frontend/packages/module-file-eval/src/routes/grading/+page.svelte` | Instructor grading (860 lines) |
+| `frontend/packages/module-file-eval/src/lib/services/api.js` | File eval API client (110 lines) |
+| `frontend/packages/module-file-eval/src/lib/services/gradingService.js` | Grading service (81 lines) |
+| `frontend/packages/module-file-eval/src/lib/services/submissionService.js` | Submission service (50 lines) |
+| `frontend/packages/module-file-eval/src/lib/locales/` | i18n across 4 locales |
+| `frontend/packages/creator-app/src/lib/components/ui/` | 20+ UI primitives (new) |
+| `frontend/packages/creator-app/src/lib/components/ui/Button.svelte` | Button primitive (153 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/IconButton.svelte` | Icon button (136 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Modal.svelte` | Modal primitive (213 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Badge.svelte` | Badge (83 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Card.svelte` | Card (77 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Toast.svelte` | Toast (110 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Tabs.svelte` | Tabs (95 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/FormField.svelte` | Form field (263 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Dropdown.svelte` | Dropdown (179 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/OverflowMenu.svelte` | Overflow menu (117 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Dropzone.svelte` | Dropzone (185 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Stepper.svelte` | Stepper (135 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Banner.svelte` | Banner (90 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Collapsible.svelte` | Collapsible (72 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Checkbox.svelte` | Checkbox (74 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/EmptyState.svelte` | Empty state (53 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Skeleton.svelte` | Skeleton loader (53 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/SkeletonCard.svelte` | Skeleton card (24 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/SkeletonRow.svelte` | Skeleton row (22 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/SkeletonTable.svelte` | Skeleton table (36 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/Tooltip.svelte` | Tooltip (102 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/icons.js` | Icon barrel (117 lines) |
+| `frontend/packages/creator-app/src/lib/components/ui/index.js` | UI barrel (31 lines) |
+| `frontend/packages/creator-app/src/lib/components/common/EntityListShell.svelte` | List shell (223 lines) |
+| `frontend/packages/creator-app/src/lib/components/common/FilterBar.svelte` | Filter bar (274 lines) |
+| `frontend/packages/creator-app/src/lib/components/common/FilterChip.svelte` | Filter chip (35 lines) |
+| `frontend/packages/creator-app/src/lib/components/common/ResizableTable.svelte` | Resizable table (45 lines) |
+| `frontend/packages/creator-app/src/lib/components/modals/NotificationModal.svelte` | Notification modal (180 lines) |
+| `frontend/packages/creator-app/src/lib/components/plugins/PluginParamFields.svelte` | Plugin params (267 lines) |
+| `frontend/packages/creator-app/src/lib/services/apiClient.js` | Centralized API client (94 lines) |
+| `frontend/packages/creator-app/src/lib/utils/statusBadge.js` | Status badge mapping (79 lines) |
+| `frontend/packages/creator-app/src/lib/utils/listHelpers.js` | List utility helpers (327 lines) |
+| `frontend/packages/creator-app/src/lib/utils/dateHelpers.js` | Date helpers (99 lines) |
+| `frontend/packages/creator-app/src/lib/utils/nameSanitizer.js` | Name sanitizer (103 lines) |
+| `frontend/packages/creator-app/src/lib/utils/orgAdmin.js` | Org admin helpers (58 lines) |
+| `frontend/packages/creator-app/src/lib/utils/sessionGuard.js` | Session guard (76 lines) |
+| `frontend/packages/creator-app/src/lib/stores/toast.js` | Toast store (128 lines) |
+| `frontend/packages/creator-app/src/lib/stores/templateStore.js` | Template store (407 lines) |
+| `frontend/packages/creator-app/src/lib/stores/rubricStore.svelte.js` | Rubric store (508 lines) |
+| `frontend/packages/creator-app/src/lib/stores/wizardDraftStore.svelte.js` | Wizard draft store (135 lines) |
+| `frontend/packages/creator-app/src/lib/stores/wizardFileStore.svelte.js` | Wizard file store (136 lines) |
+| `frontend/packages/creator-app/src/lib/services/pluginMatcher.js` | Plugin matching (58 lines) |
+| `frontend/packages/creator-app/src/lib/services/organizationService.js` | Org service (26 lines) |
+| `frontend/packages/creator-app/src/routes/api/chat/+server.js` | Chat API proxy (66 lines) |
+| `frontend/pnpm-workspace.yaml` | pnpm workspace config |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `backend/lamb/lti_router.py` | Refactor: JWT tokens, module dispatch, dead code removal (+699 lines) |
+| `backend/lamb/lti_activity_manager.py` | Refactor: module architecture, dynamic owner (+491 lines) |
+| `backend/lamb/lti_users_router.py` | LTI users adjustments |
+| `backend/lamb/auth_context.py` | Improved auth context (+108 lines) |
+| `backend/lamb/main.py` | Module router registration |
+| `backend/main.py` | Static serving + module setup (+150 lines) |
+| `backend/utils/pipelines/auth.py` | Auth pipeline (+18 lines) |
+| `backend/lamb/templates/` | **Removed** (lti_activity_setup.html, lti_dashboard.html) |
+| `frontend/svelte-app/` | **Removed** (migrated to monorepo) |
+| `frontend/packages/creator-app/src/routes/+layout.svelte` | Layout updated for monorepo |
+| `frontend/packages/creator-app/src/routes/+layout.js` | Layout JS updated |
+| `frontend/packages/creator-app/src/routes/+page.svelte` | Home page updated |
+| `frontend/packages/creator-app/src/routes/admin/+page.svelte` | Admin page updated |
+| `frontend/packages/creator-app/src/routes/assistants/+page.svelte` | Assistants page updated |
+| `frontend/packages/creator-app/src/routes/org-admin/+page.svelte` | Org admin updated |
+| `frontend/packages/creator-app/src/lib/services/assistantService.js` | Updated service (+244 lines) |
+| `frontend/packages/creator-app/src/lib/services/knowledgeBaseService.js` | Updated service (+175 lines) |
+| `frontend/packages/creator-app/src/lib/services/rubricService.js` | Updated service (+403 lines) |
+| `frontend/packages/creator-app/src/lib/services/templateService.js` | Updated service |
+| `frontend/packages/creator-app/src/lib/services/adminService.js` | Updated service (+56 lines) |
+| `frontend/packages/creator-app/src/lib/services/analyticsService.js` | Updated service |
+| `frontend/packages/creator-app/src/lib/services/aacService.js` | Updated service |
+| `frontend/packages/creator-app/src/lib/session/sessionManager.js` | Updated session manager |
+| `frontend/packages/creator-app/src/lib/stores/assistantStore.js` | Updated store |
+| `frontend/packages/creator-app/src/lib/stores/assistantPublish.js` | Updated publish store |
+| `frontend/packages/creator-app/src/lib/stores/aacStore.svelte.js` | Updated AAC store |
+| `frontend/packages/creator-app/src/app.css` | Full design system restored (123 lines) |
+| `frontend/packages/creator-app/src/lib/components/assistants/AssistantSharingModal.svelte` | Sharing modal (+168 lines) |
+| `frontend/packages/creator-app/src/lib/components/aac/AacTerminal.svelte` | Improved terminal (+358 lines) |
+| `frontend/packages/ui/src/lib/locales/en.json` | Expanded i18n keys (+414 lines) |
+| `frontend/packages/ui/src/lib/locales/es.json` | Spanish locale (1118 lines) |
+| `frontend/packages/ui/src/lib/locales/ca.json` | Catalan locale (1118 lines) |
+| `frontend/packages/ui/src/lib/locales/eu.json` | Basque locale (1118 lines) |
+
+---
+
+## 5. Bugs Fixed (Post-Merge and Pre-Merge)
+
+### 5.1 Post-merge integration fixes
+
+| Commit | Bug | Fix |
+|--------|-----|-----|
+| `ab442b79` | Design system tokens lost after merge (app.css was a 10-line stub) | Restored full design system (~123 lines) |
+| `ab442b79` | LAMB logo not displaying (404, missing `static/img/lamb_1.png`) | Created `frontend/packages/creator-app/static/img/lamb_1.png` |
+| `ab442b79` | Version shows "0.1" instead of "0.6" | `generate-version.js` now writes version.js in both packages (creator-app and @lamb/ui) |
+| `4689f244` | Admin service incorrect token handling + modal accessibility | Token handling fix + modal a11y |
+| `0b04eb1a` | Playwright tests `creator_flow` and `account_disable_security` persistently failing | Test fixes |
+| `756faf82` | Post-merge Playwright regressions + FR-10 interlock lost | Restored FR-10 + fixes |
+| `f3f18ed5` | `document_context` passed to all PPS (including `simple_augment`) | Only passed to `kvcache_augment` |
+| `322e8560` | Transcription button broken in assistants | Transcription button fix |
+| `c9d622ec` | LTI configure endpoint fails with existing activities | Configure endpoint fix |
+| `81546152` | Broken imports after merge + refactored AssistantForm lost | Restored imports and AssistantForm |
+| `d728c491` | DB migrations/PRAGMAs on every request; LTI modules missing `config.js` | `_optimizations_applied` / `_migrations_applied` flags; `config.js` samples for module-chat/file-eval |
+
+### 5.2 Pre-merge validation hardening (`eb0c8718`)
+
+Review-driven fixes before opening PR to `dev`. Plan: `docs/superpowers/plans/bug_fixes_pre_merge.md` (local, gitignored).
+
+| Bug | Symptom | Fix |
+|-----|---------|-----|
+| **Invalid PPS/RAG saved, fails at chat** | Assistant saved OK; completion returns 400 for incompatible `prompt_processor` + `rag_processor` / `document_rag` | `_validate_compatible_rag()` in `metadata_validators.py`; validation on **create** and **update** in `assistant_router.py`; UI: `clearDocumentRagIfUnsupported()`, `validateSubmission`, guard in `buildAssistantPayload` |
+| **Reference Document without library/item** | Create allowed empty `library_id`/`item_id` | `validateSubmission` requires both; backend validation on create (symmetry with update) |
+| **`library_file_rag` silent failure** | Library Manager errors → chat without document, no user feedback | `_error_result()` in `library_file_rag.py`; `_require_document_context()` → HTTP **502** in `main.py` |
+| **Import validator desaligned** | Import rejected valid `single_file_rag` + library refs | `importAssistantValidator.js` aligned with `metadata_validators.py`; PPS↔RAG checks on import |
+
+**Files touched:** `metadata_validators.py`, `assistant_router.py`, `main.py`, `library_file_rag.py`, `assistantFormSubmit.js`, `assistantFormState.svelte.js`, `importAssistantValidator.js` + unit tests.
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `frontend/packages/creator-app/static/img/lamb_1.png` | LAMB logo restored |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `frontend/packages/creator-app/src/app.css` | Design system restored: 10→123 lines |
+| `frontend/packages/creator-app/scripts/generate-version.js` | Writes version.js in both packages |
+| `frontend/packages/ui/src/lib/version.js` | Regenerated: 0.1→0.6 |
+| `frontend/packages/creator-app/src/lib/version.js` | Regenerated with version 0.6 |
+| `backend/lamb/completions/pps/kvcache_augment.py` | Fix: document_context only for kvcache_augment |
+| `backend/lamb/completions/pps/simple_augment.py` | Fix: removed erroneous document_context |
+| `backend/lamb/completions/main.py` | Fix: document_context routing; document load failure → HTTP 502 |
+| `backend/lamb/completions/rag/library_file_rag.py` | Explicit error results instead of silent empty context |
+| `backend/creator_interface/metadata_validators.py` | COMPATIBLE_RAG validation on save |
+| `backend/creator_interface/assistant_router.py` | Metadata validation on create |
+| `frontend/.../logic/assistantFormSubmit.js` | Submit validation for PPS/RAG/document refs |
+| `frontend/.../logic/assistantFormState.svelte.js` | `clearDocumentRagIfUnsupported()` |
+| `frontend/.../logic/importAssistantValidator.js` | Import rules aligned with backend |
+| `backend/lamb/lti_router.py` | Fix: configure endpoint with existing activities |
+| `frontend/.../assistants/AssistantForm.svelte` | Fix: imports restored post-merge |
+
+---
+
+## 6. Tests
+
+### 6.1 Backend Tests (new)
+
+| File | Description |
+|------|-------------|
+| `backend/tests/test_creator_knowledge_stores_integration.py` | Knowledge Stores CRUD via Creator Interface (427 lines) |
+| `backend/tests/test_creator_libraries_content.py` | Library content serving (254 lines) |
+| `backend/tests/test_creator_libraries_integration.py` | Library CRUD via Creator Interface (259 lines) |
+| `backend/tests/test_creator_library_folders.py` | Folder CRUD + tree operations (217 lines) |
+| `backend/tests/test_fr10_interlock.py` | FR-10: cannot delete items referenced by KS (145 lines) |
+| `backend/tests/test_knowledge_store_options.py` | KS setup options + locked fields (284 lines) |
+| `backend/tests/test_ks_query_helpers.py` | Shared KS query helper functions (62 lines) |
+| `backend/tests/test_query_rewriting_helper.py` | SFM query rewriting helper (47 lines) |
+| `backend/tests/test_query_rewriting_ks_rag.py` | Query rewriting KS RAG processor (161 lines) |
+| `backend/tests/test_rubric_eval_helper.py` | Rubric evaluation helper (67 lines) |
+| `backend/tests/test_simple_augment.py` | Simple augment PPS (legacy, no document_context) (52 lines) |
+| `backend/tests/test_library_route_ordering.py` | Route ordering fix (36 lines) |
+
+### 6.2 Unit Tests (testing/unit-tests/)
+
+| File | Description |
+|------|-------------|
+| `test_kvcache_augment.py` | kvcache_augment PPS: D3 fallback, labeled doc wrapper, COMPATIBLE_RAG (245 lines) |
+| `test_library_file_rag.py` | library_file_rag: Library Manager HTTP integration (171 lines) |
+| `test_document_rag_pipeline.py` | Dual-channel pipeline: document_rag + rag_processor (77 lines) |
+| `test_compatible_rag_validation.py` | COMPATIBLE_RAG validation in load_and_validate_plugins (94 lines) |
+| `test_single_file_rag.py` | Legacy single_file_rag (static file_path) (93 lines) |
+| `test_simple_augment.py` | simple_augment without document_context (13 lines) |
+| `test_metadata_validation.py` | metadata_validators: library_file_rag, COMPATIBLE_RAG on save |
+
+### 6.3 Frontend Tests (Vitest)
+
+| File | Description |
+|------|-------------|
+| `assistantFormSubmit.test.js` | Payload building with library_file_rag (227 lines) |
+| `assistantFormDocumentRag.test.js` | Document RAG toggle + LibraryItemSelector (156 lines) |
+| `assistantFormLibrary.test.js` | Library selection for single_file_rag (129 lines) |
+| `LibraryItemSelector.svelte.test.js` | Component rendering + interaction (94 lines) |
+| `ragProcessorHelpers.test.js` | PPS_COMPATIBLE_RAG, filtering, helpers (95 lines updated) |
+| `FilterBar.svelte.test.js` | Filter bar component (127 lines) |
+| `PluginParamFields.svelte.test.js` | Plugin parameter fields (197 lines) |
+| `pluginMatcher.test.js` | Plugin matching logic (89 lines) |
+| `treeOps.test.js` | File tree operations (249 lines) |
+| `sanitize.svelte.test.js` | DOMPurify sanitization (70 lines) |
+| `importAssistantValidator.spec.js` | Import validation (295 lines) |
+| `ConfigurationPanel.svelte.test.js` | PPS dropdown filtering + legacy edit lock (new) |
+| `RagOptionsPanel.svelte.test.js` | Legacy banner + disabled KB/KS selectors (new) |
+
+### 6.4 Playwright E2E Tests (new)
+
+| File | Description |
+|------|-------------|
+| `assistant_with_knowledge_store.spec.js` | E2E: create assistant with KS (522 lines) |
+| `context_aware_new.spec.js` | E2E: dual-channel RAG (KS + Document) (735 lines) |
+| `knowledge_store_api.spec.js` | API: KS CRUD + content ingestion (320 lines) |
+| `knowledge_store_e2e_workflow.spec.js` | E2E: full KS workflow (416 lines) |
+| `knowledge_store_ui.spec.js` | UI: KS list, detail, create (217 lines) |
+| `fr10_ui.spec.js` | UI: FR-10 interlock (no delete referenced items) (326 lines) |
+| `account_disable_security.spec.js` | Security: disabled user handling (474 lines) |
+| `library_tree_api.spec.js` | API: folder tree operations (229 lines) |
+| `library_wait_polling.spec.js` | Polling for import completion (137 lines) |
+| `org_migration_flow.spec.js` | Org migration flow (534 lines) |
+| `creator_flow.spec.js` | Updated creator flow (138 lines updated) |
+
+### 6.5 KB Server Tests (`lamb-kb-server/tests/`)
+
+- **Unit:** 13 files (chunking, config, database, dependencies, embedding plugins, plugin discovery/registry, schemas, services, vector DB chromadb/qdrant).
+- **Integration:** 9 files (auth, collections, content pipeline, edge cases, jobs, main lifespan, query, system, worker).
+- **E2E:** 7 files (auth boundary, concurrency, crash recovery, error paths, multitenancy, pipeline matrix, server smoke).
+
+### 6.6 Library Manager Tests (new)
+
+| File | Description |
+|------|-------------|
+| `test_capabilities.py` | Content capabilities API (241 lines) |
+| `test_folders.py` | Folder CRUD + tree (327 lines) |
+| `test_items_move.py` | Move items between folders (262 lines) |
+| `test_coverage_gaps.py` | Coverage gap filling (742 lines) |
+| `test_markitdown_errors.py` | Markitdown error handling (84 lines) |
+| `test_plugin_discovery.py` | Plugin discovery (189 lines) |
+| `test_plugin_params_passthrough.py` | Plugin params passthrough (186 lines) |
+
+### 6.7 CLI Tests (`lamb-cli/tests/`)
+
+| File | Description |
+|------|-------------|
+| `test_knowledge_store.py` | KS CLI commands (1065 lines) |
+| `test_library.py` | Library CLI commands (919 lines) |
+| `test_assistant.py` | Updated assistant CLI (145 lines updated) |
+| `test_chat.py` | Chat CLI (46 lines) |
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `backend/tests/conftest.py` | Shared test fixtures (225 lines) |
+| `backend/tests/test_creator_knowledge_stores_integration.py` | KS integration tests (427 lines) |
+| `backend/tests/test_creator_libraries_content.py` | Library content tests (254 lines) |
+| `backend/tests/test_creator_libraries_integration.py` | Library integration tests (259 lines) |
+| `backend/tests/test_creator_library_folders.py` | Folder tests (217 lines) |
+| `backend/tests/test_fr10_interlock.py` | FR-10 interlock tests (145 lines) |
+| `backend/tests/test_knowledge_store_options.py` | KS options tests (284 lines) |
+| `backend/tests/test_ks_query_helpers.py` | KS query helpers tests (62 lines) |
+| `backend/tests/test_query_rewriting_helper.py` | Query rewriting tests (47 lines) |
+| `backend/tests/test_query_rewriting_ks_rag.py` | QR KS RAG tests (161 lines) |
+| `backend/tests/test_rubric_eval_helper.py` | Rubric eval tests (67 lines) |
+| `backend/tests/test_simple_augment.py` | Simple augment tests (52 lines) |
+| `backend/tests/test_library_route_ordering.py` | Route ordering tests (36 lines) |
+| `backend/pytest.ini` | Pytest config (7 lines) |
+| `testing/unit-tests/completions/test_kvcache_augment.py` | kvcache_augment tests (245 lines) |
+| `testing/unit-tests/completions/test_library_file_rag.py` | library_file_rag tests (171 lines) |
+| `testing/unit-tests/completions/test_document_rag_pipeline.py` | Dual-channel pipeline tests (77 lines) |
+| `testing/unit-tests/completions/test_compatible_rag_validation.py` | COMPATIBLE_RAG tests (94 lines) |
+| `testing/unit-tests/completions/test_single_file_rag.py` | single_file_rag tests (93 lines) |
+| `testing/unit-tests/conftest.py` | Unit test fixtures (17 lines) |
+| `testing/unit-tests/assistant_router/test_metadata_validation.py` | Metadata validation tests (154 lines) |
+| `frontend/.../assistants/assistantFormSubmit.test.js` | Submit logic tests (227 lines) |
+| `frontend/.../assistants/assistantFormDocumentRag.test.js` | Document RAG tests (156 lines) |
+| `frontend/.../assistants/assistantFormLibrary.test.js` | Library tests (129 lines) |
+| `frontend/.../assistants/LibraryItemSelector.svelte.test.js` | Component tests (94 lines) |
+| `frontend/.../assistants/importAssistantValidator.spec.js` | Import validator tests (295 lines) |
+| `frontend/.../common/FilterBar.svelte.test.js` | FilterBar tests (127 lines) |
+| `frontend/.../plugins/PluginParamFields.svelte.test.js` | Plugin params tests (197 lines) |
+| `frontend/.../services/pluginMatcher.test.js` | Plugin matcher tests (89 lines) |
+| `frontend/.../libraries/fileTree/treeOps.test.js` | Tree ops tests (249 lines) |
+| `frontend/.../utils/sanitize.svelte.test.js` | Sanitize tests (70 lines) |
+| `testing/playwright/tests/assistant_with_knowledge_store.spec.js` | E2E KS assistant (522 lines) |
+| `testing/playwright/tests/context_aware_new.spec.js` | E2E dual-channel RAG (735 lines) |
+| `testing/playwright/tests/knowledge_store_api.spec.js` | E2E KS API (320 lines) |
+| `testing/playwright/tests/knowledge_store_e2e_workflow.spec.js` | E2E KS workflow (416 lines) |
+| `testing/playwright/tests/knowledge_store_ui.spec.js` | E2E KS UI (217 lines) |
+| `testing/playwright/tests/fr10_ui.spec.js` | E2E FR-10 interlock (326 lines) |
+| `testing/playwright/tests/account_disable_security.spec.js` | E2E security (474 lines) |
+| `testing/playwright/tests/library_tree_api.spec.js` | E2E folder tree (229 lines) |
+| `testing/playwright/tests/library_wait_polling.spec.js` | E2E polling (137 lines) |
+| `testing/playwright/tests/org_migration_flow.spec.js` | E2E org migration (534 lines) |
+| `testing/playwright/fixtures/sample.md` | Test fixture |
+| `lamb-kb-server/tests/` | 29 test files (unit + integration + e2e) |
+| `library-manager/tests/test_capabilities.py` | Capabilities tests (241 lines) |
+| `library-manager/tests/test_folders.py` | Folder tests (327 lines) |
+| `library-manager/tests/test_items_move.py` | Move items tests (262 lines) |
+| `library-manager/tests/test_coverage_gaps.py` | Coverage gaps tests (742 lines) |
+| `library-manager/tests/test_markitdown_errors.py` | Markitdown error tests (84 lines) |
+| `library-manager/tests/test_plugin_discovery.py` | Plugin discovery tests (189 lines) |
+| `library-manager/tests/test_plugin_params_passthrough.py` | Plugin params tests (186 lines) |
+| `lamb-cli/tests/test_commands/test_knowledge_store.py` | KS CLI tests (1065 lines) |
+| `lamb-cli/tests/test_commands/test_library.py` | Library CLI tests (919 lines) |
+| `lamb-cli/tests/test_commands/test_chat.py` | Chat CLI tests (46 lines) |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `testing/unit-tests/completions/test_simple_augment.py` | Adapted: no document_context |
+| `testing/unit-tests/assistant_router/test_metadata_validation.py` | Adapted: library_file_rag |
+| `frontend/.../assistants/AssistantForm.svelte.test.js` | Adapted to refactor |
+| `frontend/.../assistants/assistantFormFetchers.test.js` | Updated (+39 lines) |
+| `frontend/.../assistants/assistantFormState.svelte.test.js` | Updated (+15 lines) |
+| `frontend/.../utils/ragProcessorHelpers.test.js` | Updated: PPS_COMPATIBLE_RAG (+95 lines) |
+| `testing/playwright/tests/creator_flow.spec.js` | Updated post-merge (+138 lines) |
+| `testing/playwright/tests/access_control_and_user_dashboard.spec.js` | Adjustments |
+| `testing/playwright/tests/admin_and_sharing_flow.spec.js` | Adjustments |
+| `testing/playwright/tests/admin_role_lifecycle.spec.js` | Adjustments |
+| `testing/playwright/tests/kb_delete_modal.spec.js` | Adjustments |
+| `testing/playwright/tests/kb_detail_modals.spec.js` | Adjustments |
+| `testing/playwright/tests/org_no_admin_and_role_promotion.spec.js` | Adjustments |
+| `testing/playwright/tests/url_ingest.spec.js` | Adjustments |
+| `testing/playwright/tests/youtube_titles.spec.js` | Updated (+83 lines) |
+| `testing/playwright/playwright.config.js` | Updated config |
+| `lamb-cli/tests/test_commands/test_assistant.py` | Updated: KS support (+145 lines) |
+| `library-manager/tests/test_content_serving.py` | Updated |
+| `library-manager/tests/test_edge_cases.py` | Updated |
+| `library-manager/tests/test_import_plugins.py` | Updated |
+
+---
+
+## 7. Infrastructure and Configuration
+
+- **Docker Compose:** new services `kb-server-v2` (9092), `library-manager` (9091), workers config.
+- **Caddyfile:** reverse proxy config.
+- **Requirements:** split into `requirements-base.txt` + `requirements-ml.txt` (avoids pip resolution-too-deep).
+- **Environment data:** centralized `.env.example` files in `environment_data/`.
+- **Backend Dockerfile:** updated for monorepo frontend build.
+- **Scripts:** `clean_kb_robust.py`, `clean_knowledge_base_service.py`.
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `Caddyfile` | Reverse proxy config (17 lines) |
+| `backend/requirements-base.txt` | Base Python deps (47 lines) |
+| `backend/requirements-ml.txt` | ML Python deps (34 lines) |
+| `environment_data/.env.example` | Root env example |
+| `environment_data/.env.next.example` | Next env example (104 lines) |
+| `environment_data/backend/.env.example` | Backend env example (242 lines) |
+| `environment_data/lamb-kb-server-stable/backend/.env.example` | KB stable env example (71 lines) |
+| `environment_data/lamb-kb-server/backend/.env.example` | KB v2 env example (51 lines) |
+| `environment_data/library-manager/backend/.env.example` | Library Manager env example (42 lines) |
+| `environment_data/open-webui/.env.example` | Open WebUI env example (13 lines) |
+| `environment_data/testing/cli/.env.sample` | CLI test env sample (21 lines) |
+| `environment_data/testing/playwright/.env.sample` | Playwright env sample (17 lines) |
+| `lamb-kb-server/Dockerfile` | KB Server Dockerfile (41 lines) |
+| `lamb-kb-server/pyproject.toml` | KB Server project config (123 lines) |
+| `lamb-kb-server/docker-compose.test.yml` | KB Server test compose (36 lines) |
+| `docker-compose-workers.yaml` | Workers compose config (23 lines) |
+| `scripts/clean_kb_robust.py` | KB cleanup script (76 lines) |
+| `scripts/clean_knowledge_base_service.py` | KB service cleanup script (36 lines) |
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `docker-compose-example.yaml` | New services kb-server-v2, library-manager (+95 lines) |
+| `backend/Dockerfile` | Updated for monorepo frontend build |
+| `backend/requirements.txt` | Split into base + ml |
+| `.dockerignore` | Updated |
+| `.gitignore` | Updated: new exclusions (+114 lines) |
+| `scripts/setup.sh` | Updated |
+
+---
+
+## 8. Documentation
+
+| File | Content |
+|------|---------|
+| `docs/POST_MERGE_CHANGES.md` | Post-merge consolidation changelog |
+| `docs/superpowers/PRD-refactor-pipeline-two-plugins.md` | PRD: two-pipeline architecture (Catalan) |
+| `docs/superpowers/2026-06-08-kvcache-augment-pps-integration-changelog.md` | kvcache_augment integration changelog |
+| `docs/superpowers/mini-prd-2-knowledge-stores-integration.md` | Mini-PRD: kvcache_augment improvements post-KS merge |
+| `docs/superpowers/mini-prd-rama-A-context-to-system-prompt.md` | Mini-PRD: context-to-system-prompt merge plan |
+| `docs/superpowers/plans/2026-06-08-context-to-system-prompt-refactor.md` | Plan: Document RAG separation from legacy pipeline |
+| `docs/superpowers/plans/2026-06-01-query-rewriting-ks-rag.md` | Plan: query rewriting KS RAG |
+| `docs/superpowers/plans/2026-06-01-query-rewriting-ks-rag-implementation-summary.md` | Summary: completed implementation |
+| `docs/superpowers/plans/2026-05-29-document-rag-system-prompt-changelog.md` | Changelog: Document RAG system prompt injection |
+| `docs/superpowers/plans/2026-05-28-single-file-rag-library-manager-changelog.md` | Changelog: single_file_rag → Library Manager |
+| `docs/superpowers/mermaids.md` | Mermaid diagrams for multitool flow |
+| `docs/superpowers/TFG_PLANNING_GUIDE.md` | Thesis planning guide (Catalan) |
+| `docs/follow-ups/server-side-pagination-libraries-ks.md` | Deferred: server-side pagination |
+| `frontend/MIGRATION_MONOREPO.md` | Monorepo migration guide |
+| `frontend/packages/creator-app/src/lib/components/assistants/refactor.md` | AssistantForm refactor documentation |
+
+### New files
+
+| File | Description |
+|------|-------------|
+| `docs/POST_MERGE_CHANGES.md` | Post-merge consolidation changelog (177 lines) |
+| `docs/superpowers/PRD-refactor-pipeline-two-plugins.md` | PRD two-pipeline architecture (767 lines) |
+| `docs/superpowers/2026-06-08-kvcache-augment-pps-integration-changelog.md` | kvcache_augment changelog |
+| `docs/superpowers/mini-prd-2-knowledge-stores-integration.md` | Mini-PRD KS improvements (454 lines) |
+| `docs/superpowers/mini-prd-rama-A-context-to-system-prompt.md` | Mini-PRD context-to-system-prompt (322 lines) |
+| `docs/superpowers/plans/2026-06-08-context-to-system-prompt-refactor.md` | Plan: Document RAG separation (1363 lines) |
+| `docs/superpowers/plans/2026-06-01-query-rewriting-ks-rag.md` | Plan: query rewriting KS RAG (1915 lines) |
+| `docs/superpowers/plans/2026-06-01-query-rewriting-ks-rag-implementation-summary.md` | Implementation summary (390 lines) |
+| `docs/superpowers/plans/2026-05-29-document-rag-system-prompt-changelog.md` | Document RAG changelog (306 lines) |
+| `docs/superpowers/plans/2026-05-28-single-file-rag-library-manager-changelog.md` | single_file_rag→LM changelog (91 lines) |
+| `docs/superpowers/mermaids.md` | Mermaid diagrams (515 lines) |
+| `docs/superpowers/TFG_PLANNING_GUIDE.md` | Thesis planning guide (1786 lines) |
+| `docs/follow-ups/server-side-pagination-libraries-ks.md` | Deferred server-side pagination (295 lines) |
+| `frontend/MIGRATION_MONOREPO.md` | Monorepo migration guide (296 lines) |
+| `frontend/packages/creator-app/src/lib/components/assistants/refactor.md` | AssistantForm refactor docs (419 lines) |
+| `lamb-kb-server/Documentation/issue_334_known_bugs.md` | KB Server known bugs (73 lines) |
+| `lamb-kb-server/Documentation/issue_337_lamb_integration_adrs.md` | KB Server ADRs (215 lines) |
+| `lamb-kb-server/README.md` | KB Server README (184 lines) |
+
+---
+
+## 9. Performance Optimizations
+
+### 9.1 DB Init Once Per Process
+
+**Problem:** Every `LambDatabaseManager()` instantiation (73 per-request call sites) was running `_configure_database_optimizations()` (8 PRAGMAs) and `run_migrations()` (including migration 13's expensive backfill: full table scan + JSON extraction + aggregation over `usage_logs`).
+
+**Fix:** Added class-level flags `_optimizations_applied` and `_migrations_applied` (same pattern as existing `_system_org_initialized`). Migrations and PRAGMAs now execute once per process at startup, not on every request.
+
+**Impact:** Eliminates redundant migration 13 backfill (expensive SQL aggregation) from every request cycle. `log_token_usage()` and `check_assistant_quota()` are unaffected — they depend on tables that exist after the first instantiation (module-level, at import time).
+
+**Verification:** `WAL mode enabled` and `Running database migrations` appear once at startup in logs, not on every request.
+
+### 9.2 LTI Module config.js
+
+**Problem:** `module-chat` and `module-file-eval` had no `static/config.js` → build output missing the file → backend served an empty fallback `{}` → warnings in logs.
+
+**Fix:**
+- Added `static/config.js` and `static/config.js.sample` to both LTI modules with `window.LAMB_CONFIG = { API_BASE_URL: '' }`.
+- Updated `docker-compose-example.yaml` and `docker-compose-workers.yaml` to copy `config.js.sample` → `config.js` before build if missing.
+- Backend routes at `/m/chat/config.js` and `/m/file-eval/config.js` now serve the real file via `FileResponse` instead of the inline fallback.
+
+**Verification:** Logs show `Serving module-chat/file-eval config.js from: .../frontend/build/m/.../config.js` (no missing-config warnings for LTI modules).
+
+### Modified files
+
+| File | Change |
+|------|--------|
+| `backend/lamb/database_manager.py` | Added `_optimizations_applied` and `_migrations_applied` class flags |
+| `frontend/packages/module-chat/static/config.js` | New: LAMB_CONFIG with empty API_BASE_URL |
+| `frontend/packages/module-chat/static/config.js.sample` | New: sample config template |
+| `frontend/packages/module-file-eval/static/config.js` | New: LAMB_CONFIG with empty API_BASE_URL |
+| `frontend/packages/module-file-eval/static/config.js.sample` | New: sample config template |
+| `docker-compose-example.yaml` | Copy config.js.sample → config.js pre-build |
+| `docker-compose-workers.yaml` | Copy config.js.sample → config.js pre-build |
+
+---
+
+## 10. Known Limitations / Follow-ups
+
+### Deferred intentionally (not in scope for this PR)
+
+| Item | Notes |
+|------|-------|
+| **Edit mode Reference Document UX** | Checkbox disabled in edit, but `LibraryItemSelector` still allows changing library/item. Accepted for now. |
+| **Design system debt** | `admin/+page.svelte` and some assistant subcomponents still use raw Tailwind colors (`bg-green-100`, `bg-amber-50`, etc.) instead of semantic tokens. |
+| **MCP / direct DB assistant create** | `mcp_router.py` → `add_assistant` bypasses Creator Interface metadata validation. Low risk if only UI is used. |
+
+### Moodle grade passback (file-eval)
+
+**Symptom (Docker dev):** grade sync fails with `Connection refused` to `http://localhost:8000/outcomes`.
+
+**Cause:** Moodle sends `lis_outcome_service_url` from its `wwwroot` (`localhost:8000`). LAMB stores and POSTs to that URL as-is. This works when backend and Moodle share `localhost` (local dev without Docker). In Docker, `localhost` is the container, not the host running Moodle. This is a **network/reachability issue in dev**, not a bug in LTI passback logic (OAuth, XML, sourcedid).
+
+**Production:** should work when Moodle has a public/reachable URL (e.g. `https://moodle.example.edu/...`) and LAMB can reach it.
+
+**Options (Docker dev only; do not block this merge):**
+
+| Option | Approach | Pros | Cons |
+|--------|----------|------|------|
+| **A — Moodle config** | Set `$CFG->wwwroot` to `http://host.docker.internal:8000` | No LAMB code changes | Each dev configures Moodle; may need re-launch to refresh stored URL |
+| **B — LAMB env rewrite** | `LAMB_LTI_OUTCOME_HOST_REWRITE=host.docker.internal:8000` rewrites `localhost` only when signing/posting | Browser can keep `localhost:8000` for Moodle | Small code change + docs; opt-in |
+| **C — Defer** | Accept passback untested in Docker dev | Zero extra scope | Test passback in local or staging before release |
+
+**Recommendation for this PR:** Option C (defer). Document A or B as follow-up if the team often tests file-eval grades in Docker.
+
+### Other notes from manual testing (out of merge scope)
+
+- **`qwen3.6-flash` 403 (free tier exhausted):** fallback to `qwen3.6-plus` works; consider changing org default model to avoid double latency.
+- **`[TRACE]` logs:** not present in current branch (removed or never committed).
+
+### Phase 4 monorepo follow-ups (from `MERGE_DEV_TO_PHASE4.md`)
+
+To be reviewed in a **separate pass** after this PR is stable on `dev`:
+
+- Unify dual `apiClient.js` in creator-app (sessionGuard + 401 handling).
+- Move generic API client to `@lamb/ui` so LTI modules can share it.
+- Replace raw `fetch()` in module-chat / module-file-eval.
+- Migrate direct `marked` usage to `renderMarkdownSafe` from `@lamb/ui`.
+
+---
+
+## 11. Known Issues / Pending
+
+### Pipeline / assistants
+
+- **Org `assistant_defaults` migration (optional):** Orgs with `prompt_processor: simple_augment` stored in SQLite may still serve that value via `/creator/assistant/defaults`, but create flow now ignores it via `resolveCreatePromptProcessor()`. Follow-up: migrate org defaults or stop persisting `prompt_processor` in org Admin if desired.
+- **Legacy `single_file_rag` full migration (deferred):** Edit mode now locks all `simple_augment` assistants (generalized legacy banner + disabled KB/KS/Top-K/document controls). `single_file_rag` still shows read-only `file_path` plus the specific single-file notice. Future work: editable via `LibraryItemSelector` with RAG type locked + backend dual-path in `single_file_rag.py`.
+- **Legacy `no_rag` notice gap:** Assistants with `simple_augment` + `no_rag` do not mount `RagOptionsPanel`, so they only see the disabled PPS field — no amber banner. Acceptable for now; add a ConfigurationPanel-level notice if needed.
+- **RAG display names:** `context_aware_rag` and `query_rewriting_ks_rag` both show as "Context Aware Rag" in the UI; disambiguation suffix "(Old)" not yet implemented in `getRagProcessorDisplayName()`.
+
+### Phase 4 / monorepo (needs dedicated review)
+
+- Items listed in §10 (`MERGE_DEV_TO_PHASE4.md` TODOs) — auth client consolidation, LTI module fetch patterns, XSS sanitization gaps.
+- **Moodle passback in Docker dev** — see §10; defer unless team tests file-eval grades in containers regularly.
+
+### Resolved in this branch (no longer pending)
+
+- Invalid PPS/RAG metadata saved then failing at chat → fixed `eb0c8718`
+- Reference Document create without library/item → fixed `eb0c8718`
+- `library_file_rag` silent failure → fixed `eb0c8718`
+- Import validator vs backend mismatch → fixed `eb0c8718`
+- FR-10 interlock lost after merge → fixed `756faf82`
+- `document_context` leaking to `simple_augment` → fixed `f3f18ed5`
+- Create form stuck on `simple_augment` from org defaults / localStorage → fixed via `resolveCreatePromptProcessor()`; removed `LAMB_LEGACY_PPS_DEFAULT` hack
+- Playwright create-assistant specs: no changes required (audit: 7 specs; none assert implicit `simple_augment` on create)
diff --git a/CLAUDE.md b/CLAUDE.md
index 986694260..a3646ef99 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -18,11 +18,14 @@ docker compose up frontend # just the frontend dev server
### Running services individually (without Docker)
```bash
+# Backend — install Python deps once (two files; avoids pip resolution-too-deep)
+cd backend && pip install -r requirements-base.txt && pip install -r requirements-ml.txt
+
# Backend (FastAPI on port 9099)
cd backend && PORT=9099 uvicorn main:app --port 9099 --host 0.0.0.0 --forwarded-allow-ips '*' --reload
# Frontend dev server (Svelte on port 5173)
-cd frontend/svelte-app && npm run dev -- --host 0.0.0.0
+cd frontend && pnpm --filter creator-app dev -- --host 0.0.0.0
# KB Server (port 9090)
cd lamb-kb-server-stable/backend && uvicorn main:app --host 0.0.0.0 --port 9090 --reload
@@ -32,13 +35,13 @@ cd library-manager && source .venv/bin/activate
cd backend && LAMB_API_TOKEN=your-token uvicorn main:app --host 0.0.0.0 --port 9091 --reload
```
-### Frontend (from `frontend/svelte-app/`)
+### Frontend (from `frontend/`)
```bash
-npm run build # production build
-npm run check # svelte-kit sync + svelte-check (type checking)
-npm run lint # prettier --check + eslint
-npm run format # prettier --write
-npm run test:unit # vitest
+pnpm --filter creator-app build # production build
+pnpm --filter creator-app check # svelte-kit sync + svelte-check (type checking)
+pnpm --filter creator-app lint # prettier --check + eslint
+pnpm --filter creator-app format # prettier --write
+pnpm --filter creator-app test:unit # vitest
```
### Playwright E2E tests (from `testing/playwright/`)
@@ -114,7 +117,15 @@ Organizations are the tenant boundary. Each org isolates users, assistants, KBs,
A separate microservice (FastAPI, port 9091) that serves as a **document repository**. It imports documents into a structured, permalinkable markdown format. It does NOT chunk, embed, or interact with vector databases — that is the KB Server's responsibility.
-**Terminology:** Libraries **IMPORT** content (document repository). Knowledge Bases **INGEST** content (chunking + embedding). These terms are used consistently throughout the codebase and must not be confused.
+**Terminology — three distinct concepts in this repo:**
+
+| Concept | Verb | Service | LAMB route | Storage |
+|---------|------|---------|-----------|---------|
+| **Libraries** | IMPORT | Library Manager (port 9091) | `/creator/libraries/*` | structured markdown on disk |
+| **Knowledge Bases** | INGEST (legacy) | `lamb-kb-server-stable/` (port 9090) | `/creator/knowledgebases/*` | ChromaDB only, file-upload model |
+| **Knowledge Stores** | INGEST (new) | `lamb-kb-server/` (port 9092) | `/creator/knowledge-stores/*` | pluggable vector DB, library-only ingestion |
+
+These terms are used consistently throughout the codebase and must not be confused. "Knowledge Bases" and "Knowledge Stores" are NOT synonyms — they refer to two parallel, coexisting integrations with different KB Server backends. New work should target Knowledge Stores; the Knowledge Base surface is preserved unchanged for backward compatibility.
**Key decisions:**
- LAMB is the only caller. Single bearer token auth, no user-level ACL (LAMB handles that).
@@ -125,7 +136,23 @@ A separate microservice (FastAPI, port 9091) that serves as a **document reposit
- Internal service: no CORS, no published ports, non-root Docker container, single-instance file lock.
- 52 tests, 80% coverage. Full README at `library-manager/README.md`.
-**LAMB integration:** Creator Interface endpoints (`/creator/libraries/...`) validate ACL and proxy to the Library Manager. The `lamb-cli` commands (`lamb library ...`) are in `lamb-cli/src/lamb_cli/commands/library.py`. Svelte frontend at `/libraries` route with components in `frontend/svelte-app/src/lib/components/libraries/`.
+**LAMB integration:** Creator Interface endpoints (`/creator/libraries/...`) validate ACL and proxy to the Library Manager. The `lamb-cli` commands (`lamb library ...`) are in `lamb-cli/src/lamb_cli/commands/library.py`. Svelte frontend at `/libraries` route with components in `frontend/packages/creator-app/src/lib/components/libraries/`.
+
+### Knowledge Stores — new KB Server (`lamb-kb-server/`)
+
+A separate microservice (FastAPI, port 9092) — pluggable redesign of the legacy KB Server. Pure compute service: chunks, embeds, and stores vectors. Receives JSON payloads from LAMB containing pre-extracted text + permalinks; **never calls the Library Manager directly**. Coexists with the legacy stable KB Server (port 9090) — both run simultaneously per #334 NFR-1.
+
+**Key decisions** (see `lamb-kb-server/Documentation/` for full ADRs):
+- LAMB owns ACL, multi-tenancy, and content delivery. KB Server is a single-bearer-token compute service.
+- Plugin architecture: 3 vector-DB backends (ChromaDB, Qdrant), 4 chunking strategies (simple, hierarchical/parent-child, by_page, by_section), 3 embedding vendors (openai, ollama, local).
+- **Library-only ingestion:** Knowledge Stores are populated exclusively by linking Library items. No direct file upload path.
+- **Locked store setup:** chunking strategy, embedding vendor/model, and vector DB backend are immutable after creation. Only `name` and `description` are mutable.
+- **Per-request embedding credentials:** sent on every `/add-content` and `/query`, held in memory only by the KB Server. LAMB resolves them from `setups.default.providers.{vendor}.api_key` (the same org-level key used by chat completions and RAG).
+- **Async ingestion:** SQLite-backed job queue with polling (no webhooks). LAMB queues `add-content`, gets a `job_id`, polls `/jobs/{job_id}` until ready/failed.
+- **Per-org filesystem isolation:** vectors live at `data/storage/{org_id}/{collection_id}/`.
+- **FR-10:** a Library item that is referenced by any active Knowledge Store cannot be deleted from its Library — LAMB enforces this in `DELETE /creator/libraries/{lib}/items/{item}` against the `kb_content_links` table.
+
+**LAMB integration:** Creator Interface endpoints (`/creator/knowledge-stores/...`) validate ACL and proxy to the new KB Server. Tables: `knowledge_stores` + `kb_content_links` in LAMB DB (separate from `kb_registry` which serves the legacy stable KBs). HTTP client at `backend/creator_interface/knowledge_store_client.py`. RAG processor at `backend/lamb/completions/rag/knowledge_store_rag.py` (sibling of `simple_rag.py` — assistants opt in via `rag_processor='knowledge_store_rag'`). The `lamb-cli` commands are `lamb ks ...` (alias `lamb knowledge-store ...`) in `lamb-cli/src/lamb_cli/commands/knowledge_store.py`. Svelte frontend integrated into the unified `/libraries` page (sub-tab "Knowledge Stores" + primary "Create Knowledge" wizard); components in `frontend/svelte-app/src/lib/components/knowledgeStores/` and the wizard in `frontend/svelte-app/src/lib/components/knowledge/`. Direct entry point at `/knowledge-stores` redirects to `/libraries?section=knowledge-stores`.
### Database
- **LAMB DB** — SQLite with WAL mode (`lamb_v4.db` at `LAMB_DB_PATH`). Schema managed in `backend/lamb/database_manager.py`.
@@ -134,7 +161,7 @@ A separate microservice (FastAPI, port 9091) that serves as a **document reposit
- **Library Manager DB** — SQLite with WAL mode (`library-manager/data/library-manager.db`). Schema in `library-manager/backend/database/models.py`. Managed by SQLAlchemy `create_all` (no Alembic migrations yet).
### Frontend
-Svelte 5 + SvelteKit + Vite + TailwindCSS 4. JavaScript with JSDoc (not TypeScript). I18n via `svelte-i18n` with locales in `frontend/svelte-app/src/lib/locales/` (en, es, ca, eu). API services in `frontend/svelte-app/src/lib/services/`. Reactive state in `frontend/svelte-app/src/lib/stores/`.
+Svelte 5 + SvelteKit + Vite + TailwindCSS 4. JavaScript with JSDoc (not TypeScript). Monorepo with pnpm workspaces: `@lamb/ui` (shared components, stores, i18n), `creator-app` (main SPA), `module-chat` (LTI chat module), `module-file-eval` (LTI file evaluation module). I18n via `svelte-i18n` with locales in `frontend/packages/ui/src/lib/locales/` (en, es, ca, eu). API services in `frontend/packages/creator-app/src/lib/services/`. Reactive state in `frontend/packages/creator-app/src/lib/stores/`.
## Code Style
@@ -144,10 +171,11 @@ Svelte 5 + SvelteKit + Vite + TailwindCSS 4. JavaScript with JSDoc (not TypeScri
## Key Configuration
- Backend env: `backend/.env` (copy from `backend/.env.example`)
-- KB Server env: `lamb-kb-server-stable/backend/.env` (copy from `.env.example`)
+- KB Server (legacy) env: `lamb-kb-server-stable/backend/.env` (copy from `.env.example`)
+- KB Server (new, Knowledge Stores) env: `lamb-kb-server/backend/.env` — requires `LAMB_API_TOKEN`. Backend reads it via `LAMB_KB_SERVER_V2` / `LAMB_KB_SERVER_V2_TOKEN`.
- Library Manager env: `library-manager/backend/.env` (copy from `.env.example`) — requires `LAMB_API_TOKEN`
- Playwright env: `testing/playwright/.env` (copy from `.env.sample`)
-- Frontend runtime config: `frontend/svelte-app/static/config.js` (copy from `config.js.sample`)
+- Frontend runtime config: injected by `backend/docker-entrypoint.py` at container start
- Docker orchestration: `docker-compose.yaml` (requires `LAMB_PROJECT_PATH` env var)
- Reverse proxy: `Caddyfile`
@@ -155,15 +183,67 @@ Svelte 5 + SvelteKit + Vite + TailwindCSS 4. JavaScript with JSDoc (not TypeScri
`open-webui/` and `lamb-kb-server-stable/` are separate projects maintained in their own repositories. They are included here only as stable snapshots so Docker Compose can launch them alongside LAMB. **Do not edit code in these directories** — changes belong in their upstream repos. The `frontend/build/` directory is build output. All three are excluded from search via `.cursorignore`.
-Note: `library-manager/` is NOT vendored — it is developed in-tree as part of this repository. Edit freely.
+Note: `library-manager/` and `lamb-kb-server/` (the new KB Server backing Knowledge Stores) are NOT vendored — both are developed in-tree as part of this repository. Edit freely.
## Version Bumping
-Dev version lives in `frontend/svelte-app/scripts/generate-version.js`. Run `node frontend/svelte-app/scripts/generate-version.js` to regenerate `src/lib/version.js`. Only commit the generator script change, not the generated file.
+Dev version lives in `frontend/packages/creator-app/scripts/generate-version.js`. Run `node frontend/packages/creator-app/scripts/generate-version.js` to regenerate `src/lib/version.js`. Only commit the generator script change, not the generated file.
## Git commits
* DO NOT include authoriship information in commits (No Co-authored-By, signed-off-by , or similar)
* Commit messages should be concise and descriptive without aditional metadata.
* Include the Issue ID in commit messages like #{issue number}
-
\ No newline at end of file
+
+## Design System
+
+The Svelte frontend follows a strict design-system contract. Primitives live in `frontend/svelte-app/src/lib/components/ui/` and tokens in `frontend/svelte-app/src/app.css`. See `.claude/plans/elegant-mixing-adleman.md` for the full plan, primitive APIs, token list, and verification checklist.
+
+### Consistency Contract (the rules everything follows)
+
+**Action -> primitive mapping (locked).** Same intent always renders with the same primitive, same icon, same variant, same position:
+
+| Action | Primitive | Icon | Variant | Position |
+|---|---|---|---|---|
+| List "Create new" CTA | `Button` | `Plus` (iconLeft) | `primary` | top-right of page header |
+| Row "View" | `IconButton` | `Eye` | `ghost`, sm | leftmost in row actions |
+| Row "Edit" | `IconButton` | `Pencil` | `ghost`, sm | inline next to value, or overflow |
+| Row "More" | `OverflowMenu` | `MoreHorizontal` | `ghost` | rightmost in row actions |
+| Modal close | `IconButton` | `X` | `ghost`, sm | top-right of modal header |
+| Per-item remove | `IconButton` | `X` | `danger-ghost`, sm | right of item row |
+| Confirm destructive | `ConfirmationModal` | `AlertCircle` header / `Trash2` confirm | `variant=danger` | - |
+| Retry on error | `Button` | `RefreshCw` | `secondary` | end of error Banner |
+| Loading | `Skeleton.*` | - | - | always replaces "Loading..." text |
+| Async write feedback | `toast` | varies | `success` / `danger` | top-right stack |
+
+Overflow menus always render Delete last with a divider above it and `text-danger` styling.
+
+**Status pill mapping (locked).** Resolve via `statusBadgeProps(status)` in `src/lib/utils/statusBadge.js`; never hand-code badge colors. Mapping:
+
+| Status | Badge variant | Icon | Label |
+|---|---|---|---|
+| `ready` / completed | `success` | `CheckCircle2` | Ready |
+| `processing` / `pending` / `queued` | `info` | `Loader2` (spin) | Processing |
+| `failed` / `error` | `danger` | `AlertCircle` | Failed |
+| `empty` (count === 0) | `warning` | `AlertCircle` | Empty |
+| `private` | `neutral` | `Lock` | Private |
+| `shared` | `success` | `Users` | Shared |
+| immutable field | `neutral` | `Lock` | Locked |
+
+**Color rule.** Brand color is accessed ONLY via `bg-brand` / `text-brand` / `border-brand` / `ring-brand`. Any literal `#2271b3` or `[#2271b3]` outside `app.css` is a violation. Status colors come exclusively from the `success-* / info-* / warning-* / danger-*` tokens — never `bg-green-100`, `text-red-700`, etc.
+
+**Copy rule.** All `variant="danger"` confirmation modals MUST include the literal `common.cannotBeUndone` line in the body. `ConfirmationModal` enforces this automatically.
+
+**Modal header rule.** Canonical header: `bg-surface` (white), bottom `border-border`, title `type-section-title`, close `IconButton(X, ghost, sm)` top-right. No `bg-blue-50`, no `bg-red-50`, no gradient backgrounds — anywhere.
+
+**Toast rule.** Every async write that succeeds calls `toast.success(...)`; every async write that fails calls `toast.error(...)` (with inline-only when the user must act in place to recover). Inline persistent `successMessage` state is a smell — route it through the toast store at `src/lib/stores/toast.js`.
+
+**Perceived-performance rule.** Never block on the full payload when you can show the first chunk now. Render-as-you-receive (page 1 first, then prefetch the rest), optimistic UI for writes, stale-while-revalidate on navigation, cache list responses keyed by `(orgId, filters, page)`, stream long operations, defer non-critical work (e.g., collapsible panels), and suppress skeletons that would flicker for under 300 ms.
+
+### Icons
+
+Always import from `$lib/components/ui/icons.js` (a curated re-export of `lucide-svelte`). `flowbite-svelte` and `flowbite-svelte-icons` are removed from the project — do not reintroduce them.
+
+### Primitives barrel
+
+`import { Button, IconButton, Modal, Badge, Card, Toast, Tabs, FormField, Dropdown, OverflowMenu, Dropzone, Stepper, Banner, Collapsible, Checkbox, EmptyState, Skeleton, SkeletonRow, SkeletonCard, SkeletonTable } from '$lib/components/ui';`
diff --git a/Caddyfile b/Caddyfile
index 78e770171..ad793ddd0 100644
--- a/Caddyfile
+++ b/Caddyfile
@@ -32,6 +32,23 @@ lamb.yourdomain.com {
redir https://owi.lamb.yourdomain.com{uri} 301
}
+ handle /docs/* {
+ reverse_proxy backend:9099
+ }
+
+ # Module frontends (SPA fallback to module-specific index.html)
+ handle /m/chat/* {
+ root * /var/www/frontend
+ try_files {path} /m/chat/index.html
+ file_server
+ }
+
+ handle /m/file-eval/* {
+ root * /var/www/frontend
+ try_files {path} /m/file-eval/index.html
+ file_server
+ }
+
handle {
root * /var/www/frontend
try_files {path} /index.html
diff --git a/Documentation/DOCUMENTATION_INDEX.md b/Documentation/DOCUMENTATION_INDEX.md
deleted file mode 100644
index 9e028ae0b..000000000
--- a/Documentation/DOCUMENTATION_INDEX.md
+++ /dev/null
@@ -1,316 +0,0 @@
-# LAMB Documentation Index
-
-> **Quick Navigation Guide for Developers, DevOps Engineers, and AI Agents**
-
-This index helps you find exactly what you need in the LAMB documentation. Start here when you're looking for something specific.
-
----
-
-## 🎯 I Want To...
-
-### Understand the System
-
-| Goal | Document | Section |
-|------|----------|---------|
-| Get a high-level overview | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §1 System Overview |
-| Understand the dual API design | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §3 Dual API Architecture |
-| Learn about multi-tenancy | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §7 Organizations |
-| Understand the completion pipeline | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §6 Completion Pipeline |
-
-### Set Up Development Environment
-
-| Goal | Document | Section |
-|------|----------|---------|
-| Quick start with Docker | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §10 Development |
-| Configure environment variables | [../backend/ENVIRONMENT_VARIABLES.md](../backend/ENVIRONMENT_VARIABLES.md) | Full doc |
-| Deploy to production | [deployment.apache.md](./deployment.apache.md) | Full doc |
-
-### Work with the Backend
-
-| Goal | Document | Section |
-|------|----------|---------|
-| Add a new API endpoint | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §3 Dual API Architecture |
-| Create a custom plugin | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §6.4 Plugin System |
-| Understand database schema | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §4 Data Architecture |
-| Work with authentication | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §5 Authentication |
-| Configure logging | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §11 Logging |
-
-### Work with the Frontend
-
-| Goal | Document | Section |
-|------|----------|---------|
-| Understand frontend structure | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §9 Frontend Architecture |
-| Handle form state (Svelte 5) | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §9.3 UX Patterns |
-| Avoid async race conditions | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §9.3 UX Patterns |
-
-### Implement Features
-
-| Goal | Document | Section |
-|------|----------|---------|
-| Add Knowledge Base support | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §8.1 Knowledge Base |
-| Implement LTI integration | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §8.2 LTI Integration |
-| Add assistant sharing | [lamb_architecture_v2.md](./lamb_architecture_v2.md) | §8.3 Assistant Sharing |
-
----
-
-## 📁 Documentation Map
-
-```
-Documentation/
-├── DOCUMENTATION_INDEX.md ← YOU ARE HERE
-├── README.md ← Docs landing page
-├── lamb_architecture_v2.md ← Single canonical architecture reference
-│
-├── installationguide.md ← Installation instructions
-├── deployment.md ← General deployment guide
-├── deployLocal.md ← Local Docker deployment
-├── deployNext.md ← Hetzner autonomous deployment
-├── deployment.apache.md ← Apache reverse proxy config
-├── deployment.nginx.md ← Nginx reverse proxy config
-│
-└── (all other docs moved to private enterprise repo)
-```
-
----
-
-## 🗂️ Key File Locations
-
-### Backend (`/backend/`)
-
-```
-backend/
-├── main.py # Main entry point, mounts all routers
-├── config.py # Configuration management
-├── schemas.py # Pydantic models
-│
-├── lamb/ # LAMB Core API
-│ ├── main.py # Core router setup
-│ ├── database_manager.py # LAMB database operations
-│ ├── assistant_router.py # Assistant CRUD
-│ ├── organization_router.py # Organization management
-│ ├── logging_config.py # Centralized logging
-│ │
-│ ├── completions/ # Completion pipeline
-│ │ ├── main.py # Pipeline orchestration
-│ │ ├── pps/ # Prompt processors
-│ │ │ └── simple_augment.py
-│ │ ├── connectors/ # LLM connectors
-│ │ │ ├── openai.py
-│ │ │ ├── ollama.py
-│ │ │ └── banana_img.py
-│ │ └── rag/ # RAG processors
-│ │ └── simple_rag.py
-│ │
-│ ├── owi_bridge/ # Open WebUI integration
-│ │ ├── owi_database.py
-│ │ ├── owi_users.py
-│ │ ├── owi_group.py
-│ │ └── owi_model.py
-│ │
-│ ├── services/ # Business logic services
-│ │ └── chat_analytics_service.py
-│ │
-│ └── simple_lti/ # LTI integration
-│ └── simple_lti_main.py
-│
-├── creator_interface/ # Creator Interface API
-│ ├── main.py # Creator router setup
-│ ├── assistant_router.py # Proxied assistant ops
-│ ├── knowledges_router.py # KB operations
-│ ├── organization_router.py # Org admin
-│ ├── analytics_router.py # Chat analytics
-│ ├── evaluaitor_router.py # Rubric management
-│ ├── prompt_templates_router.py # Prompt templates
-│ └── user_creator.py # User creation
-│
-└── utils/ # Utilities
- ├── main_helpers.py
- └── name_sanitizer.py
-```
-
-### Frontend (`/frontend/svelte-app/`)
-
-```
-frontend/svelte-app/
-├── src/
-│ ├── routes/ # SvelteKit pages
-│ │ ├── +layout.svelte # Root layout
-│ │ ├── +page.svelte # Home (redirects)
-│ │ ├── assistants/+page.svelte # Assistants list
-│ │ ├── knowledge-bases/+page.svelte
-│ │ ├── admin/+page.svelte # System admin
-│ │ └── org-admin/+page.svelte # Org admin
-│ │
-│ ├── lib/
-│ │ ├── components/ # UI components
-│ │ │ ├── Nav.svelte
-│ │ │ ├── Login.svelte
-│ │ │ ├── assistants/
-│ │ │ │ └── AssistantForm.svelte
-│ │ │ └── analytics/
-│ │ │ └── ChatAnalytics.svelte
-│ │ │
-│ │ ├── services/ # API clients
-│ │ │ ├── authService.js
-│ │ │ ├── assistantService.js
-│ │ │ ├── knowledgeBaseService.js
-│ │ │ └── adminService.js
-│ │ │
-│ │ ├── stores/ # Svelte stores
-│ │ │ ├── userStore.js
-│ │ │ └── assistantStore.js
-│ │ │
-│ │ └── config.js # Runtime config
-│ │
-│ └── app.html # HTML template
-│
-├── static/
-│ └── config.js.sample # Config template
-│
-└── package.json
-```
-
-### Databases
-
-| Database | Location | Purpose |
-|----------|----------|---------|
-| LAMB DB | `$LAMB_DB_PATH/lamb_v4.db` | Assistants, users, orgs |
-| OWI DB | `$OWI_DATA_PATH/webui.db` | Chat history, mirror users, groups, models |
-| ChromaDB | `$OWI_DATA_PATH/vector_db/` | KB vectors |
-
----
-
-## 🔑 Key Concepts Quick Reference
-
-### Dual API Architecture
-```
-Browser → Creator Interface API (/creator) → LAMB Core API (/lamb/v1) → Database
-```
-- **Creator Interface**: User-facing, handles auth, file uploads, validation
-- **LAMB Core**: Business logic, database operations, completions
-
-### Plugin Types
-| Type | Purpose | Location |
-|------|---------|----------|
-| Prompt Processor | Transform messages | `lamb/completions/pps/` |
-| Connector | Call LLM providers | `lamb/completions/connectors/` |
-| RAG Processor | Retrieve KB context | `lamb/completions/rag/` |
-
-### User Types
-| Type | Access |
-|------|--------|
-| `creator` | Full creator interface access |
-| `end_user` | Redirected to Open WebUI only |
-
-### Organization Roles
-| Role | Permissions |
-|------|-------------|
-| `owner` | Full control |
-| `admin` | Manage settings and members |
-| `member` | Create assistants |
-
----
-
-## 🛠️ Common Development Tasks
-
-### Add a New Backend Endpoint
-
-1. **Choose the right router:**
- - User-facing → `creator_interface/*.py`
- - Internal/core → `lamb/*.py`
-
-2. **Add endpoint:**
- ```python
- @router.get("/my-endpoint")
- async def my_endpoint(request: Request):
- # Implementation
- ```
-
-3. **Register in router** (if new file)
-
-### Add a New Frontend Page
-
-1. Create `src/routes/my-page/+page.svelte`
-2. Add navigation link in `Nav.svelte`
-3. Create service functions in `lib/services/`
-
-### Create a New Plugin
-
-1. Create file in appropriate directory:
- - `lamb/completions/pps/my_pps.py`
- - `lamb/completions/connectors/my_connector.py`
- - `lamb/completions/rag/my_rag.py`
-
-2. Implement required function signature (see §6.4 in architecture doc)
-
-3. Configure assistant to use it via metadata
-
-### Add a Database Field
-
-1. Add migration in `database_manager.py` → `run_migrations()`
-2. Update relevant queries
-3. Update Pydantic schemas if needed
-
----
-
-## 📊 API Endpoint Quick Reference
-
-### Authentication
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| POST | `/creator/login` | User login |
-| POST | `/creator/signup` | User signup |
-| GET | `/creator/user/current` | Get current user |
-
-### Assistants
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| GET | `/creator/assistant/list` | List user's assistants |
-| POST | `/creator/assistant/create` | Create assistant |
-| GET | `/creator/assistant/{id}` | Get assistant |
-| PUT | `/creator/assistant/update` | Update assistant |
-| DELETE | `/creator/assistant/delete/{id}` | Delete assistant |
-
-### Knowledge Bases
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| GET | `/creator/knowledgebases/user` | List user's KBs |
-| POST | `/creator/knowledgebases/create` | Create KB |
-| POST | `/creator/knowledgebases/{id}/upload` | Upload document |
-| GET | `/creator/knowledgebases/{id}/query` | Query KB |
-
-### Completions (OpenAI-compatible)
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| GET | `/v1/models` | List available assistants |
-| POST | `/v1/chat/completions` | Generate completion |
-
-### Admin
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| GET | `/creator/admin/users` | List users |
-| POST | `/creator/admin/users/create` | Create user |
-| PUT | `/creator/admin/users/{id}/status` | Enable/disable user |
-
----
-
-## 🔗 External Resources
-
-- **GitHub:** https://github.com/Lamb-Project/lamb
-- **Website:** https://lamb-project.org
-- **Open WebUI:** https://github.com/open-webui/open-webui
-
----
-
-## 📝 Document Versions
-
-| Document | Purpose | When to Use |
-|----------|---------|-------------|
-| `lamb_architecture_v2.md` | **Primary reference** | Start here for any task |
-| `lamb_architecture.md` | Full detailed reference | Deep implementation details |
-| `lamb_architecture_small.md` | Legacy condensed | Deprecated, use v2 |
-
----
-
-*Last Updated: February 13, 2026*
-
diff --git a/Documentation/README.md b/Documentation/README.md
deleted file mode 100644
index c12211450..000000000
--- a/Documentation/README.md
+++ /dev/null
@@ -1,267 +0,0 @@
-# LAMB - Learning Assistants Manager and Builder
-
-
-

-
- **Create AI assistants for education integrated in your Learning Management System**
-
- [](http://www.lamb-project.org)
- [](LICENSE)
- [](https://manifesto.safeaieducation.org)
- [](https://github.com/Lamb-Project/lamb)
-
-
-## 📋 Project Description
-
-**LAMB** (Learning Assistants Manager and Builder) is an open-source web platform that enables educators to design, test, and publish AI-based learning assistants into your Learning Management System (LMS like Moodle) without writing any code. It functions as a visual "teaching chatbot builder" that seamlessly combines large language models (GPT-4, Mistral, local models) with your own educational materials.
-
-Developed by Marc Alier and Juanan Pereira, professors and researchers at the Universitat Politècnica de Catalunya (UPC) and Universidad del País Vasco (UPV/EHU), LAMB addresses the critical need for educational AI tools that maintain student privacy while providing powerful, context-aware learning support.
-
-## 🎯 Key Features
-
-### 🎓 **Specialized Subject Tutors**
-Design assistants that stay grounded on your chosen subject area, ensuring responses are always educationally appropriate and contextually relevant.
-
-### 📚 **Intelligent Knowledge Ingestion**
-Upload educational materials (PDF, Word, Markdown) and LAMB automatically processes them with:
-- Flexible data model that preserves context and relationships
-- Semantic embeddings optimized for educational search
-- Custom metadata support for each document
-- Adaptive processing for different content structures
-- RAG (Retrieval Augmented Generation) integration
-
-### 🔒 **Privacy-First Architecture**
-- The students will access the Learning Assitants as Learning Activities within the LMS Course
-- No user information is shared with AI model providers
-- Can run on open source and open weights models running on your compute
-- Secure, self-hosted solution
-
-### 🔌 **LTI Integration**
-Seamlessly integrate with Moodle and other Learning Management Systems through LTI (Learning Tools Interoperability) standard - publish your assistant as an external tool with just a few clicks.
-
-### 🤖 **Multi-Model Support**
-- Works with OpenAI API compatible models
-- Ollama inetgration
-- One-click model switching
-- Model-agnostic architecture
-
-### 🔍 **Advanced Testing & Debugging**
-- Debug mode showing complete prompts
-- Citation tracking with source references
-
-
-### 🌍 **Multilingual Interface**
-Built-in support for Basque, Catalan, Spanish, and English, with easy extensibility for additional languages.
-
-### 💾 **Portability & Versioning**
-- Export/import assistants in JSON format
-
-
-## 👥 Target Audience
-
-LAMB is designed for:
-
-- **📖 Teachers and Trainers**: Create virtual assistants focused on specific curricula without technical expertise
-- **🏫 Educational Institutions**: Integrate AI into existing LMS platforms while maintaining data sovereignty
-- **💡 Innovation Teams**: Experiment with different LLMs through a unified management interface
-- **🔬 Researchers**: Study AI in education with complete control over the learning environment
-
-## 🏗️ Architecture Overview
-
-LAMB features a modular, extensible architecture:
-
-- **Backend**: FastAPI-based server handling assistant management, LTI integration, and model orchestration
-- **Frontend**: Modern Svelte 5 application providing intuitive UI for assistant creation and management
-- **Knowledge Base Server**: Dedicated service for document ingestion and vector search
-- **Integration Layer**: Bridges with Open WebUI for model management https://github.com/open-webui/open-webui
-
-## 🚀 Installation
-
-### Recommended: Docker Installation
-
-For the easiest setup experience, we recommend using Docker Compose to run all LAMB services:
-
-📘 **[Docker Installation Guide](Documentation/slop-docs/deployment.md)** - One-command deployment with all services configured
-
-### Alternative: Manual Installation
-
-For development or custom deployments:
-
-📘 **[Complete Installation Guide](Documentation/installationguide.md)** - Step-by-step manual setup for all components
-
-### Quick Overview
-
-LAMB requires four main services:
-1. **Open WebUI Server** (port 8080) - Model management interface
-2. **LAMB Knowledge Base Server** (port 9090) - Document processing and vector search
-3. **LAMB Backend Server** (port 9099) - Core API and business logic
-4. **Frontend Application** (port 5173) - Web interface
-
-## 📖 Documentation
-
-### 📚 For End Users
-Visit our [official website](http://www.lamb-project.org) for:
-- **User guides and tutorials**
-- **Feature documentation**
-- **Educational resources**
-- **Community support**
-
-### 📖 Developer Documentation
-Comprehensive documentation is available in the `/Documentation` directory:
-
-- [Documentation Index](Documentation/DOCUMENTATION_INDEX.md) — start here
-- [Architecture Reference](Documentation/lamb_architecture_v2.md)
-- [Installation Guide](Documentation/installationguide.md)
-- [Deployment Guide](Documentation/deployment.md)
-
-## 🗂️ Project Structure
-
-```
-lamb/
-├── backend/ # FastAPI backend server
-│ ├── lamb/ # Core LAMB functionality
-│ ├── creator_interface/# Assistant creation interface
-│ └── utils/ # Utility functions
-├── frontend/ # Svelte 5 frontend
-│ └── svelte-app/ # Main web application
-├── lamb-kb-server/ # Knowledge base server
-├── Documentation/ # Project documentation
-└── docker-compose.yaml # Container orchestration
-```
-
-## 🤝 Contributing
-
-We welcome contributions! LAMB is an open-source project that thrives on community involvement. Areas where you can help:
-
-- 📝 Documentation improvements
-- 🌍 Translations to new languages
-- 🔌 New LMS integrations
-- 🤖 Additional model support
-- 🐛 Bug fixes and testing
-
-Please see our [Contributing Guide](CONTRIBUTING.md) for details.
-
-## 📜 License
-
-LAMB is licensed under the GNU General Public License v3.0 (GPL v3).
-
-Copyright (c) 2024-2025 Marc Alier (UPC) @granludo & Juanan Pereira (UPV/EHU) @juananpe
-
-See [LICENSE](LICENSE) for full details.
-
-## 📚 Publications & Research
-
-### Academic Publications on LAMB
-
-If you use LAMB in your research, please cite our work:
-
-**LAMB: An open-source software framework to create artificial intelligence assistants deployed and integrated into learning management systems**
-- **Authors**: Marc Alier, Juanan Pereira, Francisco José García-Peñalvo, Maria Jose Casañ, Jose Cabré
-- **Journal**: Computer Standards & Interfaces
-- **Volume**: 92
-- **Pages**: 103940
-- **Publication Date**: March 2025
-- **DOI**: [10.1016/j.csi.2024.103940](https://doi.org/10.1016/j.csi.2024.103940)
-- **Direct Link**: [ScienceDirect Article](https://www.sciencedirect.com/science/article/pii/S0920548924001090)
-
-```bibtex
-@article{ALIER2024103940,
-title = {LAMB: An open-source software framework to create artificial intelligence assistants deployed and integrated into learning management systems},
-journal = {Computer Standards \& Interfaces},
-volume = {92},
-pages = {103940},
-year = {2025},
-issn = {0920-5489},
-doi = {https://doi.org/10.1016/j.csi.2024.103940},
-url = {https://www.sciencedirect.com/science/article/pii/S0920548924001090},
-author = {Marc Alier and Juanan Pereira and Francisco Jos{\'e} Garc{\'i}a-Pe{\~n}alvo and Maria Jose Casan and Jose Cabr{\'e}}
-}
-```
-
-### Research Collaborators
-
-We acknowledge the valuable contributions and research collaboration from the authors and researchers who have worked on LAMB:
-
-#### Project Leaders
-- **Juanan Pereira** (Universidad del País Vasco, UPV/EHU) - Co-Lead & Principal Researcher
-- **Marc Alier** (Universitat Politècnica de Catalunya, UPC) - Co-Lead & Principal Researcher
-
-#### Senior Researchers & Academic Collaborators
-- **Francisco José García-Peñalvo** - Advisor and Senior Researcher
-- **Maria Jose Casañ** (Universitat Politècnica de Catalunya, UPC) - Research Contributor & Developer
-- **Ariadna Maria LLorens** (Universitat Politècnica de Catalunya, UPC) - Research Contributor
-- **Jose Cabré** (Universitat Politècnica de Catalunya, UPC) - Research Contributor
-- **David Lopez Alvarez** (Universitat Politècnica de Catalunya, UPC) - Research Contributor
-
-
-## 🙏 Acknowledgments
-
-### Academic & Institutional Partners
-- **Universidad del País Vasco (UPV/EHU)** - Research institution and development partner
-- **Universitat Politècnica de Catalunya (UPC)** - Research institution and development partner
- - **Barcelona School of Informatics** (https://fib.upc.edu)
- - **Institut de Ciències de l'Educació - ICE** (https://ice.upc.edu)
- - **Department of Service and Information System Engineering. ESSI** (http://essi.upc.edu)
-- **Universidad de Salamanca** - Grial Research Group
-
-### Open Source Dependencies
-- **Open WebUI Project** - (https://github.com/open-webui/) Advanced chatbot web interface integration, and a lot of design descisions borrowed from the openwebui pipelines project.
-- **TSugi Project** (https://www.tsugi.org) Used in early Lamb implementations for LTI provider support. Many thanks to Dr. Chuck (Charles Severance) for his support and inspiration.
-
-
-### Research & Educational Community
-- **TEEM Conference** - (https://teemconference.eu) The TEEM conference has a vibrant community of researchers working on multidisciplinary fields connected to technology and education. The LAMB project was born on a coffe break conversation after the "Managing Generative AI in educational settings", we lost control of it :-) .
-- **Teaching Community** - Early adopters and beta testers:
- - https://tknika.eus/en/ Basque VET Applied Research Centre
-- **All Contributors** - For their dedication to improving education through technology
-
-### Funding projects directly or indirectly contruibuting to the project
-
-- Universitat Politecnica de Cataluya. Galaxia d'Aprenentatge projecte PROPER, Factulat d'Informatica de Barcelona (2024-2025).
-- Departament de Recerca i Universitats de la Generalitat de Catalunya through the 2021 SGR 01412 research groups award (2021-2025).
-- Universidad del País Vasco/Euskal Herriko Unibertsitatea through the contract GIU21/037 under the program “Convocatoria para la Concesión de Ayudas a los Grupos de Investigación en la Universidad del País Vasco/Euskal Herriko Unibertsitatea (2021)
-
-
-## 🛡️ Safe AI in Education Manifesto
-
-LAMB proudly adheres to the **[Safe AI in Education Manifesto](https://manifesto.safeaieducation.org)** - a comprehensive framework for ethical, secure, and educationally-aligned AI deployment.
-
-### 📋 Manifesto TLDR
-
-The Safe AI in Education Manifesto outlines 7 core principles for responsible AI use in education:
-
-1. **Human Oversight** - AI complements, never replaces, human educators
-2. **Privacy Protection** - Student data confidentiality and security
-3. **Educational Alignment** - AI supports institutional strategies and learning objectives
-4. **Didactic Integration** - Seamless integration with teaching methodologies
-5. **Accuracy & Explainability** - Reliable, source-attributed information
-6. **Transparent Interfaces** - Clear communication of AI limitations and capabilities
-7. **Ethical Training** - Models trained with educational ethics and transparency
-
-### 🎯 How LAMB Implements These Principles
-
-**LAMB is designed from the ground up to embody these principles:**
-
-- **🔍 Human Oversight**: All assistants are created and managed by educators with full control over behavior and content
-- **🔒 Privacy-First**: Self-hosted architecture keeps all student data within institutional control
-- **📚 Educational Focus**: Specialized subject tutors stay grounded in educational content and objectives
-- **🧠 Didactic Integration**: Seamless LTI integration with Moodle and other LMS platforms
-- **📖 Source Attribution**: Automatic citations and references to source materials
-- **💬 Transparent Communication**: Clear assistant responses with educational context and limitations
-- **🎓 Ethical Foundation**: Open-source, academically-developed with research collaboration
-
-### 🤝 Our Commitment
-
-As signatories to the manifesto, LAMB's core team members are committed to advancing ethical AI in education. LAMB represents a practical implementation of manifesto principles in action.
-
-## 📧 Contact
-- **Project Leads**: Marc Alier (UPC), Juanan Pereira (UPV/EHU)
-- **Research**: Academic collaborations and research partnerships
-- **GitHub**: [https://github.com/Lamb-Project/lamb](https://github.com/Lamb-Project/lamb)
-- **Issues**: [GitHub Issues](https://github.com/Lamb-Project/lamb/issues)
-- **Website**: [http://www.lamb-project.org](http://www.lamb-project.org)
-
----
-
-**LAMB** - Empowering educators to create intelligent, privacy-respecting AI assistants for enhanced learning experiences.
-
diff --git a/Documentation/deployLocal.md b/Documentation/deployLocal.md
deleted file mode 100644
index e633476dd..000000000
--- a/Documentation/deployLocal.md
+++ /dev/null
@@ -1,739 +0,0 @@
-# LAMB NEXT — Local Deployment Guide
-
-This document is designed to be fed to an AI coding agent (like GitHub Copilot) that will autonomously deploy LAMB NEXT locally for development or testing purposes.
-
-**Assumptions:**
-- Docker and Docker Compose (V2) are installed on the local machine.
-- Git is installed and the agent has access to the LAMB source repository.
-- No DNS, TLS certificates, or cloud infrastructure are needed — everything runs on `localhost`.
-
----
-
-## Phase 0: Gather Information (via `askQuestions`)
-
-Before touching any files, the agent MUST collect these from the user using the `vscode_askQuestions` tool. Do NOT proceed until all answers are received.
-
-### 0.0 — Pre-Flight: Check for Existing `.env`
-
-> **CRITICAL — Do this FIRST, before asking any questions.**
-
-If the user already has a `.env` file (at `/.env` or in the current workspace), the agent MUST read it and extract all values. Use these as **pre-filled defaults** for the questions in sections 0.2–0.5. This dramatically reduces the number of questions — if the `.env` is complete, the user may only need to confirm a handful of values.
-
-**Priority order for defaults:**
-1. Existing `.env` at the install location (highest priority)
-2. `backend/.env` or `backend/.env.example` in the repo
-3. `lamb-kb-server-stable/backend/.env.example` (for embeddings vars)
-4. Built-in defaults documented in sections 0.2–0.5 below (lowest priority)
-
-**What to extract from the existing `.env`:**
-- `OPENAI_API_KEY`, `OPENAI_BASE_URL`, `OPENAI_MODEL`, `OPENAI_MODELS`
-- `EMBEDDINGS_VENDOR`, `EMBEDDINGS_MODEL`, `EMBEDDINGS_APIKEY`, `EMBEDDINGS_ENDPOINT`
-- `LAMB_BEARER_TOKEN`, `SIGNUP_SECRET_KEY`, `SIGNUP_ENABLED`, `DEV_MODE`
-- `LAMB_WEB_HOST`, `LAMB_PORT`, `KB_PORT`, `OPENWEBUI_PORT`
-- `OWI_ADMIN_NAME`, `OWI_ADMIN_EMAIL`, `OWI_ADMIN_PASSWORD`
-- `OWI_PUBLIC_BASE_URL`, `LAMB_LIBRARY_TOKEN`
-- `OLLAMA_BASE_URL` (if using Ollama)
-- Any other configured variables
-
-> **Insight:** If a fully pre-filled `.env` already exists (all required vars present), the agent can skip directly to Phase 1 after a quick confirmation with the user. The user can also place a pre-filled `.env` in the clone target directory *before* the agent runs, ensuring a near-silent deployment.
-
-
-> **🔍 AI Agent Note:** `.env` files (if they exist at all) are **gitignored** — glob-based file search will miss them. Use `list_dir` on the repo root, `backend/`, and `lamb-kb-server-stable/backend/` instead, or use `includeIgnoredFiles: true`. Read any `.env` found (priority: `/.env` > `backend/.env` > `lamb-kb-server-stable/backend/.env` > `.env.example` files). ALWAYS ask for an *initial confirmation* from the user before attempting to read the .env.
-
-### 0.1 — Operating System & Shell
-
-| Question | Purpose | Options / Notes |
-|----------|---------|-----------------|
-| Operating system | Determines install path, shell commands, and Docker setup | `Linux` or `Windows` |
-| Windows shell | (Only if Windows) Which shell to use for all commands | `PowerShell` (native) or `WSL` (Windows Subsystem for Linux) |
-
-> **Insight:** WSL behaves like Linux for all shell commands (bash, `sudo`, heredocs, etc.). If the user chooses WSL, treat the environment as Linux throughout this guide. PowerShell requires different syntax for many operations (see Phase 1–3 for platform-specific commands).
-
-### 0.2 — Workspace Setup
-
-| Question | Purpose | Options / Notes |
-|----------|---------|-----------------|
-| Install location | Where to clone the repo | **Linux / WSL:** `/opt/lamb` — **Windows PowerShell:** `C:\lamb` |
-| Git branch | Which branch to use | Default: `main` |
-| Existing data? | Does the user have data from a previous LAMB installation to migrate? | `yes` or `no`. If yes, the agent must ask for the **old project path** (where the old LAMB install lives — database, OpenWebUI data, KB server data). |
-
-### 0.3 — API Keys & Model Configuration
-
-| Question | Purpose | Default / Fallback |
-|----------|---------|---------------------|
-| OpenAI API key | `OPENAI_API_KEY` and `EMBEDDINGS_APIKEY` | Required unless using Ollama for everything |
-| OpenAI base URL | `OPENAI_BASE_URL` | `https://api.openai.com/v1` |
-| OpenAI model | `OPENAI_MODEL` | `gpt-4o-mini` |
-| OpenAI model list | `OPENAI_MODELS` | `gpt-4o-mini,gpt-4o` |
-| Embeddings vendor | `EMBEDDINGS_VENDOR` | `openai` or `ollama` (local, no API key needed) |
-| Embeddings model | `EMBEDDINGS_MODEL` | `text-embedding-3-large` (OpenAI) or `nomic-embed-text` (Ollama) |
-| Embeddings endpoint | `EMBEDDINGS_ENDPOINT` | `https://api.openai.com/v1` (OpenAI) or `http://ollama:11434` (Ollama) |
-| Embeddings API key | `EMBEDDINGS_APIKEY` | Same as `OPENAI_API_KEY` for OpenAI; leave empty for Ollama |
-
-> **IMPORTANT:** If the user has an existing `backend/.env` file or `.env.example`, extract keys from those files as defaults to pre-fill the questions.
-
-### 0.4 — Feature Toggles & Secrets
-
-| Question | Purpose | Default |
-|----------|---------|---------|
-| Enable signup? | `SIGNUP_ENABLED` | `true` |
-| Enable dev mode? | `DEV_MODE` | `true` (for local dev) |
-| Enable Ollama? | Adds `--profile ollama` to compose | `false` — ask the user if they want local LLM inference via Ollama |
-| Signup secret key | `SIGNUP_SECRET_KEY` | Auto-generate a random string |
-| LAMB bearer token | `LAMB_BEARER_TOKEN` | Auto-generate a random string |
-| OWI admin name/email/password | Bootstrap admin account for OpenWebUI | e.g., `Admin` / `admin@localhost.local` / auto-generated password |
-
-### 0.5 — Port Configuration
-
-| Question | Purpose | Default |
-|----------|---------|---------|
-| LAMB port | Backend API port | `9099` |
-| KB port | Knowledge base server port | `9090` |
-| OpenWebUI port | Chat interface port | `8080` |
-| Ollama port | Local LLM port (if enabled) | `11434` |
-
-> **Insight:** The defaults work for most users. Only ask about port overrides if the user mentions port conflicts.
-
----
-
-## Phase 1: Prerequisites Check
-
-The agent MUST verify these before proceeding. Check them on the local machine (no SSH needed).
-
-### 1.1 — Check Docker
-
-```bash
-docker --version
-docker compose version
-```
-
-Expected: Docker 29+ and Docker Compose V2 (the plugin, invoked as `docker compose`).
-
-If Docker is not installed, tell the user to install it:
-- **Windows/Mac:** [Docker Desktop](https://www.docker.com/products/docker-desktop/)
-- **Linux:** `curl -fsSL https://get.docker.com | sh`
-
-### 1.2 — Check Available Disk Space
-
-```bash
-df -h
-```
-
-The stack's Docker images total ~3-4 GB. With volumes, plan for at least 10 GB free.
-
-### 1.3 — Check Port Availability
-
-```bash
-# Check if default ports are already in use
-lsof -i :9099 2>/dev/null || netstat -tlnp 2>/dev/null | grep -E '9099|9090|8080|11434'
-```
-
-If any ports are in use, ask the user to either free them or choose alternative ports in Phase 0.5.
-
----
-
-## Phase 2: Clone the Repository
-
-### 2.1 — Create the Install Directory
-
-```bash
-sudo mkdir -p
-sudo chown $(whoami)
-```
-
-Or on Windows (PowerShell as Administrator):
-
-```powershell
-New-Item -ItemType Directory -Path -Force
-```
-
-### 2.2 — Clone
-
-```bash
-git clone https://github.com/Lamb-Project/lamb.git
-cd
-```
-
-If the user specified a branch:
-
-```bash
-git checkout
-```
-
----
-
-## Phase 3: Create the `.env` File
-
-The `.env` file lives in the repo root (same directory as `docker-compose.next.yaml`).
-
-### 3.1 — Required Variables (compose fails if missing)
-
-These MUST be set. The compose file uses `${VAR?error message}` syntax.
-
-| Variable | Local Default | Notes |
-|----------|--------------|-------|
-| `LAMB_WEB_HOST` | `http://localhost:9099` | The URL where the LAMB frontend is accessed |
-| `LAMB_BACKEND_HOST` | `http://lamb:9099` | Internal Docker service name — NOT `localhost` |
-| `LAMB_BEARER_TOKEN` | Auto-generated | Strong random string |
-| `LAMB_DB_PATH` | `/data/lamb` | Path INSIDE the container |
-| `OWI_BASE_URL` | `http://openwebui:8080` | Internal Docker service name |
-| `OWI_PATH` | `/data/openwebui` | Path INSIDE the container |
-| `OPENAI_BASE_URL` | `https://api.openai.com/v1` | Or user's custom endpoint |
-| `OPENAI_MODEL` | `gpt-4o-mini` | Or user's custom model |
-| `SIGNUP_SECRET_KEY` | Auto-generated | Strong random string |
-| `OWI_ADMIN_NAME` | `Admin` | OpenWebUI bootstrap admin |
-| `OWI_ADMIN_EMAIL` | `admin@localhost.local` | OpenWebUI bootstrap admin |
-| `OWI_ADMIN_PASSWORD` | Auto-generated | Strong password |
-| `LAMB_LIBRARY_TOKEN` | Auto-generated | Strong random string for library-manager auth |
-
-### 3.2 — Browser-Facing URLs
-
-| Variable | Local Default | Notes |
-|----------|--------------|-------|
-| `OWI_PUBLIC_BASE_URL` | `http://localhost:8080` | Browser-facing OpenWebUI URL. REQUIRED — without it, LAMB generates login redirects using the internal Docker hostname (`openwebui:8080`) that browsers cannot resolve. Must be set even for local dev. |
-
-### 3.3 — KB Embeddings Variables
-
-| Variable | Local Default (Ollama) | Local Default (OpenAI) |
-|----------|------------------------|-------------------------|
-| `EMBEDDINGS_VENDOR` | `ollama` | `openai` |
-| `EMBEDDINGS_MODEL` | `nomic-embed-text` | `text-embedding-3-large` |
-| `EMBEDDINGS_APIKEY` | (empty) | Same as `OPENAI_API_KEY` |
-| `EMBEDDINGS_ENDPOINT` | `http://ollama:11434` | `https://api.openai.com/v1` |
-
-> **Insight:** If the user is NOT running Ollama, they MUST use OpenAI for embeddings. The KB server requires a working embeddings provider.
-
-### 3.4 — Optional Variables
-
-```bash
-OPENAI_API_KEY=sk-... # only if using OpenAI
-OPENAI_MODELS=gpt-4o-mini,gpt-4o # comma-separated list of available models
-LAMB_KB_SERVER=http://kb:9090 # default, can omit
-LAMB_KB_SERVER_TOKEN=0p3n-w3bu! # must match KB's LAMB_API_KEY
-LTI_SECRET=lamb-lti-secret-key-2024 # override in production-like testing
-SIGNUP_ENABLED=true
-DEV_MODE=true # true for local dev
-GLOBAL_LOG_LEVEL=WARNING
-WEBUI_SECRET_KEY= # optional, auto-generated if empty
-OLLAMA_BASE_URL=http://ollama:11434 # REQUIRED on Linux if using Ollama profile
-LAMB_PORT=9099
-KB_PORT=9090
-OPENWEBUI_PORT=8080
-```
-
-### 3.5 — Key Insights for the Agent
-
-- **`LAMB_BACKEND_HOST`** is an INTERNAL Docker network URL (`http://lamb:9099`), NOT `localhost`. The `lamb` part is the Docker Compose service name.
-- **`OWI_BASE_URL`** likewise uses the Docker service name: `http://openwebui:8080`.
-- **`LAMB_DB_PATH`** and **`OWI_PATH`** are paths INSIDE the container, not on the host. The defaults use Docker named volumes.
-- **`LAMB_WEB_HOST`** IS `http://localhost:9099` — this is the URL the browser uses, so it must point to the host.
-- **`OWI_PUBLIC_BASE_URL`** is the browser-facing OpenWebUI URL. Without it, the code falls back to `OWI_BASE_URL` (`http://openwebui:8080`), which browsers can't resolve. For local dev, set it to `http://localhost:8080`.
-- **`LAMB_LIBRARY_TOKEN`** is required by `docker-compose.next.yaml` (the `library-manager` service uses `?Set` syntax). Auto-generate a random string.
-- **`OLLAMA_BASE_URL`** defaults to `http://host.docker.internal:11434` in `config.py`, which only works on macOS/Windows (Docker Desktop). On Linux, this hostname doesn't exist — you MUST set `OLLAMA_BASE_URL=http://ollama:11434` (the Docker service name) in `.env` and pass it through the compose file.
-- **API key reuse** — The user may want the same key for both `OPENAI_API_KEY` (lamb service) and `EMBEDDINGS_APIKEY` (kb service), or different keys. Ask explicitly.
-
-### 3.6 — Write the File
-
-The agent should construct the `.env` from the collected answers and write it:
-
-```bash
-cat > /.env << 'ENVEOF'
-# ============================================================================
-# LAMB NEXT — Local Development .env
-# ============================================================================
-
-# --- Required ---
-LAMB_WEB_HOST=http://localhost:9099
-LAMB_BACKEND_HOST=http://lamb:9099
-LAMB_BEARER_TOKEN=
-LAMB_DB_PATH=/data/lamb
-OWI_BASE_URL=http://openwebui:8080
-OWI_PUBLIC_BASE_URL=http://localhost:8080
-OWI_PATH=/data/openwebui
-OPENAI_BASE_URL=https://api.openai.com/v1
-OPENAI_MODEL=gpt-4o-mini
-SIGNUP_SECRET_KEY=
-OWI_ADMIN_NAME=Admin
-OWI_ADMIN_EMAIL=admin@localhost.local
-OWI_ADMIN_PASSWORD=
-LAMB_LIBRARY_TOKEN=
-OLLAMA_BASE_URL=http://ollama:11434
-
-# --- KB Embeddings ---
-EMBEDDINGS_VENDOR=openai
-EMBEDDINGS_MODEL=text-embedding-3-large
-EMBEDDINGS_APIKEY=sk-...
-EMBEDDINGS_ENDPOINT=https://api.openai.com/v1
-
-# --- Optional ---
-OPENAI_API_KEY=sk-...
-OPENAI_MODELS=gpt-4o-mini,gpt-4o
-SIGNUP_ENABLED=true
-DEV_MODE=true
-GLOBAL_LOG_LEVEL=WARNING
-ENVEOF
-```
-
-> **Insight:** Use single-quoted `'ENVEOF'` (heredoc delimiter) to prevent shell variable expansion. Auto-generate passwords using: `openssl rand -hex 32`.
-
----
-
-## Phase 3.5: Migrate Existing Data (GATE — Conditional)
-
-> **Only execute this phase if the user answered `yes` to "Existing data?" in Phase 0.2. If this is a fresh install with no prior data, skip to Phase 4.**
-
-The old LAMB stack stored data in host directories. The new stack uses **named Docker volumes**. This phase copies existing data into the new volumes so the user doesn't lose their database, OpenWebUI accounts, or KB server collections.
-
-### 3.5.1 — What the agent needs
-
-From Phase 0.2, the agent should have:
-- **``** — the directory where the old LAMB installation lives (e.g., `/opt/lamb-old` or `C:\lamb-old`)
-- **``** — the new install directory (from Phase 0.2)
-
-The expected data locations inside the old project:
-
-| Data | Old location (relative to old project path) | New volume |
-|------|---------------------------------------------|------------|
-| LAMB database | `/lamb_v4.db` | `lamb-data` (file: `lamb_v4.db`) |
-| OpenWebUI data | `/open-webui/backend/data/` | `openwebui-data` |
-| KB server data | `/lamb-kb-server-stable/backend/data/` | `kb-data` |
-
-### 3.5.2 — Run the migration
-
-Follow the step-by-step guide in **`Documentation/slop-docs/migrating-to-lamb-next.md`**, using the paths above. The migration document covers:
-
-1. Creating named volumes with `docker compose up --no-start`
-2. Copying the LAMB database into `lamb-data`
-3. Copying OpenWebUI data into `openwebui-data`
-4. Copying KB server data into `kb-data`
-5. Verifying the copied data
-6. Troubleshooting (schema auto-migration, large KB data, platform warnings)
-
-> **Insight:** The `.env` file must already exist (Phase 3) before running the migration, since `docker compose` reads it. Adapt the migration guide's paths — replace `/opt/lamb` with ``, and substitute the correct compose project name (defaults to the directory name of ``).
-
-### 3.5.3 — Critical gotcha
-
-**Stop the old stack first.** If the old LAMB containers are still running with bind-mounts to these data directories, the copy may produce inconsistent results. Ask the user to run this from the old project directory before migrating:
-
-```bash
-docker compose -f docker-compose.yaml down
-```
-
----
-
-## Phase 4: Launch the Stack
-
-### 4.1 — Pull Images and Start (Without Ollama)
-
-```bash
-cd
-docker compose -f docker-compose.next.yaml up -d
-```
-
-### 4.2 — With Ollama (Local LLM Inference)
-
-```bash
-cd
-docker compose -f docker-compose.next.yaml --profile ollama up -d
-```
-
-> **Note:** The first run downloads ~3-4 GB of Docker images (more with Ollama). This may take several minutes. The agent should run this in async mode with a generous timeout (≥300s).
-
-> **Insight:** When using the Ollama profile, the user will need to pull models inside the Ollama container before they can be used. See Phase 5.5.
-
-### 4.3 — With CUDA/GPU (NVIDIA DGX / GPU Servers)
-
-On NVIDIA GPU hosts, use the GPU override file to enable CUDA-accelerated PyTorch inside OpenWebUI and grant GPU access to both `openwebui` and `ollama` containers (equivalent to `--gpus=all`):
-
-```bash
-cd
-docker compose -f docker-compose.next.yaml -f docker-compose.next.gpu.yaml up -d
-```
-
-With Ollama + GPU:
-
-```bash
-cd
-docker compose -f docker-compose.next.yaml -f docker-compose.next.gpu.yaml --profile ollama up -d
-```
-
-**What the GPU override does:**
-- Passes `USE_CUDA=true` to the OpenWebUI Dockerfile build, which installs CUDA-enabled PyTorch instead of CPU-only PyTorch
-- Adds `deploy.resources.reservations.devices` with `driver: nvidia, count: all, capabilities: [gpu]` to `openwebui` and `ollama` services
-
-**Optional env vars:**
-
-| Variable | Default | Notes |
-|----------|---------|-------|
-| `OWI_CUDA_VER` | `cu124` | PyTorch CUDA version index (e.g., `cu124` for CUDA 12.4, `cu130` for CUDA 13.0). CUDA drivers are backward-compatible, so `cu124` works on CUDA 13.x hosts. |
-
-> **Insight:** Do NOT use the GPU override on Mac or non-CUDA hosts — it will fail because the `nvidia` device driver isn't available. The main `docker-compose.next.yaml` defaults to CPU-only and is safe for all platforms.
-
-### 4.4 — Windows-Specific Notes
-
-On Windows with Docker Desktop:
-- Ensure the WSL2 backend is running.
-- `host.docker.internal` is available for services that need to reach the host (macOS/Windows only — does NOT work on Linux; use Docker service names instead).
-- Volume mounts work with WSL2 paths. If the repo is on a Windows drive (e.g., `C:\`), use the Docker Desktop file sharing settings to allow that drive.
-
----
-
-## Phase 5: Verify the Deployment
-
-### 5.1 — Check Container Status
-
-```bash
-cd
-docker compose -f docker-compose.next.yaml ps
-```
-
-All services should show `Up` and `healthy`:
-
-| Container | Expected Status |
-|-----------|----------------|
-| `lamb-lamb-1` | Up (healthy) |
-| `lamb-kb-1` | Up (healthy) |
-| `lamb-openwebui-1` | Up (healthy) |
-| `lamb-ollama-1` | Up (only if Ollama profile enabled) |
-
-### 5.2 — Check Logs
-
-```bash
-docker compose -f docker-compose.next.yaml logs -f --tail=50
-```
-
-Look for:
-- Lamb: `Uvicorn running on http://0.0.0.0:9099`
-- OpenWebUI: `Uvicorn running on http://0.0.0.0:8080`
-- KB: `Uvicorn running on http://0.0.0.0:9090`
-
-### 5.3 — Verify Services Are Reachable
-
-Open these URLs in a browser:
-
-| Service | URL | What You Should See |
-|---------|-----|---------------------|
-| LAMB Creator Interface | `http://localhost:9099` | LAMB sign-in / creator page |
-| OpenWebUI Chat | `http://localhost:8080` | OpenWebUI sign-in page |
-| KB Server API | `http://localhost:9090/docs` | FastAPI Swagger docs |
-| LAMB API Docs | `http://localhost:9099/docs` | FastAPI Swagger docs |
-
-### 5.4 — Verify Health Endpoints
-
-```bash
-curl -s http://localhost:8080/health
-curl -s http://localhost:9099/health
-curl -s http://localhost:9090/health
-```
-
-All should return HTTP 200 or similar success response.
-
-### 5.5 — Verify OpenWebUI Redirect URL
-
-After the stack is up, verify the OpenWebUI redirect uses the browser-accessible URL:
-
-```bash
-# Test login and check the launch_url field
-curl -s -X POST http://localhost:9099/creator/login \
- -d "email=${OWI_ADMIN_EMAIL}&password=${OWI_ADMIN_PASSWORD}" \
- | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('launch_url','MISSING'))"
-```
-
-The `launch_url` should start with `http://localhost:8080/`, NOT `http://openwebui:8080/`. If it shows the internal hostname, `OWI_PUBLIC_BASE_URL` is missing or not being passed to the `lamb` container — add it to both `.env` and `docker-compose.next.yaml` (see Phase 3.2).
-
-### 5.6 — Pull Ollama Models (if using Ollama profile)
-
-If the Ollama profile is enabled, pull the embedding model and optionally a chat model:
-
-```bash
-# Pull the embedding model (required for KB)
-docker exec lamb-ollama-1 ollama pull nomic-embed-text
-
-# Optional: pull a chat model for local inference
-docker exec lamb-ollama-1 ollama pull llama3.2
-```
-
-> **Insight:** The `nomic-embed-text` model is required for KB embeddings if `EMBEDDINGS_VENDOR=ollama`. Without it, KB ingestion will fail.
-
----
-
-## Phase 6: Day-to-Day Operations (Cheatsheet)
-
-### Stop the stack
-```bash
-cd && docker compose -f docker-compose.next.yaml down
-```
-
-### Start the stack
-```bash
-cd && docker compose -f docker-compose.next.yaml up -d
-```
-
-### Update images and restart
-```bash
-cd && docker compose -f docker-compose.next.yaml pull && docker compose -f docker-compose.next.yaml up -d
-```
-
-### View logs for a specific service
-```bash
-cd && docker compose -f docker-compose.next.yaml logs -f lamb
-```
-
-### Restart a single service
-```bash
-cd && docker compose -f docker-compose.next.yaml restart lamb
-```
-
-### Rebuild after code changes (when using local build)
-```bash
-cd && docker compose -f docker-compose.next.yaml up -d --build
-```
-
-### Rebuild with GPU support (CUDA hosts only)
-```bash
-cd && docker compose -f docker-compose.next.yaml -f docker-compose.next.gpu.yaml up -d --build
-```
-
-### Reset all data (WARNING: deletes all databases and uploaded files)
-```bash
-cd && docker compose -f docker-compose.next.yaml down -v
-```
-
-### Access a service shell
-```bash
-docker exec -it lamb-lamb-1 bash
-docker exec -it lamb-kb-1 bash
-docker exec -it lamb-openwebui-1 bash
-```
-
----
-
-## Phase 7: Tear Down
-
-### Stop and remove containers, networks
-```bash
-cd && docker compose -f docker-compose.next.yaml down
-```
-
-### Also remove volumes (deletes ALL data)
-```bash
-cd && docker compose -f docker-compose.next.yaml down -v
-```
-
-### Remove images to free disk space
-```bash
-docker rmi ghcr.io/lamb-project/lamb:latest
-docker rmi ghcr.io/lamb-project/lamb-kb:latest
-docker rmi ghcr.io/lamb-project/openwebui:latest
-docker rmi ollama/ollama:latest # if Ollama was used
-```
-
-### Remove the repo
-```bash
-sudo rm -rf
-```
-
----
-
-## Appendix A: Complete Local `.env` Template
-
-```bash
-# ============================================================================
-# LAMB NEXT — Local Development .env
-# ============================================================================
-
-# --- Required ---
-LAMB_WEB_HOST=http://localhost:9099
-LAMB_BACKEND_HOST=http://lamb:9099
-LAMB_BEARER_TOKEN=
-LAMB_DB_PATH=/data/lamb
-OWI_BASE_URL=http://openwebui:8080
-OWI_PUBLIC_BASE_URL=http://localhost:8080
-OWI_PATH=/data/openwebui
-OPENAI_BASE_URL=https://api.openai.com/v1
-OPENAI_MODEL=gpt-4o-mini
-OPENAI_API_KEY=sk-...
-SIGNUP_SECRET_KEY=
-OWI_ADMIN_NAME=Admin
-OWI_ADMIN_EMAIL=admin@localhost.local
-OWI_ADMIN_PASSWORD=
-LAMB_LIBRARY_TOKEN=
-OLLAMA_BASE_URL=http://ollama:11434
-
-# --- KB Embeddings (Ollama — no API key needed) ---
-# EMBEDDINGS_VENDOR=ollama
-# EMBEDDINGS_MODEL=nomic-embed-text
-# EMBEDDINGS_APIKEY=
-# EMBEDDINGS_ENDPOINT=http://ollama:11434
-
-# --- KB Embeddings ---
-EMBEDDINGS_VENDOR=openai
-EMBEDDINGS_MODEL=text-embedding-3-large
-EMBEDDINGS_APIKEY=sk-...
-EMBEDDINGS_ENDPOINT=https://api.openai.com/v1
-
-# --- Optional / Tweaks ---
-OPENAI_MODELS=gpt-4o-mini,gpt-4o
-LAMB_KB_SERVER=http://kb:9090
-LAMB_KB_SERVER_TOKEN=0p3n-w3bu!
-LTI_SECRET=lamb-lti-secret-key-2024
-SIGNUP_ENABLED=true
-DEV_MODE=true
-GLOBAL_LOG_LEVEL=WARNING
-WEBUI_SECRET_KEY=
-
-# --- Ports (only if overriding defaults) ---
-# LAMB_PORT=9099
-# KB_PORT=9090
-# OPENWEBUI_PORT=8080
-
-# --- CUDA / GPU (DGX / NVIDIA GPU hosts only) ---
-# OWI_USE_CUDA=true
-# OWI_CUDA_VER=cu124
-```
-
----
-
-## Appendix B: Architecture Reference (Local)
-
-| Service | Internal Port | Local URL | Docker Image |
-|---------|--------------|-----------|--------------|
-| `lamb` | 9099 | `http://localhost:9099` | `ghcr.io/lamb-project/lamb:latest` |
-| `kb` | 9090 | `http://localhost:9090` | `ghcr.io/lamb-project/lamb-kb:latest` |
-| `openwebui` | 8080 | `http://localhost:8080` | `ghcr.io/lamb-project/openwebui:latest` |
-| `ollama` (optional) | 11434 | `http://localhost:11434` | `ollama/ollama:latest` |
-
-**Service dependencies:**
-- `lamb` depends on `kb` (service_started) and `openwebui` (service_healthy)
-- `kb` is independent
-- `openwebui` is independent
-- `ollama` is independent (used by `kb` for embeddings and by `lamb` for chat if configured)
-
-**Docker compose files:**
-- `docker-compose.next.yaml` — Main stack (CPU-only by default, safe for all platforms)
-- `docker-compose.next.gpu.yaml` — GPU override (CUDA hosts only; enables CUDA PyTorch build + `--gpus=all` for `openwebui` and `ollama`)
-
-**Docker volumes:**
-- `lamb-data` — LAMB database and uploads
-- `kb-data` — KB server database and vector store
-- `kb-static` — KB server static files
-- `openwebui-data` — OpenWebUI database and configuration
-- `ollama-data` — Ollama models (only with Ollama profile)
-
----
-
-## Appendix C: Known Gotchas
-
-1. **Docker Compose version:** The `docker compose` (V2, space, no hyphen) command is required. The old `docker-compose` (V1) won't work with the compose file syntax.
-
-2. **`.env` file location:** Must be in the same directory as `docker-compose.next.yaml` (the repo root). Docker Compose reads it automatically when running from that directory.
-
-3. **Required variable failures:** Missing `?Set VARNAME` variables cause `docker compose` to fail with a clear error message. This is a fast-fail mechanism — the agent should double-check all required vars before running compose.
-
-4. **OpenWebUI startup race:** The `lamb` service has `depends_on: openwebui: condition: service_healthy`. OpenWebUI may take 30-60 seconds on first boot to initialize its database. The healthcheck prevents the race.
-
-5. **Ollama model pull:** When using the Ollama profile, models are NOT included in the image. The user must pull them manually with `docker exec lamb-ollama-1 ollama pull `. The embedding model (`nomic-embed-text`) is required for KB ingestion.
-
-6. **Port conflicts:** If ports 9099, 9090, 8080, or 11434 are already in use, the stack will fail to start. Check with `lsof -i :PORT` before launching.
-
-7. **Memory usage:** Running all services (lamb + kb + openwebui) uses ~2-3 GB RAM. Adding Ollama with models adds 1-4 GB more depending on the models loaded.
-
-8. **`DEV_MODE=true` implications:** When `DEV_MODE=true`, the backend exposes additional debug/admin endpoints and may use development-oriented settings. This is appropriate for local development.
-
-9. **Docker Desktop on Windows/Mac:** If using Docker Desktop, ensure the file sharing settings include the drive where the repo is cloned. WSL2 backend is recommended for Windows.
-
-10. **No TLS locally:** The local deployment uses plain HTTP on localhost. Do not set `LAMB_WEB_HOST` to an `https://` URL — it won't work without Caddy and TLS certificates.
-
-11. **`host.docker.internal` does NOT work on Linux:** This hostname is a Docker Desktop feature available on macOS and Windows only. On Linux, containers reach each other via the Docker service name (e.g., `http://ollama:11434`) or the bridge gateway IP (`172.17.0.1` for the host). The `config.py` default for `OLLAMA_BASE_URL` is `http://host.docker.internal:11434`, which will fail silently on Linux. Always set `OLLAMA_BASE_URL=http://ollama:11434` when using the Ollama profile on Linux, both in `.env` and the compose file's `lamb` environment section.
-
-12. **OpenWebUI redirect breaks without `OWI_PUBLIC_BASE_URL`:** If the agent forgets to set `OWI_PUBLIC_BASE_URL=http://localhost:8080`, the LAMB frontend's OpenWebUI menu item will redirect to `http://openwebui:8080/...` — an internal Docker hostname that browsers can't resolve. Always include this variable in the `.env` and verify the login `launch_url` points to `localhost` (see Phase 5.6).
-
-13. **`LAMB_LIBRARY_TOKEN` is required:** The `docker-compose.next.yaml` uses `${LAMB_LIBRARY_TOKEN?Set LAMB_LIBRARY_TOKEN}` for the `library-manager` service. Omitting it causes `docker compose up` to fail. Always auto-generate this token.
-
-14. **Schema migration can fail silently on first boot:** The backend runs database migrations and then immediately tries to use the migrated columns. On the first startup with an existing database, you may see errors like `no such column: password_hash` in the logs. These are one-time — the migration adds the columns, and subsequent restarts are clean. If the error persists across multiple restarts, the migration itself may be failing; check `docker logs lamb-lamb-1` for `Migration error` messages.
-
-15. **OpenWebUI build may run out of memory:** The OpenWebUI Dockerfile bundles heavy dependencies (`pyodide`, `onnxruntime-web`) that can exceed Node.js's default ~2 GB heap during `npm run build`, causing `JavaScript heap out of memory`. LAMB's vendored `open-webui/Dockerfile` includes `ENV NODE_OPTIONS=--max-old-space-size=4096` to prevent this. If you still hit this error, increase Docker Desktop's memory allocation (Settings → Resources → Memory → 8 GB or more).
-
-16. **GPU override is platform-specific:** `docker-compose.next.gpu.yaml` uses `driver: nvidia` for GPU access. This only works on hosts with NVIDIA GPUs and the NVIDIA Container Toolkit installed. On Mac, Windows (non-WSL2 with GPU), or CPU-only Linux, omit this file from the compose command. The main `docker-compose.next.yaml` is safe for all platforms.
-
----
-
-## Appendix D: Agent Workflow Checklist
-
-The agent should follow this sequence and check off each step:
-
-- [ ] **0.1** — Ask operating system and (if Windows) PowerShell vs WSL
-- [ ] **0.2** — Ask install location, branch, existing data (and old project path if applicable)
-- [ ] **0.3** — Ask API keys, model configuration, embeddings vendor
-- [ ] **0.4** — Ask feature toggles (signup, dev mode, Ollama), generate secrets
-- [ ] **0.5** — Confirm port configuration
-- [ ] **1.1** — Verify Docker and Docker Compose are installed
-- [ ] **1.2** — Check available disk space
-- [ ] **1.3** — Check port availability
-- [ ] **2.1** — Create install directory
-- [ ] **2.2** — Clone repository (and checkout branch if needed)
-- [ ] **3** — Create `.env` file with all collected variables
-- [ ] **3.5** — (If existing data) Stop old stack, create volumes, copy LAMB DB, OWI data, KB data, verify
-- [ ] **3.6** — (If DGX/CUDA host) Note that `docker-compose.next.gpu.yaml` is available for GPU acceleration
-- [ ] **4** — Run `docker compose -f docker-compose.next.yaml up -d` (with `--profile ollama` and/or `-f docker-compose.next.gpu.yaml` if needed)
-- [ ] **5.1** — Verify all containers are Up and healthy
-- [ ] **5.2** — Check logs for startup errors
-- [ ] **5.3** — Report access URLs to user
-- [ ] **5.4** — Verify health endpoints
-- [ ] **5.5** — Verify OpenWebUI redirect URL uses `localhost`, not internal Docker hostname
-- [ ] **5.6** — Pull Ollama models if using Ollama profile
-
----
-
-## Appendix E: Quick Start (Minimal Path)
-
-For users who want to skip the interactive questions and go fast, the agent can use these defaults:
-
-```bash
-# Clone
-git clone https://github.com/Lamb-Project/lamb.git /opt/lamb
-cd /opt/lamb
-
-# Generate secrets
-LAMB_BEARER_TOKEN=$(openssl rand -hex 32)
-SIGNUP_SECRET_KEY=$(openssl rand -hex 32)
-LIBRARY_TOKEN=$(openssl rand -hex 32)
-ADMIN_PASSWORD=$(openssl rand -hex 16)
-
-# Write .env
-cat > .env << ENVEOF
-LAMB_WEB_HOST=http://localhost:9099
-LAMB_BACKEND_HOST=http://lamb:9099
-LAMB_BEARER_TOKEN=$LAMB_BEARER_TOKEN
-LAMB_DB_PATH=/data/lamb
-OWI_BASE_URL=http://openwebui:8080
-OWI_PUBLIC_BASE_URL=http://localhost:8080
-OWI_PATH=/data/openwebui
-OPENAI_BASE_URL=https://api.openai.com/v1
-OPENAI_MODEL=gpt-4o-mini
-OPENAI_API_KEY=sk-REPLACE_ME
-SIGNUP_SECRET_KEY=$SIGNUP_SECRET_KEY
-OWI_ADMIN_NAME=Admin
-OWI_ADMIN_EMAIL=admin@localhost.local
-OWI_ADMIN_PASSWORD=$ADMIN_PASSWORD
-LAMB_LIBRARY_TOKEN=$LIBRARY_TOKEN
-OLLAMA_BASE_URL=http://ollama:11434
-EMBEDDINGS_VENDOR=ollama
-EMBEDDINGS_MODEL=nomic-embed-text
-EMBEDDINGS_APIKEY=
-EMBEDDINGS_ENDPOINT=http://ollama:11434
-OPENAI_MODELS=gpt-4o-mini,gpt-4o
-SIGNUP_ENABLED=true
-DEV_MODE=true
-ENVEOF
-
-# Launch (with Ollama for embeddings)
-docker compose -f docker-compose.next.yaml --profile ollama up -d
-
-# Pull the embedding model
-docker exec lamb-ollama-1 ollama pull nomic-embed-text
-```
-
-> **Note:** The user MUST replace `sk-REPLACE_ME` with a real OpenAI API key, or configure their preferred LLM provider. If they want to skip Ollama entirely, they must set `EMBEDDINGS_VENDOR=openai` and provide both `EMBEDDINGS_APIKEY` and `EMBEDDINGS_ENDPOINT`.
-
-> **GPU hosts (DGX / NVIDIA):** Add `-f docker-compose.next.gpu.yaml` to the `docker compose` command above for CUDA-accelerated PyTorch and GPU access.
diff --git a/Documentation/deployNext.md b/Documentation/deployNext.md
deleted file mode 100644
index 193f04399..000000000
--- a/Documentation/deployNext.md
+++ /dev/null
@@ -1,571 +0,0 @@
-# LAMB NEXT — Autonomous Deployment Guide
-
-This document is designed to be fed to an AI coding agent (like GitHub Copilot) that will autonomously deploy LAMB NEXT to a Hetzner Cloud server in production.
-
-**Assumptions:**
-- DNS is already configured (the agent should ask for domain/subdomain names, not set them up).
-- Hetzner `hcloud` CLI is installed and authenticated on the local machine.
-- An SSH public key is registered in Hetzner (the agent can check and add one if needed).
-- The agent has access to the LAMB source repository.
-
----
-
-## Phase 0: Gather Information (via `askQuestions`)
-
-Before touching any infrastructure, the agent MUST collect these from the user using the `vscode_askQuestions` tool. Do NOT proceed until all answers are received.
-
-### 0.1 — Server Configuration
-
-| Question | Purpose | Options / Notes |
-|----------|---------|-----------------|
-| Server type | vCPU/RAM/disk sizing | `cpx22` (2 vCPU, 4 GB, 80 GB) for tests without Ollama; `cpx32` (4 vCPU, 8 GB, 160 GB) for small production; `cpx62` (16 vCPU, 32 GB, 640 GB) for production with Ollama |
-| Server location | Datacenter | `hel1` (Helsinki), `nbg1` (Nuremberg), `ash` (Ashburn US) |
-| SSH keys | Which hcloud SSH keys to add | e.g., `my-macbook`, `teammate-key` (can select multiple) |
-| Server name | Label for the instance | e.g., `lamb-prod-01` |
-
-### 0.2 — Domain Configuration
-
-| Question | Purpose | Example |
-|----------|---------|---------|
-| Main LAMB domain | `LAMB_PUBLIC_HOST` / `LAMB_WEB_HOST` | `lamb.example.com` |
-| OpenWebUI domain | `OWI_PUBLIC_HOST` | `owi.lamb.example.com` |
-| ACME email | `CADDY_EMAIL` for Let's Encrypt | `admin@example.com` |
-
-### 0.3 — API Keys & Secrets
-
-| Question | Purpose | Default / Fallback |
-|----------|---------|---------------------|
-| OpenAI API key | `OPENAI_API_KEY` and `EMBEDDINGS_APIKEY` | Required unless using Ollama |
-| OpenAI base URL | `OPENAI_BASE_URL` | `https://api.openai.com/v1` |
-| OpenAI model | `OPENAI_MODEL` | `gpt-4o-mini` (the user may have a custom model name) |
-| OpenAI model list | `OPENAI_MODELS` | Comma-separated, e.g., `gpt-4o-mini,gpt-4o` |
-| Embeddings vendor | `EMBEDDINGS_VENDOR` | `openai` or `ollama` |
-| Embeddings model | `EMBEDDINGS_MODEL` | `text-embedding-3-large` (OpenAI) or `nomic-embed-text` (Ollama) |
-| Embeddings endpoint | `EMBEDDINGS_ENDPOINT` | `https://api.openai.com/v1` or `http://ollama:11434` |
-| LAMB bearer token | `LAMB_BEARER_TOKEN` | Should be a strong random string in production |
-| Signup secret key | `SIGNUP_SECRET_KEY` | Strong random string in production |
-| LTI secret | `LTI_SECRET` | Override in production |
-| WebUI secret key | `WEBUI_SECRET_KEY` | Override in production |
-| OWI admin name/email/password | Bootstrap admin account for OpenWebUI | e.g., `Admin User` / `admin@example.com` / strong password |
-
-> **IMPORTANT:** If the user provides an existing `backend/.env` file or KB server `.env` file, extract keys from those files as defaults, but always confirm via `askQuestions`. API keys found in existing `.env` files should be offered as pre-filled defaults in the questions.
-
-### 0.4 — Feature Toggles
-
-| Question | Purpose | Default |
-|----------|---------|---------|
-| Enable signup? | `SIGNUP_ENABLED` | `true` |
-| Enable dev mode? | `DEV_MODE` | `true` for test, `false` for production |
-| Enable Ollama? | Adds `--profile ollama` to compose | `false` |
-| Existing data? | Is this server replacing an old LAMB installation with data to migrate? | `yes` or `no`. If yes, see Phase 4.6. |
-
----
-
-## Phase 1: Create the Hetzner Server
-
-### 1.1 — Check SSH Keys
-
-```bash
-hcloud ssh-key list
-```
-
-If no key matches the local machine, add it:
-
-```bash
-# List local public keys
-ls ~/.ssh/*.pub
-
-# Add to Hetzner
-hcloud ssh-key create --name "my-key-name" --public-key-from-file ~/.ssh/id_ed25519.pub
-```
-
-> **Insight:** Use MD5 fingerprints to match local keys to hcloud keys: `ssh-keygen -lf ~/.ssh/id_ed25519.pub -E md5`
-
-### 1.2 — Check Available Server Types
-
-```bash
-hcloud server-type list
-```
-
-### 1.3 — Create the Server
-
-```bash
-hcloud server create \
- --name \
- --type \
- --image ubuntu-24.04 \
- --location \
- --ssh-key \
- --ssh-key \
- --label project=lamb \
- --label environment=production
-```
-
-> **Insight:** Use `--ssh-key` multiple times to add more than one SSH key (e.g., the agent's own key + a teammate's key). All specified keys will be added to `/root/.ssh/authorized_keys` on the server. When the `lamb` user is created in Phase 2.2, those same keys are copied to the `lamb` user's authorized_keys.
-
-Note the IPv4 address from the output. The agent should report it to the user.
-
----
-
-## Phase 2: Install Dependencies on the Server
-
-SSH as root:
-
-```bash
-ssh root@
-```
-
-### 2.1 — Install Docker + Docker Compose
-
-```bash
-apt-get update -qq && \
-apt-get install -y -qq curl && \
-curl -fsSL https://get.docker.com | sh
-```
-
-Verify:
-
-```bash
-docker --version
-docker compose version
-```
-
-Expected: Docker 29+ and Docker Compose v5+.
-
-### 2.2 — Create `lamb` User
-
-Create a non-root user with Docker access and passwordless sudo:
-
-```bash
-useradd -m -s /bin/bash lamb && \
-usermod -aG docker,sudo lamb && \
-mkdir -p /home/lamb/.ssh && \
-cp /root/.ssh/authorized_keys /home/lamb/.ssh/ && \
-chown -R lamb:lamb /home/lamb/.ssh && \
-echo "lamb ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/lamb
-```
-
-Verify:
-
-```bash
-id lamb # should show uid, gid, groups=lamb,sudo,docker
-groups lamb # should include docker and sudo
-```
-
-Test Docker access as `lamb`:
-
-```bash
-su - lamb -c "docker ps"
-```
-
-> **Insight:** All subsequent steps (clone, env setup, compose) should be done as the `lamb` user, NOT root.
-
----
-
-## Phase 3: Clone the Repository
-
-SSH as `lamb`:
-
-```bash
-ssh lamb@
-```
-
-Clone into `/opt/lamb`:
-
-```bash
-sudo mkdir -p /opt/lamb
-sudo chown lamb:lamb /opt/lamb
-git clone https://github.com/Lamb-Project/lamb.git /opt/lamb
-```
-
-> **Insight:** If the repo is private or the user wants a specific branch, ask. Default is `main` from the public repo.
-
----
-
-## Phase 4: Create the `.env` File
-
-The `.env` file lives at `/opt/lamb/.env` (same directory as `docker-compose.next.yaml`).
-
-### 4.1 — Required Variables (compose fails if missing)
-
-These MUST be set. The compose file uses `${VAR?error message}` syntax, so missing vars cause immediate failure.
-
-| Variable | Affected Service | Example Value |
-|----------|-----------------|---------------|
-| `LAMB_WEB_HOST` | lamb | `https://lamb.example.com` |
-| `LAMB_BACKEND_HOST` | lamb | `http://lamb:9099` (Docker service name, not domain) |
-| `LAMB_BEARER_TOKEN` | lamb | Strong random string |
-| `LAMB_DB_PATH` | lamb | `/data/lamb` (path INSIDE container) |
-| `OWI_BASE_URL` | lamb | `http://openwebui:8080` (Docker service name) |
-| `OWI_PATH` | lamb | `/data/openwebui` (path INSIDE container) |
-| `OPENAI_BASE_URL` | lamb | `https://api.openai.com/v1` |
-| `OPENAI_MODEL` | lamb | `gpt-4o-mini` (or user's custom model) |
-| `SIGNUP_SECRET_KEY` | lamb | Strong random string |
-| `OWI_ADMIN_NAME` | lamb | `Admin User` |
-| `OWI_ADMIN_EMAIL` | lamb | `admin@example.com` |
-| `OWI_ADMIN_PASSWORD` | lamb | Strong password |
-
-### 4.2 — Caddy/TLS Variables (required for production overlay)
-
-| Variable | Affected Service | Example Value |
-|----------|-----------------|---------------|
-| `CADDY_EMAIL` | caddy | `admin@example.com` |
-| `LAMB_PUBLIC_HOST` | caddy | `lamb.example.com` |
-| `OWI_PUBLIC_HOST` | caddy | `owi.lamb.example.com` |
-
-> **Insight:** `LAMB_PUBLIC_HOST` and `LAMB_WEB_HOST` are often the same hostname, but the former is used by Caddy for TLS routing and the latter by LAMB backend for redirect/callback URLs. The agent should ask for them separately but note they're typically the same value (one with `https://`, one without).
-
-### 4.3 — KB Embeddings Variables
-
-| Variable | Affected Service | Example (OpenAI) | Example (Ollama) |
-|----------|-----------------|-------------------|-------------------|
-| `EMBEDDINGS_VENDOR` | kb | `openai` | `ollama` |
-| `EMBEDDINGS_MODEL` | kb | `text-embedding-3-large` | `nomic-embed-text` |
-| `EMBEDDINGS_APIKEY` | kb | Same as `OPENAI_API_KEY` | (empty or Ollama key) |
-| `EMBEDDINGS_ENDPOINT` | kb | `https://api.openai.com/v1` | `http://ollama:11434` |
-
-### 4.4 — Optional but Recommended
-
-```bash
-OPENAI_API_KEY=sk-... # if using OpenAI
-OPENAI_MODELS=gpt-4o-mini,gpt-4o
-LAMB_KB_SERVER=http://kb:9090 # default, can omit
-LAMB_KB_SERVER_TOKEN=change-me # ⚠️ MUST match KB server's LAMB_API_KEY below!
-LAMB_API_KEY=change-me # ⚠️ MUST match LAMB_KB_SERVER_TOKEN above!
-LTI_SECRET=change-me
-SIGNUP_ENABLED=true
-DEV_MODE=false # false for production
-GLOBAL_LOG_LEVEL=WARNING
-WEBUI_SECRET_KEY=change-me # important for production sessions
-```
-
-> **🔴 CRITICAL: `LAMB_KB_SERVER_TOKEN` and `LAMB_API_KEY` must be the SAME value.**
-> `LAMB_KB_SERVER_TOKEN` is used by the LAMB backend to authenticate with the KB server.
-> `LAMB_API_KEY` is used by the KB server to validate incoming requests.
-> If they don't match, KB creation (and all write operations) will fail with a misleading 401 error that logs the user out. See Appendix C, Gotcha #10 for the full diagnosis.
-> **In docker-compose.next.yaml, both default to `0p3n-w3bu!` — if you override one, you MUST override the other.**
-
-### 4.5 — Write the File
-
-The agent should construct the `.env` from the collected answers and write it to the server:
-
-```bash
-cat > /opt/lamb/.env << 'ENVEOF'
-# ... all variables ...
-ENVEOF
-```
-
-> **Insight:** Use single-quoted `'ENVEOF'` (heredoc delimiter) to prevent shell variable expansion inside the file content. This is critical when the values contain `$` signs or backticks.
-
-### 4.6 — Key Insights for the Agent
-
-- **`LAMB_BACKEND_HOST`** is an INTERNAL Docker network URL (`http://lamb:9099`), NOT the public domain. The `lamb` part is the Docker Compose service name.
-- **`OWI_BASE_URL`** likewise uses the Docker service name: `http://openwebui:8080`.
-- **`LAMB_DB_PATH`** and **`OWI_PATH`** are paths INSIDE the container, not on the host. The defaults (`/data/lamb`, `/data/openwebui`) map to Docker named volumes.
-- **`OPENAI_MODEL`** — if the user's existing `.env` has a non-standard model name (e.g., `gpt-5-mini`), use it. Don't second-guess; the user may have a custom endpoint.
-- **API key reuse** — The user may want the same key for both `OPENAI_API_KEY` (lamb service) and `EMBEDDINGS_APIKEY` (kb service), or different keys. Ask explicitly.
-- **`DEV_MODE`** — Set to `false` for real production, `true` for test/staging instances.
-- **`LAMB_KB_SERVER_TOKEN` and `LAMB_API_KEY` must match** — These are the same shared secret seen from two sides: LAMB uses it to talk to the KB server, the KB server uses it to validate requests. The compose file defaults both to `0p3n-w3bu!`. If you change one, change the other. A mismatch causes KB create/update/delete to return 401, which triggers the frontend's session-expiry handler and logs the user out — even though their session is perfectly valid. The KB server's `/health` endpoint does NOT require auth (so health checks pass even with mismatched tokens), but `/collections` POST/PATCH/DELETE and all ingestion endpoints DO.
-
----
-
-## Phase 4.5: Verify DNS Resolution (GATE — DO NOT SKIP)
-
-> **CRITICAL:** This is a hard gate. If DNS does not resolve correctly, the stack WILL fail to obtain TLS certificates and the deployment will be broken. Do NOT proceed to Phase 5 until DNS checks pass.
-
-### 4.5.1 — What the agent knows by now
-
-At this point the agent has:
-- The server's **IPv4 address** (from Phase 1.3)
-- The **`LAMB_PUBLIC_HOST`** (e.g., `lamb.example.com`) — from Phase 0.2 / `.env`
-- The **`OWI_PUBLIC_HOST`** (e.g., `owi.lamb.example.com`) — from Phase 0.2 / `.env`
-
-### 4.5.2 — Check DNS resolution
-
-Run from the agent's local machine (not the server):
-
-```bash
-dig +short
-dig +short
-```
-
-Or equivalently:
-
-```bash
-host
-host
-```
-
-### 4.5.3 — Evaluate results
-
-| DNS result | Action |
-|------------|--------|
-| Both domains resolve to the server's IPv4 | ✅ Proceed to Phase 5 |
-| One or both domains resolve to a **different IP** | 🛑 **STOP.** Tell the user: *"The domain(s) `` currently resolve to ``, but the server is at ``. Please update the DNS A records for these domains before I can continue."* Offer to update DNS if the Namecheap skill or equivalent is available and the user has credentials. |
-| One or both domains return **NXDOMAIN** (no records) | 🛑 **STOP.** Tell the user: *"The subdomain(s) `` don't exist in DNS yet. Please create A records pointing to `` before I can continue."* Offer to help if tools are available. |
-| DNS returns **multiple A records** (load balancing / round-robin) | ⚠️ Warn the user. Multiple A records mean Caddy's TLS challenge may hit a different server. Proceed only if the user confirms all IPs point to the same server. |
-
-### 4.5.4 — If DNS is correct, double-check TTL
-
-If the user just updated DNS, check if the old records might still be cached:
-
-```bash
-dig +short @8.8.8.8
-```
-
-If Google's resolver (8.8.8.8) returns the new IP, propagation is complete. If it returns the old IP, wait and retry (TTL is typically 1800s — check with `dig ` for the exact TTL value).
-
-### 4.5.5 — DNS check example
-
-```bash
-# Server IP (from Phase 1)
-SERVER_IP="178.104.231.37"
-
-# Check both domains
-LAMB_IP=$(dig +short lamb.example.com)
-OWI_IP=$(dig +short owi.lamb.example.com)
-
-if [ "$LAMB_IP" != "$SERVER_IP" ]; then
- echo "FAIL: lamb.example.com → $LAMB_IP (expected $SERVER_IP)"
- exit 1
-fi
-if [ "$OWI_IP" != "$SERVER_IP" ]; then
- echo "FAIL: owi.lamb.example.com → $OWI_IP (expected $SERVER_IP)"
- exit 1
-fi
-echo "OK: Both domains resolve to $SERVER_IP"
-```
-
-> **Insight:** The agent MUST NOT proceed to Phase 5 until both domains resolve correctly. TLS certificates (Let's Encrypt via Caddy) require DNS to point to the server — without correct DNS, Caddy will fail the ACME challenge and the deployment will be unreachable over HTTPS (and may not start cleanly).
-
----
-
-## Phase 4.6: Migrate Existing Data (GATE — Conditional)
-
-> **Only execute this phase if the user answered `yes` to "Existing data?" in Phase 0.4. If this is a fresh server with no prior data, skip to Phase 5.**
-
-If the server previously ran LAMB (old `docker-compose.yaml` stack), the old data lives at `/opt/lamb/lamb_v4.db`, `/opt/lamb/open-webui/backend/data/`, and `/opt/lamb/lamb-kb-server-stable/backend/data/`. The new stack uses named Docker volumes instead — the data must be copied across before launch.
-
-Follow the step-by-step guide in **`Documentation/slop-docs/migrating-to-lamb-next.md`**. The migration covers:
-
-1. Stopping the old stack
-2. Creating named volumes with `docker compose up --no-start`
-3. Copying the LAMB database into `lamb-data`
-4. Copying OpenWebUI data into `openwebui-data`
-5. Copying KB server data into `kb-data`
-6. Verifying the copied data
-
-> **Insight:** The `.env` file must already exist (Phase 4) before running the migration. The old project path is the same `/opt/lamb` — the migration copies data from the host filesystem into the new named volumes. Original files are not modified.
-
----
-
-## Phase 5: Launch the Stack
-
-### 5.1 — Pull Images and Start (Production with TLS)
-
-```bash
-cd /opt/lamb
-docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml up -d
-```
-
-> **Note:** The first run downloads ~3-4 GB of Docker images. This may take several minutes. The agent should run this in async mode with a generous timeout (≥300s).
-
-### 5.2 — Without TLS (Test/Dev, port-based access)
-
-```bash
-docker compose -f docker-compose.next.yaml up -d
-```
-
-### 5.3 — With Ollama (local LLM inference)
-
-```bash
-docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml --profile ollama up -d
-```
-
----
-
-## Phase 6: Verify the Deployment
-
-### 6.1 — Check Container Status
-
-```bash
-cd /opt/lamb
-docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml ps
-```
-
-All services should show `Up` and `healthy`:
-
-| Container | Expected Status |
-|-----------|----------------|
-| `lamb-caddy-1` | Up (if using prod overlay) |
-| `lamb-lamb-1` | Up (healthy) |
-| `lamb-kb-1` | Up (healthy) |
-| `lamb-openwebui-1` | Up (healthy) |
-
-### 6.2 — Check Logs
-
-```bash
-docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml logs -f --tail=50
-```
-
-Look for:
-- Caddy: `serving initial configuration` (TLS certs obtained automatically on first HTTPS request)
-- Lamb: `Uvicorn running on http://0.0.0.0:9099`
-- OpenWebUI: `Uvicorn running on http://0.0.0.0:8080`
-- KB: `Uvicorn running on http://0.0.0.0:9090`
-
-### 6.3 — Verify HTTPS
-
-Caddy obtains Let's Encrypt certificates on the first HTTPS request. Until DNS propagates and a request hits the server, certificates won't be issued. This is normal.
-
-Once DNS resolves, visit:
-- `https://lamb.example.com` — should show the LAMB creator interface
-- `https://owi.lamb.example.com` — should show the OpenWebUI chat interface
-
----
-
-## Phase 7: Day-to-Day Operations (Cheatsheet)
-
-### Stop the stack
-```bash
-cd /opt/lamb && docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml down
-```
-
-### Update images and restart
-```bash
-cd /opt/lamb && docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml pull && docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml up -d
-```
-
-### View logs
-```bash
-cd /opt/lamb && docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml logs -f lamb
-```
-
-### Restart a single service
-```bash
-cd /opt/lamb && docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml restart lamb
-```
-
----
-
-## Phase 8: Tear Down
-
-### Delete the server
-```bash
-hcloud server delete
-```
-
-### Delete by label (bulk cleanup)
-```bash
-hcloud server delete --selector project=lamb
-hcloud server delete --selector temporary=true
-```
-
----
-
-## Appendix A: Complete `.env` Template
-
-```bash
-# ============================================================================
-# LAMB NEXT — Production .env
-# ============================================================================
-
-# --- Required ---
-LAMB_WEB_HOST=https://lamb.example.com
-LAMB_BACKEND_HOST=http://lamb:9099
-LAMB_BEARER_TOKEN=
-LAMB_DB_PATH=/data/lamb
-OWI_BASE_URL=http://openwebui:8080
-OWI_PATH=/data/openwebui
-OPENAI_BASE_URL=https://api.openai.com/v1
-OPENAI_MODEL=gpt-4o-mini
-OPENAI_API_KEY=sk-...
-SIGNUP_SECRET_KEY=
-OWI_ADMIN_NAME=Admin User
-OWI_ADMIN_EMAIL=admin@example.com
-OWI_ADMIN_PASSWORD=
-
-# --- Caddy / TLS (production overlay) ---
-CADDY_EMAIL=admin@example.com
-LAMB_PUBLIC_HOST=lamb.example.com
-OWI_PUBLIC_HOST=owi.lamb.example.com
-
-# --- KB Embeddings ---
-EMBEDDINGS_VENDOR=openai
-EMBEDDINGS_MODEL=text-embedding-3-large
-EMBEDDINGS_APIKEY=sk-...
-EMBEDDINGS_ENDPOINT=https://api.openai.com/v1
-
-# --- Optional / Tweaks ---
-OPENAI_MODELS=gpt-4o-mini,gpt-4o
-LAMB_KB_SERVER=http://kb:9090
-LAMB_KB_SERVER_TOKEN=change-me # ⚠️ MUST equal LAMB_API_KEY below
-LAMB_API_KEY=change-me # ⚠️ MUST equal LAMB_KB_SERVER_TOKEN above
-LTI_SECRET=change-me
-SIGNUP_ENABLED=true
-DEV_MODE=false
-GLOBAL_LOG_LEVEL=WARNING
-WEBUI_SECRET_KEY=change-me
-```
-
-## Appendix B: Architecture Reference
-
-| Service | Internal Port | Public URL (via Caddy) | Docker Image |
-|---------|--------------|------------------------|--------------|
-| `lamb` | 9099 | `https://lamb.example.com` | `ghcr.io/lamb-project/lamb:latest` |
-| `kb` | 9090 | `https://lamb.example.com/kb/*` (proxied by Caddy) | `ghcr.io/lamb-project/lamb-kb:latest` |
-| `openwebui` | 8080 | `https://owi.lamb.example.com` | `ghcr.io/lamb-project/openwebui:latest` |
-| `caddy` | 80, 443 | Routes all traffic | `caddy:2.8` |
-
-**Caddy routing (from `Caddyfile.next`):**
-- `lamb.example.com/creator/*` → `lamb:9099`
-- `lamb.example.com/api/*` → `lamb:9099`
-- `lamb.example.com/lamb/*` → `lamb:9099` (strip prefix)
-- `lamb.example.com/kb/*` → `kb:9090` (strip prefix)
-- `lamb.example.com/*` (everything else) → `lamb:9099` (SPA frontend)
-- `owi.lamb.example.com` → `openwebui:8080`
-- Old `/openwebui/*` paths on main domain → 301 redirect to OWI subdomain
-
-## Appendix C: Known Gotchas
-
-1. **Docker Compose version:** The `docker compose` (V2, no hyphen) command is required. The old `docker-compose` (V1) won't work. The Docker install script installs the Compose plugin by default.
-
-2. **`.env` file location:** Must be in the same directory as `docker-compose.next.yaml` (`/opt/lamb/.env`). Docker Compose reads it automatically when running from that directory.
-
-3. **Required variable failures:** Missing `?Set VARNAME` variables cause `docker compose` to fail with a clear error message. This is a fast-fail mechanism — the agent should double-check all required vars before running compose.
-
-4. **TLS certificate timing:** Caddy obtains Let's Encrypt certificates on the first HTTPS request, not at startup. Until DNS propagates and port 443 is reachable from the internet, certificates won't be issued. The agent should NOT panic if certificates aren't present immediately.
-
-5. **OpenWebUI startup race:** The `lamb` service has `depends_on: openwebui: condition: service_healthy`. OpenWebUI may take 30-60 seconds on first boot to initialize its database. The healthcheck prevents the race.
-
-6. **Memory constraints:** With 4 GB RAM (cpx22), running all 4 containers (lamb + kb + openwebui + caddy) leaves little headroom. If the user reports crashes, upgrade to cpx32 (8 GB) or disable unused services.
-
-7. **`DEV_MODE=true` implications:** When `DEV_MODE=true`, the backend exposes additional debug/admin endpoints. Set to `false` for any internet-facing production deployment.
-
-8. **SSH into the server as `lamb`:** The `lamb` user has passwordless sudo and Docker access. For security, the agent should prefer using the `lamb` user for all operations after initial setup, avoiding root where possible.
-
-9. **DNS is a hard prerequisite for TLS:** Caddy requires DNS to be correctly pointed at the server before it can obtain Let's Encrypt certificates. Launching the stack with incorrect DNS will result in TLS failures. Always verify DNS resolution (Phase 4.5) before starting the stack. If using the production overlay (`docker-compose.next.prod.yaml`), missing or incorrect DNS will cause Caddy to fail the ACME challenge and the site will be unreachable over HTTPS.
-
-10. **`LAMB_KB_SERVER_TOKEN` and `LAMB_API_KEY` must match:** These are the same shared secret seen from two sides: LAMB uses it to talk to the KB server, the KB server uses it to validate requests. The compose file defaults both to `0p3n-w3bu!`. If you override one, you MUST override the other. A mismatch causes KB create/update/delete to return 401, which triggers the frontend's session-expiry handler and logs the user out — even though their session is perfectly valid. The KB server's `/health` endpoint does NOT require auth (so health checks pass even with mismatched tokens), but `/collections` POST/PATCH/DELETE and all ingestion endpoints DO.
-
-## Appendix D: Agent Workflow Checklist
-
-The agent should follow this sequence and check off each step:
-
-- [ ] **0.1** — Ask server type, location, name
-- [ ] **0.2** — Ask main domain, OWI domain, ACME email
-- [ ] **0.3** — Ask API keys, secrets, model names
-- [ ] **0.4** — Ask feature toggles (signup, dev mode, Ollama), existing data
-- [ ] **1.1** — Check/register SSH key in Hetzner
-- [ ] **1.3** — Create server, capture IPv4
-- [ ] **2.1** — SSH as root, install Docker + Compose
-- [ ] **2.2** — Create `lamb` user with Docker + sudo access
-- [ ] **3** — SSH as `lamb`, clone repo to `/opt/lamb`
-- [ ] **4** — Create `/opt/lamb/.env` with all collected variables
-- [ ] **4.5** — Verify DNS: both LAMB_PUBLIC_HOST and OWI_PUBLIC_HOST resolve to server IP (GATE — STOP if incorrect)
-- [ ] **4.6** — (If existing data) Migrate old data to named volumes following migration doc
-- [ ] **5** — Run `docker compose -f docker-compose.next.yaml -f docker-compose.next.prod.yaml up -d`
-- [ ] **6.1** — Verify all containers are Up and healthy
-- [ ] **6.2** — Check logs for startup errors
-- [ ] **6.3** — Report access URLs to user
-- [ ] **6.4** — Verify `LAMB_KB_SERVER_TOKEN` == `LAMB_API_KEY` across containers
diff --git a/Documentation/deployment.apache.md b/Documentation/deployment.apache.md
deleted file mode 100644
index d80689078..000000000
--- a/Documentation/deployment.apache.md
+++ /dev/null
@@ -1,170 +0,0 @@
-If you are already using Apache as a web server, you can deploy Lamb behind Apache using the following configuration. This setup uses Apache as a reverse proxy to forward requests to the appropriate services.
-
-This configuration assumes that you have two domains:
-- `lamb.lamb-project.org` for the main Lamb service
-- `openwebui.lamb-project.org` for the OpenWebUI service
-
-### Apache Configuration
-File: `/etc/apache2/sites-available/lamb.conf``
-
-```apache
-
- ServerName lamb.lamb-project.org
- ServerAdmin admin@lamb-project.org
-
- # Enable proxy modules
- # ProxyPreserveHost On
- # ProxyRequests Off
-
- # Proxy rules
- # ProxyPass / http://localhost:9099/
- # ProxyPassReverse / http://localhost:9099/
-
- RewriteEngine on
- RewriteCond %{SERVER_NAME} =lamb.lamb-project.org
- RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
-
- # Logging
- ErrorLog ${APACHE_LOG_DIR}/lamb_error.log
- CustomLog ${APACHE_LOG_DIR}/lamb_access.log combined
-
-
-```
-
-File: `/etc/apache2/sites-available/lamb-ssl.conf`
-```apache
-
-
- ServerName lamb.lamb-project.org
- ServerAdmin admin@lamb-project.org
-
- # Document root for static files
- DocumentRoot /opt/lamb/frontend/build
-
-
- Options -Indexes +FollowSymLinks
- AllowOverride None
- Require all granted
-
-
- # Enable proxy modules
- ProxyPreserveHost On
- ProxyRequests Off
-
- # Proxy /creator/* to backend
- ProxyPass /creator/ http://localhost:9099/creator/
- ProxyPassReverse /creator/ http://localhost:9099/creator/
-
- # Proxy /api/* to backend (with prefix stripping)
- ProxyPass /api/ http://localhost:9099/
- ProxyPassReverse /api/ http://localhost:9099/
-
- # Proxy /lamb/* to backend
- ProxyPass /lamb/ http://localhost:9099/lamb/
- ProxyPassReverse /lamb/ http://localhost:9099/lamb/
-
- # Proxy /kb/* to kb service (with prefix stripping)
- ProxyPass /kb/ http://localhost:9090/
- ProxyPassReverse /kb/ http://localhost:9090/
-
- # Redirect legacy /openwebui/* paths to new subdomain
- RewriteEngine On
- RewriteRule ^/openwebui/(.*)$ https://openwebui.lamb-project.org/$1 [R=301,L]
-
- # SPA fallback - serve index.html for all non-proxied, non-existent routes
- # Use explicit document root check
- RewriteCond /opt/lamb/frontend/build%{REQUEST_URI} !-f
- RewriteCond /opt/lamb/frontend/build%{REQUEST_URI} !-d
- RewriteCond %{REQUEST_URI} !^/creator/
- RewriteCond %{REQUEST_URI} !^/api/
- RewriteCond %{REQUEST_URI} !^/lamb/
- RewriteCond %{REQUEST_URI} !^/kb/
- RewriteRule ^ /index.html [L]
-
- # Logging
- ErrorLog ${APACHE_LOG_DIR}/lamb_error.log
- CustomLog ${APACHE_LOG_DIR}/lamb_access.log combined
- # LogLevel debug rewrite:trace6
-
- SSLCertificateFile /etc/letsencrypt/live/lamb.lamb-project.org/fullchain.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/lamb.lamb-project.org/privkey.pem
- Include /etc/letsencrypt/options-ssl-apache.conf
-
-
-```
-
-File: `/etc/apache2/sites-available/openwebui.conf`
-```apache
-
- ServerName openwebui.lamb-project.org
- ServerAlias www.openwebui.lamb-project.org
-
- # Redirect all HTTP to HTTPS
- RewriteEngine on
- RewriteCond %{SERVER_NAME} =openwebui.lamb-project.org [OR]
- RewriteCond %{SERVER_NAME} =www.openwebui.lamb-project.org
- RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
-
- ErrorLog ${APACHE_LOG_DIR}/openwebui_error.log
- LogLevel warn
-
-```
-
-File: `/etc/apache2/sites-available/openwebui-ssl.conf`
-```apache
-
-
- ServerName openwebui.lamb-project.org
- ServerAlias www.openwebui.lamb-project.org
-
- # Enable proxy modules
- ProxyRequests Off
- ProxyPreserveHost On
-
- # ProxyPass for HTTP
- ProxyPass / http://localhost:8080/
- ProxyPassReverse / http://localhost:8080/
-
- # WebSocket support
- RewriteEngine On
- RewriteCond %{HTTP:Upgrade} websocket [NC]
- RewriteCond %{HTTP:Connection} upgrade [NC]
- RewriteRule /(.*) ws://localhost:8080/$1 [P,L]
-
- # Proxy settings
-
- Require all granted
-
-
- # ErrorLog and LogLevel for debugging
- ErrorLog ${APACHE_LOG_DIR}/openwebui_error.log
- LogLevel warn
-
- SSLCertificateFile /etc/letsencrypt/live/openwebui.lamb-project.org/fullchain.pem
- SSLCertificateKeyFile /etc/letsencrypt/live/openwebui.lamb-project.org/privkey.pem
- Include /etc/letsencrypt/options-ssl-apache.conf
-
-
-```
-### Enabling the Configuration
-To enable the new site configurations and required modules, run the following commands:
-
-```bash
-sudo a2ensite lamb.conf
-sudo a2ensite lamb-ssl.conf
-sudo a2ensite openwebui.conf
-sudo a2ensite openwebui-ssl.conf
-sudo a2enmod proxy
-sudo a2enmod proxy_http
-sudo a2enmod proxy_wstunnel
-sudo a2enmod rewrite
-sudo a2enmod ssl
-```
-
-### Restart Apache
-After making these changes, restart Apache to apply the new configuration:
-
-```bash
-sudo systemctl restart apache2
-```
-
diff --git a/Documentation/deployment.md b/Documentation/deployment.md
deleted file mode 100644
index 865b29067..000000000
--- a/Documentation/deployment.md
+++ /dev/null
@@ -1,84 +0,0 @@
-
-# Deployment Guide
-
-## Base Containers
-
-The base LAMB setup will start the following containers:
-
-- Open WebUI API (port 8080)
-- LAMB KB server (port 9090)
-- LAMB Backend (port 9099)
-- Frontend Svelte dev server (port 5173)
-
-- Data persists via bind mounts under `/opt/lamb-project/lamb` (Open WebUI DB: `open-webui/backend/data/webui.db`, Chroma: `open-webui/backend/data/vector_db/chroma.sqlite3`).
-
-## How to Deploy
-
-For deployment, we use Docker Compose with a base configuration file (`docker-compose.yaml`) and a production override file (`docker-compose.prod.yaml`).
-
-The base file (`docker-compose.yaml`) defines all common services for the application.
-The production override file (`docker-compose.prod.yaml`) introduces the Caddy container, which acts as a reverse proxy and manages TLS certificates automatically.
-
-This approach avoids duplication: only production-specific changes are placed in the override file.
-
-To deploy in production:
-
-```bash
-docker compose -f docker-compose.yaml -f docker-compose.prod.yaml up -d
-```
-
-Wait **5-10 minutes** for all services to start and for Caddy to obtain TLS certificates.
-You can check the status of the containers with:
-
-```bash
-docker compose ps
-```
-To view logs for troubleshooting, use:
-
-```bash
-docker compose logs openwebui-build -f
-docker compose logs openwebui -f
-docker compose logs backend -f
-```
-
-### Environment Files and Configuration
-
-Before deploying, ensure you have:
-- Edited `backend/.env` to include a valid `OPENAI_API_KEY` (see `backend/.env.example` for a template).
-- Edited `lamb-kb-server-stable/backend/.env` as needed for your setup (see `lamb-kb-server-stable/backend/.env.example` for a template).
-- Copied and customized `frontend/svelte-app/static/config.js` from `config.js.sample`.
-
-This is an example of `config.js` for a production deployment:
-
-```javascript
-window.LAMB_CONFIG = {
- api: {
- baseUrl: 'https://lamb.yourdomain.com/creator',
- lambServer: 'https://lamb.yourdomain.com',
- openWebUiServer: 'https://lamb.yourdomain.com/openwebui',
- },
- assets: {
- path: '/static'
- },
- features: {
- enableOpenWebUi: true,
- enableDebugMode: true
- }
-};
-````
-
-### Caddy Configuration
-
-Caddy is configured using the `Caddyfile` in the project root. Before deploying, make sure to edit the `Caddyfile` and replace all instances of `yourdomain.com` with your actual domain name. This ensures that Caddy will correctly handle HTTPS certificates and routing for your deployment.
-
-The provided `Caddyfile` assumes that the main Lamb service will be available at `lamb.yourdomain.com`, and the OpenWebUI service will be available at its own subdomain: `owi.lamb.yourdomain.com`. Adjust these domain names as needed to match your setup.
-
-
-# Post installation Setup OpenWebUI
-
-OpenWebUI is the open source software Lamb uses as main chat client. We have several integrations between Lamb and OpenWebUI that need to work well for Lamb to work as intended. On the Lamb web interface you have a "OpenWebUI" button on the top right corner. By pressing this button you will be redirected to the OpenWebUI web interface logged as your current user. If you are the Lamb admin you will be admin on OpenWebUI as well.
-
-Go to Your user menu and get to the "Admin Panel", on the "Settings" tab, choose the "Connections" option on the side bar. Now you need to:
-* Edit the OpenAi connection and change the endpoint to **lamb-backend** on your system, and use as API KEY the value of LAMB_BEARER_TOKEN on backend/.env (that you should change for a secure key that no one knows). If you are using the Docker option it should look like this.
-
-
\ No newline at end of file
diff --git a/Documentation/deployment.nginx.md b/Documentation/deployment.nginx.md
deleted file mode 100644
index 024898204..000000000
--- a/Documentation/deployment.nginx.md
+++ /dev/null
@@ -1,154 +0,0 @@
-If you are already using nginx as a web server, you can deploy Lamb behind nginx using the following configuration. This setup uses nginx as a reverse proxy to forward requests to the appropriate services.
-
-This configuration assumes that you have two domains:
-- `lamb.lamb-project.org` for the main Lamb service
-- `openwebui.lamb-project.org` for the OpenWebUI service
-
-
-### nginx Configuration
-
-File: `lamb.conf``
-
-```nginx
-
-server {
- server_name lamb.lamb-project.org;
-
- # Logs
- access_log /var/log/nginx/lamb_access.log;
- error_log /var/log/nginx/lamb_error.log;
-
- # Document root para archivos estáticos del frontend
- root /opt/lamb/frontend/build;
- index index.html;
-
- # Proxy /creator/* al backend
- location /creator/ {
- proxy_pass http://127.0.0.1:9099/creator/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- # Proxy /api/* al backend (with prefix stripping)
- location /api/ {
- proxy_pass http://127.0.0.1:9099/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- # Proxy /lamb/* al backend
- location /lamb/ {
- proxy_pass http://127.0.0.1:9099/lamb/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- # Proxy /kb/* al servicio de Knowledge Base
- location /kb/ {
- proxy_pass http://127.0.0.1:9090/;
- proxy_http_version 1.1;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- }
-
- # Redirigir /openwebui/* al subdominio
- location /openwebui/ {
- return 301 http://openwebui-lamb.lamb-project.org$request_uri;
- }
-
- # SPA fallback - servir index.html para rutas del frontend
- location / {
- try_files $uri $uri/ /index.html;
- }
-
- listen [::]:443 ssl ipv6only=on; # managed by Certbot
- listen 443 ssl; # managed by Certbot
- ssl_certificate /etc/letsencrypt/live/lamb.lamb-project.org/fullchain.pem; # managed by Certbot
- ssl_certificate_key /etc/letsencrypt/live/lamb.lamb-project.org/privkey.pem; # managed by Certbot
- include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
-
-}
-server {
- if ($host = lamb.lamb-project.org) {
- return 301 https://$host$request_uri;
- } # managed by Certbot
-
-
- listen 80;
- listen [::]:80;
- server_name lamb.lamb-project.org;
- return 404; # managed by Certbot
-
-
-}
-
-```
-
-File: `openwebui.conf`
-
-```nginx
-
-server {
- server_name openwebui-lamb.lamb-project.org;
-
- # Logs
- access_log /var/log/nginx/openwebui_access.log;
- error_log /var/log/nginx/openwebui_error.log;
-
- location / {
- proxy_pass http://127.0.0.1:8080/;
- proxy_http_version 1.1;
-
- # Headers estándar
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
-
- # Soporte WebSocket
- proxy_set_header Upgrade $http_upgrade;
- proxy_set_header Connection "upgrade";
-
- # Timeout largo para WebSockets
- proxy_read_timeout 86400;
- proxy_send_timeout 86400;
- }
-
- listen [::]:443 ssl; # managed by Certbot
- listen 443 ssl; # managed by Certbot
- ssl_certificate /etc/letsencrypt/live/openwebui-lamb.lamb-project.org/fullchain.pem; # managed by Certbot
- ssl_certificate_key /etc/letsencrypt/live/openwebui-lamb.lamb-project.org/privkey.pem; # managed by Certbot
- include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
- ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
-
-}
-
-server {
- if ($host = openwebui-lamb.lamb-project.org) {
- return 301 https://$host$request_uri;
- } # managed by Certbot
-
-
- listen 80;
- listen [::]:80;
- server_name openwebui-lamb.lamb-project.org;
- return 404; # managed by Certbot
-
-
-}
-
-```
-
-
diff --git a/Documentation/installationguide.md b/Documentation/installationguide.md
deleted file mode 100644
index 85456d39f..000000000
--- a/Documentation/installationguide.md
+++ /dev/null
@@ -1,393 +0,0 @@
-# LAMB Installation Guide
-
-This guide provides a complete, step-by-step process for installing the LAMB application from scratch on a macOS-like environment. It is intended to be used by an automated agent.
-
-**Core Strategy:**
-The installation requires four separate, long-running terminal processes:
-
-1. **Open WebUI Server:** Runs on port `8080`.
-2. **LAMB Knowledge Base (KB) Server:** Runs on port `9090`.
-3. **Main LAMB Backend Server:** Runs on port `9099` (dev mode).
-4. **Frontend Svelte Dev Server:** Runs on port `5173`.
-
-The production server is running on a Ubuntu 22.04 server.
-You have access to the server via SSH (I have added my ssh key using ssh-add so you can forward it to the server using the -A flag). The sudo user is `ubuntu`.
-
-The installation guide is for a macOS-like environment but should work on any Linux distribution.
-
-If you ever need an OPENAI_API_KEY, you can get it running the following command:
-
-```bash
-llm keys get openai
-```
-
----
-
-### Step 1: Initial Directory Setup
-
-First, create and set permissions for the main project directory.
-
-```bash
-# Create the main directory with administrator privileges
-sudo mkdir -p $LAMB_PROJECT_PATH
-
-# Change ownership of the directory to the current user to avoid using sudo later
-sudo chown $(whoami) $LAMB_PROJECT_PATH
-
-# Navigate into the new directory
-cd $LAMB_PROJECT_PATH
-```
-
-### Step 2: Clone the Source Code Repository
-
-Clone the project from GitHub into the directory created above.
-
-```bash
-# Clone the repository
-git clone https://github.com/Lamb-Project/lamb.git
-
-# Navigate into the project root
-cd $LAMB_PROJECT_PATH
-```
-
-### Step 3: Configure and Launch Open WebUI (Terminal 1)
-
-Note: The working Open WebUI version used in this setup is v0.5.6. If you build the Open WebUI frontend, check out tag v0.5.6 to match this environment.
-
-**Instructions:** Open a new terminal for this component.
-
-1. **Navigate to the correct directory:**
-
- ```bash
- cd $LAMB_PROJECT_PATH/open-webui/backend
- ```
-
-2. **Create and activate the Python virtual environment:**
-
- ```bash
- uv venv
- source .venv/bin/activate
- ```
-
-3. **Create the required `data` directory (Fix from video):**
-
- ```bash
- mkdir -p data
- ```
-
-4. **Install Python dependencies:**
-
- ```bash
- uv pip install -r requirements.txt
- ```
-
-5. **Launch the server:** This process must be kept running.
- ```bash
- # Run from open-webui/backend (no need to cd into a dev folder)
- PORT=8080 ./dev.sh
- ```
- _Expect this server to be running on `http://0.0.0.0:8080`._
-
----
-
-### Step 4: Configure and Launch LAMB KB Server (Terminal 2)
-
-**Instructions:** Open a new terminal for this component.
-
-1. **Navigate to the correct directory:**
-
- ```bash
- cd $LAMB_PROJECT_PATH/lamb-kb-server-stable/backend
- ```
-
-2. **Create and activate the Python virtual environment:**
-
- ```bash
- uv venv
- source .venv/bin/activate
- ```
-
-3. **Fix dependencies in `requirements.txt` before installing (only if missing):**
-
- ```bash
- # Newer revisions already include these. If not present, apply:
- sed -i '' 's/langchain-text-splitters==0.7.0/langchain-text-splitters>=0.3.0,<0.4.0/' requirements.txt
- grep -q '^markdown2\b' requirements.txt || echo "markdown2" >> requirements.txt
- ```
-
-4. **Install the corrected Python dependencies:**
-
- ```bash
- uv pip install -r requirements.txt
- ```
-
-5. **Create the required `static` directory (Fix from video):**
-
- ```bash
- mkdir -p static
- ```
-
-6. **Create the environment configuration file:**
-
- ```bash
- cp .env.example .env
- ```
-
-7. **Launch the server:** This process must be kept running.
- ```bash
- python start.py
- ```
- _Expect this server to be running on `http://0.0.0.0:9090`._
-
----
-
-### Step 5: Configure and Launch Main LAMB Backend (Terminal 3)
-
-**Instructions:** Open a new terminal for this component.
-
-1. **Navigate to the correct directory:**
-
- ```bash
- cd $LAMB_PROJECT_PATH/backend
- ```
-
-2. **Create and activate the Python virtual environment:**
-
- ```bash
- uv venv
- source .venv/bin/activate
- ```
-
-3. **Install Python dependencies:**
-
- ```bash
- uv pip install -r requirements.txt
- ```
-
-4. **Create and configure the `.env` file:**
-
- ```bash
- # Create from the example
- cp .env.example .env
-
- # Point these to YOUR actual clone location
- # If you followed this guide exactly, use /opt/lamb-project/lamb
- # If you cloned elsewhere, set accordingly.
- REPO_ROOT="$LAMB_PROJECT_PATH"
-
- # Set the absolute path for the Open WebUI database
- sed -i '' "s|^OWI_PATH=.*|OWI_PATH=\"${REPO_ROOT}/open-webui/backend/data\"|" .env
-
- # Set the absolute path for the main LAMB database
- sed -i '' "s|^LAMB_DB_PATH=.*|LAMB_DB_PATH=${REPO_ROOT}|" .env
-
- # Ensure OpenWebUI base URL matches the server above
- sed -i '' "s|^OWI_BASE_URL=.*|OWI_BASE_URL=\"http://localhost:8080\"|" .env
- ```
-
- - Add a valid `OPENAI_API_KEY` in `.env` (you can retrieve one via `llm keys get openai`).
-
-5. **Launch the server (dev mode):** This process must be kept running.
- ```bash
- ./dev.sh
- ```
- _Expect this server to be running on `http://127.0.0.1:9099` (docs at `/docs`)._
-
----
-
-### Step 6: Configure and Launch Frontend (Terminal 4)
-
-**Instructions:** Open a new terminal for this component.
-
-1. **Navigate to the frontend app directory:**
-
- ```bash
- cd $LAMB_PROJECT_PATH/frontend/svelte-app
- ```
-
-2. **Create the frontend configuration file from the sample:**
-
- ```bash
- cp static/config.js.sample static/config.js
- ```
-
- - Ensure the URLs match your running services:
- - `baseUrl: 'http://localhost:9099/creator'` ← use absolute URL to avoid 404s from the FE dev server
- - `lambServer: 'http://localhost:9099'`
- - `openWebUiServer: 'http://localhost:8080'` ← the sample ships with `8090`; change it to `8080`.
-
-3. **Use a supported Node.js version (required by Vite/Svelte):**
-
- ```bash
- # Recommended: Node LTS (>=18, 20 LTS works well)
- # If you have nvm installed:
- . "$HOME/.nvm/nvm.sh" && nvm install --lts=iron && nvm use --lts=iron
- node -v && npm -v
- ```
-
-4. **Install Node.js dependencies:**
-
- ```bash
- npm install
- ```
-
-5. **Launch the frontend development server:** This process must be kept running.
- ```bash
- npm run dev -- --host
- ```
- _The frontend will be available at `http://localhost:5173`._
-
----
-
-### Step 7: Final Verification
-
-1. Open a web browser and navigate to `http://localhost:5173`.
-2. Log in using the credentials from `backend/.env` (`OWI_ADMIN_EMAIL`, `OWI_ADMIN_PASSWORD`). Defaults: `admin@owi.com` / `admin`.
-3. Quick health checks:
- - Open WebUI: `curl http://localhost:8080/health` → 200
- - KB Server: open `http://localhost:9090/docs`
- - Backend: open `http://localhost:9099/docs` and `curl http://localhost:9099/status` → `{ "status": true }`
-4. The application dashboard should load. All components are now running and communicating correctly.
-
----
-
-### Step 8: Restarting Services
-
-Use these commands to stop and start all four services cleanly on macOS/Linux.
-
-Stop all services by port (safe to run even if a service isn't running):
-
-```bash
-lsof -ti tcp:8080 | xargs kill || true # Open WebUI
-lsof -ti tcp:9090 | xargs kill || true # KB Server
-lsof -ti tcp:9099 | xargs kill || true # LAMB Backend
-lsof -ti tcp:5173 | xargs kill || true # Frontend
-```
-
-Start each service (one terminal per service):
-
-```bash
-cd $LAMB_PROJECT_PATH/open-webui/backend
-source .venv/bin/activate
-PORT=8080 ./dev.sh
-```
-
-```bash
-cd $LAMB_PROJECT_PATH/lamb-kb-server-stable/backend
-source .venv/bin/activate
-python start.py
-```
-
-```bash
-cd $LAMB_PROJECT_PATH/backend
-source .venv/bin/activate
-./dev.sh
-```
-
-```bash
-cd $LAMB_PROJECT_PATH/frontend/svelte-app
-. "$HOME/.nvm/nvm.sh" && nvm use --lts=iron
-npm run dev -- --host
-```
-
-Optional: start all in the background (logs go to .dev.log in each folder):
-
-```bash
-(cd $LAMB_PROJECT_PATH/open-webui/backend && source .venv/bin/activate && PORT=8080 ./dev.sh > .dev.log 2>&1 &)
-(cd $LAMB_PROJECT_PATH/lamb-kb-server-stable/backend && source .venv/bin/activate && python start.py > .dev.log 2>&1 &)
-(cd $LAMB_PROJECT_PATH/backend && source .venv/bin/activate && ./dev.sh > .dev.log 2>&1 &)
-(cd $LAMB_PROJECT_PATH/frontend/svelte-app && . "$HOME/.nvm/nvm.sh" && nvm use --lts=iron >/dev/null && npm run dev -- --host 0.0.0.0 > .dev.log 2>&1 &)
-```
-
-Verify after restart:
-
-```bash
-curl -fsS http://localhost:8080/health && echo "Open WebUI: OK"
-curl -fsS http://localhost:9090/docs >/dev/null && echo "KB Server: OK"
-curl -fsS http://localhost:9099/docs >/dev/null && echo "Backend Docs: OK"
-curl -fsS http://localhost:9099/status && echo
-```
-
-View logs when started in background:
-
-```bash
-tail -n 100 -f $LAMB_PROJECT_PATH/open-webui/backend/.dev.log
-tail -n 100 -f $LAMB_PROJECT_PATH/lamb-kb-server-stable/backend/.dev.log
-tail -n 100 -f $LAMB_PROJECT_PATH/backend/.dev.log
-tail -n 100 -f $LAMB_PROJECT_PATH/frontend/svelte-app/.dev.log
-```
-
----
-
-### Step 9: Build Open WebUI Frontend (optional)
-
-You can build the Open WebUI frontend contained in this repo for a production bundle.
-
-Prereqs:
-
-- Node LTS (use nvm):
-
-```bash
-. "$HOME/.nvm/nvm.sh" && nvm install --lts=iron && nvm use --lts=iron
-```
-
-Build steps:
-
-```bash
-cd $LAMB_PROJECT_PATH/open-webui
-npm install
-npm run build
-```
-
-Preview the built app:
-
-```bash
-cd $LAMB_PROJECT_PATH/open-webui
-npm run preview
-```
-
-Notes implemented in this repo to make the build succeed:
-
-- Added `svelte.config.js` using the static adapter and vite preprocess so TypeScript in `.svelte` compiles:
-
-```js
-// open-webui/svelte.config.js
-import adapter from "@sveltejs/adapter-static";
-import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
-
-export default {
- preprocess: vitePreprocess(),
- kit: {
- adapter: adapter({
- pages: "build",
- assets: "build",
- fallback: "index.html",
- }),
- },
-};
-```
-
-- Added `vite.config.ts` to force workers to ESM format (fixes Rollup IIFE error for `pyodide.worker.ts`):
-
-```ts
-// open-webui/vite.config.ts
-import { sveltekit } from "@sveltejs/kit/vite";
-import { defineConfig } from "vite";
-
-export default defineConfig({
- plugins: [sveltekit()],
- worker: { format: "es" },
-});
-```
-
-Troubleshooting:
-
-- Error: “Could not resolve entry module index.html” → ensure `svelte.config.js` exists as above.
-- Error: “Invalid value "iife" for option output.format … worker” → ensure `worker.format = 'es'` in `vite.config.ts`.
-- Error: vite not found → run with `npx vite build` or `npm run build` (uses local vite).
-
-Troubleshooting notes (observed locally):
-
-- Open WebUI may log warnings (frontend build missing, pyarrow/telemetry messages). API still runs on 8080.
-- If `npm install` fails with "Unsupported engine", switch to Node LTS via `nvm` and retry.
-- If you used a different clone path (e.g., `/opt/lamb-project/lamb`), update `OWI_PATH` and `LAMB_DB_PATH` in `backend/.env` accordingly.
diff --git a/Documentation/lamb_architecture_v2.md b/Documentation/lamb_architecture_v2.md
deleted file mode 100644
index cbd98c9d7..000000000
--- a/Documentation/lamb_architecture_v2.md
+++ /dev/null
@@ -1,1938 +0,0 @@
-# LAMB Architecture Documentation v2
-
-**Version:** 2.6
-**Last Updated:** March 31, 2026
-**Reading Time:** ~40 minutes
-
-> This is the streamlined architecture guide. For deep implementation details, see [lamb_architecture.md](./lamb_architecture.md). For quick navigation, see [DOCUMENTATION_INDEX.md](./DOCUMENTATION_INDEX.md).
-
----
-
-## Table of Contents
-
-1. [System Overview](#1-system-overview)
-2. [Architecture Principles](#2-architecture-principles)
-3. [Dual API Architecture](#3-dual-api-architecture)
-4. [Data Architecture](#4-data-architecture)
-5. [Authentication & Authorization](#5-authentication--authorization)
-6. [Completion Pipeline](#6-completion-pipeline)
-7. [Organizations & Multi-Tenancy](#7-organizations--multi-tenancy)
-8. [Feature Integration](#8-feature-integration)
-9. [Frontend Architecture](#9-frontend-architecture)
-10. [Development & Deployment](#10-development--deployment)
-11. [Logging System](#11-logging-system)
-12. [Quick Reference](#12-quick-reference)
-13. [Security Summary](#13-security-summary)
-14. [Troubleshooting](#14-troubleshooting)
-
----
-
-## 1. System Overview
-
-### 1.1 High-Level Architecture
-
-LAMB is a distributed system for creating and managing AI learning assistants:
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ LAMB Platform │
-├─────────────────────────────────────────────────────────────────┤
-│ │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ Frontend │ │ Backend │ │ Open WebUI │ │
-│ │ (Svelte) │◄─┤ (FastAPI) │◄─┤ (Python) │ │
-│ │ :5173/ │ │ :9099 │ │ :8080 │ │
-│ │ built SPA │ │ │ │ │ │
-│ └──────────────┘ └──────┬───────┘ └──────┬───────┘ │
-│ │ │ │
-│ ▼ ▼ │
-│ ┌──────────────┐ ┌──────────────┐ │
-│ │ Knowledge │ │ ChromaDB │ │
-│ │ Base Server │ │ (Vectors) │ │
-│ │ :9090 │ │ │ │
-│ └──────────────┘ └──────────────┘ │
-│ │ │
-│ ▼ │
-│ ┌──────────────┐ │
-│ │ LLM Provider│ │
-│ │ OpenAI/Ollama│ │
-│ └──────────────┘ │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### 1.2 Service Responsibilities
-
-| Service | Purpose | Technology | Port |
-|---------|---------|------------|------|
-| **Frontend** | Creator UI, Admin panels | Svelte 5, SvelteKit | 5173 (dev) / served by backend |
-| **Backend** | Core API, Completions | FastAPI, Python 3.11 | 9099 |
-| **Open WebUI** | Chat UI, Model management | FastAPI, Python | 8080 |
-| **KB Server** | Document processing, Vector search | FastAPI, ChromaDB | 9090 |
-
-### 1.3 Technology Stack
-
-**Backend:**
-- Python 3.11+
-- FastAPI (async API framework)
-- SQLite with WAL mode
-- ChromaDB (vector storage)
-
-**Frontend:**
-- Svelte 5 with SvelteKit
-- TailwindCSS
-- Axios for HTTP
-
-**Infrastructure:**
-- Docker & Docker Compose
-- Caddy (reverse proxy)
-
----
-
-## 2. Architecture Principles
-
-### 2.1 Design Principles
-
-1. **Privacy-First** — All data remains within institutional control
-2. **Modular** — Components can be updated or replaced independently
-3. **Extensible** — Plugin architecture for LLMs, prompts, and RAG
-4. **Multi-Tenant** — Organizations isolated with independent configs
-5. **Standards-Compliant** — OpenAI API compatibility, LTI 1.1
-6. **Educator-Centric** — Non-technical users can create AI assistants
-
-### 2.2 Key Patterns
-
-| Pattern | Usage |
-|---------|-------|
-| **Layered Architecture** | Creator Interface → LAMB Core → Database |
-| **Proxy Pattern** | Creator Interface proxies and enhances LAMB Core |
-| **Plugin Architecture** | Dynamic loading of processors and connectors |
-| **Repository Pattern** | Database managers encapsulate data access |
-| **Service Layer** | Business logic separated from HTTP handlers |
-
----
-
-## 3. Dual API Architecture
-
-### 3.1 Two-Tier Design
-
-LAMB uses a **dual API architecture** where the Creator Interface acts as an enhanced proxy to LAMB Core:
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│ Frontend (Browser) │
-└───────────────────────────┬─────────────────────────────────┘
- │
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ Creator Interface API (/creator) │
-│ │
-│ • User authentication & session management │
-│ • File operations (upload/download) │
-│ • Enhanced request validation │
-│ • Proxies requests with additional logic │
-│ │
-│ Location: /backend/creator_interface/ │
-└───────────────────────────┬─────────────────────────────────┘
- │ (Internal HTTP calls)
- ▼
-┌─────────────────────────────────────────────────────────────┐
-│ LAMB Core API (/lamb/v1) │
-│ │
-│ • Direct database access │
-│ • Core business logic │
-│ • Assistant, user, organization management │
-│ • Completions processing │
-│ │
-│ Location: /backend/lamb/ │
-└───────────────────────────┬─────────────────────────────────┘
- │
- ▼
- ┌───────────────┐
- │ LAMB Database │
- └───────────────┘
-```
-
-**Why Dual API?**
-- **Separation of Concerns** — User-facing logic (auth, file handling) separate from core
-- **Flexibility** — Add Creator Interface features without modifying core
-- **Security** — Additional validation layer before core operations
-- **Evolution** — Maintain API stability while core evolves
-
-### 3.2 Main Entry Point
-
-**File:** `/backend/main.py`
-
-**Mounts:**
-- `/lamb` → LAMB Core API
-- `/creator` → Creator Interface API
-- `/static` → Static file serving
-- `/{path:path}` → SPA catch-all (serves frontend)
-
-**Key Root Endpoints:**
-- `GET /v1/models` — List assistants as OpenAI models
-- `POST /v1/chat/completions` — Generate completions
-- `GET /status` — Health check
-- `GET /openapi.json` — Full OpenAPI specification (all endpoints, schemas, and parameters)
-
-### 3.3 LAMB Core Routers
-
-| Router | Prefix | Purpose |
-|--------|--------|---------|
-| `lti_router` | `/v1/lti` | **Unified LTI** — multi-assistant activities (new) |
-| `lti_users_router` | `/v1/lti_users` | Legacy student LTI (single assistant) |
-| `simple_lti_router` | `/simple_lti` | Student LTI launch handling (stub) |
-| `lti_creator_router` | `/v1/lti_creator` | LTI Creator launch (educators) |
-| `completions_router` | `/v1/completions` | Completion generation |
-| `mcp_router` | `/v1/mcp` | MCP endpoints |
-
-### 3.4 Creator Interface Routers
-
-| Router | Prefix | Purpose |
-|--------|--------|---------|
-| `assistant_router` | `/creator/assistant` | Assistant CRUD |
-| `knowledges_router` | `/creator/knowledgebases` | KB operations |
-| `organization_router` | `/creator/admin` | Org management |
-| `analytics_router` | `/creator/analytics` | Chat analytics |
-| `evaluaitor_router` | `/creator/rubrics` | Rubric management |
-| `prompt_templates_router` | `/creator/prompt-templates` | Prompt templates |
-| `aac_router` | `/creator/aac` | Agent-Assisted Creator (sessions, agent chat) |
-| `test_router` | `/creator/assistant/{id}/tests` | Test scenarios, test runner, evaluations |
-
-**Direct Endpoints in Creator Interface:**
-- `POST /creator/login` — User login
-- `POST /creator/signup` — User signup
-- `GET /creator/user/current` — Current user info
-- `GET /creator/owi-session` — Get OWI chat session token (for chat handoff)
-- `POST /creator/files/upload` — File upload
-- `GET /creator/admin/users` — List users (admin)
-
-### 3.5 OWI Bridge Components (Chat Integration)
-
-LAMB integrates with Open WebUI for **chat runtime only** through dedicated bridge classes in `/backend/lamb/owi_bridge/`. Authentication is handled natively by LAMB (see §5).
-
-| Component | File | Purpose |
-|-----------|------|---------|
-| `OwiDatabaseManager` | `owi_database.py` | Direct database access to OWI SQLite |
-| `OwiUserManager` | `owi_users.py` | Mirror user management (ensure exists, get chat handoff token) |
-| `OwiGroupManager` | `owi_group.py` | Group operations for LTI access control |
-| `OwiModelManager` | `owi_model.py` | Model (assistant) registration |
-
-**Key Operations:**
-- Ensure OWI mirror user exists when LAMB user is created (dummy password)
-- Ensure OWI mirror user exists for LTI users (dummy password)
-- Generate fresh OWI JWT for chat handoff (using dummy password, see §5.7)
-- Create/update OWI groups for published assistants
-- Register assistants as OWI models
-
-> **Mirror Users:** Every LAMB user has a corresponding "mirror" record in OWI's `user` and `auth` tables. These mirror users have a LAMB-generated dummy password that is never exposed to humans. The dummy password exists solely so LAMB can programmatically obtain OWI JWTs when redirecting users to OWI for chat.
-
-> **Security Note:** OWI router endpoints (`/v1/OWI/*`) were removed (Dec 2025) for security. OWI operations are now internal service classes only.
-
----
-
-## 4. Data Architecture
-
-### 4.1 Database Overview
-
-| Database | File | Purpose |
-|----------|------|---------|
-| **LAMB DB** | `$LAMB_DB_PATH/lamb_v4.db` | Assistants, users, orgs |
-| **OWI DB** | `$OWI_DATA_PATH/webui.db` | Chat history, mirror users, groups, models |
-| **ChromaDB** | `$OWI_DATA_PATH/vector_db/` | KB document vectors |
-
-### 4.2 LAMB Database Tables
-
-| Table | Purpose | Key Fields |
-|-------|---------|------------|
-| `organizations` | Organization/tenant records | id, slug, name, config (JSON), is_system |
-| `organization_roles` | User-organization membership | organization_id, user_id, role |
-| `Creator_users` | User accounts | email, name, user_type, organization_id, enabled, auth_provider, lti_user_id, password_hash, role |
-| `assistants` | Assistant definitions | name, owner, system_prompt, metadata*, published |
-| `assistant_shares` | Assistant sharing records | assistant_id, shared_with_user_id |
-| `kb_registry` | Knowledge Base metadata | kb_id, owner_user_id, is_shared |
-| `lti_users` | LTI launch user mappings | assistant_id, user_email |
-| `lti_creator_keys` | LTI Creator OAuth credentials | organization_id, consumer_key, consumer_secret |
-| `shared_prompt_templates` | Reusable prompt templates | name, template, organization_id |
-| `usage_logs` | Usage tracking and analytics | organization_id, assistant_id, usage_data (JSON) |
-
-> **Note:** The `assistants.metadata` field is stored in the `api_callback` column (legacy naming).
-
-### 4.3 Field Mapping Gotcha
-
-**Critical:** The `assistants` table has a field mapping that can confuse developers:
-
-| Application Code | Database Column | Why |
-|-----------------|-----------------|-----|
-| `assistant.metadata` | `api_callback` | Legacy naming, avoids schema migration |
-
-The `pre_retrieval_endpoint`, `post_retrieval_endpoint`, and `RAG_endpoint` columns are **DEPRECATED** and always empty.
-
-### 4.4 Key JSON Structures
-
-**Organization Config:**
-```json
-{
- "version": "1.0",
- "setups": {
- "default": {
- "providers": {
- "openai": {
- "enabled": true,
- "api_key": "sk-...",
- "default_model": "gpt-4o-mini",
- "models": ["gpt-4o", "gpt-4o-mini"]
- },
- "ollama": {
- "enabled": true,
- "base_url": "http://localhost:11434",
- "models": ["llama3.1:latest"]
- }
- },
- "global_default_model": {"provider": "openai", "model": "gpt-4o"},
- "small_fast_model": {"provider": "openai", "model": "gpt-4o-mini"}
- }
- },
- "kb_server": {"url": "http://localhost:9090", "api_key": "..."},
- "features": {
- "signup_enabled": false,
- "signup_key": "org-signup-key",
- "sharing_enabled": true
- }
-}
-```
-
-**Assistant Metadata:**
-```json
-{
- "connector": "openai",
- "llm": "gpt-4o-mini",
- "prompt_processor": "simple_augment",
- "rag_processor": "simple_rag",
- "capabilities": {"vision": true}
-}
-```
-
-### 4.5 Open WebUI Tables (Key)
-
-| Table | Purpose |
-|-------|---------|
-| `user` | End-user accounts (username, role, api_key) |
-| `auth` | Mirror user passwords (dummy, LAMB-managed) and sessions |
-| `model` | LLM models (published assistants use ID: `lamb_assistant.{id}`) |
-| `group` | User groups for access control |
-| `chat` | Chat history with JSON messages |
-
----
-
-## 5. Authentication & Authorization
-
-### 5.1 Authentication Flow
-
-LAMB handles authentication natively. Passwords are stored in LAMB's own database and JWT tokens are issued by LAMB. Open WebUI is **not involved** in Creator Interface authentication.
-
-```
-┌─────────┐ POST /creator/login ┌──────────────┐ Verify ┌──────────┐
-│ Browser │ ────────────────────────►│ Creator │ ────────────►│ LAMB DB │
-│ │ email + password │ Interface │ password │ │
-└─────────┘ └──────────────┘ (bcrypt) └──────────┘
- ▲ │
- │ │
- │ 200 OK {token, user_type} │
- │◄─────────────────────────────────────┤
- │ LAMB-issued JWT
- │ │
- │ Subsequent: Authorization: Bearer │
- ├─────────────────────────────────────►│
-```
-
-**User Types:**
-- `creator` — Full access to creator interface
-- `lti_creator` — Creator user authenticated via LTI (same permissions as creator, password cannot be changed; can be promoted to org admin by a system admin)
-- `end_user` — Redirected to Open WebUI only (no creator access)
-
-**Auth Providers:**
-- `password` — Standard email/password authentication (default)
-- `lti_creator` — LTI-based authentication for creator users
-
-### 5.2 AuthContext — Centralized Auth Manager
-
-Since v2.5, all authentication and authorization is handled by a single `AuthContext` object, built once per request via FastAPI `Depends()`. This replaces the previous pattern where each endpoint manually extracted tokens, looked up users, and checked permissions inline.
-
-**File:** `backend/lamb/auth_context.py`
-
-```python
-@dataclass
-class AuthContext:
- # Identity (always loaded)
- user: dict # Creator_users row (id, email, name, ...)
- token_payload: dict # Raw JWT claims (sub, email, role, exp, iat)
-
- # Roles (always loaded)
- is_system_admin: bool # role == "admin" in JWT
- organization_role: str | None # "owner" | "admin" | "member" | None
- is_org_admin: bool # organization_role in ("owner", "admin")
-
- # Organization (always loaded)
- organization: dict # Full org dict (id, name, slug, config, ...)
- features: dict # Org feature flags (sharing_enabled, rag_enabled, ...)
-
- # Resource access checkers (on-demand, query DB when called)
- def can_access_assistant(self, assistant_id) -> str # "owner"|"org_admin"|"shared"|"none"
- def can_modify_assistant(self, assistant_id) -> bool # owner or system admin only
- def can_access_kb(self, kb_id) -> str # "owner"|"shared"|"none"
-
- # Guard methods (raise HTTPException on denial)
- def require_system_admin(self)
- def require_org_admin(self)
- def require_assistant_access(self, assistant_id, level="any")
- def require_kb_access(self, kb_id, level="any")
-
- # Serialization for logging/debugging
- def to_dict(self) -> dict
-```
-
-**Three dependency functions** replace all previous auth patterns:
-
-| Dependency | Use Case |
-|---|---|
-| `get_auth_context` | Standard auth — most endpoints |
-| `get_optional_auth_context` | Endpoints that work with or without auth (e.g., `/completions/list`) |
-| `require_admin` | Admin-only shortcut |
-
-**Authentication flow (per request, ~3ms):**
-1. Extract `Bearer` token from `Authorization` header
-2. Decode LAMB JWT (fallback: OWI `get_user_auth` for backward compat)
-3. Look up user in `Creator_users` by email
-4. Load organization and org role from DB
-5. Parse org feature flags from config
-6. Return populated `AuthContext`
-
-> **No OWI dependency:** Token validation is entirely within LAMB. OWI fallback exists only for backward compatibility during migration (#265) and will be removed.
-
-### 5.3 Authorization Levels
-
-**System Roles:**
-| Role | `AuthContext` property | Scope |
-|------|---|---|
-| System Admin | `is_system_admin = True` | Full access to all orgs and resources |
-| Org Admin | `is_org_admin = True` | Manage settings and members **within own org** |
-| Org Member | `organization_role = "member"` | Create assistants and KBs within org |
-
-**Organization Roles (in `organization_roles` table):**
-| Role | Permissions |
-|------|-------------|
-| `owner` | Full control over organization (is_org_admin = True) |
-| `admin` | Manage settings and members (is_org_admin = True) |
-| `member` | Create assistants within org |
-
-### 5.4 Resource-Level Access Control
-
-The `AuthContext` enforces resource access with org-scoping built in:
-
-**Assistant access (`can_access_assistant`):**
-| Check | Returns | Allows modify/delete? |
-|---|---|---|
-| User is the owner (`assistant.owner == user.email`) | `"owner"` | Yes |
-| User is system admin | `"org_admin"` | Yes (system admin only) |
-| User is org admin **of the same org** as the assistant | `"org_admin"` | No (read-only for org admin) |
-| Assistant is shared with user, or same org member | `"shared"` | No |
-| None of the above | `"none"` | No |
-
-**Key design: org-scoped checks.** An org admin of Org A **cannot** access assistants belonging to Org B. The check at line 104 of `auth_context.py` enforces this:
-```python
-if self.is_org_admin and assistant.get("organization_id") == self.organization.get("id"):
- return "org_admin"
-```
-
-**KB access (`can_access_kb`):**
-- Delegates to `database_manager.user_can_access_kb()` which checks ownership and sharing
-- System admin override: always gets `"owner"` level
-
-**Guard methods** raise `HTTPException` immediately:
-```python
-auth.require_system_admin() # 403 if not system admin
-auth.require_org_admin() # 403 if not org admin or system admin
-auth.require_assistant_access(id, level="owner") # 404 if not found, 403 if not owner
-auth.require_assistant_access(id, level="owner_or_admin") # 403 if not owner or org admin
-```
-
-### 5.5 API Key Authentication
-
-For `/v1/chat/completions` and `/v1/models`:
-- Uses `LAMB_BEARER_TOKEN` environment variable
-- Format: `Authorization: Bearer {LAMB_BEARER_TOKEN}`
-- Default: `0p3n-w3bu!` (change in production!)
-
-### 5.6 Security Considerations
-
-**Password Storage:**
-- Passwords stored in LAMB `Creator_users.password_hash` column
-- Hashed with bcrypt (cost factor 12)
-- OWI mirror users have dummy passwords (known to LAMB, never exposed to humans)
-- LTI users have dummy passwords (no password-based login)
-
-**JWT Tokens:**
-- Generated by LAMB on successful login
-- Validated by LAMB using JWT signature verification (no database round-trip per request)
-- Include user ID/email and expiration
-- A separate OWI JWT is obtained on-demand for chat handoff only (see §5.7)
-
-**API Security:**
-- `LAMB_BEARER_TOKEN` for completion endpoints
-- LAMB-issued JWT tokens for creator interface endpoints
-- Organization isolation enforced at data layer
-
-### 5.7 Error Handling
-
-**Standard Error Response:**
-```json
-{
- "detail": "Human-readable error message"
-}
-```
-
-**Common Status Codes:**
-
-| Code | Meaning | Example |
-|------|---------|---------|
-| 401 | Unauthorized | Missing or invalid token |
-| 403 | Forbidden | User doesn't have permission |
-| 404 | Not Found | Resource doesn't exist |
-| 409 | Conflict | Duplicate resource (e.g., assistant name) |
-| 422 | Unprocessable | Invalid request data |
-| 500 | Server Error | Internal error |
-
-### 5.8 Chat Handoff to Open WebUI
-
-When a user needs to access the OWI chat interface (e.g., "Try assistant", LTI student launch, instructor "Open Chat"), LAMB provides an OWI-compatible JWT. This is the **only** remaining interaction with OWI's authentication system.
-
-```
-┌─────────────┐ GET /creator/owi-session ┌──────────────┐
-│ Frontend │ ──────────────────────────►│ Creator │
-│ (LAMB JWT) │ Authorization: Bearer │ Interface │
-└─────────────┘ └──────┬───────┘
- ▲ │
- │ ▼
- │ ┌──────────────┐
- │ │ OWI Bridge │
- │ │ │
- │ │ 1. Ensure │
- │ │ mirror │
- │ │ user │
- │ │ exists │
- │ │ │
- │ │ 2. Call OWI │
- │ │ signin │
- │ │ with dummy │
- │ │ password │
- │ └──────┬───────┘
- │ │
- │ {owi_token, owi_url} │
- │◄─────────────────────────────────────────┘
- │
- ▼
- Open OWI with owi_token
-```
-
-**Frontend behavior:**
-- Stores and manages **one token** day-to-day — the LAMB JWT
-- Requests an OWI token on-demand via `GET /creator/owi-session` only when navigating to chat
-- The OWI token is short-lived and not persisted
-
-**LTI student flows:**
-- Continue using server-side redirects (LAMB generates OWI token and redirects directly)
-- Students never interact with the Creator Interface or LAMB JWTs
-
-### 5.9 Password Migration from OWI
-
-A one-time migration copies existing bcrypt password hashes from OWI's `auth` table into `Creator_users.password_hash`. The migration:
-
-1. Runs automatically in `database_manager.py` → `run_migrations()` on backend startup
-2. Only processes users with `auth_provider='password'` and empty `password_hash`
-3. Copies the bcrypt hash as-is (same format, same cost factor — transparent to users)
-4. After migration, OWI mirror users' passwords are replaced with LAMB-generated dummy passwords
-
-**After migration:**
-- New user creation writes the real bcrypt hash to `Creator_users.password_hash` and a dummy password to the OWI mirror user
-- Password changes update `Creator_users.password_hash` only; the OWI dummy password is unchanged
-- LTI users already have dummy passwords — no change for them
-
----
-
-## 6. Completion Pipeline
-
-### 6.1 Request Flow
-
-```
-POST /v1/chat/completions
- │
- ▼
-┌───────────────────────────────────────────────────────────────────┐
-│ 1. Validate API key │
-│ 2. Load assistant from database │
-│ 3. Parse plugin config from assistant.metadata │
-│ 4. Load plugins (RAG, PPS, Connector) │
-└───────────────────────────────────────────────────────────────────┘
- │
- ▼
-┌───────────────┐ ┌───────────────┐ ┌───────────────┐
-│ RAG Processor │───►│ Prompt │───►│ Connector │
-│ │ │ Processor │ │ │
-│ Query KB │ │ Augment msgs │ │ Call LLM │
-└───────────────┘ └───────────────┘ └───────────────┘
- │
- ▼
- ┌───────────────┐
- │ LLM Provider │
- │ (OpenAI/etc) │
- └───────────────┘
- │
- ▼
- Stream/Return Response
-```
-
-### 6.2 Plugin Processing Stages
-
-| Stage | Plugin Type | Purpose |
-|-------|-------------|---------|
-| 1. Before | RAG Processor | Query KB, get context |
-| 2. Before | Prompt Processor | Transform/augment messages |
-| 3. Execute | Connector | Call LLM provider |
-| 4. After | (Optional) | Post-processing, logging |
-
-### 6.3 Organization Config Resolution
-
-When resolving LLM configuration:
-1. **Explicit** in request → use that
-2. **Assistant metadata** → use that
-3. **Organization global_default_model** → use that
-4. **Provider default_model** → use that
-5. **First available model** → use that
-
-```python
-class OrganizationConfigResolver:
- def get_provider_config(self, provider_name: str) -> Dict
- def get_global_default_model_config(self) -> Dict
- def get_small_fast_model_config(self) -> Dict
- def get_kb_server_config(self) -> Dict
-```
-
-### 6.4 Plugin System
-
-**Plugin Types & Locations:**
-
-| Type | Directory | Function Name |
-|------|-----------|---------------|
-| Prompt Processor | `lamb/completions/pps/` | `prompt_processor()` |
-| Connector | `lamb/completions/connectors/` | `llm_connect()` |
-| RAG Processor | `lamb/completions/rag/` | `rag_processor()` |
-
-**Available Plugins:**
-
-| Prompt Processors | Connectors | RAG Processors |
-|-------------------|------------|----------------|
-| `simple_augment` | `openai` | `simple_rag` |
-| | `ollama` | `single_file_rag` |
-| | `anthropic` | `no_rag` |
-| | `banana_img`* | |
-| | `bypass` | |
-
-> *`banana_img` uses Google Gen AI (Gemini) for image generation with intelligent routing (title requests → GPT-4o-mini, image requests → Gemini).
-
-**Creating a Custom Plugin:**
-
-1. Create file in appropriate directory (e.g., `lamb/completions/pps/my_pps.py`)
-2. Implement required function signature:
-
-```python
-# Prompt Processor
-def prompt_processor(request: Dict, assistant=None, rag_context=None) -> List[Dict]:
- messages = []
- # ... transform messages
- return messages
-
-# Connector
-async def llm_connect(messages: list, stream: bool, body: Dict,
- llm: str, assistant_owner: str):
- # ... call LLM
- return response # or async generator if streaming
-
-# RAG Processor
-def rag_processor(messages: List[Dict], assistant=None) -> Dict:
- # ... query KB
- return {"context": "...", "sources": [...]}
-```
-
-3. Plugin is auto-loaded at runtime — no registration needed
-4. Configure assistant metadata to use it
-
-### 6.5 Connection Pooling
-
-LLM connectors use **shared HTTP client pools** to avoid creating new TCP connections on every request. Without pooling, concurrent users exhaust OS-level connection resources and the system becomes unresponsive.
-
-**OpenAI Connector** (`openai.py`):
-- Shared `AsyncOpenAI` clients cached by `(api_key, base_url)`
-- A single client is created on the first request for each credential pair and reused for all subsequent requests
-- Explicit timeouts configured via environment variables
-
-**Ollama Connector** (`ollama.py`):
-- Shared `aiohttp.ClientSession` instances cached by `base_url`
-- Each session uses a `TCPConnector` with configurable connection limits and keepalive
-- Sessions are recreated transparently if closed
-
-**Configuration** (via `backend/.env`):
-
-| Variable | Purpose | Default |
-|----------|---------|---------|
-| `LLM_REQUEST_TIMEOUT` | Total request timeout for OpenAI calls (seconds) | `120` |
-| `LLM_CONNECT_TIMEOUT` | TCP connection establishment timeout (seconds) | `10` |
-| `LLM_MAX_CONNECTIONS` | Max concurrent connections per client pool | `50` |
-| `OLLAMA_REQUEST_TIMEOUT` | Total request timeout for Ollama calls (seconds) | `120` |
-
-> **Performance:** With connection pooling and a single uvicorn worker, the system handles 50+ concurrent users at 100% success rate with a median response time under 5 seconds against real OpenAI models.
-
-### 6.6 Streaming Responses
-
-For streaming completions (`"stream": true`), responses use Server-Sent Events (SSE):
-
-```
-data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1678886400,"model":"lamb_assistant.1","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
-
-data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1678886400,"model":"lamb_assistant.1","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
-
-data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1678886400,"model":"lamb_assistant.1","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
-
-data: [DONE]
-```
-
-**Content-Type:** `text/event-stream`
-
-### 6.7 Multimodal Support
-
-LAMB supports images via OpenAI's vision API format:
-
-```json
-{
- "messages": [{
- "role": "user",
- "content": [
- {"type": "text", "text": "What's in this image?"},
- {"type": "image_url", "image_url": {"url": "https://..."}}
- ]
- }]
-}
-```
-
-**Requirements:**
-- Assistant must have `capabilities.vision = true` in metadata
-- Supported formats: JPEG, PNG, GIF, WebP
-- Supported sources: HTTP URLs, Base64 data URLs
-
----
-
-## 7. Organizations & Multi-Tenancy
-
-### 7.1 Organization Structure
-
-```
-System Organization (slug: "system", is_system: true)
- │
- ├── Organization A (slug: "engineering")
- │ ├── Users (with roles: owner, admin, member)
- │ ├── Assistants
- │ └── Knowledge Bases
- │
- └── Organization B (slug: "physics")
- ├── Users
- ├── Assistants
- └── Knowledge Bases
-```
-
-**Isolation:**
-- Each org has independent LLM provider configs
-- Users belong to one organization
-- Assistants and KBs scoped to organization
-- Signup keys are org-specific
-
-### 7.2 System Organization
-
-The "system" organization is special:
-- `is_system = true`
-- Cannot be deleted
-- Fallback configuration source
-- System admins are members with admin role
-
-### 7.3 Organization Creation
-
-Organizations can be created with or without an admin:
-
-1. System admin creates org via `/creator/admin/organizations/enhanced`
-2. **With admin:** A user from the system org is moved to the new org and assigned the admin role
-3. **Without admin:** Org is created with no admin; a system admin can promote a member later via the Members panel
-
-### 7.4 Organization Signup Flow
-
-1. Admin creates org with `signup_enabled: true` and `signup_key`
-2. User enters email, name, password, and signup key
-3. System matches key to organization
-4. User created in that org with "member" role
-
-### 7.5 Key Organization APIs
-
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| GET | `/creator/admin/organizations` | List organizations |
-| POST | `/creator/admin/organizations/enhanced` | Create org (admin optional) |
-| PUT | `/creator/admin/organizations/{slug}/config` | Update config |
-| PUT | `/creator/admin/organizations/{slug}/members/{user_id}/role` | Promote/demote member (sys admin only) |
-| GET | `/lamb/v1/organizations/{slug}/members` | List members |
-
----
-
-## 8. Feature Integration
-
-### 8.1 Knowledge Base System
-
-**Architecture:**
-```
-Frontend → Creator Interface → KB Server → ChromaDB
-```
-
-**Key Operations:**
-1. **Create:** `POST /creator/knowledgebases/create`
-2. **Upload:** `POST /creator/knowledgebases/{id}/upload`
-3. **Query:** `GET /creator/knowledgebases/{id}/query`
-
-**Collection Naming:** `{org_slug}_{user_email}_{collection_name}`
-
-**KB Sharing:**
-- Toggle `is_shared` to share with organization
-- Shared KBs visible to all org members (read-only)
-- Protection prevents unsharing when KB is in use by other users' assistants
-
-**RAG Integration:**
-- Assistant references KB IDs in `RAG_collections`
-- RAG processor queries KBs during completion
-- Context injected into system prompt
-
-### 8.2 LTI Integration
-
-LAMB supports three types of LTI integration:
-1. **Unified LTI** — Multi-assistant activities configured by instructors (recommended, new in v2.2)
-2. **Legacy Student LTI** — Students access a single published assistant via LTI
-3. **LTI Creator** — Educators access the Creator Interface via LTI (new in v2.1)
-
-> For full details, see [lti_landscape.md](./lti_landscape.md) and [projects/lti_unified_activity_design.md](./projects/lti_unified_activity_design.md).
-
-#### 8.2.1 Unified LTI (Multi-Assistant Activities) — Recommended
-
-**Purpose:** One LTI tool for the entire LAMB instance. Instructors configure which published assistants are available per activity. Students see multiple assistants in one activity. Instructors get a **dashboard** with usage stats and optional chat transcript review.
-
-**Global credentials:** Set via `LTI_GLOBAL_CONSUMER_KEY`/`LTI_GLOBAL_SECRET` in `.env`, or overridden by admin via the database.
-
-**Launch Flow:**
-```
-User clicks LTI link in LMS
- → LMS sends OAuth 1.0 signed POST to /lamb/v1/lti/launch
- → LAMB validates signature with global key/secret
- → LAMB checks resource_link_id:
- │
- ├── Activity NOT configured:
- │ ├── Instructor with Creator account → Setup page
- │ │ (pick assistants, name, chat visibility option)
- │ │ → First instructor becomes OWNER
- │ │ → Redirect to Instructor Dashboard
- │ ├── Instructor without Creator account → "Contact LAMB admin" page
- │ └── Student → "Not set up yet" waiting page
- │
- └── Activity IS configured:
- ├── Instructor → INSTRUCTOR DASHBOARD
- │ (stats, student access log, chat transcripts if enabled)
- │ • [Open Chat] → OWI redirect
- │ • [Manage Assistants] → reconfigure (owner only)
- │
- └── Student:
- └── Direct OWI redirect (identity from LMS via LTI)
-```
-
-**Key Endpoints:**
-- `POST /lamb/v1/lti/launch` — Main LTI entry point (routes to setup, dashboard, or OWI)
-- `GET /lamb/v1/lti/setup` — Instructor setup page (first-time or reconfigure)
-- `POST /lamb/v1/lti/configure` — Save activity configuration
-- `GET /lamb/v1/lti/dashboard` — Instructor dashboard (HTML) with AJAX data endpoints:
- - `GET /dashboard/stats`, `/dashboard/students`, `/dashboard/chats`, `/dashboard/chats/{id}`
-- `GET /lamb/v1/lti/enter-chat` — Dashboard → OWI redirect for instructors
-- `GET /lamb/v1/lti/info` — LTI tool configuration info
-
-**Key Design Decisions:**
-- Activities are bound to one organization (no cross-org mixing)
-- **Activity ownership:** first instructor to configure = owner (can manage assistants for the activity; chat visibility is fixed at creation time)
-- **Instructor dashboard:** any instructor sees stats; only owner can reconfigure
-- **Chat visibility:** opt-in per activity at creation; when enabled, instructors see student conversations with real names from the LMS. LMS privacy settings control what identity data is passed to LAMB.
-- Student identity is per `resource_link_id` (each LTI placement = separate identity)
-- Org admins can manage activities: `GET/PUT /creator/admin/lti-activities`
-- Global credentials managed by system admin: `GET/PUT /creator/admin/lti-global-config`
-
-**Database Tables:**
-- `lti_global_config` — Global consumer key/secret (singleton)
-- `lti_activities` — Activity records (resource_link_id → org, OWI group, **owner**, **chat_visibility_enabled**)
-- `lti_activity_assistants` — Junction: assistants per activity
-- `lti_activity_users` — Student access tracking (**owi_user_id**, **last_access_at**, **access_count**, `consent_given_at` kept for backward compat, no longer written)
-- `lti_identity_links` — Maps LMS identities to Creator users
-
-#### 8.2.2 Legacy Student LTI (Single Published Assistant)
-
-> **Note:** This is the older approach. Consider using Unified LTI (§8.2.1) for new deployments.
-
-**Publishing Flow:**
-```
-Creator publishes assistant
- → Create OWI Group
- → Register as OWI Model
- → Generate LTI consumer key/secret
- → Return LTI launch URL
-```
-
-**LTI Launch Flow (Legacy):**
-```
-Student clicks LTI link in LMS
- → LMS sends OAuth-signed POST
- → LAMB validates signature
- → Ensure OWI mirror user exists (dummy password)
- → Add user to assistant's group
- → Generate OWI JWT (via dummy password)
- → Redirect to OWI chat
-```
-
-**Key Endpoints:**
-- `POST /lamb/v1/lti_users/lti` — Student LTI launch handler
-
-**LTI Parameters Extracted:**
-- `ext_user_username` / `user_id` — Username for email generation
-- `oauth_consumer_key` — Published assistant name (identifies the assistant)
-- Uses global `LTI_SECRET` env var for OAuth validation
-
-#### 8.2.3 LTI Creator (Creator Interface Access)
-
-**Purpose:** Allow educators to access the LAMB Creator Interface directly from their LMS without separate login credentials.
-
-**Setup Flow:**
-```
-1. Organization admin configures LTI Creator in org settings
- → Sets OAuth consumer key (format: {org_id}_{custom_name})
- → Sets OAuth consumer secret
-2. Admin configures LTI activity in LMS with these credentials
-3. Educators click LTI link → land in Creator Interface
-```
-
-**LTI Creator Launch Flow:**
-```
-Educator clicks LTI link in LMS
- → LMS sends OAuth 1.0 signed POST to /lamb/v1/lti_creator/launch
- → LAMB validates OAuth signature
- → Extract organization from consumer key prefix
- → Identify user by LMS user_id (stable across sessions)
- → Create LTI creator user if first launch (or fetch existing)
- → Ensure OWI mirror user exists (dummy password)
- → Generate LAMB JWT token
- → Redirect to Creator Interface with token in URL
- → Frontend stores LAMB token and authenticates user
-```
-
-**Key Endpoints:**
-- `POST /lamb/v1/lti_creator/launch` — LTI Creator launch handler
-- `GET /lamb/v1/lti_creator/info` — LTI Creator configuration info
-
-**LTI Creator User Characteristics:**
-| Attribute | Value |
-|-----------|-------|
-| `user_type` | `creator` |
-| `auth_provider` | `lti_creator` |
-| `organization_role` | `member` (default; can be promoted to admin by a system admin) |
-| Password changeable | No (random password, unused) |
-| Can share assistants | Yes (same as regular creator) |
-| Can create KBs | Yes |
-| Can publish assistants | Yes |
-
-**User Identification:**
-- Users identified by `lti_user_id` from LMS (stable across launches)
-- Email format: `lti_creator_{org_slug}_{lti_user_id}@lamb-lti.local`
-- Same user can launch from different LTI consumer instances
-
-**Organization Admin Configuration:**
-```json
-{
- "lti_creator": {
- "oauth_consumer_key": "2_my_lti_creator",
- "oauth_consumer_secret": "secret123"
- }
-}
-```
-
-**Database Tables:**
-- `lti_creator_keys` — Stores OAuth credentials per organization
-- `Creator_users.lti_user_id` — LMS user identifier
-- `Creator_users.auth_provider` — Set to `lti_creator`
-
-### 8.3 Assistant Sharing
-
-**Two-Level Permission System:**
-
-| Org `sharing_enabled` | User `can_share` | Result |
-|----------------------|------------------|--------|
-| ✅ | ✅ | Can share |
-| ✅ | ❌ | Cannot share |
-| ❌ | ✅ | Cannot share |
-| ❌ | ❌ | Cannot share |
-
-**Access Levels:**
-
-| Action | Owner | Shared User |
-|--------|-------|-------------|
-| View/Chat | ✅ | ✅ |
-| Edit/Delete | ✅ | ❌ |
-| Publish | ✅ | ❌ |
-| Manage sharing | ✅ | ❌ |
-
-**Key Endpoints:**
-- `GET /lamb/v1/assistant-sharing/check-permission`
-- `GET /lamb/v1/assistant-sharing/shares/{id}`
-- `PUT /lamb/v1/assistant-sharing/shares/{id}`
-
-### 8.4 Chat Analytics
-
-**Purpose:** Provide assistant owners insights into usage.
-
-**Features:**
-- Chat list with filtering and pagination
-- Full conversation detail view
-- Usage statistics (total chats, unique users, messages)
-- Activity timeline (configurable period)
-- Privacy-respecting anonymization (default on)
-
-**Key Endpoints:**
-- `GET /creator/analytics/assistant/{id}/chats`
-- `GET /creator/analytics/assistant/{id}/chats/{chat_id}`
-- `GET /creator/analytics/assistant/{id}/stats`
-- `GET /creator/analytics/assistant/{id}/timeline`
-
-> See [chat_analytics_project.md](./chat_analytics_project.md) for implementation details.
-
-### 8.5 User Blocking
-
-**Purpose:** Disable accounts without deleting data.
-
-**Behavior:**
-- Set `Creator_users.enabled = false`
-- Blocked users get 403 on login
-- Published assistants remain accessible
-- Shared resources remain functional
-
-**Key Endpoints:**
-- `PUT /creator/admin/users/{id}/disable`
-- `PUT /creator/admin/users/{id}/enable`
-- `POST /creator/admin/users/disable-bulk`
-
-### 8.6 Admin User Management
-
-**User Type Display in Admin UI:**
-
-The admin user management panel identifies users by type with color-coded badges:
-
-| User Type | Badge | Criteria |
-|-----------|-------|----------|
-| Admin | Red | Creator_users.role = 'admin' |
-| Org Admin | Purple | organization_role IN ('admin', 'owner') |
-| LTI Creator | Blue | auth_provider = 'lti_creator' |
-| Creator | Green | Default for creator users |
-| End User | Gray | user_type = 'end_user' |
-
-**LTI Creator User Management:**
-- Displayed with blue "LTI Creator" badge
-- Filter dropdown includes "LTI Creator" option
-- Password change button disabled (LTI users have dummy passwords)
-- Can be enabled/disabled by admin
-- Can be promoted to organization admin by a system admin
-
-### 8.7 Agent-Assisted Creator (AAC)
-
-**Purpose:** Conversational agent that helps educators design, configure, test, and refine AI learning assistants. Internal codename: Pastor.
-
-**Architecture:** The AAC runs as a backend module (`backend/lamb/aac/`) inside the existing FastAPI process. It uses `OrganizationConfigResolver` for LLM configuration — API keys stay in-process, never exposed to clients.
-
-**Key Components:**
-
-| Component | Location | Purpose |
-|-----------|----------|---------|
-| Liteshell | `lamb/aac/liteshell/` | CLI-shaped tool interface. Parses command strings (e.g., `lamb assistant get 4`) and calls Creator Interface HTTP endpoints via `LambClient` (from `lamb-cli`). Same code path as the frontend and CLI — validation, sanitization, and auth all enforced. Local-only commands (`docs.index`, `docs.read`, `help`) read files directly. |
-| Agent Docs | `lamb/aac/docs/` | Agent-readable LAMB documentation. 10 topic files with YAML front matter (semantic tags for question routing). Accessed via `docs.index` and `docs.read` liteshell commands. |
-| Agent Loop | `lamb/aac/agent/loop.py` | Async LLM tool-calling loop with authorization. |
-| Authorization | `lamb/aac/authorization.py` | JSON policy (auto/ask/never) per action. Write commands require user confirmation via Python-level classifier, not LLM prompting. |
-| Session Manager | `lamb/aac/session_manager.py` | Session CRUD. Table: `aac_sessions` (Migration 14). |
-| Session Logger | `lamb/aac/session_logger.py` | JSONL file logging per session for research. Config: `AAC_SESSION_LOGGING`, `AAC_LOG_PATH`. |
-| Skills | `lamb/aac/skills/*.md` | Declarative skill files loaded into the agent's system prompt. |
-| Router | `lamb/aac/router.py` | FastAPI endpoints at `/creator/aac/`. |
-
-**Key Endpoints:**
-
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| POST | `/creator/aac/sessions` | Start a design session |
-| GET | `/creator/aac/sessions` | List sessions |
-| GET | `/creator/aac/sessions/{id}` | Get session + conversation |
-| DELETE | `/creator/aac/sessions/{id}` | Archive session |
-| POST | `/creator/aac/sessions/{id}/message` | Send message, get agent response |
-
-**CLI:** `lamb aac start|sessions|get|delete|message|chat|history`
-
-> For full design details, see [lamb-agent-assisted-creator.md](./projects/lamb-agent-assisted-creator.md) and [aac-backlog.md](./projects/aac-backlog.md).
-
-### 8.8 Assistant Test Scenarios & Evaluation
-
-**Purpose:** Structured testing and evaluation framework for assistants. Educators define test prompts, run them through the real completion pipeline, and record evaluations. Foundation for the evaluation framework described in issue #172.
-
-**Data Model:**
-
-| Table | Purpose |
-|-------|---------|
-| `assistant_test_scenarios` | Test scenarios: title, messages, expected behavior, type (single_turn/multi_turn/adversarial) |
-| `assistant_test_runs` | Execution results: input, output, token usage, model, assistant config snapshot |
-| `assistant_test_evaluations` | Evaluations: verdict (good/bad/mixed), notes, evaluator (user/agent) |
-
-Tables created via Migration 15.
-
-**Test Runner:** Executes scenarios through the **real completion pipeline** — same code path as production (RAG retrieval, prompt processing, connector). Tracks token usage and elapsed time per run.
-
-**Debug Bypass:** Adding `debug_bypass: true` to a completion request (via the creator interface) overrides the connector to `bypass`, showing the fully constructed messages the LLM would receive — system prompt, RAG context, processed prompt — without calling the LLM. Zero tokens consumed. Available via:
-- `lamb chat --bypass -m "question"` (CLI)
-- `lamb test run --bypass` (test framework)
-- `lamb assistant debug --message "question"` (AAC liteshell)
-
-**Key Endpoints:**
-
-| Method | Endpoint | Purpose |
-|--------|----------|---------|
-| POST | `/creator/assistant/{id}/tests/scenarios` | Create scenario |
-| GET | `/creator/assistant/{id}/tests/scenarios` | List scenarios |
-| POST | `/creator/assistant/{id}/tests/run` | Run scenarios (supports `debug_bypass`) |
-| GET | `/creator/assistant/{id}/tests/runs` | List runs with results |
-| GET | `/creator/assistant/{id}/tests/runs/{rid}` | Full run detail |
-| POST | `/creator/assistant/{id}/tests/runs/{rid}/evaluate` | Submit evaluation |
-| GET | `/creator/assistant/{id}/tests/evaluations` | List evaluations |
-
-**CLI:** `lamb test scenarios|add|run|runs|run-detail|evaluate|evaluations`
-
----
-
-## 9. Frontend Architecture
-
-The project uses Svelte 5 , with Javascript and JSDOC ( NOT Typescript !!! repeat NOT Typescript!!! )
-### 9.1 Structure
-
-```
-/frontend/svelte-app/
-├── src/
-│ ├── routes/ # SvelteKit pages
-│ │ ├── +layout.svelte # Root layout
-│ │ ├── +page.svelte # Home
-│ │ ├── assistants/ # Assistant management
-│ │ ├── knowledge-bases/ # KB management
-│ │ ├── admin/ # System admin
-│ │ └── org-admin/ # Org admin
-│ │
-│ └── lib/
-│ ├── components/ # UI components
-│ ├── services/ # API clients
-│ ├── stores/ # Svelte stores
-│ └── config.js # Runtime config
-```
-
-### 9.2 Key Routes
-
-| Route | Purpose |
-|-------|---------|
-| `/` | Home (redirects to /assistants) |
-| `/assistants` | Assistant list and management |
-| `/knowledge-bases` | KB list and management |
-| `/admin` | System admin panel |
-| `/org-admin` | Organization admin |
-
-### 9.3 UX Patterns (Critical)
-
-#### Form Dirty State Tracking
-
-**Problem:** Svelte 5's reactivity can overwrite user edits on prop reference changes.
-
-**Solution:**
-```javascript
-let formDirty = $state(false);
-let previousId = $state(null);
-
-// Mark dirty on user input
-function handleFieldChange() {
- formDirty = true;
-}
-
-// Only repopulate on meaningful changes
-$effect(() => {
- if (data?.id !== previousId) {
- if (!formDirty) {
- populateFormFields(data);
- previousId = data.id;
- }
- }
-});
-
-// Reset on save
-async function handleSave() {
- await saveData();
- formDirty = false;
-}
-```
-
-#### Async Data Loading Race Conditions
-
-**⚠️ CRITICAL:** Setting selections before options load causes silent failures.
-
-**Problem:**
-```javascript
-// ❌ BAD - Race condition
-selectedKnowledgeBases = ["abc123"]; // Set selection
-fetchKnowledgeBases(); // Load async - happens LATER
-// bind:group can't match to empty list!
-```
-
-**Solution:**
-```javascript
-// ✅ CORRECT - Load then select
-async function populateFormFields(data) {
- await fetchKnowledgeBases(); // Wait for options
- selectedKnowledgeBases = data.RAG_collections?.split(',') || [];
-}
-```
-
-#### Preventing Spurious Repopulation
-
-**Problem:** Reference changes (not data changes) trigger form reset.
-
-**Solution:** Use ID-based change detection:
-```javascript
-$effect(() => {
- const idChanged = data?.id !== previousId;
- if (idChanged) {
- populateFormFields(data);
- previousId = data.id;
- }
- // Skip reference-only changes
-});
-```
-
-### 9.4 Service Layer Pattern
-
-```javascript
-// lib/services/assistantService.js
-import { getApiUrl } from '$lib/config';
-
-export async function getAssistants(token) {
- const response = await fetch(getApiUrl('/creator/assistant/list'), {
- headers: { 'Authorization': `Bearer ${token}` }
- });
- return response.json();
-}
-```
-
-### 9.5 Runtime Configuration
-
-```javascript
-// static/config.js
-window.LAMB_CONFIG = {
- api: {
- lambServer: 'http://localhost:9099',
- owebuiServer: 'http://localhost:8080'
- }
-};
-```
-
----
-
-### 9.6 Frontend Resilience Patterns (Required)
-
-These patterns are mandatory for any new frontend code. They exist because we keep finding the same class of "frontend goes unresponsive, reload fixes it" bugs (issue #352 catalogues 38 instances). Each pattern below has a corresponding ESLint-style smell to scan for in code review.
-
-#### 9.6.1 Loading flags must use `try/finally`
-
-**Rule:** any `loading = true` (or `set(true)`) MUST have a matching reset in a `finally` block, never only in the success branch and never only in `catch`.
-
-```javascript
-// ❌ BAD — error path leaks loading=true forever
-async function submit() {
- loading = true;
- const result = await api.call();
- if (result.success) { handleOk(); loading = false; }
- else { error = result.error; } // loading still true
-}
-
-// ❌ BAD — uncaught throw leaks loading=true forever
-async function submit() {
- loading = true;
- const result = await api.call(); // throws? we never reach next line
- loading = false;
-}
-
-// ✅ GOOD — every exit path resets loading
-async function submit() {
- loading = true;
- error = null;
- try {
- const result = await api.call();
- if (result.success) handleOk();
- else error = result.error;
- } catch (e) {
- error = e instanceof Error ? e.message : String(e);
- } finally {
- loading = false;
- }
-}
-```
-
-**Symptom this prevents:** spinners stuck spinning, submit buttons permanently disabled after a network blip.
-
-#### 9.6.2 `await` in `onMount` / `$effect` requires a mounted-check
-
-**Rule:** any async work started in `onMount` or `$effect` MUST check it's still mounted before writing reactive state. Use a local `cancelled` flag plus a teardown.
-
-```javascript
-// ❌ BAD — setState on destroyed component if user navigates away mid-fetch
-onMount(async () => {
- const data = await api.get(id);
- state = data;
-});
-
-// ✅ GOOD
-onMount(() => {
- let cancelled = false;
- (async () => {
- try {
- const data = await api.get(id);
- if (!cancelled) state = data;
- } catch (e) {
- if (!cancelled) error = e.message;
- }
- })();
- return () => { cancelled = true; };
-});
-```
-
-**Symptom this prevents:** stale data from a previous resource appearing after rapid navigation; console errors about updates on destroyed components.
-
-#### 9.6.3 Cancellable fetches use `AbortController`
-
-**Rule:** any fetch that depends on a prop/route param that can change MUST be abortable, and the previous fetch MUST be aborted when the dependency changes.
-
-```javascript
-// ✅ GOOD
-let abortController = null;
-
-async function loadResource(id) {
- if (abortController) abortController.abort();
- abortController = new AbortController();
-
- loading = true;
- try {
- const data = await fetch(`/api/${id}`, { signal: abortController.signal })
- .then(r => r.json());
- state = data;
- } catch (e) {
- if (e.name === 'AbortError') return; // expected on rapid switch
- error = e.message;
- } finally {
- loading = false;
- }
-}
-
-onDestroy(() => abortController?.abort());
-```
-
-**Symptom this prevents:** clicking through items in a list shows the wrong details because an earlier request resolved last.
-
-#### 9.6.4 401 must be handled centrally, not per call site
-
-**Rule:** all API calls go through a shared `apiFetch` wrapper that:
-1. Attaches the bearer token
-2. On 401, calls `clearCurrentSession()` from `$lib/session/sessionManager.js` and redirects to login
-3. On 403, surfaces a permission error with a redirect option
-
-Do NOT reimplement 401 handling in each service file. Do NOT silently return `{success:false}` on 401 — that lets the user keep clicking buttons that will all fail.
-
-```javascript
-// $lib/services/apiClient.js — single source of truth
-import { goto } from '$app/navigation';
-import { clearCurrentSession } from '$lib/session/sessionManager';
-
-export async function apiFetch(path, options = {}) {
- const token = getAuthToken();
- const res = await fetch(getApiUrl(path), {
- ...options,
- headers: {
- ...(options.headers || {}),
- 'Authorization': token ? `Bearer ${token}` : ''
- }
- });
-
- if (res.status === 401) {
- clearCurrentSession();
- await goto('/login');
- throw new Error('Session expired');
- }
- return res;
-}
-```
-
-**Symptom this prevents:** "everything is broken until I reload" — the classic OWI session-expiry symptom.
-
-#### 9.6.5 Timers and streams must be cleaned up on destroy
-
-**Rule:** every `setInterval`, `setTimeout`, `EventSource`, and `fetch().body.getReader()` started in a component MUST have a matching cleanup in `onDestroy` (or returned from `onMount`).
-
-```javascript
-// ❌ BAD — timer fires after unmount
-function showSuccess() {
- successMessage = 'Saved';
- setTimeout(() => { successMessage = ''; }, 4000);
-}
-
-// ✅ GOOD
-let successTimer = null;
-function showSuccess() {
- successMessage = 'Saved';
- clearTimeout(successTimer);
- successTimer = setTimeout(() => { successMessage = ''; }, 4000);
-}
-onDestroy(() => clearTimeout(successTimer));
-```
-
-For streaming readers, use `AbortController.signal` on the fetch and call `.abort()` from `onDestroy`. The `getReader()` loop will reject with `AbortError` on the next read, breaking cleanly.
-
-**Symptom this prevents:** memory leaks; ghost background HTTP traffic after navigation; stale toasts appearing on the wrong page.
-
-#### 9.6.6 Top-level `await` in `+layout.js` / `+layout.svelte` needs a fallback
-
-**Rule:** anything awaited in `load()` that can fail (locale JSON, runtime config fetch) MUST have a `.catch()` that allows the layout to render with a sensible fallback. A rejected `load()` blanks the page.
-
-```javascript
-// ❌ BAD — locale 404 blanks the entire app
-export const load = async () => {
- setupI18n();
- await waitLocale();
- return {};
-};
-
-// ✅ GOOD
-export const load = async () => {
- setupI18n();
- try {
- await waitLocale();
- } catch (e) {
- console.error('Locale load failed, using fallback', e);
- // fallback locale is already set by setupI18n()
- }
- return {};
-};
-```
-
-#### 9.6.7 Stores holding user-scoped state must register with `sessionManager`
-
-**Rule:** any store that caches per-user data (assistants, KBs, AAC sessions, drafts, tabs) MUST expose a `reset()` method and be invoked from `resetAllUserScopedStores()` in `$lib/session/sessionManager.js`. Never hold user state in module-level variables that survive logout.
-
-```javascript
-// $lib/stores/myThing.js
-function createMyThingStore() {
- const { subscribe, set, update } = writable(initialState);
- return {
- subscribe,
- // …other methods
- reset: () => set(initialState) // ← required
- };
-}
-
-// $lib/session/sessionManager.js — register here
-import { myThing } from '$lib/stores/myThing';
-export function resetAllUserScopedStores() {
- // …
- myThing.reset();
-}
-```
-
-**Symptom this prevents:** previous user's data appearing after re-login on the same browser.
-
-#### 9.6.8 No silent error swallowing on data fetches
-
-**Rule:** `Promise.all([call1.catch(()=>[]), call2.catch(()=>[])])` is forbidden for primary data loads. It conflates "empty result" with "request failed", so the UI shows "no items" when in reality the user should see an error and a retry option.
-
-If you genuinely want a partial-failure-tolerant fetch, surface the partial state explicitly (`{ owned: [...], shared: null, sharedError: '...' }`) so the UI can show a banner.
-
-#### 9.6.9 Polling loops check errors and unmount
-
-**Rule:** `setInterval` polls MUST:
-1. Stop on unmount (cleared from `onDestroy`)
-2. Stop on auth errors (don't keep hammering after 401)
-3. Surface non-transient errors after N retries (don't silently spin forever)
-
-```javascript
-// ✅ GOOD pattern
-let pollTimer = null;
-let pollFailures = 0;
-
-async function pollOnce() {
- try {
- const status = await apiFetch(`/items/${id}/status`).then(r => r.json());
- state = status;
- pollFailures = 0;
- } catch (e) {
- pollFailures += 1;
- if (pollFailures >= 5) {
- pollError = 'Lost connection to server';
- stopPolling();
- }
- }
-}
-
-function startPolling() {
- pollOnce();
- pollTimer = setInterval(pollOnce, 3000);
-}
-function stopPolling() {
- clearInterval(pollTimer);
- pollTimer = null;
-}
-onDestroy(stopPolling);
-```
-
-#### 9.6.10 Code-review checklist (paste into PR template)
-
-For any frontend PR:
-
-- [ ] Every `loading = true` has a `finally { loading = false }` (Pattern A)
-- [ ] Every `await` in `onMount`/`$effect` has a mounted-check (Pattern B)
-- [ ] Cancellable fetches use `AbortController` and abort on dep change (Pattern C)
-- [ ] All API calls go through `apiFetch` (no direct `fetch()` to backend) (Pattern D)
-- [ ] Every `setInterval`/`setTimeout`/stream has cleanup in `onDestroy` (Pattern E)
-- [ ] No `Promise.all([call.catch(()=>[])])` for primary loads
-- [ ] User-scoped stores have `reset()` and are wired into `sessionManager`
-- [ ] Top-level `await` in layout has a `.catch()` fallback
-
----
-
-## 10. Development & Deployment
-
-### 10.1 Quick Start
-
-```bash
-# Clone
-git clone https://github.com/Lamb-Project/lamb.git
-cd lamb
-
-# Setup
-./scripts/setup.sh
-
-# Start all services
-docker-compose up -d
-
-# Access
-# - Frontend: http://localhost:5173
-# - Backend: http://localhost:9099
-# - Open WebUI: http://localhost:8080
-# - KB Server: http://localhost:9090
-```
-
-### 10.2 Development Workflow
-
-**Backend:**
-```bash
-cd backend
-pip install -r requirements.txt
-uvicorn main:app --reload --port 9099
-```
-
-**Frontend:**
-```bash
-cd frontend/svelte-app
-npm install
-npm run dev
-```
-
-### 10.3 Key Environment Variables
-
-| Variable | Purpose | Default |
-|----------|---------|---------|
-| `LAMB_DB_PATH` | LAMB database directory | `.` |
-| `OWI_DATA_PATH` | OWI data directory | - |
-| `LAMB_BEARER_TOKEN` | API key for completions | `0p3n-w3bu!` |
-| `LAMB_JWT_SECRET` | Secret key for signing LAMB JWT tokens | (required) |
-| `LAMB_JWT_EXPIRATION_HOURS` | JWT token expiration time in hours | `24` |
-| `OPENAI_API_KEY` | OpenAI API key | - |
-| `KB_SERVER_URL` | KB server address | `http://localhost:9090` |
-| `OWI_BASE_URL` | OWI internal URL | `http://openwebui:8080` |
-| `OWI_PUBLIC_BASE_URL` | OWI public URL | - |
-| `LTI_GLOBAL_CONSUMER_KEY` | Unified LTI consumer key | `lamb` |
-| `LTI_GLOBAL_SECRET` | Unified LTI shared secret | (falls back to `LTI_SECRET`) |
-| `LTI_SECRET` | Legacy student LTI secret | - |
-| `LLM_REQUEST_TIMEOUT` | OpenAI request timeout (seconds) | `120` |
-| `LLM_CONNECT_TIMEOUT` | TCP connect timeout (seconds) | `10` |
-| `LLM_MAX_CONNECTIONS` | Max connections per client pool | `50` |
-| `OLLAMA_REQUEST_TIMEOUT` | Ollama request timeout (seconds) | `120` |
-
-> See [ENVIRONMENT_VARIABLES.md](../backend/ENVIRONMENT_VARIABLES.md) for complete list.
-
-### 10.4 Database Migrations
-
-LAMB uses automatic migrations on startup. To add a new field:
-
-1. Add migration code in `lamb/database_manager.py` → `run_migrations()`:
-```python
-def run_migrations(self):
- cursor.execute("PRAGMA table_info(Creator_users)")
- columns = [row[1] for row in cursor.fetchall()]
-
- if 'my_new_field' not in columns:
- cursor.execute("""
- ALTER TABLE Creator_users
- ADD COLUMN my_new_field TEXT DEFAULT ''
- """)
-```
-
-2. Migrations run automatically on backend startup
-3. Designed for backward compatibility (always use DEFAULT values)
-
-### 10.5 Testing
-
-**Test Locations:**
-- `/testing/playwright/` — E2E tests with Playwright
-- `/testing/unit-tests/` — Python unit tests
-- `/testing/curls/` — API test scripts
-- `/testing/load_test_completions.py` — Concurrent load test for completions endpoint
-
-**Running Playwright Tests:**
-```bash
-cd testing/playwright
-npm install
-npx playwright test
-```
-
-**Running Load Tests:**
-```bash
-# Test with 30 concurrent users
-python3 testing/load_test_completions.py --users 30 --url http://localhost:9099
-
-# Test with 50 concurrent users and custom timeout
-python3 testing/load_test_completions.py --users 50 --url http://localhost:9099 --timeout 180
-```
-
-### 10.6 Production Checklist
-
-- [ ] Change `LAMB_BEARER_TOKEN` from default
-- [ ] Set `LAMB_JWT_SECRET` to a secure random value
-- [ ] Configure SSL/TLS via Caddy or reverse proxy
-- [ ] Set up database backups
-- [ ] Configure organization LLM API keys
-- [ ] Review logging levels (`GLOBAL_LOG_LEVEL=WARNING`)
-- [ ] Set up monitoring
-- [ ] Verify password migration from OWI completed (automatic on first startup)
-
----
-
-## 11. Logging System
-
-### 11.1 Centralized Configuration
-
-All logging uses the centralized module at `lamb/logging_config.py`:
-
-```python
-from lamb.logging_config import get_logger
-
-logger = get_logger(__name__, component="API")
-logger.info("Processing request")
-```
-
-### 11.2 Environment Variables
-
-**Global:**
-```bash
-GLOBAL_LOG_LEVEL=WARNING # Default for all components
-```
-
-**Component-Specific (optional overrides):**
-```bash
-MAIN_LOG_LEVEL=INFO
-API_LOG_LEVEL=WARNING
-DB_LOG_LEVEL=DEBUG
-RAG_LOG_LEVEL=INFO
-EVALUATOR_LOG_LEVEL=WARNING
-OWI_LOG_LEVEL=WARNING
-```
-
-### 11.3 Log Levels
-
-| Level | Use For |
-|-------|---------|
-| `DEBUG` | Detailed debugging info |
-| `INFO` | General operational events |
-| `WARNING` | Unexpected but handled situations |
-| `ERROR` | Errors requiring attention |
-| `CRITICAL` | System failures |
-
-### 11.4 Configuration Examples
-
-**Development:**
-```bash
-GLOBAL_LOG_LEVEL=DEBUG
-```
-
-**Production:**
-```bash
-GLOBAL_LOG_LEVEL=WARNING
-```
-
-**Debugging specific component:**
-```bash
-GLOBAL_LOG_LEVEL=WARNING
-DB_LOG_LEVEL=DEBUG
-```
-
----
-
-## 12. Quick Reference
-
-### 12.1 "I Want To..." Directory
-
-| Task | Where to Look |
-|------|---------------|
-| Add API endpoint | `creator_interface/` or `lamb/` routers |
-| Create plugin | `lamb/completions/{pps,connectors,rag}/` |
-| Add database field | `lamb/database_manager.py` → `run_migrations()` |
-| Add frontend page | `frontend/svelte-app/src/routes/` |
-| Configure logging | `lamb/logging_config.py` + env vars |
-| Understand completion flow | `lamb/completions/main.py` |
-| Debug what the LLM sees | `lamb chat --bypass` or `debug_bypass` flag |
-| Debug OWI chat integration | `lamb/owi_bridge/` |
-| Work on AAC agent | `lamb/aac/` (liteshell, agent loop, skills, router) |
-| Add test scenarios | `lamb/services/test_service.py`, `test_router.py` |
-
-### 12.2 Key Files by Task
-
-| Task | Primary Files |
-|------|---------------|
-| **Auth & AuthContext** | `lamb/auth_context.py`, `lamb/auth.py`, `creator_interface/main.py` |
-| **OWI Chat Handoff** | `lamb/owi_bridge/owi_users.py` |
-| **Assistants** | `creator_interface/assistant_router.py`, `lamb/assistant_router.py` |
-| **Completions** | `lamb/completions/main.py`, `lamb/completions/connectors/` |
-| **KBs** | `creator_interface/knowledges_router.py`, `kb_server_manager.py` |
-| **Orgs** | `creator_interface/organization_router.py`, `lamb/organization_router.py` |
-| **LTI (Unified)** | `lamb/lti_router.py`, `lamb/lti_activity_manager.py` |
-| **LTI (Student legacy)** | `lamb/lti_users_router.py` |
-| **LTI (Creator)** | `lamb/lti_creator_router.py` |
-| **AAC (Agent-Assisted Creator)** | `lamb/aac/router.py`, `lamb/aac/agent/loop.py`, `lamb/aac/liteshell/` |
-| **Test Scenarios & Evaluation** | `lamb/services/test_service.py`, `lamb/services/test_router.py` |
-| **Frontend** | `frontend/svelte-app/src/routes/`, `src/lib/components/` |
-
-### 12.3 Common Patterns
-
-**Adding a Protected Endpoint (with AuthContext):**
-```python
-from lamb.auth_context import AuthContext, get_auth_context
-
-@router.get("/my-endpoint")
-async def my_endpoint(auth: AuthContext = Depends(get_auth_context)):
- # auth.user is always populated (401 raised automatically if token invalid)
- user_email = auth.user['email']
- org = auth.organization
- # Your logic here
- return {"success": True}
-```
-
-**Admin-Only Endpoint:**
-```python
-@router.get("/admin/my-endpoint")
-async def admin_endpoint(auth: AuthContext = Depends(get_auth_context)):
- auth.require_system_admin() # Raises 403 if not system admin
- # Admin logic here
-```
-
-**Org-Admin Endpoint (scoped to their organization):**
-```python
-@router.put("/org/{org_id}/settings")
-async def update_org_settings(org_id: int, auth: AuthContext = Depends(get_auth_context)):
- auth.require_org_admin() # Raises 403 if not org admin or system admin
- # IMPORTANT: also verify the target org matches the user's org
- if not auth.is_system_admin and auth.organization.get("id") != org_id:
- raise HTTPException(status_code=403, detail="Access denied for this organization")
- # Update settings...
-```
-
-**Resource Access Guarded Endpoint:**
-```python
-@router.delete("/assistant/{assistant_id}")
-async def delete_assistant(assistant_id: int, auth: AuthContext = Depends(get_auth_context)):
- # Raises 404 if assistant doesn't exist, 403 if user isn't owner or org admin
- auth.require_assistant_access(assistant_id, level="owner_or_admin")
- # Proceed with deletion...
-```
-
-**Optional Auth (for public-ish endpoints):**
-```python
-from lamb.auth_context import get_optional_auth_context
-
-@router.get("/public-or-private")
-async def flexible_endpoint(auth: Optional[AuthContext] = Depends(get_optional_auth_context)):
- if auth:
- # Authenticated — scope to user's org
- return get_data_for_org(auth.organization['id'])
- else:
- # Anonymous — return public data only
- return get_public_data()
-```
-
-**Checking Feature Flags:**
-```python
-@router.post("/share-assistant")
-async def share_assistant(auth: AuthContext = Depends(get_auth_context)):
- if not auth.features.get("sharing_enabled"):
- raise HTTPException(status_code=403, detail="Sharing not enabled for this org")
- # Sharing logic...
-```
-
-**Using Organization Config:**
-```python
-from lamb.completions.organization_config import OrganizationConfigResolver
-
-resolver = OrganizationConfigResolver(user_email)
-openai_config = resolver.get_provider_config("openai")
-api_key = openai_config.get("api_key")
-```
-
-> **Legacy patterns** (`get_creator_user_from_token`, `is_admin_user`) still exist for backward compatibility but **should not be used in new code**. All new endpoints should use `AuthContext`.
-
-### 12.4 API Quick Reference
-
-**Auth:**
-| POST `/creator/login` | POST `/creator/signup` | GET `/creator/user/current` | GET `/creator/owi-session` |
-
-**Assistants:**
-| GET `/creator/assistant/list` | POST `/creator/assistant/create` | PUT `/creator/assistant/update` |
-
-**Knowledge Bases:**
-| GET `/creator/knowledgebases/user` | POST `/creator/knowledgebases/create` | POST `/{id}/upload` |
-
-**Completions:**
-| GET `/v1/models` | POST `/v1/chat/completions` |
-
-**Admin:**
-| GET `/creator/admin/users` | PUT `/creator/admin/users/{id}/status` |
-
----
-
-## 13. Security Summary
-
-### Data Isolation
-- Organizations have independent data namespaces
-- Users can only access resources in their organization
-- Cross-organization access is prevented at data layer
-
-### Authentication Security
-- Passwords stored in LAMB database (`Creator_users.password_hash`), hashed with bcrypt (cost 12)
-- LAMB-issued JWT tokens with expiration, validated via signature (no database round-trip)
-- OWI mirror users have dummy passwords for programmatic chat handoff only
-- Real passwords never stored in OWI
-
-### API Security
-- LAMB_BEARER_TOKEN for external completion API
-- LAMB-issued JWT tokens for creator interface
-- Rate limiting via reverse proxy (recommended)
-
-### Best Practices
-- Change default `LAMB_BEARER_TOKEN` in production
-- Set `LAMB_JWT_SECRET` to a secure random value
-- Use HTTPS in production (via Caddy/nginx)
-- Rotate organization API keys periodically
-- Review user access regularly
-
----
-
-## 14. Troubleshooting
-
-### Common Issues
-
-**"401 Unauthorized" on all requests:**
-- Check token is valid and not expired
-- Verify `LAMB_JWT_SECRET` matches between token issuance and validation
-- Verify user exists in LAMB `Creator_users` table
-
-**Assistant not appearing in OWI:**
-- Check assistant is published (`published = true`)
-- Verify OWI model was created (`lamb_assistant.{id}`)
-- Check group membership
-
-**KB queries returning no results:**
-- Verify documents were uploaded and processed
-- Check collection ID is correct in assistant's `RAG_collections`
-- Test query directly against KB server
-
-**LTI launch failing (Student):**
-- Verify OAuth signature matches
-- Check consumer key/secret match
-- Ensure `custom_assistant_id` is set
-
-**LTI Creator launch failing:**
-- Verify consumer key format: `{org_id}_{name}`
-- Check OAuth signature validation in backend logs
-- Ensure organization has `lti_creator` config set
-- Check that organization is not the system organization
-
-**Chat handoff to OWI failing:**
-- Verify OWI mirror user exists (check OWI `user` table for matching email)
-- Verify OWI is reachable at `OWI_BASE_URL`
-- Check dummy password mechanism is working (LAMB can call OWI signin API)
-- Check backend logs for OWI bridge errors
-
-**Frontend not loading:**
-- Check `static/config.js` has correct API URLs
-- Verify backend is running on expected port
-- Check browser console for CORS errors
-
-### Debug Logging
-
-Enable verbose logging:
-```bash
-GLOBAL_LOG_LEVEL=DEBUG
-DB_LOG_LEVEL=DEBUG
-API_LOG_LEVEL=DEBUG
-```
-
----
-
-## Related Documentation
-
-| Document | Purpose |
-|----------|---------|
-| [new_lamb_auth_tldr.md](./new_lamb_auth_tldr.md) | **AuthContext TL;DR** — quick reference for the new auth system |
-| [DOCUMENTATION_INDEX.md](./DOCUMENTATION_INDEX.md) | Quick navigation guide |
-| [lamb_architecture.md](./lamb_architecture.md) | Full detailed reference |
-| [chat_analytics_project.md](./chat_analytics_project.md) | Analytics implementation |
-| [ENVIRONMENT_VARIABLES.md](../backend/ENVIRONMENT_VARIABLES.md) | All env vars |
-| [prd.md](./prd.md) | Product requirements |
-| [deployment.apache.md](./deployment.apache.md) | Apache deployment |
-| [features/](./features/) | Detailed feature documentation |
-
----
-
-## Support
-
-- **GitHub:** https://github.com/Lamb-Project/lamb
-- **Website:** https://lamb-project.org
-
----
-
-*Maintainers: LAMB Development Team*
-*Last Updated: March 31, 2026*
-*Version: 2.6*
-
diff --git a/Documentation/lti_identity_isolation.png b/Documentation/lti_identity_isolation.png
deleted file mode 100644
index 15b9885be..000000000
Binary files a/Documentation/lti_identity_isolation.png and /dev/null differ
diff --git a/Documentation/lti_landscape_overview.png b/Documentation/lti_landscape_overview.png
deleted file mode 100644
index 0425338d5..000000000
Binary files a/Documentation/lti_landscape_overview.png and /dev/null differ
diff --git a/Documentation/svelte-refactoring.md b/Documentation/svelte-refactoring.md
deleted file mode 100644
index 303eac51b..000000000
--- a/Documentation/svelte-refactoring.md
+++ /dev/null
@@ -1,1247 +0,0 @@
-# LAMB Frontend Refactoring Plan
-
-**Document Version:** 2.4
-**Date:** January 19, 2026
-**Status:** Phase 1 Complete - Phase 2 Complete - Phase 3 In Progress
-
-### Changelog
-
-| Version | Date | Changes |
-|---------|------|---------|
-| 2.4 | 2026-01-19 | Added `shared/UserActionModal.svelte` for enable/disable confirmations (single + bulk). Total reduction now: 790 lines combined (-24% from admin, -7% from org-admin). |
-| 2.3 | 2026-01-19 | **Pivoted to shared components architecture.** Created `shared/UserForm.svelte` and `shared/ChangePasswordModal.svelte`. Both `admin/` and `org-admin/` now use same components with role-awareness via `isSuperAdmin` prop. Total reduction: 585 lines across both pages. |
-| 2.2 | 2026-01-19 | Extracted `AdminUserForm.svelte` from `admin/+page.svelte` (-143 lines). Total reduction: 365 lines. |
-| 2.1 | 2026-01-19 | Phase 3 started. Created `admin/` components folder. Extracted `AdminDashboard.svelte` from `admin/+page.svelte` (-222 lines). |
-| 2.0 | 2026-01-19 | Migrated all admin page `confirm()` dialogs: `admin/+page.svelte` (delete org), `org-admin/+page.svelte` (reset KB config). Removed dead code for old bulk enable/disable handlers. **All native `confirm()` dialogs eliminated!** |
-| 1.9 | 2026-01-19 | Migrated remaining `confirm()` dialogs: `ChatInterface.svelte` (delete chat), `RubricTable.svelte` (remove criterion/level), `RubricEditor.svelte` (discard changes). Phase 2 complete. |
-| 1.8 | 2026-01-19 | Removed redundant unpublish functionality from `AssistantsList.svelte` (now only in detail view). Cleaned up unused `unpublishAssistant` import and `IconUnpublish`. |
-| 1.7 | 2026-01-19 | Migrated `KnowledgeBaseDetail.svelte` delete file and cancel job confirmations from native `confirm()` to `ConfirmationModal`. Added Playwright test `kb_detail_modals.spec.js`. Fixed Vite warning about mixed dynamic/static imports in `assistantService.js`. |
-| 1.6 | 2026-01-19 | Migrated `KnowledgeBasesList.svelte` delete confirmation from native `confirm()` to `ConfirmationModal`. Added Playwright test `kb_delete_modal.spec.js`. |
-| 1.5 | 2026-01-18 | Migrated `AssistantsList.svelte` and `assistants/+page.svelte` delete modals to use generic `ConfirmationModal`. Deleted redundant `DeleteConfirmationModal.svelte`. Fixed modal placement (moved outside loop). |
-| 1.4 | 2026-01-18 | Created `useLocaleReady.js` utility. Simplified locale tracking pattern using `$derived`. Updated `ConfirmationModal` to use new pattern. |
-| 1.3 | 2026-01-18 | Phase 2 started. Created generic `ConfirmationModal.svelte` component. Migrated `PromptTemplatesContent.svelte` and `prompt-templates/+page.svelte` delete modals. |
-| 1.2 | 2026-01-18 | Completed Phase 1. Removed orphaned `AssistantAccessManager` feature (component, page, service functions). Investigation revealed it was superseded by `AssistantSharingModal`. |
-| 1.1 | 2026-01-18 | Initial analysis complete, Phase 1 quick wins implemented |
-
----
-
-## ⚠️ MANDATORY REFACTORING METHODOLOGY
-
-> **CRITICAL**: The coding agent MUST follow this methodology for EVERY refactoring task.
-> No code may be modified or removed without completing all preparation steps first.
-
-### Overview
-
-This methodology ensures safe, verified refactoring by requiring comprehensive testing BEFORE and AFTER any code changes. The user maintains control at key checkpoints.
-
-```
-┌─────────────────────────────────────────────────────────────────┐
-│ REFACTORING WORKFLOW │
-├─────────────────────────────────────────────────────────────────┤
-│ │
-│ 1. PREPARATION (before touching ANY code) │
-│ ├─ 1.0 Choose limited scope │
-│ ├─ 1.1 Identify affected UI areas │
-│ ├─ 1.2 Agent tests current functionality (browser) │
-│ ├─ 1.3 User confirms it works ← USER CHECKPOINT │
-│ ├─ 1.4 Write Playwright test │
-│ └─ 1.5 User runs Playwright test ← USER CHECKPOINT │
-│ │
-│ 2. IMPLEMENTATION │
-│ └─ Do the actual fix/refactoring │
-│ │
-│ 3. VERIFICATION │
-│ ├─ 3.1 Agent tests with browser │
-│ ├─ 3.2 User tests manually ← USER CHECKPOINT │
-│ └─ 3.3 User runs Playwright tests ← USER CHECKPOINT │
-│ │
-│ 4. ITERATE │
-│ └─ Only proceed to next item when ALL tests pass │
-│ │
-└─────────────────────────────────────────────────────────────────┘
-```
-
-### Step-by-Step Instructions
-
-#### Phase 1: PREPARATION (Before Touching ANY Code)
-
-##### 1.0 Choose Limited Scope
-- Select ONE specific, well-defined refactoring task
-- Keep the scope small and manageable
-- Document exactly what will be changed
-
-##### 1.1 Identify Affected UI Areas
-- List all routes/pages that use the code being modified
-- List all components that depend on it
-- Document the user-facing functionality
-
-##### 1.2 Agent Tests Current Functionality
-- Use `cursor-ide-browser` MCP tool to navigate to affected areas
-- **URL for testing**: `http://localhost:5173` (development frontend)
-- Take screenshots to document current behavior
-- Verify all functionality works BEFORE any changes
-- Document the expected behavior
-
-> **Note**: The agent uses Cursor's internal browser (`cursor-ide-browser` MCP)
-> to test the application at `http://localhost:5173`. This provides visual
-> verification of functionality before and after changes.
-
-##### 1.3 User Confirmation Checkpoint ⏸️
-```
-┌────────────────────────────────────────────────────────────┐
-│ 🛑 STOP - USER CHECKPOINT │
-│ │
-│ Agent must ask the user: │
-│ "I have tested [feature] in the browser. Please confirm │
-│ that [specific functionality] is working correctly │
-│ before I proceed." │
-│ │
-│ DO NOT PROCEED until user confirms. │
-└────────────────────────────────────────────────────────────┘
-```
-
-##### 1.4 Write Playwright Test
-- Write a Playwright test that covers the functionality being modified
-- Test should verify the CURRENT working behavior
-- Follow patterns from existing tests in `/testing/playwright/tests/`
-- Place new test file appropriately
-
-##### 1.5 User Runs Playwright Test ⏸️
-```
-┌────────────────────────────────────────────────────────────┐
-│ 🛑 STOP - USER CHECKPOINT │
-│ │
-│ Agent must instruct the user: │
-│ "Please run the Playwright test to verify it passes: │
-│ │
-│ cd testing/playwright │
-│ npx playwright test tests/[test_file].spec.js │
-│ │
-│ Let me know when the test passes." │
-│ │
-│ DO NOT PROCEED until user confirms test passes. │
-└────────────────────────────────────────────────────────────┘
-```
-
-#### Phase 2: IMPLEMENTATION
-
-##### 2.0 Make the Changes
-- Only NOW may the agent modify/remove code
-- Make changes incrementally
-- Keep changes focused on the defined scope
-- Do not introduce unrelated changes
-
-#### Phase 3: VERIFICATION
-
-##### 3.1 Agent Tests with Browser
-- Use `cursor-ide-browser` MCP tool to test the modified functionality
-- **URL for testing**: `http://localhost:5173` (development frontend)
-- Take screenshots to document the new behavior
-- Verify all functionality still works as expected
-- Compare with pre-change screenshots if helpful
-
-##### 3.2 User Manual Testing ⏸️
-```
-┌────────────────────────────────────────────────────────────┐
-│ 🛑 STOP - USER CHECKPOINT │
-│ │
-│ Agent must ask the user: │
-│ "I have completed the changes and tested them. Please │
-│ manually verify that [specific functionality] still │
-│ works correctly in your browser." │
-│ │
-│ DO NOT PROCEED until user confirms. │
-└────────────────────────────────────────────────────────────┘
-```
-
-##### 3.3 User Runs Playwright Tests ⏸️
-```
-┌────────────────────────────────────────────────────────────┐
-│ 🛑 STOP - USER CHECKPOINT │
-│ │
-│ Agent must instruct the user: │
-│ "Please run ALL Playwright tests to verify nothing broke: │
-│ │
-│ cd testing/playwright │
-│ npm test │
-│ │
-│ Let me know the results." │
-│ │
-│ DO NOT PROCEED until all tests pass. │
-└────────────────────────────────────────────────────────────┘
-```
-
-#### Phase 4: ITERATE
-
-- Only when ALL tests pass (Playwright + manual) may the agent proceed
-- If any test fails:
- - Fix the issue
- - Return to Phase 3 verification
-- Select the next refactoring task and return to Phase 1
-
-### Development Environment Setup
-
-The application runs in Docker containers. For testing and refactoring, ensure the following services are running:
-
-#### Docker Services (from `docker-compose.yaml`)
-
-| Service | Port | Description |
-|---------|------|-------------|
-| `frontend` | **5173** | Development frontend (Vite dev server with hot reload) |
-| `backend` | **9099** | LAMB backend API (FastAPI) |
-| `openwebui` | 8080 | Open WebUI service |
-| `kb` | 9090 | Knowledge Base server |
-
-#### Starting the Development Environment
-
-```bash
-# From repository root
-docker-compose up -d
-
-# Or start specific services
-docker-compose up -d frontend backend kb
-```
-
-#### URLs for Testing
-
-| Purpose | URL |
-|---------|-----|
-| **Frontend (dev)** | http://localhost:5173 |
-| **Backend API** | http://localhost:9099 |
-| **Playwright tests** | Uses http://localhost:9099 by default |
-
-#### Admin Credentials (Development)
-
-The Playwright tests use these default credentials (from `global-setup.js`):
-
-```
-Email: admin@owi.com
-Password: admin
-```
-
-These can be overridden with environment variables:
-- `LOGIN_EMAIL` - Admin email
-- `LOGIN_PASSWORD` - Admin password
-
-The actual credentials for your environment are in `backend/.env`.
-
-#### Playwright Environment Variables
-
-| Variable | Default | Description |
-|----------|---------|-------------|
-| `BASE_URL` | `http://localhost:9099/` | Base URL for tests |
-| `LOGIN_EMAIL` | `admin@owi.com` | Admin login email |
-| `LOGIN_PASSWORD` | `admin` | Admin login password |
-| `FORCE_RELOGIN` | (unset) | Set to force re-authentication |
-| `CI` | (unset) | Set in CI environments for headless mode |
-
-### Testing Commands Reference
-
-```bash
-# Navigate to test directory
-cd testing/playwright
-
-# Install dependencies (first time only)
-npm install
-npx playwright install
-npx playwright install-deps
-
-# Run all tests
-npm test
-
-# Run specific test file
-npx playwright test tests/[filename].spec.js
-
-# Run tests with UI (interactive mode)
-npm run test:ui
-
-# View HTML report after test run
-npm run report
-
-# Force re-login (clear cached auth state)
-FORCE_RELOGIN=1 npm test
-```
-
-#### Verifying the Environment
-
-Before starting any refactoring work, verify the environment is working:
-
-```bash
-# 1. Check Docker containers are running
-docker-compose ps
-
-# 2. Check frontend is accessible
-curl -I http://localhost:5173
-
-# 3. Check backend is accessible
-curl -I http://localhost:9099
-
-# 4. Run Playwright tests to verify everything works
-cd testing/playwright
-npm test
-```
-
-### Example Refactoring Session
-
-```
-Agent: "I will now refactor the AssistantsList component to remove
- the commented PublishModal code.
-
- 1.1 AFFECTED UI: /assistants page, assistant list view
-
- 1.2 Let me test the current functionality..."
- [Agent uses browser to navigate and test]
- [Agent takes screenshots]
-
-Agent: "🛑 USER CHECKPOINT: I have verified the assistants list
- displays correctly with View, Export, and Delete buttons.
- Please confirm this is working correctly before I proceed."
-
-User: "Confirmed, it works."
-
-Agent: "1.4 I will now write a Playwright test for this functionality..."
- [Agent writes test]
-
-Agent: "🛑 USER CHECKPOINT: Please run the test:
- cd testing/playwright
- npx playwright test tests/assistants_list.spec.js"
-
-User: "Test passed."
-
-Agent: "2.0 Now implementing the change..."
- [Agent removes commented code]
-
-Agent: "3.1 Testing the changes..."
- [Agent uses browser to verify]
-
-Agent: "🛑 USER CHECKPOINT: Changes complete. Please manually verify
- the assistants list still works correctly."
-
-User: "Looks good."
-
-Agent: "🛑 USER CHECKPOINT: Please run all Playwright tests:
- cd testing/playwright && npm test"
-
-User: "All tests passed."
-
-Agent: "✅ Refactoring complete. Ready to proceed to next task."
-```
-
----
-
-## Executive Summary
-
-This document outlines a comprehensive refactoring plan for the LAMB frontend (`/frontend/svelte-app/`). The analysis identified several categories of issues including unused code, oversized components, inconsistent patterns, and opportunities for improved component architecture. The plan includes specific recommendations, new component proposals, and testing strategies using Playwright.
-
----
-
-## Table of Contents
-
-1. [Analysis Checklist](#1-analysis-checklist)
-2. [Codebase Architecture Overview](#2-codebase-architecture-overview)
-3. [Critical Issues - Offenders](#3-critical-issues---offenders)
-4. [Unused Code Inventory](#4-unused-code-inventory)
-5. [Spaghetti Code Hotspots](#5-spaghetti-code-hotspots)
-6. [Proposed Component Extractions](#6-proposed-component-extractions)
-7. [New Components to Develop](#7-new-components-to-develop)
-8. [Code Quality Improvements](#8-code-quality-improvements)
-9. [Testing Strategy](#9-testing-strategy)
-10. [Implementation Phases](#10-implementation-phases)
-11. [Risk Assessment](#11-risk-assessment)
-
----
-
-## 1. Analysis Checklist
-
-### Routes Analyzed ✅
-
-| File | Status | Size | Issues |
-|------|--------|------|--------|
-| `src/routes/+layout.svelte` | ✅ Analyzed | Small | Minor - commented TODOs |
-| `src/routes/+layout.js` | ✅ Analyzed | Small | Clean |
-| `src/routes/+page.svelte` | ✅ Analyzed | Medium | Clean |
-| `src/routes/admin/+page.svelte` | ✅ Analyzed | **~181KB** | **CRITICAL - needs splitting** |
-| `src/routes/assistants/+page.svelte` | ✅ Analyzed | Large | Medium complexity |
-| `src/routes/chat/+page.svelte` | ❌ **REMOVED** | — | Was: Hardcoded API_KEY, security risk, orphaned page |
-| `src/routes/evaluaitor/+page.svelte` | ✅ Analyzed | Medium | Clean |
-| `src/routes/evaluaitor/[rubricId]/+page.svelte` | ✅ Analyzed | Small | Clean |
-| `src/routes/knowledgebases/+page.svelte` | ✅ Analyzed | Medium | Clean |
-| `src/routes/org-admin/+page.svelte` | ✅ Analyzed | **Large** | **Needs splitting** |
-| `src/routes/org-admin/assistants/+page.svelte` | ❌ **REMOVED** | — | Redundant page (functionality in org-admin) |
-| `src/routes/prompt-templates/+page.svelte` | ✅ Analyzed | Medium | Clean |
-
-### Components Analyzed ✅
-
-| File | Status | Issues |
-|------|--------|--------|
-| `lib/components/Nav.svelte` | ✅ Analyzed | Commented Help System code |
-| `lib/components/Footer.svelte` | ✅ Analyzed | Clean |
-| `lib/components/Login.svelte` | ✅ Analyzed | Clean |
-| `lib/components/Signup.svelte` | ✅ Analyzed | Clean |
-| `lib/components/AssistantsList.svelte` | ✅ Analyzed | Commented PublishModal, placeholder handleClone |
-| `lib/components/AssistantAccessManager.svelte` | ❌ **REMOVED** | Orphaned feature - access control not needed |
-| `lib/components/ChatInterface.svelte` | ✅ Analyzed | Large but manageable |
-| `lib/components/KnowledgeBasesList.svelte` | ✅ Analyzed | Clean |
-| `lib/components/KnowledgeBaseDetail.svelte` | ✅ Analyzed | **~117KB - needs splitting** |
-| `lib/components/PublishModal.svelte` | ✅ Analyzed | Clean |
-| `lib/components/LanguageSelector.svelte` | ✅ Analyzed | Clean |
-| **assistants/** | | |
-| `lib/components/assistants/AssistantForm.svelte` | ✅ Analyzed | Large, well-structured |
-| `lib/components/assistants/AssistantSharingModal.svelte` | ✅ Analyzed | Not part of AssistantForm refactor; extensive inline styles |
-| `lib/components/assistants/AssistantSharing.svelte` | ✅ Analyzed | Not part of AssistantForm refactor; unused by routes; may overlap with modal |
-| **analytics/** | | |
-| `lib/components/analytics/ChatAnalytics.svelte` | ✅ Analyzed | Need to verify usage |
-| **common/** | | |
-| `lib/components/common/FilterBar.svelte` | ✅ Analyzed | Clean, reusable |
-| `lib/components/common/Pagination.svelte` | ✅ Analyzed | Clean, reusable |
-| **evaluaitor/** | | |
-| `lib/components/evaluaitor/RubricsList.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricForm.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricEditor.svelte` | ✅ Analyzed | Large |
-| `lib/components/evaluaitor/RubricTable.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricPreview.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricMetadataForm.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricAIGenerationModal.svelte` | ✅ Analyzed | Clean |
-| `lib/components/evaluaitor/RubricAIChat.svelte` | ✅ Analyzed | Clean |
-| **modals/** | | |
-| `lib/components/modals/ConfirmationModal.svelte` | ✅ Analyzed | Generic confirmation modal (NEW) |
-| ~~`lib/components/modals/DeleteConfirmationModal.svelte`~~ | ❌ **REMOVED** | Replaced by generic ConfirmationModal |
-| `lib/components/modals/CreateKnowledgeBaseModal.svelte` | ✅ Analyzed | Clean |
-| `lib/components/modals/TemplateSelectModal.svelte` | ✅ Analyzed | Clean |
-| **promptTemplates/** | | |
-| `lib/components/promptTemplates/PromptTemplatesContent.svelte` | ✅ Analyzed | Large but well-organized |
-
-### Services Analyzed ✅
-
-| File | Status | Issues |
-|------|--------|--------|
-| `lib/services/assistantService.js` | ✅ Analyzed | **Many commented-out functions, debug logs** |
-| `lib/services/authService.js` | ✅ Analyzed | Clean |
-| `lib/services/adminService.js` | ✅ Analyzed | Clean |
-| `lib/services/knowledgeBaseService.js` | ✅ Analyzed | Clean |
-| `lib/services/organizationService.js` | ✅ Analyzed | Clean |
-| `lib/services/rubricService.js` | ✅ Analyzed | Clean |
-| `lib/services/templateService.js` | ✅ Analyzed | Clean |
-| `lib/services/analyticsService.js` | ✅ Analyzed | Clean |
-
-### Stores Analyzed ✅
-
-| File | Status | Issues |
-|------|--------|--------|
-| `lib/stores/userStore.js` | ✅ Analyzed | Clean |
-| `lib/stores/assistantStore.js` | ✅ Analyzed | Clean |
-| `lib/stores/assistantConfigStore.js` | ✅ Analyzed | Clean |
-| `lib/stores/assistantPublish.js` | ✅ Analyzed | Clean |
-| `lib/stores/templateStore.js` | ✅ Analyzed | Clean |
-| `lib/stores/rubricStore.svelte.js` | ✅ Analyzed | Clean |
-
-### Utilities Analyzed ✅
-
-| File | Status | Issues |
-|------|--------|--------|
-| `lib/config.js` | ✅ Analyzed | **Debug console.logs present** |
-| `lib/utils/listHelpers.js` | ✅ Analyzed | Clean, well-documented |
-| `lib/utils/nameSanitizer.js` | ✅ Analyzed | Clean |
-| `lib/i18n/index.js` | ✅ Analyzed | Clean |
-
-### Tests Analyzed ✅
-
-| File | Status | Coverage |
-|------|--------|----------|
-| `testing/playwright/tests/creator_flow.spec.js` | ✅ Analyzed | KB, Assistant CRUD, Chat |
-| `testing/playwright/tests/admin_and_sharing_flow.spec.js` | ✅ Analyzed | Admin, Users, Orgs, Sharing |
-| `testing/playwright/tests/moodle_lti.spec.js` | ✅ Analyzed | LTI integration |
-
----
-
-## 2. Codebase Architecture Overview
-
-### Current Structure
-
-```
-src/
-├── routes/ # SvelteKit routes
-│ ├── admin/ # System admin interface
-│ ├── assistants/ # Assistant management
-│ ├── chat/ # Direct chat interface
-│ ├── evaluaitor/ # Rubric management
-│ ├── knowledgebases/ # KB management
-│ ├── org-admin/ # Organization admin
-│ └── prompt-templates/ # Template management
-├── lib/
-│ ├── components/ # Reusable components
-│ │ ├── analytics/ # Analytics components
-│ │ ├── assistants/ # Assistant-specific components
-│ │ ├── common/ # Shared UI components
-│ │ ├── evaluaitor/ # Rubric components
-│ │ ├── modals/ # Modal dialogs
-│ │ └── promptTemplates/ # Template components
-│ ├── services/ # API service layer
-│ ├── stores/ # Svelte stores
-│ ├── utils/ # Utility functions
-│ └── i18n/ # Internationalization
-└── app.css # Global styles
-```
-
-### Technology Stack
-- **Framework**: Svelte 5 with SvelteKit
-- **Language**: JavaScript with JSDOC (NOT TypeScript)
-- **Styling**: TailwindCSS + component-scoped CSS
-- **HTTP**: Axios + native fetch
-- **i18n**: svelte-i18n
-- **State**: Svelte stores + Svelte 5 runes
-
----
-
-## 3. Critical Issues - Offenders
-
-### 🔴 CRITICAL: Oversized Components
-
-#### 3.1 `admin/+page.svelte` (~181KB, ~4500+ lines estimated)
-
-**Problem**: This single file contains ALL admin functionality including:
-- Dashboard view
-- User management (list, create, edit, enable/disable, change password, bulk operations)
-- Organization management (list, create, delete)
-- Multiple modals and forms
-- Extensive inline styles
-
-**Impact**:
-- Extremely difficult to maintain
-- Cannot be code-split effectively
-- Testing individual features is complex
-- High cognitive load for developers
-
-**Severity**: 🔴 CRITICAL
-
----
-
-#### 3.2 `KnowledgeBaseDetail.svelte` (~117KB, ~3000+ lines estimated)
-
-**Problem**: Monolithic component handling:
-- Files tab (file listing, uploads)
-- Ingest tab (plugin selection, parameter configuration, job management)
-- Query tab (search interface, results display)
-- Complex state for ingestion parameters with conditional visibility
-
-**Impact**:
-- Poor separation of concerns
-- Difficult to test individual features
-- Performance impact from large component tree
-
-**Severity**: 🔴 CRITICAL
-
----
-
-#### 3.3 `org-admin/+page.svelte` (Large, ~500+ lines)
-
-**Problem**: Similar issues to admin page but scoped to organizations:
-- Dashboard, users, assistants, settings views
-- Complex model selection UI
-- Change tracking for unsaved settings
-- Multiple inline form patterns
-
-**Severity**: 🟠 HIGH
-
----
-
-### 🟠 HIGH: Inconsistent Patterns
-
-#### 3.4 Mixed Svelte Syntax
-
-**Components using legacy `$:` reactive syntax:**
-- ~~`AssistantAccessManager.svelte`~~ - REMOVED (was orphaned feature)
-
-**Impact**: Inconsistency makes the codebase harder to maintain and reason about.
-
-#### 3.5 Mixed Styling Approaches
-
-Some components use:
-- TailwindCSS classes (preferred)
-- Inline `
-
-
-
-
-
🐏
-
-
LAMB Activity Setup
- {% if context_title %}
-
Course: {{ context_title }}
- {% endif %}
-
-
-
-
-
-
-
-
-