Commit graph

2715 commits

Author SHA1 Message Date
Erik
44fd5ce580 docs(vt): slice-1 ledger — Part A round 2 landed, re-reviews dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 00:38:14 +02:00
Erik
a6c2e3918e docs(vt): VT1 complete, VT2 FINAL — every catalog doc citation-verified and folded
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:53:41 +02:00
Erik
7ac43cee63 docs(vt): fold the citation-pass corrections into KB 01 (tString rows not in Advanced Options, template schema is 8 tables with an empty Settings table, PluginCore list-builder ranges, bk.a semantics, RechargeHandlerSet read-back, ~~ prefix resolved)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:53:41 +02:00
Erik
b9029082ae docs(vt): slice-1 ledger — Part A round 1b complete, round 2 dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:39:27 +02:00
Erik
a3b4a8e2e5 fix(vt): H test-restoration + disabled-rule refusal + double-side gate
Item H (slice-1 fix round), six sub-parts:

1. Restored VtankMetaProfileSerializerTests.LoadsKnownTypedCondActRecord
   and SignedHighBitLandblockIdRoundTripsExactly (the latter adapted from
   the deleted RoundTripPreservesEveryVtankConditionActionAndEmbeddedNav's
   LandblockEquals/LandcellEquals coverage of unchecked((int)0x8B370000u)),
   both deleted as collateral damage of an unrelated file move in commit
   0d10399e0. Neither calls the deleted VtankMetaProfileSerializer.Save
   writer (demoted to import-only in 3ff9461ef) — both are pure reader
   assertions.

