Skip to content

Run the FieldWorks parser conformance suite against HermitCrab in CI - #615

Open
johnml1135 wants to merge 15 commits into
fix/signature-drops-id-under-identity-rulefrom
hermitcrab-conformance-runner
Open

johnml1135 wants to merge 15 commits into
fix/signature-drops-id-under-identity-rulefrom
hermitcrab-conformance-runner

Conversation

@johnml1135

@johnml1135 johnml1135 commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Quick summary

dotnet test and a new CI workflow now run the FieldWorks parser conformance suite against the C# HermitCrab engine. The suite is not vendored: conformance.commit pins its commit, and the build clones it into an ignored conformance/. The engine is untouched, but the hc tool's script mode now exits with the first failing command's code.

Where to look

  • Missing or stale suite -- ConformanceSuite.Root() fails each test with the two commands that fix it.
  • First build needs git and network -- EnsureConformanceSuite.targets only warns on failure, kills a git stalled for 3 minutes, and skips packing.
  • Engine against fixtures -- ConformanceFixtureGateTests and conformance.yml, which fails a pin off the suite's main.
  • hc exit code -- ToolScriptExitCodeTests.

Deliberately not included

  • PanGloss: --adapter can run its binary, but CI runs only the C# engine until PanGloss adopts the suite repo.

Validation

Issue / porting context

Stacked on #500; #556 and #500 land first. Replaces #480 and #578, both closed. Their grammars are now in sillsdev/fieldworks-parser-conformance.


Behaviour change to the shipped `hc` tool

Script mode (hc -i grammar.xml -s script) used to exit 0 whatever the commands returned. It now exits with the first nonzero command result, so the runner's batch wrapper can see a failure. ToolScriptExitCodeTests pins it. Interactive mode is unchanged.

What is kept and dropped

Kept: the runner (self-check and --adapter), batch, and only the tests that fail when the engine, the runner or the pin changes: fixture gate, manifest, schemas, DTD, FieldWorks-producible shapes, adapter crash classification. Dropped: semantic-coverage ledgers and their ratchets, the counterfactual workflow, eng/, global.json, the grammar-overview test.

How the pin behaves

At the pin: no network and no git writes; local changes get a warning, from the build and from the runner. At another commit: moved to the pin, or a build error if it has uncommitted changes. No git, a failed fetch, a git that stalls for 3 minutes or any other failure: a warning, then the tests fail naming the two commands. HC_CONFORMANCE_SUITE=<dir> uses that directory and skips the clone and the pin check. To bump: edit conformance.commit, build, run the runner checks, commit.

Verification detail

Plain clone: 270 passed in 44 s. Windows (PowerShell 5.1, Git for Windows 2.51, dotnet 10.0.303): first build cloned and reached the pin, a second build with GitHub unreachable touched nothing, a pin bump moved the suite and back, a two-hour-old read-only temp directory was removed, 270 passed. Linux: a hung git ended the build in 3 minutes with a warning; a failed rename kept the clone and warned. Runner: 143 on 133 fixtures, stress 16/16, manifest and coverage current. CI on this head passed on Ubuntu and Windows, including both conformance suite jobs. Not run: local_check.sh.

🤖 Generated with Claude Code


This change is Reviewable

johnml1135 and others added 6 commits November 2, 2026 14:54
The tool's batch command parses every word of a list with one grammar and
writes one row per word: index, word, milliseconds, status and result
signature. SignatureFormat builds the signature, so an external runner
compares its engine against the same text.

The script mode of the tool now returns the first nonzero command result
instead of always 0, and SplitCommandLine is internal so a runner tokenizes a
command template the same way. A MorpherTests case pins the Morpher
constructor defaults that the signature protocol documents.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
conformance.commit holds the full SHA of the commit of
sillsdev/fieldworks-parser-conformance that machine runs. The clone in
conformance/ is ignored by git and by csharpier, as is the temporary
directory a clone is made in.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The runner checks the conformance suite against the C# HermitCrab engine.
Each fixture's words.yaml is compared with what the engine returns, word by
word, in process, including traced rules and budget_ms. --adapter runs the
same fixtures through an external engine from a command template, as the
suite's PROTOCOL.md describes. --propose prints the words.yaml patch for a
mismatch, --coverage-report checks the suite's coverage.csv, rules.csv and
dead-rule list, and --check-manifest validates every grammar and words.yaml
against the suite's DTD and schema and compares the suite's generated
manifest with a fresh computation. The runner writes nothing into the suite.

Building the runner runs EnsureConformanceSuite.targets, which makes
conformance/ a clone of the suite at the commit in conformance.commit: cloned
into a temporary directory and renamed when absent, moved to the pin when it
is at another commit, left alone when it has uncommitted changes, and never
fetched when it is already at the pin. A clone at the pin with local changes
gets a warning naming them. A git that hangs is killed after three minutes,
and any failure of the target is a warning, never a build failure. The
runner warns when conformance/ is not at the pin or has local changes.
HC_CONFORMANCE_SUITE=<path> uses another copy instead. SIL.Machine, the
HermitCrab library and the tool do not reference the runner, so they never
run it.

The tests run every fixture, the manifest, the schemas and the
FieldWorks-producible shape rules in dotnet test, and fail with the command
that fixes it when the suite is missing or not at the pin. A test pins the
script-mode exit code of the hc tool, which is the first nonzero result.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Selected with TestCategory=Stress, StressFixtureParityTests runs the 16
fixtures under conformance/stress through the same comparison as the runner.
It never runs by default.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The Conformance workflow builds the runner once in Release, then runs the
self-check, the stress self-check, the manifest check and the coverage
report, and fails unless the commit in conformance.commit is an ancestor of
the suite's main. local_check.sh runs the same checks and honours
HC_CONFORMANCE_SUITE.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The README and AGENTS.md point to the suite repository. A README beside the
runner says how the suite is fetched, how to bump the pin, how to regenerate
the files the suite owns, how to run another engine with --adapter, and what
to run before pushing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Nov 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.27557% with 664 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.10%. Comparing base (d0ab6fe) to head (4d9b454).

