Skip to content

Show triggers and flows as a graph on the admin panel #54

Description

@edgehero

What

Triggers, flows (skills), and chained flows form a graph, and every edge already exists somewhere in data — but no screen shows the topology. Wanted: a graph view on the admin panel showing how triggers and flows interact, in a spot that doesn't complicate the existing UI: one drill-in, one entry key, the LIST stays as it is.

What already works (don't redo this)

  • trigger → flow is direct config: every triggers.json entry names its run.flow; the admin already renders cron/label/comment/pull_request … → flow per entry (render.mjs triggerLine, the LIST triggers section).
  • cron trigger ↔ its runs is an exact join. Scheduler ids ride the repeat:<id>:<millis> jobId into the run-history filenames; the worker does precisely this scan for previousRunAt (run-history.mjs makeFindPreviousRun). The admin just never does it.
  • Chain edges are in the records. parentJobId/chainDepth/chainRefused; RUN_DETAIL already renders "child of · spawned k → …" and the LIST prefixes .
  • The TUI can draw it. Box glyphs with an ASCII fallback (panel.mjs GLYPHS), themed styler, and a documented four-point recipe for adding a view to the dashboard's view machine.

Gap 1 — the topology is invisible

Nodes (triggers, flows, cron folders) and edges (trigger→flow, flow→chained flow, trigger→runs) exist across three files and a directory of sidecars, but the operator assembles the picture by hand. A GRAPH drill-in should render it: triggers on one side, flows on the other, edges annotated with run counts and last outcome from the history joins.

Gap 2 — github runs can't be attributed to their trigger

Issue #49's matched: { index, type, … } names exactly which triggers.json entry fired — but it lands only in /job/event.json; buildRecord never persists it, so the graph can count runs per cron trigger exactly while label/comment/PR triggers get at best a flow-name heuristic. Persist matched.index + matched.type on the run record (additive, nullable, no payload text — an integer and an enum, well inside the record's PII posture). Two triggers pointing at the same flow become distinguishable in history, which was half the point of #49.

Gap 3 — flows exist only as strings

The admin has no skill enumeration: SKILL_PATH_RE/git ls-tree live worker-side in materialize.mjs, and the admin can only gate-check one named flow in one folder (readFlowGate). So the graph can't show orphan skills (exist in a folder's .pi/skills/, no trigger points at them) or dangling triggers (flow gate says no-skill). A read-model skill enumeration for cron folders — same object-store read the worker already trusts — closes both.

Gap 4 — chain topology is history, not config

A flow chains by writing /outbox requests at runtime; nothing declares "flow A spawns flow B". So flow→flow edges can only be observed from records over the retention window — the graph must label them as observed, with the depth/fanout caps (PI_CHAIN_DEPTH_MAX, PI_CHAIN_MAX_PER_JOB) stated. Bounded further by OQ-009 (github parents don't chain; same-folder only).

Where it lives

A GRAPH view in the dashboard view machine (g is free), entered from LIST, rendered with the existing glyph/styler primitives — no graph library (panel.mjs's "thin pure module, no TUI framework" precedent holds). A /dispatch graph subcommand renders the same thing as plain text for the degraded path. LIST changes by exactly one key hint.

Explicitly NOT proposed

  • A web/HTML surface. The admin binds no port (DES-ADMIN-VIA-PI-EXTENSION); that property is load-bearing and this feature doesn't get to spend it.
  • Graph/diagram libraries, or mermaid in-product — mermaid stays a README concern.
  • Declared chain topology in config. Chains are agent-requested at runtime by design; the graph reports what happened, it doesn't promise what will.

Acceptance

The graph shows every trigger → flow edge from the live triggers file; cron trigger nodes carry exact run counts/last outcome; github-triggered runs join to their trigger via the persisted matched fields; observed chain edges render with their caps stated; orphan skills and no-skill triggers are visibly flagged; works in the narrow/ASCII degraded path; no port, no new deps, all fs/redis access stays in read-model.mjs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions