feat(assessment): she can untick questions and get the paper again (bd-60023) - #595
Conversation
The paper arrives finished and unchangeable. If two questions are wrong for
her class, the only remedy is to build another paper from scratch and hope.
The schema has anticipated this since V1.2.6 — exam_json as the living
document, original_exam_json frozen beside it, selected_question_ids, and
edited_at — and nothing was ever built on those columns. This builds it.
After the document lands she is offered a Flow that lists her questions,
every one ticked. She unticks what she does not want, taps once, and the
same paper comes back without them. No model call: the questions already
exist, so this is a filter, a render and a send, and it answers in seconds
rather than the minute generation takes.
Three things carry the risk, and each has tests:
· A question is addressed by its PATH in the tree (seen.objective.MCQs.0),
never by its printed position. Untick question 2 and everything after it
renumbers, so a stored "question 4" would come to mean a different
question than the one she chose. A test pins this traversal against the
renderer's, because her ticks are numbered against what the paper printed.
· Her ticks live in the session, not in the form. Meta renders at most 20
checkbox options and real papers on staging came back at 10, 20, 28 and
64 questions, so the screen pages — and the form only ever knows the
twenty rows on screen. Merging per page is what stops a submit from an
early page silently dropping every question she never scrolled to.
· NULL and [] stay distinct. NULL is "she never chose", which is the whole
paper; [] is "she unticked every one", which is refused on the screen
where she can still fix it, rather than rendered as a blank exam.
Two things the Flow JSON only told us when Meta was asked. `init-value` is
not allowed on a CheckboxGroup — preselection is `init-values` on the Form,
keyed by component name — and a screen may not route to itself, so REVIEW
routes only to SUBMITTED and re-renders come back as data_exchange. Both
were found by uploading to a throwaway draft Flow and reading the errors;
the file now validates with zero of them.
One latent bug found by the tests rather than by reading: this module's job
entry point is `async function process`, and that declaration shadows the
global across the whole file, so `process.env` here is the job argument's
undefined `.env`. The offer would have been a silent no-op in production.
Gate: 206 tests pass across 11 assessment suites. `npm test` exits 1 on
this branch and exits 1 identically on a clean origin/develop — same 16
suites, same 34 offender files — so nothing here is new debt.
Closes: bd-60023
This repo is public and the source-hygiene guard forbids bd-/BUG-/FEAT- references in shipped code. Two comments in the assessment services carried one. They predate this branch, but the edit above moved one of them onto a new line, which the guard reads offender-by-offender and so counts as new. Removing the refs is the fix rather than widening the guard's allowlist: the sentences say the same thing without them, and an allowlist entry would outlive the reason for it.
|
CI note — The baseline gate fails on this PR. It also fails on
The offenders are in The first run of this PR did surface one offender of mine: Locally I also ran the gate against a clean Assessment suites on this branch: 206 passing across 11 suites. |
What this adds
The generated paper arrives finished and unchangeable. If two questions are wrong for her class, the only remedy today is to build another paper from scratch. This adds the review layer that
V1.2.6has had columns for since the day it was written —exam_json,original_exam_json,selected_question_ids,edited_at— and which nothing was ever built on.After the document lands she gets a Flow listing her questions, all ticked. She unticks what she doesn't want, taps once, and the same paper comes back without them. No model call — the questions already exist, so it's a filter, a render and a send.
The three things that carry the risk
seen.objective.MCQs.0), never printed position. A test pins the traversal against the renderer'scollectQuestions, because her ticks are numbered against what the paper printed.NULLvs[]collapsingNULL= never chose = whole paper.[]= unticked everything, refused on the screen where she can still fix it, not rendered as a blank exam.Two things Meta only told us when asked
Both found by uploading to a throwaway draft Flow and reading the validation errors — the file now validates with zero:
init-valueis not allowed onCheckboxGroup. Preselection isinit-valueson the Form, keyed by component name. (Every guessed spelling —init-values,value,selected,default-valueon the component — was refused by name.)REVIEW → REVIEWis rejected as a routing loop; re-renders come back asdata_exchange, which is not a routing-model edge.A latent bug the tests caught
This module's job entry point is
async function process, and that declaration shadows the global across the whole file — soprocess.envinside it is the job argument's undefined.env. The offer would have been a silent no-op in production. Found because a test asserted the send, not by reading the code.Gate
npm testexits 1 on this branch and exits 1 identically on a cleanorigin/develop— same 16 suites, same 34 offender files, byte-for-byte. Nothing here is new debt.V1.2.6.Red-first throughout; each behaviour above failed before it passed.
Closes: bd-60023