Skip to content

Comments

Docker compose inclu9de#181

Closed
thestumonkey wants to merge 21 commits intoSimpleOpenSoftware:mainfrom
Ushadow-io:docker-compose-inclu9de
Closed

Docker compose inclu9de#181
thestumonkey wants to merge 21 commits intoSimpleOpenSoftware:mainfrom
Ushadow-io:docker-compose-inclu9de

Conversation

@thestumonkey
Copy link
Contributor

@thestumonkey thestumonkey commented Dec 9, 2025

  • Smoothed some edges over with mycelia integration
  • Added support to run multiple environments on same machine
  • Auto SSL with tailscale, no need for exceptions - works on mobile
  • Add detailed installed to configure and run environments using docker include
  • Added graph memory and visualisation to open memory

Summary by CodeRabbit

  • New Features

    • Interactive setup wizard for configuration
    • Mycelia memory provider integration with auto-login
    • Timeline visualizations for conversations and memories with Gantt charts
    • Memory detail page and metadata display
    • Multi-environment deployment support with shared infrastructure
    • Caddy reverse proxy for multi-environment access
    • Memory provider switching capability via admin panel
  • Improvements

    • Enhanced Docker setup with modular compose structure
    • Comprehensive OS-specific installation guides (Windows, macOS, Linux)
    • Tailscale integration for remote access and distributed deployments
    • Refined memory and conversation search with improved UI
    • Audio recording with better WebSocket connectivity handling
    • Zombie job detection for worker reliability
  • Documentation

    • Detailed setup guides for prerequisites, installation, and configuration
    • Advanced deployment guides (multi-environment, Caddy, Tailscale)
    • SSL/TLS setup documentation
    • Architecture documentation for shared vs. isolated infrastructure

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 9, 2025

Walkthrough

This PR introduces Mycelia memory provider integration, a setup wizard for multi-environment configuration, Docker Compose modularization, extensive documentation for deployment and setup, and frontend UI enhancements including timeline visualizations. It adds Git submodules for Mycelia and OpenMemory, restructures the backend memory service architecture, and includes significant configuration and script additions for secrets management, Tailscale integration, and SSL/TLS setup.

Changes

