> **🏗️ build-from-issue-agent**
Closes#71
## Summary
Add dynamic shell completion support using `clap_complete`'s `CompleteEnv`, plus a `navigator completions <shell>` subcommand (rustup-style) that outputs completion scripts to stdout with detailed per-shell setup instructions in `--help`.
Completions are dynamic — the shell calls back into the `navigator` binary on each tab-press, so they are always in sync with the installed version. This also lays the groundwork for future runtime completers (e.g., listing sandbox names on tab, tracked in #83).
## Changes Made
- `Cargo.toml`: Added `clap_complete = { version = "4.5", features = ["unstable-dynamic"] }` to workspace dependencies
- `crates/navigator-cli/Cargo.toml`: Added `clap_complete` dependency
- `crates/navigator-cli/src/main.rs`:
- Added `CompleteEnv::with_factory(Cli::command).complete()` at the top of `main()` (before TLS/argument parsing)
- Added `Completions { shell }` subcommand that re-invokes the binary with `COMPLETE=<shell>` and pipes the output
- Added `CompletionShell` enum (bash, fish, zsh, powershell)
- Added `COMPLETIONS_HELP` with per-shell setup instructions (similar to `rustup completions --help`)
- Added 3 unit tests
- `CONTRIBUTING.md`: Added shell completions section documenting `nav` wrapper setup
## Usage
```bash
# Generate completions
navigator completions fish > ~/.config/fish/completions/navigator.fish
navigator completions bash > ~/.local/share/bash-completion/completions/navigator
navigator completions zsh > ~/.zfunc/_navigator
navigator completions powershell >> $PROFILE
# See full per-shell instructions
navigator completions --help
# For the nav wrapper (rewrite registration target)
navigator completions fish | sed 's/--command navigator/--command nav/' > ~/.config/fish/completions/nav.fish
```
## Design Decision: Why stdout-only (no auto-install)
We researched how 12 popular CLI tools handle completion installation:
| Tool | Command | Writes files? | Modifies RC? |
|------|---------|---------------|-------------|
| gh, glab, kubectl, docker, rustup, mise, starship | `completion <shell>` | stdout only | No |
| atuin | `gen-completions --shell <shell>` | stdout (`--out-dir` option) | No |
| gcloud | `install.sh` | Yes | **Yes** |
| Amazon Q | `q integrations install` | Yes | **Yes** |
**10 of 12 tools** follow the same pattern: output to stdout, never modify RC files, explicit shell selection, clear `--help` instructions. Only gcloud and Amazon Q modify RC files (and both have been criticized for it). Our approach matches the industry consensus.
## Deviations from Plan
- The original issue proposed a static `completions <shell>` subcommand using `clap_complete::generate()`. After discussion, we switched to dynamic `CompleteEnv` which is always up-to-date and enables future runtime completers.
- Added a `navigator completions <shell>` subcommand (not in original plan) for discoverability — it wraps the `COMPLETE` env var mechanism with a familiar CLI UX.
## Tests Added
- **Unit:** `cli_debug_assert` — validates CLI structure; `completions_engine_returns_candidates` — smoke test confirming the completion engine returns subcommand candidates; `completions_subcommand_appears_in_candidates` — verifies `completions` appears in tab-completion results
- **Integration:** N/A
- **E2E:** N/A
## Verification
- [x] All tests passing
- [x] Pre-commit checks passing (clippy warnings are pre-existing, not from this change)
- [x] CONTRIBUTING.md updated with nav wrapper completion setup
> **🏗️ build-from-issue-agent**
Closes#79
## Summary
Replaces the gateway-proxied inference model (`ProxyInference` RPC) with sandbox-local execution. The sandbox now resolves routes from either a standalone YAML route file or a cluster bundle fetched via the new `GetSandboxInferenceBundle` RPC, then forwards requests directly to inference backends using `navigator-router`.
### Benefits of removing the gRPC hop
Moving inference execution from the gateway to the sandbox eliminates the gRPC round-trip for every inference request:
- **No payload size limits**: Direct HTTP forwarding replaces protobuf-over-gRPC serialization.
- **Streaming-ready**: Preserves connection semantics for future SSE support (e.g., `stream: true`).
- **Lower latency**: Direct sandbox → backend path instead of sandbox → gateway → backend round-trip.
- **Reduced gateway load**: Gateway only serves the control-plane bundle delivery RPC.
- **Simpler error model**: HTTP status codes flow through directly without gRPC wrapping.
## Changes Made
- **Proto**: Removed `ProxyInference` RPC. Added `GetSandboxInferenceBundle` RPC for route bundle delivery.
- **Gateway**: Replaced `proxy_inference` with `get_sandbox_inference_bundle`. Removed `Router` and `navigator-router` dependency — gateway is now control-plane only for inference.
- **Router**: Switched config file format from TOML to YAML. Added `Debug` impl that redacts API keys.
- **Sandbox**: New `InferenceContext` with local `Router` + route cache. Routes loaded from `--inference-routes` file (standalone) or cluster bundle via gRPC, with 30s background refresh. New `--inference-routes` / `NAVIGATOR_INFERENCE_ROUTES` CLI arg.
- **Dev sandbox**: Added `inference-routes.yaml` at repo root with default NVIDIA NIM route. `mise run sandbox` now mounts it automatically and supports `-e` flag to forward env vars (e.g., `mise run sandbox`).
- **Example**: Added standalone example (`examples/inference/routes.yaml`) and rewrote README to cover both standalone and cluster workflows.
- **E2E tests**: Updated existing test names/docs. Added tests for Anthropic messages protocol and multi-route policy filtering.
## Tests Added
- **Unit (21 new):** `router_error_to_http` (6 variants), `load_from_file` YAML round-trip (5), `resolve_sandbox_inference_bundle` gRPC handler (6), `build_inference_context` route loading (4)
- **E2E (2 new):** Anthropic messages protocol routing, route filtering by `allowed_routes`
- **Total:** 188 unit tests passing across 3 crates
## Documentation Updated
- `architecture/gateway.md`: Removed router, updated Inference Service
- `architecture/sandbox.md`: Updated orchestration flow, InferenceContext, route loading
- `architecture/inference-routing.md`: Complete rewrite for sandbox-local architecture
## Verification
- [x] All unit tests passing (188 across 3 crates)
- [x] Pre-commit Rust checks passing (fmt, clippy, tests)
- [x] Architecture documentation updated
- [x] E2E tests updated with new test cases
- [x] Standalone example and documentation added
- [x] Default inference-routes.yaml with env passthrough for mise run sandbox
The sandbox now automatically creates directories listed in the policy's
`read_write` section and sets ownership to the configured sandbox user/group.
This runs in the supervisor process before forking the child.
Previously, read_write directories had to be pre-created in the Dockerfile
with correct ownership. Now arbitrary paths can be added to the policy
and they will be prepared at runtime.
Also adds a hardcoded chown for /sandbox in Dockerfile.factory as a
belt-and-suspenders approach for the default case.