---
title: Flowcharts (visual flowchart editor)
---

# Flowcharts (visual flowchart editor)

## What it is

**Flowcharts** is a standalone visual flowchart editor inside Agent Mission
Control, surfaced as a virtual project in the sidebar (it mounts the same way
**Decks** does — a docked panel beside the projects sidebar, not a full-screen
view). You draw boxes and connectors on a React Flow canvas, drop shapes from a
palette, organise them into swimlanes, and keep a saved library of charts. Editing
is fast: **quick-connect** (drag a connector onto empty canvas to spawn a connected
shape), **snap + alignment guides** while dragging, **multi-select align / distribute**,
and **smart connectors** that route orthogonally around other shapes (see Editing
below). AI sessions can read and draw on the open board from a built-in
**Sessions** tab, and a **"Build with AI"** on-ramp turns a one-line description into
a whole chart (see Build with AI below).

A flowchart is a single graph blob: `{ nodes, edges, lanes }`. Each chart is one
row in the `flowcharts` SQLite table; the whole graph is stored as one JSON
`data` column (charts are small, so a single-column blob is the simplest reliable
shape — it mirrors the `mindmaps` table).

## Where to find it

### How it is gated

Flowcharts is an in-development feature registered as `'flowchart'` in
`src/shared/unreleased-features.ts` (`settingKey: 'flowchartEnabled'`,
`envVar: AMC_SHOW_FLOWCHART`, `status: 'in-development'`).

It is visible when **any one** of the following is true:

- The feature is marked `'shipped'` in the registry (releases to everyone).
- Omniscio is launched with `AMC_SHOW_FLOWCHART=1`.
- The user flips the **Settings → Lab → Flowcharts** toggle on.

No code reads `settings.flowchartEnabled` directly to decide visibility — the
sidebar row gate goes through `isUnreleasedFeatureVisibleInRenderer('flowchart', …)`
in `project-visibility.ts` (`UNRELEASED_PROJECT_GATES`), and the main-process
handlers gate on `isUnreleasedFeatureVisible('flowchart', …)`. Doing otherwise
bypasses the env reveal path and fails the gating lint test.

### Discoverability on enable

Flipping the toggle on reveals the Flowcharts sidebar row **reactively** — the
projects sidebar re-filters on the settings change (Dashboard's `useMemo` keyed on
`settings`), so no app refresh is needed. Omniscio also **points the user to the new
row**: the `flowchart` entry declares
`sidebarLaunch: { viewId: 'flowchart', label: 'Flowcharts' }`, so enabling it
fires the standard Lab-feature flash + toast, with an **Open** action that routes
to the `__flowchart__` virtual project via the registry-driven resolver
(`virtualProjectPathForSidebarLaunchViewId` in `useAppCustomEvents.ts`). Every
gated sidebar built-in must carry such a pointer — locked by **invariant J** in
[unreleased-feature-gating-contract.md](../../.claude/memory/contracts/unreleased-feature-gating-contract.md)
(guard: `gated-builtin-discoverability-pointer.test.ts`).

### How it is mounted

Flowcharts is an `amc-builtin` virtual project (`__flowchart__`) defined once in
`src/shared/integrations/flowchart.ts` and assembled into the registry by
`npm run integrations:reindex`. Its UI manifest in
`src/renderer/src/integrations/ui-registry.ts` carries:

- `panelComponent: FlowchartEditor` — the full editor shell.
- `panelOwnsLayout: true` — the panel renders its own multi-pane layout, so Omniscio
  suppresses the default empty `SessionsSidebar` fallback (same as Decks / Writer).
- `mobilePrefersPanel: true` — on a phone (Web Access mode) the panel renders in
  the `'sessions'` slot, so tapping the row lands on the editor (its internal
  Charts/Sessions tabs reach everything) rather than an empty session list.

`FlowchartEditor` assembles its own left sub-sidebar (`FlowchartSidebarTabs` — a
**Charts** | **Sessions** tablist), a toolbar, the React Flow canvas (wrapped in
one `ReactFlowProvider`), and a shape palette. Light/dark `colorMode` follows the
app theme via `useIsDarkMode()` (the `dark` class on `<html>` is the single source
of truth); the flowchart data model carries no per-chart theme override.

## How it behaves

### AI sessions on the board

The editor's **Sessions** tab is the stock session-host sidebar
(`SessionHostSidebar` + `useFlowchartSessionHost`). The SAME `__flowchart__`
project hosts those sessions: it is spawnable + resolver-mapped to
`<userData>/flowchart-agent` (`SPAWNABLE_VIRTUAL_PROJECT_SENTINELS` +
`resolveProjectWorkDir`). Selecting a session **reveals its chat inside the
editor** (source `'flowchart-session'`) rather than ejecting the user out of the
panel — mirroring the mind map's reveal. A spawned flowchart agent reads and edits
the open board through the CLI control server's flowchart routes
(`cli-server-flowchart-routes.ts`); an external edit emits the `flowchart:changed`
push so the renderer refreshes the list and reloads the open chart (preserving
undo).

