Source and status inventory¶
Temporary planning evidence; planning only. This page records what exists on main, what
exists only in open PRs, what is documented but not built, and which existing documents conflict
with the latest owner decisions.
How it was gathered:
- Baseline:
main=origin/main= remotemain=78c6d097d(3 October 2026, 01:35 UTC), confirmed withgit rev-parseandgit ls-remote. - Re-checked at
2949ca3a7(03:39 UTC) andc59d9d0f1(04:47 UTC, the remotemainat the last check). The changes since the baseline are FEAT-024 fold slice 6 (#3948, #3949), the FEAT-024 question-answer staleness fix (#3955), an identity registration fix (#3954), auth-migration S30 documents (#3930) and the generated API client. Among them, a refactor of the reservation claim-stage writes keeps claims keyed by stage and investigator. No other code area this plan relies on changed. - Read-only sub-agents (Opus) inspected code and documents; their reports are summarised here with file and line references.
- Load-bearing security and data claims were re-checked directly (marked re-verified). After the adversarial reviews, their new code claims were spot-checked directly too (marked re-verified after review).
- PR states were read live with
ghat about 04:52 BST (03:52 UTC), rechecked at 05:48 BST, and refreshed for the programmes in programme integration at 15:18 BST (mainde3e98c59; cluster-gitopsorigin/mainea972fd6).
Limits: no runtime checks, no database reads, and no reading of runtime flag overrides; flag
values in environments come from cluster-gitops values.yaml and may differ live. "UNVERIFIED"
marks anything that couldn't be confirmed.
Changes since the earlier research baseline (5861b5844, 2 October): FEAT-024 fold slices 4,
5 and 6 (#3925, #3926, #3948, #3949), batch risk of bias (#3931), auth-migration documents (#3930)
and two E2E fixes. No change to questions, sessions, screening, reconciliation, outcomes,
exports or permissions.
1. The facts that most constrain the plan¶
- No versioning exists for questions, forms, sessions or answers. Questions are edited in
place inside the Project document, and system questions are rebuilt from code on every read
(
Project.cs:109-110, 223-247). The QM v2 domain exists only on dormant PRs (#2461, #2572–#2575); searchingsrcfor AQVersion, StageQuestionSet or SessionVersion finds nothing. - Deleting a question hard-deletes every answer to it and its descendants across the
project. The atomicity gap is acknowledged in the code (issue #3088) —
ProjectManagementService.cs:201-222, re-verified. - Answers already belong to reviewer + question across stages, but sessions are per stage.
A save removes all of the caller's answers to any question in the stage's form, whatever stage
they were saved in, then adds the submitted ones (
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). A session is a filtered view (AnnotationSession.cs:149-164). Saving a shared question in stage B silently changes what stage A's session shows (the "little DOMS" investigation). - No drafts, autosave or immutable history on the server. Complete is a status flag;
reopening is a save with Incomplete. AF2 keeps drafts in memory only
(
annotation-form-v2.store.ts:176-179), and the web rules forbid adding per-answer server saves implicitly (src/services/web/CLAUDE.md:343-344). Reviewers can hard-delete their own session and all its answers and outcome rows (ReviewController.cs:86→ExtractionInfo.DeleteSession). - The server never enforces required answers.
Optionalis only ever assigned (AnnotationQuestion.cs:55, 227); validation is structural only (AnnotationRelationshipValidator.cs:23-275) — re-verified. Legacy "Completed" sessions were therefore never validated on the server. - Screening is one Include/Exclude decision per reviewer per project, judged against one
project-wide threshold, with
StageIdoverwritten on re-screen (ScreeningInfo.cs:79-142,Screening.cs:33-38). ScreeningProfile,screeningOutcomes[], StudyLifecycleStatus, exclusion reasons and screening questions don't exist in code, although the rootCLAUDE.mddescribes them in the present tense. - Reconciliation exists in a legacy form.
- A study is ready when completed sessions ≥
SessionCountTarget. - There is one shared reconciliation session per study and stage, editable by anyone with the Reconcile grant.
- Reconciled answers are one overwritten set per question, shared across stages.
- Counters are two booleans.
- The reconcile endpoint maps every session on the study into the response
(
ReviewController.cs:1586-1628,StudyDto.cs:53-58, re-verified). The agent reports the session DTO includesInvestigatorId(StatsWithIncompleteDto.cs:14-31; not independently re-checked). - Reconciliation reserves nothing (
StageReviewService.cs:153, re-verified after review). - There are no gold-snapshot, adjudication or query entities.
- The reconciliation UI is read-only.
reconcile/:studyIdrenders every candidate session as read-only cards, two per row (a third wraps). The reconciler has no form on that route, and no navigation links to it (stage-reconcile.component.html:18-46,stage.routes.ts:41-61). AF2's reconcile host is read-only by rule, stage-review and preview hard-fail on reconciliation, and the extraction and Experiment carve-outs on non-review hosts lift only in AF2 Phase 4 PR 9 (src/services/web/CLAUDE.mdAF2 section, re-verified after review). - Outcome direction is a per-reviewer answer copied onto rows. Direction, units, average
type and error type are system questions under Outcome Assessment, and the client copies them
into every outcome row; the export reads some from the row and some from the annotation
(
AnnotationQuestion.cs:537-661,annotation-form-submission.ts:106-109,OutcomeDataFormatRowWriter.cs:205-211).GreaterIsWorseis a non-nullablebool(OutcomeData.cs:39), and the client defaults are directionfalse, error typeSD, average typemeanand zero animals (annotation-form-outcome-topology.ts:40-43, re-verified after review), so stored values can't distinguish an answer from an untouched default. There is no outcome-measure entity and no event-count shape (TimePoint= Time/Average/Error). - Stages are three flat modes plus the D7 closed configuration (ScreeningOnly,
AnnotationOnly, Combined with Allow/Stop). They have an Active switch only: no steps, no
completion state, no rename or delete (
Stage.cs,StageReviewConfiguration.cs). The D7 grouped configuration has a reader and writer floor (#3732) but no migration tool (#3742 is a draft with no files). - The review-eligibility programme is the existing admission authority. It has 14 actions,
typed screening/annotation claims, a claim-revocation outbox and guarded settings PUT, all
behind the default-off
reviewEligibilityPolicyflag. Enabling it requires migrating legacy reservations first (Study.cs:346, re-verified after review). The browser doesn't yet read its eligibility response, and still applies #3551's universal screening-complete veto (S5 audit, #3741). Its flag reaches the API host only (env-mapping.yaml, theservices: [api]block). Per session memory, Chris paused the programme on 25 September until the statistics work finishes; the repository does not record the pause (#3746 had activity on 1 October); recheck before F3. -
Eligibility decisions and later owner decisions.
- Eligibility D3a ("no closure/reopening state machine") is superseded for the new stage model by LC1/RX2 (3 October) under the precedence rule.
- Eligibility D3b ("annotation has no screening prerequisite", pinned by
ReviewEligibilityPolicyTests.cs:176) becomes the behaviour of independent steps, with configured dependencies following DP6/DP7. - Eligibility D5 concerns excluded work (preserve new-annotation exclusion, keep saved-work resumption, no new direct-access ban). It is consistent with EW1 and the veto and carries over unchanged.
See §6 and Q-24. 13. Groups are fixed. Only the built-in Administrator group is reachable. Custom groups exist in the domain model (
ProjectSecuritySettings.cs:25-42), but no code creates them. The authorization programme gates custom groups on membership schema 1 applied in production (its gate G-D, which this plan calls X-AUTH-SCHEMA; it needs a migration runner WP-M1 that doesn't exist yet, then WP-M2) and plans their administration, including the generalised permissions dialog, as WP11 after WP9 explanations; #2224 is reference material for WP11 (its decision D10). Production is entirely schema 0. Per-member stage grants have no API writer. 14. The ownership-transfer rule is not enforced.ResourceSecurity.jsonmakesChangeOwnerowner-only, but no endpoint usesProjectChangeOwnerPolicy.PATCH /api/projects/{id}requires onlyProjectEditPolicy(Administrator group) andProject.UpdatecallsChangeProjectOwnershipwhenOwnerIddiffers (ProjectController.cs:365-390,Project.cs:855-883,ProjectUpdateDto.cs:17) — re-verified. The permission-update endpoints also accept owner-reserved activities (ProjectController.cs:1303-1320, per review C). This contradicts PM1's owner-only transfer (§7). 15. Materialized statistics are dark and narrower than they look. - Production has nothing; staging runs the ProjectScreening family for one pilot project and pins the fold flag on for both hosts (cluster-gitopssyrf/environments/staging/{api,project-management}/values.yaml, commitc4412000of 2 October, read atea972fd6; the comment says "staging pilot only (Chris, 2026-10-01)" and a project still needs an explicit admin enable; whether the project is in fold mode is unverified). - Fold protocol v4; slices 0–7 merged (slice 7 docs #3962). Gate (b) was a provisional fail on a loaded host; update: it failed on latency in the idle-host rerun (Bramble, 3 October 2026, 22:38–23:01 UTC; zero statistics-caused conflicts; #3510); the soak (#3510, #3952) has not started. Enabling fold mode on the production database is refused in code (commit89d295734). - Question-answer statistics are keyed by question only. The reservedStageQuestionVersionscope is stage-keyed and never written, so PS1's stage-free usage needs new scope kinds. - "Enough = 2" is hard-coded at three sites:StudyStats.cs:353-355, FEAT-024'sAnnotationThresholds.MinimumNumberSessions(an input of the shared configuration digest) and the study-library session filter (StudyRepository.cs:1665, 1712, 1725, #3979). - FEAT-024 explicitly excludes broad agreement statistics and outcome-level statistics. 16. Several counters and claim authorities already coexist: session tallies, slot reservations (one per reviewer per stage, typed claims), reviewer presence, allocation regimes, statistics rows with pending entries, FEAT-024 source-operation receipts, the project answer tally, and bulk-update study locks (ADR-020,Study.cs:242). New work must extend these rather than add another. 17. Exports are current-state only, and deletion is disabled. As-of modes are reserved only in open #2461/#2574 and accepted only for CurrentState. Screening decisions and answers are overwritten in place, so as-of review state can't be rebuilt from current data. Search, import-job and project deletion routes fail closed with a 503 (DeletionLifecycleUnavailableException,SearchController.cs:122, 134,ProjectController.cs:410) until a reversible-deletion scheduler exists; thedeletionLifecycleflag only chooses a message. Only service and repository paths still hard-delete, andStudyRepository.cs:1129-1130says "Search deletion is disabled today" (re-verified after review). An earlier version of this page wrongly said that removing a search hard-deletes its studies. 18. No PRISMA, deduplication, Citation, Publication or lifecycle code exists.SystematicSearchlacks the phase-7sourceTypeMUST. The FEAT-011 package is Approved and is the binding specification. 19. Two question editors coexist, and all default entry points lead to the legacy one. The new tabbed editor (Design/Assign/Preview) atadmin/questionsis guarded only by the design permission;newQuestionManagementjust hides its nav link. It saves through the old schema-v0 API, locks answered questions instead of versioning them, has only seven hard-coded categories, and its 17 specs are excluded from CI (#3655). 20. AF2 is merged but off almost everywhere.annotationFormV2is on in staging only;stageReviewRedesignandstageReviewDockvieware off in every environment; production has only ever used AF1. AF2 has no autosave and no "Complete anyway". AF2 eligibility admits only annotation and combined stages, so screening-only steps aren't rendered by AF2. 21. Study's embedded value objects use strict class maps.ScreeningInfo,ExtractionInfoandSessionTallyare mapped withAutoMap()and no extra-element tolerance (StudyRepository.cs:3176-3231). By contrast,Entitysubclasses carry[BsonExtraElements](Entity.cs:21) and FEAT-024's pending-statistics maps callSetIgnoreExtraElements(true)(StudyPendingStatisticsClassMaps.cs) (re-verified after review). An older binary reading a document with new fields in those three types throws rather than ignoring them, which is why R0 exists. 22. System-question structure depends onProject.SystemQuestionVersion. In v0 and v1 projects the outcome error-type question has a different parent and option filters (AnnotationQuestion.cs:559-592, re-verified after review), so a published form can't pin live system questions. 23. Support impersonation has an edit mode that admits side-effecting review actions under a valid edit context (ImpersonationSideEffectAttribute, re-verified after review). Review data records the effective investigator; the canonical model adds a real-actor field (per review B; not traced end to end). 24. Saved Dockview layouts are per reviewer per capability slot (screening, annotation, combined). The API validates allowed panel keys and one instance of each capability panel (docs/architecture/dockview-layout-migration.md, re-verified after review), so new panels and step kinds need a layout-contract change. 25. Exports can unmask identities regardless of blinding. The export page lets any ExportData holder choose unblinded output ("UNMASK DATA"), independent of stage blinding and candidate isolation (per review C). -
Claims exist only when tracking is on, and tracking is off everywhere deployed. Claims, capacity guards and typed admission run only when
ActiveReviewerTrackingEnabled && SignalRActive; the flag is unset in production, staging and preview and true in the E2E stack (appsettings.e2etest.json). Reconciliation is excluded at every tracking layer. Enabling tracking is a FEAT-024 durable reviewer-mode transition whose code (AdvanceModeEpochAsync, M15) has no caller. Presence snapshots carry claim holders' identities to every member who can view studies.
2. Implementation inventory by area (main, 78c6d097d)¶
Re-checked at 2949ca3a7 and c59d9d0f1; only the statistics row changed.
| Area | Verified on main |
Gaps against the plan | Conflicts with decisions |
|---|---|---|---|
| Questions and editors | Embedded Project._annotationQuestions; fields AnnotationQuestion.cs:107-150; conditional parents and filtered options (Target.cs, OptionInfo.cs); lookups; placement rules for new questions; in-place edit; cascade delete; same-project copy; system questions vary by SystemQuestionVersion. New editor: Design/Assign/Preview with locks, same-parent drag only, debug text left in the template. Legacy editor: create/delete only, Bootstrap. |
Versions, publication, library, import (in #3934 only), profile-owned questions, custom categories, re-parenting, response modes, system-question snapshots | FV1–FV3 (no versions); DP4 (unknown categories refused) |
| Sessions and answers | Per-stage sessions; stage-independent answer replacement; structural validation; SessionWriteOwnership (#3671); client-supplied IDs; hard session delete. StudyPdfCorrection (pmStudyPdfCorrection; mongodb-reference.md:103 still says pmStudyCorrection), the three BulkPdfUpload* roots inside ProjectAggregate/, the ADR-020 and M5b operation stores and the FEAT-024 fold are Study writers for R0's inventory |
Form identity, shared sessions, drafts, immutable versions, required-answer validation, provenance, withdrawal instead of delete | SF1–SF3, SL1–SL3, PV1 |
| Stages | ReviewMode; D7 configuration types; SessionCountTarget falling back to the project threshold; AllowSelfReconciliation with no writer; workload shares on annotation-only, disabled stages; guarded review-settings endpoints (flagged); 3-step create dialog; settings page with mock study filters and mock stage permissions |
Steps, dependencies, routing, lifecycle, versioned settings | DP6/DP7, PV2, RX2/LC1, SF2 (stage target; #3732 adds a per-stage override) |
| Screening | Project-level method/criteria/keywords; Include/Exclude only; ReviewEligibilityPolicy (14 actions); sufficiency guard for new votes | Profiles, eligibility questions, reasons, profile outcomes, derived decisions | DP2–DP5, RX1 |
| Reconciliation | Legacy readiness, session, overwrite model; reconcile grant (#3565); no editor exclusion: reconciliation consumes no reservations and uses no claim or presence at any layer (StageReviewService.cs:67-70, :153, stage-review-presence-policy.ts:17, per RT-01), and "Next" picks an unclaimed study at random; AF2 reconcile host renders N read-only candidate cards for non-extraction pools |
Editable reconciliation form and host, task identity, gold snapshots, matching, assignment, queries, blinding policy | RE1–RE5, RA1–RA5, GS1, BL1, SF4 |
| Outcomes | OutcomeData per (stage, outcome, cohort, experiment, investigator, reconciled); GreaterIsWorse a non-nullable bool; TimePoint (Time/Average/Error); duplicated NumberOfAnimals; AF2 matrix, cell editor, spreadsheet, graph assignment |
Measures, schemas, event counts, custom fields, versioned direction | OC1, ODIR1 |
| Permissions | 25 project + 4 stage activities; owner-only ChangeOwner/AssignPermissions/Delete in the catalogue; ChangeOwner unenforced; permission updates accept owner-reserved activities; ProjectAuthorityEvaluator (dark, enforced mode only); working group×activity PermissionsDialogComponent used only for chart visibility; Membership UI is Administrator/Reviewer only; route guard typo project.editMembership (project-admin.routes.ts:36; review C judges it most likely a no-op; UNVERIFIED) |
Configurable groups, scoped grants UI, delegation envelope, new capabilities | PM1, PM2 |
| Statistics (FEAT-024) | 10 families; fold protocol v4; slices 0–7, #3955 and #3956 merged; gate (b) failed on latency on an idle host (3 October, #3510); production fold enable refused in code; staging fold flag pinned on for both hosts (cluster-gitops c4412000; "staging pilot only"); positive-proof-only stage evidence (StageReviewStatisticsEvidence.cs:40-124); "enough = 2" inside the configuration digest |
New families over canonical sources (usage, question-version answers, profile grain); target-aware classification; scoped-rebuild service API | PS1–PS3, SF2 |
| Allocation, presence, claims | Allocation MVP and regime provenance (#3603) merged, dark; reviewEligibilityPolicy and proportionalStudyAllocation reach the API host only (AP-08); reviewer validation blocked on #3251; typed slot reservations keyed by stage and investigator, created only when tracking is on (activeReviewerTrackingEnabled ∧ signalRActive; with it off no claim is created, saves are unguarded and EnforceAnnotationTarget does nothing, RT-02); ReviewerPresence and ReviewSessionConnection roots, both keyed by stage (presence snapshots name reservation holders, identities included, to every member who can view studies, #3892); tracking off in every deployed environment, on in E2E, and enabling it is a FEAT-024 durable reviewer-mode transition whose code (AdvanceModeEpochAsync, M15) has no caller; reconciliation excluded; maxInProgressSessions effectively on through appsettings (API true, PM false) |
Per-reviewer membership projection; claim contract v2; reconciliation task editor claim; production claims route; shared-form allocation | SF1/SF2 (stage-keyed); RA1 (no editor exclusion) |
| Batches | None on main; #3939 open and conflicting (lazy shared frontier, legacy-keyed completion, PM-only flag block); #3936 plan open |
Evidence seam; pool-entry-based membership; durable opening | FEAT-026 README rejects "arbitrary sequential stage-step model" (DP6) and "a stage closure concept" (LC1/RX2) |
| Export and history | Current-state CSV (long, wide, screening, outcome, bibliographic), streamed to the browser; blinding level option open to any ExportData holder; OnlyCompleted forwarded but unused by writers; export authorization (#3243) |
Previous versions, as-of, manifests, gold export, export disclosure contract | EX1/EX2 |
| PRISMA, dedup, import, deletion | RIS import done (FEAT-022); reference-file screening columns call AddScreening; bulk update v2 with study locks (flagged); search/project deletion routes fail closed pending the reversible-deletion scheduler |
Citation, Publication, source type, retrieval status, pool entry, dedup, report; reversible deletion. FEAT-012 names pmDedupBatch (staging, 7-day TTL) and offers pmDedupAuditLog as the separate-collection option; the plan uses pmDedupAuditLedger |
PR1-compatible (nothing exists yet) |
| Setup | CreateProjectWizard (basic details → screening criteria → project setup; Living Search/ML disabled); 8-task ProjectSetup checklist (first stage only, legacy links) kept in nav by a 21 September decision | Guided replacement, templates, profile templates | SET2 (replace the content, keep the nav placement) |
| Reviewer UI | Legacy grid default; redesigned shell flagged; standard workspace and Dockview (flagged, "evaluation only"); equal-weight decision card; AF2 tabs, entity cards, Focus, branch tabs, numbering, action bar, outcome matrix; presence banners only; review preferences and layouts saved server-side | Drafts/history UI, needs-updating, Fix, steps strip, screening renderer, compact population context, "Complete anyway" | SL1–SL3, SF5, RE2 (reconciliation only) |
Design system: Angular/Material/CDK ^22.1.0, NgRx 21.1.1, Dockview 8.2,
Handsontable 18. The theme is a hybrid of M2 components and M3 tokens; dark mode needs
themeToggle (off). Zoneless switch shipped but is off everywhere; new code must avoid new
NgZone call sites.
3. Programme and feature documents¶
Doc status is the front-matter status. "Impl" uses Verified / Partial / Planned. Unless stated,
PRs are authored by chrissena (Chris's account, used by his delivery sessions); delivery
owners per lane are named at G0.
| Feature / programme | IDs | Doc status | Impl | Owning PRs (author) | Main conflicts |
|---|---|---|---|---|---|
| Question Management | FEAT-003 | In-Review (Feb) | Partial (UI on legacy API) | #2387 open | FV1–FV3, SF1/SF2 |
| QM v2 implementation | Epic #2488 | qm-v2-context Approved (Apr) | Planned (dormant PRs) | #2461 draft; #2572–#2575 | SF1/SF2, FV2, ODIR1 (per-row direction) |
| Annotation versioning | FEAT-001 | In-Review / design session Approved (Feb) | Planned | — | Per-stage question sets; previous-version-only checks; pendingAnswer drafts; auto-promotion |
| Annotation Form v2 and stage-review redesign | FEAT-002 | In-Review (Sep); STATUS stale | Verified, default off | ~40 merged; #3546, #3543, #3017, #3288 open | SF1 (EntityOrder on per-stage session) |
| Question extensibility | ADR-011; ADR-017 (only in #2812) | Approved (Sep) | Partial (answer labels only) | #2812, #2802 open | FV1 (interim definitionVersion policy) |
| Template/bulk import | delivery map 2b–2g | Planning merged (#2779) | Planned | #3934, #2781 (both conflicting) | DP4 gap, FV1 |
| Library/cross-project sharing | QM-10, SHARE-*, D27/D36 | Draft | Planned | — | DP4 (copy, not reference) |
| Answer validation | FEAT-017/020/027 | Draft/Approved | Partial | #2986, #2987, #2629 open | #2987 "AnnotationProfile" name collision |
| Category guidance | AF2 rev. 2 §6.5 | — | Verified | #3398, #3406, #3453 | SF1 (per-stage override) |
| Reviewer layouts / Dockview | contract + migration doc | Approved / In-Review | Verified, default off | #3225, #3230, #3384… | Panel model changes need a contract amendment |
| Stage-review design parity | handover 2026-09-21 | — | Partial | #3546 | SET2 (nav placement only) |
| Outcomes, experiments, cohorts | — | No schema doc | Verified (old model) | AF2 phase 4 | ODIR1, OC1 |
| Classification and entity types | — | No main doc | Planned | — | TC1 (categories are the legacy types) |
| Project templates | FEAT-014 | Draft (2025-12) | Planned | — | TC1, SET2 |
| Setup wizard and checklist | FEAT-023 nav | In-Review | Verified | M3 nav PRs | SET2 |
| M3 navigation (rail, checklist footer) | navigation plan | — | Verified (September) | merged | IA changes coordinate with it |
| Screening profiles, annotations, stage settings, stage filtering | FEAT-007/009/010/008 | In-Review (Feb) | Planned | #2621 draft | DP4, DP6, DP7 |
| Review eligibility programme | S1–S6, D1–D8 | In-Review (Sep) | Partial, flagged (API host only); paused 25 Sep (memory, not in the repository) | Merged #3579, #3646, #3659, #3663, #3691, #3695, #3719, #3732, #3736; open #3746 (conflicting), #3742 (no files), #3741; S4-C and S6b have no PR | DP6 (D3b), RX2/LC1 (D3a); D8 unmapped (D3-09) |
| Reconciliation | FEAT-006, D1–D50 | In-Review / Draft (Feb) | Legacy only | #3565 | RE4, SF4/RE3, BL1, RE2, GS1 (auto-promotion, $unset) |
| PRISMA 2020 | FEAT-011 | Approved (Feb/Mar) | Planned | #2398 docs | PR1-compatible; amendments A–J needed |
| Deduplication | FEAT-012 | Approved spec | Planned | — | Tracker uses superseded ImportRecord |
| Reversible deletion lifecycle | ADR-014 (uncommitted worktree .worktrees/bulk-pdf-deletion-lifecycle); deletionLifecycle flag |
ADR-014 records 12 August product decisions (24-hour grace, then physical deletion with tombstones) | Not built; routes fail closed | — (owner to confirm) | Amendment J and QD1 (identification history); D3-12 |
| PDF acquisition and processing | Bulk PDF upload, PDF Agent, Study Management Processing, PDF corrections | Various | Partial, feature-off | #3947 (PDF proposals) and merged work | P1 retrieval status |
| RIS import | FEAT-022 | Completed | Verified | #2971, #3004 | — |
| Materialized statistics | FEAT-024; ADR-018/019 | Approved/In-Review (STATUS stale: slice 7 and fold flag) | Partial (slices 0–7 merged, dark; gate (b) failed on latency on an idle host, 3 October, #3510) | No FEAT-024 PR open; #3840, #3960, #3845, #3506, #3524, #3510, #3952 open | PS1–PS3 gap; fixed two in the digest |
| Question-answer statistics | FEAT-024 family | In-Review | Partial (bug #3840) | #3562 | No version dimension |
| Proportional allocation | FEAT-025 | In-Review (STATUS stale: #3603 shown pending) | Partial (dark; no human acceptance) | #3327 (conflicting); #3251, #3252, #3264, #3269, #3321, #3745 open | Annotation-only, stage-keyed |
| Active reviewer tracking | ADR-008 (number duplicated) | Draft; feature narrative stale (ActiveReviewSession) |
Verified, off in every deployed environment, on in E2E | #2467, #3008, #3014, #3719; #3876 deferred; #2446 open | RA1 (reconciliation excluded); SF1/SF2 (stage-keyed) |
| Progressive batches | FEAT-026 (PR only) | In-Review (PR) | Planned (implementation PR conflicting) | #3936, #3939 | DP6, RX2 overlap; denominator decision unrecorded |
| Data export and as-of | FEAT-013 | Draft | Partial | #2461, #2574 | EX1/EX2 |
| Project groups and #3335 authorization | FEAT-005, PGRP-*, gates G-A..G-D, WP9, WP11 | Draft / handover plan | Partial | #2224 open (nurikarakaya; conflicting) | PM2; user-guide ownership wording |
| Application authority transition | #3335 M0–M8 | Approved (2 Oct) | Partial (dark) | many merged | Single evaluator; enforced mode |
| Architecture review (Oct 2026) | #3961; issues #3972–#3990 | Draft (PR only) | Phase 0 fixes merged (#3967, #3968, #3970, #3971) | #3961 (draft); #3966 parked | Persistence (#3985), events (#3973), flags (#3975), MassTransit (#3986), statistics (#3987); D1-02 |
| Feature-flag overhaul | P7 per-project targeting | Planning | Planned | — | R0 admission is its domain enrolment (PH-14) |
| Staged search import | #2612 | Plan (PR, docs only) | Planned | #2612 (mergeable, since 1 Sep) | X-IMPORT for P1/P2 |
| Notification inbox, study attention | flags notificationInbox, notificationEmail, studyAttention; reconciliationConversations (#3965) |
Draft (PR only) | Planned on main (code in open PRs; stack E2E never run in CI) |
#3932 → #3947 stack; #3965 | RE4 (stage binding), BL1, VS1 (#3944; addressed in #3965, not yet merged), QY gap |
| Keyword highlighting | FEAT-025 (duplicate ID) | In-Review | Verified, off | merged | — |
| Bulk update with study locks | ADR-020, FEAT-016 | Approved | Partial, flagged | merged | Every review write must honour locks |
| Reviewer no-work page redesign | #2412 | PR only | Planned | #2412 (conflicting) | Wording overlap with lifecycle |
The full per-feature notes (front matter, phases, file references) are in the sub-agent reports summarised here; the key document paths are linked from the integrated plan.
4. Open PRs in scope (live, 3 October 2026, about 04:52 BST; rechecked 05:48 BST)¶
Mergeability as GitHub reported it after recomputing against 2949ca3a7; the 05:48 recheck
found no change except #3955 merging. All authored by chrissena except #2224
(nurikarakaya).
| PR | State | Head | Notes |
|---|---|---|---|
| #3617 research (this package's worktree) | Merged on 3 October 2026 (f5318074d); conflicting at the first reading |
0f4c764b2 at the first reading |
5 committed files from 24 September at the first reading. The owner ledger, research inputs and this package were committed on the PR #3617 branch and merged to main on 3 October 2026 as a docs-only PR (D1-05, merge commit f5318074d) |
| #3964 ownership-transfer security fix | Merged 20:27 BST (19:27 UTC), merge commit 85e6facf7, after an approving Claude review on head 16788f9 and green checks; worktree and branches removed |
16788f9cb |
Kept by D1-01 (Chris, 3 October); ported #3969's active-member check and tests |
| #3969 duplicate ownership fix | Closed 19:27 UTC, with a comment pointing to #3964 | 9e4eaf36f |
Closed after the port (D1-01, decided by Chris on 3 October, carried out) |
| #3965 private reconciliation conversations | Open and ready for review; ten commits pushed (20:35 BST); local e2e journey passed (19:30 BST reading); the tenth commit makes the reconciler-reviewed-study refusal stage-independent | 986b1cdc2 |
Chris's Q-10 changes; stacked on #3947 (base codex/checked-pdf-proposals-and-explicit-administrator-a-r6bdl2) and waits for the stack; restack onto #3944 approved under D1-09 (3 October) if the stack owner agrees |
| #3546 AF2 tabs/Focus/action bar | Open, conflicting | b38dffbd3 |
Staging corrections 4–6 |
| #3543 branch tabs/delete/numbering | Open, mergeable (blocked) | 09684ba93 |
|
| #3394 Study fullscreen | Open, conflicting, stale (8 Sep) | aad2feca6 |
Superseded by the Focus contract |
| #3292 panel resizing | Open, conflicting, stale (6 Sep) | c71501d50 |
Chris chose Dockview instead |
| #3017 AF2 virtual scrolling | Open, conflicting, stale | 72ffc09cb |
|
| #3288 AF2 acceptance evidence | Draft, conflicting | d4fec9cc2 |
|
| #3939 progressive batches | Open, conflicting | 62e8101eb |
63 files |
| #3936 batches plan | Open, mergeable (blocked) | a799b538e |
|
| #3934 template import UI | Open, conflicting | 9d6c596cf |
Flag annotationQuestionImport |
| #2781 importer foundation | Open, conflicting | 2d8b071b0 |
Network writes disabled |
| #3932 → #3938 → #3941 → #3942 → #3943 → #3944 → #3945 → #3947 | Open, stacked, none merged; #3932 conflicting with main, the rest mergeable into their bases |
#3932 2ddd96fb0 · #3938 180f0d1bb · #3941 eb72c886d · #3942 75f89b33d · #3943 d75fd81b9 · #3944 59ab32e7b · #3945 25b364da3 · #3947 ae3c6749d |
Notification inbox, email, digests, reconciliation questions, study issues, PDF proposals; see notifications integration |
| #3955 QuestionAnswers stale scopes on transactional saves (fixes #3933) | Merged 04:47 UTC (c59d9d0f1) |
7cad12dc5 |
FEAT-024; touches the C7/C8 seams |
| #3956 reviewer screening drift guards (fixes #3937) | Merged 05:53 UTC | b0c18e033 |
FEAT-024; follow-up #3960 open |
| #3962 async point-fold slice 7 (docs, rules, status) | Merged 06:13 UTC | 5cca5e490 |
FEAT-024 STATUS still says "in review" and "fold flag off everywhere" |
| #3961 architecture review findings | Draft, mergeable | 3db9e5f6d |
Issues #3972–#3990; D1-02 |
| #2612 staged search import plan | Open, mergeable (docs only) | 03c4c57aa |
X-IMPORT |
| #3746 eligibility S6a | Open, conflicting | 66ec5a8e0 |
|
| #3742 eligibility S4-B | Draft, 0 files | 87c9de59f |
Migration tooling not started |
| #3741 eligibility S5 audit | Draft | 22a5786cf |
Only source of the G1–G6 gap list |
| #3327 allocation preview acceptance | Open, conflicting, stale | 2c1055968 |
|
| #2224 custom project groups | Open, conflicting (22 Sep) | fff8385ac |
Author nurikarakaya; the authorization plan reuses its shape (D10), not the PR |
| #2412 reviewer no-work page redesign | Open, conflicting | 3feb4912c |
Not stage completion |
| #2387 QM child visualisation and assign tree | Open, conflicting | c81426c44 |
|
| #2461 QM v2 R1 umbrella | Draft, conflicting | 1ca9f5def |
738 files |
| #2572 → #2573 → #2574 → #2575 QM v2 stack | Open, dormant since April | af5136696 … 098330759 |
Harvest per Q-08 |
| #2987, #2986, #2629 (mergeable); #2812 (conflicting) | Open, dormant since 1 Sep | — | Schema/profile/response/validation inputs; #2986 harvest into R2a |
| #2621 profile versioning prototypes | Draft, conflicting, dormant | 105bdc09e |
Seven prototypes; its ADR numbers collide with main |
| #2469 stage overview chart refactor | Open, conflicting | 2488a1f92 |
Stage-target chart conflicts with SF2 |
Opened since the first read, outside this plan's areas: #3958 (integrated PDF viewer fixes) and
3959 (statistics test isolation).¶
Recently merged and relevant: statistics slices 0–6 (6a #3948 at 02:57 UTC and 6b #3949 at 03:39 UTC on 3 October) and #3955 (04:47 UTC); eligibility S1b, S2-C, S3, S4-A and the claim-revoked event; authorization M5b (#3929, #3921) and the catalogue coverage test (#3882); bulk update v2 and study locks (#3914,
3909); route-owned statistics SignalStores (#3776–#3795); Dockview fixes (#3828, #3734); design¶
parity (#3544, #3545, #3533, #3542).
5. Feature flags in scope¶
All default false unless stated. "Staging" and "Prod" are cluster-gitops values.yaml values
(runtime overrides not read). Production pilots need a per-flag decision with each owner (Q-25).
| Flag | Default | Hosts | Staging | Prod | E2E stack | Gates |
|---|---|---|---|---|---|---|
newQuestionManagement |
false | web | off | unset | — | New editor nav link only |
annotationFormV2 |
false | web | on | unset | — | AF2 form (whole environment) |
stageReviewRedesign |
false | web | unset | unset | — | Redesigned review shell |
stageReviewDockview |
false | web | unset | unset | — | Dockview workspace |
integratedPdfViewer |
false | web | on | unset | — | Integrated PDF viewer |
reviewEligibilityPolicy |
false | API only (PM only through #3939's PR-only block) | unset | unset | — | Eligibility code paths |
proportionalStudyAllocation |
false | API only | unset | unset | — | Allocation (on only in preview pr-3327) |
activeReviewerTrackingEnabled |
false | API, PM | unset | unset | true | Tracking (with signalRActive, default true); claims and capacity guards exist only when on |
maxInProgressSessions |
true (API appsettings), false (PM), false (preview) | API, PM | — | — | — | In-progress cap |
stagePermissionsConfig |
false | — | — | — | — | Mock stage-permissions UI |
disableMembership, editProjectMembers |
false | — | — | — | — | Membership controls |
syrfOwnedApplicationRoles(Enforced), delegatedWorkAdmissionEnforced |
false | — | — | — | — | Authority transition |
deletionLifecycle |
false | — | — | — | — | Message choice only today; gates creation of reversible deletion operations once built |
bulkStudyUpdateAtomicApply, batchRiskOfBiasAtomicApply |
false | — | — | — | — | Writers to inventory |
materializedProjectStatistics* (25) |
false | API, PM | 8 on for one pilot project, plus materializedProjectStatisticsFold pinned on (API and PM; "staging pilot only (Chris, 2026-10-01)") and partial fold coverage allowed on the API |
none | — | FEAT-024 |
materializedProjectStatisticsQuestionCounts |
false | — | — | — | — | Live question counts and locks |
screeningKeywordHighlighting, graph2Data, quantitativeDataExportEnabled |
false | — | — | quantitative export on in prod web | — | — |
notificationInbox, notificationEmail, studyAttention |
PR-only | API, web | — | — | — | Notification stack, environment-wide; accepted email continues after notificationEmail goes off |
reconciliationConversations |
PR-only (#3965) | API, web | — | — | — | Private conversations; depends on notificationInbox |
annotationQuestionImport |
PR-only | — | — | — | — | #3934 |
progressiveReviewBatches |
PR-only | API, PM, web | — | — | — | #3939; requires reviewEligibilityPolicy |
6. Existing documents that conflict with later owner decisions¶
These need explicit supersession or amendment in the PR that implements the affected area. They are listed so nobody builds from them by mistake.
| Document / code | Conflicting statement | Superseded by | Handling in the plan |
|---|---|---|---|
| Review-eligibility policy D3a (In-Review, 25 Sep) | No stage closure or reopening state machine | RX2/LC1 (3 Oct), by the precedence rule | R3c introduces lifecycle for the new model; eligibility slices still don't. Record the supersession in the R3c ADR. |
Eligibility D3b and the AnnotationHasNoScreeningPrerequisite test |
Annotation never needs a screening decision | DP6/DP7 for configured dependencies | Keep it as the behaviour of independent steps and of migrated legacy combined stages; add dependency semantics only where a step edge exists (C6, Q-24) |
| FEAT-008 stage filtering (and its user-guide draft) | Pass Included, Conflict forward; annotation pool = collective Included |
DP7 default (collective Include), DP6 within-stage own Include | Amend in R3a |
| FEAT-010 stage settings | "Stages are unordered" (D30) | DP7 dependent stages | Amend in R3a |
| FEAT-026 batches README (PR) | "No arbitrary sequential stage-step model" | DP6 steps | Join at F3 with the batch owner |
| FEAT-006 reconciliation (README, design decisions, data-model migration, AF2 README) | Per-stage pools; "Annotator A vs B"; global anonymised-candidate invariant; answer every required question; single-annotator auto-promotion; rollback by $unset (D18) |
RE4, SF4/RE3, BL1, RE2, GS1; Q-29; canonical-aware rollback | Amend in R4a |
| FEAT-009 screening annotations | Separate screening and extraction reconciliation workflows; per-field choice | RE4, RE2, RX1 | Amend in R3b/R4p |
| QM v2 / annotation versioning | Per-stage question-set versions; checks against the previous version only; activation on stage enable; dataType versioned (D008) vs fixed (D38); one mutable pendingAnswer draft; gold as "latest version on the reconciliation annotation"; migration step 6 auto-promotion |
FV1–FV3, SF1, SL1, GS1, Q-29 | Contract C4/C5 decide; harvest per Q-08 |
| FEAT-011 PRISMA (Approved) | Platform-wide MIG-11/MIG-12 backfill; ScreeningOutcome with one stageId and no legacy authority; $unset rollbacks; "Delete Study removes its Citations" |
Per-project adoption; per-profile outcome; canonical-aware rollback; reversible deletion | Amendments G–J (Q-06a) through the FEAT-011 change policy |
QM v2 PR-A OutcomeDataStateSnapshot |
GreaterIsWorse per row |
ODIR1 | C14 |
| #2621 ADR-013 | Rationale library shared across profiles | DP4 | Reference only |
| #2987 ADR-016 | "AnnotationProfile" bound to a stage | SF1; profile naming clash | C4 naming; disposition at G0 |
GroupedReviewConfiguration (#3732) |
Per-stage SessionCountTargetOverride |
SF2 | Legacy-only; canonical stages take the form target, with an adapter for old readers (C7) |
| Category guidance per-stage override | Stage-owned guidance | SF1 (if it counts as form content) | Proposal A-15: keep as stage presentation text, never evidence |
| Live question locks (#3572–#3594) | Answered questions locked; additions only warned | FV1 | Canonical forms version instead of locking; legacy keeps locks |
User guide members-groups.md |
Administrators can assign a new owner | PM1/PM2 | Fix with the ownership enforcement follow-up |
Root CLAUDE.md domain model |
Describes versioning, profiles and screeningOutcomes[] as current |
— (accuracy) | Docs follow-up |
| FEAT-026 batches README (PR) | "Do not add a stage closure concept"; ConfigureProgressiveBatches requires a disabled stage |
LC1/RX2 (3 Oct) | R3c's Completed/Reopen lifecycle replaces "disable the stage before changing batches" for canonical stages |
FEAT-008 Filter Set model (JSON rules, same-profile $elemMatch simplifier) |
Undispositioned | DP6/DP7 routes | Decide at F3 whether the Filter Set schema is the route representation; keep the simplifier as a correctness rule for screeningOutcomes[] filters |
docs/features/signalr-active-reviewer-tracking.md narrative (:102-160) |
Describes ActiveReviewSession |
The delivered SlotReservation and presence contract |
Presence owner's docs PR before F1a (T1) |
| ADR-014 (uncommitted) | Physical deletion of Project, Search and Study after a 24-hour grace period | Amendment J, QD1 | D3-12; X-DEL |
7. Observed defects and risks outside this plan's scope¶
Reported, not fixed (scope discipline). Each should become a follow-up issue Chris can triage.
| Finding | Evidence | Severity (proposed) |
|---|---|---|
Project administrators can transfer ownership through PATCH /api/projects/{id}; the owner-only ChangeOwner rule is never checked. The permission-update endpoints also accept owner-reserved activities. |
ProjectController.cs:365-390, 1303-1320; Project.cs:855-883; ResourceSecurity.json ChangeOwner; no use of ProjectChangeOwnerPolicy (re-verified; the permission-update part per review C) |
High (authorization); Chris approved the fix on 3 October: PR #3964, merged on 3 October (85e6facf7) |
| Question deletion destroys every answer non-atomically | ProjectManagementService.cs:201-222; #3088 |
High (data loss), known. Under the new versioning a published question can never be permanently deleted (QD1, Chris, 3 October); legacy projects keep today's deletion until adopted |
| Reconciliation responses include every session on the study across stages; reviewer identities likely included | ReviewController.cs:1586-1628; StudyDto.cs:53-58; agent: StatsWithIncompleteDto.cs:14-31 |
Medium (privacy); current UI shows no names |
| Export page lets any ExportData holder unmask identities regardless of stage blinding | Review C: blinding-option-group.component.html:6-24; ResourceSecurity.json ExportData |
Medium (privacy) |
Membership route guard checks project.editMembership (typo) |
project-admin.routes.ts:36; authorization WP1d |
Low to medium (UI guard only; likely a no-op; server checks EditMemberships) |
| Required answers are not enforced on the server | AnnotationQuestion.cs:55, 227 |
Medium (relies on client) |
| Debug text "Focused question" ships in the new Design tab | design.component.html:14 |
Low |
| The export option "completed sessions only" is forwarded but never applied by the writers, so exports can include incomplete work | WriterConfig.cs:46-60; screening research §1.5 |
Medium (export correctness) |
COMPARISON.md line 107 says Review Prototype v4 is absent; it exists in handover/2026-09-21-stage-review-design/design_handoff_stage_review_page/ |
Prototype asset search | Low (documentation) |
| Stale status documents: AF2 STATUS, FEAT-024 STATUS, allocation STATUS, the catalogue, root CLAUDE.md domain model | Theme A/B reports | Low |
Duplicate identifiers: FEAT-025 (allocation and keyword highlighting), ADR-008 (two documents); every ADR number in open QM/annotation PRs collides with main (next free: ADR-021) |
Theme A/B reports | Low (process) |
Broken references: .planning/REQUIREMENTS.md, docs/features/annotation-management-reconciliation/… |
Theme A/B reports | Low |
The owner decision ledger, later research and this package are committed only on the PR #3617 branch (latest planning commit aac4debcd), not yet on main |
git ls-files on the PR #3617 branch; aac4debcd is not an ancestor of origin/main |
Medium (authority at risk until merged to main; resolved: PR #3617 merged to main on 3 October 2026, f5318074d) |
| Production has no claims or capacity guards because tracking is off everywhere; the over-allocation report #2446 remains open | FeatureFlags.cs:35-36; cluster-gitops values; #2446 |
Medium (capacity correctness) |
| Reconciliation has no editor exclusion: two reconcilers can edit one study | StageReviewService.cs:67-70, 153 |
Medium (data correctness) |
| Presence snapshots name claim holders to every member who can view studies, whatever the blinding | StudyReviewPresenceSnapshot.cs:86-96 (per review RT); #3892 |
Medium (privacy; dark today) |
reviewEligibilityPolicy and proportionalStudyAllocation reach the API host only, while Core code reading them runs in both hosts |
env-mapping.yaml services: [api] block |
Medium (split brain on enablement) |
| The allocation flag is read twice per request (runtime value in the controller, process value in admission) | ReviewController.cs:500, 626, 753, 790, 1173, 1443, 1609; StudyRepository.ActivityReservations.cs:19-21 (per review AP) |
Low (latent; real once overrides work) |
The question-delete cascade and the inclusion recalculation change pmStudy without bumping Audit.Version |
StudyRepository.cs:1306-1319, 1620-1650 |
Medium (CAS cannot see them; #3985) |
| Stale status documents: allocation STATUS (#3603), FEAT-024 STATUS (slice 7, staging fold flag) | proportional-study-allocation/STATUS.md:71; materialized-project-statistics/STATUS.md:372-386 |
Low |
8. Prototype and design assets¶
See the UI coverage comparison, which records every asset found and not found.
9. Notification infrastructure¶
See notifications integration.
10. Earlier-year documents and their disposition (PROPOSAL)¶
Review A listed earlier documents the first draft didn't reference. Status and dates from their front matter.
| Document | Status, date | Disposition |
|---|---|---|
docs/planning/screening-step-dependency-options.md (PR3617 worktree) |
Draft, 24 Sep | Input to Q-15: its Modes B and C are now presented alongside the hybrid |
docs/planning/active-reviewer-tracking-overhaul.md |
Approved, 10 Apr | Background for C7 claims (E18); implemented behind activeReviewerTrackingEnabled |
docs/planning/review-access-state-overhaul.md |
Draft, 17 Apr | Background for C6 admission; superseded in scope by the eligibility programme; read before F3 |
docs/planning/session-capacity-suspended-sessions-handover.md |
Draft, 15 Apr | Background for capacity and suspended-session behaviour in C7 (E6, E18); read before F1a |
docs/planning/session-copy-review.md |
Draft, 17 Apr | Input to the terminology and copy contract (C17) |
docs/planning/data-export-analysis.md |
Draft, 11 Mar | Input to C11 export modes and the OnlyCompleted fix |
docs/planning/stage-review-layouts/contract.md |
Approved (layout contract) | Binding for C17's layout amendment |