From 3137970b979c7437622c11edee2a6d203a5d8a6a Mon Sep 17 00:00:00 2001 From: hypercross Date: Mon, 13 Jul 2026 10:34:44 +0800 Subject: [PATCH] feat: rebuild reactivity state from store after replay Add `rebuildReactivityFromStore` to synchronize local reactivity state with the hydrated variable store. This ensures tag activations are correctly tracked following a reducer replay. Also remove the unused `invalidateCompletions` function. --- src/components/journal/completions.ts | 6 ------ src/components/journal/index.ts | 2 +- src/components/journal/var-reactivity.ts | 21 +++++++++++++++++++++ src/components/stores/journalStream.ts | 4 ++++ 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/components/journal/completions.ts b/src/components/journal/completions.ts index 781457f..d7ddffd 100644 --- a/src/components/journal/completions.ts +++ b/src/components/journal/completions.ts @@ -209,12 +209,6 @@ export function ensureCompletions(): Promise { return _initPromise; } -/** Force a re-fetch on the next page load. For runtime, call before invalidate triggers. */ -let _invalidated = false; -export function invalidateCompletions(): void { - _invalidated = true; -} - /** * Reactive hooks for the journal input. * Returns the current completions state + a convenience `data` extractor. diff --git a/src/components/journal/index.ts b/src/components/journal/index.ts index e501d19..b23def8 100644 --- a/src/components/journal/index.ts +++ b/src/components/journal/index.ts @@ -48,7 +48,7 @@ export { StreamMessageCard } from "./StreamMessage"; export { ComposePanel } from "./ComposePanel"; export { JournalInput } from "./JournalInput"; export { DynamicForm } from "./DynamicForm"; -export { useJournalCompletions, invalidateCompletions } from "./completions"; +export { useJournalCompletions } from "./completions"; export { dispatchCommand } from "./command-dispatcher"; export type { DispatchContext, DispatchResult } from "./command-dispatcher"; export { parseInput } from "./command-parser"; diff --git a/src/components/journal/var-reactivity.ts b/src/components/journal/var-reactivity.ts index 95ffca7..7f7424c 100644 --- a/src/components/journal/var-reactivity.ts +++ b/src/components/journal/var-reactivity.ts @@ -167,6 +167,27 @@ export function setBase(key: string, value: string): void { baseValues.set(key, value); } +/** + * Rebuild local reactivity state (baseValues, activeMods, sourceActivations) + * from the hydrated stream variable store. Must be called after replayReducers + * so that tag activations are correctly tracked for subsequent cascade + * computations. + */ +export function rebuildReactivityFromStore(variables: VariableStore): void { + if (!tagModMap) return; + + for (const [key, value] of Object.entries(variables)) { + if (!baseValues.has(key)) { + baseValues.set(key, value); + } + + const tag = isTagValue(value); + if (tag && !sourceActivations.has(key)) { + activateTagFromSource(key, tag, variables); + } + } +} + /** * Compute cascade effects after a variable change. * diff --git a/src/components/stores/journalStream.ts b/src/components/stores/journalStream.ts index aeba43b..6a15da3 100644 --- a/src/components/stores/journalStream.ts +++ b/src/components/stores/journalStream.ts @@ -14,6 +14,7 @@ import { createSignal } from "solid-js"; import * as Comlink from "comlink"; import type { StreamMessage } from "../journal/registry"; import { getMessageType, validatePayload } from "../journal/registry"; +import { rebuildReactivityFromStore } from "../journal/var-reactivity"; import type { WorkerState, WorkerPatch, SessionManifest } from "../../workers/journal.worker"; import type { JournalWorkerAPI } from "../../workers/journal.worker"; import { @@ -162,6 +163,9 @@ function handlePatch(patch: WorkerPatch): void { saveRole(patch.state.myRole); // Rebuild derived state (revealedPaths, variables) by replaying reducers replayReducers(); + // Rebuild local reactivity engine state so tag activations from + // hydrated variables are tracked for subsequent cascade computations. + rebuildReactivityFromStore(state.variables); break; }