Files
Nonemm/docs/scoring-audit-2026-09-06.md
T
erikbrockoandClaude Opus 5 f5e3d9ed8a Score All Asian from the entrant's own side
All Asian is two contests in one class and only one of them was written.
An Asian entrant was paid three times the band table for every contact,
including Asian ones, could work its own country, and counted WPX
prefixes where the rules count DXCC entities.

AllAsian now asks where the entrant is. An Asian station works anybody
outside its own country, gets the band table once for Asia and three
times for the rest, and counts entities. Everybody else works Asia only
and counts prefixes, as before.

Maritime mobiles follow the JARL rule rather than N1MM, which applies it
to non-Asian entrants alone: a station at sea makes a valid contact with
Asian stations only, is paid the Asian rate wherever it is, and counts
for no multiplier either way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157i7PiZyqBkquxS9A6FTdQ
2026-09-10 20:43:43 +00:00

16 KiB

Scoring and N1MM compatibility review, 2026-09-06. Code reviewed: 6e24e00.

Nonemm does not yet match N1MM Logger+ well enough to claim general contest compatibility. Many ordinary QSOs agree, including large WPX and WAE logs, but there are confirmed failures in entry completion, excluded QSOs, exports, and several contest rules. This review changes documentation only.

The review covered the shared scoring engine, the 26 built-in contest families, contest-name resolution, selected UDC implementation paths, entry completion, score display, storage updates and Cabrillo generation. Specific behavior was compared with the local decompiled N1MM reference and published sponsor rules. N1MM was not executed. This is not a certification of every feature, contest category, UDC option or historical rule version.

Verification. ./build.sh test Nonemm.slnx --no-restore passed all 1,058 tests in 11 projects, including 245 contest tests. There were build warnings, including Avalonia runtime-loader warnings. An independent console program in /tmp/nonemm-scoring-audit exercised the findings below against the actual project assemblies. It used synthetic QSOs and the existing test country file; it did not invoke N1MM. Results are in results.txt. These reproductions are separate from the passing test suite.

The supplied om5m.s3db was read through a read-only SQLite connection and backed up to a temporary file before opening it with Nonemm's storage code. Its 56,284 rows cover 66 nonempty instances, including deleted contacts. Using OM5M in Slovakia, CQ zone 15 and ITU zone 28, the original stored country/continent fields, and each instance's entry settings:

Historical comparison Result
Instances with built-in rules available 62
Rows in those instances 55,415
Instances with identical points and all three multiplier flags on every row 45
Rows with different points 316
Rows with different multiplier flags 141

The complete comparison is in scoring-audit-history.csv. Stored points and multiplier flags were read directly from SQLite, because QsoColumns.Read deliberately discards those cached values. The comparison is against stored caches, not a fresh N1MM rescore. No external country or call-history file was supplied, and original per-instance station settings and cache freshness were not independently established. Therefore differences in this table are investigation targets, not automatically bugs.

Notable examples: all six WAE RTTY instances matched every compared field. Field Day instance 20 changed from 41 to 181 QSO points, with all 41 rows different. IARU instances 16 and 71 changed from 663 to 563 and from 636 to 594 points; those IARU differences need further investigation. The built-in registry could not resolve OKOMDXS, OKOMDXS_DX, or SA10_DX; those require their UDC files. DELETEDQS was excluded.

