acdream/src/AcDream.Core
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
..
Audio fix: complete retail parity stability pass 2026-08-28 20:01:39 +02:00
CharGen fix(chargen): Campaign CC gate round 1 closeout — Group 2: Skills page four-bucket model 2026-08-16 15:33:39 +02:00
Chat fix: complete retail parity stability pass 2026-08-28 20:01:39 +02:00
Combat feat(core): adopt the retail members the equipment and physics enums were missing 2026-07-29 01:27:57 +02:00
Content fix(rendering): bound portal resource lifetime 2026-07-18 21:35:16 +02:00
Input fix(client): restore retail interaction parity 2026-08-26 20:45:11 +02:00
Items feat(mosstank): add VTank-style automation PoC 2026-08-27 18:57:21 +02:00
Journal fix(journal): platform-independent file-name sanitisation — CI linux-portable red since 2c2d57b2 2026-08-23 18:46:09 +02:00
Lighting fix(overhaul): integrate reviewed room-light selection repair 2026-09-05 14:57:52 +02:00
Meshing fix(rendering): carry retail SetSurface state to detail draws 2026-09-05 02:03:13 +02:00
Physics fix(physics): port ordered part-array building transit 2026-09-05 20:28:52 +02:00
Player feat(ui) Campaign CA CA4 #431: server-authoritative raises — the optimistic layer is deleted 2026-08-24 13:56:31 +02:00
Plugins fix(vt): F host-composed VtankProfiles storage replaces raw path string 2026-09-06 23:03:04 +02:00
Properties feat(net) Campaign CA CA2 #431: parse the inbound attribute/skill update family 2026-08-24 13:38:50 +02:00
Quests feat(journal): QJ3/QJ4/QJ5 — both remaining tabs are live 2026-08-21 15:42:05 +02:00
Rendering refactor(rendering): delete superseded visibility probes 2026-09-05 05:18:17 +02:00
Selection feat(runtime): FA4 -- fellowship roster enumeration + Social selection source 2026-08-12 04:40:19 +02:00
Social refactor(runtime): expose canonical gameplay state 2026-07-26 09:12:30 +02:00
Spells fix(ui): restore retail vitals and window interactions 2026-08-20 13:26:35 +02:00
Terrain feat(render): implement Campaign AR and terrain fidelity 2026-08-22 13:13:29 +02:00
Textures fix(ui): Campaign LA gate round 2 — character-select screen media resolution 2026-08-15 09:12:10 +02:00
Ui feat(journal): QJ3/QJ4/QJ5 — both remaining tabs are live 2026-08-21 15:42:05 +02:00
Vfx docs(overhaul): correct S5 c1 review comments 2026-09-04 17:09:57 +02:00
World fix: complete retail parity stability pass 2026-08-28 20:01:39 +02:00
AcDream.Core.csproj build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.linux-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.neutral.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00
packages.win-x64.lock.json build: make release restore reproducible 2026-08-18 10:29:00 +02:00