Let the walk and the completeness check read one rule

A box the walk between the entry boxes steps over cannot be typed in, so it
must not hold the contact back either. `EntryFields.IsComplete` counted every
box marked required, while `MoveFocus` stepped over the ones the contest does
not ask this station for, so a contest with a box for some stations only
could lock the operator out of his own log.

The seven contests the scoring audit named do not do that any more —
`bae40bb` gave each of them the boxes N1MM shows, and fifteen reproductions
across all seven and both station types log cleanly — but the rule they broke
was still there to break again. Completeness now takes the same
applicability rule the cursor uses, and `OperatingPosition` answers it in one
place for the entry window, the ESM window and logging alike.

The audit is marked up for the three findings that are already fixed and for
item 14, which turns out to be what N1MM does rather than a fault.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
This commit is contained in:
2026-09-10 20:33:30 +00:00
co-authored by Claude Opus 5
parent e56d6096f3
commit 211298e04c
6 changed files with 161 additions and 7 deletions
+8 -3
View File
@@ -112,10 +112,15 @@ public sealed class EntryFields
Call.Trim().Length == 0 &&
Exchange.Where((f, at) => !f.IsSent && values[at].Trim().Length > 0).Any() == false;
/// True when everything the contest asks for has been typed.
public bool IsComplete =>
/// True when everything the contest asks for has been typed. `applies`
/// says whether a box is one this station is asked for: the walk between
/// the boxes steps over the ones it is not, so an empty box the cursor
/// never lands in cannot be what is missing. `OperatingPosition` asks the
/// contest, and both use the same answer.
public bool IsComplete(Func<ExchangeField, bool> applies) =>
Call.Trim().Length > 0 &&
Exchange.Where((f, at) => f.IsRequired && values[at].Trim().Length == 0).Any() == false;
Exchange.Where((f, at) =>
f.IsRequired && applies(f) && values[at].Trim().Length == 0).Any() == false;
public void Clear()
{