Cohort / File(s) Change Summary
Setup Wizard & Secrets Management
.env.secrets.template, WIZARD.md, Makefile
New secrets template with guidance, interactive wizard for configuration setup via make wizard with sub-targets for secrets, Tailscale, and environment configuration. Expanded Makefile with 30+ new public targets for environment, infrastructure, Mycelia sync, and Caddy management.
Configuration & Environment
config-docker.env, config-k8s.env, backends/advanced/.env.template, backends/advanced/docker-compose.override.yml
Added environment-aware configuration variables for SSL/TLS, Tailscale, and new Mycelia memory provider option; expanded Kubernetes config with SSL-related variables; added development overrides template.
Docker Compose Refactoring
backends/advanced/docker-compose.yml, backends/advanced/compose/*, backends/advanced/generate-caddyfile.sh
Migrated from monolithic to modular includes-based structure with separate backend, frontend, infrastructure files; added environment-specific overrides (dev, test, prod); introduced Caddyfile generation from template; added external chronicle-network.
Git & Project Structure
.gitignore, .gitmodules, ACT_GUIDE.md (removed)
Added ignore patterns for secrets, certificates, IDE directories, Docker data; registered two submodules (mycelia, openmemory); removed GitHub Actions testing guide.
Mycelia Integration: Backend Services
backends/advanced/scripts/create_mycelia_api_key.py, backends/advanced/scripts/sync_friendlite_mycelia.py, backends/advanced/src/advanced_omi_backend/services/mycelia_sync.py
New Python scripts for programmatic Mycelia API key creation and Friend-Lite/Mycelia OAuth synchronization; includes user selection, hashing with salt, and orphaned object reassignment.
Mycelia Integration: Memory Service
backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py, backends/advanced/src/advanced_omi_backend/services/memory/config.py, backends/advanced/src/advanced_omi_backend/services/memory/prompts.py
New MyceliaMemoryService implementation with JWT auth, Mycelia HTTP API calls, LLM-based fact/temporal extraction, and MemoryEntry conversion; added MYCELIA to MemoryProvider enum; added temporal extraction prompt and data models.
Memory Service Restructuring
backends/advanced/src/advanced_omi_backend/services/memory/__init__.py, backends/advanced/src/advanced_omi_backend/services/memory/base.py, backends/advanced/src/advanced_omi_backend/services/memory/providers/__init__.py
Refactored memory service from monolithic to modular providers; removed old compat_service; moved base classes to new location; added to_dict() on MemoryEntry; expanded MemoryServiceBase with get_memory, update_memory, and lazy initialization support.
Memory Service Imports & Configuration
backends/advanced/src/advanced_omi_backend/services/memory/service_factory.py, backends/advanced/src/advanced_omi_backend/app_factory.py, backends/advanced/src/advanced_omi_backend/chat_service.py, backends/advanced/src/advanced_omi_backend/controllers/*.py, backends/advanced/src/advanced_omi_backend/routers/*.py (multiple files)
Updated import paths from advanced_omi_backend.memory to advanced_omi_backend.services.memory; added Mycelia provider factory branch; integrated admin sync on startup.
Speech Detection & Conversation
backends/advanced/src/advanced_omi_backend/config.py, backends/advanced/src/advanced_omi_backend/utils/conversation_utils.py, backends/advanced/src/advanced_omi_backend/models/conversation.py
Increased speech detection thresholds (min_words 5→10, min_confidence 0.5→0.7, min_duration 2.0→10.0s); added minimum duration validation; added MYCELIA to MemoryProvider enum.
Worker Jobs & Job Management
backends/advanced/src/advanced_omi_backend/utils/job_utils.py, backends/advanced/src/advanced_omi_backend/workers/*.py
New check_job_alive() utility to detect zombie RQ jobs; integrated zombie detection into audio streaming, conversation, and transcription jobs; added meaningful speech validation with job cancellation in transcription jobs.
Frontend Routing & Navigation
backends/advanced/webui/src/App.tsx, backends/advanced/webui/src/components/layout/Layout.tsx
Switched page components to router variants; added base path handling for routing; added Timeline navigation item with Calendar icon.
Frontend Timeline Components
backends/advanced/webui/src/pages/ConversationsRouter.tsx, backends/advanced/webui/src/pages/ConversationsTimeline.tsx, backends/advanced/webui/src/pages/FrappeGanttTimeline.tsx, backends/advanced/webui/src/pages/ReactGanttTimeline.tsx, backends/advanced/webui/src/pages/MyceliaTimeline.tsx, backends/advanced/webui/src/pages/TimelineRouter.tsx, backends/advanced/webui/src/pages/MemoryDetail.tsx, backends/advanced/webui/src/pages/MemoriesRouter.tsx
New timeline visualizations (Frappe Gantt, React Gantt, Mycelia D3-based); new MemoryDetail page; tabbed router components for timeline and conversation views; memory detail display with type, time ranges, and metadata.
Frontend Memory & System UI
backends/advanced/webui/src/pages/Memories.tsx, backends/advanced/webui/src/pages/System.tsx
Added memory click navigation to detail page; made memory cards clickable with metadata display; added memory provider selection UI in System page with provider switching and status display.
Frontend Utilities & Hooks
backends/advanced/webui/src/hooks/useD3Zoom.ts, backends/advanced/webui/src/contexts/AuthContext.tsx, backends/advanced/webui/src/hooks/useAudioRecording.ts, backends/advanced/webui/src/hooks/useSimpleAudioRecording.ts, backends/advanced/webui/src/services/api.ts, backends/advanced/webui/src/utils/storage.ts (implied)
New D3 zoom hook for interactive visualizations; centralized storage key management via getStorageKey; added Mycelia JWT token persistence; enhanced backend URL resolution; added memory provider endpoints.
Frontend Build & Dependencies
backends/advanced/webui/Dockerfile, backends/advanced/webui/Dockerfile.dev, backends/advanced/webui/package.json, backends/advanced/webui/nginx.conf, backends/advanced/webui/public/frappe-gantt.css
Updated Node base image to 22; added VITE_BASE_PATH build arg for path-based routing; updated npm from ci to install with legacy-peer-deps; added timeline libraries (d3, frappe-gantt, react-gantt-timeline, react-vertical-timeline-component); added Nginx path prefix header support; added Frappe Gantt CSS.
Frontend Type Declarations
backends/advanced/webui/src/types/react-gantt-timeline.d.ts, backends/advanced/webui/index.html
Added TypeScript declaration module for react-gantt-timeline; added frappe-gantt CSS link in HTML head.
Setup & Deployment Documentation
INSTALL.md, CLAUDE.md, DOCKER-COMPOSE.md, README.md (link added), Docs/development/README.md, Docs/development/DOCKER-SETUP-SUMMARY.md, Docs/development/SETUP_COMPLETE.md, Docs/development/SETUP_WIZARD_SUMMARY.md
Comprehensive installation guide; Docker Compose unified approach documentation; setup wizard documentation with workflows and troubleshooting; production-ready setup reference.
Advanced Setup Guides
Docs/setup/prerequisites.md, Docs/setup/linux.md, Docs/setup/macos.md, Docs/setup/windows-wsl2.md, Docs/setup/windows-gitbash.md, Docs/setup/tailscale.md, Docs/setup/advanced/README.md, Docs/setup/advanced/ENVIRONMENTS.md, Docs/setup/advanced/MULTI_ENVIRONMENT_ARCHITECTURE.md, Docs/setup/advanced/CADDY_SETUP.md, Docs/setup/advanced/SSL_SETUP.md, Docs/setup/advanced/TAILSCALE-SERVE-GUIDE.md, Docs/setup/advanced/TAILSCALE_GUIDE.md
OS-specific setup guides (Linux, macOS, Windows WSL2/Git Bash); multi-environment architecture and management; Caddy reverse proxy configuration; SSL/TLS options; Tailscale integration guides.
Mycelia Documentation
backends/advanced/Docs/README.md, backends/advanced/Docs/mycelia-setup.md, backends/advanced/Docs/mycelia-auto-login.md, backends/advanced/Docs/mycelia-auth-and-ownership.md
Mycelia memory interface setup; OAuth and auto-login authentication methods; object ownership and authentication flow documentation.
Backend Database & Configuration
backends/advanced/src/advanced_omi_backend/database.py, backends/advanced/src/advanced_omi_backend/app_config.py, backends/advanced/src/advanced_omi_backend/models/job.py, backends/advanced/src/advanced_omi_backend/auth.py
Parameterized MongoDB database selection via MONGODB_DATABASE env var; added get_database() and get_collections() helpers; added generate_jwt_for_user(); expanded connection pool settings.
Backend API Endpoints
backends/advanced/src/advanced_omi_backend/routers/modules/memory_routes.py, backends/advanced/src/advanced_omi_backend/routers/modules/system_routes.py, backends/advanced/src/advanced_omi_backend/controllers/memory_controller.py, backends/advanced/src/advanced_omi_backend/controllers/system_controller.py
New POST /memories endpoint for manual memory creation; new memory provider GET/SET endpoints; memory-to-dict conversion for JSON serialization; system controller functions for provider querying and switching.
Transcription Service Refactoring
backends/advanced/src/advanced_omi_backend/services/transcription/__init__.py, backends/advanced/src/advanced_omi_backend/services/transcription/deepgram.py, backends/advanced/src/advanced_omi_backend/services/transcription/parakeet.py, backends/advanced/src/advanced_omi_backend/services/audio_stream/producer.py
Updated import paths from models.transcription to services.transcription.base for provider base classes.
Docker Compose Test Stack
backends/advanced/docker-compose-test.yml
Added Mycelia backend and frontend services to test environment with appropriate port mappings, healthchecks, and environment variables.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Areas requiring extra attention:

  • Mycelia integration logic (backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py): Complex JWT-based authentication, LLM-driven temporal extraction, and Mycelia API interaction; requires understanding of OAuth flow and memory object mapping.
  • Memory service refactoring (backends/advanced/src/advanced_omi_backend/services/memory/): Significant architectural changes from monolithic to modular provider pattern; verify all import paths and provider initialization paths across 15+ files.
  • Docker Compose restructuring (backends/advanced/docker-compose.yml and backends/advanced/compose/): Modular includes-based approach with environment-specific overrides; ensure network configuration, volume mounts, and service dependencies are correctly preserved across modular files.
  • Speech detection threshold changes (config.py, conversation_utils.py): Increased thresholds (especially min_duration 2.0→10.0s) may significantly impact user experience and job cancellation behavior; verify implications.
  • Frontend timeline visualizations (new pages for Frappe Gantt, React Gantt, Mycelia D3): Three different implementations with D3 zoom hooks, data transformation logic, and timezone-aware date formatting; verify correctness of data mappings and performance.
  • Setup wizard orchestration (Makefile, WIZARD.md): Complex multi-step flow with nested make targets and environment variable management; verify shell script interactions and error handling.
  • Multi-file import path changes: Numerous files updated from advanced_omi_backend.memory to advanced_omi_backend.services.memory; verify all paths are consistent and no circular imports introduced.

Possibly related PRs

  • chronicler-ai/chronicle#74: Overlapping memory-provider initialization and setup system modifications affecting the same subsystems.
  • chronicler-ai/chronicle#144: Overlaps in advanced backend modifications including auth.py and memory service/provider wiring changes.
  • chronicler-ai/chronicle#121: Related backend infrastructure (Beanie models, RQ-based job system) affecting the same worker and controller modules.

Suggested reviewers

  • AnkushMalaker

Poem

🐰 A timeline of tales, in code we weave,
Mycelia dreams with JWT sleeve,
Setup wizards dance through secrets' grace,
Docker containers in their rightful place,
Memories bloom in Gantt and D3,
Chronicle grows wild and fancy! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Docker compose inclu9de' is vague and appears to contain a typo ('inclu9de' instead of 'include'). While it broadly relates to docker-compose modulization in the PR, it does not clearly convey the primary changes such as multi-environment support, Mycelia integration smoothing, SSL/Tailscale automation, or comprehensive documentation. Consider revising to: 'Add Docker Compose includes, multi-environment support, and Mycelia integration' or similar, which better captures the main objectives and fixes the apparent typo.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 96.26% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@thestumonkey
Copy link
Contributor Author

@RabbitAI review

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
backends/advanced/src/advanced_omi_backend/config.py (1)

134-140: Add missing min_duration to return value.

The function doesn't return min_duration even though it's defined in DEFAULT_SPEECH_DETECTION_SETTINGS (line 35). This creates an inconsistency where:

  1. conversation_utils.py expects min_duration in the settings dict (line 104)
  2. The value will always fall back to the hardcoded 10.0 instead of using the configured default
  3. No environment variable support for SPEECH_DETECTION_MIN_DURATION

This breaks the configuration pattern and prevents runtime configurability.

Apply this diff:

 def get_speech_detection_settings():
     """Get speech detection settings from environment or defaults."""

     return {
         "min_words": int(os.getenv("SPEECH_DETECTION_MIN_WORDS", DEFAULT_SPEECH_DETECTION_SETTINGS["min_words"])),
         "min_confidence": float(os.getenv("SPEECH_DETECTION_MIN_CONFIDENCE", DEFAULT_SPEECH_DETECTION_SETTINGS["min_confidence"])),
+        "min_duration": float(os.getenv("SPEECH_DETECTION_MIN_DURATION", DEFAULT_SPEECH_DETECTION_SETTINGS["min_duration"])),
     }
backends/advanced/src/advanced_omi_backend/services/memory/prompts.py (1)

180-180: Stale date in prompt: datetime.now() captured at import time.

The datetime.now() call in the f-string is evaluated once when the module is imported, so the "current date" in the prompt becomes stale and incorrect as time passes. This affects the LLM's temporal reasoning for fact extraction.

Apply this diff to make the date dynamic:

-Remember the following:
-- Today's date is {datetime.now().strftime("%Y-%m-%d")}.
-- Do not return anything from the custom few shot example prompts provided above.
+Remember the following:
+- Today's date is {{current_date}}.
+- Do not return anything from the custom few shot example prompts provided above.

Then update the usage sites to format the prompt with the current date when it's actually used. Alternatively, convert FACT_RETRIEVAL_PROMPT to a function that generates the prompt with the current date, similar to build_temporal_extraction_prompt.

backends/advanced/src/advanced_omi_backend/workers/memory_jobs.py (1)

145-151: Remove duplicate import.

get_memory_service is already imported at line 49. This redundant import inside the try block is unnecessary.

                # Determine memory provider from memory service
                memory_provider = conversation_model.MemoryProvider.FRIEND_LITE  # Default
                try:
-                    from advanced_omi_backend.services.memory import get_memory_service
                    memory_service_obj = get_memory_service()
                    provider_name = memory_service_obj.__class__.__name__
                    if "OpenMemory" in provider_name:
                        memory_provider = conversation_model.MemoryProvider.OPENMEMORY_MCP
                except Exception:
                    pass
backends/advanced/src/advanced_omi_backend/services/memory/providers/openmemory_mcp.py (1)

369-388: Unused user_id and user_email parameters in delete_memory.

The delete_memory method accepts user_id and user_email parameters but doesn't use them. The method uses self.mcp_client.delete_memory(memory_id) without passing user context. This appears inconsistent with other methods that switch user context.

Consider either:

  1. Removing the unused parameters
  2. Applying user context switching like other methods
-    async def delete_memory(self, memory_id: str, user_id: Optional[str] = None, user_email: Optional[str] = None) -> bool:
+    async def delete_memory(self, memory_id: str, user_id: Optional[str] = None, user_email: Optional[str] = None) -> bool:
         """Delete a specific memory by ID.
         ...
         """
         if not self._initialized:
             await self.initialize()
         
+        # Update MCP client user context for this operation
+        original_user_id = self.mcp_client.user_id
+        self.mcp_client.user_id = user_id or self.user_id
+
         try:
             success = await self.mcp_client.delete_memory(memory_id)
             if success:
                 memory_logger.info(f"🗑️ Deleted memory {memory_id} via MCP")
             return success
         except Exception as e:
-            memory_logger.error(f"Delete memory failed: {e}")
+            memory_logger.exception(f"Delete memory failed: {e}")
             return False
+        finally:
+            self.mcp_client.user_id = original_user_id
backends/advanced/src/advanced_omi_backend/services/memory/providers/mcp_client.py (1)

343-345: Move import re to the top of the file.

Per coding guidelines, all imports must be at the top of the file. The re import inside delete_all_memories violates this rule.

 import logging
 import uuid
+import re
 from typing import List, Dict, Any, Optional
 import httpx

Then remove line 343:

-                    import re

As per coding guidelines, NEVER import modules in the middle of functions.

Makefile (1)

339-344: Use uv run python instead of python3 for Python scripts.

Per the coding guidelines, Python scripts should be executed using uv run python instead of direct python or python3 commands.

 config-docker: ## Generate Docker Compose configuration files
 	@echo "🐳 Generating Docker Compose configuration files..."
-	@CONFIG_FILE=config.env.dev python3 scripts/generate-docker-configs.py
+	@CONFIG_FILE=config.env.dev uv run python scripts/generate-docker-configs.py
 	@echo "✅ Docker Compose configuration files generated"

 config-k8s: ## Generate Kubernetes configuration files (ConfigMap/Secret only - no .env files)
 	@echo "☸️  Generating Kubernetes configuration files..."
-	@python3 scripts/generate-k8s-configs.py
+	@uv run python scripts/generate-k8s-configs.py

As per coding guidelines.

🟠 Major comments (25)
backends/advanced/src/advanced_omi_backend/utils/conversation_utils.py-103-111 (1)

103-111: Fix: min_duration missing from get_speech_detection_settings() return value.

The duration check logic is correct, but settings.get("min_duration", 10.0) will always use the fallback value because get_speech_detection_settings() in config.py (lines 134-140) doesn't return min_duration. This means:

  • The hardcoded fallback 10.0 will always be used
  • Environment variable SPEECH_DETECTION_MIN_DURATION (if added) would be ignored
  • Runtime configuration changes won't take effect

This breaks the configuration pattern used for min_words and min_confidence.

The fix should be applied in backends/advanced/src/advanced_omi_backend/config.py:

 def get_speech_detection_settings():
     """Get speech detection settings from environment or defaults."""

     return {
         "min_words": int(os.getenv("SPEECH_DETECTION_MIN_WORDS", DEFAULT_SPEECH_DETECTION_SETTINGS["min_words"])),
         "min_confidence": float(os.getenv("SPEECH_DETECTION_MIN_CONFIDENCE", DEFAULT_SPEECH_DETECTION_SETTINGS["min_confidence"])),
+        "min_duration": float(os.getenv("SPEECH_DETECTION_MIN_DURATION", DEFAULT_SPEECH_DETECTION_SETTINGS["min_duration"])),
     }

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/src/advanced_omi_backend/workers/transcription_jobs.py-502-505 (1)

502-505: Move import to the top of the file.

The check_job_alive import is placed inside the while loop, which is inefficient and violates coding guidelines.

Add at the top of the file with other imports:

from advanced_omi_backend.utils.job_utils import check_job_alive

Then simplify the loop:

     while True:
         # Check if job still exists in Redis (detect zombie state)
-        from advanced_omi_backend.utils.job_utils import check_job_alive
         if not await check_job_alive(redis_client, current_job):
             break

As per coding guidelines, ALL imports must be at the top of the file.

backends/advanced/src/advanced_omi_backend/workers/audio_jobs.py-263-273 (1)

263-273: Move imports to the top of the file and ensure proper zombie detection.

The imports for get_current_job and check_job_alive are placed inside the function body. Per coding guidelines, all imports should be at the top of the file.

Add at the top of the file (around line 19):

from rq import get_current_job
from advanced_omi_backend.utils.job_utils import check_job_alive

Then simplify the function body:

-    # Get current job for zombie detection
-    from rq import get_current_job
-    from advanced_omi_backend.utils.job_utils import check_job_alive
-    current_job = get_current_job()
+    # Get current job for zombie detection
+    current_job = get_current_job()

The resource cleanup logic (closing file_sink before breaking) is correct and important for preventing resource leaks.

As per coding guidelines, ALL imports must be at the top of the file.

backends/advanced/src/advanced_omi_backend/workers/transcription_jobs.py-196-221 (1)

196-221: Move imports to the top of the file.

Multiple imports are placed inside the function body at lines 197, 215-216, and 221. Per coding guidelines, all imports must be at the top of the file.

Add these imports at the top of the file with other imports:

from rq.job import Job
from advanced_omi_backend.utils.conversation_utils import analyze_speech, mark_conversation_deleted

Note: get_current_job is already imported at line 407, so consider moving that import to the top as well for consistency.

As per coding guidelines, ALL imports must be at the top of the file.

backends/advanced/src/advanced_omi_backend/workers/conversation_jobs.py-284-287 (1)

284-287: Move import to the top of the file.

The check_job_alive import is placed inside the while loop, which violates the coding guidelines and is inefficient (re-imported on every iteration). Move this import to the top of the file with the other imports.

Add at the top of the file (around line 27):

from advanced_omi_backend.utils.job_utils import check_job_alive

Then simplify the loop:

     while True:
-        # Check if job still exists in Redis (detect zombie state)
-        from advanced_omi_backend.utils.job_utils import check_job_alive
-        if not await check_job_alive(redis_client, current_job):
+        # Check if job still exists in Redis (detect zombie state)
+        if not await check_job_alive(redis_client, current_job):
             break

As per coding guidelines, ALL imports must be at the top of the file after the docstring.

backends/advanced/webui/nginx.conf-29-31 (1)

29-31: Sanitize X-Forwarded-Prefix before injecting into HTML to mitigate potential XSS

You're directly reflecting $http_x_forwarded_prefix into an HTML attribute via sub_filter. If this nginx instance is ever reachable by clients who can set arbitrary HTTP headers (or if upstream proxies don't strip/validate X-Forwarded-Prefix), a malicious client can craft this header with quotes or angle brackets and get it echoed into <base href="...">, creating a reflected XSS vector.

Recommended mitigation:

Add a sanitized prefix variable at the http level in your main nginx config:

map $http_x_forwarded_prefix $ui_prefix {
    default "/";
    ~^/[0-9A-Za-z_/-]*$  $http_x_forwarded_prefix;
}

Then update the sub_filter directive to use the sanitized variable:

-    sub_filter '<head>' '<head><base href="$http_x_forwarded_prefix/">';
+    sub_filter '<head>' '<head><base href="$ui_prefix/">';

This ensures that only path-like values matching the regex are used; malformed or dangerous input falls back safely to "/".

Note: sub_filter is provided by ngx_http_sub_module. This module is not compiled into nginx by default when building from source (requires --with-http_sub_module), but it is typically included in pre-built distributions (Debian, Ubuntu, official nginx packages). If your Docker image uses a minimal or custom nginx build, verify the module is available; otherwise the container will fail to start with unknown directive "sub_filter". You can check inside the container with nginx -V 2>&1 | grep http_sub_module.

backends/advanced/scripts/create_mycelia_api_key.py-265-270 (1)

265-270: Remove duplicate import base64 statement.

This is a duplicate import inside the function. Move it to the top with other imports as noted above.

-    # Create API key document (matches Mycelia's format)
-    import base64
     api_key_doc = {

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/scripts/create_mycelia_api_key.py-23-31 (1)

23-31: Move import base64 to the top of the file.

Per coding guidelines, imports should never be in the middle of functions. The base64 import at line 26 and again at line 266 should be moved to the top of the file with other imports.

 import os
 import sys
 import secrets
 import hashlib
+import base64
 from pymongo import MongoClient
 from bson import ObjectId
 from datetime import datetime
 
 ...
 
 def hash_api_key_with_salt(api_key: str, salt: bytes) -> str:
     """Hash API key with salt (matches Mycelia's hashApiKey function)."""
     # SHA256(salt + apiKey) in base64
-    import base64
     h = hashlib.sha256()
     h.update(salt)
     h.update(api_key.encode('utf-8'))
     return base64.b64encode(h.digest()).decode('utf-8')  # Use base64 like Mycelia

Also remove the duplicate import at line 266.

backends/advanced/src/advanced_omi_backend/app_factory.py-33-34 (1)

33-34: Move Mycelia sync import to module top and prefer logging.exception in new block

Functionality looks good (non-fatal Mycelia OAuth admin sync on startup), but two small changes would better match project guidelines:

  1. Avoid mid-function imports

Current code:

try:
    from advanced_omi_backend.services.mycelia_sync import sync_admin_on_startup
    await sync_admin_on_startup()

Per the Python guidelines here, imports should live at the top of the file unless there’s a hard circular‑import constraint. You can move the import up with the other advanced_omi_backend imports and just await the function inside lifespan:

-from advanced_omi_backend.client_manager import get_client_manager
-from advanced_omi_backend.services.memory import get_memory_service, shutdown_memory_service
+from advanced_omi_backend.client_manager import get_client_manager
+from advanced_omi_backend.services.memory import get_memory_service, shutdown_memory_service
+from advanced_omi_backend.services.mycelia_sync import sync_admin_on_startup
@@
-    # Sync admin user with Mycelia OAuth (if using Mycelia memory provider)
-    try:
-        from advanced_omi_backend.services.mycelia_sync import sync_admin_on_startup
-        await sync_admin_on_startup()
-    except Exception as e:
-        application_logger.error(f"Failed to sync admin with Mycelia OAuth: {e}")
+    # Sync admin user with Mycelia OAuth (if using Mycelia memory provider)
+    try:
+        await sync_admin_on_startup()
+    except Exception:
+        application_logger.exception("Failed to sync admin with Mycelia OAuth")
         # Don't raise here as this is not critical for startup
  1. Use logging.exception for the new except block

Using application_logger.exception(...) here logs the full traceback, which matches the preferred pattern for new code in this repo and addresses the TRY400 hint. Based on learnings, this also improves debuggability for startup issues involving Mycelia.

Catching bare Exception is probably acceptable here (you explicitly don’t want startup to fail), but if you know the likely failure modes, narrowing the exception types would further reduce noise.

Also applies to: 76-82

backends/advanced/src/advanced_omi_backend/models/job.py-48-52 (1)

48-52: Fix error handling logic for database initialization.

The current code attempts a fallback assignment but then immediately re-raises the exception, making the fallback ineffective. The get_default_database() call requires the connection string to include a default database name. When it fails with ConfigurationError, the fallback to client[mongodb_database] should be used to continue initialization, not raise.

Apply this diff to fix the error handling:

             client = AsyncIOMotorClient(mongodb_uri)
             try:
                 database = client.get_default_database(mongodb_database)
             except ConfigurationError:
+                # Fallback: URI doesn't specify a default DB, use explicit name
                 database = client[mongodb_database]
-                raise

Based on learnings, when re-raising is necessary, use raise ... from e to preserve context. However, in this case, the fallback should handle the error gracefully rather than re-raising.

DOCKER-COMPOSE.md-79-83 (1)

79-83: Qdrant port numbers in the docs don’t match the compose configuration

Here the docs say Qdrant runs on 6033/6034, but backends/advanced/docker-compose-test.yml explicitly maps:

# gRPC - avoid conflict with dev 6333
- "6337:6333"
# HTTP - avoid conflict with dev 6334
- "6338:6334"

which implies the dev stack uses 6333 (gRPC) and 6334 (HTTP), not 6033/6034. To avoid confusion when debugging connectivity, please either:

  • Update this guide to refer to 6333/6334, or
  • Align the dev compose to actually expose 6033/6034 and adjust the test comments accordingly.

Right now the mismatch makes it hard to know which ports to hit.

Also applies to: 241-242

backends/advanced/src/advanced_omi_backend/services/memory/providers/openmemory_mcp.py-362-367 (1)

362-367: Use logging.exception() in update_memory error handler.

         except Exception as e:
-            memory_logger.error(f"Failed to update memory: {e}")
+            memory_logger.exception(f"Failed to update memory: {e}")
             return False

Based on learnings, this captures the full stack trace.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mcp_client.py-437-442 (1)

437-442: Use logging.exception() in update_memory error handlers.

         except httpx.HTTPStatusError as e:
-            memory_logger.error(f"HTTP error updating memory: {e.response.status_code}")
+            memory_logger.exception(f"HTTP error updating memory: {e.response.status_code}")
             return False
         except Exception as e:
-            memory_logger.error(f"Error updating memory: {e}")
+            memory_logger.exception(f"Error updating memory: {e}")
             return False

Based on learnings, this captures the full stack trace.

backends/advanced/src/advanced_omi_backend/services/memory/providers/openmemory_mcp.py-230-235 (1)

230-235: Use logging.exception() for better stack traces.

Per project conventions, prefer logging.exception() inside except blocks to automatically log the full stack trace.

         except MCPError as e:
-            memory_logger.error(f"Search memories failed: {e}")
+            memory_logger.exception(f"Search memories failed: {e}")
             return []
         except Exception as e:
-            memory_logger.error(f"Search memories failed: {e}")
+            memory_logger.exception(f"Search memories failed: {e}")
             return []

Based on learnings, this improves debuggability across Python files.

backends/advanced/src/advanced_omi_backend/services/memory/providers/openmemory_mcp.py-317-322 (1)

317-322: Use logging.exception() in get_memory error handler.

         except Exception as e:
-            memory_logger.error(f"Failed to get memory: {e}")
+            memory_logger.exception(f"Failed to get memory: {e}")
             return None

Based on learnings, this captures the full stack trace.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mcp_client.py-388-395 (1)

388-395: Use logging.exception() in get_memory error handlers.

         except httpx.HTTPStatusError as e:
             if e.response.status_code == 404:
                 return None
-            memory_logger.error(f"HTTP error getting memory: {e}")
+            memory_logger.exception(f"HTTP error getting memory: {e}")
             return None
         except Exception as e:
-            memory_logger.error(f"Error getting memory: {e}")
+            memory_logger.exception(f"Error getting memory: {e}")
             return None

Based on learnings, this captures the full stack trace.

backends/advanced/webui/src/pages/ConversationsTimeline.tsx-219-230 (1)

219-230: Operator precedence issue in timezone detection logic.

The condition on line 221 has unclear operator precedence that may not behave as intended. The && and || operators need explicit grouping to correctly detect ISO 8601 timestamps with timezone offsets.

   const formatDate = (timestamp: number | string): Date => {
     if (typeof timestamp === 'string') {
-      const isoString = timestamp.endsWith('Z') || timestamp.includes('+') || timestamp.includes('T') && timestamp.split('T')[1].includes('-')
+      const hasTimezone = timestamp.endsWith('Z') || 
+                          timestamp.includes('+') || 
+                          (timestamp.includes('T') && timestamp.split('T')[1]?.includes('-'))
+      const isoString = hasTimezone
         ? timestamp
         : timestamp + 'Z'
       return new Date(isoString)
     }

The current logic evaluates as: A || B || (C && D) but the intent appears to be checking if any timezone indicator is present.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mcp_client.py-350-352 (1)

350-352: Use logging.exception() for better stack traces.

         except Exception as e:
-            memory_logger.error(f"Error deleting all memories: {e}")
+            memory_logger.exception(f"Error deleting all memories: {e}")
             return 0

Based on learnings, prefer logging.exception() to automatically log the full stack trace.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py-109-117 (1)

109-117: Move imports to top of file.

Importing generate_jwt_for_user and User inside _get_user_jwt violates the coding guidelines requiring all imports at the top of the file.

 from ..base import MemoryEntry, MemoryServiceBase
 from ..prompts import FACT_RETRIEVAL_PROMPT, TEMPORAL_ENTITY_EXTRACTION_PROMPT, TemporalEntity
 from ..config import MemoryConfig
 from .llm_providers import _get_openai_client
+from advanced_omi_backend.auth import generate_jwt_for_user
+from advanced_omi_backend.users import User

Then remove the inline imports:

     async def _get_user_jwt(self, user_id: str, user_email: Optional[str] = None) -> str:
-        from advanced_omi_backend.auth import generate_jwt_for_user
-
         # If email not provided, lookup user
         if not user_email:
-            from advanced_omi_backend.users import User
             user = await User.get(user_id)

As per coding guidelines, imports must be at the top of the file.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py-218-223 (1)

218-223: Use logging.exception() and chain exceptions properly.

     except httpx.HTTPStatusError as e:
-        memory_logger.error(f"Mycelia API error: {e.response.status_code} - {e.response.text}")
-        raise RuntimeError(f"Mycelia API error: {e.response.status_code}")
+        memory_logger.exception(f"Mycelia API error: {e.response.status_code}")
+        raise RuntimeError(f"Mycelia API error: {e.response.status_code}") from e
     except Exception as e:
-        memory_logger.error(f"Failed to call Mycelia resource: {e}")
-        raise RuntimeError(f"Mycelia API call failed: {e}")
+        memory_logger.exception("Failed to call Mycelia resource")
+        raise RuntimeError(f"Mycelia API call failed: {e}") from e

Based on learnings.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py-820-827 (1)

820-827: shutdown() cannot properly close the async HTTP client.

The shutdown method is synchronous but httpx.AsyncClient requires await client.aclose() for proper cleanup. This could cause resource leaks.

-    def shutdown(self) -> None:
+    async def shutdown(self) -> None:
         """Shutdown Mycelia client and cleanup resources."""
         memory_logger.info("Shutting down Mycelia memory service")
         if self._client:
-            # Note: httpx AsyncClient should be closed in an async context
-            # In practice, this will be called during shutdown so we log a warning
-            memory_logger.warning("HTTP client should be closed with await client.aclose()")
+            await self._client.aclose()
         self._initialized = False

If the base class requires a sync method, consider implementing __aenter__ and __aexit__ for context manager support, or track whether proper async cleanup occurred.

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/src/advanced_omi_backend/services/mycelia_sync.py-79-95 (1)

79-95: MongoClient instances are never closed, causing potential resource leaks.

Multiple MongoClient instances are created throughout this file (lines 80, 144, 186) without being closed. Each unclosed client holds open connections to MongoDB.

Consider using a context manager or storing the client as an instance variable:

 class MyceliaSyncService:
     """Synchronize Friend-Lite users with Mycelia OAuth API keys."""

     def __init__(self):
         """Initialize the sync service."""
         # MongoDB configuration
         self.mongo_url = os.getenv("MONGODB_URI", os.getenv("MONGO_URL", "mongodb://localhost:27017"))
+        self._client: Optional[MongoClient] = None
         # ... rest of init ...

+    def _get_client(self) -> MongoClient:
+        """Get or create MongoDB client."""
+        if self._client is None:
+            self._client = MongoClient(self.mongo_url)
+        return self._client
+
+    def close(self) -> None:
+        """Close MongoDB client."""
+        if self._client:
+            self._client.close()
+            self._client = None

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/src/advanced_omi_backend/services/memory/providers/mycelia.py-82-94 (1)

82-94: Use logging.exception() and chain exceptions with raise ... from e.

Per retrieved learnings, use logging.exception() inside except blocks to automatically include stack traces, and chain exceptions with raise ... from e to preserve context.

         except httpx.HTTPError as e:
-            raise RuntimeError(f"Failed to connect to Mycelia service: {e}")
+            raise RuntimeError(f"Failed to connect to Mycelia service: {e}") from e

         self._initialized = True
         memory_logger.info("✅ Mycelia memory service initialized successfully")

     except Exception as e:
-        memory_logger.error(f"❌ Failed to initialize Mycelia service: {e}")
-        raise RuntimeError(f"Mycelia initialization failed: {e}")
+        memory_logger.exception("❌ Failed to initialize Mycelia service")
+        raise RuntimeError(f"Mycelia initialization failed: {e}") from e

Based on learnings: prefer logging.exception() in except blocks and chain exceptions with raise ... from e.

backends/advanced/src/advanced_omi_backend/services/mycelia_sync.py-148-148 (1)

148-148: Move import to top of file.

Importing bson.ObjectId inside sync_user_to_mycelia violates the coding guidelines which require all imports at the top of the file.

 import logging
 import os
 import secrets
 import hashlib
 import base64
 from typing import Optional, Tuple
 from pymongo import MongoClient
 from datetime import datetime
+from bson import ObjectId

Then update line 148:

-            from bson import ObjectId
             users_collection.update_one(
                 {"_id": ObjectId(user_id)},

As per coding guidelines, imports must be at the top of the file.

Docs/setup/prerequisites.md-106-106 (1)

106-106: Wrap bare URLs in Markdown link syntax.

The static analysis tool flagged 12 bare URLs that should be wrapped in Markdown link syntax for proper rendering. Replace each bare URL with [text](url) format.

Apply these diffs to wrap the bare URLs:

- **Sign up**: https://platform.openai.com/signup
+ **Sign up**: [https://platform.openai.com/signup](https://platform.openai.com/signup)

- 1. Go to https://platform.openai.com/api-keys
+ 1. Go to [https://platform.openai.com/api-keys](https://platform.openai.com/api-keys)

- **Sign up**: https://console.deepgram.com/signup
+ **Sign up**: [https://console.deepgram.com/signup](https://console.deepgram.com/signup)

- 1. Go to https://console.deepgram.com/
+ 1. Go to [https://console.deepgram.com/](https://console.deepgram.com/)

- **Sign up**: https://console.mistral.ai/
+ **Sign up**: [https://console.mistral.ai/](https://console.mistral.ai/)

- 1. Go to https://console.mistral.ai/api-keys
+ 1. Go to [https://console.mistral.ai/api-keys](https://console.mistral.ai/api-keys)

- **Sign up**: https://huggingface.co/join
+ **Sign up**: [https://huggingface.co/join](https://huggingface.co/join)

- 1. Go to https://huggingface.co/settings/tokens
+ 1. Go to [https://huggingface.co/settings/tokens](https://huggingface.co/settings/tokens)

- **Sign up**: https://console.groq.com/
+ **Sign up**: [https://console.groq.com/](https://console.groq.com/)

- **Sign up**: https://login.tailscale.com/start
+ **Sign up**: [https://login.tailscale.com/start](https://login.tailscale.com/start)

- - Check your console dashboard at https://console.deepgram.com/
+ - Check your console dashboard at [https://console.deepgram.com/](https://console.deepgram.com/)

- **GitHub Issues**: https://github.com/BasedHardware/Friend/issues
+ **GitHub Issues**: [https://github.com/BasedHardware/Friend/issues](https://github.com/BasedHardware/Friend/issues)

Also applies to: 109-109, 123-123, 126-126, 146-146, 149-149, 159-159, 162-162, 173-173, 190-190, 333-333, 345-345

🟡 Minor comments (18)
backends/advanced/webui/src/hooks/useD3Zoom.ts-57-75 (1)

57-75: Remove transform from dependency array to avoid redundant effect execution.

Including transform in dependencies causes this effect to run on every zoom event (since handleZoom calls setTransform). The effect's purpose is to attach the zoom behavior, not to respond to transform changes—D3 already maintains __zoom internally during zoom events.

Additionally, the as any assertion on line 69 bypasses type safety.

   useEffect(() => {
     if (!svgRef.current) return

     const svg = d3.select(svgRef.current)
     const node = svg.node()

     if (node) {
       node.style.touchAction = 'none'
-      node.style.webkitUserSelect = 'none'
+      ;(node.style as CSSStyleDeclaration & { webkitUserSelect: string }).webkitUserSelect = 'none'
       node.style.userSelect = 'none'
     }

-    svg.call(zoomBehavior as any)
-    svg.property('__zoom', transform)
+    svg.call(zoomBehavior)

     return () => {
       svg.on('.zoom', null)
     }
-  }, [zoomBehavior, transform])
+  }, [zoomBehavior])

If you need to programmatically set the initial transform, consider doing so only on mount or when explicitly requested, rather than on every transform change.

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/webui/src/pages/MyceliaTimeline.tsx-142-148 (1)

142-148: Missing loadMemories in useEffect dependency array.

The loadMemories function is called inside this effect but not listed in the dependency array. This could cause stale closures. Either add loadMemories to the deps or wrap it in useCallback.

   useEffect(() => {
     if (!useDemoData) {
       loadMemories()
     } else {
       setMemories(getDemoMemories())
     }
-  }, [user?.id, useDemoData])
+  }, [user?.id, useDemoData]) // Consider wrapping loadMemories in useCallback and adding to deps

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/webui/src/pages/Memories.tsx-475-479 (1)

475-479: Add keyboard accessibility to the clickable memory card.

The memory card content area is clickable but lacks keyboard accessibility. Users should be able to navigate and activate the card using keyboard.

                 {/* Clickable Memory Content Area */}
                 <div
                   onClick={() => navigate(`/memories/${memory.id}`)}
