# Living architecture diagram from a repository scan
> Let an agent scan a repository and keep a service architecture diagram current as the codebase evolves. Frame-grouped services, databases, queues, and external systems with edge labels.
## Metadata
- **Diagram family**: `architecture`
- **Source**: Codebase / configuration files
- **Workflow types**: scan, generate, update, validate, render, ci-cd
- **Audience**: developer, platform, agent
- **MCP tools**: `dsp_get_scene`, `dsp_apply_ops`, `dsp_validate_scene`, `dsp_render_scene`
- **HTTP endpoints**:
  - `GET /v1/scenes/:id`
  - `POST /v1/scenes/:id/applyOps`
  - `POST /v1/scenes/:id/render`
---
---

## What this example shows

A modern SaaS platform rendered as a frame-grouped architecture diagram - clients, edge, API layer, services, async, data plane, observability, and external systems each visually separated with labelled edges showing the dependencies between them. The same diagram an agent maintains for the team rather than a one-shot artwork that drifts the moment a service is added.

## When to use it

When your architecture documentation matters but no one wants to maintain it by hand. The agent reads source code, configuration, and infrastructure-as-code; identifies services, databases, queues, and external systems; and applies typed operations to keep a persisted diagram current. Run it on every push to main, on a nightly schedule, or as a manual refresh - the diagram is always behind by at most one CI run.

## What the agent does

The agent scans the repository and identifies the components: services from `package.json` workspaces, databases from connection strings or ORM configs, queues from worker definitions, external systems from outbound HTTP / SDK imports. It fetches the persisted scene, applies `createNode` for new components, `updateNode` when properties change, `createEdge` for new dependencies, and `deleteElement` for removed components. Then it validates structurally, renders the result, and stores the new revision.

Because Zindex preserves stable element IDs across runs, frame groupings stay intact and small changes produce small visual diffs. New engineers see the live architecture; reviewers see exactly what each PR added or removed.

## What the output includes

- A frame-grouped architecture diagram with seven logical layers (clients, edge, API, services, async, data plane, observability) plus an external-systems sidebar.
- Lucide icons on every component for quick visual recognition.
- Edge labels showing the protocol or relationship (HTTPS, queries, publishes, charges, emails).
- Dashed edges for asynchronous flows; solid for synchronous request/response.
- Frame-level edges (e.g. `services → observability`) to express "the whole layer emits telemetry" without drawing N redundant arrows.
## Agent workflow
Keep a service-architecture diagram of an entire codebase current by scanning the repository on a schedule (or on every default-branch commit), deriving services / databases / queues / external dependencies from configuration files, and patching the persisted Zindex scene incrementally so the diagram never drifts more than a single commit behind reality.
**Inputs**

- Repository checkout (full clone, default branch)
- Convention map: which directory layouts mean 'this is a service' (e.g. services/*/Dockerfile, apps/*/package.json, internal/cmd/*)
- Detector list: regexes / dep-graph patterns that identify databases (postgres URL strings), queues (Kafka topics, SQS queue names), external systems (Stripe SDK imports, Sendgrid API keys)
- Existing Zindex scene id (long-lived; one per repository)
- Zindex API key
**Outputs**

- Updated persisted scene with frames for each tier (clients / edge / API / services / data / async / observability / external) and one node per detected component
- Rendered SVG suitable for embedding in the repository's README or docs site
- Revision history that doubles as an architecture changelog (each revision message describes what shipped that day / week)
- Optional: weekly Slack digest summarising newly-detected components
**Steps**

1. **Fetch the persisted scene** - Architecture scenes are long-lived (revisions stretch across months). Fetch the current state before deriving anything new.
   _MCP: `dsp_get_scene` · HTTP: `GET /v1/scenes/${SCENE_ID}`_

2. **Scan the repository for components** - Walk the directory tree applying the convention map: directories matching `services/*/Dockerfile` become service nodes, `apps/*/package.json` become app nodes. Apply the detector list against every source file: database URL strings → database nodes; queue topic / queue-name imports → queue nodes; SDK package names → external-system nodes. Group everything into the canonical eight-tier frame layout (clients, edge, API, services, data, async, observability, external).

3. **Derive edges from imports and config** - For each component, derive its outbound dependencies. Service A imports service-B's SDK → edge A→B. Service A reads from queue Q → edge A→Q (publishes); service B reads queue Q → edge Q→B (consumes), styled dashed for async. Service A's config contains a database URL → edge A→database. Service A imports Stripe SDK → edge A→stripe (external). Reuse stable element ids derived from directory paths so renames don't churn the graph.

4. **Compute the operation diff** - Compare the parsed (component, edge) tuple against the persisted scene. New components → createNode (placed in the appropriate tier frame via `children` updates on that frame). Removed components → deleteElement. New edges → createEdge. Changed labels → updateEdge.

5. **Apply the operation batch** - One applyOps batch with `errorPolicy: "allOrNothing"`. The `revisionMessage` should describe what shipped: 'add notifications service; consume orders.placed event' is more useful in the revision history than 'sync from main'.
   _MCP: `dsp_apply_ops` · HTTP: `POST /v1/scenes/${SCENE_ID}/ops`_

6. **Validate the architecture scene** - Resolve `LABEL_DUPLICATION_DETECTED` warnings (two services connecting to the same database with the same generic 'queries' label benefit from differentiating). `CANVAS_AUTO_EXTENDED` is informational - the canvas grows as the architecture grows; this is correct.
   _MCP: `dsp_validate_scene` · HTTP: `POST /v1/scenes/validate`_

7. **Render the updated diagram** - Render to SVG. Tier frames stay translucent so the colour of each tier is visible behind the components. Embed the rendered SVG in the repository README via a stable URL (`/examples/living-architecture-docs.svg` or the project's own `/architecture.svg`).
   _MCP: `dsp_render_scene` · HTTP: `POST /v1/scenes/${SCENE_ID}/render`_

8. **Optional: weekly Slack digest** - On weekly cadence, call `dsp_diff_scene` with `from=` the revision recorded a week ago and `to=` current. Post a short Slack message summarising the diff - new services, retired services, new external dependencies. This turns the architecture diagram into an ambient awareness signal rather than a thing engineers only consult when onboarding.
   _MCP: `dsp_diff_scene` · HTTP: `GET /v1/scenes/${SCENE_ID}/diff?from=${WEEK_AGO_REVISION}&to=${NEW_REVISION}`_
## Agent prompt

Drop this into a system prompt for an MCP-connected agent.

```
You are an automated architecture-documentation agent. Your job is to keep an architecture diagram of an entire codebase in sync with the actual code, by scanning the repository on a schedule (or on every default-branch commit) and patching a long-lived persisted Zindex scene with whatever has been added, removed, or rewired since the last run.

The persisted Zindex scene id is `${SCENE_ID}`; it already exists. The scene is long-lived - you will never recreate it. Each run computes the smallest valid set of typed operations that move the scene from its current revision to one that matches the repository's current state. Over months, the revision history doubles as an architecture changelog.

Workflow on every run:

1. Call `dsp_get_scene({ sceneId: "${SCENE_ID}" })`. Read the current revision number and the elements list (you'll need both for the diff and for the post-run validation).

2. Walk the repository, applying the convention map you've been given. The canonical map for typical monorepos: `services/*/Dockerfile` directories are service nodes; `apps/*/package.json` directories are application nodes. The directory name (or `name` field in `package.json` / `go.mod`) becomes the stable element id - never re-derive ids on every run; the long-lived stable id is what makes the revision history meaningful across renames.

3. Apply the detector list against every source file. Database URL strings (`postgres://`, `redis://`) → a database node. Queue topic / queue-name imports → a queue node. Imports of external SDK packages (`stripe`, `@sendgrid/mail`, `@aws-sdk/client-s3`) → an externalSystem node. Each detected component lands in the appropriate tier frame (clients, edge, API, services, data, async, observability, external).

4. Derive edges from imports and config. Service A imports `@acme/sdk-b` → edge A→B. Service A's config references a database URL → edge A→database. Service A reads from queue Q → edge Q→A (consumes), styled dashed; service B publishes to Q → edge B→Q (publishes), also dashed. Reuse stable edge ids built from `e_${from_id}_${to_id}` so re-running doesn't churn edge identities.

5. Diff what you parsed against what's in the scene. New components → `createNode` (and update the appropriate frame's `children` array via `updateNode`). Removed components → `deleteElement`. New edges → `createEdge`. Changed edge labels → `updateEdge`. Renamed components → `updateNode` (do not delete-and-create, or you'll lose the FK / containment relationships).

6. Call `dsp_apply_ops` with one batch. `errorPolicy: "allOrNothing"`. The `revisionMessage` should describe what shipped that run - 'add notifications service; consume orders.placed event' is far more useful long-term than 'sync from main'.

7. Call `dsp_validate_scene`. Resolve `LABEL_DUPLICATION_DETECTED` (e.g. two services with a generic 'queries' label to the same database - differentiate or anchor on a column). `CANVAS_AUTO_EXTENDED` and `EDGE_LABEL_SUPPRESSED_REDUNDANT` are informational; ignore.

8. Call `dsp_render_scene({ format: "svg", theme: "clean" })`. Embed the rendered SVG in the repository's README via a stable URL - that's how the diagram becomes ambient documentation.

9. On a weekly cadence, call `dsp_diff_scene({ from: WEEK_AGO_REVISION, to: NEW_REVISION })` and post a short Slack message to the platform / engineering channel summarising new services, retired services, and new external dependencies. The diagram becomes an ambient awareness signal rather than a thing engineers only consult during onboarding.

Hard rules: never hand-edit the rendered SVG. Never regenerate the scene from scratch - always patch with stable ids. Never collapse a multi-tier topology into a single rank - the eight-tier frame layout is what makes the diagram readable as the system grows; preserve frame containment with each new node.
```
## Validation

Captured `POST /v1/scenes/validate` response: **valid** (2 diagnostics)

- `warning` `EDGE_ENDPOINT_KIND_INVALID` (/elements/e_services_obs/from): Edge 'e_services_obs' connects from 'frame' element 'services' which is not a connectable kind.
- `warning` `EDGE_ENDPOINT_KIND_INVALID` (/elements/e_services_obs/to): Edge 'e_services_obs' connects to 'frame' element 'observability' which is not a connectable kind.
## Resources
- **Canonical scene**: [/examples/living-architecture-docs.scene.json](/examples/living-architecture-docs.scene.json)
- **Operations envelope**: [/examples/living-architecture-docs.ops.json](/examples/living-architecture-docs.ops.json)
- **Workflow recipe**: [/examples/living-architecture-docs.workflow.json](/examples/living-architecture-docs.workflow.json)
- **Revision diff**: [/examples/living-architecture-docs.diff.json](/examples/living-architecture-docs.diff.json)
- **GitHub Actions workflow**: [/examples/living-architecture-docs.github-actions.yml](/examples/living-architecture-docs.github-actions.yml)
- **Rendered SVG**: [/examples/living-architecture-docs.svg](/examples/living-architecture-docs.svg)
- **Human page**: [/examples/living-architecture-docs](/examples/living-architecture-docs)
- **Manifest**: [/examples/index.json](/examples/index.json)
## Related examples

- [/examples/pr-architecture-diff](/examples/pr-architecture-diff)
- [/examples/api-dependency-map](/examples/api-dependency-map)
- [/examples/multi-agent-workflow](/examples/multi-agent-workflow)
- [/examples/request-flow-from-handler](/examples/request-flow-from-handler)