# architecture.md section reference

Per-section rendering spec for `scripts/render_architecture.py::render`.
One `# Architecture` H1, seven numbered `## N. ...` sections, plus one
conditional "Dependency graph" appendix. Source of truth: the
`metadata_tree.json` schema produced by `scripts/parse_wave.py` at the
end of phase 9.

**Source of truth: the live tree** — field shapes evolve as parse_wave
learns new agent generations. When this doc disagrees with the
`metadata_tree.json` written by `parse_wave.py` for an actual agent on
your machine (under `~/.vibe/data/agentforce-architecture-analyze/<org>/<agent>__<ver>/`),
trust the live tree.

The Mermaid templates live at `assets/mermaid/*.mmd` and are the sole
consumer of the renderer helpers; each `{{PARAM}}` contract is
documented in the template's own header comment.

---

## Per-diagram node caps

Default caps (override via `render(..., max_mermaid_nodes={...})`):

| Diagram kind | Default cap | Over-cap behaviour |
|---|---|---|
| `flowchart` | 200 | summary placeholder + top-5 fan-out + catalog pointer |
| `stateDiagram` | 40 | summary placeholder (no fan-out — states are not nodes) |
| `sequenceDiagram` | 60 | summary placeholder + top-5 fan-out |
| `graph` | 100 | summary placeholder + top-5 fan-out |

Cap is measured in rendered elements (nodes + edges for flowchart/graph,
messages for sequenceDiagram, states + transitions for stateDiagram).
Above cap, the renderer emits a `> [diagram truncated: ...]` blockquote
with the element count, the top-5 nodes by fan-out (when applicable),
and a pointer to the catalog section that enumerates the same data
without compression.

---

## 1. Header + agent overview

**Purpose**: identify the agent and its planner in a single kv table.

**Input fields** (`tree.agent.*`):

- `api_name` — agent developer name
- `version` — `v<N>` label
- `master_label` — human-readable name
- `description` — prose summary
- `agent_type` — `EinsteinAgentKind` / `EinsteinCopilotForSalesforce` / …
- `type` — `ExternalCopilot` / `InternalCopilot` / …
- `agent_template`
- `bot_source`
- `generation` — `classic` / `nga` / `search` / `byop`
- `planner_name`
- `planner_type`
- `bot_id`

Plus `tree._schema_version`.

**Output shape**: `# <H1>` with api_name + version, followed by a 2-col
markdown table.

**Empty/degenerate cases**:

- Missing `api_name` / `version` renders `?` in the H1 (not `None`).
- Any missing field renders `-` in its row — not empty, because GitHub
 table rendering collapses empty cells and the row loses alignment.

---

## 2. Anatomy summary

**Purpose**: one-paragraph executive summary plus health callouts.

**Input fields**:

- `tree._kind_counts` — preferred source for topic / action / flow /
 apex / prompt counts.
- `tree.depth`, `tree.node_count` — size metrics.
- `tree._partial`, `tree._partial_reason` — health flag + prose reason.
- `tree._pending_fetches` — `{"FLOW":[...],"APEX":[...],
 "PROMPT_TEMPLATE":[...],"STANDARD_ACTION":[...]}`. Count determines
 the "pending fetches: N" line in the callout.
- `tree._unresolved` — triggers a second warn callout with the count.
- `tree.agent.planner_name` — missing -> warn callout.

**Output shape**: `## 2. Anatomy summary` + paragraph + zero-to-three
blockquote callouts. Callouts render as `> **Health: PARTIAL.**` /
`> **Health: WARN.**` prefixes with bullet sub-lines.

**Empty/degenerate cases**: if `_kind_counts` is absent, the walker
supplies counts from its own pass over the tree. Zero topics is valid
(SequentialPlannerIntentClassifier).

---

## 3. Action tree

**Purpose**: the full declared action tree as a flowchart with per-topic
subgraphs, plus a deterministic ASCII appendix for diff review.

**Input fields**:

- Walker-derived `topics[]` -> one subgraph per topic.
- Walker-derived `edges[]` -> `parent_api_name --> child_api_name`.
- `node._cycle_back_to` -> dotted back-edge `A -.->|cycle_back_to: X| B`.
- Planner-level actions (top-level `GEN_AI_FUNCTION` with no parent
 topic) -> synthetic `_plannerActions` subgraph.

