Skip to content

Versioning model

Revised after the owner session (5 October 2026). Chris's owner session of 4 and 5 October changed this rulebook in ten places. (1) D2-01 is reversed: publication may create attributable generated session versions (PublicationGenerated), so effects are no longer always derived (§8.11 carries RD §3.15's before and after tables). (2) The form's standard reviewer target is part of the immutable form version: a target change is a new form version with publication impact (§4.6, §4.7, §8.12). (3) Compatibility is declared explicitly by the publisher and is immutable from commit; corrections use guided rollback and republication (§3.5, §8.13). (4) Q-34 is decided: option mapping writes new immutable revisions (§8.9). (5) Screening profiles have immutable versions whose outcome-shaping settings live inside the version, and profile publication detects mismatches and previews them (§5.1, §5.5). (6) Every other setting is classified as inside the version or operational (§4.6). (7) Study target overrides version per Study × form and get an explicit treatment when the standard target changes (§4.7). (8) Autosave keeps a reconstructable draft change log and superseded drafts are rebased (§7.7). (9) Training references and policies and (10) AI screening model configurations are versioned records (§16). The owner-session consolidation wins over older text, and the specifications carry the detail (mainly RD). Superseded rules are kept and marked "Superseded by the owner session (5 October 2026)"; nothing is renumbered. Feature implementation remains on hold (Chris, 5 October 2026). These are planning decisions only; brief approval and implementation authorisation are separate per gate (D1-04) and still on hold; no work has started, no gate has passed and nothing is enabled in production. Thresholds quoted here are proposed, not approved. The owner-session amendments are listed in the owner-session amendments record at the end.

Temporary planning document; planning only. This is the one rulebook for versioning in the integrated review programme. It answers the round-2 reviews VA and VB and the versioning findings of the DC, DD, PH, SR, MS and V2 reviews. Contracts C2, C3, C4, C5, C9 and C11 in contracts are amended to match it. Those amendments, and this model's changes to the integrated plan (R2a–R2d, R4a), open questions (the rewritten engineering rows, E36–E45, A-14, A-25 and A-26) and acceptance criteria (release criteria and the §7.5 conformance tests), have been merged into those documents. Mechanics the model relies on but does not own (transactions, fences, the command ledger, ownership markers, durable effects, ordering and the Study canonical summary) live in the consistency model. Where this document needs one of them it states the versioning rule and links to the section that implements it.

