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
24 changes: 15 additions & 9 deletions fixtures/react-parity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ tools never execute. Private snapshots now include broad readonly application
`values`, published atomically with messages. Full root state, history and conclusive
correlated recovery replace the map; unchanged nested data retains identity, and
token-only updates reuse it without traversal. `undefined` is unobserved state and
`{}` an observed empty map. Child/update/custom/live-interrupt envelopes are ignored.
`{}` an observed empty map. Child/update/custom/live-interrupt envelopes do not
replace application values. A separate readonly interrupt batch now joins the same
aggregate: root controls accumulate full payloads, and checkpoints/history replace
them. An empty `__interrupt__` array represents a static breakpoint; explicit empty standalone
batches clear interrupts. Observation offers no resume or target-selection API.
Core contracts are unchanged; native structural signatures preserve the concrete
snapshot extension and tool inference, with the same receiver and lifetime behavior.
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
lifecycles. The installed consumers run eleven 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
roundtrip, protected error, held partial text and Stop, full pause batch retained
after 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
Six live component submissions plus one tool continuation produce seven 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 T09/T10 coverage: thread switching, pagination, branching,
Expand All @@ -40,12 +45,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,455 records**: the historical 1,438 plus twelve private
The current inventory has **1,456 records**: the historical 1,438 plus thirteen 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. The original runtime extraction changed fourteen existing export
records' declaration/import text; history loading and values observation each add
two private sources and change no legacy public export records. There are no legacy export-name additions
two private sources; interrupt observation adds one. These increments change 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
Expand Down Expand Up @@ -169,7 +175,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 ten shared scenarios. Inferred native contract
production browser apps with the eleven 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 @@ -225,7 +231,7 @@ burst streams and repeated agent/thread disposal.

The foundation branch was `codex/react-support-baseline`; the runtime branch is
`codex/shared-runtime-quality`, based on `bdcc22ed31aa94f420077e046e88e1481088d453`.
The current values increment is `codex/langgraph-state-values`; its verified
The current interrupt increment is `codex/langgraph-interrupt-observation`; 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`
Expand All @@ -243,7 +249,7 @@ 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. Explicit history loading and application-values observation cover further
it. Explicit history loading, application-values and interrupt observation cover further
subsets of T09/T10.
Renderer reuse and SSR are deferred gates, alongside the broader T01–T39 map.
This bounded runtime proof does not establish complete migration parity.
Expand Down
42 changes: 35 additions & 7 deletions fixtures/react-parity/runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ versions come from the root lockfile. Contract probes compile the installed
public entries with `strict` and `skipLibCheck:false`, standard DOM signals, and
no workspace aliases. Negative probes check names, arguments, results and deep
readonly types directly on each binding's inferred snapshot, including broad
backend values without application-schema inference.
backend values and interrupt metadata/payloads without application-schema inference.
The installed fixture shape uses core types only; the compiler-emitted factory
declaration and native inferred result are checked together, including tool types.

`runtime-entry.ts` is development-only composition around private `createSession`,
the production `FetchStreamTransport`, and the real LangGraph SDK. A focused
Expand Down Expand Up @@ -72,6 +74,26 @@ retaining typed tool results, method receivers and borrowed lifetime semantics.
The factory and snapshot extension remain private; state writes, application
schema inference, SSR and package-root cutover remain outside this slice.

`LangGraphSnapshot.interrupts` now observes a readonly batch in that same immutable
aggregate. Each item retains SDK interrupt metadata and an owned plain payload;
neither metadata nor payload is a resume command or an inferred application schema.
Root `values`/`updates` controls must have an own array-valued `__interrupt__` field.
Separate dynamic batches accumulate: the first occurrence of a string ID wins,
while anonymous entries stay distinct. An empty `__interrupt__` array replaces
the batch with the static breakpoint sentinel `{ when: 'breakpoint' }`; the next
dynamic batch replaces that sentinel. An explicit empty standalone `interrupts`
batch clears the observed batch.

Authoritative checkpoints and latest history replace interrupts. A valid values
control takes precedence; otherwise all top-level task interrupt arrays contribute.
Child namespaces, nested task state and `next` alone do not establish a root pause.
History pause delivery is derived from the same candidate as messages, values and
interrupts. Equal refreshes retain identity, and values-only refreshes share the
unchanged interrupt batch. An accepted new submission clears the prior batch; stop, disposal
and failures retain the last observed batch. Observation adds no implicit I/O,
resume or target-selection API. Existing getter/stale-candidate guards, recovery
correlation, tool handoff and next-user-input behavior remain covered.

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,
Expand All @@ -91,21 +113,22 @@ subset of T10, not thread switching, pagination, branching, state writes,
interrupt resume, SSR, or a public LangGraph package cutover. Core public contracts
are unchanged; the native signatures now retain the concrete snapshot extension.

The native fixtures expose Load, Send, Tool, Error, Hold and Stop buttons plus text,
transcript, values, load completion/error, status, tool result, delivery, submission and
The native fixtures expose Load, Send, Tool, Error, Hold, Pause and Stop buttons plus text,
transcript, values, interrupts, 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 ten browser scenarios in installed Playwright
Both built apps run the same eleven 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
DOM updates and Stop, the full pause batch retained after Stop, reuse after Stop,
then unmount/dispose/post-disposal submission.
Six submissions through the component controls make exactly seven run requests
(including one tool continuation) and call the handler once. The separate
post-disposal submit attempt resolves aborted without making a request.
Three explicit Load clicks make exactly three history reads with `{ limit: 10 }`
Expand All @@ -116,7 +139,12 @@ the catalog and actual serialized ToolMessage payload.
Values assertions distinguish unobserved from empty state, show loaded application
fields, and verify replacement/deletion across root, tool, held and reused runs.
Separate native component tests make four history reads to cover a values-only
refresh with unchanged messages; installed browser scenarios still make three.
refresh with unchanged messages and interrupts; installed browser scenarios still
make three. History fixtures contain two separate task payloads and show paused
delivery. The Pause button sends two separate root controls and renders both
payloads; Stop retains them without another request, and the next Send clears them.
Unrelated custom/child noise does not contain a root empty control, because an
actual empty root `__interrupt__` is a static breakpoint rather than noise.

A small in-process HTTP fixture serves only built artifacts and the expected
LangGraph run/history routes on dynamic port 0. The held response writes an actual SSE
Expand Down
2 changes: 2 additions & 0 deletions fixtures/react-parity/runtime/angular-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ const submit = (input: string) => { submissions += 1; return session.submit(inpu
<button (click)="submit('Tool')">Tool</button>
<button (click)="submit('Error')">Error</button>
<button (click)="submit('Hold')">Hold</button>
<button (click)="submit('Pause')">Pause</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="Application values" data-testid="values">{{ view().values }}</output>
<output aria-label="Interrupts" data-testid="interrupts">{{ view().interrupts }}</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>
Expand Down
Loading
Loading