Skip to content

feat: Let the AI add steps and ports inside a subgraph - #2684

Merged
camielvs merged 3 commits into
masterfrom
08-31-fix_ai_subgraph_editing
Sep 4, 2026
Merged

feat: Let the AI add steps and ports inside a subgraph#2684
camielvs merged 3 commits into
masterfrom
08-31-fix_ai_subgraph_editing

Conversation

@camielvs

@camielvs camielvs commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Description

The PR below this one made the assistant change existing things wherever they
live. Creating something new still only worked at the top level, which left one
job impossible: getting a value into or out of a subgraph. That needs a new port
on the subgraph itself, and there was no way to ask for one.

Adding a step, an input, or an output can now target a subgraph. Ask for
something without saying where and nothing changes — it goes to the top level as
before. Name a subgraph, or open one and say "add a filter step here", and it
goes inside that one instead. An input added inside a subgraph shows up as a new
input port on that subgraph step, which is how you feed a value into it.

If you name something that isn't a subgraph, it says so and tells you how to add
at the top level instead, rather than failing quietly.

Getting a value in or out now finishes the job. A port on the boundary is
only a third of the work — it also has to be wired to the step inside the
subgraph that uses the value, and to whatever feeds it in the parent. Asked for
this, the assistant used to create the port and stop, leaving a value that
reaches nothing and two fresh validation errors where there had been none. It
now does all three, so "feed this into <subgraph>" is a single request that
ends with a working pipeline.

Two smaller corrections in the same area. Asked to fix a problem while you
happen to be looking at a subgraph, it no longer drops the fix into that
subgraph — repairs land on whatever they're repairing, wherever that lives. And
it now gets the subgraph you're viewing handed to it directly rather than
matching on its name, which was ambiguous whenever two subgraphs at different
levels shared a name.

image.png

Related Issue and Pull requests

Stacked on #2683#2687#2686#2685#2655.

Re-cut alongside #2683 — see that PR for why. This one is now purely the new
capability: it adds to the PR below rather than rewriting it.

Type of Change

  • New feature

Checklist

  • I have tested this does not break current pipelines / runs functionality
  • I have tested the changes on staging

Test Instructions

  1. Open a pipeline with a subgraph. Ask the chat: "add a <component> inside
    <subgraph name>". Open the subgraph and confirm it's there, and that it did
    not also appear at the top level.
  2. Open a subgraph, then say "add an input called threshold here" — it should go
    into the subgraph you're looking at, not the top level.
  3. Go back up: that subgraph step should now have a threshold input port on it.
  4. Ask for the whole route in one go — "feed <some top-level value> into
    <subgraph name> so <inner step> can use it". It should end with the port
    wired on both sides: connected inside the subgraph to the step that uses it,
    and connected in the parent to the value you named. Ask it to validate
    afterwards — there should be no new errors, and in particular no unconnected
    port left behind.
  5. Same again with an output — it should appear as an output port on the subgraph
    step, and the route out should get wired end to end the same way.
  6. Ask for something with no location at all ("add a <component>") while looking
    at the top level — it should go to the top level, as before.
  7. Ask it to add something "inside" a step that isn't a subgraph — it should
    explain that step isn't a subgraph rather than silently doing something else.
  8. Open a pipeline with a validation problem on a top-level step, then open a
    subgraph before asking the chat to fix it. The fix should land on the top-level
    step, not inside the subgraph you happen to be looking at.
  9. Undo (Cmd/Ctrl+Z) after a nested add, and confirm the pipeline still saves.

Additional Comments

The earlier version of this PR also returned the subgraph trail on every single
edit so the assistant could quote it back. That's been dropped: the assistant
already knows which subgraph it targeted, because it passed the id, so the extra
field was duplicating information it had. It's still told to name the subgraph it
changed in its reply — the canvas doesn't move, so you need to be told where to
look.

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

🎩 Preview

A preview build has been created at: 08-31-fix_ai_subgraph_editing/c2f554c

@camielvs camielvs changed the title fix: AI Subgraph Editing feat: Let the AI edit inside subgraphs Aug 31, 2026
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_error_reporting branch from 2abbff4 to d2433d2 Compare August 31, 2026 23:47
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from cdbbd18 to 79f88d3 Compare August 31, 2026 23:47
@camielvs camielvs changed the title feat: Let the AI edit inside subgraphs feat: Let the AI add steps and ports inside a subgraph Aug 31, 2026
@camielvs
camielvs changed the base branch from 08-31-fix_ai_subgraph_error_reporting to graphite-base/2684 September 1, 2026 00:06
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from 79f88d3 to 180f28f Compare September 1, 2026 00:34
@camielvs
camielvs changed the base branch from graphite-base/2684 to 08-31-fix_ai_subgraph_error_reporting September 1, 2026 00:38
@camielvs
camielvs marked this pull request as ready for review September 1, 2026 00:45
@camielvs
camielvs requested a review from a team as a code owner September 1, 2026 00:45

@camielvs camielvs left a comment

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.

