a654b0126f
Three documentation changes and one deletion. CLAUDE.md and AGENTS.md gain the Nexus/Praxis/Hexis sections that were written last session and never committed: what each service owns, where Maven's client for it lives, and the rules that are not negotiable. The p50 latency figure was wrong in two files. CLAUDE.md said the cascade costs 2.7s and that the LLM router is 90x slower than the classifier. Both come from the bakeoff table, where the number is contention on a shared llama-server, not the model. ROUTING-EVAL-31-07-2026.md line 61 says so and measures the router at p50 825ms / p95 1.2s / max 3.0s. Corrected in CLAUDE.md, and the bakeoff table now carries a header pointing at the routing eval for absolute latency. Latency work was about to be planned off a number that was never real. HANDOFF.md is deleted. It described work sitting on fix/integrated waiting for a fast-forward onto overnight/eco-versioned-traces. Neither is true: master contains that tip plus 22 commits, and both branch pointers are stale. The three live defects it recorded move to PLAN-DETERMINISM-02-08-2026.md, which is now the only planning document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaeSbLMEVG5ey8tejU3y2
162 lines
6.6 KiB
Markdown
162 lines
6.6 KiB
Markdown
# Maven — Agent Context
|
|
|
|
## Vikunja
|
|
This repo maps to **Maven** (project ID: 2) in Vikunja.
|
|
Feature work, bugs, deployment tasks all go here.
|
|
MCP endpoint: `http://localhost:9100/mcp` (or `http://192.168.1.104:9100/mcp` from workpc)
|
|
|
|
## The sibling services (Nexus, Praxis, Hexis)
|
|
|
|
Maven is the conversational front end of a four-service ecosystem. The other three
|
|
live in sibling repos next to this one.
|
|
|
|
| Service | Repo | Port | Answers |
|
|
|---|---|---|---|
|
|
| Nexus | `../nexus` | 9740 | who or what is this name |
|
|
| Praxis | `../praxis` | 8989 | what needs attention |
|
|
| Hexis | `../hexis` | 9741 | what can be run, and running it |
|
|
|
|
Division of labour: Nexus identifies, Praxis observes, Hexis acts, Maven understands
|
|
and coordinates. Maven is not the source of truth for any of the three. The full
|
|
contract is `MAVEN_ECOSYSTEM_ARCHITECTURE.md`, and the constraints that bite during
|
|
implementation are summarised in `CLAUDE.md`.
|
|
|
|
Where things are in this repo:
|
|
|
|
- `cmd/mavend/ecosystem.go` holds `nexusClient` and `praxisClient`. The Hexis client
|
|
is vendored from `github.com/kami/hexis/pkg/client`.
|
|
- `cmd/mavend/ecosystem_acts.go` routes an act through capability discovery.
|
|
- `cmd/mavend/factenrichment.go` resolves each stored fact's `Subject` against Nexus
|
|
on a background poll loop, with backoff and no give-up.
|
|
- `internal/store/entityfacts.go` holds the entity-tagged fact rows.
|
|
- Config blocks are `nexus`, `praxis` and `hexis` in `deploy/mavend.json`. Each is
|
|
optional. Absent means that integration is dark, not broken.
|
|
|
|
Bring the whole ecosystem up locally:
|
|
|
|
```sh
|
|
docker compose -f deploy/ecosystem/docker-compose.yml up -d
|
|
```
|
|
|
|
That builds all three from the sibling working trees, so commit or stash there first.
|
|
Each publishes on loopback at the port above. Maven reaches them by service name on
|
|
the shared compose network.
|
|
|
|
Testing without them running: `cmd/mavend/fakeecosystem_test.go` provides stubs, and
|
|
`cmd/mavend/ecosystem_degraded_test.go` covers each service being unreachable.
|
|
|
|
## Rendering / previewing the web UI locally
|
|
|
|
To see mavweb pages with real data without touching the production stack:
|
|
|
|
```sh
|
|
R=/tmp/mvn-preview; mkdir -p $R
|
|
go build -o $R/mavend ./cmd/mavend/ && go build -o $R/mavweb ./cmd/mavweb/
|
|
cat > $R/mavend.json <<EOF
|
|
{ "db_path": "$R/maven.db", "socket_path": "$R/mavend.sock",
|
|
"state_dir": "$R", "tick_interval": "10s" }
|
|
EOF
|
|
$R/mavend -config $R/mavend.json &
|
|
$R/mavweb -addr 127.0.0.1:9299 -core $R/mavend.sock &
|
|
```
|
|
|
|
- No models/voice/phraser config needed — the phraser stub covers it; mavend
|
|
runs fine bare. mavweb serves `/`, `/dash`, `/history`, `/trace`,
|
|
`/notifications`, `/tools`.
|
|
- **Socket path must be short** — unix sockets cap at ~108 chars; a deep tmp
|
|
dir fails with `bind: invalid argument`.
|
|
- Seed data through `ipc.Client` (internal package — the seeder must live
|
|
inside the module, e.g. a throwaway `cmd/seedtmp/main.go`, deleted after):
|
|
`WriteFact`, `RecordNudge`+`ResolveNudge`, `CreateReminder`, `ProposeTool`.
|
|
- `/trace` is empty until the first tick fires (wait one `tick_interval`).
|
|
- Screenshots: `chromium --headless --disable-gpu --screenshot=out.png
|
|
--window-size=1280,900 --hide-scrollbars --virtual-time-budget=2000
|
|
http://127.0.0.1:9299/dash` (use `--window-size=430,900` for the phone/PWA
|
|
view). **Always pass `--virtual-time-budget`** — without it the screenshot
|
|
can snap mid-layout and silently drop elements (the PWA lang toggle
|
|
"disappeared" this way).
|
|
|
|
## Embedder model for intent routing
|
|
|
|
The router uses a multilingual sentence embedder to classify intents and recall
|
|
notes. Without it, the floor `HashEmbedder` is used — deterministic but weak
|
|
(Russian recall rarely clears the confidence gate, many commands fall to
|
|
"clarify").
|
|
|
|
**Download the embedder** (ONNX, ~120 MB):
|
|
|
|
```sh
|
|
make download-embedder
|
|
```
|
|
|
|
This fetches `multilingual-e5-small` (384-dim, 12-layer, Russian and English)
|
|
to `models/embedder/multilingual-e5-small/`. It is an asymmetric retrieval
|
|
model: the code puts `query: ` in front of a question and `passage: ` in front
|
|
of a stored note, which is how e5 was trained. The quantized file is the one
|
|
that is downloaded, deployed and measured.
|
|
|
|
**Also need ONNX Runtime** (`libonnxruntime.so`):
|
|
|
|
```sh
|
|
curl -sL "https://github.com/microsoft/onnxruntime/releases/download/v1.15.1/onnxruntime-linux-x64-1.15.1.tgz" | tar xz
|
|
sudo cp onnxruntime-linux-x64-1.15.1/lib/libonnxruntime.so* /usr/local/lib/
|
|
```
|
|
|
|
**Configure in `deploy/mavend.json`**:
|
|
|
|
```json
|
|
"voice": {
|
|
"embedder": {
|
|
"model_path": "models/embedder/multilingual-e5-small/model_quantized.onnx",
|
|
"tokenizer_path": "models/embedder/multilingual-e5-small/tokenizer.json",
|
|
"lib_path": "/usr/local/lib/libonnxruntime.so"
|
|
}
|
|
}
|
|
```
|
|
|
|
Without the embedder block, the daemon uses `HashEmbedder` (works, but weak on
|
|
Russian recall — you may see many "clarify" responses).
|
|
|
|
## Qwen3 resident model for router + phraser
|
|
|
|
The target daemon uses the locally trained Qwen3-1.7B checkpoint for both
|
|
routing and phrasing. Training is Qwen3 Base → RU CPT → joint persona/router
|
|
SFT → merged GGUF, and is still in flight (#122) — until it lands, the deployed
|
|
resident model is stock **Qwen3.5-0.8B** (`Q4_K_M`), see `deploy/mavend.json`.
|
|
Without a configured model, `StubPhraser` plus the classifier remain the
|
|
deterministic floor.
|
|
|
|
During training, use the runbook in
|
|
`docs/plans/2026-07-18-qwen3-resident-training-eval.md`. After the decision gate
|
|
and SFT pass, copy the merged GGUF into the mounted model directory and set:
|
|
|
|
```json
|
|
"phraser": {
|
|
"model_path": "/opt/maven/models/llm/Qwen3-Maven-1.7B-Q8_0.gguf",
|
|
"bin_path": "llama-server",
|
|
"n_gpu_layers": 99,
|
|
"n_ctx": 2048
|
|
}
|
|
```
|
|
|
|
**Configure in `deploy/mavend.json`** — the `phraser` block points at this
|
|
model and the daemon spawns `llama-server` as a subprocess. The router and
|
|
replier use the same llama-server via the shared `internal/llm` client.
|
|
|
|
Telegram tokens are read from `deploy/telegram.env` (gitignored), expanded
|
|
via `${VAR}` in the JSON config.
|
|
|
|
**Routing is Qwen-first** with classifier fallback. The LLM router runs
|
|
after stage-0 (exact-match grammar) and before the classifier cascade. On any
|
|
error or parse failure, the classifier handles the utterance — the turn never
|
|
breaks on the model.
|
|
|
|
## Web UI conventions
|
|
|
|
- All server-rendered pages share `cmd/mavweb/static/ui.css` (served at
|
|
`/ui.css`) and the `nav` template partial (`navHTML` in `cmd/mavweb/main.go`,
|
|
invoked as `{{template "nav" "<active-page>"}}`). New pages must link both —
|
|
no per-page inline `<style>` beyond true one-offs.
|
|
- Wrap every table in `<div class=scroll>` so wide data pans on a phone
|
|
instead of breaking the layout.
|