Repository navigation
T9: Complex logic: calculation, relevance, constraint #9
Description
Importance: Must have · Size: L · Depends on: T1, T8
Why. The logic columns are where clinical intent actually lives. The design doc places them in
Phase 2, but they are marked must-have: without them the tool can only edit wording, and a
program cannot own its own decision support.
Scope. (audited 2026-10-01 under 9g, #20; each line names the test or commit that proves it)
- A visual builder for show this question when… — pick a field, an operator, and a value from
that field's real options. —client/tests/condition-builder.spec.ts,four-builders.spec.ts(test(e2e): one journey drives all four builders; a picked relevance and constraint match at runtime on a live CHT (#20) #28);
../fieldrules open since feat(parser):.as a subject,../fieldas an alias of ${field}, function forms on.(#14) #24 (parser-dot-subject.spec.ts); searchable, grouped picker feat(ui): searchable, grouped field picker with labels; technical rows hidden; choice labels in pickers (#16) #25
(field-picker.spec.ts). The sentence-shaped editor is 9f (T9f: Sentence-shaped inline rule editor for relevant and choice_filter; advanced panel regrouped #19), still open. - The same for accept the answer only if… and for filtering one choice list by another answer.
— constraint: the Validation panel feat(validation): Validation panel with presets per question type, messages beside the rule; never normalises (#18) #27 (validation-panel.spec.ts; 575 of 777 real constraints open
fully as presets, 61 partly); choice_filter: the strip (four-builders.spec.ts). - A builder for compute the value as…, including a decision-table form for
if-this-then-that rules. —shared/src/xlsform/calculationBuilder.roundtrip.test.ts, geriatric build
specs; commits06fd03536e6f784362567c66cfcb;four-builders.spec.ts. - Combining conditions with and / or, and one level of grouping — enough to express
A and (B or C). —relevantParser.chain.roundtrip.test.ts,conditionReducer.test.ts
(enter-group-mode, flat-mixed refused); commits0b332bd3aa66ff2770f2a. - Reference values from elsewhere: an earlier answer in the same form, a field on the contact,
or a value the project's contact summary already computes. ⚠ All by picking, never by typing
a reference expression. —pick-preexisting-values.spec.ts,form-data-passing.spec.ts;
commits908ddd9be8279fe4fbab17a0aa2eb96e5b1; "another question" value picker feat(ui): searchable, grouped field picker with labels; technical rows hidden; choice labels in pickers (#16) #25. - ⚠ A raw escape that preserves what it cannot model. An expression outside the builder's
grammar must be editable as text and must survive an open-and-save byte-identically.
— the parse → serialize self-check (relevantParser.selfSubject.roundtrip.test.ts,presets.test.ts),
scripts/corpus-sweep.mjsidentical tomasteron every slice, 0 / 3919 cells drift (feat(parser):.as a subject,../fieldas an alias of ${field}, function forms on.(#14) #24, feat(validation): Validation panel with presets per question type, messages beside the rule; never normalises (#18) #27).
Not in scope. Grouping beyond one level. Deeper nesting multiplies the precedence and polarity
surface for very little expressive gain, and an expression that complex belongs in a computed field
instead.
Acceptance.
- Every value in every builder can be picked; no path requires typing an identifier or a
reference expression. — met in the inline strip and the Validation panel (feat(ui): searchable, grouped field picker with labels; technical rows hidden; choice labels in pickers (#16) #25, feat(validation): Validation panel with presets per question type, messages beside the rule; never normalises (#18) #27); the modal's
optional custom-name escape goes with 9f (T9f: Sentence-shaped inline rule editor for relevant and choice_filter; advanced panel regrouped #19). - An expression the builder cannot represent round-trips byte-identically. — feat(parser):
.as a subject,../fieldas an alias of ${field}, function forms on.(#14) #24, feat(validation): Validation panel with presets per question type, messages beside the rule; never normalises (#18) #27 (see above). - An automated test drives all four builders and asserts the emitted cells on disk.
—client/tests/four-builders.spec.ts(test(e2e): one journey drives all four builders; a picked relevance and constraint match at runtime on a live CHT (#20) #28). - A condition authored through the picker matches at runtime on a live instance — verified with
real data, not by reading the generated expression. —client/tests/live-instance-check.spec.ts(test(e2e): one journey drives all four builders; a picked relevance and constraint match at runtime on a live CHT (#20) #28):
deployed with cht-conf 6.5.0 inside the image to the local CHT 5.2.0 instance; as the CHW the question
hides, shows after "Pass", rejects 25 with the authored message, accepts 10 → report
01a0f7da-9c2b-766d-90aa-3aabfd8dd6be(2026-10-01).
Epic status (2026-10-01). Sub-issues: #15 (9b) PR #23 · #14 (9a) PR #24 · #16 (9c) PR #25 · #17 (9d) PR #26 ·
#18 (9e) PR #27 · #20 (9g) PR #28 · #19 (9f) open. Plan and 777-rule counts:
docs/plans/9_complex_logic_calculation_relevant_constraint/README.md on branch
9_complex_logic_calculation_relevant_constraint. Merge order for the stacked PRs: #23 → #24 → #25 → #26 → #27 → #28.
Activity
PrjShrestha commentedon Oct 15, 2026
Second wave (2026-10-15). Two sub-issues added from the PM/PO review of the HH Survey demos: #30 (9h, add-question picker: derived ids, reuse-list dropdown) and #31 (9j, calculation presets with parameters and a guide). The pre-deploy change summary from the same review is a feature of its own, #32 (T11), not part of this epic.