2. MetafSerializer.SaveMeta now refuses (throws InvalidOperationException
   naming the count) to silently drop a disabled MetaRule: real VTank/
   metaf has zero concept of "disabled" (confirmed: metaf_monolithic.py
   has no "enabled"/"disabled" occurrences anywhere), so
   MetaRule.Enabled is a MossTank-only extension with no metaf-compatible
   marker. A new SaveMeta(profile, dropDisabledRules: true) overload lets
   a caller accept the loss explicitly.
   MossTankMetaProfileStore.WriteLegacyExport (the .af convenience mirror
   beside MossTank's own fully-fidelity JSON storage) deliberately does
   NOT opt in — it leaves that mirror stale and logs a warning via its
   existing try/catch rather than losing the rule. Recorded as gap 6 in
   docs/research/vtank-kb/07-meta-and-expressions.md section 5.

3. New VtankProfilesDefault (src/AcDream.App/Plugins/): the graphical
   host's default VtankProfiles root (<DataDirectory>/vtank), extracted
   out of Program.cs's inline Path.Combine call into its own pure,
   injectable-root function specifically so the "Path.Combine only, never
   a hard-coded Windows path" guarantee is a real, failable Linux-path
   unit test (VtankProfilesDefaultTests.ResolveIsBuiltWithPathCombineOnly)
   rather than something only checkable by reading the source — the
   pattern item F's VtankProfileDirectory rewrite removed when
   Resolve/PortableDefault moved out of the plugin.

4. New BuffedDoubleRequirementDoesNotApplyBonusWhenBaseKeyIsAbsent: the
   KeyExistsDouble gate (ComputedItemInfo.cs:234) already existed in
   BuffedDouble, but only the int side
   (BuffedIntRequirementDoesNotApplyBonusWhenBaseKeyIsAbsent) had a
   pinning test.

5/6. DoubleSpellBonuses gained an explicit Change field (KB doc 05
   section 2.2: "additive unless the static table's Change==1, in which
   case multiplicative", ComputedItemInfo.cs:244), replacing
   BuffedDouble's prior `(int)bonus.Bonus == 1` magnitude-based proxy —
   that proxy only worked because every multiplicative bonus in the
   current 19 rows happens to fall in [1.0, 2.0) and every additive one
   happens to be under 1.0; it would have silently mis-branched on a
   future row like an additive 1.5 or a multiplicative 2.0+. Every
   existing row's Change value was derived mechanically from its old
   proxy result (no behavior change for the current table), and
   BuffedDoubleRequirementAppliesAdditiveBonusWhenBaseKeyExists/
   AppliesMultiplicativeBonusWhenChangeIsSet pin both branches through
   the real named field.

Full MossTank suite: 574 -> 581. App.Tests
(Plugin|LaunchOptions|RuntimeOptions filter): 135 -> 137. Core.Tests
(Plugin filter): 50/50 (no change, no Core-side edits this item).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:20:20 +02:00
Erik
2040f2bcfb fix(vt): F host-composed VtankProfiles storage replaces raw path string
Item F (slice-1 fix round). IPluginHost.VtankProfileDirectory handed the
plugin a raw string path and told it to fall back to its own
System.IO-based portable default when null — a plugin reading and
resolving filesystem paths itself, which is exactly the seam the rest of
IPluginHost.Storage deliberately avoids (Core.Plugins.ScopedPluginHost
scopes/validates every key; the plugin never sees a path).

- IPluginHost: VtankProfileDirectory (string?) deleted; new VtankProfiles
  (IPluginStorage, defaults to NoOpPluginStorage) added — a second,
  UNSCOPED storage instance (unlike Storage, which Core scopes per
  plugin manifest id) rooted at a host-composed VTank-compatible
  directory.
- ScopedPluginHost.VtankProfiles forwards _inner.VtankProfiles directly
  (no scoping — it names one shared external location, not per-plugin
  data). New PluginSessionTests.ScopedHostForwardsVtankProfilesUnscoped
  proves the forwarded instance is the exact same object (Assert.Same),
  not a wrapper.
- AppPluginHost/Program.cs: new vtankProfiles constructor parameter,
  composed as FilePluginStorage(runtimeOptions.VtankProfileDirectoryOverride
  ?? Path.Combine(applicationPaths.DataDirectory, "vtank")).
- RuntimeOptions.VtankProfileDirectoryOverride: new init-only property
  parsed from ACDREAM_VTANK_PROFILE_DIR (row added to
  docs/launch-options.md, side-effects column states the redirect is the
  only effect and documents the NullIfEmpty whitespace-not-special-cased
  quirk it shares with every other path-override flag). New
  RuntimeOptionsTests.VtankProfileDirectoryOverrideIsNullUnlessSet.
- FilePluginStorage.List(prefix): empty prefix now means "the storage
  root itself" instead of throwing (Resolve() rejects empty/whitespace
  keys, which is correct for every OTHER caller but wrong for "list
  everything" — VtankProfileDirectory needs exactly that).
- Headless: HeadlessPluginHost gained the same VtankProfiles
  property/constructor param, threaded through HeadlessPluginSession.Create
  -> HeadlessSessionHost -> HeadlessProcessHost, composed from the new
  HeadlessPathSet.VtankProfilesDirectory (<DataDirectory>/vtank, no
  ACDREAM_VTANK_PROFILE_DIR-equivalent override — Headless path overrides
  are HeadlessPathOverrides/CLI flags, not env vars). A small
  AcDream.Headless.Plugins.FilePluginStorage duplicates the App
  implementation byte-for-byte (Headless does not reference AcDream.App
  and no shared "platform plugins" library exists yet to host one copy;
  documented as a reasonable future consolidation, not required here).
- VtankProfileDirectory.cs rewritten: Resolve/PortableDefault deleted
  outright (no more System.IO, no plugin-owned portable-default fallback);
  ListSettingsProfiles/ListNavigationProfiles/ListMetaProfiles now take
  IPluginStorage and enumerate through EnumerateFileNames, which calls
  storage.List(string.Empty) and skips any key containing '/' (VTank's
  profile directory is flat; a nested key from some other IPluginStorage
  implementation is not a profile file). VtankProfileDirectoryTests
  rewritten against an in-memory IPluginStorage fake instead of real
  temp directories; new NestedPathKeysAreNotTreatedAsProfileFiles pins
  that skip. The prior Resolve/PortableDefault-specific tests (Linux-path
  guarantee, host-override-vs-portable-default) are superseded by
  RuntimeOptionsTests.VtankProfileDirectoryOverrideIsNullUnlessSet plus
  the RuntimeOptions.FromEnvironment Path.Combine-only composition in
  Program.cs.
- docs/architecture/acdream-architecture.md: one sentence in the
  Storage/List(prefix) paragraph naming VtankProfiles as the second,
  unscoped storage.

No production caller of VtankProfileDirectory's listing methods exists
yet (A2's foundation is not wired into MossTankProfileStore/
MossTankMetaProfileStore/MossTankRouteProfileStore's own selection —
per that slice's own ledger note), so this is a contract + plumbing
change with no MossTank runtime behavior change.

MossTank suite: 562/562. Core.Tests (Plugin filter): 50/50. App.Tests
(Plugin|LaunchOptions|RuntimeOptions filter): 135/135. Headless.Tests:
173/174 (the one failure, HeadlessCredentialResolverTests.
LinuxRejectsGroupOrOtherCredentialPermissions, is a pre-existing
Linux-only lane gate that throws PlatformNotSupportedException on this
Windows host — unrelated to this change).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:03:04 +02:00
Erik
0219c6e03d fix(vt): A ptl/tlk carry two coordinate triples, jmp direction loss recorded
Item A (slice-1 fix round). VTank/metaf's Portal2/UseNPC nav nodes carry
TWO coordinate triples (metaf_monolithic.py:356-357,11482,11618 —
"FORMAT: ptl/tlk myx myy myz tgtx tgty tgtz tgtObjectClass tgtName"): the
outer header ("myxyz", retail's own dead-weight last-save player position
per docs/research/vtank-kb/06-navigation-and-nav.md section 1.2) and the
embedded d-record ("tgtxyz", the real target coordinate used to match a
live world object by name+class+proximity). The prior port's
RouteWaypoint had a single Position field, so both the .af reader
(MetafSerializer.ReadNavNode) and the binary .nav reader
(VtankNavRouteSerializer.ReadWaypoint, case 6/7) overwrote "myxyz" with
"tgtxyz" on load, and the .af writer echoed the same Position value for
BOTH triples on save — a real .af round trip of the same waypoint was
lossy, which is why aphus/augments/lockandkey/neftet were excluded from
the byte-identity proof.

- RouteWaypoint: new ReferencePosition field (Position stays "myxyz",
  ReferencePosition is "tgtxyz"); included in Clone().
- MetafSerializer.ReadNavNode/RenderNavNode: ptl/tlk read/write both
  triples distinctly. WriteBinaryNavBlob's embedded-route writer (the
  MossTank runtime blob EmbedNav actions carry) fixed the same way — it
  was echoing Position for the reference triple too.
- VtankNavRouteSerializer.ReadWaypoint case 6/7: keep the header triple in
  Position, read the trailing triple into ReferencePosition instead of
  overwriting Position.
- Navigation.TickUse: TryFindObject now searches near ReferencePosition
  (the real target coordinate) instead of Position, preserving the
  correct runtime search behavior now that Position no longer aliases it.
- MossTankPanel.AddSelectedObjectWaypoint: new Portal2/UseNPC waypoints
  now set Position from the live snapshot (matching retail's own
  "wherever the character stood") and ReferencePosition from the selected
  object's live position (the real search anchor) — previously both were
  set from the object's position.
- MossTankRouteProfileStore's WaypointDocument DTO carries the reference
  triple too, so MossTank's own JSON-persisted routes round-trip it.
- MetafSerializerTests: un-excluded aphus/augments/lockandkey/neftet.af
  from the byte-identity proof (they all embed a ptl/tlk node and now
  round-trip correctly) and added example_sort_meta.af, which also
  passes. bore_quest.af was NOT added despite the slice-1 contract's
  ask: it is hand-edited the same way as the already-excluded
  bore_enhanced.af (space instead of tab between "IF:"/"DO:" and the
  following keyword, confirmed at bore_quest.af line 9 — metaf's own
  Rule.ExportToMetAF always joins with a tab, metaf_monolithic.py:12371),
  so it can never byte-match; documented alongside bore_enhanced's
  existing exclusion note instead. New PtlNodeKeepsBothCoordinateTriplesDistinct
  test pins the two-triple split directly (failed before this change:
  Position held the second triple with nowhere to read the first triple
  back from). VtankNavRouteSerializerTests updated to assert the split
  instead of the old collapsed value.
- jmp direction: metaf's NJump class has no strafe-direction field at all
  (metaf_monolithic.py:11708-11821, confirmed reading ImportFromMetAF/
  ExportToMetAF end to end) — the .af format cannot represent
  RouteWaypoint.JumpDirection, full stop. ReadNavNode no longer assigns
  JumpDirection = Forward explicitly (the model's own default), and the
  loss is now recorded as gap 9 in docs/research/vtank-kb/
  06-navigation-and-nav.md section 6.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 22:13:48 +02:00
Erik
68627a873b docs(vt): slice-1 ledger — Part A fix round 1 (3/12) and round 1b dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:55:10 +02:00
Erik
208599fae7 docs(vt): slice-1 ledger — full App suite on the merged branch (36 pre-existing env-gated failures)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:43:47 +02:00
Erik
467cb1c381 docs(vt): slice-1 ledger — Part B merged at da0fcb374
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:42:20 +02:00
Erik
da0fcb3741 merge(vt): slice 1 Part B — multi-column <list> markup (review-closed)
Campaign VT slice 1 Part B: <list><column type=text|check|icon> with
per-column bindings, text onclick, width="*" auto share, row-bound
callback guards, shared UiCheckLamp, Meta/Monsters-shaped end-to-end
tests. Two Opus lenses + fix round + narrow re-review: MERGE-READY.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:40:54 +02:00
Erik
b44282071c docs(vt): owner clarification — VTank has no real API; acdream may create its own plugin API later
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:35:18 +02:00
Erik
4ce6ffe6ff docs(vt): KB index counts match the verified docs (49 instantiations / 66 entries; 11 waypoint types)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:34:41 +02:00
Erik
bda8745448 docs(vt): owner decision — no interop; slice 9 dropped, all plugins rewritten against acdream's own API
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:34:30 +02:00
Erik
d078863306 docs(vt): owner sharpened the UI decision — resemblance + same interactions, not VVS internals
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:32:02 +02:00
Erik
026b512074 fix(vt): list column fix round 11/11 — docs for the whole fix round
docs/plugin-ui-markup.md's Columns section is rewritten to match every
production change from items 1-9:

- New "Width semantics" section: width="*", the last-column-always-auto
  rule (and how it combines with an earlier explicit "*"), the
  non-last-column Build-time throw, and the runtime overflow clamp.
- The <column> attribute grammar table gains column onclick
  (type="text", optional, Action<int>) and points width at the new
  section instead of the retired "last column ignores its own width"
  one-liner.
- New "Short columns past their own row count" section: text/icon draw
  nothing, check draws unchecked.
- Check-column glyph section updated for the shared UiCheckLamp
  primitive and its horizontal centering.
- New "The PITCH convention for transcribing a VTank column table"
  section: VVS's WPaddingOuter=3px/WPadding=7px/16px scrollbar reserve/
  forced-13px-check-columns have no direct acdream equivalent, so
  transcribing a real mainView.xml column table means declaring each
  column's PITCH (fixedwidth+7, or 13+7 for a check column regardless
  of its real fixedwidth) and reserving 16px on the last column.
- The worked example now uses rowheight="18" (VVS's own Padding*2+
  ControlHeight pitch, already the widget's default) and PITCH-computed
  widths (20 for a check column, 127 for a 120px text column) instead
  of arbitrary numbers, with a text onclick and a width="*" icon
  column added to demonstrate both new attributes in place.
- The bindings truth table gains column onclick (type="text") and both
  column width throw/silent rows (non-last vs. the list's last column).
- Testing conventions paragraph updated to mention width semantics, the
  row-bound click guard, and the two end-to-end tests.

Verification: dotnet build AcDream.slnx -c Release (green), dotnet test
tests/AcDream.App.Tests -c Release --filter
"FullyQualifiedName~Markup|FullyQualifiedName~UiMarkupList|FullyQualifiedName~PluginSidePanel"
(130/130), dotnet test tests/AcDream.Plugins.MossTank.Tests -c Release
--filter "FullyQualifiedName~Markup" (9/9) — closes the fix round.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:31:58 +02:00
Erik
92f9b36b8f docs(vt): slice-1 ledger — Part A landed, both review verdicts, two-round fix plan
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:26:21 +02:00
Erik
8fa70c3e4a docs(vt): KB 01 RechargeHandlerSet seeds 26 rows, not 24 (found by the Part A implementer against the fixture)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 21:06:37 +02:00
Erik
17f2143a71 docs(vt): append Part A closeout ledger line to the slice-1 contract 2026-09-06 21:04:18 +02:00
Erik
054bf32605 docs(vt): slice-1 contract amended from the Part B reviews (text onclick, auto width, no header); review ledger
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:34:18 +02:00
Erik
dc91245ba8 docs(vt): KB 05 second citation pass — 31 requirement types, f0.c categories and dz.o.h readers resolved
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:25:19 +02:00
Erik
959a694823 feat(vt): Campaign VT slice 1 Part B — multi-column <list> markup
VVS HudList parity (docs/research/vtank-kb/08-ui-views.md 2-3): <list>
accepts <column type="text|check|icon" width=... [iconkind] [colors]/>
children, each binding its own per-row source parallel to the row count.
Row count is the longest bound column; a click in a text column selects
(the list's own selected/onchange, unchanged), a click in a check/icon
column fires that column's own required onchange/onclick with the row
index and does NOT change selection. Last column always absorbs the
remaining width, recomputed live off the list's own Width rather than
baked in at Build. Per-cell horizontal clipping via UiRenderContext's
existing PushClip/PopClip. The check-column glyph reuses <toggle>'s own
five-band lamp primitive (UiMarkupToggle.DrawLamp/colors promoted from
private to internal) so it looks like every other checkbox. Unknown
column type, a missing required column binding, <column> combined with
the legacy items/icons/colors list attributes, or any non-<column> child
of <list> all throw FormatException at Build. A column-less <list> is
byte-for-byte the original single-text-column widget (new if/Columns
branch in OnDraw/OnEvent; the legacy branch's code is untouched).

New UiMarkupListColumn model (src/AcDream.App/UI/UiMarkupListColumn.cs)
carries each column's kind/width/bindings; MarkupDocument's `list` case
now builds either the legacy single-column fields or a Columns list,
never both. MarkupListColumnsTests (27 new tests) cover parse/binding
validation, draw-level column-offset/clipping/check-glyph/icon pins
against the recording renderer, hit-test routing, and a backward-
compatibility proof (a column-less list built through MarkupDocument
produces a byte-identical draw record to a hand-built UiMarkupList with
equivalent fields). Every new assertion was verified to fail first via
targeted temporary mutations (hit-test isolation, clip removal, last-
column-absorbs-remainder, required onchange/onclick, row-count = max),
each reverted after confirming failure.

docs/plugin-ui-markup.md gets a full Columns section (attribute grammar,
a Monsters-tab-style example, the no-header-row idiom, backward
compatibility) and the old single-text-column LIMITATION note is
retired; the bindings truth table gains the six new column-attribute
rows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:21:16 +02:00
Erik
4549eb02af docs(vt): VT2 ledger records the folded citation pass and the re-judged rows
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:19:33 +02:00
Erik
caf7307522 docs(vt): fold the citation-pass corrections into KB 06 (0.01° turn threshold, OpenVendor busy paths, cm sites vs instantiations, resolved f9.j and item.c meanings, path/count nits)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:19:19 +02:00
Erik
d8f85848b6 docs(vt): KB 08 gap table gains the menu-overflow row the citation pass asked for
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:18:18 +02:00
Erik
69bb164845 docs(vt): fold the citation-pass corrections into KB 08 (icon-id decode, menu wraps not scrolls, slider fixed 0-1, HudList press-point click, dc.xml, resolved clRings and loot-editor call site); repair KB 09 path
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:18:07 +02:00
Erik
19a32cde2e docs(vt): KB 09 header points at the real UtilityBelt tree; slice-1 A3 carries the Change-vs-Bonus fix
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:17:11 +02:00
Erik
ee901b7f26 docs(vt): fold the citation-pass corrections into KB 05 and 09
05: recognized-vs-unrecognized rule payloads, 25 materials, only 3-arg
SpellInfo entries are live, Read/User1..5 unreachable from any loot plugin,
hv.a.e is a dead field, id.a.j = shareLoot, the Change-vs-Bonus branch
selector mis-port in the evaluator.
09: obtest location, the broken params dedup overload, the real ah call
sites, internal relay ctor, the next-waypoint handler at 3621-3639,
TryHandle is host-only, Classic Looter registry narrowed, UtilityBelt's
Harmony bypass route resolved.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:16:56 +02:00
Erik
642eacc13c docs(vt): fold the citation-pass corrections into KB 07 (hn.cs line map, dh/q hierarchy, 74-function count, ReturnFromCall guard, resolved fm/a shapes)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:15:22 +02:00
Erik
b7ed65bc64 docs(vt): VT2 slice 4 re-judged after the KB 04 citation pass (G4 is ordering only)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:04:24 +02:00
Erik
2e36ea8268 docs(vt): fold the citation-pass corrections into KB 04 (Armor Self row, stance legend swap, cr registered twice, G4 global pool, resolved handler-table and HP-yield bullets, MossTankPanel line sweep)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:04:10 +02:00
Erik
887242cd15 docs(vt): fold the citation-pass corrections into KB 02 (ga.a line map, gj/gs identity, fd.cs IdlePeace gate scope, VitalPlan.Threshold branch, entry counts)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 20:02:39 +02:00
Erik
1b81836747 docs(vt): fold the citation-pass corrections into KB 03 (DebuffScheduler.cs path, DebuffScope, six-member l enum, offhand enum, pet comparison owed)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:59:25 +02:00
Erik
59f247664a docs(vt): slice-1 contract — per-column list bindings per KB 08 §3; .af is the only meta/nav store
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:58:47 +02:00
Erik
986bd9668a docs(vt): land KB doc 07, VT2 gap audit draft, slice-1 contract; metas/navs move to metaf .af
Owner amendment 2026-09-06: MossTank implements the human-readable metaf
.af format for metas and nav routes instead of VTank's binary .met/.nav;
the reference converter lives in the owner's metas repo. Docs 06 §1/07 §1
stay as the binary record; their semantics sections remain the oracle.

Doc 07 spot-checked by the lead: ExpressionEvaluator.cs:787-790 (';'
returns the first operand), hn.cs:41-80 (the pass loop), bw.cs:25 (the
'> 5' six-view cap), d6.cs:7-8 (Button/Layout only).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:56:45 +02:00
Erik
b320cbfbc7 docs(vt): VT1 catalog index
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:49:54 +02:00
Erik
58483a1a53 docs(vt): VT1 catalog 05 — VTank loot flow and the Classic Looter .utl format
VTClassic's .utl blocks, all requirement types, EarlyMatch/NeedsID
identify-avoidance, ComputedItemInfo, salvage-combine extra block; VTank's
host-side corpse selection (rare-first, fo.cs:436-446), approach/open/loot
rules, the ownership-denial chat regexes (fo.cs:71-73), timeouts. MossTank
gap ranked. Lead spot-check of those three claims.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:48:33 +02:00
Erik
cf04897694 docs(vt): the UtilityBelt gitlab.io repo is the full plugin source; cloned locally
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:47:53 +02:00
Erik
f1418903fd docs(vt): record the UtilityBelt and Mag-Tools source references supplied by the owner
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:47:19 +02:00
Erik
1a35b83cf8 docs(vt): VT1 catalog 06 — VTank navigation engine and the .nav format
x.cs reader/writer (uTank2 NAV 1.2, route types, ten waypoint types incl.
the live-position quirk of five of them), ca.cs cycle driver, fd.cs steering
and creep band, bi.cs jump (2000 ms cap), b7.cs door/lockpick, priority
interactions, 240 m/unit confirmed at four sites, two real routes decoded.
MossTank gap ranked. Lead spot-check: header vs a real file, jump cap,
conversion sites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:46:04 +02:00
Erik
289c6706ba docs(vt): VT1 catalog 04 — VTank buffs, item enchantments, helpers and vitals
Buff plan and per-tick re-evaluation, the profile-item enchant pipeline
(PluginCore.cs:8327-8445), helper heals/random helper, the nine recharge
thresholds and rule order, kits/potions/food, dispel and worn-item mana.
MossTank gap ranked. Lead spot-check: fk.b() dead check, ba.cs 100-draw
loop, MySpell.HasScarabsInInventory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:44:02 +02:00
Erik
cf6d5b94bb docs(vt): VT1 catalog 09 — /vt commands, chat sinks, and VTank's plugin interop surface
~48 documented subcommands plus 15 parser-only debug verbs (no /vt pause),
the d5/ah chat sinks, the three-tier export model (public static PC,
permission-gated relay, LootPluginBase SPI) annotated against
MosswartMassacre's real usage, and the interop gaps in Plugin.Abstractions.
Lead spot-check: PC field, eExternalsPermissionLevel, start/stop parser.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:42:51 +02:00
Erik
8872a9286a docs(vt): VT1 catalog 01 — VTank settings, defaults and the .usd profile format
All 137 settings with type/default/category/UI control/consumer citation,
the self-describing .usd table grammar (parsed from the decompile and
verified against defaultsettings.usd), profile selection and /vt opt.
MossTank gap: no .usd reader/writer, BuffProfileDocument drops ten fields,
RechargeHandlerSet opaque; setting-name coverage is already 137/137.
Lead spot-check: f3 accessors, the four reader classes, catalog count.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:33:06 +02:00
Erik
b4cb516fac docs(vt): VT1 catalog 02 scheduler/actions and 03 combat from the VTank decompile
02: the single-winner priority list (24 rule classes, 45 instantiations),
the 293 ms heartbeat + event poke, the force-combat-mode gate and its
stuck-state recovery, IdlePeace in full, MossTank gap. Two draft errors
corrected by the lead against the source: GoToPeaceModeToUseKits exists
(a5.cs:121, defaultsettings.usd:931) and the fallback-wand list is
Items-page insertion order (eq.cs:83-94, PluginCore.cs:8422-8434).
03: target acquisition/selection, monster rules, weapon/damage/ammo, attack
execution, debuffs, pets, MossTank gap. Spot-checked: the hardcoded
debuff-kind order (hi.cs:123-168) and quality-before-UseArcs (hi.cs:509-535).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:29:22 +02:00
Erik
68abeffb37 docs(vt): VT1 catalog 08 — VTank UI views from its own view XML and VVS semantics
156 controls across nine tabs plus the three secondary views, each with type,
geometry and bound setting where determinable; VVS control semantics our
markup must offer (multi-column lists with text/check/icon columns, combo,
notebook); window icon and StoredViewInfo facts; MossTank gap. Three cited
claims spot-checked against refs/ by the lead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:23:04 +02:00
Erik
7ef596581e docs(mosstank): record the VTank-source review of c406942ef; fixes deferred to Campaign VT
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:20:29 +02:00
Erik
a40b5a345a docs(vt): open Campaign VT — VTank oracle, knowledge base before parity slices
Owner direction 2026-09-06: decompile VTank, VirindiViewService and Classic
Looter (done, under refs/, gitignored), write the full catalog with citations
before implementing, keep file compatibility (.usd/.met/.nav/.utl), use our
native UI with VTank's own view XML as the layout truth (VVS not ported), and
keep everything Linux-clean. Also records the arbitration commit c406942ef in
its plan ledger; VT2 re-judges it against the catalog.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 19:10:50 +02:00
Erik
7d5fd7cc10 docs(mosstank): plan combat-mode and caster arbitration for buffing and idle
Owner request 2026-09-06: the macro must use the Items profile to wield what
it needs, enter the right combat mode by itself to buff or fight from peace
mode, and return to peace when idle with Peace Mode When Idle on. Verified
gap: ACE drops any cast outside magic mode, and the buff pass never enters
magic mode or wields a caster; idle-peace lives only inside the combat
controller and so never runs with combat disabled.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 18:29:11 +02:00
Erik
e61edd946b fix(plugin-ui): plugin did icons use retail's keyed-white recolor, not a raw blit
Owner report 2026-09-06: MossTank's shelf icon (0x06002C41) drew with a white
ring. DAT icon art reserves pure-white-opaque pixels as the recolor key that
retail IconData::RenderIcons (0x0058d180) replaces per pixel through
SurfaceWindow::ReplaceColor (0x004415b0) from the effect tile — the solid-black
0x21 tile when there are no effects. The inventory already does this through
IconComposer; the plugin did sink (markup <icon did>, <button icon>, <list icons>
and the shelf button) blitted the art raw.

RetailMarkupIconResolver.ResolveDid now hands out IconComposer.GetKeyedIcon —
the drag-icon composite (base art + effects==0 recolor, no overlay, no
underlay), sharing that cache — so did icons look like a mundane inventory item
does. The resolver no longer needs a TextureCache. KeyedIconInstalledDatTests
pins both halves against the real DAT: the raw art carries the key, the
composite carries none, and ResolveDid returns exactly the keyed composite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 17:40:39 +02:00
Erik
f2d7562c86 chore(plugin-ui): review cleanup — hermetic memo tests, shelf button anchors, outline pin, bounded miss cache; file #486/#487; correct #461
- Split the two hermetic RetailMarkupIconResolver memoization tests (and
  their counting fakes) out of the Lane=InstalledDat class into a new
  untagged RetailMarkupIconResolverMemoizationTests.cs so CI's portable
  filter (Lane!=InstalledDat) actually runs them.
- PluginSidePanel: move the entry button's Anchors = AnchorEdges.None from
  the Add() call site into PluginShelfButton's own constructor (same
  comment carried over) so a second construction path cannot miss it.
- UiRectOutlinePainterOrderTests: assert the back panel's border segment
  carries exactly 4 quads (24 vertices, FloatsPerVertex each) so a partial
  outline cannot pass the painter-order check.
- RetailMarkupIconResolver: document the type as UI-thread-only (every
  caller is a draw-time icon source) and bound the MISS cache to 256
  entries with FIFO eviction — HIT entries stay unbounded (bounded by the
  DAT's own surface count already). New test proves the 257th distinct
  miss evicts the first (re-probe count rises); verified failing first
  against the un-bounded code (Expected 258, Actual 257) before restoring
  the fix.
- docs/plugin-ui-markup.md: split the icon-binding row's failure mode into
  Build-time (missing property only — the binder never checks CLR type)
  vs. draw-time (a resolved value that cannot convert to a number throws
  from the draw, not from Build).
- docs/ISSUES.md: filed #486 (credits picture scroll frozen by the
  per-draw anchor pass) and #487 (radar compass tokens candidate, same
  mechanism, unconfirmed); corrected #461's causality — the graceful
  logout/reveal-cancel log lines are printed by LiveSessionController.Tick's
  catch -> StopAfterFailure -> StopCore AFTER the motion-update exception,
  then it rethrows, so the logout is a consequence of the crash, not its
  cause; real chain is the #462 stalled login-reveal materialization
  leaving PlayerMovementController in RuntimeOwnedDormant outside its
  SetPosition ground phase when an inbound 0xF74C arrives.
- Plan doc: recorded the three fix-round commits' verdicts (all PASS) and
  the Smoke-plugin cleanup commit SHA in the Review ledger, plus a pointer
  to the two newly filed issues.

Verified: dotnet build AcDream.slnx -c Release (0/0), targeted filter
85/0/0, full App suite 7364 passed / 97 skipped / 36 failed (36 pre-existing
InstalledDat/Manual/Linux-only failures, unchanged by name from baseline;
net +1 passed test from the new eviction test).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 17:09:46 +02:00