docs: tier the tree by lifetime, so staleness shows in the path (V-446)

Seventeen markdown files at the repo root, twelve of them dated one-shot
reports sitting next to CLAUDE.md. That is why stale docs read as
current: nothing in the path said which was which.

Root now keeps CLAUDE.md and AGENTS.md. Living docs move under docs/
and carry a Last verified line. Dated measurements move to docs/evals/
ISO-prefixed, and are never edited after the day, so a newer number is
a new file. The senior review moves to docs/archive/.

Every reference was rewritten across markdown, Go comments, the Makefile
and the recall fixture. The touched Go packages still build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 03:28:49 +04:00
parent 7079a240f7
commit 93987f2dfc
38 changed files with 99 additions and 85 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ live in sibling repos next to this one.
Division of labour: Nexus identifies, Praxis observes, Hexis acts, Maven understands 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 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 contract is `docs/ecosystem.md`, and the constraints that bite during
implementation are summarised in `CLAUDE.md`. implementation are summarised in `CLAUDE.md`.
Where things are in this repo: Where things are in this repo:
+6 -6
View File
@@ -10,7 +10,7 @@ compose passes `/dev/dri` + the render gid) — the resident model stays ≤1.7B
**Resident model:** currently **Qwen3-1.7B** (`UD-Q4_K_XL`), stock — not yet the CPT'd one. **Resident model:** currently **Qwen3-1.7B** (`UD-Q4_K_XL`), stock — not yet the CPT'd one.
It replaced Qwen3.5-0.8B on 2026-07-31 because it measured better on both fixtures we have: It replaced Qwen3.5-0.8B on 2026-07-31 because it measured better on both fixtures we have:
67.5% vs 59.7% intent-only on the 77-case RU routing fixture, and 20/27 vs 11-17/27 on the 67.5% vs 59.7% intent-only on the 77-case RU routing fixture, and 20/27 vs 11-17/27 on the
talk fixture. See `MODEL-BAKEOFF-31-07-2026.md`. It is a Thinking variant, so `n_ctx` is 4096 talk fixture. See `docs/evals/2026-07-31-model-bakeoff.md`. It is a Thinking variant, so `n_ctx` is 4096
— reasoning tokens need the room, and 4096 is what the scores above were measured at. — reasoning tokens need the room, and 4096 is what the scores above were measured at.
The **target** is still the locally CPT'd **Qwen3-1.7B** (Vikunja #122, training in flight). The **target** is still the locally CPT'd **Qwen3-1.7B** (Vikunja #122, training in flight).
@@ -25,7 +25,7 @@ Spanish. Their strong published IFEval/BFCL numbers are English-only. Model file
`models/llm/`, so the LFM2.5 gguf sitting there is not loaded by anything. Swapping the resident `models/llm/`, so the LFM2.5 gguf sitting there is not loaded by anything. Swapping the resident
model is a one-line change to `phraser.model_path` in `deploy/mavend.json`. model is a one-line change to `phraser.model_path` in `deploy/mavend.json`.
See `REARCH.md` for the target architecture, `DESIGN.md` for the folded design spec, and See `docs/rearchitecture.md` for the target architecture, `docs/design.md` for the folded design spec, and
`AGENTS.md` for local-preview + model-download recipes. `AGENTS.md` for local-preview + model-download recipes.
## Build & test ## Build & test
@@ -72,7 +72,7 @@ protocol; the config in `deploy/mavend.json` (with `${VAR}` env expansion from g
Maven is one of four services. It owns conversation and personal memory. It does not Maven is one of four services. It owns conversation and personal memory. It does not
own identity, operational state, or execution. Full contract in own identity, operational state, or execution. Full contract in
`MAVEN_ECOSYSTEM_ARCHITECTURE.md`. `docs/ecosystem.md`.
```text ```text
Nexus identifies. Praxis observes. Hexis acts. Maven understands and coordinates. Nexus identifies. Praxis observes. Hexis acts. Maven understands and coordinates.
@@ -113,7 +113,7 @@ Every cross-service call carries a correlation id minted once per action
on in deploy** — this section used to say it was wired `nil`, which stopped being true on on in deploy** — this section used to say it was wired `nil`, which stopped being true on
2026-07-31. 2026-07-31.
- **LLM router (the intended design, REARCH.md):** the resident Qwen3-1.7B (`llmrouter.go`) - **LLM router (the intended design, docs/rearchitecture.md):** the resident Qwen3-1.7B (`llmrouter.go`)
emits GBNF-constrained structured JSON, and the SAME model phrases replies. Embedder is emits GBNF-constrained structured JSON, and the SAME model phrases replies. Embedder is
demoted from a routing gate to a RAG hint. Wired at `voice.go:214` via demoted from a routing gate to a RAG hint. Wired at `voice.go:214` via
`pickLLMRouter(cfg.Voice.UseLLMRouter(), llmClient)`; the flag is `voice.llm_router` `pickLLMRouter(cfg.Voice.UseLLMRouter(), llmClient)`; the flag is `voice.llm_router`
@@ -127,11 +127,11 @@ on in deploy** — this section used to say it was wired `nil`, which stopped be
Cascade order: `stage0.go` exact-match fast-path → LLM router (when non-nil) → classifier Cascade order: `stage0.go` exact-match fast-path → LLM router (when non-nil) → classifier
fallback. Any LLM error falls through to the classifier so a turn never breaks on the model. fallback. Any LLM error falls through to the classifier so a turn never breaks on the model.
Measured on the 77-case RU fixture (`MODEL-BAKEOFF-31-07-2026.md`): the classifier scores Measured on the 77-case RU fixture (`docs/evals/2026-07-31-model-bakeoff.md`): the classifier scores
36.8% full accuracy at p50 31ms; Qwen3-1.7B scores 67.5% intent-only / 72.7% through the 36.8% full accuracy at p50 31ms; Qwen3-1.7B scores 67.5% intent-only / 72.7% through the
cascade at p50 ≈825ms. Accuracy roughly doubled, latency is ~27× worse, and that trade was cascade at p50 ≈825ms. Accuracy roughly doubled, latency is ~27× worse, and that trade was
accepted deliberately. **The ≈2.7s figure that stood here until 2026-08-02 was contention, accepted deliberately. **The ≈2.7s figure that stood here until 2026-08-02 was contention,
not the model.** See `ROUTING-EVAL-31-07-2026.md` line 61, which measures the LLM router at not the model.** See `docs/evals/2026-07-31-routing.md` line 61, which measures the LLM router at
p50 825ms / p95 1.2s / max 3.0s and the full cascade at p50 0.80-1.04s. Do not plan latency p50 825ms / p95 1.2s / max 3.0s and the full cascade at p50 0.80-1.04s. Do not plan latency
work off the bakeoff table. `Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM work off the bakeoff table. `Confidence: 1.0` used to be hardcoded in `llmrouter.go`, so the LLM
path could never ask for clarification (6/6 refusal cases missed on the fixture) — Vikunja path could never ask for clarification (6/6 refusal cases missed on the fixture) — Vikunja
+2 -2
View File
@@ -78,7 +78,7 @@ deps-go:
done done
$(GO) version $(GO) version
# fmt-check fails if any file needs gofmt. DESIGN.md has always said `make # fmt-check fails if any file needs gofmt. docs/design.md has always said `make
# test` gates on gofmt and vet; it did not, so nine files quietly drifted. # test` gates on gofmt and vet; it did not, so nine files quietly drifted.
# Run `gofmt -w` on whatever this prints. # Run `gofmt -w` on whatever this prints.
fmt-check: fmt-check:
@@ -191,7 +191,7 @@ deps-piper:
# multilingual-e5-small: an asymmetric retrieval model. It is trained to match # multilingual-e5-small: an asymmetric retrieval model. It is trained to match
# a short question against a longer passage, which is what note recall is. # a short question against a longer passage, which is what note recall is.
# The quantized file is the one we download, deploy and measure — see # The quantized file is the one we download, deploy and measure — see
# RECALL-EVAL-31-07-2026.md. # docs/evals/2026-07-31-recall.md.
EMBEDDER_DIR := $(shell pwd)/models/embedder/multilingual-e5-small EMBEDDER_DIR := $(shell pwd)/models/embedder/multilingual-e5-small
EMBEDDER_MODEL_URL := https://huggingface.co/Xenova/multilingual-e5-small/resolve/main/onnx/model_quantized.onnx EMBEDDER_MODEL_URL := https://huggingface.co/Xenova/multilingual-e5-small/resolve/main/onnx/model_quantized.onnx
EMBEDDER_TOKENIZER_URL := https://huggingface.co/Xenova/multilingual-e5-small/resolve/main/tokenizer.json EMBEDDER_TOKENIZER_URL := https://huggingface.co/Xenova/multilingual-e5-small/resolve/main/tokenizer.json
+3 -3
View File
@@ -74,7 +74,7 @@ full-weight checkpoint at step 1000/8077; evaluation and Qwen3 SFT tooling are
tracked in `docs/plans/2026-07-18-qwen3-resident-training-eval.md`. tracked in `docs/plans/2026-07-18-qwen3-resident-training-eval.md`.
Consolidated status. The reactive↔proactive core is closed and testable through Consolidated status. The reactive↔proactive core is closed and testable through
the web PWA. The former SPEC's open items 17 (now `DESIGN.md` § execution ledger) are landed (protocol doc, away-channel the web PWA. The former SPEC's open items 17 (now `docs/design.md` § execution ledger) are landed (protocol doc, away-channel
fallthrough, CalDAV poller, quiet-hours schedule, tools enable/disable, note RAG, fallthrough, CalDAV poller, quiet-hours schedule, tools enable/disable, note RAG,
passkey step-up); item 8 (multi-user) is deliberately deferred — see the tail. passkey step-up); item 8 (multi-user) is deliberately deferred — see the tail.
The two big infra gaps from the jul5 revision are closed on `overnight-jul5`: The two big infra gaps from the jul5 revision are closed on `overnight-jul5`:
@@ -145,7 +145,7 @@ tests, `-race` in `make test`.
time window (`voice.quiet_hours`, HH:MM, midnight-crossing handled) now sets it time window (`voice.quiet_hours`, HH:MM, midnight-crossing handled) now sets it
on each tick — in addition to the "тихий режим" voice toggle. Both activate quiet. on each tick — in addition to the "тихий режим" voice toggle. Both activate quiet.
- **Client protocol (SPEC item 1):** the voice wire format (length-prefixed JSON - **Client protocol (SPEC item 1):** the voice wire format (length-prefixed JSON
frames) is published in `PROTOCOL.md`, generated from `internal/voice/wire.go` frames) is published in `docs/protocol.md`, generated from `internal/voice/wire.go`
so third-party clients don't need the Go source. so third-party clients don't need the Go source.
- **Passkey step-up (SPEC item 7):** `internal/webauthn` does real WebAuthn — - **Passkey step-up (SPEC item 7):** `internal/webauthn` does real WebAuthn —
ES256/P-256 register + assert, ecdsa signature verification, rpIdHash + UP/UV ES256/P-256 register + assert, ecdsa signature verification, rpIdHash + UP/UV
@@ -463,6 +463,6 @@ Not neglect — the one item where doing nothing now beats doing something:
- **The hard part is speaker attribution, and it needs the second voice.** A - **The hard part is speaker attribution, and it needs the second voice.** A
voice-print discriminator (kami vs gf vs unknown) can't be trained or tuned voice-print discriminator (kami vs gf vs unknown) can't be trained or tuned
with one voice in the house. Plumbing before the model is pipe with no water. with one voice in the house. Plumbing before the model is pipe with no water.
- **It's fenced deliberately** (`DO NOT TOUCH THIS PHASE` in `DESIGN.md` § Users) so an - **It's fenced deliberately** (`DO NOT TOUCH THIS PHASE` in `docs/design.md` § Users) so an
autonomous agent doesn't add `user_id` columns while touching the store and autonomous agent doesn't add `user_id` columns while touching the store and
commit us to a schema before the constraints that shape it exist. commit us to a schema before the constraints that shape it exist.
+1 -1
View File
@@ -1,6 +1,6 @@
// Package main is mavenclient — maven's reference client. // Package main is mavenclient — maven's reference client.
// //
// Per DESIGN.md § Voice pipeline (STT / TTS): capture lives on the client; // Per docs/design.md § Voice pipeline (STT / TTS): capture lives on the client;
// the server transcribes + synthesises on demand. The PC client runs the // the server transcribes + synthesises on demand. The PC client runs the
// wake-word / VAD gate (cmd/mavwaked) and ships ONE clean audio blob per // wake-word / VAD gate (cmd/mavwaked) and ships ONE clean audio blob per
// utterance on activation. The server never owns a mic. // utterance on activation. The server never owns a mic.
+1 -1
View File
@@ -512,7 +512,7 @@ func main() {
// /tools — the authed enable surface. maven proposes acts she can't run; // /tools — the authed enable surface. maven proposes acts she can't run;
// this page is where a human reviews and enables them (proposed→enabled). // this page is where a human reviews and enables them (proposed→enabled).
// Enabling is the boundary-moving act (DESIGN.md § Tool registration — // Enabling is the boundary-moving act (docs/design.md § Tool registration —
// drafting is suggest, enabling is act), so it lives ONLY here, // drafting is suggest, enabling is act), so it lives ONLY here,
// behind wg+nginx+auth — never the voice/chat path. // behind wg+nginx+auth — never the voice/chat path.
mux.HandleFunc("/tools", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/tools", func(w http.ResponseWriter, r *http.Request) {
@@ -30,7 +30,7 @@ critical workflows: voice turn (mic→STT→route→tool/reply→TTS); proactive
(/dash /chat /tools /ecosystem) (/dash /chat /tools /ecosystem)
current state: all 34 test packages pass; vet clean; `make test` still exits 1 current state: all 34 test packages pass; vet clean; `make test` still exits 1
(finding 2) (finding 2)
known failures: weak RU query routing — REARCH.md names the cause; the named fix known failures: weak RU query routing — docs/rearchitecture.md names the cause; the named fix
is wired `nil` is wired `nil`
maintenance burden: 4,518 lines of root markdown vs 33,319 lines of Go; 15 top-level maintenance burden: 4,518 lines of root markdown vs 33,319 lines of Go; 15 top-level
.md files, 3 of them dated session logs; several contradict .md files, 3 of them dated session logs; several contradict
@@ -47,11 +47,11 @@ what's obsolete: llmrouter.go (built, tested, never wired); classifier seed-p
``` ```
**Classification: healthy + misaligned.** Not fragile, not overbuilt, not abandoned. The **Classification: healthy + misaligned.** Not fragile, not overbuilt, not abandoned. The
architecture in `REARCH.md` is sound and mostly *built* — it just is not *connected*. architecture in `docs/rearchitecture.md` is sound and mostly *built* — it just is not *connected*.
## what it should become ## what it should become
The thing `REARCH.md` already describes, with the switch flipped and the drift removed: The thing `docs/rearchitecture.md` already describes, with the switch flipped and the drift removed:
one resident small model doing both routing and phrasing, classifier demoted from the live one resident small model doing both routing and phrasing, classifier demoted from the live
path to the failure floor, embedder demoted to RAG hint. No new architecture is needed. path to the failure floor, embedder demoted to RAG hint. No new architecture is needed.
**The gap is a config/wiring decision plus doc convergence, not a redesign.** **The gap is a config/wiring decision plus doc convergence, not a redesign.**
@@ -65,19 +65,19 @@ path to the failure floor, embedder demoted to RAG hint. No new architecture is
`architecture` / `repair` `architecture` / `repair`
**problem:** The most load-bearing design decision in the project is stated four different, **problem:** The most load-bearing design decision in the project is stated four different,
incompatible ways, and the code path `REARCH.md` calls "the linchpin" is disabled. incompatible ways, and the code path `docs/rearchitecture.md` calls "the linchpin" is disabled.
**evidence** (all confirmed): **evidence** (all confirmed):
- `cmd/mavend/voice.go:211``rtr := buildRouter(emb, matcher, threshold, nil) // LLM router disabled`, - `cmd/mavend/voice.go:211``rtr := buildRouter(emb, matcher, threshold, nil) // LLM router disabled`,
with comment *"the classifier handles routing reliably."* with comment *"the classifier handles routing reliably."*
- `REARCH.md:11` says the same classifier is *"the structural cause of 'she messes up - `docs/rearchitecture.md:11` says the same classifier is *"the structural cause of 'she messes up
queries.'"* **The code comment and the design doc make opposite claims about the same queries.'"* **The code comment and the design doc make opposite claims about the same
component.** component.**
- `internal/router/llmrouter.go` (139 lines) + `llmrouter_test.go` — fully built and - `internal/router/llmrouter.go` (139 lines) + `llmrouter_test.go` — fully built and
tested, zero non-test callers. tested, zero non-test callers.
- Model identity, four ways: docs say **Qwen3-1.7B** (`CLAUDE.md:6`, `REARCH.md:15`, - Model identity, four ways: docs say **Qwen3-1.7B** (`CLAUDE.md:6`, `docs/rearchitecture.md:15`,
`SPEC.md:46`, `AGENTS.md:79`, `MAVEN_ECOSYSTEM_ARCHITECTURE.md:72`); `SPEC.md:46`, `AGENTS.md:79`, `docs/ecosystem.md:72`);
`deploy/mavend.json:9` says **Qwen3.5-2B-UD-Q4_K_XL**; `models/llm/` on disk holds `deploy/mavend.json:9` says **Qwen3.5-2B-UD-Q4_K_XL**; `models/llm/` on disk holds
**LFM2.5-1.2B-Thinking**; code comments in 5 files still say **LFM**. **LFM2.5-1.2B-Thinking**; code comments in 5 files still say **LFM**.
- `deploy/mavend.json:11` sets `"n_gpu_layers": 99` while `CLAUDE.md:4` states the target - `deploy/mavend.json:11` sets `"n_gpu_layers": 99` while `CLAUDE.md:4` states the target
@@ -100,7 +100,7 @@ match. Delete nothing from `internal/router` yet — the classifier is the fallb
reconciliation, not a refactor. **Do not rewrite the router.** reconciliation, not a refactor. **Do not rewrite the router.**
**alternatives:** Delete `llmrouter.go` and commit to the classifier — only defensible if **alternatives:** Delete `llmrouter.go` and commit to the classifier — only defensible if
the eval harness shows the classifier is actually adequate, which contradicts `REARCH.md`. the eval harness shows the classifier is actually adequate, which contradicts `docs/rearchitecture.md`.
**risk:** Low-moderate. LLM route failures already fall through to the classifier **risk:** Low-moderate. LLM route failures already fall through to the classifier
(`router.go:88-96`), so a bad model cannot break a turn. The real risk is CPU latency. (`router.go:88-96`), so a bad model cannot break a turn. The real risk is CPU latency.
@@ -228,21 +228,21 @@ after finding 1, not before** — and skip it if it stays purely cosmetic.
**problem:** 15 root markdown files, 4,518 lines, several stale or superseded, at least **problem:** 15 root markdown files, 4,518 lines, several stale or superseded, at least
three pairs contradicting each other. three pairs contradicting each other.
**evidence:** `ROADMAP.md` (759) + `MAVEN_ECOSYSTEM_ARCHITECTURE.md` (884) + **evidence:** `ROADMAP.md` (759) + `docs/ecosystem.md` (884) +
`PROGRESS.md` (456) + `maven.md` (413) + `20-07-2026-BACKLOG.md` (396) + `PROGRESS.md` (456) + `maven.md` (413) + `20-07-2026-BACKLOG.md` (396) +
`SESSION-05-07-2026.md` + `SESSION-06-07-2026.md` (477 combined) + `PLANS.md` (25) + `SESSION-05-07-2026.md` + `SESSION-06-07-2026.md` (477 combined) + `PLANS.md` (25) +
`START.md` + `SPEC.md` + `PROTOCOL.md`. `PROGRESS.md:61` annotates its own staleness: `docs/operations.md` + `SPEC.md` + `docs/protocol.md`. `PROGRESS.md:61` annotates its own staleness:
*"Older LFM references below describe the currently deployed..."*. `REARCH.md` announces it *"Older LFM references below describe the currently deployed..."*. `docs/rearchitecture.md` announces it
"supersedes" a model still described as current elsewhere. "supersedes" a model still described as current elsewhere.
**impact:** The doc set is the reason finding 1 exists. When five documents describe the **impact:** The doc set is the reason finding 1 exists. When five documents describe the
architecture, the code becomes the only trustworthy one — which defeats the purpose of architecture, the code becomes the only trustworthy one — which defeats the purpose of
having them. having them.
**recommended action:** Keep `CLAUDE.md` (agent contract), `REARCH.md` (target **recommended action:** Keep `CLAUDE.md` (agent contract), `docs/rearchitecture.md` (target
architecture), `AGENTS.md` (recipes), `PROTOCOL.md` (wire format), architecture), `AGENTS.md` (recipes), `docs/protocol.md` (wire format),
`20-07-2026-BACKLOG.md` (live queue). Delete the two `SESSION-*.md` and `PLANS.md` — git `20-07-2026-BACKLOG.md` (live queue). Delete the two `SESSION-*.md` and `PLANS.md` — git
history holds them. Fold `SPEC.md` + `maven.md` + `ROADMAP.md` into one `DESIGN.md` and history holds them. Fold `SPEC.md` + `maven.md` + `ROADMAP.md` into one `docs/design.md` and
mark superseded sections instead of leaving them to read as current. Target ~1,500 lines. mark superseded sections instead of leaving them to read as current. Target ~1,500 lines.
--- ---
@@ -320,7 +320,7 @@ expected maintenance gain: none over the incremental path
suggesting Vulkan offload is intended and working — but `CLAUDE.md` says CPU-only. Likely suggesting Vulkan offload is intended and working — but `CLAUDE.md` says CPU-only. Likely
the doc is stale, not the config; unverified. the doc is stale, not the config; unverified.
- **Whether the classifier is genuinely adequate.** `voice.go:211` asserts it is; - **Whether the classifier is genuinely adequate.** `voice.go:211` asserts it is;
`REARCH.md` asserts it is not. Both are claims, neither is measured. The uncommitted eval `docs/rearchitecture.md` asserts it is not. Both are claims, neither is measured. The uncommitted eval
harness is the instrument to settle it — resolve before flipping the router, not after. harness is the instrument to settle it — resolve before flipping the router, not after.
- **Whether wg+nginx+auth actually fronts 9201 in production.** Not in this repo. If it - **Whether wg+nginx+auth actually fronts 9201 in production.** Not in this repo. If it
does, finding 3 drops from "unauthenticated RCE" to "the control is not reproducible from does, finding 3 drops from "unauthenticated RCE" to "the control is not reproducible from
@@ -350,7 +350,7 @@ Key claims independently re-verified against the working tree; the verdict stand
over WireGuard on homesrv, this is hygiene, not an emergency — but the loopback bind over WireGuard on homesrv, this is hygiene, not an emergency — but the loopback bind
and startup warning are cheap insurance either way, so do them regardless. and startup warning are cheap insurance either way, so do them regardless.
- Finding 1's "flip the router" step should be gated harder on measurement. - Finding 1's "flip the router" step should be gated harder on measurement.
`REARCH.md`'s claim that the classifier causes weak RU queries is itself unmeasured — `docs/rearchitecture.md`'s claim that the classifier causes weak RU queries is itself unmeasured —
the review admits this under uncertainties, but the "repair now" ordering buries it. the review admits this under uncertainties, but the "repair now" ordering buries it.
Run `eval_scenarios_test.go` against both paths **before** deciding to flip, not Run `eval_scenarios_test.go` against both paths **before** deciding to flip, not
after. A 2B model on CPU may add enough latency that the classifier wins in practice after. A 2B model on CPU may add enough latency that the classifier wins in practice
+8 -6
View File
@@ -1,10 +1,12 @@
# Maven — Design # Maven — Design
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
> Folded 2026-07-30 from `SPEC.md` (north star, 2026-07-03), `maven.md` > Folded 2026-07-30 from `SPEC.md` (north star, 2026-07-03), `maven.md`
> (consolidated decisions, 2026-06-30) and `ROADMAP.md` (execution plan, > (consolidated decisions, 2026-06-30) and `ROADMAP.md` (execution plan,
> 2026-07-06). Those three files are gone; git history holds them. > 2026-07-06). Those three files are gone; git history holds them.
> This is the single design document: principles, target state, and the > This is the single design document: principles, target state, and the
> execution ledger. `REARCH.md` remains authoritative wherever it disagrees > execution ledger. `docs/rearchitecture.md` remains authoritative wherever it disagrees
> with anything here. Everything the three sources asserted that is no longer > with anything here. Everything the three sources asserted that is no longer
> the intended design is preserved under **§ Superseded** — do not read that > the intended design is preserved under **§ Superseded** — do not read that
> section as current. > section as current.
@@ -160,7 +162,7 @@ presence_state ( last_bucket, last_score, updated_ts )
``` ```
Facts additionally carry `Subject`/`EntityID`/`ResolutionState` for Facts additionally carry `Subject`/`EntityID`/`ResolutionState` for
entity-aware resolution against Nexus (see `MAVEN_ECOSYSTEM_ARCHITECTURE.md`). entity-aware resolution against Nexus (see `docs/ecosystem.md`).
### Trigger model ### Trigger model
@@ -196,7 +198,7 @@ INTO the gate as an env predicate, not the LLM's job.
## Reactive path — routing ## Reactive path — routing
**Target design: LLM-as-router** (see `REARCH.md` and `CLAUDE.md`). One **Target design: LLM-as-router** (see `docs/rearchitecture.md` and `CLAUDE.md`). One
resident model emits GBNF-constrained structured JSON, and the same model resident model emits GBNF-constrained structured JSON, and the same model
phrases replies; the embedder is a RAG hint, not a routing gate. The phrases replies; the embedder is a RAG hint, not a routing gate. The
committed default today is the classifier/embedder cascade, which is an committed default today is the classifier/embedder cascade, which is an
@@ -655,7 +657,7 @@ daemon.
The voice wire protocol (length-prefixed JSON frames over TCP) is designed for The voice wire protocol (length-prefixed JSON frames over TCP) is designed for
**multiple client implementations**. The reference PWA at `cmd/mavweb` is one **multiple client implementations**. The reference PWA at `cmd/mavweb` is one
client; any app (phone, desktop CLI, smartwatch) can implement the same frame client; any app (phone, desktop CLI, smartwatch) can implement the same frame
protocol. The published spec is `PROTOCOL.md` — **generated from protocol. The published spec is `docs/protocol.md` — **generated from
`internal/voice/wire.go`**, not composed freehand, so it can't drift from `internal/voice/wire.go`**, not composed freehand, so it can't drift from
code. It covers transport (4-byte big-endian length prefix), methods code. It covers transport (4-byte big-endian length prefix), methods
(`PushToTalk`, `Pong`), push kinds (`AudioNudge`), surface identity (`PushToTalk`, `Pong`), push kinds (`AudioNudge`), surface identity
@@ -755,7 +757,7 @@ Kept for provenance. **None of this is the current or intended design.**
stay deterministic — "classifier owns the route, the SLM stays in its stay deterministic — "classifier owns the route, the SLM stays in its
phrasing lane" — with an embedding + nearest-centroid stage 1 over ~10 phrasing lane" — with an embedding + nearest-centroid stage 1 over ~10
examples per intent, and misroutes appended as new centroid examples. examples per intent, and misroutes appended as new centroid examples.
*Replaced by* LLM-as-router (`REARCH.md`): one resident model emits *Replaced by* LLM-as-router (`docs/rearchitecture.md`): one resident model emits
GBNF-constrained JSON and also phrases replies; the embedder is demoted to GBNF-constrained JSON and also phrases replies; the embedder is demoted to
a RAG hint. *Landed 2026-07-31:* the LLM router is on by default and set a RAG hint. *Landed 2026-07-31:* the LLM router is on by default and set
`true` in `deploy/mavend.json`. The classifier cascade stays as the failure `true` in `deploy/mavend.json`. The classifier cascade stays as the failure
@@ -774,7 +776,7 @@ Kept for provenance. **None of this is the current or intended design.**
*Resolved 2026-07-30 (#318), revised 2026-07-31:* the resident checkpoint is *Resolved 2026-07-30 (#318), revised 2026-07-31:* the resident checkpoint is
stock **Qwen3-1.7B** (`UD-Q4_K_XL`, `n_ctx` 4096), which replaced stock **Qwen3-1.7B** (`UD-Q4_K_XL`, `n_ctx` 4096), which replaced
Qwen3.5-0.8B after measuring better on both fixtures Qwen3.5-0.8B after measuring better on both fixtures
(`MODEL-BAKEOFF-31-07-2026.md`). The CPT'd **Qwen3-1.7B** remains the target (`docs/evals/2026-07-31-model-bakeoff.md`). The CPT'd **Qwen3-1.7B** remains the target
(#122); what stock gets wrong is the persona, not the Russian. Note the resident (#122); what stock gets wrong is the persona, not the Russian. Note the resident
model is no longer described as untrained — the target is trained model is no longer described as untrained — the target is trained
end-to-end, which is the substantive change from the old claim. end-to-end, which is the substantive change from the old claim.
@@ -1,5 +1,7 @@
# Deterministic logic around a small model # Deterministic logic around a small model
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
Written 2026-08-02. Branch `fix/integrated`. Written 2026-08-02. Branch `fix/integrated`.
## The question ## The question
@@ -268,7 +270,7 @@ rebuilt `mavend` wires it. "кто написал войну и мир?" now rou
that turn left unsettled. that turn left unsettled.
- **The turn was slow, and nobody knows yet whether that is real.** Route 7s, - **The turn was slow, and nobody knows yet whether that is real.** Route 7s,
search 1s, phrasing 15s. The p50 in `ROUTING-EVAL-31-07-2026.md` is 825ms. It search 1s, phrasing 15s. The p50 in `docs/evals/2026-07-31-routing.md` is 825ms. It
was the first turn after a cold start with the model still warming, so it was the first turn after a cold start with the model still warming, so it
proves nothing either way. Re-run the same question warm before treating it as proves nothing either way. Re-run the same question warm before treating it as
a regression. Do not plan latency work off this number. a regression. Do not plan latency work off this number.
@@ -1,5 +1,7 @@
# Maven Ecosystem Architecture # Maven Ecosystem Architecture
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
## 1. Purpose ## 1. Purpose
This document defines Maven's role in the local ecosystem formed by: This document defines Maven's role in the local ecosystem formed by:
@@ -16,7 +16,7 @@ with Qwen3-1.7B.
Settles Vikunja **#278 / #250**. Settles Vikunja **#278 / #250**.
- Same fixture and scorer as `ROUTING-EVAL-31-07-2026.md`: `internal/router/eval/` - Same fixture and scorer as `docs/evals/2026-07-31-routing.md`: `internal/router/eval/`
(`ru_routing_v1.json`, 76 held-out cases). (`ru_routing_v1.json`, 76 held-out cases).
- Reproduce: `MAVEN_LLM_URL=http://127.0.0.1:<port> make eval-router` - Reproduce: `MAVEN_LLM_URL=http://127.0.0.1:<port> make eval-router`
(`TestLLMRouterBaseline`). (This line used to say there is no `make eval-models` target. (`TestLLMRouterBaseline`). (This line used to say there is no `make eval-models` target.
@@ -148,7 +148,7 @@ Qwen3-1.7B wins every column, including against a model 20% larger than it.
| ontopic | 16, 19, 19 | **22, 23, 23** | | ontopic | 16, 19, 19 | **22, 23, 23** |
| canned fallbacks | 8, 5, 6 | **0, 2, 0** | | canned fallbacks | 8, 5, 6 | **0, 2, 0** |
This also fills the row `TALK-EVAL-31-07-2026.md` had to void for contamination: This also fills the row `docs/evals/2026-07-31-talk.md` had to void for contamination:
**600ch/1024tok on Qwen3.5-0.8B scores 13, 11, 8.** **600ch/1024tok on Qwen3.5-0.8B scores 13, 11, 8.**
`address` is the headline. It sat at 18-22 of 27 on the 0.8B no matter how the prompt `address` is the headline. It sat at 18-22 of 27 on the 0.8B no matter how the prompt
@@ -164,7 +164,7 @@ The 1.7B does that 0-2 times.
> **Stale, corrected 2026-08-02.** The p50 figures in this table are contention on a > **Stale, corrected 2026-08-02.** The p50 figures in this table are contention on a
> shared llama-server, not the model's cost. The router measures p50 825ms / p95 1.2s / > shared llama-server, not the model's cost. The router measures p50 825ms / p95 1.2s /
> max 3.0s in `ROUTING-EVAL-31-07-2026.md`, which says so at line 61. Read this table for > max 3.0s in `docs/evals/2026-07-31-routing.md`, which says so at line 61. Read this table for
> the shape of the tail only. Take absolute latency from the routing eval. > the shape of the tail only. Take absolute latency from the routing eval.
| | p50 | p95 | | | p50 | p95 |
@@ -220,11 +220,11 @@ swapped again when the CPT lands.
behind `voice.llm_router`, the default is on, and `deploy/mavend.json` sets it `true`. behind `voice.llm_router`, the default is on, and `deploy/mavend.json` sets it `true`.
These numbers are the production path now. **Corrected 2026-08-02: the p50 ≈2.7s in the These numbers are the production path now. **Corrected 2026-08-02: the p50 ≈2.7s in the
latency table above WAS a bench artifact.** It is contention on the shared llama-server, latency table above WAS a bench artifact.** It is contention on the shared llama-server,
not the model. `ROUTING-EVAL-31-07-2026.md` line 61 says so, and measures the router at not the model. `docs/evals/2026-07-31-routing.md` line 61 says so, and measures the router at
p50 825ms / p95 1.2s / max 3.0s. Cite that file for latency, not this one. p50 825ms / p95 1.2s / max 3.0s. Cite that file for latency, not this one.
- ~~`/mnt/hdd1/llms/LFM2.5/Qwen3-1.7B-UD-Q4_K_XL.gguf` is a 293 MB truncated download - ~~`/mnt/hdd1/llms/LFM2.5/Qwen3-1.7B-UD-Q4_K_XL.gguf` is a 293 MB truncated download
in the wrong directory.~~ **Deleted 2026-07-31.** The good 1.13 GB copy in `qwen3/` is in the wrong directory.~~ **Deleted 2026-07-31.** The good 1.13 GB copy in `qwen3/` is
what `deploy/mavend.json` loads. what `deploy/mavend.json` loads.
- Harness: `scratchpad/bakeoff.sh`, one server at a time, health-checked before each - Harness: `scratchpad/bakeoff.sh`, one server at a time, health-checked before each
run, `/v1/models` recorded per run. Never run two LLM consumers at once — see the run, `/v1/models` recorded per run. Never run two LLM consumers at once — see the
contamination note in `TALK-EVAL-31-07-2026.md`. contamination note in `docs/evals/2026-07-31-talk.md`.
@@ -1,7 +1,7 @@
# Phrasing evaluation — 31-07-2026 # Phrasing evaluation — 31-07-2026
How Maven words a nudge, measured instead of argued. Counterpart to How Maven words a nudge, measured instead of argued. Counterpart to
`ROUTING-EVAL-31-07-2026.md`. `docs/evals/2026-07-31-routing.md`.
- Fixture + scorer: `internal/phraser/eval/` (`nudges_v1.json`, 15 cases; `eval.go`, `checks.go`) - Fixture + scorer: `internal/phraser/eval/` (`nudges_v1.json`, 15 cases; `eval.go`, `checks.go`)
- Reproduce: `MAVEN_LLM_URL=http://127.0.0.1:18099 make eval-phrasing` - Reproduce: `MAVEN_LLM_URL=http://127.0.0.1:18099 make eval-phrasing`
@@ -65,7 +65,7 @@ prefixes) is the targeted fix, and it would move findings 1 and 2 together. Sepa
`model_quantized.onnx` — not the same file. `model_quantized.onnx` — not the same file.
`hard` cases score **2/11**: every one is a query where the operator did not reuse his own words. `hard` cases score **2/11**: every one is a query where the operator did not reuse his own words.
That is the normal case weeks later, and exactly what DESIGN.md's "recall when relevant" promises. That is the normal case weeks later, and exactly what docs/design.md's "recall when relevant" promises.
### 4. The memory-store recall branch is dead for notes ### 4. The memory-store recall branch is dead for notes
@@ -177,7 +177,7 @@ was silent ("не знаю" to "который час") while the one it introdu
### 1. The resident model does route better — 50.0% vs 36.8% ### 1. The resident model does route better — 50.0% vs 36.8%
REARCH.md's premise holds; `voice.go:211`'s comment does not. **But the classifier is only docs/rearchitecture.md's premise holds; `voice.go:211`'s comment does not. **But the classifier is only
~37% correct on held-out utterances, and the model only ~50%.** Neither is "reliable". The ~37% correct on held-out utterances, and the model only ~50%.** Neither is "reliable". The
gap between them is real but both are far from a system you would describe as working. gap between them is real but both are far from a system you would describe as working.
@@ -141,7 +141,7 @@ a model check, which catches a dead server but not a loaded one.
measured) on this fixture and the router fixture. Not the 4B — too big for measured) on this fixture and the router fixture. Not the 4B — too big for
this box, owner's call. this box, owner's call.
- Newer sub-500M candidates (LFM2.5 200M/300M) are worth a run for routing. - Newer sub-500M candidates (LFM2.5 200M/300M) are worth a run for routing.
Note `MODEL-BAKEOFF-31-07-2026.md` found LFM2.5-**1.2B** worse than Note `docs/evals/2026-07-31-model-bakeoff.md` found LFM2.5-**1.2B** worse than
Qwen3.5-0.8B at Russian routing and 2.4× slower — but those are a different, Qwen3.5-0.8B at Russian routing and 2.4× slower — but those are a different,
older generation, so that result does not predict the small ones. older generation, so that result does not predict the small ones.
- Fix `chat-how-are-you`'s `want_any`, and re-baseline once, so `ontopic` - Fix `chat-how-are-you`'s `want_any`, and re-baseline once, so `ontopic`
+2
View File
@@ -1,5 +1,7 @@
# Start Commands # Start Commands
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
All commands assume `ROOT=/home/kami/apps/Maven` and the local Go toolchain at `$ROOT/deps/go/go/bin/go`. All commands assume `ROOT=/home/kami/apps/Maven` and the local Go toolchain at `$ROOT/deps/go/go/bin/go`.
## Prerequisites ## Prerequisites
@@ -6,7 +6,7 @@
> use the embedded LFM model paths or old single-object examples as current ops > use the embedded LFM model paths or old single-object examples as current ops
> guidance; see `2026-07-18-qwen3-resident-training-eval.md`. > guidance; see `2026-07-18-qwen3-resident-training-eval.md`.
> Scope from `REARCH.md`. Make Maven trustworthy: the LFM becomes the router > Scope from `docs/rearchitecture.md`. Make Maven trustworthy: the LFM becomes the router
> (fixes "messes up queries" / "doesn't take notes"), the engine actually runs > (fixes "messes up queries" / "doesn't take notes"), the engine actually runs
> (fixes stub replies), dates stop being read as "number dot number dot number", > (fixes stub replies), dates stop being read as "number dot number dot number",
> and telegram becomes a reach channel. NOT in scope: on-demand 4B reasoner, > and telegram becomes a reach channel. NOT in scope: on-demand 4B reasoner,
@@ -693,7 +693,7 @@ ssh kami@192.168.1.104 'curl -s localhost:9201/api/chat -d "{\"text\":\"запо
4. `docker compose up -d mavend && docker logs -f maven-mavend-1` — confirm the 4. `docker compose up -d mavend && docker logs -f maven-mavend-1` — confirm the
phraser spawns and no `phraser: NewStub` path. Run the two verify curls. phraser spawns and no `phraser: NewStub` path. Run the two verify curls.
5. Update `AGENTS.md`: LFM model download + note that routing is now LFM-first 5. Update `AGENTS.md`: LFM model download + note that routing is now LFM-first
with classifier fallback (`REARCH.md` is the design of record). with classifier fallback (`docs/rearchitecture.md` is the design of record).
--- ---
+2
View File
@@ -1,5 +1,7 @@
# Maven Voice Protocol # Maven Voice Protocol
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
> Auto-generated from `internal/voice/wire.go`, `internal/voice/errors.go`, > Auto-generated from `internal/voice/wire.go`, `internal/voice/errors.go`,
> `internal/voice/frame.go`, `internal/voice/client.go`. If this file and > `internal/voice/frame.go`, `internal/voice/client.go`. If this file and
> those files disagree, the code wins. > those files disagree, the code wins.
+2
View File
@@ -1,5 +1,7 @@
# QA plan: checking Maven properly # QA plan: checking Maven properly
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
Written 2026-08-01, after the 35-PR stack landed and the box came back up. Written 2026-08-01, after the 35-PR stack landed and the box came back up.
44 of the 50 open Vikunja tasks are `QA:` tasks. They are verification work, not 44 of the 50 open Vikunja tasks are `QA:` tasks. They are verification work, not
+2
View File
@@ -1,5 +1,7 @@
# Maven — Re-architecture (Qwen3 resident model, revised 2026-07-18) # Maven — Re-architecture (Qwen3 resident model, revised 2026-07-18)
*Last verified: 2026-08-02 @ 7079a24. Living doc: correct it in place, do not append.*
> Supersedes the classifier-first routing model. Agreed in a design session > Supersedes the classifier-first routing model. Agreed in a design session
> after diagnosing that homesrv deploys with a **stub phraser** (no LLM > after diagnosing that homesrv deploys with a **stub phraser** (no LLM
> running) and an embedder-classifier that routes by nearest-neighbor between > running) and an embedder-classifier that routes by nearest-neighbor between
+1 -1
View File
@@ -1,7 +1,7 @@
// Package auth is maven's authority layer — the 4-layer cascade and the // Package auth is maven's authority layer — the 4-layer cascade and the
// "surface caps authority" invariant. // "surface caps authority" invariant.
// //
// Spec contract (from DESIGN.md § Auth): // Spec contract (from docs/design.md § Auth):
// //
// a cascade, not a pick-one — each layer answers a different question: // a cascade, not a pick-one — each layer answers a different question:
// //
+1 -1
View File
@@ -649,7 +649,7 @@ type VoiceConfig struct {
// LLMRouter — route with the resident model instead of the embedding // LLMRouter — route with the resident model instead of the embedding
// classifier. On by default since Vikunja #320. // classifier. On by default since Vikunja #320.
// //
// Measured on the held-out fixture (ROUTING-EVAL-31-07-2026.md): 63.2% of // Measured on the held-out fixture (docs/evals/2026-07-31-routing.md): 63.2% of
// intents right against the classifier's 50.0%, and no route errors. It // intents right against the classifier's 50.0%, and no route errors. It
// costs about 1s per turn instead of 30ms. // costs about 1s per turn instead of 30ms.
// //
+1 -1
View File
@@ -1,6 +1,6 @@
// Package delivery is maven's channel-routing + dispatch layer. // Package delivery is maven's channel-routing + dispatch layer.
// //
// Spec contract (from DESIGN.md § Delivery / channel routing): // Spec contract (from docs/design.md § Delivery / channel routing):
// //
// - routing = f(severity, presence). presence decides REACHABILITY; severity // - routing = f(severity, presence). presence decides REACHABILITY; severity
// decides INSISTENCE. need both. // decides INSISTENCE. need both.
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"github.com/kami/maven/internal/store" "github.com/kami/maven/internal/store"
) )
// This file walks every cell of the DESIGN.md § "Delivery / channel routing" // This file walks every cell of the docs/design.md § "Delivery / channel routing"
// table, once as the pure table and once through the dispatcher, so a change // table, once as the pure table and once through the dispatcher, so a change
// to either side has to break a named cell. // to either side has to break a named cell.
// //
@@ -205,7 +205,7 @@ func TestAwayChannelsGetMinimalBody(t *testing.T) {
// An empty Summary no longer means "send the whole body" — it means a short // An empty Summary no longer means "send the whole body" — it means a short
// generic line — so the old expectation here was wrong as well as duplicated. // generic line — so the old expectation here was wrong as well as duplicated.
// TestCareAwayDropIsRecorded — DESIGN.md's drop is a decision ("a missed water // TestCareAwayDropIsRecorded — docs/design.md's drop is a decision ("a missed water
// nudge is noise, a missed backup failure isn't"), so it should be visible // nudge is noise, a missed backup failure isn't"), so it should be visible
// rather than vanish. Today drop is a bare `continue`: no nudge row, no outbox // rather than vanish. Today drop is a bare `continue`: no nudge row, no outbox
// attempt, no log — nothing an operator can see afterwards. now it leaves a // attempt, no log — nothing an operator can see afterwards. now it leaves a
+1 -1
View File
@@ -31,7 +31,7 @@ type Completer interface {
// or reply in Russian. // or reply in Russian.
// //
// Why the JSON wrapper: this model always thinks out loud and this llama-server // Why the JSON wrapper: this model always thinks out loud and this llama-server
// build ignores the thinking switch (see ROUTING-EVAL-31-07-2026.md). A bare // build ignores the thinking switch (see docs/evals/2026-07-31-routing.md). A bare
// word-list grammar just captured the reasoning — every case came back as // word-list grammar just captured the reasoning — every case came back as
// "Let me analyze this request carefully". Demanding JSON, like routeGrammar and // "Let me analyze this request carefully". Demanding JSON, like routeGrammar and
// responseGrammar already do, gives the reasoning nowhere to go. // responseGrammar already do, gives the reasoning nowhere to go.
+5 -5
View File
@@ -10,12 +10,12 @@ import (
// Tests for the universal restraint gate. // Tests for the universal restraint gate.
// //
// DESIGN.md § Trigger model: "the gate is universal, applied by the loop, never // docs/design.md § Trigger model: "the gate is universal, applied by the loop, never
// per-rule — quiet-hours, presence, cooldown, snooze, calendar-busy all live in // per-rule — quiet-hours, presence, cooldown, snooze, calendar-busy all live in
// one fires()." These tests pin the CONSERVATIVE side of that: the cases where // one fires()." These tests pin the CONSERVATIVE side of that: the cases where
// Maven must stay quiet. They exist so nobody loosens the gate by accident. // Maven must stay quiet. They exist so nobody loosens the gate by accident.
// //
// Where the code does not yet do what DESIGN.md promises, the test is written to // Where the code does not yet do what docs/design.md promises, the test is written to
// show the gap and then skipped, with the file and line to fix. Behaviour is not // show the gap and then skipped, with the file and line to fix. Behaviour is not
// changed to make a test pass. // changed to make a test pass.
@@ -54,7 +54,7 @@ func TestGateQuietHoursSuppressesCareOnly(t *testing.T) {
// ---------------------------- presence --------------------------------------- // ---------------------------- presence ---------------------------------------
// DESIGN.md § Delivery: "sev <= 2 drops on away, sev >= 3 holds: a missed water // docs/design.md § Delivery: "sev <= 2 drops on away, sev >= 3 holds: a missed water
// nudge is noise, a missed backup failure isn't." // nudge is noise, a missed backup failure isn't."
func TestGateAwayDropsCareHoldsOps(t *testing.T) { func TestGateAwayDropsCareHoldsOps(t *testing.T) {
cases := []struct { cases := []struct {
@@ -250,7 +250,7 @@ func TestTickOrderOfRulesDoesNotMatter(t *testing.T) {
// ---------------------------- reminders bypass the gate ---------------------- // ---------------------------- reminders bypass the gate ----------------------
// DESIGN.md § User reminders: "bypasses the restraint gate — 'wake me 7' fires // docs/design.md § User reminders: "bypasses the restraint gate — 'wake me 7' fires
// in quiet hours; that's the point." Every suppressor set at once, and the // in quiet hours; that's the point." Every suppressor set at once, and the
// reminder still comes through. // reminder still comes through.
func TestRemindersBypassEverySuppressor(t *testing.T) { func TestRemindersBypassEverySuppressor(t *testing.T) {
@@ -269,7 +269,7 @@ func TestRemindersBypassEverySuppressor(t *testing.T) {
} }
} }
// GAP — DESIGN.md § User reminders ends "Snooze still applies." RemindDecisions // GAP — docs/design.md § User reminders ends "Snooze still applies." RemindDecisions
// passes every due reminder straight through with no snooze check, so a snoozed // passes every due reminder straight through with no snooze check, so a snoozed
// reminder fires anyway. The test below is what the contract asks for. // reminder fires anyway. The test below is what the contract asks for.
func TestRemindersStillHonourSnooze(t *testing.T) { func TestRemindersStillHonourSnooze(t *testing.T) {
+4 -4
View File
@@ -18,7 +18,7 @@ import (
// - does it stay quiet when it should? // - does it stay quiet when it should?
// - is it silent when the key it needs has no data at all? // - is it silent when the key it needs has no data at all?
// //
// The last one is load-bearing. DESIGN.md: "since(key)==null → don't fire. // The last one is load-bearing. docs/design.md: "since(key)==null → don't fire.
// Silence on no-data is 'shuts up when uncertain'." // Silence on no-data is 'shuts up when uncertain'."
// stateWith builds a snapshot at refTime() holding just the given facts. // stateWith builds a snapshot at refTime() holding just the given facts.
@@ -179,7 +179,7 @@ func TestCareRulePredicates(t *testing.T) {
// ---------------------------- ops rules -------------------------------------- // ---------------------------- ops rules --------------------------------------
// The two ops rules match on a value AND on which poller wrote it. DESIGN.md: // The two ops rules match on a value AND on which poller wrote it. docs/design.md:
// "a compromised poller must not be able to forge a trigger." Half of this // "a compromised poller must not be able to forge a trigger." Half of this
// table is forgery attempts; all of them must be refused. // table is forgery attempts; all of them must be refused.
func TestOpsRulePredicates(t *testing.T) { func TestOpsRulePredicates(t *testing.T) {
@@ -331,7 +331,7 @@ func TestNoDefaultRuleFiresOnEmptyState(t *testing.T) {
} }
} }
// Severities are the delivery contract (DESIGN.md § Delivery / channel // Severities are the delivery contract (docs/design.md § Delivery / channel
// routing): care is sev1-2 and drops when away, ops is sev3-4 and holds. Pin // routing): care is sev1-2 and drops when away, ops is sev3-4 and holds. Pin
// them so a change to a rule's insistence has to be deliberate. // them so a change to a rule's insistence has to be deliberate.
func TestDefaultRuleSeverities(t *testing.T) { func TestDefaultRuleSeverities(t *testing.T) {
@@ -356,7 +356,7 @@ func TestDefaultRuleSeverities(t *testing.T) {
} }
} }
// Cooldown bounds keep the feedback tuner honest — DESIGN.md wants // Cooldown bounds keep the feedback tuner honest — docs/design.md wants
// `cooldown in [min,max]` "so a weird week can't mutate Maven silent or // `cooldown in [min,max]` "so a weird week can't mutate Maven silent or
// stalker". A base outside its own envelope would make that meaningless. // stalker". A base outside its own envelope would make that meaningless.
func TestDefaultRuleCooldownsAreBounded(t *testing.T) { func TestDefaultRuleCooldownsAreBounded(t *testing.T) {
+1 -1
View File
@@ -41,7 +41,7 @@
"tags": ["preference", "homelab", "paraphrase", "hard"], "tags": ["preference", "homelab", "paraphrase", "hard"],
"query": "когда запускать резервное копирование", "query": "когда запускать резервное копирование",
"want": "n1", "want": "n1",
"note": "The DESIGN.md preference-seam example, phrased as the operator would ask it later.", "note": "The docs/design.md preference-seam example, phrased as the operator would ask it later.",
"notes": [ "notes": [
{"id": "n1", "text": "бэкапы лучше делать ночью в три часа", "kind": "note"}, {"id": "n1", "text": "бэкапы лучше делать ночью в три часа", "kind": "note"},
{"id": "n2", "text": "обновления ставлю по субботам", "kind": "note"}, {"id": "n2", "text": "обновления ставлю по субботам", "kind": "note"},
+3 -3
View File
@@ -15,7 +15,7 @@ const (
CheckLang = "lang" // the operator's language, not the prompt's CheckLang = "lang" // the operator's language, not the prompt's
CheckLength = "length" // a nudge is one sentence, not a paragraph CheckLength = "length" // a nudge is one sentence, not a paragraph
CheckFeminine = "feminine" // her self-reference is feminine (hard constraint) CheckFeminine = "feminine" // her self-reference is feminine (hard constraint)
CheckCringe = "cringe" // DESIGN.md § Non-goals, "not a relationship" CheckCringe = "cringe" // docs/design.md § Non-goals, "not a relationship"
CheckOnTopic = "ontopic" // says the thing the rule is about CheckOnTopic = "ontopic" // says the thing the rule is about
// CheckHisGender — the other half of the persona rule: SHE is feminine, HE // CheckHisGender — the other half of the persona rule: SHE is feminine, HE
@@ -105,7 +105,7 @@ func checkLength(body string) Result {
// --- feminine self-reference --------------------------------------------- // --- feminine self-reference ---------------------------------------------
// //
// The hard constraint (CLAUDE.md, DESIGN.md § Identity): Maven's Russian // The hard constraint (CLAUDE.md, docs/design.md § Identity): Maven's Russian
// self-reference is feminine. The operator is male, so second-person forms // self-reference is feminine. The operator is male, so second-person forms
// addressed to him are MASCULINE and must not be flagged — "ты не пил воду" is // addressed to him are MASCULINE and must not be flagged — "ты не пил воду" is
// correct, "я напомнил" is not. Both directions matter, which is why this is a // correct, "я напомнил" is not. Both directions matter, which is why this is a
@@ -497,7 +497,7 @@ func isLatinWord(w string) bool {
// --- the cringe checks --------------------------------------------------- // --- the cringe checks ---------------------------------------------------
// //
// "Think Jarvis without the cringe part". DESIGN.md § Non-goals: "Not a // "Think Jarvis without the cringe part". docs/design.md § Non-goals: "Not a
// relationship — mom-tone is a function that makes nudges land, not emotional // relationship — mom-tone is a function that makes nudges land, not emotional
// company. Names the drift a warm small model falls into." Each pattern below // company. Names the drift a warm small model falls into." Each pattern below
// is one shape of that drift. They are deliberately specific: a check that // is one shape of that drift. They are deliberately specific: a check that
+2 -2
View File
@@ -11,7 +11,7 @@
// length test that a human can read and disagree with. A score here is a claim // length test that a human can read and disagree with. A score here is a claim
// about measurable properties, not about whether a sentence is good. // about measurable properties, not about whether a sentence is good.
// //
// DESIGN.md § "Rules decide, LLM phrases" is why there is no send/veto signal // docs/design.md § "Rules decide, LLM phrases" is why there is no send/veto signal
// anywhere in this package: the rule already decided she speaks. The phraser // anywhere in this package: the rule already decided she speaks. The phraser
// only words it, so a nudge the model refuses to write is a failure, never a // only words it, so a nudge the model refuses to write is a failure, never a
// legitimate outcome. // legitimate outcome.
@@ -38,7 +38,7 @@ var fixtureJSON []byte
const SchemaVersion = 1 const SchemaVersion = 1
// Case — one nudge situation, as a real tick would present it. The fields are // Case — one nudge situation, as a real tick would present it. The fields are
// the (rule, severity, context) input DESIGN.md names, flattened to JSON. // the (rule, severity, context) input docs/design.md names, flattened to JSON.
// //
// WantAny is the on-topic contract: at least one of these lowercased fragments // WantAny is the on-topic contract: at least one of these lowercased fragments
// must appear in the message. A water nudge that never mentions water is a // must appear in the message. A water nudge that never mentions water is a
+1 -1
View File
@@ -692,7 +692,7 @@ func (p *LLMPhraser) chatWithSystem(ctx context.Context, system, user string, ma
// Written as filled-in examples, not as a schema with "..." in it. A 0.8B // Written as filled-in examples, not as a schema with "..." in it. A 0.8B
// copies whatever sits in the response slot, so a literal placeholder there // copies whatever sits in the response slot, so a literal placeholder there
// teaches it to answer with the placeholder. Measured: 7/15 nudges came back // teaches it to answer with the placeholder. Measured: 7/15 nudges came back
// as "..." before this. See PHRASING-EVAL-31-07-2026.md. // as "..." before this. See docs/evals/2026-07-31-phrasing.md.
// //
// Russian only, feminine self-reference, second person masculine (the owner is // Russian only, feminine self-reference, second person masculine (the owner is
// a man). She talks TO him, informally, singular — never "вы", never "он". // a man). She talks TO him, informally, singular — never "вы", never "он".
+2 -2
View File
@@ -1,9 +1,9 @@
// Package phraser is maven's "rules decide, llm phrases" seam — the layer // Package phraser is maven's "rules decide, llm phrases" seam — the layer
// that turns a loop decision into the body + summary the delivery module ships. // that turns a loop decision into the body + summary the delivery module ships.
// //
// Per DESIGN.md § Resident language model: the phraser is the resident model // Per docs/design.md § Resident language model: the phraser is the resident model
// (Qwen3-1.7B — RU continued pretraining plus joint persona/router SFT, not a // (Qwen3-1.7B — RU continued pretraining plus joint persona/router SFT, not a
// sub-1b prompted-only model as the retired spec claimed; see DESIGN.md // sub-1b prompted-only model as the retired spec claimed; see docs/design.md
// § Superseded, "small-model phrasing claim"). It takes // § Superseded, "small-model phrasing claim"). It takes
// (rule, severity, context) and produces Body (full voice message, local — no // (rule, severity, context) and produces Body (full voice message, local — no
// shoulder-surf concern beyond who's in the room) + Summary (minimal body for // shoulder-surf concern beyond who's in the room) + Summary (minimal body for
+1 -1
View File
@@ -39,7 +39,7 @@ import (
// Re-measured with everything else held equal, thinking off scores exactly the // Re-measured with everything else held equal, thinking off scores exactly the
// same, case for case — and a direct probe shows this llama-server build ignores // same, case for case — and a direct probe shows this llama-server build ignores
// enable_thinking / reasoning_budget for this model anyway, so there was nothing // enable_thinking / reasoning_budget for this model anyway, so there was nothing
// to turn off. Full write-up in ROUTING-EVAL-31-07-2026.md (Vikunja #376). // to turn off. Full write-up in docs/evals/2026-07-31-routing.md (Vikunja #376).
func TestLLMRouterBaseline(t *testing.T) { func TestLLMRouterBaseline(t *testing.T) {
base := os.Getenv("MAVEN_LLM_URL") base := os.Getenv("MAVEN_LLM_URL")
if base == "" { if base == "" {
+3 -3
View File
@@ -1,13 +1,13 @@
// Package router is maven's reactive path — the cascade that turns a free-form // Package router is maven's reactive path — the cascade that turns a free-form
// utterance into a deterministic Decision. // utterance into a deterministic Decision.
// //
// Spec contract (from DESIGN.md § Reactive path — routing): // Spec contract (from docs/design.md § Reactive path — routing):
// //
// - the TARGET design is LLM-as-router: the resident model (Qwen3-1.7B) // - the TARGET design is LLM-as-router: the resident model (Qwen3-1.7B)
// emits GBNF-constrained structured JSON for the route, and the same // emits GBNF-constrained structured JSON for the route, and the same
// model phrases replies; the embedder is a RAG hint, not a routing gate. // model phrases replies; the embedder is a RAG hint, not a routing gate.
// the classifier/embedder cascade below is the committed default today, // the classifier/embedder cascade below is the committed default today,
// but it is an interim stopgap (DESIGN.md § Superseded, "classifier-owns- // but it is an interim stopgap (docs/design.md § Superseded, "classifier-owns-
// the-route") and the known cause of weak RU query handling — not a // the-route") and the known cause of weak RU query handling — not a
// design to extend. // design to extend.
// - a CASCADE, not one decider — layers: // - a CASCADE, not one decider — layers:
@@ -35,7 +35,7 @@ package router
import "time" import "time"
// Intent — the seven save-where labels from DESIGN.md's routing table. The // Intent — the seven save-where labels from docs/design.md's routing table. The
// discriminator is "does the loop evaluate a predicate against it?": // discriminator is "does the loop evaluate a predicate against it?":
// //
// - act: command now, not stored (function call into the allowlist) // - act: command now, not stored (function call into the allowlist)
+2 -2
View File
@@ -18,9 +18,9 @@
// returns a canned string the router + action path operate on); with a // returns a canned string the router + action path operate on); with a
// worker socket configured, it wires Remote. // worker socket configured, it wires Remote.
// //
// Per DESIGN.md § Voice pipeline (STT / TTS): whisper.cpp (CGo, Vulkan) in // Per docs/design.md § Voice pipeline (STT / TTS): whisper.cpp (CGo, Vulkan) in
// cmd/mavsttd is the production stt — the older faster-whisper/vosk picks are // cmd/mavsttd is the production stt — the older faster-whisper/vosk picks are
// retired (DESIGN.md § Superseded, "named STT/TTS model picks"). The // retired (docs/design.md § Superseded, "named STT/TTS model picks"). The
// server-side stt module is the heavy multilingual path; the client's // server-side stt module is the heavy multilingual path; the client's
// wake-word + stage-0 command grammar (cmd/mavwaked) hits the router directly // wake-word + stage-0 command grammar (cmd/mavwaked) hits the router directly
// and never crosses this seam. Today's Remote + Stub both return plain text // and never crosses this seam. Today's Remote + Stub both return plain text
+2 -2
View File
@@ -2,7 +2,7 @@
// store's allowlist, and drafts 'proposed' scaffolds for acts that aren't on // store's allowlist, and drafts 'proposed' scaffolds for acts that aren't on
// it yet. // it yet.
// //
// Boundary discipline (DESIGN.md § "Tool registration — drafting is suggest, // Boundary discipline (docs/design.md § "Tool registration — drafting is suggest,
// enabling is act"): // enabling is act"):
// //
// - The store is the allowlist. Only status='enabled' rows run. A verb not // - The store is the allowlist. Only status='enabled' rows run. A verb not
@@ -26,7 +26,7 @@
// - Destructive tools don't run on first hearing: Exec returns ErrNeedsConfirm // - Destructive tools don't run on first hearing: Exec returns ErrNeedsConfirm
// and the handler runs a confirm turn ("выполнить X? да/нет"); only a // and the handler runs a confirm turn ("выполнить X? да/нет"); only a
// confirmed re-Exec runs them. A gate assumes a fully-formed action, which // confirmed re-Exec runs them. A gate assumes a fully-formed action, which
// an enabled+matched act is (DESIGN.md § "Confirmation is not one // an enabled+matched act is (docs/design.md § "Confirmation is not one
// mechanism"). // mechanism").
package tool package tool
+2 -2
View File
@@ -5,9 +5,9 @@
// PCM, headerless per the audio package; the voice sink + reference client // PCM, headerless per the audio package; the voice sink + reference client
// wrap it in a WAV at the disk edge. // wrap it in a WAV at the disk edge.
// //
// Per DESIGN.md § Voice pipeline (STT / TTS): piper is the production tts // Per docs/design.md § Voice pipeline (STT / TTS): piper is the production tts
// (subprocess + espeak-ng, CPU-only on the ryzen box, driven by cmd/mavttsd); // (subprocess + espeak-ng, CPU-only on the ryzen box, driven by cmd/mavttsd);
// the older silero pick is retired (DESIGN.md § Superseded, "named STT/TTS // the older silero pick is retired (docs/design.md § Superseded, "named STT/TTS
// model picks"). A different voice is a model-file swap, not a code change. // model picks"). A different voice is a model-file swap, not a code change.
// The daemon wires one impl — Remote pointing at the worker socket if // The daemon wires one impl — Remote pointing at the worker socket if
// configured, Stub otherwise. // configured, Stub otherwise.
+3 -3
View File
@@ -1,6 +1,6 @@
# maven — feature ranking # maven — feature ranking
> dated 2026-07-03. companion to `DESIGN.md` (folded from the former `maven.md`). ranks everything discussed post-repo-state against the infra blockers, not a replacement for the build order. > dated 2026-07-03. companion to `docs/design.md` (folded from the former `maven.md`). ranks everything discussed post-repo-state against the infra blockers, not a replacement for the build order.
--- ---
@@ -20,7 +20,7 @@ nothing feature-level below should land before 12 are done. 34 can interle
### mandatory ### mandatory
things that block correctness or safety of stuff already shipped — not new capability, just closing gaps in existing design. things that block correctness or safety of stuff already shipped — not new capability, just closing gaps in existing design.
- **destructive-confirm policy** — open question in `DESIGN.md` § open questions, blocks correx and any new tool domain from having a coherent risk tier - **destructive-confirm policy** — open question in `docs/design.md` § open questions, blocks correx and any new tool domain from having a coherent risk tier
- **quiet-hours definition** — open question, blocks proactive delivery being trustworthy - **quiet-hours definition** — open question, blocks proactive delivery being trustworthy
- **schema migrations** — sqlcipher rollout alone forces a schema touch. want this mechanism before that, not after. - **schema migrations** — sqlcipher rollout alone forces a schema touch. want this mechanism before that, not after.
@@ -30,7 +30,7 @@ cheap, no dependencies, no new invariants.
- **grocery / `list_items` table** — fourth append-only shape (item, status, list-tag), no predicate touches it, multi-adder just works for free - **grocery / `list_items` table** — fourth append-only shape (item, status, list-tag), no predicate touches it, multi-adder just works for free
- **go.mod tidy** - **go.mod tidy**
- **capability model** (deepseek) — `homelab.docker.restart` instead of flat `tool→enabled`. cheap now, expensive to retrofit once tools surface passes ~15 entries. time-sensitive, not urgent. - **capability model** (deepseek) — `homelab.docker.restart` instead of flat `tool→enabled`. cheap now, expensive to retrofit once tools surface passes ~15 entries. time-sensitive, not urgent.
- **conversation repair** — already free: `DESIGN.md` has "misroute correction = new centroid example," this is just naming the existing mechanism as a feature - **conversation repair** — already free: `docs/design.md` has "misroute correction = new centroid example," this is just naming the existing mechanism as a feature
- **command history** — read-only query over existing facts, no new mechanism - **command history** — read-only query over existing facts, no new mechanism
- **clarification templates** — canned phrasing for the router's existing confidence-gate fallback, phraser-lane only - **clarification templates** — canned phrasing for the router's existing confidence-gate fallback, phraser-lane only
- **pronunciation dictionary** — tts config, no architecture - **pronunciation dictionary** — tts config, no architecture