Annotation question shape: configurable response modes and metadata¶
Context captured: 2026-08-10
Original discussion: Codex task “Excel Template and Parser” (019fcd33-0552-7b52-a56f-96603c5aebcd), 2026-08-05
Status: product and interface ideas; not an implemented SyRF contract
Purpose¶
This document collects the design ideas discussed while preparing a bulk upload of annotation questions for two BRCA1 pilot projects. The discussion began with practical questions about optionality, conditional questions, repeatable answer groups, missing answers, units, and supporting evidence. It developed into a possible extension to SyRF's annotation-question model.
The central idea is to preserve SyRF's ordinary nested-question model while allowing a question definition to optionally describe:
- alternative response modes when a reviewer cannot or should not provide the ordinary answer; and
- typed metadata fields that qualify or document the response.
The proposal separates normal answers, alternative response modes, metadata, and derived visibility status rather than representing all four as ordinary child questions.
Existing conceptual baseline¶
SyRF's current model can be understood as:
question definition
→ ordinary typed answer
→ optional repeatable answer instances
→ nested child questions
→ child visibility determined by parent-answer conditions
Two existing multiple-answer shapes are important:
multiple = true,answerArray = false: separate answer instances. Each instance can own its own child answers.answerArray = true: one response containing several values. The individual values do not each own a separate child-question subtree.
For example, confounding factors with factor-specific follow-up questions fit separate answer instances rather than one answer array.
The proposed response-mode and metadata concepts add optional response detail without replacing this baseline.
Conceptual question and response shape¶
Annotation question definition
├─ ordinary answer definition
├─ existing parent/conditional structure
├─ optional project-defined response modes
└─ optional project-defined metadata fields
Annotation response
├─ ordinary value OR selected response-mode ID
└─ optional metadata values conforming to the question definition
Derived runtime state
└─ whether the question is available or suppressed by an ancestor
Question definition¶
export type AnswerType =
| "text"
| "number"
| "singleSelect"
| "multiSelect"
| "boolean";
export interface MetadataFieldDefinition {
/** Stable project-defined machine and export key. */
key: string;
/** Reviewer-facing label. */
label: string;
type: "text" | "number" | "singleSelect" | "boolean";
/** Used for select fields. */
options?: string[];
required?: boolean;
/** Inline for inseparable qualifiers such as units; details for supporting context. */
display?: "inline" | "details";
}
export interface ResponseModeDefinition {
/** Stable project-defined key. */
id: string;
/** Project-defined label, for example “Not reported”. */
label: string;
/** Generic platform behaviour; the label itself is not platform-defined. */
suppressDescendants?: boolean;
requiresReason?: boolean;
/** Details that apply specifically when this mode is selected. */
metadataFields?: MetadataFieldDefinition[];
}
export interface AnnotationQuestionDefinition {
id: string;
text: string;
answerType: AnswerType;
options?: string[];
parentId?: string;
showWhen?: {
parentAnswerEquals?: string | boolean;
};
responseModes?: ResponseModeDefinition[];
metadataFields?: MetadataFieldDefinition[];
}
This is a conceptual interface, not the current generated SyRF API type. Names and types would need to be reconciled with the active domain and versioning model.
Stored response¶
export interface AnnotationResponse {
questionId: string;
/** Ordinary typed response. */
value?: string | number | boolean | string[];
/** Alternative to value, not an additional value. */
responseModeId?: string;
/** Values validated against the question's metadata-field definitions. */
metadata?: Record<string, string | number | boolean>;
}
An ordinary value and responseModeId are mutually exclusive. Metadata may accompany either where its definition permits that.
Derived question status¶
export interface DerivedQuestionStatus {
questionId: string;
status: "available" | "suppressedByAncestor";
suppressedByQuestionId?: string;
}
suppressedByAncestor is a runtime interpretation of the question hierarchy. It is not a response option selected by a reviewer and does not need to be stored as a replacement for an answer.
Response modes¶
A response mode is a configured alternative to supplying the normal typed answer.
Possible labels include:
- Not reported
- Not applicable
- Unable to determine
- Not assessed
- Not observed
These labels are project-defined rather than universal SyRF enum values. Different reviews can use different terminology or omit response modes entirely.
SyRF supplies a limited set of generic mechanics, such as:
- suppress descendant questions;
- require an explanatory reason;
- show additional metadata fields associated with the mode.
Example:
const doseResponseModes: ResponseModeDefinition[] = [
{
id: "not-reported",
label: "Not reported",
suppressDescendants: true,
},
{
id: "not-applicable",
label: "Not applicable",
suppressDescendants: true,
},
{
id: "unable-to-determine",
label: "Unable to determine",
suppressDescendants: true,
requiresReason: true,
},
];
The stable ID supports persistence, filtering, auditing, and export even if the displayed label is later translated.
Metadata fields¶
Metadata is structured information stored alongside a response. It qualifies or documents the response rather than representing a separate substantive review decision.
Examples include:
- measurement unit;
- evidence location;
- reviewer confidence;
- reviewer note;
- verification state;
- response-specific supporting information.
Project-defined but typed¶
The project administrator defines the metadata schema when defining the question. Reviewers then supply values that conform to that schema.
For example:
const doseMetadata: MetadataFieldDefinition[] = [
{
key: "unit",
label: "Unit",
type: "singleSelect",
options: ["mg", "mg/kg", "µg", "Other"],
required: true,
display: "inline",
},
{
key: "evidenceLocation",
label: "Evidence location",
type: "text",
display: "details",
},
{
key: "reviewerConfidence",
label: "Reviewer confidence",
type: "singleSelect",
options: ["High", "Moderate", "Low"],
display: "details",
},
];
This gives projects flexibility without allowing response-time labels and keys to drift between reviewers.
Boundary between metadata and ordinary questions¶
| Ordinary question | Metadata field |
|---|---|
| Substantive extraction value | Qualifier or supporting context |
| Screening or inclusion decision | Evidence location |
| Risk-of-bias judgment | Reviewer confidence |
| Outcome, dose, diagnosis, or category | Unit or measurement scale |
| A value with its own conditional children | Verification or reviewer note |
A useful test is whether the information independently affects the review's substantive logic. If it does, it is usually an ordinary question. If it qualifies or documents an existing response, it may fit metadata.
Metadata is not intended to have children or its own conditional rules. Allowing this would create a second form engine beside the question tree.
Complete question example¶
export const interventionDose: AnnotationQuestionDefinition = {
id: "intervention-dose",
text: "What was the intervention dose?",
answerType: "number",
responseModes: [
{
id: "not-reported",
label: "Not reported",
suppressDescendants: true,
},
{
id: "not-applicable",
label: "Not applicable",
suppressDescendants: true,
},
{
id: "unable-to-determine",
label: "Unable to determine",
suppressDescendants: true,
requiresReason: true,
},
],
metadataFields: [
{
key: "unit",
label: "Unit",
type: "singleSelect",
options: ["mg", "mg/kg", "µg", "Other"],
required: true,
display: "inline",
},
{
key: "evidenceLocation",
label: "Evidence location",
type: "text",
display: "details",
},
{
key: "reviewerConfidence",
label: "Reviewer confidence",
type: "singleSelect",
options: ["High", "Moderate", "Low"],
display: "details",
},
],
};
export const interventionDoseTiming: AnnotationQuestionDefinition = {
id: "intervention-dose-timing",
text: "When was the dose administered?",
answerType: "text",
parentId: "intervention-dose",
};
Ordinary response:
export const reportedDose: AnnotationResponse = {
questionId: "intervention-dose",
value: 5,
metadata: {
unit: "mg/kg",
evidenceLocation: "Table 2",
reviewerConfidence: "High",
},
};
Alternative response mode:
export const doseNotReported: AnnotationResponse = {
questionId: "intervention-dose",
responseModeId: "not-reported",
metadata: {
evidenceLocation: "Methods section, page 4",
reviewerConfidence: "High",
},
};
Note that unit is declared required: true above and is deliberately absent
here. That is correct rather than an oversight: question-level requirements
bind an ordinary value only, so they are not enforced once a response mode is
selected — see Decision 6 in the
architecture record.
Asking for the unit of a dose the reviewer has just recorded as not reported
would be incoherent. Requirements that should survive a mode belong on
mode-scoped definitions.
When the suppressing mode is selected, the dose-timing child is derived as unavailable:
export const doseTimingStatus: DerivedQuestionStatus = {
questionId: "intervention-dose-timing",
status: "suppressedByAncestor",
suppressedByQuestionId: "intervention-dose",
};
Interaction with conditional questions¶
Existing parent-answer conditions remain distinct from response-mode behavior.
Conceptually:
If an ancestor selected a response mode with suppressDescendants=true:
status = suppressedByAncestor
Else if the existing parent-answer condition is not met:
status = suppressedByAncestor
Else:
status = available
This preserves two explanations for why a child is not shown:
- the normal conditional answer did not match; or
- an ancestor used a response mode that intentionally suppresses its descendants.
Metadata values do not participate in condition resolution.
Multi-option conditional-parent answers are part of SyRF's existing condition model and are separate from metadata. The exact compatibility and schema-v0 behavior has evolved through later SyRF work, so this context document does not define that implementation contract.
Interaction with repeatable groups¶
Repeatable groups solve a different problem.
Example:
Confounding factor: Age
Measured variable: Age at baseline
Controlled? Yes
Confounding factor: Disease severity
Measured variable: Stage at diagnosis
Controlled? No
Each factor is a separate parent response with its own children. This corresponds conceptually to multiple=true with answerArray=false.
An answer array would instead represent something like:
The values inside the array would not independently own their measured-variable and control follow-ups.
Response metadata does not replace either of these answer structures.
Annotation-form presentation ideas¶
The ordinary answer control remains central.
Possible presentation conventions:
- units and similarly inseparable qualifiers appear inline;
- evidence location, confidence, and notes appear under a details disclosure;
- the response-mode chooser appears only where configured;
- selecting a response mode replaces or disables the ordinary answer control;
- a suppressing response mode removes descendant questions from the active form;
- suppressed required descendants do not create incomplete-form errors;
- review or audit views may explain why descendants were suppressed.
The exact control for response modes—buttons, radio group, dropdown, or disclosure link—was left open because discoverability and accessibility need evaluation.
Question Management authoring ideas¶
The question properties panel could contain an optional Response handling section after ordinary answer/control configuration.
Possible configuration surfaces:
- enable alternative response modes;
- edit each mode's label and stable ID;
- choose whether a mode suppresses descendants;
- require a reason for selected modes;
- define typed metadata fields;
- select inline or details presentation;
- preview the reviewer-facing question;
- validate duplicate IDs/keys and invalid option lists;
- show compact badges in the question tree;
- include configuration changes in draft/publish diffs and impact summaries.
Changing a stable metadata key, deleting a response mode already used in responses, or changing descendant-suppression behavior may be a breaking version change.
API and export ideas¶
Nested API response with an ordinary value:
{
"questionId": "intervention-dose",
"value": 5,
"metadata": {
"unit": "mg/kg",
"evidenceLocation": "Table 2"
}
}
Response mode:
{
"questionId": "intervention-dose",
"responseModeId": "not-reported",
"metadata": {
"evidenceLocation": "Methods, p. 4"
}
}
Flat exports could generate stable columns such as:
intervention-dose__value
intervention-dose__response_mode
intervention-dose__unit
intervention-dose__evidence_location
The question-definition version or manifest would accompany exported data so project-defined stable IDs, labels, types, and options remain interpretable.
Backwards-compatibility ideas¶
- Existing question definitions have no response modes or metadata fields and remain unchanged.
- Existing responses continue to use their ordinary value.
- The new fields are optional and activated only by compatible question definitions.
- A response cannot contain both an ordinary value and a response-mode ID.
- Suppression status is calculated rather than written over old answers.
- Stable IDs and versioned definitions prevent later labels from reinterpreting historical data.
- Persistence, API, forms, exports, audit views, and migration readers need compatible understanding before definitions use the extensions.
Rationale and rejected shapes¶
Fixed global response states¶
An early sketch used globally defined states such as answered, unavailable, notApplicable, and skippedByAncestor.
This was rejected because those labels are not universal across systematic reviews. The project should define selectable labels. Only generic mechanics belong to SyRF. Ancestor suppression is derived rather than selected.
Completely free-form response metadata¶
Allowing each reviewer to invent keys and free-form values would produce incompatible spellings and concepts that cannot be reliably validated, compared, filtered, or exported.
The more structured idea gives the project admin freedom at question-definition time and gives every reviewer the same typed schema when answering that question.
Metadata as a second conditional questionnaire¶
Allowing metadata fields to have child fields or conditional logic would duplicate the existing question tree and make validation, completion, auditing, and export harder to understand.
The bounded proposal keeps all substantive flow in normal questions and gives response modes only a small set of generic behaviors.
Repeatable groups represented as metadata¶
Repeatable substantive entities such as confounding factors can own their own child questions. They remain repeatable answer instances, not metadata fields.
Open design questions¶
The discussion left these questions open:
- Which metadata types should exist initially: text, number, boolean, single-select, multi-select, date, or specialised unit/evidence fields?
- Should metadata definitions exist only per versioned question, or can projects reuse schemas across a category, section, or stage?
- How are existing responses handled when a mode or metadata key is renamed, removed, or changes type?
- If a suppressing response mode is selected after descendants already contain answers, are those answers preserved as inactive, cleared, or explicitly reconciled?
- Which response-mode control is clearest and most accessible?
- Can mode-specific metadata coexist with question-level metadata without confusing reviewers?
- How are stable keys localised while remaining consistent in exports?
- What manifest best preserves the exact definition version used for exported responses?
- How does this concept integrate with the active versioned Question Management and Annotation Form architecture?
- Which concepts belong in the core domain and which are presentation concerns?
Relationship to the BRCA1 bulk upload¶
This concept emerged while adapting approximately 130 animal and human annotation questions for upload. The pilot needed practical handling for:
- nested conditional questions;
- explicit missing/not-reported choices;
- repeatable confounding-factor groups;
- units and supporting evidence;
- optionality and required validation.
The metadata and response-mode feature was intentionally not used in the immediate templates because it was not implemented in the live question model. The pilot used current capabilities instead: ordinary questions, explicit options, conditional parents, helper text, and repeatable answer instances.
The bulk-upload context that informed this discussion lives in an untracked handover note outside the repository, so it is deliberately not linked here — a path into one machine's working directory would not resolve for any other reader. The parts of it that matter to this design are summarised above.
Recovered artifacts and provenance¶
Both artifacts are now tracked in this repository — see "Tracked locations" below for the paths to use. The machine-local origins are recorded here only as provenance, and will not resolve in a clone:
| Artifact | Tracked path (use this) | Origin (provenance only) |
|---|---|---|
| Standalone TypeScript sketch | docs/superpowers/plans/2026-08-05-response-modes-metadata-example.ts |
a local Codex working directory, 2026-08-04, via the untracked handover/artifacts/ directory |
| Original proposed QM v2 plan | docs/superpowers/plans/2026-08-05-configurable-response-modes-and-metadata.md |
untracked file in the historical PR #2461 worktree |
At the time this context was assembled, the original plan was untracked in the PR #2461 worktree. PR #2461 is a draft integration tracker rather than a merge candidate, so the plan would have been lost with that worktree. Promoting both artifacts here preserves the ideas independently of its lifecycle.
Tracked locations (added at promotion, 2026-08-11)¶
This document and its companions were promoted into the repository from untracked session state so they cannot be lost with a worktree or temp dir:
- This context document:
docs/features/question-management/response-modes-and-metadata-context.md - The original QM v2 plan:
docs/superpowers/plans/2026-08-05-configurable-response-modes-and-metadata.md - The standalone TypeScript sketch:
docs/superpowers/plans/2026-08-05-response-modes-metadata-example.ts
The handover-directory and PR #2461 worktree copies referenced above are now secondary; prefer these tracked paths.