# Explore the architecture graph

Inspect what an authoring composition declares: business modules, contracts, event channels, consumers, projections and infrastructure requirements. This is a declared map, not production traffic or active RPC routing.

> [!NOTE]
>
> **Package availability**
>
> Available in `sandloom>=0.7.0a7,<0.8`.

```
uv run sandloom explore                  # generate, save HTML, open the browser
uv run sandloom graph                    # compact text project map
uv run sandloom graph notes              # module and immediate neighbors
uv run sandloom graph impact stats.quota.v1
uv run sandloom graph diff main          # compare the committed model on main
```

`explore` explicitly runs `sandloom generate`: it loads the application in its own process and writes managed artifacts after compatibility checks. It then saves `.sandloom/cache/explore.html` and opens its local file URL. The HTML embeds its renderer and data; no web server or external service is needed. Generation failure stops the command before opening a stale page. The file URL is always printed; a headless environment can use `--no-open`.

```
uv run sandloom explore notes --hops 0
uv run sandloom explore --no-generate    # inspect the saved model, without app loading
uv run sandloom explore --no-open        # generate and save only
uv run sandloom graph --format json      # structured fields and stable IDs
uv run sandloom graph --format text      # readable output even through a pipe
uv run sandloom graph --format html > architecture.html
```

`graph` reads the saved model and never opens a browser or regenerates files. It defaults to text in terminals, pipes and redirects; request JSON explicitly with `--format json`. Without a module selector, each module gets its recorded package purpose and a compact facts line: routes, tables, provided contracts, published event count, called modules and listened-to producer modules. Infrastructure requirements remain in detailed module views. `generate` records the first line of each module package's docstring, including installed batteries without owned source. The cold reader never imports those packages.

Every action uses one positional grammar: `graph [MODULE]`, `graph impact ITEM`, `graph diff REV_OR_FILE`, `graph sequence [ID]` and `graph state ID`. Use `module:NAME` for a module named `impact`, `diff`, `sequence` or `state`; `modules` is an ordinary module name. Import-path module selectors also work. JSON includes the module list with stable IDs and recorded summaries.

HTML provides search results across all layers (opening an entity also selects its layer), semantic layers, directed neighborhoods, zoom/pan/fit, a persistent inspector and equivalent entity/relationship lists. Disconnected modules are placed separately; arrows route around unrelated blocks. Large graphs open at readable scale inside a scrollable canvas. Fit-all is explicit and can require zooming back in to read labels. Module purposes appear on blocks and in the inspector; route counts open recorded routes and their framework lifecycles.

## What an arrow means

| Raw relationship | Direction |
| --- | --- |
| Provided / required contract | Provider / caller module → versioned contract |
| Produced event | Producer module → event channel |
| Consumed event | Event channel → consumer |
| Consumer ownership | Owning module → consumer |
| Projection input / ownership | Channel → projection; module → projection |
| Capability requirement | Module → capability/instance/version |
| Ordering | Dependent module → prerequisite module |

The overview derives caller → provider and producer → consumer-owner relations, retaining the raw contract/channel witness edges. A hop crosses one such module relation. `--direction incoming|outgoing|both` controls expansion (default: both). Shared capabilities or DTO imports do not invent cross-module calls. The HTML module layer draws one labelled line per direction of a module pair; the list below it keeps every declaration. In the HTML interface layer, a module filter shows that module's contracts and events with the modules on their far side, with consumers and projections collapsed into their owner. In the infrastructure layer it shows only that owner's declarations; direction and distance controls apply to the module-relationship layer. Missing providers and optional contracts remain explicit. Available authoring metadata is not evidence of deployed services.

Required contracts, optional contracts, ordering and event-impact cycles are reported separately. An event feedback loop may be intentional; a declared cycle is not proof of deadlock. The graph never claims an event causes an RPC merely because both touch one module.

## Processes: sequence and state

```
uv run sandloom graph sequence
uv run sandloom graph sequence SAGA_OR_ROUTE_ID
uv run sandloom graph sequence SAGA_OR_ROUTE_ID --format mermaid
uv run sandloom graph state SAGA_ID --format html > states.html
```

