Skip to content

feat(langgraph)!: classify any namespaced event as child content - #844

Open
blove wants to merge 2 commits into
mainfrom
blove/child-namespace-classification
Open

feat(langgraph)!: classify any namespaced event as child content#844
blove wants to merge 2 commits into
mainfrom
blove/child-namespace-classification

Conversation

@blove

@blove blove commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Implements the library-level fix behind the transcriptNodeNames workaround that #838 shipped and three docs PRs (#839/#841#843) documented. Breaking change, deliberately: best architecture over compatibility.

The inconsistency this removes

The bridge answered "is this event the parent's?" three different ways:

Site Old test Consequence
Terminal-evidence guard any namespace correct
Transcript merge tools: only plain-child tokens leaked into messages() mid-stream
values/updates routing tools: only a plain child's values event replaced the parent's values$; its updates spread-merged into it

Now there is one classification, answered once: an event with any namespace belongs to a child graph. Which child owns it is a separate (attribution) question.

What changes

Child content never merges into the parent transcript. Not opt-out — structural. Shared-state children still arrive at settle via the authoritative top-level values sync, so no content is lost, only the accidental mid-stream preview. The leak class #842 documented ("mid-stream bug with a clean end state") is now impossible rather than configurable.

filterSubagentMessages is removed (breaking). Exclusion is the semantic, so the option has nothing left to govern. Survey before removal: zero consumers set it anywhere in-repo, and its documented behavior was already a no-op for the plain-node shape.

Plain subgraph children appear in subagents(). Keyed by namespace segment (unique per invocation), named by node prefix, registered on first streamed event, settled by the run's terminal outcome in finalizeAttempt (success→complete, error/aborted→error; paused/interrupted leave them running — a child can resume with the thread). This reverses the freshly-documented limitation — the docs described a real gap honestly, and removing the gap beats documenting it.

Attribution ladder scoped to tool children. The last-resort "any unmapped pending/running subagent" fallback could otherwise absorb a plain child's first message into an unrelated pending tool call. Pinned by a test.

Tool-path behavior is byte-equivalent. Same key extraction, same addMessageToSubagent, same ladder, same settle-by-tool-result.

Downstream sweep (same PR so docs never contradict the lib)

  • cockpit/langgraph/subgraphs: sidebar gains a "Child streams" section fed by subagents() — the same child shown as state boundary (value()) and as stream. Its e2e now asserts research — complete renders, which exercises the new tracker path against a real langgraph server.
  • Docs: subgraphs guide (warning callout → how it actually works), provide-agent (option row + workaround paragraph), agent-architecture, langgraph-basics, the blog post's two stale sections, the example's guide/prompts/docstrings. transcriptNodeNames docs updated — its "top-level" scope is now literally true, and it keeps its real job (routers, title generators).
  • api-docs.json regenerated.

Verification

  • Lib unit: 340/340, including 4 new pinning tests. Mutation-tested: narrowing classification back to tools: fails exactly those 4.
  • e2e: cockpit-langgraph-subgraphs 3/3 (incl. the new child-stream assertion), cockpit-chat-subagents 1/1, cockpit-deep-agents-subagents 1/1, examples/chat full suite 54/54 (research-subagent, itinerary, client-tools flows all under the new semantics).
  • Live model, Chrome, both shapes (the streaming-shape smoke gate):
    • Subgraphs: transcript never exceeded 2 messages at any sampled point (timeline 12); child stream showed research — running mid-flight, research — complete at settle; topic + 2.5k-char brief populated.
    • Chat subagents: all three tool cards render and complete, parent summary streams, 5-message transcript is all parent turns.
  • Wire check on the tool path: 1742 messages|tools:<id> events captured; the namespace carries an internal UUID, not the tool-call id, so early-chunk drops before ladder attribution ("0 message(s)" on cards) reproduce on the untouched code path — pre-existing, not a regression from this PR.
  • Website build green, blog specs 12/12, example production build green, lint 0 errors.

Breaking-change notes for consumers

  • filterSubagentMessages is a compile-time error on upgrade — delete it; the behavior it opted into is now the default (and broader).
  • A UI that (accidentally) relied on child tokens streaming into messages() should render them from subagents() instead.
  • subagents() may now contain more entries; tool children are distinguishable by their call_*-style keys and subagent_type names.

Per repo policy this ships as a patch bump (0.0.x always patches).

🤖 Generated with Claude Code

blove and others added 2 commits August 28, 2026 17:26
One classification question, answered once: an event with any namespace
belongs to a child graph. Consistent with the terminal-evidence guard,
which has always refused ANY namespaced event — the transcript merge was
the only site still using the narrow tools:-only test.

- Child message events route to their child stream and never merge into
  the parent transcript (kills the mid-stream leak class structurally)
- A child's values/updates no longer replace or spread-merge into the
  parent's values$
- Plain subgraph children now appear in subagents(), keyed by namespace
  segment, named by node prefix, settled by the run's terminal outcome
- filterSubagentMessages removed (exclusion is the semantic, not an option)
- Attribution ladder scoped to tool children so a plain child can never
  be absorbed by an unrelated pending tool call

340/340 lib tests; classification mutation-tested (narrowing it back to
tools: fails exactly the 4 new pinning tests).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sweeps every surface that taught the old limitation: the subgraphs guide's
warning callout (now describes where child tokens actually go), the
provide-agent option table and workaround paragraph, agent-architecture,
langgraph-basics, the blog post's two stale sections, and the cockpit
example's prompts/guide/docstrings.

The cockpit example's sidebar gains a 'Child streams' section fed by
agent.subagents() — the same child shown as state boundary (value()) and
as stream, and the e2e asserts 'research — complete' renders, which
exercises the new tracker path against a real langgraph server.

api-docs regenerated (option removed, transcriptNodeNames doc updated).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
threadplane Ready Ready Preview Aug 29, 2026 12:54am

Request Review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated approval: this PR received an intelligent (AI) code review. See the review comments on this PR.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @blove's task in 0s —— View job


I'll analyze this and get back to you.

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.

1 participant