Temporary planning review record. The report below is reproduced verbatim as returned by the independent read-only reviewer (Plan agent, Opus model, launched 3 October 2026 about 04:05 BST). Only this front matter and note were added. Resolutions are in the resolution matrix.
Review B: data contracts, migration, PRISMA and history¶
Reviewer: independent adversarial reviewer B, read-only, 2026-10-03.
Scope: contracts C1–C17, migration, adoption and rollback, PRISMA, and history/point-in-time reproducibility. I modified nothing. I read every package file, including the four (contracts, decision register, integrated plan, open questions) that were edited at 04:06 while this review was running. My line numbers refer to those 04:06 versions.
Path legend (absolute roots):
- 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/
- SRC = /home/chris/workspace/syrf/main/ at 78c6d097d, read with git show 78c6d097d:<path>. Every cited source file is unchanged at today's HEAD 9d74077d8.
- Code prefixes inside SRC:
- Core/ = src/libs/project-management/SyRF.ProjectManagement.Core/
- Mongo/ = src/libs/project-management/SyRF.ProjectManagement.Mongo.Data/
- API/ = src/services/api/SyRF.API.Endpoint/
- Web/ = src/services/web/src/app/
- DOCS = /home/chris/workspace/syrf/main/docs/features/
- RULES = /home/chris/workspace/syrf/main/.claude/rules/
- NOTIF = /home/chris/workspace/syrf/pr/pr3947.checked-pdf-proposals-and-explicit-administrator-a-r6bdl2/ (head ae3c6749d)
Verdict¶
Not ready as an implementation basis for R2 or later. Its principles are sound, but its contracts are not yet safe.
What holds up. The package restates the owner decisions faithfully at the level of rules. Its migration principles are right: additive, no fabricated history, per-project adoption, canonical-aware rollback, rehearsal. Its baseline facts are mostly verified.
The blocker. Rollback rests on "flag off" plus a writer floor that no release actually establishes: - Today's strict BSON class maps mean old pods would throw on new embedded fields, not just drop them. - Admission is described as a config allowlist, so a config change or an image rollback could hand canonical scopes back to legacy writers.
Major gaps by theme.
- Storage and Study coupling:
- Moving revisions out of Study does not remove Study coupling: fold pending statistics, claims, bulk-lock write skew, and the embedded tallies that pool and capacity queries read.
- Autosave drafts have no contract.
- Publication at scale is undefined.
- FEAT-024 statistics:
- Confirmed decision PS1 is quietly replaced by assumption A-08, because statistics are dark in production and refused there until gate (b).
- Several FEAT-024 seams are wrong or missing.
- History capture: it starts later than first use. R2 pilots screen on the overwrite-in-place legacy path.
- PRISMA:
- The binding FEAT-011 spec conflicts with the plan in ways amendments A–F don't cover: platform-wide MIG-11/MIG-12, the outcome schema, $unset rollback, release-checklist mapping.
- The core three-level model and deduplication are not placed in any release.
- The package is factually wrong about search deletion, which is disabled today pending a separate reversible-deletion programme.
- Legacy adoption:
- Session membership after cross-stage overwrites is unspecified.
- The status of legacy reconciled answers ("authority unknown") is unspecified.
- The outcome-direction mapping (ODIR1) would turn default false values into real directions.
Recommendation. Resolve B-01 and the G1-level Majors before G1 can pass: B-02–B-07, B-12, B-13, B-26, B-27, B-33. Correct B-16, B-17 and B-20 before P1 design proceeds.
Findings¶
| ID | Severity | Location | Finding | Evidence | Recommended resolution |
|---|---|---|---|---|---|
| B-01 | Blocker | PKG/contracts.md C16 (l.369-376); PKG/migration-adoption-rollback.md §1.5 (l.37-41), §5 R2/R3 rows (l.97-99), R7 row (l.110), §6 (l.114-121) | Rollback safety rests on "flag off" plus an unspecified writer floor, but nothing ensures that the binaries a rollback would restore already honour the floor or refuse legacy writes to canonical scopes. (1) Study's non-Entity embedded types use strict class maps, so a new embedded field makes older pods throw FormatException during a rolling deploy or image rollback, rather than drop the field. (2) Per-project admission is described as a config allowlist, so removing a project from the list, or reverting Helm values, would route an adopted or greenfield scope back to legacy writers. (3) The R7 row ("old binaries are no longer valid rollback targets") implies that earlier image rollbacks are valid. |
Mongo/Repositories/StudyRepository.cs:3176-3187 (ScreeningInfo), 3209-3216 (ExtractionInfo), 3220-3231 (SessionTally): AutoMap() with no SetIgnoreExtraElements. Only Entity subclasses carry [BsonExtraElements] (SRC src/libs/kernel/SyRF.SharedKernel/BaseClasses/Entity.cs:21-22). ScreeningInfo and SessionTally are ValueObjects (Core/Model/StudyAggregate/ScreeningInfo.cs:25, SessionTally.cs:7). FEAT-024 first had to make its maps tolerant (RULES/materialized-stats.md l.33-35, #3145) and warns "rolling back to retirement-unaware writer binaries is not [safe]" (l.256-262). #3939 body: "no automatic mixed-version fleet gate". The FEAT-024 allowlist is config (IsProjectAllowlisted). |
Add an "R2-0 compatibility floor" release before any canonical write: (1) tolerant maps or ExtraElements on every embedded type the programme may extend; (2) a durable canonical-ownership record per project or scope, stored as data rather than config and read inside the transaction by every legacy writer (API, PM consumers, Quartz, imports, bulk update, reconcile, session delete, question edit/delete), which refuses with a typed 409. Flags gate only new admission, never ownership. Record the minimum rollback image per service in each release ADR, and rehearse an image rollback, not just flag-off, with canonical data present. Repeat the floor step before R3 (ScreeningInfo) and before P1 (Study root fields). |
| B-02 | Major | PKG/contracts.md C1 baseline (l.73-81), C7 (l.242-256); PKG/open-questions E15 (l.85); PKG/integrated-plan.md §10 first risk | Moving revisions out of Study does not remove Study coupling, and no contract says what each canonical transaction must still write to Study. (1) Pool selection, capacity guards, reconciliation readiness and legacy statistics read Study-embedded, stage-keyed SessionTallies. (2) Claims live in Study.SlotReservations. (3) Fold-mode statistics need a version-guarded whole-Study write of the complete PendingStatistics set. (4) A canonical commit that only reads Study.BulkUpdateLock under snapshot isolation cannot conflict with a concurrent bulk-update lock write (write skew). | SessionTallies predicates at Mongo/Filters.cs:245, 334-370, 432-439, 583-590. Tallies computed from embedded sessions: Core/Model/StudyAggregate/ExtractionInfo.cs:31-81. PendingStatistics and StatisticsFoldSequence: Study.cs:142-171. RULES/materialized-stats.md l.56-68: "Every whole-Study write must stay version-guarded". RULES/bulk-study-locks.md l.21-22: snapshot writes check current.BulkUpdateLock. |
Add a "Study summary projection" contract to C1/C7: the bounded per-bound-stage tallies and flags that legacy query surfaces need, maintained in the canonical commit's transaction. Require each canonical commit to version-bump Study (or another document the lock writer also writes) and to carry fold pending entries. Inventory every reader of embedded tallies (pool filters, capacity, readiness, StudyStats, exports, #3944) with its cutover. Make all of this E15 acceptance criteria. |
| B-03 | Major | PKG/contracts.md C5 states (l.185-197); PKG/integrated-plan.md R2 MVP (l.236-250) | Autosave drafts (SL1) have no contract. Undefined: storage; identity, given that SF1 lets two stage tabs reach one session (one draft per session or per tab?); base-version CAS; retention and audited discard; visibility; and how drafts are enumerated by form version for the draft_only publication category. (1) LC1 makes drafts block automatic stage completion, so an abandoned draft can block completion indefinitely. (2) Two incompatible draft models already exist in the docs. (3) Drafts stored on Study would bump Study.Version on every autosave, and both statistics source revisions and CAS retries depend on that version. |
Ledger SL1 l.42 (representation "not settled"); LC1 (drafts count in readiness). PLN/review-lifecycle-gold-settings-proposal-2026-10-03.md:36-38: disposal "explicit and audited". DOCS/annotation-versioning/README.md:480: one mutable pendingAnswer per Annotation. PLN/screening-specialised-annotation-research.md:705-710 rejects that model. SRC src/services/web/CLAUDE.md:343: no implicit server saves. |
Add a draft/workspace contract at G1: (1) drafts live outside Study; (2) one draft per session, pinned to a base explicit version, with CAS; (3) a lease or merge rule for two stage tabs, with a typed stale conflict that preserves both; (4) explicit, audited discard by the owner, or by an admin after revocation, which feeds LC1; (5) drafts indexed by base form version for PS1; (6) never visible to reconcilers or exports; (7) a retention policy. Mark the pendingAnswer model as superseded. |
| B-04 | Major | PKG/contracts.md C5 transitions and tests (l.193-204); PKG/integrated-plan.md R2 "Entity order ... move to the form session" (l.250) | C5 omits transitions that existing writers already perform. (1) Session removal: today's reviewer endpoint hard-deletes the session and all its answers and outcome rows. Canonical sessions need an append-only Withdraw, and revisions pinned by gold or reconciliation must never be deleted. (2) Presentation-only edits: an AF2 entity-order change enables "Save progress", and under SL3 an order-only Save after Complete removes completed qualification. (3) No rule for Save on a session still pinned to an older form version after a doNothing publication. (4) No SF6 tests: a warning alone keeps Complete, while a policy-created incomplete version removes it. |
API/Controllers/ReviewController.cs:86 RemoveSession → ExtractionInfo.DeleteSession (Core/Model/StudyAggregate/ExtractionInfo.cs:355-369); Study.cs:336-371. SRC src/services/web/CLAUDE.md:337: "Order-only edits enable Save progress". Ledger SL3 l.44, SF6 l.527-540. | Add Withdraw, with its effects on qualification and candidate pins, and decide whether reviewers may remove canonical sessions at all. Keep presentation state outside immutable versions, with no effect on qualification. Define Save under an older pin in E1. Add the SF6 tests. |
| B-05 | Major | PKG/contracts.md C4 publication command (l.168-175), C8 (l.258-269); PKG/integrated-plan.md R2 acceptance 4 (l.270-271); PKG/notifications-integration.md §3 first row (l.110) | Publication is written as one atomic act: version, transition records, affected identities and notifications. (1) Large projects cannot apply transitions to thousands of sessions inside one MongoDB transaction (lifetime and size limits), nor hold a "brief" fence that long. (2) "A concurrent Save lands ... under the new version" reads as silently rebasing v1 work onto v2, which VU1 and the publication-pause guidance reject. (3) FV3 counting must change at publish time even if application is deferred. | Ledger publication-pause section l.235-243: "retain the draft and request reviewer action rather than silently rebasing". VU1 l.60. Inbox capture requires the source transaction (NOTIF src/libs/project-management/SyRF.ProjectManagement.Mongo.Data/Repositories/SourceInboxCapture.cs:58-60). | Use two phases. (1) Under the fence, atomically publish the version, the policy record and a frozen impact-set manifest. (2) Apply transitions idempotently in batches, or evaluate the recorded policy lazily on read. Qualification and statistics follow the policy immediately. A late Save based on v1 is either accepted as v1 and swept into the policy, or refused with a draft-preserving conflict; never rebased. Capture notifications once per recipient per publication. Add a 10,000-session fixture. |
| B-06 | Major | PKG/contracts.md C8 (l.258-269); PKG/open-questions A-08 (l.121); PKG/integrated-plan.md G2 (l.484) | Confirmed decision PS1 requires materialized, version-aware usage statistics. FEAT-024 is dark in production, and fold enable is refused on the production database until gate (b), which belongs to a separately approved rollout. Assumption A-08 replaces PS1 with authoritative computation whenever the family "is not ready", which in production means always for the foreseeable future. G2 needs only the FEAT-024 owner's signature, not live statistics. This deviates from an owner decision but is presented as an assumption. | Ledger PS1–PS3 l.51-53 and l.158-184. Commit 89d295734, merged in #3948 at 02:56Z on 3 Oct: "enable and reset still refuse the production database ... until gate (b)". DOCS/materialized-project-statistics/README.md:246-249: production pilot needs 7 days, 10,000 reads and 1,000 mutations of staging proof. | Add a Batch B question for Chris. Either (a) R2 production publication waits for the FEAT-024 usage family plus production activation, as an explicit cross-programme G2 dependency; or (b) authoritative counting and identity enumeration under the protected boundary satisfies PS1/PS2 for pilots. Record the answer instead of A-08. |
| B-07 | Major | PKG/source-status-inventory.md fact 15 (l.114-119); PKG/contracts.md C7 (l.250-256), C8, C1 Command row (l.92) | Several FEAT-024 seams are wrong or missing. (1) The "natural seam for PS1" scope is stage-keyed (stage + question + version), while PS1 forbids stage-summed usage. (2) Changes to form target, binding and publication policy move counters without a Study write, so they need the existing definition-rewrite fence. (3) Replacing stage targets and thresholds changes the inputs to the projection configuration digest, which needs the documented reconciliation plan. (4) ProjectQuestion authorization resolves through Project.AnnotationQuestions. (5) C1 creates a second idempotency system beside FEAT-024's source-operation receipts, which are bound to Study.Version and have a retirement floor. |
Scope kinds StageQuestionVersion=4 and ProjectQuestion=6: SRC src/libs/project-management/SyRF.ProjectManagement.Core.Tests/ProjectStatistics/ProjectStatisticsValueObjectTests.cs:227-238. RULES/materialized-stats.md: digest l.30-46; ProjectQuestion l.121-124; definition-rewrite fence l.244-247; receipts l.256-262. API/Services/SubmitAnnotationSessionService.cs:155-183: BeginOperationAsync and ResolveByReceiptAsync bound to the Study version. | Amend C7/C8: (1) request new stage-free FormVersion and QuestionVersion scope kinds (append-only ordinals); (2) use IProjectStatisticsDefinitionRewriteFence for definition moves; (3) supply a digest compatibility plan; (4) resolve canonical questions for ProjectQuestion authorization; (5) declare derivation changes as breaking in the N-1 window. In C1, define a single receipt authority, or reuse the FEAT-024 receipt store with canonical identity. |
| B-08 | Major | PKG/integrated-plan.md R2 MVP and critical path (l.227-285) | R2 is too large for its dependencies. It bundles: the engine; form versioning; autosave drafts (an AF2 contract change); immutable Save/Complete; history; the full impact dialog with the publish gate; a new statistics derivation; claim re-keying for presence; allocation refusal; export modes; and server-side validation. Its critical path runs through FEAT-024 (B-06) and AF2 production readiness (Q-25), so the early value of shared sessions and history waits on the most externally dependent part, publication. | R2 MVP list and acceptance criteria 1-8; A-14 (pilots publish several times); FEAT-024 production refusal (89d295734). | Split R2. R2a: engine, shared sessions, drafts, Save/Complete, history, v1 forms and previous-version export; adding a question to a used form is blocked or forces a new form. R2c: publication impact plus the publish gate, after B-06 is decided. Keep claim re-keying in R2a only if the presence owner signs E18 at G1. |
| B-09 | Major | PKG/integrated-plan.md R2 "ancestors included automatically" (l.233-235) and "Temporary R2 constraint" (l.260-261) | "A question can belong to only one form" conflicts with automatic ancestor inclusion. Any two forms on the same entity category share its label root, and system outcome and cohort trees share system roots. So either R2 allows only one form per root tree, which limits pilots and should be stated, or ancestors are exempt, in which case shared ancestor answers bring SF3/SF5 obligations (lineage display, outdated flags) into R2, contrary to the R2b scoping. | Ledger SF5 l.452-458: "answers for all its ancestors, regardless of the stage or form". C4: a form holds "question-version references including all ancestors" (PKG/contracts.md:164). Roots rebuilt from code: Core/Model/ProjectAggregate/Project.cs:223-247. | State the rule precisely. Either the constraint covers non-ancestor questions only, with ancestor answers shared read-only and lineage displayed in R2; or R2 allows one form per root tree. Add a fixture with two forms under one entity root. |
| B-10 | Major | PKG/integrated-plan.md R2 "Pilot extraction stages are annotation-only" (l.256), graph edge O1 -.new schemas.-> R4c (l.467); PKG/contracts.md C4 form row (l.164) vs C14 freeze (l.61) |
R2 never says whether quantitative extraction (Stage.Extraction / OutcomeData) is admitted. (1) Legacy OutcomeData is mutable and replaced per stage and investigator, so admitting it breaks SL2 immutability. (2) C4 already puts "extraction features enabled" into form-version content at G1/G2, while the outcome storage contract (C14) freezes only at the O1 gate. (3) R4c is drawn with O1 as optional, but canonical projects have no outcome series before O1. |
Stage.Extraction: Core/Model/ProjectAggregate/StageEntity/Stage.cs:22, 34, 75. Replace-by-stage in AddOutcomeData: ExtractionInfo.cs:228-260. AnnotationSession.cs:153-155. | Canonical forms refuse Extraction=true until O1 delivers canonical observation storage, or a canonical legacy-continuous series kind is frozen at G1. Remove "extraction features enabled" from R2 form content. Make R4c depend on O1 outright. |
| B-11 | Major | PKG/integrated-plan.md R2–R4; PKG/migration-adoption-rollback.md §5 (l.97-101) | Nothing says what reconciliation does for R2/R3 canonical pilots before R4. Legacy reconciliation readiness, candidate views and the AF2 reconcile source all read embedded sessions and tallies, and legacy reconciliation overwrites reconciled answers per question across stages and reconcilers. So pilots are either unreconcilable (canonical sessions are invisible to legacy reconciliation) or get overwritten, non-snapshot gold on top of canonical candidates. | Pool filters on SessionTallies (Mongo/Filters.cs:245-370). Reconciled-answer replacement ignores reconciler and stage (ExtractionInfo.cs:187-192). Reconciliation session uniqueness ignores reconciler (ExtractionInfo.cs:202-208). Candidates come from session annotation lists (PLN/screening-specialised-annotation-research.md:241-243). | Refuse legacy reconciliation and reconciled-answer writes for canonical forms until R4, using B-01's ownership marker. Make "no reconciliation needed before R4" a pilot selection and exit criterion, or schedule a read adapter. |
| B-12 | Major | PKG/integrated-plan.md "Screening stays on the legacy path" (l.256), R3 PRISMA groundwork (l.322), P1 (l.406), windows W2/W3 (l.502-503) | History capture starts later than first use, so the programme creates unrecoverable gaps for its own pilots. (1) Pilots screen on the legacy path, where decisions are overwritten in place. (2) Pilots created in W2/W3 import searches before P1 is live, so they have no sourceType or Citations, which FEAT-011 then excludes from source-column boxes. (3) Pool entry, retrieval status and lifecycle changes are not recorded as events until P1/R5, although FEAT-011 asks for timestamped pool entry. As a result, the EX2 limits ("whatever history exists") will bite on data produced after plan approval. | Screening overwrite: Core/Model/StudyAggregate/ScreeningInfo.cs:111-142. Null sourceType excluded from boxes 2, 4-9 and 11-15: DOCS/prisma-specification/study-lifecycle-and-source-taxonomy.md:579-585. Phase 14 SHOULD "pool entry events are timestamped": DOCS/prisma-specification/prisma-constraint-annotations.md:243-245. Amendment F: PLN/review-prisma-integration-2026-10-03.md:132-139. | Add a "capture from first canonical write" contract at G1: (1) an append-only screening-decision event log for allow-listed projects from R2, which R3 adoption can consume; (2) pool-entry events from R3; (3) retrieval and lifecycle events from P1. Either admit pilots only after P1 is live, or ship FEAT-011's admin source-classification tool with P1. |
| B-13 | Major | PKG/contracts.md catalogue C12 freeze at G7 (l.59) and C12 (l.327-336); PKG/integrated-plan.md G3 (l.485) | C12 freezes at G7, after R3 and R4 ship, yet R3 already writes PRISMA-shaped data: per-profile outcomes, reason coverage, authority values and pool-entry projections. FEAT-011's ScreeningOutcome placeholder needs refining before those writes: it has a single stageId although profiles are shared across stages, and its authority enum has no legacy or unknown value. The preliminary plan had pinned the PRISMA unit and authority contract at M0. | ScreeningOutcome {profileId, stageId, result, primaryExclusionReason, resolvedAt, authority} and the Phase 13 MUST: DOCS/prisma-specification/study-lifecycle-and-source-taxonomy.md:267-294; prisma-constraint-annotations.md:225-228. PLN/review-implementation-plan-2026-10-03.md:77. |
Split C12. Freeze the parts that shape writes (unit identities, outcome schema with authority values, structured reason, pool-entry event) at G1/G3 together with amendment A. Leave report and manifest semantics at G7. |
| B-14 | Major | PKG/migration-adoption-rollback.md §2 Legacy row (l.52), §7 (l.123-128); PKG/integrated-plan.md amendments A–F; PKG/open-questions Q-06 (l.54) | FEAT-011 conflicts that A–F don't cover. (1) Phase 16 MIG-11/MIG-12 require backfilling lifecycleStatus on all studies and migrating all screening into screeningOutcomes[]. The package keeps legacy projects unchanged indefinitely (A-04, MIG1), contradicting a binding spec without an amendment, which the owner's "not silently changed" instruction forbids. (2) The ScreeningOutcome shape (see B-13). (3) FEAT-011's $unset rollbacks, which are unsafe after canonical writes. (4) FEAT-011's "Release ½/3" checklists and FEAT-001's release "R1" collide with the new R1–R7 numbering, and nothing maps them. (5) The cascade "Delete Study removes its Citations". |
DOCS/prisma-specification/prisma-constraint-annotations.md:279-281 (MIG-11/12), 294-343 (checklists). DOCS/prisma-specification/three-level-data-model.md:262 (cascade), 291-322 ($unset). Ledger l.898: "Original Approved source specs are not silently changed". |
Add amendments G–J to Q-06: per-project adoption replaces MIG-11/12; outcome schema with a LegacyCompatibility authority value; canonical-aware rollback replaces $unset; deletion cascade. Add a table mapping each FEAT-011 checklist item to the new release that must pass it. |
| B-15 | Major | PKG/decision-register.md §2 (l.170-195); PKG/source-status-inventory.md §6 (l.270-292) | The supersession lists miss documents that would fabricate authority or misstate gold. (1) Annotation-versioning migration step 6 auto-promotes single-annotator studies to reconciled ("SingleAnnotator") versions. (2) The FEAT-006 design decisions do the same, and set "rollback = $unset" (D18). (3) Annotation versioning defines gold as "the latest AV on its reconciliation annotation", contradicting snapshot gold (GS1/QY3). (4) Gold semantics for target-1 forms is an unasked owner question, and auto-promotion conflicts with RE2/SF4's ban on automatic gold. |
DOCS/annotation-versioning/README.md:514, 571, 579 (In-Review). DOCS/reconciliation/design-decisions.md:299, 360-378, 983 (Draft). DOCS/reconciliation/data-model-migration.md:392-402. | Add all of these to the superseded lists. Add a Batch C question on target-1 forms (recommended: no gold required, exports use the candidate, or an explicit attributed "accept as gold" act; never auto-promotion). Forbid auto-promotion in R6. |
| B-16 | Major | PKG/integrated-plan.md P1 (l.406); PKG/source-status-inventory.md fact 17 (l.124-127) | The package says search removal hard-deletes studies today and that P1 will stop it. In fact, the public search, import-job and project deletion routes fail closed with a 503 until a separately owned reversible-deletion scheduler exists; only the service and repository paths still hard-delete. So P1 claims to change behaviour that doesn't currently run, and misses its real dependency. How PRISMA should treat a withdrawn search is also undecided. | API/Controllers/SearchController.cs:108-136 (both routes throw DeletionLifecycleUnavailableException). API/Infrastructure/DeletionLifecycleUnavailableExceptionFilter.cs:21-31. Mongo/Repositories/StudyRepository.cs:1122-1141: "Search deletion is disabled today ... durable deletion scheduler must close this window". Flag deletionLifecycle: SRC src/charts/syrf-common/env-mapping.yaml:786-793. |
Correct fact 17 and the P1 row. Add the deletion-lifecycle programme to §8 and to the writer inventory. Ask Chris how PRISMA should treat search and project deletion, and add a FEAT-011 cascade amendment. P1 coordinates with the scheduler's design. |
| B-17 | Major | PKG/integrated-plan.md P1 (l.406), R5 (l.388-399); PKG/decision-register.md QM v2 crosswalk (DEDUP-01..08 → L12, R5) | No release delivers FEAT-011's core three-level model or FEAT-012 deduplication: pmPublication, Study.citations[] and its backfill, lifecycleStatus with the "all required profiles Included" transition, FullTextStatus, metaAnalysisIncluded, and the dedup service and audit log. Yet R5 boxes 3, 6, 7, 10, 16 and 17 and fixtures 1, 5 and 7 depend on them, and P1's "reviewed-dedup trail" assumes dedup exists. Reviewed-record dedup is also not engine-independent: C2 keys on studyId, so merges must remap sessions and gold without double counting (amendment D). |
DOCS/prisma-specification/prisma-constraint-annotations.md:189-213 (Phase 12 MUSTs); study-lifecycle-and-source-taxonomy.md:252-259 (Included transition). PLN/review-implementation-plan-2026-10-03.md:92 (the preliminary "Phase 12 prerequisite lane"). PKG/contracts.md:110-112. | Add a P2 lane release for identification and dedup, with its own gate, or extend P1 explicitly. Define the "required profiles" set and who sets metaAnalysisIncluded. Make study merge and split a C1/C2 contract operation (remap with provenance, no double count) before any dedup of reviewed studies. |
| B-18 | Major | PKG/contracts.md C7 progressive-batches row (l.255), C12 (l.333-335) | "Frontier never used as a PRISMA count" prejudges amendment A. FEAT-011 defines box 4/8 as studies "made available to screeners". #3939 withholds studies until a shared batch opens or a personal grant is issued. Counting protocol-scope membership would therefore overstate "records screened" for reviews that stop early, or living reviews whose later batches haven't opened. | DOCS/prisma-specification/study-lifecycle-and-source-taxonomy.md:439-441 ("made available to screeners ... regardless of the outcome"). gh pr view 3939 body: shared batches open on a completion fraction; personal access to later batches. |
Remove the flat rule from C7. Put "entering screening" (protocol scope, or actual release including shared batches and personal grants) into amendment A, with fixtures for early-stopped and batched reviews. |
| B-19 | Major | PKG/integrated-plan.md R5 acceptance (l.398), G8 (l.490), R2 acceptance 7 (l.274) | All eight PRISMA fixtures gate only R5, although the source says they are "required before implementation approval/activation". Lanes that can break the invariants earlier ship without them. R2's "No PRISMA count changes" is vacuous, since no PRISMA code exists. | PLN/review-prisma-integration-2026-10-03.md:141-165. | Distribute the fixtures: F2 → R2/R3; F3 → R3; F4 → R2 and R3b; F6 → C1/C2/O1; F1, F5, F7, F8 → P1/P2, O2 and R5. Restate R2 acceptance 7 as variants of F2 and F4. |
| B-20 | Minor | PKG/open-questions Q-06 (l.54), Q-20 (l.59); PKG/integrated-plan.md §11 (l.588-592), P1 dependency (l.406), W2/W3 (l.502-503), G3/G5 (l.485-487), graph (l.473-476), W5 (l.505) | Several ordering inconsistencies. (1) Q-06 is in Batch C ("Needed by R5"), yet P1 builds in W2 against amendments C/D, the amendment PRs come only in W3, and G3 needs amendment A. (2) Q-20 is in Batch C in one document and Batch B in another. (3) G3 and G5 require the prior release to be "piloted" while W3 and W4 build R3 and R4 alongside those pilots. (4) The graph makes R6 wait for O2, while the text says waves follow each domain. (5) The W5 "O2 dry-runs" aren't marked synthetic, although Q-05 and MIG1 allow only synthetic runs. | As cited. | Move Q-06 (at least A, C and D) to Batch B, with the amendment PRs ahead of the P1 build. Fix Q-20's batch. Reword gate entry criteria as integrate/activate conditions, since consumers build against fakes. Give R6 per-domain edges. Mark the dry-runs as synthetic. |
| B-21 | Major | PKG/migration-adoption-rollback.md §2 (l.50-51), §3 (l.59-61); PKG/integrated-plan.md R6 (l.412-417) | Per-domain R6 waves ("sessions after R2, screening after R3, gold after R4") are not executable as separate scopes for most real projects. Combined stages need atomic Complete-and-Include (R3); legacy reconciliation can't see sessions once they leave Study (R4); extraction needs canonical outcomes (O1/O2). A sessions-only wave would split ownership inside a stage. | Combined mode in the D7 configuration (PKG/source-status-inventory.md fact 10); evidence for B-10 and B-11. | Define adoption scope completeness as a G9 check: a stage is adoptable for a domain only when every reader and writer of its data is canonical. In practice: annotation-only stages with no reconciliation after R2; Combined stages after R3; anything reconciled after R4; extraction after O1/O2. |
| B-22 | Major | PKG/migration-adoption-rollback.md §3 sessions, answers and stage-assignment rows (l.58-62) | The legacy-session adoption contract has four gaps. (1) A stage-A session's view filters answers by StageId, so answers later re-saved from stage B drop out of it; neither the stage filter nor the question set reconstructs what A actually submitted. (2) Merging stages into one shared form leaves two legacy sessions with conflicting statuses. (3) Legacy "Completed" sessions were never validated on the server, which matters for UA1 candidate eligibility and SF2 qualification. (4) Pinning legacy answers to an adoption-time v1 question version misstates the wording they were authored under, so AG3 would compare across overwritten wording. | AnnotationSession view filters by StageId: Core/Model/StudyAggregate/AnnotationSession.cs:149-151. Replacement ignores stage: ExtractionInfo.cs:187-194. The server never enforces requiredness (inventory fact 5). Ledger UA1 l.860-864, AG3 l.803-814. | In E10, specify: (1) a membership rule, with coverage "membership-uncertain" where an answer's StageId differs from the session's stage; (2) an admin choice when merging sessions; (3) validation at adoption, with status "legacy-completed, unvalidated" and an admin count/don't-count decision; (4) definitionVersionAtAuthoring = unknown on every adopted pin, excluded from same-version agreement unless the wording is proven unchanged. |
| B-23 | Major | PKG/contracts.md C9 baseline (l.274-280); PKG/migration-adoption-rollback.md §3 reconciled row (l.65) | The status of LegacyAuthorityUnknown is unspecified. Is it effective gold for current exports? Can it be queried (QY)? Does R4 treat a study with legacy reconciled answers as needing canonical reconciliation, as already reconciled, or as conflicting? What happens to legacy per-stage reconciliation sessions marked Completed, versus per-question answers that have been overwritten across stages? |
Overwrite: ExtractionInfo.cs:187-192. One reconciliation session per stage: ExtractionInfo.cs:202-208. Ledger EX1 l.55 (current downloads include gold); QY1–QY3 l.61. | Decide in the C9 ADR. Recommended: exportable as "legacy reconciled (authority unknown)"; never a gold snapshot (GS1); not queryable; superseded by a canonical task with explicit provenance; R4 readiness treats it as "reconciliation required for canonical gold". Add fixtures. |
| B-24 | Major | PKG/migration-adoption-rollback.md §3 outcome row (l.66); PLN/outcome-data-migration-plan-proposal-2026-10-03.md:89-100 | "Direction ... consolidated only when values agree (ODIR1)" will fabricate directions. Legacy GreaterIsWorse is a non-nullable bool whose untouched default is false at every layer, so "agreeing" falses may simply be unset. The v1 defaults errorType 'SD', averageType 'mean' and NumberOfAnimals 0 also look like valid answers. The proposal only warns about zero in TimePoint. |
OutcomeData.cs:39 (bool). System checkbox defaults to Unchecked: Core/Model/ProjectAggregate/AnnotationQuestion.cs:646-661. Client defaults V1_DEFAULT_OUTCOME_GREATER_IS_WORSE=false, 'SD', 'mean', 0: Web/shared/annotation/annotation-form-v2/annotation-form-outcome-topology.ts:40-44. |
Treat legacy false, 'SD', 'mean' and 0 as "value or default (unknown)" unless the dry-run proves an explicitly stored answer. Prefer the outcome-level system answer over row copies, and flag stale copies. Consolidate only within one author's (or one reconciled authority's) series for one outcome entity, never across reviewers. Add these cases to the Q-05 dry-run acceptance. |
| B-25 | Major | PKG/contracts.md C14 (l.356-357); PKG/ui-coverage-comparison.md outcome rows and gap 15 (l.149) | C14 doesn't say whether an outcome measure is a reviewer-created, per-study entity (its direction then being a revisable, reconcilable answer) or a project-level definition. The UI comparison's "outcome-measure definition" points to a project-level catalogue, which reverses the recorded clarification that reviewers create measures from the paper and that admins must not be required to predefine them. ODIR1's scope ("in a paper/population") supports the per-study reading. | PLN/unified-annotation-classification-research.md:564-568, 600-608, 623. Ledger ODIR1 l.871-873, OC2 l.729-735. | Decide in the C14 ADR before O1. If per-study: direction is a single measure-level answer with no series copy, reconciled in R4c, and legacy mapping comes from outcome-level answers. If project-level: raise it as an owner question. |
| B-26 | Major | PKG/contracts.md C1 head row (l.90), C2 key (l.110-113); PKG/integrated-plan.md C1 lane "C2 context includes population" (l.407) | The identity keys are defective and will need re-keying. (1) C2's key omits the author and the authority role (candidate or reconciled), although its prose requires "same reviewer"; and C1's "currentRevisionId per author context" implies one head shared by several authors. (2) populationId joins the key only "when classification is enabled", so turning on C1 for a project with R2 data re-keys existing answers, breaks SF3 sharing and floods sessions with false outdated flags. |
Research natural keys include reviewer and authority role: PLN/screening-specialised-annotation-research.md:675-682. C13: every instance belongs to exactly one population (PKG/contracts.md:343-344). | Freeze the C2 key at G1 as {project, study, author or authority scope, ownerScope, questionId, entityPath, populationId}, with a system default whole-study population from the first write, so enabling classification never re-keys. One head per author. |
| B-27 | Major | PKG/contracts.md C4 baseline and D38 proposal (l.149-158); PKG/migration-adoption-rollback.md §3 questions row (l.58) | System questions are rebuilt from code on every read, and their structure varies with Project.SystemQuestionVersion: the outcome error-type question has a different parent and different option filters in v0 and v1 projects. This conflicts with D38 (structural properties fixed on identity) and with pinned form versions, since a code change would silently alter a published form. The adoption mapping doesn't cover system questions. |
Core/Model/ProjectAggregate/AnnotationQuestion.cs:559-592 (parent and options depend on systemQuestionVersion). Project.cs:223-247 (rebuilt per read), :475. | Snapshot system-question definitions as immutable versions per (question ID, SystemQuestionVersion, code revision). Form versions pin those snapshots, never live code, and a code change publishes a new system version through the normal impact flow. D38 must either allow the existing per-project structural variants or mint new identities for them. |
| B-28 | Major | PKG/contracts.md C9 task, matching and gold rows (l.284-287) | C9 omits the hard concurrency and semantic cases. (1) Overlapping forms share question gold (RE4 forbids competing gold), but two tasks can answer the same question differently. (2) Task identity when stages bind different or incompatible versions of the same form (PV2, frozen bindings). (3) Candidate drift: a new qualifying candidate, a Save after Complete, or a Fix after gold. (4) The screening-profile part must not be a reconciliation session that writes screeningOutcomes (a FEAT-011 MUST NOT). (5) CAS on the study's current-snapshot pointer when tasks and query resolutions publish concurrently. |
Ledger RE4 l.655-669 ("Overlapping forms retain shared question gold ... Later input or requirement changes can require an update"). DOCS/prisma-specification/prisma-constraint-annotations.md:146, 163. "Inputs changed · re-check" is retained (PKG/ui-coverage-comparison.md:98). | Add to C9: (1) shared-question gold ownership (first publisher wins; other tasks show it as accepted and challenge it only by query); (2) task key = study × form × compatibility class; (3) an input-drift state that never retracts gold; (4) profile adjudication as a separate aggregate writing FinalScreeningOutcome; (5) snapshot-pointer CAS. Add fixtures for each. |
| B-29 | Major | PKG/contracts.md C7 normal-reconciliation row (l.256); PKG/integrated-plan.md R3 admission bullets, §8 | Production pilots depend on other programmes' environment-wide modes and data migrations, which the plan doesn't list. (1) Reconciliation reserves nothing today, and active-reviewer tracking (a fleet-wide durable mode) has never been enabled, so the "existing active-work claims" R4 relies on don't exist in production. (2) R3's admission extends ReviewEligibilityPolicy, whose flag is environment-wide, requires legacy reservations to be migrated first, and needs the D7 migration tool, which is an empty draft (#3742). Only AF2 (Q-25) is called out. | Core/Services/StageReviewService.cs:153: "Reconciliation reserves nothing". Study.cs:345-346: "Migrate legacy reservations before enabling review eligibility." RULES/materialized-stats.md l.74-78 (durable reviewer-mode epoch). PKG/source-status-inventory.md facts 10-11. | List per-release production prerequisites in G4 and G6, each with its owner and go/no-go evidence: (1) the eligibility flag plus reservation migration; (2) either tracking activation or a dedicated reconciliation task claim (E6). |
| B-30 | Major | PKG/contracts.md C3 (l.128-137) | Provenance and exposure don't fail safe. (1) Exposure is reported by the client ("rendered into view"). If the report is lost, informed work defaults to independent, contrary to VS2. (2) There is no field for the real actor versus the effective author. Support impersonation in edit mode can write reviews as a reviewer, so immutable revisions would attribute those writes to the reviewer and count them as independent work. | Ledger VS2 l.46, PV1 l.39. API/Authorization/SupportImpersonation/SupportImpersonationAttributes.cs:11-17; ImpersonationSideEffect at API/Controllers/ReviewController.cs:1587. | Use three exposure states: no gold available (derived on the server from "accepted snapshot available"); exposure recorded; and available but unrecorded, treated as informed or unknown. Deduplicate exposure per session version and revision. Add realActorId and onBehalfOf to every command, receipt and revision, and exclude or flag impersonated writes in independence statistics. |
| B-31 | Major | PKG/contracts.md C11 (l.315-325); PKG/integrated-plan.md R5 (l.391-399) | C11 lacks the building blocks for reproducible "as of" exports. (1) Ordering: wall-clock createdAt from many hosts is not monotonic, and a transaction can commit after a later-stamped one, so two exports "as of T" can differ. (2) Coverage per dataset: answers, sessions, screening, gold, outcomes, study metadata, lifecycle, citations, aliases. (3) Dates before a project's adoption should return "not observed", not the adoption snapshot. (4) Legacy timestamps are unreliable: DateTimeCreated is settable and stamped at construction. (5) Exports are not said to be stored or regenerated. (6) No rule for which stage's identity blinding (BL1) applies to a form bound to several stages. (7) Fixing the OnlyCompleted export option changes legacy output without a flag decision. | SRC src/libs/kernel/SyRF.SharedKernel/BaseClasses/Entity.cs:17, 33. Export files are browser-streamed (PLN/review-export-history-investigation-2026-10-02.md:70). Ledger EX2 l.816-824. WriterConfig OnlyCompleted unused (inventory §7). | Write the C11 ADR, pulled forward to G2 for previous-version exports. It should specify: (1) a per-project commit sequence allocated inside the transaction; (2) each "as of" request resolved to a watermark at least one maximum transaction lifetime in the past; (3) a per-dataset coverage manifest; (4) no substitution of adoption snapshots; (5) trust levels for legacy timestamps; (6) a storage and retention decision; (7) a blinding scope rule. Put the OnlyCompleted fix behind a flag. |
| B-32 | Major | PKG/migration-adoption-rollback.md §1.4 (l.31-36); PKG/notifications-integration.md §2 | The writer and reader inventory misses live paths. Writers missing from it: (1) RemoveSession's hard delete; (2) the question-delete cascade and in-place question edits through the legacy API, which R1's default editor and #3934's import both use; (3) the fold worker, which bumps Study's Audit.Version; (4) the reversible-deletion scheduler; (5) import-failure compensation, which deletes Studies; (6) bulk PDF finalize; (7) the preview-seeding delete, which is exempt from bulk locks; (8) impersonated writes. Reader missing: #3944 finds candidates and authorises replies through Study.ExtractionInfo.Sessions and stores legacy session IDs, so it is inert for canonical sessions and its references dangle after adoption. |
ReviewController.cs:86; Core/Services/ProjectManagementService.cs:200-232 (DeleteQuestionAsync); Mongo/Repositories/StudyRepository.cs:1160-1164. NOTIF src/services/api/SyRF.API.Endpoint/Services/Notifications/StudyConversationAccess.cs:46, 52; NOTIF src/libs/project-management/SyRF.ProjectManagement.Core/Model/StudyConversationAggregate/StudyConversation.cs:28. | Extend the G1 inventory to readers, and give each path its canonical-scope behaviour: route, refuse or adapt. Put the notification-stack aggregates (StudyConversation, StudyIssue, inbox SourceIds) into the R6 adoption manifest. Ask #3944's owners to resolve sessions through an abstraction, or disable it for canonical scopes. |
| B-33 | Major | PKG/integrated-plan.md R2 "Complete validates required applicable answers on the server" (l.244-245) | Server-side Complete validation needs exact client/server parity on which questions are applicable: multi-option conditional parents, filtered options, branch context (ADR-011). Otherwise the client hides a question the server requires, and Complete is blocked with no visible remedy. The same evaluator drives Needs Updating (VU1), RE2 validity and UA1, yet no contract or conformance suite pins it. | Server validation is structural only (inventory fact 5; AnnotationRelationshipValidator). /home/chris/workspace/syrf/main/docs/decisions/ADR-011-schema-v0-multi-option-conditional-parent-answers.md. |
Make applicability a C4/C5 conformance requirement: a written spec plus shared fixtures run by both the .NET validator and AF2. Typed errors must name the blocking question and its context. Include a fixture proving a question hidden by a condition is never required. |
| B-34 | Major | PKG/contracts.md C4 profile row (l.165); PKG/integrated-plan.md R3 profile versions; PKG/ui-coverage-comparison.md gap 3 (l.137) | Nobody has asked Chris what a new screening-profile version (a new eligibility question or a changed rule) does to existing decisions and collective outcomes: re-screen, keep pinned, or recompute? The answer affects admission and PRISMA counts. FV1–FV3 cover forms only, and the UI comparison confirms there is no design for profile-version impact. | Ledger FV1–FV3 l.48-50, PV2 l.40. E5 lists "profile-version reassessment" only as engineering. | Add a Batch B question on profile-version transition policy. Recommended: per-category requireReanswer/autoUpdate/doNothing; collective outcomes recomputed under the pinned version; old reports frozen. Extend C8 usage evidence to profile versions. |
| B-35 | Minor | PKG/contracts.md C15 (l.366); PKG/notifications-integration.md §4.1 (l.127-130), §3 assignment row | (1) C15 says recipients are "resolved with fresh authorization at delivery and read", but the stack, and §1.3/§4.2 of the package itself, expand recipients when the notification is written and only shape details on read or send. (2) Capture is an insert-only upsert on the unique (RecipientId, Kind, SourceId) index, so a second event for the same source is silently dropped. The "stable SourceId" guidance invites exactly that for assignment lifecycles (created, expiring, expired, released) and for a concern later "addressed by update". | NOTIF src/libs/project-management/SyRF.ProjectManagement.Mongo.Data/Repositories/SourceInboxCapture.cs:62-71; InboxNotificationRepository.cs:38-39. PKG/notifications-integration.md:72, 131. | Reword C15: recipients are expanded at write time, and details are shaped with fresh authority on read and send. Define SourceId as the identity of each event occurrence, as reviewAccessGranted already does. Add a two-event lifecycle fixture. |
| B-36 | Minor | PKG/contracts.md C12 (l.327-336) | C12 lacks three things. (1) A versioned per-project mapping from each profile to its PRISMA phase (title/abstract, full text, or not reported, for example a sub-study profile or the legacy compatibility profile), because FEAT-011's boxes reference "the TA profile" and "the FT profile". (2) FEAT-011's Admin authority source and override audit. (3) An owner and UI for metaAnalysisIncluded (box 17). | DOCS/prisma-specification/study-lifecycle-and-source-taxonomy.md:587-595; three-level-data-model.md:191, 221-232. | Add all three to C12 and the report manifest. Set the phase mapping in R3 profile settings. |
| B-37 | Minor | PKG/integrated-plan.md R5 agreement; PKG/contracts.md C8 (outcome-schema usage) | FEAT-024 explicitly excludes broad agreement/kappa statistics ("requires a separately approved same-stage cohort and denominator") and outcome-level statistics. So R5 agreement, and PS1-style usage for outcome-schema versions, cannot simply be new FEAT-024 kinds. | DOCS/materialized-project-statistics/README.md:215-231. | Compute these authoritatively from canonical revisions, or agree a FEAT-024 scope amendment with its owner. |
| B-38 | Minor | PKG/integrated-plan.md P1 (l.406); PKG/migration-adoption-rollback.md §3 search row (l.71) | (1) PRISMA boxes 2 and 11 should extend the existing FEAT-024 SearchPopulation family with sourceType rather than add a new counter (OPS1). (2) FEAT-011's backfill ("Citation per existing Study from SystematicSearch data") would create "raw, as imported" Citations from current, mutable Study metadata that bulk updates may have rewritten. | RULES/materialized-stats.md "Materialized search population". DOCS/prisma-specification/three-level-data-model.md:165-171, 312. | Add both rules to P1/P2 and to the adoption mapping. Backfilled Citations come from re-parsed retained reference files, or are labelled as derived from current Study metadata. |
| B-39 | Note | PKG/source-status-inventory.md header and §4; PKG/integrated-plan.md §3, §8 | The baseline is already stale. #3948 (fold slice 6a: the protocol-4 baseline and the N-1 window) merged at 02:56Z on 3 Oct. Main is now 9d74077d8, seven commits after 78c6d097d, and production fold enable stays refused until gate (b). |
gh pr view 3948 shows MERGED at 2026-10-03T02:56:56Z; git log 78c6d097d..HEAD. |
Refresh the inventory before G0 and cite 9d74077d8 for FEAT-024 facts. |
Verified as correct¶
- Answer replacement and session identity. A save removes all of the caller's answers to the stage's questions regardless of stage (ExtractionInfo.cs:183-194). There is one session per (study, stage, reviewer, reconciliation flag), and only one reconciliation session per stage whoever the reconciler is (ExtractionInfo.cs:202-208). Session views filter by StageId (AnnotationSession.cs:149-164).
- Writes and locks. SubmitAnnotationSessionService defaults to
maxAttempts = 3(API/Services/SubmitAnnotationSessionService.cs:113). TheBulkUpdateLockfield is at Study.cs:242, and the bulk-lock rules are stated correctly. - Statistics. The hard-coded "enough = 2" is real (Mongo/StudyStats.cs:355). The "positive proof only" evidence hazard cited in C8 is accurate (Core/Services/ReviewEligibility/StageReviewStatisticsEvidence.cs:40-52).
- Screening. Decisions are keyed by project + screener, and StageId is overwritten on re-screen (ScreeningInfo.cs:111-142).
- Question deletion. It cascades to answers non-atomically under a FEAT-024 fence, as acknowledged in #3088 (ProjectManagementService.cs:200-232).
- Eligibility test. The pinned eligibility test
AnnotationHasNoScreeningPrerequisiteexists at ReviewEligibilityPolicyTests.cs:176. - Notification stack.
- Capture refuses to run outside an active source transaction (SourceInboxCapture.cs:58-60).
- There is a unique (RecipientId, Kind, SourceId) index (InboxNotificationRepository.cs:38-39).
- StudyConversation uses deterministic SHA-256 IDs (StudyConversation.cs:20-25).
-
3944 candidates include incomplete sessions (StudyConversationAccess.cs:52), and threads are keyed by stage and reconciler (StudyConversation.cs:10-13).¶
-
3944 is OPEN at
59ab32e7, and #3932, #3939, #3934 and #2224 are open.¶ - AF2 rule. AF2's rule against implicit per-answer server saves is cited correctly (SRC src/services/web/CLAUDE.md:343).
- PRISMA gap. There is no PRISMA, Citation, Publication, dedup or lifecycle code, and SystematicSearch lacks sourceType. This matches FEAT-011's gap tables.
- Migration principles. The principles (additive, no fabricated versions or votes or gold, per-project adoption, canonical-aware rollback, rehearsal) match research §5 and §7.3 and MIG1. The outcome-migration proposal is adopted as Q-05 with execution unauthorised. The legacy compatibility profile for screening matches research §5: no split by StageId, no attachment to the first profile, and InclusionInfo not treated as historical profiles.
- PR1. "Excluded stays Excluded despite extraction" is written into C6's conformance tests, C12's rule and R3 acceptance 5.
- C9, C14 and C15 rules.
- At the rule level, C9 restates RE1–RE5, SF4/RE3, GS1, QY1–QY9 and RA1–RA5 faithfully.
- C14 respects OC1 (legacy-compatible and event-count schemas plus project customisation) and ODIR1 (no context override).
- Apart from B-35, C15's no-parallel-machinery rule and its in-transaction, resolver-shaped design match the stack.
- Unit separation. C12 keeps Citation, Publication, Report and Study distinct from animal populations and cohorts, as PRISMA requires.
Missing coverage¶
These are gaps that need a contract, decision or section, beyond what the findings already ask for.
- Erasure versus immutable history. Account deletion (Identity "Deactivated" is the irreversible deletion record) against immutable revisions, exposure events, receipts and inbox items; and a retention policy for drafts, exposure events and notifications, which currently have no TTL.
- ID minting. Validating or minting identities for canonical heads, entity instances and revisions. Annotation and entity IDs are client-supplied today, and only SessionWriteOwnership stops one reviewer reusing another's IDs.
- Large-form ceiling. An explicit maximum form size admitted until publication of large immutable submissions is proven, as research §3.10 requires ("Admit an explicit maximum until this path is proven"). No such cap appears in C1 or C5.
- LC1 completion trigger. How automatic stage completion is evaluated: transactional or eventual, and its consistency while FEAT-024 is dark. Also how that transition captures its notification.
- Post-commit side effects. One rule covering SignalR invalidation for cross-stage outdated flags, the existing claim-revocation outbox, and the in-transaction inbox rows.
- Cross-form conditional consistency. What happens to earlier annotation-versioning decisions D51 and D53–D55 (cross-stage parent-answer condition consistency) now that SF5 outdated flags and Fix exist.
- Evidence-based PRISMA lower bounds for adopted legacy projects. For example, a study with a recorded legacy decision certainly entered screening; report that instead of blanket "unknown".
- Updated reviews. Box 1 remains deferred. A rule that as-of exports and protocol amendments never activate an updated-review population.
- Storage evidence. An authorised, aggregate-only survey of Study document sizes and annotation counts before the E15 storage ADR. Production inventory needs its own authorisation, and without it M0 benchmarks rest on synthetic sizes only.
- Restore validation. Multi-collection restore checks: snapshot pointers, drafts, receipts and the Study summary projection must agree after a backup restore.
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/migration-adoption-rollback.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/Repositories/StudyRepository.cs
- /home/chris/workspace/syrf/main/src/libs/project-management/SyRF.ProjectManagement.Core/Model/StudyAggregate/ExtractionInfo.cs