# Neuron Council Service

Anti-confabulation layer for the Neuron soul. Before a claim enters long-term memory, the council convenes: three independent LLMs vote on whether the claim is plausible, uncertain, or a confabulation. The aggregate vote produces a confidence score and tags that downstream storage can act on.

## Running the service

```bash
# Foreground
python3 council_service.py --port 7771

# Background (managed by LaunchAgent on macOS)
launchctl load ~/Library/LaunchAgents/ai.neuron.council.plist
launchctl unload ~/Library/LaunchAgents/ai.neuron.council.plist
```

Logs: `~/.neuron/logs/council.log`

## API

### `POST /api/neuron/council/verify`

```json
// Request
{ "claim": "...", "context": "..." }

// Response
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "claim": "...",
  "confidence": 0.85,
  "council_votes": ["plausible", "plausible", "plausible"],
  "summary": "3/3 council members agree this is plausible.",
  "tags": ["verified"],
  "latency_ms": 1420
}
```

### `GET /healthz`

Returns `{"status": "ok"}` when the service is up.

## Confidence thresholds and tag meanings

| Votes plausible | Confidence | Tags |
|---|---|---|
| 3/3 | 0.85 | `verified` |
| 2/3 | 0.65 | `council-split` |
| 1/3 or 0/3 | 0.30 | `unverified`, `council-flagged` |
| Ollama down | 0.50 | `council-unavailable` |

Recommended storage policy:
- `confidence >= 0.65` → store normally
- `0.30 <= confidence < 0.65` → store with `council-split` tag for later review
- `council-flagged` → store in a quarantine bucket or reject entirely
- `council-unavailable` → store normally (fail-open); council will re-evaluate later

## How to call from soul (.el)

The soul is implemented in Neuron's Emacs Lisp-like `.el` language. Add a pre-storage hook in the memory capture path:

```elisp
;; In memory.el or safety.el — pre-storage council check
(defun council-verify (claim context)
  "Call the council service. Returns a plist with :confidence and :tags."
  (let* ((url "http://localhost:7771/api/neuron/council/verify")
         (body (json-encode `((claim . ,claim) (context . ,context))))
         (resp (neuron-http-post url body))
         (data (json-decode resp)))
    data))

;; In the capture handler — wire it in before (engram-write ...)
(defun capture-memory-with-council (claim context &rest store-args)
  (let* ((verdict (council-verify claim context))
         (confidence (plist-get verdict :confidence))
         (tags (plist-get verdict :tags)))
    (when (>= confidence 0.30)  ; only reject hard confabulations if you want
      (apply #'engram-write
             (append store-args
                     (list :council-confidence confidence
                           :council-tags tags))))))
```

The exact hook point depends on where `engram-write` (or equivalent) is called in `memory.el`. Search for the write call and wrap it with `capture-memory-with-council`.

## Future soul.c patch point

If the soul is ever rewritten in C or another compiled language, the integration point is:

```c
// Before inserting a memory node into the engram database:
CouncilResult result = council_verify(claim, context);
if (result.confidence < COUNCIL_REJECT_THRESHOLD) {
    log_warn("Council flagged claim as confabulation (conf=%.2f): %s",
             result.confidence, claim);
    return MEMORY_REJECTED;
}
memory_node.council_confidence = result.confidence;
memory_node.council_tags = result.tags;
engram_insert(memory_node);
```

## Council members

The council is currently three models:
- `neuron:latest` — the primary Neuron model
- `dolphin3:8b` — uncensored general-purpose model for independent perspective
- `neuron-ft:latest` — fine-tuned Neuron variant

Each member votes independently with a 10-second timeout. If a member times out, their vote counts as "uncertain". If Ollama is entirely unreachable, the service returns `council-unavailable` immediately (fail-open: confidence 0.5, no rejection).

## Example curl

```bash
# Should get high confidence (true fact)
curl -s http://localhost:7771/api/neuron/council/verify -X POST \
  -H 'Content-Type: application/json' \
  -d '{"claim": "Neuron is a personal AI memory system built by Will Anderson", "context": "product description"}'

# Should get low confidence (false claim)
curl -s http://localhost:7771/api/neuron/council/verify -X POST \
  -H 'Content-Type: application/json' \
  -d '{"claim": "The Eiffel Tower is located in Berlin and was built in 1950", "context": "geography"}'
```
