● Zion Boggan
repos/Darwin/cortex/CONTRACTS.md
zionboggan.com ↗
118 lines · markdown
History for this file →
1
# Darwin Cortex - Gateway Contract (binding)
2
 
3
The keystone. One service, one verb: **`ask`**. Fans out to every memory engine in parallel,
4
reconciles by the source-of-truth hierarchy, synthesizes one answer with clickable receipts, and
5
is itself exposed as the single MCP every agent calls.
6
 
7
**Runtime:** Python 3.11 · FastAPI · `httpx` (async) · `mcp` SSE client · `pydantic` v2.
8
**Lives on:** Darwin CT (REDACTED-IP). During dev may run on CT215.
9
**Ports:** HTTP API `:9890` · MCP SSE shim `:9891`.
10
 
11
---
12
 
13
## 1. Engines (fan-out targets) - all already live on REDACTED-IP
14
 
15
| key | transport | primary calls | answers |
16
|---|---|---|---|
17
| `rag` | SSE `:9876` | `homelab_search`, `homelab_context` | content / what / where / why |
18
| `cbm` | SSE `:9877` | `search_graph`, `get_architecture` | code structure, blast radius |
19
| `serena` | SSE `:9878` | `find_symbol`, `find_referencing_symbols` | live symbol truth |
20
| `memgraph` | SSE `:9879` | `query_entities`, `query_relations` | decisions, ownership, handoffs |
21
| `inbox` | HTTP (Memos API) | list/search memos | Zion's raw captures |
22
 
23
Each engine is wrapped in an **adapter** with: a hard timeout (default 4000ms), a normalizer that
24
maps its native result into the common `Hit` shape, and **graceful degradation** - a dead engine
25
returns `{engine, status:"down", hits:[]}`, never throws. The set of up/down engines is returned to
26
the UI verbatim (it drives the organism's health-as-anatomy).
27
 
28
---
29
 
30
## 2. API
31
 
32
### `POST /ask`
33
```jsonc
34
// request
35
{
36
  "query": "string",
37
  "mode": "both",          // "chat" | "search" | "both"  (default "both")
38
  "scope": ["rag","cbm"],  // optional engine subset; default all
39
  "k": 12,                 // max hits per engine
40
  "synthesize": true       // run LLM synthesis for the chat answer
41
}
42
```
43
```jsonc
44
// response
45
{
46
  "answer": "string|null",          // prose synthesis (null if mode=search or synth unavailable)
47
  "receipts": [                      // every claim in `answer` cites one of these
48
    {"id":"r1","source":"rag","path":"...","last_seen":"2026-06-24T00:04:00Z","confidence":0.92,"snippet":"..."}
49
  ],
50
  "hits": [                          // raw ranked results (the "search" half of the surface)
51
    {"engine":"rag","title":"...","path":"...","snippet":"...","score":0.81,
52
     "last_seen":"...","confidence":0.92}
53
  ],
54
  "conflicts": [                     // where engines disagreed + who won and why
55
    {"claim":"Hermes status","winner":"live-infra","losers":["agent-memory"],"reason":"memory stale 40d"}
56
  ],
57
  "engines": [ {"engine":"rag","status":"up","ms":312}, {"engine":"cbm","status":"down"} ],
58
  "took_ms": 480
59
}
60
```
61
 
62
### `GET /health`
63
Per-engine liveness + freshness (drives organism anatomy):
64
```jsonc
65
{"engines":[{"engine":"rag","status":"up","last_ingest":"2026-06-24T00:04:00Z","staleness_h":0.4}], "ok":true}
66
```
67
 
68
---
69
 
70
## 3. Ranking & reconciliation (Zion's ruling, recency-gated)
71
 
72
Authority when sources conflict:
73
```
74
live infra state  >  recent files  >  agent memory  >  older notes
75
```
76
Implement as a **per-hit score**:
77
```
78
score = w_authority(source) * recency_decay(last_seen) * confidence * relevance
79
```
80
- `w_authority`: live-infra 1.0, recent-file 0.8, agent-memory 0.6, older-note 0.4
81
- `recency_decay`: `exp(-age_days / HALF_LIFE)`, HALF_LIFE default 30d. **This is the Darwin valve** -
82
  it pushes stale agent-memory *below* fresher notes automatically (the exact fix for the Hermes lie).
83
- A "recent file" is `mtime < 30d`; older → demoted to the notes tier.
84
- When two hits assert the same fact and disagree, the higher `score` wins; the loss is recorded in
85
  `conflicts[]` with a human reason. Never silently drop the loser.
86
 
87
---
88
 
89
## 4. Synthesis routing (cost-aware; 3060 currently DOWN)
90
 
91
| question complexity | synthesizer | note |
92
|---|---|---|
93
| cheap / factual | qwen3:8b on 3060 (`http://REDACTED-IP:11434/api/chat`, `think:false`) | **fallback while 3060 down → deterministic ranked-merge (no LLM)** |
94
| hard / multi-source | Opus (Claude API) | gated behind a flag; no paid calls until enabled |
95
 
96
v1 ships with **deterministic ranked-merge always working** (top hits → templated answer + receipts),
97
LLM synthesis pluggable behind `synthesize` + an available-backend check. Never hard-depend on a
98
backend that may be asleep.
99
 
100
---
101
 
102
## 5. MCP exposure - the unified verb
103
 
104
Expose one tool, `darwin_ask(query, mode?, scope?)`, over **stdio + SSE `:9891`**, so Zion's agents
105
(Codex CT241, GLM/OpenCode CT247, Cockpit, this Claude) replace four memory tools with one. Register
106
in each agent's MCP config as `darwin`. The four underlying engines stay available for power use, but
107
`darwin_ask` is the front door.
108
 
109
---
110
 
111
## 6. Non-negotiables
112
- No claim in `answer` without a matching `receipts[]` entry. Provenance or it doesn't ship.
113
- Every engine failure degrades gracefully and is surfaced, never hidden.
114
- State/cache on **local ext4** (`/var/lib/darwin` or `/var/tmp/darwin`), NEVER on the CIFS share
115
  (CIFS → `database is locked`).
116
- Read-only against the engines. The Cortex never writes to RAG/cbm/Serena/memgraph.
117
- Self-test: `tests/test_reconcile.py` must prove a 40-day-stale agent-memory "Hermes active" loses
118
  to live infra "Hermes stopped". That test is the regression guard for the bug that started Darwin.