Reviewed this PR on its own base (08-31-fix_ai_subgraph_error_reporting). The bridge/tool code checks out: nested adds do surface as ports on the parent task (resolvedComponentSpec is a live proxy read inside observer renders), resolveDestination's subgraph guard cannot misfire, a nested add undoes cleanly and the doc round-trips byte-identical, and all 66 tests in toolBridge.test.ts / csomTools.test.ts pass.

The substantive risk is in the prompt guidance rather than the code. Inline comments below.

One finding with no line to attach to: src/routes/v2/shared/components/AiChat/serializeSpecForAi.ts:10 still asserts "Edits always target the root spec; the path is a hint, not a target" — which this stack makes the opposite of true. The file isn't in this diff, so it needs a touch either here or in #2683.


🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Comment thread src/agent/prompts/pipelineRepair.md Outdated
Comment thread src/agent/prompts/architect.md Outdated
Comment thread src/agent/prompts/architect.md Outdated
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from 180f28f to 1533df6 Compare September 1, 2026 18:49
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_error_reporting branch from b85983b to 45533d3 Compare September 1, 2026 18:49
@camielvs

camielvs commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

The unattached finding — the stale serializeSpecForAi.ts header claiming edits always target the root — is fixed in #2683, where the rest of that wording already changed.

@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from 1533df6 to df40629 Compare September 1, 2026 20:45
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_error_reporting branch from 45533d3 to 9b875f1 Compare September 1, 2026 20:45

@morgan-wowk morgan-wowk 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.

🤖 Automated review

Approving. Add-into-subgraph honors the same resolver/refusal contract as #2687: resolveDestination returns root for undefined, reuses resolveTarget(..., "task") otherwise, and refuses a non-subgraph or unknown destination — fail-closed. New entity ids use the shared global counter (unique within and across the target subgraph), and names are uniquified within the destination spec so adding a colliding name into a subgraph can't create a duplicate. activeSubgraphTaskId is sourced from the currently-viewed subgraph, so "add here" targets what the user is actually looking at. addTask hydrates then resolves against the current spec (fail-closed if the subgraph was detached mid-await). Good tests. Nit: minor whitespace misalignment in subgraphBridge.test.ts (the formatter will catch it).

Comment thread src/routes/v2/pages/Editor/components/AiChat/toolBridge/csomBridge.ts Outdated
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_error_reporting branch from 9b875f1 to a57d93b Compare September 4, 2026 00:10
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from df40629 to 4989dea Compare September 4, 2026 00:10

camielvs commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

Merge activity

  • Sep 4, 12:51 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Sep 4, 1:22 AM UTC: Graphite rebased this pull request as part of a merge.
  • Sep 4, 1:26 AM UTC: @camielvs merged this pull request with Graphite.

@camielvs
camielvs changed the base branch from 08-31-fix_ai_subgraph_error_reporting to graphite-base/2684 September 4, 2026 01:15
camielvs added a commit that referenced this pull request Sep 4, 2026
## Description

When you asked the AI assistant to change something inside a subgraph, it applied
the change to the top-level pipeline instead — whatever depth the thing you named
actually lived at. One cause, four ways of showing up:

- **Nothing happened.** The edit went looking at the top level, found nothing, and
  quietly did nothing — or worse, hit an unrelated top-level step that happened to
  match.
- **Connections could corrupt the pipeline.** Asked to wire something across a
  subgraph boundary, it wrote a connection pointing at a step that isn't in that
  graph. The pipeline format has no such connection, so the result was broken.
- **Deleting a connection lied.** It reported success while deleting nothing.
- **Nine tools couldn't explain a failure.** They could only answer "that didn't
  work", with no way to say whether the thing didn't exist or was a different kind
  of thing than expected — so the assistant guessed, and often told you something
  untrue.

Each edit now works out which graph owns the thing you named and changes that
graph. This turned out to be mostly plumbing that was already in place: every
editing operation already took the graph to act on as an argument, and the editor
itself has always used that to let you edit inside a subgraph. The assistant was
the only caller that always handed over the top-level pipeline.

The layer that answers "which graph, and is this the kind of thing you asked
for" — along with the wording of every refusal — now lives in #2687, directly
below. This PR is the rewiring: swapping each handler onto it, and the knock-on
fixes below.

Also fixed here, because they're the same underlying thing:

- **Renaming or deleting a subgraph's input no longer breaks the wiring above it.**
  The matching port on the subgraph step is renamed or removed with it, so the
  connection feeding it in the parent survives.
- **Validation reports where issues are.** Issues found inside subgraphs now carry
  the name and the trail to the subgraph they're in, so the assistant can act on
  them instead of reporting a problem it can't locate.
- **Setting a value can't reach across a subgraph boundary either.** Same
  corruption as the connection case above, through a different door: asked to set
  a step's input to a value coming from another graph, it used to write it and
  produce a pipeline that no longer loads. It's refused now, with the way round
  it.
- **Grouping needs at least two steps.** Asked to make a subgraph out of one step
  — or the same step named twice — it used to do it, despite saying elsewhere
  that it wouldn't.

Two refusals remain, and both are about structure rather than depth, so they're
correct rather than missing. Both now say what to do instead:

