# Story 2.8: Flowchart Diagram Type Renderer

Status: done

<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->

## Story

As a user,
I want to create and view flowcharts with decision nodes, process steps, and terminals,
so that I can model logic flows and decision processes.

## Acceptance Criteria

1. **Given** I open or create a flowchart, **When** the canvas renders, **Then** I see standard flowchart shapes: process (rectangle), decision (diamond), start/end terminals (rounded rectangle/stadium), I/O (parallelogram), subprocess (double-bordered rectangle), **And** each node displays its label text.

2. **Given** a flowchart has decision nodes, **When** rendered, **Then** decision diamonds show the question/condition text, **And** outgoing edges are labeled with the decision outcomes (Yes/No, True/False, or custom).

3. **Given** auto-layout runs on a flowchart, **When** the diagram has branching paths, **Then** ELK.js produces a clean top-down flow with branches clearly separated, **And** merge points are visually clear.

## Tasks / Subtasks

- [x] Task 1: Create flowchart constants and type registry (AC: #1, #2)
  - [x] 1.1: Create `apps/web/src/modules/diagram/types/flowchart/constants.ts` with `FLOW_SIZES`, `resolveFlowchartNodeType()`, `resolveFlowchartEdgeType()`, `getFlowNodeSize()`
  - [x] 1.2: Define sizes for 5 node subtypes: process (160x60), decision (140x80), terminal (140x50), io (160x60), subprocess (160x60)

- [x] Task 2: Create FlowProcessNode custom @xyflow/react component (AC: #1)
  - [x] 2.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowProcessNode.tsx` — standard rectangle with label text, icon support, rose accent border

- [x] Task 3: Create FlowDecisionNode custom @xyflow/react component (AC: #1, #2)
  - [x] 3.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowDecisionNode.tsx` — diamond shape via CSS `transform: rotate(45deg)` on a wrapper with counter-rotated content, condition/question text centered

- [x] Task 4: Create FlowTerminalNode custom @xyflow/react component (AC: #1)
  - [x] 4.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowTerminalNode.tsx` — rounded rectangle/stadium shape (`border-radius: 999px` sides), label text, used for both start and end nodes. `data.tag` distinguishes "start" vs "end" for optional styling (e.g., start=green tint, end=red tint, or both use accent)

- [x] Task 5: Create FlowIoNode custom @xyflow/react component (AC: #1)
  - [x] 5.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowIoNode.tsx` — parallelogram shape via CSS `transform: skewX(-10deg)` on wrapper with counter-skewed content, label text

- [x] Task 6: Create FlowSubprocessNode custom @xyflow/react component (AC: #1)
  - [x] 6.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowSubprocessNode.tsx` — double-bordered rectangle (outer border + inner inset border), label text

- [x] Task 7: Create FlowEdge custom @xyflow/react component (AC: #2, #3)
  - [x] 7.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowEdge.tsx` — solid arrow using `getSmoothStepPath` (orthogonal routing), label from `edge.label` shown at midpoint (for decision outcomes Yes/No), rose accent color, markerEnd `url(#flow-arrow)`

- [x] Task 8: Update graph converter for flowchart type resolution (AC: #1, #2)
  - [x] 8.1: Import `resolveFlowchartNodeType`, `resolveFlowchartEdgeType` from `../types/flowchart/constants` in `graph-converter.ts`
  - [x] 8.2: Replace `// Future: flowchart` comment (line 50) with: `if (diagramType === "flowchart" || nodeType.startsWith("flow:")) { return resolveFlowchartNodeType(nodeType); }`
  - [x] 8.3: Add flowchart diagramType handling in `resolveFlowEdgeType()` before the default return

- [x] Task 9: Register flowchart types in DiagramCanvas (AC: #1, #2)
  - [x] 9.1: Import FlowProcessNode, FlowDecisionNode, FlowTerminalNode, FlowIoNode, FlowSubprocessNode, FlowEdge in `DiagramCanvas.tsx`
  - [x] 9.2: Add flowchart entries to `nodeTypes` object: `flowProcess`, `flowDecision`, `flowTerminal`, `flowIo`, `flowSubprocess` (OUTSIDE component)
  - [x] 9.3: Add flowchart entry to `edgeTypes` object: `flowEdge` (OUTSIDE component)
  - [x] 9.4: Add flowchart arrow marker to `MarkerDefs`: `#flow-arrow` with rose accent color

- [x] Task 10: Integrate flowchart node sizing in ELK layout (AC: #3)
  - [x] 10.1: Import `getFlowNodeSize` from `../types/flowchart/constants` in `elk-layout.ts`
  - [x] 10.2: Add flowchart sizing to the width/height computation chain in `buildElkGraph()` — after `seqSize` check, add `flowSize` check

- [x] Task 11: Add flowchart CSS styles (AC: #1, #2)
  - [x] 11.1: Add `.flow-process`, `.flow-decision`, `.flow-decision-diamond`, `.flow-terminal`, `.flow-io`, `.flow-io-skew`, `.flow-subprocess`, `.flow-subprocess-inner` styles to `globals.css`
  - [x] 11.2: Use `--diagram-flowchart` rose accent color for all flowchart elements (already defined at line 29)

- [x] Task 12: Tests (AC: all)
  - [x] 12.1: Unit tests for flowchart constants — `resolveFlowchartNodeType` for all 5 subtypes (prefixed and bare), `resolveFlowchartEdgeType`, `getFlowNodeSize`
  - [x] 12.2: Unit tests for graph converter flowchart type mapping — node types correctly resolved for `diagramType === "flowchart"`, edge types resolved, flat layout (no containers)
  - [x] 12.3: Update "all 6 diagram types" test (line 150 of graph-converter.test.ts): change assertion from `"default"` to `"flowProcess"` for `flow:process` node
  - [x] 12.4: Unit tests for ELK layout flowchart node sizing — `buildElkGraph` produces correct dimensions for flowchart nodes
  - [x] 12.5: All tests pass — no regressions

## Dev Notes

### Overview — What This Story Builds

This story adds the Flowchart diagram type renderer: the last of the 6 core diagram types. Flowcharts use standard ISO 5807-inspired shapes (process, decision, terminal, I/O, subprocess) with the standard ELK layered algorithm for top-down flow layout.

**This story builds:**
- 5 custom flowchart node components (FlowProcessNode, FlowDecisionNode, FlowTerminalNode, FlowIoNode, FlowSubprocessNode)
- 1 custom flowchart edge component (FlowEdge) with label support for decision outcomes
- Flowchart constants and type registry (5 node subtypes + 1 edge type)
- Graph converter flowchart type resolution
- ELK layout flowchart node sizing
- Flowchart CSS styles with rose `--diagram-flowchart` accent
- Flowchart arrow marker in MarkerDefs

**This story does NOT implement:**
- Other diagram types (all 6 complete after this)
- Smart Inspector for flowchart field editing (future epic)
- Manual node repositioning (Story 2.9)
- Liveblocks/CRDT integration (Epic 4)
- AI-triggered mutations (Epic 3)
- Nested diagrams (Epic 7)

### Architecture Compliance

**MANDATORY patterns from Architecture Decision Document:**

1. **Unified Graph Data Model (Decision 1):** Flowchart nodes use type-prefixed `type` field (`flow:process`, `flow:decision`, `flow:terminal`, `flow:io`, `flow:subprocess`). Existing DiagramNode fields are semantically reused: `label` = step description/question text, `tag` = category (e.g., "start"/"end" for terminals), `icon` = optional step emoji, `color` = custom accent override. Edge types use a single value: `sequence` (or default). NO new fields on DiagramNode or DiagramEdge needed.

2. **Component Structure:** Feature code in `~/modules/diagram/types/flowchart/` — follows the BPMN (`types/bpmn/`), E-R (`types/er/`), Org Chart (`types/orgchart/`), Architecture (`types/architecture/`), Sequence (`types/sequence/`) pattern.

3. **@xyflow/react Custom Nodes:** All custom node components use `NodeProps` typing. The `nodeTypes` and `edgeTypes` objects MUST be defined OUTSIDE the component (performance critical — established in Stories 2.1-2.7).

4. **Standard ELK Layout:** Flowchart uses the standard ELK layered algorithm — no custom layout needed (unlike sequence diagrams). `buildElkGraph()` needs flowchart node sizing via `getFlowNodeSize()`. Default direction: `DOWN` for top-down flow.

5. **Lean JSON Data Model:** No x/y positions stored for flowchart nodes. All positioning computed by ELK at render time.

6. **Type Prefixing Convention (Enforcement Rule #5):** `flow:` prefix on DiagramNode.type. Edge type is bare lowercase: `sequence` (or undefined for default).

### Flowchart Diagram Data Model — Field Mapping

The existing DiagramNode and DiagramEdge fields satisfy flowchart needs:

```typescript
// flow:process node
{
  id: "step1",
  type: "flow:process",
  label: "Process Payment",          // Step description
  icon: "💳",                        // Optional step icon
  color: "#e11d48",                  // Custom accent (optional)
}

// flow:decision node
{
  id: "check1",
  type: "flow:decision",
  label: "Payment Valid?",           // Question/condition text
  icon: "❓",                        // Optional icon
}

// flow:terminal node (start)
{
  id: "start",
  type: "flow:terminal",
  label: "Start",
  tag: "start",                      // Distinguishes start vs end
}

// flow:terminal node (end)
{
  id: "end",
  type: "flow:terminal",
  label: "End",
  tag: "end",                        // Distinguishes start vs end
}

// flow:io node
{
  id: "input1",
  type: "flow:io",
  label: "Read User Input",
}

// flow:subprocess node
{
  id: "sub1",
  type: "flow:subprocess",
  label: "Validate Credentials",
}
```

**Edge model:**
```typescript
// Standard flow edge
{
  id: "e1",
  from: "start",
  to: "step1",
  // No label needed for sequential flow
}

// Decision outcome edge
{
  id: "e2",
  from: "check1",
  to: "step2",
  label: "Yes",                      // Decision outcome label
}

// Decision outcome edge (alternative path)
{
  id: "e3",
  from: "check1",
  to: "error1",
  label: "No",                       // Alternative outcome
}
```

### Flowchart Node Types — Size Constants

```typescript
export const FLOW_SIZES = {
  process:    { w: 160, h: 60 },     // Standard rectangle
  decision:   { w: 140, h: 80 },     // Diamond (wider for rotation)
  terminal:   { w: 140, h: 50 },     // Rounded rectangle / stadium
  io:         { w: 160, h: 60 },     // Parallelogram
  subprocess: { w: 160, h: 60 },     // Double-bordered rectangle
} as const;
```

### Flowchart Node Type → @xyflow/react Type Mapping

| DiagramNode.type | @xyflow Node type | Component | Visual |
|---|---|---|---|
| `flow:process` | `flowProcess` | `FlowProcessNode` | Rectangle with label |
| `flow:decision` | `flowDecision` | `FlowDecisionNode` | Diamond with condition text |
| `flow:terminal` | `flowTerminal` | `FlowTerminalNode` | Rounded rectangle / stadium |
| `flow:io` | `flowIo` | `FlowIoNode` | Parallelogram |
| `flow:subprocess` | `flowSubprocess` | `FlowSubprocessNode` | Double-bordered rectangle |

**Type resolution:**
```typescript
export function resolveFlowchartNodeType(type: string): string {
  const bare = type.startsWith("flow:") ? type.slice(5) : type;
  switch (bare) {
    case "process":
      return "flowProcess";
    case "decision":
      return "flowDecision";
    case "terminal":
      return "flowTerminal";
    case "io":
      return "flowIo";
    case "subprocess":
      return "flowSubprocess";
    default:
      return "flowProcess"; // Default to process
  }
}

export function resolveFlowchartEdgeType(_type: string | undefined): string {
  return "flowEdge"; // Single edge type for flowcharts
}

export function getFlowNodeSize(
  flowType: string | undefined,
): { w: number; h: number } | null {
  switch (flowType) {
    case "flowProcess":
      return FLOW_SIZES.process;
    case "flowDecision":
      return FLOW_SIZES.decision;
    case "flowTerminal":
      return FLOW_SIZES.terminal;
    case "flowIo":
      return FLOW_SIZES.io;
    case "flowSubprocess":
      return FLOW_SIZES.subprocess;
    default:
      return null; // Not a flowchart node
  }
}
```

### Flowchart Edge Type

| DiagramEdge.type | @xyflow Edge type | Visual |
|---|---|---|
| `sequence` (or default/undefined) | `flowEdge` | Solid orthogonal path with filled arrowhead, optional label at midpoint |

Only one edge type needed. Decision outcomes use the `label` field on the edge (e.g., "Yes", "No", "True", "False").

### Standard ELK Layout — Flowchart Uses the Default Path

**Flowchart diagrams USE the standard ELK layered algorithm.** No custom layout function needed. The `computeLayout()` function in `elk-layout.ts` routes through the default path — the only change needed is adding `getFlowNodeSize()` to the `buildElkGraph()` sizing chain.

**ELK settings for flowcharts:**
- Algorithm: `layered` (default)
- Direction: `DOWN` (top-down flow — best for flowcharts)
- Edge routing: `ORTHOGONAL` (right-angle connectors — standard for flowcharts)
- Crossing minimization: `LAYER_SWEEP` (default)
- Node placement: `BRANDES_KOEPF` (default)

**Integration in `buildElkGraph()` — sizing chain update:**
```typescript
// After existing seqSize check:
const flowSize = getFlowNodeSize(node.type);
const height =
  isErEntity && data.columns
    ? getErEntityHeight(data.columns)
    : isOcPerson
      ? OC_SIZES.person.h
      : archSize
        ? archSize.h
        : seqSize
          ? seqSize.h
          : flowSize
            ? flowSize.h
            : (node.measured?.height ?? DEFAULT_NODE_HEIGHT);
const width = archSize
  ? (data.w ?? archSize.w)
  : seqSize
    ? (data.w ?? seqSize.w)
    : flowSize
      ? (data.w ?? flowSize.w)
      : isOcPerson
        ? (data.w ?? OC_SIZES.person.w)
        : (data.w ?? node.measured?.width ?? DEFAULT_NODE_WIDTH);
```

### Node Component Patterns

All 5 node components follow the established pattern:

```typescript
import { Handle, Position } from "@xyflow/react";
import type { NodeProps } from "@xyflow/react";
import type { DiagramNode } from "../graph";
import { HIDDEN_HANDLE } from "../architecture/constants";

export function FlowProcessNode({ data }: NodeProps) {
  const d = data as unknown as DiagramNode & { label: string };
  const icon = d.icon || "⚙️";

  return (
    <div className="flow-process">
      <span className="flow-node-icon">{icon}</span>
      <span className="flow-node-label">{d.label}</span>
      <Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
      <Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
    </div>
  );
}
```

**Decision node — Diamond shape via CSS:**
```typescript
export function FlowDecisionNode({ data }: NodeProps) {
  const d = data as unknown as DiagramNode & { label: string };

  return (
    <div className="flow-decision">
      <div className="flow-decision-diamond">
        <span className="flow-decision-label">{d.label}</span>
      </div>
      <Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
      <Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
    </div>
  );
}
```

**Terminal node — Stadium/pill shape:**
```typescript
export function FlowTerminalNode({ data }: NodeProps) {
  const d = data as unknown as DiagramNode & { label: string };
  const isStart = d.tag === "start";
  const isEnd = d.tag === "end";

  return (
    <div className={`flow-terminal ${isStart ? "flow-terminal-start" : ""} ${isEnd ? "flow-terminal-end" : ""}`}>
      <span className="flow-node-label">{d.label}</span>
      <Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
      <Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
    </div>
  );
}
```

**I/O node — Parallelogram via CSS skew:**
```typescript
export function FlowIoNode({ data }: NodeProps) {
  const d = data as unknown as DiagramNode & { label: string };

  return (
    <div className="flow-io">
      <div className="flow-io-skew">
        <span className="flow-node-label">{d.label}</span>
      </div>
      <Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
      <Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
    </div>
  );
}
```

**Subprocess node — Double-bordered rectangle:**
```typescript
export function FlowSubprocessNode({ data }: NodeProps) {
  const d = data as unknown as DiagramNode & { label: string };

  return (
    <div className="flow-subprocess">
      <div className="flow-subprocess-inner">
        <span className="flow-node-label">{d.label}</span>
      </div>
      <Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
      <Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
      <Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
    </div>
  );
}
```

**Reuses `HIDDEN_HANDLE`** from `architecture/constants.ts` (established in Story 2.6).

### Edge Component — Standard Orthogonal with Label

```typescript
import { BaseEdge, getSmoothStepPath, EdgeLabelRenderer } from "@xyflow/react";
import type { EdgeProps } from "@xyflow/react";

export function FlowEdge(props: EdgeProps) {
  const [edgePath, labelX, labelY] = getSmoothStepPath({
    sourceX: props.sourceX,
    sourceY: props.sourceY,
    targetX: props.targetX,
    targetY: props.targetY,
    sourcePosition: props.sourcePosition,
    targetPosition: props.targetPosition,
  });

  return (
    <>
      <BaseEdge
        id={props.id}
        path={edgePath}
        style={{ stroke: "var(--diagram-flowchart)", strokeWidth: 1.5 }}
        markerEnd="url(#flow-arrow)"
      />
      {props.label && (
        <EdgeLabelRenderer>
          <div
            className="flow-edge-label"
            style={{
              transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
              position: "absolute",
            }}
          >
            {props.label}
          </div>
        </EdgeLabelRenderer>
      )}
    </>
  );
}
```

Uses `getSmoothStepPath` for orthogonal routing (right-angle connectors). `EdgeLabelRenderer` for HTML label at midpoint — critical for decision outcome labels (Yes/No).

### Graph Converter Updates

Replace the `// Future: flowchart` comment with real dispatch:

```typescript
// In resolveFlowNodeType():
if (diagramType === "flowchart" || nodeType.startsWith("flow:")) {
  return resolveFlowchartNodeType(nodeType);
}
// Remove: // Future: flowchart
return "default"; // Truly unknown types

// In resolveFlowEdgeType():
if (diagramType === "flowchart") {
  return resolveFlowchartEdgeType(edgeType);
}
return "default";
```

**Import from flowchart/constants.ts** — same pattern as BPMN, E-R, Org Chart, Architecture, Sequence.

**graphToFlow for flowchart:** Uses the standard default path (flat node mapping). No container nodes. No compound layout.

**No CONTAINER_TYPES update needed:** Flowchart has no container nodes (unlike BPMN pools/lanes or sequence fragments).

### DiagramCanvas Updates

```typescript
import { FlowProcessNode } from "../../types/flowchart/FlowProcessNode";
import { FlowDecisionNode } from "../../types/flowchart/FlowDecisionNode";
import { FlowTerminalNode } from "../../types/flowchart/FlowTerminalNode";
import { FlowIoNode } from "../../types/flowchart/FlowIoNode";
import { FlowSubprocessNode } from "../../types/flowchart/FlowSubprocessNode";
import { FlowEdge } from "../../types/flowchart/FlowEdge";

const nodeTypes = {
  // Existing BPMN + E-R + Org Chart + Architecture + Sequence types...
  flowProcess: FlowProcessNode,
  flowDecision: FlowDecisionNode,
  flowTerminal: FlowTerminalNode,
  flowIo: FlowIoNode,
  flowSubprocess: FlowSubprocessNode,
};

const edgeTypes = {
  // Existing types...
  flowEdge: FlowEdge,
};

// CONTAINER_TYPES — NO CHANGE needed (no flowchart containers)
```

**Arrow marker needed in MarkerDefs:**
```tsx
{/* Flowchart markers */}
<marker
  id="flow-arrow"
  viewBox="0 0 10 10"
  refX={10}
  refY={5}
  markerWidth={8}
  markerHeight={8}
  orient="auto-start-reverse"
>
  <path
    d="M 0 0 L 10 5 L 0 10 Z"
    fill="var(--diagram-flowchart, #e11d48)"
  />
</marker>
```

### CSS Styles for Flowchart

Add to `globals.css`. The flowchart theme uses `--diagram-flowchart` (rose accent: `oklch(0.645 0.246 16)`, already defined at line 29).

```css
/* ── Flowchart Diagram Styles ─────────────────────────────────── */

.flow-process {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--node-bg);
  border: 1.5px solid var(--diagram-flowchart);
  border-radius: 6px;
  padding: 10px 16px;
  min-width: 120px;
  cursor: pointer;
}
.flow-process:hover {
  background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
}

.flow-decision {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 140px;
  height: 80px;
  cursor: pointer;
}

.flow-decision-diamond {
  width: 90px;
  height: 90px;
  transform: rotate(45deg);
  background: var(--node-bg);
  border: 1.5px solid var(--diagram-flowchart);
  display: flex;
  align-items: center;
  justify-content: center;
}
.flow-decision-diamond:hover {
  background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
}

.flow-decision-label {
  transform: rotate(-45deg);
  font-weight: 600;
  font-size: 11px;
  color: var(--foreground);
  text-align: center;
  max-width: 80px;
  word-wrap: break-word;
  line-height: 1.3;
}

.flow-terminal {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--node-bg);
  border: 1.5px solid var(--diagram-flowchart);
  border-radius: 999px;
  padding: 10px 24px;
  min-width: 100px;
  cursor: pointer;
}
.flow-terminal:hover {
  background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
}
.flow-terminal-start {
  border-color: var(--diagram-flowchart);
  border-width: 2px;
}
.flow-terminal-end {
  border-color: var(--diagram-flowchart);
  border-width: 2.5px;
}

.flow-io {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.flow-io-skew {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--node-bg);
  border: 1.5px solid var(--diagram-flowchart);
  padding: 10px 20px;
  transform: skewX(-10deg);
  min-width: 120px;
}
.flow-io-skew:hover {
  background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
}
.flow-io-skew .flow-node-label {
  transform: skewX(10deg);
}

.flow-subprocess {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--node-bg);
  border: 2px solid var(--diagram-flowchart);
  border-radius: 6px;
  padding: 4px;
  cursor: pointer;
}
.flow-subprocess:hover {
  background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
}

.flow-subprocess-inner {
  display: flex;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--diagram-flowchart);
  border-radius: 4px;
  padding: 8px 14px;
  width: 100%;
}

.flow-node-icon {
  font-size: 16px;
  flex-shrink: 0;
}

.flow-node-label {
  font-weight: 600;
  font-size: 12px;
  color: var(--foreground);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.flow-edge-label {
  font-size: 11px;
  color: var(--diagram-flowchart);
  background: var(--node-bg);
  padding: 2px 8px;
  border-radius: 4px;
  border: 1px solid color-mix(in oklch, var(--diagram-flowchart) 30%, transparent);
  font-weight: 600;
  pointer-events: none;
  white-space: nowrap;
}
```

### Existing Code to Reuse / Modify

| File | Action | What |
|------|--------|------|
| `apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx` | **MODIFY** | Register flowchart node types, edge type, arrow marker |
| `apps/web/src/modules/diagram/lib/graph-converter.ts` | **MODIFY** | Add flowchart type resolution for nodes and edges (replace `// Future: flowchart` comment) |
| `apps/web/src/modules/diagram/lib/elk-layout.ts` | **MODIFY** | Add `getFlowNodeSize()` to buildElkGraph sizing chain |
| `apps/web/src/assets/styles/globals.css` | **MODIFY** | Add flowchart CSS styles |
| `apps/web/src/modules/diagram/types/graph.ts` | **READ** | DiagramNode (tag, icon, color) — already defined, no changes |
| `apps/web/src/modules/diagram/lib/bfs-path.ts` | **REUSE** | Path highlighting works for flowchart — diagram-type-agnostic |
| `apps/web/src/modules/diagram/stores/useGraphStore.ts` | **REUSE** | highlightedNodeId — no changes needed |
| `apps/web/src/modules/diagram/hooks/useAutoLayout.ts` | **REUSE** | Auto-layout triggers computeLayout → standard ELK path for flowchart |
| `apps/web/src/modules/diagram/types/architecture/constants.ts` | **REUSE** | `HIDDEN_HANDLE` constant for handle styles |

### Library & Framework Requirements

**No new packages required.** Everything built with existing dependencies:
- `@xyflow/react` 12.10.1 — custom nodes, edges, handles, EdgeLabelRenderer, BaseEdge, getSmoothStepPath
- `elkjs` 0.11.0 — standard layered algorithm for flowchart layout
- `zustand` 5.0.8 — highlight state (reuse existing)

### File Structure for This Story

New files:
```
apps/web/src/modules/diagram/
├── types/flowchart/
│   ├── constants.ts                     # FLOW_SIZES, type resolution functions, getFlowNodeSize
│   ├── constants.test.ts                # Tests for flowchart constants
│   ├── FlowProcessNode.tsx              # Rectangle - standard process step
│   ├── FlowDecisionNode.tsx             # Diamond - decision/branching
│   ├── FlowTerminalNode.tsx             # Stadium/pill - start and end nodes
│   ├── FlowIoNode.tsx                   # Parallelogram - input/output
│   ├── FlowSubprocessNode.tsx           # Double-bordered rectangle - subprocess
│   └── FlowEdge.tsx                     # Orthogonal edge with label support
```

Modified files:
```
apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx  # Register flowchart types + marker
apps/web/src/modules/diagram/lib/graph-converter.ts               # Flowchart type mapping (replace // Future: flowchart)
apps/web/src/modules/diagram/lib/graph-converter.test.ts          # Add flowchart converter tests + fix line 150 assertion
apps/web/src/modules/diagram/lib/elk-layout.ts                    # Flowchart node sizing in buildElkGraph
apps/web/src/modules/diagram/lib/elk-layout.test.ts               # Add flowchart sizing tests
apps/web/src/assets/styles/globals.css                             # Flowchart CSS styles
```

### Project Structure Notes

- Flowchart node components go in `~/modules/diagram/types/flowchart/` — follows BPMN/E-R/Org Chart/Architecture/Sequence pattern
- Uses standard ELK layout — no separate layout module needed (unlike sequence)
- Tests co-located next to source files
- No barrel files — import from specific subpaths

### Anti-Patterns to Avoid

- **NEVER put `nodeTypes` or `edgeTypes` inside the component** — causes re-renders (established pattern)
- **NEVER hardcode positions for flowchart nodes in GraphData** — all positioning from ELK layout
- **NEVER import from `reactflow`** — use `@xyflow/react` (v12+)
- **NEVER use `require()`** — ESM-only project
- **NEVER co-locate feature code in route directories** — use `~/modules/diagram/`
- **NEVER store layout-computed positions in persisted graph data** — positions are ephemeral
- **NEVER add new fields to DiagramNode or DiagramEdge** — use existing fields (`tag`, `icon`, `color`) with flowchart semantics
- **NEVER create inline style objects in render** — use CSS classes; conditionally apply inline styles only when data-driven
- **NEVER use `??` for empty string fallbacks** — use `||` so falsy `""` falls back correctly (lesson from Story 2.5 code review)
- **NEVER create barrel `index.ts` files** — per project rules, no barrel files in feature modules
- **DO NOT implement Smart Inspector for flowchart** — future story
- **DO NOT implement other diagram type renderers** — all 6 are now complete
- **DO NOT break existing tests** — 476 tests must continue passing (29 test files across monorepo)
- **DO NOT create a custom layout for flowchart** — ELK layered algorithm handles flowcharts perfectly
- **DO NOT add arrow markers inline** — use SVG `<marker>` in `MarkerDefs` with `markerEnd="url(#flow-arrow)"`
- **DO NOT create multiple edge types** — flowcharts need only one edge type with optional labels for decision outcomes

### Previous Story Intelligence (Story 2.7 — Sequence)

**Key learnings to carry forward:**
- Constants file pattern: `SEQ_SIZES` → `FLOW_SIZES` equivalent. Type resolution functions: `resolve*NodeType()`, `resolve*EdgeType()`, `get*NodeSize()`
- Node component pattern: Cast `data` as `DiagramNode & { label: string }`, use `Handle` with `style={HIDDEN_HANDLE}` (constant from architecture/constants.ts — reuse it)
- Edge component: For flowchart, use `BaseEdge` + `getSmoothStepPath` (unlike sequence which uses custom SVG paths). This is simpler — matches the E-R/Architecture/OrgChart pattern
- Graph converter: `resolveFlowNodeType` switch on `diagramType`, import type resolver from `types/[type]/constants`
- DiagramCanvas: import components, add to `nodeTypes`/`edgeTypes` objects OUTSIDE component
- CSS: Use `--diagram-[type]` CSS variable for accent colors. Use `color-mix()` for hover/bg tints
- `graphNodeToFlowNode` already spreads all DiagramNode fields into `data` — custom nodes access `data.tag`, `data.icon`, `data.color` directly
- `flowNodeToGraphNode` already preserves `tag`, `icon`, `color` fields — roundtrip works
- Code review lessons from previous stories: Remove dead helper functions, use `||` not `??` for empty string color guards, reuse `HIDDEN_HANDLE` constant
- 476 tests currently pass across monorepo — don't break them
- `computeLayout` returns `LayoutResult{nodes, edges?}` — flowchart uses standard path so `edges` is not returned (only ELK node positions updated)

### Previous Story Intelligence (Story 2.3 — BPMN)

**Key learnings relevant to flowchart:**
- BPMN's `MarkerDefs` pattern: `#bpmn-arrow-filled` — reuse same SVG marker structure for `#flow-arrow`
- Standard edge with `BaseEdge` + `getSmoothStepPath` is the simplest edge pattern — use it for flowchart
- BPMN's compound detection in `computeLayout()` shows the detection routing pattern — flowchart does NOT need this (uses standard path)

### Git Intelligence

Recent commits:
- `1ff8ff8 feat: implement Stories 2.4-2.7 — E-R, Org Chart, Architecture, Sequence diagram type renderers`
- `0a7838a feat: implement Story 2.3 — BPMN diagram type renderer`
- `7dd5af1 feat: implement Story 2.2 — ELK.js auto-layout engine in Web Worker`
- `5033109 feat: implement Story 2.1 — canvas workspace with @xyflow/react and unified graph model`

Established patterns:
- Commit message: `feat: implement Story X.Y — description`
- Feature code in `apps/web/src/modules/diagram/`
- Co-located tests next to source files
- `diagramTypeConfig` in DiagramCard.tsx already has flowchart config: `{ label: "Flowchart", icon: Icons.GitBranch, color: "text-rose-500" }`
- CSS variable `--diagram-flowchart: oklch(0.645 0.246 16)` already defined in globals.css (line 29)
- Graph-converter.ts line 50 has `// Future: flowchart` — the exact hook point to replace
- Graph-converter.test.ts line 150 expects `"default"` for `flow:process` — must update to `"flowProcess"` after adding resolver

### Latest Tech Information

**@xyflow/react 12.10.1 — BaseEdge + getSmoothStepPath:**
- `getSmoothStepPath` produces orthogonal (right-angle) edge paths — ideal for flowcharts
- Returns `[path, labelX, labelY]` tuple — `labelX`/`labelY` are the midpoint coordinates for label placement
- `BaseEdge` wraps the path in a proper SVG `<path>` with all @xyflow/react edge features (selection, animation, etc.)
- `EdgeLabelRenderer` renders HTML labels in a separate div layer above the SVG — use for decision labels
- `markerEnd` on `BaseEdge` references SVG markers from `<defs>` — same as all other diagram types

**CSS Diamond Shape for Decision Nodes:**
- Standard technique: `transform: rotate(45deg)` on container, `transform: rotate(-45deg)` on content
- Container dimensions should be square (the diamond "width" = side * √2)
- Handle positions work correctly with transformed containers in @xyflow/react — handles are positioned on the outer bounding box
- Alternative: Use CSS `clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)` — simpler but may have handle positioning issues

**CSS Parallelogram for I/O Nodes:**
- `transform: skewX(-10deg)` on container, `transform: skewX(10deg)` on content (counter-skew)
- Keeps text readable while giving the parallelogram visual
- Handles work correctly since @xyflow/react uses bounding box positioning

### References

- [Source: _bmad-output/planning-artifacts/epics.md#Story 2.8] — Full AC: process, decision, terminal, I/O, subprocess shapes; decision labels; ELK top-down layout
- [Source: _bmad-output/planning-artifacts/epics.md#Technical Notes] — Port Flexicar FlowDiagram nodes; custom nodes at types/flowchart/; rose accent; standard ELK layered; parallel development
- [Source: _bmad-output/planning-artifacts/architecture.md#Decision 1] — Unified Graph Data Model: `flow:` prefix, shared base fields
- [Source: _bmad-output/planning-artifacts/architecture.md#Enforcement Guidelines] — 7 mandatory rules, type prefixing: `flow:`
- [Source: _bmad-output/planning-artifacts/ux-design-specification.md#Diagram Type Color Accents] — Flowchart: rose oklch(0.645 0.246 16)
- [Source: _bmad-output/planning-artifacts/ux-design-specification.md#DiagramEdgeRenderer] — Flowchart edges: standard directed flow
- [Source: _bmad-output/implementation-artifacts/2-7-sequence-diagram-type-renderer.md] — Sequence patterns: constants, nodes, edges, converter, canvas registration, HIDDEN_HANDLE, code review learnings
- [Source: apps/web/src/modules/diagram/types/graph.ts] — DiagramNode (tag, icon, color), DiagramType includes "flowchart" (line 7)
- [Source: apps/web/src/modules/diagram/lib/graph-converter.ts] — Line 50: `// Future: flowchart` — exact hook point to replace
- [Source: apps/web/src/modules/diagram/lib/graph-converter.test.ts] — Line 150: `flow:process` currently resolves to "default" — update to "flowProcess"
- [Source: apps/web/src/modules/diagram/lib/elk-layout.ts] — buildElkGraph() sizing chain needs flowSize addition after seqSize
- [Source: apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx] — Canvas to register flowchart types + marker
- [Source: apps/web/src/modules/diagram/components/DiagramCard.tsx] — Flowchart config with Icons.GitBranch and text-rose-500
- [Source: apps/web/src/assets/styles/globals.css] — Line 29: `--diagram-flowchart: oklch(0.645 0.246 16)` already defined

## Dev Agent Record

### Agent Model Used

Claude Opus 4.6 (1M context)

### Debug Log References

No issues encountered. One pre-existing test (`graphNodeToFlowNode` basic node) expected `"default"` type for `flow:process` — updated to `"flowProcess"` since the `flow:` prefix now correctly resolves via the new flowchart resolver.

### Completion Notes List

- Created flowchart constants with FLOW_SIZES (5 subtypes), resolveFlowchartNodeType, resolveFlowchartEdgeType, getFlowNodeSize using FLOW_TYPE_MAP for O(1) lookup
- Implemented FlowProcessNode: rectangle with icon + label, rose accent border, 4 hidden handles
- Implemented FlowDecisionNode: diamond shape via CSS rotate(45deg) with counter-rotated label
- Implemented FlowTerminalNode: stadium/pill shape (border-radius: 999px), data.tag-based start/end distinction with varying border widths
- Implemented FlowIoNode: parallelogram shape via CSS skewX(-10deg) with counter-skewed label
- Implemented FlowSubprocessNode: double-bordered rectangle (outer 2px + inner 1px borders)
- Implemented FlowEdge: BaseEdge + getSmoothStepPath for orthogonal routing, EdgeLabelRenderer for decision outcome labels (Yes/No), markerEnd flow-arrow
- Updated graph-converter.ts: replaced `// Future: flowchart` with real dispatch for both node and edge resolution, added flowchart edge type handling
- Registered all flowchart node/edge types in DiagramCanvas nodeTypes/edgeTypes (OUTSIDE component), added #flow-arrow SVG marker to MarkerDefs
- Added getFlowNodeSize to elk-layout.ts buildElkGraph sizing chain after seqSize
- Added flowchart CSS styles with --diagram-flowchart rose accent, color-mix hover states, all 5 node shapes + edge label styling
- Updated existing test assertions: flow:process now resolves to flowProcess (previously "default")
- Added 15 flowchart constants tests, 6 graph converter flowchart tests, 4 ELK layout flowchart sizing tests
- All 497 tests pass across 30 test files (0 regressions, +21 new tests)

### File List

**New files:**
- apps/web/src/modules/diagram/types/flowchart/constants.ts
- apps/web/src/modules/diagram/types/flowchart/constants.test.ts
- apps/web/src/modules/diagram/types/flowchart/FlowProcessNode.tsx
- apps/web/src/modules/diagram/types/flowchart/FlowDecisionNode.tsx
- apps/web/src/modules/diagram/types/flowchart/FlowTerminalNode.tsx
- apps/web/src/modules/diagram/types/flowchart/FlowIoNode.tsx
- apps/web/src/modules/diagram/types/flowchart/FlowSubprocessNode.tsx
- apps/web/src/modules/diagram/types/flowchart/FlowEdge.tsx

**Modified files:**
- apps/web/src/modules/diagram/lib/graph-converter.ts
- apps/web/src/modules/diagram/lib/graph-converter.test.ts
- apps/web/src/modules/diagram/lib/elk-layout.ts
- apps/web/src/modules/diagram/lib/elk-layout.test.ts
- apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx
- apps/web/src/assets/styles/globals.css
- _bmad-output/implementation-artifacts/sprint-status.yaml

### Change Log

- 2026-02-27: Implemented Story 2.8 — Flowchart Diagram Type Renderer. Added 5 node components (process, decision, terminal, I/O, subprocess), 1 edge component with label support, constants + type registry, graph converter integration, ELK layout sizing, CSS styles with rose accent, DiagramCanvas registration + arrow marker. 497 tests passing.
- 2026-02-27: Code review (adversarial) — 7 issues found (4M, 3L), all fixed. M1: Decision diamond ELK height increased 80→130 to fit rotated 90px square. M2: Removed forced ⚙️ icon default from FlowProcessNode, now only renders when data.icon is truthy. M3: Added data.color border override support to all 5 node components. M4: Extracted getNodeDimensions() helper in elk-layout.ts to replace 6-level nested ternary. L1: Unified constants.ts to use lookup maps for both type resolution and sizing. L2: Removed dead gap:8px from .flow-io-skew CSS. L3: Added sprint-status.yaml to File List. 180 web tests passing, 0 regressions.
