bae81b66c8
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>
135 lines
6.3 KiB
Markdown
135 lines
6.3 KiB
Markdown
# 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
|
|
|
|
```sh
|
|
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.
|