Skip to content

Study Management — target acceptance criteria tracker

This is the single running record of what the programme must deliver and how far it has got. It exists so that shipping thin slices early (the programme's delivery rule) never loses sight of the target. Every wave PR updates the rows it delivers, with a link to the evidence (spec, test name, PR, or staging capture). A row is Met only with evidence; Partial names what is missing; Deferred names the follow-up that owns it.

Authority for each criterion: README.md (MVP boundary), implementation-plan.md (wave outcomes and tests), design-decision-log.md (D1–D17, O1–O5) and design-decision.md (binding corrections).

Delivery rule (recorded 2026-09-02): favour feature delivery, then iterate. The rule governs how review findings are triaged: a finding holds a PR only when it shows a regression, data-loss or corruption risk, or a security problem; every other finding is recorded as a follow-up and the PR ships. It does not waive the plan's CI and merge gates — focused tests, a current generated client, lint/build/docs validation, every review thread resolved or explicitly deferred, no failed or in-progress required check, and the final user review — which still apply to every PR. For flag-gated slices the controlled staging proof is attached before the flag is enabled anywhere (C7.2), not before each merge of dormant code; unflagged corrections attach preview or test evidence to their own PR.

Scope change (recorded 2026-09-02): the Risk of Bias feature is disabled and its development is on ice, so the Risk of Bias family is omitted from Waves 2–5. Rows that mention it are marked Out of scope until product revives the feature. Its server-side revision field from Wave 1B remains as dormant backend code.

Status legend: Not started · In progress (PR #) · Met (evidence) · Partial (gap) · Deferred (follow-up) · Out of scope (reason).

A. Programme target (the MVP the user must be able to do)

ID Criterion Source Status
A1 An authorised user launches a Bulk Study Update from the Library and finds the same operation in Processing after navigation or refresh README MVP Not started (Waves 3 + 5)
A2 Every operation shows the exact raw status, truthful progress, bounded errors, initiator only when the server can prove it, and valid created/updated/completed timestamps with no year-one defaults README MVP; D7–D9, D13 Partial — contract truth met by PR #3052/#3053 (year-one fix, truthful counts, initiator projection); UI rows Wave 2B, pages Waves 3–5
A3 Library, Searches and Processing link to each other and back to the owning context consistently README MVP; D1, D3 Not started (Waves 3–5)
A4 The experience works at 320 CSS px, at 200–400% zoom, with keyboard and screen reader README MVP; D16, D17 Not started (Waves 3–5)
A5 State stays stable during loading, reconnecting, partial failure, completion and live updates; stale HTTP/SignalR payloads never regress newer state README MVP; plan Wave 2 In progress — server revisions met (PR #3052/#3053); client rejection Wave 2A
A6 Users find things intuitively placed and easy to navigate (product owner's UX mandate, 2026-09-01) design-decision.md Not started — verified per UI wave by the staging walkthrough and final review

B. Cross-cutting design contract (applies to every UI wave)

ID Criterion Source Status
B1 Three linked routes under one "Study management" navigation group; compact navigation uses PR #2994's adaptive primitive D1 Not started (Wave 5)
B2 Processing is read-only history; only Bulk PDF exposes cancel/retry; no other family ever renders cancel/retry D2 Not started (Wave 5) — no cancel/retry API exists for other families (verified)
B3 Contextual summaries on Library/Searches with "View in Processing"; the deep link reveals the operation even when terminal and outside the default filter, with notice + Clear + focus D3, D17 Not started (Waves 3–5)
B4 Processing default filter = Active + Needs attention; "All operations" pages 25 rows with honest remaining count D4 Not started (Wave 5)
B5 One shared status-chip component on the Bulk PDF StatusView contract (ok/stopped/warn/muted/info, honest bar modes, ariaValueText) D5 Not started (Wave 2B)
B6 Deployed labels reused verbatim; Bulk Study Update sentence-cased enum with raw enum + value in row detail D6, D14 Not started (Wave 2B presenter)
B7 Unknown status renders "Unknown status (n)", never Running D7 Not started (Wave 2B) — Bulk PDF's "Status unavailable" reconciliation is O5/Wave 4
B8 Honest progress only: determinate bars need a real numerator/denominator; completion reports matched/modified, and a 0-changed run says so D8 Partial — resultSummary contract met (PR #3052); presentation Wave 2B
B9 Missing data renders "Not available", never fabricated D9 Partial — nullable timestamps met (PR #3052); presentation Wave 2B/3
B10 Processing grouping Needs attention → Active → Completed with counts; fixed column grid; contained horizontal scrollers D10, D11 Not started (Wave 5)
B11 Live updates re-sort deferred while a row is expanded; reconnecting keeps rows visible and marks them stale D12 Not started (Wave 5)
B12 Bounded error lists (first 10 of N, Show all) D13 Not started (Wave 5; strips in 3–4)
B13 Server-filtered aggregate with per-family view gates; route visibility decided by PermissionReport capabilities, never by emptiness D15; README decision 2 Partial — server gates in PR #3053 (merging); client guard Wave 5
B16 Accessibility contract on every page: one polite live region per page, role="status" notices, aria-expanded on expandable rows, labelled focusable scroll regions, progressbar aria-valuetext, reduced-motion static indeterminate track D16 Not started (each UI wave proves its own surface)
B17 Security/product approval of the permission matrix is recorded before the flag is enabled in any environment (exposure gate; server proof from PR #3053 is evidence, not the approval) README plan gate Not started — blocks C7.4, never a wave PR
B14 Theme tokens: --syrf-warning-container, --syrf-warning-accent, --syrf-info-container exist and are the only new tokens; no existing --syrf-* redefined. Feature components consume only emitted --mat-sys-* / --syrf-* tokens per design-token-mapping.md: no hard-coded hex colours, no Bootstrap or private Material variables, no new fonts, and no breakpoint system competing with the shared responsive contract design-decision.md; design-token-mapping.md Met for the tokens (PR #3059 merged 945fa86a4); consumption restrictions checked per UI wave
B15 Feature exposure gated by generated default-off studyManagementProcessing flag with runtime-catalog entry plan Wave ⅖ In progress (PR #3066)

C. Per-wave acceptance

Wave 1A / 1B — contract truth (PRs #3052, #3053)

ID Criterion Status
C1.1 DateTimeLastModified mapped from domain; both Bulk Update timestamps nullable, never year one Met — PR #3052 (merged bf9d2f3a4), tests DateTimeLastModified_IsMappedFromTheDomainLastModified, NoProjectedTimestampIsEverAYearOneDate
C1.2 Validated count distinct from parsed count Met — PR #3052
C1.3 resultSummary matched/modified/unmatched/errorRows; null = not available Met — PR #3052
C1.4 Upload-failure transition endpoint + derived uploadWindowExpired Met — PR #3052
C1.5 Monotonic stateRevision on Bulk Update, Search Import, RoB, matching Bulk PDF Met for Bulk Update — PR #3052 (merged bf9d2f3a4); In progress (PR #3053) for Search Import/RoB
C1.6 Per-family server view gates on ProjectDetails and SignalR; absent family for unauthorised viewer In progress (PR #3053) — permission matrix tests in that PR
C1.7 Initiators projected only for visible families; Guid.Empty dropped In progress (PR #3053)

Wave 2 — adapters, reducers, flag (no visible change)

ID Criterion Status
C2.1 Reducers reject lower/equal revisions; legacy 0 seeds but cannot overwrite versioned rows Not started (Wave 2A)
C2.2 Snapshot membership reconciled by project audit.version; omitted-job race test proves a late HTTP payload cannot delete a newer SignalR-created row Not started (Wave 2A)
C2.3 ProjectDetails per-family id arrays reconciled the same way Not started (Wave 2A)
C2.4 Pure adapters for Search Import, Bulk Study Update, Bulk PDF: every enum value, unknown branch, invalid dates → null, no invented data, warnings separate from errors Not started (Wave 2B)
C2.5 Chronological sort transport-order independent; stable kind:id key Not started (Wave 2B)
C2.6 Shared presenter + chip component per B5–B9 Not started (Wave 2B)
C2.7 studyManagementProcessing flag generated, default false, catalog entry + parity test In progress (PR #3066)

Wave 3 — Library vertical slice

ID Criterion Status
C3.1 Bulk study update is a visible page action beside Add systematic search (not in the table overflow menu) Not started
C3.2 Action visibility by permission; upload success/failure; refresh and re-entry show the current/latest update truthfully Not started
C3.3 Contextual summary is the shared status strip; "View in Processing" only when the route exists and the flag is on (no dead link) Not started
C3.4 Year-one regression test; initiator availability; error disclosure; empty/summary states Not started
C3.5 Keyboard dialog focus; throttled live announcements; 320px card layout; 200–400% zoom; no page-level overflow; ≥44px touch targets below 600px; forced-colors CSS block (D17 assigns it to Wave 3) Not started
C3.6 PR #2990 closed as superseded with a link to the replacement Not started

Wave 4 — Searches consistency

ID Criterion Status
C4.1 Search Import and Bulk PDF use the shared status/time/error language and contextual summaries with gated Processing links Not started
C4.2 Bulk PDF flag/permission/capacity/cancel/retry/report behaviour unchanged (regression suite) Not started
C4.3 Unknown-status fallback reconciled with Bulk PDF (O5) Not started
C4.4 Warning/error disclosure, per-search navigation, aggregate reconnect, keyboard/card/table semantics, zoom, focus-preserving live updates Not started
C4.5 Risk of Bias rows/summary Out of scope (feature on ice, 2026-09-02)

Wave 5 — Processing destination

ID Criterion Status
C5.1 processing and canonical searches routes; the legacy admin/systematic-searches URL keeps working via a compatibility redirect with a routing test; Study management navigation group; flag-off direct URL and navigation absence Not started
C5.2 Guard and visibility by PermissionReport capabilities for every permission combination Not started
C5.3 Default filter, reset, result count; 250-row fixture filter/sort/Show more Not started
C5.4 Deep-link reveal of a completed referenced operation with notice, Clear and focus Not started
C5.5 Grouped list, deferred resort while expanded, bounded error lists, row detail with raw enum + value, full UTC timestamps (created/updated/completed, "Not available" when null) alongside any relative time, initiator, and copyable job ID Not started
C5.6 One loading boundary; empty state reads "none visible to you" with links to Library and Searches; load failure states "Your operations are unaffected" with a Retry action; reconnecting keeps rows visible and marked stale; stable focus during SignalR updates (D15, D12) Not started
C5.7 Mobile/zoom reflow; screen-reader names and result-count announcement; colour-independent meaning; forced-colors block; app-shell skip link Not started
C5.8 Processing describes itself as the history the server retains (Bulk PDF keeps 20 per search / 100 per project; other families per their retention) and never implies nothing was pruned; payload size/retention recorded with the 250-row fixture; paged history API follow-up created if needed Not started

Wave 7 — final consistency and release audit

ID Criterion Status
C7.1 Audit of statuses, progress units, errors, names, timestamps, permissions, entry points, routes, breakpoints, tokens, announcements, telemetry, support identifiers, user-guide copy Not started
C7.2 Controlled staging proof captured per the plan's validation strategy for each UI wave Not started
C7.3 User guide updated for the three destinations Not started
C7.4 Flag enabled per environment by explicit release decision (never by a wave PR), after B17 is recorded Not started

D. Deferred follow-ups (owned, not forgotten)

Item Owner / trigger
Migrate the Bulk PDF entity reducer onto the shared revision helper introduced by Wave 2A Post-Wave 2 cleanup PR
O1 — decided: Expired Bulk PDF jobs group under Needs attention (design-decision.md); implementation lands in Wave 2B/5 Closed decision; implementation only
O2 — Risk of Bias retry Out of scope while RoB is on ice
O3 — decided: missing initiator renders "Not available", column kept (design-decision.md) Closed decision; implementation only
O4 — old RoB "hide completed" toggle Out of scope while RoB is on ice
O5 — unknown-status fallback alignment with Bulk PDF Wave 4
Background sweep for long-expired UploadingToS3 Bulk Update jobs Separate backend PR (PR #3052 follow-up)
CLAUDE.md pointer to this feature and the delivery rule above After PR #2900 releases the file (single-writer rule)
Paged history API Only if the 250-row measurement in Wave 5 shows the aggregate is not acceptable
SearchController.CalculateRob authorization gap (PR #3060, drafted, CI green) Parked by product 2026-09-02 while RoB is on ice. Note: robToolEnabled=false only hides the UI; the endpoint stays callable by any authenticated user. Product owner to decide whether to merge #3060 as a security fix regardless of the UI freeze