-                  className="p-6 cursor-pointer"
+                  className="p-6 cursor-pointer"
+                  role="button"
+                  tabIndex={0}
+                  onKeyDown={(e) => {
+                    if (e.key === 'Enter' || e.key === ' ') {
+                      e.preventDefault()
+                      navigate(`/memories/${memory.id}`)
+                    }
+                  }}
                 >
backends/advanced/webui/public/frappe-gantt.css-1-1 (1)

1-1: Fix CSS variable usage and invalid background-color value

Two small correctness issues in this minified stylesheet:

  1. CSS variable missing var()

--g-weekend-label-color is used directly as a color:

.gantt-container .holiday-label{ ... background:--g-weekend-label-color; ... }

This should go through var():

-.gantt-container .holiday-label{position:absolute;top:0;left:0;opacity:0;z-index:1000;background:--g-weekend-label-color;border-radius:5px;padding:2px 5px}
+.gantt-container .holiday-label{position:absolute;top:0;left:0;opacity:0;z-index:1000;background:var(--g-weekend-label-color);border-radius:5px;padding:2px 5px}
  1. Invalid background-color:brightness(97%)

brightness() is a filter function, not a color value:

.gantt-container .popup-wrapper .action-btn:hover{background-color:brightness(97%)}

You likely intended to dim via a filter:

