Skip to content

feat(examples/chat): /hero route — replayed LangGraph walkthrough with live takeover - #976

Merged
blove merged 24 commits into
mainfrom
blove/homepage-rebuild-spec
Sep 3, 2026
Merged

feat(examples/chat): /hero route — replayed LangGraph walkthrough with live takeover#976
blove merged 24 commits into
mainfrom
blove/homepage-rebuild-spec

Conversation

@blove

@blove blove commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the /hero route to the canonical demo: a recorded LangGraph run replayed through the real <chat> with a scripted cursor, handing off to the live agent the moment a visitor touches the surface. This is the demo-side half of the homepage rebuild; the website hero (HeroDemo) that embeds it is the next PR.

  • HeroReplayTransport plays public/hero-replay.json (3 runs, 367 kB, served as a static asset) pacing events by recorded gaps clamped to 30–600 ms.
  • HeroScriptRunner drives the walkthrough through a host interface with a generation token, so a superseded run cannot resume and fight the visitor; poll loops time out and every error path lands in an explicit state.
  • Takeover via the "Take control" pill, pointerdown, or focusin swaps to a fresh live LangGraph thread. The status pill and banner never hide that the walkthrough was a recording. "Replay walkthrough" returns to the replay agent.
  • postMessage bridge posts { type: 'tplane-hero', state } and accepts { type: 'tplane-hero', visible } only from allowlisted parent origins (threadplane.ai, www, localhost:3000, 127.0.0.1:4308, *.vercel.app); it also learns the parent origin from the first allowlisted message so a strict Referrer-Policy cannot silently disable the handshake.
  • The fixture was recorded through an aimock-backed .record.ts script — no API key. A companion record-hero-poster.record.ts captured the website poster (apps/website/public/screenshots/hero-walkthrough-poster.webp, 1200×720, 37 kB).
  • Also on the branch: the approved design spec and the two implementation plans under docs/superpowers/.

Deviations from the plan

  • Two provideAgent(ref, …) calls at one injector level alias the same AGENT token (libs/langgraph/src/lib/agent.provider.ts), so each hero agent is created in its own child EnvironmentInjector (scopedAgent()). Worth a follow-up in the adapter.
  • DOM selectors and cursor geometry live in hero-dom-host.ts so they are unit-tested against a mounted <chat>; a lib aria-label rename now fails a test instead of the homepage.
  • e2e/fixtures/interrupt-approval.json gained a hasToolResult: true entry ahead of the plain one so the post-approval turn does not re-issue request_approval (the existing @drift spec still passes).

Verification

  • nx test examples-chat-angular: 175/175 (23 files)
  • nx lint examples-chat-angular: 0 errors
  • nx build examples-chat-angular --configuration=production: green; hero is a lazy chunk (17.97 kB raw / 5.87 kB transfer), zero hero code in any initial chunk. The 30 kB initial-budget warning is pre-existing (hero contributes ~0.25 kB).
  • Playwright: hero.spec.ts 3/3 plus interrupt-approval.spec.ts 1/1
  • Screenshot walkthrough at 1200×720: typing → interrupt panel with cursor at Accept → streamed plan → A2UI contact form → clean loop restart → live takeover with banner and chips → replay → reduced-motion instant typing. All correct.

What the website PR must honor

HeroDemo posts visible on iframe load and after the frame reports ready, with targetOrigin https://demo.threadplane.ai, and listens for ready | live | replay.

🤖 Generated with Claude Code

blove and others added 22 commits September 2, 2026 16:45
…demo, install dialog

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…apter and demo APIs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ation plans

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…mped pacing

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e hero fixture

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ansport

joinStream's no-op fallback used an empty async generator, which trips
@typescript-eslint's require-yield rule. Replace it with a plain
AsyncIterable object instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a host interface

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…list

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rator

Constructor params are interface/function typed, so DI metadata is
inert; the class is provided via useValue/useFactory instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ister runs lazily, reject empty runs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…takeover to live LangGraph

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… error path; bridge source check and preview origins

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds the recording harness (record-hero.config.ts + record-hero-fixture.record.ts)
that drives /hero?record=1 against the aimock-backed LangGraph backend and writes
public/hero-replay.json, plus a spec that guards the committed fixture.

Two blockers the recording surfaced:

- HeroMode.boot() bailed out before starting the script runner whenever the
  replay fixture failed to load. In record mode the fixture is the artifact
  being produced, so it is always missing on the first pass — record mode now
  falls through and drives the live agent.
- The interrupt-approval aimock fixture had no `hasToolResult: true` entry, so
  the post-resume turn re-matched the tool-call fixture and the approval looped
  forever. Adds the narration entry ahead of it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…per-instance agents, DOM host extraction and tests

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…eplay stop, clear composer on takeover

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…st allowlisted message

