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>
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user