# DOMAIN.md -- Gitlore Technical Reference

Read this when you need implementation details. AGENTS.md has the summary; this has the depth.

## Architecture Map

```
src/
  main.rs              # Entry: RuntimeBuilder -> block_on(async main)
  http.rs              # HTTP client wrapping asupersync::http::h1::HttpClient
  lib.rs               # Crate root
  test_support.rs      # Shared test helpers
  cli/
    mod.rs             # Clap app (derive), global flags, subcommand dispatch
    args.rs            # Shared argument types
    robot.rs           # Robot mode JSON envelope: {ok, data, meta}
    render.rs          # Human output (lipgloss/console)
    progress.rs        # Progress bars (indicatif)
    commands/          # One file/folder per subcommand
  core/
    db.rs              # SQLite connection, MIGRATIONS array, LATEST_SCHEMA_VERSION
    error.rs           # LoreError (thiserror), ErrorCode, exit codes 0-21
    config.rs          # Config structs (serde)
    shutdown.rs        # Cooperative cancellation (ctrl_c + RuntimeHandle::spawn)
    timeline.rs        # Timeline types (5-stage pipeline)
    timeline_seed.rs   # SEED stage
    timeline_expand.rs # EXPAND stage
    timeline_collect.rs # COLLECT stage
    trace.rs           # File -> MR -> issue -> discussion trace
    file_history.rs    # File-level MR history
    path_resolver.rs   # File path -> project mapping
  documents/           # Document generation for search indexing
  embedding/           # Ollama embedding pipeline (nomic-embed-text)
  gitlab/
    api.rs             # REST API client
    graphql.rs         # GraphQL client (status enrichment)
    transformers/      # API response -> domain model
  ingestion/           # Sync orchestration
  search/              # FTS5 + vector hybrid search
tests/                 # Integration tests
```

## Async Runtime: asupersync

- `RuntimeBuilder::new().build()?.block_on(async { ... })` -- no proc macros
- HTTP: `src/http.rs` wraps `asupersync::http::h1::HttpClient`
- Signal: `asupersync::signal::ctrl_c()` for shutdown
- Sleep: `asupersync::time::sleep(wall_now(), duration)` -- requires Time param
- `futures::join_all` for concurrent HTTP batching
- tokio only in dev-dependencies (wiremock tests)
- Nightly toolchain: `nightly-2026-03-01`

## Database Schema Gotchas

| Gotcha | Detail |
|--------|--------|
| `projects` columns | `gitlab_project_id` (NOT `gitlab_id`). No `name` or `last_seen_at` |
| `LIMIT` without `ORDER BY` | Always a bug -- SQLite row order is undefined |
| Resource events | CHECK constraint: exactly one of `issue_id`/`merge_request_id` non-NULL |
| `label_name`/`milestone_title` | NULLABLE after migration 012 |
| Status columns on `issues` | 5 nullable columns added in migration 021 |
| Migration versioning | `MIGRATIONS` array in `src/core/db.rs`, version = array length |

## Error Pipeline

`LoreError` (thiserror) -> `ErrorCode` -> exit code + robot JSON

Each variant provides: display message, error code, exit code, suggestion text, recovery actions array. Robot errors go to stderr. Clap parsing errors -> exit 2.

## Embedding Pipeline

- Model: `nomic-embed-text`, context_length ~1500 bytes
- CHUNK_MAX_BYTES=1500, BATCH_SIZE=32
- `floor_char_boundary()` on ALL byte offsets, with forward-progress check
- Box-drawing chars (U+2500, 3 bytes), smart quotes, em-dashes trigger boundary issues

## Pipelines

**Timeline:** SEED -> HYDRATE -> EXPAND -> COLLECT -> RENDER
- CLI: `lore timeline <query>` with --depth, --since, --expand-mentions, --max-seeds, --max-entities, --limit

**GraphQL status enrichment:** Bearer auth (not PRIVATE-TOKEN), adaptive page sizes [100, 50, 25, 10], graceful 404/403 handling.

**Search:** FTS5 + vector hybrid. Import: `crate::search::{FtsQueryMode, to_fts_query}`. FTS count: use `documents_fts_docsize` shadow table (19x faster).

## Test Infrastructure

Helpers in `src/test_support.rs`:
- `setup_test_db()` -> in-memory DB with all migrations
- `insert_project(conn, id, path)` -> test project row (gitlab_project_id = id * 100)
- `test_config(default_project)` -> Config with sensible defaults

Integration tests in `tests/` invoke the binary and assert JSON + exit codes. Unit tests inline with `#[cfg(test)]`.

## Performance Patterns

- `INDEXED BY` hints when SQLite optimizer picks wrong index
- Conditional aggregates over sequential COUNT queries
- `COUNT(*) FROM documents_fts_docsize` for FTS row counts
- Batch DB operations, avoid N+1
- `EXPLAIN QUERY PLAN` before shipping new queries

## Key Dependencies

| Crate | Purpose |
|-------|---------|
| `asupersync` | Async runtime + HTTP |
| `rusqlite` (bundled) | SQLite |
| `sqlite-vec` | Vector search |
| `clap` (derive) | CLI framework |
| `thiserror` | Error types |
| `lipgloss` (charmed-lipgloss) | TUI rendering |
| `tracing` | Structured logging |
