You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
T9a: Parser reads real rules: . as subject, ../field alias, function forms #14
Parent: T9 (#9) · Importance: Must have · Size: M · Depends on: nothing. Unblocks 9e and 9g.
Why. The shared parser only recognises comparisons whose subject is ${field}. Real configs write
validation rules against the answer itself as . (. >= 0 and . <= 20), and real relevants use
relative paths (../lmp_approx). Across 777 constraint rules in seven configs the builders open 3. Nothing in the UI can improve until the parser reads what real forms contain. This slice is
shared-only and carries the round-trip risk, so it ships alone, before any UI depends on it.
Scope.
relevantParser.ts: accept . as a comparison subject. Values may be a number, a quoted string, ${field}, ../field, today(), now(), today() ± N, date('…').
Accept ../field as an alias of ${field} for subject and value. ⚠ Re-emit exactly as written;
never rewrite ../field to ${field} or back.
Recognise the function forms on . that real configs use: string-length(.), regex(., '…'), selected(., '…'), count-selected(.), int(.), int(format-date(., '%Y')), max(coalesce(${a}, 0), …), add-date(today(), …), difference-in-months(., today()), decimal-date-time(.).
Recognise true, true() and 1 as an always-true rule kind so the UI can label it. ⚠ Never
rewritten.
⚠ Additive only. New rule kinds or new entry points; parseRelevant / parseRelevantGrouped
return shapes seen by the five existing consumers do not change.
The parse → serialize self-check stays authoritative: anything that does not re-serialize
byte-identical is raw text. .<=100 and . <= 100 both round-trip unchanged.
node --test round-trip case per new form in shared/src/xlsform/, each calling the serializer
and starting from a non-canonical fixture.
Convert the ../field hostile fixtures from { todo: true } to live for the byte-stability half;
only the "opens as clauses" half stays todo until 9e / 9f.
QA: measure the ../ vs ${} share across the seven configs and record it in the plan folder.
Not in scope. Any UI. The preset recogniser (9e). The function catalogue (separate issue).
Acceptance.
scripts/corpus-sweep.mjs shows zero drift on all seven configs after the change.
A test over the 777-rule corpus reports how many constraint rules now parse structurally; the
number is recorded in the README of the plan folder (target after 9e: about 705 of 726).
Every new round-trip test starts from a non-canonical fixture and asserts serialize(parse(x)) === x.
pnpm typecheck is clean with no change to any consumer of parseRelevant, which proves the
widening is additive.
Plan: docs/plans/9_complex_logic_calculation_relevant_constraint/README.md · Analysis: validation-rules-v2.md §"Proposed support" 1, §"Test plan". Line anchors against bff69cb: relevantParser.ts:370 is the ${}-only comparison regex.
What an author can do after this ticket · PR #24 · demo client/demo/t9/t9a.mp4 (attached below)
Rules written by hand about the answer itself (.) or with ../field now open in the builder as structure instead of a "could not be parsed" text box.
You want
Do this in the UI
Sheet gets / keeps
See a hand-written . >= 0 and . <= 20 as a rule
show advanced → Validation
reads Between 0 and 20; cell unchanged
See a tight .<=100 as a rule
show advanced → Validation
reads at most 100; cell stays .<=100
See a ../lmp_date != '' relevant as a sentence
show advanced → Logic
reads Last menstrual period has an answer; cell stays ../lmp_date != ''
Re-apply or save without edits
Apply / Save
every cell byte-identical, including spacing and ../ spelling
Unchanged on disk: everything. This ticket only reads; 682 of 777 real constraints now open structurally (was 3). Still code: rules mixing and/or without parentheses, decimal-date-time arithmetic, curly quotes.
Parent: T9 (#9) · Importance: Must have · Size: M · Depends on: nothing. Unblocks 9e and 9g.
Why. The shared parser only recognises comparisons whose subject is
${field}. Real configs writevalidation rules against the answer itself as
.(. >= 0 and . <= 20), and real relevants userelative paths (
../lmp_approx). Across 777constraintrules in seven configs the builders open3. Nothing in the UI can improve until the parser reads what real forms contain. This slice is
shared-only and carries the round-trip risk, so it ships alone, before any UI depends on it.
Scope.
relevantParser.ts: accept.as a comparison subject. Values may be a number, a quoted string,${field},../field,today(),now(),today() ± N,date('…').../fieldas an alias of${field}for subject and value. ⚠ Re-emit exactly as written;never rewrite
../fieldto${field}or back..that real configs use:string-length(.),regex(., '…'),selected(., '…'),count-selected(.),int(.),int(format-date(., '%Y')),max(coalesce(${a}, 0), …),add-date(today(), …),difference-in-months(., today()),decimal-date-time(.).true,true()and1as an always-true rule kind so the UI can label it. ⚠ Neverrewritten.
parseRelevant/parseRelevantGroupedreturn shapes seen by the five existing consumers do not change.
byte-identical is raw text.
.<=100and. <= 100both round-trip unchanged.node --testround-trip case per new form inshared/src/xlsform/, each calling the serializerand starting from a non-canonical fixture.
../fieldhostile fixtures from{ todo: true }to live for the byte-stability half;only the "opens as clauses" half stays todo until 9e / 9f.
../vs${}share across the seven configs and record it in the plan folder.Not in scope. Any UI. The preset recogniser (9e). The function catalogue (separate issue).
Acceptance.
scripts/corpus-sweep.mjsshows zero drift on all seven configs after the change.constraintrules now parse structurally; thenumber is recorded in the README of the plan folder (target after 9e: about 705 of 726).
serialize(parse(x)) === x.pnpm typecheckis clean with no change to any consumer ofparseRelevant, which proves thewidening is additive.
Validate.
Plan:
docs/plans/9_complex_logic_calculation_relevant_constraint/README.md· Analysis:validation-rules-v2.md§"Proposed support" 1, §"Test plan". Line anchors againstbff69cb:relevantParser.ts:370is the${}-only comparison regex.