**Output shape**: ` ```mermaid ` block with `flowchart TB` + subgraphs
+ edges, followed by a `<details>` with ASCII-tree appendix. When
above cap, the mermaid block is replaced with the truncation
placeholder; the ASCII appendix still renders.

**Empty/degenerate cases**:

- No topics, no planner actions -> `%% no topics` + `%% no edges`
 mermaid comments; diagram still parses.
- Cycle annotations render in both the mermaid (dotted edge) and the
 ASCII appendix (`[cycle]` marker on the node line).

---

## 4. Topic anatomy

**Purpose**: per-topic detail dump. One H3 per topic with a bullet list
of actions.

**Input fields**:

- Walker-derived `topics[]` with `api_name`, `label`, `actions`.
- `topic.raw.master_label` (preferred) or api_name for the label.

**Output shape**: `## 4. Topic anatomy` + one `### \`<api_name>\``
block per topic + kv list (`- Label:`, `- Action count:`, `- Actions:`
with sub-bullets).

**Empty/degenerate cases**: 0 topics -> `_No topics defined (planner
exposes actions directly)._` italic fallback. This is the expected
case for `SequentialPlannerIntentClassifier`.

---

## 5. Action catalog

**Purpose**: flat markdown table of every declared action.

**Input fields**:

- Walker-derived `actions[]` with `api_name`, `topic`, `raw.unwraps_to`.

**Output shape**: 3-col table (`Action | Topic | Unwraps to`). Unwraps
column renders as `KIND \`api_name\`` when `unwraps_to` is present, `-`
otherwise. Planner-level actions show `(plannerAction)` in the topic
column.

**Empty/degenerate cases**: no actions -> `_No actions declared._`
italic fallback.

---

## 6. Data flow / context propagation

**Purpose**: show which slot values propagate from user utterance
through the planner into each action.

**Input fields**:

- Walker-derived `topics[]` and `actions[]`.
- `action.raw.planner_attr.variable_name` + `planner_attr.data_type` —
 labels the edge `A -->|var: Type| B`. When absent, the edge is bare.

**Output shape**: `flowchart LR` via `load_mermaid("data_flow", ...)`.
Skeleton: `User` -> `Planner` -> each `Topic` -> each `Action` under
that topic.

**Empty/degenerate cases**: no planner_attr metadata -> every edge is
bare. 0 topics -> only `User --> Planner` renders (no action-level
edges). Above cap -> truncation placeholder.

---

## 7. Flow / Apex / Prompt catalogs

**Purpose**: per-artifact detail section for every backing flow, apex
class, and prompt template referenced by any action.

**Input fields**:

- Walker-derived `flows{}`, `apex{}`, `prompts{}`, keyed on api_name.
- `node.signature` (or `_signature` fallback) — signature block.

**Output shape**: `### Flows` / `### Apex classes` / `### Prompt
templates` H3 buckets, each containing `#### \`<api_name>\`` + a fenced
signature block or `_Signature not captured._` fallback. Prompt
templates render as a flat bullet list (signature capture is
per-template prose, not a flow/apex shape).

**Empty/degenerate cases**: no backing artifacts in the tree ->
`_No backing artifacts in tree._` italic fallback.

---

## 8. Unresolved refs + artifact pointers

**Purpose**: surface every `_unresolved[]` entry and point at the
sidecar files the reader can open.

**Input fields**:

- `tree._unresolved[]` — list of `{kind, api_name, reason}` records.

**Output shape**: Either a 3-col `Kind | Api name | Reason` table or
an `_No unresolved references._` italic fallback. Followed by an
`### Artifact pointers` sub-heading with a bullet list of relative
paths (tree JSON, manifest, summary).

**Empty/degenerate cases**: always renders — the artifact-pointer list
is unconditional.

---

## Conditional: Dependency graph

**Purpose**: rendered only when `tree._unresolved[]` is non-empty OR
the tree contains cycle annotations. Shows cross-artifact dependencies
plus the unresolved nodes as dashed-outline markers.

**Input fields**:

- Walker-derived `edges[]`.
- `tree._unresolved[]` — each becomes a `:::unresolved` styled node.
- Walker-derived `cycles[]` — each becomes a dotted back-edge.

**Output shape**: `graph LR` via `load_mermaid("dependency_graph",
...)`. Includes a `classDef unresolved` class marker at the bottom of
the template so unresolved nodes render with `stroke-dasharray`.

**Empty/degenerate cases**: not rendered at all when both conditions
are false. Above the `graph` cap -> truncation placeholder replaces
the mermaid block.
