feat(journeys,core): buildInput, runtime.goBack, state hooks, typed step/simulator output#38
Conversation
…ed step/simulator output Six API additions surfaced by autopilot's first journey-adoption flow (EXP-1848) that previously had to be hand-rolled at the consumer: - `ModuleEntryPoint.buildInput?: (state) => TInput` — runtime re-derives a step's input from journey state at every entry (initial, push, pop, resume), so back-navigated forms see accumulated state instead of the snapshot frozen at first push. - `JourneyRuntime.goBack(id)` on the public runtime so a shell wiring the browser back button doesn't have to capture the active step's closure through a React context. - `useJourneyState<T>(id)` and `useActiveLeafJourneyState<T>(rootId)` hooks built on `useSyncExternalStore`; the leaf variant follows `activeChildId` and re-subscribes as the chain shifts. - `JourneyStepFor<TModules>` — discriminated-union step shape so simulator step / history reads narrow input by entry without casts. - `simulateJourney` fourth generic `TOutput` so journeys with a typed terminal payload are assignable without `as unknown as Parameters<…>`. Internal: the leaf-chain bookkeeping that lived in `outlet.tsx` (`useInstanceSnapshot`, `useCallChain`, `useLeafId`) moves to `instance-hooks.ts` and is shared with the new hooks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rneyStepFor Follow-up to the runtime additions: README sections covering the new authoring + runtime + testing surface so consumers can find them without reading the changelog. - Entry-points table + new "Pattern - `buildInput` for re-entered forms" walking through state-derived input and the headless-test wiring (`SimulateJourneyOptions.modules`). - Runtime methods table adds `goBack(id)` with the same no-op guards the `goBack` prop already documents. - Rendering + context table adds `useJourneyState` and `useActiveLeafJourneyState`. - `simulateJourney` API entry calls out the new `TOutput` generic and the `JourneyStepFor`-typed `step` / `currentStep` / `history`. - TS inference notes mention `JourneyStepFor` alongside `StepSpec` so readers know which to reach for when asserting on a step snapshot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedAn error occurred during the review process. Please try again later. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds per-entry optional buildInput (recomputes step.input on entry/re-entry and same-step state changes), a JourneyRuntime.goBack(id) API, instance-chain snapshot hooks and public state hooks, typed JourneyStepFor/JourneyStep, simulator typing/options, tests, and docs updates. ChangesJourney Runtime Enhancements: Step Input, Navigation, State Hooks, and Typed Simulation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/journeys/src/types.ts (1)
7-23:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove the unused
JourneyStepFortype import.Line 13 imports
JourneyStepFor, but it isn’t used locally (it’s re-exported directly at Line 45), which triggers lint noise.Suggested diff
import type { AbandonCtx, CatalogMeta, JourneyHandleRef, JourneyPersistence, JourneyStep, - JourneyStepFor, ModuleTypeMap, ResumeMap, SerializedJourney, StepSpec, TerminalCtx,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/journeys/src/types.ts` around lines 7 - 23, Remove the unused JourneyStepFor import from the import list in types.ts: currently JourneyStepFor is imported along with other types but not used locally (it's re-exported later), so delete JourneyStepFor from the import statement to silence the linter while leaving the re-export (export type { JourneyStepFor }) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/journeys/README.md`:
- Around line 2303-2314: The docs table lists a goBack(id) API but the
JourneyRuntime TypeScript interface omitted it; update the JourneyRuntime
interface to include a goBack method with the correct signature (matching the
docs table) so the interface and docs stay in sync—locate the JourneyRuntime
declaration (around where methods like start, hydrate, getInstance, subscribe,
end, forget are declared) and add a goBack(id: InstanceId): void (or the
project-appropriate return type) entry.
---
Outside diff comments:
In `@packages/journeys/src/types.ts`:
- Around line 7-23: Remove the unused JourneyStepFor import from the import list
in types.ts: currently JourneyStepFor is imported along with other types but not
used locally (it's re-exported later), so delete JourneyStepFor from the import
statement to silence the linter while leaving the re-export (export type {
JourneyStepFor }) intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e9df6ea4-8b7c-4b80-bfa0-882a52391a77
📒 Files selected for processing (16)
CHANGELOG.mdpackages/core/src/index.tspackages/core/src/journey-contracts.tspackages/core/src/types.tspackages/journeys/README.mdpackages/journeys/src/build-input.test.tspackages/journeys/src/index.tspackages/journeys/src/instance-hooks.tspackages/journeys/src/outlet.tsxpackages/journeys/src/runtime-go-back.test.tspackages/journeys/src/runtime.tspackages/journeys/src/simulate-journey.test-d.tspackages/journeys/src/simulate-journey.tspackages/journeys/src/types.tspackages/journeys/src/use-journey-state.test.tsxpackages/journeys/src/use-journey-state.ts
- Drop unused `JourneyStepFor` import in journeys/src/types.ts (it was re-exported in the export block but never referenced locally — lint noise). - Add `goBack(id: InstanceId): void` to the `JourneyRuntime` interface snippet in the README so the inline interface matches the methods table further down. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…afJourneyInstance, better discoverability Three follow-ups from autopilot's EXP-1848 adoption work: - `SimulateJourneyOptions.modules` typed as `Record<string, ModuleDescriptor<any, any, any, any>>` (matching `JourneyRuntimeOptions.modules`) so apps with a custom `TNavItem` don't need `as unknown as AnyModuleDescriptor` casts at the call site. The bivariant `any` on every generic absorbs the difference. - New `useActiveLeafJourneyInstance(rootId)` hook returning the full leaf `JourneyInstance` so hosts that need `step` / `status` / `terminalPayload` from the leaf don't have to pair `useActiveLeafJourneyState` with `useJourneyCallStack` + `runtime.getInstance(leafId)`. `useActiveLeafJourneyState` now delegates to it. - README "Pattern - `buildInput` for re-entered forms" gains a callout reminding test authors to always pass `options.modules` to `simulateJourney`; without it `buildInput` silently falls back to the cached handler input and contract-validation warnings surface in test output. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kibertoad
left a comment
There was a problem hiding this comment.
Reviewed for architecture fit, design, perf, and correctness. Net read: this is a well-scoped, additive PR — six APIs that replace three hand-rolled consumer patterns, with reasonable tests and docs. Most comments below are about polish; the substantive items are (1) a possible behaviour gap with the documented "buildInput fires on every entry" promise on the invoke-resume path, (2) the state: unknown type-safety leak in buildInput, (3) unhandled buildInput throws, and (4) the silent override of the handler-supplied input.
Non-inline note — the test plan still has the autopilot EXP-1848 consumer-side validation unchecked. Six new public APIs are hard to retract once published; worth confirming the actual replacement of ProjectCreationStateContext / useJourneyInstance / JourneyBackHandlerContext lands end-to-end before merging, in case the API shape needs another pass.
| * first push. Returns the original `step` reference when no factory | ||
| * is declared — caller-facing identity matters for snapshot bail-out. | ||
| */ | ||
| function withBuiltInput(step: JourneyStep, state: unknown): JourneyStep { |
There was a problem hiding this comment.
Three concerns clustered on this helper, all worth thinking about together:
1. Possible gap with the "every entry" promise on invoke-resume. PR body and README claim buildInput runs on "initial start, forward push, goBack pop, and re-entry after a child journey returns." I see three callsites — here at line 1171 (applyTransition → next), line 1628 (dispatchGoBack), and line 1773 (startInstance). The invoke-resume path only fires buildInput if the parent's resume handler returns { next: … }. If resume just bumps state (as use-journey-state.test.tsx's fixture does: back: ({state}) => ({ state: { counter: state.counter + 10 } })), the parent's step stays put and step.input is the cached snapshot. That's a real-feeling case for a parent step that wants to refresh after a child returns. Either close the gap (re-run withBuiltInput against the parent's current step on resume even when step doesn't change) or tighten the docs to "every step-changing entry".
2. Throws are unhandled. entry.buildInput(state) is called synchronously and any throw bubbles through applyTransition / dispatchGoBack / startInstance into the original caller (popstate handler, component click, runtime.start). Instance may end up half-transitioned. The runtime already wraps transition handlers carefully; buildInput deserves the same treatment — try/catch into an abort with a typed reason, or at minimum a debug-mode log so the failure mode is observable.
3. Step identity. When buildInput is declared, this always allocates a fresh step object (and a fresh input) on every entry. That defeats reference equality for consumers diffing instance.step in onTransition / memoization. Probably fine — the doc-comment already calls out that identity is preserved when no factory exists, which is the important case — but worth a one-liner noting this in the buildInput docs.
|
|
||
| if ("next" in result) { | ||
| const nextStep = stepFromSpec(result.next); | ||
| const nextStep = withBuiltInput(stepFromSpec(result.next), record.state); |
There was a problem hiding this comment.
buildInput's silent override of the handler-supplied next.input is action-at-a-distance: an author who adds buildInput later won't get any signal that the input they're carefully constructing in transitions just became dead code. The README documents the override, but consider either:
- a dev-only warning when both are set and produce different values, or
- making the override explicit at the type level (e.g.
next.inputbecomes optional / nullable when the target entry declaresbuildInput).
The "stamp null / a placeholder" guidance in the README is a smell that the contract leaks.
There was a problem hiding this comment.
Worth noting I took the dev-only warning route. Test in build-input.test.ts.
Holding off on making next.input conditionally optional at the type level. It would require StepSpec to map over each entry's buildInput field to flip input between required and optional. Doable but invasive enough that I'd rather want it on its own PR with the type-test additions to cover it.
Twelve clustered review comments addressed in one pass.
Runtime
- buildInput now re-fires on same-step state changes too (an `{ invoke }`
arm carrying `state`, or a resume that bumps state without advancing
the parent step). Closes the gap between the docs' "every entry"
promise and the previous "every step-changing entry" reality. Covered
by a new test that drives a state-bumping resume and asserts the
parent's `step.input` reflects the post-resume state.
- buildInput throws now abort with a new typed
`JourneySystemAbortReason` code `"build-input-threw"` (carries
`moduleId`, `entry`, `error`) instead of bubbling through
applyTransition / dispatchGoBack / startFresh and leaving the
instance half-transitioned. Mirrors how transition-handler throws are
already treated. Test asserts the aborted terminalPayload shape.
- Dev-mode warning when a transition handler stamps a `next.input` that
buildInput would override with a different value — the action-at-a-
distance footgun the README's "stamp a placeholder" guidance hinted
at. Stays out of production builds.
React hooks
- New `useJourneyInstance(id)` for symmetry with the leaf variant —
returns the full `JourneyInstance` for callers with a known id who
need more than `state`. `useJourneyState` becomes sugar over it.
- `useActiveLeafJourneyState` likewise becomes sugar over
`useActiveLeafJourneyInstance(rootId)?.state`. The instance variant
is now documented as the recommended primitive when the leaf may be
any of several journeys — `inst.journeyId` is the natural
discriminator vs typing `<ParentState | ChildState>` and asserting
manually.
Types / API hygiene
- `SimulateJourneyOptions.modules` switched from inline
`ModuleDescriptor<any, any, any, any>` back to
`Readonly<Record<string, AnyModuleDescriptor<any>>>` so the `any`s
are localized to one definition site in core.
- `JourneyStep<TInput>` JSDoc gains `@see JourneyStepFor` so IDE hovers
point at the typed counterpart.
- `MAX_CHAIN_DEPTH` in instance-hooks.ts no longer exported (it was
private in outlet.tsx before the extraction; the move accidentally
promoted it).
- `runtime.goBack(id)` comment reworded — the previous comment claimed
"stale-closure protection still applies" but there's no captured
token on the id-based path, so the runtime's `stepToken` check is a
no-op here. New comment is honest about which guards actually fire.
README
- Hooks table reordered: instance variants listed first; state
variants documented as sugar over them. Discrimination pattern
called out explicitly so callers don't reach for `<A | B>` casts.
- `buildInput` pattern section gains a NOTES block:
- State-typing tradeoff: `(state: unknown)` at the module surface
pushes type safety to the author; `tsconfig` with
`strictFunctionTypes` may flag the narrowed annotation, and a
wrong annotation passes silently. A `buildInputFor<TState>` helper
on the journey side is called out as a candidate follow-up.
- Step-identity churn: every entry allocates a fresh `{ moduleId,
entry, input }` when buildInput is declared. Consumers diffing
`instance.step` reference equality should compare structurally.
- Throw semantics documented inline (typed
`JourneySystemAbortReason`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/journeys/src/build-input.test.ts`:
- Around line 127-140: The test currently asserts no warning but its description
says it should test the warning path; update the test that uses
createJourneyRuntime, runtime.start and createTestHarness(...).fireExit so it
performs a second push that produces a divergent next.input vs the email
module's buildInput (e.g., fireExit a second time with a payload that makes
handler-returned next.input differ from buildInput), then assert the
console.warn spy (vi.spyOn(console, "warn")) was called (use
expect(warn).toHaveBeenCalled()) — optionally clear or reset the spy between
pushes if needed to avoid false positives.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 37b287b0-7efc-4621-b973-d20f58c38678
📒 Files selected for processing (9)
CHANGELOG.mdpackages/core/src/journey-contracts.tspackages/journeys/README.mdpackages/journeys/src/build-input.test.tspackages/journeys/src/index.tspackages/journeys/src/instance-hooks.tspackages/journeys/src/runtime.tspackages/journeys/src/simulate-journey.tspackages/journeys/src/use-journey-state.ts
✅ Files skipped from review due to trivial changes (1)
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/journeys/src/index.ts
- packages/core/src/journey-contracts.ts
- packages/journeys/src/runtime.ts
- packages/journeys/src/simulate-journey.ts
Addresses concerns surfaced reviewing 0ed07a4: Correctness - Skip the same-step `buildInput` rebuild for the `{ invoke }` arm. Previously a `buildInput` throw on the rebuild block would abort the parent AFTER `beginInvoke` had already minted the child, orphaning the in-flight child with a now-terminal parent. The rebuild is also pointless during invoke (parent is paused, hidden form doesn't render); `buildInput` re-fires naturally when the resume's `{ next }` lands. DRY / readability - Extract `abortFromBuildInputThrow` — collapses the 4 near-identical try/abort/fireOnError blocks at the `withBuiltInput` callsites (initial start, next-arm push, goBack pop, same-step rebuild) into a shared helper. Each callsite is now 4 lines, not 20. - Rename `deepEqual` → `shallowEqual`. The implementation is one-level strict-equality per top-level field; the old name was misleading. - Comment on the same-step rebuild block explicitly calls out that `record.step === previousStep` is reference equality (not just `!== null`) — the arm-replacement paths already ran `withBuiltInput` and replaced `record.step`, so the equality check is what excludes them from re-running here. Test correctness - Split the dev-mode drift-warning test into two cases: 1. The original (renamed) covers the no-drift path (handler-stamped input shallow-equals buildInput output). 2. A new case actually drives drift (handler stamps `{ value: 0 }`, buildInput returns `{ value: 99 }`) and asserts the warning fires with the expected message. The earlier single test asserted `not.toHaveBeenCalled()` despite its name promising drift. Docs - `JourneySystemAbortReasonCode` JSDoc calls out that the string codes are reserved — author abort payloads using one of them get misidentified by `isJourneySystemAbort`. Suggests app-slug namespacing. - `shallowEqual` JSDoc spells out that nested-object drift may slip through (dev-only warning, observability log not load-bearing). - `useJourneyInstance` / `useActiveLeafJourneyInstance` JSDoc now lead with WHY they're the primitives, not just WHAT they do. State variants are documented as sugar. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
aa21229 narrowed the same-step rebuild to skip the `{ invoke }` arm (orphan-child correctness fix), but the README still claimed buildInput fires on an `{ invoke }` carrying state. Aligned the doc with the implementation; called out why the invoke case is excluded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up from PR review on `buildInput`'s state typing. The inline annotation pattern — `buildInput: (state: ProjectState) => …` — works in most configs but trips `strictFunctionTypes`: TS can refuse to assign a narrower-parameter function to the entry's declared `(state: unknown) => TInput` shape. `buildInputFor<TState>()` does the unknown→TState cast inside its own body, so the *outer* signature always matches the entry's declared shape regardless of consumer strictness. Curried, mirroring `defineJourney`'s shape: spell `TState` explicitly, infer `TInput` from the body. What this doesn't do: verify that the spelled `TState` matches the host journey's actual state type. Modules stay journey-agnostic — the cross-cut isn't expressible at the entry-declaration site. README spells this out for both annotation patterns. - `buildInputFor` in `@modular-react/core` next to `defineEntry` - Type tests covering `state` narrowing, defineEntry composition, and TInput-mismatch rejection via the contextually-expected signature - Runtime sanity test against the simulator - README NOTES block updated to show both annotation patterns side by side, and the API-reference table picks up the new export Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Concerns surfaced reviewing 1427c8e: - Drop the parallel `wrappedModule`/`wrappedJourney` test fixture and convert `emailModule`'s `buildInput` to the `buildInputFor<FormState>()` wrapper instead. `nameModule` keeps the inline annotation; the "rebuilds input from state when navigating back" test now exercises both patterns side by side, pinning their runtime equivalence without a 35-line duplicate fixture. - Cross-link the curry rationale: `buildInputFor`'s JSDoc calls out that `defineJourney` uses the same visual two-call shape for a different reason (partial-inference workaround vs. clean TInput inference), and `defineJourney` gains an `@see` line pointing at `buildInputFor`. - Trim "Zero runtime cost beyond the wrapping closure" from `buildInputFor`'s JSDoc — the closure is two lines below in the body; the comment was narrating WHAT instead of WHY. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dev-mode `buildInput` drift warning was comparing `step.input` (the value stored on the step at the moment `withBuiltInput` ran) against the freshly-derived value. That worked at the next-arm and startFresh callsites — `step.input` there is the handler's literal stamp from `result.next.input` / `def.start(...).input`. But the same helper also runs on `dispatchGoBack` (where `step.input` is what was stored in the history frame at first push — itself a prior `buildInput` derivation) and on the same-step rebuild after a state-changing resume (where `step.input` is the prior step's derived value). Anytime state changed between visits, those callsites false-positive: the comparison reads "a previous derivation differs from the current one," which is exactly what `buildInput` is supposed to do. Fix: - `withBuiltInput` becomes a pure derivation helper — no drift check at all. Its JSDoc spells out why: a popped frame's `input` is a prior derivation, not a handler stamp, and comparing it would false-positive whenever state changed between visits. - New `warnIfHandlerInputDiffers(handlerInput, derivedInput, step)` helper does the structural comparison. Called ONLY at the next-arm and startFresh sites, where the handler's literal `next.input` / `def.start(...).input` is still in scope. Same shallow-equality, same warning text. Regression test: drive forward to bump state, then goBack — assert the warning did not fire (it would have, on every back-nav, before this fix). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cutover-ap branch was created specifically to validate this concern. Three checks: Local stand-ins are deleted, not wrapped
Plus the Each new API has a real consumer in the adoption commit The API shape went through two rounds of adoption-driven revisionExactly what the concern called for. Both rounds were prompted by running the adoption end-to-end and hitting rough edges: Caveat worth namingSingle first consumer (autopilot's project-creation journey). A second consumer ‚ portal request creation, future |
Summary
Six API additions surfaced by a consumer project first journey-adoption flow that previously had to be hand-rolled at the consumer.
ModuleEntryPoint.buildInput?: (state) => TInput— runtime re-derives a step's input from journey state at every entry (initial, push, pop, resume), so back-navigated forms see accumulated state instead of the snapshot frozen at first push. Opt-in; entries without it keep cache-on-push behaviour.JourneyRuntime.goBack(id)on the public runtime so a shell wiring browser-Back /popstate/ breadcrumb navigation doesn't have to capture the active step'sgoBackcallback through a React context.useJourneyState<T>(id)anduseActiveLeafJourneyState<T>(rootId)—useSyncExternalStore-backed hooks. The leaf variant followsactiveChildIdand re-subscribes as the call chain grows / shrinks.JourneyStepFor<TModules>— discriminated-union step shape so simulatorstep/currentStep/historyreads narrowinputby entry withoutRecord<string, unknown>casts.JourneyStepitself becomesJourneyStep<TInput = unknown>(backwards-compatible).simulateJourneyfourth genericTOutput— journeys with a typed terminal payload are assignable withoutas unknown as Parameters<typeof simulateJourney>[0].SimulateJourneyOptions.modulesadded so headless tests can bind descriptors (required forbuildInputto fire).Internal cleanup: the leaf-chain bookkeeping that lived in
outlet.tsx(useInstanceSnapshot,useCallChain,useLeafId) moved toinstance-hooks.tsand is shared with the new hooks — no duplication of the depth bound, subscription teardown, or stable-snapshot caching.Commits
feat(journeys,core): the six additions, 10 new unit / type tests.docs(journeys): README sections covering each new export —buildInputpattern with worked example, runtime / hooks / simulator API tables, TS inference note forJourneyStepFor.Test plan
pnpm --filter @modular-react/journeys test— 393/393 passing.pnpm --filter @modular-react/core test— 167/167 passing.pnpm typecheck— 92/92 packages.workspace:link) and delete the localProjectCreationStateContext,useJourneyInstance, andJourneyBackHandlerContextstand-ins to confirm the new APIs cover the same ground.buildInput-enabled step in the autopilot project-creation flow to confirm re-entered forms see the accumulateddraftName/draftEmailinstead of the first-push snapshot.Backwards compatibility
All additions are opt-in:
buildInputkeep cache-on-push semantics.goBackprop onModuleEntryPropskeeps working —runtime.goBack(id)is a parallel access point.JourneyStep<TInput = unknown>defaults to the existing wide form; existing usages compile unchanged.simulateJourney's new fourth generic defaults tounknown, matching pre-PR behaviour.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Tests
Documentation