[package]
name = "sqry-daemon"
description = "sqry daemon (sqryd) — persistent code-graph service"
version.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(disabled)', 'cfg(fuzzing)'] }

[lib]
name = "sqry_daemon"
path = "src/lib.rs"

[[bin]]
name = "sqryd"
path = "src/main.rs"

# Phase 8c U16 — test-helper binary for end-to-end shim-client integration
# tests in sqry-lsp and sqry-mcp. Starts a fully-functional IpcServer bound
# to the socket path in SQRYD_TEST_SOCKET, writes READY\n to stdout, then runs
# until stdin closes. Not part of the production distribution.
#
# Gated behind the `testharness` feature so the binary never ships in
# release artefacts. Build it explicitly with:
#   cargo build -p sqry-daemon --features testharness --bin sqryd-test-server
# Integration tests in sqry-lsp / sqry-mcp gracefully skip when the binary
# is not present on disk (see `find_sqryd_test_server_binary`), so a plain
# `cargo test --workspace` (without `--features testharness`) is still a
# valid green build — the daemon-mode shim tests just self-skip.
[[bin]]
name = "sqryd-test-server"
path = "src/bin/sqryd-test-server.rs"
required-features = ["testharness"]

[features]
default = []

# Audit C086 (cli-help-impl-alignment) — gates the `sqryd-test-server`
# helper binary so it is never built unless integration tests explicitly
# request it. Prevents accidental shipping of a test-only entrypoint in
# release artefacts.
testharness = []

# SGA04 Gate-A blocker fix — gates the public-on-the-type
# `WorkspaceManager::evict_for_test` helper so it is unreachable in
# default release builds. SGA07 parity tests that live in
# `sqry-daemon/tests/` enable this feature explicitly:
#   cargo test -p sqry-daemon --features test-hooks --tests
# In-crate `#[cfg(test)] mod tests` paths reach the helper without the
# feature flag because `cfg(test)` is also part of the gate.
test-hooks = []

# Task 7 Phase 7c: exposes test-only helpers on LoadedWorkspace +
# RebuildDispatcher (e.g., set_last_good_at_for_test, post-reservation
# TestCapture hooks). Automatically enabled by sqry-daemon's own
# `[dev-dependencies]` / `#[cfg(test)]` paths; external consumers should
# NOT enable this in production.
rebuild-internals = []

# Forwarding feature for integration tests that need sqry-nl's
# ONNX-backed classifier surface (IntentClassifier::load,
# ClassifierPool, etc). Off by default — `cargo clippy --all-targets`
# skips gated test files unless this is passed on the command line.
# Run the gated tests via:
#   cargo test --features classifier-tests --test concurrent_ask_smoke -- --ignored
classifier-tests = ["sqry-nl/classifier"]

[dependencies]
# sqry workspace crates
sqry-core = { version = "16.0.2", path = "../sqry-core", features = ["rebuild-internals"] }
sqry-plugin-registry = { version = "16.0.2", path = "../sqry-plugin-registry" }
# Phase 8c U1: wire types + framing codec extracted into the
# `sqry-daemon-protocol` leaf crate so shim clients can depend on
# the wire format without pulling in the full daemon (and creating
# a `sqry-daemon → sqry-lsp → sqry-daemon` cycle).
sqry-daemon-protocol = { version = "16.0.2", path = "../sqry-daemon-protocol" }
# Phase 8b Task 7 — the daemon routes 14 MCP tool methods through
# sqry-mcp's `daemon_adapter::execute_*_for_daemon` wrappers and the
# shared `build_tool_response` renderer (via `tool_response_json`).
# sqry-mcp depends only on sqry-core / sqry-db / sqry-nl /
# sqry-mcp-redaction / sqry-plugin-registry — none of which depend on
# sqry-daemon, so this edge does not create a cycle (verified via
# `cargo tree -p sqry-daemon`).
sqry-mcp = { version = "16.0.2", path = "../sqry-mcp" }

# Phase 8c U10 — the daemon hosts an in-process tower_lsp server on
# each LSP shim byte-pump connection via
# `sqry_lsp::daemon_host::host_on_streams`. Verified acyclic: sqry-lsp
# depends only on sqry-core / sqry-db / sqry-nl /
# sqry-plugin-registry — none of which depend on sqry-daemon.
# Confirmed via `cargo tree -p sqry-daemon` post-commit.
sqry-lsp = { version = "16.0.2", path = "../sqry-lsp" }

# Phase 8c U8 — the daemon hosts an in-process rmcp `ServerHandler`
# (`mcp_host::DaemonMcpHandler`) on each MCP shim byte-pump connection
# so every `tools/call` is bit-identical with direct sqryd JSON-RPC
# dispatch. Version/feature set MUST match sqry-mcp's rmcp dep so the
# two crates agree on `ErrorData`, `CallToolRequestParams`, etc.
rmcp = { version = "1.6", features = ["server", "transport-io"] }

