Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions fixtures/react-parity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ The existing Angular packages and release group remain the production path.

The runtime owns immutable snapshots, request generations, stop/dispose, protected
errors, read-only reconciliation after uncertain failures and fixed function-tool
execution. Angular and
React borrow the app-owned session and observe it through their native lifecycles.
The installed consumers run seven scenarios each: inert mount, text, weather tool
execution. The private session also supports explicit fixed-thread history loading
when its transport can read history. The latest checkpoint replaces the transcript;
equal reads preserve identity, failures preserve the prior snapshot, and loaded
tools never execute. Core contracts and native binding implementations are unchanged.
Angular and React borrow the app-owned session and observe it through their native
lifecycles. The installed consumers run ten scenarios each: inert mount, explicit
history load, equal refresh, empty replacement, text, weather tool
roundtrip, protected error, held partial text and Stop, reuse after Stop, and
unmount followed by explicit disposal and an aborted post-disposal submission.
Five live component submissions plus one tool continuation produce six exact wire
requests, with one handler invocation and zero page errors or unexpected requests.
The three explicit loads make three history reads and no run requests or handler
calls. This is partial T10 coverage: thread switching, pagination, branching, full
backend state and interrupt resume remain outside this proof.

A local HTTP/SSE fixture serves production-built apps and writes real held response
bytes. Browser assertions observe incremental DOM text and a native response-close
Expand All @@ -26,11 +33,13 @@ exercise that subscription replay. See [runtime/README.md](./runtime/README.md)
reproduction and [runtime/evidence.json](./runtime/evidence.json) for fresh commands,
counts, source provenance, cleanup assertions and limitations.

The current inventory has **1,451 records**: the historical 1,438 plus eight private
The current inventory has **1,453 records**: the historical 1,438 plus ten private
runtime production sources, three testing helpers, a runtime Vitest config and its
type-test config asset. Public export occurrences remain 550 with 514 distinct local
definitions. Fourteen existing export records changed declaration/import text;
there are no legacy export-name additions or removals. Existing task assignments
definitions. The original runtime extraction changed fourteen existing export
records' declaration/import text; history loading adds two private sources and
changes no legacy public export records. There are no legacy export-name additions
or removals. Existing task assignments
are preserved; touched extraction/configuration subsets are in progress, not whole
T03–T16 completion. Core and native package contracts remain outside this legacy
inventory scope and are checked by their own tests and package gates.
Expand Down Expand Up @@ -153,7 +162,7 @@ type-checks the tarballs outside workspace aliases with `skipLibCheck: false`.
Its core-only consumer checks all three core exports and rejects extra dependencies.
The separate Angular check packs the one Angular APF entry and proves CLI
compilation/linking with `skipLibCheck: false`. Both frameworks now run installed
production browser apps with the seven shared scenarios. Inferred native contract
production browser apps with the ten shared scenarios. Inferred native contract
probes reject invalid tool names/arguments/results and deep mutations. The private
runtime's narrow declaration is compiler-generated against installed core declarations,
never hand-written; the staged SDK bundle is fixture-only. Both inspect consumer
Expand Down Expand Up @@ -207,14 +216,16 @@ burst streams and repeated agent/thread disposal.

## Maintenance and release

The foundation candidate is `codex/react-support-baseline`; the runtime candidate is
The foundation branch was `codex/react-support-baseline`; the runtime branch is
`codex/shared-runtime-quality`, based on `bdcc22ed31aa94f420077e046e88e1481088d453`.
The history-loading increment is `codex/langgraph-history-loading`; its verified
base and working-source fingerprint are recorded in `runtime/evidence.json`.
The local maintenance
branch `codex/angular-maintenance-v0.2` points to released tag `v0.2.0`
(`8daea78d35bfa27513474bd624d0e9495af3cfab`) and retains its released lockfile.
Creating that local branch does not establish an operated release lane: a maintainer
must own the backport/publication workflow before it is used. No Angular facade
currently depends on a new package. Version/tag enforcement, remote maintenance
must own the backport/publication workflow before it is used. The legacy Angular
package roots retain their existing production path. Version/tag enforcement, remote maintenance
policy and a tested backport/rollback remain T37 work. The existing release group
is unchanged, and none of the new private packages is publishable.

Expand All @@ -225,7 +236,8 @@ research documents. It is a scope map, not evidence that the tasks are complete.
T01/T02 describe the foundation increment. The current G1 proof is deliberately limited
to shared LangGraph text streaming and fixed function-tool execution with borrowed native
Angular and React bindings: the runtime owns execution while each binding observes
it. Renderer reuse and SSR are deferred gates, alongside the broader T01–T39 map.
it. Explicit fixed-thread history loading now covers a further subset of T10.
Renderer reuse and SSR are deferred gates, alongside the broader T01–T39 map.
This bounded runtime proof does not establish complete migration parity.

| Task | Scope |
Expand Down
40 changes: 33 additions & 7 deletions fixtures/react-parity/runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ readonly types directly on each binding's inferred snapshot.
the production `FetchStreamTransport`, and the real LangGraph SDK. A focused
TypeScript check resolves its public core imports against the installed tarball
declarations, then emits its narrow annotated `AgentSession<FixtureTools>` return
type. Vite bundles the private backend and SDK into temporary ESM, externalizing
type with an optional fixture `load` capability. Vite bundles the private backend
and SDK into temporary ESM, externalizing
`@threadplane/core` and `@threadplane/core/tools`. Only that JavaScript bundle and
entry declaration are copied into each installed consumer. No private TypeScript,
transitive private declarations, workspace aliases, or core/framework source is
Expand All @@ -47,25 +48,50 @@ transport owns its retry policy. Canonical updates may replace or remove pending
tool calls for the same assistant message while retaining other messages' calls
and completed results.

