# src (normalize crate source root)

Root of the normalize library and binary. `main.rs` handles argv[0] dispatch (symlink-as-drop-in for `rg`, `jq`, `sg`), auto-starts the daemon in the background on every command invocation (skipping `daemon`, `serve`, and informational flags), rewrites command aliases (`search`/`find` → `grep`, `lint` → `rules run`, `check` → `ci`, `index` → `structure rebuild`, `refactor` → `edit`), runs the first-use grammar check (`commands::grammars::ensure_grammars_first_use`) on commands that need parsers, and delegates to the server-less service layer. The `init_sqlite_serialized()` workaround was removed when the workspace migrated rusqlite consumers to libsql (single sqlite implementation, no co-init race). `lib.rs` declares all top-level modules.

**Key infrastructure:** `config.rs` (NormalizeConfig/TOML), `output.rs` (OutputFormatter trait + format dispatch), `diagnostic_convert.rs` (Finding/ABI diagnostic → Issue), `runtime.rs` (async helpers: `block_on`/`try_block_on` using `block_in_place` inside tokio, new runtime outside), `index.rs`, `symbols.rs`, `parsers.rs`, `extract.rs`, `skeleton.rs`, `tree.rs`.

**Naming convention:** All public output structs use the `*Report` suffix (not `*Result`).

**`service/`** is the primary CLI registration point (server-less `#[cli]` proc macro). Key services: `view.rs` (file/dir/symbol navigation; `ViewReport.summary` populated from SUMMARY.md for dirs and from module-level doc comments for files), `rename.rs` (`normalize rename` — cross-file symbol rename with `--dry-run`, structured `RenameReport`), `rank.rs` (20+ ranking commands), `syntax.rs` (`syntax ast` with `--depth`/compact outline; `syntax query` compact shows per-match locations), `rules.rs` (mounts `normalize-rules::RulesService`; `compile` validates `.dl` files; `show`/`tags` return structured JSON), `facts.rs` (SQL query against structural index), `config.rs` (`normalize config schema/show/validate/set`), `ratchet.rs`/`budget.rs`, `guide.rs`, `daemon.rs` (Unix domain sockets, ENGINE_CACHE warming; diagnostics persisted to SQLite `daemon_diagnostics` table — no in-memory issue cache; reverse-dep graph derived from `imports` table on each refresh (transient, not stored on `WatchedRoot`); `WatchedRoot` holds `last_refresh: Instant`, `primed: bool`, `has_git_index: bool`, the persistent `index` connection, the cached `(RulesConfig, WalkConfig)` snapshot used for `ConfigDiff`, `cached_scm_hashes: HashMap<PathBuf, [u8; 32]>` (per-file blake3 hashes for `.normalize/rules/*.scm`), a `serve_filter_pending: bool` for Tier 1 reloads, and a `context_index: Arc<Mutex<ContextIndex>>` (in-memory cache of parsed `.normalize/context/` blocks, updated incrementally via the file watcher; serves `QueryContext` requests at near-zero latency) — no per-root watcher handles; `DaemonServer` owns a single shared `RecommendedWatcher` (`Mutex<RecommendedWatcher>`) and one dispatch thread handles events for all roots, routing by path prefix, debouncing full refreshes vs `.git/index` changes vs `.normalize/config.toml` / `.normalize/rules/**` reloads (config-reload route is tier-routed via `normalize_rules_config::ConfigDiff`: **Tier 1** filter-only diffs — severity / allow / `enabled = false` — update the cached `(RulesConfig, WalkConfig)` snapshot stored on `WatchedRoot` and flip a `serve_filter_pending` flag so the serve paths re-filter cached blobs in place, with no `clear_all_diagnostics()` call and no reprime; **Tier 2** per-rule re-eval — when `ConfigDiff::rules_to_rerun` is non-empty but `requires_full_reprime()` is false, `surgical_rerun_rules` re-runs only the affected rule IDs through each engine (syntax via `filter_ids`, fact via `filter_ids`, native via post-filter), loads each per-engine blob, drops findings for the affected rule IDs, merges new findings, saves, rebuilds the "all" blob and per-file rows, updates the `(RulesConfig, WalkConfig)` snapshot and `cached_scm_hashes`, and broadcasts `DiagnosticsUpdated`; `.scm` file edits under `.normalize/rules/` are routed through Tier 2 via `hash_scm_files` — `WatchedRoot` stores `cached_scm_hashes: HashMap<PathBuf, [u8; 32]>` (blake3 per `.scm` file) and `reload_config_and_reprime` diffs old vs new hashes to identify changed rule IDs (by file stem), which are unioned into `ConfigDiff::rules_to_rerun` before the tier decision; **Tier 3** full reprime — `[walk] exclude` changed — `clear_all_diagnostics()` then a full reprime. All paths broadcast `IndexRefreshed { files: 0 }`; Tier 2 and Tier 3 additionally broadcast `DiagnosticsUpdated` via per-file delta; cached blobs also carry a `config_hash` (`compute_config_hash` over binary version + `.normalize/config.toml` + `.normalize/rules/**`) so a daemon restarted after an offline config edit treats stale rows as cache misses and reprimes — covers the gap left by the live-reload path which only fires on notify events that arrive during the daemon's lifetime); saves ~3 OS threads per root vs the previous per-root pair; `FilesChanged { root, paths }` request is the direct "push" complement to inotify: callers (e.g. refactoring commands) send it immediately after writing files — the handler broadcasts `FileChanged` events for each path and enqueues the root into `refresh_tx` (same path as inotify), so the index refreshes without waiting for the OS watcher; non-fatal if the root is not watched. `DaemonClient::notify_files_changed(root, paths)` is the client-side API. `RunRules` request/handler reads issues from SQLite with `filter_ids`/`filter_rule`/`engine`/`filter_files` filtering; `QueryContext` request/handler queries the in-memory `ContextIndex` for matching frontmatter blocks (when only `filter_files` is set, served directly from the per-file `daemon_diagnostics_per_file` table); per-file table + `.normalize/diagnostics.json` (atomic write, BTreeMap-keyed) are reconciled on every prime/refresh as a canonical-state mirror for ephemeral consumers; subscribers also receive an `Event::DiagnosticsUpdated { root, updates }` push (per-file delta, batched per refresh, blob-compared to skip unchanged files) via JSON-line or `0x01` binary-rkyv subscribe (`DaemonClient::watch_events_binary`); cache primed eagerly on source-file changes via `refresh_root()` and lazily on first request via `prime_diagnostics_cache()`; stub on Windows), `package.rs` (`PackageListReport.ecosystems_detected`).

**`commands/`** contains domain logic modules. Key areas: `commands/analyze/` (AST/complexity/ownership/coupling/architecture analysis; `analyze health --limit` caps `large_files`; `analyze architecture --limit` caps `cross_imports`), `commands/view/` (unified `ViewReport` with `summary` for both dirs and files; `render_line_range` header uses `strip_range_suffix` to avoid duplicating `:N-M` in file:range requests), `commands/sessions/` (session analysis, Markov matrices; `sort.rs` provides shared `SortDir`/`SortKey<F>`/`SortSpec<F>`/`DefaultDir` used by `list`, `messages`, `stats`, and `patterns` for `--sort` support), `commands/tools/` (linter/formatter/type-checker integration). `text_search.rs` (`GrepReport` output groups consecutive same-symbol matches under a single header to avoid repeating symbol tags per line).