# NL07 — the daemon caches one `Arc<Translator>` per workspace on
# `LoadedWorkspace.nl_translator` (lazy `OnceCell`). `sqry-nl` is a
# leaf crate from the daemon's POV; no cycle.
sqry-nl = { version = "16.0.2", path = "../sqry-nl" }

# PF03A — the production `QueryDbHook` calls `sqry_db::persistence::save_derived`
# from `sqry-daemon/src/workspace/hook.rs`. sqry-db depends only on sqry-core,
# so this edge does not introduce a cycle (verified via `cargo tree -p sqry-daemon`).
# The dependency is unconditional rather than feature-gated because the
# corrective program (PF03A spec) requires the production hook to be present
# in every production sqryd build, not opt-in.
sqry-db = { version = "16.0.2", path = "../sqry-db" }
# NL07 — `OnceCell::get_or_try_init` for fallible lazy init of the
# per-workspace `Arc<Translator>`. `parking_lot::OnceCell` does not
# exist; `std::sync::OnceLock` lacks `get_or_try_init`.
once_cell = { workspace = true }

# Core utilities
anyhow = { workspace = true }
thiserror = { workspace = true }
parking_lot = { workspace = true }
arc-swap = "1"
tracing = { workspace = true }
# Task 9 U5 — override workspace tracing-subscriber to add `json` feature
# required by install_tracing's JSON fmt layer for structured log files.
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter", "json"] }
# Task 9 U5 — tracing-appender: non_blocking writer + WorkerGuard used by
# RollingSizeAppender in lifecycle/log_rotate.rs. The non_blocking wrapper
# decouples the caller's hot path from filesystem I/O.
tracing-appender = "0.2"

# Serialization / config
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
toml = { workspace = true }

# issue-238 tier-2 (DAEMON_SEARCH_HANDLER) — regex pattern matching for
# the `daemon/search` JSON-RPC method's `SearchMode::Regex` arm.
regex = { workspace = true }

# Async IPC
tokio = { workspace = true, features = ["full"] }
tokio-util = { version = "0.7", default-features = false, features = ["rt"] }

# Filesystem / locking
dirs = "6"
fs2 = "0.4"
chrono = { workspace = true }

# Task 9 U15a — clap CLI for sqryd subcommands (Start, Foreground, Stop,
# Status, InstallSystemdUser, InstallSystemdSystem, InstallLaunchd,
# InstallWindows, PrintConfig). The `derive` + `env` features are present in
# the workspace pin. Workspace already has clap = "4.5" with derive+env.
clap = { workspace = true }

# Unix-only: `getuid()` for the per-user runtime-dir fallback so sqryd
# paths never collide across users when USER/USERNAME are unset (e.g.
# systemd units, containers). Already a transitive workspace dep.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Storage_FileSystem"] }

# Task 9 U15a — sd-notify: Linux-only. Sends READY=1 + STATUS= notifications to
# systemd on Type=notify units. Guarded by target_os="linux" because systemd is
# Linux-specific; macOS (launchd) and Windows (SCM) have no equivalent protocol
# and the wrapper module provides no-op stubs for those platforms.
[target.'cfg(target_os = "linux")'.dependencies]
sd-notify = { workspace = true }
# Task 9 U15a — users: Linux-only. Provides `get_user_by_name` to validate the
# %i instance specifier in the systemd system-unit generator (U6). Gated to
# Linux because InstallSystemdSystem is a #[cfg(target_os="linux")] subcommand.
users = { workspace = true }

[dev-dependencies]
tempfile = "3.27"
# Phase 8c U15 — MCP host integration tests drive `tools/call` and
# `tools/list` through an rmcp client (`serve_client` + `Peer` methods).
# The client feature is NOT needed by the production daemon code (which
# only uses the server feature); it is a dev-only dependency.
rmcp = { version = "1.6", features = ["client", "transport-io"] }
# Task 9 U14 — e2e smoke tests use `connect_shim_with_timeouts` to perform
# the shim handshake and assert `daemon_version` against the live sqryd binary.
sqry-daemon-client = { version = "16.0.2", path = "../sqry-daemon-client" }
# Codex gpt-5.4 iter-1 MINOR fix — integration tests that mutate
# process-global state (current working directory) must run
# serialised; `serial_test` mirrors sqry-mcp's existing usage
# (sqry-mcp/Cargo.toml: serial_test = "3.0").
serial_test = "3.4"
# Codex iter2 SGA04 Major #2 — RealWorkspaceBuilder::load_persisted unit
# test crafts a snapshot file with a bogus header version directly to
# force `PersistenceError::IncompatibleVersion`. `postcard` encodes the
# header struct; `sha2` recomputes the snapshot SHA-256 written into the
# stub manifest so the integrity check passes the way the production
# loader requires.
postcard = { workspace = true }
sha2 = { workspace = true }
# SGA08 — dogfood regression test invokes the workspace `sqry` CLI
# binary against a tempdir and parses its JSON output. `assert_cmd` is
# the canonical helper used by sqry-cli, sqry-lsp, and sqry-core's
# `test-support` feature for the same purpose.
assert_cmd = "2.2.2"
