feat(adr-0026): schema daemon CLI verb + runtime (slice 4b)#5
Merged
Conversation
…slice 4b) ADR-0026 slice 4b: shared daemon now runs end-to-end against a registry. Per-project ADR-0019 / ADR-0020 deployment still ships in production until slice 5 (template collapse) and slice 6 (canary E2E gate) flip the operator-facing path. * `schema daemon` subcommand — no `--config` flag (the registry is the source of truth per ADR-0026 amendment to ADR-0020). * `run_daemon`: load registry from `Registry::default_path()`, build one `FastembedEmbedder` (the ~1.7 GB BGE-M3 ONNX workstation baseline that ADR-0026 is built around), resolve the LLM provider from env (`SCHEMA_LLM_PROVIDER` / `SCHEMA_LLM_MODEL` / `*_API_KEY`), call `Daemon::wire`. Empty registry logs a warning but still serves /health. * `serve_daemon` binds `127.0.0.1:0`, runs per-slot startup side effects, builds the multi-tenant router, runs `axum::serve` with the shared `CancellationToken` so a single SIGTERM drains every project's in-flight sessions in parallel, then unlinks every `endpoint.toml` it wrote. * `startup_each_slot` for each `ProjectSlot`: runs initial delta-sync, spawns the per-project `NotifyWatcher` (ADR-0010), and writes `~/.cache/schema/projects/<project_id>/endpoint.toml` with `url = "http://127.0.0.1:<port>/mcp/<project_id>"` and the slot's UUIDv4 bearer (still `0600` per ADR-0021). URL includes the project path so consumers can paste it directly into `.mcp.json`. * `resolve_llm_provider_for_daemon` reads from env (`SCHEMA_LLM_PROVIDER` defaults to `auto`); the registry has no `[llm]` section because that knob is per-project today and would conflict if two projects pinned different providers. Daemon's shared provider is workstation-level by construction. * Templates (`launchd.plist.template`, `systemd.service.template`) and `cli/install.rs` are untouched — slice 5 collapses them to a single workstation-level unit invoking `schema daemon`. ADR-0026 Evidence and amendments entry appended for slice 4b including follow-up scope (slice 4c hot-reload admin endpoint, slice 5 templates, slice 6 canary E2E gate) — closes the arch-first loop per CLAUDE.md. Strict-lint gate: cargo clippy --all-features --all-targets --workspace -- -D warnings exits 0. cargo fmt --all -- --check clean. cargo test --all-features 115 passed (110 unit + 5 integration; no new tests — real-Embedder paths are exercised by slice 6 E2E). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ADR-0026 slice 4b. The shared multi-project daemon now runs end-to-end against a registry. Production still defaults to ADR-0019 per-project units until slice 5 (template collapse) and slice 6 (canary E2E gate) ship.
schema daemonsubcommand — no--configflag; readsRegistry::default_path().run_daemon: 1×FastembedEmbedder(the workstation-level ONNX baseline), env-driven LLM provider,Daemon::wire.serve_daemon: bind127.0.0.1:0, per-slot startup, multi-tenant router,axum::serveuntil SIGTERM, then cleanup everyendpoint.toml.startup_each_slot: initial delta-sync, spawn watcher, writeendpoint.toml(url=http://127.0.0.1:<port>/mcp/<project_id>, token=slot's UUIDv4, mode0600).resolve_llm_provider_for_daemon: env-driven (SCHEMA_LLM_PROVIDER/SCHEMA_LLM_MODEL); the registry has no[llm]section by design (would conflict if two projects pinned different providers).cli/install.rsuntouched — slice 5's job.Test plan
cargo clippy --all-features --all-targets --workspace -- -D warningsexits 0cargo fmt --all -- --checkcleancargo test --all-features— 115 passed (110 unit + 5 integration); no regressions in single-project pathsschema daemon --helpshows the new verb;schema --helplists itunsafe, no cache layout change, no embedding model changelintjob green on PR commitschema daemonagainst a 2-project registry, verify per-projectendpoint.tomlfiles appear with paths + tokens, hit/healthand one/mcp/<project_id>route — deferred to operator's box (CI can't do localhost-bind reliably from a sandboxed runner)🤖 Generated with Claude Code