Allocation editor save workflow¶
This follow-up addresses issue #3095 without changing allocation policy, backend writes, runtime flags or rollout.
Behaviour¶
- The allocation editor uses the review target in the latest allocation response, then follows subsequent changes to the persisted stage review target. Unrelated stage entity updates do not restore an older cached target over the endpoint value. A response to an earlier request cannot replace a target saved while that request was pending, even if the saved target changes back to its original value. Saving Review Settings therefore updates the displayed requirement, percentage cap and minimum reviewer count immediately, including on the same stage ID. Percentage edits, enabled state, version and dirty tracking are preserved. Review Settings omits an unchanged review target from its PATCH, so an unrelated setting cannot overwrite a fresher server target with the cached form baseline. Intentional target edits are still submitted. A confirmed replacement form emits its initial raw value immediately, clearing dirty state and allowing allocation next.
- Navigating away with both Review Settings and allocation dirty offers STAY with instructions to save Review Settings first, then allocation. Staying issues neither write and preserves both forms; explicit discard still leaves without saving. Allocation-only navigation saves still await their endpoint result and keep the page open on failure. Once a write has started, navigation and discard wait for its outcome; they cannot cancel a submitted write or a live RETRY.
- Review-Settings-only navigation offers SAVE AND STAY. It submits settings but keeps the form mounted while the PATCH runs; after success, the administrator can navigate away. Failure preserves the edits, and dismissing the retry snackbar clears the pending state so settings can be retried or discarded.
- Allocation editing and saving are disabled while Review Settings are dirty or stage/settings or question writes in the same project are pending. These scoped counts include queued requests and live retries; unrelated application activity cannot strand the allocation editor. Both successful and dismissed failed requests release their own count. Question failures do not replay an old whole-stage snapshot. Review Settings cannot save an incompatible mode while allocation has unsaved edits, or issue a write while allocation is saving. These direct-button guards enforce the instructed ordering without introducing a combined transaction. Toggle, percentage and distribution controls (including their event handlers) freeze without resetting earlier allocation edits. A pending mode change cannot acquire new allocation edits that would disappear when the confirmed mode removes the editor. Saving or discarding settings unlocks allocation when the stage remains annotation-only.
- Review Settings controls freeze during its project writes and allocation saves. Completion restores their previous disabled states and any allocation locks; failures retain edits. Enabled allocation continues to reject incompatible retained settings even after the allocation form becomes clean.
- Successful allocation GET responses synchronize the persisted enabled state used by Review Settings locks. An unsaved allocation toggle does not change those locks. Load request generations discard obsolete successes and failures, including an A-to-B-to-A return followed by a newer successful allocation save. Destroyed editors stop their pending GET subscription before it can publish stale state.
- While an allocation PUT is live, Enable Stage, annotation-question selection and study-filter saves are disabled and their handlers reject writes. These sibling controls resume after success or failure. This prevents overlapping writes from this page; it is not a server-side guarantee against another administrator's writes.
The two endpoints are not a transaction. Blocking their combined navigation save is the deliberately small safe option in the issue: enabling allocation first could lock settings that the subsequent stage update is trying to change. A future coordinated save can improve convenience but is not required for this workflow. The existing stage PATCH effect remains the persistence path; this follow-up does not introduce a transaction or generic effects framework. Explicit write completion releases project-scoped pending state without changing stage entities. Queued stage PATCHes finish individually instead of cancelling their predecessor's live retry. Dispatching a stage save is never treated as successful persistence by the guard.
Acceptance checks¶
- Same-stage target changes update the visible requirement and cap without another allocation GET, reset, or loss of dirty percentage edits.
- An invalid share after a target increase cannot be submitted; an adjusted valid distribution can be submitted, and decreasing the target relaxes validation.
- Combined navigation save/stay issues no allocation PUT or stage PATCH, retains edits, and explains the correct save order.
- Cancel/discard retain their respective navigation semantics, and allocation-only navigation waits for successful persistence and blocks on failure.
- Stage-only saves stay on the page, and a dismissed failed PATCH releases pending controls without resetting edits. A retry can still complete successfully.
- Discard is blocked while a write/retry is live; after dismissal, retry cannot resurrect the discarded write. Real controls are frozen until the outcome.
- Question Q1 failure followed by queued Q2 success retains Q2 and releases pending controls. Completing one request cannot unlock allocation while another remains.
- Fresh target B plus stale form A survives an unrelated settings PATCH and permits the next allocation save; a deliberate target change is still sent.
- Request target revisions reject stale responses after A-to-B-to-A stage changes, including changes between component renders.
- Fresh allocation GET enabled/disabled states correct stale Review Settings locks; unsaved toggles do not masquerade as persisted state.
- Sibling stage activation, question and filter handlers issue no write during an allocation PUT, and resume after either successful or failed completion.
- Pending review-mode or study-selection edits incompatible with annotation-only allocation are identified explicitly. The dialog requires restoring compatible settings to keep allocation edits, or discarding both pending forms and reopening Review Settings to change mode. It does not promise those edits can be saved together.
Component specs and a real-store, two-form, effect/reducer integration fixture cover these checks alongside existing allocation versioning, stage-switch and in-flight-save cases. See the stage settings user guide for administrator-facing instructions.
Ordering rules (binding)¶
Relocated verbatim from the root CLAUDE.md.
Allocation PUT prepares a private persisted project through the general
ICrudRepository.GetUncachedAsync, bypassing the shared mutable repository cache. The stage
review-activity probe stays sequential after that read: the check is check-then-publish, nothing
fences review from starting before the save, so the window must not be widened. Never configure
a cached project before awaiting bucket preparation: another whole-project writer could persist
that unpublished configuration. Publication still uses project optimistic concurrency; cache
eviction after a failure alone does not isolate in-flight mutations.
The allocation editor preserves the latest endpoint review target until the saved stage target changes. Its navigation guard does not combine dirty Review Settings and allocation saves: stay on the page and save compatible Review Settings first, then allocation. Incompatible review-mode edits must be resolved explicitly before saving, because leaving annotation-only mode removes the allocation editor. Direct save buttons enforce this order too: pending/dirty Review Settings block allocation editing and saving without resetting earlier edits; allocation event handlers share the same lock. Pending allocation edits block incompatible Review Settings saves. Monotonic target revisions prevent late allocation responses from reverting targets, including A-to-B-to-A changes. Stage-only navigation offers SAVE AND STAY rather than treating a dispatched PATCH as persisted; dismissing a failed stage-save retry clears pending state without resetting the dirty form. Stage/settings and question writes have project-scoped pending counts, including queued requests and live retries. Navigation/discard waits for completion; Review Settings controls freeze during these writes and allocation PUTs. Unchanged review targets are omitted from settings PATCHes to avoid overwriting fresher allocation responses; replacement forms seed their raw-value stream so successful saves become clean immediately. Enabled-allocation guards survive the dirty-to-clean transition. Successful allocation GETs synchronize the persisted enabled-state lock; unsaved toggles do not. Enable Stage, annotation-question selection and study-filter save controls/handlers also reject writes while an allocation PUT is live. This is same-page ordering, not cross-client serialization.
The stage overview allocation panel uses the authorized read-only
StageAllocationReadController routes: workload-shares/my-studies for the caller's paged
studies and workload-shares/progress for stage designers. Mongo aggregate reads return
distinct-study counts without materializing a new allocation collection. Assigned includes
preserved own saved work; remaining and startable new work are different counts. Stale plans
show paused state and administrator-only inactive reviewer identities. The browser clears
data and cancels pending requests when stage, feature flag or permissions change. See the
delivery continuation for
remaining membership migration, third-party authorization and activation gates.
Allocation query measurements use the opt-in AllocationQueryPerformanceTests harness in
the Project Management Mongo test project. It requires SYRF_ALLOCATION_PERF=1 and a Release
build, creates only disposable synthetic MongoDB data, and skips ordinary CI runs before
container creation. The measurement protocol
distinguishes shared-host observations from environment qualification; it does not set a new
latency SLA or authorize feature activation, live database access or migrations.