feat(pipeline): show backing services and sidecar routes in the graph #411

Merged
gmackie merged 1 commit from feat/pipeline-graph-services into main 2026-08-24 07:55:42 +00:00
Owner

What

The pipeline chart flattened complex deployments into fiction. forgegraph was the worst case:

  • ws.forgegraf.com — the hub — drawn as a domain feeding the production Worker. It's a systemd service on hetzner-master:8443; the Worker never serves it. (This is the same misreading that made tonight's hub work confusing in the UI.)
  • PRODUCTION captioned hetzner-fg although its target is a Cloudflare Worker — a stale stage→node binding presented as fact.
  • No database anywhere on the chart, despite the plan's promise that it would make "one-box shares production's DB" literal.

Changes

Query — pipeline.graph now returns services (Postgres/Redis from node_database_bindings joined through node_services, deduplicated so app-user + migration-user bindings read as one database), and stage routes carry nodeName/targetPort.

Chart —

  • Backing services render as cards with a shared dashed edge from every target on the stage — one-box visibly binds the same database as production.
  • A route terminating on a node:port under a Workers stage renders as a sidecar service card (hostname + node:port) hanging off the stage. Node-platform stages keep the domain-into-target edge.
  • A stage header shows its bound node only when some target actually runs on a node.

For forgegraph this means: the hub appears as ws.forgegraf.com → hetzner-master:8443, production stops claiming to run on hetzner-fg, and the shared Postgres shows under production with edges from both lanes.

Verification

  • tsc --noEmit clean for packages/api and apps/web
  • 175 web test files (1115 tests) pass — 5 new graph tests: shared DB edges, sidecar rendering, node-platform fallback, both stage-header cases

🤖 Generated with Claude Code

## What The pipeline chart flattened complex deployments into fiction. forgegraph was the worst case: - **`ws.forgegraf.com` — the hub — drawn as a domain feeding the production Worker.** It's a systemd service on `hetzner-master:8443`; the Worker never serves it. (This is the same misreading that made tonight's hub work confusing in the UI.) - **PRODUCTION captioned `hetzner-fg`** although its target is a Cloudflare Worker — a stale stage→node binding presented as fact. - **No database anywhere on the chart**, despite the plan's promise that it would make "one-box shares production's DB" literal. ## Changes **Query** — `pipeline.graph` now returns `services` (Postgres/Redis from `node_database_bindings` joined through `node_services`, deduplicated so app-user + migration-user bindings read as one database), and stage routes carry `nodeName`/`targetPort`. **Chart** — - Backing services render as cards with a **shared dashed edge from every target on the stage** — one-box visibly binds the same database as production. - A route terminating on a `node:port` under a Workers stage renders as a **sidecar service card** (hostname + node:port) hanging off the stage. Node-platform stages keep the domain-into-target edge. - A stage header shows its bound node **only when some target actually runs on a node**. For forgegraph this means: the hub appears as `ws.forgegraf.com → hetzner-master:8443`, production stops claiming to run on hetzner-fg, and the shared Postgres shows under production with edges from both lanes. ## Verification - `tsc --noEmit` clean for `packages/api` and `apps/web` - 175 web test files (1115 tests) pass — 5 new graph tests: shared DB edges, sidecar rendering, node-platform fallback, both stage-header cases 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(pipeline): show backing services and sidecar routes in the graph
All checks were successful
CI / gitleaks (pull_request) Successful in 6s
forgegraph/ci CI passed
CI / ci (pull_request) Successful in 9m18s
d8544873c8
The pipeline chart flattened complex deployments into fiction. For forgegraph
it drew ws.forgegraf.com — the WebSocket hub, a systemd service on
hetzner-master:8443 — as a plain domain feeding the production Worker, showed
"hetzner-fg" under PRODUCTION although the target is a Cloudflare Worker (a
stale stage→node binding), and omitted the database entirely, despite the plan
promising the chart would make "one-box shares production's DB" literal.

Three changes:

- pipeline.graph now returns `services` (node_database_bindings joined through
  node_services: Postgres/Redis per stage, deduplicated so app-user and
  migration-user bindings read as one database) and gives stage routes their
  node identity (nodeName, targetPort).

- The chart renders each backing service as its own card with a shared dashed
  edge from every target on the stage — one-box visibly binds the same
  database as production. A route that terminates on a node:port under a
  Workers-platform stage is drawn as a sidecar service card (hostname,
  node:port) hanging off the stage, not as a domain wired into a Worker that
  never serves it. Node-platform stages keep the domain-into-target edge.

- A stage header only shows its bound node when some target actually runs on a
  node; an all-Workers stage no longer displays a stale binding.

Verification: tsc clean for packages/api and apps/web; 175 web test files
(1115 tests) pass, including five new graph tests covering the shared database
edges, the sidecar rendering, the node-platform fallback, and both stage-header
cases.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMpzX1b6swjezEptw3T71f
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gmackie/ForgeGraph!411
No description provided.