docs(runtime): detail Linux multi-session host
Fix the portable host, scheduler, credential, shared-content, parity, and two-hour 30-session stress contracts before Slice K implementation begins. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0a5bc68e70
commit
7d1f88da61
2 changed files with 335 additions and 0 deletions
|
|
@ -1061,6 +1061,10 @@ host-parity tests after each move.
|
|||
|
||||
### Slice K — Linux headless and multi-session host
|
||||
|
||||
**Prepared execution plan:**
|
||||
[`2026-07-26-modern-runtime-slice-k.md`](2026-07-26-modern-runtime-slice-k.md).
|
||||
Implementation begins after Slice J's no-window closeout.
|
||||
|
||||
**Purpose:** Deliver efficient automated bots.
|
||||
|
||||
- Add Linux CI for Core, Core.Net, Content, Runtime, and Headless.
|
||||
|
|
|
|||
331
docs/plans/2026-07-26-modern-runtime-slice-k.md
Normal file
331
docs/plans/2026-07-26-modern-runtime-slice-k.md
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
# Modern runtime Slice K — Linux headless and multi-session host
|
||||
|
||||
**Status:** PREPARED — BEGINS AFTER SLICE J CLOSEOUT
|
||||
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice K
|
||||
**Authorization:** the user approved Slices F–L on 2026-07-24
|
||||
**Purpose:** run efficient automated clients on Linux without a display, GPU,
|
||||
audio device, hidden graphical process, or duplicated gameplay model, then
|
||||
prove 30 isolated sessions can share immutable content safely in one process.
|
||||
|
||||
## 1. Entry contract
|
||||
|
||||
Slice K starts only after J8 proves one direct `GameRuntime` can connect, enter
|
||||
world, receive authoritative state, move, use, fight/cast, portal, stop,
|
||||
reconnect, and converge without presentation assemblies.
|
||||
|
||||
K adds a production host over that same root. It does not move gameplay out of
|
||||
App again and does not create a bot-specific world model.
|
||||
|
||||
Dependency direction:
|
||||
|
||||
```text
|
||||
AcDream.Headless ──► AcDream.Runtime
|
||||
├──► AcDream.Core.Net
|
||||
├──► AcDream.Core
|
||||
├──► AcDream.Content
|
||||
└──► AcDream.Plugin.Abstractions
|
||||
|
||||
AcDream.Headless -X► AcDream.App / AcDream.UI.* / Silk.NET / OpenAL / ImGui
|
||||
```
|
||||
|
||||
The existing `AcDream.Cli` is asset/performance tooling with ImageSharp and DAT
|
||||
inspection dependencies. It is not renamed or expanded into the bot process.
|
||||
Create a dedicated `AcDream.Headless` executable and matching test project.
|
||||
|
||||
## 2. Initial platform inventory
|
||||
|
||||
The Runtime/Core.Net/Content closure is already mostly portable:
|
||||
|
||||
- `NetClient` uses the Windows `SIO_UDP_CONNRESET` socket control code only
|
||||
behind `OperatingSystem.IsWindows()`.
|
||||
- retail CP1252 encoding is provided by .NET code pages and is not a Windows
|
||||
OS dependency.
|
||||
- Runtime has no App, Silk, OpenAL, Arch, ImGui, or native-window reference.
|
||||
- `PakReader`, prepared collision, and immutable content contracts are
|
||||
presentation-independent.
|
||||
|
||||
Known work:
|
||||
|
||||
- no ordinary Linux build/test workflow exists;
|
||||
- no portable application-path/config owner exists;
|
||||
- graphical `RuntimeOptions` is App-owned and cannot be reused;
|
||||
- no production headless session host or typed bot command adapter exists;
|
||||
- `WorldSession` uses wall-clock deadlines and a fixed `Thread.Sleep(200)`
|
||||
during connect; scheduler/timing ownership needs an explicit audit;
|
||||
- a few Runtime/Core diagnostics still read environment variables at call
|
||||
sites;
|
||||
- `RuntimeRemotePhysicsUpdater` reads `DateTime.UtcNow`;
|
||||
- no multi-session mutable-static/source guard exists;
|
||||
- each `RuntimePhysicsState` currently creates a production
|
||||
`PhysicsDataCache`; shared immutable collision content versus per-session
|
||||
mutable cache/scratch must be made explicit;
|
||||
- no long-duration, 30-session process resource ledger exists.
|
||||
|
||||
## 3. Fixed host model
|
||||
|
||||
### Process owner
|
||||
|
||||
One `HeadlessProcessHost` owns:
|
||||
|
||||
- parsed immutable process options;
|
||||
- portable paths;
|
||||
- credential provider;
|
||||
- shared read-only DAT/prepared-content/collision sources;
|
||||
- one deterministic scheduler;
|
||||
- a registry of session hosts keyed by configured bot identity;
|
||||
- process cancellation and terminal diagnostics.
|
||||
|
||||
### Session owner
|
||||
|
||||
Each `HeadlessSessionHost` owns exactly:
|
||||
|
||||
- one `GameRuntime`;
|
||||
- one headless host lease;
|
||||
- one `LiveSessionHost` borrowing `GameRuntime.Session`;
|
||||
- one session command adapter implementing `IGameRuntimeCommands`;
|
||||
- one event subscription;
|
||||
- one bot policy/plugin instance;
|
||||
- per-session clock deadlines, sequence state, query scratch, and cancellation;
|
||||
- an exact retryable teardown cursor.
|
||||
|
||||
Sessions never share:
|
||||
|
||||
- `GameRuntime`, session/generation, local-player identity;
|
||||
- entity directory, object table, inventory, character, communication, action,
|
||||
movement, environment, transit, or event sequencer;
|
||||
- physics engine, moving bodies, transition scratch, projectile workset, or
|
||||
spatial queries;
|
||||
- mutable bot/plugin state;
|
||||
- credentials or logs containing credentials.
|
||||
|
||||
Sessions may share only immutable, thread-safe content:
|
||||
|
||||
- mapped `acdream.pak`;
|
||||
- read-only DAT databases behind a proven synchronization/reader contract;
|
||||
- immutable spell/skill/region tables;
|
||||
- immutable flat collision payloads.
|
||||
|
||||
Sharing a cache is permitted only when both its values and all bookkeeping
|
||||
mutations are thread-safe and session-neutral. Otherwise each session gets its
|
||||
own bounded cache over the shared immutable source.
|
||||
|
||||
## 4. Scheduler and timing contract
|
||||
|
||||
Use one monotonic process scheduler, not one busy loop or dedicated OS thread
|
||||
per bot.
|
||||
|
||||
- Each session publishes its next absolute deadline.
|
||||
- The scheduler waits until the earliest deadline or cancellation/wakeup.
|
||||
- Due sessions tick in stable configured order.
|
||||
- A bounded catch-up loop preserves elapsed-time behavior without a tick-count
|
||||
clock.
|
||||
- One slow session cannot make another session skip generation validation or
|
||||
accepted packet order.
|
||||
- Network receive may remain asynchronous, but every accepted inbound callback
|
||||
for a session is serialized through that session's Runtime turn.
|
||||
- Bot policy runs only after accepted inbound and simulation work for that
|
||||
turn, then emits typed commands synchronously.
|
||||
- No `Thread.Sleep` appears in the steady scheduler.
|
||||
|
||||
Wire cadence uses the instance clock and absolute elapsed deadlines. It never
|
||||
derives AutonomousPosition or MoveToState emission from “every N scheduler
|
||||
ticks.” The gate compares:
|
||||
|
||||
- AutonomousPosition inter-packet intervals;
|
||||
- MoveToState-on-change emission points;
|
||||
- use/combat/cast request order;
|
||||
- portal LoginComplete placement;
|
||||
|
||||
against the graphical host under the same deterministic scenario and a
|
||||
committed tolerance band.
|
||||
|
||||
## 5. Configuration, paths, and credentials
|
||||
|
||||
### Portable paths
|
||||
|
||||
Introduce a BCL-only `HeadlessPathSet`:
|
||||
|
||||
- Linux config: `$XDG_CONFIG_HOME/acdream`, else `~/.config/acdream`;
|
||||
- Linux data: `$XDG_DATA_HOME/acdream`, else `~/.local/share/acdream`;
|
||||
- Linux cache: `$XDG_CACHE_HOME/acdream`, else `~/.cache/acdream`;
|
||||
- Windows equivalents use the appropriate `Environment.SpecialFolder`;
|
||||
- explicit command-line paths override defaults;
|
||||
- all paths use `Path` APIs and are normalized before use.
|
||||
|
||||
### Configuration
|
||||
|
||||
Use a versioned JSON document with process settings plus session descriptors.
|
||||
Unknown properties are rejected by default. Each session has stable ID,
|
||||
endpoint, character selector, bot policy/plugin ID, and credential reference.
|
||||
Passwords never appear in the ordinary process config.
|
||||
|
||||
### Credentials
|
||||
|
||||
Support non-interactive providers suitable for local automation:
|
||||
|
||||
- environment-variable reference;
|
||||
- standard-input secret stream;
|
||||
- credential file with an explicit user-only-permission check on Linux.
|
||||
|
||||
Do not accept a plaintext password directly on the command line because process
|
||||
lists expose it. `ToString`, validation errors, logs, checkpoints, crash
|
||||
reports, and metrics contain only redacted credential IDs. Secrets are cleared
|
||||
from retained buffers when the session is disposed where the BCL representation
|
||||
allows it.
|
||||
|
||||
## 6. Bot surface
|
||||
|
||||
The public bot API is the existing stable seam:
|
||||
|
||||
- `IGameRuntimeView`;
|
||||
- `IGameRuntimeCommands`;
|
||||
- `IRuntimeEventSource`;
|
||||
- `RuntimeStateCheckpoint`;
|
||||
- generation and teardown acknowledgements.
|
||||
|
||||
Add a headless command adapter that maps those typed commands to the same
|
||||
Runtime owners and `WorldSession` sends used by the graphical adapter. It may
|
||||
provide host operations for target queries or decoded content lookups, but it
|
||||
must not expose mutable owner internals or reproduce App's selection/render
|
||||
world.
|
||||
|
||||
Minimum bot commands/events:
|
||||
|
||||
- movement and stance;
|
||||
- selection and target cycling;
|
||||
- use, appraisal, pickup, and looting;
|
||||
- combat mode, attack, and spell cast;
|
||||
- portal recalls;
|
||||
- inventory/shortcut/spellbook/character/social mutations;
|
||||
- chat and slash/server commands;
|
||||
- lifecycle, entity, inventory, chat, movement, portal, combat, and command
|
||||
outcomes.
|
||||
|
||||
Commands are generation-gated, synchronous at the Runtime seam, and inert as
|
||||
soon as session teardown begins.
|
||||
|
||||
## 7. Execution
|
||||
|
||||
### K0 — portability boundary and Linux CI
|
||||
|
||||
- Add `AcDream.Headless` and `AcDream.Headless.Tests`.
|
||||
- Add dependency-closure and loaded-assembly guards.
|
||||
- Add a normal GitHub Actions matrix for Windows and Ubuntu.
|
||||
- Build/test Core, Core.Net, Content, Runtime, Plugin.Abstractions, Headless,
|
||||
and their tests on Ubuntu with no App restore/build requirement.
|
||||
- Audit and correct portable socket, encoding-provider, file-map, path,
|
||||
executable, and cancellation behavior.
|
||||
- Promote surviving per-call Runtime diagnostics into instance/process
|
||||
diagnostic options where necessary.
|
||||
|
||||
Gate: Ubuntu restores, builds, tests, and runs `--help` plus a no-connect
|
||||
configuration validation without a graphical/audio/native-window dependency.
|
||||
|
||||
### K1 — portable config, credentials, and single-session CLI
|
||||
|
||||
- Implement portable paths and versioned strict config.
|
||||
- Implement redacted credential providers.
|
||||
- Compose one `GameRuntime`, one `LiveSessionHost`, one command adapter, one
|
||||
bot policy, and one teardown transaction.
|
||||
- Support start, graceful stop, reconnect, Ctrl+C/SIGTERM, and deterministic
|
||||
exit codes.
|
||||
- Emit structured credential-safe lifecycle/resource diagnostics.
|
||||
|
||||
Gate: one Windows and one Linux no-window client connect to local ACE, enter
|
||||
world, receive state, issue a harmless command, portal, reconnect, and exit
|
||||
cleanly without loading presentation assemblies.
|
||||
|
||||
### K2 — deterministic scheduler and bot API parity
|
||||
|
||||
- Add the absolute-deadline multi-session scheduler.
|
||||
- Serialize accepted per-session work while allowing sessions to progress
|
||||
independently.
|
||||
- Implement the complete typed bot command/event adapter.
|
||||
- Add deterministic graphical-versus-headless command, packet-order, and
|
||||
packet-timing traces.
|
||||
- Inject `TimeProvider`/monotonic clocks into remaining wall-clock Runtime
|
||||
reads that affect session behavior.
|
||||
|
||||
Gate: command/event traces match; AutonomousPosition cadence and
|
||||
MoveToState-on-change fall within the committed tolerance; idle sessions do
|
||||
not busy-loop.
|
||||
|
||||
### K3 — shared immutable content and isolation
|
||||
|
||||
- Introduce a process content owner with leases and exact last-user disposal.
|
||||
- Share mapped pak/DAT/immutable tables and flat collision payloads.
|
||||
- Retain per-session mutable physics/cache/scratch/state.
|
||||
- Audit all mutable statics, thread-local caches, random sources, diagnostics,
|
||||
and process-wide identifiers in the Headless dependency closure.
|
||||
- Add 1/5/10-session connect, activity, portal, disconnect, reconnect, and
|
||||
GUID-collision tests in one process.
|
||||
- Add plugin/bot-policy fault isolation.
|
||||
|
||||
Gate: equal server GUIDs in different sessions remain isolated; one failing
|
||||
session/plugin cannot corrupt or stop another; shared content opens once and
|
||||
disposes only after the final host acknowledgement.
|
||||
|
||||
### K4 — 30-session stress and closeout
|
||||
|
||||
- Measure a 1-session warm baseline, then 5, 10, and 30 sessions.
|
||||
- Before the final run, commit numeric ceilings derived from K3 evidence for:
|
||||
- incremental private bytes per established session;
|
||||
- managed heap per session;
|
||||
- idle CPU per session and whole process;
|
||||
- handles, threads, timers, and sockets;
|
||||
- scheduler wakeups and maximum lateness;
|
||||
- reconnect/teardown debt.
|
||||
- Run at least two hours with 30 local-server sessions, including recurring
|
||||
movement, use, combat/cast, chat, portal, death/reconnect, and randomized
|
||||
cancellation.
|
||||
- Require stable plateaus over the final hour, no per-session creep, no
|
||||
busy-loop CPU, no credential exposure, and terminal convergence for all 30.
|
||||
- Update architecture, milestones, roadmap, issues/divergences,
|
||||
AGENTS/CLAUDE, and durable memory.
|
||||
|
||||
Gate: 30 sessions in one Linux process for at least two hours, within committed
|
||||
numeric resource ceilings, with wire timing parity and clean teardown. Slice K
|
||||
then closes.
|
||||
|
||||
## 8. Adversarial matrix
|
||||
|
||||
- missing XDG variables, unwritable home, relative paths, symlinked config,
|
||||
and paths containing spaces/non-ASCII;
|
||||
- invalid JSON version, duplicate session IDs, duplicate credential refs, and
|
||||
unknown fields;
|
||||
- password present in exception, log, metric label, process arguments, or
|
||||
crash output;
|
||||
- SIGINT/SIGTERM during connect, world entry, portal, combat, and teardown;
|
||||
- DNS failure, IPv4-only/IPv6 answers, port exhaustion, socket reset, packet
|
||||
loss, and delayed receive;
|
||||
- one session floods inbound while another is idle;
|
||||
- one bot policy blocks, throws, recursively commands, or retains a stale
|
||||
generation;
|
||||
- same server GUID and local IDs across different roots;
|
||||
- shared content disposed while another session reads;
|
||||
- mutable cache/scratch shared accidentally between sessions;
|
||||
- scheduler clock jump, long pause, catch-up, cancellation wakeup, and
|
||||
deadline tie;
|
||||
- 30 simultaneous reconnects and staggered teardown;
|
||||
- stale outbound route after reconnect;
|
||||
- plugin load/unload failure;
|
||||
- no renderer/audio/window allocation or assembly load.
|
||||
|
||||
## 9. Non-goals
|
||||
|
||||
- No Linux graphical window, input, audio, or packaging; those are Slice L.
|
||||
- No GPU work.
|
||||
- No hidden/minimized App process.
|
||||
- No server-authoritative gameplay decisions on the client.
|
||||
- No bot-only shortcuts around the Runtime command/event seam.
|
||||
- No per-bot duplicate DAT/pak mapping unless evidence proves the shared reader
|
||||
cannot be made safe.
|
||||
- No service locator, global current-session singleton, or mutable static bot
|
||||
registry.
|
||||
|
||||
## 10. Commit and rollback discipline
|
||||
|
||||
Land K0–K4 as separate buildable commits. Record the full production SHA and
|
||||
exact `git revert <sha>` after each complete focused/Release/Linux/connected
|
||||
gate. No later sub-slice begins while the prior exact rollback is absent from
|
||||
this plan.
|
||||
Loading…
Add table
Add a link
Reference in a new issue