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>
6.3 KiB
docs/architecture
An observation of Maven as built, read at 5cae33a on 2026-08-25. It describes
what the code does today. It proposes nothing.
This directory is a build output plus its sources. index.html,
maven-architecture.json, anchors.md and diagrams/*.svg are generated.
Read it
| file | what it is |
|---|---|
index.html |
the viewer. Open it from the filesystem, no server needed. Five views, the rendered diagram above each, click a component for its record. |
findings.md |
the analysis. Kept apart from the facts on purpose. |
maven-architecture.json |
the inventory. 160 components, 204 relations. The factual source for everything else. |
anchors.md |
every symbol the inventory names, resolved to path:line with the line quoted. |
diagrams/*.mmd |
the five views as Mermaid source. 03a, 03b and 03c are the three traced requests. |
diagrams/*.svg |
the same, rendered. |
Rebuild it
python3 docs/architecture/build_inventory.py # → maven-architecture.json
python3 docs/architecture/verify_anchors.py # → anchors.md, exit 1 if stale
sh docs/architecture/render.sh # → diagrams/*.svg, then index.html
python3 docs/architecture/build_viewer.py # → index.html alone
render.sh drives mermaid-cli through the system chromium rather than letting
puppeteer download its own. It is also the only syntax check this repo has for a
.mmd.
What is verified, and what is not
Verified mechanically. verify_anchors.py resolves all 692 claimed symbols
against the files their component names. Current state: 681 resolved to a line
and 0 unresolved, with 0 missing files. The other 11 are config keys and make
targets rather than Go identifiers, so they are skipped. The script exits
non-zero on any failure, which makes it a staleness gate.
Writing it caught 29 symbols filed under the wrong component and seven names
that were wrong outright. Two examples: Store.RecordEvent for what is really
Store.CreateEvent, and media.Keeper for what is really media.Store.
Not verified. That a symbol means what its responsibility says. An anchor
proves the identifier is on that line and nothing more. Judgements about
ownership, coupling and enforcement are readings of the code. A reading can be
wrong in a way grep cannot catch.
Marked, not resolved. Relations carry a confidence field. medium means
the wiring is in the source and the call path was not traced end to end. low
means it was inferred from one reference. The viewer can hide both. Four
relations are medium and one is low.
Deployment-specific. Sixteen components are configured-off against
deploy/mavend.json as it stood on the day, and that file was dirty in the
working tree. A different config makes different components live. status says
which, per component.
The one thing to check first
findings.md 6.3 through 6.3d. They say the system has no single point that
decides whether an origin may cause an effect, and that the pieces which look
like that point are each answering a different question.
Revised on 2026-08-25 after an independent second pass. Four readings changed and one earlier statement was wrong. Section 6.3 marks the corrections.
Start at internal/tool/tool.go:181, cmd/mavend/ecosystem_acts.go:158 and
internal/router/claim.go:38.
The evidence pack
sh docs/architecture/pack_evidence.sh builds maven-evidence.zip at the repo
root: this directory, the structural context, and whole source files for the
architectural seams. Whole files, never snippets, because a cut-down file loses
the call path that makes a claim checkable.
The path list is an allowlist, not an exclusion list. A denylist ships whatever nobody thought to exclude, and this tree has a database key in it.
architecture-evidence.txt is the reviewer's index. It resolves a named symbol
list against the checkout and says plainly when a requested name does not exist.
It also re-runs the probe under every contradiction, so a claim and its grep
cannot drift apart.
One file is not verbatim. docker-compose.yml carries an uptime-kuma API key,
so a redacted copy ships in its place with that one value replaced. The script
diffs the two and aborts if anything else changed.
The scan at the end refuses to build on a credential-shaped hit rather than
printing a warning. Both of its first two versions were wrong in instructive
ways. The name filter deleted internal/router/singletoken.go for matching
*token*. The value scan flagged docker volume lines that name where a secret
would live and contain none.
The authorization function as implemented
The reconstruction, at the one decision point that gates an act
(internal/tool/tool.go:156):
permit(tool, confirmed) =
row.status == "enabled" tool.go:164
AND tier != irreversible risk.go:74 VoiceMayRun:false
AND (tier == safe OR confirmed) risk.go:72,76
tier is RiskOf(row). The reach is not an input: Executor.Exec takes
(ctx, name, args, confirmed) and no surface.
confirmed is unproven at this boundary too. The invariant that a confirmation
binds one capability, one target and an expiry lives in pendingAct and
resolveConfirm. Exec trusts the boolean.
The expression covers two of the three act paths. Hexis reuses it deliberately
(cmd/mavend/ecosystem_acts.go:768). The Praxis lifecycle path has no tier and
no confirm turn: praxisItemAction.handle calls straight through at
ecosystem_acts.go:158.
Behind the IPC boundary, auth.Can(method, scope, params) runs with
scope.Surface always SurfaceCoreProcess (internal/auth/enrollment.go:65)
and step-up held as one global timestamp that ignores Scope
(internal/webauthn/session.go:38 and :62).
auth answers who may carry what authority. tool answers what effect a
capability has and what proof it demands. Those are orthogonal, not competing.
The decision combining them does not exist.
Two representations of reach exist and both are ignored. server.go:198 only
defaults an empty p.Surface, so a client-asserted one survives and nothing
reads it. server.go:148 hardcodes Session.Surface. Since req.Surface is
request payload on an unauthenticated wire, it must not become an authorization
input as it stands.