-.gantt-container .popup-wrapper .action-btn:hover{background-color:brightness(97%)}
+.gantt-container .popup-wrapper .action-btn:hover{filter:brightness(0.97)}

These changes should clear the linter warning and ensure expected hover and holiday-label styling.

INSTALL.md-96-96 (1)

96-96: Fix inconsistent directory name.

This line clones the repository to friend-lite, while all other examples in this document use chronicle. This inconsistency could confuse users following different sections.

Apply this diff to maintain consistency:

 # Clone and setup Chronicle
-git clone https://github.com/BasedHardware/Friend.git friend-lite
-cd friend-lite
+git clone https://github.com/BasedHardware/Friend.git chronicle
+cd chronicle
 make wizard

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/src/advanced_omi_backend/services/memory/service_factory.py-56-67 (1)

56-67: Add exception chaining for better debuggability.

The Mycelia provider integration follows the established pattern correctly. However, the exception handling should chain exceptions to preserve the original context.

Based on learnings, prefer exception chaining with 'raise ... from e' to preserve the original context.

Apply this diff to improve exception handling:

         try:
             from .providers.mycelia import MyceliaMemoryService
         except ImportError as e:
-            raise RuntimeError(f"Mycelia memory service not available: {e}")
+            raise RuntimeError(f"Mycelia memory service not available: {e}") from e
backends/advanced/src/advanced_omi_backend/services/memory/service_factory.py-44-54 (1)