Under a strict Referrer-Policy the referrer is empty and the frame could never
post ready. The bridge now also learns the origin from the first allowlisted
inbound message and replays the latest state; the website plan posts visibility
on iframe load so that handshake happens. Also fixes the poster script's
documented command and the poster alt text in the website plan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
threadplane Ready Ready Preview Sep 3, 2026 4:30am UTC

Request Review

@blove
blove enabled auto-merge (squash) September 3, 2026 03:43

@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 Sep 3, 2026

Copy link
Copy Markdown
Contributor

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


I'll analyze this and get back to you.

…trict consumers

The packaged-consumer smoke compiles the demo app under noImplicitAny; the
Array.isArray narrowing left two forEach parameters implicitly any.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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


I'll analyze this and get back to you.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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


I'll analyze this and get back to you.

@blove
blove merged commit 471855d into main Sep 3, 2026
36 checks passed
blove added a commit that referenced this pull request Sep 3, 2026
#976 landed 23 files under examples/chat/angular while this PR was open;
they were written before the header was dropped and carried it in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 3, 2026
* chore: remove SPDX-License-Identifier headers repo-wide

The repo is uniformly MIT — root LICENSE, all six libs/*/LICENSE.md, and
every one of the 130 package.json manifests. Nothing lints, tests, or
otherwise enforces the per-file header, so it was 1,300+ lines of
duplicated licensing metadata that new files had to remember to carry.

Removes the header from the top of every source file (.ts/.tsx/.mjs/.py/
.css/.sh/.md), and from the three generators that stamped it into
generated output (both inject-env.mjs scripts and the aimock scaffold
generator) so it does not come back. Also drops it from the code samples
in the human-in-the-loop blog post.

Left alone: docs/superpowers plans and specs, which mention the header
(and the old PolyForm identifier) in prose as a record of past work.

Verified: lint/test/build green for chat, langgraph, ag-ui, render, a2ui,
telemetry; nx test scripts; website lint + test; verify-release-versions,
check-dx-coverage, verify-angular-support, and the node:test smoke specs;
all 72 touched Python files compile.

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

* chore: sweep SPDX headers off the new /hero files

#976 landed 23 files under examples/chat/angular while this PR was open;
they were written before the header was dropped and carried it in.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 4, 2026
…ialog

Rebuilds the top of threadplane.ai and reorders the bottom, per
docs/superpowers/specs/2026-09-02-homepage-rebuild-design.md. The demo-side
/hero route this embeds shipped in #976.

Rewritten: stacked category hero, InstallDialog (fake/LangGraph/AG-UI),
HeroDemo (server-rendered poster as the LCP, origin-checked postMessage
bridge to demo.threadplane.ai/hero, poster fallback), LogoRibbon into three
labelled compatibility rows, HomeFAQ, PilotBlock, FinalCTA.

New: RuntimeParity, ThreeSteps, CodingAgentQuickstart, ScopeTable, a shared
Modal primitive, and a Try-without-a-backend docs page verified from a clean
ng new application.

All homepage copy, install commands and snippets are single-sourced in
src/lib/positioning.ts behind drift guards that resolve package names and
peers against libs/*/package.json, parse every snippet through the TypeScript
compiler, and pin the license word and Angular range to their manifests.

Adopts main's decisions where they superseded the spec: the Yes wall stays
deleted and the reliability band keeps its slot and styling (#996), Promises
stays unmounted, and the telemetry FAQ answer uses the privacy-policy
language the public-copy contract requires (#980).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 4, 2026
…ialog (#997)

Rebuilds the top of threadplane.ai and reorders the bottom, per
docs/superpowers/specs/2026-09-02-homepage-rebuild-design.md. The demo-side
/hero route this embeds shipped in #976.

Rewritten: stacked category hero, InstallDialog (fake/LangGraph/AG-UI),
HeroDemo (server-rendered poster as the LCP, origin-checked postMessage
bridge to demo.threadplane.ai/hero, poster fallback), LogoRibbon into three
labelled compatibility rows, HomeFAQ, PilotBlock, FinalCTA.

New: RuntimeParity, ThreeSteps, CodingAgentQuickstart, ScopeTable, a shared
Modal primitive, and a Try-without-a-backend docs page verified from a clean
ng new application.

All homepage copy, install commands and snippets are single-sourced in
src/lib/positioning.ts behind drift guards that resolve package names and
peers against libs/*/package.json, parse every snippet through the TypeScript
compiler, and pin the license word and Angular range to their manifests.

Adopts main's decisions where they superseded the spec: the Yes wall stays
deleted and the reliability band keeps its slot and styling (#996), Promises
stays unmounted, and the telemetry FAQ answer uses the privacy-policy
language the public-copy contract requires (#980).

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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