Skip to content

Proportional Study Allocation — status and remaining plan

Is the feature fully implemented? No. The original MVP is merged and dormant. The first follow-up, direct annotation endpoint enforcement, merged in #3211 (fdc79576c). Reviewer UI and administrator allocation progress merged in #3213 (761b3c70d5). No environment flags are enabled by this change.

This document records what is done, what is deliberately not done, and the ordered plan to reach a feature reviewers actually benefit from. The design itself is in README.md; this file is the delivery status. The acceptance contract defines required behavior, failure paths, integration, performance and rollout evidence.

Where it stands

Area Status Evidence
Domain model and allocation plan Done StageWorkloadShareConfiguration, StageWorkloadSharePlan, Study.WorkloadShareBucket
Assignment filtering for new annotation work Done StageReviewService filters only the NewForAnnotation candidate query
Fail-closed behaviour on stale or invalid configuration Done Empty result, never an exception; bucket-exhausted reviewers report complete
Configuration API Done StageWorkloadSharesController, optimistic concurrency, ProblemDetails conflicts
Feature flag Done, off proportionalStudyAllocation, default false, set in no environment
Admin configuration UI Minimal Enable toggle, per-reviewer percentage, distribute-evenly, total validation, save
Admin visibility into the resulting allocation Merged in #3213 (761b3c70d5) Stage-design-authorized per-reviewer counts/progress and stale-membership guidance
Reviewer-facing UI Merged in #3213 (761b3c70d5) Read API and stage-overview panel; see delivery continuation
Enforcement on direct study endpoints Merged in #3211 (fdc79576c) Shared StageWorkloadShareEligibility gates ordinary direct GET and session PUT, and each freshly loaded submission retry; denied eligibility returns 404 before side effects
Deployed anywhere No Flag absent from staging and production values

Merged in #2991 (merge commit 277802248), superseding the abandoned #2450.

What the MVP does today

An administrator with the flag on can, on a disabled annotation-only stage with no review activity, give each active project member a percentage of the stage's review slots. Those percentages become a deterministic 10,000-bucket plan; each study carries a stable bucket derived from its id. When a reviewer asks for their next study, the candidate query is restricted to their buckets. Resuming a saved session and reconciliation remain unfiltered. The first follow-up also restricts direct annotation access and new annotation-session submissions to those same buckets. Saved ordinary work is exempt only for the same reviewer and stage, and submission must match the saved session ID. A reservation alone does not exempt a direct request. Disabled allocation retains legacy eligibility; a disabled runtime flag retains legacy behaviour, including submission identifier handling.

While an allocation is enabled the stage's review mode, study-selection mode and reviews-per-study are locked, because the plan is only valid against the count it was built for. A configuration naming a member who is no longer active, or one that no longer matches reviews-per-study, yields no new work rather than an error.

What it does not do

These are the gaps that make "merged" different from "usable", in the order they block value.

  1. Direct enforcement is merged in #3211 (fdc79576c). It closes the ordinary direct GET and session PUT bypass with the same bucket policy as next-study selection. Focused tests cover both tracking modes, stale configuration, saved work, and denial without mutations or reservations. This is the prerequisite for the reviewer UI, not completion of that UI.
  2. Reviewer visibility merged in #3213. It adds the caller's assigned studies, saved-work links, remaining/startable counts and enabled/paused state to stage overview.
  3. Administrator visibility merged in #3213. The same overview adds a stage-design authorized per-reviewer allocation/progress table, separate from configured percentages.
  4. Stage-review authorization is not validated. A member who cannot open the stage can still be given a positive share, and those slots then go unfilled. The check needs the reviewer's own application-group claims. Identity stores groups, but no canonical out-of-request, cross-provider authorization resolver is exposed to allocation configuration.
  5. Membership changes after activation are unhandled. Deactivating a configured member pauses that stage's new annotation work by design, but there is no reviewer lifecycle story beyond that. #3048 adds the deactivation admin surface and is unmerged. Pause visibility is implemented here; the user has authorized membership-first compatibility/migration implementation after the current allocation PRs ship, instead of extending runtime schema branches. Live migration execution and activation remain separate gates.
  6. No rebalancing. Shares can only be set before any review activity exists in the stage. A stage that is already underway cannot be re-divided.