44-54: Add exception chaining for OpenMemory MCP provider.

The exception handling should chain exceptions to preserve context, consistent with the coding guidelines.

Based on learnings, prefer exception chaining with 'raise ... from e'.

Apply this diff:

         try:
             from .providers.openmemory_mcp import OpenMemoryMCPService
         except ImportError as e:
-            raise RuntimeError(f"OpenMemory MCP service not available: {e}")
+            raise RuntimeError(f"OpenMemory MCP service not available: {e}") from e
backends/advanced/src/advanced_omi_backend/routers/modules/system_routes.py-131-145 (1)

131-145: Align POST /admin/memory/provider docstring with actual behavior

The route only updates MEMORY_PROVIDER and returns a message that a restart is required; it does not itself restart backend services. Consider updating the docstring to avoid implying an automatic restart. The current_user argument being unused in the body is fine here since it exists purely to enforce admin auth via current_superuser.

backends/advanced/webui/src/pages/MemoryDetail.tsx-89-91 (1)

89-91: Missing loadMemory in useEffect dependency array.

The loadMemory function is called but not listed as a dependency. This violates React's exhaustive-deps rule and can cause stale closure issues.

Wrap loadMemory in useCallback or inline the effect logic:

+  const loadMemory = useCallback(async () => {
+    // ... existing implementation
+  }, [user?.id, id])
+
   useEffect(() => {
     loadMemory()
-  }, [id, user?.id])
+  }, [loadMemory])

