refactor(parser): extract line classifier #104

Merged
TimCane merged 4 commits from feat/91-extract-line-classifier into main 2026-07-07 11:06:57 +01:00
TimCane commented 2026-07-07 10:47:40 +01:00 (Migrated from github.com)

Summary

  • Extract line classification (A3 of #77 / ADR-0006) into ILineClassifier +
    KeywordClassifier under Parsing/Classification, mirroring the A2 normalizer
    shape (internal interface + default impl, wired into the engine).
  • Move the keyword/positional LineType decisions - subtotal, item-count,
    rollup, VAT breakdown, tax, tip, service, total, payment noise, bare charge,
    and the lone-service-label positional case - out of the two engine cascades.
  • Engine consumes LineType for grand-total detection and above-total
    classification; item-name and unit-price rules stay in the engine for A4.
  • Carry PreviousHasAmount on Candidate so the lone-service-label check reuses
    the engine's single money regex instead of a duplicate in the classifier.
  • Behaviour-preserving: a single classifier precedence unifies the old
    Tax-before-Total quirk (a "Total Taxes" row reads as tax and never wins the
    grand total), leaving corpus output byte-identical.

Test plan

  • dotnet build backend/BillSplitter.sln clean, 0 warnings.
  • dotnet test backend/tests/BillSplitter.Tests green: 85 passed, all 33 corpus
    fixtures byte-identical, no expected.json changed.

Closes #91

## Summary - Extract line classification (A3 of #77 / ADR-0006) into `ILineClassifier` + `KeywordClassifier` under `Parsing/Classification`, mirroring the A2 normalizer shape (internal interface + default impl, wired into the engine). - Move the keyword/positional `LineType` decisions - subtotal, item-count, rollup, VAT breakdown, tax, tip, service, total, payment noise, bare charge, and the lone-service-label positional case - out of the two engine cascades. - Engine consumes `LineType` for grand-total detection and above-total classification; item-name and unit-price rules stay in the engine for A4. - Carry `PreviousHasAmount` on `Candidate` so the lone-service-label check reuses the engine's single money regex instead of a duplicate in the classifier. - Behaviour-preserving: a single classifier precedence unifies the old Tax-before-Total quirk (a "Total Taxes" row reads as tax and never wins the grand total), leaving corpus output byte-identical. ## Test plan - `dotnet build backend/BillSplitter.sln` clean, 0 warnings. - `dotnet test backend/tests/BillSplitter.Tests` green: 85 passed, all 33 corpus fixtures byte-identical, no `expected.json` changed. Closes #91
Sign in to join this conversation.
No description provided.