Skip to content

Study Management Programme

Decision requested

Approve a phased redesign of the project Studies area, rather than merging any historical Study Management branch or treating PR #2990 as the finished feature.

The approved information architecture uses a Study management navigation group with three stable destinations. Short navigation labels are paired with precise page titles:

  1. Library / Study library — browse and filter study records; launch Bulk Study Update; show the current or latest update and link to its processing history (the history the server retains).
  2. Searches / Systematic searches — create and manage searches; show current or latest Search Import and Bulk PDF state in context (Risk of Bias omitted since 2026-09-02: the feature is disabled and on ice — see acceptance-criteria.md).
  3. Processing / Processing activity — the canonical read-only view of the retained history across permitted long-running operations, with links back to the owning screen.

This preserves the domain distinction between studies and searches while making long-running work consistently discoverable. It does not create a universal backend job type or place unsupported commands behind generic buttons.

MVP boundary

The first genuinely usable release is the smallest end-to-end slice that lets an authorised user:

  • launch a Bulk Study Update from the Library and find the same operation in Processing after navigation or refresh;
  • see exact status, truthful progress, bounded errors, initiator when the server can prove it, and valid created/updated/completed timestamps without year-one defaults;
  • follow consistent links among Library, Searches, Processing, and the owning context;
  • use the experience at 320 CSS pixels, at 200–400% zoom, with keyboard and screen reader; and
  • receive stable state during loading, reconnecting, partial failure, completion, and live updates.

The shortest critical path is: contract truth → Claude Design iteration → shared presentation adapters → Library vertical slice → Searches consistency → Processing destination → final audit.

Cancellation and retry are capability-specific. Bulk PDF retains the commands it already supports. Search Import, Bulk Study Update, and Risk of Bias do not gain cancel/retry UI until a separate backend PR defines safe semantics and permissions for that job family.

Acceptance criteria for the plan gate

  • Historical Study Management issues, PRs, branches, and feature documentation are reconciled.
  • Current-main routes, APIs, projections, stores, job models, permissions, and deployed flags are inventoried.
  • PR #2990 is classified as prototype evidence, with its correctness defects and useful parts recorded.
  • Central and contextual job responsibilities are explicit.
  • Material 3, responsive, high-zoom, accessibility, live-update, and state requirements are contractual rather than deferred polish.
  • Implementation is split into independently reviewable PRs with file ownership, dependencies, focused tests, rollout, rollback, and staging proof.
  • Product approves Library / Searches / Processing and the read-only Processing boundary.
  • Claude Design completes the reviewed interaction and responsive design iteration (approved 2026-09-01; see design-decision.md).
  • Security/product approve the permission matrix before any aggregate job view is exposed (Wave 1B supplies the server-filtered proof; gates Wave 5 exposure, not implementation start) (approved 2026-09-07).
  • Material 3 PR #2994 lands or publishes its final token/navigation contract on main (merged; its contract is an ancestor of this plan's base).