Confirmed findings, ordered by impact. P1 means an ordinary operating or submission path is broken. P2 means incorrect scoring, compatibility or display in a more specific case.

  1. P1 — Valid exchanges cannot be logged in seven contest definitions. EntryFields.cs, line 90, requires every field marked required. OperatingPosition.MoveFocus skips fields according to Contest.SkipsField, but LogContact and the ESM window still use that unconditional completeness check. In OK/OM DX, a Japanese station sends a serial; the county field is skipped but its emptiness prevents logging. The same problem affects CQ 160, ARRL 10M, REF, Ukrainian DX, YO DX HF and Mexico RTTY. Filling every applicable field produced IsComplete == false in all seven reproductions. Completion must use the same applicability rules as cursor movement. Do not fix this by requiring fabricated values in skipped boxes.

    Fixed 2026-09-10. bae40bb gave each of these contests the boxes N1MM shows, and none of them now marks a box required that the walk steps over: fifteen reproductions across all seven contests and both station types log cleanly. EntryFields.IsComplete takes the applicability rule as well, so the cursor and the completeness check read the same answer and this cannot come back; see SkippedFieldTests.

  2. P1 — Excluded contacts still score. ContestLog.cs, line 48, does not inspect Qso.IsClaimed. A CQ WW JA contact with IsClaimed=false still received 3 points and two multipliers, producing a score of 6. The edit dialog exposes this flag, statistics filter it, and Cabrillo omits the contact, so the user sees inconsistent results. The local N1MM Contest.ComputePoints, line 437, explicitly returns zero for an unclaimed QSO. Apply the exclusion consistently to points and multiplier eligibility, and verify N1MM's duplicate treatment separately.

    Fixed. ContestLog.Judge returns nothing for an unclaimed contact, and Index leaves it out of the dupe and multiplier searches; see ExcludedContactTests.

  3. P1 — Three Cabrillo exporters replace actual serials with 001. Arrl10Metre.cs, line 68; UkrainianDx.cs, line 80; YoDxHf.cs, line 75. Each calls SentExchangeFor(me) when formatting a contact. For a DX entrant that method returns the initial serial, not qso.SentNumber. All three exported 001 for a QSO whose sent serial was 42. The actual serial must be used where the entrant sends serial numbers. ARRL explicitly requires sequential DX serials in its 10M rules.

    Fixed. All three write qso.SentNumber where the entrant sends serial numbers.

  4. P1 — All Asian implements the wrong rules for Asian entrants. AllAsian.cs, lines 37 and 46, multiplies every Asian entrant's band points by three and always uses WPX prefixes. Reproductions: JA-to-JA on 20M scored 3 instead of 0; JA-to-UA9 scored 3 instead of 1; JA-to-K1 produced prefix K1 instead of country K. Asian entrants count DXCC entities, same-entity QSOs do not score, and the threefold factor applies to non-Asian contacts. Both the local N1MM CAA implementation and the 2026 JARL rules distinguish these cases. The existing tests only cover a non-Asian entrant.

    Fixed 2026-09-10. AllAsian now asks which side the entrant is on. An Asian entrant cannot work its own country, is paid the band table once for Asia and three times for everywhere else, and counts DXCC entities; everybody else works Asia only and counts WPX prefixes. Maritime mobiles follow the published rule, which N1MM gets half right: a station at sea makes a valid contact with Asian stations alone, pays the Asian rate wherever it is, and never brings a multiplier. AllAsianTests covers both entrant sides, all four point values and every band. One cosmetic gap is left: the score window heads the column "Prefixes" for an Asian entrant as well, because Contest.MultiplierNames is not asked which station is entering.

  5. P1 — BARTG HF and Sprint are merged into one incompatible definition. BartgSprint.cs uses report, serial and time, and countries/areas per band, but advertises BARTGRTTYS and BARTG-SPRINT. N1mmContestNames.cs also maps BARTGSRTTY to this class. N1MM has separate CBARTGSpring and CBARTGSprint classes. The January Sprint exchanges a serial only and counts countries/areas once across bands, as specified in the BARTG Sprint rules. Split the definitions and preserve each one's identity, exchange, multiplier scope and export format. Agreement on the supplied March HF log does not validate Sprint support.

  6. P2 — ARRL 10M uses the wrong state multiplier list. Arrl10Metre.cs, line 49, uses the ARRL DX list. A Mexican JAL exchange became country multiplier XE; another Mexican state would therefore not add its own multiplier. The code also lacks the contest's maritime ITU-region multiplier treatment, and its send-state decision omits Alaska and Hawaii. Use the actual 10M rules and ARRL's 32 Mexican multipliers, rather than sharing the ARRL DX eligibility list.

  7. P2 — Oceania undercounts 15M and 10M. OceaniaDx.cs, PointsOn, returns 1 for both bands. The local N1MM COceania.ComputePoints returns 2 and 3 respectively, also documented by the WIA contest scoring table. Both incorrect values were reproduced. The four supplied Oceania contacts do not exercise these bands, so their matching points do not cover this defect.

    Fixed 2026-09-10. The whole table is written out as N1MM writes it — 20, 10, 5, 1, 2, 3 — and it ends where N1MM's ends, so a band that is not in the contest scores nothing rather than one point.

  8. P2 — New SARTG and BARTG contacts miss call-area multipliers. SartgRtty.cs, line 35, and BartgSprint.cs, line 49, obtain call areas from Qso.Section. Neither entry definition includes that field, and OperatingPosition.BuildQso does not derive it from the call. A new SARTG K1ABC QSO returned only country K. N1MM's CSARTGRTTY.MultSqlString2 fills an empty section from its call-area lookup for K, VE, JA and VK. Imported rows with cached sections can pass comparison while newly logged contacts fail. Add call-area derivation and test both fresh and imported contacts.

  9. P2 — IOTA cannot score an island entrant correctly. Iota.cs, line 40, depends only on the received reference. It gives 2 for a world station and 15 for any island. An island entrant should receive 5 for a world station or its own island group, and 15 for another group. The station's own reference needs to participate in scoring. This limitation is acknowledged in a comment, but that comment misses the island-to-world case. See the 2026 RSGB rules.

  10. P2 — IARU zones can be counted twice through formatting. IaruHf.cs, line 50, uses the exchange text as the multiplier value. Working zone 8, then 08, awarded two zone multipliers in the reproduction. N1MM's CIaru.MultSqlString formats numeric zones consistently. Parse and normalize zones before producing multiplier keys; validate zone versus HQ exchanges separately. This finding does not explain all historical IARU point differences above.

  11. P2 — CQ 160 maritime mobiles receive incorrect points and countries. CqOneSixty.cs, line 54, has no maritime exception. Through the shared country fallback, JA1XYZ/MM from Germany scored 10 and could claim Japan. The CQ 160 rules specify 5 points and no multiplier for a maritime mobile. Similar country fallback behavior exists in CQ WW; review it against each contest's rules rather than changing the common fallback indiscriminately. Some existing tests deliberately assert N1MM-like fallback behavior; the project objective explicitly says to follow published rules where N1MM is wrong.

  12. P2 — Field Day is an unconditional approximation of one rules variant. FieldDayRegionOne.cs, line 32, always uses portable=5, EU/AF fixed=2, other fixed=3. N1MM's CFdReg1.ComputePoints branches on the contest variant and the entrant's country; its generic fallback is 1. For the supplied Slovak FDREG1 instance, all 41 stored one-point QSOs were changed, raising the points to 181. Continent alone also cannot determine IARU Region 1. Identify the intended national/contest variant and its published rules before replacing these tables; there is no single safe universal table here.

  13. P2 — Rescoring after edits does not persist all changed cached scores. ContestSession.cs, lines 158 and 240, rebuilds the in-memory log after deletion or replacement but does not update every affected stored row. Deleting the first of two Japanese CQ WW contacts made the second contact a multiplier in memory while its stored flag remained false. Incoming network inserts have the same cache consistency concern. Explicit Rescore repairs these columns. N1MM itself documents manual rescoring after some edits, so this is principally a limitation of automatic interoperability: reopening the file in N1MM without a rescore can show stale totals. See N1MM's log-window instructions.

  14. P2 — The score window mixes unweighted and weighted multiplier counts. ScoreWindow.axaml.cs sums Boolean flags for each band but uses Tally.TotalMultipliers for the total. A single WAE multiplier on 80M appears as 1 in the band row and contributes 4 to the total. QTC rows are also counted in the QSO column even though Contest.IsContact identifies them as traffic. Display raw multipliers, weights and QTC counts consistently. This does not by itself change WAE's correctly weighted total score.