Labels: OWNER (ledger ID or register §1.11 ID), RECOVERED (existing design documents or code, not a new owner decision), PROPOSAL (this plan's recommendation), OPEN (Q-xx), ASSUMPTION (A-xx), CODE-MAIN (verified on main at 0f5c61073; file and line cited). A rule with no label is a PROPOSAL. Nothing here reopens a confirmed owner decision; where a decision has consequences, the consequence is stated. Anything that needs Chris cites a Batch D question from the resolution brief; this document mints no question IDs.

1. Purpose and status

1.1 What this document decides

The owner decisions fix the shape: forms own evidence and stages own workflow (SF1, SF2), the latest explicit Save or Complete is current (SL3), publication checks every prior version and records an admin choice (FV1–FV3), compatible same-context answers are shared across forms (SF3, SF5), gold is an immutable snapshot (GS1), and a published question is never deleted (QD1). The reviews found that the package used "compatibility" in four senses, keyed shared answers without a version component, modelled publication two incompatible ways, over-stuffed version containers with operational settings, and left options, entity instances, system questions, drafts and legacy duplicates without identity rules. This document decides each of those cells once.

The model in one paragraph: every definition and every piece of evidence has a stable identity and append-only immutable versions; every version, revision, session version and snapshot pins the exact versions it depends on by ID; a compatibility class derived from per-version declarations says which question versions mean the same thing; derived states (current, outdated, needs updating, qualifying, held, needs re-reconciliation) are computed from pins, classes and recorded policies and are never stored as facts; publication is a recorded policy, not a writer of evidence; and operational settings change with an audit trail and never trigger an impact flow. Time is transaction time only.

Owner session (5 October 2026): the paragraph's "not a writer of evidence" is superseded. Publication records a policy and may write attributable generated session versions and mapped revisions under the generation table in §8.11; where it writes none, effects stay derived. The standard target is inside the form version and every other setting follows the classification in §4.6, so "operational settings" now means the settings that change only who is offered work and when. Compatibility declarations are made by the publisher and never change after commit (§3.5).

1.2 Status of each part

Part Status
Shared sessions, SL3, FV1–FV4, VU1–VU3, PS1–PS3, SF3–SF6, PV1, PV2, GS1, RE2, RE4, RE5, AG2, AG3, QY1–QY9, DP4, DP5, QD1 OWNER; restated, never reopened
Per-question requireReanswer / autoUpdate / doNothing; BreakingChange declared at version creation; breaking transitivity; FEAT-003's per-session categories; stored answer wording (Annotation.Question); the 25 September adjudication precedence rule RECOVERED (sources cited where used)
Compatibility algebra, class in the head key, option identity, requirement versus settings split, system questions as data, Upgrade, treatment vocabulary, per-answer state enum, entity-instance rules, storage rules, task held state, gold re-reconciliation PROPOSAL; the parts that need Chris are listed in §15.1 with their Batch D IDs
Option mapping (Q-34), profile-version transition (Q-26), gold completeness (Q-04), target-1 forms (Q-29), legacy reconciled answers (Q-35), self-reconciliation (Q-36) OPEN; this document says what holds until each is answered. Owner session (5 October 2026): Q-34, Q-26, Q-04 and Q-36 decided; Q-29 decided-amended; Q-35 removed (§15.1)
Owner session (5 October 2026): publication-generated versions (D2-01 amended), the publisher's immutable compatibility declaration with guided rollback (D2-02 amended), the target inside the form version (target-versions-form; D2-05 amended in part), Study target overrides, profile versions with mismatch preview (Q-26), the draft change log (D2-07, D2-08 decided), training and AI model configuration versions OWNER for the decisions; the generation table, the setting classification beyond the target, the override treatment set, the rebase mechanics and every storage shape are PROPOSALs for the brief

1.3 Code baseline

Code claims are verified on /home/chris/workspace/syrf/main at 0f5c61073 unless marked UNVERIFIED. The files that matter here are src/libs/project-management/SyRF.ProjectManagement.Core/Model/ProjectAggregate/AnnotationQuestion.cs, OptionInfo.cs, Target.cs, Project.cs, StudyAggregate/Annotation.cs, AnnotationOptions.cs, AnnotationSession.cs, ExtractionInfo.cs, src/libs/mongo/SyRF.Mongo.Common/MongoExtensions.cs and MongoContext.cs, and src/services/web/src/app/shared/annotation/annotation-form-v2/annotation-form-v2-eligibility.ts.

2. Version kinds rulebook

One row per kind. "Identity" is what never changes. "Pinned by" says who references the exact version. "Immutable" says what may never be rewritten. "Derived" says what is computed on read and never stored as a fact (a materialised copy may exist only with its input-version vector, consistency model §8). "Impact flow" says whether a change can start the FV2/Q-26 admin flow.

Kind Identity Pinned by Immutable Derived Impact flow
Question definition QuestionRef = {questionId, systemQuestionVersion (system questions only)} plus structural identity {definitionOwner (project or profile), parent QuestionRef, entityTypeId, repeatable} (§3.1) Versions, heads Identity; a structural change is a new question Status (draft, published, retired) from references (§3.8) Never at commit; only through form or profile publication
Question content version (QuestionRef, seq) Revisions; form, profile and system versions Content once committed; compatibleWithPrevious once any revision pins it (§3.5; owner session: the publisher's declaration and any option mapping are immutable from commit) classSeq, class membership, system suggestion None by itself
Option optionId inside its question Revision payloads (by ID); conditions and parent filters (by ID) The ID; per-version content Validity of answers that select it Retirement or meaning change makes the version incompatible by default (§3.5)
Form requirement version (formId, seq) Session versions, tasks, stage settings versions, publication records; owner session: accepted result versions and readiness snapshots Everything once published: ordered pins with ancestors, requiredness, applicability graph, renderability result. Owner session: also standardTarget and ReconciliationPolicy (§4.1, §4.6) Usage, impact categories Publication starts the FV2 flow (owner session: including a target-only change, §8.12)
Form settings formId head Nothing Nothing; every change appends an audit entry (§4.4) Sufficiency and readiness read the live value (owner session: the target is no longer here; sufficiency reads the effective target, §4.7) Never
Profile criteria version (profileId, seq) Decision revisions, profile-owned answer revisions, stage settings versions; owner session: outcomes, adjudication versions, stage filter clauses, external decisions Everything once published: eligibility question pins, decision rules, must-agree set, requiredness. Owner session: sufficiency rule, Unsure, tie policy and bound, discussion, adjudication rationale, reason handling, identity blinding and the ScreeningSourcePolicy (§5.1) Decision standing under Q-26 Profile publication starts the Q-26 flow (owner session: with mismatch detection, §5.5)
Profile settings profileId head Nothing Nothing; audited (§5.1) Routes and DP5 read live (owner session: superseded, they are version content) Never. Owner session: what remains here is the PRISMA phase mapping reference, optional adjudication-task expiry and keyword lists; the adjudicator assignment is its own versioned record (§16.5)
Stage settings version (stageId, seq) Revisions (PV1 provenance), session versions (route), tasks (route) Bindings, steps, dependency edges, route policies, VS1, BL1, EW1 defaults, filter element. Owner session: the embedded StageStudyFilterVersion, the progression basis, step kinds, exclusion-stop, saved-work and departure-continuation settings, an optional stricter route cap, the hide-only hint override and browsing; route policies, BL1 and the VS1 default are superseded (§5.3) Active, from lifecycle status Never for sessions; a new binding is an admission change (§5.4)
Stage lifecycle stageId Nothing Status events are append-only Active, readiness Never
Session version (sessionId, seq) Tasks, gold (through revisions), exports, manifests Kind (Save, Complete, Withdraw), pinned form version, route, full pin map, resolved question set, exposure state. Owner session: kinds also PublicationGenerated, MergeResolved, MergeCarriedForward, UnmergeCarriedForward; the generating operation and generation; admissionBasis Per-answer state, requirement standing, qualification (§7.4, §7.5) Never (owner session: a publication may create one per affected session per generation, §8.11)
Draft sessionId (one record; bounded conflict copies) Nothing Nothing; lease, etag, patches (§7.6). Owner session: the head moves by CAS on draftVersion and each accepted autosave appends an immutable SessionDraftChange (§7.7) Draft-changes flag; draft_only category Counted in the manifest, never transitioned (owner session: rebased when its base is superseded)
Revision revisionId on a head Session versions, gold snapshots, tasks, queries, policy records (mapping) Everything; append-only Current (relative to pins), outdated, valid under a version Never
Head AnswerContextKey including classSeq (§6.1) Session versions (through revisions) The key; currentRevisionId changes by CAS only Current, outdated flags, conflicted Never
Entity instance Label head ID in the author's scope (§6.5) Context keys (entity path), outcome cells, population membership The ID; rename is a label revision; delete is withdrawal Population membership attribute, order (presentation) Never
Entity type entityTypeId (system IDs minted at F1a, §3.1) Question identity The ID; legacy category string is a display alias Capabilities (C1) Never
Gold snapshot (studyId, seq) Queries, exports, PRISMA manifests Entries (question context, reconciled revision ID), provenance goldNeedsReReconciliation per entry (§9.4), pending-query flag Never
Reconciliation task input set (taskId, seq) Gold snapshot provenance Pinned form version and candidate session versions per input set Held per question, drift state (§9.2, §9.3) Never
Screening outcome projection (studyId, profileId) Nothing Nothing; rebuildable with its input-version vector Entirely (consistency model §8) Never
Publication policy record (operationId, generation) Nothing; read by derivation Each generation once written; FV4 appends a generation (§8.7) Every effect on sessions (owner session: every effect not written as a generated version; generated versions reference the operation and generation) This record is the impact flow
System question version (systemGuid, systemQuestionVersion, seq) Form versions, revisions Everything once published by CAMARADES Class, as for any question None until a project form publishes a pin (§3.7, D2-06)
Template (templateId, seq) Nothing after copy; copies record the source (templateId, seq) Everything once published Nothing Never; a copy never changes with its template (DP4). Owner session: the system scope is the CAMARADES catalogue (CatalogueItemVersion, §16.5)
Claim Not a version kind. Claims are keyed by form or profile identity, never by version (brief §2.1, RT-11)
Design draft (owner session) draftId with scope (form or profile); changes (draftId, draftRevision) Nothing; a published version records the actor, time and base revision of each change it contains Every DesignDraftChange; the head moves by CAS Draft working state Never; publishing from a draft is the impact point (§3.9)
Study target override (owner session) (project, study, form); versions (overrideId, seq) Accepted result versions, readiness snapshots Every version (value, basis, made against form version and standard target, reason, actor, time) Effective target (§4.7) None by itself; a standard-target publication gives each override an explicit treatment (§8.12)
Study version (owner session) (studyId, seq) Study.currentVersionId; merge and unmerge records Every version, including per-field provenance and reference and source-document links (§16.1) Displayed bibliography Never; merges and unmerges are C21 operations
Stage study filter version (owner session) (stageId, filterSeq) Stage settings versions; pool events; ReviewStartEligibility The clause tree, dependency set and digest Pool membership at read (§16.2) Never for sessions; activation runs a pool history sweep
Accepted result version (owner session) (project, study, form, seq) Gold snapshots; queries; exports; stage filters through filterInputs[] Authority, acceptance method, origin, input set, form and override versions, effective target, actor, snapshot (§9.8) Standing (Current, InputsChanged, BelowCurrentTarget, SuspendedByTarget, NeedsReReconciliation, Superseded) Never; a target or policy change creates a new version only through the entitled rule or person
AI screening model configuration (owner session) (project, configurationId, seq) Profile versions (source policy), runs, decisions Every version (§16.3) Nothing None by itself; a profile version must pin a new version to use it
External screening decision (owner session) (project, profile, sourceKey, study, seq) Outcomes and adjudication versions as inputs Every version (§16.3) Current pointer (latest accepted) Never
Training reference and policy versions (owner session) (project, referenceId, seq); (project, policyId, seq) Training steps (through stage settings versions), attempts, assessments Every version (§16.4) Attempt results under the pinned versions Never for live work
Adjudicator assignment, classification rule, catalogue item and notification content policy versions (owner session) Per record (§16.5) Adjudication tasks; inference results; project copies; email shaping Every version As each record says Never

Rules that hold for every kind:

  1. Identity never changes. A structural change creates a new identity; lineage is recorded by reference (derivedFrom, copiedFrom, supersedes), never by rewriting.
  2. Versions are append-only with a content digest. Nothing published is edited or deleted (QD1, SL2, GS1). Discard applies only to unpublished definition versions and to drafts (§3.8, §7.6).
  3. Pins reference exact versions by ID. A reader never resolves "latest" to find what a stored record meant.
  4. Effects are derived, never written. Publication, option retirement and definition changes change what readers derive; they write no session versions and no revisions, with the single exception in §8.9. Superseded by the owner session (5 October 2026; D2-01 amended): a publication may write attributable generated session versions and mapped revisions under the generation table in §8.11; option retirement and definition changes still write nothing by themselves; where a publication generates nothing, effects stay derived.
  5. Impact flows start only at form or profile publication. Committing a question version, changing a setting or publishing stage settings never prompts an admin about sessions. Owner session: a standard-target change is a form publication and so starts the impact flow (§8.12); changing an operational setting or a Study target override shows an active-work impact preview but is not a publication.
  6. Operational settings never version. They change with an audit entry and are read live (D2-05). Owner session (D2-05 amended in part): the standard target is inside the form version; every other setting is classified by §4.6, and settings that change what counts as valid evidence, how evidence is produced or how an accepted result is formed are version content.
  7. Time is transaction time. Order comes from per-aggregate sequences and the HLC commit stamp (consistency model §11); observedAt and legacy DateTimeCreated are evidence fields and never order anything (§10.5).

3. Questions

3.1 Identity

A question is identified by a QuestionRef value object and a set of structural properties:

Property Role Note
questionId Identity For system questions the fixed GUID from AnnotationQuestion.cs:396-434 (CODE-MAIN)
systemQuestionVersion Identity, system questions only The v0 and v1 structural variants of the outcome error-type question have different parents and option filters (AnnotationQuestion.cs:559-592, CODE-MAIN), so they are different identities that share a GUID. A project has exactly one value (Project.cs:475), so the pair is unambiguous within a project. Null for project and profile questions
definitionOwner Identity project, or profile:<profileId> for eligibility questions (DP4). Named definitionOwner to keep it apart from the revision edge owningParent (§6.4, DD-26)
parent Identity Parent QuestionRef; the subtree shape (FEAT-001 D38, docs/features/annotation-versioning/design-session.md:90, RECOVERED)
entityTypeId Identity The entity type (today's category string). Stable system IDs for the seven legacy categories plus cohort, outcome measure and experiment are minted at F1a as a shared value object of C4 and C13, and the legacy string becomes a display alias with no identity change (VA-25, DD-12). R2a forms pin the ID, so C1 later adds capabilities without re-identifying anything
repeatable Identity Whether the question creates repeated instances (today Multiple && !AnswerArray). It is identity because it changes the shape of the context key (an instance element appears in entityPath)

Data type and selection multiplicity (D2-03). Two designs exist in the earlier documents:

  • D38 (FEAT-001): data type, parent and grouping are identity; changing data type creates a new question (design-session.md:355, RECOVERED). Cost: with parent also identity, every descendant must be recreated, which severs SF5 lineage and answer history for the subtree (VA-16).
  • D008 / K007 (QM v2): data type and multiplicity are version content because revisions pin the version that defines the payload (docs/planning/qm-v2-context/qm-v2-architecture-and-knowledge.md:36, :442-470, RECOVERED).

Recommendation (Batch D D2-03): data type and selection multiplicity (single or multi-select, today AnswerArray) are version content and a change to either is always classified incompatible (§3.5), so lineage and history survive and nothing is ever compared or carried across the change. Parent, definition owner, entity type and repeatability stay identity. Until D2-03 is answered the designer refuses data-type and multiplicity edits on a published question, which is today's behaviour and loses nothing.

3.2 Content versions

A content version is (QuestionRef, seq) with seq sequential from 1 (ASSUMPTION A-25: versions are linear per identity). Content:

Field group Content
Presentation wording, description, help, control type, display labels, option order, answer label (AnnotationQuestion.cs:138-150, CODE-MAIN), category guidance reference
Shape data type, selection multiplicity (D2-03), validators
Options the option list (§3.3)
Applicability conditional-parent condition expressed in parent option IDs or a boolean; option parent filters in option IDs
Extensibility response modes and metadata fields (§3.4)
Change record "Why it changed" and "What reviewers need to do differently" (VU2, optional)
Compatibility compatibleWithPrevious with the system suggestion, the admin's confirmation, an optional rationale, and an optional option mapping (§3.5, §8.9). Owner session: compatibility {suggestedBySyrf, declared, declaredBy, declaredAt, rationale} and optionMapping {oldOptionId → newOptionId, declaredBy, declaredAt}, declared by the authorised publisher (RD §3.4)
Integrity contentDigest, author, HLC stamp

Requiredness is not version content: a form or profile decides whether a pinned question is required (§4.1), so one question can be required in F and optional in G. This differs from today's Optional on the question and from QM v2's AQVersion.Optional (VB-17), deliberately.

Committing a version has no session impact and no admin prompt (VA improvement 3). It makes the version available for form and profile composition. A version that no published form or profile version references may be discarded (§3.8).

3.3 Options and stable option IDs

Today an option answer is stored as the option's value string (StringAnnotation.Answer, Annotation.cs:122; StringArrayAnnotation.Answer, Annotation.cs:210; CODE-MAIN). Schema-v1 options are keyed by Value only (OptionInfo.cs:105); schema-v0 options carry a stable OptionInfo.Id that the setter keeps only by matching the old value (AnnotationQuestion.cs:275-289); v0 single-option conditions reference _v0OptionId (Target.cs:291) while v1 and ADR-011 hybrid conditions reference value strings (Target.cs:50; docs/decisions/ADR-011-schema-v0-multi-option-conditional-parent-answers.md:50-68); parent filters validate against the live parent's values (OptionInfo.cs:51-60). Renaming a value therefore re-means every stored answer, and a meaning change that keeps the value is invisible (VA-05). The classification research already asked for versioned option identity (../unified-annotation-classification-research.md:186).

Rules:

  1. Every option has an optionId (GUID, minted once, never reused) inside its question. A version lists {optionId, value, displayLabel?, description, parentFilter (parent option IDs), state: active | retired}.
  2. Canonical answers store option IDs; values and labels are display data resolved from the pinned version. Exports carry both (§10.2).
  3. Renaming value or displayLabel keeps the ID and is compatible. Retiring an option, or minting a new ID because the meaning changed, is incompatible unless a one-to-one mapping is recorded (§3.5, §8.9).
  4. Conditions and parent filters reference option IDs and are validated against the pinned parent version at composition time (§4.2), not against the live parent.
  5. Adoption mints IDs per legacy value, reusing OptionInfo.Id where a v0 project has one, and maps answers by value (§11).

3.4 Response modes, metadata and suppression

The approved extensibility architecture (docs/features/question-management/annotation-question-extensibility-architecture.md:87-93, :169-191, RECOVERED) defines responses as a value or a response mode, validated metadata, a required reason on modes that need one, descendants that a mode suppresses are preserved and never tree-shaken, exports that must resolve suppression, and a definition-version stamp on every response. It leaves open whether definitions are frozen once used or snapshotted per response. This model settles that question by frozen versions (PH-06):

  • Response modes and metadata fields are version content (§3.2).
  • A revision payload is value XOR responseModeId, plus metadata validated against the pinned version's fields; the definitionVersion stamp is the revision's questionVersionRef.
  • Suppressed descendants are preserved. A canonical Save or Complete keeps every pinned revision whose question an ancestor's answer or mode now suppresses; the per-answer state is NotApplicable (§7.4), derived per ancestor instance, never per question. The legacy path tree-shakes (ExtractionInfo.cs:183-194, CODE-MAIN); the canonical path never does.
  • Exports resolve suppression with an explicit status column or by omission, never by emitting a suppressed value as live (§10.2).
  • "Not applicable" is an explicit response mode. Blank is not N/A (UA1); two explicit N/A answers agree and N/A against a value disagrees (AG3).

3.5 Compatibility

Compatibility is defined once, here, and used by SF3, SF5, FV3, AG3, RE4, RE5 and the head key.

Declaration (D2-02). compatibleWithPrevious(Q, n) is declared when version n is committed in the designer. The system computes a suggestion from the diff with n−1; the admin confirms it, may tighten freely, and may loosen only where the table allows. The declaration is immutable once any revision pins version n. Before that it may change, and the change recomputes the classes of n and its successors (all unpinned) and re-validates every active publication policy that references them; a flip that would make a recorded autoUpdate invalid is refused until FV4 revises the policy. FV4 revises policy, never compatibility.

Superseded by the owner session (5 October 2026; D2-02 amended, Q-34 decided; OWNER). Compatibility belongs to the immutable question version. SyRF suggests a default from the diff; the authorised publisher explicitly declares compatibility and whether option mapping is appropriate, with actor and time. A question version is normally committed as part of publishing the first form or profile version that pins it, so the declaration is the publisher's at that moment. The declaration and the mapping are immutable from commit, so the "before that it may change" window above no longer exists. A meaning change must be declared incompatible. Mapping never bypasses type or option validation, and invalid or mandatory re-review constraints cannot be bypassed. A wrong declaration is corrected by guided rollback and republication (§8.13): a corrected question version with the same content and the corrected declaration, a new form version, and generated versions that restore or re-treat the affected sessions; the wrong declaration stays in history with a "corrected by" link. In the table below, "Admin may" reads "Publisher may" (RD-R20, RD-R21).

Change between n−1 and n Suggestion Admin may
Wording, description, help, display labels, control type, option order, answer label, guidance compatible tighten to incompatible (a wording change that alters meaning)
Option added; option value or label renamed keeping the ID; parent filter changed; condition changed; validator loosened or tightened; response mode or metadata field added or loosened compatible tighten
Option retired; option replaced by a new ID (meaning changed); response mode removed; metadata field removed or made required incompatible loosen only with a one-to-one option mapping covering every retired option and no free-text change (Q-34 OPEN, SR-16; owner session: Q-34 decided, the mapping is explicit per option and allowed only where meaning is unchanged; a declared meaning change cannot be mapped)
Data type or selection multiplicity (D2-03 recommendation) incompatible never loosen
Parent, entity type, definition owner, repeatability not a version; new identity n/a

Classes. class(1) = {1}; class(n) = class(n−1) ∪ {n} if compatibleWithPrevious(n), else {n}. classSeq(n) is the lowest seq in class(n) and is stored on the version when the class is settled. Compatibility is therefore an equivalence relation: two versions are compatible if and only if they share classSeq, however far apart they are. This is exactly FEAT-001's transitivity rule ("breaking if any skipped version was breaking", docs/features/annotation-versioning/README.md:360-368, RECOVERED) restated as classes (PH-18).

Asymmetry is handled by validity, not by the class. "Options were only added" makes v2 compatible with v1, yet a v2 answer that selects the new option is not valid under v1. The class says the two versions may share a head and be compared; §3.6 says whether a particular answer is valid under a particular version.

How each decision uses it:

Decision Use
SF3, SF5 Answers are shared only within a class (one head per class, §6.1); lineage across classes is shown read-only
AG3 Agreement compares only within a class and flags differing versions inside it (§10.3)
FV3 A completed session satisfies a changed question only when the pinned revision's class matches and the answer is valid (§7.5)
SR-16 autoUpdate is allowed only within a class and only for valid answers (§8.2)
RE4, RE5 A question is held when candidates' pinned revisions span classes (§9.2); prefill only within a class
GS1 Gold whose class differs from the form's current pin is flagged for re-reconciliation (§9.4)

3.6 Per-answer validity

valid(r, v) for a revision r and a question version v of the same QuestionRef holds when:

  1. classSeq(r.questionVersion) = classSeq(v); otherwise validity is not evaluated and the answer's state is NeedsUpdatingVersion (§7.4);
  2. the payload shape matches v's data type and selection multiplicity (true by construction inside a class);
  3. for option answers, every selected optionId is active in v, or an applied mapping (§8.9) has produced a policy-derived successor revision that is valid;
  4. a response mode used by r exists in v, and metadata validate against v's fields;
  5. v's validators accept the value.

Applicability is separate: whether the question applies at all in the session's resolved graph is evaluated by the E23 evaluator and yields NotApplicable, never "invalid". The same evaluator runs on the server for Complete, for Needs updating (VU1), for task validity (RE2) and in AF2 with shared fixtures (E23; FEAT-020's rules file per PH-07).

3.7 System questions

Today system questions are rebuilt from code on every read with fixed GUIDs shared by all projects (Project.cs:225-231, AnnotationQuestion.cs:813-816, CODE-MAIN) and their structure varies by Project.SystemQuestionVersion (AnnotationQuestion.cs:559-592). A snapshot "per code revision" (the package's E24) would make every CAMARADES wording fix a forced FV2 prompt in every canonical project (VA-09).

Rules (D2-06):

  1. System questions are stored as data in a global pmSystemQuestionVersion collection keyed (systemGuid, systemQuestionVersion, seq) with a structuralDigest, seeded idempotently from code at start-up and never rebuilt per read on canonical paths (VB-11). The seed for seq = 1 is today's definition for each systemQuestionVersion that exists in production.
  2. Identity is (systemGuid, systemQuestionVersion) (§3.1), so the v0 and v1 structural variants are distinct identities. definitionOwner = system.
  3. CAMARADES publishes content versions through the designer with the same compatibility declaration as any question (application role; D2-15 for template curation).
  4. Project forms pin a system version like any other. A new system version reaches a project only when its admin publishes a form version that pins it. Deploying code that seeds a new version changes no published form and prompts nobody; the designer shows "newer system version available" and the next publication offers it with the ordinary treatments.
  5. Answers to system questions use the head key with the system QuestionRef; the project ID in the key keeps them project-scoped.

3.8 Lifecycle and in use

Published. A question is published once any of its versions is referenced by a published form or profile version, or once it is adopted from a legacy project (§11). Thereafter (QD1, OWNER):

  • it can be removed from a form by publishing a form version that no longer pins it (its answers stay in history, §8.2);
  • it can be retired (status that refuses new pins in any form or profile version);
  • it can never be deleted, and no code path may delete it: the legacy delete cascade (Project.cs question delete) refuses canonical questions, and the integrity checker (§12.4) treats a missing published definition as a corruption.

Discardable. A committed version that no published form or profile version references, and any pending edit, may be discarded by the designer with an audit entry. A question none of whose versions is published may be deleted (AC-R2a-10's "unpublished draft question").

In use is defined per container because each needs a different test (VA-13):

Container In use when Consequence
Question version any revision pins it compatibility declaration immutable (§3.5; owner session: immutable from commit, whether or not anything pins it)
Question version any published form or profile version pins it not discardable (QD1)
Form or profile requirement version it is published immutable; A-14's "a draft form can change until first use" means an unpublished requirement version. Preview (the AF2 admin host) creates no session
Form or profile requirement version any FormSession (including draft-only), task or gold entry references it reported in usage evidence (PS1); never a mutability question, because published versions are already immutable
Stage settings version it is bound by an Active stage its bindings govern admission (§5.4)

A reviewer session can only exist against a published requirement version, so the "draft-only session against a draft definition" case cannot arise; the research's warning against turning an unedited open into a persistent session is met by creating the session on the first autosave (§7.1).

3.9 Designer pending edits

Designer edits before commit are a per-definition pending record with the same lease, etag and take-over model as reviewer drafts (§7.6), so two admins editing one question get a typed conflict and presence is a hint, never a control (QM v2 D010 harvested; PH-33). No pending-edit trail is kept beyond the current record.

Superseded by the owner session (5 October 2026; D2-11 decided, collaborative question drafts amendment OS-A01; OWNER). Pending edits live in design drafts. A DesignDraft is a named, unpublished proposal for the next version of one form or profile, including pending question edits; several may exist for one form. Each accepted edit appends an immutable DesignDraftChange (draftId, draftRevision) recording the actor, time, item changed, the item revision it was based on and the before and after values; the draft head moves by CAS. A stale base is refused and the local edit is kept beside the current value for "keep mine" or "discard mine"; nothing is overwritten silently. Accepted changes appear in every open view while unsaved local edits survive. Authorised presence (viewers and editors) is shown only to users with design access, lives in hub connection state and is never an audit record. A draft never changes a published requirement or a reviewer's form and never starts an impact flow. Only one publication operation per form runs at a time; a draft published after another rebases item by item and recomputes its impact against the resulting state (RD §3.7, §4.7; RD-R25, RD-R26). The design-draft change log is the pending-edit trail.

4. Forms

4.1 Requirement versions

An AnnotationFormVersion is (formId, seq), immutable once published, containing:

  • the ordered pins: one (QuestionRef, seq) per question, with every ancestor included automatically (FEAT-001 parent integrity, README.md:244, RECOVERED); at most one version per question identity (ASSUMPTION A-26);
  • per pinned question: required or optional, and for repeatable questions the minimum instance count if any;
  • the validated applicability graph (§4.2) and the renderability result (§4.3);
  • the entity types present, and from O1 the allowed outcome-schema versions (C14);
  • contentDigest, publisher, HLC stamp, and the publication operation that published it (§8);
  • owner session (5 October 2026): FormVersion.standardTarget, the form's standard reviewer target (target-versions-form, OWNER), and ReconciliationPolicy {targetOneHandling ∈ AutoAccept, RequireHumanReconciliation (R1, OWNER; baseline conversion alone may set NoAcceptance, RS-R04a PROPOSAL); acceptedCompleteness; identityBlinding; reconciledHints; outdatedAnswerCompletion}, the last four classified inside the version as PROPOSALs (§4.6).

Publishing a new requirement version is the only way to change any of this (FV1). Removing a question, reordering, changing requiredness or upgrading a pin all create a new version. Owner session: so does changing the standard target or the reconciliation policy, even when no question changes (§8.12).

4.2 Composition validity

A requirement version is composable only if, for every pinned child version, every conditional-parent reference and every option parent filter resolves to an active option ID in the pinned parent version (VA-06). Today these are validated against the live parent (OptionInfo.cs:51-60, Target.cs:160-194, CODE-MAIN), which is exactly what a version-pinned form cannot rely on. The designer refuses composition with a typed error naming the child, the parent and the missing option; the admin picks a compatible parent version or commits a new child version. The validated graph (nodes = pinned versions; edges = conditions and filters by option ID) is stored in the version and is what the E23 evaluator reads. Dependency cycles are refused as they are for steps.

4.3 Renderability

Canonical forms render on AF2 only; there is no AF1 fallback for canonical routes (VB-08). AF2's stage-review host fails closed on supported categories, exactly one label question per mutable-unit category, parent-closed hierarchies and ordinary roots without targets (src/services/web/CLAUDE.md:353, CODE-MAIN), and its eligibility reads projectDetails.annotationQuestions and stage.annotationQuestions (annotation-form-v2-eligibility.ts:263-274, :295, CODE-MAIN; VB's citation of annotation-form-data-source.ts:25, 748 for the stage-keyed question source is UNVERIFIED here). Therefore:

  1. The AF2 structural guards are part of publication validation, run server-side against the requirement version with fixtures shared with AF2. A version AF2 cannot render is refused at publication with a typed FormVersionNotRenderable error.
  2. AF2 gets a VersionedAnnotationFormDataSource (seam frozen at F1c, built in R2a; E44) that loads the session's pinned form version and its question versions by ID (immutable, cacheable, §12.5), never the current stage's question list.
  3. A canonical route whose version cannot be rendered shows a typed error visible to admins; it never falls back to AF1, which posts legacy submissions that R0 refuses for canonical scopes.

4.4 Operational settings

Form settings are not in the requirement version (VA-07; D2-05). They live on the form head, change by CAS with an append-only audit entry, are read live, and never start an impact flow:

Setting Source
Minimum review target SF2, SF4 (a minimum, so a task is largely target-independent once open). Superseded by the owner session (5 October 2026): the standard target is inside the form version (§4.1, §4.6)
Optional capacity cap D3-17 (off by default; separate from the target). Owner session: the form-owned CapacityCap baseline applied through every route; a stage may set only a stricter route cap; defaults proposed, not approved
Reconciliation compare settings C9
Gold-completeness policy Q-04 (OPEN). Owner session: Q-04 decided; the completeness override is inside the form version (ReconciliationPolicy.acceptedCompleteness, classification PROPOSAL)
Form-level guidance A-15
Bulk-approve flag Q-11 (OPEN, after R4a). Owner session: Q-11 decided; bulk acceptance is an operational setting, off by default
Inactivity timeout; per-reviewer in-progress limit (owner session) D2-07, Q-28: form-owned across every route and tab; defaults not approved
Unstarted reconciliation-assignment expiry (owner session) Q-30: optional, admin-configured, off by default; the profile holds the equivalent for adjudication tasks

Sufficiency reads the current target, as SessionCountTarget does today. Changing the target is an audited setting change under the FEAT-024 definition-rewrite fence (C8), not a publication. Superseded by the owner session (5 October 2026; target-versions-form, OWNER): sufficiency reads the effective target (§4.7), and changing the standard target creates a new form version and goes through the publication impact process, even without question changes (§8.12). The FEAT-024 definition-rewrite fence is admitted in that publication's phase 1 as for any definition move.

4.5 Templates

A template version is (templateId, seq). Instantiation copies question versions into the project (or profile) as new identities at seq = 1, recording copiedFrom = (templateId, seq, QuestionRef). A later template change never changes a copy (DP4, SET1; ownership D2-15). Owner session (D2-15 amended): the system-scoped templates are one application-wide CAMARADES catalogue of immutable CatalogueItemVersions; a copy records copiedFrom {catalogueId, itemId, versionId, copiedBy, copiedAt} on the new canonical definition records; a project edit never changes the catalogue and a new catalogue version never changes a copy; adopting a newer catalogue version is an explicit copy into a new project version through ordinary publication (ACD §3.4).

4.6 Setting classification (owner session, D2-05 amended in part)

The owner fixed one row: the standard target is inside the form version. Every other row is a PROPOSAL for the brief, using one rule (RD §3.5): a setting goes inside the version when it changes what counts as valid evidence, how evidence is produced or how an accepted result is formed, because the records it affects must pin it; a setting stays operational when it changes only who is offered work and when. Operational changes are audited, read live, recorded on the events that use them, and preceded by an active-work impact preview where they affect people working now.

Setting Owner Classification Basis
Question pins with ancestors, requiredness, minimum instances, applicability graph, renderability result Form Inside the form version FV1; §4.1
Standard reviewer target (FormVersion.standardTarget) Form Inside the form version OWNER (target-versions-form)
Target-one handling (AutoAccept, RequireHumanReconciliation; conversion-only NoAcceptance) Form Inside the form version OWNER (R1); NoAcceptance is PROPOSAL (RS-R04a)
Accepted-answer completeness override Form Inside the form version (PROPOSAL) Q-04
Reconciliation identity blinding Form; the profile for screening reconciliation and adjudication Inside the version (PROPOSAL for forms; profiles version it) Q-28, Q-30, blinding amendment
Reconciled-answer hint baseline Form; a step may only hide Inside the form version (PROPOSAL); the step override is in the stage settings version Q-28 hints amendment
Outdated-answer completion mode Form Inside the form version (PROPOSAL); the mode in the declared version applies D4-17 (O3)
Sufficiency, Unsure, tie policy and bound, discussion, adjudication rationale, reason handling, source policies Profile Inside the profile version (tie policy by owner decision; the others PROPOSAL) S1, S2, Q-32, S3, E3; §5.1
Inactivity timeout; per-reviewer in-progress limit Form Operational (PROPOSAL) D2-07, Q-28
Capacity cap (separate from the target) Form baseline; stricter route cap in the stage Operational, off by default (PROPOSAL) D3-17
Unstarted assignment expiry Form (tasks); profile (adjudication) Operational, optional, off by default Q-30
Bulk acceptance Form Operational, off by default Q-11
Adjudicator assignment Profile, as AdjudicatorAssignmentVersion Its own versioned, audited record; no profile publication Adjudicator assignment amendment
Comparison display; guidance text Form Operational D2-05

4.7 Standard target, Study target overrides and the effective target (owner session)

  • Effective target (derived, never stored as a fact; projected in Study.currentEvidence): the current StudyTargetOverride value for the Study × form unless it is Removed, otherwise the current published form version's standardTarget (OWNER consolidation §1; precedence RS-R05 PROPOSAL). There is no step-level override, and a stage never supplies a target (RD-R31).
  • StudyTargetOverride is an immutable, versioned Study × form requirement decision applying across every route: versions (overrideId, seq) with value, basis (SetByAdmin, AdditionalReviewRequest(requestId, raiseBy), MergeConfirmation(mergeId) or Removed), madeAgainst {formVersionSeq, standardTarget}, reason, actor, time and clock stamp. Changing an override creates no form version (RD-R14). An additional-review request raises the effective target through an override version per Study and counts qualifying reviewers once across routes (RD-R17).
  • Pins. Accepted result versions and readiness snapshots record the form version and any override version they used (RD-R15), so a later target change never reinterprets them.
  • Override treatment at publication. When a new standard target is published, the preview lists every override with an explicit treatment: keep its effective value, rebase it, or remove it. The suggested default keeps each Study's effective target unchanged and flags overrides that become redundant or fall below the new standard; no effective target changes silently (RD-R16; the treatment set is a PROPOSAL, the explicit definition is the owner's requirement).
  • Results. A target change may lead to a new accepted-result version under the agreed authority rules; it never rewrites an old result, invents a missing review or invalidates a recorded answer (RD-R29; §9.8).
  • Statistics. General statistics show the standard target and list study-specific exceptions separately; per-Study completion uses the effective target (RD-R19; RD ambiguity 2).
  • The target is a minimum and stays separate from any enforced capacity cap (RD-R18).

5. Screening profiles and stage settings

5.1 Profile criteria versions and profile settings

The same split applies (VA-19). A ScreeningProfileVersion is (profileId, seq) and holds the scientific requirement: eligibility question pins with ancestors (profile-owned questions, DP4), decision rules (which answers derive Include or Exclude, DP3), the must-agree supporting-answer set (RX1) and requiredness. It is immutable once published, pinned by decision revisions and profile-owned answer revisions, and published through the Q-26 flow (OPEN; until answered, a profile version can be published only before any decision exists under the profile). Owner session (5 October 2026): Q-26 is decided; the "only before any decision exists" restriction is superseded by the mismatch detection and treatment in §5.5.

Profile settings live on the profile head with audit history and no impact flow: the exclusion-reason reconciliation toggle (DP5; Off keeps recorded reasons and history), agreement and resolution routes (RX1), rationale settings (Q-32), the Unsure/Maybe and discussion-route options if D4-01 and D4-02 are approved, and the reference to the project's PRISMA phase mapping version (C12; the mapping itself is project-level per DD-20).

Superseded in part by the owner session (5 October 2026). Profile settings that change how outcomes are derived are version content of the ScreeningProfileVersion and change only through profile publication with an impact preview: the sufficiency rule (initial decisions and agreeing definite decisions required); unsureEnabled (D4-01 decided, on in the proposed title/abstract template); tiePolicy ∈ ExtraReview with an extraReviewBound, or Adjudication, with handling per trigger (decision tie, unresolved Unsure, reason disagreement, sole-source AI-model Unsure) and no inferred default (tie amendment, OWNER); discussionEnabled (D4-02 decided, with an optional admin-set time limit and no default duration); adjudicationRationaleRequired (Q-32, off by default); reason handling (DP5, must-agree answers RX1, primary-reason derivation as template guidance, no question-count limit; S3, D4-13); identityBlinding (Blinded default, blinding amendment); and the ScreeningSourcePolicy (E3; the slot is reserved at F5 and populated only from XS1, §16.3). The tie policy's placement is the owner's decision; the others are RS's classification (PROPOSAL). The profile head keeps only the PRISMA phase mapping reference, optional adjudication-task expiry (Q-30, off by default) and keyword lists if profile-owned (PH-28). The adjudicator assignment is a separate AdjudicatorAssignmentVersion (§16.5) whose change needs no profile publication (RS §3.7, §3.12).

5.2 Decision heads and profile classes

A profile criteria version carries compatibleWithPrevious like a question version. Suggestion: decision-rule change or must-agree change that adds a requirement → incompatible; eligibility question changes inherit from the questions' own classes; everything else → compatible.

Decision heads (kind ScreeningDecision) are keyed {project, study, authorScope, profileId} with no question and no class (§6.1). Unlike a shared answer, a decision has exactly one requirement owner (its profile), and two active stages never present different versions of one profile at the same time (§5.4), so the branching that VA-02 found for answers cannot occur. The profile's class is used only to derive a decision's standing under the recorded Q-26 policy (pinnedOlderVersion, needsRenewal) and to decide which cast decisions a collective outcome may read (consistency model §8).

5.3 Stage settings versions and lifecycle

A StageSettingsVersion is (stageId, seq) on the Stage aggregate (brief §1.12) holding bindings (form and profile identities with the version bound and when), steps, dependency edges, route policies, VS1, BL1 and EW1 defaults and the filter element. It references the allocation regime and batch plan by ID; allocation, batch, expiry, target enforcement, in-progress limit, idle timeout and tracking settings are operational and live in their own records with audit (VA-08a; D2-05; D3-18). PV1's "exact stage-settings version" stamp on a revision refers to this requirement-bearing part only.

Owner session (5 October 2026; Q-15 replaced, Q-01, Q-24, Q-28 decided; SP §3.2 to §3.4): the settings version embeds the StageStudyFilterVersion (§16.2), replacing the filter element; the route policies (the DP6/DP7 cross-stage part) are superseded because no incoming stage-entry gate exists; BL1 and the VS1 default leave the stage (blinding and the hint baseline belong to the form or profile version, §4.6), and a step may only hide hints; EW1 becomes the saved-work setting. The version adds the progression basis (OwnIncludeSufficient default or CollectiveIncludeRequired) with a tighten-only step override, step kinds (review, system adjudication, training), extra screening after sufficiency (Stop default for new combined steps), exclusion-stop (personal, collective, scope), the started-work-after-departure setting (placement SP-AMB-01), an optional route capacity cap stricter than the form's baseline, and the browsing setting. The capacity cap baseline, the inactivity timeout and the in-progress limit move to the form's operational settings, and the most-restrictive-stage rule for them is superseded. A settings version that changes only steps keeps its filter version. Publishing a settings version still changes no session pin and no qualification; a new filter version starts a pool history sweep (C20).

Lifecycle status is an append-only event list; Active is derived from status; Completed freezes the bindings for display and readiness (RX2, RECOVERED from the v10 prototype lines cited in the ledger) and refuses settings publication except through an approved change request (LC1).

5.4 PV2 binding meaning

PV2 says stage settings bind form and profile versions and historical work pins its requirements, and leaves the transition undecided. Two readings exist (VA-08b): a stage pins a version it presents (so two stages can present different versions of one form to the same session, which contradicts SF1's one session per study and form), or a stage binds the form and records which version it bound and when.

Recommendation (Batch D D2-04): the second. A stage binds the form (or profile) identity and records the bound version and time; the live route always presents the session's resolved version (its pinned version, or the current published version after Upgrade, §7.3); a Completed stage's frozen binding governs only historical display and readiness evaluation for that stage's reports. Consequences: publishing a form version is per form (FV2) and reaches every bound stage; binding a form to a new stage is an admission change for that stage, never a version transition for sessions; claims are keyed by form identity (RT-11). Owner session: D2-04 is an engineering contract with no owner question; the recommendation stands and the effective target never comes from a stage (RD-R31).

5.5 Screening profile publication and mismatch preview (owner session, Q-26 decided)

Screening profiles have immutable versions (OWNER, Q-26; RD-R27). Publishing a profile version:

  1. Preview. The proposed version is compared with every profile version still in use by existing decisions, collective outcomes and adjudications. The preview shows each mismatch and its consequence per Study, active screeners and adjudicators, dependent stage pools and accepted results, open ties, AwaitingExtraReview and PendingAdjudication Studies, open discussions, in-flight extra decisions, and whether eligibility changes and so needs a protocol amendment entry in the same publication (D4-05).
  2. Treatment. The admin chooses, at profile scope and never per stage: keep decisions pinned to their version; request new decisions; or carry compatible decisions forward as attributable generated profile-session versions. Open adjudication tasks and discussions created under the old version continue and their results stand; Studies with no open work item are re-evaluated under the new version only as the chosen treatment says (RS §4.12, PROPOSAL).
  3. Recheck and phases. Phase 1 as for forms (§8.5) with a final recheck at commit. Phase 2 works per Study: generated profile-session versions where the treatment carries forward or requests renewal, the collective outcome recomputed under the chosen policy, the Study write and notices.
  4. What never changes. Original decisions, outcome history and frozen reports. Required new screening stays visibly pending. Changed outcomes trigger targeted pool reevaluation with structured history (C20); accepted or adjudicated results whose inputs changed follow the explicit reconsideration rule (RS-R58).
  5. Source policies. A change to a ScreeningSourcePolicy (adding or removing an external or AI source, or changing its role, scope, mapping or the pinned model configuration version) is a new profile version through this flow; decisions accepted under the earlier version keep it (§16.3; RI-R35).

Fixture: FX-VM-51 (replacing the parameterised FX-VM-40 behaviour for decided Q-26).

6. Answers

6.1 Context key

AnswerContextKey (a shared-kernel value object frozen at F1a):

AnswerContextKey
  projectId
  studyId
  authorScope        candidate reviewer ID | reconciled | imported
  definitionOwner    project | profile:<profileId>
  questionRef        {questionId, systemQuestionVersion?}
  classSeq           the compatibility class of the pinned question version (§3.5)
  entityPath[]       instance IDs from the root (§6.5); empty for study-level questions
  populationId       default whole-study population ID derived deterministically from the study ID (DD-24)

classSeq is the change from the package's C2 (VA-02). One head exists per context and class. A revision under a version of a different class creates a new head with the same questionRef and a new classSeq; SF5 shows the older head's revisions as lineage, read-only. "Current", "contains outdated annotations" (SF5, SF6) and Fix are then defined within a class, and the VA-02 scenario (form F on Q v2 with requireReanswer, form G on Q v1 with doNothing) produces two heads that never flag each other.

Consequence to note: when two forms pin different compatible versions of one question, they share a head. An answer changed in F under v2 flags the reviewer's G session "contains outdated annotations" (correct: it is the same answer, changed by the same reviewer), and if the v2 value uses a v2-only option it is NeedsUpdatingValue in G's v1 context. The reviewer chooses whether to Fix (SF5); the flag alone never removes qualification (SF6). The publication dialog lists every other form that pins the question so admins can publish shared questions consistently (U6).

Decision heads omit questionRef, classSeq and entityPath; decision-owned answers add the owning decision's head ID (§6.4). The research's per-kind natural keys (../screening-specialised-annotation-research.md:675-682) are the source of these shapes.

6.2 Key hash and indexes

A unique compound index over entityPath[] is multikey, and MongoDB enforces uniqueness per array element across documents, so heads for (Q, [cohortA, tp1]) and (Q, [cohortA, tp2]) would collide on cohortA (VB-05; documented MongoDB behaviour, not exercised in this repository). Therefore:

  • the head stores the key as an ordered value object and contextKeyHash = SHA-256 over a versioned canonical serialisation (keySchemaVersion included in the hash input);
  • unique indexes are on scalars only: {projectId, contextKeyHash} unique; per kind, partial unique indexes such as {projectId, studyId, authorScope, profileId} where kind = ScreeningDecision;
  • non-unique {projectId, studyId, authorScope, questionId} serves ancestor and SF5 reads, and {projectId, questionId, currentQuestionVersionSeq} serves usage per question version;
  • the head _id is derived deterministically from the hash (§12.3), so a duplicate insert is a DuplicateKey that the command resolves by reload and CAS (consistency model §5).

6.3 Heads and revisions

A head holds the key, kind, state ∈ {Active, Conflicted, Withdrawn}, currentRevisionId (CAS), a denormalised copy of the current payload for reads, and version. A revision holds revisionId (client-proposed, validated, §12.3), headId, seq within the head, questionVersionRef (or a typed AuthoredUnder union for adopted revisions, §11), the typed payload (§3.4), owningParentRevisionRef? and ownedChildRevisionRefs[] (§6.4), PV1 provenance (source stage, step, stage settings version, accepted-answer revision shown), authorship ∈ {reviewer, reconciler, adjudicator, policyDerived, legacySnapshot, imported}, real actor and on-behalf-of, commandId, HLC stamp, recordedAt, observedAt?, contentDigest.

Rules: a revision is never edited; the head's current pointer moves only by CAS inside the command transaction (consistency model §4); a withdrawal is a revision whose payload is the withdrawn marker; "current" is a property of the head, while "pinned" is a property of a session version, and the two differ whenever the reviewer has changed the answer since that session version was made (that difference is the OutdatedOwnAnswer state, §7.4).

Owner session (5 October 2026): a mapped revision written by a publication keeps authorship = policyDerived with the reviewer as effective author and records the operation and generation (§8.9, §8.11); merge-created revisions on a consolidated Study copy the source revision's authorship and add merge provenance to the exact source revision (C21); a SingleAnnotator acceptance writes reconciled-scope revisions with derivedFrom the candidate revision and the system rule as actor (RS §3.2); a revision written by hint click-to-fill is an ordinary reviewer revision carrying adoptedFrom (RS-R34); promoted training answers carry promotedFrom (TI §3.6); imported is limited to mapped human annotation imports (XA1). Training sessions use author scope training(attemptId) and never share heads with live work.

6.4 Owner scopes

Two ownership notions share one word in the package (DD-26). They are named apart:

  • definitionOwner (project or profile) is part of the context key and says which requirement owner defines the question (DP4). Two profiles never share answers even with identical wording.
  • owningParent is a revision edge: a decision revision owns its reason revisions; an answer revision may own child answer revisions. Ownership forbids cycles, cross-study edges, a reason owned by two decisions, and a candidate child attached to a reconciled parent (../screening-specialised-annotation-research.md:726-733); the last is a C1 conformance test.

6.5 Entity instances

Nothing in the package said what an instance is or who mints it (VB-09). Today a unit's identity is its label root annotation (AnnotationSession.cs:68-71, CODE-MAIN), AF2 creates units inline with client IDs (src/services/web/CLAUDE.md:347), and delete prunes the unit's answers and outcome cells (:354).

Rules:

  1. Identity is the label head ID in the author's scope, minted once from a client-proposed, server-validated GUID (§12.3). entityPath elements are instance IDs: label head IDs for entity units, instance IDs for repeated-answer branches of a repeatable non-entity question, and option IDs for branches keyed by a multi-select parent's option (the element kinds are listed in the C2 value object, PH-16).
  2. Rename is a new revision on the label head. Identity, answers and cells are untouched.
  3. Delete is a set of withdrawal revisions on the label head and every descendant head of that instance, in one commit. It is append-only; the reviewer's other sessions pinning the instance are flagged OutdatedOwnAnswer on those heads; nothing is pruned. Outcome cells keyed by the instance become inapplicable by derivation.
  4. Duplicate mints new instance IDs; copied revisions carry copiedFrom = sourceRevisionId and the reviewer's authorship.
  5. Population membership is an attribute of the instance; the default population needs no document (DD-24). Outcome cells (O1) key on instance IDs.
  6. Presentation order lives outside versions in a per-session presentation record and never CAS-es the session head (AnnotationSession.cs:49, CODE-MAIN, is today's equivalent).

6.6 Conflicted heads

Legacy duplicates for one context cannot map to one head with a current pointer (VB-13a). A head adopted from conflicting legacy duplicates has state = Conflicted, holds two or more unordered legacySnapshot revisions with a conflictGroup, and no current pointer until the owner resolves it by Fix or Save. While conflicted it is shown with a conflict marker (SF5), excluded from prefill, agreement and autoUpdate satisfaction, and counted as "conflicted" in manifests. Resolution is the reviewer's first explicit revision, which becomes current; the snapshots remain history.

7. Sessions

7.1 Identity and creation

A FormSession is keyed (projectId, studyId, formId, reviewerId) with a deterministic _id (SHA-256 over a versioned canonical key, as CSUUID; VB-16, DD-15), so two tabs cannot create two sessions for one natural key. The document is created by upsert on the natural key at the first autosave, as an explicit Study-free write (consistency model §4); opening a study creates nothing. draft_only is therefore a real state of a real document.

The reconciler's session is not a FormSession: it is an entity of the ReconciliationTask with authorScope = reconciled and a current holder (§9.1), so the natural key never collides with a candidate session even if Q-36 allows self-reconciliation (V2-18).

Screening-only steps have no form. PROPOSAL for F3/F5 (the domain-model revision records it): the session container generalises to ReviewSession keyed (project, study, owner ∈ {form F | profile P}, reviewer); a screening-only step uses the profile-owned session whose versions pin profile criteria versions and decision revisions; a combined step writes a form session version and a profile session version in one transaction under one command ID; drafts follow §7.6 for both.

Claims are separate: canonical claims are keyed by form or profile identity, never by version, and the first explicit Save releases the reviewer's claim inside the canonical transaction (brief §2.1, RT-05, RT-11). Whether a draft holds the reviewer's place is D2-07 (§7.6). Owner session: D2-07 and D2-08 are decided; one session and one place serve every stage, tab and device, and the form owns the inactivity timeout and the in-progress limit (§7.7).

7.2 Session versions and pin maps

A FormSessionVersion is (sessionId, seq) with kind Save, Complete or Withdraw, the pinned form version, the route (stage settings version, step), the accepted-snapshot-available fact and exposure state (C3), commandId, HLC stamp and digest. Owner session: kinds also include PublicationGenerated (with the operation and generation, deterministic ID per session, operation and generation) and MergeResolved, MergeCarriedForward and UnmergeCarriedForward (with the merge or unmerge and the exact source versions); every version records admissionBasis (C5, SP §3.8), and the declared form version fixes the outdated-answer completion mode that applied. It pins the complete map of (headId → revisionId) for every answer in the session at that moment (VA-22), the shape FEAT-001's ASV already had (README.md:296-303, RECOVERED). Storage may delta-encode behind the aggregate; the logical contract, previous-version exports, candidate pinning, gold pins and E28's ceiling are all defined on the full map. Each version also stores its resolved question set (the pinned versions applicable in that session version, computed from the pin map and the form version's applicability graph), immutable with the version and recomputable from it (PH-18).

7.3 Transitions

Transition Who Writes Rule
Autosave reviewer draft only never a version, never Study (SL1)
Save reviewer revisions for changed answers, one incomplete session version becomes current (SL3); removes completed qualification; consumes the draft (§7.6)
Complete reviewer as Save, kind Complete validates every required applicable answer under the declared form version (§3.6); one qualifying contribution per reviewer, study and form (SF2)
Fix reviewer, from an outdated flag one incomplete session version pinned to the session's resolved version; opens the form (SF5) explicit; the prior completed version stays history; may be combined with Upgrade
Upgrade reviewer one incomplete session version pinned to the current published form version with the same revision pins 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; nothing is rebased because pins are unchanged; every Needs-updating mark then shows against the new version (VA-10)
Withdraw reviewer or admin (settled in the C5 ADR) one session version of kind Withdraw append-only; the session no longer counts or qualifies; revisions stay readable; the hard delete reviewers have today never applies
Versioned clear reviewer withdrawal revisions for every head in the session plus a Save replaces "Remove all annotations" (AC-R2a-09)
PublicationGenerated (owner session) a publication operation, attributed to the publisher; the reviewer stays effective author of the answers one session version per publication generation, re-pinned to the new form version, with the same revision pins or mapped revisions as the generation table says (§8.11) computed against the latest head and never overwrites a newer reviewer version; completed only if the pin map validates under the new version, otherwise incomplete; draft-only sessions get none (their draft is rebased)
MergeResolved, MergeCarriedForward, UnmergeCarriedForward (owner session) a merge or unmerge operation for the committing user; the actual resolver recorded for resolved conflicts a session version on the Study the operation targets, copying or resolving current state from exact source versions Complete only when validation passes under the declared form version; drafts are never carried (C21)

A Save, Complete or Fix declares the form version it is based on. The server accepts exactly two values: the session's currently pinned version, or the form's current published version (which is the Upgrade). Any other declared version is refused with a typed StaleBase. A Save declaring a superseded version after a publication is accepted pinned to that version and never rebased; the recorded policy applies to it by derivation (§8.8). There is no publication-written transition: the package's "policy transition (creates incomplete versions)" is deleted (VA-03, D2-01). Superseded by the owner session (5 October 2026; D2-01 amended): the PublicationGenerated transition above exists. A late Save on a superseded version is still accepted pinned to it where no version was generated for that session; where one was, the Save is refused as stale and the draft is rebased (RD-R24, §7.7, §8.8).

7.4 Per-answer state

Derived per pinned answer from (pinned revision, head current revision, pinned form version's class for the question, current published form version's class, recorded policy, validity, applicability):

State Meaning Copy (D3-03)
Current Pinned revision is the head's current revision; class matches the required version; valid none
OutdatedOwnAnswer The same reviewer has a newer revision on this head (same class) that this session version does not pin "Outdated answers" / "contains outdated annotations" (SF5)
NeedsUpdatingVersion The required version's class differs from the pinned revision's class under requireReanswer, or the version is incompatible "Needs updating" (VU1)
NeedsUpdatingValue Same class, but the pinned value is invalid under the required version (retired option, tightened validator, removed mode) "Needs updating"
NeedsAnswering Required, applicable and blank, including a newly added required question and a question newly visible after an ancestor change (FEAT-003 "new question" and "newly visible", RECOVERED) "Needs an answer"
PinnedOlderVersion The session is pinned to an older form version under doNothing; no action required "Answered under an earlier version"
NotApplicable Suppressed by an ancestor's answer or mode in this session's resolved graph; the revision is preserved none
Conflicted Legacy duplicates not yet resolved (§6.6) "Conflicting earlier answers"

VA-27 asked for six states; NeedsAnswering and Conflicted are added because FEAT-003 and VB-13 need them. U13 validates all eight.

7.5 Session effective state

Status is the owner's SL3 classification and is a stored fact of the latest explicit version: draft_only, saved_incomplete, completed, withdrawn, plus the separate draft-changes flag. Everything else is derived on read (D2-01):

  • Requirement standing against the current published form version fv_cur, given the latest explicit version E pinned to fv_E and the recorded policies for the publications between them (the latest generation of each, §8.7):
  • fv_E = fv_cur → satisfies if every required applicable answer is Current or OutdatedOwnAnswer, else the session has NeedsAnswering or NeedsUpdating* answers;
  • fv_E < fv_cur → evaluate each question of fv_cur under its treatment (§8.2): unchanged → satisfied by the pinned answer; added → satisfied only under countEarlierCompletes; changedCompatible with autoUpdate → satisfied iff valid; requireReanswer → not satisfied until a revision pinned to the new class exists; doNothing → the session is pinnedOlder, counted or not per the policy's counting choice; removed → ignored.
  • Qualifying = completed ∧ standing ∈ {satisfies, pinnedOlderCounted} ∧ eligible (membership per D4-20; support edit-mode writes excluded from independence only).
  • Flags: hasOutdatedOwnAnswers, hasDraftChanges, informed (exposure, C3, including the NS-06 kind "questioned in reconciliation").

SF6's "if a recorded admin update treatment creates a new incomplete session version" is read as "changes the session's effective standing": under requireReanswer a completed session stops qualifying by derivation, with no version written (D2-01). Superseded by the owner session (5 October 2026): SF6 now applies literally; under requireReanswer on an answered question the publication generates an incomplete PublicationGenerated version, so the completed session stops qualifying through an explicit, attributed version. Derived standing remains for the cases where the generation table writes nothing (§8.11). Qualifying also requires that no active ContributionExclusion covers the contribution (O1). Canonical readers (admission, qualification, AF2, exports, tasks) derive this on read; query-path projections carry the definition-version vector they were evaluated under and are rewritten by the publication operation (consistency model §8).

7.6 Drafts

Rules only; mechanics are consistency model §4 and §5:

  • One draft record per session, pinned to a base explicit version and base form version, stored as patches against the base, size-capped with E28. It holds 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 (retained N days), so "keeps both" is literal; the second tab is read-only with "Take over editing", which transfers the lease (D2-08).
  • Save and Complete present the draft etag and consume the draft atomically in the commit.
  • No TTL on any draft; removal only by audited discard by the owner, an admin after revocation, or the system with an audit record; stale drafts are visible to admins because they block LC1.
  • No autosave trail beyond the current draft and its conflict copies (PH-33): SL1's "draft changes/history" is satisfied because draft changes are preserved without explicit versions.
  • A cross-form draft: a draft in form G based on an older revision of a head changed through form F gets a typed stale-base conflict at Save that shows both values and keeps G's draft (VB-15).
  • Whether an autosaved draft holds the reviewer's place is D2-07; the recommended middle ground is held while the reviewer is active under today's idle and disconnect timers counting draft activity, released when they lapse with the draft kept, and Complete still allowed afterwards as an extra contribution (SF4's target is a minimum) unless an optional capacity cap applies (D3-17), in which case the reviewer is told honestly and may keep or discard the draft.

Superseded in part by the owner session (5 October 2026), see §7.7: the lease and "Take over editing" bullet (D2-08 decided: one shared session across tabs and devices with base-version checks; the take-over presentation is a brief detail); the "no autosave trail" bullet (replaced by the draft change log); the stale-autosave rejection (replaced by rebasing); and the D2-07 middle ground (replaced by the decided form-owned timeout and in-progress limit). The no-TTL, audited-discard, cross-form conflict and consume-on-Save bullets stand.

7.7 Draft change log, rebase and the shared place (owner session)

Rules (OWNER for the behaviour, consolidation §1, D2-07, D2-08, Q-20; mechanism PROPOSAL; RD §3.10, §3.11, §4.2, §4.6):

  • Diffs with per-answer bases. Autosave sends only changed answers, each with its per-answer base. The draft head records the base explicit version, the base form version and an increasing draftVersion, and moves by CAS. Each accepted autosave appends an immutable SessionDraftChange {draftVersion, basedOnDraftVersion, per-answer base, patch, connectionId, tabId, actor, at} to pmSessionDraftChange. The draft rebuilds byte for byte from its base and change log, so its history is fully reconstructable (replaces PH-33's "no autosave trail"). Save and Complete consume the draft atomically and link its changes to the version they produced. Retention and any compaction are brief items; compaction never removes an explicit version.
  • Concurrent tabs and devices. Two connections may autosave. A change to an answer that has not moved since its per-answer base applies; a change to an answer that moved becomes a recoverable conflict copy and both values are shown. A read-only take-over presentation may be chosen in the brief for small screens.
  • Rebase. A draft whose base was superseded, by a PublicationGenerated version or by the reviewer's own Save in another tab, is rebased onto the new version at the next autosave or Save: non-overlapping changes apply, overlapping ones are kept with both values, and nothing is discarded silently (RD-R24). A late Save on a superseded base is refused as stale where a version was generated.
  • The shared place. One place per reviewer per Study × form across every stage, tab and device. The form owns the inactivity timeout and the per-reviewer in-progress limit. One idle or closed tab never releases the place while another connection is active; when every connection is idle beyond the timeout or closed, the place is released and the draft is kept; on return admission and capacity are rechecked and SyRF says honestly if no place is free (RD-R11). The in-progress limit counts sessions, never connections.
  • Status words. "Draft auto-saved" and "Version checkpoint saved" stay distinct from completion; the words are illustrative (D3-03 decided-amended).

Fixtures: FX-VM-32 (amended), FX-VM-47.

8. Publication

8.1 Two-step publication

Committing a question version has no session impact (§3.2). Publishing a form or profile version is the only impact point (FV2, Q-26). Publishing a system question version has no project impact until a project form pins it (§3.7). A publication records a policy and starts an operation; it writes no session versions and no answer revisions (D2-01). Superseded by the owner session (5 October 2026; D2-01 amended): a publication records a policy, starts an operation and may write attributable generated session versions and mapped revisions under §8.11. A standard-target change is a publication (§8.12).

8.2 Treatment vocabulary

The three recovered choices cover changed questions only; FV1's headline case (an added question) and removed questions had no treatment (VA-14). The per-question vocabulary is:

Change class (derived from the diff of fv_prev and fv_new) Treatments Default
added (new pin, required) countEarlierCompletes (earlier Completes satisfy v2 with the question blank; a missing answer is never manufactured, FV3) · requireAnswerBeforeCounting requireAnswerBeforeCounting
added (new pin, optional) no choice; earlier Completes satisfy
removed (pin dropped, or inapplicable through an ancestor change) dropFromRequirement; answers stay in history and remain exportable with their version; children of a removed parent become NotApplicable
changedCompatible (same class, higher version) autoUpdate (the pinned revision satisfies the new version iff valid, §3.6; no revision is written) · requireReanswer · doNothing autoUpdate
changedIncompatible (different class) requireReanswer · doNothing requireReanswer
mapped (incompatible by default, loosened with a Q-34 mapping) autoUpdate applies the mapping (§8.9) · requireReanswer · doNothing requireReanswer until Q-34 is answered
requiredness raised treated as added for the counting choice

autoUpdate is only offered within a class and only satisfies valid answers (SR-16, VA-01); a many-to-one mapping or any free-text question change forces requireReanswer. The per-category part of the policy says which session categories the treatments apply to (completed, saved-incomplete, draft-only) and, for completed sessions left pinned under doNothing, whether they count toward the new requirement (FV3's admin choice; no universal default is asked). The dialog is FEAT-003's four-step flow (scope, compatible changes, incompatible changes, confirmation with per-question and per-category counts; docs/features/question-management/README.md:266-298, RECOVERED) extended with added and removed rows and the system suggestion (U6, PH-18).

8.3 Policy record

IssuePolicyRecord(operationId, generation) (embedded in FormVersionIssue in the domain model, with no collection of its own; the F1a naming ADR confirms the names and shape) holds: the form and the transition (fv_prev versions → fv_new); per question {changeClass, treatment, mappingRef?}; per category {applies, countingChoice}; the admin's rationale (SR-16); the admin, real actor and HLC stamp; the preview digest confirmed; the usage evidence identity read at the fence (projection revision, source revision, digest, or the authoritative-count basis under Q-31(b), MS-04). Generation 1 is written in phase 1; FV4 appends generations (§8.7). The record is append-only and digested.

8.4 Derived effects

Every effect on sessions is the function in §7.5 evaluated against the policy record. Nothing is "applied" to a session. The same evaluator serves admission, qualification, readiness, AF2's banners and exports, so there is one truth and no fan-out. Superseded in part by the owner session (5 October 2026): generated versions apply the chosen treatment explicitly where §8.11 says so; the evaluator still derives standing for every session the publication did not generate a version for, and for every session it did, from the generated version like any other explicit version. The query-path projections (Study canonical summary, FEAT-024 rows) are rewritten by phase 2 so legacy and pool readers converge; until the sweep reaches a study, admission and readiness for that form pause (D2-10) and fail closed on a stale projection (DC-08).

8.5 Phases

Summary only; the protocol is consistency model §4 and §7:

  • Phase 1 is O(1). Fence the form (Publishing), drain for at least the transaction lifetime plus the expired-transaction sweep plus a margin (D2-10, about 90 s at most), re-check the preview digest (re-confirm with the admin if it changed), then in one short transaction CAS the AnnotationForm head (currentPublishedSeq, publicationSeq), write the policy record (generation 1) and the operation record, and release the fence. No session is enumerated.
  • Phase 2 is an ADR-020-style operation (lease, generation fencing, chunks, predicate pinnedFormVersionSeq < current ∧ appliedPolicyOp < op swept until it matches nothing) that rewrites projections, writes the §8.9 derived revisions, and captures notices once per recipient (C15). Reviewers keep saving throughout; a Save that races the sweep is covered by the predicate.
  • The impact manifest is an audit and preview snapshot built after commit (§8.6).
  • Owner session (5 October 2026): phase 2 also writes, per Study item in one short transaction, the generated session versions (CAS on each session head against the version the plan used, recomputing if the reviewer saved meanwhile; deterministic ID per session, operation and generation), mapped revisions, result and override versions under the confirmed treatments, the Study write and notice intents; a crash resumes and replays return existing versions. Phase 1 still does constant work, admission and readiness for the form still pause while phase 2 runs, and the pause and operation limits are measured (D2-10 brief item; the 90-second and 30-minute figures are proposed, not approved).

8.6 Impact manifest

Built after phase 1 from authoritative records at the operation's HLC stamp, chunked, and used for the admin's summary, the notices and PS3 reproducibility; it is never an input to any effect. Its categories are FEAT-003's per-session categories expressed in this model's states (PH-18):

Per session Per question in the session
category (completed, saved_incomplete, draft_only), prior form version, every stage that reaches the session, resulting standing under the policy carriedForward, carriedForwardBlank, autoUpdateSatisfied, needsUpdatingValue, needsUpdatingVersion, needsAnsweringNew, needsAnsweringNewlyVisible, removed, mappedByPolicy, conflicted

Counts and identities come from pmFormSession, pmFormSessionVersion, pmAnnotationHead and pmSessionDraft in one pinned snapshot. draft_only is counted authoritatively from pmSessionDraft (indexed by base form version), never from the materialised usage family (MS-03); the manifest records each count with its basis. The preview shown before phase 1 is the same computation with a digest; phase 1 refuses if the digest moved.

8.7 Revision of a policy

FV4 (OWNER) lets the admin revise an unnecessary update requirement later with history. In this model FV4 appends a policy generation to the same operation; it never rewrites versions, work, drafts or the compatibility declaration (D2-02). The new generation is written by CAS on the policy generation; a phase-2 batch in flight asserts the generation and re-sweeps. Derived states recompute immediately for canonical readers; projections converge through the sweep. Supersession can only loosen or tighten treatments and counting choices within the vocabulary of §8.2; it cannot make autoUpdate available across classes. Owner session: a new generation may generate further versions, for example re-pinning a session to the form version under which its Complete was validated to restore its completed status; earlier generated versions stay in history.

8.8 Late Saves and Upgrade

A Save or Complete based on a superseded form version, whether it arrives during the drain, during phase 2 or weeks later, is accepted pinned to the version its client declared and is never rebased (§7.3). The recorded policy applies to it by derivation: under requireReanswer the session is completed but not qualifying until the reviewer Upgrades and Completes; under doNothing it is pinnedOlder and counts per the counting choice. The client shows the typed response ("saved under v1; this form is now on v2") and offers Upgrade. Under doNothing the reviewer may keep working on v1 indefinitely; Upgrade is always available and never forced (VA-10). Owner session: still true where no version was generated for the session (for example under doNothing); where the publication generated a version, the late Save is refused as stale and the draft is rebased onto the generated version (RD-R24, RD-AE18).

8.9 Option mapping as the only derived-revision writer

Q-34 (OPEN) asks whether QM v2's "map answers to updated options" is an approved form of autoUpdate. Until answered, no mapping exists and nothing writes derived revisions. If approved as recommended (explicit per-option mapping, one-to-one, meaning unchanged, old revisions untouched):

  • the mapping {oldOptionId → newOptionId} is declared with the question version as the ground for loosening its compatibility (§3.5) and referenced by the policy record;
  • the phase-2 operation writes, for every head whose pinned revision selects a mapped option, exactly one successor revision with authorship = policyDerived, provenance = {sourceRevisionId, mappingRef, operationId, generation}, the same real-actor fields as the source and the reviewer as effective author, idempotent per (headId, operationId);
  • derived revisions are excluded from SF5 outdated flags and from the "changed since" counts, are included in agreement as the reviewer's answer (meaning unchanged by definition), and are shown with a "mapped on publication" marker and the original value in history;
  • the session's pin map moves to the derived revision on its next explicit version; until then §3.6 rule 3 treats the pinned revision as valid through its derived successor.

This is the single exception to rule 4 in §2 (brief §1.1). An alternative with no writer at all (apply the mapping on read) is recorded in §13; it was not chosen because every reader would need to know every mapping.

Owner session (5 October 2026; Q-34 decided, OWNER). Option mapping is approved as recommended: the publisher declares it explicitly per option, only where meaning is unchanged, and it writes new immutable revisions with mapping provenance while keeping the originals; it never bypasses type or option validation or a mandatory re-review (RD-R21). It is no longer "the single exception": mapping is one of the generated-version cases of §8.11, and the session's pin map moves to the mapped revisions within that generated version.

8.10 One active publication per form

At most one publication operation per form is active at a time, enforced by a unique partial index on pmFormVersionIssue {formId} where the operation is active, the pattern pmRobRunOperation.ActiveSearch already uses (MongoRobRunStore.cs:67-72, CODE-MAIN). A second publish is refused with a typed PublicationInProgress (D2-11). FV4 is not a new publication; it is a generation on the existing operation (§8.7). The same rule applies per profile. Owner session (D2-11 decided): many design drafts may exist; the next publication rechecks its impact against the state the previous one produced (§3.9).

8.11 Generated session versions (owner session; D2-01 reversed)

The decision. The round-2 proposal said publication writes no evidence, so a published policy would change only what readers derive. Chris rejected it: publication may create attributable generated session versions (OWNER; register "Decisions already fixed"; consolidation §1). SF6 already anticipated this, so SF6 now applies literally.

When publication generates a version (PROPOSAL for the brief; RD §3.15). For each affected session whose latest explicit version pins an older form version and falls in a category the admin chose to treat:

Outcome of the recorded treatment for the session Generated version? What the generated version holds Resulting status
Every changed question is doNothing, or no question changed (a target-only or policy-only version) No Nothing Standing derived: pinned to the older version, counted or not as the admin chose; unchanged questions satisfy the new version
autoUpdate on compatible changes and every pinned answer is valid Yes, PublicationGenerated The same revision pins, re-pinned to the new form version Completed stays completed only if the pin map validates under the new version
Option mapping (Q-34) Yes New revisions for mapped answers with provenance {sourceRevisionId, mappingRef, operationId, generation}; pins moved to them As validated
requireReanswer on an answered question Yes Pins kept; the answer shows Needs updating; re-pinned to the new version Incomplete; a completed session stops qualifying
New required question with requireAnswerBeforeCounting Yes Re-pinned to the new version; the new question needs an answer Incomplete
New required question with countEarlierCompletes No Nothing Standing derived: pinned older and counted
Category outside the admin's chosen scope No Nothing Standing derived
Draft-only session (no explicit version) Never The draft is rebased (§7.7) Not applicable

One session gets at most one generated version per publication generation, combining every question's treatment. Generated versions are attributed to the publication operation and the publishing admin; the reviewer stays the effective author of the answers. A generated version never records a Complete that fails validation under its declared form version and never overwrites a newer reviewer version. Whether autoUpdate generates a version or stays derived is confirmed in the brief; the recommendation is to generate, so that a session's current version pins the form version it satisfies, with the cost measured under D2-10 (RD §12).

Before and after for this rulebook (RD §3.15).

Section Before After
§1.1 model paragraph; §2 rule 4 Publication is a recorded policy and never writes evidence, with option mapping as the only exception Publication records a policy and may write attributable generated session versions and mapped revisions under the table above; where it writes none, effects stay derived
§2 rule 6; §4.4 Operational settings never version; the minimum target is an operational setting The standard target is inside the form version; other settings follow §4.6
§3.5 A declaration may change until a revision pins the version Immutable from commit; declared by the publisher; corrected by guided rollback and republication (§8.13)
§3.9 One designer pending record with a lease and take-over Collaborative design drafts with per-item base checks
§7.2, §7.3 Kinds Save, Complete, Withdraw; "there is no publication-written transition" Kinds include PublicationGenerated and the merge kinds; generated transitions follow the table above
§7.5 SF6 read as "changes the effective standing" SF6 applies literally when a version is generated; derived standing remains for the no-generation rows
§7.6 No autosave trail; second tab read-only with take-over Draft change log kept; concurrent tabs reconciled by base checks; the take-over presentation is a brief detail (§7.7)
§8.1, §8.4 "Nothing is applied to a session" Generated versions apply the chosen treatment explicitly
§8.5 Phase 2 rewrites projections only Phase 2 also writes generated versions and mapped revisions, per Study, idempotently
§8.7 FV4 appends a policy generation Unchanged, and a new generation may generate further versions; earlier generated versions stay in history
§8.8 A late Save on a superseded version is accepted pinned to it Still true where no version was generated; where one was, the Save is refused as stale and the draft is rebased
§8.9 Option mapping as the only derived-revision writer, OPEN Q-34 decided; mapping is one of the generated-version cases
§9.3 Any candidate version change drifts the task A generated version drifts only the questions whose pinned revision or class changed, or a candidate that stopped qualifying

What does not change. Phase 1 does constant work (fence, drain, digest recheck, head CAS, policy and operation records). One publication per form. Policy records are append-only generations. The impact manifest is built after commit and is never an input to an effect. Admission and readiness for the form pause while phase 2 runs.

Fixture: FX-VM-46.

8.12 Target-only publications and the override treatment (owner session)

A change to FormVersion.standardTarget creates a new form version and uses the publication impact process even without question changes (target-versions-form, OWNER; RD §4.9).

  • Preview. Studies whose sufficiency changes (below or above the new target); reconciliation readiness changes; accepted results that would change authority; Studies with overrides and their treatment (§4.7); active places beyond the new target where capacity enforcement uses it.
  • Treatments the admin confirms. For a raised target: keep existing accepted results current, labelled "below current target" (default), or suspend them pending reconciliation with the new number of reviewers (RS-R10, PROPOSAL). For a lowered target with AutoAccept: create SingleAnnotator results now, or at the next qualifying completion, for Studies that now have exactly one qualifying candidate and no accepted result (RS-R11); Studies with several agreeing candidates are never auto-accepted. For each override group: an explicit treatment (keep the effective value, rebase, remove). A switch to RequireHumanReconciliation keeps existing SingleAnnotator results or queues their tasks for human reconciliation (RS-R12).
  • Writes. Phase 1 as §8.5 with no question versions. Phase 2 per Study: result versions under the confirmed treatment, override versions where the admin chose to change them, the Study write. No session version is generated.
  • Afterwards. Effective targets and readiness at query time; automatic stage completion re-evaluates; stage filters that read accepted answers re-evaluate the affected Studies (C20). Raising the target invents no reviewer, vote or placeholder.

Fixtures: FX-VM-42 (rewritten), FX-VM-48, FX-VM-49.

8.13 Guided rollback and republication (owner session; D2-02 amended)

A wrong compatibility declaration or option mapping is never edited in place (RD §4.13):

  1. Preview. Read the publications that used the declaration and every generated version and mapped revision they produced; show what rolling back changes, per session and per accepted result.
  2. Commit. Create a corrected question version (same content, corrected declaration and mapping), publish a new form version through the ordinary flow, and generate versions that restore the pre-mapping values or apply the corrected treatment, each attributed to the correction.
  3. History. The wrong declaration stays in history with a "corrected by" link; accepted results built on affected answers follow the explicit reconsideration rule; nothing is deleted.

Fixture: FX-VM-50.

9. Reconciliation and gold under versioning

9.1 Task identity and pins

The package keyed the task by study × form × version-compatibility class (a round-1 resolution of B-28). That is wrong at form grain, because compatibility is per question, and it contradicts RE4's one task per study and form (VA-04, DD-07). Correction: the task key is (projectId, studyId, formId) with a deterministic _id. The task pins, as state, an input set (taskId, seq) holding the form version it reconciles against and the full set of qualifying candidate session versions (SF4: all of them). A new input set is appended when inputs change (§9.3); the gold snapshot records which input set produced it. The reconciler's session is an entity of the task with authorScope = reconciled, a current holder (X-RECLAIM, brief §2.1) and drafts keyed by the task.

9.2 Held questions

Per question of the task's form version, held is derived when the candidates' pinned revisions for that question fall in different classes, or when a candidate's pinned value is invalid under the task's form version, or when a candidate head is Conflicted. A held question blocks only itself (prefill, agreement, the question's own final acceptance); the rest of the form reconciles. The workspace shows the v10 held banner (/home/chris/.codex/visualizations/2026/09/23/01a0cbec-0c32-7103-ab5a-bfc05665deb7/syrf-v10-review-2026-10-02/source/design_handoff_syrf_v10/RECONCILIATION.md:47-50) with "Ask vN reviewers to update", which raises a Needs-updating request on those sessions (a C15 notice; the session's standing is unchanged by the request). v10's "Mark compatible" (RD12) is replaced by the admin's compatibility declaration, which is immutable once pinned (D2-02); a held question is released by the candidates' Upgrade, never by relabelling.

9.3 Drift triggers

A task moves to "inputs changed · re-check", never to retraction of gold, when: a new qualifying candidate appears; a candidate Saves after Complete; a candidate Fixes or Upgrades after gold; the form's current version changes by publication; a publication policy de-qualifies a pinned candidate (requireReanswer, VA-11b); a candidate head becomes withdrawn; a dedup merge or split aliases the study. Under doNothing with counting, candidates keep counting until they submit, which is v10's "their last completed review keeps counting" (RECONCILIATION.md:49); under requireReanswer they stop qualifying by derivation. The admin's publication choice selects which; the task never guesses. Owner session (5 October 2026): "a dedup merge or split aliases the study" reads "a duplicate consolidation or reversal" (C21; the consolidated Study gets its own task once it has a qualifying candidate and tasks on inputs freeze); a generated version drifts only the questions whose pinned revision or class changed, or a candidate that stopped qualifying (under requireReanswer through the generated incomplete version); a contribution exclusion drifts the task and leaves existing results flagged until explicit reconsideration.

9.4 Gold snapshots

A GoldSnapshot is (studyId, seq); entries are (question context without author, reconciled revisionId); a new snapshot keeps unchanged references (GS1). Each reconciled revision pins a question version, so each entry has a class. Derived per entry: goldNeedsReReconciliation = classSeq(current form pin of q) ≠ classSeq(entry's revision) ∨ ¬valid(entry's revision, current form pin) (VA-11a). Gold stays effective while flagged (QY1's analogue), exports and PRISMA manifests label every gold value with its question version and class (§10.2), and the task shows the flag; re-reconciliation produces a new snapshot. The pointer on StudyGold moves by CAS (consistency model §4).

9.5 Shared question gold

Overlapping forms share question gold (RE4, OWNER). The package proposed "first publisher wins, challenge only by query". Batch D D2-09 asks which: the recommended alternative is that the second task sees existing shared gold prefilled as accepted, with its source snapshot and reconciler shown, and the second reconciler may revise it in their own final submission, producing a new snapshot with provenance {supersedesEntry, task, reconciler}; queries remain the route for everyone else. Until D2-09 is answered, pilots use forms that do not overlap on reconciled questions (A-19 holds until R2d anyway). Owner session: D2-09 is the one owner decision still open after the session (T-OI-01); the recommendation is unchanged and FX-VM-45 stays parameterised.

9.6 Queries

The query target is the reconciled revision ID (VA-23), so "one work item per accepted-answer version" (QY2) is one work item per revision, shared unchanged across any number of snapshots that pin it. QY9's "current applicability" compares the target revision with the snapshot's current revision for that head and with the form's current class for the question; a concern on a revision that gold no longer pins, or whose class is no longer current, is flagged for the assigned reviewer and never retargeted silently.

9.7 Screening adjudication

Adjudications are revisions on the reconciled-authority ScreeningDecision head, so they are versioned by construction (V2-18); the ProfileAdjudication record in the domain model is the command record with a generation, not a second store of the decision. Following Chris's 25 September clarification, a submitted replacement of an input decision makes the earlier adjudication inapplicable to the new input vector while keeping its history (../screening-specialised-annotation-research.md:818-823, RECOVERED); the outcome projection derives this (consistency model §8).

Owner session (5 October 2026). The ProfileAdjudication record becomes the AdjudicationTask aggregate, one per Study × profile × trigger (decision tie, unresolved Unsure, reason disagreement, sole-source AI-model Unsure), holding immutable AdjudicationVersions (decision, reasons and must-agree answers, rationale when the profile requires it, the input vector, the individual adjudicator). The 25 September fallback after resolution is superseded by RS-R58: before an adjudicator starts, a correction or new decision that satisfies the profile rule withdraws the open task with a reason and one that still needs adjudication refreshes its input vector; after the adjudicator starts, a changed input vector returns a typed InputsChanged at submit with the draft kept; after resolution, nothing rewrites the adjudicated outcome. A later correction, contribution exclusion, surplus decision or replaced external decision leaves the AdjudicationVersion as the current final facet, flagged InputsChanged because its recorded input vector no longer equals the current decisions, until an eligible adjudicator explicitly reconsiders it, which writes a new AdjudicationVersion against the current vector. The candidate facet keeps recomputing and is shown beside the flag. The actor is warned before commit at the level their access allows and the adjudicator and assignee are informed (Q-27; O1 amendment; RS-R58). Adjudicator assignment is a separate AdjudicatorAssignmentVersion (§16.5).

9.8 Accepted result versions under target and policy changes (owner session)

An AcceptedResultVersion (project, study, form, seq) is one immutable accepted result with authority ∈ SingleAnnotator (system rule), HumanReconciled, Adjudicated (profile-owned screening annotations), MergeResolved; it records its input set and candidate versions, the form version (which pins standardTarget and ReconciliationPolicy), any override version, the effective target used, the actor and the gold snapshot it produced (RS §3.2). Rules:

  • A new target, policy or input creates a new version only through the entitled rule or person; earlier versions stay immutable and queryable (RS-R09). Under AutoAccept the configured rule is the entitled creator of SingleAnnotator versions (PROPOSAL, owner-visible in RS §12).
  • Raising the target invents nothing: results below it stay effective, labelled BelowCurrentTarget, unless the publishing admin chose to suspend them (SuspendedByTarget, derived from the recorded treatment with no version written); readers that use accepted answers as current treat a suspended result as absent (RS-R10).
  • A publication that lowers the target to one or switches to AutoAccept creates results only through the confirmed treatment, recorded with the operation and confirming admin (RS-R11; D2-01 amended).
  • Switching to RequireHumanReconciliation keeps existing SingleAnnotator results or queues their tasks; a queued result is replaced only when a reconciler completes (RS-R12).
  • Two or more qualifying candidates always need a human reconciler (T-POL-03 unapproved).
  • Derived standing (Current, InputsChanged, BelowCurrentTarget, SuspendedByTarget, NeedsReReconciliation, Superseded) is never stored; NeedsReReconciliation keeps §9.4's class rule.

10. Statistics, agreement, exports and PRISMA with mixed versions

10.1 Usage

  • Question-version usage is counted from revisions: distinct (studyId, authorScope) with a revision pinned to (QuestionRef, seq), read from pmAnnotationHead and pmAnnotationRevision.
  • Form-version usage is counted from session versions: the latest explicit version per session by category, deduplicated across stages (one session per study, form and reviewer, so there is nothing to sum).
  • draft_only is counted from pmSessionDraft by base form version, authoritatively, inside the publish fence (MS-03); it is never point-maintained by FEAT-024.
  • The materialised usage families (FEAT-024 amendment at F2 and F5, MS-02) are a swap-in behind the same interface; Q-31(b) authoritative counting at the protected boundary is the first pilot path.

10.2 Exports and manifests

  • Every exported answer carries (questionRef, questionVersionSeq, classSeq, optionId[], value[], responseMode?, answeredUnderVersion, qualificationPolicy); gold values carry the snapshot seq and goldNeedsReReconciliation (VA-17, SR-16).
  • Wide exports (one column per question) are generated per form version or per class, with a per-cell version column; a column never mixes classes.
  • Suppressed answers are omitted or carry an explicit status; a preserved inactive value is never emitted as live (PH-06).
  • Manifests list every definition version and its digest, the policy generations in force, the watermark, coverage per dataset and, for adopted data, AuthoredUnder (§11).
  • Extraction exports default to collectively Included studies (SR-17, methodology coverage) with explicit options for the rest.
  • Owner session: previous-version exports include generated and merge-created versions with their operation provenance; accepted values carry AcceptedResultVersion.authority and the form and override versions; adopted hints are labelled; manifests list the publisher's compatibility declarations and any guided corrections.

10.3 Agreement

  • Agreement compares only within a class, flags differing versions inside a class (AG3), never compares across classes, and treats Conflicted, Unknown-authored (§11) and held answers as "not comparable" with explicit counts.
  • Multi-select agreement needs identical option-ID sets; overlap is shown separately (AG2).
  • Independent versus informed follows exposure (VS2): an accepted revision rendered into view, and the NS-06 kind "questioned in reconciliation", make later versions of that reviewer's session on that study × form informed. The agreement store is its own rebuildable store (D3-11).
  • Statistical method and denominators are Q-16 and Q-04 (OPEN). Owner session: Q-04 decided (a blank candidate comparison is "not assessed", Unknown and Not reported are answers); Q-16 and D4-12 are specialist inputs (T-SI-02); adopted hints (reconciledHintAdopted) are excluded from independent observations; human independent, human informed, external human and machine classes stay apart, and outputs on a model's training inputs are excluded by default (C22).

10.4 PRISMA

PRISMA reads the collective authoritative outcome (PR1) from the outcome projection with its input-version vector; it never reads FEAT-024 rows (MS-11) and never changes because a form version, target or extra assessment changed. Report snapshots freeze the definition versions they were built from. Owner session: snapshots also freeze the profile versions with their source policies, the model configuration versions, the filter versions behind pool history, the search documentation versions and the protocol record version they used (C12, C20, C22).

10.5 Transaction time only

As-of means "what SyRF knew at that commit stamp", not "what was true then" (VA-21). Order comes from per-aggregate sequences and the HLC stamp; as-of(T) is offered only for T older than the watermark (consistency model §11). observedAt on a revision and legacy DateTimeCreated (settable, stamped at construction) are evidence fields with trust levels and are never used for ordering or as-of selection.

11. Legacy adoption as v1

Rules for the R6 domain mapping (migration §3), which this document corrects where VB-13 showed it could not be applied. Owner session (5 October 2026; R4, OS-A14, OS-A15): R6 is universal baseline conversion of every project after trials and pilots, faithful to legacy behaviour, with genuinely missing history carried as legacy-gap states (NotRecordedInLegacy, CurrentSnapshotOnly, UnknownLegacyAuthor, UnknownLegacyTime, UnknownAuthoredUnderDefinition, ValueOrDefaultUnknown, LegacyCompletionUnvalidated) that are never answer values; versions, reviews, votes and reconciliation results are never fabricated (BC §3.2, §3.4). The table is amended in place and BC §3.4 is the full mapping.

Legacy record Canonical result Rule
Project question Identity (questionId kept, definitionOwner = project, entityTypeId from the category alias, parent kept) plus content version seq = 1, classSeq = 1, published Counts as published (QD1). Ancestors added to a form at adoption are display-only and never required (VB-13c)
Options v0: optionId = OptionInfo.Id (OptionInfo.cs:114-131, CODE-MAIN); v1: IDs minted per value Unmatched values listed in the manifest with a disposition
Conditions and parent filters v0 _v0OptionId → optionId; v1 and ADR-011 hybrid value sets → option IDs by value; boolean conditions kept Any value with no option is a manifest exception; the form version is not composable until resolved
System questions Pin (systemGuid, Project.SystemQuestionVersion, seq 1) No structure is inferred from the current code; the seed for that systemQuestionVersion is the pinned definition
Stage question set One initial form requirement version per stage set, bound to that stage; merging identical sets is an admin-reviewed choice Never automatic
Stage target (owner session) FormVersion.standardTarget in the immutable v1 form version, materialised from the stage's effective legacy target (SessionCountTarget, override or inherited); converging stages with different targets need the admin's choice; EnforceAnnotationTarget becomes a CapacityCap equal to the target only where legacy enforced it; stage idle timeout and in-progress limit become form-owned values The target is never an operational setting after conversion (superseded wording 3); never a guessed total
Single-reviewer studies on target-one forms (owner session) Converted candidate evidence only; the v1 form version carries targetOneHandling = NoAcceptance (RS-R04a, PROPOSAL), so conversion creates no accepted result and no reconciliation work; exports keep "single annotator, not accepted" The admin moves the form to AutoAccept or RequireHumanReconciliation by publishing a new version whose treatment decides whether results are created (owner-visible ambiguity BC A1)
Answer Head with the legacy annotation ID preserved through LegacyIdAlias, one legacySnapshot revision, AuthoredUnder typed union: Verified(v1) when Annotation.Question (Annotation.cs:41, AnnotationOptions.cs:30, CODE-MAIN) equals the adopted v1 wording, else Unknown (VA-18) Verified by comparison because the legacy upsert validates placement only for new questions (Project.cs:501-510, CODE-MAIN) and locks nothing, so wording can change after answers. Unknown revisions are excluded from same-version agreement and from exact-match prefill; they remain candidates with a coverage label. Option answers map by value to option IDs; an unmapped value is kept verbatim in an unmappedLegacyValue payload and is invalid under v1. Owner session: AuthoredUnder = Unknown carries the legacy-gap state UnknownAuthoredUnderDefinition; a missing or unresolvable author carries UnknownLegacyAuthor
Conflicting duplicates for one context One Conflicted head (§6.6) Never "latest wins"
Session One current-only session version per legacy session with a full pin map of the adopted revisions; legacy-completed, unvalidated with the admin's count choice (E10); nullable timestamps kept Prior Save or Complete versions are never fabricated. Owner session: current-only is named CurrentSnapshotOnly and "legacy-completed, unvalidated" is LegacyCompletionUnvalidated, counted as completed by default because legacy counted it; missing times carry UnknownLegacyTime
Reconciled answers LegacyAuthorityUnknown snapshot, never a gold snapshot (Q-35) Removed by the owner session (5 October 2026; Q-35 removed): no legacy reconciliation records are assumed and no LegacyAuthorityUnknown authority exists; the conversion dry run counts any found and an unexpected record stops that project's case (BC-R20)
Everything with a legacy ID LegacyIdAlias {projectId, legacyKind, legacyId, canonicalKind, canonicalId, manifestId}, unique on (projectId, legacyKind, legacyId) Used by #3944/#3945 remapping and exports (VB-13e); the alias table is itself append-only

12. Storage and enforcement summary

Physical choices are the F1a storage ADR's (E15); this section fixes the rules the ADR must meet and the blueprint it starts from (VB improvement 1). Transactions, retries and the cache rule are consistency model §4 and §5.

12.1 Collections

Collection names are explicit and decoupled from class names (today they derive from the class name, MongoContext.cs:154-163, CODE-MAIN, which is why QM v2's AnnotationQuestionV2 would land in pmAnnotationQuestionV2); a test asserts the map. Names below are PROPOSAL for the F1a naming ADR. Enums are stored as strings parsed from a closed set.

Collection Identity and unique keys Other indexes Notes
pmQuestionDefinition _id record GUID; {projectId, questionId, systemQuestionVersion} unique {projectId, status} identity and status only
pmQuestionDefinitionVersion {definitionId, seq} unique {projectId, questionId, classSeq} immutable; digest
pmSystemQuestionVersion {systemGuid, systemQuestionVersion, seq} unique global; seeded idempotently
pmEntityType _id stable system IDs minted at F1a; {projectId, name} unique for project types
pmAnnotationForm {projectId, formId} unique; head holds currentPublishedSeq, publicationSeq, settings, version phase-1 CAS target
pmAnnotationFormVersion {formId, seq} unique immutable; applicability graph; renderability result
pmDefinitionSettingsAudit append-only {ownerRef, seq} form, profile and stage operational settings changes
pmScreeningProfile, pmScreeningProfileVersion as for forms
pmStageSettingsVersion {stageId, seq} unique on the Stage aggregate per brief §1.12
pmFormSession deterministic _id; {projectId, studyId, formId, reviewerId} unique {projectId, formId, currentFormVersionSeq, status}; {projectId, studyId, reviewerId} publication predicate, usage, SF5 reads, candidates
pmFormSessionVersion {sessionId, seq} unique; {projectId, commandId} unique (the command ledger entry) {projectId, formId, commitStamp} full pin map; resolved question set
pmSessionDraft {sessionId} unique {projectId, formId, baseFormVersionSeq} conflict copies embedded, bounded
pmSessionPresentation {sessionId} unique entity order and similar; never CAS-es the session
pmAnnotationHead deterministic _id; {projectId, contextKeyHash} unique; partial unique per kind {projectId, studyId, authorScope, questionId}; {projectId, questionId, currentQuestionVersionSeq} current payload copy
pmAnnotationRevision _id client-proposed validated; {headId, seq} unique; {projectId, commandId, headId} unique {projectId, studyId, commitStamp} as-of reconstruction
pmFormVersionIssue, pmFormVersionIssueChunk one active per form (unique partial index); chunks {operationId, chunk} unique phase 2; manifest chunks; IssuePolicyRecord generations embedded (generation unique within the issue; FV4), no collection of their own; the F1a naming ADR confirms the shape
pmReconciliationTask deterministic _id; {projectId, studyId, formId} unique input sets embedded or {taskId, seq}
pmStudyGold, pmGoldSnapshot {studyId} unique; {studyId, seq} unique pointer CAS
pmQueryWorkItem {projectId, reconciledRevisionId} unique R4b
pmLegacyIdAlias {projectId, legacyKind, legacyId} unique {canonicalId} append-only
pmSessionDraftChange (owner session) {sessionId, draftVersion} unique {projectId, sessionId, at} append-only draft change log (§7.7)
pmDesignDraft, pmDesignDraftChange (owner session) _id; {draftId, draftRevision} unique {projectId, scopeRef, status} append-only changes (§3.9)
pmStudyTargetOverride (owner session) deterministic override ID from {projectId, studyId, formId}; {overrideId, seq} unique {projectId, formId} append-only (§4.7)
pmStudyVersion (owner session) {studyId, seq} unique append-only (§16.1)
pmAcceptedResultVersion (owner session) {projectId, studyId, formId, seq} unique; {projectId, commandId} unique append-only, or embedded in the task per the storage ADR (§9.8)
pmAIScreeningModelConfiguration (owner session) {projectId, configurationId, seq} unique immutable versions; head pointer for new references only (§16.3)
pmExternalScreeningDecision (owner session) {projectId, profileId, sourceKey, studyId, seq} unique current-pointer index on the decision key §16.3
pmTrainingReferenceVersion, pmTrainingPolicyVersion (owner session) {projectId, referenceId, seq} and {projectId, policyId, seq} unique append-only (§16.4)

Owner-session rows are PROPOSALs for the F1a storage ADR; a distinct domain record does not by itself justify a collection (consolidation §1), so the ADR may embed where an append-only repository can still enforce insert-only writes.

Constraints the ADR must keep: revisions are never embedded in sessions (SF3 and SF5 share them; gold and tasks reference them); the full pin map lives in its own document per session version; revisions stay in their own collection for the as-of index; Study holds only the canonical summary (consistency model §8); every new pmStudy index goes through the operator route (VB-18).

12.2 Append-only enforcement

The generic save is an upsert ReplaceOne filtered on Audit.Version (MongoExtensions.cs:262-290, CODE-MAIN), so re-saving a loaded immutable record silently replaces it (VB-10). Therefore: an AppendOnlyRecord base and an IAppendOnlyRepository<T> exposing only Insert, InsertMany and Find; on DuplicateKey the repository compares digests and returns the existing record idempotently or throws a typed conflict; an architecture test in the pattern of StudyWriteLockArchitectureTests fails the build on ReplaceOne, Update*, Delete*, FindOneAnd* or update models in bulk writes against a registered immutable collection; no TTL index on any canonical collection; mutable heads (form, profile, session, head, task, gold pointer) are written only through non-upsert CAS saves (#3985 prerequisite).

12.3 Identifiers and digests

  • Deterministic IDs (SHA-256 over a versioned canonical key, stored as CSUUID, the #3944 precedent) for aggregates with natural keys: FormSession, AnnotationHead (from the key hash), ReconciliationTask, StudyGold, the default population, CanonicalOwnership. The natural-key unique index remains the real guard; a DuplicateKey means "reload and CAS".
  • Client-proposed, server-validated IDs for revisions and entity instances: well-formed, unused, same project, generated per command; AF2 keeps its optimistic client IDs and never remaps temporary IDs across the store, comments, order and outcome cells (VB-16).
  • Legacy IDs are kept at adoption through LegacyIdAlias (§11).
  • Content digests (SHA-256 over a versioned canonical serialisation) on every definition version, revision, session version, policy record and snapshot; recorded in export manifests, so "two exports at the same watermark are identical" is a checksum comparison.

12.4 Referential invariants and the integrity checker

ID Invariant
I1 Every revision references an existing question version (or system version) of the same QuestionRef as its head, in the head's class, with a payload shape the version defines; a policy-derived revision references an existing source revision, mapping and operation
I2 Every session version references an existing published form version and existing revisions; every pinned head belongs to the session's project, study and author; (projectId, commandId) is unique
I3 Every gold snapshot entry references an existing revision with authorScope = reconciled on the same study; StudyGold.currentSnapshotSeq exists; every query references a reconciled revision
I4 Every stage settings version references existing published form and profile versions in the same project; every form version's pins reference existing question versions whose applicability graph resolves (§4.2)
I5 Every task input set references existing candidate session versions on its study and form; every held question references an existing pin
I6 No cross-project reference except system versions; every LegacyIdAlias target exists; every head's classSeq equals the class of its revisions' versions; no published definition is missing
I7 (owner session) Every PublicationGenerated version references an existing publication operation and generation and an existing published form version; at most one exists per session, operation and generation; every mapped revision references its source revision and mapping
I8 (owner session) Every StudyTargetOverride version references an existing Study and form in its project; every accepted result version references an existing form version and, where it records one, an existing override version; every profile version's source policy references an existing model configuration version in the same project

A read-only integrity checker ships in R2a, runs on seeds in CI, in E31 restore rehearsals, in R6 verification and after every restore; it reports zero findings on seeds and detects an injected dangling reference per invariant. An architecture test lists every pm* collection with a projectId against the deletion-lifecycle and restore registries (VB-11d).

12.5 Caching

Immutable definitions (question, system, form, profile and stage settings versions) are cached process-wide by (versionId, digest) with no invalidation, and version-by-ID endpoints return Cache-Control: immutable, which keeps AF2 history and Needs-updating reads off the Project document (118–465 KB typical per .claude/rules/repository-cache.md). Canonical commands never take deciding reads from the shared RepositoryCache (consistency model §4).

12.6 QM v2 harvest and avoid for versioning

5 October 2026: the harvest map extends this table to all the existing question-management work and the related dormant PRs, checked against the owner-session model. Its §7 notes, row by row, what this table still holds and what changes; where they differ, the harvest map applies.

Harvest (adapted to this model) Avoid
VersionHistory<T>; the typed AnnotationAnswer payload with EnsureCompatible (as the §3.6 validity check); ChildQuestionScope as the repeatable identity property; CandidateProjectQuestionSetValidator, CrossQuestionValidationService and AnnotationValidationState as E23 inputs; SystemQuestionFactory as the §3.7 seed builder; AnnotationMutationMapper, ExtractedAnnotationLegacyMapper, MigratedStudyReadModelAssembler and MigrationValidationService as R6 adapters and parity checks; the ExportSpec mode reservation renamed to form-version selectors ReconstructiveRollbackService and RevertToEmbeddedQuestionModel (destructive down-migrations); drafts and question-set versions inside the Project document; AQVersion.PublishDecisions, Optional and Multiple placement (requiredness belongs to the form; multiplicity is content but always incompatible); untyped AnswerOptionFilters; unbounded version arrays embedded in annotation and session documents; annotation identity without entity path, owner scope or population; stage-keyed export selectors; raw AsOfDate; ReplacementDraftLineagePlanner unless D2-03 keeps D38

13. Alternatives considered

Alternative Why not
Event sourcing (commands as the store, state by replay) Commands already produce receipts and a total order (the command ledger plus HLC); nothing needs replay; projections here are rebuilt from immutable revisions and versions, not from events; the repository and team have no event-store tooling. The model keeps what event sourcing gives (append-only, derived projections) without a second write model
Bitemporal records (valid time plus transaction time) Valid time is provenance only (observedAt, trust levels); no reader needs "what was true then" as a query dimension; as-of by transaction time plus coverage labels satisfies EX1 and EX2 (VA-21)
Copy-on-write session documents (a full session document per version) Would embed revisions in sessions, which SF3 and SF5 forbid (shared revisions across forms) and which gold and tasks reference; document growth and the as-of index both suffer
Per-form snapshots with content-hash equality (freeze the whole form per publication; equal hash means compatible) SF3 and DP4 need question-level identity and compatibility; hash equality is fragile under presentation edits and silent under meaning changes; the per-question class gives the same immutability with meaning preserved
Materialised policy-created session versions (DD-10's alternative: phase 2 writes real versions with provenance = policy) Contradicts SL3 (a version no reviewer made becomes current), makes the publish operation an author of evidence on shared heads, makes FV4 resurrect or supersede them, and costs one version per affected session; derived standing gives the same answers at O(1) (brief §1.1, D2-01). Superseded by the owner session (5 October 2026): Chris chose attributable generated versions (D2-01 amended); the objections are met by attributing each version to the operation and publisher while the reviewer stays effective author, by never overwriting a newer reviewer version, by generating only where §8.11 says so, by letting FV4 add generations without rewriting earlier ones, and by measuring the per-session cost under D2-10 (§8.11)
Apply option mappings on read (no derived revisions at all) Simpler in storage, but every reader (exports, agreement, AF2, PRISMA) would have to know every mapping ever recorded; a single idempotent derived revision keeps readers ignorant of mappings (§8.9)
Class per form (the package's task key) Compatibility is per question; a form version that changes five questions would split one study × form into several tasks against RE4 (§9.1)
Per-question versions with classes (chosen) Transaction-time revision log with derived projections; satisfies every owner decision; fits MongoDB as SyRF uses it (append-only inserts, partial unique indexes, deterministic IDs, short transactions on a few documents)

14. Conformance fixtures

Fixtures are versioned data run by the C2, C4, C5 and C9 suites at F1a and F4; the acceptance drafter maps them to criteria. IDs are provisional. Owner session (5 October 2026): rows amended in place are marked; FX-VM-46 to FX-VM-58 are added and run in the same suites (C4 and C5 at F2 for the generation table and draft rebase, which RD §10 requires the F2 freeze to include) and, where they cite them, in the C20 to C22 suites.

ID Fixture
FX-VM-01 Compatible added option: the answer is shared by two forms with no flag
FX-VM-02 A v2-only option is never offered under v1; a prior v2 value is rendered read-only with v2's labels by the Needs-updating presenter
FX-VM-03 Two forms on incompatible versions of one question never flag each other (two heads)
FX-VM-04 Fix shows the in-class current revision
FX-VM-05 A late Save declaring v1 after v2 is published is accepted pinned to v1 and evaluated under the recorded policy; a Save declaring any other version is refused StaleBase. Owner session: where the publication generated a version for that session, the late Save is refused as stale and the draft is rebased (RD-AE18)
FX-VM-06 Upgrade keeps every pin, shows Needs-updating marks against v2, rebases nothing
FX-VM-07 Publishing a form with 10,000 sessions writes no session versions and no revisions; phase-1 time is flat across 1k, 10k and 100k sessions. Owner session (D2-01 amended): phase-1 time stays flat; phase 2 writes at most one generated version per affected session per generation and only for the treatments that generate one (§8.11); a doNothing-only publication still writes none
FX-VM-08 FV4 appends a policy generation; qualification is restored without touching versions or work; a batch in flight asserts the generation
FX-VM-09 Renaming an option's value keeps answers valid; retiring it makes them NeedsUpdatingValue
FX-VM-10 Composing a form that pins a child condition on an option absent from the pinned parent version is refused, naming the child, parent and option
FX-VM-11 An incompatible publication flags affected gold entries for re-reconciliation; gold stays effective and is labelled with its version
FX-VM-12 One task per study × form; one held question when candidates span classes; the rest reconciles; "Ask vN reviewers to update" raises a request and changes no standing
FX-VM-13 A wide export with mixed versions carries per-cell version, class and option IDs and never mixes classes in a column
FX-VM-14 Adopted answers are Verified(v1) when Annotation.Question equals the v1 wording, otherwise Unknown; Unknown is excluded from same-version agreement and prefill
FX-VM-15 Three-version chain v1→v2 compatible, v2→v3 incompatible: classSeq(v1)=classSeq(v2)=1, classSeq(v3)=3; agreement v1/v2 flagged; v1/v3 never compared
FX-VM-16 Flipping a compatibility flag after a revision pins the version is refused; flipping before that recomputes classes and re-validates active policies. Owner session (D2-02 amended): any edit of a committed declaration or mapping is refused, pinned or not; the declaration records the publisher and time; correction goes through guided rollback and republication (FX-VM-50; RD-AE14)
FX-VM-17 A data-type change creates an incompatible version of the same identity (pending D2-03); old revisions remain readable; a new head is created on first answer
FX-VM-18 Heads differing only in the second entityPath element coexist; an exact duplicate is refused; the hash is stable across key schema versions
FX-VM-19 Conflicting legacy duplicates adopt as a Conflicted head with no current pointer; excluded from prefill and agreement; resolved by the owner's Fix
FX-VM-20 LegacyIdAlias remaps a #3944 thread reference and an export reference
FX-VM-21 Unit delete is a withdrawal commit that flags the reviewer's other forms; rename keeps identity; duplicate mints new IDs with copiedFrom
FX-VM-22 Suppressed descendants survive Save and Complete; exports resolve suppression
FX-VM-23 Added required question: countEarlierCompletes keeps earlier Completes qualifying; requireAnswerBeforeCounting does not; no answer is manufactured
FX-VM-24 Removed question: answers stay in history and exports; children of a removed parent are NotApplicable
FX-VM-25 Deploying a new system-question version changes no published form and prompts no admin; the next form publication offers it
FX-VM-26 v0 and v1 system error-type variants are distinct identities with distinct pins
FX-VM-27 A session pinned to v1 renders v1 after v2 is published; a form AF2 cannot render is refused at publication; a canonical route never falls back to AF1
FX-VM-28 The append-only architecture test is green; digests verify on read-back; the collection-name map test passes; no TTL index exists on canonical collections
FX-VM-29 The integrity checker reports zero findings on seeds and detects one injected violation per invariant I1 to I6 (owner session: I1 to I8)
FX-VM-30 A previous-version export of a session equals its full pinned map
FX-VM-31 All eight per-answer states render and are explained (U13)
FX-VM-32 Two tabs: conflict copy kept; take-over transfers the lease; a stale autosave is rejected; a duplicate write sequence succeeds; Save consumes the draft atomically. Owner session (D2-08 decided): two tabs or devices editing different answers both apply; the same answer edited from a stale per-answer base yields a conflict with both values recoverable; the lease and take-over clauses apply only if the brief keeps that presentation; Save consumes the draft atomically and links its change log (RD-AE02)
FX-VM-33 A draft in form G based on an older revision changed through form F gets a typed stale-base conflict showing both values and keeps G's draft
FX-VM-34 A policy-derived mapping revision (pending Q-34) has provenance, is excluded from outdated flags, is written once per head and operation, and re-running the sweep writes nothing. Owner session: Q-34 decided, no longer pending; originals are unchanged and a declared incompatible change cannot be mapped (RD-AE15)
FX-VM-35 Question-version usage counts revisions; form-version usage counts session versions; draft_only counts pmSessionDraft; a shared session counts once across stages
FX-VM-36 Agreement never crosses a class, flags differing versions within one, and separates informed contributions including "questioned in reconciliation"
FX-VM-37 Two tabs' first autosaves create one FormSession; a client-proposed revision ID already used in another project is refused
FX-VM-38 A second publish on a form with an active operation is refused PublicationInProgress; FV4 during phase 2 CAS-es the generation
FX-VM-39 doNothing with and without counting yields pinnedOlderCounted and pinnedOlderNotCounted; the per-answer state is PinnedOlderVersion
FX-VM-40 A profile criteria version with a rule change is incompatible; cast decisions take the standing the Q-26 policy records (parameterised until Q-26 is answered). Owner session: Q-26 decided; the treatments are those of §5.5 and FX-VM-51
FX-VM-41 Publishing a stage settings version changes no session pin and no qualification; a Completed stage's display is frozen (pending D2-04)
FX-VM-42 Changing a target, compare setting, DP5 or route creates no version, no impact flow, and one audit entry. Rewritten by the owner session (5 October 2026): changing the standard target creates a new form version through the publication impact process, generates no session version and shows sufficiency, readiness, authority and override changes (RD-AE08); changing a compare setting, bulk acceptance, the timeout, the in-progress limit or the capacity cap creates no version and one audit entry; DP5 and profile routes are profile version content and change only by profile publication
FX-VM-43 The legacy category string resolves to the stable entity-type ID; enabling C1 changes no identity
FX-VM-44 A query targets a reconciled revision ID; after a new snapshot that no longer pins it, or a class change, the concern is flagged for current-applicability review and not retargeted
FX-VM-45 Shared gold in a second task is prefilled as accepted; a revision produces a new snapshot with provenance (parameterised until D2-09 is answered; owner session: still parameterised, D2-09 open)
FX-VM-46 (owner session) The generation table (§8.11): every treatment combination produces the expected generated version or none; each is attributed to the operation and publisher; none records an invalid Complete; a forced phase-2 crash replays idempotently; a barrier-forced race never overwrites a newer reviewer version (RD-AE16)
FX-VM-47 (owner session) Draft change log and rebase: 200 autosaves on the 2,023-question form send only changed answers and the draft rebuilds byte for byte from its base and change log; after a publication or a Save in another tab, non-overlapping edits rebase, overlapping ones show both values and no edit is lost (RD-AE01, RD-AE17)
FX-VM-48 (owner session) Target-only publications: lowering 2 → 1 under AutoAccept creates SingleAnnotator results only where exactly one qualifying candidate exists; Studies with several agreeing candidates are never auto-accepted; raising the target deletes no result and labels results "below current target" unless suspension was chosen (RD-AE09, RS-R10, RS-R11)
FX-VM-49 (owner session) Override treatment: publishing a new standard target lists every override with its treatment; no effective target changes without a recorded choice; each override change is a new immutable version and creates no form version; results record form and override versions (RD-AE10, RD-AE11)
FX-VM-50 (owner session) Guided rollback: correcting a wrong declaration creates a corrected question version, a new form version and attributed restoring versions; the wrong declaration stays with a "corrected by" link; nothing is edited in place (RD §4.13)
FX-VM-51 (owner session) Profile publication finds every mismatched decision, outcome and adjudication version; required new decisions stay pending; generated profile-session versions carry forward compatible decisions only under that treatment; frozen reports are unchanged (RD-AE22)
FX-VM-52 (owner session) Setting classification (§4.6): a change to any inside-the-version setting (standard target, ReconciliationPolicy members, profile outcome settings) creates a version; a change to any operational setting creates one audit entry and no version; a step can hide but never reveal hints the form hides
FX-VM-53 (owner session) Design drafts: two drafts on one form; the second publication is refused while the first runs, then rebases and recomputes its impact; a stale-base edit keeps the local value beside the current one; every change records actor, time and base revision (RD-AE20, RD-AE21)
FX-VM-54 (owner session) AI model configuration versions: after a configuration moves to version 2, decisions accepted under version 1 still reference version 1; a source-policy change creates a new profile version through the Q-26 flow (RI-AE22, RI-AE23)
FX-VM-55 (owner session) Training references and policies: editing a reference or policy publishes a new version; attempts in progress keep their pinned versions; no training record appears among live sessions, results or PRISMA inputs (TI-AE01, TI-AE02)
FX-VM-56 (owner session) Stage study filter versions: a settings version that changes only steps keeps its filter version; a new filter version starts a sweep whose effective time is the activation stamp; publishing stage settings changes no session pin (SP-AE09, SP-AE13)
FX-VM-57 (owner session) Study versions: a bibliographic edit, a merge and an unmerge each append a StudyVersion; references are byte-unchanged; field provenance is complete (DM-AE15)
FX-VM-58 (owner session) Outdated-answer completion: under WarnAllowComplete Complete proceeds after the warning; under BlockUntilAddressed it is refused with the list; mandatory re-answering blocks in both; a session pinned to an older form version keeps that version's mode until it upgrades (RS-R39 to RS-R42)

15. Decisions needed and engineering items

15.1 Decisions for Chris

All cited from the resolution brief's Batch D; none is minted here. Owner-session statuses (5 October 2026) are added to each row; the vocabulary is Decided, Decided-amended, Replaced, Removed, Deferred, Brief item, Specialist input, Engineering contract (no owner question) and Open owner decision.

ID What this document assumes until answered Recommendation in the brief
D2-01 Effects derived; projections rewritten by an operation; SF6 read as "changes the effective standing". Decided-amended: publication may create attributable generated session versions (§8.11); SF6 applies literally yes (superseded by the owner's amendment)
D2-02 Compatibility declared at commit, immutable once pinned; FV4 never changes it. Decided-amended: the publisher declares compatibility and mapping explicitly; immutable from commit; guided rollback and republication (§3.5, §8.13) yes (amended)
D2-03 Data type and multiplicity edits refused on published questions (today's behaviour). Engineering contract (no owner question): the recommendation stands as a PROPOSAL frozen at F1a (RD-R30) incompatible version of the same identity
D2-04 A stage binds the form identity; the live route presents the session's resolved version. Engineering contract (no owner question): stands; targets never come from stages (RD-R31) yes
D2-05 Target, compare settings, gold completeness, guidance, DP5, routes, allocation, batches, expiry outside requirement versions. Decided-amended (in part): the standard target is inside the form version; every other setting follows §4.6 (gold completeness, DP5 and routes move inside versions as PROPOSALs) yes (amended)
D2-06 System questions as data; adoption only through a form publication. Engineering contract (no owner question): stands (RD-R32) yes
D2-07 Draft and the reviewer's place: the recommended middle ground (§7.6). Decided: form-owned inactivity timeout and per-reviewer in-progress limit; one place across tabs and routes; expiry keeps the draft (§7.7) middle ground (superseded)
D2-08 Second tab read-only with take-over; conflict copy. Decided: one shared session and place across tabs and devices; connections tracked separately; base-version checks; the take-over or read-only presentation is a brief detail (§7.7) yes (amended)
D2-09 Pilots avoid overlapping reconciled questions until answered. Open owner decision (T-OI-01), the only one left from the 89 second task may revise
D2-10 Scoped pauses with limits; drafts kept. Brief item: tested publication active-work protection with generated versions; measured pause and operation limits (figures proposed, not approved) yes
D2-11 One active publication per form. Decided: many drafts; one publication operation at a time; final state recheck (§3.9) yes
D2-16 Form size ceiling in pins and bytes; the 2,023-question project stays legacy (VB-12's figure; UNVERIFIED here). Replaced: no size-based exclusion; the largest project is an acceptance case; limits engineered and measured to fit it yes (superseded)
D3-17 Optional capacity cap as a form setting separate from the target. Brief item (carry-forward alignment): form-owned baseline through every route, stricter route caps, off by default (proposed) yes
D4-17 FEAT-001 D54/D55 replaced by RE2's non-blocking warning; no enforcement levels. Decided-amended (O3): configurable WarnAllowComplete (default) or BlockUntilAddressed, inside the form version (RS-R39 to RS-R42) yes (amended)
D4-04 Training rounds as a step kind that never versions evidence as a contribution (PH-33's disposition). Decided-amended (S4): training steps with versioned references and policies, scoring, manual assessment, retries and optional group admission; still never live evidence unless explicitly promoted (§16.4) yes (extended)

OPEN questions this model depends on: Q-34 (no mapping and no derived writer until answered), Q-26 (profile versions publishable only before any decision), Q-04 (gold completeness follows requiredness), Q-29 (target-1 forms create no task), Q-35 (legacy reconciled answers never gold), Q-36 (no self-reconciliation by default), Q-16 (agreement method). Owner session (5 October 2026): Q-34, Q-26, Q-04 and Q-36 are decided; Q-29 is decided-amended (a task exists; SingleAnnotator or one-candidate human reconciliation); Q-35 is removed; Q-16 is a specialist input (T-SI-02). The only open owner decision this model still depends on is D2-09.

15.2 Engineering items E36 to E45

ID What Owner lane and contract Freeze gate
E36 Compatibility and class evaluator: diff classifier producing the system suggestion (§3.5 table), class derivation and classSeq stamping, the immutability guard (refuse a flip once pinned; re-validate active policies before that), and the designer pending-edit record with lease and etag (§3.9). Owner session: the guard refuses any edit after commit; the publisher's declaration and mapping are recorded with actor and time; guided rollback (§8.13); design drafts with per-item base checks replace the pending-edit lease L2 / C4 F1a
E37 Option identity and payload contract: optionId minting, the typed payload (value XOR mode, metadata, option IDs), display value and label resolution, and the v0/v1 legacy mapping with the manifest of unmatched values (§3.3, §11) L2 and L1 / C1, C4 F1a (payload); R6 (mapping)
E38 Composition and renderability validator: applicability graph against pinned parent versions, AF2 structural guards server-side with shared fixtures, typed refusals naming the question (§4.2, §4.3) L2 and L5 / C4 F1a
E39 System question store: idempotent seeding into pmSystemQuestionVersion, (systemGuid, systemQuestionVersion) identity, the CAMARADES publication command, no per-read rebuild on canonical paths (§3.7) L2 / C4 F1a
E40 Session effective-state evaluator: per-answer state enum, requirement standing, qualification, policy composition across publications and FV4 generations, one implementation used by admission, readiness, AF2 and exports (§7.4, §7.5, §8.4). Owner session: plus the generation planner of §8.11 (which sessions get a generated version and what it holds), the draft rebase rule (§7.7) and contribution-exclusion awareness in qualification L1 and L7 / C5 F1a design; F2
E41 Head key value object, canonical serialisation and hash, partial unique indexes per kind, Conflicted state, AuthoredUnder union and LegacyIdAlias (§6.1, §6.2, §6.6, §11) L1 and L15 / C2 F1a; R6 for the alias
E42 Entity instance commands (create, rename, withdraw, duplicate), population membership as an instance attribute, outcome cells keyed by instance, presentation record outside versions (§6.5) L1 / C2, C13 F1a
E43 Append-only persistence: AppendOnlyRecord, IAppendOnlyRepository<T>, explicit collection-name map with its test, content digests, the architecture test, no-TTL rule, string enums, and the integrity checker (§12.2 to §12.4) L1 / C1 F1a; checker in R2a
E44 AF2 VersionedAnnotationFormDataSource, the Needs-updating presenter contract (fromVersion, toVersion, treatment, reason, guidance, prior value rendered with fromVersion's labels), immutable-definition cache with Cache-Control: immutable, typed no-fallback error (§4.3, §12.5) L5 / C17 F1c (seam), R2a
E45 Reconciliation under versioning: task input-set versions, per-question held derivation, gold re-reconciliation derivation, second-task revision of shared gold (D2-09), query target identity (§9). Owner session: AcceptedResultVersion with its standing derivation and target-change treatments (§9.8); the AdjudicationTask input-vector rules (RS-R58) L6 / C9 F4

15.3 Assumptions

ID Assumption Basis Cost if wrong
A-25 Question versions form one linear sequence per identity (seq 1..n); branches never exist; a copy from a template or another profile is a new identity at seq 1 FEAT-001's sequential VersionNumber; DP4 copies Class derivation and classSeq need DAG rules; the designer needs merge semantics
A-26 A requirement version pins at most one version of each question identity; two forms may pin different versions of one question only across forms, never within one FEAT-001 QSV: one AQVersionRef per question Composition, the pin map and the head key need a per-pin version dimension

16. Versioned records added by the owner session

Added on 5 October 2026. Every record follows §2's rules: stable identity, append-only versions with a digest, pins by ID, derived states never stored as facts. Names and storage are PROPOSALs for the F1a naming and storage ADRs. Implementation remains on hold.

16.1 Study parent and StudyVersion

  • The Study parent is mutable: state (Current or Tombstoned) and currentVersionId, changed only by CAS (consolidation §2). Content lives in immutable **StudyVersion**s (studyId, seq): displayed bibliographic fields with per-field provenance ({value, origin, decidedBy, decidedAt, confirmation}; origin InheritedFromReference, SelectedFromReference, ManualOverride, SystemRule), referenceLinks[] and sourceDocumentLinks[].
  • A bibliographic edit, a merge and an unmerge each append a version; references never change. A P1 import writes version 1; a Study created before P1 gets version 1 on first need, labelled CurrentSnapshotOnly (RD §3.2, DM §3.2).
  • Merges and unmerges are C21 operations; merge-created session versions are MergeResolved or MergeCarriedForward, unmerge-created ones UnmergeCarriedForward (§7.3). Fixture FX-VM-57.

16.2 Stage study filter versions

  • A StageStudyFilterVersion is (stageId, filterSeq), embedded in the stage settings version, with a clause tree of AND/OR groups over profile outcome clauses and accepted answer clauses (option identities, accepted question versions, optional authority restriction), the implicit base predicate, its dependency set and a digest (Q-15 replaced; SP §3.3).
  • Evaluation is three-valued and fails closed on Unknown; an accepted answer recorded under a question version the clause does not accept is Unknown. Option identities make clauses survive wording changes. A settings version that changes only steps keeps the filter version. Activation runs a pool history sweep (C20). Fixture FX-VM-56.

16.3 AI screening model configurations, source policies and external decisions

  • An AIScreeningModelConfiguration (project, configurationId, seq) holds immutable versions of the project's description of a model: name, provider, model version or artifact, intended use, label vocabulary and its mapping to Include, Exclude or Unsure, project-supplied thresholds, the training-set context and an explicit "not supplied" list. Every change creates a new attributable version; the head points to the current version for new references only (E3 metadata ownership; RI §3.11).
  • A ScreeningSourcePolicy is part of the immutable ScreeningProfileVersion and pins the exact configuration version, the role (ContributingVote or SoleScreener), scope and mapping. A policy change is a new profile version through §5.5; earlier decisions keep the profile version they were accepted under (RI-R35).
  • An ExternalScreeningDecision (project, profile, sourceKey, study, seq) is versioned per source: a rerun or correction creates a new version and only the latest accepted version is current, so a source never becomes extra voters (RI-R37). An ExternalScreeningRun is idempotent by source, run ID and file digest. Contract C22. Fixture FX-VM-54.

16.4 Training reference and policy versions

  • A TrainingReferenceVersion (project, referenceId, seq) holds practice items (project Studies) and expected answers or decisions pinned to exact form, profile and question versions; it may be seeded by copying an accepted result or outcome, recording the source version without staying linked (S4; TI §3.2).
  • A TrainingPolicyVersion (project, policyId, seq) holds the scoring rubric, pass criteria, assessment mode, feedback disclosure, retry rules, admission and whether promotion is allowed. Numbers are per policy and not approved platform defaults (TI §3.3).
  • A training step binds exact form and profile versions and one version of each; attempts pin the versions current when they start; edits publish new versions and never change an attempt in progress. Training sessions use author scope training(attemptId). Training records are never accepted results, gold, targets, live statistics or PRISMA inputs; promotion is an explicit operation creating new live versions with promotedFrom. Fixture FX-VM-55.

16.5 Other versioned records

Record Identity and versions Rule
AcceptedResultVersion (project, study, form, seq) §9.8
AdjudicatorAssignmentVersion Per profile (optionally per trigger), with actor and time Versioned and audited; no profile publication; never grants authority (RS §3.9)
ClassificationRuleVersion (project, ruleId, seq) over ProjectRule Immutable; changes supersede dependent InferenceResults, which keep history (TI §3.8, §3.9)
CatalogueItemVersion (catalogueId, itemId, seq) Immutable; copies record copiedFrom and never change with the catalogue (§4.5)
NotificationContentPolicy (project, version) with a current pointer Immutable versions with actor and time; read at send time; can only narrow disclosure (ACD §3.5)
Inference beta setting Project operational setting with audit history Off by default; explicit project-designer opt-in; never enabled by conversion (TI §3.7)

Resolution record

Finding Category Where Note
VA-01 Adopted §3.5, §3.6 One definition: declared at commit, immutable once pinned, classes as an equivalence relation with classSeq; validity separate; per-decision usage table; D2-02
VA-02 Adopted §6.1 classSeq in the head key; one head per context and class; the shared-compatible-version consequence stated
VA-03 Corrected §7.3, §7.5, §8.1, §8.4, §8.9 Derived model chosen; "policy transition" deleted from C5; mapping is the only derived writer; D2-01
VA-04 Corrected §9.1, §9.2 Task key study × form (RE4 already decides); held per question
VA-05 Adopted §3.3, §11 Stable optionId; answers store IDs; rename compatible, retire incompatible; adoption reuses OptionInfo.Id
VA-06 Adopted §4.2 Composition validity against pinned parent versions; graph stored
VA-07 Question §4.4 Requirement version versus form settings; D2-05
VA-08 Question §5.3, §5.4 (a) allocation, batches, expiry outside the stage settings version (D2-05); (b) binding meaning D2-04
VA-09 Question §3.7 System questions as data, (guid, SystemQuestionVersion) identity, opt-in adoption; D2-06
VA-10 Adopted §7.3, §8.8 Upgrade transition; late Save pinned to the declared version
VA-11 Adopted; © Question §9.3, §9.4, §9.5 (a) gold re-reconciliation derived; (b) publication de-qualification joins drift triggers; © D2-09
VA-12 Adopted §7.6 One draft record with conflict copies (brief §1.8); D2-08 for the UX
VA-13 Adopted §3.8 "In use" defined per container; published versions immutable regardless of sessions
VA-14 Adopted §8.2 added, removed, changedCompatible, changedIncompatible, mapped with treatments and defaults
VA-15 Adopted §3.8 Published, retired, removable from forms, discardable when unreferenced
VA-16 Question §3.1 Both options presented; recommendation incompatible version; D2-03
VA-17 Adopted §10.1, §10.2 Per-cell version, class and option IDs; wide exports per form version or class; usage from revisions versus session versions
VA-18 Adopted §11 Verified(v1) versus Unknown from Annotation.Question; code verified
VA-19 Adopted §5.1 Profile criteria version versus profile settings; Q-26 applies to criteria versions only
VA-20 Adopted §2 table, §9.7 Outcome is a rebuildable projection with its vector; mechanics in the consistency model
VA-21 Adopted §10.5 Transaction time only; observedAt and legacy timestamps never order
VA-22 Adopted §7.2 Full pin map per session version; storage may delta-encode
VA-23 Adopted §9.6 Query target = reconciled revision ID; QY9 comparison defined
VA-24, VA-26 Noted — No findings with these IDs exist in the verbatim VA report
VA-25 Adopted §3.1 Entity-type ID is the structural property; category string is a display alias
VA-27 Adopted §7.4 Eight per-answer states (six asked plus NeedsAnswering and Conflicted); session standing in §7.5
VA improvement 1 Adopted §2 The rulebook table
VA improvement 2 Adopted whole document The simplest model that satisfies the ledger, with D2-03 as the identity choice
VA improvement 3 Adopted §8.1 Two-step publication stated once
VA improvement 4 Adopted §7.5, §8.4, §9.2, §9.4 Derive rather than store; PRISMA phase mapping stays project-level (DD-20), referenced from profile settings
VA improvement 5 Adopted §14 Fixture set, extended
VA improvement 6 Adopted §11 Wording evidence and OptionInfo.Id reuse
VA improvement 7 Adopted §8.2 U6 shows the per-question vocabulary and suggestion
VA question 1 Question §3.5 D2-02
VA question 2 Question §3.1 D2-03
VA question 3 Question §8.4, §8.9 D2-01
VA question 4 Question §9.5 D2-09
VA question 5 Question §5.4 D2-04
VA question 6 Question §3.7 D2-06
VA question 7 Question §4.4, §5.1, §5.3 D2-05
VB-05 Adopted §6.2 Key hash, scalar unique indexes, partial per kind; fixture FX-VM-18
VB-06 Adopted §8.5, §8.8, §8.10 Versioning rules stated (late Save pin, one active publication, FV4 generation CAS); protocol in the consistency model §4 and §7
VB-08 Adopted §4.3, §12.5 Versioned data source, Needs-updating presenter, renderability at publication, no AF1 fallback; one VB citation UNVERIFIED
VB-09 Adopted §6.5 Instance identity, rename, delete as withdrawal, duplicate with provenance, population attribute
VB-10 Adopted §12.1, §12.2, §12.3 Append-only repository and test, explicit names, digests, no TTL
VB-11 Adopted §3.7, §12.4 Record GUID _id with unique natural key; global system store; I1 to I6; checker in R2a
VB-13 Adopted §6.6, §11 Conflicted head, AuthoredUnder union, display-only ancestors, v0/v1 mapping, LegacyIdAlias
VB-15 Adopted §7.6 No TTL, audited discard, patches with E28 cap, cross-form draft conflict fixture
VB-16 Adopted §12.3 Deterministic IDs for natural keys; client-proposed validated IDs for revisions and instances
VB-17 Adopted §12.6 Harvest and avoid table for versioning; AC-M0-04 wording goes to the acceptance drafter
VB improvement 1 (storage blueprint) Adopted §12.1 Blueprint extended with policy records, settings audit, presentation, alias, entity type
VB improvement 8 (harvest/avoid) Adopted §12.6 As above
DC-06 Adopted §8.5, §8.6 Phase-1 O(1), drain, digest re-check, manifest after commit, predicate sweep; mechanics in the consistency model
DC-07 Adopted §7.6 Brief §1.8 model restated as rules
DC-08 Adopted §7.5, §8.4, §9.7, §10.4 Derived records carry their version vector; readers fail closed; mechanics in the consistency model §8
DD-07 Corrected §7.1, §9.1 Task keyed (study, form); versions as state; reconciler session an entity of the task
DD-10 Corrected §8.4, §13 The two designs collapsed to derived effects per brief §1.1; DD-10's materialised alternative recorded and not chosen; D2-01
DD-12 Adopted §3.1 System entity-type IDs minted at F1a; O1 depends on them
DD-15 Adopted §7.1 Deterministic SessionId; FormSession created on first autosave
DD-26 Adopted §3.1, §6.4 definitionOwner versus owningParent; candidate child never attaches to a reconciled parent (C1 test)
PH-06 Adopted §3.4, §10.2 Response modes and metadata in version content; suppressed answers preserved; exports resolve suppression; frozen versions settle the open question
PH-18 Adopted §3.5, §7.2, §8.2, §8.6 Transitivity as classes; resolved question set stored per session version; FEAT-003 categories in the manifest and the four-step U6 flow
PH-33 Adopted; disposition Question §3.9, §7.6 No autosave trail (brief §1.8); multi-admin editing via pending-edit leases; training rounds D4-04. Superseded by the owner session (5 October 2026), see §3.9, §7.7 and §16.4: draft change log, collaborative design drafts, training records
SR-16 Adopted §3.5, §8.2, §8.3, §10.2 autoUpdate only within a class with valid values; one-to-one mappings only; rationale stored; qualificationPolicy and answeredUnderVersion exported
MS-03 Adopted §8.6, §10.1 draft_only counted from pmSessionDraft; usage family over explicit versions only
V2-18 Adopted; container PROPOSAL §7.1, §7.6, §9.7 Candidate key stays (study, form, reviewer); reconciler session on the task; adjudications are revisions; ReviewSession generalisation for screening-only steps at F3/F5
VA-03, DD-10 Corrected (round 2) §7.3, §8.1, §8.4, §13 Superseded by the owner session (5 October 2026): D2-01 amended; generated versions per §8.11

Owner-session amendments record

Amendments from the specifications' §13 lists (5 October 2026) and where this page applies them. Planning only; implementation remains on hold.

Source Change Where
RD §13 and RD §3.15 The rows of the D2-01 before and after table; §15.1 statuses for D2-01, D2-02, D2-05, D2-07, D2-08, D2-09, D2-10, D2-11, D2-16; FX-VM-42 rewritten (a target change creates a form version); FX-VM-45 kept parameterised; fixtures for the generation table (FX-VM-46) and draft rebase (FX-VM-47); target inside the form version; explicit compatibility declaration immutable from commit with guided rollback; option mapping with new immutable revisions; setting classification; override interaction; draft change log Header; §1.1, §1.2; §2 table and rules 4 to 6; §3.2, §3.5, §3.8, §3.9; §4.1, §4.4, §4.6, §4.7; §6.3; §7.1 to §7.3, §7.5 to §7.7; §8.1, §8.4, §8.5, §8.7 to §8.13; §11; §12; §13; §14; §15
RS §13 §5.1: profile settings that change outcome derivation are versioned (tie policy by owner decision; the others by RS classification); §9.7 gains the triggers and RS-R58 §5.1, §9.7, §9.8, §10.3
SP §13 §2 and §5.3: the stage settings version embeds the filter version; capacity, timeout and limit move to the form's operational settings §2, §4.4, §4.6, §5.3, §16.2
DM §13 §9.3: "a dedup merge or split aliases the study" becomes "a duplicate consolidation or reversal" §9.3, §16.1
BC §13 §11: current-only → CurrentSnapshotOnly; "legacy-completed, unvalidated" → LegacyCompletionUnvalidated; AuthoredUnder = Unknown tied to UnknownAuthoredUnderDefinition; the reconciled-answers row removed (Q-35); the stage-target row added §11
Phase-2 brief (screening profile versions and mismatch preview; training reference versions; AI model configuration versions) Profile publication with mismatch detection; training and AI configuration version kinds §5.5, §16.3, §16.4, FX-VM-51, FX-VM-54, FX-VM-55
UX §13 Status words "Draft auto-saved" and "Version checkpoint saved" (illustrative) §7.7

Open items carried from this page: D2-09 stays open (T-OI-01); whether autoUpdate generates a version is a brief confirmation (§8.11); the override treatment set, the setting classification beyond the target and the rebase mechanics are PROPOSALs for the brief; every numeric limit is proposed, not approved.