Text catalogs show process names, owning modules and saga versions. Select a unique name, quoting names containing spaces, for example `uv run sandloom graph sequence "POST /api/v1/enrollments"`. For ambiguous names, use `graph sequence --format json` to obtain an exact stable ID. Saga IDs include their registered version so parallel definitions remain distinct. A saga sequence shows declared forward steps, the pivot and unit boundaries. Compensation is a separate alternative: only completed compensatable steps are undone, in reverse order, when the runtime enters compensation before the pivot. The state view shows conditional framework transitions, including parking; it is not an attempt history. Callbacks and their argument functions are never executed by inspection.

Route sequences use resolved generated route policy, not a guessed call graph. They show session checks, idempotency, conditional unit/commit phases or the operation-intent release rule. Handler internals stay opaque. A declaration does not establish that an event was published, a request succeeded, a consumer ran, or multiple module commits were atomic. Kernel routes without a module policy snapshot are not added as invented business-process declarations.

Scoped HTML contains only the selected modules and their process declarations; a specific process query includes only that process. Public interface references can still identify related providers. Freshness covers the exported module scope.

The HTML **Processes** tab offers forward sequence, compensation alternative and saga states, each with an equivalent step/transition list. The inspector explains the selected step and source when known. A saved URL fragment restores filters, representation and typed selection (including Changes/Impact); the same local export must be available to its recipient. The mobile details button leads to the same inspector.

## Potential impact and architecture changes

```
uv run sandloom graph impact stats.quota.v1
cp .sandloom/inspection.json architecture-before.json
# Change declarations, then explicitly regenerate.
uv run sandloom generate
uv run sandloom graph diff architecture-before.json
uv run sandloom graph diff architecture-before.json --format html > changes.html
```

Replace the item ID with one from your graph. Impact preserves the direct interface/owner witnesses, then follows potential module reachability. Transitive results can involve other interfaces: this is an overapproximation of declared impact, not complete call-site or test coverage. Optional dependencies remain optional. Suggested module test commands never replace normal quality gates.

`graph diff REV` reads this project's `.sandloom/inspection.json` from a local Git revision, including the project prefix in a monorepo. The response records the resolved commit ID. Commit the generated index on the baseline branch first; a revision without it produces an explicit error, with no inferred replacement. No fetch, checkout, old-code execution or historical source resolution occurs. Git is required only for revision baselines. `graph diff old.json` reads a project-relative saved index without Git. Existing files take precedence over revision names; `.json` names and explicit `./` paths select files even when missing. Both forms treat the baseline as JSON data. It lists additions, removals and changes to declarations/interfaces; implementation bodies are not compared. Contract compatibility uses the framework's rolling-upgrade rules; event changes use its same-version schema rules. Missing interface evidence is `unknown`, never automatically compatible. Other changes remain `not_assessed`. The comparison basis and counts are included in JSON and the HTML Changes tab.

## Exports and limits

```
uv run sandloom graph module:notes --hops 0
uv run sandloom graph notes --format json --detail full
uv run sandloom graph notes --format mermaid
uv run sandloom graph --require-verified
```

Compact JSON preserves relation types, endpoints, requiredness and witness IDs. Full detail adds provenance, module declarations and process policy; compact topology includes process counts and defers their bodies to the sequence query. `--max-bytes` limits complete JSON output (default 131072 UTF-8 bytes); overflow fails with a remedy to focus the graph or raise the budget. Nodes/edges are never silently truncated. Scoped exports report hidden counts. `--hops` requires a module selector and accepts non-negative integers.

`--detail` and `--max-bytes` are JSON options; presentation formats reject them. Mermaid is interchange output; third-party layout can vary. HTML embeds its data and renderer locally, with no CDN, telemetry or external fonts. Save SVG exports the current overview; Save JSON retains the original export and its evidence. Its JSON download retains the exported scope; viewer filtering does not pretend to change the underlying export inventory.

Freshness, explicit generation, output safety and structured errors follow the [module context guide](<https://sandloom.dev/docs/ai/module-context/index.md>). Cold reads need no app environment, PostgreSQL, Redis or optional PyYAML. For cold `graph` reads, a missing inspection index requires explicit `sandloom generate`; it is never run automatically. `explore` is the separate authoring command that generates by default.
