Goldie Blocks

A Question Is Done When It Is Answered or Skipped

Why skipped is a boolean on the field rather than a status on the block, why it is refused on a required field, and why not-applicable is a separate question answered by logic

Decision

A field is done when it holds a value or carries skipped: true. A module or document is complete when every field beneath it is done. skipped is refused on a required field.

Nothing above the field stores a status. Progress at every height — block, module, document — is folded from the fields, so there is no second copy to go stale.

In plain terms: every question on the form is either answered or deliberately struck through. Nothing is left silently blank. You know the form is finished because there is nothing on it that is neither.

Who may skip, and what requiring does

Owner ruling, 2026-09-15. Two halves, both about who acts rather than about the invariant above, which is unchanged.

Skipping belongs to the Maker. Striking a question through is a way of answering it — the field is done — so it is the act of the role that fills the document in, not the one that builds it. A Builder laying out the form has no client in front of them to decline a question, and is no longer offered Skip or Unskip. The capability model already said so: the seam sits with onValueChange and onClearValue, never gated on canEditBlock like move, delete and retype beside it; what was missing was the other half, so the menu read as though skipping were structural.

Requiring a struck-through question reopens it. The Builder has raised the bar after the Maker passed the question over, so the question comes back and must be answered again. transitionBlockFields clears skipped in the same write — this is one act, not two facts, so the pair-invariant below is upheld rather than bypassed: storage is never asked to hold "required and skipped" at once. canSetRequired therefore no longer withholds the control on a skipped field; it withholds it only where the write would still fail, on a computed field.

In plain terms: the client struck a question out, then the firm decided it must be answered after all. The strike goes and the question comes back, so the client is asked again.

The Maker's decision is overwritten silently. A mark saying the field was reopened is wanted but unbuilt, and belongs with the rest of Maker mode (GB-93), which also owns moving Skip off the type mark — that control answers "what kind of thing is this", not "am I filling this in".

Three separate questions, at three levels

The retired interviewStatus column blurred these together, which is why it could not be replaced by a single rule.

QuestionAnswered byLevel
Does this part of the document apply at all?logicblock or module
Must this question be answered?requiredfield
Was this optional question deliberately passed over?skippedfield

They compose in that order, and the order is what makes each one small:

  1. logic decides applicability. A guarantee module that does not apply is excluded, and its fields never enter the count at all.
  2. required is only meaningful where the field applies. Asking whether a guarantor's name is required is a question you can only ask once you know there is a guarantee.
  3. skipped: true may only be set where required is false. If a guarantee is required, we need the guarantor's name. There is no honest way to record that we finished without it. Absent required is not required, so the flag is available there too — the invariant reads off requiredness, not off whether the author typed the word.

In plain terms: whether the clause is in the deed at all is one question; whether a blank in that clause has to be filled is a second; whether a blank that need not be filled was deliberately left is a third. Answering them in that order is what stops any of them growing into a catch-all.

Skipping is therefore never how an inapplicable section is handled. That was the tempting shortcut — a client says "there is no guarantor", and marking the section skipped looks like the answer. It is not: the section's fields are required, skipped is refused on them, and the honest record is that the section does not apply. That is logic, and it excludes the whole thing rather than striking through its parts.

Why the field, and not the block

An earlier draft of this decision put the skipped fact on the data block, on the reasoning that a data block is the unit that holds an interview. The retired system did something close to that: skipping a module wrote skipped onto the module version and onto each of its data blocks — the same fact stored twice, at two heights, free to disagree.

Putting it on the field is strictly better:

  • It sits with the thing it describes. A skipped question is a decision about one question, not about a container.
  • Everything above it derives. A block is done when its fields are done, a module when its blocks are, a document when its modules are. One stored fact at the leaf, and no fold to keep in step with a stored parent.
  • It removes an ambiguity rather than adding a field. Under the previous rule, complete meant "nothing required is outstanding" — so a block whose questions are all optional flipped to complete on its first answer, with the rest blank. There was no way to tell a blank that had been considered from a blank nobody had reached. Now there is.

Consequences

  • skipped joins the field contract as an optional boolean, beside required. Absence means not skipped, the same way absent required means not required. It prints on the About card only when true, like required.
  • complete tightens. It now means every applicable field is answered or skipped, not merely every required one. A document sitting at complete with a blank optional field drops back to in progress until that field is answered or skipped. This is the intended behaviour change, not a side effect: it is what removes the ambiguity above.
  • A write that sets skipped on a required field is refused, and so is a write that makes a skipped field required without clearing the flag. The two are one invariant read from either end.
  • No reason is stored. The retired column carried a reason vocabulary (declined, deferred, condition false, agent judgment) and a free-text summary. Ruled out: the reason a required thing was not done would matter, but a required field cannot be skipped; and why an optional question was passed over is not a fact the document needs to carry.
  • Nothing else stores interview progress. resolveDataBlockStatus and the folds above it in @goldie-blocks/domain/interview remain the only answer to "how far along", and they read value and skipped.

Status

Ruled by the owner, 2026-09-02, settling backlog decision 11 (Library master or original — which Folio does the owner edit after Save to Library?).

logic is post-MVP, so the applicability leg above is not yet buildable. That is not a gap for MVP: the MVP proof is a deliberately unconditional services or confidentiality agreement, which has no inapplicable sections.

The skipped leg is MVP, and stays MVP after the interview was ruled post-MVP on 2026-09-02. skipped is a fact about a question, not a piece of interview machinery: a Builder filling a document directly answers some questions and deliberately passes over others, and the completion rule above is what tells them the form is finished. What ships later is Goldie conducting the question-and-answer pass, not the ability to record that an optional question was passed over.

Built 2026-09-05 (GB-152 Stage D row 2). skipped is a member of the one field contract (packages/domain/src/field/field-schema.ts), stored only as true; the contract's refinement refuses a skipped required field from either end, and every field write parses with it before storage (convex/folio/fieldWrites.ts). write-field-values sets and clears it (fieldValueWrites.ts: answering drops the mark). The progress fold (packages/domain/src/interview/progress.ts) reads it: a field is done when it holds a value or is skipped, and complete means every answerable field is done — the behaviour change this record intended. No field row offers a skip control yet; that is owed to the canvas.

On this page