Alternatively, add // eslint-disable-next-line react-hooks/exhaustive-deps with justification if intentional.

Committable suggestion skipped: line range outside the PR's diff.

WIZARD.md-289-293 (1)

289-293: Add language specifier to fenced code block.

The command output example should have a language specifier.

-```
+```text
 anubis    100.83.66.30   anubis.tail12345.ts.net   linux   -
WIZARD.md-251-267 (1)

251-267: Add language specifier to fenced code block.

The directory structure code block should have a language specifier for consistent markdown rendering.

-```
+```text
 friend-lite/
 ├── .env.secrets                    # Secrets (gitignored)
backends/advanced/webui/src/pages/FrappeGanttTimeline.tsx-214-220 (1)

214-220: loadMemories function should be in useEffect dependency array or wrapped with useCallback.

Similar to the issue in ReactGanttTimeline, the effect calls loadMemories but it's not in the dependency array.

Docs/development/DOCKER-SETUP-SUMMARY.md-7-17 (1)

7-17: Replace user-specific absolute path with a generic placeholder.

The path /Users/stu/repos/friend-lite/ is specific to one developer's machine. Use a generic path or placeholder for documentation.

-/Users/stu/repos/friend-lite/          # PROJECT ROOT
+/path/to/friend-lite/                   # PROJECT ROOT
backends/advanced/webui/src/pages/ReactGanttTimeline.tsx-150-150 (1)

