# car-reason

Code reasoning engine for [Common Agent Runtime](https://github.com/Parslee-ai/car). Adaptive, graph-driven, learning.

## What it does

Instead of a hardcoded "diagnose → suggest → explain" pipeline, reasoning actions are **skills in the memgine graph**. The graph's spreading activation and success/failure tracking determine which actions to run, in what order, routed to which models. As outcomes accumulate, the routing improves without code changes.

## Usage

```rust,no_run
use std::sync::{Arc, Mutex};
use car_reason::ReasoningSession;

let inference = Arc::new(car_inference::InferenceEngine::new(Default::default()));
let mut memgine = car_memgine::MemgineEngine::new(None);

// Seed the default reasoning skills
car_reason::skills::seed_defaults(&mut memgine);

let memgine = Arc::new(Mutex::new(memgine));
let session = ReasoningSession::new(memgine, inference);
let result = session.reason("fix the off-by-one error in parse_header").await?;

println!("Diagnosis: {}", result.diagnosis);
for s in &result.suggestions {
    println!("Fix: {}", s.suggested);
}
println!("Explanation: {}", result.explanation);
```

## Where it fits

A higher-level capability over `car-memgine` + `car-inference` + `car-ast`. Used by the `car` CLI's diagnostic / fix slash commands and by `.claude-plugin/agents/reason.md`.

## Inference handle abstraction (Parslee-ai/car#189)

`ReasoningSession` does not depend on the concrete `InferenceEngine`. It holds an `Arc<dyn ReasoningInferenceHandle>` — the narrow trait surface this crate exports in [`handle.rs`](src/handle.rs).

The trait extends [`car_inference::InferenceHandle`](../car-inference/src/handle.rs) (which provides `generate` + `embed`) with three reasoning-specific methods:

| Method | Used for | In-process impl | Daemon impl |
|---|---|---|---|
| `generate_tracked(req)` | Per-action `InferenceResult` (trace_id, model_used, latency_ms) | `InferenceEngine::generate_tracked` | daemon's `infer` JSON-RPC method |
| `find_model_by_name(name)` | Tier-picker checks whether a candidate model is registered before naming it | `unified_registry.find_by_name` | cached `models.list_unified` snapshot |
| `record_inferred_outcomes(action_results)` | Post-session learning loop (writes inferred outcomes for each action's trace) | takes the engine's tracker write lock | best-effort no-op (no `outcomes.resolve_pending` endpoint yet — follow-up) |

This lets callers pick the inference backend at runtime:

```rust
// In-process (offline dev, tests, CI)
let inference: Arc<dyn ReasoningInferenceHandle> =
    Arc::new(car_inference::InferenceEngine::new(Default::default()));

// Daemon-routed (`car-server` running locally; shared hot weights + accumulated profiles)
let inference: Arc<dyn ReasoningInferenceHandle> =
    Arc::new(DaemonInferenceHandle::new("ws://127.0.0.1:9100/"));
```

`cmd_reason` in `car-cli` does the runtime selection — probes the daemon with a cheap `models.list` RPC and falls back to an embedded engine if unreachable. See `car-rs/crates/car-cli/src/daemon_handle.rs` for the daemon-side `ReasoningInferenceHandle` impl.

**Why a new trait instead of growing `InferenceHandle`.** `InferenceHandle` (added in #188 for memgine) is intentionally minimal — `generate` + `embed` — because memgine is reachable from many crates and a wide trait surface multiplies daemon-proxy work for every implementor. `ReasoningInferenceHandle` extends it with the three methods car-reason actually uses, keeping the lower-level trait clean while still letting daemon impls compose: `DaemonInferenceHandle` implements both, with one shared WebSocket connection.
