Commit graph

2723 commits

Author SHA1 Message Date
Erik
b6ccb261e4 docs(vt): slice 7 ledger — round E landed, scrollbar fix merged, round F dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 18:27:46 +02:00
Erik
850f84e54f docs: file #493 (in-game meta/loot rule editors, deferred); slice 7 ledger — owner's second live look
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 18:10:25 +02:00
Erik
3bb3ec930d docs: file #492 — Vulkan device loss crash + shutdown re-throw on the lost device
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:58:53 +02:00
Erik
94caf0ba60 docs(vt): slice 7 ledger — selection-band and geometry-revision merges; round F scope
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:55:29 +02:00
Erik
3d7065ebc0 merge: fix #490 part 2 — plugin windows adopt an authored size change (geometry-hash revision)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:54:57 +02:00
Erik
94ebe945f3 docs: mark #490 part 2 fixed and note the reset-once contract in plugin-ui-markup
#490 part 2 (plugin layout persistence has no revision bump) is fixed at
05f22d46f (RetailWindowManager.ComputeAuthoredGeometryRevision +
MigrateAuthoredGeometry's inequality comparison + MountPlugins wiring).
Part 1 (StartVisible=false + ShowInSidePanel=false is permanently
unshowable) remains open -- not touched by this work.

Also adds one sentence to docs/plugin-ui-markup.md's "Resizable panels and
anchors" section: changing a panel's authored w/h/minw/minh/resizable in a
later plugin update resets every user's stored size to the new default
exactly once, automatically -- no plugin-author call site to remember.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:54:07 +02:00
Erik
3cec9df4ab fix(vt): plugin <list> draws no selection band by default in either mode
Slice-7 resemblance re-check found UiMarkupList's column-less items= mode
and its <column> mode disagreed on whether a selected row gets a
persistent SelectedColor fill, so a plugin's Buffs lists highlighted a
row while the Monsters/Meta grids looked different for the same widget.
Real VVS lists (VTank's HudList) draw no such fill at all.

UiMarkupList.SelectionBandEnabled (default false) now gates the fill in
both OnDraw's legacy branch and DrawColumns; MarkupDocument parses
<list selectionband="true"> (same literal-bool convention as
openupward/clearonsubmit) to opt a single list back in. selected/onchange
semantics and scroll-into-view of the selected row are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:49:27 +02:00
Erik
69638584e8 docs(vt): slice 7 ledger — resemblance re-check of D; geometry-revision fix dispatched; round F queued
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:41:05 +02:00
Erik
5de8ab6063 docs(vt): slice 7 ledger — architecture re-check of C+D; round E dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:39:42 +02:00
Erik
fc6e6b98e9 docs(vt): slice 7 ledger — round D landed; final re-checks dispatched; gate build up
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 17:28:16 +02:00
Erik
9eeabb481d docs(vt): slice 7 ledger — fix round C landed; round D dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:49:48 +02:00
Erik
e2cca20e23 docs(vt): slice 7 ledger — scrollbar chrome and resizable/anchor markup merged; round D queued
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:45:34 +02:00
Erik
2e63391cc4 merge(vt): resizable plugin panels (resizable/minw/minh) and anchor markup (owner: larger default, resizable)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:45:06 +02:00
Erik
b71a8ea377 merge(vt): retail scrollbar chrome on plain <menu> popups and overflowing <list>s (owner: same assets as chat/inventory)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:44:26 +02:00
Erik
c7fbbc5608 docs(ui): document resizable panels and anchor grammar
Adds the "Resizable panels and anchors" section to plugin-ui-markup.md:
the resizable/minw/minh attribute table for <panel>, the anchor grammar
for every element that now supports it, the AnchorEdges semantics
(left top default, left right / top bottom stretch, right/bottom pin-
and-move), group-relative child anchoring, and one worked example
(a resizable panel with a group and list that both stretch on drag).
Also updates the Elements table, the "every registered window" intro
paragraph, the ApplyCommon common-attributes paragraph, and the Testing
conventions section to reference the new grammar and its test coverage.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:43:28 +02:00
Erik
e9108277c4 feat: retail scrollbar chrome for markup <menu> overflow and <list> overflow
Owner live-client report 2026-09-07: "For scrollable dropdown or the meta
window we use the same assets as we do in for example chat or inventory
window."

<menu> markup wiring (MarkupDocument.cs): a plugin <menu> is now always
Scrollable (single-column, VTank HudCombo shape) instead of wrapping
overflow into more grid columns, with PopupScrollbarHideWhenDisabled=true
so the bar is entirely absent while the item count fits the "rows"
window. RetailScrollbarChrome.ApplyToMenuPopup wires the same chrome ids
the previous commit taught DrawScrollablePopupPlain to draw, for both
style="plain" and style="retail" markup menus.

<list> markup (UiMarkupList.cs / MarkupDocument.cs): a plugin <list>
(single-column or <column> multi-column) that overflows its own row
viewport now draws the retail scrollbar chrome at its right edge (VVS's
own placement, 16px wide) instead of being wheel-scroll-only with no
visible bar. The reserved 16px column only exists while rows actually
overflow, in both column-layout modes (ComputeColumnLayout receives the
already-shrunk width so the last/auto column absorbs the remainder
correctly); the bar is fully interactive (up/down arrows, track paging,
thumb drag) via a small UiScrollable projection kept in sync with the
list's own _topRow, which stays the single source of truth. Wheel
scrolling and a no-resolver hand-built list (draws nothing, no crash) are
unchanged.

Mutation shown to fail first: new
UiMarkupListScrollbarTests/MarkupDocumentTests cases were written against
pre-change UiMarkupList/MarkupDocument and failed (no scrollbar sprites
ever emitted since UiMarkupList had no SpriteResolve property at all, and
<menu> markup never set Scrollable) before the implementation landed;
after: SingleColumn_Overflowing_DrawsRetailScrollbarChromeAtRightEdge and
Columns_Overflowing_ReservesSixteenPixels_LastColumnShrinksAccordingly
pin sprite ids + exact reserved-width geometry,
*_ContentFits_DrawsNo(Scrollbar|ReservationLastColumnKeepsFullRemainder)
pin the no-overflow/no-bar case, *_UpArrowClick_ScrollsUpByOneRow and
ThumbDrag_MovesTopRowAndIsReadableByASubsequentClick pin interactivity via
a following row click resolving to the moved position (mirroring
MarkupListColumnsTests' own wheel-scroll pin), and the four new
MarkupDocumentTests menu cases pin Scrollable/PopupScrollbarHideWhenDisabled/
the six chrome-id properties plus an end-to-end open-popup draw for both
the overflowing (draws chrome) and non-overflowing (draws none) cases.
Every pre-existing MarkupListColumnsTests/MarkupDocumentTests case stays
green unchanged (none of their fixtures overflow their own viewport).

docs/plugin-ui-markup.md updated: the <menu> style paragraph and a new
<list> "Scrollbar" section describe the new chrome + auto-reservation, and
the PITCH-transcription guidance is corrected to say the 16px scrollbar
column is now automatic (no more manual fold-in/double-reservation advice).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:43:00 +02:00
Erik
bc273adcbd docs(vt): slice 7 ledger — owner's live look; App-side scrollbar/resizable work dispatched; panel round D queued
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:17:12 +02:00
Erik
b36b036475 docs(vt): slice 7 ledger — round B closed at 16/18, arch re-check verdict, round C dispatched, owner testing live
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 14:48:37 +02:00
Erik
e6e47dd98a docs(vt): post-campaign idea — readable export/diff for settings and loot, not a second storage format
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:51:14 +02:00
Erik
c62dc495a5 docs(vt): slice 7 ledger — both review verdicts, lead decisions, fix round B dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:13:00 +02:00
Erik
805d10e2b8 docs: file #490 (plugin-panel host StartVisible/ShowInSidePanel + layout revision) and #491 (buff lists not consumed by BuffPlan.Build)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 11:12:40 +02:00
Erik
327e0e93df docs(vt): slice 7 ledger — all nine tabs landed; lead's read of Route/Meta for fix round B; reviews dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 10:55:58 +02:00
Erik
fd5dfa49e0 docs(vt): document that plain <menu> style covers the open popup too
Follow-up to the UiMenu popup fix: the existing "menu style" paragraph in
plugin-ui-markup.md only described the closed-state button face swap from
the earlier S7 fix. Extend it to say the plain style now covers the whole
menu (closed AND open) — flat popup chrome matching <list>, a lighter hover
fill, no checkmark, and a plain scrollbar past the row cap — so a plugin
author reading the doc doesn't assume style="plain" only affects the
closed face.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 10:19:54 +02:00
Erik
fab134ae4f docs(vt): slice 7 ledger — owner: the open dropdown is still retail art; plain popup dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 09:55:44 +02:00
Erik
422ca75172 docs(vt): slice 7 ledger — fix round A landed, remaining tabs dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 09:55:03 +02:00
Erik
80dc762375 docs: headless console CLOSED — connected proof passed; #489 gains the diagnostics-interleave polish item
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:30:35 +02:00
Erik
39ed2b5f95 docs: file #489 — headless SpewBox growth without a console, plus console polish items
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:27:48 +02:00
Erik
8cb284d6f7 merge: headless console — interactive chat/command CLI for the bot host (review-closed)
Owner direction 2026-09-07. Reader thread → tick-drained queue, the same
ChatCommandRouter.Submit the chat box uses, event-stream renderer,
SpewBox pump, --console / ACDREAM_HEADLESS_CONSOLE (=0 disables).
Opus review APPROVE-WITH-FIXES, 12-item fix round, narrow re-check
MERGE-READY.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:25:22 +02:00
Erik
66160741f0 merge: plugin text and unknown-command refusals go to the chat window (owner-directed, AD-124)
Owner 2026-09-07: plugin output and 'Unknown command' land in the chat
scroll, not the SpewBox. Plugin text = Decal/VTank-faithful; the
unknown-command re-route is recorded as AD-124 (retail types it 0x1A).
Bad-argument refusals of real retail commands stay SpewBox-only (AP-183).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:24:02 +02:00
Erik
6b7b4bb213 fix #363/#367: route unknown-command refusals to chat, not SpewBox
Owner direction 2026-09-07 (verbatim): "Unknown commands like /vt or
stuff from plugins shall now go to the SpewBox. They should go to the
chatbox." Retail itself types ChatCommandRouter's "Unknown command"
refusals as ClientLocal (0x1A) -- the bit every ChatInterface window's
default filter excludes, so they only ever reached the transient
SpewBox overlay and left no transcript record.

Three call sites in ChatCommandRouter.Submit/EmitVerbHelp now call
IChatCommandFeedback.ShowSystemMessage (chat scroll, retail
Default/0x00) instead of ShowInterfaceText (SpewBox): the degenerate-
prefix "Unknown command: {verb}." guard, EmitVerbHelp's confirmed-
null-help branch, and EmitVerbHelp's unresolved-verb fallback. Every
OTHER 0x1A refusal in this file (AP-183 bad-argument refusals of REAL
retail commands -- lifestone, marketplace, channel list/on/off,
allegiance, house, the generic HandleFailureEvent(0x26) fallback,
DoStupidChannelHack, DoReply) is unchanged and still SpewBox-only --
the owner named only unknown commands and plugin text.

This is a deliberate deviation from retail's own 0x1A typing, recorded
as register row AD-124 (also covers the sibling plugin-text change in
a follow-up commit). docs/ISSUES.md #363/#367 get a one-line note
under each pointing at the re-route; their CLOSED status is untouched.

Mutation check: temporarily reverted all three ShowSystemMessage call
sites back to ShowInterfaceText and confirmed the 3 new/changed pinned
tests fail (Assert.Single() on an empty chat log) while the AP-183
boundary test (real command, bad args, still SpewBox) continues to
pass -- see ChatCommandRouterFeedbackRoutingTests.cs and the updated
ChatCommandRouterTests.cs/RetailCommandHelpTableTests.cs assertions.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:22:06 +02:00
Erik
47a1fe555c docs(vt): slice 7 ledger — Monsters landed; fix round A dispatched
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:20:10 +02:00
Erik
7381112394 docs(headless-console): record the 2026-09-07 Opus fix-round ledger entry
N4 + summary: /status and /quit are console-intercepted verbs (they
never reach ChatCommandRouter), unlike @status which is a real server
command and still passes through untouched. Records the full S1-S7/
N1-N5 fix-round outcome, final Headless (207/1/208) and App LaunchOptions
(4/4) suite counts, and the one-commit-per-item/mutation-shown-to-fail
discipline used throughout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:15:35 +02:00
Erik
d8846d7a2d docs(vt): document <menu style> in plugin-ui-markup.md
Adds style to the <menu> attribute row and one sentence explaining why
plain is now the default (owner report: retail's gold pushbutton art
read as an out-of-place button next to a plugin's own dark list boxes)
and what style="retail" opts back into.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 08:15:32 +02:00
Erik
1637bae31d docs(vt): slice 7 ledger — owner's live look: overlaps and the gold dropdowns must go
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 07:56:49 +02:00
Erik
d1fe52368d docs(vt): slice 7 — live screenshots of S7.1/S7.2 (cropped) and the lead's read for the fix round
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 07:54:27 +02:00
Erik
55e454aedc fix(headless): S1 ACDREAM_HEADLESS_CONSOLE=0 disables even on a real terminal
HeadlessConsoleOptions.Resolve tested the environment variable against
the literal "1", so ACDREAM_HEADLESS_CONSOLE=0 silently fell through to
the terminal-shaped default (on when stdin is a real console) instead of
acting as an A/B off-switch. Now: once the variable is SET AT ALL, any
value other than "0" enables and "0" disables -- the same idiom
ACDREAM_RETAIL_CLOSE_DEGRADES / ACDREAM_RETAIL_UI already use. An unset
variable still falls through to the terminal default.

Registered the flag as the sixth entry in
LaunchOptionsDocumentationTests.DefaultOnBehaviorFlags and updated the
Conventions section of docs/launch-options.md plus the flag's own row
(side-effects column corrected to describe the real precedence).

ResolvePrefersFlagThenEnvironmentThenTerminalDefault's env="0"/terminal
=true case was shown to fail against the prior `== "1"` implementation
(expected false, old code returned true) before the fix landed; the
env="yes" case also failed on the same mutation (old code required the
literal "1", so "yes" fell through to terminal=false instead of enabling).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 07:51:41 +02:00
Erik
9ec145173e docs(vt): slice 7 ledger — ACE was up; the TCP probe was wrong (ACE is UDP)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 07:37:21 +02:00
Erik
8681a25e6c docs(vt): slice 7 ledger — S7.1/S7.2 landed, popup-height note, screenshots owed (ACE down)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 07:28:59 +02:00
Erik
97b1c9f6ce docs(headless-console): record implementation ledger and connected proof
Appends the plan's ledger with what shipped, the deliberate deviation
from the plan's illustrative bracket-prose example (Headless cannot
reference AcDream.UI.Abstractions, so the console's chat rendering is
its own terminal-shaped format using the same channel-name strings,
not a byte-for-byte port of ChatVM.FormatEntry), the chosen console
default and why, and the exact connected proof recipe for the owner to
run against a live ACE with +Acdream and MossTank (not run here per
the contract).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:57:05 +02:00
Erik
7bff9c649d feat(headless): typed --console option resolution + command-line flag
Adds the first piece of the interactive headless console (docs/plans/
2026-09-07-headless-console.md): HeadlessConsoleOptions.Resolve picks
--console, then ACDREAM_HEADLESS_CONSOLE=1, then a terminal-shaped
default, matching the project's typed-options-object convention rather
than a scattered env-var read. HeadlessCommandLine.Parse now accepts
--console as a bare flag (no value token) alongside the existing
paired options. Both new launch-options.md rows are added in this
commit per LaunchOptionsDocumentationTests' bidirectional rule.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:56:03 +02:00
Erik
fbdbbc7f90 docs(vt): slice 1c review D1 — the profile-dir override row and AD-122 say the first load moves flat .af files
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:54:00 +02:00
Erik
7cfe57827c merge(vt): slice 1c — metas/ and navs/ folders, no nav_ prefix (review-closed)
Owner decision 2026-09-07. Folder-scoped listing, one-time flat-file
migration, content sanity on load. Narrow Opus review: MERGE-READY.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:53:43 +02:00
Erik
615a401994 docs(vt): slice 7 visual gate script (draft, filled from KB 08)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:46:39 +02:00
Erik
812a533b83 feat(vtank): slice 1c step 1 — two-folder metas/navs layout, no nav_ marker
Owner decision 2026-09-07: Meta and Nav profiles both use metaf .af and are
told apart by living in two dedicated VtankProfiles subfolders (metas/,
navs/) instead of the flat-directory nav_/--nav_ marker scheme from slice 1
Part A, which was only ever how the owner happened to name files in their
own metas repo. VtankProfileDirectory.ListMetaProfiles/ListNavigationProfiles
now enumerate metas/ and navs/ respectively via a new folder-scoped
EnumerateFolderFileNames helper; the NavMarker constant and the marker
overload of AutoCharacterFileName are deleted. MossTankMetaProfileStore and
MossTankRouteProfileStore build every real storage key with their folder
prefix (CurrentFileName, Select, Create, TryImportLegacy, the legacy-roster
sweep) and strip it back off for display (StripAf/Strip). The .cdf's Nav/Meta
lines (4-5) now carry the folder-relative key ("metas/Name.af",
"navs/Name.af"); AD-122 and the ACDREAM_VTANK_PROFILE_DIR launch-option row
are updated to describe this.

Mutation demonstrated: reverting VtankProfileDirectory.cs,
MossTankMetaProfileStore.cs, and MossTankRouteProfileStore.cs to HEAD~ (the
flat nav_-marker layout) while keeping the updated tests reproduces 8 test
failures (KeyNotFoundException / Assert.True(false) against the new
"metas/…"/"navs/…" keys the tests now expect, e.g.
MetaSaveAcceptsAnAfSuffixedNameWithoutDoublingIt,
MetaAndRouteProfilesWithTheSameNameDoNotCollide,
NavCommandsImportLegacyAndExportAf, MetaCommandsImportLegacyAndExportAf,
MetaRosterSweepConvertsEveryNamedLegacyProfileOnce,
MetaStoreLeavesLegacyJsonUntouchedWhenAfCounterpartExists,
MetaStoreRefusesToSaveADisabledRuleAndKeepsThePriorAfContent,
NavSaveAcceptsAnAfSuffixedNameWithoutDoublingIt) — confirmed by running the
suite immediately after the production-code edit, before the test-file
updates landed. All 625 tests pass after both sides of the change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:32:56 +02:00
Erik
a427d7db05 docs: plan the headless console (interactive chat/command CLI for the bot host)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:32:47 +02:00
Erik
43abc32edf docs(vt): owner direction — one plugin, two hosts; slice 2 gains headless autostart + a headless MossTank smoke test
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:29:46 +02:00
Erik
074762dd36 docs(vt): slice 7 plan — the nine tabs at VTank's geometry on the column markup; pulled forward after slice 1
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:23:28 +02:00
Erik
aafa66c821 docs(vt): owner decision — metas/ and navs/ subfolders, no nav_ prefix
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:20:16 +02:00
Erik
8080a99eef docs(vt): owner confirmed — binary .met/.nav readers stay for convert-on-import only
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 06:16:54 +02:00
Erik
55e0f12ae6 docs(vt): slice 1 closeout — Part A merged at f680bf234; owner items; next = slice 7
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 02:26:46 +02:00