Skip to content

Temporary planning review record. The report below is reproduced verbatim as returned by the independent read-only reviewer (Plan agent, Fable model, launched 3 October 2026 about 15:00 BST at Chris's request to review in-flight programmes and their integration with the plan). Only this front matter and note were added. Line numbers refer to the package as it stood when the reviewer read it. Resolutions are in the round-2 resolution matrix.

AP review: study allocation, pool partitioning and progressive batches

Path legend (all absolute): MAIN = /home/chris/workspace/syrf/main; PKG = /home/chris/workspace/syrf/pr/pr3617.research-screening-as-specialised-annotation-gxgahs/docs/planning/integrated-review-plan-2026-10; PLN = /home/chris/workspace/syrf/pr/pr3617.research-screening-as-specialised-annotation-gxgahs/docs/planning; GITOPS = /home/chris/workspace/cluster-gitops; CORE = MAIN/src/libs/project-management/SyRF.ProjectManagement.Core; MONGO = MAIN/src/libs/project-management/SyRF.ProjectManagement.Mongo.Data. Main was read at 0f5c61073 (3 Oct 2026); live PR/issue state read with gh during this review.

1. Verdict

The plan correctly keeps the three programmes under their owners and names the right joins (C7, E18–E20, AL1, X-BATCH, X-ELIG), but it under-specifies the one thing every one of them depends on: what per-reviewer facts the Study summary projection must carry once sessions, claims and decisions leave the Study document. Every admission, pool, allocation-exemption and D8 slot computation on main reads embedded ExtractionInfo.Sessions, SlotReservations, SessionTallies and ScreeningInfo.Screenings for this reviewer's membership, not tallies, so an E20 frozen as "bounded per-stage tallies and flags" would re-offer completed studies, deny resumption of out-of-bucket saved work and miscount slots for every canonical session. The five changes that matter most: (1) expand E20/C7 at F1 into a per-reviewer, per-form membership projection (own session state, own claim, own decision per profile) that the existing pool predicates can be re-pointed at, and make it a conformance fixture; (2) add an explicit "allocation on canonical stages" rule for R2a (refuse, or read the form target through an equality-checked adapter), because the regime evaluator reads stage.SessionCountTarget and the editor requires ReviewMode.Annotation, neither of which exists on a canonical stage; (3) treat the out-of-request authority resolver (#3251) as a named external join (the authorization programme's evaluator) because it blocks both allocation reviewer validity and the plan's own "Who is offered what" (AC-R3a-09), whose admission evaluation requires the reviewer's app groups; (4) rewrite X-BATCH so that #3939's completion definition is extracted behind a form-unique evidence seam and its lazily recomputed shared frontier becomes a durable, event-emitting transition (amendment A's pool-entry event has nothing to attach to today), and hold #3939 to a performance gate before any activation; (5) specify in C7/C9 how an additional-review request (RA5) and >target completions (SF4) are admitted when allocation buckets hold exactly target reviewers and EnforceAnnotationTarget refuses a further place, because today's policy returns AllocationNotAssigned/AtCapacity for exactly that reviewer.

2. Current-state table

