# Visual architecture diffs in pull requests
> Let an agent update an architecture diagram when a PR changes services, dependencies, queues, databases, or API edges - then post the visual diff (added=green, removed=red, modified=amber) directly into the PR comment.
## Metadata
- **Diagram family**: `architecture`
- **Source**: Pull-request diff
- **Workflow types**: scan, update, diff, render, ci-cd
- **Audience**: developer, platform, agent
- **MCP tools**: `dsp_get_scene`, `dsp_apply_ops`, `dsp_diff_scene`, `dsp_render_scene`
- **HTTP endpoints**:
  - `GET /v1/scenes/:id`
  - `POST /v1/scenes/:id/applyOps`
  - `GET /v1/scenes/:id/diff`
  - `POST /v1/scenes/:id/render`
---
---

## What this example shows

The "after" architecture for a PR that introduces a new Payments service plus its connections to Stripe, the orders flow, and PostgreSQL. Scroll to the **Revision diff** panel below to see the captured `before` and `after` diagrams side-by-side along with the structured `dsp_diff_scene` response listing the four added elements (Payments, Stripe, the two new edges) and the modified Orders service.

## When to use it

When a PR meaningfully changes the system's shape - adds a new service, replaces a database, removes a dependency, swaps a vendor - and you want reviewers to see that before they merge. Architecture diagrams are notoriously stale because nobody updates them; this pattern flips it: the architecture diagram updates on every PR, and the diff lands in the PR comment automatically.

## What the agent does

Triggered by a pull-request webhook, the agent fetches the persisted "main" scene representing current production architecture, reads the PR diff to identify added / removed / modified services and dependencies, applies typed operations (`createNode`, `createEdge`, `deleteElement`) to a fresh revision, and calls `dsp_diff_scene` to compare the new revision against main. The result is rendered with diff styling - additions in green, removals in red, modifications in amber - and posted to the PR as a comment.

If the PR is merged, the new revision becomes the canonical "main" scene. If closed without merging, the revision is dropped and main is unchanged. The persisted-scene model means architectural state is durable and auditable across PRs without ever being regenerated from scratch.

## What the output includes

- A "before" + "after" pair of architecture scenes per PR.
- A visual diff highlighting added (green), removed (red), and modified (amber) elements.
- A scene-diff JSON for programmatic consumers (CI gates that block merges introducing forbidden dependencies, for example).
- A PR comment template the bot posts automatically with the rendered diff inline.
- A revision history accessible via `dsp_list_revisions` showing every PR's architectural delta over time.

> **Tip:** The CI/CD recipe panel below contains the complete runnable GitHub Actions workflow that produces the side-by-side diff on every pull request - copy it into `.github/workflows/zindex-pr-arch-diff.yml`, add the listed secrets, and PR comments with before / after diagrams start landing on the next push.
## Agent workflow
Surface architectural impact during PR review by deriving the architecture state from the PR's diff, comparing it to the architecture on main, and posting the visual diff back to the PR - so reviewers see what services and dependencies a PR adds or removes without re-reading the change set.
**Inputs**

- PR diff (list of changed files in the PR)
- Repository checkout at the PR head AND at the merge-base
- Existing Zindex scene id storing the architecture on main (long-lived, never recreated)
- Zindex API key with scene-write scope
**Outputs**

- Two transient revisions - 'before' (= main's revision) and 'after' (= main + PR changes)
- Rendered before-after diagrams as separate SVGs (or a single side-by-side composite)
- Structural diff JSON listing added / removed / changed elements
- PR comment with the diff summary + rendered diagrams, idempotently re-posted as the PR evolves
**Steps**

1. **Fetch the architecture scene on main** - Get the persisted scene representing the architecture on the default branch. Note the current revision number - that's the 'before' baseline.
   _MCP: `dsp_get_scene` · HTTP: `GET /v1/scenes/${SCENE_ID}`_

2. **Scan the repository at the PR head** - Check out the PR branch. Run the same component-detection logic that the living-architecture workflow uses, but against the PR's tree. Emit the (component, edge) tuple that represents what the architecture *would be* if this PR merged.

3. **Compute the operation batch that would update main** - Diff the PR-head scan against the persisted main scene. Emit a hypothetical applyOps batch (createNode for new services, deleteElement for removed ones, createEdge / deleteElement for changed dependencies). Do NOT apply this batch to the production scene - this is a what-if comparison, not a merge.

4. **Apply ops to a transient branch revision** - Apply the computed ops to the persisted scene with a revision message tagged with the PR number - `pr-1234: hypothetical post-merge state`. This creates a new revision; you'll diff against it and then leave it. (Optional: tag the revision with the PR id so you can clean up old branch revisions on PR close.)
   _MCP: `dsp_apply_ops` · HTTP: `POST /v1/scenes/${SCENE_ID}/ops`_

5. **Diff main's revision against the PR's hypothetical revision** - Call dsp_diff_scene with from = main's revision, to = the PR's branch revision. The output describes precisely what the PR adds and removes from the architecture - far more reviewable than re-reading source files.
   _MCP: `dsp_diff_scene` · HTTP: `GET /v1/scenes/${SCENE_ID}/diff?from=${MAIN_REVISION}&to=${PR_REVISION}`_

6. **Render before-and-after diagrams** - Render the scene at main's revision (before) and at the PR's revision (after). Both watermarks include the revision number so the reviewer can verify exactly which state each diagram represents.
   _MCP: `dsp_render_scene` · HTTP: `POST /v1/scenes/${SCENE_ID}/render`_

7. **Post the architectural diff to the PR** - Comment on the PR with both rendered SVGs (or a side-by-side composite) and the structural diff summary. Re-post idempotently as new commits land on the PR - overwrite the previous comment rather than appending. On PR merge, the next scheduled living-architecture run will pick up the merged change and the branch revision is no longer authoritative.

8. **Optional: roll back the branch revision on PR close** - If the PR closes without merging, the branch revision in the persisted scene no longer represents reality. Either delete the revision (if revision deletion is supported in your environment) or rely on the next scheduled main-branch sync to overwrite it. Most teams just leave branch revisions in place - the revision history then doubles as a record of architectural proposals, accepted and rejected.
## Agent prompt

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

```
You are an automated PR-review agent. Your job is to surface the architectural impact of a pull request by deriving what the architecture would look like if the PR merged, comparing that against the architecture on main, and posting the visual diff back to the PR - so a human reviewer can see at a glance which services and dependencies the PR adds, removes, or rewires.

The persisted Zindex scene id is `${SCENE_ID}`; it represents the architecture on the default branch and is updated independently by the living-architecture workflow on every default-branch commit. You will not modify main's revision history. Instead, you will create transient PR-tagged revisions for the diff comparison.

Workflow on every PR (open + every push to the PR branch):

1. Call `dsp_get_scene({ sceneId: "${SCENE_ID}" })`. Read the current revision number - call this `MAIN_REVISION`. This is the 'before' state for the diff.

2. Check out the repository at the PR head. Run the same component-detection logic the living-architecture workflow uses (services from `services/*/Dockerfile`, databases from URL strings, queues from imports, external systems from SDK packages) - but apply it to the PR's tree, not main's. Emit a `(components, edges)` tuple that represents the architecture *as the PR would leave it*.

3. Compute the operation batch that would move the persisted scene from its current state to the PR's hypothetical state. New components → `createNode`; removed components → `deleteElement`; changed labels / endpoints → `updateEdge`. Reuse stable element ids (the directory name / service name) so renames produce `updateNode` rather than delete-and-create.

4. Call `dsp_apply_ops` with that batch. Set `errorPolicy: "allOrNothing"` and use a `revisionMessage` tagged with the PR number - `pr-1234: hypothetical post-merge state`. This produces a new revision in the persisted scene; call this `PR_REVISION`. (Optional: also pass a `tags` field so you can clean up old PR revisions on close, if your environment supports tagged-revision cleanup.)

5. Call `dsp_diff_scene({ from: MAIN_REVISION, to: PR_REVISION })`. The platform returns a structural diff: which elements were added, removed, or changed. This is the canonical answer to 'what does this PR change architecturally?' - and it is precisely what you'll surface in the PR comment.

6. Call `dsp_render_scene` twice - once at `MAIN_REVISION` (the 'before' diagram) and once at `PR_REVISION` (the 'after' diagram). The watermark includes the revision number on both, so reviewers can verify which state each image represents. Render both at `theme: "clean"` and the same dimensions so they compare visually.

7. Post a single PR comment containing: the diff summary (`+1 service, -2 edges, 1 service renamed`), the before SVG, the after SVG, and a short prose description if the diff is non-trivial. Re-post idempotently on every new push to the PR branch - overwrite the previous comment rather than appending - so the reviewer always sees the latest comparison pinned at the top of the conversation.

8. On PR close (merged or rejected), do nothing about `PR_REVISION` - the next scheduled living-architecture run on main will produce a new authoritative revision regardless. The PR-tagged revisions can stay in the history as a record of architectural proposals (some accepted, some not).

Hard rules: never modify the scene under any other revision message - main's history must reflect what actually shipped, not what was proposed. Never collapse the diff into a single 'after' diagram without the 'before' for comparison; the value here is the comparison, not just the rendered output. If the diff is empty (the PR is purely cosmetic / non-architectural), comment with 'No architectural changes detected' so the reviewer knows the workflow ran but had nothing to report.
```
## Validation

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

_Scene validates with no diagnostics._
## Resources
- **Canonical scene**: [/examples/pr-architecture-diff.scene.json](/examples/pr-architecture-diff.scene.json)
- **Operations envelope**: [/examples/pr-architecture-diff.ops.json](/examples/pr-architecture-diff.ops.json)
- **Workflow recipe**: [/examples/pr-architecture-diff.workflow.json](/examples/pr-architecture-diff.workflow.json)
- **Revision diff**: [/examples/pr-architecture-diff.diff.json](/examples/pr-architecture-diff.diff.json)
- **GitHub Actions workflow**: [/examples/pr-architecture-diff.github-actions.yml](/examples/pr-architecture-diff.github-actions.yml)
- **Rendered SVG**: [/examples/pr-architecture-diff.svg](/examples/pr-architecture-diff.svg)
- **Human page**: [/examples/pr-architecture-diff](/examples/pr-architecture-diff)
- **Manifest**: [/examples/index.json](/examples/index.json)
## Related examples

- [/examples/living-architecture-docs](/examples/living-architecture-docs)
- [/examples/api-dependency-map](/examples/api-dependency-map)
- [/examples/er-diagram-from-migrations](/examples/er-diagram-from-migrations)