Plan

Ordered by dependency, not by size. Items 1 and 2 are what turn this from a scheduling tweak into the feature people asked for.

# Work Why it is here Status
1 Enforce eligibility on the direct specific-study review and submission endpoints Prerequisite for any browsing UI; without it shares are advisory Merged in #3211 (fdc79576c). See implementation plan
2 Reviewer UI: my assigned studies, remaining share, allocation-active indicator The reviewer-facing half of the feature; depends on 1 Merged in #3213 (761b3c70d5); combined editor integration remains pending #3212
3 Admin allocation view: per-reviewer assigned/completed counts and progress Lets an administrator verify the split is working Merged in #3213 alongside 2 (761b3c70d5)
4 Reviewer lifecycle handling for membership changes after activation Deactivation currently pauses a stage silently Pause visibility implemented; membership-first migration/lifecycle implementation authorized after current PRs ship; live execution gated
5 Validate stage-review authorization when configuring shares Prevents configuring a member who cannot open the stage Blocked on out-of-request authorization
6 Refresh the editor's reviews-per-study constraint on a Review Settings save Client-side pre-validation only; server already fails closed Implemented in #3212; shipping
7 Coordinate combined navigation-dialog SAVE ordering Allocation publishes before the stage PATCH the domain then rejects Implemented in #3212 with safe combined-save refusal; shipping
8 Memoise the derived plan by stage, configuration version and reviews-per-study 10,000 buckets are walked per request; measurable CPU for a large team Not started
9 Measure bucket-query performance at production scale Decides whether materialized plans are ever needed Isolated Release runs passed at 10k/10 and 100k/50 studies/reviewers; expensive admin query identified under shared-host contention. Protocol and evidence; production-like qualification remains outstanding
10 History-aware rebalancing for stages with completed work Currently shares are fixed once review starts Not started
11 Reserve/onboarding policy Only after its review-slot semantics are specified Deferred
12 Co-allocation optimisation and progress dashboard Only if real usage demonstrates value Deferred

Current implementation slice

The direct endpoint plan maps the affected paths to acceptance criteria. GET .../studies/{studyId}/review and ordinary PUT .../session/{sessionId} now return 404 for out-of-share new work before recording usage, reserving slots, mapping submissions, or calling the save orchestrator. Flagged ordinary submission route/body stage and session IDs must match (400 on mismatch), so one stage or saved session cannot authorize another. Reconciliation uses the existing body mode and remains unchanged; the unused query parameter cannot bypass an ordinary submission check. Session deletion and screening remain unchanged.

The ledger originated in PR #3202 and is carried forward here without changing that worktree. Do not treat this branch as deployed evidence. The delivery continuation tracks integrated reviewer/admin visibility, independent editor fixes and the remaining membership, third-party authorization and activation gates.

Local validation on 2026-09-05: full core suite 2,116 passed / one skipped; full API suite 1,227 passed / none skipped. API and Project Management endpoint builds succeed. Commands and the 66 newly added regression cases are recorded in the implementation plan.

Rollout

The flag is default-off and appears in no environment values file, so nothing changes for any user until someone deliberately enables it. Suggested sequence:

  1. Enable in a preview environment and configure a two-reviewer split on a test stage.
  2. Confirm each reviewer's "next study" stream stays inside their buckets and that a disabled member's stage yields no new work rather than an error.
  3. Ship items 1 and 2 before enabling anywhere reviewers will notice, because until then the allocation cannot be seen or relied upon.
  4. Enable in staging, then production, with the runtime flag as the rollback boundary.

Review-driven hardening already applied

Four review cycles on #2991 produced fixes worth recording, since they describe real failure modes rather than style: cache eviction after any failed allocation publish; navigation-guard save sequencing so the allocation and stage-settings writes cannot race; a per-request save token so a stale response cannot disable the wrong stage's Save button; review-settings form preservation when the allocation lock changes; reuse of an in-flight save instead of issuing a duplicate; share inputs locked while saving; allocation state cleared when a stage load fails; completion reported when a reviewer's buckets are exhausted; and an inherited reviews-per-study materialised on the stage so a later project agreement-mode change cannot invalidate an enabled allocation.