A route (.af) file placed in metas/ used to silently "succeed" as an empty
MetaProfile: TryLoadMeta's STATE:/NAV: loop never adds a Rule for a NAV:-only
file, so the mistake was invisible. TryLoadMeta now tracks whether it saw
any STATE: block; a file with at least one NAV: block and zero STATE:
blocks throws (same FormatException path every other malformed-content
error already uses), naming the navs/ folder the file actually belongs in.
The opposite direction was already structurally caught by TryLoadNav's
existing "no NAV: block found" throw when a file has zero NAV: blocks (a
Meta profile with no embedded route, placed in navs/) — only the message
text is improved to name the metas/ folder. Both notices flow unchanged
through the existing MossTankProfileRecovery.Preserve/RecoveryNotice path
in MossTankMetaProfileStore.LoadCurrent/MossTankRouteProfileStore.LoadCurrent,
so no store-side code changes were needed for the wiring itself.
Mutation demonstrated: `git stash push -- src/AcDream.Plugins.MossTank/MetafSerializer.cs`
(reverting only the production fix, keeping every new test) reproduced 8
failures — the 5-fixture EveryNavOnlyAfFixtureIsRefusedByTryLoadMeta theory
(every real nav_*.af fixture parsed as a "successful" empty MetaProfile),
MetaOnlyContentIsRefusedByTryLoadNavWithMetasFolderNotice (message lacked
"metas/"), and the two store-level tests
MetaStoreRefusesToLoadANavOnlyFileWithNoticeNamingNavsFolder/
RouteStoreRefusesToLoadAMetaOnlyFileWithNoticeNamingMetasFolder — confirmed
by running the suite with the stash applied, then `git stash pop` to
restore the fix. All 640 tests (632 + 8 new) pass after.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>