Goal
The Configure tab used to share fetch, save, and dirty state through hook-call order and four parallel baselines (originalScriptValues, JSON lastSaved*Refs, orgBackgroundInfoBaselineRef). Those clusters now share one last-confirmed snapshot.
Three layers
Unsaved state is not in either store. It lives in
useScriptFormState as two flags plus their synchronous nav refs, and dirty is a function of the draft: useScriptDirtyCheck is keyed on form values and reads the snapshot at comparison time. Anything that moves lastSaved away from the draft therefore has to write the form in the same turn or set the flag itself, because no comparison is scheduled on a commit’s behalf.
orgBackgroundInfoBaselineRef is still the PATCH gate: a failed org GET leaves it unchanged (or null if it never loaded) so the editor will not write empty defaults over org-wide settings. Hydrate on failure copies that leftover baseline into lastSaved.orgBackground instead of an empty sentinel. Discard restores org text from the baseline ref, not from a failed-GET snapshot.
Actions
hydrate(campaignId, snapshot)— fetch writes this once aftermapScriptReadandsettleFetchedScript(virtual-calendar follow-up users, session auto-match, new-script default outro). Then onesetValues.commit(campaignId, patch)— save (and question-bank persist, org PATCH, version-restore clocks) merge intolastSaved. No-op ifcampaignIddoes not match orlastSavedis null. Clock fields (currentScriptVersionId,cosmosUpdatedAt) only advance when the patch value is non-null.clear(campaignId)— campaign switch. Same turn as form reset so a late commit of the previous job cannot land.
lastSaved == null means “not hydrated yet.” Dirty skips; the post-generation one-shot waits.
Late settle
Calendar provider, SWR outro text, and session auto-match can arrive after hydrate. A small effect patches only fields the draft still matches onlastSaved (so a dirty call intro cannot block a virtual-calendar user clear), then commits those same field patches. User edits to a settled field are left alone. Post-generation one-shot save waits until calendar integration has resolved so it cannot persist pre-settle values.
What this page does not cover
- Phase 2 — collapse fetch/save/version into
useJobDocumentController. - Phase 3 — provider composition.
JobDocumentProvidersnests four providers; its doc comment describes the grouping. - Phase 4 — org-template bootstrap aggregator. See Job editor bootstrap.