The native fixtures expose Send, Tool, Error, Hold and Stop buttons plus text,
status, error, tool result, delivery, submission and handler count outputs. A single app-owned
The private `LangGraphSession` offers `load({ signal })` only when its transport
supports history reads. Loading is explicit: construction, mount and subscription
perform no I/O. The latest checkpoint authoritatively replaces the transcript,
including deleted/reordered messages and shorter or empty corrections. Equal
reads preserve snapshot identity; unchanged explicit message IDs retain shared
immutable objects. Snapshots stay unchanged while loading and on read failure;
failures reject with protected diagnostics. Cancellation, supersession, stop and
disposal settle promptly even if a transport ignores abort, and stale reads cannot
publish. Loading is refused while execution, uncertain recovery, staged tool
results or asynchronous tool settlement/write work remains unresolved.

History is observation only: loading never executes pending tools. Execution
deduplication survives a load, while locally authored result provenance is cleared.
Persisted ToolMessage strings remain transcript text rather than becoming typed
handler results, including on later stream replay. This is a fixed-thread history
subset of T10, not thread switching, pagination, branching, full backend state,
interrupt resume, SSR, or a public LangGraph package cutover. Core public contracts
and the native binding implementations are unchanged.

The native fixtures expose Load, Send, Tool, Error, Hold and Stop buttons plus text,
transcript, load completion/error, status, tool result, delivery, submission and
handler count outputs. A single app-owned
session is created outside component lifetime and outside React's StrictMode
tree; owner buttons perform framework unmount and explicit session disposal.
React uses a Vite production build. Angular uses the existing consumer template's
installed Angular CLI application builder and real APF linking, with output in
`dist/consumer/browser` and input evidence from `dist/consumer/stats.json`.

Both built apps run the same seven browser scenarios in installed Playwright
Chromium: inert mount, successful text, a real local tool handler and exact
Both built apps run the same ten browser scenarios in installed Playwright
Chromium: inert mount, explicit history load, equal history refresh, empty history
replacement, successful text, a real local tool handler and exact
two-request result continuation, protected visible server error, held streaming
DOM updates and Stop, reuse after Stop, then unmount/dispose/post-disposal submission.
Five submissions through the component controls make exactly six run requests
(including one tool continuation) and call the handler once. The separate
post-disposal submit attempt resolves aborted without making a request.
Request bodies check the catalog and actual serialized ToolMessage payload.
Three explicit Load clicks make exactly three history reads with `{ limit: 10 }`
and no run requests or handler calls. Every completed load must leave its visible
error output empty, so retained text cannot conceal a failed equal refresh. Both
registered handlers increment the same counter if executed. Request bodies check
the catalog and actual serialized ToolMessage payload.

A small in-process HTTP fixture serves only built artifacts and the expected
LangGraph run route on dynamic port 0. The held response writes an actual SSE
LangGraph run/history routes on dynamic port 0. The held response writes an actual SSE
assistant chunk and stays open. The test observes partial DOM text and streaming
delivery before pressing Stop, then awaits the server response-close handshake
and aborted delivery. This proves incremental DOM updates and native request abort in
Expand Down
16 changes: 15 additions & 1 deletion fixtures/react-parity/runtime/angular-app.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component } from '@angular/core';
import { Component, signal } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { observeAgent } from '@threadplane/angular';
import { createFixtureSession } from './runtime-entry.js';
Expand All @@ -14,13 +14,17 @@ const submit = (input: string) => { submissions += 1; return session.submit(inpu
standalone: true,
template: `
<main>
<button [disabled]="!canLoad" (click)="load()">Load</button>
<button (click)="submit('Send')">Send</button>
<button (click)="submit('Tool')">Tool</button>
<button (click)="submit('Error')">Error</button>
<button (click)="submit('Hold')">Hold</button>
<button (click)="stop()">Stop</button>
<output aria-label="Status" data-testid="status">{{ snapshot().status }}</output>
<output aria-label="Text" data-testid="text">{{ view().text }}</output>
<output aria-label="Transcript" data-testid="transcript">{{ view().transcript }}</output>
<output aria-label="Loads finished" data-testid="loads-finished">{{ loadsFinished() }}</output>
<output aria-label="Load error" data-testid="load-error">{{ loadError() }}</output>
<output aria-label="Error" data-testid="error">{{ view().error }}</output>
<output aria-label="Tool result" data-testid="tool">{{ view().tool }}</output>
<output aria-label="Delivery" data-testid="delivery">{{ view().delivery }}</output>
Expand All @@ -30,6 +34,16 @@ const submit = (input: string) => { submissions += 1; return session.submit(inpu
`,
})
class App {
readonly canLoad = !!session.load;
readonly loadsFinished = signal(0);
readonly loadError = signal('');
async load() {
if (!session.load) return;
this.loadError.set('');
try { await session.load(); }
catch { this.loadError.set('History unavailable'); }
finally { this.loadsFinished.update((count) => count + 1); }
}
readonly snapshot = observeAgent(session);
readonly view = () => display(this.snapshot());
readonly handlerCalls = () => handlerCalls;
Expand Down
Loading
Loading