Component State Evidence Known gaps
Proportional allocation MVP (FEAT-025) Merged, dark. #2991 277802248 (2 Sep); direct enforcement #3211 fdc79576c; reviewer/admin visibility #3213 761b3c70d; benchmark #3228 98bc8e3b8; editor fixes #3212 b56ef635c; publication isolation #3267 6de8ef82e; admin-read perf #3329 053598ba4; covered-bucket rejection #3604 c9b1a4fde (24 Sep); regime identity/provenance #3603 2562a5d0e (24 Sep); reviewer-validity docs #3611 (merged); tie-break perf 7f8b9f4c1 (3 Oct, after the plan's baseline c59d9d0f1) Flag proportionalStudyAllocation default false (MAIN/src/charts/syrf-common/env-mapping.yaml:1253-1262), delivered to the API host only (:1146-1150); unset in staging and production values (grep of GITOPS/syrf/environments/{staging,production} finds no entry); true only in GITOPS/syrf/environments/preview/pr-3327/services/{api,project-management,web}.values.yaml:15/15/20. STATUS is stale: it still says #3603 is "pending review/merge" (MAIN/docs/features/proportional-study-allocation/STATUS.md:71). Reviewer validity (Chris, 22 Sep) not met, blocked on #3251 (STATUS.md:14-23; acceptance.md:85-93). Preview acceptance checklist entirely pending (/home/chris/workspace/syrf/pr/pr3327.proportional-allocation-preview-acceptance/docs/features/proportional-study-allocation/preview-acceptance-2026-09-07.md:39-47). Admin progress budget not met (STATUS.md:136). Regime-evolution Phases 2–5 unstarted (regime-evolution-plan.md:474-513).
Allocation open PRs/issues #3327 OPEN, CONFLICTING, last updated 8 Sep (preview-only flag vehicle, no code). Issues OPEN: #3251 (resolver), #3252 (perf), #3264 (tracker), #3269 (integrated acceptance), #3321 (isolation follow-ups), #3745 (D8 2c under reallocation, deferred to Phase 3), #2042 (membership). #3048 CLOSED unmerged. gh pr view/gh issue view, 3 Oct No human acceptance anywhere; #3269 checklist open.
Pool partitioning (10,000 buckets) Merged, inert when flag off. StudyWorkloadShareBucket.FromStudyId FNV-1a mod 10,000 (CORE/Model/StudyAggregate/Study.cs:536-553); normalised on load (Study.cs:382-394); Filters.InWorkloadShareBuckets (MONGO/Filters.cs:141-152); plan walks 10,000 buckets per request (CORE/Services/WorkloadShares/StageWorkloadSharePlan.cs:39-57). Regime schema v1 floor with startup refusal (MAIN/docs/features/proportional-study-allocation/README.md:194-215). Study.WorkloadShareBucket is a persisted root field with index ProjectId_1_WorkloadShareBucket_1 (README:111). Memoisation not started (STATUS item 8).
Active-only pools / stage filtering (FEAT-008) Planned only. No StudyLifecycleStatus in MAIN/src (grep; the only ScreeningOutcome hit is an unrelated save-outcome mapper, MAIN/src/services/api/SyRF.API.Endpoint/Controllers/ReviewController.cs:905,1098). Pools are project-wide minus excluded/own-session predicates (MONGO/ReviewEligibilityPoolFilters.cs:63-115). Random selection is $sample after $match (MONGO/Repositories/StudyRepository.cs:763, 801), not FEAT-008's rand field (MAIN/docs/features/stage-filtering/README.md:199-209). Root CLAUDE.md states lifecycle/screeningOutcomes[] in the present tense; the inventory already flags this (PKG/source-status-inventory.md:371). Legacy PartitionSet and an unflagged study-partitions admin route still exist (CORE/Model/ProjectAggregate/StageEntity/PartitionSet.cs; MAIN/src/services/web/src/app/project/project-admin/project-admin.routes.ts:53-55).
Session targets Merged. Stage.SessionCountTarget falls back to Project.AgreementThreshold.NumberScreened (CORE/Model/ProjectAggregate/StageEntity/Stage.cs:360-365); allocation materialises the inherited value (Stage.cs:177) and locks target/mode edits while enabled (Stage.cs:65-68, 374-375); the FEAT-024 definition-rewrite fence fires on effective-target change (CORE/Services/ProjectStatistics/Families/Annotation/ProjectStageConfigurationChange.cs:16-22). Fixed-two hardcode: MONGO/StudyStats.cs:353-355, mirrored by AnnotationThresholds.MinimumNumberSessions = 2 (CORE/Services/ProjectStatistics/Families/Annotation/AnnotationThresholds.cs:16-28). Per-stage override via #3732 grouped configuration (merged 210f9160f). Screening threshold doubles as the default annotation target; no decoupling row in the plan's migration mapping.
Review eligibility programme Partially merged, dark. S1a #3646 ac4251ccd, S1b #3659 c65da40c8, settings/claims #3579 b66f5e08d, S2 #3691 97658bb5b, S3 #3719 389425ff0, S4-A #3732 210f9160f, claim-revoked #3736 118424964 (24–25 Sep). OPEN: #3746 S6a CONFLICTING (activity on 1 Oct), #3742 S4-B DRAFT with 0 files, #3741 S5 audit DRAFT (docs). Flag reviewEligibilityPolicy default false, unset in every environment, delivered to the API host only (env-mapping.yaml:1146-1150, 1242-1251). Typed admission throws when the flag is off and refuses unmigrated reservations (CORE/Services/ReviewEligibility/ActivityReservationAdmission.cs:34-36; #3939 diff context in StudyRepository.ActivityReservations.cs). S5 audit headline: the browser never reads eligibility, #3551's veto remains, only two revocation causes, no e2e/staging acceptance, fixed-two discrepancy "not done" (D3a.2), D8(2c) deferred to #3745 (branch origin/feat/review-eligibility-completion-audit, docs/planning/review-eligibility-completion-audit.md "Headline findings" and rows D3a.2, D8.2c, UI.1, RO.1, TT.3). The "paused 25 Sep" status is UNVERIFIED in the repository (session memory only).
Active reviewer tracking Merged, never enabled; ActiveReviewerTrackingAvailable = Enabled && SignalRActive (MAIN/src/libs/kernel/SyRF.SharedKernel/Settings/FeatureFlags.cs:35-36); flag delivered to api and project-management (env-mapping.yaml:1592-1606); unset everywhere. Reconciliation reserves nothing (CORE/Services/StageReviewService.cs:67-69, 153-156). X-TRACK as planned.
Progressive shared review batches (FEAT-026) Nothing on main. #3936 (plan docs) OPEN, MERGEABLE, head a799b538e; #3939 (implementation) OPEN, CONFLICTING, 63 files, +3233/−60, flag progressiveReviewBatches default off and requires reviewEligibilityPolicy (PR body; ProgressiveReviewBatches.ConfigureAsync refuses otherwise). #3939 adds Stage.ProgressiveBatches (embedded, BsonIgnoreIfNull), three collections pmProgressiveBatchPlan/Membership/Access, ProgressiveBatchCompletion.IsFinished keyed by stage tally and stage.SessionCountTarget, and a PM-side flag block progressiveReviewBatchBackendFlags (its env-mapping.yaml diff). #3939's README diff turns the three open decisions into "Decisions for initial implementation" and states "Chris confirmed this disposition" for the denominator; that confirmation is not in the ledger (PLN/review-form-owner-decisions-2026-10-02.md) or PKG/decision-register.md. Shared frontier is recomputed on every Next/status read with no durable opening transition (see AP-05). Perf unqualified (PR body: "production-scale latency/contention qualification remains a rollout requirement").
FEAT-024 reads of allocation/targets Merged dark: reservation claim fold slice 5 (#3926 12e17b92d, 2 Oct) keyed by stage; ReviewerAnnotation family reclassified on effective-target change (ProjectStageConfigurationChange.cs:42-53); stage/membership annotation families use fixed two (AnnotationThresholds.cs:28). Replacing fixed two is a breaking derivation change under the N-1 rule (MAIN/.claude/rules/materialized-stats.md, digest section).

3. Findings

ID Sev Location Finding Evidence Recommended change
AP-01 Blocker PKG/contracts.md C1 "Study summary projection" (l.108), C7 (l.323-330); PKG/open-questions-and-assumptions.md E20 (l.122); PKG/integrated-plan.md R2a "Study coupling" (l.406-408) E20 defines the projection as "bounded per-bound-stage tallies and flags". Every admission and pool computation the plan says it will extend needs per-reviewer membership, not tallies: own ordinary session and its status, own claim per activity, own screening decision, and the set of other reviewers with work (for the D8 slot rule). Once canonical FormSessions, claims and decisions leave Study, these readers return wrong answers for canonical data: Next re-offers a study the reviewer completed (no DuplicateSession), the allocation saved-work exemption fails so an out-of-bucket resume returns 404, and AllocationClaimSlot undercounts occupied slots. Own-session/own-claim/own-decision reads: MONGO/ReviewEligibilityPoolFilters.cs:77-84, 117-123, 140-150; CORE/Services/WorkloadShares/StageWorkloadShareEligibility.cs:73-76, 81-84; CORE/Services/ReviewEligibility/AllocationClaimSlot.cs:29-39; CORE/Services/ReviewEligibility/ActivityReservationAdmission.cs:41-58; CORE/Services/StageReviewService.cs:539-541, 676-681. The plan lists "pool filters" among projection readers but describes only tallies (contracts.md:108). At F1, redefine E20 as a per-form membership projection on Study: for each bound form (and profile), the list of (reviewerId, sessionState ∈ {draft_only, saved_incomplete, completed, withdrawn}, claim activities, admittingRegimeId) plus the current per-profile decision per reviewer and the derived tallies. Bound it by the SF4 rule (all qualifying candidates, so not by target). Add a C7 conformance fixture: every truth-table row in review-eligibility-truth-table.md must produce the same answer from the projection as from embedded data. Make this the first signed C7 identity amendment (AC-M0-04).
AP-02 Major PKG/integrated-plan.md R3a "Pages" (l.517-518), AC-R3a-09 (PKG/acceptance-criteria.md:224); PKG/integrated-plan.md AL1 (l.695) "Who is offered what" evaluates admission for other reviewers. The admission service and pool scope take the reviewer's app groups from the current user and fail closed otherwise, so an administrator cannot evaluate another reviewer's grants without an out-of-request resolver. That resolver is exactly #3251, the blocker the allocation programme has carried since 5 Sep, and which Chris's 22 Sep reviewer-validity requirement makes a hard dependency. The plan treats #3251 as AL1's problem only. CORE/Services/ReviewEligibility/ReviewEligibilityPoolScope.cs:55-60, 79-82; CORE/Services/StageReviewService.cs:160-166; #3251 body ("no canonical out-of-request, cross-provider resolver"); MAIN/docs/features/proportional-study-allocation/acceptance.md:85-93; regime-evolution-plan.md:474-479. Add external join X-AUTH-RESOLVER (owner: authorization programme #3335; the single ProjectAuthorityEvaluator is the natural home) to §5.11 and the graph, as a prerequisite of AC-R3a-09 (per-reviewer preview), AL1 and allocation Phase 2. Until it exists, scope R3a's Monitor to pool-level counts by refusal reason with no per-reviewer evaluation (see Q-AP-4). Reference #3251 and #3611 in C10.
AP-03 Major PKG/contracts.md C7 "Implements … SF4 (more than the target)" (l.318), C9 "Additional review request" row (l.380); ledger RA1–RA5 (PLN/review-form-owner-decisions-2026-10-02.md:260-266) Under allocation every bucket holds exactly target reviewers and EnforceAnnotationTarget refuses a further place; the pure policy refuses that reviewer with AllocationNotAssigned, AtCapacity or AnnotationTargetMet. So an RA5 additional reviewer, or any SF4 >target completion, cannot start under allocation or enforced capacity. Neither C7 nor C9 says how the request is admitted; the earlier integration proposal's "scoped one-request admission token" was not carried into the contracts. README.md:53-57, 71-77; CORE/Services/ReviewEligibility/ReviewEligibilityPolicy.cs:279-292; MONGO/ReviewEligibilityPoolFilters.cs:77-84, 219-227; ActivityReservationAdmission.cs:55-68; PLN/review-statistics-allocation-batching-integration-2026-10-03.md:70. In C9 and C7 (F4/F-A): an AdditionalReviewRequest carries a scoped admission (study × form × requested reviewer, single use, expiring) that bypasses bucket membership and the enforced target for that reviewer only, is recorded as provenance on the resulting session, never changes the target, and is counted outside allocation progress. Add AC-R4a: "with allocation enabled and the target met, the requested reviewer can open and complete; no other reviewer can."
AP-04 Major PKG/integrated-plan.md R3c (l.557-558); AC-R3c-05 (acceptance-criteria.md:248); PKG/contracts.md C7 batches row (l.329) AC-R3c-05 requires readiness to "give the same result as #3939's for the same data". #3939's definition is legacy-keyed: per-stage SessionTallies.NumberOfCompletedCandidateSessions against stage.SessionCountTarget, stage.ReviewMode, and project-threshold screening. Under SF2 (form-owned target, one contribution across stages) and R3b (per-profile outcomes) the two definitions diverge for any shared form, so the criterion is unsatisfiable or forces legacy semantics into R3c. Separately, #3939 enrols every project study in every stage's plan (Filters.InProject), so once R3a routes exist a later stage's denominators include studies that cannot enter it, and thresholds never open. #3939 diff: ProgressiveBatchConfiguration.cs IsFinished (tally, stage.SessionCountTarget, ReviewMode), ProgressiveReviewBatches.PrepareAsync (Studies.Find(session, Filters.InProject(project.Id))). SF2 ledger:37. Rewrite X-BATCH: #3939's completion must be extracted behind an IStudyObligationEvidence seam that R3c binds to the form-unique projection (AP-01), and batch membership/denominator must be defined over pool-entry membership (PoolEntryEvent, amendment A), appending late entrants as cohorts. Reword AC-R3c-05 to "uses the same definition (seam) and the shared fixtures; results differ only where SF2/R3b semantics differ, and those cases are listed."
AP-05 Major PKG/prisma-amendments.md A (l.63-66); PKG/domain-model.md PoolEntryEvent (l.93), §5 transactions (l.172-180); PKG/contracts.md C7 batches row (l.329) Amendment A defines "entering screening" as actual release "including shared batches and personal grants", recorded as a PoolEntryEvent. #3939 has no such moment: the shared frontier is recomputed lazily inside RefreshAsync on every Next and status read, and personal grants are a $max on an access row; nothing is emitted, audited or notified. The #3936 plan required a CAS opening plus an outbox; #3939 dropped it. The lazy recompute also streams and deserialises every member study of the frontier batch on each Next (SharedFrontierAsync) plus a project-wide anti-join (RefreshAsync), which the PR itself leaves unqualified. #3939 diff: ProgressiveReviewBatches.cs RefreshAsync, SharedFrontierAsync, ResolveNewWorkAsync ($max PersonalThrough), GetStatusAsync (calls RefreshAsync); #3936 technical plan "Shared opening is a compare-and-swap … outbox" (git show origin/feat/plan-progressive-shared-review-batches-5g8ysx:docs/features/progressive-review-batches/technical-plan.md, "Concurrency, lifecycle and migration"); #3939 body ("Arrival detection scans project study IDs … qualification remains a rollout requirement"). X-BATCH entry criteria: (a) shared opening and personal grant are durable transitions (CAS on plan/access row) that write a PoolEntryEvent (or its legacy capture, E26) in the same transaction; (b) a performance gate comparable to #3252 (Next p95 with 100k studies / 2,500 batches, documented keys examined) before any environment enables the flag; © GetStatusAsync must not mutate plan state. Add §5 transaction rows "batch opened", "personal batch grant".
AP-06 Major PKG/integrated-plan.md R2b (l.456-457), AL1 (l.695); AC-R2b-04 (acceptance-criteria.md:181); PKG/domain-model.md:61, 165; A-09 (open-questions-and-assumptions.md:191) The plan refuses shares only for shared-form stages and extends the regime only at AL1. It never says what allocation reads on a canonical single-stage form in R2a. Today the regime's ReviewsPerStudy is stage.SessionCountTarget, ConfigureWorkloadShares requires ReviewMode.Annotation + StudySelectionMode.Annotation, and UpdateStage locks mode/target while shares are enabled. On a canonical stage the target is form-owned (SF2), mode is replaced by StageSettings, and the #3732 override is legacy-only (R2a MVP), so the evaluator has no valid inputs and the lock has nothing to guard: a form-version publication could change the target under a published regime. CORE/Model/ProjectAggregate/StageEntity/Stage.cs:53-68, 150-191, 360-377; CORE/Services/WorkloadShares/StageAllocationRegimeEvaluator.cs:97-104; PKG/integrated-plan.md:410-411 ("#3732 … legacy-only"). Decide (Q-AP-1) and state in R2a: either (i) ConfigureWorkloadShares refuses canonical stages until AL1 (add AC-R2a: "allocation cannot be enabled on a canonical stage; an enabled allocation blocks admission of its stage"), or (ii) a target adapter reads formVersion.Target, the regime records FormId/FormVersionId, publication of a form version with a different target is refused while a regime exists, and the regime schema moves to v2 with the documented floor. Either way, the D8 slot rule and saved-work exemption must be fed from the AP-01 projection in R2b, since R2b re-keys claims and sessions to form.
AP-07 Major PKG/open-questions-and-assumptions.md E18 (l.120); PKG/domain-model.md:159, 164; PKG/integrated-plan.md §5.11 X-ELIG (l.740) E18 re-keys claims to study + form + reviewer at F1/F3 but lists no consumer inventory. The (InvestigatorId, StageId) natural key is read by pool predicates, the D8 slot rule, typed admission, Study.GetSlotReservation, the FEAT-024 reservation fold (kept "keyed by stage and investigator" after slice 6), pmReviewerPresence, the hub join and the idle/suspension consumers. X-ELIG already requires migrating legacy untyped reservations before the eligibility flag can be enabled; E18 implies a second reservation migration. CORE/Model/StudyAggregate/SlotReservation.cs:11-12, 165-169; MONGO/ReviewEligibilityPoolFilters.cs:140-150; AllocationClaimSlot.cs:32-34; ActivityReservationAdmission.cs:34-36, 51; PKG/source-status-inventory.md:22-27 (claim-stage refactor), :110-114 (reservation migration); MAIN/docs/features/review-session-model-redesign.md presence key (l.133-139). Make E18 one migration, not two: the eligibility programme's reservation migration tool (S4-B #3742, 0 files today) should target the final key (study, form or profile, reviewer, activity) with a stage provenance field, and the plan should list every consumer above with its cutover. Add the FEAT-024 reservation fold kinds to the C7 identity amendment explicitly (protocol bump, IntroducedAt).
AP-08 Major PKG/integrated-plan.md §5.11 X-ELIG (l.740); Q-25 (open-questions-and-assumptions.md:52) The Helm mapping delivers reviewEligibilityPolicy and proportionalStudyAllocation to the API host only; project-management receives neither. Core code that branches on FeatureFlags.ReviewEligibilityPolicy runs in both hosts (StageReviewService, typed admission), and #3939 had to add a PM-side block to deliver the eligibility flag. Enabling the flag in one host but not the other is the same split-brain hazard the materialized-stats rules guard against with the durable reviewer-mode epoch. Whether any PM-hosted writer currently branches on the flag (e.g. S2-C #3695 import/bulk-update fences) is UNVERIFIED (not read). env-mapping.yaml:1146-1150 (services: [api]), :1242-1251, :1253-1262; FeatureFlags.cs:13-16; #3939 diff env-mapping.yaml (progressiveReviewBatchBackendFlags, services: [project-management]); MAIN/.claude/rules/materialized-stats.md "Durable reviewer-mode epoch". X-ELIG and Q-25: add "flag delivered to API and PM with a cross-host agreement check (durable mode, as for tracking)". Ask the eligibility owner to move both flags into fullStackFeatureFlags or an equivalent block, and to inventory PM-hosted branches. Record in the inventory §5 that these two flags are API-only today.
AP-09 Major PKG/integrated-plan.md R3a "Admission" (l.510-514), §8 eligibility row (l.982); AC-R3a-03 (acceptance-criteria.md:218) R3a assumes "one admission service extending ReviewEligibilityPolicy … including the browser, which today ignores the eligibility response". That is the paused programme's unfinished S6b, plus S4-B/S4-C and the migration tool. The plan's X-ELIG lists the flag, reservation migration and D7 tool but not the browser consumption (S6b), the S6a contract residue (#3746, conflicting), or the fixed-two fix (S5 row D3a.2, "not done, no issue tracks it"). D8(2c) is deferred to allocation Phase 3 (#3745), which AL1 does not include, so claims under reallocation stay unowned. S5 audit headline findings 1–3 and rows D3a.2, D8.2c, UI.1, RO.1 (branch origin/feat/review-eligibility-completion-audit); gh pr view 3746/3742/3741; #3745 body. Enumerate X-ELIG as S4-B, S4-C, S6a, S6b and the fixed-two correction, each with an owner and a "merged or extracted" criterion, and state which of them R3a absorbs if the programme stays paused (recommend: R3a absorbs S6b's browser consumption as part of AC-R3a-03, nothing else). Add #3745 to AL1's scope or to a named allocation Phase 3 join.
AP-10 Major PKG/domain-model.md §4 StageAllocationRegime row (l.165), §8 AL1 (l.246); PKG/integrated-plan.md §8 allocation row (l.979) AL1 is a new allocation phase that the allocation owner's roadmap does not contain (Phases 1–5: provenance, authority/lifecycle, revisions, materialisation, switchover). The plan does not say where AL1 sits relative to Phase 2 (resolver, revalidation, repair) and Phase 3 (reallocation, protected claims), nor that a form-keyed regime is a regime schema bump with an image floor and a StageAllocationRegimeCompatibilityCheck change. regime-evolution-plan.md:460-513, 543-567; README.md:194-215 (floor). In §5.8 AL1 and §8: place AL1 after allocation Phase 2 (it needs the resolver for reviewer validity on a form-scoped roster) and before Phase 3; state the regime schema v2 contents (form binding, target source) and the floor step (R0-style, one release ahead). Add to F-A exit evidence: allocation owner's phase mapping signed.
AP-11 Major #3939 Stage.cs diff (ProgressiveBatches on the embedded Stage); PKG/domain-model.md StageSettings row (l.81: "allocation and batch settings") Double ownership of batch settings: #3939 stores a mutable Stage.ProgressiveBatches record (own SchemaVersion, PlanId, guarded by project version) on the embedded Stage; the plan puts "allocation and batch settings" inside immutable StageSettingsVersions (PV2). Nothing says which is authoritative for a canonical stage, how a settings version publication interacts with #3939's "disable the stage before changing batches" rule, or how Stage.WorkloadShares/AllocationRegime pointer (also on the embedded Stage) relate to StageSettings versions. #3939 diff Stage.ConfigureProgressiveBatches; Stage.cs:150-226 (allocation fields on Stage); domain-model.md:81, 158. Decide at F3: canonical stages reference the batch plan and allocation regime by ID from the StageSettings version (settings version carries batchPlanId, allocationRegimeId; plan/regime records stay in their own collections); the embedded Stage.ProgressiveBatches/WorkloadShares remain legacy-only. Record in domain-model §4 and in #3939's X-BATCH join.
AP-12 Minor #3939 README diff ("Decisions for initial implementation", "Chris confirmed this disposition"); PKG/decision-register.md; ledger The implementation PR records an owner decision (excluded studies stay in the denominator and count as finished) that appears nowhere in the ledger or register. The plan's precedence rules make the ledger authoritative; an unrecorded decision in a PR body is at best a proposal. git diff origin/feat/plan-progressive-shared-review-batches-5g8ysx origin/feat/implement-progressive-shared-review-batches-auxiaq -- docs/features/progressive-review-batches/README.md. Ask Chris to confirm (Q-AP-2); if confirmed, add a ledger entry (e.g. BT1) and cite it in C7; if not, mark #3939's README "PROPOSAL".
AP-13 Minor PKG/migration-adoption-rollback.md §3 (l.92-110) No adoption row decouples the legacy target/threshold coupling: Stage.SessionCountTarget inherits the project screening threshold and allocation freezes the inherited value. Under SF2/R3b the form owns the target and the profile owns the screening rule, so adoption must materialise the effective annotation target into the form version and the threshold into the compatibility profile, and must say what an enabled legacy regime becomes. Stage.cs:177, 360-365; STATUS.md:220-226 ("inherited reviews-per-study materialised on the stage"). Add E10 rows: "Stage target (override or inherited) → form target, materialised"; "Project agreement threshold → compatibility profile rule"; "enabled legacy regime → frozen legacy regime record, allocation disabled on adoption unless AL1 is live". Add AC-R6 criteria.
AP-14 Minor AC-R3a-06 (acceptance-criteria.md:221); PKG/contracts.md C7 "Statistics protocols" (l.326) Replacing "enough = 2" with targets changes the stage/membership annotation and domain-reconciliation derivations that FEAT-024 deliberately mirrors; that is a breaking derivation change requiring a digest/reconciliation plan under the FEAT-024 rules, and today no issue tracks it (S5 D3a.2). The plan assigns it to R3a without a FEAT-024-owned step. AnnotationThresholds.cs:16-28; ProjectStageConfigurationChange.cs:19-22; MAIN/.claude/rules/materialized-stats.md (digest/N-1); review-eligibility-policy.md:1048-1058. Make the fixed-two correction a FEAT-024-owned prerequisite of R3a (protocol bump, BreakingSince, rebuild plan), tracked by its own issue, and cite it in F3 exit evidence.
AP-15 Minor PKG/integrated-plan.md R2a "Coexistence" (l.410-411); PKG/ui-coverage-comparison.md:89 The allocation read model (workload-shares/my-studies, /progress) counts "own ordinary saved sessions" from embedded data, and the editor gates on ReviewMode.Annotation. The UI comparison retains the allocation progress panel but the plan never says what these read on a canonical stage. MAIN/docs/features/proportional-study-allocation/delivery-plan.md:39-61; Stage.cs:156; MAIN/src/services/web/src/app/stage/stage-admin/proportional-allocation/* (exists). Add to C17/AL1: the allocation read APIs and editor either refuse canonical stages (with AP-06 option i) or read the AP-01 projection; add AC-AL1 criteria for both reads.
AP-16 Minor AC-AL1-01..03 (acceptance-criteria.md:378-380) AL1's criteria omit: regime provenance on canonical session versions (today AnnotationSession.AllocationRegimeId and the claim's regime), reviewer validity on the form-scoped roster (#3611), refusal when a bound form version's target differs from the regime, the regime schema floor, and the D8 slot rule over form-keyed claims. README.md:173-192; acceptance.md:45-98. Add AC-AL1-04..08 covering each item.
AP-17 Minor PKG/integrated-plan.md R3a (l.515-520); AC-ALL-09 Selection is $sample after $match on project-wide pools; R3a adds per-profile route filters and batches add an $in of up to 10,000 study IDs per Next (InBatch). FEAT-008 proposed a rand-field range scan and a 400 ms p95 target; the plan carries neither a selection budget nor a decision on the strategy. StudyRepository.cs:755-804; #3939 ReviewEligibilityPoolFilters.cs diff (InBatch); MAIN/docs/features/stage-filtering/README.md:199-237. Add an AC-R3a selection p95 budget (PROPOSAL: 400 ms at 100k studies, from FEAT-008) and decide the sampling strategy at F3; make it part of the X-BATCH perf gate.
AP-18 Minor PKG/source-status-inventory.md §6 (l.353-371) The supersession list includes FEAT-026's "no sequential stage-step model" but not its D3a-derived "Do not add a stage closure concept", which LC1/RX2 supersede; #3939's ConfigureProgressiveBatches requires a disabled stage, which R3c's Completed/Reopen lifecycle must map onto. FEAT-008's Filter Set model (JSON rules, same-profile $elemMatch simplifier, which remains a correctness requirement for screeningOutcomes[] filters) is not dispositioned either. #3936 README "Do not add a stage closure concept"; stage-filtering/README.md:75-96. Add both rows to §6; at F3 decide whether the Filter Set schema is the route representation and keep the simplifier rule.
AP-19 Minor PKG/integrated-plan.md §5.11 X-BATCH (l.745) Batches require reviewEligibilityPolicy on; the legacy (flag-off) Next path has no batch integration. R3c's reuse of batch readiness therefore transitively depends on X-ELIG for every project, which §5.11 does not say. ProgressiveReviewBatches.ConfigureAsync (both flags), GetStatusAsync (returns disabled unless both on); StageReviewService diff (batches only inside GetRandomEligibleStudyAsync). Add X-ELIG as a prerequisite of batch activation in §5.11 and the graph.
AP-20 Minor PKG/ui-coverage-comparison.md §4; PKG/contracts.md C17 The unfinished legacy PartitionSet model and the unflagged study-partitions admin route survive on main; FEAT-026's README warns not to expose them as batches. The plan's IA does not retire or hide them. PartitionSet.cs; project-admin.routes.ts:53-55. Add to C17 coexistence: remove the route (or flag it off) in the first L16 PR; add to the writer/reader inventory as "retire".
AP-21 Note PKG/source-status-inventory.md §4, §7; allocation STATUS.md Inventory is stale on allocation: #3603 merged 24 Sep (2562a5d0e), 7f8b9f4c1 landed 3 Oct after the plan's baseline; STATUS.md:71 still says "pending review/merge"; #3048 is closed, not "paused". git log; gh pr view 3603/3048. Refresh before G0; ask the allocation owner to update STATUS (it is listed as stale in §7 already).
AP-22 Note ReviewController.cs:500-1609 vs StudyRepository.ActivityReservations.cs:19-21 The allocation flag is read twice per request on the eligibility path: the controller passes the runtime-overridable RuntimeFeatureFlags value as applyWorkloadShares, while the default TryAdmitActivityReviewAsync overload reads the process FeatureFlags.ProportionalStudyAllocation. A runtime override can disagree with admission. ReviewController.cs:500, 626, 753, 790, 1173, 1443, 1609; #3939 diff context of StudyRepository.ActivityReservations.cs:19-21. C6/C16: one flag evaluation per request, passed into admission; add a test.
AP-23 Note PKG/integrated-plan.md AL1 AL1 builds on a base that has never passed human acceptance: the #3327 checklist is entirely pending and #3269 remains open. preview-acceptance-2026-09-07.md:39-55. AL1 entry: #3269's legacy-stage checklist complete (preview or staging) before F-A.
Change Rationale Timing Compatibility / migration Risk PR slicing Owner
Introduce an IReviewMembershipFacts/projection seam that the pool predicates, StageWorkloadShareEligibility, AllocationClaimSlot and ActivityReservationAdmission read, with the embedded-Study implementation as the first provider AP-01: lets canonical FormSessions, claims and decisions feed the same policy without a fork; the truth table becomes the conformance suite Before F1 freezes E20; no behaviour change on legacy None persisted; pure refactor behind the same tests; the projection provider arrives in R2a Low; regression risk covered by ReviewEligibilityPoolPredicateTests row-by-row PR 1: Core seam + embedded provider + truth-table parity; PR 2 (R2a lane): projection provider Eligibility owner (seam), L1 (provider)
Allocation on canonical stages: either refuse ConfigureWorkloadShares when the stage is canonical, or add a form-target adapter with equality check and FormId/FormVersionId on the regime (schema v2) AP-06: the evaluator reads stage.SessionCountTarget and the editor requires ReviewMode.Annotation, neither valid on canonical stages Decision at F-A design; refusal variant ships with R0/R2a; adapter variant with AL1 Refusal: no data change. Adapter: regime schema bump ⇒ image floor one release ahead (README floor rules); pmStageAllocationRegime keeps unknown elements Refusal: low. Adapter: medium (floor, startup check) Refusal: one small PR with the R0 admission service. Adapter: PR A regime schema v2 + floor; PR B evaluator reads form target; PR C editor/read APIs Allocation owner
Scoped admission for RA5 / >target work: a single-use AdditionalReviewAdmission honoured by typed admission and the pool filters (bypasses bucket and enforced target for one reviewer/study/form) AP-03 Design at F4; implement in R4a; allocation owner signs the bypass in C7 Additive claim field; flagged with R4a Low One PR in L6 touching ActivityReservationAdmission and ReviewEligibilityPolicy facts (AdditionalReviewAdmitted) L6 with eligibility and allocation owners
Progressive batches (#3939): split into (1) pure completion/progression decisions behind an evidence seam, (2) membership/plan persistence with a durable opening/grant transition that emits a pool-entry record, (3) selection integration; hold (3) and any environment activation behind a perf qualification like #3252 AP-04, AP-05, AP-11, AP-19 Now, before X-BATCH; do not merge #3939 as one 63-file PR into the plan's seam Three new collections are additive; Stage.ProgressiveBatches must become legacy-only and the plan's StageSettings reference the plan by ID; flag already requires reviewEligibilityPolicy Medium: the PR is conflicting and its lazy frontier is O(N) per Next PR 1 (Core): ProgressiveBatchCompletion + IStudyObligationEvidence + truth tables; PR 2 (Mongo): plan/membership/access + CAS opening + event; PR 3: Next integration + direct-access checks; PR 4: UI Batch owner (with Chris's decision Q-AP-6)
Deliver reviewEligibilityPolicy and proportionalStudyAllocation to the project-management host and add a cross-host agreement check AP-08 Before any X-ELIG enablement (staging first) Helm mapping change only; generator run; no data change Low One PR in the eligibility programme (replace #3939's partial block) Eligibility owner
One reservation-key migration (S4-B tool) targeting the final key (study, form or profile, reviewer, activity) with stage provenance, consumed by both X-ELIG and E18 AP-07 Design at F1 (key), execute with X-ELIG Migrates untyped→typed and stage→form keys once; FEAT-024 reservation fold kinds bump protocol (IntroducedAt) Medium: touches hub, consumers, fold S4-B as the vehicle; FEAT-024 protocol bump as a separate PR Eligibility owner; FEAT-024 owner; presence owner
Replace fixed two in StudyStats/AnnotationThresholds with the effective target, as a FEAT-024 breaking derivation with a rebuild plan AP-14 Before R3a ships (F3 exit) Digest input change ⇒ reconciliation plan; N-1 window Medium FEAT-024-owned PR with its own issue FEAT-024 owner
Out-of-request authority resolver (#3251) delivered by the authorization programme's evaluator and consumed by allocation validity and the R3a Monitor AP-02 Join X-AUTH-RESOLVER at F3; blocks AC-R3a-09 and AL1 None; read-only evaluation Medium (auth semantics across providers) Authorization programme PR; allocation Phase 2 PR consumes it Authorization owner (#3335), allocation owner
Retire the legacy study-partitions route and PartitionSet AP-20 First L16 PR PartitionSet field remains readable; route removal only Low One web PR L16
Do not build further: allocation Phases 3–5 (reallocation, materialisation) and #3939's selection integration against embedded Study, until E20/E18 and X-BATCH freeze AP-01, AP-07 Until F1/F3 — Avoids a second rewrite — Allocation and batch owners
Adopt from the programmes into the plan: the regime pattern (immutable record + transition log + pointer + startup compatibility check) as the template for StageSettingsVersion and batch plans; the #3603 project-revision fence as the publication fence for StageSettings; the review-eligibility truth table as the C6 conformance format The plan's C6/PV2 and R0 floor describe exactly these mechanisms without naming the working implementations At F1/F3 — — — L0/L4

5. Changes to the plan

  • PKG/contracts.md C1 "Study summary projection" (l.108) and C7 (l.323-330): replace "bounded per-bound-stage tallies and flags" with the per-reviewer membership projection of AP-01; add the truth-table parity fixture; add the RA5 scoped admission row (AP-03); add a consumer inventory to the slot-reservation row (AP-07); add "allocation on canonical single-stage forms" (AP-06).
  • PKG/open-questions-and-assumptions.md E18/E19/E20 (l.120-122): E20 as above; E18 "one migration with S4-B"; E19 to name the evidence seam and the pool-entry-based denominator for batches.
  • PKG/integrated-plan.md §5.4 R2a/R2b: state the canonical-stage allocation rule (AP-06) and that R2b re-keys the D8 slot rule; §5.5 R3a: Monitor limited to pool-level counts until X-AUTH-RESOLVER (AP-02); §5.5 R3c: X-BATCH defined per AP-04/05; §5.11: add X-AUTH-RESOLVER, PM flag delivery under X-ELIG (AP-08), X-ELIG as a batch prerequisite (AP-19), the S4-B/S4-C/S6a/S6b enumeration (AP-09); §5.8 AL1: phase placement and schema floor (AP-10); §8: refresh allocation and batch rows (AP-21), add #3745.
  • PKG/domain-model.md §4: batch and allocation settings referenced by ID from StageSettingsVersion, embedded Stage.ProgressiveBatches/WorkloadShares legacy-only (AP-11); §5: rows for batch opened / personal grant (AP-05); §3.2 PoolEntryEvent: name the batch sources.
  • PKG/acceptance-criteria.md: reword AC-R3c-05 (AP-04); add AC-AL1-04..08 (AP-16); add RA5-under-allocation (AP-03); add a selection p95 budget to R3a (AP-17); add AC-R2a "allocation refused/adapted on canonical stage" (AP-06); add to AC-R2b-03 "pool predicates and D8 slot rule read form-keyed claims".
  • PKG/migration-adoption-rollback.md §3: target/threshold decoupling rows and enabled-regime disposition (AP-13); batch row: "membership re-based on pool-entry events".
  • PKG/source-status-inventory.md §5: both flags API-only (AP-08); §6: FEAT-026 "no closure concept" and FEAT-008 Filter Set rows (AP-18); §4/§7: refresh (AP-21).
  • PKG/decision-register.md: record or demote the #3939 denominator decision (AP-12).

6. Questions for Chris

ID Question Recommendation
Q-AP-1 In R2a, may an administrator enable proportional allocation on a canonical (single-stage) form at all, or is allocation refused on every canonical stage until AL1? Refuse until AL1. Pilots are small, the adapter needs a regime schema bump and floor, and refusing is a one-line guard in R0's admission service.
Q-AP-2 #3939 says you confirmed that sufficiently excluded studies stay in a batch's denominator and count as finished. Is that a decision to record in the ledger? Record it (it matches the "no fresh annotation obligation" rule) and add "restored scope returns to its original membership" as part of the same entry.
Q-AP-3 May an additional-review request (RA5) override allocation buckets and the enforced annotation target for the requested reviewer only? Yes: a single-use, expiring, audited admission that never changes the target and is reported separately from allocation progress. Without it RA5 cannot work on allocated or enforced-target stages.
Q-AP-4 Until the out-of-request resolver exists, should "Who is offered what" ship as pool-level counts by refusal reason (no per-reviewer rows), or wait? Ship the pool-level view in R3a; add per-reviewer rows when X-AUTH-RESOLVER lands. Per-reviewer evaluation with the admin's own groups would be the "empty-claims approximation" you ruled out for allocation.
Q-AP-5 For PRISMA box 4/8, does a study "enter screening" at the first release to any reviewer (shared batch open or first personal grant), with both kinds recorded? Yes: pool entry = first release to anyone; record shared-open and personal-grant events separately so early-stopped and batched reviews report honestly.
Q-AP-6 Should #3939 be merged now as a legacy-only feature behind its two flags (accepting rework at X-BATCH), or held until the completion seam, durable opening events and the performance gate exist? Merge in slices: the pure completion/progression decisions and the persistence with durable opening now (small, reusable by R3c); hold the Next integration until the performance gate; never activate before X-ELIG is enabled in the same environment.

7. Coverage gaps

  • Not read: #3695 (S2-C fences; AP-08's PM-branch claim is therefore partly unverified), #3736's dispatcher, the SignalR hub join code, the allocation web components' logic, StudyRepository.ActivityReservations.cs on main beyond the #3939 diff context, the FEAT-024 reservation fold kinds in detail, review-eligibility-truth-table.md, performance-validation.md and editor-save-workflow.md in full, notifications-integration.md.
  • Not run: any tests, benchmarks or the e2e allocation-capacity-integration.spec.ts; no database reads; no live runtime-flag overrides read (cluster-gitops values only, head 7c377aa4).
  • Unverified: the eligibility programme's pause (session memory only; #3746 had activity on 1 Oct); whether #3939's Mongo tests cover its claimed 2,500-batch index plan; whether ProjectAuthorityEvaluator can serve as #3251's resolver (plausible from the inventory, not checked in code).
  • Round-1 findings were not repeated except where their resolution is inadequate: C-27 (allocation join moved to F1/F-A) and B-18 (amendment A) are the two whose resolutions AP-05/AP-06 show to be incomplete.

Critical Files for Implementation

  • /home/chris/workspace/syrf/pr/pr3617.research-screening-as-specialised-annotation-gxgahs/docs/planning/integrated-review-plan-2026-10/contracts.md
  • /home/chris/workspace/syrf/pr/pr3617.research-screening-as-specialised-annotation-gxgahs/docs/planning/integrated-review-plan-2026-10/integrated-plan.md
  • /home/chris/workspace/syrf/main/src/libs/project-management/SyRF.ProjectManagement.Mongo.Data/ReviewEligibilityPoolFilters.cs
  • /home/chris/workspace/syrf/main/src/libs/project-management/SyRF.ProjectManagement.Core/Services/WorkloadShares/StageWorkloadShareEligibility.cs
  • /home/chris/workspace/syrf/main/src/libs/project-management/SyRF.ProjectManagement.Core/Services/ReviewEligibility/ActivityReservationAdmission.cs