- A connection can't cross a subgraph boundary.
- A new subgraph can't be made from steps that live at different levels.

Where a refusal has a knowable cause, it now names it — a rename that collides
with an existing name, or being asked to unpack something that isn't a subgraph.
The vague "couldn't do that" is what's left when there's genuinely nothing to
say, rather than the standard answer.

**The canvas doesn't follow the assistant into a subgraph.** A single request can
produce several edits across several subgraphs, and yanking the view around for
each one would lose wherever you were. The assistant is told to name the subgraph
it changed, so you're told where to look rather than being taken there.

## Related Issue and Pull requests

Stacked on #2687#2686#2685#2655.

This PR and #2684 were re-cut. Previously the two split this fix down the middle:
the lower one described the failures and refused them, the upper one rewrote it to
actually fix them. Since all four symptoms above share one cause and one fix, that
seam meant ~635 lines were written twice and reviewed twice to net zero. The stack
is now split by concern instead: shared resolver (#2685), an unrelated chip bug
(#2686), this fix, then the new capability on top (#2684).

## Type of Change

- [x] Bug fix

## Checklist

- [ ] I have tested this does not break current pipelines / runs functionality
- [ ] I have tested the changes on staging

## Test Instructions

1. Open a pipeline with a subgraph in it (nested subgraphs are a better test) and
   ask the AI chat for edits inside it: "rename the second step in \<subgraph
   name>", "delete the connection between X and Y in \<subgraph name>", "set the
   input file on \<nested step>". Each should apply, and the reply should say which
   subgraph it changed.
2. Open the subgraph afterwards and confirm the change is there and looks right.
3. Undo (Cmd/Ctrl+Z) — a nested edit should undo like any other, and the pipeline
   should still save normally.
4. Rename an input inside a subgraph that the parent feeds a value to, then check
   the parent: the port should be renamed and still connected, not orphaned.
5. Ask for something genuinely impossible — "connect \<top-level step> straight to
   \<a step inside a subgraph>" — and it should explain that a connection can't
   cross a subgraph boundary and offer the way round it. Same for asking it to
   group a top-level step together with a nested one.
6. Ask it to change something that doesn't exist, and something of the wrong kind
   ("delete the connection \<id of a step>"). It should say what's actually wrong
   rather than inventing a reason. Same for renaming a step to a name already
   taken in that graph, and for "unpack \<a step that isn't a subgraph>".
7. Ask it to set an input on a nested step to a value that comes from the
   top-level pipeline — it should refuse and explain, not write it. Then ask it
   to group a single step into a subgraph, which it should also refuse.
8. Ask it to validate a pipeline that has problems inside a subgraph — it should
   report them and say where they are.
9. Sanity check that ordinary top-level editing through the chat is unchanged.

## Additional Comments

Nothing new was needed for undo or saving: both already watch the whole pipeline
including everything nested, which the editor has always relied on.

One pre-existing quirk left alone: renaming a subgraph step doesn't update the
name stored on the subgraph's own contents. Nothing depends on it, and the same
thing happens when you rename a subgraph step by hand in the editor, so it's not
new here — but it's worth a separate look at some point.
@camielvs
camielvs changed the base branch from graphite-base/2684 to master September 4, 2026 01:20
camielvs and others added 3 commits September 4, 2026 01:21
The PR below this one made the assistant edit existing entities wherever they
live. Creating something new still only worked at the top level, which left one
job impossible: getting a value into or out of a subgraph, since that needs a
new port on the subgraph itself.

`add_task`, `add_input` and `add_output` now take an optional `inSubgraphTaskId`.
Omitted, they behave exactly as before. Given a subgraph task's `$id`, the new
entity is created inside it — and an input or output added inside a subgraph
becomes a port on that subgraph task, which the parent can then wire up.

The assistant is told to read the user's "here" from the subgraph they are
looking at, so opening a subgraph and saying "add a filter step here" puts it in
the right place. An explicitly named subgraph still wins over what's on screen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Surface `activeSubgraphTaskId` alongside `activeSubgraphPath` so the model
has the `$id` `inSubgraphTaskId` needs — the breadcrumb holds display
names, which are unique only within one graph.

Hydrate the component reference before resolving the destination in
`addTask`, so an undo or navigation during the fetch cannot leave the new
task in a detached spec.

Spell out the inner `connect_nodes` step in the subgraph-boundary recipe
in both prompts; stopping at the port left a value reaching nothing plus
two new validation errors.

Stop the repair prompt defaulting additions to wherever the user is
looking — a repair adds to the graph that owns the entity being fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`add_task` resolved its destination against the live spec after awaiting
hydration, so opening another pipeline during that fetch sent the task
into the newly opened one and still reported success. The root is now
pinned before the await and the add is refused if it changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@camielvs
camielvs force-pushed the 08-31-fix_ai_subgraph_editing branch from a0f0be3 to c2f554c Compare September 4, 2026 01:21
@camielvs
camielvs merged commit c1cd8ab into master Sep 4, 2026
17 checks passed
@camielvs
camielvs deleted the 08-31-fix_ai_subgraph_editing branch September 4, 2026 01:26
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.

3 participants