Skip to content

feat: watch an agent build a workflow live - #88

Merged
ripgrim merged 12 commits into
mainfrom
feat/live-agent-view
Oct 24, 2026
Merged

ripgrim merged 12 commits into
mainfrom
feat/live-agent-view

Conversation

@ripgrim

@ripgrim ripgrim commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

an agent that builds a workflow over the mcp now builds it where you can watch it.

  • drafts live in a new workflow_drafts table, apart from workflow_definitions, so nothing that runs workflows can read one. a draft never runs
  • agent_activity records which nodes each agent touches (read, write, edit, delete) and when
  • a node outline clears 40 s after its last touch; an agent drops off 2 min after its last signal; finish_workflow_work clears at once
  • validate_workflow keeps each checked workflow as a draft when the agent may manage workflows (configure scope, workflows:manage, not read only). read-only agents still validate with no draft
  • apply_workflow saves as before (a new workflow stays off) and promotes the open draft
  • new begin_workflow_work opens an empty draft at once and returns the id to write with; finish_workflow_work releases listed nodes or all
  • each signal computes a node and edge change set (added, changed, removed) and stores it with a revision
  • /events/stream listens on a new agent channel; work goes to viewers with activity:read on the repo, setup progress only to the run's owner
  • the web shares one EventSource and merges agent messages into the Query cache. the home setup card stops polling while the stream is open
  • workflows page: Draft badge, dithered blue outline while an agent works, the agent's name with a 3 by 2 activity mark, Save and Discard
  • canvas: the draft streams in; new nodes fade in staggered, new edges draw in blue like a trail, changed nodes pulse, removed nodes fade out
  • the outline is canvas drawn from the 4 by 4 bayer grid in dither/pixel.ts, one shared 30 fps loop, per action sweep (read and write 1500 ms, edit 1000, delete 800)
  • one new token, --agent (oklch 0.637 0.167 254, dark 0.7 0.15 254), used only for agent activity
  • account settings > general: reduce agent animations. it and the os setting draw everything still
  • dev only: the demo head serves the stream from pglite through the api's message builder, and POST /api/dev/agent-sim plays an agent through the real services
  • migration 0035_live_agent_work is additive: two tables and user.reduce_agent_motion

decisions on the issue's open questions

  • dither, not ascii characters
  • a draft appears on the first validate or begin
  • apply_workflow keeps saving and promotes the draft, rather than writing drafts by default. the cli's push uses it and keeps working
  • drafts expire 24 h after their last change; expired drafts are hidden, not deleted
  • two agents share one draft; each has its own presence row
  • everyone with read access to the repo sees drafts and outlines; only workflows:manage saves or discards

proof (local pglite, port 3445): screenshots light and dark and recordings in /tmp/wf-feedback/live-agent/

tests: contracts change set and presence math, db integration on real postgres (drafts never run on any trigger incl. schedules, expiry, promote, discard, live edit keeps the live version running, notify), mcp tools and permissions, sse channel incl. a user without repo access getting nothing, web cache merge, canvas decoration and dither ring

Closes #84

- workflow_drafts holds what an agent builds, apart from workflow_definitions
- agent_activity records the nodes each agent touches and when
- a node change set names added, changed and removed nodes and edges
- every signal notifies the agent channel; setup progress does too
- migration 0035 adds both tables and the reduce agent motion setting
- validate_workflow keeps each checked workflow as a draft when the agent may manage workflows
- apply_workflow saves as before and promotes the draft
- begin_workflow_work opens a draft at once and returns the id to write with
- finish_workflow_work releases listed nodes, or every node and the session
- the SSE stream listens on agent and sends each change to viewers who can see the repo
- setup progress goes only to the person whose run it is
- drafts show on the workflows page with a Draft badge, Save and Discard
- a dithered blue outline in the new agent token marks the card and each node an agent works on
- the canvas animates each change as it lands: nodes fade in, edges draw in, removed steps fade out
- the editor header names the agent with a 3 by 2 activity mark
- one shared event stream feeds the Query cache; the setup card stops polling while it is open
- account settings can reduce agent animations, and the OS setting is honoured
- the demo head serves the event stream from embedded Postgres through the api's message builder
- POST /api/dev/agent-sim plays an agent building Close stale PRs through the real services
- the outline sizes to the border box and is not capped by the canvas max width
- the card names the working agent under the title

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found 3 potential issues (3 medium).

