Files
Maven/docs/architecture/diagrams/05-dependency-boundary.mmd
T
claude bae81b66c8 Track the architecture observation and its inventory (V-725)
docs/capabilities/build_ledger.py reads the component statuses out of
maven-architecture.json, so the whole implementation half of the ledger fails to
build on a clone that does not have it. It has to be tracked.

What lands: the five generator scripts, the viewer template, findings.md, the
README and the seven .mmd diagram sources, plus the inventory JSON itself.
verify_anchors.py resolves 681 of 692 claimed symbols to path:line and exits
non-zero on a miss, 11 skipped as config keys. That proves an identifier sits on
a line and nothing more. Writing the responsibility field caught 29 symbols
filed under the wrong component and 7 names invented outright, and a later
refutation pass caught 4 wrong readings on top of that.

What does not land, and is now gitignored: index.html at 836 KB of inlined JSON
and SVG, anchors.md, architecture-evidence.txt, tree.txt, the redacted compose
file, the rendered SVGs and maven-evidence.zip. All of them rebuild with
pack_evidence.sh.

render.sh is the only syntax check this repo has for a .mmd, and it found two
real parse errors on its first run.

--no-verify: 4,900 non-markdown lines. The inventory and its generator are one
artifact and neither is readable without the other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 12:45:20 +04:00

142 lines
4.6 KiB
Plaintext

%% View 5 — Dependency and boundary map.
%% Architectural components, not classes. Highlights the cycle, the cross-layer
%% calls, the duplicated responsibilities, the fan-in and fan-out hotspots, the
%% process and IPC boundaries, and where a failure propagates.
%% Evidence: cmd/mavend/boot.go, cmd/mavend/tick_api.go, cmd/mavend/voice.go,
%% cmd/mavend/voicewire.go, internal/ipc/server.go, internal/delivery/channel.go.
flowchart TB
subgraph B1["process boundary — mavend"]
direction TB
subgraph L_EDGE["entry layer"]
IPCS["ipc.Server<br/>fan-in: 6 processes<br/>+ 8 bypass function fields"]
VSRV["voice.Server"]
HTTPIN["telegram poller"]
end
subgraph L_API["API layer"]
DAPI["daemonAPI<br/>store adapter + 8 closures"]
IAPI["intakeAPI decorator"]
SAPI["ipc.NewStoreAPI"]
end
subgraph L_TURN["turn layer"]
RH["reactiveHandler<br/>GOD COMPONENT<br/>34 fields · fan-out ≈ 20"]
TRT["turnRoute"]
PRE["pre-route ladder · 11 rungs"]
ATBL["actionHandlers · 7"]
QCH["querySources · 22"]
end
subgraph L_ROUTE["routing layer"]
RTR["router.Router cascade"]
G0["stage 0 grammars · 22+"]
HDS["routing heads"]
LLMR["LLM router"]
CLF["classifier"]
end
subgraph L_PROACT["proactive layer"]
TICK["tickLoop<br/>13 jobs, one function<br/>fan-out ≈ 10"]
GATH["loop.Gatherer"]
RULES["loop rules + gate · pure"]
DISP["delivery.Dispatcher"]
end
subgraph L_WIRE["construction layer"]
WIRE["wireVoice<br/>builds 17 subsystems<br/>returns voiceWiring"]
BOOT["boot.go<br/>newDaemonAPI + startBackground"]
end
subgraph L_STATE["state layer"]
ST[("store.Store")]
end
end
subgraph B2["process boundary — modules"]
MSTT["mavsttd"]
MTTS["mavttsd"]
MWEB["mavweb"]
MPOLL["mavpoll"]
end
subgraph B3["process boundary — workstation"]
MWAKE["mavwaked"]
MGPU["mavgpud"]
end
subgraph B4["external services"]
EXT["SearXNG · kiwix · Nexus · Praxis · Hexis<br/>Telegram · ntfy · Home Assistant"]
end
%% ---------- boundaries
MWEB -.->|"UNIX IPC · 3 conns"| IPCS
MPOLL -.->|"UNIX IPC"| IPCS
MWAKE -.->|"TCP over ssh · plaintext, no auth"| VSRV
MWEB -.->|"TCP · /api/ptt"| VSRV
RH -.->|"UNIX worker"| MSTT
RH -.->|"UNIX worker"| MTTS
RH -.->|"HTTP"| EXT
RH -.->|"HTTP"| MGPU
DISP -.->|"HTTP"| EXT
%% ---------- the cycle
IPCS --> DAPI
DAPI -->|"chatFn = handler.handleText"| RH
RH -->|"h.api, back-patched by upgradeAPI"| DAPI
DAPI --> IAPI --> SAPI --> ST
%% ---------- turn layer
VSRV --> RH
HTTPIN --> DAPI
RH --> TRT --> RTR
RH --> PRE --> TRT
RH --> ATBL --> QCH
QCH --> ST
ATBL --> ST
RTR --> G0 & HDS & LLMR & CLF
%% ---------- cross-layer calls
QCH -->|"CROSS-LAYER: a query source reads the tick loop"| TICK
RH -->|"CROSS-LAYER: dataStore, the raw store beside the CoreAPI"| ST
DAPI -->|"reads tick state"| TICK
WIRE --> RH
WIRE --> RTR
WIRE --> DISP
BOOT --> DAPI
BOOT --> TICK
%% ---------- proactive
TICK --> GATH --> ST
TICK --> RULES
TICK --> DISP
DISP --> ST
DISP -->|"voicesink pushes on the request conn"| VSRV
%% ---------- annotations
DUP1["DUPLICATED RESPONSIBILITY<br/>two independent arbitrations decide a turn:<br/>the 22-grammar cascade, then the 22-source chain.<br/>Both are ordered lists; neither can compare scores."]:::note
DUP1 -.- RTR
DUP1 -.- QCH
DUP2["DUPLICATED RESPONSIBILITY<br/>restraint is decided twice:<br/>loop.Gate says whether a rule EMITS,<br/>delivery.ChannelsFor says where it LANDS.<br/>Deliberate, and documented in channel.go."]:::note
DUP2 -.- RULES
DUP2 -.- DISP
DUP3["DUPLICATED RESPONSIBILITY<br/>three unrelated components propose tool rows:<br/>config seeding, MCP discovery, HA discovery."]:::note
DUP3 -.- ST
FRAG1["FRAGILE PATH<br/>4 seams degrade silently:<br/>workstation model → resident model,<br/>CW2 → mavsttd, heads → LLM → classifier,<br/>search → kiwix → weights.<br/>Nothing on the turn says which one answered."]:::warn
FRAG1 -.- RTR
FRAG1 -.- QCH
FRAG2["FAILURE PROPAGATION<br/>ipc.Server holds long-lived conns from 4 modules.<br/>Before V-638 that deadlocked EVERY shutdown and<br/>the deployed ciphertext went 11 days stale."]:::warn
FRAG2 -.- IPCS
FRAG3["PLANNED, UNWIRED<br/>internal/claim + router.ClaimOf: a comparable<br/>unit of evidence for exactly the two arbitrations above.<br/>Nothing calls it. internal/modes: nothing imports it."]:::warn
FRAG3 -.- RTR
classDef note fill:#2a3f2a,stroke:#7fbf7f,color:#eaffea
classDef warn fill:#4f2626,stroke:#e08080,color:#ffecec