It is deliberately NOT in `SESSION_HOSTING_VIRTUAL_PROJECT_PATHS` — that set drives
Ctrl+T / auto-select for sidebar-SELECTED virtual projects, and the Flowchart owns
its own session-open path.

### Build with AI (describe → flowchart)

The headline differentiator: type a one-line description and an AI session builds the
whole chart. It is NOT a bespoke generation engine — it is a **regular Omniscio session**
(your normal per-session model picker applies) given **CLI access** to build the chart,
so quality/cost follow your model choice and there is no separate billing path.

- **On-ramp** — a "Build with AI" button on the Charts empty-state + the Charts header
  (`FlowchartListSidebar`) opens a small "Describe your flowchart" dialog
  (`BuildWithAiModal` — a `DialogShell` + a `FormField` textarea; Ctrl/Cmd+Enter submits).
- **What Build does** (`useFlowchartAiBuild`) — `createFlowchart()` a fresh chart, then
  `launchSession` into `__flowchart__` with the description as **`draftText`** (pre-filled
  in the composer) and a BUILD primer as **`firstSendPrefix`** (hidden setup context). It
  uses `firstSendPrefix`, NOT `initialPrompt`, so the session opens _ready_ with the model
  picker — you pick a model and **send**, which is what preserves your model choice. The
  session chat reveals in place over the editor (source `'flowchart-session'`). A failed
  spawn soft-deletes the just-created blank chart (no orphan).
- **The build primer** (`buildFlowchartBuildPrompt` in `flowchart-spawn-prompt.ts`) hands
  the agent the full `/flowcharts` CLI verb set (list / create / replace / rename / delete
  — "make whatever you need"), the 19 `FLOWCHART_NODE_TYPES`, when to use swimlanes, and
  the validate-and-retry-on-400 loop — and tells it to set every node to `x:0, y:0` and
  let Omniscio arrange, so it spends its effort on the LOGICAL graph (nodes + edges + labels).
- **Auto-arrange** — an LLM can't run the renderer-side elk layout, so it drops every node
  on one point. When a chart arrives via the CLI **unlaid-out** (`isUnlaidOut`: 2+ nodes
  coincident — the `0,0` sentinel), the store's `arrangeIfUnlaidOut` **dynamic-imports**
  `flowchart-layout.ts` (keeping its heavy elk dep out of the always-on startup bundle —
  the `layout-lazy-loaded` invariant), runs `autoLayoutFlowchart` (elk) once and
  `mergeNodePositions` the result, folded into ONE undo entry and persisted. It fires on
  the live `FLOWCHART_CHANGED` reload AND on chart open (so an agent-built chart tidies
  even if it wasn't open at build time); an elk OR layout-load failure falls back to the
  manual Auto-layout button.

Locked by the **`ai-build-via-session`** invariant in the
[flowchart-feature-contract](../../.claude/memory/contracts/flowchart-feature-contract.md):
"describe → flowchart" stays a regular session over the CLI — never a bespoke generation /
fixed-model / cost-cap path — and the primer's `0,0` sentinel stays in lock-step with the
`isUnlaidOut` detector.

### Editing — quick-connect, snap, align, smart routing

Four interactions make the canvas fast, each backed by a PURE, unit-tested module (no
React / store / React Flow) that `FlowchartCanvas.tsx` wires:

- **Quick-connect** — drag a connector off a shape onto empty canvas and the store's
  `connectToNewNode` creates the node + edge in ONE commit (one undo step), opening it
  for rename. Wired via `onConnectStart` / `onConnectEnd`; a drop inside a lane joins
  that lane.
- **Snap + alignment guides** — `flowchart-align-guides.ts` `computeSnap` snaps the
  dragged node's edge/center to a nearby node's (8px threshold) and returns guide
  segments drawn as 1px accent SVG lines in a `<ViewportPortal>` (`onNodeDrag`).
- **Multi-select align / distribute** — `flowchart-align.ts` `alignNodes` (6 modes) /
  `distributeNodes` (h/v) feed the store's `alignSelected` / `distributeSelected`, which
  bulk-apply via `applyLayout` (ONE undo step). The toolbar shows the cluster at ≥2
  selected shapes (tracked in the transient `selectedNodeIds`).
- **Smart connectors** — `flowchart-edge-router.ts` `routeOrthogonal` (Hanan-grid A\*
  with a turn penalty + an obstacle/size CAP → `null` fallback to the simple path)
  routes each edge AROUND other shapes. Routes are DERIVED + TRANSIENT: computed in the
  canvas (a memo keyed on a committed-geometry signature, recompute on drag-stop, never
  per frame) and injected onto the React Flow edge `data.routePoints` — **never persisted**
  (model edges keep only their `routing` mode). An edge touching the dragging node drops
  its route to follow live.

Locked by the `smart-routes-transient` + `snap-and-align-pure` + `commit-chokepoint`
invariants in the
[flowchart-feature-contract](../../.claude/memory/contracts/flowchart-feature-contract.md).

### Status

In-development (default-hidden). Ships the data model, the IPC + CLI-route
surface, the React Flow editor (canvas, toolbar, shape palette, swimlanes, saved
library) with quick-connect / snap + alignment guides / multi-select align-distribute /
smart obstacle-avoiding connectors, Mermaid export (one-way, lossy), AI sessions on
the board (the Sessions tab spawning into `__flowchart__`), and **"Build with AI"**
(describe → a regular session builds the chart via the CLI, auto-arranged). Reveal via
Settings → Lab toggle or `AMC_SHOW_FLOWCHART=1`.

## For agents

### Data model — `flowcharts`

Created by migration `src/main/db/migrations/20260627150737-flowcharts-table.ts`.

`flowcharts`: `id` (PK), `title`, `title_manually_set`, `data` (the
`{ nodes, edges, lanes }` graph as JSON), `is_deleted`, `created_at`,
`updated_at`. Global per install (no `account_id`) and soft-deleted — every read
filters `AND is_deleted = 0`; the list query is
`WHERE is_deleted = 0 ORDER BY updated_at DESC` (backed by `idx_flowcharts_active`).

### IPC channels (`flowchart:*`)

Defined in `src/shared/ipc-channels/flowchart.ts`; handlers in
`src/main/ipc/flowchart-handlers.ts`; Zod schemas in
`src/shared/ipc-schemas/flowchart.ts`; response types in
`src/shared/ipc-response-map/flowchart.ts`.

| Constant            | Channel string      | Purpose                                         |
| ------------------- | ------------------- | ----------------------------------------------- |
| `FLOWCHART_LIST`    | `flowchart:list`    | list all non-deleted flowcharts                 |
| `FLOWCHART_GET`     | `flowchart:get`     | fetch one flowchart by id                       |
| `FLOWCHART_CREATE`  | `flowchart:create`  | create a blank flowchart                        |
| `FLOWCHART_SAVE`    | `flowchart:save`    | persist the graph blob of an open chart         |
| `FLOWCHART_RENAME`  | `flowchart:rename`  | rename a flowchart                              |
| `FLOWCHART_DELETE`  | `flowchart:delete`  | soft-delete a flowchart                         |
| `FLOWCHART_CHANGED` | `flowchart:changed` | push — an external (agent) edit; refetch/reload |

Every request channel is registered in `tests/integration/ipc-contract.test.ts`.

### Renderer

The panel mounts `FlowchartEditor`; state lives in the co-located Zustand store
(`src/renderer/src/features/flowchart/flowchart-store.ts`): the saved-charts list,
the open chart, load/save/rename/delete, and node/edge/lane mutations, with a
`usePushListener` on `FLOWCHART_CHANGED` (via `useFlowchartPushEvents`). The canvas
maps the stored graph to React Flow through `flowchart-rf-adapter.ts`; shapes come
from `flowchart-shapes.tsx` / `ShapePalette`. A `flowchart-mermaid.ts` helper
provides Mermaid **export only** (one-way and lossy — there is no Mermaid import;
positions, sizes, colors, and swimlanes drop, so never round-trip through it).

### Key files

| What                   | Where                                                               |
| ---------------------- | ------------------------------------------------------------------- |
| Migration              | `src/main/db/migrations/20260627150737-flowcharts-table.ts`         |
| Query layer            | `src/main/db/queries-flowchart.ts`                                  |
| IPC handlers           | `src/main/ipc/flowchart-handlers.ts`                                |
| IPC channels           | `src/shared/ipc-channels/flowchart.ts`                              |
| CLI control routes     | `src/main/services/cli/cli-server-flowchart-routes.ts`              |
| Session host (workdir) | `src/main/services/flowchart/flowchart-session-context-provider.ts` |
| Shared types           | `src/shared/types/flowchart.ts`                                     |
| Integration manifest   | `src/shared/integrations/flowchart.ts`                              |
| Gating registry        | `src/shared/unreleased-features.ts` (`flowchart`)                   |
| Renderer (store + UI)  | `src/renderer/src/features/flowchart/`                              |

## Related

Decks mounts the same way — a docked panel beside the projects sidebar rather than a full-screen view — and is the closest sibling in the sidebar.