A static (no rendered pass) design review of the shipped Library/Searches/Processing UI ran 2026-09-08/09 and found status-language, accessibility and page-shell consistency issues, grouped into PR-sized fix slices. All slices have merged: A (Processing + dialog defects/a11y, PR #3413), B (one status language, PR #3414), B2 (Searches table status language, PR #3420), C1 (shared page shell + Library, PR #3412), C2a (Searches on the shared page shell, PR #3434), C2b (Processing on the shared page shell, PR #3439), D (docs truth, PR #3411), and the docs closeout (PR #3435). These are pre-C7.2 work: see acceptance-criteria.md section D for the tracker entry and the acceptance rows it keeps Partial until the C7.2 rendered walkthrough.

Recovered history and disposition

Artefact Recovered intent Decision
PR #1865 Unified Study Management page with upload, Bulk Update, and progress Closed prototype; retain user intent and test ideas, not code
PR #2511 Monorepo rebase of the same combined page Closed prototype; superseded
PR #2528 Large admin/project job-progress implementation behind newStudyManagement Do not merge; preserve evidence, split by the waves in this plan, then close as superseded
PR #1875 Tests for the pre-monorepo implementation Preserve behavioural scenarios only; paths and component assumptions are obsolete
Issue #1917 / FEAT-018 Original integration checklist and known last-modified mismatch Replace the stale checklist with this programme; keep the issue as historical tracking until new slices exist
Issue #2562 Production-ready refactor of the admin job progress UI Separate admin-monitor successor; it does not own the project Studies experience
PR #2990 Bulk Study Update history on Systematic Searches Prototype evidence; do not merge in current form; supersede after the Library slice is reviewed
PR #2993 Central Jobs hub plan Reused as the canonical plan-only PR and broadened to this complete programme
PR #2612 Staged Search Import design Retain as a backend evolution dependency; do not absorb into the UI programme
PRs #2858, #2887, #2900 Search-upload metadata and Bulk Update operational recovery Retain and sequence; avoid their owned files
PRs #2976, #2979 Bulk PDF runtime/release and hosting plans Retain as authoritative Bulk PDF dependencies; avoid their owned files
PR #2994 Material 3 tokens and adaptive-navigation contract Read-only design dependency; Study Management retains product/domain ownership
PRs #2992, #3010 About/navigation and Admin responsive work Explicitly out of scope and untouched

Why PR #2990 is not the end state

PR #2990 usefully demonstrates a bounded error disclosure, responsive table-to-card treatment, explicit Bulk Study Update status copy, and focused component tests. Those ideas should be reused after their data contract is corrected.

It diverges from the required programme in material ways:

  • it puts project-wide Bulk Study Update history on Systematic Searches, although the launch action belongs in Library and canonical history belongs in Processing;
  • it covers one job type without resolving entry points, navigation, refresh behaviour, or the relationship between contextual and central monitoring;
  • it can report parsed rows as updated studies even when no study changed;
  • its ordinary-member initiator lookup can display Unknown Investigator because the server projection omits secure related investigators;
  • DateTimeLastModified is not mapped from the domain LastModified property, so the DTO can expose a year-one default and sort incorrectly;
  • it assumes no backend work is required despite those correctness and permission defects; and
  • it does not reconcile Search Import, Risk of Bias, Bulk PDF, mobile/high-zoom behaviour, or the shared Material 3 status contract.

PR #2990 must remain untouched until this plan is reviewed. If the Library slice is approved, that implementation PR should explicitly supersede #2990 and carry forward only verified pieces.

Planning pack

Ownership and coordination

This plan owns Study Management product decisions and future files under a dedicated project/study-management feature boundary plus the explicitly listed contextual screens. Each implementation PR must publish its exact file ledger before editing and must not have a second writer.

PR #2994 has merged, so this plan now consumes its Material 3 contract and may regenerate the feature indexes and MkDocs navigation. PRs #2900 and #2979 still touch CLAUDE.md; the durable agent-context pointer must therefore be added conflict-free after those owners land. This is a sequencing dependency, not permission to modify another task's file.

Remaining product decisions

  1. Use Claude Design to validate the Processing default, filter hierarchy, disclosure model, and contextual summary pattern before implementation. Closed — approved 2026-09-01 (design-decision.md).
  2. Confirm project members may see only operation families already authorised by server projection. Route visibility is decided by explicit PermissionReport capabilities — never by "absence of every family", which is indistinguishable from an authorised project with no jobs yet.
  3. Confirm history retention/pagination remains the current aggregate behaviour for the MVP. Closed — the brief scopes the MVP to the retained ProjectDetails aggregate with a paged historical API as evidence-triggered follow-up.

The Claude Design gate is accepted; decision 2's server-side proof lands with Wave 1B and gates Wave 5 exposure.

Current implementation summary

Relocated verbatim from the root CLAUDE.md.

  • Canonical routes: /projects/:projectId/studies (Library), /projects/:projectId/searches (Searches; the former admin/systematic-searches URL redirects to it), /projects/:projectId/processing (Processing).
  • Processing is read-only history for three job families — Search Import, Bulk Study Update, Bulk PDF; Risk of Bias is deliberately excluded while that feature is parked.
  • Route/navigation gating: Processing requires the generated default-off studyManagementProcessing flag plus any of the six capabilities in PROCESSING_CAPABILITIES. Searches requires viewSearches, importSearch or removeSearch (hasSearchesCapability, mirroring the Search Import family's server viewPermissions). With the flag off the Processing URL falls through to the in-project "This page isn't available" page (the project ** child, nav still visible) and no navigation entry renders. With the flag off, the "Systematic searches" nav item uses the same hasSearchesCapability as its route. The Searches delete button renders only with removeSearch; the stage Review row and route need stages[id].review; data export needs exportData (#4703).
  • Per-study PDF status (slices 8a and 8b; flagged studyPdfStatus, default off everywhere, because it adds a collection, a background worker, outbound fetches and a new push): the server keeps one pmStudyPdfAvailability document per study (MongoDB reference), outside pmStudy. The review and reconciliation retrievals return at once with PdfStatus and, when it is unconfirmed or due (Unavailable after 24 hours, Unverified after 15 minutes; Available and External are reused until the link changes), offer the study to an in-process queue (256, deduplicated per study). A worker runs 4 checks at a time, claims each study for two minutes across pods, and fetches only links built from SyRF's configured PDF bases, with the pdf-agent's check (PdfSignatureProbe: five bytes, 10 s, no redirects, no cookies). An external link is recorded as External without a fetch. The Studies list (#4887) starts the same checks for the page it returns, through the same rules and queue, and returns without waiting; the web list then re-reads that page quietly up to three times (about 5, 10 and 20 s apart, PDF_CHECK_REREAD_DELAYS_MS) while a row is Not yet confirmed or Checking, but not while a PDF status filter is chosen (the page then changes only when the reader loads it) or under support impersonation. Single-study reads report the stored status and never start a check. A background sweep (#4890, its own default-off flag studyPdfBackgroundChecks) walks every study under one lease document (pmStudyPdfCheckSweep) and offers those with a link and no current result, or a stale Couldn't check, to the same queue while fewer than 64 checks are pending, a pass at a time with StudyPdfCheckSweep:IdleBetweenPasses between passes (6 hours by default, clamped to 15 minutes to 7 days), so PDFs added by imports and bulk uploads are picked up by the next pass, up to about 6 hours later by default, while the Studies list and review still start checks at once for what people open. It never rechecks Unavailable on a timer (implementation plan, slice 8 decision 11). Before production: each pass reads every study's link inputs (production's pmStudy is about 10 GB), so the owner decides the interval before the flag is switched on there; the preferred follow-up is an index-driven pass on the stored classification index (migration 0037) once it is armed in production. If the PDF host is unreachable for a long time, every internal link records Couldn't check and each pass offers them all again, so at the 10 s timeout a pass could take days. No back-off is built: the cost is bounded (four fetches at a time, list and review offers keep their headroom, nothing else waits on the sweep), and studyPdfBackgroundChecks is the lever during an outage. Backing off after consecutive Couldn't check results is a follow-up, not built, beside the index-driven pass. With the flag off nothing is read or written, nothing is offered and PdfStatus is null. Documents for deleted, moved or erased studies are ignored on read and go when the flag is retired.
  • Push (8b): SubscribeToStudyPdfStatus(projectId, stageId, studyId) and the stage-less SubscribeToProjectStudyPdfStatus(projectId, studyId) (both ProjectViewStudies, the presence lookups, a disabled member refused, refused while the feature is closed) send StudyPdfStatusUpdated { studyId, status, checkedAt }, with no URL or file content, whenever a check changes the stored status. Every subscription starts with the study's current status, read only once the subscription is registered and decided by the same authority as every later update, so a check that finished before the panel subscribed (or while the client was disconnected; the client subscribes again on every new connection) still reaches it. v1 projects the availability change stream (the snapshot first, DistinctUntilChanged on status, ended on project revocation, current authority when enforced); v2 adds the pdf:{projectId}:{studyId} topic and a ChangeFeedReader for pmStudyPdfAvailability, flushed with the per-subscriber authority decision, and the hub asks the dispatcher for the snapshot once the registry entry is confirmed, so that read is ordered with every real change's. It isn't gated on active reviewer tracking.
  • Web (8b): with the flag on, the review PDF panel takes its state from the study's pdfStatus and then the pushes, and never probes the PDF from the browser. Couldn't check offers Retry, which only reads the status again. A viewer load failure shows the viewer's own error and leaves the status alone. The Library PDF cell shows the stored state with an accessible name ("PDF: Not yet confirmed"); every state with a PDF is the PDF icon with a corner badge saying what kind (Not yet confirmed a clock, Checking sync, Unavailable an error mark, and so on), so the states differ without colour, and the details card's PDF heading and Open PDF use the same icon (PR #4885). With the flag off, or when the server sends no status, both are as before, including the browser's PDF check.
  • Navigation with studiesWorkspace on (C+B′, W6): the project nav shows one "Studies" item instead of the Study management group. It's visible with viewStudies and current on /studies, /studies/stage/:id and /searches, with exactly one aria-current="page" row. With the flag off, the nav is unchanged in both studyManagementProcessing states.
  • Search read rules (SM-13, server-enforced): GET api/projects/{projectId}/searches needs an active member holding viewSearches, importSearch or removeSearch (ProjectSearchReadPolicy); anyone else, including signed-in non-members of public projects, gets 403. With studiesWorkspace off (or decided at its safe value, off), project details still give every member each search's id, name and study count for the Library, Overview and study filters, but without the description or original reference files unless the member holds one of those activities; with it on, W4's rule below applies instead. See classify an API endpoint's authorization.
  • Who can see search names (W4, server-enforced while studiesWorkspace is on): with the flag on, a member without viewSearches, importSearch or removeSearch gets search names and study counts only while holding ViewSearchOverview, which has no default grant (a manager grants it to groups or to All members from the Searches page), and no searches at all otherwise. The same rule names the search in the SearchDeleted notification: other members are told a search was deleted without its name. The restriction applies only while studiesWorkspace is on. With the flag off, or when the runtime decision falls back to its safe value (off), every member sees names and counts as on main (the search read rules above). Holders see no audit or living-search link either. With the flag on, the Searches page is the page for managing (W4): a "Back to Studies" arrow beside the title, "Who can see search names" for managers who may change permissions (assignPermissions), and "Activity" with its attention count, linking to W3's /studies;activity=open. Since #4884 its body is the C+B′ board's list of search rows (two lines of words, "View N studies", Add PDFs, a history expander and the board's More menu), with no operations summary, no table and no Processing link; with the flag off it is today's table. See the implementation plan's "Searches page matches C+B′". The project setup checklist's "Upload your systematic search" step also counts as done once the Library total is above 0, so a design-only member who gets no searches still sees it done.
  • Studies workspace (C+B′, W1): with the generated default-off studiesWorkspace flag on, /studies and its stage scope open StudiesWorkspaceComponent (studies/studies-workspace/) instead of the Library. It embeds the Library's own list (StudyTableComponent in workspace mode: compact Study, Search and PDF columns by default), a Searches pane (searchPaneAccess: the full pane for managers, hasSearchesCapability; the read-only "Searches (names and counts)" pane for viewSearchOverview holders, from W4; none for anyone else) and a study detail frame. The details are one card with a tinted "Selected study" header, the selected row is a filled 16 px container in the same tint, one decorative selection shape slides between rows, and the card enters from the right as the list narrows, on M3 expressive springs with reduced-motion fades (PR #4885; the implementation plan's "W2 details card, sliding selection and motion as built"). Since PR #4899 the card leads each section with its answer: the citation under the title, a PDF lead line, a Review section with one item per stage and its own Start or Resume (finished stages fold past four; a stage scope shows that stage with the others a click away), and a quiet Record section with Search, Added and a Copy study ID button (the implementation plan's "W2 details card rearranged (PR #4899)"). With the flag off, the Library, Searches and Processing are unchanged. Scope and seams: the implementation plan's "W1 · Workspace shell". The list's controls (PR #4900): one filter row of a visible search field and chip menus (shared/chip-menu/, extracted from the Stage chip and drawn as the board's 32 px M3 filter chips, with Only mine the same chip as a toggle), sorting from the column headings (a Sort button below the card breakpoint), a Columns chip whose default set follows the list's width, PDF cells with words in the state's colour (Available in success, a waiting proposal in warning, Unavailable in error) and a Search column; no ⋮ menu or columns drawer (the implementation plan's "List controls and columns as built"). PDF filter and pane PDF counts (#4719, PR #4764): with studyPdfStatus on as well, the filter bar adds a single-choice PDF filter (Any PDF status, Without a confirmed PDF, Available, Not yet confirmed, Unavailable, No PDF; ;pdfStatus= in the URL) and managers' settled pane rows add "N without a confirmed PDF" (GET …/studies/pdf-status/search-counts), both from slice 8's server-tracked state; the server refuses the filter (409) while either is off. It is ANDed with a My studies tab (#4720): a request may carry both, and the list and its count are their intersection; a malformed tab (400) or unknown stage (404) is refused before the PDF filter's 409. The Searches page and Overview reuse the counts for search managers (#4770): each settled Searches page row with a count above 0 links "N without a confirmed PDF" to Studies on that search and Without a confirmed PDF, and Overview's Studies line adds " · N without a confirmed PDF" for the project, from one shared loader (shared/search-pdf-counts/) fetched once per page view and refetched on the pane's events. See the implementation plan's W1 as built, decision 1, and "Searches page and Overview counts (#4770)". Stored PDF classification (#4771, four stacked PRs starting with #4838, ADR-028): the same filter and counts answered from fields stored on pmStudy, exactly as the C# classifier answers, behind a default-off studyPdfStatusStored; PR 1 adds the shared link-shape rule and maps the four stored fields on Study (read, never written), and changes no behaviour; PR 2 (#4845) adds the writers, armed only by migration 0037; PR 3 (#4849) adds the flag, the stored read with its admission, and PM's backfill worker, all inert until PR 4 (#4873), which registers 0036 and 0037 and merges only after PRs 1–3 are on staging. See the implementation plan's "Stored PDF classification (#4771)".
  • Bulk study update in the workspace, server half (BSU-a, #4766): three read-only fields, each null while studiesWorkspace is off. Each bulk study update job carries allOrNothing (its own stamped execution version is 2, ADR-020), in project details, the project stream and the upload-failure report, only to members who already see the job. Project details carry newBulkStudyUpdatesAllOrNothing for members holding bulkUpdateStudies: whether a job started now will run all-or-nothing, read from the API's own deployed bulkStudyUpdateAtomicApply, the value that stamps new jobs, so the two can't disagree (there is no web flag). The Library list carries bulkUpdateLockedAt on each study an all-or-nothing update holds: the stored lock that study writes are refused on, so it says nothing the 409 doesn't. Nothing new is stored, and no UI reads them yet (BSU-b).
  • Bulk study update per-row unmatched detail (BSU-c, #4810; behind studiesWorkspace, no new flag): a bulk study update job created while the flag is on is stamped RecordsUnmatchedRows (once, at creation). A stamped version 1 job records up to 100 rows naming no study in the project, with their study IDs (parseResult.unmatchedRowSample); a stamped version 2 job's StudyNotFound problem carries the study ID. Counts are unchanged, an unstamped job stores nothing new, and the API sends the detail only while the flag is on. Activity then lists "Row : no study with ID in this project." instead of the count line. As built: implementation plan, BSU-c as built.
  • My studies tabs, server half (#4720, PR A; behind studiesWorkspace, no new flag; the web half is listed next): the server decides the reader's own work in a stage. The reader is always the signed-in user (under support impersonation, the impersonated member): no query, route or body field names a reviewer, and no response carries an investigator id or another reviewer's decision.
  • MyStageWork per study (Core/Services/MyStudies): InProgress, Completed, OpenToStart, NotStartedNotOpen, Finished or NotInStage. A stage is screening-kind when it reviews screening, or screening and annotation with studies selected by screening (StageWorkKind, the twin of the web's yourStatusFilterFor); every other stage is annotation-kind. Annotation reads the reader's own non-reconciliation sessions in the stage (an unfinished one wins over a completed one), then exclusion, then the stage's allocation target (SufficientlyAllocated, counting reservations only while tracking is available), then, with proportionalStudyAllocation on, the reader's share (an invalid configuration opens nothing). Screening reads the reader's own screening, then whether the study is sufficiently screened. MyStageWorkFilters (Mongo) and MyStageWorkEvaluator (in memory) agree for every study and reuse the review page's reviewer-stats predicates.
  • Team-excluded studies follow the review page's My studies panel (settled with the owner, 4 November): with the stage's hide-excluded setting on, an excluded study is in no tab and no count, completed or in progress, because the panel hides every excluded saved study. With it off, excluded work stays the reader's own (In progress or Completed), as the panel's rows list it and its summary counts it when excluded sessions are grouped with the rest. This is deliberate, not an asymmetry to "fix": it is not the progress meter's raw sum.
  • Tabs: To do = InProgress + OpenToStart; In progress = InProgress (always 0 in a screening-kind stage); Completed = Completed.
  • List: POST api/projects/{projectId}/studies (unchanged policy, ProjectViewStudiesPolicy) reads filters.myStudiesTab (None, ToDo, InProgress, Completed) while the flag is on, ANDed with every other filter. A tab needs filters.stageId (400 my-studies-tab-refused without one, or for an unknown tab; 404 my-studies-stage-not-found for a stage not in the project; both are ProblemDetails). Each row carries myStageWork whenever the request names one of the project's stages, tab or not, and the response says myStudiesTabApplied: true only when the tab was applied. Off, the tab is ignored, myStageWork is null and myStudiesTabApplied false.
  • Counts: GET api/projects/{projectId}/stages/{stageId}/my-studies/counts (StageReviewPolicy, FeatureGated: unroutable 404 while off) answers { inProgress, notStarted, completed, toDo }, stage-wide (no list filter), 404 for a missing project or stage. Three CountDocumentsAsync calls over the project; a count no study can meet (In progress in a screening-kind stage) is 0 without a query. The list's tab and the counts build their scope through one MyStudiesScopeResolver (one proportionalStudyAllocation read through IFeatureDecisions), so the To do total and the To do count can't drift.
  • Indexes: none new. Explained on synthetic data with the study collection's indexes (MyStudiesCountsExplainTests): every count is an IXSCAN; In progress and Completed fetch only what they count; not-started counts scan the project's index range, as the review page's available counts do.
  • Nothing is stored in any flag state, so switching the flag back and forth leaves no data behind.
  • My studies tabs, web half (#4720, PR B #4775; studiesWorkspace only, no new flag): in a workspace stage scope a reader who may review the stage (stages[id].review) gets To do, In progress and Completed with the counts, the lede "Your studies in : the ones you’ve started or been given." and the line " in progress · not started · completed" while no other filter narrows the list. Managers see the whole stage and "Only mine" (in the filter bar) when they may review it. While the tabs show, "Your status" is hidden and cleared: the guard rewrites it to All in the URL, so the form holds All and Only mine off doesn't restore it. Every reader in a workspace stage scope (tabs or not: managers with Only mine off and readers who may not review included) gets the "Your " column, the reader's own MyStageWork per row: Start or Resume (active stage, stages[id].review), In progress (started, no Resume here), Not started, Completed, Finished, or "—" for NotInStage (excluded at screening with no own session; with the stage's hide-excluded setting on, every excluded study). The tab is ;myStudies=todo|inProgress|completed (core/state/ui/my-studies-tabs.ts), fetched by the workspace's studiesWorkspaceTableGuard; the review page's "See them all in Studies" opens ;myStudies=todo. A tab answered with myStudiesTabApplied not true shows "Your studies couldn't be loaded." with Try again and no rows. With the flag off the Library ignores and drops ;myStudies. Details: the implementation plan's #4720 "Web as built".
  • Stage chooser chip (#4776, PR #4808; studiesWorkspace only, no new flag, web only): the workspace's filter bar has "My studies" (members) or "Stage" (managers), a menu of "All studies"/"All stages" and every stage the reader may review (stages[id].review, active or not). A member's choice opens the stage on To do (;myStudies=todo); a manager's opens the whole stage, or To do while Only mine is on. The search filter, title search, PDF filter and page size carry over. Members outside a stage scope see "Choose My studies to see your work in a stage." Details: the implementation plan's #4720 "Stage chooser chip (#4776)".
  • PDFs to check (slice 9, PR #4785; behind studiesWorkspace, no new flag, plus an index admission): R2's Set holders (ApprovePdfCorrections) see "N proposed PDFs to check" first on Overview's Needs attention card, with Check proposals, which opens the workspace filtered to "Proposed PDF waiting" (appended to #4719's PDF filter; it needs no studyPdfStatus). The PDF cell reads "No PDF · N proposed" or "Unavailable · N proposed" with studyPdfStatus on, and the detail names the proposer for Set holders only. Server: GET …/studies/pdf-proposals/summary and GET …/studies/pdf-proposals?studyId=… (Set only, FeatureGated, Available: false while the 0035 pmStudyPdfCorrection index is missing, through StudyPdfProposalsAdmission), and the filter value (403 without Set, 409 off, closed or above 10,000 studies). Use proposed PDF writes the link and the decision in one transaction and Reject is one conditional write; both are idempotent, and a race leaves the study and its proposal agreeing. Off, Approve and Reject are as before. See the implementation plan's slice 9 section.
  • Shared status language: app-study-job-status-chip + presentStudyJobStatus, deployed labels verbatim, "Unknown status (n)" for unknown enums, no invented progress.
  • Overview's study count is the Library total (GET api/projects/{projectId}/studies/count, ProjectViewStudiesPolicy, from CountStudiesInProjectAsync), fetched afresh on every Overview visit (the state is provided by ProjectOverviewComponent, so nothing is fetched while you are on another page) and linked to the Library; it is not the sum of the searches' imported counts. While it loads nothing shows. When it is 0, Overview says "No studies yet." if the project has searches, otherwise "Add a search to begin." to someone with the project's design permission and "No studies uploaded yet." to everyone else.
  • Authority: this directory (README, implementation-plan, design-decision, design-decision-log, design-source-truth, acceptance-criteria); flag enablement is gated on B17 sign-off and the C7.2 staging walkthrough recorded there.

Glossary

The study-management terms used in the app and the user guide. Keep the app copy and the user guide glossary in step.

  • Library: every study currently in the project, wherever it came from. Its total is the number of studies in the project.
  • Search: one set of records you imported from a search strategy or database. Each is a systematic search; the app calls it a search. A project can have many.
  • Import a search: the action that brings a search's records into the project. It starts from the Searches page, whether or not the import wizard is switched on.
  • Records and studies: records are the rows in the file you import, counted per search on the Searches page. Studies are what SyRF holds after the import. The two numbers can differ, for example when rows cannot be read.
  • Import notes: anything SyRF tells you about a file while importing it, shown against the search.
  • Uploaded: a PDF file has reached SyRF. It does not yet mean the PDF belongs to a study.
  • Attached: a PDF is linked to its study, including one that was already stored.
  • Verified available: SyRF fetched the stored file the way a reviewer's viewer will. Otherwise a PDF is Unavailable or Not checked.
  • Not matched: files left on your computer because no study matched them.
  • Already stored: files SyRF already held, so they were not sent again.
  • Studies without a PDF: studies that have no PDF attached.
  • PDF states: where per-study PDF status is switched on, a study's PDF is shown as No PDF, Not yet confirmed, Checking, Available, Unavailable, Couldn't check or Hosted elsewhere. SyRF checks a study's PDF when the study is opened for review and when the Studies page lists it, and the review page and the Studies list update when the check finishes. Where background PDF checks are also switched on, SyRF also checks, in the background and a few at a time, the PDFs it hasn't checked yet, so their state appears without anyone opening the study. PDFs added by an import or a bulk upload are picked up by the next background pass, which can be up to about 6 hours later; the Studies page and review still check what you open straight away.
  • Without a confirmed PDF: every study whose PDF isn't Available, including studies with no PDF. The Studies workspace's PDF filter offers it, and for search managers the Searches pane and the Searches page count it for each search, and Overview counts it for the project.
  • Activity: the read-only history of imports, bulk study updates and bulk PDF uploads, with where each got to and what went wrong. Where it is switched on it appears as Processing in the project menu.