merge(vt): plain <menu> popup from the campaign branch into the slice-7 panel work (ledger union)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
commit
dbdde0783d
30 changed files with 2530 additions and 83 deletions
|
|
@ -74,30 +74,40 @@ after each deliberate `Top` write for the imported-layout element. Precedent:
|
|||
`MapPageController.cs:235-249` (the same fix already landed for other
|
||||
runtime-repositioned imported/programmatic elements).
|
||||
|
||||
## #488 — MossTank `.utl` expression block: length prefix measured before newline normalization
|
||||
## #489 — Headless: SpewBox pending queue grows unbounded when no console ticks it; console polish
|
||||
|
||||
**Status:** OPEN — found 2026-09-07 by the final Opus re-check of Campaign VT
|
||||
slice 1 Part A (`f58e997b1`), not reachable from the UI.
|
||||
**Severity:** LOW (latent)
|
||||
**Component:** `src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs` (`AttachMossTankExpressions` ~504-521, `ApplyMossTankExpressions` ~539-554) vs `VtankLootProfileSerializer.cs` (`WriteBlock` ~357-366, `NormalizePayload`)
|
||||
**Status:** OPEN — found 2026-09-07 by the Opus re-check of the headless console (`738111239`).
|
||||
**Severity:** LOW/MEDIUM (leak in long-lived bots)
|
||||
**Component:** `src/AcDream.Runtime/.../SpewBoxState.cs` (`Enqueue` ~:110, `_pending`), `src/AcDream.Headless/Hosting/HeadlessConsoleSpewBoxPump.cs`
|
||||
|
||||
**Description.** The MossTank-owned unknown block that carries each loot rule's
|
||||
`Expression` text writes `expression.Length` as a length prefix and then the raw
|
||||
text; `WriteBlock` normalizes the whole payload afterwards, rewriting a lone
|
||||
`
|
||||
**Description.** `RuntimeCommunicationState.AddText` routes every `ClientLocal` (0x1A) line into `SpewBoxState.Enqueue`; the only `Tick` caller in the headless host is the console pump, so with the console disabled (every scripted/CI bot) `_pending` grows for the life of the session. Pre-existing before the console; the console merely made it visible. Fix shape: tick the SpewBox from the session tick regardless of the console (or drop `ClientLocal` text when nothing observes it), with a pin that a 10,000-line burst without a console does not grow the queue.
|
||||
|
||||
**Polish carried from the same re-check:** in `--console` mode the JSON diagnostics/resources stream still interleaves with the chat lines on stdout — quiet it or send it to stderr when the console is on; `--console` missing from `--help`; `HeadlessConsoleOptions.cs:51` re-types the env-var literal (the LaunchOptions regex needs it — a const rename would split the two reads); the `/quit`/`/status`/"not handled" writes and `Pump()` sit outside the S4 try/catch (a broken stdout pipe would fault the session); the SpewBox's 4-entry visible cap can drop interface-text lines produced between two pumps.
|
||||
|
||||
## #488 — MossTank `.utl` expression block: length prefix measured before newline normalization
|
||||
|
||||
**Status:** OPEN — found 2026-09-07 by the final Opus re-check of Campaign VT
|
||||
slice 1 Part A (`f58e997b1`), not reachable from the UI.
|
||||
**Severity:** LOW (latent)
|
||||
**Component:** `src/AcDream.Plugins.MossTank/MossTankLootProfileStore.cs` (`AttachMossTankExpressions` ~504-521, `ApplyMossTankExpressions` ~539-554) vs `VtankLootProfileSerializer.cs` (`WriteBlock` ~357-366, `NormalizePayload`)
|
||||
|
||||
**Description.** The MossTank-owned unknown block that carries each loot rule's
|
||||
`Expression` text writes `expression.Length` as a length prefix and then the raw
|
||||
text; `WriteBlock` normalizes the whole payload afterwards, rewriting a lone
|
||||
`
|
||||
`/`
` to `
|
||||
`. An expression containing a bare newline therefore grows
|
||||
after its prefix was measured, the reader truncates it, lands mid-text on the
|
||||
next length line, fails `int.TryParse` and silently abandons every remaining
|
||||
rule's expression. The loot expression control is a single-line field so the UI
|
||||
cannot author one; the legacy-JSON sweep can (free-form JSON).
|
||||
|
||||
**Fix shape.** Normalize the expression before measuring it (or escape/refuse
|
||||
newlines in the block), with a pin that writes a two-line expression and reads
|
||||
it back through `VtankLootProfileSerializer.TryRead`. Companion cosmetics from
|
||||
the same re-check: the unreachable `remaining` roster branch in the route and
|
||||
loot sweeps, and the meta Delete notice printing the raw file name.
|
||||
|
||||
`. An expression containing a bare newline therefore grows
|
||||
after its prefix was measured, the reader truncates it, lands mid-text on the
|
||||
next length line, fails `int.TryParse` and silently abandons every remaining
|
||||
rule's expression. The loot expression control is a single-line field so the UI
|
||||
cannot author one; the legacy-JSON sweep can (free-form JSON).
|
||||
|
||||
**Fix shape.** Normalize the expression before measuring it (or escape/refuse
|
||||
newlines in the block), with a pin that writes a two-line expression and reads
|
||||
it back through `VtankLootProfileSerializer.TryRead`. Companion cosmetics from
|
||||
the same re-check: the unreachable `remaining` roster branch in the route and
|
||||
loot sweeps, and the meta Delete notice printing the raw file name.
|
||||
|
||||
## #487 — Radar compass tokens may be pinned by the anchor pass (candidate)
|
||||
|
||||
**Status:** OPEN — CANDIDATE, found 2026-09-06 by the Opus review of
|
||||
|
|
@ -5922,6 +5932,13 @@ slice CH4).
|
|||
|
||||
## #363 — Chat refusal/usage call sites are typed ClientLocal 0x00 where retail types several 0x1A
|
||||
|
||||
**2026-09-07 owner-directed re-route:** the "Unknown command" refusals this
|
||||
issue's closure routed to `ShowInterfaceText`/SpewBox now route to
|
||||
`ShowSystemMessage`/the chat scroll instead, per explicit owner direction
|
||||
that unknown commands must be visible in chat, not the SpewBox overlay.
|
||||
Every OTHER site this issue named (bad-args refusals of real commands,
|
||||
AP-183) is unaffected. See register row AD-124.
|
||||
|
||||
**Status:** CLOSED 2026-08-10. `ChatVM` gained a typed interface-text seam
|
||||
(`OnInterfaceText` init hook + `ShowInterfaceText(text)`) that the App-layer
|
||||
composition (`InteractionRetainedUiComposition.CreateRetainedUi`) wires to
|
||||
|
|
@ -6261,6 +6278,14 @@ still missing); `src/AcDream.App/UI/Layout/LayoutImporter.cs`
|
|||
|
||||
## #367 — ChatCommandRouter's local-presentation fallbacks type-0x1A text still lands in the chat scroll, never the SpewBox
|
||||
|
||||
**2026-09-07 owner-directed re-route:** the two fallbacks this issue named
|
||||
(`RetailCommandHelpTable.UnknownCommand` in `EmitVerbHelp`, and the
|
||||
degenerate-prefix "Unknown command: {verb}." refusal) now call
|
||||
`ShowSystemMessage(...)` again — back to the chat scroll, by explicit owner
|
||||
direction that unknown commands must be visible there rather than in the
|
||||
SpewBox this issue's 2026-08-10 closure moved them to. See register row
|
||||
AD-124; this is a deliberate re-reversal, not a regression of this issue.
|
||||
|
||||
**Status:** CLOSED 2026-08-10, closed as a side effect of #363's
|
||||
interface-text seam (fix shape (a) from this issue's own filing).
|
||||
`ChatVM.OnInterfaceText` is exactly the hook this issue asked for; both
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -40,12 +40,16 @@ Assume a flag has a side effect until its row says otherwise.
|
|||
- **Everything diagnostic is OFF by default.** Every probe, dump, capture,
|
||||
and measurement flag in this document is inert until its variable is
|
||||
explicitly set — an unset environment runs zero diagnostics. Exactly
|
||||
five flags default ON, and none is a diagnostic: `ACDREAM_RETAIL_CHASE`,
|
||||
six flags default ON, and none is a diagnostic: `ACDREAM_RETAIL_CHASE`,
|
||||
`ACDREAM_CAMERA_COLLIDE`, `ACDREAM_CAMERA_ALIGN_SLOPE`, and
|
||||
`ACDREAM_RETAIL_CLOSE_DEGRADES` are retail *behaviors* wearing an A/B
|
||||
off-switch (`=0` disables the behavior for a comparison run), while
|
||||
`ACDREAM_RETAIL_UI` is the product's only gameplay presentation and uses
|
||||
the same explicit diagnostic opt-out. That five-flag set is frozen by
|
||||
the same explicit diagnostic opt-out. `ACDREAM_HEADLESS_CONSOLE` is the
|
||||
sixth: its unset default is terminal-shaped (on when stdin is a real
|
||||
console, off when redirected — not unconditionally on like the other
|
||||
five), but once the variable is SET AT ALL it uses the identical `=0`
|
||||
override (any other value enables). That six-flag set is frozen by
|
||||
`LaunchOptionsDocumentationTests` — a new
|
||||
default-on flag fails the build.
|
||||
- `=1` means the code tests for exactly the string `1`. Setting `true`,
|
||||
|
|
@ -93,6 +97,7 @@ dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Release
|
|||
| `ACDREAM_DAT_DIR` | `=<path>` | Fallback dat-directory when no positional argument is given. App: single read at `Program.cs:58`. Cli: read independently per-subcommand (each subcommand does `args.ElementAtOrDefault(N) ?? Env.GetEnvironmentVariable("ACDREAM_DAT_DIR")`) plus once more for the default (no-subcommand) asset-inventory mode at line 152. | Two of the four `Program.cs` line numbers in the raw grep (91, 135) are **not reads** — they're the literal string `ACDREAM_DAT_DIR` inside `Log.Error` usage-text messages, not `GetEnvironmentVariable` calls. Only line 58 is a real read in `AcDream.App`. | none — hard usage error (exit 2) if unset and no positional arg | `Program.cs:58` (App); `Cli/Program.cs:24,35,47,59,71,84,113,125,137,152` (every Cli subcommand) |
|
||||
| `ACDREAM_DISPLAY_PROTOCOL` | `="auto"` / `"x11"` / `"wayland"` (case-insensitive, trimmed); any other value throws `InvalidOperationException` at startup | Linux-only: forces the GLFW 3.4 platform-init hint (X11 vs Wayland vs auto) before any window is created; ignored entirely on Windows (always `Windows` protocol) | An invalid value is fatal at startup (throws before any window exists), not a silent fallback | unset → auto-detected from `XDG_SESSION_TYPE`/`WAYLAND_DISPLAY`/`DISPLAY`, falling back to GLFW `Automatic` | `GraphicalWindowBackendSelection.Resolve` (`GraphicalWindowBackendSelection.cs:26-58`) |
|
||||
| `ACDREAM_FAR_RADIUS` | `=<int>` | Overrides preset's `FarRadius` (outer streaming/reveal window, landblocks) | Enlarging changes streaming memory budget and what's resident/rendered — CLAUDE.md: leave unset for measurement/gate runs (same family as legacy `ACDREAM_STREAM_RADIUS`) | preset's `FarRadius` (Low=5, Medium=8, High=12, Ultra=15) | `QualitySettings.WithEnvOverrides` (`QualityPreset.cs:47`) |
|
||||
| `ACDREAM_HEADLESS_CONSOLE` | `=0` disables (once set at all); any other value enables; unset falls through to the terminal-shaped default | Turns on the headless host's interactive console (docs/plans/2026-09-07-headless-console.md): a background thread reads stdin lines, each drained on the session tick through the SAME plugin-verb/client-slash-command pipeline the graphical chat box uses, with chat/lifecycle/portal output rendered to stdout. Only takes effect for `run` with a single configured session — a multi-session process reports `console: single-session only` via the diagnostics stream and does not attach one. | Starts a background stdin-reader thread and writes plain-text lines to the same stdout stream `HeadlessDiagnosticWriter` already uses for its JSON lines — the two interleave. Only applies to `run`; `--console` (bare flag, no value) always wins over this variable. S1 fix (2026-09-07): the variable itself now wins outright once SET AT ALL — `=0` disables even when stdin is a real terminal, matching every other `=0`-disables flag in this table; only an UNSET variable falls through to the terminal-shaped default. | unset → on when stdin is a real console, off when redirected (`!Console.IsInputRedirected`, checked once in `Program.cs`); set → `!= "0"` | `HeadlessConsoleOptions.Resolve` (`Configuration/HeadlessConsoleOptions.cs`) → `HeadlessEntryPoint.Run` → `HeadlessProcessHost`'s `consoleEnabled` |
|
||||
| `ACDREAM_LIVE` | `=1` (exactly the literal string `"1"`) | Core switch: connect to a live ACE server instead of running offline/no-connect. | The 4 non-`RuntimeOptions.cs` line numbers in the raw grep are **all comments or log-message text**, not reads — `SessionStartComposition.cs:39` is inside the string `"live: ACDREAM_LIVE set but TEST_USER/TEST_PASS missing; skipping"`; `Program.cs:126` is inside a `--session-config` override log line; `GameWindow.cs:614,627` are doc comments. The only actual parse is `RuntimeOptions.cs:141`. Requires `ACDREAM_TEST_USER`/`ACDREAM_TEST_PASS` too (`HasLiveCredentials`) or the session silently reports `MissingCredentials` and skips. Forced to effectively-on (LiveMode=true) unconditionally by `--session-config` launches regardless of this var. | `false` | `RuntimeOptions.LiveMode` → `SessionStartComposition.cs` (log text only), `Program.cs:126` (log text only), `GameWindow.cs:614,627` (comments only), consumed for real via `RuntimeOptions.HasLiveCredentials` and `WorldSession`/`GameRuntime` session-start gating |
|
||||
| `ACDREAM_MAX_COMPLETIONS_PER_FRAME` | `=<int>` | Overrides preset's per-frame streaming-completion throughput cap | Directly changes the streaming admission budget measured by perf/completion gates — do not vary during a measurement run | preset's value (Low=2, Medium=3, High=4, Ultra=6) | `QualitySettings.WithEnvOverrides` (`QualityPreset.cs:59`) |
|
||||
| `ACDREAM_MSAA_SAMPLES` | `=<int>` (0/2/4/8) | Overrides preset's MSAA sample count | Changes GPU multisample anti-aliasing (visual + GPU-cost change) | preset's `MsaaSamples` (Low=0, Medium=2, High/Ultra=4) | `QualitySettings.WithEnvOverrides` (`QualityPreset.cs:48`) |
|
||||
|
|
@ -143,6 +148,7 @@ config without connecting; `run` connects.
|
|||
| `--config <path>` | The versioned headless session-configuration document. Required. | — |
|
||||
| `--config-dir` / `--data-dir` / `--cache-dir` `<path>` | Override each portable path root. | Merged over the config document's own `process.paths`; the command line wins. |
|
||||
| `-user` / `--user`, `-password` / `--password` | Direct single-session credentials, bypassing the config's credential source. | Plaintext in the process command line — prefer the config's credential reference. |
|
||||
| `--console` | Forces the interactive console on for `run` (bare flag, no value) — see `ACDREAM_HEADLESS_CONSOLE`. | Same side effects as the environment variable; this flag always wins over it. |
|
||||
| `--help` / `-h` (or no args) | Prints usage, exits 0. | — |
|
||||
|
||||
### `AcDream.Launcher`
|
||||
|
|
|
|||
|
|
@ -131,3 +131,6 @@ re-review, merge to the campaign branch, then the owner's visual gate.
|
|||
- 2026-09-07 09:10 S7.3 Monsters landed on the panel worktree (`57ced0aff`, `c3b4f7862`; MossTank suite 645 → 651) — same commits as the entry above; recorded again here because the plain-menu-style branch (`cfa703065`) merged into the panel worktree afterward to pick up fix (b) before fix round A started. Fix round A (grid scaling, Profiles leftovers, 260-tall window, Advanced Options / Loot Editor as their own panels, blank trailing slots, fresh screenshots) dispatched on the same worktree after merging the plain-menu style in. S7.4–S7.6 follow.
|
||||
- 2026-09-07 fix round A landed on the panel worktree, four commits: `045cd0a19` (merge `claude/latest-main-sync-497549`, bringing the plain-`<menu>`-style fix (b) in — resolved the ledger/markup/test conflicts by keeping both sides' content), `565a33d78` (grid scaling + Profiles cleanup + popups split into their own panels), `e414b2f56` (AcDream.App.csproj's CopyMossTankPlugin* targets hardcoded mosstank.xml as the only file to copy into `plugins/AcDream.Plugins.MossTank/` — the two new popup markup files silently landed in the App's own bin root instead and would have thrown `FileNotFoundException` on load; caught before any screenshot by inspecting the build output layout, not by a test), `78b42a519` (StartVisible=true fix for both popups — `StartVisible=false` left `PluginWindowVisibilityController`'s "requested visible" axis permanently false with no shelf entry to ever call `OnShown()`, so neither popup ever rendered despite a checked/green checkbox; plus dropped each popup's now-redundant in-content title label, and repositioned both away from the overlapping (440,60) placeholder). Real DAT-font measurements (`AcDream.Cli dump-font-atlas` against the installed DAT: font 0x40000000 MaxCharHeight=16, matching VVS's own assumed row height exactly) replaced the "sy row-pitch" theory in the 07:55 lead's read — the actual fix is a translation of the columns after each overflowing caption (Options +62px, Profiles +16px), not a font-driven vertical scale. Fresh screenshots recaptured end-to-end against a live local ACE with an isolated `ACDREAM_CONFIG_DIR` (stale persisted popup window positions from earlier probe runs would otherwise have overridden the new authored defaults forever — `RetailWindowLayoutPersistence` has no revision bump wired for plugin windows). All six requested screenshots (Options/Profiles/Vitals/Monsters/both popups) confirm: no overlap, no gold buttons, no stacked New/Loot-engine/path-string leftovers, both popups open as genuinely separate windows with clean titles, and the Route/Meta/Loot-editor move-up/move-down slots render real DAT icons instead of blank buttons. MossTank suite 651 → 654 (three new pins: the two-file `SecondaryPopupPanelsFitTheirOwnBoundsAndEveryBindingResolves` theory cases + `NoButtonAnywhereUsesTheUnrenderableArrowGlyphs`); App markup/plugin/menu filter holds 237/237. Deviation carried forward: Macro/Nav CopyTo lost their only in-UI target-name entry (the deleted 3-row block was their sole source; Meta already has one on its own tab) — matches VTank's own Profiles table having no name-draft control at all, but is a real, accepted capability regression pending a future naming-UX slice. Owner's connected visual gate is the next step.
|
||||
- 2026-09-07 S7.4–S7.6 implemented on the panel worktree (base `66b070def`), three commits: `f5409530f` (S7.4 — Items' 2-column name/hands grid, Consumables' "Excluded Scarab Types" icon+text grid and "Add Selected" button, Buffs' Extra Buff Spells / Blacklisted Buff Families lists plus a shared `mosstank-buffpicker.xml` SelfBuffChoiceView-style picker popup registered the same way fix round A's two popups are), `6118062a7` (S7.5 — Route's clWP/clWPc 2-column waypoint grid, the "Follow" nav-mode display remap, `scroll="true"` on the recall menu, and a third nav image button for "Select Nearest Point"), `cc323f6a5` (S7.6 — Meta's 6-column lstMetaRules grid: delete/move-up/move-down cells plus State/Condition/Action text cells opening the existing rule editor). Deviations documented at their own binding site: Items' Hands column is session-local only (no backing wieldable-handedness data anywhere in the plugin surface); Consumables' "Add Selected" accepts any selected owned item rather than requiring VTank's own SpellComponent object-class check (no classifier surface exists for plugins); ExtraBuffSpellNames/BlacklistedBuffFamilyNames (BuffPlan.cs) add storage + UI only, not wired into `BuffPlan.Build`'s cast selection (real casting-algorithm behavior, owned by a future Campaign VT behavior slice); Route's recall menu keeps its real 4 kinds rather than VTank's 27 named recalls (needs real per-recall spell-id data); Route's "Select Nearest Point" moves the tab's own edit selection rather than VTank's live navigation cursor (no mutable cursor exposed to a plugin); Meta's delete cell is a text "X" rather than an icon (no retail DAT delete-glyph id confirmed anywhere in this codebase, unlike the established move-up/move-down `0x060028FC`/`0x060028FD` pair). Every new/changed pin (contract control count 167→177→180→186, the new `mosstank-buffpicker.xml` popup pin, six new `MossTankPanelTests` interaction tests) was shown to fail against a targeted mutation before being confirmed green. MossTank suite 654 → 660; App markup/plugin filter holds 192/192; full solution builds clean in Release. Fresh live screenshots recaptured against the same local ACE recipe fix round A established (isolated `ACDREAM_CONFIG_DIR`/`ACDREAM_DATA_DIR`, an `ACDREAM_UI_PROBE_SCRIPT` route through the five changed tabs plus the new buff picker popup) → `docs/research/2026-09-07-slice7-screenshots/` (`tab-items.png`, `tab-consumables.png`, `tab-buffs.png`, `tab-route.png`, `tab-meta.png` recaptured at 900×300; `popup-buffpicker.png` added at 940×715). All six confirm: plain (non-gold) controls throughout, no overlapping captions, the two new Consumables/Buffs grids and the buff picker popup render correctly, and the Route/Meta move icons render real DAT art. No crashes or ungraceful exits across the probe runs. S7.7 (the gate script) and the owner's connected visual gate remain.
|
||||
- 2026-09-07 09:10 S7.3 Monsters landed on the panel worktree (`57ced0aff`, `c3b4f7862`; MossTank suite 645 → 651): the 23-column grid with VTank's exact cycle lists (P −1…4; Dmg type 14 values; Ex. Vuln 9; PetDmg 10; name click deletes; arrows reorder with DEFAULT pinned). Implementer deviations for the review: Weapon/Offhand cycle MossTank's registered item roster instead of VTank's opaque weapon-type ids (MossTank models concrete owned items); the move-up/down DEFAULT guard is symmetric. Fix round A (grid scaling, Profiles leftovers, 260-tall window, Advanced Options / Loot Editor as their own panels, blank trailing slots, fresh screenshots) dispatched on the same worktree after merging the plain-menu style in. S7.4–S7.6 follow.
|
||||
- 2026-09-07 10:10 fix round A landed on the panel worktree (`045cd0a19` merge of the plain menu, `565a33d78` column shifts + Profiles cleanup + 236-tall window + popup panel files, `e414b2f56` csproj plugin-copy fix, `78b42a519` popups actually render (`StartVisible` gotcha) + fresh screenshots, `66b070def` ledger; MossTank suite 651 → 654). Owner's two complaints verified fixed on the new screenshots. Deviation for the review: Macro/Nav CopyTo lost their in-UI target-name field with the deleted block (VTank has none either). S7.4–S7.6 dispatched on the same worktree.
|
||||
- 2026-09-07 10:20 owner, live: "Drop down menus look horrible, there is also a checkmark on the text there." — the OPEN popup still draws retail art (tan gradient panel, ornate gold scrollbar, checkmark on the selected row). Plain open state (dark list rows, selected fill, plain scrollbar, no checkmark) dispatched on the plain-menu worktree; merges to the campaign branch, then into the panel worktree at fix round B.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# Headless console — an interactive CLI for the bot host
|
||||
|
||||
Date: 2026-09-07
|
||||
Status: ACTIVE (owner direction 2026-09-07: "the headless client should have
|
||||
Status: CLOSED 2026-09-07 — merged `8cb284d6f`, connected proof passed (owner direction 2026-09-07: "the headless client should have
|
||||
a CLI as well. Like we have the chat loaded in headless so we can see what it
|
||||
does and we can talk via it if we want and control plugins like /moss bla or
|
||||
/say hello")
|
||||
|
|
@ -66,3 +66,159 @@ it or the lead may, it is not a visual gate.
|
|||
|
||||
## Ledger
|
||||
- 2026-09-07 planned; implementer dispatched.
|
||||
- 2026-09-07 IMPLEMENTED. The dispatch seam already existed:
|
||||
`AcDream.Runtime.Chat.ChatCommandRouter.Submit` is the SAME presentation-
|
||||
free pipeline `LoginCommandSequence` (headless) and every graphical chat
|
||||
window (`ChatWindowController`, `FloatingChatWindowController`,
|
||||
`RetailUiRuntime`) already call — no lift was needed. Added
|
||||
`HeadlessSessionHost.SubmitConsoleLine` (`Hosting/HeadlessSessionHost.cs`)
|
||||
as the one new call site, reusing the host's own retained
|
||||
`LiveChatCommandSurface`/plugin registry (now promoted from ctor locals to
|
||||
fields) instead of a second parser.
|
||||
New files: `Configuration/HeadlessConsoleOptions.cs` (typed `--console` /
|
||||
`ACDREAM_HEADLESS_CONSOLE=1` / terminal-default resolution),
|
||||
`Hosting/HeadlessConsoleInputReader.cs` (background stdin thread → FIFO
|
||||
queue, never executes handler code), `Hosting/HeadlessConsoleController.cs`
|
||||
(drains the queue on the session tick via a new `HeadlessSessionHost.
|
||||
ConsolePump` hook; owns `/quit`/`/status`), `Hosting/
|
||||
HeadlessConsoleChatFormatter.cs` + `Hosting/HeadlessConsoleRenderer.cs`
|
||||
(renders the K2 bot event stream — `IRuntimeEventObserver`, the same
|
||||
interface a bot policy subscribes — as bracket-labelled lines:
|
||||
`[Tell] Bob: hi`, `[Fellowship] …`, `[Local] …`), `Hosting/
|
||||
HeadlessConsoleChatFeedback.cs` (decorates `RuntimeChatCommandFeedback` so
|
||||
retail's transient SpewBox/`ClientLocal` interface text — which never
|
||||
touches `ChatLog`, so it never reaches the K2 event stream — also reaches
|
||||
the console). `/quit` cancels a `CancellationTokenSource` linked into the
|
||||
scheduler's run token in `HeadlessProcessHost` (the SAME graceful-exit
|
||||
path an external Ctrl+C/SIGTERM already takes); `/status` reports
|
||||
generation, position (or "unknown" without a live movement controller),
|
||||
and loaded-plugin count (no plugin today reports a richer macro-state
|
||||
string). Console only attaches for a single-session `run` (per the plan's
|
||||
"out of scope for the first cut" multi-session note); constructed AFTER
|
||||
every session's own credential resolution so the reader thread never
|
||||
races a `StandardInput`-provider password prompt on the same stream.
|
||||
Chosen console default: on when `!Console.IsInputRedirected` (a real
|
||||
operator at a terminal), off when redirected (scripts/CI/piped fixtures,
|
||||
where a blocked `ReadLine` on a background thread would just sit idle) —
|
||||
resolved once in `Program.cs`, the only place that can see the real
|
||||
`Console`.
|
||||
Deviation from the plan's illustrative example: retail's own transcript
|
||||
never prefixes Tell/Local lines with a bracket (`ChatVM.FormatEntry`
|
||||
renders "Bob tells you, ..."/"Bob says, ..." with no label) — Headless
|
||||
cannot reference `AcDream.UI.Abstractions` (the dependency-boundary
|
||||
test), so `HeadlessConsoleChatFormatter` is a deliberately DIFFERENT,
|
||||
terminal-shaped "[Label] Sender: text" rendering using the SAME channel-
|
||||
name strings (matching the plan's literal `[Tell] Bob: hi` example), not
|
||||
a byte-for-byte port of the graphical prose.
|
||||
Tests: `tests/AcDream.Headless.Tests/HeadlessConsoleTests.cs` (20 new
|
||||
tests — options resolution, command-line flag parsing, reader-thread
|
||||
ordering/never-on-reader-thread, controller drain/quit/status, chat
|
||||
formatting, and full `/say`/plain-text/plugin-verb/unknown-verb dispatch
|
||||
against a real `HeadlessSessionHost` + `FixtureSessionOperations`, no live
|
||||
server) plus the existing `LaunchOptionsDocumentationTests` (4/4 green)
|
||||
and `HeadlessDependencyBoundaryTests` (3/3 green, unchanged — Headless
|
||||
still references only `AcDream.Runtime`). Every test in this batch was
|
||||
mutation-checked to fail before the corresponding production line existed
|
||||
(see the implementer's final report for the specific mutations run:
|
||||
skipping the interface-text callback, skipping `_quitRequested.Cancel()`,
|
||||
forcing `TryHandlePluginCommand` to always return false, dropping the
|
||||
reader thread's `Enqueue`, and swapping `ChatChannelKind.Say` for `.Tell`
|
||||
in `SubmitConsoleLine`).
|
||||
Suites: `dotnet test tests/AcDream.Headless.Tests -c Release` → 193
|
||||
passed / 1 pre-existing failure (`LinuxRejectsGroupOrOtherCredentialPermissions`,
|
||||
a Linux-only lane test that cannot run on this Windows host — unrelated
|
||||
to this change) / 194 total. `dotnet test tests/AcDream.Runtime.Tests -c
|
||||
Release` → 1891/1891 passed. `dotnet test tests/AcDream.App.Tests -c
|
||||
Release --filter "FullyQualifiedName~Chat|FullyQualifiedName~Command|
|
||||
FullyQualifiedName~LaunchOptions"` → 414 passed / 2 pre-existing failures
|
||||
(`ChatIndicatorButtonLiveMountProbeTests`/`OptionsPanelLiveMountProbeTests`
|
||||
— both gated on `ACDREAM_PROBE_LIVE_MOUNT=1`, a manual live-DAT probe lane,
|
||||
unrelated to this change) / 3 skipped / 419 total. `dotnet build
|
||||
AcDream.slnx -c Release` green throughout.
|
||||
|
||||
- 2026-09-07 FIX ROUND (Opus review, APPROVE-WITH-FIXES). S1: `ACDREAM_
|
||||
HEADLESS_CONSOLE=0` now disables the console even when stdin is a real
|
||||
terminal — the prior `== "1"` test let `"0"` silently fall through to the
|
||||
terminal-shaped default; the flag is now the sixth entry in
|
||||
`LaunchOptionsDocumentationTests.DefaultOnBehaviorFlags` (a default-on
|
||||
behavior with an A/B off-switch, once set at all, like
|
||||
`ACDREAM_RETAIL_CHASE`). S2: the reader-thread pin is now falsifiable — a
|
||||
fixture `TextReader` records the actual thread id `ReadLine` ran on, and a
|
||||
new test asserts the controller's submit callback runs on neither that
|
||||
thread nor any other unexpected one, only the `DrainDue` caller's. S3: one
|
||||
`HeadlessProcessHost` end-to-end test proves a console line reaches the
|
||||
session's real `SubmitConsoleLine` pipeline and `/quit` returns
|
||||
`HeadlessExitCode.Success`. S4: `HeadlessConsoleController.Handle` now
|
||||
wraps `_submit` in try/catch (mirroring `LoginCommandSequence.DrainDue`)
|
||||
and prints a line for `UnknownCommand`/`Dropped`, so a console typo can
|
||||
never escape into the scheduler's per-session quarantine catch. S5:
|
||||
deleted the per-call `HeadlessConsoleChatFeedback` decorator — it only
|
||||
ever saw text produced by the console's OWN `SubmitConsoleLine` calls.
|
||||
The new `HeadlessConsoleSpewBoxPump` polls the shared `SpewBoxState` on
|
||||
the console's own per-tick pump instead, the SAME seam the graphical
|
||||
overlay's `SpewBoxController.Tick` reads, so server- and plugin-driven
|
||||
`ClientLocal` interface text prints too. S6: `Program.cs` now resolves
|
||||
`standardOutputIsTerminal` next to the stdin probe and threads it through
|
||||
`HeadlessEntryPoint.Run` → `HeadlessProcessHost`, which no longer reads
|
||||
`System.Console.IsOutputRedirected` itself. S7: a multi-session process
|
||||
launched with `--console` now reports `_diagnostics.Message("console",
|
||||
"single-session only")` instead of silently skipping console attachment.
|
||||
N1: corrected two stale dispatch-order doc comments
|
||||
(`HeadlessSessionHost.SubmitConsoleLine`, `HeadlessConsoleController`'s
|
||||
class remarks) to the real `ChatCommandRouter.Submit` order: retail's
|
||||
client-command catalog, local `/help`, plugin verbs, the unregistered-
|
||||
channel-tag fallback, an explicit server command, then plain chat. N2:
|
||||
`HeadlessCommandLine.Console` renamed to `ConsoleEnabled`. N3: `validate`
|
||||
mode now rejects `--console` outright rather than silently ignoring it.
|
||||
N4: **`/status` and `/quit` are console-intercepted verbs — they never
|
||||
reach `ChatCommandRouter`, unlike `@status`, which is a real server
|
||||
command and still passes through untouched.** N5: `HeadlessConsoleRenderer`
|
||||
now dims only lifecycle/command/portal lines; chat and interface text
|
||||
print at the terminal's default weight.
|
||||
Every new/changed test was shown to fail first against a targeted
|
||||
mutation of the corresponding production code (see each commit's own
|
||||
body for the specific mutation) before the fix landed; one commit per
|
||||
item, all with `Co-Authored-By: Claude Fable 5.1`.
|
||||
Suites (Release): `dotnet test tests/AcDream.Headless.Tests` → 207
|
||||
passed / 1 pre-existing Linux-lane failure
|
||||
(`LinuxRejectsGroupOrOtherCredentialPermissions`) / 208 total (up from
|
||||
193/1/194 before this round — 14 new/changed tests). `dotnet test
|
||||
tests/AcDream.App.Tests --filter "FullyQualifiedName~LaunchOptions"` →
|
||||
4/4 passed, including the corrected `OnlyTheSixProductBehaviorFlagsDefaultOn`
|
||||
(renamed from Five). `dotnet build AcDream.slnx -c Release` green
|
||||
throughout.
|
||||
|
||||
### Connected proof recipe (owner runs; NOT run by the implementer)
|
||||
|
||||
Against a running local ACE at `127.0.0.1:9000` with MossTank loaded for
|
||||
the second half:
|
||||
|
||||
```powershell
|
||||
$env:ACDREAM_DAT_DIR = "$env:USERPROFILE\Documents\Asheron's Call"
|
||||
dotnet run --project src\AcDream.Headless\AcDream.Headless.csproj --no-build -c Release -- `
|
||||
run --config <path-to-a-one-session-config.json> `
|
||||
-user testaccount -password testpassword --console
|
||||
```
|
||||
|
||||
The referenced config's one session should target character `+Acdream`
|
||||
(server guid `0x5000000A`) against `127.0.0.1:9000`, an `idle` bot policy,
|
||||
and (for the second half) the MossTank plugin id under `plugins`. Once the
|
||||
console prints `entered world`:
|
||||
|
||||
1. Type `/say hello` and press Enter — expect the SAME line ACE echoes back
|
||||
to any other observer (a retail client or a second acdream session
|
||||
watching `+Acdream`) to also print `[Local] You: hello` in this console
|
||||
(the server's own HearSpeech echo, rendered through the normal chat
|
||||
event stream).
|
||||
2. Type `/status` — expect a line with `generation=`, `position=` (a real
|
||||
cell/local-frame triple once in world), and `plugins=N loaded`.
|
||||
3. With MossTank loaded, type `/vt start` (or whatever verb MossTank
|
||||
registers) — expect MossTank's own handler to run (check its own
|
||||
status/log output) and confirm NOTHING was sent to the wire for that
|
||||
line (no `@vt` server command).
|
||||
4. Type `/quit` — expect a graceful ACE logout (same as the existing
|
||||
Ctrl+C behavior) and the process to exit 0.
|
||||
|
||||
This is not a visual gate; the owner (or the lead) runs it opportunistically
|
||||
before considering the plan CLOSED.
|
||||
- 2026-09-07 narrow re-check: all twelve fix items CLOSED; MERGE-READY. Merged into the campaign branch at `8cb284d6f`; the unknown-verb pin re-targeted to the chat scroll after AD-124 (`074a1561b`). **Connected proof PASSED (lead, 2026-09-07):** `acdream-headless run --config <one idle session, +Acdream> --console` with scripted stdin — `/say hello` → the server's echo printed as `[Local] You: hello`; `/status` → `generation=1 position=unknown plugins=0 loaded` (idle policy has no movement controller); `/quit` → `[session] graceful logout confirmed`, exit 0. The MossTank half (`/vt start`) is owed with slice 2's autostart work. Follow-ups filed as #489 (SpewBox growth without a console; polish; and the JSON diagnostics stream interleaving with chat lines in console mode — the console should quiet or redirect it). Status: CLOSED.
|
||||
|
|
|
|||
|
|
@ -117,7 +117,18 @@ list boxes (owner live-client report, 2026-09-07), so a plugin `<menu>` now
|
|||
draws the flat VTank/Decal `HudCombo` box (list-matching fill/border, a
|
||||
left-aligned value, and a small ▾) by default; `style="retail"` opts back
|
||||
into the gold face for a panel that genuinely wants it. Any other value
|
||||
throws `FormatException` at `Build`.
|
||||
throws `FormatException` at `Build`. The plain style covers the WHOLE menu,
|
||||
closed and open: a follow-up owner report (still 2026-09-07 — "Drop down
|
||||
menus look horrible, there is also a checkmark on the text there") found the
|
||||
OPEN popup still drew retail's tan/orange gradient panel, its ornate gold
|
||||
scrollbar, and a baked checkmark glyph on the current entry even with
|
||||
`style="plain"`. The open popup now matches `<list>`'s own chrome too: a
|
||||
flat fill + 1px border, one row per entry in the list text color, the
|
||||
current entry filled like a list selection, the hovered entry a slightly
|
||||
lighter fill, and no checkmark; more entries than the row cap show a plain
|
||||
1px-bordered scrollbar track with a flat thumb, no DAT scrollbar art.
|
||||
`style="retail"` keeps the sprite popup (gradient panel, checkmark-bearing
|
||||
row art, ornate scrollbar) exactly as before, unchanged.
|
||||
|
||||
Common to every element via `ApplyCommon`: `name`/`id` (a stable control
|
||||
name), `visible` (literal `true`/`false` or a bound `bool` property),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue