{
  "schemaVersion": "1.0",
  "id": "living-architecture-docs",
  "title": "Living architecture diagram from a repository scan",
  "slug": "living-architecture-docs",
  "diagramFamily": "architecture",
  "goal": "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"
  ],
  "mcpTools": [
    "dsp_get_scene",
    "dsp_apply_ops",
    "dsp_validate_scene",
    "dsp_render_scene"
  ],
  "httpEndpoints": [
    "GET /v1/scenes/:id",
    "POST /v1/scenes/:id/applyOps",
    "POST /v1/scenes/:id/render"
  ],
  "steps": [
    {
      "id": "fetch_scene",
      "label": "Fetch the persisted scene",
      "description": "Architecture scenes are long-lived (revisions stretch across months). Fetch the current state before deriving anything new.",
      "mcp": "dsp_get_scene",
      "http": {
        "method": "GET",
        "path": "/v1/scenes/${SCENE_ID}"
      }
    },
    {
      "id": "scan_repo",
      "label": "Scan the repository for components",
      "description": "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).",
      "mcp": null,
      "http": null
    },
    {
      "id": "derive_edges",
      "label": "Derive edges from imports and config",
      "description": "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.",
      "mcp": null,
      "http": null
    },
    {
      "id": "diff_against_scene",
      "label": "Compute the operation diff",
      "description": "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.",
      "mcp": null,
      "http": null
    },
    {
      "id": "apply_ops",
      "label": "Apply the operation batch",
      "description": "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": {
        "method": "POST",
        "path": "/v1/scenes/${SCENE_ID}/ops"
      }
    },
    {
      "id": "validate",
      "label": "Validate the architecture scene",
      "description": "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": {
        "method": "POST",
        "path": "/v1/scenes/validate"
      }
    },
    {
      "id": "render",
      "label": "Render the updated diagram",
      "description": "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": {
        "method": "POST",
        "path": "/v1/scenes/${SCENE_ID}/render"
      }
    },
    {
      "id": "weekly_digest",
      "label": "Optional: weekly Slack digest",
      "description": "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": {
        "method": "GET",
        "path": "/v1/scenes/${SCENE_ID}/diff?from=${WEEK_AGO_REVISION}&to=${NEW_REVISION}"
      }
    }
  ]
}