150-150: Inconsistent API call: user.id not passed to memoriesApi.getAll().

This component calls memoriesApi.getAll() without the user ID, whereas FrappeGanttTimeline.tsx (line 157) passes user.id. This inconsistency may cause different data fetching behavior between the two timeline implementations.

-      const response = await memoriesApi.getAll()
+      const response = await memoriesApi.getAll(user?.id)
backends/advanced/webui/src/pages/FrappeGanttTimeline.tsx-606-606 (1)

606-606: Duplicate CSS class: focus:ring-2 appears twice.

-              className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-sm focus:ring-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+              className="px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
backends/advanced/webui/src/pages/ReactGanttTimeline.tsx-179-183 (1)

179-183: Missing fetchMemoriesWithTimeRanges in useEffect dependency array.

The effect depends on user but calls fetchMemoriesWithTimeRanges. If the function reference changes, the effect won't re-run. Either wrap the function with useCallback or include it in the dependency array.

   useEffect(() => {
     if (user) {
       fetchMemoriesWithTimeRanges()
     }
-  }, [user])
+  }, [user]) // Consider wrapping fetchMemoriesWithTimeRanges in useCallback

Committable suggestion skipped: line range outside the PR's diff.

backends/advanced/Docs/mycelia-auth-and-ownership.md-165-198 (1)

165-198: Add a language specifier to the fenced code block for the diagram.

The ASCII diagram block lacks a language specifier. While the diagram renders fine, adding a text or empty language identifier satisfies linting rules.

-```
+```text
 ┌─────────────────────────────────────────────────────────────┐
 │ 1. HTTP Request with JWT Token                             │
backends/advanced/Docs/mycelia-auth-and-ownership.md-242-247 (1)

242-247: Convert bare URL to proper markdown link.

The bare URL should be wrapped in a markdown link for better formatting and accessibility.

-Access Mycelia through Friend-Lite at **http://localhost:5173/memories**
+Access Mycelia through Friend-Lite at **[http://localhost:5173/memories](http://localhost:5173/memories)**

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant