Skip to content

Target architecture and shared contracts

Temporary planning document; planning only. This page defines the shared contracts that let the lanes in the integrated plan work in parallel. Each contract has an owner, consumers, a freeze gate and conformance tests. Field lists are logical and illustrative; physical storage is decided by ADR at the engine freeze F1a (E15).

Revised after round-2 review (3 October 2026). The contracts now summarise the round-2 companion documents and defer to them for mechanics: the versioning model (compatibility, options, publication, sessions, and reconciliation under versioning), the consistency model (transactions, idempotency, ownership markers, fences, durable effects, ordering and restore), the revised domain model (aggregates, naming, events and commands), programme integration (allocation, batches, eligibility, tracking, statistics and notifications), the UX strategy and methodology coverage. The single engine freeze F1 is split into F1a (engine contracts), F1b (catalogue and export disclosure) and F1c (IA, copy, AF2 seams and the Dockview amendment), as the delivery operating model §3.3 sets out. Two contracts are new: C18 (concurrency, transactions and idempotency) and C19 (durable effects and events). Decisions still pending with Chris cite their Batch D question IDs (D1-xx to D4-xx).

The aggregates these contracts imply (new ones, changes to existing ones, transaction boundaries and the release that introduces each) are brought together in the domain model.

Labels: a rule followed by an owner decision ID is that decision (OWNER, or recovered where the register says so). Rules marked PROPOSAL are recommendations from earlier designs or from this plan. Baselines are CODE-MAIN unless marked otherwise. Engineering items (E1–E35, and E36 onwards from the round-2 documents) are in open questions §2.

1. Design stance

  1. One shared annotation infrastructure, explicit domain capabilities. Ordinary answers, profile-owned screening decisions and, later, classification assertions and quantitative observations share identity, revisions, context, provenance, commands and history. They keep distinct validators, authority and derived results. The research verdict rejects a generic graph that infers scientific meaning (classification research §8).
  2. Forms own evidence; stages own workflow. A form version defines requirements and target. A stage settings version binds form/profile versions and defines steps, routing and policies. Stages never copy evidence or targets (SF1/SF2, PV2).
  3. Immutable history, explicit transitions. Autosave is draft. Save and Complete create immutable versions. Publication, correction, Fix, withdrawal, release and approval are explicit commands with receipts (SL1–SL3, SF5, FV1–FV4, RA4, LC1).
  4. Extend existing machinery rather than clone it. Reuse FEAT-024 projections (its receipts stay statistics-protocol receipts; C18 owns command idempotency), allocation buckets, presence/claims, progressive batches, ReviewEligibilityPolicy, AF2, the question designer, import/template paths, the notification stack, the authorization programme's evaluator and audit, and existing grants (OPS1 and the 3 October notification update).
  5. The server is authoritative. Every read and command checks scope, permission, admission and blinding. UI hiding is never a control.
  6. Fail closed on unsupported scope. A project, form or context the new path can't represent is rejected before editing. Ownership is never split between legacy and canonical writers, and ownership is data, not configuration (C16).

2. Contract catalogue

ID Contract Owner lane Main consumers Freeze gate
C1 Canonical annotation identity, revisions, commands and Study coupling L1 Engine L2–L6, L9–L12, L15 F1a
C2 Answer context identity and sharing L1 Engine L2, L5, L6, L9 F1a
C3 Provenance, exposure and history capture L1 Engine L5, L6, L11, L12 F1a
C4 Question, form and profile definitions and versioning L2 Definitions L1, L3–L6, L9, L10, L13 F1a (identity, compatibility and publication shape), F2 (publication), F5 (profiles)
C5 Form session lifecycle, drafts and contribution qualification L1 Engine L4–L7, L11 F1a
C6 Workflow binding, steps and admission L4 Workflow L5–L7, L12 F3
C7 Operational projections and claims (statistics, allocation, presence, batches) L7 Operations, with existing owners L4–L6 F1a (identity, claim contract v2), F3 (routing), F-A (allocation)
C8 Version usage evidence and protected publish boundary L7 + L2 L2, L3 F2 (forms), F5 (profiles)
C9 Reconciliation task, gold snapshots, assignments and queries L6 Reconciliation L5, L11, L12, L14 F4
C10 Capabilities, groups, delegation and disclosure L8 Permissions All F1b (catalogue, export disclosure), per feature
C11 History, export and manifests L11 History L12, L15 F1b (versions, previous-version exports), F6a (as-of); HLC stamps freeze with C18 at F1a
C12 PRISMA units, authority and report manifest L12 PRISMA L3, L11, L15 F3 (write-shaping parts), F-P (identification), F6b (reports)
C13 Classification, populations and inference L9 Classification L5, L6, L10 F-C (population key and EntityTypeId at F1a)
C14 Outcome schemas, measures and observations L10 Outcomes L5, L6, L11, L15 F-O
C15 Notification capture contract (C15 v2) through the existing notification stack Notification programme; L14 writes the specification L2, L4, L6, L12 F1b (contract); kinds per feature
C16 Compatibility floor, admission and reader/writer compatibility L0 Integration All F1a
C17 Information architecture, coexistence, overview and settings DTOs L16 Admin UX L2–L8, L13 F1c (IA, copy, AF2 extension points, Dockview layout amendment), F3 (DTOs, coexistence)
C18 Concurrency, transactions and idempotency L1 Engine with L0 All writing lanes F1a
C19 Durable effects and events L1 Engine with L14 L2, L4, L6, L12, L14, L15 F1a

3. Contracts

C1 — Canonical annotation identity, revisions, commands and Study coupling

Implements: SF1–SF3, SL1–SL3, PV1, DP3/DP4 (screening as a specialised kind), GS1 inputs. Current baseline (verified at 78c6d097d, unchanged at 2949ca3a7; the Study read-surface bullet re-read at eb93caffa; see the inventory):

  • Annotation subclasses (bool, int, decimal, string, plus arrays) are mutable and embedded in Study, with client-supplied stable IDs and a StageId that is only a provenance stamp.
  • A save removes all of the caller's answers to the stage form's questions, whatever stage they came from, and adds the submitted ones (ExtractionInfo.cs:183-194). A cross-stage save therefore changes what an older session exposes.
  • Screening is a separate mutable entity keyed by project + screener, with no profile and no history.
  • Writes go through SubmitAnnotationSessionService (three CAS retries, FEAT-024 source-operation receipts bound to the Study version) and must honour bulk-update study locks (Study.cs:242).
  • Study's legacy read surfaces are computed getters whose results are persisted: ExtractionInfo.SessionTallies, the SessionTally sums and ScreeningInfo's inclusion members are recomputed from embedded data on every whole-Study save, and stored values are ignored on read (StudyRepository.cs:3176-3228, ExtractionInfo.cs:31-81). ScreeningInfo, ExtractionInfo and SessionTally have no extra-element capture. Entity subclasses capture unknown elements through [BsonExtraElements] (Entity.cs:21-22), and FEAT-024's pending classes both ignore and capture them (StudyPendingStatistics.cs:14-19). C16 and the consistency model §3 address both.

Detailed analysis: screening research §1.

Logical model (illustrative):

Element Fields / rules
Annotation head annotationId (server-minted, or client-proposed and validated; legacy IDs preserved through the adoption manifest and LegacyIdAlias), kind (OrdinaryAnswer, ScreeningDecision, decision-owned answer; later ClassificationAssertion, Observation), contextKey (C2), definitionOwner (project, or the profile for eligibility answers; DD-26), author or authority scope, currentRevisionId. One head per context key (C2), so per author or authority scope and compatibility class.
Revision revisionId, annotationId, immutable typed payload, questionVersionRef, parentRevisionRef and owningParent edges (answer-tree ownership; distinct from definitionOwner), provenance (C3, including authorship), recordedAt, its per-aggregate sequence and HLC stamp (C11, C18), commandId
Command commandId (minted per user intent and reused for every retry) and a request digest; the bases the command observed (session version, draft etag, task input etag, query target; CR-5); actor, realActorId and onBehalfOf; admission evidence (C6). Its command-bearing record is its receipt (C18)
Kind policies Validators and authority per kind: one effective screening decision per reviewer/study/profile; decision-owned reasons; derived decision confirmed only on explicit submit (DP3)
Study canonical summary Study.CanonicalSummary, a top-level sub-document keyed by form and profile: per form, per-reviewer members {state: placeHeld, savedIncomplete, completed or withdrawn; standing: qualifying, needsUpdating, pinnedOlderCounted, pinnedOlderNotCounted or notApplicable; versionSeq; claimActivities; admittingRegimeId; routeStageId}, where draft_only is never written to Study (it is read from pmFormSession and pmSessionDraft, and appears on Study only as placeHeld when a Study-writing command recorded it); per profile, the current screening outcome summary (FEAT-011's screeningOutcomes[], one array); a per-bound-stage projection (tallies) for legacy readers; readiness flags; and the definition-version vector it was evaluated under. Written in every study-scoped canonical transaction and by projection-rewrite operations; legacy getters and predicates merge it from R0 (E20, E48; consistency model §3.3). Readers (pool filters, capacity guards, readiness, statistics, exports) reach the membership facts through IReviewMembershipFacts. #3944's candidate lookup is not a reader: conversations refuse canonical scopes until R4a. A coexistence adapter, retired at R7 with the readers in E20's inventory, and not the legacy computed fields
Merge and split A dedup merge is an alias that never re-keys immutable records: Study.mergedInto on the secondary plus a StudyAlias entry on the primary. Reads, candidate selection, statistics and PRISMA resolve aliases (AliasResolutionPolicy). A reviewer who reviewed both is counted once: the current session is chosen and the other superseded with provenance. Merges and splits are ADR-020-style operations that write both Study documents and refuse busy studies; a split removes the alias and re-derives (E33, amendments D and L; presentation D2-12). PROPOSAL

Invariants:

  • A revision is never edited. A retry with the same commandId returns the original result from the command ledger; a different request digest under the same ID is refused (CommandDigestMismatch); an indeterminate commit returns OutcomeUnknown, and the client retries the same ID. A stale base is rejected with a typed conflict that keeps the draft. Kind validators can't bypass common checks (research A20, A23).
  • Command ledger (E50, replacing E35): each canonical command writes one command-bearing record with a unique (ProjectId, CommandId), the result IDs and the request digest. FEAT-024 source-operation receipts stay statistics-protocol receipts; FEAT-024 reuses the CommandId as its OperationId, for correlation only (C18).
  • Study coupling (E20, CR-1): every canonical command that changes evidence, gold, outcomes, capacity claims or any fact in the canonical summary writes the Study document in the same transaction: an Audit.Version compare-and-set, the summary and the study clock. It writes Study only through FEAT-024's source-write seam, so pending entries or intents follow the project's statistics path; the engine never writes statistics itself. No per-project or per-form document is written by interactive commands (CR-2).
  • Real actor: every command, receipt and revision records the real actor and the effective author. Support edit-mode writes are flagged and excluded from independence statistics.
  • Limits: an explicit ceiling in pins and bytes per commit applies until large immutable submissions are proven (E28, D2-16).

Conformance tests: the screening research's acceptance cases A1, A3, A7, A8, A11, A13, A14 and A20–A23, and C18-T01 to C18-T04 (the DC review's AC-DC-01 to 04). Answer and ScreeningDecision pass the same suite through one logical repository. A canonical commit racing a bulk-update lock conflicts. Legacy readers see correct tallies through R0's floor (AC-R0-09). A candidate child revision never attaches to a reconciled parent (C1-T09; DD-26). A merge leaves every secondary-study record unchanged and the primary counts each reviewer once. Test IDs: C1-T01 to C1-T19 (acceptance criteria §7.5).

C2 — Answer context identity and sharing

Implements: SF3, SF5, DP4, SF6 (flags). Same reviewer + same study + same question identity + same entity or branch context + same compatibility class = the same shared answer. QuestionId alone never merges repeated entities or branches. Eligibility answers are scoped to their profile (definitionOwner), so two profiles never share answers even with identical wording. The rulebook is the versioning model §3.5, §6.

Logical key (frozen at F1a), the AnswerContextKey value object: {projectId, studyId, authorScope (candidate reviewer, reconciled, imported), definitionOwner (project | profile:<id>), questionRef {questionId, systemQuestionVersion?}, classSeq, entityPath[], populationId}. classSeq is the compatibility class of the pinned question version (versioning model §3.5): one head exists per context and class; a revision under an incompatible version creates a new head whose lineage SF5 shows read-only. entityPath[] elements are typed instance identities from the root: instance(labelHeadId) for reviewer-created entity units, instance IDs for repeated-answer branches, and optionBranch(optionId) for option-keyed multi-select branches (PH-16). Every answer carries a population from its first write: the default whole-study population ID is derived deterministically from the study ID, so enabling classification never re-keys answers and needs no document (DD-24). Decision heads (kind ScreeningDecision) omit questionRef, classSeq and entityPath; decision-owned answers add the owning decision's head ID. The key is stored as an ordered value object beside contextKeyHash (SHA-256 over a versioned canonical serialisation); unique indexes are on scalars only ({projectId, contextKeyHash}, plus partial unique indexes per kind), never on the array, because a unique index over entityPath[] would be multikey (VB-05).

Two ownership notions, named apart (DD-26): definitionOwner (part of the key: which requirement owner defines the question, DP4) and owningParent (a revision edge: a decision owns its reason revisions; an answer may own child answers). Ownership forbids cycles, cross-study edges, a reason owned by two decisions, and a candidate child attached to a reconciled parent.

Rules: show the reviewer's own prior answer and all ancestor answers across forms and stages in the matching context (SF5), including heads of earlier classes, read-only. Changing a shared answer creates a new revision on its head; every other session of the same reviewer pinning an older revision of that head is flagged "contains outdated annotations"; nothing adopts the new revision automatically; the flag alone never removes qualification (SF6). Two forms pinning incompatible versions of one question never flag each other (different heads). Legacy duplicates that conflict adopt as one head in state Conflicted with no current pointer until the owner's Fix or Save; it is shown with a conflict marker and excluded from prefill, agreement and autoUpdate. Entity instances: identity is the label head ID in the author's scope (client-proposed, server-validated, minted once); rename is a label revision; delete is a withdrawal commit on the label head and its descendants; duplicate mints new IDs with copiedFrom provenance; population membership is an instance attribute (versioning model §6.5).

Conformance tests: identical QuestionId under different entities stays separate; heads that differ only in the second entityPath element coexist and an exact duplicate is refused; ancestor lineage across two forms including an earlier class; a compatible added option shared across two forms with no flag; two forms on incompatible versions never flag each other; a v2-only option is never offered under v1; legacy duplicate conflict surfaced as a Conflicted head; two profiles stay separate; candidate and reconciled heads stay separate; a candidate child never attaches to a reconciled parent; unit delete flags the reviewer's other forms, rename keeps identity, duplicate mints new IDs; enabling classification changes no existing key. Test IDs: C2-T01 to C2-T08 (acceptance criteria §7.5).

C3 — Provenance, exposure and history capture

Implements: PV1, VS2, GS1 context, EX2 legacy coverage.