View 6 other findings in Capy.

Open in Capy

Comment on lines +664 to +669
definition: input.definition ?? input.draft.definition,
revision: changed ? input.draft.revision + 1 : input.draft.revision,
lastChange: changed ? input.change : input.draft.lastChange,
updatedAt: input.now,
})
.where(eq(workflowDrafts.id, input.draft.id));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Draft mutations are not serialized per workflow

Medium Severity

If promoteDraft reads revision 1 and an agent successfully saves revision 2 while applyWorkflowDefinition is awaiting its queries, closeDraft overwrites the row with the revision-1 snapshot and marks it promoted. Both callers report success, but the newer work is lost. Initial concurrent signals also both observe no open draft: writeDraft can either discard the other caller's newly inserted draft or fail on workflow_drafts_open_unique. Serialize creation, revision updates, and promotion/discard per repo and definition, using a transaction with appropriate locking or revision/status checks rather than unconditional writes by row ID.

Open in Capy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 270706d

Comment thread apps/web/src/lib/live-stream.ts Outdated
}
const next = new EventSource(STREAM_URL);
source = next;
next.onopen = () => setConnected(true);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reconnecting the stream does not recover missed state

Medium Severity

During a brief API restart, an agent can finish setup or change/promote a draft while the browser's EventSource is disconnected. Postgres notifications are not replayed, and this onopen handler only changes connection status: neither useLiveWork nor useLiveSetup refetches its queries. Once connected again, AgentSetupCard also disables its fallback polling, so a missed final report can leave the checklist stale indefinitely; non-scheduled workflow views likewise have no periodic recovery. Refetch the subscribed setup and workflow queries when the stream opens or reconnects, or supply a replay/snapshot protocol.

Open in Capy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 83d20e3

Comment on lines +393 to +395
const nodes = agentNodeTouchesSchema.parse(
mergeTouches(fresh ? activityTouches(row) : [], input.touches, input.now),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Presence validation rejects otherwise valid workflow node IDs

Medium Severity

A compiled workflow with a 201-character node ID passes workflowDefinitionSchema, whose node IDs are unrestricted z.string() values. saveAgentDraft now writes the draft and then throws here because agentNodeTouchSchema caps IDs at 200 characters; applyAgentWorkflow can similarly commit the saved workflow before touch throws, causing apply_workflow to report failure and abort the remaining definitions despite already saving one. Empty IDs produce the same mismatch. Keep presence compatible with the workflow ID contract, and ensure auxiliary presence validation cannot turn an already committed workflow save into a reported failure.

Open in Capy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 270706d

Brings in CLI push review (propose_workflow_push, get_workflow_push),
the Pierre code view, project links and slug workflow keys.

- 0035_live_agent_work is regenerated on top of 0034_workflow_push_proposals.
  The SQL is unchanged and the snapshot now chains from 0034.
- MCP lists the union of tools: push review plus begin and finish work.
- validate_workflow and apply_workflow match server ids before drafts.
- An accepted push review promotes the open draft for each saved workflow.
Each draft mutation (begin, check, apply, promote, discard, finish, push
promotion) now runs in one transaction behind a per-workflow advisory lock.
Concurrent first signals share one draft, and a promote or discard can no
longer overwrite a newer revision.

Presence touches use the workflow node id schema, so any id a definition
accepts also passes presence, and a touch can never fail a committed save.
Postgres notifications are not replayed, so the stream now tells views to
resync when it reconnects or the tab becomes visible again. The workflows
list, open workflows, setup card and activity feed refetch on resync. A
stream the browser gave up on reopens after a short delay.
…sing

Each push entry records the open draft's id and revision when the push is
proposed. On accept, under the per-workflow draft lock, the draft is
promoted only if it is still that id and revision. A newer draft stays
open on the saved workflow, so a person can still save or discard it.
@ripgrim
ripgrim merged commit 0dde1a9 into main Oct 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Live agent drafts: show workflows an agent is building as drafts with a live outline

1 participant