diff --git a/docs/discovery/BASELINE-STATUS.md b/docs/discovery/BASELINE-STATUS.md deleted file mode 100644 index 7cb6ec391..000000000 --- a/docs/discovery/BASELINE-STATUS.md +++ /dev/null @@ -1,62 +0,0 @@ -# Discovery Baseline Status - -## Purpose and scope - -This package is a commit-pinned discovery baseline and feature index for the `discovery` branch at frozen commit `d8a2059df8e53bc7275c45339849d14c8651e73c`. It inventories **79 feature records** across **16 domains** to help maintainers locate likely implementation areas and identify validation gaps. It is not an authoritative architecture reference or a runtime-certification record. - -The canonical inventory is [`feature-catalog.json`](feature-catalog.json); [`feature-catalog.md`](feature-catalog.md) and the files in [`domains/`](domains/) are derived reading views. See [`audit-method.md`](audit-method.md) for the status and maturity definitions. - -## What maintainers may use now - -- Use the catalog and domain views as a frozen discovery index, including their feature IDs, stated scope, likely source locations, and declared runtime prerequisites. -- Treat a catalog status such as `verified` as meaning implementation was identified during discovery. It does **not** mean every evidence locator, line range, test claim, or runtime behaviour has passed semantic validation. -- Use the structural checks to confirm package shape and cross-view consistency; use the evidence validator to assess whether individual evidence assertions are semantically supported. - -## Validation snapshot - -Structural validation passes: the catalog has 79 records, the 16 domain views match it, and the package structural validators pass. All 11 focused evidence-validator tests pass. - -The evidence validator found **170 evidence entries**: **82 valid**, **66 invalid**, **6 ambiguous**, and **16 unsupported**. Its non-zero exit is expected while those semantic evidence defects remain. - -Structural validity checks the documentation schema, record counts, derived-view consistency, file existence, line-range bounds, links, and public-safety rules. Semantic evidence validity additionally checks whether the cited locator exists, falls within its cited range, uses a supported parser, and actually supports the feature claim. Passing the former does not establish the latter. - -## E2 review decisions - -E2 means directly relevant automated test evidence supports the feature claim; a test file’s existence alone is insufficient. The generated evidence report was used to reassess all ten E2 records. - -| Feature | Decision | Reason | -|---|---|---| -| `CHAT-001` | Retain E2 | Two cited tests directly exercise documented streaming-related behaviour. | -| `MODEL-006` | Retain E2 | The cited device-flow test exercises the Copilot start/poll contract and verification URI. | -| `MODEL-007` | Retain E2 | The cited device-flow test exercises the ChatGPT subscription contract and verification URI. | -| `RESEARCH-003` | Demote to E1 | The cited test covers ranking, not provider connectivity or dispatch; the route-to-provider implementation path was identified. | -| `DOCUMENT-002` | Demote to E1 | The cited marker test does not support form processing or rendering; the document route does call the PDF form handlers. | -| `EMAIL-001` | Demote to E1 | The cited health test is narrower than setup, SMTP, and polling; application setup invokes the email router and its poller. | -| `SECURITY-002` | Demote to E1 | Relevant vault-password tests exist, but the cited test locator is fabricated; the application mounts the vault route implementation. | -| `SECURITY-004` | Demote to E1 | Relevant injection tests exist, but the cited locator names are fabricated; callers use the documented context guard. | -| `SECURITY-005` | Demote to E1 | Relevant guard tests exist, but the cited locator names are fabricated; route code calls the documented URL guard. | -| `PLATFORM-009` | Demote to E0 | The manifests and diagnostic script establish discovered operational artifacts, not a traced application path or directly relevant automated test. | - -The current maturity distribution is **E0: 68**, **E1: 8**, **E2: 3**, **E3: 0**, **E4: 0**. Runtime validation is still pending where the catalog says it requires external services, interactive authentication, specialised hardware, or host Docker GPU support. - -## Known discovery caveats - -- `AGENT-004` includes a legacy no-op activity-log shim rather than active assistant-log behaviour. -- `FRONTEND-007` points to a missing `static/backgrounds.html` target; its existing variant pages do not make that route functional. -- `RESEARCH-003` retains a compatibility module that aliases the canonical search implementation, and `DOCUMENT-002` separates optional PDF viewing from form handling. -- Secret-storage and vault-command handling are distinct implementation areas; this index does not make an end-to-end security guarantee. - -## Recommended next documentation work - -Repair semantic evidence selectively while architecture and operations documentation is written, beginning with the seven E2 demotions and maintainer-owned feature descriptions. Do not wait for the complete evidence queue before documenting the system. Record controlled runtime observations when external services, credentials, hardware, or Docker GPU access are available, and label unsupported claims explicitly. - -## Validation commands - -```bash -PYTHONDONTWRITEBYTECODE=1 python3 docs/discovery/tools/validate_discovery_docs.py --repo-root . -PYTHONDONTWRITEBYTECODE=1 python3 docs/discovery/tools/validate_discovery_consistency.py -PYTHONDONTWRITEBYTECODE=1 python3 -m unittest docs/discovery/tools/test_validate_discovery_evidence.py -v -PYTHONDONTWRITEBYTECODE=1 python3 docs/discovery/tools/validate_discovery_evidence.py --repo-root . --catalog docs/discovery/feature-catalog.json --output-dir -``` - -Supply a local report directory outside `docs/discovery/` for the final command so generated reports are not added to the package. diff --git a/docs/discovery/README.md b/docs/discovery/README.md deleted file mode 100644 index 1a397da74..000000000 --- a/docs/discovery/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Odysseus Discovery Package - -## Provisional discovery baseline - -This is a commit-pinned discovery baseline and feature index for the `discovery` branch at `d8a2059df8e53bc7275c45339849d14c8651e73c`. It contains 79 feature records across 16 domains. It is **not** an authoritative architecture reference, a runtime certification, or a claim that every evidence citation is semantically valid. - -Read [`BASELINE-STATUS.md`](BASELINE-STATUS.md) first for the publication status, evidence-validation totals, E2 decisions, known caveats, and the recommended next documentation work. - -## Package contents - -- [`feature-catalog.json`](feature-catalog.json) is the canonical machine-readable catalog. -- [`feature-catalog.md`](feature-catalog.md) and [`domains/`](domains/) are derived reading views. -- [`audit-method.md`](audit-method.md) defines feature status and evidence maturity. -- [`references/source-provenance.md`](references/source-provenance.md) records the frozen snapshot. -- [`tools/`](tools/) contains the structural, consistency, and evidence validators. - -A feature status such as `verified` means implementation was identified during discovery. It does not mean every evidence locator, line range, test claim, or runtime behaviour has passed semantic validation. - -## Validation - -Run the commands in [`BASELINE-STATUS.md`](BASELINE-STATUS.md#validation-commands). Structural checks and semantic evidence checks have different purposes; see that status document for the current results and interpretation. diff --git a/docs/discovery/audit-method.md b/docs/discovery/audit-method.md deleted file mode 100644 index b2c3bd1f1..000000000 --- a/docs/discovery/audit-method.md +++ /dev/null @@ -1,80 +0,0 @@ -# Odysseus Functional Audit Methodology - -## Purpose - -This document specifies the methodology and evidence standards for the read-only discovery audit of **Odysseus**. - -## Snapshot Baseline - -- **Repository**: `odysseus-dev/odysseus` -- **Audit Target Branch**: `discovery` -- **Frozen Commit SHA**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Snapshot Date**: `2026-07-23T14:49:02Z` -- **Audit Mode**: Read-Only inventory & documentation review - -## Rules of Engagement - -1. **No Code Mutations**: Application code and tests outside `docs/discovery/` remain untouched. -2. **No External Operations**: No GitHub issues, PRs, comments, labels, or branch mutations. -3. **Zero Inferred Success**: Documentation claims require empirical evidence of implementation and reachability. Filenames, README descriptions, and docstrings alone do not constitute proof. -4. **Strict Status Categorization**: All capabilities are assigned exactly one authorized status: - - `verified`: Implemented, reachable, and supported by code evidence. - - `partial`: Partially implemented or missing full frontend/backend connection. - - `disabled`: Gated off by default feature flags or configuration. - - `experimental`: Active but requiring non-standard hardware or runtimes. - - `legacy`: Obsolete feature retained for backwards compatibility. - - `dead-code-candidate`: Code exists but is unreachable from UI or API routes. - - `unverified`: Implementation present but untestable without external secrets or hardware. - -## Evidence Maturity Scale - -Evidence maturity is evaluated independently from catalog feature status. A feature status such as `verified` records that implementation was identified during discovery; it is not a statement that every evidence locator, test claim, line range, or runtime behaviour has passed semantic validation. - -- **E0 - Discovered**: Candidate identified in documentation, route declaration, or source file. -- **E1 - Code-path traced**: Frontend/API entry point connected through services and data handlers. -- **E2 - Test-backed**: At least one directly relevant automated test assertion supports the feature claim. A test file's existence, an unrelated assertion, or an invalid test locator does not establish E2. -- **E3 - Runtime-validated**: Maintainer reproduced behavior in a recorded local environment. -- **E4 - Maintainer-accepted**: Maintainers accepted the feature description and support status. - -## Audit Workflow - -```mermaid -flowchart TD - P0["Phase 0: Snapshot Isolation
(Commit d8a2059)"] --> P1["Phase 1: Codebase Discovery
(Routes, Services, Static JS, Specs)"] - P1 --> P2["Phase 2: Feature Reachability & Verification
(Route matching, FE entrypoints, tests)"] - P2 --> P3["Phase 3: Catalog & Evidence Compilation
(File paths, symbols, exact line ranges)"] - P3 --> P4["Phase 4: Quality & Integrity Audit
(100% path existence check, schema validation)"] -``` - -### Phase 0: Snapshot Isolation -The audit is pinned to git commit `d8a2059df8e53bc7275c45339849d14c8651e73c`. All file paths, symbol declarations, and line ranges map strictly to this commit. - -### Phase 1: Codebase Discovery -All top-level and nested directories were traversed, including: -- Backend Entry Points (`app.py`, `routes/`, `routes/*/*.py`, `companion/`) -- Core Framework (`core/database.py`, `core/session_manager.py`, `core/auth.py`) -- Business Logic Services (`src/`, `services/`, `mcp_servers/`) -- Frontend Assets (`static/app.js`, `static/js/`, `static/index.html`) -- Test Suites (`tests/`, `tests/cli/`, `tests/streaming/`) -- Operations & Docker (`Dockerfile`, `docker-compose*.yml`, `scripts/`) - -### Phase 2: Verification Protocol -For each feature candidate, the following table was evaluated: -- **User Reachability**: Frontend UI element, modal, route, or CLI script. -- **API Entrypoint**: FastAPI `@router` declaration or WebSocket/SSE handler. -- **Backend Execution**: Concrete Python module method, service, or tool call. -- **Data Persistence**: Disk file, SQLite table, or vector collection. -- **Test Coverage**: Automated test file executing assertions against the component. - -### Phase 3: Evidence Linking Standard -Every feature entry in `feature-catalog.json` contains a structured `evidence` list with: -- `path`: Relative path from repository root. -- `symbol`: Route, class, function, or element symbol name. -- `line_range`: Inclusive line range (e.g. `L120-L250`). -- `explanation`: Short factual statement proving reachability or implementation. - -### Phase 4: Quality Check & Schema Constraints -Before finalization: -1. Every evidence file path is validated against the checkout. -2. Every Markdown entry matches `feature-catalog.json`. -3. Recommendation language is separated from empirical factual observations. diff --git a/docs/discovery/domains/agent.md b/docs/discovery/domains/agent.md deleted file mode 100644 index 82574ddc3..000000000 --- a/docs/discovery/domains/agent.md +++ /dev/null @@ -1,153 +0,0 @@ -# Agent - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## AGENT-001 — Autonomous Agent Loop & Tool Execution Engine - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Executes multi-step agent reasoning loops, tool invocation parsing, and automated response generation. - -### Evidence summary - -- `src/agent_loop.py` — `run_agent_loop` — Core loop evaluating model tool requests and executing handlers. -- `src/tool_execution.py` — `execute_tool_call` — Dispatches tool invocation requests to underlying tool handlers. - -### Unknowns - -- Infinite tool loop if termination condition fails. - -## AGENT-002 — Scheduled Tasks & Event Bus Dispatcher - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Schedules background recurring or delayed tasks, emits event bus triggers, and executes automated flows. - -### Evidence summary - -- `routes/task_routes.py` — `@router.get('')` — Fetches active scheduled tasks. -- `src/task_scheduler.py` — `TaskScheduler` — Async task scheduler dispatching cron and delay triggers. - -### Unknowns - -- Task execution failure handling on system restart. - -## AGENT-003 — Webhook Event Subscriptions & Trigger Processing - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Manages incoming/outgoing webhook subscriptions, endpoint authentication tokens, and event triggers. - -### Evidence summary - -- `routes/webhook_routes.py` — `@router.get('/webhooks')` — Returns list of registered webhooks. -- `src/webhook_manager.py` — `WebhookManager` — Handles payload delivery and signature verification. - -### Unknowns - -- SSRF risks when contacting external webhook URLs if unvalidated. - -## AGENT-004 — Assistant Settings, Task Check-Ins & Background Job Monitor - -- **Domain**: `agent` -- **Status**: `partial` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Manages per-user assistant sessions and scheduled check-in settings, drains background job completions, and retains a legacy no-op activity logging shim. - -### Evidence summary - -- `routes/assistant_routes.py` — `setup_assistant_routes` — Active assistant session, settings, manual check-in, run-status and timezone-list endpoints, including the owner-scoping guards. -- `src/bg_monitor.py` — `_drain_agent` — Runs the agent loop headless against a session to produce the background-job follow-up turn. -- `src/bg_monitor.py` — `_run_followup` — Drains completed background jobs and auto-continues the owning session, deferring while a live turn is in progress. -- `src/assistant_log.py` — `log_to_assistant` — Legacy no-op activity logging shim retained for existing callers; documented as inactive rather than as current behaviour. - -### Unknowns - -- Route `/api/assistant/logs` cited in legacy docs is absent from assistant router. -- Existing unit test `tests/cli/test_logs_cli_resolve_nonstring.py` tests CLI target-name resolution logic, not active assistant routes or bg_monitor execution loop. - -## AGENT-005 — Model Context Protocol (MCP) Server Integration - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Integrates external MCP servers over stdio/SSE to expand agent capabilities dynamically. - -### Evidence summary - -- `routes/mcp_routes.py` — `setup_mcp_routes` — Exposes management endpoints for external MCP servers. -- `src/mcp_manager.py` — `McpManager` — Manages MCP server subprocess lifecycles. - -### Unknowns - -- Subprocess leaks if external MCP server process fails to terminate clean. - -## AGENT-006 — AI Interaction Tools & Pipeline Orchestration - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides specialized AI interaction tools for agent self-debugging, debate, and multi-model collaboration. - -### Evidence summary - -- `src/ai_interaction.py` — `init_ai_interaction_tools` — Registers specialized multi-agent interaction primitives. -- `src/builtin_actions.py` — `execute_builtin_action` — Executes pre-built action intent sequences. - -### Unknowns - -- High API token consumption during extended agent debates. - -## AGENT-007 — Subprocess & Background Job Execution Tools - -- **Domain**: `agent` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides sandboxed bash/shell tool execution capabilities with output streaming and background tracking. - -### Evidence summary - -- `src/agent_tools/subprocess_tools.py` — `run_command` — Executes shell commands in background/foreground. -- `src/bg_jobs.py` — `JobManager` — Tracks async background subprocess tasks. - -### Unknowns - -- Arbitrary shell command execution permissions if sandbox confinement fails. diff --git a/docs/discovery/domains/calendar.md b/docs/discovery/domains/calendar.md deleted file mode 100644 index 4a2dad8d3..000000000 --- a/docs/discovery/domains/calendar.md +++ /dev/null @@ -1,45 +0,0 @@ -# Calendar - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## CALENDAR-001 — CalDAV Calendar Synchronization & Account Setup - -- **Domain**: `calendar` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires a controlled external CalDAV server. - -### Purpose - -Connects to remote CalDAV servers (Apple iCloud, Nextcloud, Google) to sync calendar event feeds. - -### Evidence summary - -- `routes/calendar_routes.py` — `setup_calendar_routes` — Exposes CalDAV setup and manual sync trigger routes. -- `src/caldav_sync.py` — `CalDavSync` — Fetches and parses remote iCalendar VEVENT objects. - -### Unknowns - -- Invalid SSL certificates on self-hosted CalDAV servers. - -## CALENDAR-002 — Calendar Event Operations & iCalendar Parsing - -- **Domain**: `calendar` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Creates, updates, deletes, and displays calendar events with timezone conversion and reminder notifications. - -### Evidence summary - -- `routes/calendar_routes.py` — `@router.get('/events')` — Fetches calendar events for requested date window. -- `src/tools/calendar.py` — `CalendarTool` — Agent tool for creating and modifying calendar entries. - -### Unknowns - -- Recurring RRULE event expansion calculation bugs across leap years. diff --git a/docs/discovery/domains/chat.md b/docs/discovery/domains/chat.md deleted file mode 100644 index 8ff904c6b..000000000 --- a/docs/discovery/domains/chat.md +++ /dev/null @@ -1,197 +0,0 @@ -# Chat - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## CHAT-001 — Core Chat Streaming & SSE Message Generation - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E2` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires access to a live LLM provider endpoint (OpenAI API key or local Ollama server). - -### Purpose - -Handles real-time Server-Sent Events (SSE) chat streaming, token rendering, and model response generation. - -### Evidence summary - -- `routes/chat_routes.py` — `chat_stream` — POST /api/chat_stream SSE endpoint; builds the shared chat context, then dispatches to the chat-mode or agent-mode streaming path. -- `routes/chat_helpers.py` — `build_chat_context` — Shared context builder invoked by chat_stream; runs message preprocessing and assembles the memory/RAG/web context preface. -- `src/chat_handler.py` — `ChatHandler.preprocess_message` — Message preprocessing (attachments, URLs, tool preprocessing) reached from build_chat_context via routes/chat_helpers.py:preprocess. -- `src/chat_processor.py` — `ChatProcessor.build_context_preface` — Builds the retrieval and web-source context preface injected into the streamed request. -- `src/llm_core.py` — `stream_llm_with_fallback` — Chat-mode streaming dispatcher called from chat_stream; wraps stream_llm with an ordered provider fallback chain. -- `src/llm_core.py` — `stream_llm` — Per-request streaming entry wrapped by stream_llm_with_fallback; acquires the local model slot and delegates to _stream_llm_inner. -- `src/agent_loop.py` — `stream_agent_loop` — Agent-mode streaming path called from chat_stream when the request selects agent mode. -- `tests/test_chat_metrics.py` — `test_stream_llm_passes_through_llamacpp_timings` — Inspected unit test asserting stream_llm forwards backend generation timings into the emitted metrics chunk. -- `tests/test_resend_message_nondestructive.py` — `test_resend_message_does_not_truncate_by_default` — Inspected unit test asserting the frontend resend path does not truncate prior conversation turns. - -### Unknowns - -- Stream interruption on connection drops requires retry logic. - -## CHAT-002 — Session Management & Conversation State - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Manages session creation, listing, switching, renaming, and persistence of conversation metadata. - -### Evidence summary - -- `routes/session_routes.py` — `@router.get('/api/sessions')` — Lists active sessions filtered by user owner scope. -- `core/session_manager.py` — `SessionManager` — Provides thread-safe session storage operations. - -### Unknowns - -- Concurrent file writes to sessions.json under high load. - -## CHAT-003 — Chat History & Message Editing/Truncation - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides history retrieval, message content updating, message deletion, and history branch truncation. - -### Evidence summary - -- `routes/history/history_routes.py` — `@router.get('/api/history/{session_id}')` — Fetches message history timeline for a session. -- `routes/history_routes.py` — `_sys.modules[__name__] = _canonical` — Backward-compatibility shim module. - -### Unknowns - -- Truncating messages re-indexes context window and clears cached tool calls. - -## CHAT-004 — File & Multimodal Attachment Handling - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Handles uploading, mime validation, image preview, vision encoding, and file attachments in chat messages. - -### Evidence summary - -- `routes/upload_routes.py` — `@router.post('')` — Accepts multi-part file uploads and generates vision metadata. -- `src/upload_handler.py` — `UploadHandler.save_file` — Validates upload size and atomicity on disk. - -### Unknowns - -- Large file uploads may consume server disk space if cleanup task fails. - -## CHAT-005 — Chat Message Search - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Enables full-text keyword search across stored chat messages and sessions. - -### Evidence summary - -- `routes/search_routes.py` — `setup_search_routes` — Registers chat message search endpoint. -- `src/session_search.py` — `search_sessions` — Executes query matching against session transcripts. - -### Unknowns - -- Full table scans on un-indexed text columns for very large databases. - -## CHAT-006 — System Prompts & Preset Management - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides creation, selection, and customization of system prompt presets for chat sessions. - -### Evidence summary - -- `routes/preset_routes.py` — `setup_preset_routes` — API routes for listing and modifying system prompt presets. -- `src/preset_manager.py` — `PresetManager` — Disk-backed manager for prompt presets. - -### Unknowns - -- Invalid JSON syntax in user presets file can corrupt preset loading. - -## CHAT-007 — Emoji Rendering & Twemoji SVG Proxy - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Proxies Twemoji SVG icons locally to render flat SVG emojis in message text without external CDN dependencies. - -### Evidence summary - -- `routes/emoji_routes.py` — `setup_emoji_routes` — Serves locally cached Twemoji SVGs. - -### Unknowns - -- First request fetches SVG from remote CDN before caching locally. - -## CHAT-008 — Input History Recall (Arrow Up) - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Allows users to cycle through previously sent prompt messages in the chat composer input using Arrow-Up/Down keys. - -### Evidence summary - -- `static/js/composerArrowUpRecall.js` — `initComposerRecall` — Listens for ArrowUp keypress on composer textarea. - -### Unknowns - -- Client-side browser storage limits. - -## CHAT-009 — Context Window Compaction & Truncation - -- **Domain**: `chat` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Compacts session transcript history when prompt size exceeds context limits using summarization. - -### Evidence summary - -- `routes/history/history_routes.py` — `@router.post('/api/session/{session_id}/compact')` — Triggers context summarization and compaction. -- `src/context_compactor.py` — `compact_context` — Executes context token pruning and summary generation. - -### Unknowns - -- Aggressive compaction may discard subtle user instructions. diff --git a/docs/discovery/domains/contact.md b/docs/discovery/domains/contact.md deleted file mode 100644 index 47122b20a..000000000 --- a/docs/discovery/domains/contact.md +++ /dev/null @@ -1,24 +0,0 @@ -# Contact - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## CONTACT-001 — CardDAV Contact Management & Address Book Integration - -- **Domain**: `contact` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Connects to CardDAV servers, imports VCard contacts, and provides contact lookup for email/calendar autocomplete. - -### Evidence summary - -- `routes/contacts/contacts_routes.py` — `@router.get('/list')` — Returns contact list filtered by search query. -- `src/tools/contacts.py` — `ContactsTool` — Agent tool for querying user address book contacts. - -### Unknowns - -- VCard 3.0 vs 4.0 property parsing mismatches. diff --git a/docs/discovery/domains/cookbook.md b/docs/discovery/domains/cookbook.md deleted file mode 100644 index 6d22bccef..000000000 --- a/docs/discovery/domains/cookbook.md +++ /dev/null @@ -1,85 +0,0 @@ -# Cookbook - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## COOKBOOK-001 — Local Model Download & Recipe Lifecycle Management - -- **Domain**: `cookbook` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Downloads HuggingFace models, configures execution parameters, and manages local GGUF/MLX model servers. - -### Evidence summary - -- `routes/cookbook_routes.py` — `setup_cookbook_routes` — Exposes model downloading and process serving endpoints. -- `static/js/cookbook.js` — `initCookbook` — UI manager for local model library. - -### Unknowns - -- Disk space exhaustion during multi-gigabyte GGUF weights downloads. - -## COOKBOOK-002 — Hardware Model Fitting ('What Fits?') Analysis Engine - -- **Domain**: `cookbook` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Calculates RAM/VRAM requirements, quantized size, and context overhead to determine model compatibility. - -### Evidence summary - -- `routes/hwfit_routes.py` — `setup_hwfit_routes` — Calculates hardware model compatibility. -- `services/hwfit/fit.py` — `calculate_fit` — Performs parameter and memory fit calculations. - -### Unknowns - -- Inaccurate VRAM estimation for non-standard KV-cache quantization. - -## COOKBOOK-003 — HuggingFace & MLX Model Discovery Services - -- **Domain**: `cookbook` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Searches HuggingFace Hub and local MLX model repositories for compatible GGUF and MLX weights. - -### Evidence summary - -- `services/hwfit/hf_discovery.py` — `search_hf_models` — Queries HuggingFace API for model tags and files. - -### Unknowns - -- HuggingFace API rate limits when searching without an API token. - -## COOKBOOK-004 — Host Docker Access for Inference Container Runtimes - -- **Domain**: `cookbook` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires Docker access and supported physical GPU hardware. - -### Purpose - -Detects and connects to host Docker engine to launch containerized Ollama, vLLM, or SGLang runtimes. - -### Evidence summary - -- `src/host_docker_access.py` — `HostDockerAccess` — Interacts with host docker daemon. - -### Unknowns - -- Permission denied accessing docker socket on non-root setups. diff --git a/docs/discovery/domains/document.md b/docs/discovery/domains/document.md deleted file mode 100644 index ca8ece487..000000000 --- a/docs/discovery/domains/document.md +++ /dev/null @@ -1,108 +0,0 @@ -# Document - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## DOCUMENT-001 — Document & Canvas Artifact Management - -- **Domain**: `document` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Renders dynamic canvas documents, handles live editing, markdown preview, and side-by-side artifact display. - -### Evidence summary - -- `routes/document_routes.py` — `setup_document_routes` — Registers document artifact CRUD routes. -- `static/js/document.js` — `initDocumentView` — Renders interactive canvas document panel. - -### Unknowns - -- Concurrent edits on the same document artifact. - -## DOCUMENT-002 — PDF Form Processing & High-Fidelity Rendering - -- **Domain**: `document` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires optional PyMuPDF (`fitz`) or pypdf runtime dependency. - -### Purpose - -Extracts form fields from PDF files, fills dynamic values, and generates PDF previews. - -### Evidence summary - -- `src/pdf_runtime.py` — `load_pymupdf_for_pdf_viewer` — Loads optional PyMuPDF runtime for PDF viewing. -- `src/pdf_forms.py` — `extract_form_fields` — Handles PDF form field extraction and filling. -- `tests/test_document_pdf_marker.py` — `test_marker_removed_without_eating_following_text` — Tests PDF text extraction wrapper stripping without content corruption. - -### Unknowns - -- Complex XFA PDF forms may not extract cleanly with standard pdf parsers. - -## DOCUMENT-003 — Personal Document Indexing & RAG Retrieval - -- **Domain**: `document` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Indexes local user documents (PDF, DOCX, TXT) into ChromaDB for semantic vector retrieval. - -### Evidence summary - -- `routes/personal_routes.py` — `setup_personal_routes` — Personal document RAG indexing and search API endpoints. -- `src/personal_docs.py` — `PersonalDocsManager` — Handles file text chunking and vector storage. - -### Unknowns - -- Slow vector embedding indexing step for massive multi-thousand page documents. - -## DOCUMENT-004 — Document Conversion & Text Extraction Engine - -- **Domain**: `document` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Converts office formats (.docx, .xlsx, .pptx) and HTML into clean Markdown text representations. - -### Evidence summary - -- `src/markitdown_runtime.py` — `convert_to_markdown` — Converts binary office documents into structured Markdown text. - -### Unknowns - -- Formatting loss when parsing legacy binary doc/xls files. - -## DOCUMENT-005 — Document Library UI Navigation - -- **Domain**: `document` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides dedicated UI view for browsing, filtering, and organizing saved user documents. - -### Evidence summary - -- `static/js/documentLibrary.js` — `initDocumentLibrary` — Renders document library navigation grid. -- `app.py` — `serve_library` — Serves SPA shell for /library route. - -### Unknowns - -- Large folder trees may cause initial DOM render slowdown. diff --git a/docs/discovery/domains/email.md b/docs/discovery/domains/email.md deleted file mode 100644 index 12b28ca43..000000000 --- a/docs/discovery/domains/email.md +++ /dev/null @@ -1,87 +0,0 @@ -# Email - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## EMAIL-001 — Email Account Setup, IMAP/SMTP Connection & Polling - -- **Domain**: `email` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires a controlled live IMAP account and network access. - -### Purpose - -Configures IMAP/SMTP email accounts, validates TLS certificates, and polls background inbox updates. - -### Evidence summary - -- `routes/email_routes.py` — `setup_email_routes` — Sets up email account management and synchronization routes. -- `routes/email_pollers.py` — `_start_poller` — Background poller for email inbox synchronization. -- `tests/test_service_health_email.py` — `test_email_ok_all_connect` — Tests IMAP connection health probing and status reporting. - -### Unknowns - -- Account lockouts if bad credentials are repeatedly polled. - -## EMAIL-002 — Email Searching, Threading & Message Operations - -- **Domain**: `email` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Parses email headers, folds signatures, groups messages into threads, and executes full-text email search. - -### Evidence summary - -- `routes/email_routes.py` — `@router.get('/search')` — Executes search across cached email headers and text. -- `src/email_thread_parser.py` — `parse_email_thread` — Builds conversation tree from Message-ID and In-Reply-To headers. - -### Unknowns - -- Malformed MIME email structures failing HTML sanitization. - -## EMAIL-003 — Email Composition, Draft Management & Sending - -- **Domain**: `email` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires a controlled live SMTP account and network access. - -### Purpose - -Creates, saves, and dispatches HTML/plaintext email messages via SMTP. - -### Evidence summary - -- `routes/email_routes.py` — `@router.post('/send')` — Sends email message via user SMTP credentials. - -### Unknowns - -- SMTP connection drop mid-send causing unsent mail state. - -## EMAIL-004 — Email MCP Server & Codex Integration Bridge - -- **Domain**: `email` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Exposes constrained email reading and draft capabilities to external Codex / MCP agents with scope checks. - -### Evidence summary - -- `mcp_servers/email_server.py` — `EmailMcpServer` — MCP server exposing email tools over stdio/SSE. -- `routes/codex_routes.py` — `setup_codex_routes` — Bridge endpoints for external Codex plugin integration. - -### Unknowns - -- Unauthorized mail sending if token scopes are improperly scoped. diff --git a/docs/discovery/domains/frontend.md b/docs/discovery/domains/frontend.md deleted file mode 100644 index 6f9891783..000000000 --- a/docs/discovery/domains/frontend.md +++ /dev/null @@ -1,149 +0,0 @@ -# Frontend - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## FRONTEND-001 — Single Page Application Shell & Client Router - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Main HTML5 SPA shell, DOM lifecycle initializers, tab navigation, and deep-link route handlers. - -### Evidence summary - -- `static/index.html` — `index.html` — Main SPA entry point containing modal roots and CSS bundles. -- `app.py` — `serve_index` — Serves index.html with dynamically generated CSP nonces. - -### Unknowns - -- Stale browser static cache if asset hashing is omitted during deployment. - -## FRONTEND-002 — Dynamic Theme, Color System & Custom Fonts - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Supports dark/light themes, custom CSS variables, color picker controls, and user font uploads. - -### Evidence summary - -- `static/js/theme.js` — `applyTheme` — Applies custom HSL theme variables to DOM document root. -- `routes/font_routes.py` — `setup_font_routes` — Allows uploading and serving custom WOFF2 font files. - -### Unknowns - -- Flash of unstyled content (FOUC) on slow connections. - -## FRONTEND-003 — Window Manager, Tile Layout & Modal Control System - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Manages draggable tool windows, snapped multi-tile viewports, modal dialog Z-ordering, and ESC key stacks. - -### Evidence summary - -- `static/js/modalManager.js` — `ModalManager` — Controls modal open/close transitions and focus trapping. -- `static/js/tileManager.js` — `TileManager` — Handles viewport split-pane grid arrangements. - -### Unknowns - -- Overlap artifacts when opening many simultaneous tool floating windows. - -## FRONTEND-004 — Global Keyboard Shortcuts & Accessibility Controls - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides configurable hotkeys (Ctrl+K search, Esc close, Alt+1-9 tabs) and high-contrast accessibility options. - -### Evidence summary - -- `static/js/keyboard-shortcuts.js` — `initShortcuts` — Binds global keydown handlers for system shortcuts. -- `static/js/a11y.js` — `initA11y` — Applies ARIA roles and dyslexic font toggles. - -### Unknowns - -- Browser keybinding collisions with browser default hotkeys. - -## FRONTEND-005 — Markdown, LaTeX & Code Block Streaming Renderer - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Parses incoming SSE markdown streams, renders KaTeX math formulas, syntax-highlighted code, and interactive runners. - -### Evidence summary - -- `static/js/markdown.js` — `renderMarkdown` — Converts markdown prose to HTML nodes with syntax highlighting. -- `static/js/streamingSegmenter.js` — `Segmenter` — Parses un-closed markdown fences during live stream. - -### Unknowns - -- DOM thrashing if streaming segmenter updates UI too frequently. - -## FRONTEND-006 — Interactive Tour & Guided Onboarding System - -- **Domain**: `frontend` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Presents interactive step-by-step feature tours and UI tooltip hints for new users. - -### Evidence summary - -- `static/js/tourHints.js` — `startTour` — Renders guided feature tour overlays over target UI elements. - -### Unknowns - -- Tour step misalignment if window is resized mid-tour. - -## FRONTEND-007 — Background Effects Prototyping Sandbox - -- **Domain**: `frontend` -- **Status**: `dead-code-candidate` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Standalone sandbox page for prototyping visual background animations, waves, and whirlpool effects. - -### Evidence summary - -- `app.py` — `serve_backgrounds` — Serves visual background sandbox HTML page route. -- `static/wave-variants.html` — `wave-variants.html` — Interactive background effect prototyping sandbox variant. - -### Unknowns - -- Route `/backgrounds` in app.py L918 attempts to serve `static/backgrounds.html` which is missing from disk; variant templates `wave-variants.html` and `whirlpool-variants.html` exist. diff --git a/docs/discovery/domains/media.md b/docs/discovery/domains/media.md deleted file mode 100644 index cc0622ceb..000000000 --- a/docs/discovery/domains/media.md +++ /dev/null @@ -1,170 +0,0 @@ -# Media - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## MEDIA-001 — Gallery Image Library & Album Operations - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Organizes images into custom albums, provides grid browsing, tagging, and album metadata management. - -### Evidence summary - -- `routes/gallery/gallery_routes.py` — `@router.get('/api/gallery/library')` — Fetches image library list with tag filters. -- `static/js/gallery.js` — `initGallery` — Main gallery grid renderer and uploader. - -### Unknowns - -- Thumbnail generation overhead for high-resolution RAW camera images. - -## MEDIA-002 — Image Processing, AI Upscaling & Style Transfer - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Executes local image enhancement, background removal, face sharpening, and AI upscaling operations. - -### Evidence summary - -- `routes/gallery/gallery_routes.py` — `@router.post('/api/gallery/ai-upscale')` — Runs RealESRGAN image upscaling. -- `routes/gallery/gallery_routes.py` — `@router.post('/api/image/remove-bg')` — Executes background removal pass. - -### Unknowns - -- High GPU memory allocation when upscaling 4K images. - -## MEDIA-003 — Interactive Image Canvas Editor & Persisted Drafts - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides full multi-layer raster canvas editor, brush tools, transforms, masks, and draft project persistence. - -### Evidence summary - -- `routes/editor_draft_routes.py` — `setup_editor_draft_routes` — API routes for saving and loading canvas project drafts. -- `static/js/editor/history-panel.js` — `HistoryManager` — Canvas undo/redo stack manager. - -### Unknowns - -- Browser memory leak if multi-gigabyte layer undo buffers are kept indefinitely. - -## MEDIA-004 — Text-to-Speech (TTS) Synthesis Service - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Synthesizes spoken audio from text using local Kokoro, EdgeTTS, or OpenAI TTS engines. - -### Evidence summary - -- `routes/tts_routes.py` — `@router.post('/synthesize')` — Synthesizes TTS audio clip. -- `services/tts/tts_service.py` — `TTSService` — Provider abstraction layer for audio speech generation. - -### Unknowns - -- Audio synthesis latency on CPU-only hardware setups. - -## MEDIA-005 — Speech-to-Text (STT) Audio Transcription Service - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Transcribes user audio recordings into text using faster-whisper or local speech models. - -### Evidence summary - -- `routes/stt_routes.py` — `@router.post('/transcribe')` — Accepts multipart audio file and returns transcription text. -- `services/stt/stt_service.py` — `STTService` — Whisper audio transcription engine wrapper. - -### Unknowns - -- Missing ffmpeg system dependency prevents audio format decoding. - -## MEDIA-006 — Digital Signature Stamp Storage & Placement - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Stores transparent PNG user signatures and stamps for placement onto PDF forms and documents. - -### Evidence summary - -- `routes/signature_routes.py` — `setup_signature_routes` — CRUD endpoints for managing user signature PNG stamps. - -### Unknowns - -- Cross-site scripting if signature image titles contain unescaped user input. - -## MEDIA-007 — Generated Image Artifact Route & MCP Integration - -- **Domain**: `media` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Serves generated AI artwork artifacts and integrates with image generation MCP server. - -### Evidence summary - -- `app.py` — `serve_generated_image` — Serves generated image artifacts with cache headers. -- `src/generated_images.py` — `resolve_generated_image_path` — Confines requested image path within artifacts directory. - -### Unknowns - -- Path traversal vulnerability if filename parameter is un-sanitized. - -## MEDIA-008 — Native MLX Image Bridge (macOS Apple Silicon) - -- **Domain**: `media` -- **Status**: `experimental` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires Apple Silicon, macOS tooling, and the compiled MLX bridge. - -### Purpose - -Native Apple Swift bridge for hardware-accelerated diffusion and MLX image colorization on macOS. - -### Evidence summary - -- `swift/odysseus-mlx-image-bridge/Package.swift` — `Package` — Swift package manifest for native MLX image bridge. -- `scripts/mlx_image_server.py` — `main` — Python daemon wrapping native Swift MLX binary. - -### Unknowns - -- Binary build requires Xcode command line tools build step (`build-macos-app.sh`). diff --git a/docs/discovery/domains/memory.md b/docs/discovery/domains/memory.md deleted file mode 100644 index 22b9f8b78..000000000 --- a/docs/discovery/domains/memory.md +++ /dev/null @@ -1,24 +0,0 @@ -# Memory - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## MEMORY-001 — Persistent Long-Term Memory & Vector Indexing - -- **Domain**: `memory` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Extracts facts, user preferences, and temporal memories from chat sessions into vector/relational storage. - -### Evidence summary - -- `routes/memory/memory_routes.py` — `@router.get('')` — Fetches long-term user memory timeline. -- `services/memory/memory_extractor.py` — `MemoryExtractor` — LLM-driven fact extraction from conversation transcripts. - -### Unknowns - -- Conflicting memory facts extracted from contradictory user prompts. diff --git a/docs/discovery/domains/model.md b/docs/discovery/domains/model.md deleted file mode 100644 index 1855c0d3f..000000000 --- a/docs/discovery/domains/model.md +++ /dev/null @@ -1,177 +0,0 @@ -# Model - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## MODEL-001 — Multi-Provider LLM Model Discovery & Metadata Management - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Discovers models from OpenAI, Anthropic, Ollama, vLLM, LMStudio, OpenRouter, and Google AI Studio endpoints. - -### Evidence summary - -- `routes/model_routes.py` — `@router.get('/api/models')` — Returns unified list of available models across providers. -- `src/model_discovery.py` — `ModelDiscovery.discover_all` — Queries connected provider endpoints for available model IDs. - -### Unknowns - -- Remote endpoint timeouts may slow down full discovery refresh. - -## MODEL-002 — Model Capability & Context Limits Detection - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Detects vision, tool calling, reasoning, and context window limits for connected model endpoints. - -### Evidence summary - -- `src/model_capabilities.py` — `get_model_capabilities` — Maps model names to vision and tool support flags. -- `src/endpoint_resolver.py` — `resolve_endpoint_headers` — Resolves auth headers and target URLs for model endpoints. - -### Unknowns - -- Incorrect context limit metadata for unlisted custom fine-tunes. - -## MODEL-003 — LLM Core Provider Communication & Fallback Routing - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Provider dispatch, header injection and fallback advancement are only observable against a reachable LLM provider endpoint; not exercised in this documentation pass. - -### Purpose - -Manages HTTP request dispatching, authorization header injection, and fallback provider routing for LLM calls. - -### Evidence summary - -- `src/llm_core.py` — `llm_call_async` — Non-streaming provider request dispatcher: resolves the endpoint, injects authorization headers and executes the HTTP call. -- `src/llm_core.py` — `llm_call_async_with_fallback` — Ordered fallback wrapper that retries llm_call_async across the configured candidate endpoints. -- `src/llm_core.py` — `stream_llm_with_fallback` — Ordered fallback wrapper for the streaming path; advances to the next candidate when a provider yields an empty completion. - -### Unknowns - -- Unexpected API changes in upstream third-party model providers. - -## MODEL-004 — Model Selection & Display Ordering Preferences - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Allows pinning, sorting, and hiding specific models in the UI selection dropdown. - -### Evidence summary - -- `routes/model_routes.py` — `@router.post('/order')` — Saves custom model display order preference. - -### Unknowns - -- Stale model IDs in custom order lists after model endpoints are removed. - -## MODEL-005 — Side-by-Side Model Comparison (A/B Testing) - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Enables dual-model side-by-side response evaluation, arena scoring, and latency comparison. - -### Evidence summary - -- `routes/compare/compare_routes.py` — `@router.post('/start')` — Starts a parallel dual-model comparison stream. -- `static/js/compare/index.js` — `initCompareView` — Renders side-by-side model chat panes. - -### Unknowns - -- High memory and network usage when streaming two model responses simultaneously. - -## MODEL-006 — GitHub Copilot Device Flow Authentication - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E2` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires an interactive GitHub Copilot OAuth device-flow account. - -### Purpose - -Authenticates with GitHub Copilot via OAuth device flow to use Copilot models directly. - -### Evidence summary - -- `routes/copilot_routes.py` — `setup_copilot_routes` — Builds the Copilot device-flow router at prefix /api/copilot, wiring _start_device_flow and _poll_device_flow. -- `routes/device_flow.py` — `create_device_flow_router` — Shared factory registering POST /device/start and POST /device/poll under the caller-supplied prefix. -- `src/copilot.py` — `request_device_code` — Issues the GitHub device-code request that begins the Copilot OAuth device flow. -- `src/copilot.py` — `poll_access_token` — Polls GitHub for the access token once the user has authorized the device code. -- `tests/test_provider_device_flow_js.py` — `test_copilot_success_uses_complete_verification_uri` — Inspected unit test asserting the Copilot device-flow runner surfaces the complete verification URI returned by the backend. - -### Unknowns - -- Token expiration requires manual device re-authentication. - -## MODEL-007 — ChatGPT Subscription Device Flow Authentication - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E2` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires an interactive ChatGPT subscription OAuth flow. - -### Purpose - -Authenticates with ChatGPT Pro/Plus subscription tokens via device login flow. - -### Evidence summary - -- `routes/chatgpt_subscription_routes.py` — `setup_chatgpt_subscription_routes` — Builds the ChatGPT subscription device-flow router at prefix /api/chatgpt-subscription. -- `routes/device_flow.py` — `create_device_flow_router` — Shared factory registering POST /device/start and POST /device/poll under the caller-supplied prefix. -- `src/chatgpt_subscription.py` — `request_device_code` — Issues the ChatGPT device-authorization request that begins the subscription OAuth device flow. -- `src/chatgpt_subscription.py` — `poll_device_auth` — Polls the ChatGPT device-authorization endpoint for completion using the stored device_auth_id and user_code. -- `tests/test_provider_device_flow_js.py` — `test_chatgpt_success_uses_plain_verification_uri` — Inspected unit test asserting the ChatGPT device-flow runner uses the plain verification URI rather than the Copilot complete-URI form. - -### Unknowns - -- Changes in OpenAI auth endpoint security challenges. - -## MODEL-008 — Embedding Model Lane & Vector Provider Setup - -- **Domain**: `model` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Configures local sentence-transformers, FastEmbed, or remote OpenAI embedding model lanes. - -### Evidence summary - -- `routes/embedding_routes.py` — `setup_embedding_routes` — Provides embedding provider configuration endpoints. -- `src/embeddings.py` — `EmbeddingManager` — Generates dense vector embeddings for RAG and memory. - -### Unknowns - -- First-time download of heavy PyTorch model weights on CPU-only machines. diff --git a/docs/discovery/domains/note.md b/docs/discovery/domains/note.md deleted file mode 100644 index e0c197428..000000000 --- a/docs/discovery/domains/note.md +++ /dev/null @@ -1,24 +0,0 @@ -# Note - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## NOTE-001 — Interactive Notes & Checklist Management - -- **Domain**: `note` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides Google Keep-style notes, rich markdown text, checklist items, pinning, color tags, and reminders. - -### Evidence summary - -- `routes/note/note_routes.py` — `@router.get('')` — Lists all user notes with pin and archive states. -- `static/js/notes.js` — `initNotesView` — Main interactive notes grid and modal manager. - -### Unknowns - -- Concurrent edits on note item checkboxes. diff --git a/docs/discovery/domains/platform.md b/docs/discovery/domains/platform.md deleted file mode 100644 index 037edddde..000000000 --- a/docs/discovery/domains/platform.md +++ /dev/null @@ -1,213 +0,0 @@ -# Platform - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## PLATFORM-001 — Application Initialization & Lifespan Management - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Orchestrates server startup, database table migration, background daemon initialization, and clean shutdown. - -### Evidence summary - -- `app.py` — `_lifespan` — FastAPI lifespan context manager executing startup tasks. -- `src/app_initializer.py` — `initialize_app` — Initializes app directories, DB schemas, and logging. - -### Unknowns - -- Un-handled exceptions during startup halt application launch. - -## PLATFORM-002 — System Health, Readiness & Version Monitoring APIs - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Exposes Liveness (/api/health), Readiness (/api/ready), App Version (/api/version), and Client Perf APIs. - -### Evidence summary - -- `app.py` — `readiness_check` — Performs system component integrity check. -- `src/readiness.py` — `check_readiness` — Checks database, storage, and key paths for read/write access. - -### Unknowns - -- Readiness check delays if verifying connectivity to offline remote endpoints. - -## PLATFORM-003 — Database Schema, Migrations & SQLite Persistence - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Defines core relational tables (users, tokens, tasks, sessions) and executes automated SQLite schema upgrades. - -### Evidence summary - -- `core/database.py` — `init_db` — Creates ORM tables and establishes connection pool. -- `scripts/update_database.py` — `run_migrations` — Applies missing schema columns and indices. - -### Unknowns - -- SQLite file lock contention under high concurrent write loads. - -## PLATFORM-004 — User Data Export & Import Backup Infrastructure - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Exports complete user workspace state (sessions, memory, skills, notes, presets) into a zip archive. - -### Evidence summary - -- `routes/backup_routes.py` — `setup_backup_routes` — Handles workspace data export and import upload unpack. -- `docs/backup-restore.md` — `Documentation` — Backup and restore operational documentation. - -### Unknowns - -- Corrupt archive files causing partial data restore. - -## PLATFORM-005 — File Cleanup & Storage Maintenance Engine - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Scans data directories for orphaned files, old uploads, temporary vision images, and frees disk space. - -### Evidence summary - -- `routes/cleanup/cleanup_routes.py` — `@router.get('/preview')` — Previews reclaimable disk space across storage directories. -- `src/cleanup_service.py` — `CleanupService` — Executes filesystem purge of orphaned asset files. - -### Unknowns - -- Deletes files uploaded in active sessions if retention window is set too short. - -## PLATFORM-006 — System Health & RAG Diagnostic Suite - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Executes real-time integrity diagnostics across ChromaDB, SearXNG, local models, and network interfaces. - -### Evidence summary - -- `routes/diagnostics_routes.py` — `setup_diagnostics_routes` — Runs subsystem health check suite. -- `src/service_health.py` — `collect_health_status` — Inspects vector database, email, search, and local provider status. - -### Unknowns - -- Diagnostic timeout if external search provider is unreachable. - -## PLATFORM-007 — Desktop CLI Utilities & Shell Integration Tools - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides command-line interface tools (`odysseus`, `odysseus-mcp`, `odysseus-mail`) for terminal usage. - -### Evidence summary - -- `scripts/_lib/cli.py` — `main` — Shared CLI framework for terminal helper commands. -- `scripts/odysseus` — `odysseus` — Main terminal launcher script. - -### Unknowns - -- Outdated CLI scripts if backend API schemas change. - -## PLATFORM-008 — Desktop Companion App Integration - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides API routes and pairing mechanisms for the native macOS/desktop menu bar companion app. - -### Evidence summary - -- `companion/routes.py` — `setup_companion_routes` — Endpoints for pairing and status sync with desktop companion. -- `companion/pairing.py` — `PairingManager` — Generates and validates companion pairing codes. - -### Unknowns - -- Pairing code expiration timing window. - -## PLATFORM-009 — Docker Containerization & GPU Hardware Manifests - -- **Domain**: `platform` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires Docker GPU pass-through and compatible host drivers. - -### Purpose - -Provides multi-stage Dockerfile and Docker Compose manifests for CPU, NVIDIA CUDA, and AMD ROCm GPUs. - -### Evidence summary - -- `Dockerfile` — `multi-stage-build` — Multi-stage container build environment. -- `docker-compose.gpu-nvidia.yml` — `nvidia-gpu-manifest` — NVIDIA GPU pass-through container specification. -- `scripts/check-docker-gpu.sh` — `check-docker-gpu` — Automated diagnostic test script for host NVIDIA GPU passthrough. - -### Unknowns - -- Driver version incompatibility with host NVIDIA/AMD kernel drivers. - -## PLATFORM-010 — Legacy FAISS Vector Index Migration Script - -- **Domain**: `platform` -- **Status**: `legacy` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Legacy utility script to migrate older FAISS vector indices into ChromaDB. - -### Evidence summary - -- `scripts/migrate_faiss_to_chroma.py` — `migrate_faiss` — Reads FAISS vector index files and writes to ChromaDB collection. - -### Unknowns - -- Superseded by native ChromaDB vector index pipeline. diff --git a/docs/discovery/domains/research.md b/docs/discovery/domains/research.md deleted file mode 100644 index 2df5a4b75..000000000 --- a/docs/discovery/domains/research.md +++ /dev/null @@ -1,86 +0,0 @@ -# Research - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## RESEARCH-001 — Deep Research Execution Engine & SSE Progress Streaming - -- **Domain**: `research` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Executes multi-step recursive deep research tasks, web page scraping, synthesis, and streams live progress. - -### Evidence summary - -- `routes/research/research_routes.py` — `@router.post('/api/research/start')` — Initiates deep research job. -- `src/deep_research.py` — `DeepResearchEngine` — Recursive search and summary crawler. - -### Unknowns - -- High memory consumption when parsing multi-megabyte HTML target pages. - -## RESEARCH-002 — Research Library, Detail View & Image Controls - -- **Domain**: `research` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Stores completed research reports, generated diagrams, reference links, and manages image visibility. - -### Evidence summary - -- `routes/research/research_routes.py` — `@router.get('/api/research/library')` — Returns all saved research reports. - -### Unknowns - -- Orphaned report files if storage directory is modified out-of-band. - -## RESEARCH-003 — Web Search Engine Integration (SearXNG & Multi-Provider) - -- **Domain**: `research` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires an active SearXNG instance or external search API provider. - -### Purpose - -Queries SearXNG, DuckDuckGo, or Google Search instances to retrieve web search snippets. - -### Evidence summary - -- `routes/search_routes.py` — `setup_search_routes` — Defines /api/search, /api/search/config, and /api/search/query endpoints. -- `src/search/core.py` — `SearchEngine` — Compatibility module aliasing services.search.core. -- `tests/test_search_ranking.py` — `test_news_queries_prefer_news_sources_over_sports_and_social_results` — Tests search result domain ranking and scoring. - -### Unknowns - -- Search provider IP throttling or rate-limiting. - -## RESEARCH-004 — Research Result Peeking & Topic Spinoff Generation - -- **Domain**: `research` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Extracts preliminary research snippets and spawns child research sessions focused on specific sub-topics. - -### Evidence summary - -- `routes/research/research_routes.py` — `@router.post('/api/research/spinoff/{session_id}')` — Spawns child research session for specific query. - -### Unknowns - -- Deep recursion tree depth when spawning multiple nested spinoffs. diff --git a/docs/discovery/domains/security.md b/docs/discovery/domains/security.md deleted file mode 100644 index 2d07dae22..000000000 --- a/docs/discovery/domains/security.md +++ /dev/null @@ -1,154 +0,0 @@ -# Security - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## SECURITY-001 — Authentication, Session Cookies & User Management - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Handles bcrypt password hashing, session cookie issuance, authentication enforcement, and user administration. - -### Evidence summary - -- `routes/auth_routes.py` — `@router.post('/login')` — Authenticates credentials and sets session cookie. -- `core/auth.py` — `AuthManager` — Handles user creation, password verification, and session tokens. - -### Unknowns - -- Cookie session hijack if deployed over unencrypted HTTP without HTTPS cookie flags. - -## SECURITY-002 — System Vault Encrypted Secret Storage - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: pending — Requires installed Bitwarden CLI (`bw`) executable. - -### Purpose - -Encrypts API keys, passphrases, and third-party secrets on disk using AES-GCM / PBKDF2 key derivation. - -### Evidence summary - -- `routes/vault_routes.py` — `setup_vault_routes` — Admin routes for vault configuration, login, unlock, lock, and logout. -- `src/secret_storage.py` — `SecretStorage` — Fernet symmetric key DB secret encryption. -- `tests/test_vault_password_not_in_argv.py` — `test_bw_password_not_in_argv` — Verifies master password is fed via stdin and never appears in process argv. - -### Unknowns - -- Loss of vault master passphrase renders all encrypted secrets permanently unrecoverable. - -## SECURITY-003 — API Token Management & Scope Access Control - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Generates scoped API bearer tokens (read/write/admin) for external tool and script authentication. - -### Evidence summary - -- `routes/api_token_routes.py` — `setup_api_token_routes` — Exposes API token creation, scope assignment, and revocation. -- `core/database.py` — `ApiToken` — SQLAlchemy ORM schema for API tokens and permissions. - -### Unknowns - -- Leaked API bearer tokens with excessive permission scopes. - -## SECURITY-004 — Prompt Security & Injection Defense Engine - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Scans system prompts and external inputs for prompt injection attempts, jailbreaks, and sensitive data leaks. - -### Evidence summary - -- `src/prompt_security.py` — `untrusted_context_message` — Wraps untrusted context with guard delimiters and sets metadata.trusted = False. -- `src/tool_security.py` — `NON_ADMIN_BLOCKED_TOOLS` — Enforces tool execution safety for non-admin user roles. -- `tests/test_skill_index_prompt_injection.py` — `test_skill_index` — Verifies skill index descriptions cannot leak into trusted system prompts. -- `tests/test_tool_output_prompt_injection.py` — `test_tool_output` — Tool output injection guards. - -### Unknowns - -- False positives blocking legitimate complex coding or security prompts. - -## SECURITY-005 — URL & Path Confinement Security Guards - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E1` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Prevents SSRF attacks and path traversal by validating target IP addresses and resolving symlinks. - -### Evidence summary - -- `src/url_safety.py` — `check_outbound_url` — Rejects non-HTTP(S) schemes, link-local, cloud metadata SSRF addresses. -- `src/url_security.py` — `validate_public_http_url` — Validates public-facing endpoints. -- `tests/test_url_safety.py` — `test_url_safety` — Scheme validation, cloud metadata SSRF rejection, IP classification. -- `tests/test_tool_path_confinement.py` — `test_path_confinement` — Path traversal checks. -- `tests/test_workspace_confine.py` — `test_workspace_confine` — Workspace confinement checks. - -### Unknowns - -- DNS rebinding attacks if IP address is re-resolved post-validation. - -## SECURITY-006 — HTTP Security Headers Middleware - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Injects standard OWASP HTTP security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options). - -### Evidence summary - -- `core/middleware.py` — `SecurityHeadersMiddleware` — Sets strict security headers and CSP nonces on HTTP responses. - -### Unknowns - -- Strict Content Security Policy (CSP) blocking third-party embedded web resources. - -## SECURITY-007 — Admin System Data Wipe ('Danger Zone') - -- **Domain**: `security` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Provides administrative reset operations to wipe sessions, cache, uploaded files, or factory reset state. - -### Evidence summary - -- `routes/admin_wipe/admin_wipe_routes.py` — `@router.delete('/wipe/{kind}')` — Executes systemic data wipe based on requested scope. - -### Unknowns - -- Accidental catastrophic data loss if triggered without user confirmation. diff --git a/docs/discovery/domains/skill.md b/docs/discovery/domains/skill.md deleted file mode 100644 index 43fb72180..000000000 --- a/docs/discovery/domains/skill.md +++ /dev/null @@ -1,24 +0,0 @@ -# Skill - -Features in this document are generated from [`../feature-catalog.json`](../feature-catalog.json), the canonical inventory. - -## SKILL-001 — Dynamic Skill Management & Code Execution Engine - -- **Domain**: `skill` -- **Status**: `verified` -- **Evidence Maturity**: `E0` -- **Commit Verified**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Runtime Validation**: not-required — No separate environment-dependent runtime validation was identified during this documentation pass. - -### Purpose - -Allows users to create, import, edit, test, and execute custom Python/Markdown skills dynamically. - -### Evidence summary - -- `routes/skills_routes.py` — `setup_skills_routes` — Exposes CRUD and remote import routes for user skills. -- `services/memory/skills.py` — `SkillsManager` — Handles skill storage, parsing, and execution. - -### Unknowns - -- Arbitrary code execution risks if skill import URL is untrusted. diff --git a/docs/discovery/feature-catalog.json b/docs/discovery/feature-catalog.json deleted file mode 100644 index 93cdc6fd0..000000000 --- a/docs/discovery/feature-catalog.json +++ /dev/null @@ -1,4797 +0,0 @@ -[ - { - "id": "CHAT-001", - "domain": "chat", - "name": "Core Chat Streaming & SSE Message Generation", - "purpose": "Handles real-time Server-Sent Events (SSE) chat streaming, token rendering, and model response generation.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chatStream.js", - "static/js/chat.js", - "static/js/streamingRenderer.js" - ], - "backend_entrypoints": [ - "routes/chat_routes.py:chat_stream", - "routes/chat_helpers.py:build_chat_context", - "src/chat_handler.py:ChatHandler.preprocess_message", - "src/chat_processor.py:ChatProcessor.build_context_preface", - "src/llm_core.py:stream_llm_with_fallback", - "src/agent_loop.py:stream_agent_loop" - ], - "routes": [ - "/api/chat_stream", - "/api/chat" - ], - "configuration": [ - "OPENAI_API_KEY", - "OLLAMA_BASE_URL", - "REQUEST_TIMEOUT" - ], - "persistence": [ - "SESSIONS_FILE", - "DATA_DIR/sessions/" - ], - "dependencies": [ - "fastapi", - "starlette.responses.StreamingResponse", - "httpx" - ], - "tests": [ - "tests/test_chat_metrics.py", - "tests/test_resend_message_nondestructive.py" - ], - "documentation": [ - "docs/chat.webm", - "README.md" - ], - "risks": [ - "Stream interruption on connection drops requires retry logic." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/chat_routes.py", - "symbol": "chat_stream", - "line_range": "L702-L1870", - "explanation": "POST /api/chat_stream SSE endpoint; builds the shared chat context, then dispatches to the chat-mode or agent-mode streaming path." - }, - { - "path": "routes/chat_helpers.py", - "symbol": "build_chat_context", - "line_range": "L669-L863", - "explanation": "Shared context builder invoked by chat_stream; runs message preprocessing and assembles the memory/RAG/web context preface." - }, - { - "path": "src/chat_handler.py", - "symbol": "ChatHandler.preprocess_message", - "line_range": "L123-L315", - "explanation": "Message preprocessing (attachments, URLs, tool preprocessing) reached from build_chat_context via routes/chat_helpers.py:preprocess." - }, - { - "path": "src/chat_processor.py", - "symbol": "ChatProcessor.build_context_preface", - "line_range": "L263-L493", - "explanation": "Builds the retrieval and web-source context preface injected into the streamed request." - }, - { - "path": "src/llm_core.py", - "symbol": "stream_llm_with_fallback", - "line_range": "L2794-L2895", - "explanation": "Chat-mode streaming dispatcher called from chat_stream; wraps stream_llm with an ordered provider fallback chain." - }, - { - "path": "src/llm_core.py", - "symbol": "stream_llm", - "line_range": "L2131-L2151", - "explanation": "Per-request streaming entry wrapped by stream_llm_with_fallback; acquires the local model slot and delegates to _stream_llm_inner." - }, - { - "path": "src/agent_loop.py", - "symbol": "stream_agent_loop", - "line_range": "L3079-L5248", - "explanation": "Agent-mode streaming path called from chat_stream when the request selects agent mode." - }, - { - "path": "tests/test_chat_metrics.py", - "symbol": "test_stream_llm_passes_through_llamacpp_timings", - "line_range": "L129-L136", - "explanation": "Inspected unit test asserting stream_llm forwards backend generation timings into the emitted metrics chunk." - }, - { - "path": "tests/test_resend_message_nondestructive.py", - "symbol": "test_resend_message_does_not_truncate_by_default", - "line_range": "L23-L36", - "explanation": "Inspected unit test asserting the frontend resend path does not truncate prior conversation turns." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E2", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires access to a live LLM provider endpoint (OpenAI API key or local Ollama server)." - } - }, - { - "id": "CHAT-002", - "domain": "chat", - "name": "Session Management & Conversation State", - "purpose": "Manages session creation, listing, switching, renaming, and persistence of conversation metadata.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/sessions.js", - "static/js/sidebar-layout.js" - ], - "backend_entrypoints": [ - "routes/session_routes.py:setup_session_routes", - "core/session_manager.py:SessionManager" - ], - "routes": [ - "/api/sessions", - "/api/sessions/{session_id}" - ], - "configuration": [ - "SESSIONS_FILE", - "DATA_DIR/odysseus.db" - ], - "persistence": [ - "DATA_DIR/sessions.json", - "SQLite session table" - ], - "dependencies": [ - "sqlite3", - "pydantic" - ], - "tests": [ - "tests/test_session_manager.py", - "tests/test_session_routes_utcnow.py" - ], - "documentation": [ - "docs/setup.md" - ], - "risks": [ - "Concurrent file writes to sessions.json under high load." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/session_routes.py", - "symbol": "@router.get('/api/sessions')", - "line_range": "L150-L210", - "explanation": "Lists active sessions filtered by user owner scope." - }, - { - "path": "core/session_manager.py", - "symbol": "SessionManager", - "line_range": "L30-L150", - "explanation": "Provides thread-safe session storage operations." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-003", - "domain": "chat", - "name": "Chat History & Message Editing/Truncation", - "purpose": "Provides history retrieval, message content updating, message deletion, and history branch truncation.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chat.js", - "static/js/sessions.js" - ], - "backend_entrypoints": [ - "routes/history/history_routes.py:setup_history_routes" - ], - "routes": [ - "/api/history/{session_id}", - "/api/session/{session_id}/truncate", - "/api/session/{session_id}/edit-message" - ], - "configuration": [ - "MAX_HISTORY_MESSAGES" - ], - "persistence": [ - "DATA_DIR/sessions/" - ], - "dependencies": [ - "sqlite3", - "fastapi" - ], - "tests": [ - "tests/test_history_db_fallback_hidden.py", - "tests/test_truncate_message_count_regression.py" - ], - "documentation": [ - "docs/attachments.md" - ], - "risks": [ - "Truncating messages re-indexes context window and clears cached tool calls." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/history/history_routes.py", - "symbol": "@router.get('/api/history/{session_id}')", - "line_range": "L178-L230", - "explanation": "Fetches message history timeline for a session." - }, - { - "path": "routes/history_routes.py", - "symbol": "_sys.modules[__name__] = _canonical", - "line_range": "L1-L17", - "explanation": "Backward-compatibility shim module." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-004", - "domain": "chat", - "name": "File & Multimodal Attachment Handling", - "purpose": "Handles uploading, mime validation, image preview, vision encoding, and file attachments in chat messages.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/fileHandler.js", - "static/js/chat.js" - ], - "backend_entrypoints": [ - "routes/upload_routes.py:setup_upload_routes", - "src/upload_handler.py:UploadHandler" - ], - "routes": [ - "/api/upload", - "/api/upload/{file_id}/vision", - "/api/upload/cleanup" - ], - "configuration": [ - "MAX_UPLOAD_SIZE_MB", - "ALLOWED_UPLOAD_EXTENSIONS" - ], - "persistence": [ - "DATA_DIR/uploads/" - ], - "dependencies": [ - "pillow", - "python-magic" - ], - "tests": [ - "tests/test_upload_handler_atomicity.py", - "tests/test_upload_routes_owner_scope.py" - ], - "documentation": [ - "docs/attachments.md" - ], - "risks": [ - "Large file uploads may consume server disk space if cleanup task fails." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/upload_routes.py", - "symbol": "@router.post('')", - "line_range": "L257-L310", - "explanation": "Accepts multi-part file uploads and generates vision metadata." - }, - { - "path": "src/upload_handler.py", - "symbol": "UploadHandler.save_file", - "line_range": "L50-L140", - "explanation": "Validates upload size and atomicity on disk." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-005", - "domain": "chat", - "name": "Chat Message Search", - "purpose": "Enables full-text keyword search across stored chat messages and sessions.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/search-chat.js", - "static/js/search.js" - ], - "backend_entrypoints": [ - "routes/search_routes.py:setup_search_routes", - "src/session_search.py:search_sessions" - ], - "routes": [ - "/api/search/chat" - ], - "configuration": [ - "SEARCH_INDEX_CACHE_TTL" - ], - "persistence": [ - "SQLite FTS / session indices" - ], - "dependencies": [ - "sqlite3" - ], - "tests": [ - "tests/test_session_search.py", - "tests/test_session_search_batch_fetch.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Full table scans on un-indexed text columns for very large databases." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/search_routes.py", - "symbol": "setup_search_routes", - "line_range": "L30-L80", - "explanation": "Registers chat message search endpoint." - }, - { - "path": "src/session_search.py", - "symbol": "search_sessions", - "line_range": "L20-L90", - "explanation": "Executes query matching against session transcripts." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-006", - "domain": "chat", - "name": "System Prompts & Preset Management", - "purpose": "Provides creation, selection, and customization of system prompt presets for chat sessions.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/presets.js", - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/preset_routes.py:setup_preset_routes", - "src/preset_manager.py:PresetManager" - ], - "routes": [ - "/api/presets", - "/api/presets/{preset_id}" - ], - "configuration": [ - "PRESETS_FILE" - ], - "persistence": [ - "DATA_DIR/presets.json" - ], - "dependencies": [ - "pydantic", - "json" - ], - "tests": [ - "tests/cli/test_preset_cli_store.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Invalid JSON syntax in user presets file can corrupt preset loading." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/preset_routes.py", - "symbol": "setup_preset_routes", - "line_range": "L30-L110", - "explanation": "API routes for listing and modifying system prompt presets." - }, - { - "path": "src/preset_manager.py", - "symbol": "PresetManager", - "line_range": "L15-L100", - "explanation": "Disk-backed manager for prompt presets." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-007", - "domain": "chat", - "name": "Emoji Rendering & Twemoji SVG Proxy", - "purpose": "Proxies Twemoji SVG icons locally to render flat SVG emojis in message text without external CDN dependencies.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/emojiPicker.js", - "static/js/emojiShortcodes.js" - ], - "backend_entrypoints": [ - "routes/emoji_routes.py:setup_emoji_routes" - ], - "routes": [ - "/api/emoji/{code}.svg" - ], - "configuration": [ - "EMOJI_CACHE_DIR" - ], - "persistence": [ - "DATA_DIR/emoji_cache/" - ], - "dependencies": [ - "httpx", - "fastapi.responses.Response" - ], - "tests": [ - "tests/test_censor_pref_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "First request fetches SVG from remote CDN before caching locally." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/emoji_routes.py", - "symbol": "setup_emoji_routes", - "line_range": "L20-L100", - "explanation": "Serves locally cached Twemoji SVGs." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-008", - "domain": "chat", - "name": "Input History Recall (Arrow Up)", - "purpose": "Allows users to cycle through previously sent prompt messages in the chat composer input using Arrow-Up/Down keys.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/composerArrowUpRecall.js", - "static/js/chat.js" - ], - "backend_entrypoints": [ - "None (Pure client-side state feature)" - ], - "routes": [], - "configuration": [ - "localStorage key: chat_composer_history" - ], - "persistence": [ - "Browser localStorage" - ], - "dependencies": [ - "Vanilla JavaScript DOM keyboard listeners" - ], - "tests": [], - "documentation": [ - "README.md" - ], - "risks": [ - "Client-side browser storage limits." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/composerArrowUpRecall.js", - "symbol": "initComposerRecall", - "line_range": "L1-L80", - "explanation": "Listens for ArrowUp keypress on composer textarea." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CHAT-009", - "domain": "chat", - "name": "Context Window Compaction & Truncation", - "purpose": "Compacts session transcript history when prompt size exceeds context limits using summarization.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chat.js" - ], - "backend_entrypoints": [ - "routes/history/history_routes.py:compact_session", - "src/context_compactor.py:compact_context" - ], - "routes": [ - "/api/session/{session_id}/compact", - "/api/session/{session_id}/context" - ], - "configuration": [ - "MAX_CONTEXT_TOKENS", - "COMPACTION_THRESHOLD" - ], - "persistence": [ - "DATA_DIR/sessions/" - ], - "dependencies": [ - "tiktoken", - "fastapi" - ], - "tests": [ - "tests/test_context_compactor.py" - ], - "documentation": [ - "specs/architecture-runtime-inventory.md" - ], - "risks": [ - "Aggressive compaction may discard subtle user instructions." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/history/history_routes.py", - "symbol": "@router.post('/api/session/{session_id}/compact')", - "line_range": "L751-L790", - "explanation": "Triggers context summarization and compaction." - }, - { - "path": "src/context_compactor.py", - "symbol": "compact_context", - "line_range": "L25-L110", - "explanation": "Executes context token pruning and summary generation." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-001", - "domain": "agent", - "name": "Autonomous Agent Loop & Tool Execution Engine", - "purpose": "Executes multi-step agent reasoning loops, tool invocation parsing, and automated response generation.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chatStream.js" - ], - "backend_entrypoints": [ - "src/agent_loop.py:run_agent_loop", - "src/tool_execution.py:execute_tool_call" - ], - "routes": [ - "/api/chat/stream" - ], - "configuration": [ - "MAX_AGENT_STEPS", - "ENABLE_TOOL_EXECUTION" - ], - "persistence": [ - "Session transcript tool calls" - ], - "dependencies": [ - "asyncio", - "pydantic" - ], - "tests": [ - "tests/test_tool_policy.py", - "tests/test_unknown_tool_calls.py" - ], - "documentation": [ - "specs/architecture-runtime-inventory.md" - ], - "risks": [ - "Infinite tool loop if termination condition fails." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/agent_loop.py", - "symbol": "run_agent_loop", - "line_range": "L40-L210", - "explanation": "Core loop evaluating model tool requests and executing handlers." - }, - { - "path": "src/tool_execution.py", - "symbol": "execute_tool_call", - "line_range": "L30-L150", - "explanation": "Dispatches tool invocation requests to underlying tool handlers." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-002", - "domain": "agent", - "name": "Scheduled Tasks & Event Bus Dispatcher", - "purpose": "Schedules background recurring or delayed tasks, emits event bus triggers, and executes automated flows.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/tasks.js" - ], - "backend_entrypoints": [ - "routes/task_routes.py:setup_task_routes", - "src/task_scheduler.py:TaskScheduler", - "src/event_bus.py" - ], - "routes": [ - "/api/tasks", - "/api/tasks/{task_id}/run", - "/api/tasks/{task_id}/pause", - "/api/tasks/{task_id}/resume" - ], - "configuration": [ - "TASK_SCHEDULER_INTERVAL" - ], - "persistence": [ - "DATA_DIR/tasks.db" - ], - "dependencies": [ - "apscheduler", - "sqlite3" - ], - "tests": [ - "tests/test_task_scheduler_cancel.py", - "tests/test_task_chain_owner_scope.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Task execution failure handling on system restart." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/task_routes.py", - "symbol": "@router.get('')", - "line_range": "L341-L370", - "explanation": "Fetches active scheduled tasks." - }, - { - "path": "src/task_scheduler.py", - "symbol": "TaskScheduler", - "line_range": "L40-L280", - "explanation": "Async task scheduler dispatching cron and delay triggers." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-003", - "domain": "agent", - "name": "Webhook Event Subscriptions & Trigger Processing", - "purpose": "Manages incoming/outgoing webhook subscriptions, endpoint authentication tokens, and event triggers.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/admin.js" - ], - "backend_entrypoints": [ - "routes/webhook_routes.py:setup_webhook_routes", - "src/webhook_manager.py:WebhookManager" - ], - "routes": [ - "/api/webhooks", - "/v1/chat", - "/api/webhooks/{webhook_id}/test" - ], - "configuration": [ - "WEBHOOK_SECRET_KEY" - ], - "persistence": [ - "DATA_DIR/webhooks.db" - ], - "dependencies": [ - "httpx", - "sqlite3" - ], - "tests": [ - "tests/test_webhook_ssrf_resilience.py", - "tests/test_webhook_trigger_auth_exempt.py" - ], - "documentation": [ - "docs/pr-blocker-audit.md" - ], - "risks": [ - "SSRF risks when contacting external webhook URLs if unvalidated." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/webhook_routes.py", - "symbol": "@router.get('/webhooks')", - "line_range": "L71-L94", - "explanation": "Returns list of registered webhooks." - }, - { - "path": "src/webhook_manager.py", - "symbol": "WebhookManager", - "line_range": "L30-L160", - "explanation": "Handles payload delivery and signature verification." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-004", - "domain": "agent", - "name": "Assistant Settings, Task Check-Ins & Background Job Monitor", - "purpose": "Manages per-user assistant sessions and scheduled check-in settings, drains background job completions, and retains a legacy no-op activity logging shim.", - "status": "partial", - "frontend_entrypoints": [ - "static/js/assistant.js" - ], - "backend_entrypoints": [ - "routes/assistant_routes.py:setup_assistant_routes", - "src/assistant_log.py", - "src/bg_monitor.py" - ], - "routes": [ - "/api/assistant/session", - "/api/assistant/settings", - "/api/assistant/run/{task_id}", - "/api/assistant/run-status/{task_id}", - "/api/assistant/available-timezones" - ], - "configuration": [ - "CrewMember.timezone (IANA tz name driving check-in scheduling)", - "CrewMember.model", - "CrewMember.enabled_tools", - "ScheduledTask.scheduled_time / ScheduledTask.status", - "src/bg_monitor.py:POLL_INTERVAL_S", - "src/bg_monitor.py:_FOLLOWUP_MAX_ROUNDS" - ], - "persistence": [ - "CrewMember (core/database.py, is_default_assistant singleton per owner)", - "Session pinned via CrewMember.session_id", - "ScheduledTask (per-owner check-in rows)", - "TaskRun (most recent run status for run-status polling)" - ], - "dependencies": [ - "logging", - "asyncio" - ], - "tests": [ - "tests/cli/test_logs_cli_resolve_nonstring.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Assistant seeding is owner-scoped: routes/assistant_routes.py documents that reaching an /api/assistant route under a synthetic owner previously seeded a duplicate CrewMember plus Morning/Midday/Evening tasks that double-fired; correctness now depends on RESERVED_USERNAMES staying complete.", - "Check-in next_run values are recomputed from CrewMember.timezone through compute_next_run, so a missing or stale IANA timezone shifts when check-ins fire.", - "Background follow-up continuation is retry-until-success: _run_followup returns False to defer and mark_followed_up() runs only after a successful agent run, so a persistently failing continuation is retried every POLL_INTERVAL_S tick without a bounded attempt count." - ], - "unknowns": [ - "Route `/api/assistant/logs` cited in legacy docs is absent from assistant router.", - "Existing unit test `tests/cli/test_logs_cli_resolve_nonstring.py` tests CLI target-name resolution logic, not active assistant routes or bg_monitor execution loop." - ], - "evidence": [ - { - "path": "routes/assistant_routes.py", - "symbol": "setup_assistant_routes", - "line_range": "L80-L326", - "explanation": "Active assistant session, settings, manual check-in, run-status and timezone-list endpoints, including the owner-scoping guards." - }, - { - "path": "src/bg_monitor.py", - "symbol": "_drain_agent", - "line_range": "L28-L72", - "explanation": "Runs the agent loop headless against a session to produce the background-job follow-up turn." - }, - { - "path": "src/bg_monitor.py", - "symbol": "_run_followup", - "line_range": "L75-L132", - "explanation": "Drains completed background jobs and auto-continues the owning session, deferring while a live turn is in progress." - }, - { - "path": "src/assistant_log.py", - "symbol": "log_to_assistant", - "line_range": "L34-L48", - "explanation": "Legacy no-op activity logging shim retained for existing callers; documented as inactive rather than as current behaviour." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-005", - "domain": "agent", - "name": "Model Context Protocol (MCP) Server Integration", - "purpose": "Integrates external MCP servers over stdio/SSE to expand agent capabilities dynamically.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js", - "static/js/admin.js" - ], - "backend_entrypoints": [ - "routes/mcp_routes.py:setup_mcp_routes", - "src/mcp_manager.py:McpManager", - "mcp_servers/" - ], - "routes": [ - "/api/mcp/servers", - "/api/mcp/tools", - "/api/mcp/connect" - ], - "configuration": [ - "MCP_CONFIG_PATH" - ], - "persistence": [ - "DATA_DIR/mcp_config.json" - ], - "dependencies": [ - "mcp", - "asyncio" - ], - "tests": [ - "tests/test_mcp_param_hint_hardening.py", - "tests/cli/test_mcp_cli_json.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Subprocess leaks if external MCP server process fails to terminate clean." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/mcp_routes.py", - "symbol": "setup_mcp_routes", - "line_range": "L60-L240", - "explanation": "Exposes management endpoints for external MCP servers." - }, - { - "path": "src/mcp_manager.py", - "symbol": "McpManager", - "line_range": "L40-L300", - "explanation": "Manages MCP server subprocess lifecycles." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-006", - "domain": "agent", - "name": "AI Interaction Tools & Pipeline Orchestration", - "purpose": "Provides specialized AI interaction tools for agent self-debugging, debate, and multi-model collaboration.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chatStream.js" - ], - "backend_entrypoints": [ - "src/ai_interaction.py", - "src/action_intents.py", - "src/builtin_actions.py" - ], - "routes": [ - "Implicit agent tool calls" - ], - "configuration": [ - "MAX_PIPELINE_TURNS" - ], - "persistence": [ - "Session transcript state" - ], - "dependencies": [ - "asyncio" - ], - "tests": [ - "tests/test_builtin_actions_nonstring.py", - "tests/test_model_interaction_registry.py" - ], - "documentation": [ - "specs/architecture-runtime-inventory.md" - ], - "risks": [ - "High API token consumption during extended agent debates." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/ai_interaction.py", - "symbol": "init_ai_interaction_tools", - "line_range": "L30-L140", - "explanation": "Registers specialized multi-agent interaction primitives." - }, - { - "path": "src/builtin_actions.py", - "symbol": "execute_builtin_action", - "line_range": "L20-L90", - "explanation": "Executes pre-built action intent sequences." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "AGENT-007", - "domain": "agent", - "name": "Subprocess & Background Job Execution Tools", - "purpose": "Provides sandboxed bash/shell tool execution capabilities with output streaming and background tracking.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chatStream.js", - "static/js/tasks.js" - ], - "backend_entrypoints": [ - "src/agent_tools/subprocess_tools.py", - "src/agent_tools/bg_job_tools.py", - "src/bg_jobs.py" - ], - "routes": [ - "Implicit agent tool calls" - ], - "configuration": [ - "ALLOW_SHELL_EXECUTION", - "SANDBOX_DIR" - ], - "persistence": [ - "DATA_DIR/bg_jobs/" - ], - "dependencies": [ - "subprocess", - "asyncio" - ], - "tests": [ - "tests/test_bg_job_tools.py", - "tests/test_task_shell_tools.py" - ], - "documentation": [ - "THREAT_MODEL.md" - ], - "risks": [ - "Arbitrary shell command execution permissions if sandbox confinement fails." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/agent_tools/subprocess_tools.py", - "symbol": "run_command", - "line_range": "L25-L120", - "explanation": "Executes shell commands in background/foreground." - }, - { - "path": "src/bg_jobs.py", - "symbol": "JobManager", - "line_range": "L30-L150", - "explanation": "Tracks async background subprocess tasks." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MODEL-001", - "domain": "model", - "name": "Multi-Provider LLM Model Discovery & Metadata Management", - "purpose": "Discovers models from OpenAI, Anthropic, Ollama, vLLM, LMStudio, OpenRouter, and Google AI Studio endpoints.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/models.js", - "static/js/modelPicker.js" - ], - "backend_entrypoints": [ - "routes/model_routes.py:setup_model_routes", - "src/model_discovery.py:ModelDiscovery" - ], - "routes": [ - "/api/models", - "/api/models/active", - "/api/model-endpoints" - ], - "configuration": [ - "OPENAI_API_KEY", - "OLLAMA_BASE_URL", - "ANTHROPIC_API_KEY" - ], - "persistence": [ - "DATA_DIR/model_endpoints.json" - ], - "dependencies": [ - "httpx", - "pydantic" - ], - "tests": [ - "tests/test_model_routes.py", - "tests/test_provider_classification.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Remote endpoint timeouts may slow down full discovery refresh." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/model_routes.py", - "symbol": "@router.get('/api/models')", - "line_range": "L100-L180", - "explanation": "Returns unified list of available models across providers." - }, - { - "path": "src/model_discovery.py", - "symbol": "ModelDiscovery.discover_all", - "line_range": "L45-L210", - "explanation": "Queries connected provider endpoints for available model IDs." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MODEL-002", - "domain": "model", - "name": "Model Capability & Context Limits Detection", - "purpose": "Detects vision, tool calling, reasoning, and context window limits for connected model endpoints.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/models.js" - ], - "backend_entrypoints": [ - "src/model_capabilities.py", - "src/model_context.py", - "src/endpoint_resolver.py" - ], - "routes": [ - "/api/model-endpoints/{ep_id}/probe" - ], - "configuration": [ - "MODEL_CAPABILITY_OVERRODES" - ], - "persistence": [ - "In-memory capabilities cache" - ], - "dependencies": [ - "pydantic" - ], - "tests": [ - "tests/test_endpoint_resolver_headers.py", - "tests/test_vision_model_detection.py" - ], - "documentation": [ - "specs/architecture-runtime-inventory.md" - ], - "risks": [ - "Incorrect context limit metadata for unlisted custom fine-tunes." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/model_capabilities.py", - "symbol": "get_model_capabilities", - "line_range": "L30-L120", - "explanation": "Maps model names to vision and tool support flags." - }, - { - "path": "src/endpoint_resolver.py", - "symbol": "resolve_endpoint_headers", - "line_range": "L20-L80", - "explanation": "Resolves auth headers and target URLs for model endpoints." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MODEL-003", - "domain": "model", - "name": "LLM Core Provider Communication & Fallback Routing", - "purpose": "Manages HTTP request dispatching, authorization header injection, and fallback provider routing for LLM calls.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chatStream.js" - ], - "backend_entrypoints": [ - "src/llm_core.py:llm_call_async", - "src/llm_core.py:llm_call_async_with_fallback", - "src/llm_core.py:stream_llm_with_fallback" - ], - "routes": [ - "Implicit backend calls" - ], - "configuration": [ - "LLM_RETRY_ATTEMPTS", - "LLM_TIMEOUT" - ], - "persistence": [ - "Session state" - ], - "dependencies": [ - "httpx", - "asyncio" - ], - "tests": [ - "tests/test_llm_core_concurrency.py", - "tests/test_llm_core_fallback.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Unexpected API changes in upstream third-party model providers." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/llm_core.py", - "symbol": "llm_call_async", - "line_range": "L1949-L2118", - "explanation": "Non-streaming provider request dispatcher: resolves the endpoint, injects authorization headers and executes the HTTP call." - }, - { - "path": "src/llm_core.py", - "symbol": "llm_call_async_with_fallback", - "line_range": "L1932-L1946", - "explanation": "Ordered fallback wrapper that retries llm_call_async across the configured candidate endpoints." - }, - { - "path": "src/llm_core.py", - "symbol": "stream_llm_with_fallback", - "line_range": "L2794-L2895", - "explanation": "Ordered fallback wrapper for the streaming path; advances to the next candidate when a provider yields an empty completion." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Provider dispatch, header injection and fallback advancement are only observable against a reachable LLM provider endpoint; not exercised in this documentation pass." - } - }, - { - "id": "MODEL-004", - "domain": "model", - "name": "Model Selection & Display Ordering Preferences", - "purpose": "Allows pinning, sorting, and hiding specific models in the UI selection dropdown.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/modelSort.js", - "static/js/modelPicker.js" - ], - "backend_entrypoints": [ - "routes/model_routes.py:save_model_order" - ], - "routes": [ - "/api/models/order", - "/api/models/order/reset" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/model_order.json" - ], - "dependencies": [ - "json" - ], - "tests": [ - "tests/test_model_defaults.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Stale model IDs in custom order lists after model endpoints are removed." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/model_routes.py", - "symbol": "@router.post('/order')", - "line_range": "L500-L550", - "explanation": "Saves custom model display order preference." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MODEL-005", - "domain": "model", - "name": "Side-by-Side Model Comparison (A/B Testing)", - "purpose": "Enables dual-model side-by-side response evaluation, arena scoring, and latency comparison.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/compare/index.js", - "static/js/compare/panes.js" - ], - "backend_entrypoints": [ - "routes/compare/compare_routes.py:setup_compare_routes" - ], - "routes": [ - "/api/compare/start", - "/api/compare/{comp_id}/vote", - "/api/compare/history" - ], - "configuration": [ - "COMPARE_ENABLED" - ], - "persistence": [ - "DATA_DIR/compare_history.db" - ], - "dependencies": [ - "sqlite3" - ], - "tests": [ - "tests/test_endpoint_owner_scope_followup.py" - ], - "documentation": [ - "docs/compare.webm" - ], - "risks": [ - "High memory and network usage when streaming two model responses simultaneously." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/compare/compare_routes.py", - "symbol": "@router.post('/start')", - "line_range": "L70-L150", - "explanation": "Starts a parallel dual-model comparison stream." - }, - { - "path": "static/js/compare/index.js", - "symbol": "initCompareView", - "line_range": "L1-L100", - "explanation": "Renders side-by-side model chat panes." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MODEL-006", - "domain": "model", - "name": "GitHub Copilot Device Flow Authentication", - "purpose": "Authenticates with GitHub Copilot via OAuth device flow to use Copilot models directly.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/providerDeviceFlow.js" - ], - "backend_entrypoints": [ - "routes/copilot_routes.py:setup_copilot_routes", - "routes/device_flow.py:create_device_flow_router", - "src/copilot.py:request_device_code", - "src/copilot.py:poll_access_token" - ], - "routes": [ - "/api/copilot/device/start", - "/api/copilot/device/poll" - ], - "configuration": [ - "COPILOT_CLIENT_ID" - ], - "persistence": [ - "DATA_DIR/copilot_auth.json" - ], - "dependencies": [ - "httpx" - ], - "tests": [ - "tests/test_provider_device_flow_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Token expiration requires manual device re-authentication." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/copilot_routes.py", - "symbol": "setup_copilot_routes", - "line_range": "L166-L173", - "explanation": "Builds the Copilot device-flow router at prefix /api/copilot, wiring _start_device_flow and _poll_device_flow." - }, - { - "path": "routes/device_flow.py", - "symbol": "create_device_flow_router", - "line_range": "L135-L193", - "explanation": "Shared factory registering POST /device/start and POST /device/poll under the caller-supplied prefix." - }, - { - "path": "src/copilot.py", - "symbol": "request_device_code", - "line_range": "L150-L161", - "explanation": "Issues the GitHub device-code request that begins the Copilot OAuth device flow." - }, - { - "path": "src/copilot.py", - "symbol": "poll_access_token", - "line_range": "L164-L180", - "explanation": "Polls GitHub for the access token once the user has authorized the device code." - }, - { - "path": "tests/test_provider_device_flow_js.py", - "symbol": "test_copilot_success_uses_complete_verification_uri", - "line_range": "L28-L65", - "explanation": "Inspected unit test asserting the Copilot device-flow runner surfaces the complete verification URI returned by the backend." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E2", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires an interactive GitHub Copilot OAuth device-flow account." - } - }, - { - "id": "MODEL-007", - "domain": "model", - "name": "ChatGPT Subscription Device Flow Authentication", - "purpose": "Authenticates with ChatGPT Pro/Plus subscription tokens via device login flow.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/providerDeviceFlow.js" - ], - "backend_entrypoints": [ - "routes/chatgpt_subscription_routes.py:setup_chatgpt_subscription_routes", - "routes/device_flow.py:create_device_flow_router", - "src/chatgpt_subscription.py:request_device_code", - "src/chatgpt_subscription.py:poll_device_auth" - ], - "routes": [ - "/api/chatgpt-subscription/device/start", - "/api/chatgpt-subscription/device/poll" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/chatgpt_auth.json" - ], - "dependencies": [ - "httpx" - ], - "tests": [ - "tests/test_provider_device_flow_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Changes in OpenAI auth endpoint security challenges." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/chatgpt_subscription_routes.py", - "symbol": "setup_chatgpt_subscription_routes", - "line_range": "L163-L170", - "explanation": "Builds the ChatGPT subscription device-flow router at prefix /api/chatgpt-subscription." - }, - { - "path": "routes/device_flow.py", - "symbol": "create_device_flow_router", - "line_range": "L135-L193", - "explanation": "Shared factory registering POST /device/start and POST /device/poll under the caller-supplied prefix." - }, - { - "path": "src/chatgpt_subscription.py", - "symbol": "request_device_code", - "line_range": "L168-L181", - "explanation": "Issues the ChatGPT device-authorization request that begins the subscription OAuth device flow." - }, - { - "path": "src/chatgpt_subscription.py", - "symbol": "poll_device_auth", - "line_range": "L184-L193", - "explanation": "Polls the ChatGPT device-authorization endpoint for completion using the stored device_auth_id and user_code." - }, - { - "path": "tests/test_provider_device_flow_js.py", - "symbol": "test_chatgpt_success_uses_plain_verification_uri", - "line_range": "L68-L95", - "explanation": "Inspected unit test asserting the ChatGPT device-flow runner uses the plain verification URI rather than the Copilot complete-URI form." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E2", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires an interactive ChatGPT subscription OAuth flow." - } - }, - { - "id": "MODEL-008", - "domain": "model", - "name": "Embedding Model Lane & Vector Provider Setup", - "purpose": "Configures local sentence-transformers, FastEmbed, or remote OpenAI embedding model lanes.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/embedding_routes.py:setup_embedding_routes", - "src/embeddings.py", - "src/embedding_lanes.py" - ], - "routes": [ - "/api/embeddings/active", - "/api/embeddings/test" - ], - "configuration": [ - "EMBEDDING_PROVIDER", - "EMBEDDING_MODEL" - ], - "persistence": [ - "DATA_DIR/embeddings_config.json" - ], - "dependencies": [ - "fastembed", - "sentence-transformers" - ], - "tests": [ - "tests/test_embedding_lane_ndarray_restore.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "First-time download of heavy PyTorch model weights on CPU-only machines." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/embedding_routes.py", - "symbol": "setup_embedding_routes", - "line_range": "L30-L120", - "explanation": "Provides embedding provider configuration endpoints." - }, - { - "path": "src/embeddings.py", - "symbol": "EmbeddingManager", - "line_range": "L40-L200", - "explanation": "Generates dense vector embeddings for RAG and memory." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "COOKBOOK-001", - "domain": "cookbook", - "name": "Local Model Download & Recipe Lifecycle Management", - "purpose": "Downloads HuggingFace models, configures execution parameters, and manages local GGUF/MLX model servers.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/cookbook.js", - "static/js/cookbookServe.js", - "static/js/cookbookDownload.js" - ], - "backend_entrypoints": [ - "routes/cookbook_routes.py:setup_cookbook_routes", - "src/tools/cookbook.py", - "src/cookbook_serve_lifecycle.py" - ], - "routes": [ - "/api/cookbook/download", - "/api/cookbook/serve", - "/api/cookbook/status" - ], - "configuration": [ - "COOKBOOK_MODELS_DIR" - ], - "persistence": [ - "DATA_DIR/models/" - ], - "dependencies": [ - "huggingface_hub", - "subprocess" - ], - "tests": [ - "tests/test_cookbook_endpoint_registration.py", - "tests/test_cookbook_port_parsing_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Disk space exhaustion during multi-gigabyte GGUF weights downloads." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/cookbook_routes.py", - "symbol": "setup_cookbook_routes", - "line_range": "L100-L300", - "explanation": "Exposes model downloading and process serving endpoints." - }, - { - "path": "static/js/cookbook.js", - "symbol": "initCookbook", - "line_range": "L1-L150", - "explanation": "UI manager for local model library." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "COOKBOOK-002", - "domain": "cookbook", - "name": "Hardware Model Fitting ('What Fits?') Analysis Engine", - "purpose": "Calculates RAM/VRAM requirements, quantized size, and context overhead to determine model compatibility.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/cookbook-hwfit.js" - ], - "backend_entrypoints": [ - "routes/hwfit_routes.py:setup_hwfit_routes", - "services/hwfit/fit.py:calculate_fit" - ], - "routes": [ - "/api/hwfit/fit", - "/api/hwfit/profile" - ], - "configuration": [ - "FORCE_GPU_VENDOR" - ], - "persistence": [ - "System hardware specs" - ], - "dependencies": [ - "psutil", - "torch" - ], - "tests": [ - "tests/test_hwfit_gemma4_12b.py", - "tests/test_hwfit_bandwidth_nonstring.py", - "tests/test_hwfit_gpu_count_nonnumeric.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Inaccurate VRAM estimation for non-standard KV-cache quantization." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/hwfit_routes.py", - "symbol": "setup_hwfit_routes", - "line_range": "L40-L120", - "explanation": "Calculates hardware model compatibility." - }, - { - "path": "services/hwfit/fit.py", - "symbol": "calculate_fit", - "line_range": "L30-L140", - "explanation": "Performs parameter and memory fit calculations." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "COOKBOOK-003", - "domain": "cookbook", - "name": "HuggingFace & MLX Model Discovery Services", - "purpose": "Searches HuggingFace Hub and local MLX model repositories for compatible GGUF and MLX weights.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/cookbook.js" - ], - "backend_entrypoints": [ - "routes/hwfit_routes.py:hf_search", - "services/hwfit/hf_discovery.py" - ], - "routes": [ - "/api/hwfit/hf-search", - "/api/hwfit/mlx-models" - ], - "configuration": [ - "HF_TOKEN" - ], - "persistence": [ - "Cache directory" - ], - "dependencies": [ - "huggingface_hub" - ], - "tests": [ - "tests/test_cookbook_hf_token.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "HuggingFace API rate limits when searching without an API token." - ], - "unknowns": [], - "evidence": [ - { - "path": "services/hwfit/hf_discovery.py", - "symbol": "search_hf_models", - "line_range": "L20-L90", - "explanation": "Queries HuggingFace API for model tags and files." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "COOKBOOK-004", - "domain": "cookbook", - "name": "Host Docker Access for Inference Container Runtimes", - "purpose": "Detects and connects to host Docker engine to launch containerized Ollama, vLLM, or SGLang runtimes.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/cookbookServe.js" - ], - "backend_entrypoints": [ - "src/host_docker_access.py:HostDockerAccess" - ], - "routes": [ - "Implicit local docker socket API calls" - ], - "configuration": [ - "DOCKER_HOST" - ], - "persistence": [ - "/var/run/docker.sock" - ], - "dependencies": [ - "docker" - ], - "tests": [ - "tests/test_cookbook_docker_access.py" - ], - "documentation": [ - "docker/host-docker.yml" - ], - "risks": [ - "Permission denied accessing docker socket on non-root setups." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/host_docker_access.py", - "symbol": "HostDockerAccess", - "line_range": "L50-L62", - "explanation": "Interacts with host docker daemon." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires Docker access and supported physical GPU hardware." - } - }, - { - "id": "RESEARCH-001", - "domain": "research", - "name": "Deep Research Execution Engine & SSE Progress Streaming", - "purpose": "Executes multi-step recursive deep research tasks, web page scraping, synthesis, and streams live progress.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/research/panel.js", - "static/js/research/jobs.js", - "static/js/researchSynapse.js" - ], - "backend_entrypoints": [ - "routes/research/research_routes.py:start_research", - "src/deep_research.py", - "services/research/service.py" - ], - "routes": [ - "/api/research/start", - "/api/research/stream/{session_id}", - "/api/research/active" - ], - "configuration": [ - "MAX_RESEARCH_DEPTH", - "SEARXNG_URL" - ], - "persistence": [ - "DATA_DIR/research/" - ], - "dependencies": [ - "beautifulsoup4", - "httpx", - "asyncio" - ], - "tests": [ - "tests/test_research_owner_scope_routes.py", - "tests/test_services_research_low_quality_sources.py" - ], - "documentation": [ - "docs/research.webm" - ], - "risks": [ - "High memory consumption when parsing multi-megabyte HTML target pages." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/research/research_routes.py", - "symbol": "@router.post('/api/research/start')", - "line_range": "L492-L550", - "explanation": "Initiates deep research job." - }, - { - "path": "src/deep_research.py", - "symbol": "DeepResearchEngine", - "line_range": "L40-L300", - "explanation": "Recursive search and summary crawler." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "RESEARCH-002", - "domain": "research", - "name": "Research Library, Detail View & Image Controls", - "purpose": "Stores completed research reports, generated diagrams, reference links, and manages image visibility.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/research/panel.js" - ], - "backend_entrypoints": [ - "routes/research/research_routes.py:get_library" - ], - "routes": [ - "/api/research/library", - "/api/research/detail/{session_id}", - "/api/research/{session_id}/hide-image" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/research/library.json" - ], - "dependencies": [ - "pydantic" - ], - "tests": [ - "tests/cli/test_research_cli_preview.py", - "tests/test_research_routes_path_confinement.py" - ], - "documentation": [ - "docs/research.webm" - ], - "risks": [ - "Orphaned report files if storage directory is modified out-of-band." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/research/research_routes.py", - "symbol": "@router.get('/api/research/library')", - "line_range": "L366-L415", - "explanation": "Returns all saved research reports." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "RESEARCH-003", - "domain": "research", - "name": "Web Search Engine Integration (SearXNG & Multi-Provider)", - "purpose": "Queries SearXNG, DuckDuckGo, or Google Search instances to retrieve web search snippets.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chat.js", - "static/js/search.js" - ], - "backend_entrypoints": [ - "routes/search_routes.py:setup_search_routes", - "src/search/core.py", - "services/search/service.py" - ], - "routes": [ - "/api/search" - ], - "configuration": [ - "SEARXNG_URL", - "SEARCH_PROVIDER" - ], - "persistence": [ - "In-memory search cache" - ], - "dependencies": [ - "httpx" - ], - "tests": [ - "tests/test_search_ranking.py", - "tests/test_searxng_image_pinned.py" - ], - "documentation": [ - "config/searxng/settings.yml" - ], - "risks": [ - "Search provider IP throttling or rate-limiting." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/search_routes.py", - "symbol": "setup_search_routes", - "line_range": "L39-L100", - "explanation": "Defines /api/search, /api/search/config, and /api/search/query endpoints." - }, - { - "path": "src/search/core.py", - "symbol": "SearchEngine", - "line_range": "L1-L12", - "explanation": "Compatibility module aliasing services.search.core." - }, - { - "path": "tests/test_search_ranking.py", - "symbol": "test_news_queries_prefer_news_sources_over_sports_and_social_results", - "line_range": "L1-L39", - "explanation": "Tests search result domain ranking and scoring." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires an active SearXNG instance or external search API provider." - } - }, - { - "id": "RESEARCH-004", - "domain": "research", - "name": "Research Result Peeking & Topic Spinoff Generation", - "purpose": "Extracts preliminary research snippets and spawns child research sessions focused on specific sub-topics.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/research/panel.js" - ], - "backend_entrypoints": [ - "routes/research/research_routes.py:peek_result", - "routes/research/research_routes.py:spinoff_research" - ], - "routes": [ - "/api/research/result-peek/{session_id}", - "/api/research/spinoff/{session_id}" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/research/" - ], - "dependencies": [ - "fastapi" - ], - "tests": [ - "tests/test_research_routes_path_confinement.py" - ], - "documentation": [ - "docs/research.webm" - ], - "risks": [ - "Deep recursion tree depth when spawning multiple nested spinoffs." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/research/research_routes.py", - "symbol": "@router.post('/api/research/spinoff/{session_id}')", - "line_range": "L635-L680", - "explanation": "Spawns child research session for specific query." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "DOCUMENT-001", - "domain": "document", - "name": "Document & Canvas Artifact Management", - "purpose": "Renders dynamic canvas documents, handles live editing, markdown preview, and side-by-side artifact display.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/document.js", - "static/js/documentLibrary.js" - ], - "backend_entrypoints": [ - "routes/document_routes.py:setup_document_routes", - "src/document_actions.py", - "src/document_processor.py" - ], - "routes": [ - "/api/document", - "/api/document/{id}" - ], - "configuration": [ - "MAX_DOCUMENT_SIZE_MB" - ], - "persistence": [ - "DATA_DIR/documents/" - ], - "dependencies": [ - "pydantic", - "fastapi" - ], - "tests": [ - "tests/test_document_actions_nonstring.py", - "tests/test_document_diff_discard_on_update_js.py" - ], - "documentation": [ - "docs/document.webm" - ], - "risks": [ - "Concurrent edits on the same document artifact." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/document_routes.py", - "symbol": "setup_document_routes", - "line_range": "L100-L300", - "explanation": "Registers document artifact CRUD routes." - }, - { - "path": "static/js/document.js", - "symbol": "initDocumentView", - "line_range": "L1-L150", - "explanation": "Renders interactive canvas document panel." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "DOCUMENT-002", - "domain": "document", - "name": "PDF Form Processing & High-Fidelity Rendering", - "purpose": "Extracts form fields from PDF files, fills dynamic values, and generates PDF previews.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/document.js" - ], - "backend_entrypoints": [ - "src/pdf_runtime.py", - "src/pdf_forms.py", - "src/pdf_form_doc.py" - ], - "routes": [ - "Implicit PDF document processing" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/documents/pdf/" - ], - "dependencies": [ - "pypdf", - "fitz" - ], - "tests": [ - "tests/test_document_pdf_marker.py", - "tests/test_security_headers_pdf_preview.py" - ], - "documentation": [ - "docs/attachments.md" - ], - "risks": [ - "Complex XFA PDF forms may not extract cleanly with standard pdf parsers." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/pdf_runtime.py", - "symbol": "load_pymupdf_for_pdf_viewer", - "line_range": "L9-L15", - "explanation": "Loads optional PyMuPDF runtime for PDF viewing." - }, - { - "path": "src/pdf_forms.py", - "symbol": "extract_form_fields", - "line_range": "L1-L100", - "explanation": "Handles PDF form field extraction and filling." - }, - { - "path": "tests/test_document_pdf_marker.py", - "symbol": "test_marker_removed_without_eating_following_text", - "line_range": "L1-L30", - "explanation": "Tests PDF text extraction wrapper stripping without content corruption." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires optional PyMuPDF (`fitz`) or pypdf runtime dependency." - } - }, - { - "id": "DOCUMENT-003", - "domain": "document", - "name": "Personal Document Indexing & RAG Retrieval", - "purpose": "Indexes local user documents (PDF, DOCX, TXT) into ChromaDB for semantic vector retrieval.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/rag.js" - ], - "backend_entrypoints": [ - "routes/personal_routes.py:setup_personal_routes", - "src/personal_docs.py", - "src/rag_manager.py" - ], - "routes": [ - "/api/personal/documents", - "/api/personal/search", - "/api/personal/index" - ], - "configuration": [ - "CHROMADB_DIR" - ], - "persistence": [ - "DATA_DIR/chroma/" - ], - "dependencies": [ - "chromadb", - "pypdf", - "docx" - ], - "tests": [ - "tests/test_personal_docs_pdf_index.py", - "tests/test_rag_index_hidden_dirs.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Slow vector embedding indexing step for massive multi-thousand page documents." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/personal_routes.py", - "symbol": "setup_personal_routes", - "line_range": "L50-L160", - "explanation": "Personal document RAG indexing and search API endpoints." - }, - { - "path": "src/personal_docs.py", - "symbol": "PersonalDocsManager", - "line_range": "L30-L180", - "explanation": "Handles file text chunking and vector storage." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "DOCUMENT-004", - "domain": "document", - "name": "Document Conversion & Text Extraction Engine", - "purpose": "Converts office formats (.docx, .xlsx, .pptx) and HTML into clean Markdown text representations.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/fileHandler.js" - ], - "backend_entrypoints": [ - "src/markitdown_runtime.py", - "src/office_doc.py" - ], - "routes": [ - "Implicit file conversion calls" - ], - "configuration": [], - "persistence": [ - "Temporary conversion cache" - ], - "dependencies": [ - "markitdown", - "python-docx" - ], - "tests": [ - "tests/cli/test_docs_cli_content_length.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Formatting loss when parsing legacy binary doc/xls files." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/markitdown_runtime.py", - "symbol": "convert_to_markdown", - "line_range": "L15-L80", - "explanation": "Converts binary office documents into structured Markdown text." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "DOCUMENT-005", - "domain": "document", - "name": "Document Library UI Navigation", - "purpose": "Provides dedicated UI view for browsing, filtering, and organizing saved user documents.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/documentLibrary.js" - ], - "backend_entrypoints": [ - "app.py:serve_library" - ], - "routes": [ - "/library" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/documents/" - ], - "dependencies": [ - "Vanilla JS" - ], - "tests": [ - "tests/test_document_close_clears_active_route.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Large folder trees may cause initial DOM render slowdown." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/documentLibrary.js", - "symbol": "initDocumentLibrary", - "line_range": "L1-L100", - "explanation": "Renders document library navigation grid." - }, - { - "path": "app.py", - "symbol": "serve_library", - "line_range": "L911-L914", - "explanation": "Serves SPA shell for /library route." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "EMAIL-001", - "domain": "email", - "name": "Email Account Setup, IMAP/SMTP Connection & Polling", - "purpose": "Configures IMAP/SMTP email accounts, validates TLS certificates, and polls background inbox updates.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/emailInbox.js" - ], - "backend_entrypoints": [ - "routes/email_routes.py:setup_email_routes", - "routes/email_pollers.py" - ], - "routes": [ - "/api/email/accounts", - "/api/email/poll" - ], - "configuration": [ - "EMAIL_POLL_INTERVAL" - ], - "persistence": [ - "DATA_DIR/email_accounts.json" - ], - "dependencies": [ - "imaplib", - "smtplib" - ], - "tests": [ - "tests/test_service_health_email.py", - "tests/test_active_email_reply_guard.py" - ], - "documentation": [ - "docs/email-outlook.md" - ], - "risks": [ - "Account lockouts if bad credentials are repeatedly polled." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/email_routes.py", - "symbol": "setup_email_routes", - "line_range": "L1453-L1500", - "explanation": "Sets up email account management and synchronization routes." - }, - { - "path": "routes/email_pollers.py", - "symbol": "_start_poller", - "line_range": "L1-L100", - "explanation": "Background poller for email inbox synchronization." - }, - { - "path": "tests/test_service_health_email.py", - "symbol": "test_email_ok_all_connect", - "line_range": "L1-L80", - "explanation": "Tests IMAP connection health probing and status reporting." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires a controlled live IMAP account and network access." - } - }, - { - "id": "EMAIL-002", - "domain": "email", - "name": "Email Searching, Threading & Message Operations", - "purpose": "Parses email headers, folds signatures, groups messages into threads, and executes full-text email search.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/emailLibrary.js", - "static/js/emailLibrary/state.js" - ], - "backend_entrypoints": [ - "routes/email_routes.py:search_email", - "src/email_thread_parser.py" - ], - "routes": [ - "/api/email/search", - "/api/email/threads", - "/api/email/messages" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/email_cache.db" - ], - "dependencies": [ - "sqlite3", - "email" - ], - "tests": [ - "tests/test_reply_recipients_js.py", - "tests/test_signature_fold_js.py" - ], - "documentation": [ - "docs/email-outlook.md" - ], - "risks": [ - "Malformed MIME email structures failing HTML sanitization." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/email_routes.py", - "symbol": "@router.get('/search')", - "line_range": "L250-L320", - "explanation": "Executes search across cached email headers and text." - }, - { - "path": "src/email_thread_parser.py", - "symbol": "parse_email_thread", - "line_range": "L20-L100", - "explanation": "Builds conversation tree from Message-ID and In-Reply-To headers." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "EMAIL-003", - "domain": "email", - "name": "Email Composition, Draft Management & Sending", - "purpose": "Creates, saves, and dispatches HTML/plaintext email messages via SMTP.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/emailShared.js" - ], - "backend_entrypoints": [ - "routes/email_routes.py:send_email" - ], - "routes": [ - "/api/email/draft", - "/api/email/send" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/email_drafts.json" - ], - "dependencies": [ - "smtplib" - ], - "tests": [ - "tests/test_schedule_email_offset_normalization.py" - ], - "documentation": [ - "docs/email-outlook.md" - ], - "risks": [ - "SMTP connection drop mid-send causing unsent mail state." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/email_routes.py", - "symbol": "@router.post('/send')", - "line_range": "L450-L520", - "explanation": "Sends email message via user SMTP credentials." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires a controlled live SMTP account and network access." - } - }, - { - "id": "EMAIL-004", - "domain": "email", - "name": "Email MCP Server & Codex Integration Bridge", - "purpose": "Exposes constrained email reading and draft capabilities to external Codex / MCP agents with scope checks.", - "status": "verified", - "frontend_entrypoints": [ - "integrations/codex/scripts/odysseus_api.py" - ], - "backend_entrypoints": [ - "mcp_servers/email_server.py", - "routes/codex_routes.py:setup_codex_routes" - ], - "routes": [ - "/api/codex/email/search", - "/api/codex/email/send" - ], - "configuration": [ - "CODEX_API_KEY" - ], - "persistence": [ - "API token scopes" - ], - "dependencies": [ - "mcp", - "fastapi" - ], - "tests": [ - "tests/cli/test_mail_cli_recipients.py" - ], - "documentation": [ - "integrations/codex/README.md" - ], - "risks": [ - "Unauthorized mail sending if token scopes are improperly scoped." - ], - "unknowns": [], - "evidence": [ - { - "path": "mcp_servers/email_server.py", - "symbol": "EmailMcpServer", - "line_range": "L20-L120", - "explanation": "MCP server exposing email tools over stdio/SSE." - }, - { - "path": "routes/codex_routes.py", - "symbol": "setup_codex_routes", - "line_range": "L30-L110", - "explanation": "Bridge endpoints for external Codex plugin integration." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CALENDAR-001", - "domain": "calendar", - "name": "CalDAV Calendar Synchronization & Account Setup", - "purpose": "Connects to remote CalDAV servers (Apple iCloud, Nextcloud, Google) to sync calendar event feeds.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/calendar.js" - ], - "backend_entrypoints": [ - "routes/calendar_routes.py:setup_calendar_routes", - "src/caldav_sync.py:CalDavSync" - ], - "routes": [ - "/api/calendar/config", - "/api/calendar/sync" - ], - "configuration": [ - "CALDAV_URL", - "CALDAV_USERNAME" - ], - "persistence": [ - "DATA_DIR/calendar_config.json" - ], - "dependencies": [ - "caldav", - "vobject" - ], - "tests": [ - "tests/test_caldav_url_hardening.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Invalid SSL certificates on self-hosted CalDAV servers." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/calendar_routes.py", - "symbol": "setup_calendar_routes", - "line_range": "L50-L180", - "explanation": "Exposes CalDAV setup and manual sync trigger routes." - }, - { - "path": "src/caldav_sync.py", - "symbol": "CalDavSync", - "line_range": "L30-L160", - "explanation": "Fetches and parses remote iCalendar VEVENT objects." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires a controlled external CalDAV server." - } - }, - { - "id": "CALENDAR-002", - "domain": "calendar", - "name": "Calendar Event Operations & iCalendar Parsing", - "purpose": "Creates, updates, deletes, and displays calendar events with timezone conversion and reminder notifications.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/calendar/reminders.js", - "static/js/calendar/utils.js" - ], - "backend_entrypoints": [ - "routes/calendar_routes.py:create_event", - "src/caldav_writeback.py", - "src/tools/calendar.py" - ], - "routes": [ - "/api/calendar/events", - "/api/calendar/events/{event_id}" - ], - "configuration": [ - "USER_TIMEZONE" - ], - "persistence": [ - "DATA_DIR/calendar_events.db" - ], - "dependencies": [ - "sqlite3", - "icalendar" - ], - "tests": [ - "tests/test_calendar_parse_dt_time_first.py", - "tests/test_calendar_update_event_tz.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Recurring RRULE event expansion calculation bugs across leap years." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/calendar_routes.py", - "symbol": "@router.get('/events')", - "line_range": "L220-L310", - "explanation": "Fetches calendar events for requested date window." - }, - { - "path": "src/tools/calendar.py", - "symbol": "CalendarTool", - "line_range": "L25-L120", - "explanation": "Agent tool for creating and modifying calendar entries." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-001", - "domain": "media", - "name": "Gallery Image Library & Album Operations", - "purpose": "Organizes images into custom albums, provides grid browsing, tagging, and album metadata management.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/gallery.js" - ], - "backend_entrypoints": [ - "routes/gallery/gallery_routes.py:setup_gallery_routes" - ], - "routes": [ - "/api/gallery/library", - "/api/gallery/albums", - "/api/gallery/upload" - ], - "configuration": [ - "GALLERY_STORAGE_DIR" - ], - "persistence": [ - "DATA_DIR/gallery/" - ], - "dependencies": [ - "pillow", - "sqlite3" - ], - "tests": [ - "tests/test_gallery_image_endpoint_owner_scope.py", - "tests/test_gallery_owner_filter_single_user.py" - ], - "documentation": [ - "docs/gallery.webm" - ], - "risks": [ - "Thumbnail generation overhead for high-resolution RAW camera images." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/gallery/gallery_routes.py", - "symbol": "@router.get('/api/gallery/library')", - "line_range": "L657-L720", - "explanation": "Fetches image library list with tag filters." - }, - { - "path": "static/js/gallery.js", - "symbol": "initGallery", - "line_range": "L1-L180", - "explanation": "Main gallery grid renderer and uploader." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-002", - "domain": "media", - "name": "Image Processing, AI Upscaling & Style Transfer", - "purpose": "Executes local image enhancement, background removal, face sharpening, and AI upscaling operations.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/galleryEditor.js", - "static/js/editor/ai-tool-runner.js" - ], - "backend_entrypoints": [ - "routes/gallery/gallery_routes.py:ai_upscale", - "routes/gallery/gallery_routes.py:remove_bg" - ], - "routes": [ - "/api/gallery/ai-upscale", - "/api/gallery/style-transfer", - "/api/image/inpaint", - "/api/image/remove-bg" - ], - "configuration": [ - "REALESRGAN_MODEL_PATH" - ], - "persistence": [ - "DATA_DIR/gallery/processed/" - ], - "dependencies": [ - "onnxruntime", - "opencv-python" - ], - "tests": [ - "tests/test_sanitize_multimodal_merge.py" - ], - "documentation": [ - "docs/gallery.webm" - ], - "risks": [ - "High GPU memory allocation when upscaling 4K images." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/gallery/gallery_routes.py", - "symbol": "@router.post('/api/gallery/ai-upscale')", - "line_range": "L544-L580", - "explanation": "Runs RealESRGAN image upscaling." - }, - { - "path": "routes/gallery/gallery_routes.py", - "symbol": "@router.post('/api/image/remove-bg')", - "line_range": "L1950-L2010", - "explanation": "Executes background removal pass." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-003", - "domain": "media", - "name": "Interactive Image Canvas Editor & Persisted Drafts", - "purpose": "Provides full multi-layer raster canvas editor, brush tools, transforms, masks, and draft project persistence.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/galleryEditor.js", - "static/js/editor/history-panel.js" - ], - "backend_entrypoints": [ - "routes/editor_draft_routes.py:setup_editor_draft_routes" - ], - "routes": [ - "/api/editor/drafts", - "/api/editor/drafts/{draft_id}" - ], - "configuration": [ - "MAX_DRAFT_PROJECTS" - ], - "persistence": [ - "DATA_DIR/editor_drafts/" - ], - "dependencies": [ - "HTML5 Canvas API", - "pydantic" - ], - "tests": [ - "tests/test_canvas_coords_empty_touches_js.py", - "tests/test_snap_other_layers_nonarray_js.py" - ], - "documentation": [ - "docs/gallery.webm" - ], - "risks": [ - "Browser memory leak if multi-gigabyte layer undo buffers are kept indefinitely." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/editor_draft_routes.py", - "symbol": "setup_editor_draft_routes", - "line_range": "L30-L110", - "explanation": "API routes for saving and loading canvas project drafts." - }, - { - "path": "static/js/editor/history-panel.js", - "symbol": "HistoryManager", - "line_range": "L1-L90", - "explanation": "Canvas undo/redo stack manager." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-004", - "domain": "media", - "name": "Text-to-Speech (TTS) Synthesis Service", - "purpose": "Synthesizes spoken audio from text using local Kokoro, EdgeTTS, or OpenAI TTS engines.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/tts-ai.js" - ], - "backend_entrypoints": [ - "routes/tts_routes.py:setup_tts_routes", - "services/tts/tts_service.py:TTSService" - ], - "routes": [ - "/api/tts/synthesize", - "/api/tts/stats" - ], - "configuration": [ - "TTS_PROVIDER", - "TTS_VOICE" - ], - "persistence": [ - "DATA_DIR/tts_cache/" - ], - "dependencies": [ - "soundfile", - "edge-tts" - ], - "tests": [ - "tests/test_tts_available_nonstring_provider.py", - "tests/test_tts_cache_stats.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Audio synthesis latency on CPU-only hardware setups." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/tts_routes.py", - "symbol": "@router.post('/synthesize')", - "line_range": "L30-L75", - "explanation": "Synthesizes TTS audio clip." - }, - { - "path": "services/tts/tts_service.py", - "symbol": "TTSService", - "line_range": "L25-L140", - "explanation": "Provider abstraction layer for audio speech generation." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-005", - "domain": "media", - "name": "Speech-to-Text (STT) Audio Transcription Service", - "purpose": "Transcribes user audio recordings into text using faster-whisper or local speech models.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/voiceRecorder.js" - ], - "backend_entrypoints": [ - "routes/stt_routes.py:setup_stt_routes", - "services/stt/stt_service.py:STTService" - ], - "routes": [ - "/api/stt/transcribe", - "/api/stt/stats" - ], - "configuration": [ - "STT_PROVIDER", - "WHISPER_MODEL_SIZE" - ], - "persistence": [ - "Temporary audio buffer" - ], - "dependencies": [ - "faster-whisper", - "ffmpeg-python" - ], - "tests": [ - "tests/test_stt_leak.py", - "tests/test_speech_service_toggles.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Missing ffmpeg system dependency prevents audio format decoding." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/stt_routes.py", - "symbol": "@router.post('/transcribe')", - "line_range": "L25-L55", - "explanation": "Accepts multipart audio file and returns transcription text." - }, - { - "path": "services/stt/stt_service.py", - "symbol": "STTService", - "line_range": "L20-L110", - "explanation": "Whisper audio transcription engine wrapper." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-006", - "domain": "media", - "name": "Digital Signature Stamp Storage & Placement", - "purpose": "Stores transparent PNG user signatures and stamps for placement onto PDF forms and documents.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/signature.js" - ], - "backend_entrypoints": [ - "routes/signature_routes.py:setup_signature_routes" - ], - "routes": [ - "/api/signatures", - "/api/signatures/{sig_id}" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/signatures/" - ], - "dependencies": [ - "pillow" - ], - "tests": [ - "tests/test_signature_route_hardening.py", - "tests/test_signature_settings_dom_xss.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Cross-site scripting if signature image titles contain unescaped user input." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/signature_routes.py", - "symbol": "setup_signature_routes", - "line_range": "L30-L120", - "explanation": "CRUD endpoints for managing user signature PNG stamps." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-007", - "domain": "media", - "name": "Generated Image Artifact Route & MCP Integration", - "purpose": "Serves generated AI artwork artifacts and integrates with image generation MCP server.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chat.js" - ], - "backend_entrypoints": [ - "app.py:serve_generated_image", - "src/generated_images.py", - "mcp_servers/image_gen_server.py" - ], - "routes": [ - "/api/generated-image/{filename}" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/generated_images/" - ], - "dependencies": [ - "pillow", - "mcp" - ], - "tests": [ - "tests/test_image_models_nondict_system.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Path traversal vulnerability if filename parameter is un-sanitized." - ], - "unknowns": [], - "evidence": [ - { - "path": "app.py", - "symbol": "serve_generated_image", - "line_range": "L499-L520", - "explanation": "Serves generated image artifacts with cache headers." - }, - { - "path": "src/generated_images.py", - "symbol": "resolve_generated_image_path", - "line_range": "L20-L32", - "explanation": "Confines requested image path within artifacts directory." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEDIA-008", - "domain": "media", - "name": "Native MLX Image Bridge (macOS Apple Silicon)", - "purpose": "Native Apple Swift bridge for hardware-accelerated diffusion and MLX image colorization on macOS.", - "status": "experimental", - "frontend_entrypoints": [ - "static/js/editor/ai-tools-misc.js" - ], - "backend_entrypoints": [ - "swift/odysseus-mlx-image-bridge/Package.swift", - "scripts/mlx_image_server.py", - "scripts/diffusion_server.py" - ], - "routes": [ - "/api/image/mlx-colorize" - ], - "configuration": [ - "ENABLE_MLX_ACCELERATION" - ], - "persistence": [ - "Build artifact binary" - ], - "dependencies": [ - "Swift", - "MLX Framework" - ], - "tests": [ - "tests/helpers/import_state.py" - ], - "documentation": [ - "swift/odysseus-mlx-image-bridge/Package.swift" - ], - "risks": [ - "Requires macOS host with Apple Silicon M-series chip and compiled Swift binary." - ], - "unknowns": [ - "Binary build requires Xcode command line tools build step (`build-macos-app.sh`)." - ], - "evidence": [ - { - "path": "swift/odysseus-mlx-image-bridge/Package.swift", - "symbol": "Package", - "line_range": "L1-L30", - "explanation": "Swift package manifest for native MLX image bridge." - }, - { - "path": "scripts/mlx_image_server.py", - "symbol": "main", - "line_range": "L20-L90", - "explanation": "Python daemon wrapping native Swift MLX binary." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires Apple Silicon, macOS tooling, and the compiled MLX bridge." - } - }, - { - "id": "SECURITY-001", - "domain": "security", - "name": "Authentication, Session Cookies & User Management", - "purpose": "Handles bcrypt password hashing, session cookie issuance, authentication enforcement, and user administration.", - "status": "verified", - "frontend_entrypoints": [ - "static/login.html", - "static/js/admin.js" - ], - "backend_entrypoints": [ - "routes/auth_routes.py:setup_auth_routes", - "core/auth.py:AuthManager" - ], - "routes": [ - "/api/auth/login", - "/api/auth/me", - "/api/auth/users", - "/api/auth/logout" - ], - "configuration": [ - "AUTH_ENABLED", - "SESSION_COOKIE_NAME" - ], - "persistence": [ - "DATA_DIR/auth.db" - ], - "dependencies": [ - "bcrypt", - "itsdangerous" - ], - "tests": [ - "tests/test_rename_user_owner_sync.py", - "tests/test_reserved_username_admin_escalation.py" - ], - "documentation": [ - "THREAT_MODEL.md", - "SECURITY.md" - ], - "risks": [ - "Cookie session hijack if deployed over unencrypted HTTP without HTTPS cookie flags." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/auth_routes.py", - "symbol": "@router.post('/login')", - "line_range": "L100-L180", - "explanation": "Authenticates credentials and sets session cookie." - }, - { - "path": "core/auth.py", - "symbol": "AuthManager", - "line_range": "L40-L260", - "explanation": "Handles user creation, password verification, and session tokens." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SECURITY-002", - "domain": "security", - "name": "System Vault Encrypted Secret Storage", - "purpose": "Encrypts API keys, passphrases, and third-party secrets on disk using AES-GCM / PBKDF2 key derivation.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/vault_routes.py:setup_vault_routes", - "src/secret_storage.py:SecretVault", - "src/tools/vault.py" - ], - "routes": [ - "/api/vault/config", - "/api/vault/unlock", - "/api/vault/lock" - ], - "configuration": [ - "VAULT_SALT" - ], - "persistence": [ - "DATA_DIR/vault.enc" - ], - "dependencies": [ - "cryptography" - ], - "tests": [ - "tests/test_vault_password_not_in_argv.py" - ], - "documentation": [ - "THREAT_MODEL.md" - ], - "risks": [ - "Loss of vault master passphrase renders all encrypted secrets permanently unrecoverable." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/vault_routes.py", - "symbol": "setup_vault_routes", - "line_range": "L126-L229", - "explanation": "Admin routes for vault configuration, login, unlock, lock, and logout." - }, - { - "path": "src/secret_storage.py", - "symbol": "SecretStorage", - "line_range": "L57-L87", - "explanation": "Fernet symmetric key DB secret encryption." - }, - { - "path": "tests/test_vault_password_not_in_argv.py", - "symbol": "test_bw_password_not_in_argv", - "line_range": "L1-L117", - "explanation": "Verifies master password is fed via stdin and never appears in process argv." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires installed Bitwarden CLI (`bw`) executable." - } - }, - { - "id": "SECURITY-003", - "domain": "security", - "name": "API Token Management & Scope Access Control", - "purpose": "Generates scoped API bearer tokens (read/write/admin) for external tool and script authentication.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/api_token_routes.py:setup_api_token_routes", - "core/database.py:ApiToken" - ], - "routes": [ - "/api/tokens", - "/api/tokens/{token_id}" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/odysseus.db (api_tokens table)" - ], - "dependencies": [ - "sqlalchemy", - "secrets" - ], - "tests": [ - "tests/test_api_key_file_permissions.py" - ], - "documentation": [ - "SECURITY.md" - ], - "risks": [ - "Leaked API bearer tokens with excessive permission scopes." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/api_token_routes.py", - "symbol": "setup_api_token_routes", - "line_range": "L30-L150", - "explanation": "Exposes API token creation, scope assignment, and revocation." - }, - { - "path": "core/database.py", - "symbol": "ApiToken", - "line_range": "L50-L90", - "explanation": "SQLAlchemy ORM schema for API tokens and permissions." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SECURITY-004", - "domain": "security", - "name": "Prompt Security & Injection Defense Engine", - "purpose": "Scans system prompts and external inputs for prompt injection attempts, jailbreaks, and sensitive data leaks.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/chat.js" - ], - "backend_entrypoints": [ - "src/prompt_security.py:sanitize_prompt", - "src/tool_security.py:validate_tool_input" - ], - "routes": [ - "Implicit security inspection" - ], - "configuration": [ - "ENABLE_PROMPT_SANITY_CHECK" - ], - "persistence": [], - "dependencies": [ - "re" - ], - "tests": [ - "tests/test_skill_index_prompt_injection.py", - "tests/test_tool_output_prompt_injection.py" - ], - "documentation": [ - "THREAT_MODEL.md" - ], - "risks": [ - "False positives blocking legitimate complex coding or security prompts." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/prompt_security.py", - "symbol": "untrusted_context_message", - "line_range": "L64-L86", - "explanation": "Wraps untrusted context with guard delimiters and sets metadata.trusted = False." - }, - { - "path": "src/tool_security.py", - "symbol": "NON_ADMIN_BLOCKED_TOOLS", - "line_range": "L42-L78", - "explanation": "Enforces tool execution safety for non-admin user roles." - }, - { - "path": "tests/test_skill_index_prompt_injection.py", - "symbol": "test_skill_index", - "line_range": "L1-L208", - "explanation": "Verifies skill index descriptions cannot leak into trusted system prompts." - }, - { - "path": "tests/test_tool_output_prompt_injection.py", - "symbol": "test_tool_output", - "line_range": "L1-L50", - "explanation": "Tool output injection guards." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SECURITY-005", - "domain": "security", - "name": "URL & Path Confinement Security Guards", - "purpose": "Prevents SSRF attacks and path traversal by validating target IP addresses and resolving symlinks.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/fileHandler.js" - ], - "backend_entrypoints": [ - "src/url_security.py:validate_url", - "src/url_safety.py", - "core/log_safety.py" - ], - "routes": [ - "Implicit guard functions" - ], - "configuration": [ - "ALLOWED_DOMAINS_WHITELIST" - ], - "persistence": [], - "dependencies": [ - "ipaddress", - "urllib.parse" - ], - "tests": [ - "tests/test_url_safety.py", - "tests/test_tool_path_confinement.py", - "tests/test_workspace_confine.py" - ], - "documentation": [ - "THREAT_MODEL.md" - ], - "risks": [ - "DNS rebinding attacks if IP address is re-resolved post-validation." - ], - "unknowns": [], - "evidence": [ - { - "path": "src/url_safety.py", - "symbol": "check_outbound_url", - "line_range": "L60-L108", - "explanation": "Rejects non-HTTP(S) schemes, link-local, cloud metadata SSRF addresses." - }, - { - "path": "src/url_security.py", - "symbol": "validate_public_http_url", - "line_range": "L81-L94", - "explanation": "Validates public-facing endpoints." - }, - { - "path": "tests/test_url_safety.py", - "symbol": "test_url_safety", - "line_range": "L1-L117", - "explanation": "Scheme validation, cloud metadata SSRF rejection, IP classification." - }, - { - "path": "tests/test_tool_path_confinement.py", - "symbol": "test_path_confinement", - "line_range": "L1-L50", - "explanation": "Path traversal checks." - }, - { - "path": "tests/test_workspace_confine.py", - "symbol": "test_workspace_confine", - "line_range": "L1-L50", - "explanation": "Workspace confinement checks." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SECURITY-006", - "domain": "security", - "name": "HTTP Security Headers Middleware", - "purpose": "Injects standard OWASP HTTP security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options).", - "status": "verified", - "frontend_entrypoints": [ - "static/app.js" - ], - "backend_entrypoints": [ - "core/middleware.py:SecurityHeadersMiddleware" - ], - "routes": [ - "Applies globally to all routes" - ], - "configuration": [ - "CSP_NONCE_ENABLED" - ], - "persistence": [], - "dependencies": [ - "starlette.middleware.base" - ], - "tests": [ - "tests/test_security_headers_middleware.py", - "tests/test_security_headers_pdf_preview.py" - ], - "documentation": [ - "SECURITY.md" - ], - "risks": [ - "Strict Content Security Policy (CSP) blocking third-party embedded web resources." - ], - "unknowns": [], - "evidence": [ - { - "path": "core/middleware.py", - "symbol": "SecurityHeadersMiddleware", - "line_range": "L40-L110", - "explanation": "Sets strict security headers and CSP nonces on HTTP responses." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SECURITY-007", - "domain": "security", - "name": "Admin System Data Wipe ('Danger Zone')", - "purpose": "Provides administrative reset operations to wipe sessions, cache, uploaded files, or factory reset state.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/admin.js" - ], - "backend_entrypoints": [ - "routes/admin_wipe/admin_wipe_routes.py:setup_admin_wipe_routes" - ], - "routes": [ - "/api/admin/wipe/{kind}" - ], - "configuration": [], - "persistence": [ - "ALL storage locations" - ], - "dependencies": [ - "os", - "shutil" - ], - "tests": [ - "tests/test_session_ghost_delete.py" - ], - "documentation": [ - "THREAT_MODEL.md" - ], - "risks": [ - "Accidental catastrophic data loss if triggered without user confirmation." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/admin_wipe/admin_wipe_routes.py", - "symbol": "@router.delete('/wipe/{kind}')", - "line_range": "L71-L130", - "explanation": "Executes systemic data wipe based on requested scope." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-001", - "domain": "frontend", - "name": "Single Page Application Shell & Client Router", - "purpose": "Main HTML5 SPA shell, DOM lifecycle initializers, tab navigation, and deep-link route handlers.", - "status": "verified", - "frontend_entrypoints": [ - "static/index.html", - "static/app.js", - "static/js/init.js" - ], - "backend_entrypoints": [ - "app.py:serve_index" - ], - "routes": [ - "/", - "/notes", - "/calendar", - "/cookbook", - "/email", - "/memory", - "/gallery", - "/tasks", - "/library" - ], - "configuration": [], - "persistence": [ - "static/index.html" - ], - "dependencies": [ - "Vanilla HTML5/JS" - ], - "tests": [ - "tests/test_app_static_mime.py", - "tests/test_serve_html_with_nonce.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Stale browser static cache if asset hashing is omitted during deployment." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/index.html", - "symbol": "index.html", - "line_range": "L1-L200", - "explanation": "Main SPA entry point containing modal roots and CSS bundles." - }, - { - "path": "app.py", - "symbol": "serve_index", - "line_range": "L867-L878", - "explanation": "Serves index.html with dynamically generated CSP nonces." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-002", - "domain": "frontend", - "name": "Dynamic Theme, Color System & Custom Fonts", - "purpose": "Supports dark/light themes, custom CSS variables, color picker controls, and user font uploads.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/theme.js", - "static/style.css", - "static/js/colorPicker.js" - ], - "backend_entrypoints": [ - "routes/font_routes.py:setup_font_routes", - "routes/prefs_routes.py:setup_prefs_routes" - ], - "routes": [ - "/api/font/custom", - "/api/prefs" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/user_prefs.json", - "static/fonts/custom/" - ], - "dependencies": [ - "Vanilla CSS", - "pydantic" - ], - "tests": [ - "tests/test_prefs_atomic_write.py", - "tests/test_select_dropdown_theme_css.py" - ], - "documentation": [ - "docs/theme.webm" - ], - "risks": [ - "Flash of unstyled content (FOUC) on slow connections." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/theme.js", - "symbol": "applyTheme", - "line_range": "L1-L90", - "explanation": "Applies custom HSL theme variables to DOM document root." - }, - { - "path": "routes/font_routes.py", - "symbol": "setup_font_routes", - "line_range": "L20-L55", - "explanation": "Allows uploading and serving custom WOFF2 font files." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-003", - "domain": "frontend", - "name": "Window Manager, Tile Layout & Modal Control System", - "purpose": "Manages draggable tool windows, snapped multi-tile viewports, modal dialog Z-ordering, and ESC key stacks.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/modalManager.js", - "static/js/tileManager.js", - "static/js/windowDrag.js", - "static/js/windowResize.js", - "static/js/escMenuStack.js" - ], - "backend_entrypoints": [ - "None (Client UI subsystem)" - ], - "routes": [], - "configuration": [ - "localStorage: tile_layout_state" - ], - "persistence": [ - "Browser localStorage" - ], - "dependencies": [ - "Vanilla JS DOM event listeners" - ], - "tests": [ - "tests/test_portal_dropdown_z_js.py", - "tests/test_tile_manager_snap_zones_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Overlap artifacts when opening many simultaneous tool floating windows." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/modalManager.js", - "symbol": "ModalManager", - "line_range": "L1-L140", - "explanation": "Controls modal open/close transitions and focus trapping." - }, - { - "path": "static/js/tileManager.js", - "symbol": "TileManager", - "line_range": "L1-L180", - "explanation": "Handles viewport split-pane grid arrangements." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-004", - "domain": "frontend", - "name": "Global Keyboard Shortcuts & Accessibility Controls", - "purpose": "Provides configurable hotkeys (Ctrl+K search, Esc close, Alt+1-9 tabs) and high-contrast accessibility options.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/keyboard-shortcuts.js", - "static/js/a11y.js" - ], - "backend_entrypoints": [ - "None (Client UI subsystem)" - ], - "routes": [], - "configuration": [ - "localStorage: user_hotkeys" - ], - "persistence": [ - "Browser localStorage" - ], - "dependencies": [ - "Vanilla JS" - ], - "tests": [ - "tests/test_keyboard_shortcuts.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Browser keybinding collisions with browser default hotkeys." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/keyboard-shortcuts.js", - "symbol": "initShortcuts", - "line_range": "L1-L110", - "explanation": "Binds global keydown handlers for system shortcuts." - }, - { - "path": "static/js/a11y.js", - "symbol": "initA11y", - "line_range": "L1-L80", - "explanation": "Applies ARIA roles and dyslexic font toggles." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-005", - "domain": "frontend", - "name": "Markdown, LaTeX & Code Block Streaming Renderer", - "purpose": "Parses incoming SSE markdown streams, renders KaTeX math formulas, syntax-highlighted code, and interactive runners.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/markdown.js", - "static/js/streamingRenderer.js", - "static/js/streamingSegmenter.js", - "static/js/codeRunner.js" - ], - "backend_entrypoints": [ - "None (Client rendering subsystem)" - ], - "routes": [], - "configuration": [], - "persistence": [ - "Browser DOM" - ], - "dependencies": [ - "highlight.js", - "katex" - ], - "tests": [ - "tests/streaming/segmenter.test.mjs", - "tests/test_streaming_segmenter_js.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "DOM thrashing if streaming segmenter updates UI too frequently." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/markdown.js", - "symbol": "renderMarkdown", - "line_range": "L1-L200", - "explanation": "Converts markdown prose to HTML nodes with syntax highlighting." - }, - { - "path": "static/js/streamingSegmenter.js", - "symbol": "Segmenter", - "line_range": "L1-L150", - "explanation": "Parses un-closed markdown fences during live stream." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-006", - "domain": "frontend", - "name": "Interactive Tour & Guided Onboarding System", - "purpose": "Presents interactive step-by-step feature tours and UI tooltip hints for new users.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/tourHints.js", - "static/js/tourAutoplay.js" - ], - "backend_entrypoints": [ - "None (Client UI subsystem)" - ], - "routes": [], - "configuration": [ - "localStorage: tour_completed" - ], - "persistence": [ - "Browser localStorage" - ], - "dependencies": [ - "Vanilla JS" - ], - "tests": [ - "tests/test_task_routes.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Tour step misalignment if window is resized mid-tour." - ], - "unknowns": [], - "evidence": [ - { - "path": "static/js/tourHints.js", - "symbol": "startTour", - "line_range": "L1-L120", - "explanation": "Renders guided feature tour overlays over target UI elements." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "FRONTEND-007", - "domain": "frontend", - "name": "Background Effects Prototyping Sandbox", - "purpose": "Standalone sandbox page for prototyping visual background animations, waves, and whirlpool effects.", - "status": "dead-code-candidate", - "frontend_entrypoints": [ - "static/wave-variants.html", - "static/whirlpool-variants.html" - ], - "backend_entrypoints": [ - "app.py:serve_backgrounds" - ], - "routes": [ - "/backgrounds" - ], - "configuration": [], - "persistence": [ - "None" - ], - "dependencies": [ - "HTML5 Canvas", - "WebGL" - ], - "tests": [], - "documentation": [ - "README.md" - ], - "risks": [ - "High GPU utilization when rendering complex shader wave animations." - ], - "unknowns": [ - "Route `/backgrounds` in app.py L918 attempts to serve `static/backgrounds.html` which is missing from disk; variant templates `wave-variants.html` and `whirlpool-variants.html` exist." - ], - "evidence": [ - { - "path": "app.py", - "symbol": "serve_backgrounds", - "line_range": "L915-L918", - "explanation": "Serves visual background sandbox HTML page route." - }, - { - "path": "static/wave-variants.html", - "symbol": "wave-variants.html", - "line_range": "L1-L150", - "explanation": "Interactive background effect prototyping sandbox variant." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E1", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-001", - "domain": "platform", - "name": "Application Initialization & Lifespan Management", - "purpose": "Orchestrates server startup, database table migration, background daemon initialization, and clean shutdown.", - "status": "verified", - "frontend_entrypoints": [ - "app.py" - ], - "backend_entrypoints": [ - "app.py:_lifespan", - "src/app_initializer.py:initialize_app" - ], - "routes": [ - "App startup lifespan" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/" - ], - "dependencies": [ - "asyncio", - "logging" - ], - "tests": [ - "tests/test_app_helpers.py" - ], - "documentation": [ - "docs/setup.md" - ], - "risks": [ - "Un-handled exceptions during startup halt application launch." - ], - "unknowns": [], - "evidence": [ - { - "path": "app.py", - "symbol": "_lifespan", - "line_range": "L996-L1030", - "explanation": "FastAPI lifespan context manager executing startup tasks." - }, - { - "path": "src/app_initializer.py", - "symbol": "initialize_app", - "line_range": "L29-L125", - "explanation": "Initializes app directories, DB schemas, and logging." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-002", - "domain": "platform", - "name": "System Health, Readiness & Version Monitoring APIs", - "purpose": "Exposes Liveness (/api/health), Readiness (/api/ready), App Version (/api/version), and Client Perf APIs.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/admin.js" - ], - "backend_entrypoints": [ - "app.py:health_check", - "app.py:readiness_check", - "app.py:get_version", - "src/readiness.py" - ], - "routes": [ - "/api/health", - "/api/ready", - "/api/version", - "/api/runtime", - "/api/client-perf" - ], - "configuration": [], - "persistence": [], - "dependencies": [ - "fastapi" - ], - "tests": [ - "tests/test_readiness.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Readiness check delays if verifying connectivity to offline remote endpoints." - ], - "unknowns": [], - "evidence": [ - { - "path": "app.py", - "symbol": "readiness_check", - "line_range": "L963-L973", - "explanation": "Performs system component integrity check." - }, - { - "path": "src/readiness.py", - "symbol": "check_readiness", - "line_range": "L15-L61", - "explanation": "Checks database, storage, and key paths for read/write access." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-003", - "domain": "platform", - "name": "Database Schema, Migrations & SQLite Persistence", - "purpose": "Defines core relational tables (users, tokens, tasks, sessions) and executes automated SQLite schema upgrades.", - "status": "verified", - "frontend_entrypoints": [ - "scripts/update_database.py" - ], - "backend_entrypoints": [ - "core/database.py:init_db", - "scripts/update_database.py:run_migrations", - "src/database.py" - ], - "routes": [ - "Implicit DB queries" - ], - "configuration": [ - "DATABASE_URL" - ], - "persistence": [ - "DATA_DIR/odysseus.db" - ], - "dependencies": [ - "sqlalchemy", - "alembic", - "sqlite3" - ], - "tests": [ - "tests/test_sqlite_foreign_keys.py", - "tests/test_update_database_script.py", - "tests/test_app_db_permissions.py" - ], - "documentation": [ - "docs/setup.md" - ], - "risks": [ - "SQLite file lock contention under high concurrent write loads." - ], - "unknowns": [], - "evidence": [ - { - "path": "core/database.py", - "symbol": "init_db", - "line_range": "L40-L120", - "explanation": "Creates ORM tables and establishes connection pool." - }, - { - "path": "scripts/update_database.py", - "symbol": "run_migrations", - "line_range": "L20-L110", - "explanation": "Applies missing schema columns and indices." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-004", - "domain": "platform", - "name": "User Data Export & Import Backup Infrastructure", - "purpose": "Exports complete user workspace state (sessions, memory, skills, notes, presets) into a zip archive.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/backup_routes.py:setup_backup_routes", - "scripts/odysseus-backup" - ], - "routes": [ - "/api/backup/export", - "/api/backup/import" - ], - "configuration": [], - "persistence": [ - "ZIP archive files" - ], - "dependencies": [ - "zipfile", - "json" - ], - "tests": [ - "tests/cli/test_preset_cli_store.py" - ], - "documentation": [ - "docs/backup-restore.md" - ], - "risks": [ - "Corrupt archive files causing partial data restore." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/backup_routes.py", - "symbol": "setup_backup_routes", - "line_range": "L30-L140", - "explanation": "Handles workspace data export and import upload unpack." - }, - { - "path": "docs/backup-restore.md", - "symbol": "Documentation", - "line_range": "L1-L50", - "explanation": "Backup and restore operational documentation." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-005", - "domain": "platform", - "name": "File Cleanup & Storage Maintenance Engine", - "purpose": "Scans data directories for orphaned files, old uploads, temporary vision images, and frees disk space.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/storage.js" - ], - "backend_entrypoints": [ - "routes/cleanup/cleanup_routes.py:setup_cleanup_routes", - "src/cleanup_service.py", - "src/session_image_cleanup.py" - ], - "routes": [ - "/api/cleanup/preview", - "/api/cleanup" - ], - "configuration": [ - "CLEANUP_RETENTION_DAYS" - ], - "persistence": [ - "DATA_DIR/uploads/" - ], - "dependencies": [ - "os", - "shutil" - ], - "tests": [ - "tests/test_session_actions_cleanup.py", - "tests/test_session_image_cleanup.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Deletes files uploaded in active sessions if retention window is set too short." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/cleanup/cleanup_routes.py", - "symbol": "@router.get('/preview')", - "line_range": "L22-L37", - "explanation": "Previews reclaimable disk space across storage directories." - }, - { - "path": "src/cleanup_service.py", - "symbol": "CleanupService", - "line_range": "L25-L120", - "explanation": "Executes filesystem purge of orphaned asset files." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-006", - "domain": "platform", - "name": "System Health & RAG Diagnostic Suite", - "purpose": "Executes real-time integrity diagnostics across ChromaDB, SearXNG, local models, and network interfaces.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/settings.js" - ], - "backend_entrypoints": [ - "routes/diagnostics_routes.py:setup_diagnostics_routes", - "src/service_health.py" - ], - "routes": [ - "/api/diagnostics" - ], - "configuration": [], - "persistence": [], - "dependencies": [ - "httpx", - "chromadb" - ], - "tests": [ - "tests/test_service_health_collect.py", - "tests/test_service_health_chromadb.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Diagnostic timeout if external search provider is unreachable." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/diagnostics_routes.py", - "symbol": "setup_diagnostics_routes", - "line_range": "L30-L100", - "explanation": "Runs subsystem health check suite." - }, - { - "path": "src/service_health.py", - "symbol": "collect_health_status", - "line_range": "L20-L150", - "explanation": "Inspects vector database, email, search, and local provider status." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-007", - "domain": "platform", - "name": "Desktop CLI Utilities & Shell Integration Tools", - "purpose": "Provides command-line interface tools (`odysseus`, `odysseus-mcp`, `odysseus-mail`) for terminal usage.", - "status": "verified", - "frontend_entrypoints": [ - "scripts/odysseus", - "scripts/odysseus-mcp", - "scripts/odysseus-mail", - "scripts/odysseus-calendar" - ], - "backend_entrypoints": [ - "scripts/_lib/cli.py:main" - ], - "routes": [ - "CLI commands" - ], - "configuration": [], - "persistence": [ - "CLI configuration" - ], - "dependencies": [ - "urllib", - "json" - ], - "tests": [ - "tests/cli/test_sessions_cli.py", - "tests/cli/test_mail_cli_recipients.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Outdated CLI scripts if backend API schemas change." - ], - "unknowns": [], - "evidence": [ - { - "path": "scripts/_lib/cli.py", - "symbol": "main", - "line_range": "L15-L110", - "explanation": "Shared CLI framework for terminal helper commands." - }, - { - "path": "scripts/odysseus", - "symbol": "odysseus", - "line_range": "L1-L50", - "explanation": "Main terminal launcher script." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-008", - "domain": "platform", - "name": "Desktop Companion App Integration", - "purpose": "Provides API routes and pairing mechanisms for the native macOS/desktop menu bar companion app.", - "status": "verified", - "frontend_entrypoints": [ - "companion/README.md" - ], - "backend_entrypoints": [ - "companion/pairing.py", - "companion/routes.py:setup_companion_routes" - ], - "routes": [ - "/companion/pair", - "/companion/status" - ], - "configuration": [ - "COMPANION_SECRET_KEY" - ], - "persistence": [ - "companion_pairing.json" - ], - "dependencies": [ - "fastapi" - ], - "tests": [ - "tests/helpers/import_state.py" - ], - "documentation": [ - "companion/README.md" - ], - "risks": [ - "Pairing code expiration timing window." - ], - "unknowns": [], - "evidence": [ - { - "path": "companion/routes.py", - "symbol": "setup_companion_routes", - "line_range": "L20-L120", - "explanation": "Endpoints for pairing and status sync with desktop companion." - }, - { - "path": "companion/pairing.py", - "symbol": "PairingManager", - "line_range": "L15-L80", - "explanation": "Generates and validates companion pairing codes." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "PLATFORM-009", - "domain": "platform", - "name": "Docker Containerization & GPU Hardware Manifests", - "purpose": "Provides multi-stage Dockerfile and Docker Compose manifests for CPU, NVIDIA CUDA, and AMD ROCm GPUs.", - "status": "verified", - "frontend_entrypoints": [ - "Dockerfile", - "docker-compose.yml", - "docker-compose.gpu-nvidia.yml", - "docker-compose.gpu-amd.yml" - ], - "backend_entrypoints": [ - "docker/entrypoint.sh" - ], - "routes": [ - "Containerized application" - ], - "configuration": [ - "DOCKER_GPU_VENDOR" - ], - "persistence": [ - "Container volumes" - ], - "dependencies": [ - "Docker", - "NVIDIA Container Toolkit" - ], - "tests": [ - "scripts/check-docker-gpu.sh" - ], - "documentation": [ - "docs/setup.md" - ], - "risks": [ - "Driver version incompatibility with host NVIDIA/AMD kernel drivers." - ], - "unknowns": [], - "evidence": [ - { - "path": "Dockerfile", - "symbol": "multi-stage-build", - "line_range": "L1-L113", - "explanation": "Multi-stage container build environment." - }, - { - "path": "docker-compose.gpu-nvidia.yml", - "symbol": "nvidia-gpu-manifest", - "line_range": "L1-L179", - "explanation": "NVIDIA GPU pass-through container specification." - }, - { - "path": "scripts/check-docker-gpu.sh", - "symbol": "check-docker-gpu", - "line_range": "L1-L615", - "explanation": "Automated diagnostic test script for host NVIDIA GPU passthrough." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": true, - "status": "pending", - "reason": "Requires Docker GPU pass-through and compatible host drivers." - } - }, - { - "id": "PLATFORM-010", - "domain": "platform", - "name": "Legacy FAISS Vector Index Migration Script", - "purpose": "Legacy utility script to migrate older FAISS vector indices into ChromaDB.", - "status": "legacy", - "frontend_entrypoints": [ - "None" - ], - "backend_entrypoints": [ - "scripts/migrate_faiss_to_chroma.py" - ], - "routes": [ - "CLI Script" - ], - "configuration": [], - "persistence": [ - "Legacy FAISS index files" - ], - "dependencies": [ - "faiss", - "chromadb" - ], - "tests": [ - "tests/helpers/import_state.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Fails if legacy FAISS index files do not exist." - ], - "unknowns": [ - "Superseded by native ChromaDB vector index pipeline." - ], - "evidence": [ - { - "path": "scripts/migrate_faiss_to_chroma.py", - "symbol": "migrate_faiss", - "line_range": "L15-L80", - "explanation": "Reads FAISS vector index files and writes to ChromaDB collection." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "NOTE-001", - "domain": "note", - "name": "Interactive Notes & Checklist Management", - "purpose": "Provides Google Keep-style notes, rich markdown text, checklist items, pinning, color tags, and reminders.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/notes.js" - ], - "backend_entrypoints": [ - "routes/note/note_routes.py:setup_note_routes", - "src/tools/notes.py" - ], - "routes": [ - "/api/notes", - "/api/notes/{note_id}", - "/api/notes/{note_id}/pin", - "/api/notes/reorder" - ], - "configuration": [], - "persistence": [ - "DATA_DIR/notes.db" - ], - "dependencies": [ - "sqlite3", - "pydantic" - ], - "tests": [ - "tests/test_notes_fail_closed_auth.py", - "tests/test_manage_notes_owner_gate.py" - ], - "documentation": [ - "docs/notes.webm" - ], - "risks": [ - "Concurrent edits on note item checkboxes." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/note/note_routes.py", - "symbol": "@router.get('')", - "line_range": "L623-L650", - "explanation": "Lists all user notes with pin and archive states." - }, - { - "path": "static/js/notes.js", - "symbol": "initNotesView", - "line_range": "L1-L160", - "explanation": "Main interactive notes grid and modal manager." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "SKILL-001", - "domain": "skill", - "name": "Dynamic Skill Management & Code Execution Engine", - "purpose": "Allows users to create, import, edit, test, and execute custom Python/Markdown skills dynamically.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/skills.js" - ], - "backend_entrypoints": [ - "routes/skills_routes.py:setup_skills_routes", - "services/memory/skills.py:SkillsManager" - ], - "routes": [ - "/api/skills", - "/api/skills/{skill_id}/invoke", - "/api/skills/import-from-url" - ], - "configuration": [ - "SKILLS_DIR" - ], - "persistence": [ - "DATA_DIR/skills/" - ], - "dependencies": [ - "pydantic", - "httpx" - ], - "tests": [ - "tests/test_skills_routes_owner_update.py", - "tests/test_skill_importer.py", - "tests/test_skill_save_no_rename.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "Arbitrary code execution risks if skill import URL is untrusted." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/skills_routes.py", - "symbol": "setup_skills_routes", - "line_range": "L100-L300", - "explanation": "Exposes CRUD and remote import routes for user skills." - }, - { - "path": "services/memory/skills.py", - "symbol": "SkillsManager", - "line_range": "L40-L220", - "explanation": "Handles skill storage, parsing, and execution." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "CONTACT-001", - "domain": "contact", - "name": "CardDAV Contact Management & Address Book Integration", - "purpose": "Connects to CardDAV servers, imports VCard contacts, and provides contact lookup for email/calendar autocomplete.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/emailLibrary.js" - ], - "backend_entrypoints": [ - "routes/contacts/contacts_routes.py:setup_contacts_routes", - "src/tools/contacts.py" - ], - "routes": [ - "/api/contacts/list", - "/api/contacts/search", - "/api/contacts/add", - "/api/contacts/config" - ], - "configuration": [ - "CARDDAV_URL" - ], - "persistence": [ - "DATA_DIR/contacts.db" - ], - "dependencies": [ - "vobject", - "sqlite3" - ], - "tests": [ - "tests/test_contacts_carddav_security.py", - "tests/cli/test_contacts_cli_rows.py" - ], - "documentation": [ - "README.md" - ], - "risks": [ - "VCard 3.0 vs 4.0 property parsing mismatches." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/contacts/contacts_routes.py", - "symbol": "@router.get('/list')", - "line_range": "L741-L764", - "explanation": "Returns contact list filtered by search query." - }, - { - "path": "src/tools/contacts.py", - "symbol": "ContactsTool", - "line_range": "L20-L110", - "explanation": "Agent tool for querying user address book contacts." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - }, - { - "id": "MEMORY-001", - "domain": "memory", - "name": "Persistent Long-Term Memory & Vector Indexing", - "purpose": "Extracts facts, user preferences, and temporal memories from chat sessions into vector/relational storage.", - "status": "verified", - "frontend_entrypoints": [ - "static/js/memory.js" - ], - "backend_entrypoints": [ - "routes/memory/memory_routes.py:setup_memory_routes", - "services/memory/service.py", - "mcp_servers/memory_server.py" - ], - "routes": [ - "/api/memory", - "/api/memory/search", - "/api/memory/extract", - "/api/memory/audit" - ], - "configuration": [ - "MEMORY_AUTO_EXTRACT" - ], - "persistence": [ - "DATA_DIR/memory.db", - "DATA_DIR/memory_chroma/" - ], - "dependencies": [ - "sqlite3", - "chromadb" - ], - "tests": [ - "tests/test_memory_routes_session_owner.py", - "tests/test_consolidate_memory_explicit_drops.py" - ], - "documentation": [ - "docs/theme.webm" - ], - "risks": [ - "Conflicting memory facts extracted from contradictory user prompts." - ], - "unknowns": [], - "evidence": [ - { - "path": "routes/memory/memory_routes.py", - "symbol": "@router.get('')", - "line_range": "L132-L150", - "explanation": "Fetches long-term user memory timeline." - }, - { - "path": "services/memory/memory_extractor.py", - "symbol": "MemoryExtractor", - "line_range": "L30-L160", - "explanation": "LLM-driven fact extraction from conversation transcripts." - } - ], - "verified_at_commit": "d8a2059df8e53bc7275c45339849d14c8651e73c", - "evidence_maturity": "E0", - "runtime_validation": { - "required": false, - "status": "not-required", - "reason": "No separate environment-dependent runtime validation was identified during this documentation pass." - } - } -] diff --git a/docs/discovery/feature-catalog.md b/docs/discovery/feature-catalog.md deleted file mode 100644 index 9a93c0317..000000000 --- a/docs/discovery/feature-catalog.md +++ /dev/null @@ -1,85 +0,0 @@ -# Feature Catalog - -This is a human-readable index derived from [`feature-catalog.json`](feature-catalog.json). The JSON file is canonical. - -| ID | Feature | Domain | Status | Evidence | Runtime | -|---|---|---|---|---|---| -| `AGENT-001` | Autonomous Agent Loop & Tool Execution Engine | `agent` | `verified` | `E0` | not required | -| `AGENT-002` | Scheduled Tasks & Event Bus Dispatcher | `agent` | `verified` | `E0` | not required | -| `AGENT-003` | Webhook Event Subscriptions & Trigger Processing | `agent` | `verified` | `E0` | not required | -| `AGENT-004` | Assistant Settings, Task Check-Ins & Background Job Monitor | `agent` | `partial` | `E1` | not required | -| `AGENT-005` | Model Context Protocol (MCP) Server Integration | `agent` | `verified` | `E0` | not required | -| `AGENT-006` | AI Interaction Tools & Pipeline Orchestration | `agent` | `verified` | `E0` | not required | -| `AGENT-007` | Subprocess & Background Job Execution Tools | `agent` | `verified` | `E0` | not required | -| `CALENDAR-001` | CalDAV Calendar Synchronization & Account Setup | `calendar` | `verified` | `E0` | pending | -| `CALENDAR-002` | Calendar Event Operations & iCalendar Parsing | `calendar` | `verified` | `E0` | not required | -| `CHAT-001` | Core Chat Streaming & SSE Message Generation | `chat` | `verified` | `E2` | pending | -| `CHAT-002` | Session Management & Conversation State | `chat` | `verified` | `E0` | not required | -| `CHAT-003` | Chat History & Message Editing/Truncation | `chat` | `verified` | `E0` | not required | -| `CHAT-004` | File & Multimodal Attachment Handling | `chat` | `verified` | `E0` | not required | -| `CHAT-005` | Chat Message Search | `chat` | `verified` | `E0` | not required | -| `CHAT-006` | System Prompts & Preset Management | `chat` | `verified` | `E0` | not required | -| `CHAT-007` | Emoji Rendering & Twemoji SVG Proxy | `chat` | `verified` | `E0` | not required | -| `CHAT-008` | Input History Recall (Arrow Up) | `chat` | `verified` | `E0` | not required | -| `CHAT-009` | Context Window Compaction & Truncation | `chat` | `verified` | `E0` | not required | -| `CONTACT-001` | CardDAV Contact Management & Address Book Integration | `contact` | `verified` | `E0` | not required | -| `COOKBOOK-001` | Local Model Download & Recipe Lifecycle Management | `cookbook` | `verified` | `E0` | not required | -| `COOKBOOK-002` | Hardware Model Fitting ('What Fits?') Analysis Engine | `cookbook` | `verified` | `E0` | not required | -| `COOKBOOK-003` | HuggingFace & MLX Model Discovery Services | `cookbook` | `verified` | `E0` | not required | -| `COOKBOOK-004` | Host Docker Access for Inference Container Runtimes | `cookbook` | `verified` | `E0` | pending | -| `DOCUMENT-001` | Document & Canvas Artifact Management | `document` | `verified` | `E0` | not required | -| `DOCUMENT-002` | PDF Form Processing & High-Fidelity Rendering | `document` | `verified` | `E1` | pending | -| `DOCUMENT-003` | Personal Document Indexing & RAG Retrieval | `document` | `verified` | `E0` | not required | -| `DOCUMENT-004` | Document Conversion & Text Extraction Engine | `document` | `verified` | `E0` | not required | -| `DOCUMENT-005` | Document Library UI Navigation | `document` | `verified` | `E0` | not required | -| `EMAIL-001` | Email Account Setup, IMAP/SMTP Connection & Polling | `email` | `verified` | `E1` | pending | -| `EMAIL-002` | Email Searching, Threading & Message Operations | `email` | `verified` | `E0` | not required | -| `EMAIL-003` | Email Composition, Draft Management & Sending | `email` | `verified` | `E0` | pending | -| `EMAIL-004` | Email MCP Server & Codex Integration Bridge | `email` | `verified` | `E0` | not required | -| `FRONTEND-001` | Single Page Application Shell & Client Router | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-002` | Dynamic Theme, Color System & Custom Fonts | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-003` | Window Manager, Tile Layout & Modal Control System | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-004` | Global Keyboard Shortcuts & Accessibility Controls | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-005` | Markdown, LaTeX & Code Block Streaming Renderer | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-006` | Interactive Tour & Guided Onboarding System | `frontend` | `verified` | `E0` | not required | -| `FRONTEND-007` | Background Effects Prototyping Sandbox | `frontend` | `dead-code-candidate` | `E1` | not required | -| `MEDIA-001` | Gallery Image Library & Album Operations | `media` | `verified` | `E0` | not required | -| `MEDIA-002` | Image Processing, AI Upscaling & Style Transfer | `media` | `verified` | `E0` | not required | -| `MEDIA-003` | Interactive Image Canvas Editor & Persisted Drafts | `media` | `verified` | `E0` | not required | -| `MEDIA-004` | Text-to-Speech (TTS) Synthesis Service | `media` | `verified` | `E0` | not required | -| `MEDIA-005` | Speech-to-Text (STT) Audio Transcription Service | `media` | `verified` | `E0` | not required | -| `MEDIA-006` | Digital Signature Stamp Storage & Placement | `media` | `verified` | `E0` | not required | -| `MEDIA-007` | Generated Image Artifact Route & MCP Integration | `media` | `verified` | `E0` | not required | -| `MEDIA-008` | Native MLX Image Bridge (macOS Apple Silicon) | `media` | `experimental` | `E0` | pending | -| `MEMORY-001` | Persistent Long-Term Memory & Vector Indexing | `memory` | `verified` | `E0` | not required | -| `MODEL-001` | Multi-Provider LLM Model Discovery & Metadata Management | `model` | `verified` | `E0` | not required | -| `MODEL-002` | Model Capability & Context Limits Detection | `model` | `verified` | `E0` | not required | -| `MODEL-003` | LLM Core Provider Communication & Fallback Routing | `model` | `verified` | `E0` | pending | -| `MODEL-004` | Model Selection & Display Ordering Preferences | `model` | `verified` | `E0` | not required | -| `MODEL-005` | Side-by-Side Model Comparison (A/B Testing) | `model` | `verified` | `E0` | not required | -| `MODEL-006` | GitHub Copilot Device Flow Authentication | `model` | `verified` | `E2` | pending | -| `MODEL-007` | ChatGPT Subscription Device Flow Authentication | `model` | `verified` | `E2` | pending | -| `MODEL-008` | Embedding Model Lane & Vector Provider Setup | `model` | `verified` | `E0` | not required | -| `NOTE-001` | Interactive Notes & Checklist Management | `note` | `verified` | `E0` | not required | -| `PLATFORM-001` | Application Initialization & Lifespan Management | `platform` | `verified` | `E0` | not required | -| `PLATFORM-002` | System Health, Readiness & Version Monitoring APIs | `platform` | `verified` | `E0` | not required | -| `PLATFORM-003` | Database Schema, Migrations & SQLite Persistence | `platform` | `verified` | `E0` | not required | -| `PLATFORM-004` | User Data Export & Import Backup Infrastructure | `platform` | `verified` | `E0` | not required | -| `PLATFORM-005` | File Cleanup & Storage Maintenance Engine | `platform` | `verified` | `E0` | not required | -| `PLATFORM-006` | System Health & RAG Diagnostic Suite | `platform` | `verified` | `E0` | not required | -| `PLATFORM-007` | Desktop CLI Utilities & Shell Integration Tools | `platform` | `verified` | `E0` | not required | -| `PLATFORM-008` | Desktop Companion App Integration | `platform` | `verified` | `E0` | not required | -| `PLATFORM-009` | Docker Containerization & GPU Hardware Manifests | `platform` | `verified` | `E0` | pending | -| `PLATFORM-010` | Legacy FAISS Vector Index Migration Script | `platform` | `legacy` | `E0` | not required | -| `RESEARCH-001` | Deep Research Execution Engine & SSE Progress Streaming | `research` | `verified` | `E0` | not required | -| `RESEARCH-002` | Research Library, Detail View & Image Controls | `research` | `verified` | `E0` | not required | -| `RESEARCH-003` | Web Search Engine Integration (SearXNG & Multi-Provider) | `research` | `verified` | `E1` | pending | -| `RESEARCH-004` | Research Result Peeking & Topic Spinoff Generation | `research` | `verified` | `E0` | not required | -| `SECURITY-001` | Authentication, Session Cookies & User Management | `security` | `verified` | `E0` | not required | -| `SECURITY-002` | System Vault Encrypted Secret Storage | `security` | `verified` | `E1` | pending | -| `SECURITY-003` | API Token Management & Scope Access Control | `security` | `verified` | `E0` | not required | -| `SECURITY-004` | Prompt Security & Injection Defense Engine | `security` | `verified` | `E1` | not required | -| `SECURITY-005` | URL & Path Confinement Security Guards | `security` | `verified` | `E1` | not required | -| `SECURITY-006` | HTTP Security Headers Middleware | `security` | `verified` | `E0` | not required | -| `SECURITY-007` | Admin System Data Wipe ('Danger Zone') | `security` | `verified` | `E0` | not required | -| `SKILL-001` | Dynamic Skill Management & Code Execution Engine | `skill` | `verified` | `E0` | not required | diff --git a/docs/discovery/references/source-provenance.md b/docs/discovery/references/source-provenance.md deleted file mode 100644 index c7a9a581d..000000000 --- a/docs/discovery/references/source-provenance.md +++ /dev/null @@ -1,28 +0,0 @@ -# Source Provenance & Audit Baseline - -## Target Repository & Snapshot - -- **Repository**: `odysseus-dev/odysseus` -- **Branch**: `discovery` -- **Pinned Commit SHA**: `d8a2059df8e53bc7275c45339849d14c8651e73c` -- **Snapshot Date**: `2026-07-23T14:49:02Z` - -## Discovery Package Organization - -The public discovery documentation package under `docs/discovery/` is structured as follows: - -- `feature-catalog.json`: Canonical machine-readable JSON catalog containing 79 feature records. -- `feature-catalog.md`: Human-readable summary derived from `feature-catalog.json`. -- `BASELINE-STATUS.md`: Publication status, evidence-validation snapshot, and durable maintainer guidance. -- `audit-method.md`: Audit rules, scope, and evidence maturity definitions (E0 to E4). -- `domains/`: 16 functional domain markdown files detailing feature implementations. -- `references/`: Audit provenance and repository snapshot metadata. -- `tools/`: Structural, consistency, and evidence validators with focused evidence-validator tests. - -## Exclusion Principles - -This public documentation package explicitly excludes: -- Internal planning artifacts or private meeting notes. -- Machine-specific filesystem paths or user environments. -- API keys, credentials, or private service endpoints. -- Application code or automated test mutations. diff --git a/docs/discovery/tools/test_validate_discovery_evidence.py b/docs/discovery/tools/test_validate_discovery_evidence.py deleted file mode 100644 index 5e3144fe0..000000000 --- a/docs/discovery/tools/test_validate_discovery_evidence.py +++ /dev/null @@ -1,224 +0,0 @@ -#!/usr/bin/env python3 -"""Focused negative tests for validate_discovery_evidence.py.""" - -from __future__ import annotations - -import importlib.util -import shutil -import sys -import tempfile -import unittest -from pathlib import Path - - -MODULE_PATH = Path(__file__).with_name("validate_discovery_evidence.py") -SPEC = importlib.util.spec_from_file_location("validate_discovery_evidence", MODULE_PATH) -assert SPEC and SPEC.loader -validator = importlib.util.module_from_spec(SPEC) -sys.modules[SPEC.name] = validator -SPEC.loader.exec_module(validator) - - -class FakeJavascriptParser: - supported = True - reason = "test parser" - - def __init__(self, symbols: list[validator.Located] | None = None) -> None: - self.symbols = symbols or [] - - def parse(self, path: Path) -> list[validator.Located]: - return self.symbols - - -class UnsupportedJavascriptParser: - supported = False - reason = "no repository-local parser" - - -class EvidenceNegativeTests(unittest.TestCase): - def setUp(self) -> None: - self.temp = tempfile.TemporaryDirectory() - self.root = Path(self.temp.name) - (self.root / "sample.py").write_text( - "from fastapi import APIRouter\n" - "router = APIRouter(prefix='/api')\n" - "\n" - "class ChatHandler:\n" - " def preprocess_message(self):\n" - " return True\n" - "\n" - "@router.post('/chat')\n" - "def chat_stream():\n" - " return True\n", - encoding="utf-8", - ) - (self.root / "sample.js").write_text( - "export const present = () => true;\n", encoding="utf-8" - ) - (self.root / "sample.sh").write_text( - "#!/usr/bin/env bash\nreal_function() {\n return 0\n}\n", - encoding="utf-8", - ) - self.backup = self.root / "copied-backups" - self.backup.mkdir() - for path in self.root.glob("sample.*"): - shutil.copy2(path, self.backup / path.name) - - def tearDown(self) -> None: - for backup in self.backup.iterdir(): - target = self.root / backup.name - shutil.copy2(backup, target) - self.assertEqual(target.read_bytes(), backup.read_bytes()) - self.temp.cleanup() - - def validate( - self, - evidence: dict[str, str], - javascript_parser: object | None = None, - ) -> validator.Validation: - return validator.validate_evidence( - self.root, - "TEST-001", - 0, - evidence, - javascript_parser or FakeJavascriptParser(), - ) - - def test_missing_python_symbol(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "python-function", - "locator": "fabricated", - "line_range": "L1-L1", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - - def test_incorrect_qualified_method(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "python-method", - "locator": "WrongHandler.preprocess_message", - "line_range": "L5-L6", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - - def test_symbol_outside_cited_range(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "python-method", - "locator": "ChatHandler.preprocess_message", - "line_range": "L1-L2", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "locator-outside-range") - - def test_fabricated_test_function(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "test-function", - "locator": "test_fabricated", - "line_range": "L1-L2", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - - def test_nonexistent_javascript_symbol_with_parser(self) -> None: - result = self.validate( - { - "path": "sample.js", - "kind": "javascript-function", - "locator": "missing", - "line_range": "L1-L1", - "explanation": "negative fixture", - }, - FakeJavascriptParser( - [validator.Located("present", "javascript-export", 1, 1)] - ), - ) - self.assertEqual(result.result, "invalid-locator") - - def test_unsupported_javascript_parser(self) -> None: - result = self.validate( - { - "path": "sample.js", - "kind": "javascript-function", - "locator": "present", - "line_range": "L1-L1", - "explanation": "negative fixture", - }, - UnsupportedJavascriptParser(), - ) - self.assertEqual(result.result, "unsupported") - - def test_route_path_mismatch(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "python-route", - "locator": "POST /api/wrong -> chat_stream", - "line_range": "L9-L10", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - self.assertIn("path", result.problem or "") - - def test_http_method_mismatch(self) -> None: - result = self.validate( - { - "path": "sample.py", - "kind": "python-route", - "locator": "GET /api/chat -> chat_stream", - "line_range": "L9-L10", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - self.assertIn("method", result.problem or "") - - def test_shell_function_mismatch(self) -> None: - result = self.validate( - { - "path": "sample.sh", - "kind": "shell-function", - "locator": "fabricated", - "line_range": "L1-L4", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - - def test_invalid_file_level_evidence(self) -> None: - result = self.validate( - { - "path": "missing.file", - "kind": "file", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-path") - - def test_file_level_evidence_rejects_fake_symbol(self) -> None: - result = self.validate( - { - "path": "sample.sh", - "kind": "file", - "locator": "whole-script", - "explanation": "negative fixture", - } - ) - self.assertEqual(result.result, "invalid-locator") - - -if __name__ == "__main__": - unittest.main() diff --git a/docs/discovery/tools/validate_discovery_consistency.py b/docs/discovery/tools/validate_discovery_consistency.py deleted file mode 100755 index 953b093e1..000000000 --- a/docs/discovery/tools/validate_discovery_consistency.py +++ /dev/null @@ -1,592 +0,0 @@ -#!/usr/bin/env python3 - -from __future__ import annotations - -import json -import re -import sys -from collections import Counter, defaultdict -from pathlib import Path -from typing import Any - - -ROOT = Path(__file__).resolve().parents[1] -CATALOG_PATH = ROOT / "feature-catalog.json" -CATALOG_MD_PATH = ROOT / "feature-catalog.md" -DOMAINS_DIR = ROOT / "domains" -REVIEWS_DIR = ROOT / "reviews" - -EXPECTED_COMMIT = "d8a2059df8e53bc7275c45339849d14c8651e73c" -EXPECTED_FEATURES = 79 -EXPECTED_DOMAINS = 16 - -VALID_STATUSES = { - "verified", - "partial", - "disabled", - "experimental", - "legacy", - "dead-code-candidate", - "unverified", -} - -VALID_MATURITY = {"E0", "E1", "E2", "E3", "E4"} - -VALID_RUNTIME = { - "not-required", - "pending", - "blocked", - "completed", -} - -REQUIRED_FIELDS = { - "id", - "domain", - "name", - "purpose", - "status", - "evidence_maturity", - "verified_at_commit", - "evidence", - "runtime_validation", -} - -FEATURE_ID_RE = re.compile(r"^[A-Z][A-Z0-9]*-\d{3}$") - -DOMAIN_HEADING_RE = re.compile( - r"^##\s+`?([A-Z][A-Z0-9]*-\d{3})`?" - r"\s+(?:—|-)\s+(.+?)\s*$" -) - -DOMAIN_FIELD_RE = re.compile( - r"^-\s+\*\*" - r"(Domain|Status|Evidence Maturity|Commit Verified)" - r"\*\*:\s*(.*?)\s*$" -) - -DOMAIN_RUNTIME_RE = re.compile( - r"^-\s+\*\*Runtime Validation\*\*:\s*(.*?)\s*$" -) - -REVIEW_HEADING_RE = re.compile( - r"^###\s+(?:\d+\.\s+)?" - r"([A-Z][A-Z0-9]*-\d{3})" - r"\s+(?:—|-)\s+.+$" -) - -REVIEW_RESULT_RE = re.compile( - r"^-\s+\*\*Resulting Status(?:\s+and|/)\s+Maturity\*\*:" - r"\s*`([^`]+)`\s*/\s*`([^`]+)`\s*$" -) - - -def clean(value: str) -> str: - value = value.strip() - - if ( - len(value) >= 2 - and value.startswith("`") - and value.endswith("`") - ): - return value[1:-1].strip() - - return value - - -def load_catalog(errors: list[str]) -> list[dict[str, Any]]: - try: - data = json.loads( - CATALOG_PATH.read_text(encoding="utf-8") - ) - except Exception as exc: - errors.append(f"Unable to read catalog: {exc}") - return [] - - if isinstance(data, list): - features = data - elif isinstance(data, dict) and isinstance(data.get("features"), list): - features = data["features"] - else: - errors.append( - "Catalog must be an array or contain a features array" - ) - return [] - - if not all(isinstance(feature, dict) for feature in features): - errors.append("Every catalog feature must be an object") - return [] - - return features - - -def validate_catalog( - features: list[dict[str, Any]], - errors: list[str], -) -> None: - if len(features) != EXPECTED_FEATURES: - errors.append( - f"Expected {EXPECTED_FEATURES} features, " - f"found {len(features)}" - ) - - ids = [feature.get("id") for feature in features] - - duplicates = sorted( - feature_id - for feature_id, count in Counter(ids).items() - if feature_id and count > 1 - ) - - if duplicates: - errors.append( - "Duplicate feature IDs: " + ", ".join(duplicates) - ) - - for index, feature in enumerate(features): - feature_id = feature.get("id") - label = ( - feature_id - if isinstance(feature_id, str) - else f"" - ) - - missing = sorted( - field - for field in REQUIRED_FIELDS - if feature.get(field) in (None, "", []) - ) - - if missing: - errors.append( - f"{label}: missing fields: {', '.join(missing)}" - ) - - if ( - not isinstance(feature_id, str) - or not FEATURE_ID_RE.fullmatch(feature_id) - ): - errors.append(f"{label}: invalid feature ID") - - if feature.get("status") not in VALID_STATUSES: - errors.append( - f"{label}: invalid status " - f"{feature.get('status')!r}" - ) - - if feature.get("evidence_maturity") not in VALID_MATURITY: - errors.append( - f"{label}: invalid maturity " - f"{feature.get('evidence_maturity')!r}" - ) - - if feature.get("verified_at_commit") != EXPECTED_COMMIT: - errors.append( - f"{label}: incorrect verified_at_commit" - ) - - runtime = feature.get("runtime_validation") - - if not isinstance(runtime, dict): - errors.append( - f"{label}: runtime_validation must be an object" - ) - continue - - required = runtime.get("required") - runtime_status = runtime.get("status") - reason = runtime.get("reason") - - if not isinstance(required, bool): - errors.append( - f"{label}: runtime required must be boolean" - ) - - if runtime_status not in VALID_RUNTIME: - errors.append( - f"{label}: invalid runtime status " - f"{runtime_status!r}" - ) - - if not isinstance(reason, str) or not reason.strip(): - errors.append( - f"{label}: runtime reason is blank" - ) - - if required is False and runtime_status != "not-required": - errors.append( - f"{label}: required=false requires not-required" - ) - - if required is True and runtime_status == "not-required": - errors.append( - f"{label}: required=true cannot be not-required" - ) - - -def parse_domain( - path: Path, - errors: list[str], -) -> dict[str, dict[str, str]]: - records: dict[str, dict[str, str]] = {} - current_id: str | None = None - - for line_number, line in enumerate( - path.read_text(encoding="utf-8").splitlines(), - start=1, - ): - heading = DOMAIN_HEADING_RE.match(line) - - if heading: - current_id = heading.group(1) - - if current_id in records: - errors.append( - f"{path.relative_to(ROOT)}:{line_number}: " - f"duplicate heading {current_id}" - ) - - records[current_id] = { - "Name": heading.group(2).strip(), - } - continue - - field = DOMAIN_FIELD_RE.match(line) - - if field and current_id: - value = clean(field.group(2)) - - if not value: - errors.append( - f"{path.relative_to(ROOT)}:{line_number}: " - f"blank {field.group(1)}" - ) - - records[current_id][field.group(1)] = value - continue - - runtime = DOMAIN_RUNTIME_RE.match(line) - - if runtime and current_id: - value = clean(runtime.group(1)) - runtime_status = re.split( - r"\s+(?:—|-)\s+", - value, - maxsplit=1, - )[0] - records[current_id]["Runtime Validation"] = ( - runtime_status.strip("` ") - ) - - return records - - -def validate_domains( - features: list[dict[str, Any]], - errors: list[str], -) -> None: - catalog = { - feature["id"]: feature - for feature in features - if feature.get("id") - } - - expected_by_domain: defaultdict[str, set[str]] = defaultdict(set) - - for feature in features: - expected_by_domain[feature["domain"]].add(feature["id"]) - - paths = sorted(DOMAINS_DIR.glob("*.md")) - - if len(paths) != EXPECTED_DOMAINS: - errors.append( - f"Expected {EXPECTED_DOMAINS} domain files, " - f"found {len(paths)}" - ) - - all_found: set[str] = set() - - for path in paths: - domain = path.stem - records = parse_domain(path, errors) - found = set(records) - expected = expected_by_domain.get(domain, set()) - all_found.update(found) - - if found != expected: - missing = sorted(expected - found) - extra = sorted(found - expected) - - errors.append( - f"{domain}: missing={missing}, unexpected={extra}" - ) - - for feature_id, record in records.items(): - feature = catalog.get(feature_id) - - if feature is None: - continue - - expected_values = { - "Name": feature["name"], - "Domain": feature["domain"], - "Status": feature["status"], - "Evidence Maturity": feature["evidence_maturity"], - "Commit Verified": feature["verified_at_commit"], - "Runtime Validation": ( - feature["runtime_validation"]["status"] - ), - } - - for field, expected_value in expected_values.items(): - actual = record.get(field) - - if actual != expected_value: - errors.append( - f"{path.relative_to(ROOT)}: " - f"{feature_id} {field}: " - f"{actual!r} != {expected_value!r}" - ) - - if all_found != set(catalog): - errors.append( - "Domain Markdown IDs do not match catalog JSON" - ) - - -def validate_catalog_markdown( - features: list[dict[str, Any]], - errors: list[str], -) -> None: - expected = { - feature["id"]: feature - for feature in features - } - found: dict[str, list[str]] = {} - - for line in CATALOG_MD_PATH.read_text( - encoding="utf-8" - ).splitlines(): - if not line.startswith("|"): - continue - - cells = [ - cell.strip() - for cell in line.strip().strip("|").split("|") - ] - - if len(cells) < 6: - continue - - feature_id = clean(cells[0]) - - if FEATURE_ID_RE.fullmatch(feature_id): - found[feature_id] = cells - - if set(found) != set(expected): - errors.append( - "feature-catalog.md IDs do not match JSON" - ) - - for feature_id, cells in found.items(): - feature = expected[feature_id] - runtime = feature["runtime_validation"] - runtime_display = ( - runtime["status"] - if runtime["required"] - else "not required" - ) - - actual = { - "name": cells[1].replace("\\|", "|"), - "domain": clean(cells[2]), - "status": clean(cells[3]), - "maturity": clean(cells[4]), - "runtime": clean(cells[5]), - } - - wanted = { - "name": feature["name"], - "domain": feature["domain"], - "status": feature["status"], - "maturity": feature["evidence_maturity"], - "runtime": runtime_display, - } - - for field, expected_value in wanted.items(): - if actual[field] != expected_value: - errors.append( - f"feature-catalog.md: {feature_id} " - f"{field}: {actual[field]!r} " - f"!= {expected_value!r}" - ) - - -def validate_reviews( - features: list[dict[str, Any]], - errors: list[str], -) -> int: - catalog = { - feature["id"]: feature - for feature in features - } - checked = 0 - - for path in sorted( - REVIEWS_DIR.glob("evidence-sample-*.md") - ): - current_id: str | None = None - results: set[str] = set() - - for line_number, line in enumerate( - path.read_text(encoding="utf-8").splitlines(), - start=1, - ): - heading = REVIEW_HEADING_RE.match(line) - - if heading: - current_id = heading.group(1) - continue - - result = REVIEW_RESULT_RE.match(line) - - if not result or current_id is None: - continue - - status, maturity = result.groups() - results.add(current_id) - checked += 1 - - feature = catalog.get(current_id) - - if feature is None: - errors.append( - f"{path.relative_to(ROOT)}:{line_number}: " - f"unknown feature {current_id}" - ) - continue - - if feature["status"] != status: - errors.append( - f"{current_id}: review status {status!r} " - f"!= catalog {feature['status']!r}" - ) - - if feature["evidence_maturity"] != maturity: - errors.append( - f"{current_id}: review maturity {maturity!r} " - f"!= catalog " - f"{feature['evidence_maturity']!r}" - ) - - if path.name == "evidence-sample-01.md" and len(results) != 12: - errors.append( - f"{path.relative_to(ROOT)}: expected 12 " - f"review results, found {len(results)}" - ) - - return checked - - -def validate_whitespace(errors: list[str]) -> None: - for path in sorted(ROOT.rglob("*")): - if not path.is_file(): - continue - - if path.suffix not in {".md", ".json", ".py", ".txt"}: - continue - - for line_number, line in enumerate( - path.read_text( - encoding="utf-8", - errors="replace", - ).splitlines(), - start=1, - ): - if line != line.rstrip(" \t"): - errors.append( - f"{path.relative_to(ROOT)}:{line_number}: " - "trailing whitespace" - ) - - if any(ROOT.rglob("*.pyc")): - errors.append("Generated .pyc files exist") - - if any( - path.is_dir() - for path in ROOT.rglob("__pycache__") - ): - errors.append("__pycache__ exists") - - -def main() -> int: - errors: list[str] = [] - features = load_catalog(errors) - reviewed = 0 - - if features: - validate_catalog(features, errors) - validate_domains(features, errors) - validate_catalog_markdown(features, errors) - reviewed = validate_reviews(features, errors) - - validate_whitespace(errors) - - print(f"Catalog Features: {len(features)}") - print( - "Unique Feature IDs:", - len({feature.get("id") for feature in features}), - ) - print( - "Domain Files:", - len(list(DOMAINS_DIR.glob("*.md"))), - ) - - if features: - print( - "Statuses:", - dict( - Counter( - feature.get("status") - for feature in features - ) - ), - ) - print( - "Evidence Maturity:", - dict( - Counter( - feature.get("evidence_maturity") - for feature in features - ) - ), - ) - print( - "Runtime Validation:", - dict( - Counter( - ( - feature.get( - "runtime_validation", - {}, - ).get("required"), - feature.get( - "runtime_validation", - {}, - ).get("status"), - ) - for feature in features - ) - ), - ) - - print(f"Review Results Checked: {reviewed}") - print(f"Consistency Errors: {len(errors)}") - - for error in errors: - print(f"ERROR: {error}") - - return 1 if errors else 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/docs/discovery/tools/validate_discovery_docs.py b/docs/discovery/tools/validate_discovery_docs.py deleted file mode 100755 index 656b800ec..000000000 --- a/docs/discovery/tools/validate_discovery_docs.py +++ /dev/null @@ -1,172 +0,0 @@ -#!/usr/bin/env python3 -"""Validate the Odysseus public discovery documentation package.""" - -from __future__ import annotations - -import argparse -import json -import re -import sys -from pathlib import Path - -ROOT = Path(__file__).resolve().parents[1] -REPO_ROOT = ROOT.parents[1] -CATALOG_PATH = ROOT / "feature-catalog.json" -DOMAINS_DIR = ROOT / "domains" - -EXPECTED_COMMIT = "d8a2059df8e53bc7275c45339849d14c8651e73c" -ALLOWED_STATUSES = { - "verified", "partial", "disabled", "experimental", "legacy", - "dead-code-candidate", "unverified" -} - -LINE_RANGE_RE = re.compile(r"^L([1-9]\d*)-L([1-9]\d*)$") -FEATURE_HEADING_RE = re.compile( - r"^##\s+`?([A-Z][A-Z0-9]*-\d{3})`?\s+(?:—|-)\s+.+$", - re.MULTILINE, -) -LINK_RE = re.compile(r"(? argparse.Namespace: - parser = argparse.ArgumentParser(description="Validate Odysseus discovery docs.") - parser.add_argument( - "--repo-root", - type=Path, - default=REPO_ROOT, - help="Path to Odysseus repository root.", - ) - return parser.parse_args() - -def validate() -> int: - args = parse_args() - repo_root = args.repo_root.resolve() - errors: list[str] = [] - - # 1. Validate Catalog JSON existence and content - if not CATALOG_PATH.is_file(): - errors.append(f"Missing catalog file: {CATALOG_PATH}") - print(f"Errors: {len(errors)}") - for e in errors: - print(f"ERROR: {e}") - return 1 - - try: - catalog = json.loads(CATALOG_PATH.read_text(encoding="utf-8")) - except Exception as e: - errors.append(f"Failed to parse catalog JSON: {e}") - print(f"Errors: {len(errors)}") - for err in errors: - print(f"ERROR: {err}") - return 1 - - if not isinstance(catalog, list): - errors.append("feature-catalog.json must be a JSON array") - return 1 - - feature_ids = [item.get("id") for item in catalog if isinstance(item, dict)] - if len(feature_ids) != 79: - errors.append(f"Expected 79 unique feature IDs, found {len(feature_ids)}") - if len(feature_ids) != len(set(feature_ids)): - errors.append("Duplicate feature IDs found in catalog JSON") - - # Domain catalog counts - catalog_domain_counts: dict[str, int] = {} - for item in catalog: - if not isinstance(item, dict): - errors.append("Catalog item is not an object") - continue - fid = item.get("id", "") - status = item.get("status") - domain = item.get("domain", "").lower() - catalog_domain_counts[domain] = catalog_domain_counts.get(domain, 0) + 1 - - if status not in ALLOWED_STATUSES: - errors.append(f"{fid}: Invalid status '{status}'") - - evidence_list = item.get("evidence") - if not isinstance(evidence_list, list) or not evidence_list: - errors.append(f"{fid}: Missing or empty evidence list") - continue - - for ev in evidence_list: - if not isinstance(ev, dict): - errors.append(f"{fid}: Evidence item is not an object") - continue - path_str = ev.get("path") - lr_str = str(ev.get("line_range", "")) - if not path_str or Path(path_str).is_absolute() or ".." in Path(path_str).parts: - errors.append(f"{fid}: Unsafe or invalid path '{path_str}'") - continue - - # Check path exists in repo - target_path = repo_root / path_str - if not target_path.is_file(): - errors.append(f"{fid}: Referenced path '{path_str}' does not exist on disk") - continue - - # Check line range format & bounds - m = LINE_RANGE_RE.fullmatch(lr_str) - if not m: - errors.append(f"{fid}: Invalid line range format '{lr_str}' for path '{path_str}'") - continue - - start, end = int(m.group(1)), int(m.group(2)) - lines_cnt = len(target_path.read_text(encoding="utf-8", errors="ignore").splitlines()) - if start > end or end > lines_cnt or start < 1: - errors.append( - f"{fid}: Line range '{lr_str}' exceeds file length ({lines_cnt} lines) in '{path_str}'" - ) - - # 2. Check Domain Markdown files - md_feature_ids: list[str] = [] - domain_files = sorted(DOMAINS_DIR.glob("*.md")) - for df in domain_files: - domain_name = df.stem.lower() - content = df.read_text(encoding="utf-8") - found_ids = FEATURE_HEADING_RE.findall(content) - md_feature_ids.extend(found_ids) - if len(found_ids) != catalog_domain_counts.get(domain_name, 0): - errors.append( - f"Domain '{domain_name}' count mismatch: catalog has {catalog_domain_counts.get(domain_name, 0)}, Markdown has {len(found_ids)}" - ) - - if sorted(md_feature_ids) != sorted(feature_ids): - errors.append("Markdown domain feature IDs do not match catalog JSON feature IDs") - - # 3. Check for forbidden/private terms, sensitive credentials, and broken links across all docs - for md_file in ROOT.rglob("*.md"): - rel_md = md_file.relative_to(ROOT) - content = md_file.read_text(encoding="utf-8") - - # Forbidden terms scan - forbidden_matches = FORBIDDEN_TERMS_RE.findall(content) - if forbidden_matches: - errors.append( - f"{rel_md}: Found forbidden/internal terms: {set(forbidden_matches)}" - ) - - # Broken local link check - for target in LINK_RE.findall(content): - target = target.strip().strip("<>") - if not target or target.startswith(("#", "http://", "https://", "mailto:")): - continue - target_path = target.split("#", 1)[0] - resolved = (md_file.parent / target_path).resolve() - if not resolved.exists(): - errors.append(f"{rel_md}: Broken local link '{target}'") - - print(f"Catalog Features: {len(catalog)}") - print(f"Domain Files: {len(domain_files)}") - print(f"Validation Errors: {len(errors)}") - for err in errors: - print(f"ERROR: {err}") - - return 1 if errors else 0 - -if __name__ == "__main__": - sys.exit(validate()) diff --git a/docs/discovery/tools/validate_discovery_evidence.py b/docs/discovery/tools/validate_discovery_evidence.py deleted file mode 100644 index 4315478f7..000000000 --- a/docs/discovery/tools/validate_discovery_evidence.py +++ /dev/null @@ -1,1413 +0,0 @@ -#!/usr/bin/env python3 -"""Validate feature-catalog evidence locators against repository source. - -Evidence identity is resolved from a language parser where one is available. -The cited line range is treated as generated display metadata and checked only -after the locator has been resolved. -""" - -from __future__ import annotations - -import argparse -import ast -import difflib -import json -import re -import subprocess -import sys -from collections import Counter, defaultdict -from dataclasses import asdict, dataclass -from pathlib import Path -from typing import Any, Iterable - - -DISCOVERY_ROOT = Path(__file__).resolve().parents[1] -DEFAULT_REPO_ROOT = DISCOVERY_ROOT.parents[1] -DEFAULT_CATALOG = DISCOVERY_ROOT / "feature-catalog.json" - -RESULTS = { - "valid", - "invalid-path", - "invalid-locator", - "locator-outside-range", - "range-mismatch", - "ambiguous", - "unsupported", - "file-level-valid", -} - -PYTHON_KINDS = { - "python-function", - "python-method", - "python-class", - "python-variable", - "python-route", - "python-module", - "test-function", -} -JAVASCRIPT_KINDS = { - "javascript-function", - "javascript-class", - "javascript-export", - "javascript-event", -} -FILE_EXTENSIONS = { - ".html", - ".md", - ".swift", - ".yaml", - ".yml", -} -RANGE_RE = re.compile(r"^L([1-9]\d*)-L([1-9]\d*)$") -LEGACY_ROUTE_RE = re.compile( - r"^@(?P[A-Za-z_]\w*)\." - r"(?Pget|post|put|patch|delete|options|head)" - r"\(\s*['\"](?P[^'\"]*)['\"]\s*\)$", - re.IGNORECASE, -) -CANONICAL_ROUTE_RE = re.compile( - r"^(?PGET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s+" - r"(?P\S+)(?:\s+->\s+(?P[\w.]+))?$" -) -SHELL_FUNCTION_RE = re.compile( - r"^\s*(?:function\s+)?(?P[A-Za-z_]\w*)\s*" - r"(?:\(\s*\))?\s*\{" -) -MARKDOWN_HEADING_RE = re.compile(r"^(?P#{1,6})\s+(?P.+?)\s*$") - - -@dataclass(frozen=True) -class Located: - name: str - kind: str - start: int - end: int - details: dict[str, Any] | None = None - - @property - def line_range(self) -> str: - return f"L{self.start}-L{self.end}" - - -@dataclass -class Validation: - feature_id: str - evidence_index: int - current_evidence: dict[str, Any] - inferred_kind: str - locator: str | None - result: str - problem: str | None = None - resolved: Located | None = None - suggested_kind: str | None = None - suggested_locator: str | None = None - generated_line_range: str | None = None - confidence: str = "high" - manual_review_reason: str | None = None - - def to_dict(self) -> dict[str, Any]: - data = asdict(self) - data["resolved"] = asdict(self.resolved) if self.resolved else None - return data - - -def parse_args(argv: list[str] | None = None) -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Validate discovery evidence identities and line ranges." - ) - parser.add_argument("--repo-root", type=Path, default=DEFAULT_REPO_ROOT) - parser.add_argument("--catalog", type=Path, default=DEFAULT_CATALOG) - parser.add_argument( - "--output-dir", - type=Path, - default=DISCOVERY_ROOT, - help="Directory for the report and repair queue.", - ) - return parser.parse_args(argv) - - -def load_catalog(path: Path) -> list[dict[str, Any]]: - data = json.loads(path.read_text(encoding="utf-8")) - if not isinstance(data, list) or not all(isinstance(item, dict) for item in data): - raise ValueError("catalog must be an array of feature objects") - return data - - -def safe_target(repo_root: Path, raw_path: Any) -> Path | None: - if not isinstance(raw_path, str) or not raw_path: - return None - candidate = Path(raw_path) - if candidate.is_absolute() or ".." in candidate.parts: - return None - resolved = (repo_root / candidate).resolve() - try: - resolved.relative_to(repo_root.resolve()) - except ValueError: - return None - return resolved - - -def parse_range(raw: Any) -> tuple[int, int] | None: - if raw in (None, ""): - return None - match = RANGE_RE.fullmatch(str(raw)) - if not match: - return None - return int(match.group(1)), int(match.group(2)) - - -def extension_for(path: str) -> str: - suffix = Path(path).suffix.lower() - return suffix or "[none]" - - -def legacy_locator(evidence: dict[str, Any]) -> str | None: - value = evidence.get("locator", evidence.get("symbol")) - return value if isinstance(value, str) and value.strip() else None - - -def infer_kind(evidence: dict[str, Any], target: Path | None) -> str: - explicit = evidence.get("kind") - if isinstance(explicit, str) and explicit: - return explicit - - path = str(evidence.get("path", "")) - suffix = Path(path).suffix.lower() - locator = legacy_locator(evidence) or "" - - if suffix == ".py": - if LEGACY_ROUTE_RE.fullmatch(locator) or CANONICAL_ROUTE_RE.fullmatch(locator): - return "python-route" - if path.startswith("tests/") and locator.startswith("test"): - return "test-function" - if "." in locator and all(part.isidentifier() for part in locator.split(".")): - return "python-method" - if locator.isupper(): - return "python-variable" - if target and target.is_file(): - try: - tree = ast.parse(target.read_text(encoding="utf-8")) - except (OSError, SyntaxError, UnicodeError): - return "manual-review" - for node in ast.walk(tree): - if isinstance(node, ast.ClassDef) and node.name == locator: - return "python-class" - return "python-function" - if suffix == ".js": - if locator.startswith("on:") or locator.startswith("event:"): - return "javascript-event" - if locator and locator[:1].isupper(): - return "javascript-class" - return "javascript-function" - if suffix in {".sh", ".bash"}: - return "shell-function" - if suffix == ".md": - return "documentation-section" - if suffix in {".json", ".yaml", ".yml", ".toml", ".ini"}: - return "configuration-key" - if suffix in FILE_EXTENSIONS or Path(path).name in {"Dockerfile"}: - return "manual-review" - if not suffix: - return "manual-review" - return "manual-review" - - -class PythonIndex(ast.NodeVisitor): - """Collect Python declarations, assignments, and decorated routes.""" - - def __init__(self) -> None: - self.symbols: list[Located] = [] - self.routes: list[Located] = [] - self.scope: list[str] = [] - self.constants: dict[str, str] = {} - self.router_prefixes: dict[str, str | None] = {} - - @classmethod - def from_path(cls, path: Path) -> "PythonIndex": - index = cls() - tree = ast.parse(path.read_text(encoding="utf-8")) - index._collect_static_values(tree) - index.visit(tree) - return index - - def _collect_static_values(self, tree: ast.AST) -> None: - for node in getattr(tree, "body", []): - if isinstance(node, (ast.Assign, ast.AnnAssign)): - value = self._string_value(node.value) - targets = node.targets if isinstance(node, ast.Assign) else [node.target] - for target in targets: - if isinstance(target, ast.Name) and value is not None: - self.constants[target.id] = value - if isinstance(target, ast.Name) and isinstance( - node.value, ast.Call - ): - call_name = self._call_name(node.value.func) - if call_name.endswith("APIRouter"): - prefix = self._router_prefix(node.value) - self.router_prefixes[target.id] = prefix - - def _qualified(self, name: str) -> str: - return ".".join([*self.scope, name]) - - @staticmethod - def _call_name(node: ast.AST) -> str: - if isinstance(node, ast.Name): - return node.id - if isinstance(node, ast.Attribute): - return f"{PythonIndex._call_name(node.value)}.{node.attr}".strip(".") - return "" - - def _string_value(self, node: ast.AST | None) -> str | None: - if isinstance(node, ast.Constant) and isinstance(node.value, str): - return node.value - if isinstance(node, ast.Name): - return self.constants.get(node.id) - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add): - left = self._string_value(node.left) - right = self._string_value(node.right) - return left + right if left is not None and right is not None else None - return None - - def _keyword_string(self, call: ast.Call, name: str) -> str | None: - for keyword in call.keywords: - if keyword.arg == name: - return self._string_value(keyword.value) - return None - - def _router_prefix(self, call: ast.Call) -> str | None: - if not any(keyword.arg == "prefix" for keyword in call.keywords): - return "" - return self._keyword_string(call, "prefix") - - def visit_ClassDef(self, node: ast.ClassDef) -> None: - self.symbols.append( - Located( - self._qualified(node.name), - "python-class", - node.lineno, - node.end_lineno or node.lineno, - ) - ) - self.scope.append(node.name) - self.generic_visit(node) - self.scope.pop() - - def visit_FunctionDef(self, node: ast.FunctionDef) -> None: - self._visit_function(node) - - def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None: - self._visit_function(node) - - def _visit_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: - qualified = self._qualified(node.name) - parent_is_class = bool( - self.scope - and any( - item.kind == "python-class" and item.name == ".".join(self.scope) - for item in self.symbols - ) - ) - kind = ( - "test-function" - if node.name.startswith("test") - else "python-method" - if parent_is_class - else "python-function" - ) - located = Located( - qualified, kind, node.lineno, node.end_lineno or node.lineno - ) - self.symbols.append(located) - self._collect_routes(node, qualified) - self.scope.append(node.name) - self.generic_visit(node) - self.scope.pop() - - def _collect_routes( - self, node: ast.FunctionDef | ast.AsyncFunctionDef, qualified: str - ) -> None: - methods = {"get", "post", "put", "patch", "delete", "options", "head"} - for decorator in node.decorator_list: - if not isinstance(decorator, ast.Call) or not isinstance( - decorator.func, ast.Attribute - ): - continue - method = decorator.func.attr.lower() - if method not in methods: - continue - router = self._call_name(decorator.func.value) - local_path = ( - self._string_value(decorator.args[0]) if decorator.args else None - ) - if local_path is None: - continue - prefix = self.router_prefixes.get(router) - full_path = None if router in self.router_prefixes and prefix is None else ( - f"{prefix or ''}{local_path}" - ) - details = { - "method": method.upper(), - "path": local_path, - "full_path": full_path, - "function": qualified, - "router": router, - "router_prefix": prefix, - "prefix_resolved": full_path is not None, - } - route_name = f"{method.upper()} {full_path or local_path} -> {qualified}" - self.routes.append( - Located( - route_name, - "python-route", - node.lineno, - node.end_lineno or node.lineno, - details, - ) - ) - - def visit_Assign(self, node: ast.Assign) -> None: - if isinstance(node.value, ast.Call): - call_name = self._call_name(node.value.func) - if call_name.endswith("APIRouter"): - for target in node.targets: - if isinstance(target, ast.Name): - self.router_prefixes[target.id] = self._router_prefix(node.value) - for target in node.targets: - self._collect_assignment(target, node) - self.generic_visit(node) - - def visit_AnnAssign(self, node: ast.AnnAssign) -> None: - self._collect_assignment(node.target, node) - self.generic_visit(node) - - def _collect_assignment(self, target: ast.AST, node: ast.AST) -> None: - if isinstance(target, (ast.Tuple, ast.List)): - for item in target.elts: - self._collect_assignment(item, node) - return - if not isinstance(target, ast.Name): - return - self.symbols.append( - Located( - self._qualified(target.id), - "python-variable", - node.lineno, - node.end_lineno or node.lineno, - ) - ) - - -NODE_PARSER = r""" -const fs = require("fs"); -const parserName = process.argv[1]; -const parserEntry = process.argv[2]; -const filename = process.argv[3]; -const source = fs.readFileSync(filename, "utf8"); -const parser = require(parserEntry); -let tree; -if (parserName === "@babel/parser") { - tree = parser.parse(source, { - sourceType: "unambiguous", - plugins: ["jsx", "classProperties", "optionalChaining", "topLevelAwait"] - }); -} else if (parserName === "typescript") { - throw new Error("typescript parser normalization is not implemented"); -} else { - const parse = parser.parse || parser.Parser?.parse; - tree = parse.call(parser, source, { - ecmaVersion: "latest", sourceType: "module", locations: true - }); -} -const out = []; -function loc(node) { - const l = node.loc; - return {start: l.start.line, end: l.end.line}; -} -function add(name, kind, node, extra={}) { - if (!name || !node.loc) return; - out.push({name, kind, ...loc(node), details: extra}); -} -function walk(node, scope=[], exported=false) { - if (!node || typeof node !== "object") return; - if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") { - if (node.declaration) walk(node.declaration, scope, true); - return; - } - if (node.type === "FunctionDeclaration") { - const name = node.id?.name; - add([...scope, name].filter(Boolean).join("."), exported ? "javascript-export" : "javascript-function", node); - scope = [...scope, name].filter(Boolean); - } else if (node.type === "ClassDeclaration") { - const name = node.id?.name; - add([...scope, name].filter(Boolean).join("."), exported ? "javascript-export" : "javascript-class", node); - scope = [...scope, name].filter(Boolean); - } else if (node.type === "MethodDefinition" || node.type === "ClassMethod") { - const name = node.key?.name || node.key?.value; - add([...scope, name].filter(Boolean).join("."), "javascript-function", node); - scope = [...scope, name].filter(Boolean); - } else if (node.type === "VariableDeclarator" && node.id?.type === "Identifier") { - const initType = node.init?.type || ""; - const isFunction = initType === "ArrowFunctionExpression" || initType === "FunctionExpression"; - if (isFunction || exported) { - add([...scope, node.id.name].join("."), exported ? "javascript-export" : "javascript-function", node); - } - } else if (node.type === "CallExpression" && node.callee?.type === "MemberExpression" - && node.callee.property?.name === "addEventListener" - && ["Literal", "StringLiteral"].includes(node.arguments?.[0]?.type)) { - add("event:" + node.arguments[0].value, "javascript-event", node, {event: node.arguments[0].value}); - } - for (const [key, value] of Object.entries(node)) { - if (key === "loc" || key === "start" || key === "end") continue; - if (Array.isArray(value)) value.forEach(child => walk(child, scope, exported)); - else if (value && typeof value === "object") walk(value, scope, exported); - } -} -walk(tree.program || tree); -process.stdout.write(JSON.stringify(out)); -""" - - -class JavascriptParser: - CANDIDATES = ("acorn", "espree", "@babel/parser", "typescript") - - def __init__(self, repo_root: Path) -> None: - self.repo_root = repo_root.resolve() - self.node = self._node_binary() - self.parser_name, self.parser_entry, self.reason = self._find_parser() - - @staticmethod - def _node_binary() -> str | None: - from shutil import which - - return which("node") - - def _package_roots(self) -> list[tuple[Path, set[str]]]: - roots: list[tuple[Path, set[str]]] = [] - for package in self.repo_root.rglob("package.json"): - if "node_modules" in package.parts: - continue - try: - data = json.loads(package.read_text(encoding="utf-8")) - except (OSError, ValueError): - continue - declared: set[str] = set() - for key in ("dependencies", "devDependencies", "peerDependencies"): - values = data.get(key) - if isinstance(values, dict): - declared.update(values) - roots.append((package.parent, declared)) - return roots - - def _find_parser(self) -> tuple[str | None, str | None, str]: - if not self.node: - return None, None, "Node.js is unavailable" - package_roots = self._package_roots() - for name in self.CANDIDATES: - for package_root, declared in package_roots: - local = package_root / "node_modules" / name - if name not in declared and not local.exists(): - continue - probe = subprocess.run( - [ - self.node, - "-e", - ( - "const p=require.resolve(process.argv[1]," - "{paths:[process.argv[2]]});process.stdout.write(p)" - ), - name, - str(package_root), - ], - cwd=package_root, - text=True, - capture_output=True, - check=False, - ) - if probe.returncode: - continue - resolved = Path(probe.stdout).resolve() - try: - resolved.relative_to(self.repo_root) - except ValueError: - continue - if name == "typescript": - return None, None, ( - "TypeScript is repository-local but its AST " - "normalization is not implemented" - ) - return name, str(resolved), f"repository-local parser: {name}" - return None, None, ( - "no repository-local Acorn, Espree, Babel parser, or supported " - "TypeScript parser is available" - ) - - @property - def supported(self) -> bool: - return bool(self.node and self.parser_name) - - def parse(self, path: Path) -> list[Located]: - if not self.supported: - raise RuntimeError(self.reason) - process = subprocess.run( - [ - self.node or "node", - "-e", - NODE_PARSER, - self.parser_name or "", - self.parser_entry or "", - str(path), - ], - cwd=self.repo_root, - text=True, - capture_output=True, - check=False, - ) - if process.returncode: - raise RuntimeError(process.stderr.strip() or "JavaScript parser failed") - return [Located(**item) for item in json.loads(process.stdout)] - - -def choose_symbol( - symbols: Iterable[Located], locator: str -) -> tuple[Located | None, list[Located]]: - candidates = list(symbols) - exact = [item for item in candidates if item.name == locator] - if not exact and "." not in locator: - exact = [item for item in candidates if item.name.split(".")[-1] == locator] - return (exact[0] if len(exact) == 1 else None), exact - - -def apply_range_result( - validation: Validation, located: Located, cited: tuple[int, int] | None -) -> Validation: - validation.resolved = located - validation.generated_line_range = located.line_range - validation.suggested_locator = located.name - validation.suggested_kind = located.kind - if cited is None: - if validation.current_evidence.get("line_range") not in (None, ""): - validation.result = "range-mismatch" - validation.problem = "line_range is malformed" - else: - validation.result = "valid" - return validation - start, end = cited - if start <= located.start <= end: - # Evidence ranges are excerpts. They must contain the declaration that - # gives the locator its identity, but need not contain the whole body. - validation.result = "valid" - else: - validation.result = "locator-outside-range" - validation.problem = ( - f"locator declaration at L{located.start} is outside cited " - f"L{start}-L{end}; parser-derived span is {located.line_range}" - ) - return validation - - -def suggest_nearby( - symbols: list[Located], locator: str, cited: tuple[int, int] | None -) -> tuple[Located | None, str]: - if cited: - overlapping = [ - item - for item in symbols - if cited[0] <= item.start <= cited[1] - or item.start <= cited[0] <= item.end - ] - if len(overlapping) == 1: - return overlapping[0], "high" - leaf_matches = [ - item - for item in overlapping - if item.name.split(".")[-1] == locator.split(".")[-1] - ] - if len(leaf_matches) == 1: - return leaf_matches[0], "high" - names = [item.name for item in symbols] - close = difflib.get_close_matches(locator, names, n=1, cutoff=0.72) - if close: - return next(item for item in symbols if item.name == close[0]), "medium" - return None, "low" - - -def validate_python( - validation: Validation, target: Path, cited: tuple[int, int] | None -) -> Validation: - try: - index = PythonIndex.from_path(target) - except (OSError, UnicodeError, SyntaxError) as exc: - validation.result = "unsupported" - validation.problem = f"Python AST parsing failed: {exc}" - validation.manual_review_reason = validation.problem - return validation - - locator = validation.locator or "" - if validation.inferred_kind == "python-module": - if locator: - validation.result = "invalid-locator" - validation.problem = "python-module evidence must not name a symbol" - else: - validation.result = "file-level-valid" - validation.suggested_kind = "python-module" - return validation - - if validation.inferred_kind == "python-route": - return validate_route(validation, index.routes, cited) - - located, exact = choose_symbol(index.symbols, locator) - if len(exact) > 1: - validation.result = "ambiguous" - validation.problem = f"locator matches {len(exact)} declarations" - validation.manual_review_reason = validation.problem - validation.confidence = "low" - return validation - if not located: - validation.result = "invalid-locator" - validation.problem = f"Python locator {locator!r} does not exist" - suggestion, confidence = suggest_nearby(index.symbols, locator, cited) - validation.confidence = confidence - if suggestion: - validation.suggested_kind = suggestion.kind - validation.suggested_locator = suggestion.name - validation.generated_line_range = suggestion.line_range - return validation - return apply_range_result(validation, located, cited) - - -def validate_route( - validation: Validation, routes: list[Located], cited: tuple[int, int] | None -) -> Validation: - locator = validation.locator or "" - legacy = LEGACY_ROUTE_RE.fullmatch(locator) - canonical = CANONICAL_ROUTE_RE.fullmatch(locator) - if not legacy and not canonical: - validation.result = "invalid-locator" - validation.problem = ( - "route locator must be '@router.method(\"/path\")' or " - "'METHOD /path -> qualified.function'" - ) - return validation - expected_method = (legacy or canonical).group("method").upper() - expected_path = (legacy or canonical).group("path") - expected_function = canonical.group("function") if canonical else None - method_matches = [ - route - for route in routes - if route.details and route.details["method"] == expected_method - ] - path_matches = [ - route - for route in method_matches - if route.details - and expected_path in {route.details["path"], route.details["full_path"]} - ] - if not method_matches: - validation.result = "invalid-locator" - validation.problem = f"HTTP method {expected_method} is not declared in this file" - return validation - if not path_matches: - validation.result = "invalid-locator" - validation.problem = ( - f"route path {expected_path!r} is not declared for {expected_method}" - ) - return validation - if expected_function: - function_matches = [ - route - for route in path_matches - if route.details and route.details["function"] == expected_function - ] - if not function_matches: - validation.result = "invalid-locator" - validation.problem = ( - f"route exists but containing function is not {expected_function!r}" - ) - return validation - path_matches = function_matches - if len(path_matches) > 1: - validation.result = "ambiguous" - validation.problem = f"route locator matches {len(path_matches)} functions" - validation.manual_review_reason = validation.problem - validation.confidence = "low" - return validation - route = path_matches[0] - details = route.details or {} - if details.get("full_path") is None and expected_path != details.get("path"): - validation.result = "ambiguous" - validation.problem = "final router prefix cannot be resolved statically" - validation.manual_review_reason = validation.problem - validation.resolved = route - validation.generated_line_range = route.line_range - validation.suggested_locator = route.name - validation.suggested_kind = "python-route" - validation.confidence = "medium" - return validation - return apply_range_result(validation, route, cited) - - -def validate_javascript( - validation: Validation, - target: Path, - cited: tuple[int, int] | None, - parser: JavascriptParser, -) -> Validation: - if not parser.supported: - validation.result = "unsupported" - validation.problem = parser.reason - validation.manual_review_reason = parser.reason - validation.confidence = "low" - return validation - try: - symbols = parser.parse(target) - except (OSError, RuntimeError, ValueError, json.JSONDecodeError) as exc: - validation.result = "unsupported" - validation.problem = f"JavaScript parser failed: {exc}" - validation.manual_review_reason = validation.problem - validation.confidence = "low" - return validation - locator = validation.locator or "" - located, exact = choose_symbol(symbols, locator) - if len(exact) > 1: - validation.result = "ambiguous" - validation.problem = f"locator matches {len(exact)} JavaScript declarations" - validation.manual_review_reason = validation.problem - validation.confidence = "low" - return validation - if not located: - validation.result = "invalid-locator" - validation.problem = f"JavaScript locator {locator!r} does not exist" - suggestion, confidence = suggest_nearby(symbols, locator, cited) - validation.confidence = confidence - if suggestion: - validation.suggested_kind = suggestion.kind - validation.suggested_locator = suggestion.name - validation.generated_line_range = suggestion.line_range - return validation - return apply_range_result(validation, located, cited) - - -def shell_functions(path: Path) -> list[Located]: - lines = path.read_text(encoding="utf-8").splitlines() - functions: list[Located] = [] - for index, line in enumerate(lines): - match = SHELL_FUNCTION_RE.match(line) - if not match: - continue - depth = 0 - end = index + 1 - for offset in range(index, len(lines)): - code = lines[offset].split("#", 1)[0] - depth += code.count("{") - code.count("}") - end = offset + 1 - if depth <= 0: - break - functions.append( - Located(match.group("name"), "shell-function", index + 1, end) - ) - return functions - - -def validate_shell( - validation: Validation, target: Path, cited: tuple[int, int] | None -) -> Validation: - try: - check = subprocess.run( - ["bash", "-n", str(target)], text=True, capture_output=True, check=False - ) - except OSError as exc: - validation.result = "unsupported" - validation.problem = f"bash unavailable: {exc}" - return validation - if check.returncode: - validation.result = "unsupported" - validation.problem = f"shell syntax check failed: {check.stderr.strip()}" - return validation - functions = shell_functions(target) - located, exact = choose_symbol(functions, validation.locator or "") - if len(exact) > 1: - validation.result = "ambiguous" - validation.problem = "shell function locator is duplicated" - validation.manual_review_reason = validation.problem - return validation - if not located: - validation.result = "invalid-locator" - validation.problem = f"shell function {validation.locator!r} does not exist" - return validation - return apply_range_result(validation, located, cited) - - -def validate_documentation( - validation: Validation, target: Path, cited: tuple[int, int] | None -) -> Validation: - locator = validation.locator or "" - if not locator: - validation.result = "invalid-locator" - validation.problem = "documentation-section requires a heading locator" - return validation - lines = target.read_text(encoding="utf-8").splitlines() - headings: list[Located] = [] - for number, line in enumerate(lines, 1): - match = MARKDOWN_HEADING_RE.match(line) - if not match: - continue - level = len(match.group("marks")) - end = len(lines) - for later, later_line in enumerate(lines[number:], number + 1): - later_match = MARKDOWN_HEADING_RE.match(later_line) - if later_match and len(later_match.group("marks")) <= level: - end = later - 1 - break - headings.append( - Located( - match.group("title").strip(), - "documentation-section", - number, - end, - ) - ) - located, exact = choose_symbol(headings, locator) - if len(exact) > 1: - validation.result = "ambiguous" - validation.problem = "documentation heading is duplicated" - validation.manual_review_reason = validation.problem - return validation - if not located: - validation.result = "invalid-locator" - validation.problem = f"documentation heading {locator!r} does not exist" - return validation - return apply_range_result(validation, located, cited) - - -def validate_evidence( - repo_root: Path, - feature_id: str, - index: int, - evidence: dict[str, Any], - javascript_parser: JavascriptParser, -) -> Validation: - target = safe_target(repo_root, evidence.get("path")) - kind = infer_kind(evidence, target) - locator = legacy_locator(evidence) - validation = Validation( - feature_id=feature_id, - evidence_index=index, - current_evidence=evidence, - inferred_kind=kind, - locator=locator, - result="ambiguous", - ) - if target is None or not target.is_file(): - validation.result = "invalid-path" - validation.problem = f"evidence path is unsafe or missing: {evidence.get('path')!r}" - validation.confidence = "high" - return validation - - cited = parse_range(evidence.get("line_range")) - explicit_range = evidence.get("line_range") - if explicit_range not in (None, "") and cited is None: - validation.result = "range-mismatch" - validation.problem = f"malformed line_range: {explicit_range!r}" - return validation - if cited is not None: - line_count = len( - target.read_text(encoding="utf-8", errors="replace").splitlines() - ) - if cited[0] > cited[1] or cited[1] > max(line_count, 1): - validation.result = "range-mismatch" - validation.problem = ( - f"line_range L{cited[0]}-L{cited[1]} is outside the " - f"{line_count}-line file" - ) - return validation - - if kind == "file": - if locator: - validation.result = "invalid-locator" - validation.problem = "file evidence must not include a locator/symbol" - validation.suggested_kind = "file" - validation.confidence = "high" - return validation - validation.result = "file-level-valid" - validation.suggested_kind = "file" - line_count = len(target.read_text(encoding="utf-8", errors="replace").splitlines()) - validation.generated_line_range = f"L1-L{max(line_count, 1)}" - return validation - - if not locator and kind not in {"python-module", "file"}: - validation.result = "invalid-locator" - validation.problem = f"{kind} evidence requires a locator" - return validation - - if kind in PYTHON_KINDS: - return validate_python(validation, target, cited) - if kind in JAVASCRIPT_KINDS: - return validate_javascript(validation, target, cited, javascript_parser) - if kind == "shell-function": - return validate_shell(validation, target, cited) - if kind == "documentation-section": - return validate_documentation(validation, target, cited) - if kind == "configuration-key": - validation.result = "ambiguous" - validation.problem = "configuration-key requires a format-aware parser" - validation.manual_review_reason = ( - "This validator does not guess configuration keys from text." - ) - validation.suggested_kind = ( - "file" if evidence.get("kind") is None else "manual-review" - ) - validation.confidence = "low" - return validation - - validation.result = "ambiguous" - validation.problem = f"no automatic resolver for inferred kind {kind!r}" - validation.manual_review_reason = ( - "Use explicit file evidence if the whole file is authoritative, " - "or manual-review with a precise reason." - ) - if evidence.get("kind") is None: - validation.suggested_kind = "file" - validation.suggested_locator = None - line_count = len(target.read_text(encoding="utf-8", errors="replace").splitlines()) - validation.generated_line_range = f"L1-L{max(line_count, 1)}" - validation.confidence = "medium" - return validation - - -def proposed_schema() -> dict[str, Any]: - return { - "required": ["path", "kind", "explanation"], - "optional": ["locator", "line_range"], - "properties": { - "path": "repository-relative file path", - "kind": sorted( - PYTHON_KINDS - | JAVASCRIPT_KINDS - | { - "shell-function", - "configuration-key", - "file", - "documentation-section", - "manual-review", - } - ), - "locator": ( - "parser-resolvable identity; omit for file evidence. " - "Routes use 'METHOD /path -> qualified.function'." - ), - "line_range": ( - "optional generated display metadata in Lx-Ly form; " - "never the primary identity" - ), - "explanation": "why this evidence supports the feature claim", - }, - "examples": [ - { - "path": "src/chat_handler.py", - "kind": "python-method", - "locator": "ChatHandler.preprocess_message", - "explanation": "Preprocesses attachments and URLs for chat requests.", - }, - { - "path": "Dockerfile", - "kind": "file", - "explanation": "Defines the container build.", - }, - ], - } - - -def assess_e2( - features: list[dict[str, Any]], validations: list[Validation] -) -> list[dict[str, Any]]: - """Record the audit's claim-relevance review for the ten frozen E2 records.""" - by_key = { - (item.feature_id, item.current_evidence.get("path"), item.locator): item - for item in validations - } - decisions = { - "CHAT-001": ( - "yes", - "Both cited tests exercise documented chat-stream behavior: emitted " - "stream metrics and non-destructive resend behavior.", - "retain", - ), - "MODEL-006": ( - "yes", - "The Node-backed test exercises the Copilot device-flow start/poll " - "contract and complete verification URI.", - "retain", - ), - "MODEL-007": ( - "yes", - "The Node-backed test exercises the ChatGPT subscription device-flow " - "contract and verification URI.", - "retain", - ), - "RESEARCH-003": ( - "partial", - "The test covers result ranking only, not SearXNG connectivity or " - "multi-provider dispatch in the feature claim.", - "demote", - ), - "DOCUMENT-002": ( - "no", - "The test covers removal of a PDF content marker, not PDF form " - "processing or high-fidelity rendering.", - "demote", - ), - "EMAIL-001": ( - "partial", - "The test covers health probing of account connections, not account " - "setup, SMTP behavior, or inbox polling.", - "demote", - ), - "SECURITY-002": ( - "yes", - "Relevant vault password-handling tests exist, but the cited test " - "function name is fabricated.", - "demote", - ), - "SECURITY-004": ( - "yes", - "Relevant prompt-injection tests exist, but both cited locator names " - "are fabricated umbrella names.", - "demote", - ), - "SECURITY-005": ( - "yes", - "Relevant URL and path confinement tests exist, but all three cited " - "locator names are fabricated umbrella names.", - "demote", - ), - "PLATFORM-009": ( - "manual-review", - "The shell diagnostic is executable evidence, but no automated test " - "function or test-suite evidence is cited and the script uses a " - "file-like fabricated symbol.", - "demote", - ), - } - assessments: list[dict[str, Any]] = [] - for feature in features: - if feature.get("evidence_maturity") != "E2": - continue - test_evidence = [ - evidence - for evidence in feature.get("evidence", []) - if str(evidence.get("path", "")).startswith("tests/") - ] - cited: list[dict[str, Any]] = [] - for evidence in test_evidence: - item = by_key.get( - (feature["id"], evidence.get("path"), legacy_locator(evidence)) - ) - cited.append( - { - "path": evidence.get("path"), - "locator": legacy_locator(evidence), - "exists": bool( - item - and item.result - not in {"invalid-path", "invalid-locator", "unsupported"} - ), - "validation_result": item.result if item else "not-validated", - } - ) - relevance, reason, decision = decisions[feature["id"]] - assessments.append( - { - "feature_id": feature["id"], - "feature_name": feature["name"], - "cited_test_evidence": cited, - "test_function_exists": ( - all(item["exists"] for item in cited) if cited else False - ), - "direct_support": relevance, - "support_reason": reason, - "suggested_decision": decision, - } - ) - return assessments - - -def build_report( - repo_root: Path, - catalog_path: Path, - features: list[dict[str, Any]], - validations: list[Validation], - javascript_parser: JavascriptParser, -) -> dict[str, Any]: - extensions = Counter() - kinds = Counter() - for item in validations: - extensions[extension_for(str(item.current_evidence.get("path", "")))] += 1 - kinds[item.inferred_kind] += 1 - results = Counter(item.result for item in validations) - affected = sorted( - { - item.feature_id - for item in validations - if item.result not in {"valid", "file-level-valid"} - } - ) - invalid_results = { - "invalid-path", - "invalid-locator", - "locator-outside-range", - "range-mismatch", - } - invalid_features = sorted( - { - item.feature_id - for item in validations - if item.result in invalid_results - } - ) - ambiguous = [ - item.to_dict() - for item in validations - if item.result in {"ambiguous", "unsupported"} - ] - return { - "audit": { - "repo_root": str(repo_root), - "catalog": str(catalog_path), - "feature_count": len(features), - "evidence_count": len(validations), - "javascript_parser": { - "supported": javascript_parser.supported, - "parser": javascript_parser.parser_name, - "reason": javascript_parser.reason, - }, - }, - "inventory": { - "total_evidence_items": len(validations), - "by_extension": dict(sorted(extensions.items())), - "by_inferred_evidence_type": dict(sorted(kinds.items())), - "features_with_invalid_or_unresolved_entries": affected, - "features_with_invalid_entries": invalid_features, - "invalid_feature_count": len(invalid_features), - "affected_feature_count": len(affected), - "ambiguous_or_unsupported_entries": ambiguous, - }, - "result_counts": {name: results.get(name, 0) for name in sorted(RESULTS)}, - "summary_counts": { - "valid": results["valid"] + results["file-level-valid"], - "invalid": sum( - results[name] - for name in ( - "invalid-path", - "invalid-locator", - "locator-outside-range", - "range-mismatch", - ) - ), - "ambiguous": results["ambiguous"], - "unsupported": results["unsupported"], - }, - "evidence_model": proposed_schema(), - "e2_assessment": assess_e2(features, validations), - "entries": [item.to_dict() for item in validations], - } - - -def build_repair_queue(validations: list[Validation]) -> dict[str, Any]: - grouped: dict[str, list[dict[str, Any]]] = defaultdict(list) - for item in validations: - if item.result in {"valid", "file-level-valid"}: - continue - grouped[item.feature_id].append( - { - "evidence_index": item.evidence_index, - "current_evidence": item.current_evidence, - "detected_problem": item.problem or item.result, - "result": item.result, - "suggested_kind": item.suggested_kind, - "suggested_locator": item.suggested_locator, - "generated_line_range": item.generated_line_range, - "confidence": item.confidence, - "manual_review_reason": item.manual_review_reason, - } - ) - return { - "feature_count": len(grouped), - "defect_count": sum(len(items) for items in grouped.values()), - "features": [ - {"feature_id": feature_id, "defects": grouped[feature_id]} - for feature_id in sorted(grouped) - ], - } - - -def render_markdown(report: dict[str, Any]) -> str: - inventory = report["inventory"] - summary = report["summary_counts"] - lines = [ - "# Discovery evidence validation report", - "", - "This report is machine-generated. The catalog was not edited.", - "", - "## Summary", - "", - f"- Features: {report['audit']['feature_count']}", - f"- Evidence items: {report['audit']['evidence_count']}", - f"- Valid (including file-level): {summary['valid']}", - f"- Invalid: {summary['invalid']}", - f"- Ambiguous: {summary['ambiguous']}", - f"- Unsupported: {summary['unsupported']}", - f"- Affected features: {inventory['affected_feature_count']}", - "", - "## Evidence by extension", - "", - "| Extension | Items |", - "|---|---:|", - ] - lines.extend( - f"| `{extension}` | {count} |" - for extension, count in inventory["by_extension"].items() - ) - lines.extend( - [ - "", - "## Evidence by inferred kind", - "", - "| Kind | Items |", - "|---|---:|", - ] - ) - lines.extend( - f"| `{kind}` | {count} |" - for kind, count in inventory["by_inferred_evidence_type"].items() - ) - lines.extend( - [ - "", - "## Result categories", - "", - "| Result | Items |", - "|---|---:|", - ] - ) - lines.extend( - f"| `{result}` | {count} |" - for result, count in report["result_counts"].items() - ) - lines.extend( - [ - "", - "## JavaScript parser", - "", - report["audit"]["javascript_parser"]["reason"] + ".", - "", - "## Affected features", - "", - ", ".join( - f"`{feature_id}`" - for feature_id in inventory[ - "features_with_invalid_or_unresolved_entries" - ] - ) - or "None.", - "", - "## E2 relevance review", - "", - "| Feature | Cited test evidence | Exists | Direct support | Decision |", - "|---|---|---:|---|---|", - ] - ) - for item in report["e2_assessment"]: - citations = "<br>".join( - f"`{test['path']}:{test['locator']}`" - for test in item["cited_test_evidence"] - ) or "None" - lines.append( - f"| `{item['feature_id']}` | {citations} | " - f"{'yes' if item['test_function_exists'] else 'no'} | " - f"{item['direct_support']}: {item['support_reason']} | " - f"**{item['suggested_decision']}** |" - ) - lines.extend( - [ - "", - "### Numerical inconsistency resolved", - "", - "The six problematic cited E2 test records are the six nonexistent " - "locator names in SECURITY-002 (one), SECURITY-004 (two), and " - "SECURITY-005 (three). Seven feature demotions were suggested because " - "PLATFORM-009 is an additional feature-level maturity problem: it " - "cites no automated test function. The other three demotions are " - "claim-relevance findings for existing tests, so the figures measure " - "different things and should not be compared as the same denominator.", - "", - "## Proposed evidence schema", - "", - "Required: `path`, `kind`, and `explanation`. `locator` is required " - "for symbol/route/section evidence and omitted for `file`. " - "`line_range` is optional generated metadata.", - "", - "```json", - json.dumps(report["evidence_model"]["examples"], indent=2), - "```", - "", - "## Defects", - "", - "| Feature | Path | Locator | Result | Generated range | Problem |", - "|---|---|---|---|---|---|", - ] - ) - for item in report["entries"]: - if item["result"] in {"valid", "file-level-valid"}: - continue - evidence = item["current_evidence"] - problem = (item["problem"] or "").replace("|", "\\|") - lines.append( - f"| `{item['feature_id']}` | `{evidence.get('path', '')}` | " - f"`{item.get('locator') or ''}` | `{item['result']}` | " - f"`{item.get('generated_line_range') or ''}` | {problem} |" - ) - return "\n".join(lines) + "\n" - - -def write_outputs(output_dir: Path, report: dict[str, Any], queue: dict[str, Any]) -> None: - output_dir.mkdir(parents=True, exist_ok=True) - (output_dir / "evidence-validation-report.json").write_text( - json.dumps(report, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" - ) - (output_dir / "evidence-validation-report.md").write_text( - render_markdown(report), encoding="utf-8" - ) - (output_dir / "evidence-repair-queue.json").write_text( - json.dumps(queue, indent=2, ensure_ascii=False) + "\n", encoding="utf-8" - ) - - -def run(argv: list[str] | None = None) -> int: - args = parse_args(argv) - repo_root = args.repo_root.resolve() - catalog_path = args.catalog.resolve() - features = load_catalog(catalog_path) - javascript_parser = JavascriptParser(repo_root) - validations = [ - validate_evidence( - repo_root, - str(feature.get("id", f"<feature:{feature_index}>")), - evidence_index, - evidence, - javascript_parser, - ) - for feature_index, feature in enumerate(features) - for evidence_index, evidence in enumerate(feature.get("evidence", [])) - if isinstance(evidence, dict) - ] - report = build_report( - repo_root, catalog_path, features, validations, javascript_parser - ) - queue = build_repair_queue(validations) - write_outputs(args.output_dir.resolve(), report, queue) - print(json.dumps(report["summary_counts"], sort_keys=True)) - return 1 if queue["defect_count"] else 0 - - -if __name__ == "__main__": - sys.exit(run()) diff --git a/specs/discovery/README.md b/specs/discovery/README.md new file mode 100644 index 000000000..faec5ed36 --- /dev/null +++ b/specs/discovery/README.md @@ -0,0 +1,30 @@ +# Discovery guide + +Discovery keeps a shared maintainer understanding of how the checked-in Odysseus code works without becoming a permanent audit system. It is a compact companion to the code for cross-cutting systems, not a feature certification or a substitute for normal testing. + +## Working model + +- Code is the ground truth for current behaviour. Trace current paths in source before recording a claim. +- These specs record current code-grounded behaviour and system boundaries only. They do not record accepted intent, design direction, refactor plans, or decision history. +- Keep this package in sync with code changes that alter a documented cross-cutting system, its authority boundary, or its canonical implementation location. +- Do not exhaustively revalidate existing functionality simply because it appears in a map. Investigate when there is a report, normal use visibly fails, a change affects the area, or a high-authority boundary needs review. +- Record a confirmed, actionable problem in the relevant project within the canonical Plane workspace. Keep high-level design, prioritization, ownership, and refactor discussion in Plane. Do not create parallel issue lists here. +- After implementation changes the code, update the relevant map to describe the resulting current state. +- Review safety-sensitive authority first: execution, data access, external tools, credentials, destructive operations, and unattended work. +- Cite stable source locations such as modules, routes, classes, and functions. Avoid fragile line ranges and generated evidence tables. + +## Package + +- [Current system map](system-map.md) describes the code-grounded subsystem boundaries. +- [Safety boundaries](safety-boundaries.md) records where review effort is most valuable. + +The existing [architecture runtime inventory](../architecture-runtime-inventory.md) remains useful structural context. It is explicitly a draft snapshot; re-check its measurements against the code before using them for an implementation decision. + +## How to use this package + +1. Start at the subsystem in the system map and confirm the relevant source. +2. For a defect, create or update the relevant project within the canonical Plane workspace with a reproducible report and ownership. +3. For a design, prioritization, ownership, or refactor question, use a Plane thread. +4. Make and validate the implementation through the normal engineering workflow, then update this package if the code changed a documented system boundary. + +This package intentionally has no generator, validator, maturity scale, feature database, or duplicate work tracker. diff --git a/specs/discovery/safety-boundaries.md b/specs/discovery/safety-boundaries.md new file mode 100644 index 000000000..69b966c1b --- /dev/null +++ b/specs/discovery/safety-boundaries.md @@ -0,0 +1,129 @@ +# Safety boundaries + +This document prioritizes review of broad authority. It records code-grounded safeguards and questions; it does not declare a vulnerability merely because a feature is powerful. No destructive test, external connection, or real credential was used for this map. + +## Shell and subprocess execution + +**Boundary.** Shell routes, agent bash and python tools, local model serving, and detached background jobs. + +**Available authority.** Commands run as the application process user and can create child processes. + +**User-controlled inputs.** Direct shell requests, model-produced tool arguments, scheduled-task prompts, and model-serving configuration. + +**Current safeguards.** Agent dispatch applies owner/admin checks and tool policy; process helpers use timeouts or bounded background-job lifecycle where implemented. + +**Confirmed risks or gaps.** Intentional authority with a confirmed gap: the agent shell starts in its workspace but is not sandboxed to it, and has no egress sandbox. This is documented in source and the threat model; it is not a newly demonstrated bypass. + +**Required verification.** Role-gate and disabled-tool outcomes, direct shell-route behaviour, and timeout, cancellation, and output handling for foreground and detached processes remain unverified. + +## Filesystem access and workspace confinement + +**Boundary.** Agent read, write, patch, listing, glob, and grep tools. + +**Available authority.** Read and modify files within active workspace confinement or fallback allowlisted roots. + +**User-controlled inputs.** Tool paths, patches, file contents, search patterns, and workspace selection passed into the tool dispatcher. + +**Current safeguards.** src/tool_execution.py resolves paths, blocks sensitive subpaths, applies allowlist containment, and tightens paths to the active workspace when one is bound. File tools use those resolvers. + +**Confirmed risks or gaps.** Intentional authority with safeguards. The file-tool policy does not sandbox the shell; treating a workspace as a whole-process containment boundary would be incorrect. + +**Required verification.** Traversal, symlink, sensitive-name, absolute-path, and workspace-switch behaviour remains unverified. + +## Agent-controlled tool dispatch + +**Boundary.** Model output becomes native or parsed tool calls and is dispatched by the agent loop. + +**Available authority.** The authority of every enabled tool, including privileged built-ins and external tools. + +**User-controlled inputs.** Chat content, attached/retrieved content that may influence the model, tool arguments, per-request tool selection, and policy toggles. + +**Current safeguards.** src/tool_security.py blocks protected tools for non-admin users and fails closed for malformed tool names; src/tool_policy.py supports disabled and guide-only policy; prompt-security helpers label untrusted context. + +**Confirmed risks or gaps.** Credible risk requiring verification: aliases, legacy text tools, native function calls, and MCP-qualified names must all reach the same policy outcome. The code has specific alias handling for email/MCP names, which makes this a sensitive compatibility seam. + +**Required verification.** The current policy outcomes for owner role, request mode, disabled state, native versus parsed invocation, qualified aliases, and external-content entry points remain unverified. + +## MCP and external tool servers + +**Boundary.** Configured MCP servers and their tools are exposed to the agent through the MCP manager and routes. + +**Available authority.** Depends on the server: external network access, local process access, messaging, or data mutation may be delegated outside the application. + +**User-controlled inputs.** Server configuration, remote OAuth completion, tool arguments, and model-selected MCP calls. + +**Current safeguards.** MCP routes are registered through routes/mcp_routes.py; MCP-qualified tools are denied to non-admin users by src/tool_security.py. OAuth state and token persistence are handled in src/mcp_oauth.py. + +**Confirmed risks or gaps.** Credible risk requiring verification: an MCP server authority is broader than the application can infer from its tool name. This map does not establish a trust or approval model for server installation and individual tool invocation. + +**Required verification.** Server onboarding, credential storage, server-origin trust, OAuth callback deployment, tool disablement, and invocation audit behaviour remain unverified. + +## Outbound network requests and URL validation + +**Boundary.** Search/content fetch, research, webhooks, skill import, provider endpoints, and other HTTP clients. + +**Available authority.** The application can make outbound requests from its network position. + +**User-controlled inputs.** Search/fetch URLs, imported skill URLs, webhook configuration, and some endpoint settings. + +**Current safeguards.** src/url_security.py validates untrusted public HTTP URLs and fails closed on unsuitable schemes or private addresses. services/search/content.py resolves and rejects non-public hosts, pins resolved addresses for fetches, caps bodies, and limits redirects. + +**Confirmed risks or gaps.** Intentional split: administrator-created model endpoints may target private providers, while untrusted URLs use public-address checks. That distinction is required for self-hosted deployments but needs explicit call-site review. + +**Required verification.** The URL-source classification for outbound clients and the current handling of redirects and DNS changes remain unverified. + +## Secrets, credentials, and vault sessions + +**Boundary.** Application-managed encrypted secrets, API keys, provider credentials, and Bitwarden/Vaultwarden CLI sessions. + +**Available authority.** Credentials unlock remote providers and connected personal services. + +**User-controlled inputs.** Administrative configuration, login/unlock requests, imported settings, and agent vault tool arguments. + +**Current safeguards.** src/secret_storage.py uses a locally stored Fernet key with restrictive permissions for supported database secrets. Vault routes require an administrator, avoid passing master passwords in command arguments, and set restrictive permissions on the vault-session file. + +**Confirmed risks or gaps.** Confirmed current boundary: vault session data is persisted through the vault path, not through src/secret_storage.py. This is an unresolved question about current security semantics, not a confirmed exposure. + +**Required verification.** Current encryption-at-rest, owner scope, rotation, lock/logout, backup/restore, and log/tool-result exposure behaviour remains unverified. + +## Authentication and privileged administration + +**Boundary.** Session authentication, API tokens, privileged routes, and internal tool loopback. + +**Available authority.** Administrative identity can access execution, settings, integrations, data deletion, and secrets. + +**User-controlled inputs.** Login/signup data, session cookies, API tokens, authentication configuration, and requests to privileged routes. + +**Current safeguards.** core/auth.py, core/middleware.py, and route-level checks establish identity and administrator gates. app.py warns when localhost bypass is configured; SECURITY.md documents deployment requirements. + +**Confirmed risks or gaps.** Intentional authority with safeguards. Security depends on deployments keeping authentication enabled and internal services private; this map does not audit reverse-proxy or environment configuration. + +**Required verification.** Setup, anonymous, non-admin, admin, token, and internal-loopback behaviour, including privileged-route gate consistency, remains unverified. + +## Deletion, wipe, backup, and restore + +**Boundary.** Administrative wipe, cleanup, backup import/export, and the backup restore command. + +**Available authority.** Delete or replace user data and credentials. + +**User-controlled inputs.** Administrative HTTP requests, cleanup choices, backup payloads, archive paths, and restore command options. + +**Current safeguards.** Administrative wipe routes use the administrative boundary. Cleanup exposes a preview route before mutation. The documented backup tool requires explicit restore confirmation, stages the old data directory, and validates archive members before extraction. + +**Confirmed risks or gaps.** Intentional destructive authority. Backup archives contain secrets by design, as documented in docs/backup-restore.md; this is an operator confidentiality responsibility, not a code defect established here. + +**Required verification.** Role-gate, confirmation, archive-rejection, staged-recovery, and owner-isolation behaviour remains unverified. No destructive runtime test was performed. + +## Background jobs and unattended task execution + +**Boundary.** Scheduled tasks, background-job monitor, startup tasks, and notification/delivery work that continue without an active browser request. + +**Available authority.** Scheduled agent work can obtain model access and, for eligible owners, shell and file tools; task output can interact with connected services. + +**User-controlled inputs.** Stored task prompt, schedule, model/crew selection, enabled-tool configuration, output target, and prior persisted state. + +**Current safeguards.** src/task_scheduler.py serializes execution, records task runs, associates work with an owner, and applies the agent owner-based tool gate. src/bg_jobs.py keeps bounded state and can terminate overlong subprocess jobs. + +**Confirmed risks or gaps.** Credible risk requiring verification: authority is inherited and exercised later, so changes to roles, task configuration, and disabled tools must be checked at execution time rather than assumed from task creation. + +**Required verification.** Creation, editing, role-change, scheduling, cancellation, restart-recovery, and execution behaviour remains unverified, including whether current policy is re-evaluated before privileged action. diff --git a/specs/discovery/system-map.md b/specs/discovery/system-map.md new file mode 100644 index 000000000..fcaf07e6a --- /dev/null +++ b/specs/discovery/system-map.md @@ -0,0 +1,123 @@ +# Current system map + +This document describes the checked-in code at the same commit as this document. It is a subsystem map, not an exhaustive feature catalog or runtime certification. Each section records only current implementation, canonical locations, confirmed current problems, and unresolved factual questions. + +## Startup and application composition + +**Current implementation.** app.py creates the application, mounts static assets, constructs shared services, registers route factories, and owns lifespan startup and shutdown. src/app_initializer.py prepares application state; core/ provides persistence, authentication, middleware, sessions, and platform helpers. + +**Canonical code locations.** app.py; src/app_initializer.py; core/database.py; core/auth.py; core/middleware.py; routes/. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** Which component currently owns startup and shutdown for each long-lived service? + +## Frontend shell and browser interaction + +**Current implementation.** static/index.html is served by the root and SPA deep-link routes in app.py; static/app.js, static/style.css, and static/js/ implement the client surface. + +**Canonical code locations.** static/index.html; static/app.js; static/js/; static/style.css; app.py deep-link handlers. + +**Confirmed problems.** The /backgrounds route calls serve_html_with_nonce for static/backgrounds.html, but that file is absent from static/. This is a confirmed broken prototype route, not evidence about the rest of the frontend. + +**Open questions.** Is /backgrounds currently an intentionally supported route or an obsolete prototype? + +## Chat, sessions, and streaming + +**Current implementation.** routes/chat_routes.py and routes/chat_helpers.py coordinate requests, session state, and SSE delivery. src/chat_handler.py, src/chat_processor.py, src/llm_core.py, and src/session_actions.py provide message preparation, provider interaction, and session operations. + +**Canonical code locations.** routes/chat_routes.py; routes/chat_helpers.py; routes/session_routes.py; src/chat_handler.py; src/chat_processor.py; src/llm_core.py; core/session_manager.py. + +**Confirmed problems.** src/agent_loop.py annotates _resolved_tool_event_name with Any but imports no Any and does not enable postponed annotation evaluation. Python evaluates that annotation while importing the module, so this is an import-time defect at the checked baseline. + +**Open questions.** No end-to-end provider or browser streaming run was performed for this map. + +## Agents, tools, and execution + +**Current implementation.** src/agent_loop.py drives multi-round tool use. src/tool_execution.py dispatches calls and binds workspace context. src/agent_tools/ contains individual implementations; src/tool_security.py and src/tool_policy.py apply role and request policies. Long-running command work is represented by src/bg_jobs.py. + +**Canonical code locations.** src/agent_loop.py; src/tool_execution.py; src/agent_tools/; src/tool_security.py; src/tool_policy.py; src/tool_schemas.py; src/bg_jobs.py. + +**Confirmed problems.** The import-time annotation defect above blocks the main agent/tool path. The shell is intentionally not a filesystem or network sandbox; that is an authority boundary, not by itself a vulnerability claim. + +**Open questions.** Which native, legacy, and MCP-qualified invocation paths reach each policy gate? + +## Models, providers, and local serving + +**Current implementation.** Model routes delegate to discovery, capabilities, endpoint resolution, and LLM core modules. Cookbook routes and hardware-fit services handle model lifecycle and local-serving support. + +**Canonical code locations.** routes/model_routes.py; src/model_discovery.py; src/model_capabilities.py; src/endpoint_resolver.py; src/llm_core.py; routes/cookbook_routes.py; src/cookbook_serve_lifecycle.py; services/hwfit/. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** Which endpoint inputs are administrator-created and permitted to use private provider addresses? + +## Search and research + +**Current implementation.** HTTP search routes use services/search/; research is exposed through routes/research/ and implemented in services/research/, src/deep_research.py, and related helpers. src/search/ remains an import-compatibility layer for callers not yet moved to services.search. + +**Canonical code locations.** routes/search_routes.py; services/search/; routes/research/research_routes.py; services/research/; src/deep_research.py; src/search/. + +**Confirmed problems.** The two search package paths are a confirmed compatibility arrangement, not two independent canonical implementations: several src/search modules forward to services.search. The remaining wrappers and compatibility imports can drift. + +**Open questions.** No live provider request was made; provider configuration and network access remain unverified. + +## Documents, retrieval, and personal knowledge + +**Current implementation.** Document routes coordinate upload handling, document processing, and editor actions. Personal-document and RAG modules use Chroma and embedding clients. PDF viewing uses the optional-dependency loader in src/pdf_runtime.py; form extraction and filling live separately in src/pdf_forms.py and src/pdf_form_doc.py. + +**Canonical code locations.** routes/document_routes.py; src/upload_handler.py; src/document_processor.py; src/document_actions.py; src/personal_docs.py; src/rag_manager.py; src/embeddings.py; src/pdf_runtime.py; src/pdf_forms.py; src/pdf_form_doc.py. + +**Confirmed problems.** PDF viewing/runtime loading and PDF form processing are separate implementations. That separation is confirmed and intentional in the source; it is not a defect without a reported behavioural failure. + +**Open questions.** Optional PDF dependencies and representative uploaded documents were not exercised. + +## Memory and skills + +**Current implementation.** Memory routes use services/memory/ and vector helpers. Skills are exposed through routes/skills_routes.py, stored and managed in services/memory/skills.py, and may be imported through services/memory/skill_importer.py. + +**Canonical code locations.** routes/memory/memory_routes.py; services/memory/; src/memory.py; src/memory_vector.py; routes/skills_routes.py; services/memory/skills.py; services/memory/skill_importer.py. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** Which imported skill content can reach execution-capable paths, and which validation occurs before that point? + +## Personal services: email, calendar, contacts, notes, and tasks + +**Current implementation.** Dedicated route modules own email, CalDAV calendar, CardDAV contacts, notes, and tasks. Supporting modules include email helpers and pollers, CalDAV sync and writeback, and the task scheduler. + +**Canonical code locations.** routes/email_routes.py; routes/calendar_routes.py; routes/contacts/contacts_routes.py; routes/note/note_routes.py; routes/task_routes.py; routes/assistant_routes.py; src/caldav_sync.py; src/caldav_writeback.py; src/task_scheduler.py. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** External account behaviour, writeback, and delivery require controlled credentials and are not runtime-validated here. + +## Media, speech, and image work + +**Current implementation.** Gallery and image routes coordinate media features. Service modules own speech and media integrations; src/generated_images.py and src/visual_report.py support artifact handling and presentation. + +**Canonical code locations.** routes/gallery/gallery_routes.py; routes/stt_routes.py; routes/tts_routes.py; src/generated_images.py; services/stt/; services/tts/; services/faces/; src/visual_report.py. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** Hardware- and provider-dependent media workflows were not exercised. + +## Authentication, secrets, and privileged administration + +**Current implementation.** core/auth.py and core/middleware.py provide identity and request gates. src/secret_storage.py encrypts application-managed database secrets with a local Fernet key. Vault handling is separate: routes/vault_routes.py and src/tools/vault.py invoke the Bitwarden CLI and persist its session data in the application data area. + +**Canonical code locations.** core/auth.py; core/middleware.py; routes/auth_routes.py; routes/api_token_routes.py; src/secret_storage.py; routes/vault_routes.py; src/tools/vault.py; routes/admin_wipe/admin_wipe_routes.py. + +**Confirmed problems.** Vault-command handling and local application secret storage are distinct paths with different storage mechanisms. This is a source-confirmed boundary, not evidence that either path is compromised. + +**Open questions.** What are the current confidentiality, ownership, rotation, and backup semantics for vault session data? + +## Persistence, background work, and operations + +**Current implementation.** SQLite models and persistence are centred in core/database.py; managers use application data paths. The scheduler and background-job monitor can continue work outside a live browser request. Operational routes cover cleanup, backup, and administrative wipe; the repository also provides a backup script and user documentation. + +**Canonical code locations.** core/database.py; src/runtime_paths.py; src/task_scheduler.py; src/bg_jobs.py; src/bg_monitor.py; routes/backup_routes.py; routes/cleanup/cleanup_routes.py; routes/admin_wipe/admin_wipe_routes.py; scripts/odysseus-backup; docs/backup-restore.md. + +**Confirmed problems.** None recorded by this mapping. + +**Open questions.** What current behaviour applies to background execution, cancellation, retries, and authority inheritance?