Record Content
Revision provenance Source stage, step, stage-settings version (the requirement-bearing part only, D2-05), question version (questionVersionRef), accepted-answer revision actually shown (if any), real actor and on-behalf-of, authorship (reviewer, reconciler, adjudicator, policyDerived, legacySnapshot, imported), command ID, HLC stamp, recordedAt, optional observedAt (evidence only, never ordering)
Session-version workflow context Stage/step route used, declared (pinned) form version, accepted snapshot available, the full pin map (head → revision) of every answer in the session at that version, the resolved question set, exposure state, command ID, HLC stamp
Exposure state Three states, failing safe: no gold available (derived on the server from "accepted snapshot available"); exposure recorded (an accepted revision rendered into view, deduplicated per session version and revision; and the kind "questioned in reconciliation", NS-06, which makes later versions of that reviewer's session on that study × form informed); available but unrecorded, treated as informed or unknown, never as independent. Visibility permission alone is not exposure. Exposures travel in the draft, Save and Complete payloads and are recorded in the commit; late events are accepted keyed by draft etag and base version; the independent, informed or unknown class is derived on read, so a late event downgrades it. Exposure never travels over the presence hub (RT-26).
Observation-basis markers (PROPOSAL, frozen at F1a) Initial independent submission: the first effective Complete (form) or decision (screening) by a reviewer for a (study, form) or (study, profile) context, made before any collective outcome, accepted answer or adjudicator output for that study was visible to that reviewer; derived at commit from the commit order and the visibility events; stored on the session version. Collective exposure at correction: for DP2 corrections, extra votes and the discussion route (D4-02), whether the collective outcome (Pending, Conflict, Included, Excluded, Unsure) or any reconciler or adjudicator output was visible to the actor, and through which route (availability, discussion, monitor); written on the correcting submission. Questioned in reconciliation: the exposure kind above, written by #3965 and looked up by session; consumed by R5c, C11 manifests and R6 adoption mapping. Calibration purpose: purpose = calibration records (D4-04) never vote, qualify or count. Conversations are audit record only, never agreement inputs except as these markers (D3-25).
Import/migration provenance Source system, legacy ID (kept through the append-only LegacyIdAlias table), mapping version; historyCoverage (observed-record-only, unknown-authoring, …); AuthoredUnder typed union on adopted revisions: Verified(v1) when the stored Annotation.Question wording equals the adopted v1 wording, otherwise Unknown (excluded from same-version agreement and exact-match prefill); migration time is never presented as original time. Imported authority (PROPOSAL): decisions and answers imported from a CSV column or another tool (FEAT-004, D4-14) carry authority = Imported with source system, import job, mapped investigator and independence ∈ {unknown, declared-independent (declaring admin, time)}; declared-independent records count toward sufficiency and appear in a labelled agreement view; unknown ones never count toward sufficiency or agreement; for PRISMA they are "screened in SyRF (imported record)" (amendment K rule 6); they never become gold or adjudicated outcomes automatically.
History capture from first admission Append-only capture of legacy-path writes for enrolled projects: legacy screening from R2a, pool entry from R3a, retrieval and lifecycle from P1 (E26, E54). Captured inside the aggregate: the legacy write appends a bounded entry to a top-level Study log in the same document write, and a leased worker moves entries to pmLegacyWriteLedger; overflow is a coverage gap (consistency model §12)

Conformance tests: a reused answer keeps its original authoring stage. An informed contribution counts for progress but is labelled informed in agreement statistics. A lost exposure report never turns informed work into independent work. Legacy records never gain fabricated versions. An adopted answer whose stored wording equals the v1 wording is Verified(v1); one whose wording differs is Unknown and is excluded from same-version agreement. A reviewer questioned in reconciliation who saves a new version is classified as informed. A DP2 correction after a visible conflict never changes the initial-observation agreement. An imported decision with independence = unknown never counts toward sufficiency. A calibration record never creates a pool-entry or screening event. Test IDs: C3-T01 to C3-T09 (acceptance criteria §7.5).

C4 — Question, form and profile definitions and versioning

Implements: FV1–FV4, VU1–VU3, recovered transition choices, DP4, DP5, RX1, TC1, SET1 (templates copy). Current baseline:

  • AnnotationQuestion is embedded in Project and edited in place. Its fields are at AnnotationQuestion.cs:107-150: target/parent, root, label, category (one of seven hard-coded values), type, control, multiplicity, conditional parents, filtered options, lookups, answer labels.
  • System questions are rebuilt from code on every read, and their structure depends on Project.SystemQuestionVersion: in v0 and v1 projects the outcome error-type question has a different parent and option filters (AnnotationQuestion.cs:559-592).
  • Placement rules apply to new questions only; parent and category can't change.
  • Deletion cascades to answers. Answered questions are locked in the new editor rather than versioned. The only version-like markers are the schema version and SystemQuestionVersion.

The QM v2 domain (#2572/#2573) is unmerged reference code harvested per Q-08; the versioning model's §12.6 says what to harvest and what to avoid. The rulebook for everything below is the versioning model; this contract summarises it.

Identity versus content. A question is identified by questionRef {questionId, systemQuestionVersion?} plus structural identity {definitionOwner, parent, entityTypeId, repeatable}; a structural change is a new question with lineage by reference. Data type and selection multiplicity are version content and a change to either is always incompatible (PROPOSAL, Batch D D2-03; FEAT-001's D38 is the alternative). Stable system entity-type IDs for the seven legacy categories plus cohort, outcome measure and experiment are minted at F1a; the category string is a display alias (DD-12).

Definition Identity Immutable version content (once committed; published through forms or profiles) Operational settings (audited, read live, no impact flow; D2-05)
Annotation question (project-owned) questionRef, definitionOwner = project, parent, entity type, repeatable Wording, description, help, control type, display labels, data type, selection multiplicity, validators, options {optionId, value, displayLabel?, description, parentFilter (option IDs), state}, conditions (option IDs), response modes and metadata fields, "Why it changed", "What reviewers need to do differently", compatibleWithPrevious with the system suggestion, the admin's confirmation, rationale and optional option mapping (Q-34) none
System question (systemGuid, SystemQuestionVersion); definitionOwner = system Stored as data in a global store, seeded idempotently from code, published by CAMARADES with the same compatibility declaration; never rebuilt per read; never keyed by code revision (D2-06) none
Profile eligibility question Same editor and types; definitionOwner = profile:<id> As above; copied from templates, never live-linked (DP4) none
Annotation form formId (project-owned) Requirement version (formId, seq): ordered question-version pins including all ancestors (one version per question), per-question requiredness and minimum instance counts, the validated applicability graph, the renderability result, entity types present, allowed outcome-schema versions from O1 Minimum target (SF2, SF4), optional capacity cap (D3-17), reconciliation compare settings, gold-completeness policy (Q-04), form guidance (A-15), bulk-approve flag (Q-11)
Screening profile profileId Criteria version (profileId, seq): eligibility question pins with ancestors, decision rules, must-agree supporting answers (RX1), requiredness, compatibleWithPrevious DP5 toggle, agreement and resolution routes (RX1), rationale settings (Q-32), Unsure/Maybe and discussion route if D4-01/D4-02 approve, reference to the project's PRISMA phase mapping (C12)
Template (templateId, seq) Copy source only; a copy is a new identity at seq 1 recording copiedFrom; a template change never changes a copy none

Compatibility (frozen at F1a; versioning model §3.5). compatibleWithPrevious(Q, n) is declared when version n is committed: the system suggests it from the diff (presentation, added options, renamed options keeping their ID, condition and filter changes, validator changes, added modes → compatible; retired or replaced options, removed modes or required metadata → incompatible; data type or multiplicity → incompatible, never loosened), the admin confirms, may tighten freely and may loosen only with a one-to-one option mapping and no free-text change (Q-34). The declaration is immutable once any revision pins version n (D2-02); before that a change recomputes classes and re-validates active publication policies. Classes: class(n) = class(n−1) ∪ {n} if compatible, else {n}; classSeq = the lowest version in the class; compatibility is an equivalence relation, which is FEAT-001's breaking transitivity restated. Per-answer validity is separate: an answer is valid under a version when the classes match, every selected option ID is active (or mapped), the response mode and metadata exist, and validators pass; applicability is evaluated separately by E23 and never counts as invalid. SF3 and SF5 share only within a class; AG3 compares only within a class and flags differing versions; FV3 qualification needs class match and validity; RE4 holds a question whose candidates span classes; RE5 prefills only within a class; gold whose class differs from the form's current pin is flagged for re-reconciliation.

Options. Every option has a stable optionId; canonical answers store option IDs; values and labels are display data resolved from the pinned version; conditions and parent filters reference option IDs and are validated against the pinned parent version (composition validity); renaming keeps the ID and is compatible; retiring is incompatible unless mapped.

Response modes, metadata and suppression (PH-06). Modes and metadata fields are version content; a payload is value XOR responseModeId plus validated metadata; descendants suppressed by an ancestor's answer or mode are preserved (state NotApplicable, derived per ancestor instance) and never tree-shaken on the canonical path; exports resolve suppression. "Not applicable" is an explicit mode; blank is not N/A (UA1).

Applicability (E23): one written specification of which questions apply (multi-option conditional parents by option ID, filtered options, branch context, ADR-011 hybrids mapped to option IDs), with shared fixtures run by both the .NET validator and AF2, based on FEAT-020's rules file (PH-07). The same evaluator drives server Complete validation, Needs updating (VU1), reconciliation validity (RE2) and UA1. Typed errors name the blocking question and context.

Composition and renderability. A requirement version is composable only if every pinned child's condition and parent filter resolves to an active option in the pinned parent version; the validated graph is stored in the version. AF2's structural guards run server-side at publication with shared fixtures; a version AF2 cannot render is refused (FormVersionNotRenderable); canonical routes render on AF2 through a VersionedAnnotationFormDataSource that loads the pinned versions by ID and never fall back to AF1 (VB-08).

Two-step publication. Committing a question version has no session impact and no prompt. Publishing a form or profile requirement version is the only impact point (FV2, Q-26). A new system question version reaches a project only when its admin publishes a form version that pins it (D2-06). Operational settings changes never publish anything.

Publication command (FV2/FV3/PS3; versioning model §8):

  • Input: the requirement version to publish; per question a change class derived from the diff with every prior version in use, added (required: countEarlierCompletes | requireAnswerBeforeCounting), removed (dropFromRequirement; answers stay in history; children of a removed parent become not applicable), changedCompatible (autoUpdate | requireReanswer | doNothing), changedIncompatible (requireReanswer | doNothing), mapped (autoUpdate applies a one-to-one Q-34 mapping | requireReanswer | doNothing); per category (completed, saved-incomplete, draft-only) whether the treatments apply, and for completed sessions left pinned whether they count toward the new requirement (FV3); the admin's rationale. autoUpdate is offered only within a class and satisfies only valid answers (SR-16); it writes no revision. The dialog follows FEAT-003's four steps (scope, compatible, incompatible, confirmation) with the system suggestion and per-question and per-category counts (U6).
  • Preconditions: usage evidence at the protected boundary (C8, Q-31; draft_only counted authoritatively from drafts by base form version, MS-03), the preview digest, and a recorded admin choice. One active publication per form (unique partial index; D2-11); a second publish is refused.
  • Effects are derived, never written (D2-01). Publication writes no session versions and no answer revisions. Session standing (satisfies, needs updating, pinned older counted or not counted) is derived by one evaluator from the latest explicit version, its full pin map, the current published version, the recorded policy generations and per-answer validity; canonical readers derive it on read. The only derived-revision writer is Q-34 option mapping, which writes one policyDerived revision per mapped head and operation, with provenance, excluded from SF5 outdated flags.
  • Two phases (consistency model §4, §7): phase 1 is O(1): fence the form, drain, re-check the preview digest, CAS the form head and write the policy record (generation 1) and the operation record in one short transaction. Phase 2 is an ADR-020-style operation that rewrites query-path projections by predicate until nothing matches, writes the Q-34 derived revisions, and captures notices once per recipient; admission and readiness for the form pause while it runs (D2-10). The impact manifest is an audit and preview snapshot built after commit from authoritative records, never an input to any effect.
  • Late Saves and Upgrade: a Save or Complete declaring a superseded version is accepted pinned to that version and never rebased; the policy applies to it by derivation. The reviewer's Upgrade (offered from the Needs-updating banner, implied by Fix when accepted, or taken on the next Save or Complete that declares the current version) pins the current version with unchanged revision pins. Under doNothing the reviewer may keep working on the old version indefinitely.
  • Rules: a missing reason gives a non-blocking warning (VU3). A missing required answer is never declared answered. A question is published once any of its versions is referenced by a published form or profile version (or adopted); thereafter it can be removed from forms by a new form version or retired (no new pins), but never deleted (QD1, Chris, 3 October). A committed but unreferenced version, a pending edit, and a question none of whose versions is published may be discarded with an audit entry. A published requirement version never changes; an unpublished one may (this is A-14's "until first use"). FV4: a later policy revision appends a generation to the same operation, CAS-ed on the generation; it never rewrites versions, work, drafts or the compatibility declaration. Notices are captured once per recipient per publication (C15).
  • Profiles: profile criteria versions publish through the same command, with treatment of decisions cast under prior versions as Q-26 decides (F5). Decision heads carry no class; the profile's class only derives a decision's standing under the recorded policy.

Designer pending edits use the same lease, etag and take-over model as reviewer drafts, so two admins editing one question get a typed conflict (PH-33).

Conformance tests: the versioning model's fixtures FX-VM-01 to FX-VM-17, FX-VM-23 to FX-VM-27, FX-VM-38, FX-VM-39 and FX-VM-42, plus: adding a question creates a new requirement version; sessions under v1 and v2 are both found when publishing v3; Needs updating stays visible and blocks Complete; two profiles importing one template stay independent after the template changes; a question hidden by a condition is never required; publishing a form with 10,000 sessions writes no session versions and phase-1 time is flat across session counts. Test IDs: C4-T01 to C4-T20 (acceptance criteria §7.5).

C5 — Form session lifecycle, drafts and contribution qualification

Implements: SL1–SL3, SF2, SF5/SF6, FV3, EW1 surplus, LC1 readiness input.

State (SL3, stored fact of the latest explicit version) Definition
draft_only Session exists (created on the first autosave) with a draft but no explicit version
saved_incomplete Latest explicit version is a Save
completed Latest explicit version is a validated Complete
withdrawn An append-only withdrawal version; the session no longer counts or qualifies; its revisions stay readable
Draft-changes flag A draft exists over the current explicit version (any state)
Derived on read (never stored as a fact; versioning model §7.4, §7.5) Definition
Per-answer state Current, OutdatedOwnAnswer, NeedsUpdatingVersion, NeedsUpdatingValue, NeedsAnswering, PinnedOlderVersion, NotApplicable, Conflicted, from the pinned revision, the head's current revision, the classes of the pinned and required versions, the recorded policy, validity and applicability
Requirement standing satisfies, needsUpdating, pinnedOlderCounted, pinnedOlderNotCounted, against the current published requirement version under the recorded policy generations
Qualifying contribution completed ∧ standing ∈ {satisfies, pinnedOlderCounted} ∧ eligible (D4-20). One per reviewer per study and form. A warning alone never removes it (SF6); a requireReanswer policy removes it by derivation with no version written (D2-01)
Flags hasOutdatedOwnAnswers, hasDraftChanges, informed (C3 exposure, including "questioned in reconciliation")

Identity (E27, DD-15): a FormSession is keyed (project, study, form, reviewer) with a deterministic ID and is created by upsert on the first autosave as a Study-free write; opening a study creates nothing. The reconciler's session is an entity of the reconciliation task with authorScope = reconciled and a current holder (C9), never a FormSession. Screening-only steps use the profile-owned session container (PROPOSAL at F3/F5, versioning model §7.1). Claims are keyed by form or profile identity, never by version, and the first explicit Save releases the reviewer's claim in the canonical transaction (C7, claim contract v2).

Session versions (E28): every explicit version pins the complete map (head → revision) of every answer in the session, the declared form version, the route and the resolved question set; storage may delta-encode, but the contract, previous-version exports, candidate pinning, gold pins and E28's ceiling (in pins, changed revisions and bytes per commit) are defined on the full map.

Drafts contract (E21; versioning model §7.6; mechanics in consistency model §4.4): drafts are stored outside Study. One draft record per session, pinned to a base explicit version and base form version, stored as patches, size-capped with E28, with a lease holder (a stable client tab ID shared with tracking's connection model but working with tracking off, RT-10), an etag and a per-holder write sequence; a duplicate write sequence is success; a stale autosave arriving after a newer explicit version is rejected and discarded by the client. A non-holder's edits are kept as a bounded conflict copy, so "keeps both" is literal; the second tab is read-only with "Take over editing" (D2-08). Save and Complete present the draft etag and consume the draft atomically. No TTL; removal only by audited discard (owner, admin after revocation, or the system with an audit record), which feeds LC1. No autosave trail beyond the current draft and its conflict copies (SL1 is satisfied without explicit versions). Drafts are indexed by base form version (the draft_only category, counted authoritatively at publication), are never visible to reconcilers or exports. A cross-form draft based on an older revision changed through another form gets a typed stale-base conflict at Save that shows both values and keeps the draft. Whether an autosaved draft holds the reviewer's place is Batch D D2-07 (recommended middle ground: held while the reviewer is active under today's idle and disconnect timers counting draft activity; released when they lapse with the draft kept; Complete still allowed as an extra contribution unless an optional capacity cap applies, D3-17). The earlier single mutable pendingAnswer model in annotation versioning is superseded.

Transitions: autosave (draft only; never a version, never Study); Save (new immutable incomplete version, becomes current, consumes the draft); Complete (validates every required applicable answer under the declared form version, new immutable completed version, becomes current); Fix (explicit, from an outdated flag: a current incomplete version pinned to the session's resolved version; opens that session's form; SF5); Upgrade (a new incomplete version pinned to the current published form version with the same revision pins; nothing is rebased; offered from the Needs-updating banner, implied by Fix when the reviewer accepts it, or taken on the next Save or Complete that declares the current version); Withdraw (append-only); versioned clear (withdrawal revisions for every head plus a Save; replaces "Remove all annotations"). A Save, Complete or Fix declares a form version that must be the session's pinned version or the current published version; any other value is a typed StaleBase. A Save declaring a superseded version after a publication is accepted pinned to it and never rebased; the recorded policy applies by derivation. There is no publication-written transition (the former "policy transition" is deleted; D2-01). Revisions pinned by gold or reconciliation are never deleted. Whether reviewers may withdraw canonical sessions themselves, or only through an admin, is settled in the C5 ADR; the hard delete reviewers have today never applies to canonical sessions. Presentation state, such as entity order, lives in a separate per-session record outside versions and never affects qualification. History never counts as a session or reviewer.

Downstream effects of an incomplete Save (proposal from the ledger, Q-27): recompute shared sufficiency and dependent-step applicability; show affected steps as needing reassessment; preserve dependent work; never change gold or screening decisions as a side effect.

Conformance tests: Save after Complete removes qualification. Autosave alone does not. Complete restores one contribution, not two. The same session is reached from two stages. An order-only change never removes Complete. SF6: a warning alone keeps Complete; a requireReanswer policy removes qualification by derivation with no version written; doNothing with and without counting yields the two pinned-older standings. A withdrawn session neither counts nor loses its history. A late Save pinned to the declared version; Upgrade keeps pins; a previous-version export equals the full pinned map; two tabs' first autosaves create one session; the eight per-answer states render and are explained (U13); the draft fixtures FX-VM-32 and FX-VM-33. Test IDs: C5-T01 to C5-T12 (acceptance criteria §7.5).

C6 — Workflow binding, steps and admission

Implements: PV2, DP6, DP7, EW1, VS1 (setting), BL1 (setting), RX2/LC1, RA1 expiry default, DP2 (admission recheck), RC6 (skip semantics). Current baseline: one stage owns its mode, questions, target, allocation and security; there is no multi-step domain. ReviewEligibilityPolicy and the eligibility programme (S1–S7) own action-specific admission (eligibility policy; inventory).

Element Content
Stage settings version Bound form versions and profile versions; steps; incoming cross-stage route policy (Collective Include required by default, own Include sufficient as advanced; collective-Exclude veto always); within-stage policy (own Include; strict collective only if Q-01 approves); EW1 default; VS1 default; BL1 setting; lifecycle mode; allocation/batch settings; assignment expiry default (defaults per Q-30)
Review step Screening profile reference and/or form reference; display order (never a dependency by itself); dependency edges with AND/OR groups (cycles rejected); compulsory flag; handoff allowed; collective satisfaction for unvoted reviewers (recorded as collectively satisfied, no vote); terminal-on-Exclude scope; extra-vote admission after sufficiency (Allow/Stop, from eligibility D1/D2); per-step overrides (EW1 advanced, VS1)
Admission decision Evaluated identically for selection, reservation, direct access and submit. Returns allow/deny with reasons and the policy/version evidence used. Never creates a vote, submission or outcome. Extends ReviewEligibilityPolicy and reads per-reviewer facts only through IReviewMembershipFacts (E64). The eligibility decisions map as Q-24 sets out (D3b, D1/D2, D4, D6; D5 and D7 unchanged); D8 maps as D3-09 recommends (programme integration §5.2). Never writes a per-project document per admission: it checks the bound stage-settings version in the Study write filter, and a settings change publishes a new version under the engine's scoped fence (DC-05).
Shared-evidence policies When stages reaching one task or session differ: BL1 uses the most restrictive bound stage; EW1 and VS1 are evaluated per route (Q-28, PROPOSAL)
Route status DTO Gate status, form sufficiency and work status are separate fields; a lock is never labelled "Excluded"

Evaluation table (DP6/DP7 plus assumption A-06):

Personal decision Collective state Within stage (default) Across stages (default: collective) Across stages (advanced: own Include)
Include Pending/Conflict Allowed Wait Allowed
Include Included Allowed Allowed Allowed
Any Excluded Blocked Blocked Blocked
Exclude Any not Excluded Blocked for this reviewer Blocked for this reviewer (A-06) Blocked for this reviewer
None Included Allowed only with collective satisfaction Allowed (A-06), no vote invented Allowed: the advanced option adds the own-Include path and keeps the collective path (A-18)
None Pending/Conflict Screening prerequisite to do Wait Prerequisite to do

The within-stage column follows the confirmed handoff table. The cross-stage columns apply DP7. The rows for personal Exclude and for an unvoted reviewer are interpretations A-06 and A-18, put to Chris as Q-15.

Permissions, allocation, batches, capacity, lifecycle and other configured dependencies apply to every allowed row. Saved-work completion after collective exclusion follows EW1, never this table.

Programme notes (PROPOSAL):

  • reviewEligibilityPolicy and proportionalStudyAllocation are delivered to API and PM with a cross-host agreement check before either is enabled, and one evaluation per request is passed into admission (E75).
  • The generated eligibility truth table is C6's conformance format; it gains step and route columns (PH-04).
  • A requestedReview claim admits exactly the requested reviewer past bucket, pool and capacity filters (E66, D3-13c).
  • Proportional allocation is refused on canonical stages until AL1 (D3-13a).
  • Batch access is scheduling access, never permission; X-ELIG precedes batch activation.
  • A dependent form's claim is taken at Include, not while the reviewer is still screening; a refusal shows "Enough reviewers" for that step and keeps the screening decision (D3-19).

Conformance tests: the access-policy fixtures, the research A12–A16, FX-PRISMA-03a (C6-T16), "PRISMA stays Excluded with completed extraction" (PR1), and every eligibility truth-table row answering identically from CanonicalSummary and from embedded data (E64). Test IDs: C6-T01 to C6-T22 (acceptance criteria §7.5).

C7 — Operational projections and claims

Implements: OPS1, SF2 (counting once), SL3 (moves), SF4 (more than the target), RA1 (active-work tracking for pool work, delivered by the task editor claim), RA5, E18, E19, E20. Owners: FEAT-024 statistics, allocation, active reviewer tracking, eligibility and progressive batches keep their programmes. This contract is the amendment list they review. It is not a takeover. Details, timing and PR slicing: programme integration.

Boundary Required amendment (PROPOSAL unless a decision ID is given)
Study membership projection (E20) Per form and per profile, per reviewer: the member state {state: placeHeld, savedIncomplete, completed or withdrawn; standing: qualifying, needsUpdating, pinnedOlderCounted, pinnedOlderNotCounted or notApplicable} (draft_only is never written to Study; it is read from pmFormSession and pmSessionDraft, and appears on Study only as placeHeld when a Study-writing command recorded it), claim kinds held, admitting allocation regime, and the reviewer's current decision per profile; derived tallies per bound stage for legacy readers. Bounded by the SF4 rule (every qualifying candidate), not by the target. Carried by Study.CanonicalSummary (C1; shape and write rules in the consistency model §3.3), written in every study-scoped canonical transaction and by projection-rewrite operations, and read only through IReviewMembershipFacts (E64)
Stage and membership annotation tallies Derive form-unique current explicit contributions once per reviewer; stage views project their bound form; never sum duplicate stage projections. R0's floor merges the summary's per-stage projection into SessionTallies and the claim pipeline, so legacy readers stay correct (E48)
Statistics protocols and checkpoints The engine never writes statistics or pending entries; it writes Study only through FEAT-024's source-write seam (a projection-only shape). Three compatibility mechanisms stay separate (C8): extra-element capture (R0), the fold protocol window (one bump, protocol 5, after gate (b); intents until then) and per-family catalogue versions with the configuration digest. Targets, thresholds and bindings are digest inputs, so each change has a reconciliation plan; replacing the fixed two-reviewer classification with targets is a FEAT-024-owned change at all three sites (E71, X-STATS-c). Enrolled projects never run transactional point mode. Old stage-only checkpoints are never reinterpreted
Claims (claim contract v2, frozen at F1a) {kind, scopeId, routeStage, routeStep, reservedAt, allocationRegimeId, leaseExpiry, holders} with kind ∈ formSlot, profileSlot, requestedReview, taskEditor, queryEditor; unique per (study, kind, scope, reviewer); keyed by form or profile identity, never by version; released when the last page using it ends. Capacity claims live on Study, inside the atomic guard; editor claims live on their own aggregates and work with tracking off. The first explicit Save releases the slot claim inside the canonical transaction; release paths read drafts in the same snapshot (D2-07); a lease expiry and a backstop sweep catch orphans (E57). New hub methods rather than new parameters until MinUiVersion moves; additive DTOs; new command contracts with old handlers kept for at least the suspension grace plus the idle timeout; presence index migrated by create, read both, drop. Legacy v0/v1 pages stay for legacy scopes. One reservation migration, with the eligibility programme's S4-B (AP-07, E68)
Capacity and target The form target is a minimum (SF4). An optional capacity cap (stage or route policy) is separate: off by default; when on it defaults to the form target, never limits requested extra reviews and never evicts existing work (D3-17). Places are held by a draft-backed claim while the reviewer is active (D2-07), and by saved-incomplete, completed and Needs-updating sessions; withdrawal frees the place. For a form bound to stages with different settings, the most restrictive bound stage sets the cap and the idle timeout, the stage in use sets the in-progress limit counting a shared session once, and the form is tracked if any bound stage is (D3-18). Screening capacity follows the profile's collective rule (D6)
Requested review (RA5) The AdditionalReviewRequest command writes a requestedReview claim on Study: single use, expiring, audited. It admits exactly that reviewer past bucket membership, the at-target pool filter and the capacity guard; it never changes the target; the resulting session records it as provenance and is counted outside allocation progress (E66, D3-13c)
Proportional allocation Refused on every canonical stage until AL1 (D3-13a): shares cannot be configured on a canonical stage, and R0 refuses a stage whose regime is enabled. AL1, after allocation Phase 2 and X-AUTH-RESOLVER and before Phase 3: one plan per form with equality checks, regime schema v2 (form binding, target source) with a floor one release ahead, read APIs on the membership projection, the D8 slot rule on form-keyed claims (E65). Canonical stage settings reference the regime by ID
Progressive batches (#3939) Completion through IStudyObligationEvidence with legacy and canonical providers; membership over pool-entry events with late cohorts appended; shared opening and personal grant are compare-and-swap transitions that write pool-entry events and durable intents; status reads never mutate; a performance gate precedes any enablement; X-ELIG first (E67). Pool entry is the first release to anyone, with shared openings and personal grants recorded separately (D3-13e; amendment A). Canonical stage settings reference the plan by ID
Normal reconciliation Reconciliation reserves nothing today (StageReviewService.cs:153), and tracking excludes it at every layer, so enabling tracking gives RA1 no protection. R4a's task editor claim (taskEditor: compare-and-set plus lease; atomic "Start reconciling"; assignment and release hooks) delivers RA1's editor exclusion in every environment, with tracking on or off (X-RECLAIM). Explicit assignment is a separate record (C9)
Tracking mode and production claims Claims, capacity guards and typed admission exist only when tracking is effective, which is off in every deployed environment. Production claims need X-CLAIMS: the route D3-16 chooses; API and PM switched together statically; load, failover and orphan backstop; E2E in both tracking modes (E69). Realtime presence is a disclosure channel under C10 (D3-20)
FEAT-024 reservation derivations The reservation fold kinds move to the claim contract's key through a protocol bump (IntroducedAt), batched into protocol 5 after gate (b)

Conformance tests: truth-table parity between the embedded and summary providers; two tabs through stages A and B hold one claim, closing either keeps it, closing both releases it once (AC-R2b-03); twelve concurrent first joins through two stages create one claim (AC-R2b-09); a stage-keyed claim on a canonical form is refused or translated (AC-R0-02); under allocation at target the requested reviewer is admitted and nobody else is (AC-R4a-38); N reconcilers pressing "Start reconciling" never share a task (AC-R4a-37). Test IDs: C7-T01 to C7-T12 (acceptance criteria §7.5).

C8 — Version usage evidence and protected publish boundary

Implements: PS1–PS3, FV2. Owner: FEAT-024 owner with L2.

  • Usage families (MS-02, MS-03): new FEAT-024 families over canonical sources, delivered by a FEAT-024 technical-plan amendment ("canonical sources"): FormVersionUsage (sessions by category, deduplicated across stages, explicit versions only) and QuestionVersionAnswers (counted from revisions) at F2; profile-version decisions, and profile-grain screening if D3-10c chooses families, at F5. Their scope kinds and key components (FormId, FormVersionId, ProfileId, ProfileVersionId) are authoritative over the canonical collections in the same pinned snapshot. Their catalogue and source versions are independent of ProjectScreening's constants (#3506 first). A new-family onboarding contract with a rolling-deploy test precedes F2 (E72). draft_only counts are authoritative counts from the draft collection inside the publish fence, never a materialised row.
  • Protected boundary: publication raises a fence on the form head, drains for at least the server's transaction lifetime plus its expired-transaction sweep plus a margin, and reads the usage families at the fence in a pinned snapshot (currentness below). Every dependency-relevant write (Save, Complete, Fix, Upgrade, imports, binding changes, session creation) reads the form head in its snapshot, so it either commits before the drain ends or is refused and retried after release (consistency model §7.3). There is no high-water mark and no per-project sequence. Definition moves that change counters without a Study write (form target, binding, publication policy) admit FEAT-024's definition-rewrite fence in the phase-1 transaction; that fence makes statistics bundles answer a typed 503 and serialises no reviewer writes. The reviewer "pause" is the engine's scoped write fence, not a FEAT-024 facility.
  • Currentness (MS-04, D3-10a): "current" for PS2/PS3 is a FEAT-024 read at the fence whose result is Materialised-Fresh or pinned-Authoritative, with the read's identity (projection revision, source revision, digest) recorded in the frozen manifest. FEAT-024 exposes a "scoped rebuild at a pinned snapshot" service API, reusing the rebuild publication (its sanctioned retry unit), callable by the publication command under the fence. Missing or stale evidence is never treated as zero. Affected identities come from authoritative records in the same scope, never from counts.
  • Production (MS-01, Q-31): the materialised families reach production only after the X-STATS-b chain (idle gate (b); soak; production pending index; separately approved rollout lifting the in-code refusal; production eligibility; family built; staging proof). Under Q-31, authoritative counting and identity enumeration at the protected boundary is the designed first pilot path, with the materialised family a swap-in behind the same interface. Preview pilots use that path (D3-10d); staging pilots need X-STATS-a.
  • Compatibility (MS-09): three separate mechanisms: (a) BSON extra-element tolerance (R0's floor); (b) the fold protocol window: new transition kinds for canonical commits go into one bump, protocol 5, after gate (b), with intents only until then; © per-family catalogue and source versions plus the configuration digest: new families, dimensions and target-aware classification.
  • Write path (MS-06, DC-21): the engine never writes statistics or pending entries; FEAT-024's source-write seam does. Admitted projects run FEAT-024 in fold mode or with statistics writes off, never in transactional point mode.
  • Authorization (MS-18): ProjectQuestion resolves canonical questions, not only Project.AnnotationQuestions; the canonical designer reads QuestionVersionAnswers (or authoritative counts), never the legacy question locks.
  • Limits: FEAT-024 excludes broad agreement and outcome-level statistics. R5c keeps its own rebuildable store (D3-11). PRISMA snapshots are computed from authoritative records at the report watermark; FEAT-024 rows and history are never report or as-of inputs (MS-11, MS-22).
  • Known hazard: today's per-projection fresh zero can't prove "unused" across snapshots (StageReviewStatisticsEvidence, cited in COMPARISON F9).

Conformance tests: Test IDs: C8-T01 to C8-T08 (acceptance criteria §7.5).

C9 — Reconciliation task, gold snapshots, assignments and queries

Implements: RE1–RE5, RA1–RA5, SF4/RE3, MG1, NT1, BL1, GS1, UA1, QY1–QY9, RX1, DP5, VS2. Baseline: readiness is completed sessions ≥ the stage target. There is one shared reconciliation session per study and stage, editable by any Reconcile holder, with no editor exclusion (reconciliation uses no claim or presence at any layer). Reconciled answers are one overwritten set per question across stages, and counters are two booleans. The reconcile response maps every study session across stages. The web route shows read-only candidate cards (any number, two per row) with no reconciler form, and AF2's reconcile host is read-only by rule. No gold, adjudication or query entities exist. The canonical task replaces this model.

Element Content
Reconciliation task One per study × form (RE4, PV2), keyed (project, study, form) with a deterministic ID, reachable through any stage using the form; there is never a second task for one study × form (DD-07). It pins, as versioned state and never as part of the key, an input set holding the form version it reconciles against and the full set of qualifying candidate session versions (all of them; SF4); a new input set is appended when inputs change, PublicationImpactPolicy decides which candidates still qualify after a publication, and the gold snapshot records which input set produced it. Per question, a derived held state when the candidates' pinned revisions fall in different compatibility classes, a candidate's value is invalid under the task's form version, or a candidate head is Conflicted; a held question blocks only itself (prefill, agreement, its own acceptance) and "Ask vN reviewers to update" raises a Needs-updating request that changes no standing. v10's later "Mark compatible" is replaced by the admin's immutable compatibility declaration (D2-02). The compatibility class is not part of the key (corrects B-28).
Candidate drift A new qualifying candidate, a Save after Complete, a Fix or Upgrade after gold, a publication of the form's version, a publication policy that de-qualifies a pinned candidate (requireReanswer), a candidate withdrawal, or a dedup alias puts the task into "inputs changed · re-check"; it never retracts gold. Under doNothing with counting, candidates keep counting until they submit (v10's rule); under requireReanswer they stop qualifying by derivation; the admin's publication choice selects which.
Matching Suggested pairs/groups across all candidates (MG1); reconciler confirms; candidates preserved; re-pairing keeps prior pairings in history and re-evaluates only dependent answers (PROPOSAL, COMPARISON F7)
Reconciliation session An entity of the task with authorScope = reconciled and a current holder (the editor claim below), with Save and Complete versions and drafts through SessionDraft keyed by the task; "started" means it has a draft or an explicit version. Never a FormSession, so it cannot collide with a candidate session under Q-36. On a new editable AF2 reconcile host. Autosave/Save are unfinished; final Complete accepts the displayed valid answers including prefill (RE2); exact-text free-text prefill only within a class (RE5); exposure tracking for the unseen-control warning (actual controls, not tab visits); Complete anyway allowed; validity and affected children enforced.
Editor claim Compare-and-set plus lease on the task (taskEditor, X-RECLAIM); atomic "Start reconciling"; only the assignee may claim an assigned task; admin release revokes the claim through the outbox; lease expiry never expires a started assignment; works with tracking off (AC-R4a-36 to 39). Query review uses the same mechanism on QueryWorkItem (queryEditor, AC-R4b-09).
Gold snapshot Immutable per study (studyId, seq); entries reference exact reconciled revisions (each carrying its question version and class); new snapshot on change, keeping unchanged references (GS1); compare-and-set on the study's current-snapshot pointer when tasks and query resolutions publish concurrently. Derived per entry: goldNeedsReReconciliation when the form's current pin for the question is in another class than the entry's revision, or the entry's value is invalid under it; gold stays effective while flagged and is labelled with its version in exports and PRISMA manifests.
Shared question gold Overlapping forms share question gold (RE4). All reconciled heads of a study live in StudyGold, so ownership is local to the study. Batch D D2-09 decides between "first publisher wins; other tasks challenge by query" and the recommended "the second task sees existing gold prefilled as accepted, with its source snapshot and reconciler shown, and may revise it in its final submission, producing a new snapshot with provenance; queries remain for everyone else". Until answered, pilots avoid overlapping reconciled questions.
Target-1 forms No task and no automatic gold; exports label the single assessment "single reviewer, unreconciled"; optional attributed "accept as gold" (Q-29). If D4-03 approves, a Verification step produces Verified gold (C14)
Screening-profile part A separate aggregate that adjudicates decisions and writes the final screening outcome (RX1); never a reconciliation session writing screeningOutcomes (a FEAT-011 MUST NOT). Who reconciles each part follows stage grants (C10).
Assignment Optional; eligible reconciler; expiry only for explicit, unstarted assignments; audited override; started assignments are released by an admin and must be reacquired (RA2–RA4). Random eligible start and assigned-work-first ordering are PROPOSAL (v10 r2).
Additional review request Separate capability (RA5); independent eligible reviewer; no candidate exposure; result returns to the reconciler; target unchanged; no automatic gold. The request writes a single-use requestedReview claim on Study (study × form × reviewer) that lets exactly that reviewer past pool, bucket and capacity filters (AP-03, D3-13c); provenance on the resulting session; counted outside allocation progress. The requested reviewer is excluded from conversations until the review is returned (Q-N9, recorded)
Query work item One per reconciled revision ID (the accepted-answer version, QY2), shared unchanged across every snapshot that pins it; individual concerns, proposed corrections and outcomes; gold stays effective with a pending flag; children resolved before a replacement snapshot; audited self-review; optional rejection explanation; QY8/QY9 closure rules, where QY9's current applicability compares the target revision with the snapshot's current revision for that head and with the form's current class, flagging without silent retargeting. After a correction, screening decisions re-run profile rules, while annotation gold always needs the reconciler's final submission: candidate agreement never confirms it automatically (RC10 split).
Legacy reconciled answers Readable as LegacyAuthorityUnknown; treatment in exports, queries and readiness per Q-35. Legacy reconciliation refuses canonical forms from R2a.
Clarification thread (StudyConversation; #3944 and #3965, open PRs) Pre-gold questions from a reconciler to selected candidates: one-to-one threads on completed sessions only, an exposure record looked up by session (C3 kind "questioned in reconciliation"), a read-only context link, reconciler eligibility across bound stages. Kept separate from queries; must meet Q-10 before conversations are enabled. Refuses canonical scopes until R4a, when it binds to the task identity and L6 owns it (NS-18, NS-19). Threads are part of the project's audit record (D3-25). Study issues never carry answer disputes after R4b (NS-24)

v10 settings dispositions (PROPOSAL): step-level "who reconciles, per part" is replaced by stage-scoped grants, with a per-part grant if needed; "earlier gold standard … candidates never see it" is revised under VS1, which lets candidates see gold by step policy; "rationale for a reconciled decision: Always" applies only to screening adjudication, if Q-32 allows it; "EDIT RECONCILIATION reopens it" is replaced by the query or new-snapshot route (GS1, QY).

Conformance tests: Test IDs: C9-T01 to C9-T19 (acceptance criteria §7.5).

C10 — Capabilities, groups, delegation and disclosure

Implements: PM1, PM2, AG1, RA5, QY4/QY7, EX1 authority. Baseline:

  • ResourceSecurity.json has 25 project and 4 stage activities; ChangeOwner, AssignPermissions and Delete are owner-only in the catalogue.
  • ChangeOwner is not enforced: ownership moves through the project PATCH under the Edit permission (inventory §7). The permission-update endpoints accept any activity, so an AssignPermissions holder can grant owner-reserved activities.
  • Only the built-in Administrator group is reachable. Custom groups exist in the domain but have no creation path; the authorization programme gates them on membership schema 1 (G-D) and plans their administration as WP11 after WP9 explanations.
  • Stage grants for groups and all members exist; per-member stage grants have no API writer.
  • ProjectAuthorityEvaluator is the single new evaluator, dark until the authority programme's enforced-mode cutover.

See the matrix and the inventory.

Rules:

  • Capability is distinct from administration. Assignments and notifications never grant access. Every read and command rechecks active membership and grants; revocation applies to new access and never erases evidence.
  • Owner-reserved activities are refused by every permission-update endpoint and hidden in the dialog. ChangeOwner is never grantable (ownership transfer stays owner-only, PM1). AssignPermissions becomes grantable only through R1d's delegation envelope (PM2). Delete follows the permission matrix decision (Q-03).
  • Anti-escalation: a membership editor can't add themselves or anyone else to a group whose grants exceed what the editor may administer (Q-03a).
  • Delegation stays inside an owner-defined envelope without recursion (PROPOSAL, Q-03). Audit reuses the authorization programme's authorizationAudit; no new audit store.
  • Disclosure policy is shared by interactive reads, exports, statistics and notifications. The export disclosure contract says who may unmask identities (a capability or an explicit mapping to ExportData), keeps candidates separate from gold for exporters who aren't reconcilers, applies BL1 to exports and audits every unmasking. Today any ExportData holder can choose "UNMASK DATA" regardless of blinding.
  • Presence is a disclosure channel. Realtime presence snapshots pass DisclosurePolicy: people who are not holders get counts and their own claim only; identities need the Monitor capability and never cross BL1 (RT-14, D3-20, AC-T-08).
  • Every new view maps to a capability: Monitor ("Who is offered what", which can reveal personal votes), History & corrections, Agreement (AG1, with its own navigation entry, R5c), PRISMA.
  • New capabilities at F1b: "receive and resolve study issues" and "approve PDF corrections" (recipients expand by capability, not by Administrator-group membership; NS-20); only the requester or an admin may download an export (#3335 D11, PH-25).
  • Per-reviewer evaluation for other reviewers ("Who is offered what", reviewer validity) needs the out-of-request resolver X-AUTH-RESOLVER (#3251, #3611); until it exists, Monitor shows pool-level counts by refusal reason (AP-02, D3-13d).
  • Enum ordinals are appended, never reordered.

Conformance tests: Test IDs: C10-T01 to C10-T09 (acceptance criteria §7.5).

C11 — History, export and manifests

Implements: EX1, EX2, AG1–AG3, VS2 reporting. Baseline: current exports only, streamed to the browser. Open #2461/#2574 reserve AsOfDate and other modes but accept only CurrentState. OnlyCompleted is forwarded but not consumed by writers (research §1.5).

  • Modes: current (default; gold and candidates as permitted), previous versions (session and gold snapshot versions), as of a date (review-state reconstruction where history exists; coverage labels where it doesn't). Reuse the reserved export-spec modes rather than inventing a second export architecture.
  • Ordering (F1a): per-aggregate sequences order records within an aggregate. Every canonical record carries a hybrid logical clock stamp: the larger of the host clock and every stamp the command read, plus one, so a record that references another always has a larger stamp. Every study-scoped command writes its Study (CR-1), so stamps on one study increase strictly. There is no per-project commit sequence: it would serialise every commit in a project (DC-01, ADR-019's measurement). Wall-clock fields never order anything: observedAt on a revision and legacy DateTimeCreated are evidence fields with trust levels, never used for ordering or as-of selection.
  • As-of rule (F6a): as-of means "what SyRF knew at that stamp", not "what was true then". As-of(T) is offered only when T ≤ now − (transaction lifetime + expired-transaction sweep + twice the clock-skew bound + margin), and a cut at T is causally closed. Every dataset in the manifest is classified as versioned (reproducible), current-only (labelled "as at export time") or not observed. Two exports at one T are identical for versioned datasets and the same requester authority, except identities erased since, which the manifest records (D2-14); identity is proven by the content digests recorded in the manifest. A current export is a cut at the stamp taken when it starts. A restore adds a history-discontinuity record that manifests report (D2-13).
  • Coverage manifest per dataset: answers, sessions, screening, gold, outcomes, study metadata, lifecycle, citations and aliases each state their basis and coverage. Dates before a project's adoption return "not observed", never the adoption snapshot. Legacy timestamps carry trust levels (DateTimeCreated is settable and stamped at construction).
  • Mixed versions (versioning model §10): every exported answer carries (questionRef, questionVersionSeq, classSeq, optionId[], value[], responseMode?, answeredUnderVersion, qualificationPolicy); gold values carry the snapshot seq and goldNeedsReReconciliation; wide exports are generated per form version or per compatibility class with a per-cell version column, never mixing classes in a column; manifests list every definition version with its digest and the policy generations in force; adopted answers carry AuthoredUnder (Verified(v1) or Unknown).
  • Suppression: a descendant preserved under a suppressing ancestor answer or mode is omitted or carries an explicit status; it is never emitted as a live value (PH-06).
  • Usage: question-version usage is counted from revisions; form-version usage from session versions (latest explicit version per session, by category, once across stages); draft_only from drafts by base form version.
  • Extraction datasets default to studies whose required profiles are collectively Included, with an explicit option for the rest and per-row collective outcome and surplus-assessment columns (SR-17; methodology coverage).
  • Retention: whether exports are stored or regenerated is decided in the C11 ADR.
  • Blinding: for a form bound to several stages, the most restrictive bound stage's BL1 applies (Q-28).
  • Disclosure: authority and visibility are checked at retrieval and delivery (C10).
  • Legacy fix: making OnlyCompleted work changes legacy output, so it ships behind a flag.

Conformance tests: Test IDs: C11-T01 to C11-T09 (acceptance criteria §7.5).

C12 — PRISMA units, authority and report manifest

Implements: PR1, FEAT-011 constraints, amendments A–O once approved (Q-06a, Q-06b, Q-37, D4-07, D4-08, D4-11).

  • Units stay distinct: Citation (immutable import occurrence with source type), Publication (system bibliographic identity; privacy rule in amendment L.7), Report (full-text identity; amendment B), Study (project reviewable unit; StudyLink groups collate reports of one study, amendment O), animal populations and cohorts (never PRISMA units).
  • Write-shaping parts, frozen at F3 with amendments A and H (before R3a writes them): unit identities; the per-profile screening outcome with route provenance instead of a single stage ID; authority values {CandidateAgreement, Reconciled, Admin with an override audit, Imported with an independence declaration, LegacyUnknown}; results including Unsure (D4-01), never Excluded; a structured reason with coverage status (primary versus several counted reasons is Q-22, so the shape must allow both until it is answered) carrying the primary-reason rule (D4-13); the StudyEnteredPool event (FEAT-011's pool-entry event), written to StudyPoolLedger with filter/profile versions, separate from personal admission and batches. Calibration records (D4-04) are never pool-entry or screening events.
  • "Entering screening" (boxes 4 and 8, "made available to screeners") is defined in amendment A: protocol scope, or actual release including shared batches and personal grants. Fixtures cover early-stopped and batched reviews.
  • Identification part (F-P): Citation with source type; earliest-source downstream column; the Citation to Publication link record (amendment N); retrieval as fullTextStatus only, with human actions and events (amendment M); search documentation fields and searchRound (D4-05, SR-24); the search-population family extended with source type (OPS1) for statistics screens only; withdrawn searches per amendment J and D3-12; reported counts for steps done outside SyRF with their entry phase, kept separate from computed counts in every manifest (amendment K).
  • Deduplication: ASySD inside SyRF as FEAT-012 specifies, with merge as an alias, aligned with the admission service (amendment L); box 3 combines SyRF-detected and externally reported duplicates.
  • Phase mapping: a versioned per-project mapping from each profile to its PRISMA phase (title and abstract, full text, or not reported, for example a sub-study profile or the legacy compatibility profile), set in profile settings (R3b). The required profiles for the lifecycle "Included" transition (P2) are those mapped to the title/abstract and full-text phases.
  • Arithmetic identities (F6b, PROPOSAL): every snapshot satisfies, per source column (Database/Register, Other, Unclassified) and with explicit remainders shown in the manifest and the diagram footnote: I1 #31 = #3 + #5; I2 #32 = #29 (all Column 2 source types, correcting FEAT-011's #32 = #10 + #11 + #12, which drops Other records); I3 #33 = #31 + #32; I4 #34 = #33 − #7 − #8 − #9 (remainder: pending dedup review and check); I5 records_after_removal = records_screened + not yet entered screening; I6 records_screened = records_excluded + sought + unresolved at title/abstract; I7 sought = not_retrieved + assessed + awaiting retrieval or assessment; I8 assessed = excluded_with_reasons + included + unresolved at full text; I9 Σ reasons = excluded_with_reasons − reason not recorded; I10 new_studies = Σ columns included resolving StudyLink groups once, new_reports ≥ new_studies; I11 total = new + previous (D4-11); I12 total_studies_ma ≤ total_studies; I13 reported external counts reconcile per field and per search. A mismatch blocks freezing unless an administrator records an explanation in the manifest.
  • External steps (amendment K): entry phase per search or import; boxes 2–9 and 11–15 = computed + reported; #31–#34 computed only; boxes 10, 16 and 17 computed only; box 1 from previous-review records with the template variant switch.
  • Snapshots from authoritative records only (MS-11): a snapshot is computed from Citations, ExternalStepLedger entries, ScreeningOutcomes, StudyLifecycleLedger and StudyPoolLedger entries, StudyLink groups, alias sets and the PrismaPhaseMapping version at the report watermark (a hybrid-logical-clock stamp) and stored frozen (PrismaFlowSnapshot). FEAT-024 rows are never a report input.
  • Report part (F6b): reports freeze against a manifest; amendments append; the manifest records the deduplication algorithm version, tier rules, auto versus reviewed share, QC sample and reversals (PRISMA-S item 16); the methods-summary block (PRISMA 2020 items 5–11, 16, 24) is generated from the manifest. Box 17 uses the "Synthesis inclusion" attribute recorded under the Record synthesis inclusion capability (placeholder name, A-03), owned by L12 in R5b.
  • Rule: no personal Include, extra completed work, form target, inferred cohort, imported decision with unknown independence, calibration record or extraction completion changes a PRISMA count.

Conformance tests: Test IDs: C12-T01 to C12-T11 (acceptance criteria §7.5).

C13 — Classification, populations and inference

Implements: the population/cohort decisions recorded in COMPARISON and the classification research (§2–§4), TC1, OD12 follow-ups. Entity types carry capabilities (classifies animals, structured subsets) and numbered child questions. Entity-type identities (EntityTypeId) for the seven legacy categories, cohort, outcome measure and experiment are minted at F1a as a shared-kernel value object; C1 adds capabilities and project-defined types without re-identifying anything (DD-12). Every answer carries a population reference from its first canonical write; the default whole-study population is derived deterministically from the study ID and needs no document until C1 enables classification for the project (DD-24). Each study population has a system whole-population cohort; every instance belongs to exactly one population; instance identity is the label head's ID (VB-09). Relationship/set annotations live on the parent/set with evidence (disjointness and exhaustiveness are separate claims). Shared concepts are defined once; paper-specific mappings are answers; project rules are versioned and link specific concepts. Inference is derived, explainable, withdrawable, never written back as a reported answer, and never creates counts without support. Implications such as Pregnant ⊆ Female apply without automatic strictness. Inferred cohorts are shown with their outcome associations, and conflicts are shown with their supporting provenance (C2). Population merging and cross-type count solving are follow-ups after C2.

Conformance tests: Test IDs: C13-T01 to C13-T05 (acceptance criteria §7.5).

C14 — Outcome schemas, measures and observations

Implements: OC1, OC2/ODIR1, TC1, RD16 (keep the shipped entry pattern), MIG1.

  • Supplied schemas (legacy-compatible, event-count) and project-owned custom schemas are versioned. Each schema defines series-level and observation-level fields with semantic roles, types, validators and cardinality.
  • A dedicated system schema-selection question targets schema configuration records, not reviewer-created instances (owner clarification of 27 September, recorded in the classification research).
  • Outcome measures are reviewer-created per-study entities (A-20): reviewers create them from the paper; admins never have to predefine them. Each measure carries one versioned direction across cohorts in that paper, with no context override (ODIR1); direction is a single measure-level answer, revisable and reconciled in R4c, never copied per series. Direction is never derived from numeric type and is not a validator or treatment-effect claim (OC2). The C14 ADR confirms this reading before O1, or raises it with Chris.
  • The legacy-compatible schema enables mapping, never automatic migration. Canonical forms admit quantitative extraction only from O1. Existing outcome entry (matrix, cell dialog, spreadsheet, graph) is kept and extended.
  • Extraction provenance (PROPOSAL, SR-06; E90): every observation carries an extractionMethod role ∈ {reported, graph-estimated, calculated, author-supplied, unknown} and a series-level dataSource note (table, figure, page); graph-estimated is the default when a PDF graph region is linked.
  • Unit vocabulary: a project unit vocabulary (controlled list with SI-aware labels and free-text fallback, seeded from a CAMARADES list); the validator "same measure, different unit" warns at Save and blocks binding at reconciliation (R4c).
  • Dispersion catalogue and legacy-compatible fields (before Q-17 closes; E12): average {mean, median, other}; dispersion {SD, SEM, 95% CI lower and upper, IQR Q1 and Q3, range min and max, none reported}; n at observation (default "same as cohort n", with provenance); events and total for dichotomous outcomes; time with unit. Dispersion is never converted on export. "Variation" (OC1) means the dispersion role and its catalogue value.
  • Domain validators (AC-O1-02, AC-O1-11): SD ≥ 0; SEM ≥ 0; n integer > 0; events ≤ total; time monotone within a series; CI lower ≤ average ≤ CI upper; Q1 ≤ median ≤ Q3; an SEM/SD plausibility warning (SEM × √n ≈ SD); warnings never block Save, blocking rules block Complete.
  • Sample-size rule: the analysis n is the observation-level n when recorded, else the cohort n; exports carry both and nSource; O2 never overwrites a cohort count with a series count.
  • Verified gold (D4-03): a Verification step on target-1 forms produces a gold snapshot with authority = Verified, distinct from Reconciled and from Q-29's accept-as-gold; no agreement statistic is computed for it; exports label "single extraction, verified".

Conformance tests: Test IDs: C14-T01 to C14-T06 (acceptance criteria §7.5).

C15 — Notification capture contract (C15 v2)

Implements: the 3 October scope addition; delivery for QY6/QY8 notices, RA assignment notices and LC1 alerts. Owner: the notification programme (capture, inbox, email, digests); L14 writes the specification and L8 the disclosure hook. Freeze: F1b for the contract (with C10's disclosure); kinds per feature. PROPOSAL (NS §4.1–§4.2). Baseline and catalogue: notifications integration; programme changes: programme integration §8.

  • No second notification store. Durable intents are part of C15; in-memory outboxes stay forbidden. Workflow state lives in feature aggregates and feature-owned queues, never in ReadAtUtc.
  • Kind registry. Each owning feature registers, through DI, an INotificationKind: kind, email category, scope (legacy, canonical or both), admission flag, inline recipient limit and resolver. A registry test fails the build when a kind lacks a category, resolver, label or disclosure fixtures. The eight existing kinds keep their typed fields and map to themselves.
  • Source and view. Inbox rows carry a generic Source sub-document (type, IDs, stage list, task ID). The server provides the action label, context lines, typed availability and workflow state (a NotificationView). One capture service serves every writer.
  • Occurrence identity. SourceId = SHA-256(kind | source type | source ID | occurrence key), the same for every recipient; row ID = SHA-256(SourceId | recipient). The occurrence key comes from durable identity (command ID, aggregate version, transition, publish operation ID), never a random value. Capture is a bulk upsert with $setOnInsert. Two lifecycle transitions of one source create two items; replaying an occurrence creates nothing. StudyConversation and StudyIssue are the models; reviewAccessGranted's random IDs are not.
  • Capture modes. Inline: inside the active source transaction, with at most the kind's inline recipient limit; if capture fails, the source rolls back. Recorded fan-out (C19 class (b)): the source transaction writes one durable intent (NotificationFanOut: the occurrence plus a recipient selector, either a frozen list such as a publication manifest's owners or a capability evaluated with current authority at expansion); a leased, idempotent worker writes rows in bounded batches and records progress. Above the threshold (PROPOSAL: 200 recipients) recorded fan-out is mandatory. Time-driven notices (assignment expiry warnings, LC1 reminders) are domain transitions run by a scheduler that writes a marker on the aggregate and captures inline.
  • Admission. Capture happens only when the kind's flag is on, its scope matches the project mode, and the project is admitted for notifications (per-project notification admission, an R0 enrolment scope). Inbox reads stay available whenever saved items exist, whatever the capture admission.
  • Disclosure. INotificationDisclosurePolicy runs after every resolver, for HTTP reads and both email processors, given the channel (inbox, email, digest), the recipient's role and the stages' BL1 and VS1 settings. Every channel: never candidate answers, personal votes, other raisers' concerns, or a raiser's identity where blinding applies; reviewer references are BL1 aliases from one stage-owned alias source. Email and digest: title, project name (D3-22) and link only; never study titles, aliases or free text. Fixtures cover each channel and the candidate, reconciler, admin, revoked and blinded roles.
  • Recipients are expanded when a row is written (or when a fan-out intent is expanded); the actor is excluded; per-raiser notices go to the raiser only. Feature queues, computed at read time, reach people granted after an event. Every release passes with every notification flag off.
  • Near claims (RT-25). No notice per routine claim; one workload notice per reviewer per plan change; revocation events keyed by claim scope.
  • Version skew. Preferences accept unknown keys and treat missing keys as off; the client sends back keys it does not render; dispatch, discovery and digests look up the kind's category. An unknown kind leaves its ledger row ready (never terminal Suppressed); that behaviour ships one deploy before the first new kind.
  • Enablement. An operator delivery halt pauses dispatch without cancelling it; notificationEmail declares its dependency on notificationInbox; G-NOTIF, per environment and kind family, precedes any enablement outside the e2e stack and Mailpit (D3-21).
  • Ownership. The notification programme keeps capture, inbox, email and digests. StudyConversation moves to L6 at R4a; study issues move to Study Management and checked PDFs to the PDF programme (NS-19). Operations hosted in PM capture notices, so PM receives the capture capability as an R0 item (E59).

Conformance tests: C15-T01 to C15-T11 (acceptance criteria §7.5) for every release that adds a kind (the NS review's AC-C15-01 to 09 are C15-T01 to T09; AC-C15-10 is C15-T11 with AC-ALL-04 (d)).

C16 — Compatibility floor, admission and reader/writer compatibility

  • Floor (E16, E48): every embedded type the programme may extend captures unknown elements and writes them back; ignoring alone is never enough. ScreeningInfo, ExtractionInfo, SessionTally and StudyBulkUpdateLock gain no fields; no field is added inside a persisted computed collection; schema-version-conditional serialisation is audited. Canonical facts live in top-level Study and Project fields or in new collections. R0 also ships reader logic: the SessionTallies getter and the claim pipeline merge the canonical summary's per-stage projection, and an IReviewMembershipFacts seam with shared predicate fragments lets pool, capacity and readiness checks read canonical membership, all inert until a canonical writer exists. A writer floor (ServiceVersionFloor) refuses canonical commands while any instance runs below R0. Floor steps repeat before R2b, R3a and P1, and before C1 or O1 where they add embedded fields. Flag-off stops new writes and enrolment but keeps saved work readable.
  • Ownership as data, enforced on the documents writers write (E49): a CanonicalScopes marker on Study and Project, checked in legacy aggregate methods (one document write with the Audit.Version CAS, with or without a transaction), by a composite registered IAggregateWriteGuard for generic and direct writers (composed with the bulk-lock guard and covered by the architecture test), and by project-wide pre-checks. pmCanonicalOwnership is the audited registry, reconciled with the markers. Markers on existing scopes are set through ADR-020's lock, verify, stamp and release protocol. Flags and enrolment gate only new admission, never ownership.
  • Admission service: one server-authoritative per-project admission service (the CanonicalEnrolment record) read by API and web, with an audited admit/remove action and a rule for new projects. Environment-wide flags owned by other programmes are settled per flag (Q-25). It is never FEAT-024's statistics allowlist (next bullet).
  • Admission record versus statistics eligibility (MS-24, DC-21): R0's admission record is never the FEAT-024 allowlist. FEAT-024's durable eligibility (#3524) is built after C16 freezes and shares the record shape and audit, with statistics eligibility read inside the transaction by FEAT-024's gates. R0 refuses to admit a project that is allowlisted for FEAT-024 writes but not in fold mode.
  • Admission scopes: canonical forms and profiles; per-project notification admission (NS-04); per-project AF2, shell and eligibility admission (DS-04); the binding-scope tracking pilot (D3-16). The flag overhaul's P7 per-project targeting keys to the same record (PH-14).
  • Compatibility mechanisms are named separately: extra-element capture plus reader logic (this floor), the fold protocol window, and per-family catalogue versions with the configuration digest (MS-09; C8). Existing floors are reused: ServiceVersionFloor, ADR-019's storage-version tripwire with its allowlist guard, and ADR-011's writer-floor precedent (PH-29).
  • Rollback: each release ADR records the minimum rollback image per service, rehearsed by an image rollback with canonical data present. After canonical writes, rollback is canonical-aware (forward recovery or read-only containment), never a destructive down-migration (screening research, §5). Binaries below R0 are not a rollback target once canonical data exists. A release that changed a statistics writer, family or protocol follows FEAT-024's rollback order (MS-16).
  • Inventory: every writer and reader with its canonical-scope behaviour (route, refuse or adapt) (consistency model §6.5; migration §1), including:
  • every UpdateMany on pmStudy with its version-bump status (verified on main eb93caffa): the question-delete cascade (StudyRepository.cs:1306-1319) and the three inclusion-recalculation updates (StudyRepository.cs:1619-1651), neither of which bumps Audit.Version, and the bulk-update lock release (MongoBulkStudyUpdateStudyWriter.cs:313-320), which does. Each refuses canonical scopes through the CanonicalScopes marker or is adapted;
  • the tracking Study writers (RT-08): the hub's join, leave, dirty/clean, disconnect and prior-study release; the PM idle, suspension and liveness consumers; the claim pipelines and typed admission; the direct-navigation claim; the screened-reservation release; the guarded settings save's "Apply anyway" revocation; the reservation restore on session deletion. Tracking readers: the presence snapshot and FEAT-024's availability calculators. Each has a route, refuse or adapt decision, and R0's floor makes the tally getter and claim pipeline merge canonical counts (RT-07);
  • the notification stack's Study writers (NS-08): study-issue acceptance (#3945; Title, Abstract, Year, DOI, Url) and checked-PDF approval (#3947; PdfRelativePath and the bulk-PDF delivered fields). Both are version-guarded and lock-aware today; for admitted projects after P2, a bibliographic correction appends a correction event and re-runs DOI/PMID matching, and a PDF approval records a P1 retrieval event;
  • 3939's batch writers (plan, membership, access) and #3941's capture paths, once merged.

  • Flags (E75): a flag read in both hosts is delivered to both, with a cross-host agreement check before enablement; no gate or evidence relies on runtime overrides until #3975 is fixed.
  • Job families (PH-15): every new background job family (publication phase 2, adoption backfills, retroactive deduplication, expiry, lifecycle transitions, outcome recomputation, notification fan-out expansion, batch-opening evaluation) gets an authority-transition M5/P9 classification and broker permission rules in its release ADR.
  • One writer host per canonical collection, following the architecture review's direction.
  • Every release states its flag decision, as the repository rules require.

Conformance tests: Test IDs: C16-T01 to C16-T08 (acceptance criteria §7.5).

C17 — Information architecture, coexistence, overview and settings DTOs

  • Navigation (Q-13; details in the UI comparison §4): Overview (project dashboard with setup progress, keeping the Project setup checklist footer with readiness-based content from R2a); Review (per-stage review); Design (questions, entity types, concepts & rules, outcome schemas, forms, screening profiles); Stages (per stage: overview, steps and settings, monitor); one Reconcile entry per study × form task; Members & groups; Data (export, history, agreement, PRISMA); Project settings (including the Workflow version panel). "Library" stays with Study Management; the duplicate review queue and the merge or split wizard live there (P2). One owner per route group, recorded in the route inventory (UX strategy §3.2).
  • My work (PROPOSAL, pending D3-07; NS-07): a project-level surface that is the reviewer's and reconciler's landing inside a project, listing every actionable item by role (to review by step, needs your attention, reconciliation, requested reviews, your queries, awaiting your approval) with the four feature-owned queues as filters and the inbox as history; a global app-bar badge whose counts are computed at read time over the queues and admission-based work, with no notification dependency; a cross-project "My work" tab beside "Pending Projects"; an admin banner on the project overview for pending LC1 requests. Rows deep-link to the task identity (RE4) and use stage-owned aliases (BL1); an item resolved by someone else leaves the list and stays in history (pending D3-23). Release: R3c (badge, banner, changes awaiting approval), R4a (surface, assigned work, requested reviews), R4b (concerns). The #2621 cross-project landing is post-GA with its own owner.
  • Coexistence: navigation per project mode (classic or versioned, names pending D3-03), the question editor's two modes (legacy API and canonical), and legacy labels, until the GA milestone and adoption. The minimum shared chrome (app bar, badge, inbox, rail geometry and states, Overview, Members & groups, Data › Export, Project settings, status chips, page shell and state, job language, copy deck terms) is identical in both modes; the workflow version badge appears in both; legacy chrome and shared pages are restyled under D3-06.
  • DTOs: overview and settings DTOs carry gate status, sufficiency and work status separately; stage overviews show bound forms, targets, steps, gates, readiness and batch frontier, never duplicated stage copies of shared evidence. Every read model declares its consistency regime (in-transaction projection, materialised rows, or computed at read), and every count or status DTO carries a freshness label (fresh from a materialised read, authoritative from a pinned fence read, rebuilding, or not observed, with its asOfStamp) and, for derived records, the definition-version vector it was evaluated under. Gates read only authoritative or fence-verified sources (DD-21). The UI shows "as of [time]" and "updating" from these labels and never shows a stale count as fresh or a count without a real numerator. "Who is offered what" is pool-level until X-AUTH-RESOLVER (D3-13).
  • Dockview layouts: saved layouts are stored per reviewer per capability slot (screening, annotation, combined), and the API validates allowed panel keys and one instance of each capability panel. A layout-contract amendment (capability keys per step kind, new panel keys for history, population context and reconciliation, and migration of saved version-2 layouts) is agreed with the layouts owner at F1c, before R2a's history panel.
  • AF2 extension points (step host, history panel, outdated and provenance markers, population context, outcome-schema entry, save-status indicator slot, conflict screen slot, the VersionedAnnotationFormDataSource port, and the Needs-updating presenter contract: fromVersion, toVersion, treatment, reason, guidance, and the prior value rendered with fromVersion's labels; E44) are agreed with the AF2 owner and merged as code at F1c, before L5 consumers build.
  • UI standard: every new or updated screen is consistent, modern and built with Material 3, following FEAT-023's semantic contract so it renders correctly before and after that programme's cutover (acceptance criteria §3, UI-1 to UI-11). The notification stack's inbox and preferences screens meet the standard before testers see them; its conversation, issue and PDF screens before production (NS-10, D3-01).
  • Terminology and copy contract (F1c): one copy deck (one definition per term, internal name, "never use"), implemented as typed message constants per feature with a shared core-terms file, a banned-string and import guard spec, and a user-guide glossary parity check in docs CI (UX strategy §5). Core terms pending D3-03: "Save progress", "Complete", "Changes kept, not yet saved", "Needs updating", "Outdated answers", "Fix", "Accepted answers (gold standard)", "Screening result"; plus "Update to version N", "Unpublished", the reviewer progress vocabulary (your work, available to you, waiting on others, locked by a step, enough reviewers), the slot vocabulary (review slot, held, released, offline, enough reviewers; the D2-07 rule copy), a distinct term for each kind of draft, the error and recovery copy for every C18 typed outcome, and one alias scheme across candidate cards, threads, history, presence and exports. v10's "Save draft" and v4's "All changes saved" are banned strings.

Conformance tests: Test IDs: C17-T01 to C17-T06 (acceptance criteria §7.5).

C18 — Concurrency, transactions and idempotency

Implements: PS3 (an exact publication boundary), SL2 and SL3, LC1 (no admission into a Completed stage), RA1–RA4 (editor and assignment races), GS1 (the gold pointer), EX1 (reproducible exports). Owner: L1 Engine with L0. Freeze: F1a. The full design is the consistency model §2, §4, §5, §10 and §11.

  • Per-study serialisation (CR-1): every canonical command that changes evidence, gold, outcomes, capacity claims or any fact in the Study canonical summary writes its Study in the same transaction (an Audit.Version CAS, the summary, the study clock). Different studies never conflict.
  • No hot document (CR-2): no per-project or per-form document is written by interactive commands: no project commit sequence, no per-save Project token, no FEAT-024 transactional point mode for enrolled projects.
  • Options (CR-10): snapshot read concern, primary, majority write concern with journal, maxCommitTime and a command deadline.
  • Re-execution: a definite abort re-executes the whole command from a fresh snapshot with jittered backoff. Retries are free when Study moved only through maintenance writes (fold, claim and idle tokens, capture moves). StaleBase is returned only when the command's own business base moved.
  • Command ledger (CR-6): one command-bearing record per command with a unique (ProjectId, CommandId), its result IDs and a request digest; lookup first; a digest mismatch is refused; an indeterminate commit returns OutcomeUnknown. FEAT-024 receipts correlate through OperationId = CommandId only.
  • Commands carry what they observed (CR-5): bases, snapshot IDs, input etags, query targets and draft etags; a retry never substitutes "current".
  • Cache rule (CR-9): deciding reads never come from the shared repository cache; existing aggregates use non-upsert saves (#3985).
  • Natural keys (CR-11): deterministic IDs; a DuplicateKey is a reload and CAS; collections and indexes are created at start-up, and pmStudy indexes are built through the operator route.
  • Ordering (CR-7): per-aggregate sequences plus an HLC stamp on every canonical record; as-of reads only beyond the watermark (C11).
  • Typed outcomes: one frozen catalogue (consistency model §10.3); never a 500.
  • Budgets: CanonicalCommitCommandBudgetTests; the write-path gate per D1-08.
  • Prerequisites: #3985 and #3973 (D1-02).

Conformance tests: C18-T01 to T04, C5-T08, C18-T06 and C18-T07 (the DC review's AC-DC-01 to 04, 08, 11 and 12); the screening research's cases A8, A13 and A14; the CR-1 architecture test; the stamp-collector test. Test IDs: C18-T01 to C18-T11 (acceptance criteria §7.5).

C19 — Durable effects and events

Implements: C15 capture, LC1 alerts, QY6 and QY8 notices, RA notices, publication phase 2, adoption and merges. Owner: L1 Engine with L14 and the notification programme. Freeze: F1a. The full design is the consistency model §7 and §9.

  • Three classes (CR-8): (a) derived on read; (b) a durable intent written in the commit transaction and handled by a leased, idempotent dispatcher (the claim-revocation outbox pattern) or by an operation record; © a best-effort hint (SignalR, change streams) that never carries correctness.
  • Operations (CR-4): ADR-020-shaped records with a lease, a generation, a stable cursor, chunks and a final pass; one active per scope; limits with a stalled state.
  • Fences (CR-3): a scoped fence on the scope's head; a drain of at least the transaction lifetime plus the expired-transaction sweep plus a margin; action in a pinned snapshot; release by the operation, by lease expiry or by an audited operator action.
  • Notifications: inline capture (bounded) or recorded fan-out (a durable intent plus a leased expander); time-driven notices through scheduler markers on the aggregate; deterministic SourceId and row IDs with $setOnInsert. No second notification store; durable intents are part of C15; in-memory outboxes stay forbidden.
  • In-process IDomainEvent: only for loss-tolerant, same-process effects, and only after #3973.
  • Change streams: hints only, because an invalid resume token restarts from now.
  • Event catalogue: in the domain model §6.2.

Conformance tests: C18-T05, C19-T01, and C15-T01, T02 and T08 (the DC review's AC-DC-09 and 10; NS's AC-C15-01, 02 and 08). Test IDs: C19-T01 to C19-T05 (acceptance criteria §7.5).