fix(vt): round 3 item 12 — cleanup pass (stale refs, silent swallows, comments)
Five small fixes bundled per the round's cleanup item:
- VtankNavRouteSerializer.cs's doc comment cited a "WriteBinaryNavBlob"
method that no longer exists anywhere in the codebase (MetaEngine's
embedded-navigation contract moved to the typed MetaAction.EmbeddedRoute
NavigationSettings, saved/loaded through MetafSerializer.SaveNav/
TryLoadNav, back at round 2 step B) — corrected to name the real
mechanism.
- MossTankCommands.cs:274's comment referenced an "exports/nav/" mirror
directory that stopped existing when route profiles cut over to writing
their real .af file directly (round 2 steps 2-3) — corrected.
- docs/research/vtank-kb/07-meta-and-expressions.md section 5.2 row 6
described the pre-cutover "MossTankMetaProfileStore.WriteLegacyExport
convenience mirror" design; .af is now the SOLE authoritative Meta
store, so a disabled rule's save refusal now blocks the profile itself
— the row now says a disabled rule makes the profile file genuinely
unsaveable, not that a mirror goes stale.
- The two bare `catch (FormatException) { }` blocks that silently dropped
a corrupt monster-rule expression (one in SideCarDocument.Apply, reached
from a corrupt side-car; one in LegacyCombatProfileDocument.Apply,
reached during legacy-JSON migration) now log a warning via the host's
IPluginLogger, threaded through as an optional parameter from every call
site.
- VtankDatabase.Render()'s table-sort doc comment now states explicitly
that StringComparer.Ordinal matching .NET Framework's SortedDictionary
default order is confirmed only for the plain-ASCII table names VTank
ships (AntiExtraBuffSpells, MyMonsters, Settings, …), not as a general
claim for any string — comment only, no behavior change.
Added CorruptSideCarMonsterRuleIsLoggedNotSilentlySwallowed (FakeLogger
now captures Warn() calls via a new FakeHost.Logger property) pinning the
swallow-to-log fix.
Mutation: reverted MossTankProfileStore.cs to HEAD (keeping only the new
test) and ran it — failed with an empty Warnings collection, confirming
the silent-swallow bug before the fix.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
55010f92c7
commit
f58e997b19
6 changed files with 95 additions and 24 deletions
|
|
@ -723,7 +723,7 @@ line counts). This is an unusually precise reverse-engineering result — the
|
|||
| 3 | **`MonsterNameCountWithinDistance`'s name pattern is case-insensitive in MossTank, case-sensitive in retail** | `d7.c()` compiles with `RegexOptions.Compiled` only (`d7.cs:26`) — case-sensitive | `Meta.cs`'s `MonsterCount` helper (`Meta.cs:536-552`) compiles with `RegexOptions.IgnoreCase \| RegexOptions.CultureInvariant` | **Medium-high.** A pattern authored against retail's case-sensitive matching (e.g. deliberately excluding a differently-cased variant name) will over-match in MossTank. Note `ChatMessage`/`ChatMessageCapture` do **not** have this divergence — MossTank's `ChatMatch` (`Meta.cs:463-513`) is correctly case-sensitive (`RegexOptions.CultureInvariant` only), matching `hl.cs`/`c5.cs`. |
|
||||
| 4 | **`;` sequence-operator value convention is inverted vs. retail (but matches UtilityBelt, and was chosen deliberately)** | Retail VTank: `a;b` evaluates to **`a`** (the left/first operand), discarding `b`'s value, while still executing `b` for side effects (`ExpressionEvaluator.cs:787-790`); `;` is an ordinary operator that can nest anywhere via normal precedence | `ExpressionProgram.Evaluate` (`ExpressionEngine.cs:20-27`) treats top-level `;`-separated statements as a `Node[]` program and returns the value of the **last** statement — this matches UtilityBelt's own audited grammar ("multiple `;`-separated statements, returning the final result", `2026-08-26-mosstank-vtank-utilitybelt-research.md:237`), which the campaign explicitly chose as MossTank's baseline dialect (§3.1 there) | **Medium, and by design, not an oversight.** Retail VTank and UtilityBelt already disagree with each other on `;`'s return value; MossTank correctly implements UtilityBelt's convention. The compat risk is narrower than a plain bug: only a `.met` authored *against retail's own* semantics (chaining `sideeffect[]; realcheck[]` and relying on the *first* value) evaluates to the opposite result once imported. |
|
||||
| 5 | **Watchdog ring pre-fill differs (far sentinel vs. arm-time position)** | All 10 position-history slots start as a sentinel far from any real coordinate (`(1000,1000,1000)`, `h7.cs:45-49`), guaranteeing expiry cannot even be *reachable* until one full `timeSpan` window has elapsed and every slot has been overwritten with a real sample | `SetWatchdog` (`Meta.cs:585-596`) pre-fills all 10 slots with the **current** position at arm time | **Lower-medium.** Both converge on "earliest possible expiry is ~one `TimeSpanSeconds` after arming" in the common case, but they diverge for a watchdog that is armed, then the player leaves and returns to very near the arm point before the window closes — retail's sentinel-seeded ring cannot spuriously read the arm-time position as one of its 10 samples, MossTank's can. |
|
||||
| 6 | **`MetaRule.Enabled` (a per-rule disable-without-delete toggle) has no metaf-compatible representation at all** | Real VTank/metaf has no such concept — `metaf_monolithic.py` has zero occurrences of "enabled"/"disabled" anywhere; a rule in a `.met`/`.af` is simply present or absent | `MetafSerializer.SaveMeta` (Campaign VT slice-1 item H) now **refuses to save** a profile containing any disabled rule by default (throws `InvalidOperationException` naming the count) rather than silently dropping it — the caller must opt in via `SaveMeta(profile, dropDisabledRules: true)` to accept the loss explicitly. `MossTankMetaProfileStore.WriteLegacyExport` (the `.af` convenience mirror written alongside MossTank's own fully-fidelity JSON storage) deliberately does NOT opt in: it leaves that mirror stale rather than losing the disabled rule, and logs a warning via the existing try/catch. | **Low** (by design, not a bug): this is a MossTank-only UI extension with no VTank equivalent to diverge from; the representational loss is confined to the legacy-export mirror, never the authoritative profile data, and is now impossible to hit silently. |
|
||||
| 6 | **`MetaRule.Enabled` (a per-rule disable-without-delete toggle) has no metaf-compatible representation at all** | Real VTank/metaf has no such concept — `metaf_monolithic.py` has zero occurrences of "enabled"/"disabled" anywhere; a rule in a `.met`/`.af` is simply present or absent | `MetafSerializer.SaveMeta` (Campaign VT slice-1 item H) **refuses to save** a profile containing any disabled rule by default (throws `InvalidOperationException` naming the count) rather than silently dropping it — the caller must opt in via `SaveMeta(profile, dropDisabledRules: true)` to accept the loss explicitly. Round 2 (Campaign VT slice 1 Part A) made `.af` the SOLE authoritative Meta store — `MossTankMetaProfileStore` has no separate JSON storage or `.af` "convenience mirror" any more, so this refusal now blocks the save of the profile itself: a disabled rule makes that profile file genuinely **unsaveable** until the user re-enables or deletes the rule (the file on disk keeps its last successfully-saved content in the meantime; round 3 item 12 corrected this row, which previously described the pre-cutover "legacy-export mirror" design). | **Low** (by design, not a bug): this is a MossTank-only UI extension with no VTank equivalent to diverge from, and the refusal is loud (a user-visible notice) rather than a silent data loss. |
|
||||
|
||||
### 5.3 Confirmed non-gaps (the format/engine is otherwise unusually faithful)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue