# ossCtx — AI Agent Instructions

> **IMPORTANT:** Keep this file and `AGENTS.md` up to date whenever project structure, conventions, or architecture changes. If you add a new subsystem, endpoint, model, or pattern — update these files.

## Project Overview

ossCtx is a unified CLI and web platform for **code context**, **document context**, and **observability (OTel) context**. It serves as the backend intelligence layer for agentic AI systems that need to query code, docs, and telemetry to support 150+ component service maps.

- **CLI**: `ossCtx` (Typer-based, subcommands per subsystem)
- **Web**: FastAPI serving a React SPA + REST APIs + MCP servers
- **Default port**: 8070 (HTTP), 4317 (gRPC for OTLP)

## Architecture

```
ossCtx serve
  └── FastAPI (ui/app.py)
        ├── /api/code    → CodeCtx sub-app (codectx/cli.py:build_app)
        ├── /api/docs    → DocsCtx sub-app (docsctx/cli.py:build_app)
        ├── /api/otel    → OtelCtx sub-app (otelctx/cli.py:build_app)
        ├── /api/context → Unified context assembly endpoints
        ├── /api/health  → Health check
        ├── /mcp/code    → CodeCtx MCP server (streamable HTTP)
        ├── /mcp/docs    → DocsCtx MCP server
        ├── /mcp/otel    → OtelCtx MCP server
        ├── /mcp/unified → Cross-system MCP server
        ├── /v1/traces   → OTLP HTTP trace ingestion
        ├── /v1/logs     → OTLP HTTP log ingestion
        ├── /v1/metrics  → OTLP HTTP metric ingestion
        └── /* (catch-all) → React SPA (ui/static/dist/)
```

## Directory Structure

```
ossctx/
├── common/          # Shared: CLI entry, server factory, middleware, DB base, context assembly
│   ├── cli.py       # Main Typer app, mounts subsystem CLIs
│   ├── server.py    # create_app() factory, mount_spa() for React SPA
│   ├── db.py        # SQLAlchemy engine (WAL), session factory, ChromaDB singleton
│   ├── context.py   # ContextAssembler, route_query()
│   ├── agent.py     # AgentInterface for unified MCP tools
│   ├── actions/     # Action registry + MCP tool registration
│   └── middleware.py # CORS, exception handlers, logging, rate limiting
├── codectx/         # Code context subsystem
│   ├── cli.py       # build_app(), build_mcp_server()
│   ├── config.py    # CodeSettings (pydantic-settings)
│   ├── db/          # models.py (File, Entity, Dependency, EntityRelation), repository.py
│   ├── api/routes.py # REST endpoints (/stats, /files, /entities, /graph, /tree)
│   ├── parser/      # Language parsers (20+ languages), registry
│   ├── indexer.py   # Directory indexing orchestration
│   ├── vectorstore.py # ChromaDB code embeddings
│   └── mcp.py       # MCP tool definitions
├── docsctx/         # Document context subsystem
│   ├── cli.py       # build_app(), build_mcp_server()
│   ├── config.py    # DocsSettings (embedding_provider, llm_provider, chunk_size)
│   ├── db/          # models.py (Document, DocumentChunk, DocumentEntity, etc.), repository.py
│   ├── api/routes.py # REST endpoints (/stats, /documents, /search, /upload, /communities)
│   ├── finalize.py  # Community detection, entity extraction pipeline
│   ├── vectorstore.py # ChromaDB doc embeddings
│   └── mcp.py       # MCP tool definitions
├── otelctx/         # Observability context subsystem
│   ├── cli.py       # build_app(), build_mcp_server()
│   ├── config.py    # OtelSettings (db_path, grpc_port)
│   ├── db/          # models.py (Span, Log, Metric, ServiceNode), repository.py
│   ├── api/routes.py # REST endpoints (/stats, /traces, /logs, /metrics, /health)
│   ├── grpc_server.py # gRPC OTLP receiver (port 4317)
│   ├── telemetry.py # Telemetry system with alerts, sampling
│   └── mcp.py       # MCP tool definitions
├── ui/              # Web interface
│   ├── app.py       # Unified app factory (mounts all sub-apps, MCP, SPA)
│   ├── frontend/    # React SPA (Vite + TypeScript + TailwindCSS v4)
│   │   ├── src/
│   │   │   ├── App.tsx           # Router + QueryClient
│   │   │   ├── components/       # layout/, ui/, graph/, treemap/, charts/, mcp/
│   │   │   ├── pages/            # Dashboard, Code, Docs, OTel, MCP pages
│   │   │   ├── api/              # Fetch wrappers per subsystem
│   │   │   └── hooks/            # React Query hooks
│   │   ├── package.json
│   │   └── vite.config.ts        # Proxy to :8070, build to ../static/dist
│   └── static/dist/  # Built React assets (gitignored)
├── tests/           # pytest suite (68+ tests)
├── hatch_build.py   # Hatch build hook: runs npm ci && npm run build
└── pyproject.toml   # Hatchling build, dependencies, scripts
```

## Critical Conventions

### API Path Pattern (NEVER change)
Sub-apps are mounted at `/api/{subsystem}` and each has internal routes under `/api/`:
```
/api/code/api/stats     ← NOT /api/code/stats
/api/docs/api/search    ← Double /api is intentional
/api/otel/api/traces
```
This is because each sub-app's router uses `prefix="/api"` internally, and the sub-app is mounted at `/api/code`. **Do not "fix" this.**

### Database Conventions
- **SQLite with WAL mode** — all repos use `create_engine_with_wal()`
- **Default paths**: `.codecontext.db`, `.docscontext.db`, `.otelcontext.db`
- **Schema creation**: `repo.create_schema()` called on startup, idempotent
- **Session pattern**: Always use `session_scope()` context manager
- **No migrations**: Schema is created fresh via `metadata.create_all()`

### Repository Pattern
All repositories follow the same initialization:
```python
class XyzRepository:
    def __init__(self, db_path: str | Path):
        self.db_path = Path(db_path)
        self.engine = create_engine_with_wal(f"sqlite:///{self.db_path}")
        self.session_factory = create_session_factory(self.engine)

    def create_schema(self) -> None:
        Base.metadata.create_all(self.engine, tables=[...specific tables...])
```

### MCP Server Pattern
All MCP servers use `FastMCP` with streamable HTTP:
```python
server = FastMCP(name="...", stateless_http=True, json_response=True)
server.settings.streamable_http_path = "/"
app.mount("/mcp/subsystem", server.streamable_http_app())
```

### Frontend Conventions
- **React Query** for all API data — no auto-refetch, manual refresh buttons
- **TailwindCSS v4** with `@theme` design tokens (bg-surface, text-text-primary, etc.)
- **Path alias**: `@/` → `src/`
- **Build output**: `ui/static/dist/` (gitignored, built by hatch hook)
- **Dev workflow**: `npm run dev` at :5173 proxies API to :8070
- **No data tables**: Pages show stats cards + graph visualizations, not raw data lists
- **Sigma.js**: All node labels must be `String()` coerced (crashes on non-string)

### SPA Serving (mount_spa in common/server.py)
- Mounts `/assets` for Vite hashed files
- Catch-all `/{path}` returns `index.html` for client-side routing
- **Skips** paths starting with `api/`, `mcp/`, `v1/`
- **Must be mounted LAST** — after all API and MCP mounts

## Development

### Running locally
```bash
# Backend
uv run ossCtx serve --port 8070

# Frontend dev (separate terminal)
cd ui/frontend && npm run dev    # Vite at :5173, proxies to :8070

# Production build
cd ui/frontend && npm run build  # Output to ui/static/dist/
uv run ossCtx serve              # Serves SPA from built assets
```

### Testing
```bash
uv run pytest tests/ -x -q       # 68+ tests
cd ui/frontend && npx tsc --noEmit  # TypeScript check
cd ui/frontend && npm run build     # Vite build check
```

### Building a wheel
```bash
pip install .   # hatch_build.py runs npm ci && npm run build automatically
```
The `hatch_build.py` hook uses `shutil.which("npm")` for cross-platform npm resolution.

## Key Dependencies
- **Web**: FastAPI, Uvicorn, Starlette
- **ORM**: SQLAlchemy 2.0+
- **Vector DB**: ChromaDB (HNSW index)
- **LLM**: LangChain (OpenAI, Ollama providers)
- **MCP**: mcp>=1.26.0 (streamable HTTP transport)
- **Telemetry**: gRPC, protobuf, OpenTelemetry proto
- **CLI**: Typer[all]
- **Frontend**: React 18, Vite, TailwindCSS v4, Sigma.js, Recharts, d3-hierarchy

## Configuration Classes
- `CodeSettings` — `codectx/config.py` (db_path, max_file_size_mb, parser_timeout)
- `DocsSettings` — `docsctx/config.py` (db_path, embedding_provider, llm_provider, chunk_size)
- `OtelSettings` — `otelctx/config.py` (db_path, grpc_port, otlp_timeout)

All use `pydantic-settings` with YAML file support.

## Context Assembly
The `ContextAssembler` (common/context.py) combines results from all three subsystems:
1. Routes query to relevant subsystems via keyword matching
2. Queries each repo + vectorstore
3. Token-budgets the combined result
4. Returns unified context for LLM consumption

Exposed as `POST /api/context/assemble` and as the `query` tool in the unified MCP server.

## Things to Watch Out For
1. **Double /api paths** — intentional, do not consolidate
2. **SPA mount order** — must be LAST in `build_app()`
3. **Sigma.js labels** — must be strings, `String(label || '')` always
4. **TreemapCanvas data** — backend returns dict-of-dicts, not `{name, children}`
5. **No precompiled C extensions** — user's machine has no C compiler, only precompiled wheels
6. **SQLite WAL mode** — enables concurrent reads, critical for multi-endpoint serving
7. **MCP lifespan** — session managers must be started in the FastAPI lifespan context