Files with missing lines Patch % Lines
...chine.Morphology.HermitCrab.Conformance/Program.cs 38.77% 190 Missing and 20 partials ⚠️
...rphology.HermitCrab.Conformance/WordsYamlLoader.cs 74.80% 68 Missing and 31 partials ⚠️
...ology.HermitCrab.Conformance/MaterializedRunner.cs 54.87% 67 Missing and 7 partials ⚠️
...Morphology.HermitCrab.Conformance/AdapterEngine.cs 0.00% 73 Missing ⚠️
...achine.Morphology.HermitCrab.Conformance/Runner.cs 81.58% 45 Missing and 13 partials ⚠️
....Machine.Morphology.HermitCrab.Conformance/Diff.cs 67.60% 21 Missing and 2 partials ⚠️
...gy.HermitCrab.Conformance/WordsSchemaValidation.cs 76.47% 12 Missing and 8 partials ⚠️
...logy.HermitCrab.Conformance/FixtureMaterializer.cs 79.01% 12 Missing and 5 partials ⚠️
...itCrab.Conformance/ConformanceManifestGenerator.cs 85.39% 10 Missing and 3 partials ⚠️
...ology.HermitCrab.Conformance/ProposePatchWriter.cs 45.45% 10 Missing and 2 partials ⚠️
... and 11 more
Additional details and impacted files
@@                              Coverage Diff                               @@
##           fix/signature-drops-id-under-identity-rule     #615      +/-   ##
==============================================================================
+ Coverage                                       74.62%   75.10%   +0.48%     
==============================================================================
  Files                                             453      490      +37     
  Lines                                           38345    41265    +2920     
  Branches                                         5285     5682     +397     
==============================================================================
+ Hits                                            28614    30993    +2379     
- Misses                                           8568     9048     +480     
- Partials                                         1163     1224      +61     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread local_check.sh Resolved
@claude

This comment has been minimized.

johnml1135 and others added 8 commits November 2, 2026 15:10
The dispatcher counted a batch line's arguments after it had parsed
--start, and the reset ran only when Run completed. A script line such as
"batch --start=2 words.txt" was rejected for its missing output path and
left the next batch line resuming at index 2. BatchCommand now counts its
own arguments and clears the start index before it rejects a line.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Self-check passed an expect_crash fixture on any exception except the
watchdog timeout, so a harness failure such as the rule index rejecting a
duplicate rule name read as the expected crash. Adapter mode already
credited only EngineCrashException. The engine load and the parse now
throw that exception, and the crash match requires it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A grammar that was not well-formed XML made GrammarValidation throw from
the reader, and --check-manifest ended in an unhandled exception whose
stack trace did not name the fixture. The loop now reports the fixture
id and the reader's message and exits 2, as it does for a grammar that
fails the DTD.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
RunSelfCheck adds one result per word run, so a stress fixture with a
variants list reported two passes and failed the test's expectation of
exactly one. The test now requires at least one result and every result
to have passed. No current stress fixture has variants.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
local_check reported suite-modified whenever the suite had uncommitted
edits, which the targets allow and HC_CONFORMANCE_SUITE is meant for, and
it missed files the runner left untracked. It now records the suite's
status and diff before the runner steps and fails only if they differ
afterwards.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Three diagnostic messages sorted names with the current culture, so the
order could differ between machines. They now use StringComparer.Ordinal
as the rest of the runner does. Only the message text is affected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The template is split by the same routine as an hc script line, which
drops quote characters and keeps no empty token, so --grammar="{grammar}"
reached the engine as two arguments. The README now says to write
--grammar {grammar}, and that placeholders are filled after the split so
a path with spaces stays one argument.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Self-check mode runs through Runner.RunSelfCheck, and RunAdapter is
reached only with an AdapterEngine, so SelfCheckEngine.Run was never
called. Without it the class only carried a name and a capability set
into the report header, so Program now passes those to PrintRunReport
directly. Comments that named it as an IEngine are corrected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@claude

This comment has been minimized.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@claude

claude Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

Verdict: approve. The only change since the last round fixes F8, and no findings remain open.

Most important: F8 (tests/SIL.Machine.Morphology.HermitCrab.Tests/ConformanceCommandLineTests.cs:126) is fixed in 4d9b454. The line that was too wide is now wrapped one argument per line.

Counts: Critical 0, Important 0, Low 0 open. No new findings.

Ran: git diff 1bd1dc9d 4d9b454d: one file, 4 lines added and 1 removed, formatting only. gh pr checks 615: both Conformance suite jobs and both Comment hygiene jobs pass. Build on ubuntu-22.04, Build on windows-latest and claude-review are still pending. Comment hygiene is advisory, so its pass does not show that --agent-strict passed.

Not verified: I could not run CSharpier here, so whether F8 is fully fixed depends on the pending ubuntu Build reaching "Check formatting" and passing it.

API / parity: None verified changed since the last round. This is not a port from sillsdev/machine.py.

Findings: F1 addressed, F2 addressed, F3 withdrawn, F4 addressed, F5 addressed, F6 addressed, F7 addressed, F8 addressed.

Reviewed at 4d9b454

johnml1135 added a commit to sillsdev/fieldworks-parser-conformance that referenced this pull request Nov 3, 2026
… the wrapper script.

Removes the machine commit hash, the "last copy" wording and the sillsdev/machine#615 mentions
that the previous commit added, and returns PROVENANCE.md to its content on main. The
consumer-pin corrections stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants