diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be4debcae..8559073c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -156,7 +156,7 @@ jobs: fail-fast: false matrix: include: - - features: "desktop,ide,server,chat,pdf,scheduler,registry,testing,deep-link" + - features: "desktop,ide,server,chat,pdf,scheduler,testing" label: desktop-ide-server-chat-pdf-scheduler - features: bench label: bench @@ -182,7 +182,7 @@ jobs: fail-fast: false matrix: include: - - features: "desktop,ide,server,chat,pdf,scheduler,registry,deep-link" + - features: "desktop,ide,server,chat,pdf,scheduler" label: desktop-ide-server-chat-pdf-scheduler - features: bench label: bench @@ -226,7 +226,7 @@ jobs: - name: Build and archive tests env: RUSTFLAGS: "-Z threads=4" - run: cargo nextest archive --config-file .github/nextest.toml --cargo-profile ci --workspace --features "desktop,ide,server,chat,pdf,scheduler,registry,deep-link" --lib --bins --tests --archive-file nextest-archive.tar.zst + run: cargo nextest archive --config-file .github/nextest.toml --cargo-profile ci --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins --tests --archive-file nextest-archive.tar.zst - name: Upload test archive uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: @@ -759,7 +759,7 @@ jobs: # `#![recursion_limit]` fix) because it skips codegen/LTO layout finalization. Only a # real `cargo build --release` with this workspace's `[profile.release]` (lto = true, # codegen-units = 1) exercises the same query depth CI is meant to gate on. - run: cargo build --release --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,registry,testing,deep-link" + run: cargo build --release --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" release-build-full: name: Release Build Check (full) @@ -859,9 +859,9 @@ jobs: with: shared-key: "ci" - name: Build docs (all features except candle) - run: cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler,registry,deep-link" + run: cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler" - name: Doc-tests - run: cargo test --doc --workspace --features "desktop,ide,server,chat,pdf,scheduler,registry,deep-link" + run: cargo test --doc --workspace --features "desktop,ide,server,chat,pdf,scheduler" validate-specs: name: Validate Specs diff --git a/CHANGELOG.md b/CHANGELOG.md index 047791443..e3a830680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). build hit the 30-minute limit and was cancelled — a direct consequence of the sccache removal above, since a cold `--features full` build (including the heavier candle/ML dependencies) now compiles from scratch on every release instead of reusing a warm cache. +- **BREAKING**: `full` no longer implies `testing`. Anyone relying on `--features full` for mock + LLM providers must now add `testing` explicitly (`--features full,testing`) (#6747). +- `index` (AST-based code indexing) joins the `desktop` bundle, and therefore `full` — the first + time `full` compiles `zeph-index` in (#6747). + +### Removed + +- Workspace feature-flag audit: `deep-link`, `cocoon`, `registry`, `zeph-orchestration/llm-planning`, + `zeph-scheduler/daemon`, and six stabilized `zeph-acp/unstable-*` flags (`unstable-boolean-config`, + `unstable-session-delete`, `unstable-session-resume`, `unstable-logout`, `unstable-session-add-dirs`, + `unstable-message-id`) are removed — all gated code is now always-on. `zeph-core/sysinfo` merged + into `zeph-core/profiling`. Three genuinely-empty `default = []` declarations (`zeph-config`, + `zeph-vault`, `zeph-worktree`) removed. `zeph-skills/miner` no longer synthesizes five implicit + `dep:`-less features (`anyhow`, `clap`, `dirs`, `toml`, `tracing-subscriber`) — replaced with + explicit `dep:` prefixes. `cargo metadata` feature count: 184 → 157 (#6747). ### Security diff --git a/Cargo.toml b/Cargo.toml index abe94f1de..1ff362b4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,7 +92,7 @@ rand = "0.10.2" rand_distr = "0.6" ratatui = "0.30.2" regex = "1.13.1" -reqwest = { version = "0.13.4", default-features = false } +reqwest = { version = "0.13.4", default-features = false, features = ["query"] } ripemd = "0.2" rmcp = "3.1.2" rubato = "5.0.0" @@ -243,20 +243,19 @@ readme = "README.md" default = ["scheduler", "sqlite"] # === Use-case bundles === -desktop = ["tui", "deep-link", "session"] +desktop = ["tui", "session", "index"] ide = ["acp", "acp-http"] server = ["gateway", "a2a", "otel", "prometheus", "session"] chat = ["discord", "slack"] ml = ["candle", "pdf"] -full = ["desktop", "ide", "server", "chat", "pdf", "scheduler", "classifiers", "profiling", "sandbox", "gonka", "cocoon", "testing", "registry"] +full = ["desktop", "ide", "server", "chat", "pdf", "scheduler", "classifiers", "profiling", "sandbox", "gonka"] testing = ["zeph-llm/testing"] sandbox = ["zeph-tools/sandbox"] bench = ["dep:zeph-bench"] # === Individual feature flags === -deep-link = ["zeph-common/deep-link", "zeph-config/deep-link"] a2a = ["dep:zeph-a2a", "zeph-a2a?/server", "zeph-a2a?/ibct", "zeph-a2a?/card-signing", "zeph-config/card-signing"] -acp = ["dep:zeph-acp", "zeph-acp/unstable-session-delete", "zeph-acp/unstable-session-fork", "zeph-acp/unstable-session-resume", "zeph-acp/unstable-session-usage", "zeph-acp/unstable-elicitation", "zeph-acp/unstable-llm-providers", "zeph-acp/unstable-logout", "zeph-acp/unstable-auth-methods", "zeph-acp/unstable-message-id", "zeph-acp/unstable-session-add-dirs"] +acp = ["dep:zeph-acp", "zeph-acp/unstable-session-fork", "zeph-acp/unstable-session-usage", "zeph-acp/unstable-elicitation", "zeph-acp/unstable-llm-providers", "zeph-acp/unstable-auth-methods"] acp-http = ["acp", "dep:axum", "zeph-acp/acp-http"] candle = ["zeph-llm/candle", "zeph-core/candle"] classifiers = ["candle", "zeph-llm/classifiers", "zeph-sanitizer/classifiers", "zeph-core/classifiers"] @@ -267,10 +266,7 @@ discord = ["zeph-channels/discord"] slack = ["zeph-channels/slack"] gateway = ["dep:zeph-gateway"] prometheus = ["gateway", "dep:prometheus-client", "zeph-gateway/prometheus"] -scheduler = ["dep:zeph-scheduler", "dep:blake3", "dep:cron", "dep:schemars", "zeph-core/scheduler", "zeph-scheduler/daemon", "zeph-memory/scheduler"] -# Skill/plugin marketplace discovery (spec-045, #5869): gates zeph-plugins' `marketplace` -# module (skills.sh registry client). CLI args and RegistryConfig parsing always compile. -registry = ["zeph-plugins/registry"] +scheduler = ["dep:zeph-scheduler", "dep:blake3", "dep:cron", "dep:schemars", "zeph-core/scheduler", "zeph-memory/scheduler"] # Session persistence, event-log replay, and `zeph serve` (spec-068, #5343). Independent of `acp`: # `acp` gates ACP protocol transports, `session` gates the persistence CLI verbs and `zeph serve`. # The `zeph-session` crate/agent-loop dual-write is always compiled and config-gated @@ -282,7 +278,6 @@ profiling = [ "dep:tracing-chrome", "dep:sysinfo", "zeph-core/profiling", - "zeph-core/sysinfo", "zeph-llm/profiling", "zeph-memory/profiling", "zeph-tools/profiling", @@ -306,7 +301,6 @@ profiling-pyroscope = ["profiling", "otel", "dep:pprof"] # cargo build --no-default-features --features full,sqlite # Or simply `cargo build --features full` to rely on the default sqlite backend. gonka = ["zeph-llm/gonka", "zeph-core/gonka"] -cocoon = ["zeph-llm/cocoon", "zeph-core/cocoon", "zeph-tui?/cocoon"] index = ["zeph-core/index"] sqlite = [ "zeph-db/sqlite", @@ -443,7 +437,7 @@ wiremock.workspace = true zeph-core.workspace = true zeph-llm = { workspace = true, features = ["testing"] } zeph-memory.workspace = true -zeph-plugins = { workspace = true, features = ["registry", "mock"] } +zeph-plugins = { workspace = true, features = ["mock"] } zeph-skills.workspace = true # Features are declared by the crates that use them, see workspace dependencies diff --git a/book/src/advanced/acp.md b/book/src/advanced/acp.md index 7eab7b420..827d53a75 100644 --- a/book/src/advanced/acp.md +++ b/book/src/advanced/acp.md @@ -872,7 +872,6 @@ Each feature adds a standard ACP protocol method or notification to the agent's |--------------|---------------------------|-------------| | `unstable-session-list` | `list_sessions` | Enumerate in-memory sessions. Accepts an optional `cwd` filter; returns session ID, working directory, and last-updated timestamp for each matching session. | | `unstable-session-fork` | `fork_session` | Clone an existing session's persisted event history into a new session and immediately spawn a fresh agent loop from that checkpoint. The source session continues unaffected. | -| `unstable-session-resume` | `resume_session` | Reattach to a session that exists in SQLite but is not currently active in memory. Spawns an agent loop without replaying historical events. Useful for continuing a session after a Zeph restart. | | `unstable-session-usage` | `UsageUpdate` in `PromptResponse` | Include token consumption data (input tokens, output tokens, cache read/write tokens) in each prompt response. IDEs use this to display per-turn and cumulative cost estimates. | | `unstable-session-model` | `set_session_model` | Allow the IDE to switch the active LLM model mid-session via a model picker UI. Zeph emits a `SetSessionModel` notification so the IDE can reflect the change immediately. | | `unstable-session-info-update` | `SessionInfoUpdate` | Zeph automatically generates a short title for the session after the first exchange and emits a `SessionInfoUpdate` notification. IDEs display this as the conversation title in their session list. | @@ -881,6 +880,8 @@ The composite flag `acp-unstable` (root crate) enables all six at once. > **Note:** These features are gated on the `zeph-acp` crate. Each flag also enables the corresponding feature in the `agent-client-protocol` dependency. Stability and wire format are not guaranteed across minor versions until promoted to stable. +> **Note:** `unstable-session-resume` was removed entirely in the 2026-08 feature-flag audit rather than kept as a no-op tombstone — `session/resume` is stabilized upstream and now compiles unconditionally. + ### Enabling the features Enable individual flags: @@ -888,7 +889,6 @@ Enable individual flags: ```bash cargo build --features unstable-session-list cargo build --features unstable-session-fork -cargo build --features unstable-session-resume cargo build --features unstable-session-usage cargo build --features unstable-session-model cargo build --features unstable-session-info-update @@ -907,7 +907,6 @@ When embedding `zeph-acp` as a library dependency: zeph-acp = { version = "...", features = [ "unstable-session-list", "unstable-session-fork", - "unstable-session-resume", "unstable-session-usage", "unstable-session-model", "unstable-session-info-update", @@ -963,7 +962,7 @@ The source session remains active and unchanged. Both sessions are independent a ### resume_session -When `unstable-session-resume` is active, the agent advertises `resume` in `session_capabilities`. The IDE can call `resume_session` to reattach to a previously persisted session. +The agent advertises `resume` in `session_capabilities`. The IDE can call `resume_session` to reattach to a previously persisted session. The resume operation: @@ -1438,7 +1437,7 @@ Restrict which filesystem paths an ACP session is allowed to access: additional_directories = ["/workspace", "/tmp"] ``` -Requests to access paths outside this list are rejected at session start. Feature-gated by `unstable-session-add-dirs`. +Requests to access paths outside this list are rejected at session start. ### Auth Methods Configuration @@ -1460,8 +1459,6 @@ When enabled, the client's `message_id` from the prompt is echoed back on all st message_ids_enabled = true ``` -Feature-gated by `unstable-message-id`. - ### CLI Overrides All three ACP configuration options can be overridden at runtime: diff --git a/book/src/reference/configuration.md b/book/src/reference/configuration.md index 11a671a1b..a6ee998fa 100644 --- a/book/src/reference/configuration.md +++ b/book/src/reference/configuration.md @@ -730,7 +730,7 @@ discovery_enabled = true # Expose GET /.well-known/acp.json manifest e available_models = [] # Models advertised to IDE for switching: ["claude:claude-sonnet-5", "ollama:llama3"] (default: []) additional_directories = [] # Extra workspace directories ACP clients may reference (default: []) auth_methods = ["agent"] # Auth methods advertised in initialize (currently only "agent" supported) (default: ["agent"]) -message_ids_enabled = true # Echo PromptRequest.message_id onto responses (requires unstable-message-id feature) (default: true) +message_ids_enabled = true # Echo PromptRequest.message_id onto responses (default: true) [acp.model_config] default_temperature_preset = "balanced" # Default sampling-temperature: "precise" (0.2), "balanced" (0.7), or "creative" (1.0) (default: "balanced") diff --git a/book/src/reference/feature-flags.md b/book/src/reference/feature-flags.md index 63ab97b7b..fa77aa03e 100644 --- a/book/src/reference/feature-flags.md +++ b/book/src/reference/feature-flags.md @@ -8,12 +8,12 @@ Bundles are named Cargo features that group individual flags by deployment scena | Bundle | Included Features | Description | |--------|-------------------|-------------| -| `desktop` | `tui` | Interactive desktop agent with TUI dashboard | +| `desktop` | `tui`, `session`, `index` | Interactive desktop agent with TUI dashboard, session persistence, and AST-based code indexing | | `ide` | `acp`, `acp-http` | IDE integration via ACP (Zed, Helix, VS Code) | -| `server` | `gateway`, `a2a`, `otel` | Headless server deployment: HTTP webhook gateway, A2A agent protocol, OpenTelemetry tracing | +| `server` | `gateway`, `a2a`, `otel`, `prometheus`, `session` | Headless server deployment: HTTP webhook gateway, A2A agent protocol, OpenTelemetry tracing, Prometheus metrics, session persistence | | `chat` | `discord`, `slack` | Chat platform adapters | | `ml` | `candle`, `pdf` | Local ML inference (HuggingFace GGUF) and PDF document loading | -| `full` | `desktop` + `ide` + `server` + `chat` + `pdf` + `scheduler` + `classifiers` | All optional features except `candle`, `metal`, and `cuda` (hardware-specific) | +| `full` | `desktop` + `ide` + `server` + `chat` + `pdf` + `scheduler` + `classifiers` + `profiling` + `sandbox` + `gonka` | Everything intended to ship in a release binary, except hardware-exclusive (`metal`, `cuda`, `postgres`) and dev-only harness features (`bench`, `testing`) | ### Bundle build examples @@ -25,14 +25,17 @@ cargo build --release --features desktop,server # combined: TUI + server cargo build --release --features ml # local model inference cargo build --release --features ml,metal # local inference with Metal GPU (macOS) cargo build --release --features ml,cuda # local inference with CUDA GPU (Linux) -cargo build --release --features full # all optional features (CI / release builds) +cargo build --release --features full # everything except hardware-exclusive/dev-only features cargo build --release --features full,ml # everything including local inference +cargo build --release --features full,testing # full plus mock LLM providers for testing ``` > Bundles are purely additive. All existing `--features tui,scheduler` style builds continue to work unchanged. > **No `cli` bundle**: the default build (`cargo build --release`, no features) already represents the minimal CLI use case. A separate `cli` bundle would be a no-op alias. +> **`full` does not imply `testing`**: mock LLM providers are a dev-only test double, not something a release binary should ship with `full` alone. Add `testing` explicitly if you need `zeph-llm`'s mock provider outside `cargo test`. + ## Built-In Capabilities (always compiled, no feature flag required) The following capabilities compile unconditionally into every build. They are **not** Cargo feature flags — there is no `#[cfg(feature)]` gate and no way to disable them. They are listed here for reference only. @@ -47,9 +50,8 @@ The following capabilities compile unconditionally into every build. They are ** | Qdrant integration | Qdrant-backed vector storage for skill matching and MCP tool registry | | Age vault | Age-encrypted vault backend for file-based secret storage ([age](https://age-encryption.org/)) | | MCP client | MCP client for external tool servers via stdio/HTTP transport | -| Mock providers | Mock providers and channels for integration testing | | Daemon supervisor | Daemon supervisor with component lifecycle, PID file, and health monitoring | -| Task orchestration | DAG-based execution with failure strategies and SQLite persistence | +| Task orchestration | DAG-based execution with failure strategies and SQLite persistence, including LLM-backed planning/verification | | Graph memory | SQLite-based knowledge graph with entity-relationship tracking and BFS traversal | | Guardrail | Content sanitization, PII filtering, exfiltration guard, and quarantine | | Context compression | Reactive and focus-driven context compaction with summarization | @@ -59,6 +61,9 @@ The following capabilities compile unconditionally into every build. They are ** | Experiments | Autonomous self-experimentation engine with LLM-as-judge evaluation | | Bundled skills | SKILL.md files compiled into the binary via `include_dir` | | Speech-to-text | OpenAI Whisper API transcription for audio input | +| `zeph://` deep-link URI dispatch | OS-level `zeph://` scheme registration and prompt injection (spec #066); registration itself remains opt-in at the user level via `--init` or a CLI subcommand | +| Cocoon inference provider | TEE-sidecar confidential-compute provider (spec #055) | +| Skill/plugin registry marketplace | `zeph skill search`/`get` and `zeph plugin search`/`get` against external registries (e.g. skills.sh); opt-in via config, no network calls by default (spec #045-adjacent, #5869) | ## Optional Features @@ -70,70 +75,67 @@ The following capabilities compile unconditionally into every build. They are ** | `cuda` | CUDA GPU acceleration for candle on Linux — implies `candle` | | `discord` | Discord channel adapter with Gateway v10 WebSocket and slash commands ([guide](../advanced/channels.md#discord-channel)) | | `slack` | Slack channel adapter with Events API webhook and HMAC-SHA256 verification ([guide](../advanced/channels.md#slack-channel)) | -| `acp` | ACP (Agent Client Protocol) server over stdio for IDE embedding — includes all `unstable-session-*` handlers (Zed, Helix, VS Code) ([guide](../advanced/acp.md)) | +| `acp` | ACP (Agent Client Protocol) server over stdio for IDE embedding — includes the stabilised-upstream `unstable-session-*` handlers (Zed, Helix, VS Code) ([guide](../advanced/acp.md)) | | `acp-http` | ACP server over HTTP+SSE and WebSocket transport — implies `acp` ([guide](../advanced/acp.md#http-transport)) | | `a2a` | [A2A protocol](https://github.com/a2aproject/A2A) client and server for agent-to-agent communication | | `gateway` | HTTP gateway for webhook ingestion with bearer auth and rate limiting ([guide](../advanced/gateway.md)) | +| `prometheus` | OpenMetrics `/metrics` endpoint — implies `gateway` | | `scheduler` | Cron-based periodic task scheduler with SQLite persistence, including the `update_check` handler for automatic version notifications ([guide](../advanced/daemon.md#cron-scheduler)) | -| `registry` | Skill and plugin discovery-and-install marketplace via external registries (e.g., skills.sh) — provides `zeph skill search`/`get` and `zeph plugin search`/`get` commands; opt-in via config, no network calls by default ([guide](../concepts/skills.md#skill--plugin-registry-discovery)) | +| `session` | Session persistence, event-log replay, and `zeph serve`'s HTTP/SSE session API (spec #068) | | `otel` | OpenTelemetry tracing export via OTLP/gRPC ([guide](../advanced/observability.md)) | | `pdf` | PDF document loading via [pdf-extract](https://crates.io/crates/pdf-extract) for the document ingestion pipeline | | `classifiers` | ML-based content classifiers via local candle inference (implies `candle`) | +| `index` | AST-based code indexing, semantic retrieval, and repo map generation (spec #017) | +| `gonka` | gonka.ai decentralized inference provider (specs #051, #052) | +| `profiling` | Diagnostic tracing spans (Chrome trace format) and system metrics via `sysinfo`; zero overhead when not actively tracing | +| `profiling-alloc` | Per-span heap allocation counters — implies `profiling` | +| `profiling-pyroscope` | Continuous profiling export to Pyroscope — implies `profiling` and `otel` | +| `sandbox` | Linux `landlock`/`seccompiler` and macOS Seatbelt tool-execution sandboxing; runtime-disabled by default (`tools.sandbox.enabled = false`) | +| `testing` | Mock LLM provider test doubles (`zeph-llm/testing`) — dev-only, not in `full` | +| `bench` | Benchmark harness CLI (spec #034) — dev-only, not in `full` | | `sqlite` | SQLite database backend via `sqlx` (enabled by default) | | `postgres` | PostgreSQL database backend via `sqlx` — mutually exclusive with `sqlite`; activating both causes a compile error. Use `--no-default-features --features postgres` to switch | > [!IMPORTANT] -> `--all-features` activates both `sqlite` and `postgres` simultaneously, which triggers a `compile_error!` in `zeph-db`. Use `--features full` for local development instead. +> `--all-features` activates both `sqlite` and `postgres` simultaneously, which triggers a `compile_error!` in `zeph-db`. Use `--features full` for local development instead (it defaults to `sqlite` via the crate's default features; add `postgres` explicitly with `--no-default-features --features full,postgres` for a Postgres build). ## Crate-Level Features -Some workspace crates expose their own feature flags for fine-grained control: - -| Crate | Feature | Default | Description | -|-------|---------|---------|-------------| -| `zeph-llm` | `schema` | on | Enables `schemars` dependency and typed output API (`chat_typed`, `Extractor`, `cached_schema`) | -| `zeph-acp` | `unstable-session-list` | on | `list_sessions` RPC handler — enumerate in-memory sessions (unstable, see [ACP guide](../advanced/acp.md#list_sessions)) | -| `zeph-acp` | `unstable-session-fork` | on | `fork_session` RPC handler — clone session history into a new session (unstable, see [ACP guide](../advanced/acp.md#fork_session)) | -| `zeph-acp` | `unstable-session-resume` | on | `resume_session` RPC handler — reattach to a persisted session without replaying events (unstable, see [ACP guide](../advanced/acp.md#resume_session)) | -| `zeph-acp` | `unstable-session-usage` | on | `UsageUpdate` session notification — per-turn token consumption (`used`/`size`) sent after each LLM response; IDEs that handle this event render a context window badge (unstable, see [ACP guide](../advanced/acp.md#usage-tracking-unstable-session-usage)) | -| `zeph-acp` | `unstable-session-model` | on | `set_session_model` handler — IDE model picker support; emits `SetSessionModel` notification on switch (unstable, see [ACP guide](../advanced/acp.md#model-picker-unstable-session-model)) | -| `zeph-acp` | `unstable-session-info-update` | on | `SessionInfoUpdate` notification — auto-generated session title emitted after the first exchange (unstable, see [ACP guide](../advanced/acp.md#session-title-unstable-session-info-update)) | - -### ACP session management (unstable) +`zeph-acp` exposes its own `unstable-*` flags for ACP protocol surface still marked unstable upstream. The `acp` feature in the root crate enables all of them automatically — there is no separate `acp-unstable` flag. -The `unstable-session-*` flags gate ACP session lifecycle handlers and IDE integration features that depend on draft ACP spec additions. They are enabled by default but the API surface may change before the spec stabilises. Each flag also enables the corresponding feature in `agent-client-protocol` so the SDK advertises the capability during `initialize`. +| Crate | Feature | In `acp`? | Description | +|-------|---------|-----------|-------------| +| `zeph-acp` | `unstable-session-fork` | yes | `session/fork` — clone session history into a new session | +| `zeph-acp` | `unstable-session-usage` | yes | `UsageUpdate` session notification — per-turn token consumption sent after each LLM response | +| `zeph-acp` | `unstable-elicitation` | yes | `elicitation/create` — structured user-input requests mid-turn | +| `zeph-acp` | `unstable-llm-providers` | yes | LLM provider listing/switching extension | +| `zeph-acp` | `unstable-auth-methods` | yes | Auth-methods advertisement extension | +| `zeph-acp` | `unstable-cancel-request` | no | Wires the `$/cancel_request` notification onto the internal cancel signal — deliberate local opt-in, not enabled by `acp` or `default` (#5362) | -The `acp` feature in the root crate automatically enables all `unstable-session-*` flags in `zeph-acp`. There is no separate `acp-unstable` flag. +Session lifecycle handlers that were previously gated behind `unstable-session-delete`, `unstable-session-resume`, `unstable-logout`, `unstable-session-add-dirs`, and `unstable-message-id` compile unconditionally — the corresponding upstream ACP features stabilised, and the Zeph Cargo features were removed entirely rather than kept as no-op tombstones. -Disable all session management flags to build a minimal ACP server without them: +Disable all `unstable-*` handlers to build a minimal ACP server without them: ```bash cargo build -p zeph-acp --no-default-features ``` -Disable the `schema` feature to compile `zeph-llm` without `schemars`: - -```bash -cargo build -p zeph-llm --no-default-features -``` - ## Build Examples ```bash cargo build --release # default build (scheduler + sqlite + always-on features) -cargo build --release --features desktop # TUI dashboard -cargo build --release --features ide # ACP (includes all unstable-session-* flags) -cargo build --release --features server # gateway + a2a + otel +cargo build --release --features desktop # TUI dashboard + session + index +cargo build --release --features ide # ACP (includes the stabilised unstable-session-* handlers) +cargo build --release --features server # gateway + a2a + otel + prometheus + session cargo build --release --features desktop,server # combined desktop and server cargo build --release --features ml,metal # local inference with Metal GPU (macOS) cargo build --release --features ml,cuda # local inference with CUDA GPU (Linux) -cargo build --release --features full # all optional features (except candle/metal/cuda) +cargo build --release --features full # everything except hardware-exclusive/dev-only features +cargo build --release --features full,testing # full plus mock LLM providers cargo build --release --features tui # individual flag still works cargo build --release --features tui,a2a # combine individual flags freely ``` -The `full` feature enables every optional feature except `candle`, `metal`, and `cuda` (hardware-specific, opt-in). - ## Build Profiles | Profile | LTO | Codegen Units | Use Case | @@ -147,15 +149,3 @@ Build with the CI profile: ```bash cargo build --profile ci ``` - -## zeph-index Language Features - -Tree-sitter grammars are controlled by sub-features on the `zeph-index` crate (always-on). All are enabled by default. - -| Feature | Languages | -|---------|-----------| -| `lang-rust` | Rust | -| `lang-python` | Python | -| `lang-js` | JavaScript, TypeScript | -| `lang-go` | Go | -| `lang-config` | Bash, TOML, JSON, Markdown | diff --git a/config/default.toml b/config/default.toml index 1cc71e417..5c2309011 100644 --- a/config/default.toml +++ b/config/default.toml @@ -144,7 +144,7 @@ embedding_model = "qwen3-embedding" # max_tokens = 4096 # Cocoon — decentralized AI inference via local TEE sidecar (https://cocoon.org) -# Requires the Cocoon client runner at localhost:10000 (--features cocoon). +# Requires the Cocoon client runner at localhost:10000. # Set access hash in vault: zeph vault set ZEPH_COCOON_ACCESS_HASH # [[llm.providers]] # name = "cocoon" @@ -319,9 +319,7 @@ injection_patterns = true # External skill/plugin registry discovery (spec-045, #5869). Off by default — no network # call is ever made to any registry unless explicitly opted in below. See -# `zeph skill search --help` / `zeph plugin search --help`. Requires building with -# `--features registry` (included in `full`); a build without it prints an actionable -# message instead of erroring. +# `zeph skill search --help` / `zeph plugin search --help`. # [skills.registry] # enabled = false # backend_kind = "skills-sh" @@ -1150,7 +1148,6 @@ additional_directories = [] # MVP only accepts "agent"; unknown values fail startup rather than silently being skipped. auth_methods = ["agent"] # Echo PromptRequest.message_id onto PromptResponse.user_message_id and chunk events. -# Requires the `unstable-message-id` feature. message_ids_enabled = true [acp.lsp] diff --git a/crates/zeph-acp/Cargo.toml b/crates/zeph-acp/Cargo.toml index fcbb227bc..dd8ec596b 100644 --- a/crates/zeph-acp/Cargo.toml +++ b/crates/zeph-acp/Cargo.toml @@ -19,30 +19,17 @@ default = ["sqlite"] # builds in isolation, and expose `postgres` for PostgreSQL deployments (#4956). sqlite = ["zeph-memory/sqlite", "zeph-session/sqlite", "zeph-core/sqlite", "zeph-mcp/sqlite", "zeph-tools/sqlite"] postgres = ["zeph-memory/postgres", "zeph-session/postgres", "zeph-core/postgres", "zeph-mcp/postgres", "zeph-tools/postgres"] -acp-http = ["dep:axum", "dep:blake3", "dep:dashmap", "dep:async-stream", "dep:tower-http", "dep:subtle", "dep:tower", "zeph-common/deep-link"] -# session/close and session/delete stabilized in acp 0.14.0; no upstream feature gate needed -unstable-session-delete = [] +acp-http = ["dep:axum", "dep:blake3", "dep:dashmap", "dep:async-stream", "dep:tower-http", "dep:subtle", "dep:tower"] unstable-session-fork = ["agent-client-protocol/unstable_session_fork"] -# session/resume is stable in acp 0.12.1; no feature gate needed -unstable-session-resume = [] # renamed from unstable_session_usage in acp 0.14.0; Usage struct + PromptResponse.usage remain gated unstable-session-usage = ["agent-client-protocol/unstable_end_turn_token_usage"] unstable-elicitation = ["dep:agent-client-protocol-schema", "agent-client-protocol-schema/unstable_elicitation", "agent-client-protocol/unstable_elicitation"] unstable-llm-providers = ["dep:agent-client-protocol-schema", "agent-client-protocol-schema/unstable_llm_providers"] -# logout stabilized in acp 0.13.0; no upstream feature gate needed -unstable-logout = [] unstable-auth-methods = ["agent-client-protocol/unstable_auth_methods"] -# boolean session-config values stabilized in acp-schema 1.1.0 (SessionConfigOptionValue::Boolean -# is now unconditional); no upstream feature gate needed -unstable-boolean-config = [] # Wires the real $/cancel_request protocol notification onto the existing internal cancel_signal. # Cancellation itself was stabilized upstream in acp 1.1.0 (no feature gate needed there anymore); # this stays a local opt-in gate for the zeph-acp integration. Not in `default` — see #5362. unstable-cancel-request = [] -# message-id stabilized in acp 0.14.0; no upstream feature gate needed -unstable-message-id = [] -# session-add-dirs stabilized in acp 0.14.0; no upstream feature gate needed -unstable-session-add-dirs = [] [dependencies] agent-client-protocol = { workspace = true } diff --git a/crates/zeph-acp/README.md b/crates/zeph-acp/README.md index 104e4010c..724746b19 100644 --- a/crates/zeph-acp/README.md +++ b/crates/zeph-acp/README.md @@ -363,14 +363,17 @@ The `initialize` response includes an `auth_hint` key in its metadata map. For s |---------|--------|-------------| | `acp-http` | stable | Enables the HTTP+SSE and WebSocket transports (axum-based). Required for `post_handler`, `get_handler`, `ws_upgrade_handler`, and `router`. | | `unstable-session-fork` | unstable | Enables the `fork_session` ACP method. See below. | -| `unstable-session-resume` | unstable | Enables the `resume_session` ACP method. See below. | | `unstable-session-usage` | unstable | Enables `UsageUpdate` events — token counts (input, output, cache) sent to the IDE after each turn. See below. | | `unstable-elicitation` | unstable | Exposes elicitation schema types (`ElicitationRequest`, etc.) for future agent-loop integration. | | `unstable-llm-providers` | unstable | Exposes the `LlmProtocol` wire type for advertising available LLM providers to the IDE. | -| `unstable-logout` | unstable | Enables the `logout` ACP method and advertises `auth.logout` capability. Zeph logout is a no-op (vault-based auth). | | `unstable-auth-methods` | unstable | Enables the ACP auth-methods extension. | -| `unstable-cancel-request` | unstable | Wires the real `$/cancel_request` protocol notification onto the internal cancel signal. | -| `unstable-message-id` / `unstable-session-add-dirs` / `unstable-session-delete` / `unstable-boolean-config` | stable in SDK | Retained as no-op flags; the underlying methods/types stabilized upstream and need no gate. | +| `unstable-cancel-request` | unstable | Wires the real `$/cancel_request` protocol notification onto the internal cancel signal. Deliberate local opt-in, not in `default` (#5362). | + +`session/resume`, `session/delete`, `logout`, `additional_directories`, and the inbound +`message_id` echo compile unconditionally — `unstable-session-resume`, `unstable-session-delete`, +`unstable-logout`, `unstable-session-add-dirs`, `unstable-message-id`, and +`unstable-boolean-config` were removed entirely in the 2026-08 feature-flag audit rather than +kept as no-op tombstones. **Warning:** > All `unstable-*` features have wire protocol that is not yet finalized. Expect breaking changes before these features graduate to stable. @@ -381,10 +384,8 @@ To opt in, add the desired features in your `Cargo.toml`: [dependencies] zeph-acp = { version = "*", features = [ "unstable-session-fork", - "unstable-session-resume", "unstable-session-usage", "unstable-elicitation", - "unstable-logout", ] } ``` @@ -407,9 +408,11 @@ Enables the `fork_session` method. Branches an existing conversation into a new The forked session is immediately available for new turns. The event copy is fire-and-forget — if the store write fails, a warning is logged but the session is still created. Model config options are forwarded to the fork response when `available_models` is non-empty. -### `unstable-session-resume` +### `session/resume` -Enables the `resume_session` method. Restores a persisted session to an active in-memory state without replaying history as `session/update` events: +Compiles unconditionally (feature removed in the 2026-08 feature-flag audit — the upstream +gate stabilised). Restores a persisted session to an active in-memory state without replaying +history as `session/update` events: - If the session is already active in memory, returns success immediately (no-op). - Otherwise, verifies existence in SQLite and hydrates a new `SessionEntry`, making the session available for new turns with lower latency than the default `load_session` replay path. diff --git a/crates/zeph-acp/src/agent/handlers/close_session.rs b/crates/zeph-acp/src/agent/handlers/close_session.rs index 47760732e..1c31bb3ec 100644 --- a/crates/zeph-acp/src/agent/handlers/close_session.rs +++ b/crates/zeph-acp/src/agent/handlers/close_session.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2026 Andrei G // SPDX-License-Identifier: MIT OR Apache-2.0 -//! Handler for `session/close` (feature `unstable-session-delete`). +//! Handler for `session/close`. use std::sync::Arc; diff --git a/crates/zeph-acp/src/agent/handlers/delete_session.rs b/crates/zeph-acp/src/agent/handlers/delete_session.rs index 134b5c79e..0c4e50408 100644 --- a/crates/zeph-acp/src/agent/handlers/delete_session.rs +++ b/crates/zeph-acp/src/agent/handlers/delete_session.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2026 Andrei G // SPDX-License-Identifier: MIT OR Apache-2.0 -//! Handler for `session/delete` (feature `unstable-session-delete`). +//! Handler for `session/delete`. use std::sync::Arc; diff --git a/crates/zeph-acp/src/agent/handlers/logout.rs b/crates/zeph-acp/src/agent/handlers/logout.rs index 7b10e5e26..9403c893a 100644 --- a/crates/zeph-acp/src/agent/handlers/logout.rs +++ b/crates/zeph-acp/src/agent/handlers/logout.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2026 Andrei G // SPDX-License-Identifier: MIT OR Apache-2.0 -//! Handler for `logout` (feature `unstable-logout`). +//! Handler for `logout`. use std::sync::Arc; diff --git a/crates/zeph-acp/src/agent/handlers/resume_session.rs b/crates/zeph-acp/src/agent/handlers/resume_session.rs index 757b1b2bc..4f093cba9 100644 --- a/crates/zeph-acp/src/agent/handlers/resume_session.rs +++ b/crates/zeph-acp/src/agent/handlers/resume_session.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2026 Andrei G // SPDX-License-Identifier: MIT OR Apache-2.0 -//! Handler for `session/resume` (feature `unstable-session-resume`). +//! Handler for `session/resume`. use std::sync::Arc; diff --git a/crates/zeph-acp/src/agent/mod.rs b/crates/zeph-acp/src/agent/mod.rs index c41696888..fc1b0a5d8 100644 --- a/crates/zeph-acp/src/agent/mod.rs +++ b/crates/zeph-acp/src/agent/mod.rs @@ -675,11 +675,6 @@ impl ZephAcpAgentState { .sse(false), ); } - #[cfg(any( - feature = "unstable-session-delete", - feature = "unstable-session-fork", - feature = "unstable-session-resume", - ))] let caps = { let mut session_caps = acp::schema::v1::SessionCapabilities::new(); session_caps = session_caps.list(acp::schema::v1::SessionListCapabilities::default()); diff --git a/crates/zeph-acp/src/lib.rs b/crates/zeph-acp/src/lib.rs index 8462ae444..292af5475 100644 --- a/crates/zeph-acp/src/lib.rs +++ b/crates/zeph-acp/src/lib.rs @@ -36,12 +36,12 @@ //! | Flag | Description | //! |------|-------------| //! | `acp-http` | HTTP/SSE and WebSocket transports via axum | -//! | `unstable-session-delete` | ACP `session/close` and `session/delete` handlers | //! | `unstable-session-fork` | ACP session fork extension | -//! | `unstable-session-resume` | ACP session resume (stable since acp 0.12.1; no SDK gate needed) | //! | `unstable-session-usage` | ACP session token-usage extension | //! | `unstable-elicitation` | ACP elicitation schema types | -//! | `unstable-logout` | ACP logout extension | +//! | `unstable-llm-providers` | ACP LLM provider listing extension | +//! | `unstable-auth-methods` | ACP auth-methods extension | +//! | `unstable-cancel-request` | Wires `$/cancel_request` onto the internal cancel signal (#5362) | //! //! # Quick start (stdio) //! diff --git a/crates/zeph-agent-context/src/service.rs b/crates/zeph-agent-context/src/service.rs index bd5e98c98..6e6a8cd86 100644 --- a/crates/zeph-agent-context/src/service.rs +++ b/crates/zeph-agent-context/src/service.rs @@ -2033,6 +2033,8 @@ mod tests { skill_registry: registry, skill_paths: &[], correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: None, sanitizer: &sanitizer, @@ -2177,6 +2179,8 @@ mod tests { skill_registry: Arc::new(RwLock::new(SkillRegistry::default())), skill_paths: &[], correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: None, sanitizer, @@ -2417,6 +2421,8 @@ mod tests { skill_registry: registry, skill_paths: &[], correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: None, sanitizer: &sanitizer, @@ -2499,6 +2505,8 @@ mod tests { skill_registry: registry, skill_paths: &[], correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: None, sanitizer: &sanitizer, @@ -2588,6 +2596,8 @@ mod tests { skill_registry: registry, skill_paths: &[], correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: None, sanitizer: &sanitizer, @@ -2828,6 +2838,8 @@ mod tests { skill_registry: Arc::clone(&fixture.registry), skill_paths: &fixture.skill_paths, correction_config: None, + #[cfg(feature = "index")] + index: None, sidequest_turn_counter: 0, proactive_explorer: Some(Arc::clone(&fixture.explorer)), sanitizer, diff --git a/crates/zeph-commands/Cargo.toml b/crates/zeph-commands/Cargo.toml index 3f7c978fe..4c505d2e7 100644 --- a/crates/zeph-commands/Cargo.toml +++ b/crates/zeph-commands/Cargo.toml @@ -23,7 +23,6 @@ zeph-config.workspace = true tokio = { workspace = true, features = ["macros", "rt"] } [features] -cocoon = [] profiling = [] [lints] diff --git a/crates/zeph-commands/src/commands.rs b/crates/zeph-commands/src/commands.rs index e541394cf..515d4a5d9 100644 --- a/crates/zeph-commands/src/commands.rs +++ b/crates/zeph-commands/src/commands.rs @@ -280,7 +280,7 @@ pub const COMMANDS: &[CommandInfo] = &[ args: "[status | models]", description: "Inspect Cocoon sidecar (status, models)", category: SlashCategory::Integration, - feature_gate: Some("cocoon"), + feature_gate: None, }, CommandInfo { name: "/image", diff --git a/crates/zeph-commands/src/handlers/cocoon.rs b/crates/zeph-commands/src/handlers/cocoon.rs index 93067c18f..f33061b23 100644 --- a/crates/zeph-commands/src/handlers/cocoon.rs +++ b/crates/zeph-commands/src/handlers/cocoon.rs @@ -29,10 +29,6 @@ impl CommandHandler> for CocoonCommand { SlashCategory::Integration } - fn feature_gate(&self) -> Option<&'static str> { - Some("cocoon") - } - fn requires_auth(&self) -> bool { true } @@ -65,11 +61,6 @@ mod tests { assert_eq!(CocoonCommand.category(), SlashCategory::Integration); } - #[test] - fn feature_gate_is_cocoon() { - assert_eq!(CocoonCommand.feature_gate(), Some("cocoon")); - } - #[test] fn description_and_args_hint_non_empty() { assert!(!CocoonCommand.description().is_empty()); diff --git a/crates/zeph-commands/src/handlers/mod.rs b/crates/zeph-commands/src/handlers/mod.rs index 9847742e7..14f51f643 100644 --- a/crates/zeph-commands/src/handlers/mod.rs +++ b/crates/zeph-commands/src/handlers/mod.rs @@ -15,7 +15,6 @@ pub mod agents_fleet; pub mod caveman; pub mod cd; pub mod checkpoint; -#[cfg(feature = "cocoon")] pub mod cocoon; pub mod compaction; pub mod conv; diff --git a/crates/zeph-common/Cargo.toml b/crates/zeph-common/Cargo.toml index 61fdce0fe..d45e21689 100644 --- a/crates/zeph-common/Cargo.toml +++ b/crates/zeph-common/Cargo.toml @@ -14,7 +14,6 @@ description = "Shared utility functions and security primitives for Zeph crates" readme = "README.md" [features] -deep-link = [] http-middleware = ["dep:axum", "dep:subtle"] jsonschema = ["dep:schemars"] treesitter = [ diff --git a/crates/zeph-common/README.md b/crates/zeph-common/README.md index d3f819698..82037e708 100644 --- a/crates/zeph-common/README.md +++ b/crates/zeph-common/README.md @@ -69,7 +69,9 @@ assert!(!is_private_ip(addr)); // false — public IP | `treesitter` | Enables tree-sitter parser helpers and ts-query constants for Rust, Python, JavaScript, TypeScript, Go, Bash, TOML, JSON, Markdown | | `http-middleware` | Enables the `http_middleware` module (shared axum bearer-token auth with constant-time comparison and per-IP rate limiting) | | `jsonschema` | Derives `schemars::JsonSchema` on shared types (e.g. `memory` types) for JSON-schema generation | -| `deep-link` | Enables the `deep_link` module for parsing/handling `zeph://` deep links | + +The `deep_link` module (parsing/handling `zeph://` deep links) compiles unconditionally — the +`deep-link` feature was removed in the 2026-08 feature-flag audit. ## Installation diff --git a/crates/zeph-common/src/lib.rs b/crates/zeph-common/src/lib.rs index 240848c5d..8e6f13289 100644 --- a/crates/zeph-common/src/lib.rs +++ b/crates/zeph-common/src/lib.rs @@ -13,7 +13,6 @@ pub mod anchor; pub mod audit; pub mod clock; pub mod config; -#[cfg(feature = "deep-link")] pub mod deep_link; pub mod error_taxonomy; pub mod fidelity; diff --git a/crates/zeph-config/Cargo.toml b/crates/zeph-config/Cargo.toml index 53f486569..9918ec4ee 100644 --- a/crates/zeph-config/Cargo.toml +++ b/crates/zeph-config/Cargo.toml @@ -14,7 +14,6 @@ description = "Pure-data configuration types for Zeph" readme = "README.md" [features] -default = [] # Marker feature only (no deps) — mirrors `zeph-a2a/card-signing` so `Config::validate()` # can fail fast when `card_trust_policy = require` is set without the crypto feature # compiled in anywhere in the binary. MUST be enabled together with `zeph-a2a/card-signing` @@ -22,7 +21,6 @@ default = [] # splitting them would make `require` falsely fail config validation even when the crypto in # zeph-a2a actually works (#5928). card-signing = [] -deep-link = [] [dependencies] dirs.workspace = true diff --git a/crates/zeph-config/src/lib.rs b/crates/zeph-config/src/lib.rs index 887ede7ff..6f5d602b1 100644 --- a/crates/zeph-config/src/lib.rs +++ b/crates/zeph-config/src/lib.rs @@ -76,7 +76,6 @@ pub mod classifiers; pub mod cli; pub mod cocoon; mod de_helpers; -#[cfg(feature = "deep-link")] pub mod deep_link; pub mod defaults; pub mod dump_format; @@ -214,7 +213,6 @@ pub use worktree::{BgIsolation, WorktreeBaseRef, WorktreeConfig}; // Top-level config struct, error type, and resolved secrets — moved from zeph-core. pub use classifiers::{ClassifiersConfig, InjectionEnforcementMode}; -#[cfg(feature = "deep-link")] pub use deep_link::{AcpPreference, DeepLinkConfig}; pub use error::ConfigError; pub use root::{Config, ResolvedSecrets}; diff --git a/crates/zeph-config/src/root.rs b/crates/zeph-config/src/root.rs index 2e2af64f9..93ebfa413 100644 --- a/crates/zeph-config/src/root.rs +++ b/crates/zeph-config/src/root.rs @@ -166,8 +166,7 @@ pub struct Config { /// name-similarity/typosquat check (spec-043, #5864). #[serde(default)] pub plugins: crate::plugins::PluginsConfig, - /// Deep-link scheme configuration (`[deep_link]`). Gated by the `deep-link` feature. - #[cfg(feature = "deep-link")] + /// Deep-link scheme configuration (`[deep_link]`). #[serde(default)] pub deep_link: crate::deep_link::DeepLinkConfig, } @@ -404,7 +403,6 @@ impl Default for Config { caveman: crate::features::CavemanConfig::default(), knowledge: crate::knowledge::KnowledgeConfig::default(), plugins: crate::plugins::PluginsConfig::default(), - #[cfg(feature = "deep-link")] deep_link: crate::deep_link::DeepLinkConfig::default(), } } diff --git a/crates/zeph-config/src/ui.rs b/crates/zeph-config/src/ui.rs index 271c9e3e5..fefab3268 100644 --- a/crates/zeph-config/src/ui.rs +++ b/crates/zeph-config/src/ui.rs @@ -738,7 +738,7 @@ pub struct AcpConfig { /// Echo `PromptRequest.message_id` onto `PromptResponse.user_message_id` and every /// streamed chunk, enabling IDE-side correlation. /// - /// Requires the `unstable-message-id` feature. Default: `true`. + /// Default: `true`. #[serde(default = "default_true")] pub message_ids_enabled: bool, /// Sub-agent delegation configuration (`[acp.subagents]`). diff --git a/crates/zeph-core/Cargo.toml b/crates/zeph-core/Cargo.toml index 9ff4033df..fe7ded961 100644 --- a/crates/zeph-core/Cargo.toml +++ b/crates/zeph-core/Cargo.toml @@ -19,16 +19,14 @@ candle = ["zeph-llm/candle"] classifiers = ["zeph-llm/classifiers", "zeph-sanitizer/classifiers"] cuda = ["zeph-llm/cuda"] gonka = ["zeph-llm/gonka"] -cocoon = ["zeph-llm/cocoon", "zeph-commands/cocoon"] index = ["zeph-agent-context/index"] metal = ["zeph-llm/metal"] mock = ["zeph-vault/mock"] postgres = ["zeph-db/postgres", "zeph-agent-context/postgres", "zeph-agent-persistence/postgres", "zeph-durable/postgres", "zeph-session/postgres", "zeph-index/postgres", "zeph-mcp/postgres", "zeph-memory/postgres", "zeph-orchestration/postgres", "zeph-sanitizer/postgres", "zeph-skills/postgres", "zeph-subagent/postgres", "zeph-tools/postgres", "zeph-experiments/postgres"] -profiling = ["dep:tracing-subscriber", "zeph-commands/profiling"] +profiling = ["dep:tracing-subscriber", "dep:sysinfo", "zeph-commands/profiling"] profiling-alloc = ["profiling"] scheduler = [] sqlite = ["zeph-db/sqlite", "zeph-agent-context/sqlite", "zeph-agent-persistence/sqlite", "zeph-durable/sqlite", "zeph-session/sqlite", "zeph-index/sqlite", "zeph-mcp/sqlite", "zeph-memory/sqlite", "zeph-orchestration/sqlite", "zeph-sanitizer/sqlite", "zeph-skills/sqlite", "zeph-subagent/sqlite", "zeph-tools/sqlite", "zeph-experiments/sqlite"] -sysinfo = ["dep:sysinfo"] [dependencies] base64.workspace = true @@ -79,7 +77,7 @@ zeph-index.workspace = true zeph-llm.workspace = true zeph-mcp.workspace = true zeph-memory.workspace = true -zeph-orchestration = { workspace = true, features = ["llm-planning"] } +zeph-orchestration.workspace = true zeph-plugins.workspace = true zeph-sanitizer.workspace = true zeph-session = { workspace = true, default-features = false } diff --git a/crates/zeph-core/src/agent/acp_commands.rs b/crates/zeph-core/src/agent/acp_commands.rs index 7d8524544..e314b53f8 100644 --- a/crates/zeph-core/src/agent/acp_commands.rs +++ b/crates/zeph-core/src/agent/acp_commands.rs @@ -8,7 +8,6 @@ use std::pin::Pin; use tracing::Instrument as _; use zeph_commands::{CommandError, IntegrationAccess}; -#[cfg(feature = "cocoon")] use super::command_macros::delegate_cmd; use super::{Agent, error::AgentError}; use crate::channel::Channel; @@ -323,18 +322,7 @@ impl IntegrationAccess for Agent { // ----- /cocoon ----- - #[cfg(feature = "cocoon")] delegate_cmd!(handle_cocoon, handle_cocoon_as_string, args: &'a str => String); - - #[cfg(not(feature = "cocoon"))] - fn handle_cocoon<'a>( - &'a mut self, - _args: &'a str, - ) -> Pin> + Send + 'a>> { - Box::pin(async { - Ok("Cocoon support is not compiled in. Rebuild with `--features cocoon`.".to_owned()) - }) - } } #[cfg(test)] diff --git a/crates/zeph-core/src/agent/mod.rs b/crates/zeph-core/src/agent/mod.rs index 6c42ff3d9..b1f023538 100644 --- a/crates/zeph-core/src/agent/mod.rs +++ b/crates/zeph-core/src/agent/mod.rs @@ -8,7 +8,6 @@ mod autodream; mod autonomous_turn; mod builder; pub use builder::{SecurityWiringSnapshot, SkillConfigParams}; -#[cfg(feature = "cocoon")] mod cocoon_cmd; mod command_context_impls; mod command_macros; diff --git a/crates/zeph-core/src/agent/slash_commands.rs b/crates/zeph-core/src/agent/slash_commands.rs index a7541edd3..ceb38f954 100644 --- a/crates/zeph-core/src/agent/slash_commands.rs +++ b/crates/zeph-core/src/agent/slash_commands.rs @@ -860,7 +860,6 @@ pub(crate) fn build_agent_command_registry<'ctx>() agent_reg.register(LoopCommand); agent_reg.register(PluginsCommand); agent_reg.register(AcpCommand); - #[cfg(feature = "cocoon")] agent_reg.register(zeph_commands::handlers::cocoon::CocoonCommand); agent_reg.register(TrajectoryCommand); agent_reg.register(ScopeCommand); diff --git a/crates/zeph-core/src/lib.rs b/crates/zeph-core/src/lib.rs index 324a65218..b13cc81b6 100644 --- a/crates/zeph-core/src/lib.rs +++ b/crates/zeph-core/src/lib.rs @@ -95,7 +95,7 @@ pub mod quality; pub mod redact; pub mod serve; pub mod session_resume; -#[cfg(feature = "sysinfo")] +#[cfg(feature = "profiling")] pub mod system_metrics; pub mod http; diff --git a/crates/zeph-core/src/provider_factory.rs b/crates/zeph-core/src/provider_factory.rs index 2316e480e..331ee13b9 100644 --- a/crates/zeph-core/src/provider_factory.rs +++ b/crates/zeph-core/src/provider_factory.rs @@ -10,7 +10,6 @@ use zeph_llm::any::AnyProvider; use zeph_llm::claude::ClaudeProvider; -#[cfg(feature = "cocoon")] use zeph_llm::cocoon::{CocoonClient, CocoonProvider}; use zeph_llm::compatible::CompatibleProvider; use zeph_llm::gemini::GeminiProvider; @@ -156,12 +155,7 @@ fn build_provider_from_entry_inner( ProviderKind::Gonka => Err(BootstrapError::Provider( "gonka feature is not enabled; rebuild with --features gonka".into(), )), - #[cfg(feature = "cocoon")] ProviderKind::Cocoon => build_cocoon_provider(entry, config), - #[cfg(not(feature = "cocoon"))] - ProviderKind::Cocoon => Err(BootstrapError::Provider( - "cocoon feature is not enabled; rebuild with --features cocoon".into(), - )), _ => Err(BootstrapError::Provider(format!( "unknown provider kind: {:?}", entry.provider_type @@ -533,7 +527,6 @@ fn build_gonka_provider( /// Resolved connection parameters for a `CocoonClient`, shared by the provider-build and /// health-check call sites so both derive `access_hash`/`base_url`/`timeout` with identical /// gating logic. -#[cfg(feature = "cocoon")] struct CocoonClientParams { base_url: String, access_hash: Option, @@ -551,7 +544,6 @@ struct CocoonClientParams { /// /// Returns [`BootstrapError::Provider`] when the vault key `ZEPH_COCOON_ACCESS_HASH` is /// expected (field is `Some`) but not present in the resolved secrets. -#[cfg(feature = "cocoon")] fn resolve_cocoon_client_params( entry: &ProviderEntry, config: &Config, @@ -622,7 +614,6 @@ fn resolve_cocoon_client_params( /// /// Returns [`BootstrapError::Provider`] when the vault key `ZEPH_COCOON_ACCESS_HASH` is /// expected (field is `Some`) but not present in the resolved secrets. -#[cfg(feature = "cocoon")] fn build_cocoon_provider( entry: &ProviderEntry, config: &Config, @@ -658,9 +649,8 @@ fn build_cocoon_provider( /// path is advisory and runs after providers have already been built successfully. /// /// Call this once after [`build_provider_from_entry`] has succeeded for all providers, passing -/// the session-level supervisor. The function is a no-op when `cocoon` feature is not enabled -/// or no provider has `cocoon_health_check = true`. -#[cfg(feature = "cocoon")] +/// the session-level supervisor. The function is a no-op when no provider has +/// `cocoon_health_check = true`. pub fn spawn_cocoon_health_checks( providers: &[&ProviderEntry], config: &Config, @@ -938,7 +928,6 @@ mod tests { assert!(params.embedding_sha256.is_none()); } - #[cfg(feature = "cocoon")] use super::spawn_cocoon_health_checks; use super::{build_provider_from_entry, resolve_named_provider}; use crate::config::{Config, ProviderKind}; @@ -1242,7 +1231,6 @@ mod tests { assert_eq!(provider.name(), "claude"); } - #[cfg(feature = "cocoon")] mod cocoon_tests { use super::*; diff --git a/crates/zeph-llm/Cargo.toml b/crates/zeph-llm/Cargo.toml index 2bc4ef01c..1e86b37e8 100644 --- a/crates/zeph-llm/Cargo.toml +++ b/crates/zeph-llm/Cargo.toml @@ -19,7 +19,6 @@ candle = ["dep:audioadapter-buffers", "dep:candle-core", "dep:candle-nn", "dep:c classifiers = ["candle"] cuda = ["candle", "candle-core/cuda", "candle-nn/cuda", "candle-transformers/cuda"] gonka = ["dep:bech32", "dep:k256", "dep:ripemd", "dep:hex", "dep:sha2", "dep:zeroize"] -cocoon = [] metal = ["candle", "candle-core/metal", "candle-nn/metal", "candle-transformers/metal"] testing = [] diff --git a/crates/zeph-llm/src/any.rs b/crates/zeph-llm/src/any.rs index 7198e8ffb..adc2db006 100644 --- a/crates/zeph-llm/src/any.rs +++ b/crates/zeph-llm/src/any.rs @@ -23,7 +23,6 @@ #[cfg(feature = "candle")] use crate::candle_provider::CandleProvider; use crate::claude::ClaudeProvider; -#[cfg(feature = "cocoon")] use crate::cocoon::CocoonProvider; use crate::compatible::CompatibleProvider; use crate::gemini::GeminiProvider; @@ -62,7 +61,6 @@ macro_rules! delegate_provider { AnyProvider::Triage($p) => $expr, #[cfg(feature = "gonka")] AnyProvider::Gonka($p) => $expr, - #[cfg(feature = "cocoon")] AnyProvider::Cocoon($p) => $expr, #[cfg(any(test, feature = "testing"))] AnyProvider::Mock($p) => $expr, @@ -104,9 +102,6 @@ pub enum AnyProvider { #[cfg(feature = "gonka")] Gonka(GonkaProvider), /// Cocoon confidential compute provider — routes requests through the TEE sidecar. - /// - /// Only available when the `cocoon` feature is enabled. - #[cfg(feature = "cocoon")] Cocoon(CocoonProvider), /// A mock provider for use in tests and benchmarks. /// @@ -330,7 +325,6 @@ impl AnyProvider { #[cfg(feature = "gonka")] AnyProvider::Gonka(_) => Ok(vec![]), // Cocoon model discovery is done via CocoonClient::list_models(), not LlmProvider. - #[cfg(feature = "cocoon")] AnyProvider::Cocoon(_) => Ok(vec![]), #[cfg(any(test, feature = "testing"))] AnyProvider::Mock(p) => Ok(p.models.clone()), @@ -384,7 +378,6 @@ impl AnyProvider { #[cfg(feature = "gonka")] Self::Gonka(_) => "cloud", // Cocoon is a metered TEE network — treat as cloud for cost tracking. - #[cfg(feature = "cocoon")] Self::Cocoon(_) => "cloud", Self::Masked(p) => p.inner().provider_kind_str(), _ => "cloud", @@ -475,7 +468,6 @@ impl AnyProvider { } #[cfg(feature = "gonka")] Self::Gonka(p) => Self::Gonka(p.with_generation_overrides(overrides)), - #[cfg(feature = "cocoon")] Self::Cocoon(p) => Self::Cocoon(p.with_generation_overrides(overrides)), Self::Router(_) | Self::Triage(_) => { tracing::warn!("generation overrides not supported for this provider variant"); @@ -729,7 +721,6 @@ impl AnyProvider { Self::Gonka(p) => { p.set_status_tx(tx); } - #[cfg(feature = "cocoon")] Self::Cocoon(p) => { p.set_status_tx(tx); } @@ -1315,7 +1306,6 @@ mod tests { assert_eq!(make_gonka().provider_kind_str(), "cloud"); } - #[cfg(feature = "cocoon")] fn make_cocoon() -> AnyProvider { use crate::cocoon::{CocoonClient, CocoonProvider}; use std::sync::Arc; @@ -1327,19 +1317,16 @@ mod tests { AnyProvider::Cocoon(CocoonProvider::new("Qwen/Qwen3-0.6B", 4096, None, client)) } - #[cfg(feature = "cocoon")] #[test] fn any_cocoon_name() { assert_eq!(make_cocoon().name(), "cocoon"); } - #[cfg(feature = "cocoon")] #[test] fn any_cocoon_supports_streaming() { assert!(make_cocoon().supports_streaming()); } - #[cfg(feature = "cocoon")] #[test] fn any_cocoon_provider_kind_str() { assert_eq!(make_cocoon().provider_kind_str(), "cloud"); diff --git a/crates/zeph-llm/src/cocoon/tests.rs b/crates/zeph-llm/src/cocoon/tests.rs index c4bcb59fe..7cd268d75 100644 --- a/crates/zeph-llm/src/cocoon/tests.rs +++ b/crates/zeph-llm/src/cocoon/tests.rs @@ -569,7 +569,6 @@ async fn cocoon_malformed_json_response() { // Live integration tests — require a running Cocoon sidecar // --------------------------------------------------------------------------- -#[cfg(feature = "cocoon")] mod integration { use std::sync::Arc; use std::time::Duration; diff --git a/crates/zeph-llm/src/lib.rs b/crates/zeph-llm/src/lib.rs index 4d4da5b4f..5adefb514 100644 --- a/crates/zeph-llm/src/lib.rs +++ b/crates/zeph-llm/src/lib.rs @@ -78,7 +78,6 @@ pub mod candle_provider; pub mod candle_whisper; pub mod classifier; pub mod claude; -#[cfg(feature = "cocoon")] pub mod cocoon; pub mod compatible; pub mod debug_dump; diff --git a/crates/zeph-llm/src/openai/mod.rs b/crates/zeph-llm/src/openai/mod.rs index 2fa789a7d..95e58a2a2 100644 --- a/crates/zeph-llm/src/openai/mod.rs +++ b/crates/zeph-llm/src/openai/mod.rs @@ -1290,7 +1290,6 @@ impl OpenAiProvider { /// # Errors /// /// Returns [`LlmError::StructuredParse`] if schema extraction or serialisation fails. - #[cfg(any(feature = "gonka", feature = "cocoon"))] pub(crate) fn build_typed_chat_body(&self, messages: &[Message]) -> Result, LlmError> where T: serde::de::DeserializeOwned + schemars::JsonSchema + 'static, diff --git a/crates/zeph-orchestration/Cargo.toml b/crates/zeph-orchestration/Cargo.toml index f7cd019be..94c9bb010 100644 --- a/crates/zeph-orchestration/Cargo.toml +++ b/crates/zeph-orchestration/Cargo.toml @@ -17,7 +17,7 @@ readme = "README.md" blake3.workspace = true chrono = { workspace = true, features = ["clock"] } dirs.workspace = true -futures = { workspace = true, optional = true } +futures.workspace = true parking_lot.workspace = true rand.workspace = true rand_distr.workspace = true @@ -34,11 +34,10 @@ zeph-common.workspace = true zeph-config.workspace = true zeph-db.workspace = true zeph-durable.workspace = true -zeph-llm = { workspace = true, optional = true } +zeph-llm.workspace = true zeph-subagent.workspace = true [dev-dependencies] -futures.workspace = true tempfile.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread", "test-util"] } tracing-test.workspace = true @@ -57,11 +56,8 @@ testcontainers.workspace = true [features] sqlite = ["zeph-db/sqlite", "zeph-durable/sqlite", "zeph-memory/sqlite", "zeph-subagent/sqlite"] postgres = ["zeph-db/postgres", "zeph-durable/postgres", "zeph-memory/postgres", "zeph-subagent/postgres"] -llm-planning = ["dep:zeph-llm", "dep:futures"] # Enables test utilities and testcontainers for PostgreSQL integration tests. -# Implies `llm-planning`: the only test-utils-gated suite (postgres_integration.rs) -# exercises `PlanCache`, which lives behind that feature. -test-utils = ["dep:testcontainers-modules", "postgres", "llm-planning"] +test-utils = ["dep:testcontainers-modules", "postgres"] default = ["sqlite"] [lints] diff --git a/crates/zeph-orchestration/README.md b/crates/zeph-orchestration/README.md index 1ea69d447..7cd6c8d7f 100644 --- a/crates/zeph-orchestration/README.md +++ b/crates/zeph-orchestration/README.md @@ -26,13 +26,13 @@ Implements the multi-agent task orchestration pipeline extracted from `zeph-core | `durable` | `ReplanBudgetSnapshot`, `journal_budget`/`restore_budget` — journals the replan budget to `zeph-durable` on pause and restores it on `/plan resume` (one execution per save generation, so a stale snapshot is never replayed) | | `command` | `PlanCommand` parser for `/plan` CLI slash commands; `HandoffCommand`/`parse_handoff_command`/`has_handoff_fence` — parses a node's trailing ` ```zeph-command ` fenced JSON block for Command-style dynamic task handoff (spec-080, feature `[orchestration.command]`) | | `error` | `OrchestrationError` unified error type | -| `planner` | `Planner` trait + `LlmPlanner` — goal decomposition via `chat_typed` structured output (feature `llm-planning`) | -| `aggregator` | `Aggregator` trait + `LlmAggregator` — synthesizes completed task outputs; content-sanitized before injection (feature `llm-planning`) | -| `verifier` | `PlanVerifier` — post-task and whole-plan completeness verifier with targeted replan, grounded against the DAG-wide tool-call trace (feature `llm-planning`) | -| `verify_predicate` | `PredicateEvaluator` — evaluates a node's optional `VerifyPredicate` and records a `PredicateOutcome`, driving predicate-scoped reruns (feature `llm-planning`) | +| `planner` | `Planner` trait + `LlmPlanner` — goal decomposition via `chat_typed` structured output | +| `aggregator` | `Aggregator` trait + `LlmAggregator` — synthesizes completed task outputs; content-sanitized before injection | +| `verifier` | `PlanVerifier` — post-task and whole-plan completeness verifier with targeted replan, grounded against the DAG-wide tool-call trace | +| `verify_predicate` | `PredicateEvaluator` — evaluates a node's optional `VerifyPredicate` and records a `PredicateOutcome`, driving predicate-scoped reruns | | `ensemble` | `EnsembleVerifier`, `EnsembleTracker` — N-fold parallel dispatch of `PlanVerifier` gap-severity checks across configured providers with deterministic majority-vote merge (spec `073-orch-ensemble-merge`, `[orchestration.ensemble]`, feature `llm-planning`) | -| `plan_cache` | `PlanCache` — caches plan templates by normalized goal hash; `normalize_goal` + `goal_hash` for deterministic cache keys (feature `llm-planning`) | -| `adaptorch` | `TopologyAdvisor` — adaptive topology hints for the scheduler (feature `llm-planning`) | +| `plan_cache` | `PlanCache` — caches plan templates by normalized goal hash; `normalize_goal` + `goal_hash` for deterministic cache keys | +| `adaptorch` | `TopologyAdvisor` — adaptive topology hints for the scheduler | ## Usage @@ -123,22 +123,22 @@ When a goal is decomposed into a task graph, the resulting structure is cached a |---------|---------|-------------| | `sqlite` | yes | SQLite backend for graph persistence (via `zeph-db`, `zeph-durable`, `zeph-memory`, `zeph-subagent`) | | `postgres` | no | PostgreSQL backend | -| `llm-planning` | no | Enables the LLM-dependent modules (`planner`, `aggregator`, `verifier`, `verify_predicate`, `plan_cache`, `adaptorch`, `ensemble`) and the `zeph-llm` dependency | -| `test-utils` | no | Testcontainers for PostgreSQL integration tests (implies `postgres` and `llm-planning`) | +| `test-utils` | no | Testcontainers for PostgreSQL integration tests (implies `postgres`) | > [!NOTE] -> Without `llm-planning`, the crate builds as a pure-DAG scheduler subset with no `zeph-llm` dependency — useful for embedding the scheduler without an LLM backend. +> The LLM-dependent modules (`planner`, `aggregator`, `verifier`, `verify_predicate`, `plan_cache`, +> `adaptorch`, `ensemble`) and the `zeph-llm` dependency compile unconditionally — the +> `llm-planning` feature was removed in the 2026-08 feature-flag audit (it gated no real optional +> dependency; `zeph-llm` was already transitively present). ## Installation ```bash cargo add zeph-orchestration - -# With LLM-backed planning and aggregation -cargo add zeph-orchestration --features llm-planning ``` -Enabled via the `orchestration` feature flag on the root `zeph` crate. +`zeph-orchestration` is a mandatory dependency of `zeph-core` — it is always compiled into the +`zeph` binary, not gated by a root feature flag. ## Documentation diff --git a/crates/zeph-orchestration/src/ensemble/mod.rs b/crates/zeph-orchestration/src/ensemble/mod.rs index 39695da92..60e210c8e 100644 --- a/crates/zeph-orchestration/src/ensemble/mod.rs +++ b/crates/zeph-orchestration/src/ensemble/mod.rs @@ -9,8 +9,7 @@ //! single `VerificationResult` the existing `should_replan` gate already consumes. See //! `specs/073-orch-ensemble-merge/spec.md` for the full design. //! -//! Gated behind the `llm-planning` feature (same gate as [`crate::verifier`], whose `Gap` type -//! this module reuses). +//! Reuses the `Gap` type from [`crate::verifier`]. pub mod merge; pub mod tracker; diff --git a/crates/zeph-orchestration/src/lib.rs b/crates/zeph-orchestration/src/lib.rs index 73c17d65f..2e33e6853 100644 --- a/crates/zeph-orchestration/src/lib.rs +++ b/crates/zeph-orchestration/src/lib.rs @@ -45,13 +45,6 @@ //! - [`PlanCache`] — caches and reuses completed plan skeletons //! - [`PlanVerifier`] — post-task completeness verifier with targeted replan //! -//! # Feature flags -//! -//! - `llm-planning`: enables LLM-dependent modules (`planner`, `aggregator`, -//! `verifier`, `verify_predicate`, `plan_cache`, `adaptorch`) and the `zeph-llm` dependency. -//! Not enabled by default (the crate's default feature set is `sqlite` only) — opt in -//! explicitly with `features = ["llm-planning"]` to use `LlmPlanner`/`LlmAggregator`. -//! //! # Example: build a plan and run the scheduler //! //! ```rust,ignore @@ -86,19 +79,12 @@ pub mod router; pub mod scheduler; pub mod topology; -#[cfg(feature = "llm-planning")] pub mod adaptorch; -#[cfg(feature = "llm-planning")] pub mod aggregator; -#[cfg(feature = "llm-planning")] pub mod ensemble; -#[cfg(feature = "llm-planning")] pub mod plan_cache; -#[cfg(feature = "llm-planning")] pub mod planner; -#[cfg(feature = "llm-planning")] pub mod verifier; -#[cfg(feature = "llm-planning")] pub mod verify_predicate; pub use admission::AdmissionGate; @@ -118,19 +104,12 @@ pub use topology::{ DispatchStrategy, Topology, TopologyAnalysis, TopologyClassifier, build_rev_adj, }; -#[cfg(feature = "llm-planning")] pub use adaptorch::{AdaptOrchMetrics, AdvisorVerdict, TaskClass, TopologyAdvisor, TopologyHint}; -#[cfg(feature = "llm-planning")] pub use aggregator::{Aggregator, LlmAggregator}; -#[cfg(feature = "llm-planning")] pub use ensemble::{Ballot, EnsembleAttempt, EnsembleTracker, EnsembleVerifier, MergeOutcome}; -#[cfg(feature = "llm-planning")] pub use plan_cache::{ PlanCache, PlanCacheError, PlanTemplate, TemplateTask, normalize_goal, plan_with_cache, }; -#[cfg(feature = "llm-planning")] pub use planner::{LlmPlanner, Planner}; -#[cfg(feature = "llm-planning")] pub use verifier::{Gap, GapSeverity, PlanVerifier, VerificationResult}; -#[cfg(feature = "llm-planning")] pub use verify_predicate::PredicateEvaluator; diff --git a/crates/zeph-orchestration/src/scheduler/mod.rs b/crates/zeph-orchestration/src/scheduler/mod.rs index bf79f0874..f99a2c3e4 100644 --- a/crates/zeph-orchestration/src/scheduler/mod.rs +++ b/crates/zeph-orchestration/src/scheduler/mod.rs @@ -32,12 +32,10 @@ use zeph_config::OrchestrationConfig; /// Built from `MessagePart::ToolUse`/`ToolResult` pairs — either read from the sub-agent /// transcript (spawn dispatch path) or collected in-loop (`RunInline` dispatch path). Consumed /// unconditionally by the scheduler's tool-outcome heuristics (`tick::counts_toward_completion_heuristic`, -/// issue #6397) and, when the `llm-planning` feature is enabled, also fed to -/// [`crate::verifier::PlanVerifier::verify`] as the ground truth a verify response's -/// `claimed_executions` is checked against (`specs/009-orchestration/spec.md` § "Verifier -/// Tool-Call Grounding"). Lives here rather than in `verifier` because the scheduler's own -/// `TaskOutcome`/`SchedulerAction` variants carry it unconditionally — `llm-planning` off must -/// still compile (issue #6744, cargo-hack `--each-feature` isolation). +/// issue #6397) and also fed to [`crate::verifier::PlanVerifier::verify`] as the ground truth a +/// verify response's `claimed_executions` is checked against (`specs/009-orchestration/spec.md` +/// § "Verifier Tool-Call Grounding"). Lives here rather than in `verifier` because the +/// scheduler's own `TaskOutcome`/`SchedulerAction` variants carry it unconditionally. #[derive(Debug, Clone)] pub struct ToolCallSummary { /// Tool name (matches `MessagePart::ToolUse::name`). diff --git a/crates/zeph-orchestration/src/scheduler/tick/tests.rs b/crates/zeph-orchestration/src/scheduler/tick/tests.rs index a2b985a59..bdfe13cbe 100644 --- a/crates/zeph-orchestration/src/scheduler/tick/tests.rs +++ b/crates/zeph-orchestration/src/scheduler/tick/tests.rs @@ -400,7 +400,6 @@ fn test_handoff_event_all_tools_failed_marks_task_failed_not_handoff() { ); } -#[cfg(feature = "llm-planning")] #[test] fn test_plan_with_verify_criteria_and_predicate_disabled_reaches_completed() { // End-to-end regression for #5403: a planner response where a task has a diff --git a/crates/zeph-orchestration/src/verifier.rs b/crates/zeph-orchestration/src/verifier.rs index 06af9dc3c..6c0cf9dd8 100644 --- a/crates/zeph-orchestration/src/verifier.rs +++ b/crates/zeph-orchestration/src/verifier.rs @@ -2225,7 +2225,7 @@ mod tests { /// `claimed_executions` field + trace section added for #6287) end-to-end through /// `chat_typed::`. Ignored by default (requires a local Ollama instance with /// `qwen2.5:7b` pulled) — run manually with `cargo nextest run -p zeph-orchestration - /// --features llm-planning -- --ignored`. + /// -- --ignored`. #[tokio::test] #[ignore = "requires a local Ollama instance with qwen2.5:7b"] async fn verify_plan_live_ollama_round_trip_does_not_error() { diff --git a/crates/zeph-orchestration/src/verify_predicate.rs b/crates/zeph-orchestration/src/verify_predicate.rs index a39354929..5e6e115d1 100644 --- a/crates/zeph-orchestration/src/verify_predicate.rs +++ b/crates/zeph-orchestration/src/verify_predicate.rs @@ -5,7 +5,7 @@ //! //! [`VerifyPredicate`] and [`PredicateOutcome`] (the data types stored on `TaskNode`) live //! in [`crate::graph`]. This module contains only [`PredicateEvaluator`], which requires -//! an LLM provider and is therefore behind the `llm-planning` feature. +//! an LLM provider. use std::sync::Arc; use std::time::Duration; diff --git a/crates/zeph-plugins/Cargo.toml b/crates/zeph-plugins/Cargo.toml index 759249838..691e3a9c8 100644 --- a/crates/zeph-plugins/Cargo.toml +++ b/crates/zeph-plugins/Cargo.toml @@ -46,15 +46,9 @@ wiremock.workspace = true default = ["sqlite"] sqlite = ["zeph-skills/sqlite", "zeph-tools/sqlite"] postgres = ["zeph-skills/postgres", "zeph-tools/postgres"] -# Skill/plugin marketplace discovery (spec-045, #5869). Gates only the `marketplace` module -# body — `reqwest`/`serde_json` are already unconditional dependencies of this crate. The only -# additive dependency surface is `reqwest`'s own `query` Cargo feature (a thin wrapper around -# `serde_urlencoded`, no new crate), needed to build the skills.sh search request. -registry = ["reqwest/query"] # Exposes `marketplace::mock::MockRegistryClient` outside `#[cfg(test)]` so downstream crates # (the `zeph` binary) can drive it from their own `dev-dependencies`. Mirrors `zeph-vault`'s -# `mock` feature. No-op unless `registry` is also enabled (the `marketplace` module itself is -# gated on `registry`). +# `mock` feature. mock = [] [lints] diff --git a/crates/zeph-plugins/README.md b/crates/zeph-plugins/README.md index ef52797b6..1655826aa 100644 --- a/crates/zeph-plugins/README.md +++ b/crates/zeph-plugins/README.md @@ -51,7 +51,7 @@ zeph plugin add ./path/to/my-plugin --strict-reputation | `manager` | `PluginManager` — install/remove/list with path-traversal defense (`canonicalize + starts_with(root)`), recursive `.bundled` marker stripping, symlink skip, and atomic install-then-verify | | `manifest` | `plugin.toml` schema (`PluginManifest`, `PluginMeta`, `SkillEntry`, `McpSection`) | | `overlay` | `apply_plugin_config_overlays` — scans installed plugins, validates overlays, and merges tighten-only keys into the live `Config` struct | -| `marketplace` | `RegistryClient` trait, `RegistryEntry`, `PackageArchive`, `RegistryError` — opt-in skill/plugin discovery-and-install marketplace backing `zeph plugin search`/`get` (feature `registry`) | +| `marketplace` | `RegistryClient` trait, `RegistryEntry`, `PackageArchive`, `RegistryError` — skill/plugin discovery-and-install marketplace backing `zeph plugin search`/`get`; always compiled, opt-in only via `[skills.registry] enabled` config | | `error` | `PluginError` typed error enum | | `types` | `PluginName` validated identifier | @@ -134,7 +134,7 @@ zeph plugin remove my-plugin ### Marketplace discovery (opt-in) ```bash -# Requires the `registry` feature and [skills.registry] enabled = true in config.toml +# Requires [skills.registry] enabled = true in config.toml zeph plugin search zeph plugin get ``` @@ -205,8 +205,12 @@ Enabled automatically when the `zeph-plugins` crate is a dependency of the root |---------|---------|-------------| | `sqlite` | yes | SQLite backend — the default, lets the crate build in isolation | | `postgres` | no | PostgreSQL backend for PostgreSQL deployments (#4956) | -| `registry` | no | Enables the `marketplace` module body backing `zeph plugin search`/`get` (spec-045). Adds `reqwest`'s `query` Cargo feature only — no new crate | -| `mock` | no | Exposes `marketplace::mock::MockRegistryClient` outside `#[cfg(test)]` for downstream crates' `dev-dependencies`. No-op unless `registry` is also enabled | +| `mock` | no | Exposes `marketplace::mock::MockRegistryClient` outside `#[cfg(test)]` for downstream crates' `dev-dependencies` | + +The `marketplace` module body backing `zeph plugin search`/`get` (spec-045) compiles +unconditionally — the `registry` feature was removed in the 2026-08 feature-flag audit (it gated +no real optional dependency; `reqwest`'s `query` sub-feature is now unconditional on the +workspace `reqwest` dependency). ## Documentation diff --git a/crates/zeph-plugins/src/lib.rs b/crates/zeph-plugins/src/lib.rs index 5209d7daf..862aae1d8 100644 --- a/crates/zeph-plugins/src/lib.rs +++ b/crates/zeph-plugins/src/lib.rs @@ -22,7 +22,6 @@ pub mod error; pub(crate) mod integrity; pub mod manager; pub mod manifest; -#[cfg(feature = "registry")] pub mod marketplace; pub mod overlay; pub mod types; @@ -35,7 +34,6 @@ pub use manager::{ download_and_extract, validate_url_scheme_ephemeral, }; pub use manifest::PluginManifest; -#[cfg(feature = "registry")] pub use marketplace::{PackageArchive, RegistryClient, RegistryEntry, RegistryError}; pub use overlay::{ResolvedOverlay, apply_plugin_config_overlays}; pub use types::PluginName; diff --git a/crates/zeph-plugins/src/marketplace/mod.rs b/crates/zeph-plugins/src/marketplace/mod.rs index ce0630cab..6b23c6088 100644 --- a/crates/zeph-plugins/src/marketplace/mod.rs +++ b/crates/zeph-plugins/src/marketplace/mod.rs @@ -10,17 +10,6 @@ //! existing [`zeph_skills::manager::SkillManager`] and [`crate::manager::PluginManager`] //! install pipelines unchanged (NFR-002). //! -//! # Compile-time gate -//! -//! The entire module body is gated by the `registry` Cargo feature (see `Cargo.toml`). This is -//! a *thin* feature: `reqwest` is already an unconditional dependency of this crate, and the -//! only additive surface it enables is `reqwest`'s own `query` Cargo feature (a thin wrapper -//! around `serde_urlencoded`, not a new crate) — the feature's purpose is to satisfy the -//! project convention that every new optional network capability gets a dedicated feature -//! flag, not to gate a heavyweight dependency. The CLI argument variants and -//! [`zeph_config::RegistryConfig`] parsing are **not** gated by this feature and always -//! compile, so `--help` and `--migrate-config` keep working in a build without it. -//! //! # Backends //! //! - [`skills_sh::SkillsShClient`] — the only shipped backend, targeting the public diff --git a/crates/zeph-scheduler/Cargo.toml b/crates/zeph-scheduler/Cargo.toml index 782e9a6d0..29909b76a 100644 --- a/crates/zeph-scheduler/Cargo.toml +++ b/crates/zeph-scheduler/Cargo.toml @@ -14,12 +14,6 @@ description = "Cron-based periodic task scheduler with SQLite persistence for Ze readme = "README.md" [features] -## Enable Unix daemon lifecycle: `PidFile`, `detach_and_run`, and `run_foreground`. -## -## Pulls in `rustix` (fs + process features) for advisory file locking, used by the -## shared `flock(2)` pid-file guard primitive (`zeph_common::pidfile`). -## Only meaningful on Unix targets; daemon code is `#[cfg(unix)]`-gated. -daemon = ["dep:rustix"] sqlite = ["zeph-db/sqlite", "zeph-durable/sqlite"] postgres = ["zeph-db/postgres", "zeph-durable/postgres"] default = ["sqlite"] @@ -28,7 +22,7 @@ default = ["sqlite"] chrono = { workspace = true, features = ["std", "clock"] } cron.workspace = true reqwest = { workspace = true, features = ["json", "rustls"] } -rustix = { workspace = true, features = ["fs", "process", "std"], optional = true } +rustix = { workspace = true, features = ["fs", "process", "std"] } semver.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/crates/zeph-scheduler/README.md b/crates/zeph-scheduler/README.md index f20a4eeff..2ebe15d34 100644 --- a/crates/zeph-scheduler/README.md +++ b/crates/zeph-scheduler/README.md @@ -20,7 +20,7 @@ Manages recurring and deferred background tasks. Periodic tasks run on a cron sc - **sanitize** — `sanitize_task_prompt` — strips control characters and truncates to 512 code points - **update_check** — `UpdateCheckHandler` for GitHub releases version check - **durable** — `SchedulerDurableAdapter` bridges scheduled runs onto the `zeph-durable` journaled execution layer for crash-resume -- **daemon** / **pidfile** — `#[cfg(all(unix, feature = "daemon"))]` — `DaemonConfig`, `detach_and_run`, `run_foreground`, `daemon_status`, `stop_daemon`, and the `PidFile` `flock(2)` guard +- **daemon** / **pidfile** — `#[cfg(unix)]` — `DaemonConfig`, `detach_and_run`, `run_foreground`, `daemon_status`, `stop_daemon`, and the `PidFile` `flock(2)` guard - **error** — `SchedulerError` error types ## Task Modes @@ -228,7 +228,10 @@ Enabled via the `scheduler` feature flag on the root `zeph` crate. |---------|---------|-------------| | `sqlite` | yes | SQLite backend for `zeph-db` / `zeph-durable` job persistence | | `postgres` | no | PostgreSQL backend for `zeph-db` / `zeph-durable` | -| `daemon` | no | Unix daemon lifecycle: `PidFile`, `detach_and_run`, `run_foreground`, `daemon_status`, `stop_daemon`. Pulls in `rustix` for advisory `flock(2)` pid-file locking; `#[cfg(unix)]`-gated | + +Unix daemon lifecycle (`PidFile`, `detach_and_run`, `run_foreground`, `daemon_status`, +`stop_daemon`, `rustix`-backed advisory `flock(2)` pid-file locking) compiles unconditionally, +`#[cfg(unix)]`-gated — the `daemon` feature was removed in the 2026-08 feature-flag audit. ## Documentation diff --git a/crates/zeph-scheduler/src/daemon.rs b/crates/zeph-scheduler/src/daemon.rs index 73a0a9404..c574eb440 100644 --- a/crates/zeph-scheduler/src/daemon.rs +++ b/crates/zeph-scheduler/src/daemon.rs @@ -17,7 +17,7 @@ //! separate log-rotate step truncates the original. SIGHUP-based reopen is not //! implemented in this MVP. -#![cfg(all(unix, feature = "daemon"))] +#![cfg(unix)] use std::path::PathBuf; use std::process::Stdio; diff --git a/crates/zeph-scheduler/src/error.rs b/crates/zeph-scheduler/src/error.rs index d72200183..2bb6745a6 100644 --- a/crates/zeph-scheduler/src/error.rs +++ b/crates/zeph-scheduler/src/error.rs @@ -36,7 +36,7 @@ pub enum SchedulerError { /// Another `zeph serve` instance is already running with the given PID. /// /// Returned by [`crate::PidFile::acquire`] when the pid file is locked by another process. - #[cfg(all(unix, feature = "daemon"))] + #[cfg(unix)] #[error( "daemon pid file is locked: another zeph serve instance appears to be running (pid {pid})" )] diff --git a/crates/zeph-scheduler/src/lib.rs b/crates/zeph-scheduler/src/lib.rs index 3df989fe6..4fd663f1a 100644 --- a/crates/zeph-scheduler/src/lib.rs +++ b/crates/zeph-scheduler/src/lib.rs @@ -90,9 +90,9 @@ mod store; mod task; pub mod update_check; -#[cfg(all(unix, feature = "daemon"))] +#[cfg(unix)] pub mod daemon; -#[cfg(all(unix, feature = "daemon"))] +#[cfg(unix)] pub mod pidfile; pub use durable::SchedulerDurableAdapter; @@ -106,11 +106,11 @@ pub use task::{ normalize_cron_expr, }; -#[cfg(all(unix, feature = "daemon"))] +#[cfg(unix)] pub use daemon::{ DaemonConfig, DaemonStatus, TaskRunSummary, daemon_status, detach_and_run, run_foreground, stop_daemon, }; -#[cfg(all(unix, feature = "daemon"))] +#[cfg(unix)] pub use pidfile::PidFile; pub use update_check::UpdateCheckHandler; diff --git a/crates/zeph-skills/Cargo.toml b/crates/zeph-skills/Cargo.toml index 6156101de..fd06ca04e 100644 --- a/crates/zeph-skills/Cargo.toml +++ b/crates/zeph-skills/Cargo.toml @@ -16,7 +16,7 @@ readme = "README.md" [features] profiling = [] default = ["sqlite"] -miner = ["anyhow", "clap", "dirs", "toml", "tracing-subscriber"] +miner = ["dep:anyhow", "dep:clap", "dep:dirs", "dep:toml", "dep:tracing-subscriber"] qdrant = ["dep:zeph-memory", "dep:qdrant-client"] # `zeph-memory` is optional (pulled in by `qdrant`); when present it requires a backend. # These no-op unless `qdrant` activates `zeph-memory`, then select its backend (#4956). diff --git a/crates/zeph-tui/Cargo.toml b/crates/zeph-tui/Cargo.toml index 484a93f0c..5b8724bb6 100644 --- a/crates/zeph-tui/Cargo.toml +++ b/crates/zeph-tui/Cargo.toml @@ -66,7 +66,6 @@ tokio-util.workspace = true [features] default = ["sqlite"] clipboard = ["dep:arboard"] -cocoon = [] profiling = [] # `zeph-memory`/`zeph-db` require a backend to compile; default to `sqlite` so this crate # builds in isolation, and expose `postgres` for PostgreSQL deployments (#4956). diff --git a/crates/zeph-tui/src/command.rs b/crates/zeph-tui/src/command.rs index c018317ad..929d9dfbe 100644 --- a/crates/zeph-tui/src/command.rs +++ b/crates/zeph-tui/src/command.rs @@ -849,7 +849,6 @@ fn build_graph_experiment_commands() -> Vec { ] } -#[cfg(feature = "cocoon")] fn build_cocoon_commands() -> Vec { vec![ CommandEntry { @@ -946,25 +945,6 @@ const ZEPH_COMMANDS_DEDUP: &[&str] = &[ "/subagent", // acp:subagent-spawn (already prefills "/subagent spawn " when empty) ]; -/// Returns `false` only for entries whose `feature_gate` names a Cargo feature that is -/// unified, via the root binary crate, with this crate's own feature of the same name — and -/// that feature is disabled in this build. -/// -/// Every `feature_gate` value in [`zeph_commands::COMMANDS`] is otherwise purely descriptive -/// (rendered as `[requires: X]` in `/help` text): the underlying `CommandHandler` is -/// unconditionally registered in `Agent::run` regardless of any Cargo feature with a -/// matching name (most such names — `"acp"`, `"guardrail"`, `"scheduler"`, `"session"`, -/// etc. — do not even exist as Cargo features on the relevant crates). `"cocoon"` is the one -/// exception: `CocoonCommand`'s registration in `crates/zeph-core/src/agent/slash_commands.rs` -/// really is `#[cfg(feature = "cocoon")]`-gated, and the root `Cargo.toml`'s `cocoon` feature -/// unifies `zeph-core/cocoon` with this crate's own `cocoon` feature (which already gates -/// `build_cocoon_commands`), so checking it here faithfully predicts whether `CocoonCommand` -/// exists in this exact build (#5875 F2) — without this check, a `cocoon`-feature-off build -/// would still show `/cocoon` in autocomplete and fail when submitted. -fn command_is_compiled_in_this_build(entry: &zeph_commands::CommandInfo) -> bool { - entry.feature_gate != Some("cocoon") || cfg!(feature = "cocoon") -} - /// Returns the [`CommandEntry`] projection of every [`zeph_commands::COMMANDS`] entry that /// has no dedicated hand-authored `TuiCommand` (see `ZEPH_COMMANDS_DEDUP`). /// @@ -985,11 +965,6 @@ fn command_is_compiled_in_this_build(entry: &zeph_commands::CommandInfo) -> bool /// bracket-consistent — e.g. `/goal` and `/worktree` both default sensibly on empty args /// despite their hint text not being `[`-wrapped). /// -/// Entries whose `feature_gate` corresponds to a real, compile-time-relevant Cargo feature -/// are excluded when that feature is off in this build (see `command_is_compiled_in_this_build`) -/// — otherwise a feature-gated command that was never actually registered would still appear -/// in autocomplete and fail when submitted (#5875 F2). -/// /// Lazily initialised and shared for the process lifetime, like [`command_registry`] and /// [`extra_command_registry`]. /// @@ -1010,7 +985,6 @@ pub fn zeph_commands_entries() -> &'static [CommandEntry] { zeph_commands::COMMANDS .iter() .filter(|c| !ZEPH_COMMANDS_DEDUP.contains(&c.name)) - .filter(|c| command_is_compiled_in_this_build(c)) .map(|c| CommandEntry { id: c.name, label: c.description, @@ -1067,7 +1041,6 @@ fn build_extra_commands() -> Vec { command: String::new(), }, }); - #[cfg(feature = "cocoon")] cmds.extend(build_cocoon_commands()); cmds.extend(build_clipboard_commands()); cmds.extend(build_knowledge_commands()); @@ -1172,15 +1145,13 @@ mod tests { // 24 base (14 + 5 plan + 5 graph) + 5 experiment + 1 log:status + 1 config:migrate // + 1 compaction:status + 1 guidelines:view + 1 tafc:status + 1 lsp:status // + 1 forgetting-sweep + 3 acp + 1 sandbox:status (#3294) = 43 - // + 2 cocoon (#3673) when feature = "cocoon" + // + 2 cocoon (#3673) // + 2 clipboard (#3685, #5098) // + 2 worktree (#4679) // + 3 knowledge (#5019, #5020) - let expected = 50 + if cfg!(feature = "cocoon") { 2 } else { 0 }; - assert_eq!(extra_command_registry().len(), expected); + assert_eq!(extra_command_registry().len(), 52); } - #[cfg(feature = "cocoon")] #[test] fn filter_cocoon_returns_cocoon_entries() { let results = filter_commands("cocoon"); @@ -1534,22 +1505,11 @@ mod tests { } #[test] - #[cfg(feature = "cocoon")] - fn zeph_commands_entries_includes_cocoon_when_feature_enabled() { - // #5875 F2. + fn zeph_commands_entries_includes_cocoon() { let entries = zeph_commands_entries(); assert!(entries.iter().any(|e| e.id == "/cocoon")); } - #[test] - #[cfg(not(feature = "cocoon"))] - fn zeph_commands_entries_excludes_cocoon_when_feature_disabled() { - // #5875 F2: without this, a cocoon-feature-off build would still show /cocoon in - // autocomplete and fail when submitted, since CocoonCommand is never registered. - let entries = zeph_commands_entries(); - assert!(!entries.iter().any(|e| e.id == "/cocoon")); - } - #[test] fn filter_commands_merges_zeph_commands_entries() { let results = filter_commands("model"); diff --git a/crates/zeph-vault/Cargo.toml b/crates/zeph-vault/Cargo.toml index 416aa049c..39e92f0f0 100644 --- a/crates/zeph-vault/Cargo.toml +++ b/crates/zeph-vault/Cargo.toml @@ -14,7 +14,6 @@ description = "VaultProvider trait and backends (env, age) for Zeph secret manag readme = "README.md" [features] -default = [] mock = [] [dependencies] diff --git a/crates/zeph-worktree/Cargo.toml b/crates/zeph-worktree/Cargo.toml index d7ca70d0c..bcb38b32e 100644 --- a/crates/zeph-worktree/Cargo.toml +++ b/crates/zeph-worktree/Cargo.toml @@ -13,9 +13,6 @@ publish.workspace = true description = "Git worktree lifecycle management for Zeph subagents" readme = "README.md" -[features] -default = [] - [dependencies] parking_lot.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/specs/001-system-invariants/spec.md b/specs/001-system-invariants/spec.md index fb8f2da48..b55307857 100644 --- a/specs/001-system-invariants/spec.md +++ b/specs/001-system-invariants/spec.md @@ -183,9 +183,9 @@ Feature flags (`Cargo.toml [features]`): - New optional crates: `dep:zeph-` in the feature definition — never unconditionally import - Optional features that extend the TUI: use `zeph-tui?/feature-name` (conditional propagation) - Bundles (`desktop`, `ide`, `server`, `full`) are the only way to enable groups of features -- CI MUST use `--features full` for lint and test runs — partial feature builds do not count +- CI MUST exercise the `full` configuration on every PR — satisfied by four independent legs (coverage, `bundle-check (full)`, `release-build-full`, `ci-non-linux.yml`) rather than a literal `--features full` on the hot lint/test path; see spec 029 §5.6 -**NEVER**: add a pure behavioral marker to `default` (must gate real optional deps per spec 029 §2); never skip `--features full` in pre-merge checks. +**NEVER**: add a pure behavioral marker to `default` (must gate real optional deps per spec 029 §2); never drop one of the four CI legs that together exercise `full` without replacing it. ## 10. Concurrency & Safety Contract diff --git a/specs/013-acp/spec.md b/specs/013-acp/spec.md index 60c1af06d..f9326cec8 100644 --- a/specs/013-acp/spec.md +++ b/specs/013-acp/spec.md @@ -143,8 +143,8 @@ and `agent-client-protocol-tokio` was removed from both workspace `Cargo.toml` a `session/close`, `session/resume`, `session/delete`, and `session/logout` are unconditional in core 2.0.0 (unconditional since the 0.14.0 bump; unaffected by the 1.0.1 schema-path migration or the 2.0.0 crate-API migration). The corresponding `unstable-session-*` Zeph feature flags -are tombstoned as no-op `= []` (retained only so root `Cargo.toml` forwarding resolves without -changes). +have been removed entirely (2026-08 feature-flag audit, spec 029 §3.3) — they are no longer +present in `Cargo.toml` at all, not merely tombstoned. **Unchanged in 2.0.0**: the builder + `on_receive_request!`/`on_receive_notification!`/ `on_receive_dispatch!` macros, `Responder`, `ConnectionTo`, `ByteStreams`, `.block_task()`, and the @@ -295,15 +295,16 @@ string for diagnostics (e.g., `"user_initiated"`, `"timeout"`, `"error"`). Reconnect to an existing session by ID, restoring conversation history and tool context. Previously gated behind `unstable-session-resume` feature flag in Zeph. -The `unstable-session-resume` Zeph feature flag is now a tombstone `= []`. All `#[cfg(feature = -"unstable-session-resume")]` gates are removed; the resume handler runs unconditionally. +The `unstable-session-resume` Zeph feature flag has been removed entirely (2026-08 audit, spec +029 §3.3); the resume handler runs unconditionally. ### session/delete **Status: stable** (unconditional in core 2.0.0 (since the 0.14.0 bump; unaffected by the 2.0.0 crate-major migration)) Remove a session from the `session/list` registry. Previously gated behind `unstable-session-delete`. -The `unstable-session-delete` Zeph feature flag is now a tombstone `= []`. All cfg gates removed. +The `unstable-session-delete` Zeph feature flag has been removed entirely (2026-08 audit, spec +029 §3.3); all cfg gates removed. Custom `_session/delete` extension (backward compat) is retained alongside the standard method. @@ -311,8 +312,8 @@ Custom `_session/delete` extension (backward compat) is retained alongside the s **Status: stable** (unconditional in core 2.0.0 (since the 0.14.0 bump; unaffected by the 2.0.0 crate-major migration)) -Previously gated behind `unstable-logout`. The `unstable-logout` Zeph feature flag is now a -tombstone `= []`. All cfg gates removed; logout handler runs unconditionally. +Previously gated behind `unstable-logout`. The `unstable-logout` Zeph feature flag has been +removed entirely (2026-08 audit, spec 029 §3.3); logout handler runs unconditionally. ### Capability Negotiation @@ -473,17 +474,19 @@ exposing tools over ACP. | `unstable-elicitation` | **active** | Now also adds `agent-client-protocol/unstable_elicitation` passthrough so core wires `elicitation/create` | | `unstable-llm-providers` | **active** | Still gated upstream (`unstable_llm_providers`); provider type renames apply here (see Providers API) | | `unstable-auth-methods` | **active** | Still gated upstream (`unstable_auth_methods`) | -| `unstable-boolean-config` | **tombstone** `= []` | Stabilized — `SessionConfigOptionValue::Boolean` is unconditional since schema 1.1.0 (core 1.1.0 dropped its `unstable_boolean_config` forward). `do_set_session_config_option` always matches the enum; flag retained as no-op. | -| `unstable-session-delete` | **tombstone** `= []` | Stabilized — `session/delete` handler is unconditional in core 2.0.0 (since the 0.14.0 bump). Flag retained as no-op for workspace forwarding (root `Cargo.toml` references it). | -| `unstable-session-resume` | **tombstone** `= []` | Stabilized — `session/resume` handler is unconditional in core 2.0.0 (since the 0.14.0 bump). Flag retained as no-op. | -| `unstable-logout` | **tombstone** `= []` | Stabilized — logout handler is unconditional in core 2.0.0 (since the 0.14.0 bump). Flag retained as no-op. | -| `unstable-session-add-dirs` | **tombstone** `= []` | Stabilized — `additional_directories` field is plain `Vec`, unconditional since schema 0.13.6 (currently schema 1.5.0; unaffected by the 2.0.0 migration). Flag retained as no-op. | -| `unstable-message-id` | **tombstone** `= []` | Removed — `PromptRequest.message_id` and `PromptResponse.user_message_id` deleted upstream. Entire inbound echo feature removed. Flag retained as no-op for workspace forwarding. | +| `unstable-boolean-config` | **DELETED** (2026-08 audit) | Stabilized — `SessionConfigOptionValue::Boolean` is unconditional since schema 1.1.0. Cargo feature removed entirely (spec 029 §3.3), not just tombstoned. | +| `unstable-session-delete` | **DELETED** (2026-08 audit) | Stabilized — `session/delete` handler is unconditional. Cargo feature removed entirely, including its root `Cargo.toml` forward (spec 029 §3.3). | +| `unstable-session-resume` | **DELETED** (2026-08 audit) | Stabilized — `session/resume` handler is unconditional. Cargo feature removed entirely (spec 029 §3.3). | +| `unstable-logout` | **DELETED** (2026-08 audit) | Stabilized — logout handler is unconditional. Cargo feature removed entirely (spec 029 §3.3). | +| `unstable-session-add-dirs` | **DELETED** (2026-08 audit) | Stabilized — `additional_directories` field is plain `Vec`, unconditional since schema 0.13.6. Cargo feature removed entirely (spec 029 §3.3). | +| `unstable-message-id` | **DELETED** (2026-08 audit) | Inbound message-id echo is unconditional; the previously-retained no-op Cargo feature has now been removed entirely (spec 029 §3.3). | | `unstable-cancel-request` | **active (local-only gate)** | Implemented (#5362). Core `1.1.0` made `$/cancel_request` unconditional and dropped the `unstable_cancel_request` feature entirely, so this Zeph flag no longer forwards to any upstream feature — it is now purely a local opt-in for the zeph-acp bridge itself. Not in `default`. The `session/prompt` handler (`agent/handlers/prompt.rs`) bridges `Responder::cancellation()`, scoped to that specific JSON-RPC request, onto the session's existing `cancel_signal: Arc` (the same signal `session/cancel` notifies in `agent/handlers/cancel.rs`) via a short-lived watcher task that races cancellation against prompt completion. A low-level `CancelRequestNotification` handler is also registered in the `Agent.builder()` chain (`agent/mod.rs`) for tracing-only observability — the SDK updates per-request cancellation markers automatically regardless of whether a handler is registered. | | `unstable-session-model` | **DELETED** | Removed entirely — `session/set_model` RPC deleted upstream. Feature name removed from Cargo.toml and root `Cargo.toml`. Model switching survives via `set_config_option`. | -> **Tombstone flags** are `= []` no-ops retained solely so root `Cargo.toml` feature forwarding -> resolves without changes. They add zero behavior. +> **DELETED flags** (2026-08 feature-flag audit): formerly `= []` no-op tombstones retained for +> root `Cargo.toml` forwarding, now removed entirely — the Cargo feature no longer exists and all +> `#[cfg(feature = "...")]` gates for it were stripped, so the previously-gated behavior compiles +> unconditionally. See spec 029 §3.3. > **2.0.0 migration note**: all currently-forwarded unstable features (`unstable_session_fork`, > `unstable_end_turn_token_usage` / `unstable-session-usage`, `unstable_elicitation`, @@ -600,7 +603,7 @@ schema 0.13.6. The entire inbound message-id echo feature is removed from Zeph: - `current_message_id` session slot removed - `build_prompt_response` no longer accepts or echoes a message ID - `apply_message_id_to_chunk` removed (no live data source) -- `unstable-message-id` feature is a tombstone `= []` +- `unstable-message-id` Cargo feature has been removed entirely (2026-08 audit, spec 029 §3.3) `ContentChunk.message_id` field still exists in schema 0.13.6 for potential future agent-generated per-chunk IDs, but Zeph does not inject it (no inbound source). diff --git a/specs/017-index/spec.md b/specs/017-index/spec.md index 7bb96dbea..ba2314cc5 100644 --- a/specs/017-index/spec.md +++ b/specs/017-index/spec.md @@ -37,7 +37,7 @@ related: --- -`crates/zeph-index/` (feature: `index`) — AST-based code indexing, semantic retrieval, repo map. +`crates/zeph-index/` (feature: `index`, in the `desktop` bundle since the 2026-08 feature-flag audit) — AST-based code indexing, semantic retrieval, repo map. ## Indexing Pipeline diff --git a/specs/029-feature-flags/spec.md b/specs/029-feature-flags/spec.md index 292f256da..9475dad0d 100644 --- a/specs/029-feature-flags/spec.md +++ b/specs/029-feature-flags/spec.md @@ -102,25 +102,25 @@ default = ["scheduler", "sqlite"] | `pdf` | `zeph-memory/pdf` | pdf-extract crate; large optional dep | | `postgres` | `zeph-db/postgres`, `zeph-memory/postgres` | Mutually exclusive with `sqlite` | | `sqlite` | `zeph-db/sqlite`, `zeph-memory/sqlite` | Mutually exclusive with `postgres` (also in default) | -| `deep-link` | `zeph-common/deep-link`, `zeph-config/deep-link` | `zeph://` URI scheme registration/dispatch (spec #066); optional OS integration | | `session` | `dep:axum`, `dep:tokio-stream`, `zeph-common/http-middleware` | Session persistence + `zeph serve` mode (spec #068, new `zeph-session` crate); HTTP/SSE session API | | `profiling` | `dep:tracing-chrome`, `dep:sysinfo` (+ per-crate `profiling` propagation) | Diagnostic tracing spans and system metrics; zero overhead when not actively tracing | | `sandbox` | `zeph-tools/sandbox` (`dep:landlock`, `dep:seccompiler` on Linux; macOS Seatbelt compiles unconditionally) | Runtime-disabled by default (`tools.sandbox.enabled = false`) | | `prometheus` | `gateway`, `dep:prometheus-client`, `zeph-gateway/prometheus` | OpenMetrics `/metrics` endpoint (spec #036); requires `gateway` | | `gonka` | `zeph-llm/gonka`, `zeph-core/gonka` | gonka.ai inference provider (specs #051, #052) | -| `cocoon` | `zeph-llm/cocoon`, `zeph-core/cocoon`, `zeph-tui?/cocoon` | Cocoon distributed compute provider (spec #055) | -| `index` | `zeph-core/index` | AST-based code indexing (spec #017) | -| `registry` | `zeph-plugins/registry` | Skill/plugin marketplace discovery client (spec #045) | -| `testing` | `zeph-llm/testing` | Test-only provider harness helpers | +| `index` | `zeph-core/index` → `zeph-agent-context/index` (`dep:zeph-index`, +43 packages) | AST-based code indexing (spec #017); in the `desktop` bundle | +| `testing` | `zeph-llm/testing` (marker) | Test-double harness; exempt from §5.1 per the test-double clause. Not in `full` — see §4 | | `bench` | `dep:zeph-bench` | Benchmark harness CLI (spec #034) | > [!note] -> `prometheus`, `gonka`, `cocoon`, `index`, `registry`, `testing`, and `bench` were backfilled above +> `prometheus`, `gonka`, `index`, `testing`, and `bench` were backfilled above > (2026-07 reconciliation pass against `Cargo.toml`'s `[features]` block). `profiling-alloc` and > `profiling-pyroscope` are still not individually documented here — both are thin variants of > `profiling` (`profiling-alloc = ["profiling", "zeph-core/profiling-alloc"]`, > `profiling-pyroscope = ["profiling", "otel", "dep:pprof"]`) and are omitted as low-risk; add rows > for them if either gains independent justification beyond extending `profiling`. +> +> `deep-link`, `cocoon`, and `registry` were consolidated into always-on capabilities in the +> 2026-08 feature-flag audit — see §3.3. ### 3.3 Always-On Capabilities (No Flag) @@ -142,6 +142,12 @@ As of v0.18.0, they were consolidated into always-on capabilities per the Decisi | MARCH self-check pipeline | `self-check` | Consolidated v0.20.x | | Environment variable vault fallback | `env-vault` | Consolidated v0.20.x | | Per-task CPU/wall-time metrics | `task-metrics` | Consolidated v0.20.x | +| `zeph://` deep-link URI dispatch | `deep-link` | Consolidated v0.22.x | +| Cocoon inference provider | `cocoon` | Consolidated v0.22.x | +| Skill/plugin registry marketplace | `registry` | Consolidated v0.22.x | +| Orchestration LLM planning | `zeph-orchestration/llm-planning` | Consolidated v0.22.x | +| Scheduler daemon mode | `zeph-scheduler/daemon` | Consolidated v0.22.x | +| ACP stabilised unstable handlers | 5 × `zeph-acp/unstable-*` | Consolidated v0.22.x | **Why**: Each flag gated only behavioral code with no optional crate dependencies — they violated the Decision Rule (§2). All these subsystems are active by default and cannot be disabled at build time; @@ -155,24 +161,24 @@ Bundles are the **only** mechanism for enabling groups of features. Do not instr | Bundle | Expands to | Target use case | |---|---|---| -| `desktop` | `tui`, `deep-link`, `session` | Local developer workstation with terminal UI | +| `desktop` | `tui`, `session`, `index` | Local developer workstation with terminal UI | | `ide` | `acp`, `acp-http` | IDE integration via Agent-Client Protocol | | `server` | `gateway`, `a2a`, `otel`, `prometheus`, `session` | Headless server: webhook ingestion, A2A, telemetry, metrics | | `chat` | `discord`, `slack` | Bot deployment on messaging platforms | | `ml` | `candle`, `pdf` | On-device ML inference and PDF memory | -| `full` | `desktop`, `ide`, `server`, `chat`, `pdf`, `scheduler`, `classifiers`, `profiling`, `sandbox`, `gonka`, `cocoon`, `testing`, `registry` | CI, pre-merge checks, complete feature matrix | +| `full` | `desktop`, `ide`, `server`, `chat`, `pdf`, `scheduler`, `classifiers`, `profiling`, `sandbox`, `gonka` | CI, pre-merge checks, complete feature matrix | Bundle invariants: -- `full` must activate every flag that is safe to combine (excluding `metal`, `cuda`, `postgres` — platform/exclusive). +- `full` must activate every flag that is safe to combine (excluding `metal`, `cuda`, `postgres` — platform/exclusive), and excluding dev-only harness features (`bench`, `testing`) — `release.yml` ships `full` to users, so benchmark harnesses and test doubles must not be in it. Each has its own CI leg (`bundle-check (bench)`; `testing` in the curated pre-merge string). - `default` must remain minimal: only features that gate real optional deps AND have `Tested` coverage status. See §3.1 for the current list. -- CI MUST run with `--features full` for lint and tests. Partial-feature builds do not count as pre-merge validation. +- The `full` configuration must be exercised by CI on every PR; this is satisfied by four independent legs — coverage (`ci.yml:507`), `bundle-check (full)`, `release-build-full`, and `ci-non-linux.yml:42` — while the hot `clippy`/`nextest` path runs a curated feature string. Moving the hot path to `full` would add candle's ~541 packages to every PR for no new coverage. - `--all-features` is **not a supported build mode**: `sqlite` and `postgres` are mutually exclusive and `--all-features` triggers a `compile_error!`. --- ## 5. Key Invariants -1. **No pure-marker flags.** A flag that gates only behavioral code with no distinct optional dependency MUST NOT exist. Remove it and make the code unconditional. +1. **No pure-marker flags.** A flag that gates only behavioral code with no distinct optional dependency MUST NOT exist. Remove it and make the code unconditional. A marker flag whose only effect is to **withhold** test-double or test-harness code from non-test builds is exempt from this rule; §5.1 governs markers that gate shipped behaviour. This covers `zeph-llm/testing`, `zeph-memory/testing`, and the five `mock` flags (`zeph-vault`, `zeph-mcp`, `zeph-plugins`, `zeph-experiments`, `zeph-core`). 2. **Flags only for real optional deps or platform exclusives.** The gated content must be a crate or a transitive dependency that would otherwise link unconditionally. @@ -182,7 +188,7 @@ Bundle invariants: 5. **Mutual exclusion must be enforced at compile time.** The `sqlite` and `postgres` flags activate a `compile_error!` in `zeph-db` when both are set. This guard must never be removed. -6. **`--features full` is the CI gate.** Pre-merge checks (`fmt`, `clippy`, `nextest`) run with `--features full`. This must match what CI runs exactly. +6. **The `full` configuration must be exercised by CI on every PR.** This is satisfied by four independent legs — coverage (`ci.yml:507`), `bundle-check (full)`, `release-build-full`, and `ci-non-linux.yml:42` — rather than requiring a literal `--features full` on the hot `clippy`/`nextest` path. Moving that path to `full` would add candle's ~541 packages to every PR for no new coverage. 7. **Flag names use kebab-case.** No underscores, no camelCase. @@ -220,7 +226,7 @@ Before opening a PR that adds a new feature flag: ### Always (without asking) - Default features must satisfy §2 Decision Rule AND `Tested` coverage — verify both before adding -- Run `--features full` for all pre-merge checks +- Run the curated pre-merge feature string for `fmt`/`clippy`/`nextest`; ensure `full` stays covered by its four independent CI legs (§4, §5.6) - Use `dep:` prefix for all optional crate dependencies - Remove `#[cfg(feature = "...")]` gates for deleted flags diff --git a/specs/055-cocoon/spec.md b/specs/055-cocoon/spec.md index 8cbe72312..3cc2636b2 100644 --- a/specs/055-cocoon/spec.md +++ b/specs/055-cocoon/spec.md @@ -50,7 +50,7 @@ related: | File | Contents | |---|---| -| `crates/zeph-llm/src/cocoon/mod.rs` | Module root, feature gate | +| `crates/zeph-llm/src/cocoon/mod.rs` | Module root (always compiled — consolidated v0.22.x, spec 029 §3.3) | | `crates/zeph-llm/src/cocoon/client.rs` | `CocoonClient` — HTTP transport, health check, model listing | | `crates/zeph-llm/src/cocoon/provider.rs` | `CocoonProvider : LlmProvider` | | `crates/zeph-llm/src/cocoon/tests.rs` | Unit tests | @@ -182,7 +182,7 @@ THEN a spinner appears, the /stats endpoint is queried, and the result (proxy_co | NFR-1 | Reliability | All `CocoonClient` HTTP requests MUST use a configurable timeout (default 30 s); no request ever blocks indefinitely | | NFR-2 | Resilience | WHEN the sidecar is unreachable THE SYSTEM SHALL return `LlmError::Unavailable` without panicking; no `unwrap()` in any Cocoon code path | | NFR-3 | Observability | All async I/O in the Cocoon module MUST be wrapped in `tracing::info_span!` with names `llm.cocoon.request`, `llm.cocoon.health`, `llm.cocoon.models` | -| NFR-4 | Portability | The `cocoon` feature MUST compile cleanly with and without `--features cocoon`; no conditional compilation leakage | +| NFR-4 | Portability | ~~The `cocoon` feature MUST compile cleanly with and without `--features cocoon`~~ — superseded: the module is always compiled (spec 029 §3.3, 2026-08 audit) | | NFR-5 | Minimalism | Zero new Cargo dependencies; `reqwest` (already in workspace) is the only HTTP transport needed | | NFR-6 | Security | `ZEPH_COCOON_ACCESS_HASH` MUST be loaded exclusively from the age vault; never from env vars or plain config fields | | NFR-7 | Testability | Unit tests MUST cover `CocoonClient` via a local mock server (wiremock pattern); integration tests MUST be gated behind `#[ignore]` | @@ -219,7 +219,7 @@ Cocoon Worker (TEE + GPU) ``` crates/zeph-llm/src/cocoon/ -├── mod.rs — module root, feature gate (#[cfg(feature = "cocoon")]) +├── mod.rs — module root (always compiled) ├── provider.rs — CocoonProvider : LlmProvider ├── client.rs — CocoonClient: HTTP transport, health check, model listing └── tests.rs — unit tests (mock server) @@ -440,10 +440,10 @@ Result: 5/5 checks passed |----|--------|--------| | SC-001 | `CocoonProvider` passes all `LlmProvider` method tests | 100% | | SC-002 | `zeph cocoon doctor` exits 0 when sidecar is healthy | 100% | -| SC-003 | Feature compiles cleanly with and without `--features cocoon` | 100% | +| SC-003 | ~~Feature compiles cleanly with and without `--features cocoon`~~ — superseded, always compiled | N/A | | SC-004 | Zero new Cargo dependencies introduced | 0 new deps | | SC-005 | All async I/O paths have tracing spans | 100% coverage | -| SC-006 | clippy `--features cocoon -D warnings` passes | 0 warnings | +| SC-006 | clippy `-D warnings` passes | 0 warnings | --- diff --git a/specs/066-deep-link-scheme/spec.md b/specs/066-deep-link-scheme/spec.md index 917ddb548..89158ec7b 100644 --- a/specs/066-deep-link-scheme/spec.md +++ b/specs/066-deep-link-scheme/spec.md @@ -203,13 +203,10 @@ pub enum AcpPreference { ## 8. Feature Flag -Cargo feature: `deep-link`. Added in root `Cargo.toml` `[features]`. -Included in the `desktop` bundle. -NOT in `default` (per NFR-7.1 and Feature Flag Contract §9). - -All `deep-link`-specific code in `src/` is gated with `#[cfg(feature = "deep-link")]`. -`zeph-common/src/deep_link.rs` is always compiled (no feature gate on the parser itself, -which has no runtime cost) — only the CLI subcommands and OS registration code are gated. +Consolidated into an always-on capability in the 2026-08 feature-flag audit (spec 029 §3.3): +the `deep-link` Cargo feature has been removed and all deep-link code compiles unconditionally. +OS-level `zeph://` scheme registration remains opt-in at the user level — via the `--init` +wizard step or an explicit CLI subcommand — not gated by a Cargo feature or a config toggle. ## 9. TUI Status Message diff --git a/src/agent_setup.rs b/src/agent_setup.rs index 8c0464146..c39f7e11f 100644 --- a/src/agent_setup.rs +++ b/src/agent_setup.rs @@ -2584,7 +2584,6 @@ pub(crate) fn apply_whisper_stt( /// /// [`CocoonClient`]: zeph_llm::cocoon::CocoonClient /// [`CocoonSttProvider`]: zeph_llm::cocoon::CocoonSttProvider -#[cfg(feature = "cocoon")] pub(crate) fn apply_cocoon_stt( agent: zeph_core::agent::Agent, entry: &zeph_core::config::ProviderEntry, diff --git a/src/cli.rs b/src/cli.rs index 3babeed76..87c96eb03 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -361,14 +361,12 @@ pub(crate) struct Cli { /// Initial prompt pre-queued from a deep-link URI (set by `handle_url_open` before bootstrap). /// /// Not a CLI flag — populated programmatically by the `url-open` dispatch arm. - #[cfg(feature = "deep-link")] #[arg(skip)] pub(crate) deep_link_prompt: Option, /// URI that originated this session (emitted as a TUI status notification). /// /// Not a CLI flag — populated programmatically by the `url-open` dispatch arm. - #[cfg(feature = "deep-link")] #[arg(skip)] pub(crate) deep_link_uri: Option, } @@ -568,7 +566,6 @@ pub(crate) enum Command { command: GonkaCommand, }, /// Cocoon sidecar diagnostics - #[cfg(feature = "cocoon")] Cocoon { #[command(subcommand)] command: CocoonCommand, @@ -600,13 +597,11 @@ pub(crate) enum Command { command: KnowledgeCommand, }, /// Open a session from a zeph:// URI dispatched by the OS scheme handler. - #[cfg(feature = "deep-link")] UrlOpen { /// The `zeph://` URI to dispatch (e.g. `zeph://new-session?prompt=Hello`) uri: String, }, /// Manage OS-level `zeph://` scheme registration. - #[cfg(feature = "deep-link")] UrlScheme { #[command(subcommand)] command: UrlSchemeCommand, @@ -1277,7 +1272,6 @@ pub(crate) enum GonkaCommand { } /// Cocoon sidecar subcommands. -#[cfg(feature = "cocoon")] #[derive(Subcommand)] pub(crate) enum CocoonCommand { /// Run Cocoon sidecar connectivity and configuration diagnostics @@ -1299,7 +1293,6 @@ pub(crate) enum NotifyCommand { } /// Subcommands for `zeph url-scheme`. -#[cfg(feature = "deep-link")] #[derive(Subcommand)] pub(crate) enum UrlSchemeCommand { /// Register the `zeph://` URI scheme with the OS. @@ -1733,7 +1726,6 @@ mod tests { } } - #[cfg(feature = "deep-link")] #[test] fn cli_parses_url_open() { use super::Command; @@ -1745,7 +1737,6 @@ mod tests { )); } - #[cfg(feature = "deep-link")] #[test] fn cli_parses_url_scheme_register() { use super::{Command, UrlSchemeCommand}; @@ -1758,7 +1749,6 @@ mod tests { )); } - #[cfg(feature = "deep-link")] #[test] fn cli_parses_url_scheme_status() { use super::{Command, UrlSchemeCommand}; @@ -1771,7 +1761,6 @@ mod tests { )); } - #[cfg(feature = "deep-link")] #[test] fn cli_parses_url_scheme_status_check_flag() { use super::{Command, UrlSchemeCommand}; diff --git a/src/commands/bench.rs b/src/commands/bench.rs index ee6c77f2e..d6c1880b6 100644 --- a/src/commands/bench.rs +++ b/src/commands/bench.rs @@ -42,7 +42,7 @@ pub(crate) async fn handle_bench_command( no_deterministic, } => { if *baseline { - handle_run_baseline( + Box::pin(handle_run_baseline( dataset, output, data_file.as_deref(), @@ -54,7 +54,7 @@ pub(crate) async fn handle_bench_command( vault_override, vault_key_override, vault_path_override, - ) + )) .await } else { handle_run( @@ -574,7 +574,7 @@ mod tests { let config_path = dir.path().join("missing-config.toml"); let output = dir.path().join("out.json"); - let err = handle_run_baseline( + let err = Box::pin(handle_run_baseline( "longmemeval", &output, Some(&data_file), @@ -586,7 +586,7 @@ mod tests { Some("bogus_backend_name"), None, None, - ) + )) .await .expect_err("bogus --vault backend must be rejected"); diff --git a/src/commands/cocoon.rs b/src/commands/cocoon.rs index baa13fdc1..20214ed96 100644 --- a/src/commands/cocoon.rs +++ b/src/commands/cocoon.rs @@ -399,7 +399,6 @@ mod tests { use super::*; use crate::commands::doctor::CheckStatus; - #[cfg(feature = "cocoon")] #[test] fn cocoon_doctor_cli_parses() { use crate::cli::{Cli, CocoonCommand, Command}; @@ -417,7 +416,6 @@ mod tests { )); } - #[cfg(feature = "cocoon")] #[test] fn cocoon_doctor_cli_parses_json_flag() { use crate::cli::{Cli, CocoonCommand, Command}; @@ -432,7 +430,6 @@ mod tests { )); } - #[cfg(feature = "cocoon")] #[test] fn cocoon_doctor_cli_parses_timeout() { use crate::cli::{Cli, CocoonCommand, Command}; @@ -657,7 +654,6 @@ mod tests { assert_eq!(results[0].status, CheckStatus::Ok); } - #[cfg(feature = "cocoon")] #[tokio::test] #[ignore = "requires running Cocoon sidecar (COCOON_TEST_URL)"] async fn test_doctor_all_pass() { diff --git a/src/commands/doctor.rs b/src/commands/doctor.rs index e577c6cd2..a6bc050ab 100644 --- a/src/commands/doctor.rs +++ b/src/commands/doctor.rs @@ -889,7 +889,6 @@ fn build_doctor_transport(server: &zeph_config::McpServerConfig) -> zeph_mcp::Mc /// scheme is not registered (non-fatal — user may not have run `url-scheme register`), and /// `Fail` when the registration exists but points to a binary that is missing or does not match /// the currently running executable. -#[cfg(feature = "deep-link")] fn check_url_scheme() -> CheckResult { use crate::url_scheme::register; let start = Instant::now(); @@ -1102,8 +1101,7 @@ async fn build_doctor_report( results.push(check_mcp_server(server, &config.mcp, mcp_timeout_secs).await); } - // 16. url_scheme.registration (deep-link feature only) - #[cfg(feature = "deep-link")] + // 16. url_scheme.registration results.push(check_url_scheme()); // 17. integrity.anchor / durable.seal (issue #6449) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index e56d7ad52..7b5abb58b 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,7 +7,6 @@ pub(crate) mod agents; #[cfg(feature = "bench")] pub(crate) mod bench; pub(crate) mod classifiers; -#[cfg(feature = "cocoon")] pub(crate) mod cocoon; pub(crate) mod db; pub(crate) mod doctor; @@ -20,7 +19,6 @@ pub(crate) mod memory; pub(crate) mod migrate; pub(crate) mod plugin; pub(crate) mod project; -#[cfg(feature = "registry")] pub(crate) mod registry_client; pub(crate) mod router; #[cfg(feature = "scheduler")] diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 85b4787c2..fdbc264e3 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -49,11 +49,6 @@ fn print_overlay_section(plugins_dir: &std::path::Path) -> anyhow::Result<()> { /// # Errors /// /// Returns an error if the plugin operation fails (invalid manifest, conflicts, etc.). -// `async` is unused when compiled without the `registry` feature (the Search/Get arms' -// `.await` calls are cfg'd out, leaving the fn body synchronous) — the signature must stay -// `async` regardless, since `runner.rs` always `.await`s this call and the feature is a -// caller-invisible build-time choice (M1, critic handoff). -#[allow(clippy::unused_async)] #[allow(clippy::too_many_lines)] pub(crate) async fn handle_plugin_command( cmd: PluginCommand, @@ -138,57 +133,30 @@ pub(crate) async fn handle_plugin_command( } PluginCommand::Search { query } => { - #[cfg(feature = "registry")] - { - registry_search( - &config, - &query, - vault_override, - vault_key_override, - vault_path_override, - ) - .await?; - } - #[cfg(not(feature = "registry"))] - { - let _ = &query; - println!( - "This zeph build was compiled without the `registry` feature; rebuild \ - with `--features registry` (or `full`) to use `zeph plugin search`." - ); - } + registry_search( + &config, + &query, + vault_override, + vault_key_override, + vault_path_override, + ) + .await?; } PluginCommand::Get { registry_id } => { - #[cfg(feature = "registry")] - { - // Fetched packages install via the same `mgr.add(...)` path as `plugin add`, so - // they get the same reputation check (spec-043, #5864). No `--strict-reputation` - // flag on `get` — config's `enforcement` applies as-is. - let mgr = mgr.with_reputation_config(&config.plugins.reputation, false); - registry_get( - &config, - &mgr, - ®istry_id, - vault_override, - vault_key_override, - vault_path_override, - ) - .await?; - } - #[cfg(not(feature = "registry"))] - { - let _ = ( - ®istry_id, - vault_override, - vault_key_override, - vault_path_override, - ); - println!( - "This zeph build was compiled without the `registry` feature; rebuild \ - with `--features registry` (or `full`) to use `zeph plugin get`." - ); - } + // Fetched packages install via the same `mgr.add(...)` path as `plugin add`, so + // they get the same reputation check (spec-043, #5864). No `--strict-reputation` + // flag on `get` — config's `enforcement` applies as-is. + let mgr = mgr.with_reputation_config(&config.plugins.reputation, false); + registry_get( + &config, + &mgr, + ®istry_id, + vault_override, + vault_key_override, + vault_path_override, + ) + .await?; } } @@ -201,7 +169,6 @@ pub(crate) async fn handle_plugin_command( /// Prints [`crate::commands::registry_client::REGISTRY_NOT_CONFIGURED_MSG`] and makes zero /// network calls when `skills.registry.enabled = false` (FR-004, NFR-001). Thin wrapper around /// [`registry_search_with`] — see that fn's tests for `MockRegistryClient`-driven coverage. -#[cfg(feature = "registry")] #[tracing::instrument(name = "plugin.registry_search", skip(config), fields(query))] async fn registry_search( config: &zeph_core::config::Config, @@ -233,7 +200,6 @@ async fn registry_search( /// Search logic parameterized over a [`zeph_plugins::marketplace::RegistryClient`] — split out /// of [`registry_search`] so tests can drive it with `MockRegistryClient` without network or a /// real `Config`/vault (review fix #4). -#[cfg(feature = "registry")] async fn registry_search_with( client: &dyn zeph_plugins::marketplace::RegistryClient, query: &str, @@ -256,7 +222,6 @@ async fn registry_search_with( /// `zeph skill get` when the fetched package has no `plugin.toml` (a bare skill package). /// Thin wrapper around [`registry_get_with`] — see that fn's tests for `MockRegistryClient`- /// driven coverage. -#[cfg(feature = "registry")] #[tracing::instrument(name = "plugin.registry_get", skip(config, mgr), fields(registry_id))] async fn registry_get( config: &zeph_core::config::Config, @@ -288,7 +253,6 @@ async fn registry_get( /// Fetch-and-install logic parameterized over a [`zeph_plugins::marketplace::RegistryClient`] — /// split out of [`registry_get`] so tests can drive it with `MockRegistryClient` (review fix #4). -#[cfg(feature = "registry")] async fn registry_get_with( client: &dyn zeph_plugins::marketplace::RegistryClient, mgr: &zeph_plugins::PluginManager, @@ -331,7 +295,7 @@ async fn registry_get_with( Ok(()) } -#[cfg(all(test, feature = "registry"))] +#[cfg(test)] mod registry_tests { use super::*; use zeph_plugins::marketplace::RegistryEntry; diff --git a/src/commands/registry_client.rs b/src/commands/registry_client.rs index 423bafe0b..e5c225f4c 100644 --- a/src/commands/registry_client.rs +++ b/src/commands/registry_client.rs @@ -116,9 +116,7 @@ pub(crate) fn build_registry_client( /// The standard "registry not configured" message for FR-004. /// -/// Distinct from the "compiled without the `registry` feature" message printed by the -/// `#[cfg(not(feature = "registry"))]` arm in `skill.rs`/`plugin.rs` — this one fires when the -/// feature IS compiled in but `enabled = false` (the default). +/// Fires when `enabled = false` (the default). pub(crate) const REGISTRY_NOT_CONFIGURED_MSG: &str = "no skill/plugin registry is configured. Add `[skills.registry] enabled = true` (and a \ backend_kind/backend_url) to config.toml, or run `zeph --init` to configure it \ interactively. See `zeph skill search --help`."; @@ -152,7 +150,7 @@ pub(crate) fn print_search_results(entries: &[zeph_plugins::marketplace::Registr } } -#[cfg(all(test, feature = "registry"))] +#[cfg(test)] mod tests { use super::*; use zeph_plugins::marketplace::RegistryEntry; diff --git a/src/commands/skill.rs b/src/commands/skill.rs index 965423db6..05f53850e 100644 --- a/src/commands/skill.rs +++ b/src/commands/skill.rs @@ -450,55 +450,28 @@ pub(crate) async fn handle_skill_command( } SkillCommand::Search { query } => { - #[cfg(feature = "registry")] - { - registry_search( - &config, - &query, - vault_override, - vault_key_override, - vault_path_override, - ) - .await?; - } - #[cfg(not(feature = "registry"))] - { - let _ = &query; - println!( - "This zeph build was compiled without the `registry` feature; rebuild \ - with `--features registry` (or `full`) to use `zeph skill search`." - ); - } + registry_search( + &config, + &query, + vault_override, + vault_key_override, + vault_path_override, + ) + .await?; } SkillCommand::Get { registry_id } => { - #[cfg(feature = "registry")] - { - registry_get( - &config, - &mgr, - &managed_dir, - &sqlite_path, - ®istry_id, - vault_override, - vault_key_override, - vault_path_override, - ) - .await?; - } - #[cfg(not(feature = "registry"))] - { - let _ = ( - ®istry_id, - vault_override, - vault_key_override, - vault_path_override, - ); - println!( - "This zeph build was compiled without the `registry` feature; rebuild \ - with `--features registry` (or `full`) to use `zeph skill get`." - ); - } + registry_get( + &config, + &mgr, + &managed_dir, + &sqlite_path, + ®istry_id, + vault_override, + vault_key_override, + vault_path_override, + ) + .await?; } } @@ -512,7 +485,6 @@ pub(crate) async fn handle_skill_command( /// [`registry_search_with`] so the fetch/search logic itself is testable in isolation from the /// config-gate and client construction — see that fn's tests for `MockRegistryClient`-driven /// coverage. -#[cfg(feature = "registry")] #[tracing::instrument(name = "skill.registry_search", skip(config), fields(query))] async fn registry_search( config: &zeph_core::config::Config, @@ -544,7 +516,6 @@ async fn registry_search( /// Search logic parameterized over a [`zeph_plugins::marketplace::RegistryClient`] — split out /// of [`registry_search`] so tests can drive it with `MockRegistryClient` without network or a /// real `Config`/vault (review fix #4). -#[cfg(feature = "registry")] async fn registry_search_with( client: &dyn zeph_plugins::marketplace::RegistryClient, query: &str, @@ -566,7 +537,6 @@ async fn registry_search_with( /// as `zeph skill install ` — no bypass of frontmatter validation or the /// injection-pattern scan (NFR-002). Thin wrapper around [`registry_get_with`] — see that fn's /// tests for `MockRegistryClient`-driven coverage. -#[cfg(feature = "registry")] #[tracing::instrument(name = "skill.registry_get", skip(config, mgr), fields(registry_id))] #[allow(clippy::too_many_arguments)] async fn registry_get( @@ -601,7 +571,6 @@ async fn registry_get( /// Fetch-and-install logic parameterized over a [`zeph_plugins::marketplace::RegistryClient`] — /// split out of [`registry_get`] so tests can drive it with `MockRegistryClient` (review fix #4). -#[cfg(feature = "registry")] async fn registry_get_with( client: &dyn zeph_plugins::marketplace::RegistryClient, mgr: &zeph_skills::manager::SkillManager, @@ -669,7 +638,7 @@ async fn registry_get_with( Ok(()) } -#[cfg(all(test, feature = "registry"))] +#[cfg(test)] mod registry_tests { use super::*; use zeph_plugins::marketplace::RegistryEntry; diff --git a/src/init/mod.rs b/src/init/mod.rs index 3a0bb7b62..bd71b8faa 100644 --- a/src/init/mod.rs +++ b/src/init/mod.rs @@ -365,11 +365,9 @@ pub(crate) struct WizardState { /// Provider name from `[[llm.providers]]` chosen for knowledge ingest (Phase 2 graph). /// Empty = use primary provider. pub(crate) knowledge_ingest_provider: String, - /// Whether to register the `zeph://` URI scheme during `--init` (deep-link feature). - #[cfg(feature = "deep-link")] + /// Whether to register the `zeph://` URI scheme during `--init`. pub(crate) deep_link_register: bool, /// Whether to require confirmation before injecting a deep-link prompt (INV-TRUST). - #[cfg(feature = "deep-link")] pub(crate) deep_link_confirm_before_prompt: bool, /// TUI visual theme name (preset or user file). pub(crate) tui_theme_name: String, @@ -613,9 +611,7 @@ impl Default for WizardState { durable_key_b64: None, caveman_default_on: false, knowledge_ingest_provider: String::new(), - #[cfg(feature = "deep-link")] deep_link_register: false, - #[cfg(feature = "deep-link")] deep_link_confirm_before_prompt: true, tui_theme_name: "zephyr".to_owned(), tui_color_mode: zeph_config::ColorMode::Auto, @@ -707,7 +703,6 @@ pub fn run(output: Option) -> anyhow::Result<()> { step_plugins_reputation(&mut state)?; step_caveman(&mut state)?; step_knowledge(&mut state)?; - #[cfg(feature = "deep-link")] step_deep_link(&mut state)?; step_tui_theme(&mut state)?; step_tui_delights(&mut state)?; @@ -1519,7 +1514,6 @@ pub(crate) fn build_config(state: &WizardState) -> Config { } // Apply deep-link security settings (spec-066, TASK-9). - #[cfg(feature = "deep-link")] { config.deep_link.confirm_before_prompt = state.deep_link_confirm_before_prompt; } @@ -2237,16 +2231,14 @@ fn step_knowledge(state: &mut WizardState) -> anyhow::Result<()> { /// Configure `zeph://` deep-link URI scheme (spec-066, TASK-9). /// /// Offers to register the OS-level `zeph://` handler and configures the security gate -/// (`confirm_before_prompt`). Gated behind the `deep-link` Cargo feature. +/// (`confirm_before_prompt`). /// /// # Errors /// /// Returns an error if the terminal prompt interaction fails. -#[cfg(feature = "deep-link")] fn step_deep_link(state: &mut WizardState) -> anyhow::Result<()> { println!("== Deep Link (zeph:// URI scheme) ==\n"); - println!("Allows other applications to open a Zeph session via a zeph:// URL."); - println!("Requires the binary to be compiled with --features deep-link.\n"); + println!("Allows other applications to open a Zeph session via a zeph:// URL.\n"); state.deep_link_register = Confirm::new() .with_prompt("Register the zeph:// URI scheme on this OS?") @@ -2464,7 +2456,6 @@ fn step_review_and_write(state: &WizardState, output: Option) -> anyhow print_next_steps(state, &path); // Perform OS-level scheme registration after writing the config (TASK-9). - #[cfg(feature = "deep-link")] if state.deep_link_register { println!("\nRegistering zeph:// URI scheme..."); match crate::url_scheme::register::handle_url_scheme_register() { diff --git a/src/main.rs b/src/main.rs index 76186e47b..e8926b479 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,7 +133,6 @@ mod startup_checks; mod tracing_init; mod tui_bridge; mod tui_remote; -#[cfg(feature = "deep-link")] mod url_scheme; use clap::Parser; diff --git a/src/runner.rs b/src/runner.rs index 4d1b48e04..db4051d95 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -16,7 +16,7 @@ use crate::tui_bridge::forward_status_to_stderr; use crate::tui_bridge::{ TuiRunParams, forward_index_progress_to_tui, run_tui_agent, start_tui_early, }; -#[cfg(all(feature = "tui", feature = "cocoon"))] +#[cfg(feature = "tui")] use tracing::Instrument as _; use crate::bootstrap::find_repo_root; @@ -25,13 +25,10 @@ use crate::bootstrap::resolve_config_path; #[cfg(not(feature = "tui"))] use crate::bootstrap::warmup_provider; use crate::bootstrap::{AppBuilder, create_mcp_registry}; -#[cfg(feature = "deep-link")] use crate::url_scheme::prompt::confirm_prompt; -#[cfg(feature = "deep-link")] use crate::url_scheme::validate::validate_deep_link_cwd; use parking_lot::RwLock; use zeph_channels::AnyChannel; -#[cfg(feature = "deep-link")] use zeph_common::deep_link::parse_deep_link; use zeph_common::{RestartPolicy, SessionId, TaskDescriptor, TaskSupervisor}; use zeph_config::{ThinkingConfig, ThinkingEffort}; @@ -802,7 +799,6 @@ fn configure_history_integrity(key_path: &std::path::Path, vault_path: &std::pat } #[allow(clippy::too_many_lines, clippy::large_futures)] -#[cfg_attr(not(feature = "deep-link"), allow(unused_mut))] pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { // Early-exit flags that do not require config loading. if cli.dump_config_defaults { @@ -1165,7 +1161,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { .await?; crate::tracing_init::exit_with_flush(tracing_guards, exit_code); } - #[cfg(feature = "cocoon")] Some(Command::Cocoon { command: crate::cli::CocoonCommand::Doctor { json, timeout_secs }, }) => { @@ -1218,7 +1213,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { return crate::commands::knowledge::handle_knowledge(kn_cmd, cli.config.as_deref()) .await; } - #[cfg(feature = "deep-link")] Some(Command::UrlScheme { command: url_scheme_cmd, }) => { @@ -1244,7 +1238,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { } }; } - #[cfg(feature = "deep-link")] Some(Command::UrlOpen { ref uri }) => { // Clone `uri` and `config` before passing `&mut cli` to avoid a // simultaneous borrow conflict (uri borrows cli.command while &mut cli is exclusive). @@ -1572,7 +1565,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { Some(tokio::spawn(async move { warmup_provider(&p).await })) // EXEMPT(#5143): awaited before agent.run(), needs JoinHandle }; - #[cfg(feature = "cocoon")] { let provider_refs: Vec<&zeph_core::config::ProviderEntry> = config.llm.providers.iter().collect(); @@ -3657,7 +3649,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { ); agent } - #[cfg(feature = "cocoon")] zeph_core::config::ProviderKind::Cocoon => agent_setup::apply_cocoon_stt( agent, stt_entry, @@ -3665,15 +3656,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { config.timeouts.llm_request_timeout_secs, Some(agent_status_tx.clone()), ), - #[cfg(not(feature = "cocoon"))] - zeph_core::config::ProviderKind::Cocoon => { - tracing::error!( - provider = stt_entry.effective_name(), - "STT provider is type cocoon but the `cocoon` feature is not enabled; \ - STT disabled" - ); - agent - } _ => { let api_key = resolve_stt_api_key(config, stt_entry); agent_setup::apply_whisper_stt(agent, stt_entry, language, api_key) @@ -3777,7 +3759,7 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { #[cfg(all(feature = "tui", feature = "scheduler"))] let metrics_tx_for_sched = metrics_tx.clone(); - #[cfg(all(feature = "tui", feature = "cocoon"))] + #[cfg(feature = "tui")] let metrics_tx_for_cocoon = metrics_tx.clone(); let extended_context = config .llm @@ -3911,7 +3893,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { }, }); } - #[cfg(feature = "cocoon")] if let Some(cocoon_cfg) = config .llm .providers @@ -4078,7 +4059,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { // INV-TRUST: sanitize the deep-link prompt before enqueuing it. // ContentSourceKind::McpResponse maps to ExternalUntrusted, the same tier as any // network-supplied text; the sanitizer applies injection detection and spotlighting. - #[cfg(feature = "deep-link")] if let Some(raw_prompt) = cli.deep_link_prompt.take() { use zeph_sanitizer::{ ContentIsolationConfig, ContentSanitizer, ContentSource, ContentSourceKind, @@ -4117,7 +4097,6 @@ pub(crate) async fn run(mut cli: Cli) -> anyhow::Result<()> { backfill_rx, task_supervisor: Some((*supervisor).clone()), fleet_session_id: fleet_session_id.clone(), - #[cfg(feature = "deep-link")] deep_link_uri: cli.deep_link_uri.take(), resume_banner: session_resume_info .as_ref() @@ -4485,7 +4464,6 @@ fn parse_plugin_url_arg(raw: &str) -> (&str, Option<&str>) { // point, but no spawned tasks read this env var on this call path — the guard is // a one-time write. #[allow(unsafe_code)] -#[cfg(feature = "deep-link")] fn handle_url_open( uri: String, config_override: Option<&std::path::Path>, @@ -4609,7 +4587,6 @@ fn handle_url_open( None } -#[cfg(feature = "deep-link")] #[cfg(test)] mod deep_link_tests { #[test] diff --git a/src/tui_bridge.rs b/src/tui_bridge.rs index 86fb38cb4..29939f7eb 100644 --- a/src/tui_bridge.rs +++ b/src/tui_bridge.rs @@ -40,7 +40,6 @@ pub(crate) struct TuiRunParams<'a> { /// /// When `Some`, a one-shot status notification is emitted in the TUI status area within /// 1 s of launch per spec §9 (TASK-8). - #[cfg(feature = "deep-link")] pub(crate) deep_link_uri: Option, /// Pre-formatted "Resuming session" banner text (spec-068 §13.5), sent as /// `AgentEvent::ResumeBanner` once the TUI's `agent_tx` is available. `None` for a fresh @@ -422,7 +421,6 @@ pub(crate) async fn run_tui_agent( // TASK-8: emit a one-shot deep-link notification within 1 s of launch. // Tracked in `forwarders` so it is aborted cleanly when the TUI or agent exits. - #[cfg(all(feature = "deep-link", feature = "tui"))] if let Some(uri) = params.deep_link_uri.take() { forwarders.spawn(deep_link_notification_task(agent_tx.clone(), uri)); } @@ -461,7 +459,7 @@ pub(crate) async fn run_tui_agent( } /// Emits a deep-link launch notification in the TUI status bar, then clears it after 3 s. -#[cfg(all(feature = "deep-link", feature = "tui"))] +#[cfg(feature = "tui")] async fn deep_link_notification_task( tx: tokio::sync::mpsc::Sender, uri: String,