Not a fault; this is what N1MM does. ScoreRow.AddContact adds the weight of a WAE multiplier, four on 80M and so on, to the band row and to the total alike, and ScoreTally.AddMultiplier has done the same since d4d0375. N1MM counts QTC rows as Qs too — ValidQs counts every row that reaches the score — and keeps them readable by splitting each WAE band into a row per kind under Q/QTC, which the score window here does as well. Taking the QTC rows out of the QSO column would leave N1MM's numbers behind.

Coverage and remaining limits. The 26 built-in families are CQ WW, CQ WPX, ARRL DX, IARU, Sweepstakes, WAE, ARRL RTTY Roundup, NAQP, OK/OM DX CW, YOTA, IOTA, EU DX, CQ 160, Mexico RTTY, REF, BARTG, ARRL 10M, Russian DX RTTY, RDAC, YO DX HF, Oceania, Region 1 Field Day, SARTG, All Asian and general logging. CW/SSB/RTTY variants share classes where the registry supplies a mode. This is a subset of N1MM's supported contests.

The shared engine correctly centralizes incremental dupe/multiplier checks and performs chronological rebuilds for edits and network inserts. However, test success and matching existing rows do not establish correctness for entrant countries, new-entry field generation, excluded contacts, or bands absent from those logs.

The following require further work before claiming full compatibility:

  • UDC support is explicitly a subset. Unrecognized settings are silently ignored. A successfully loaded file is not proof that all its scoring options work. Validate against a collection of actual UDC files and report unsupported scoring directives.
  • No built-in rule overrides BandChangesFor; all use the category fallback. The interface comments describe contest-specific overrides that are absent. Multi-station band-change rules therefore need contest-by-contest verification.
  • There is no shared contest-band eligibility model. ShowsWarcBands controls presentation, not scoring. Several rules award points outside their contest bands; WAE has an explicit band check. Separate band validity from N1MM's permissive frequency-to-band mapping.
  • The core score types use int, including products. Very large scores or UDC bonuses can overflow. Establish supported bounds and use a wider score type if needed.
  • The historical YOTA, EU DX, CQ WW and IARU differences need individual row analysis with the original station setup and country/history files. Do not change rules merely to force agreement with potentially stale caches.
  • Rig, keying, digital, cluster and network transport behavior was not certified by this scoring review. Existing limitations are documented in unfinished.md, which also contains stale or contradictory status statements.

Fix entry completion and the three serial exporters first, then excluded-contact scoring and the contest-specific P1 findings. Add targeted regression tests from these reproductions, including fresh entry through OperatingPosition, followed by database and Cabrillo checks. For each contest, cover both entrant sides and every scoring band before describing it as compatible.