diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md
index 443bc4c..3c0ef0f 100644
--- a/docs/PROJECT-STRUCTURE.md
+++ b/docs/PROJECT-STRUCTURE.md
@@ -169,7 +169,9 @@ simple hosts. `engine/Age.Engine/Hosting/IMovieHost.cs` owns ordinary, positione
and modal movie playback contracts. `IHost` inherits all four focused contracts; their required and default
behavior is unchanged. `engine/Age.Engine/Hosting/IGraphicsHost.cs` owns mutable surfaces, retained-range/frame
presentation, transitions, texture lifecycle, pixel capture/replace, draw, and size contracts; `IHost` also
-inherits this focused surface without changing existing hosts. The neutral
+inherits this focused surface without changing existing hosts. `engine/Age.Engine/Hosting/IInputHost.cs` owns
+modal fullwidth entry, ADV waits/callback servicing, input clock, cursor resources, and skip-state interaction;
+its required wait and overload/default chains remain unchanged. The neutral
`engine/Age.Engine/Model/MovieMaskTransitionRequest.cs` record carries movie mask work between the VM, host, and
retained graphics without making `Model` depend on `Hosting`.
`engine/Age.Engine/Model/RgbaImage.cs` owns the format-independent packed RGBA image transported between SYS4
diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md
index e55b3c5..8a39569 100644
--- a/docs/remake-architecture-and-roadmap.md
+++ b/docs/remake-architecture-and-roadmap.md
@@ -798,6 +798,12 @@ do not mix mechanical moves with semantic changes.
imported `Model`, so no call sites changed; surface request shape, enum values, and graphics behavior remain
unchanged. Runtime validation remains green.
+ The eighth bounded contract slice introduced `IInputHost` for modal fullwidth entry, ADV wait overloads and
+ callback servicing, input clock, cursor resources, and skip-state interaction. `IHost` inherits the focused
+ contract; all fourteen members, the required base wait, overload/default chains, and existing host classes
+ remain behaviorally unchanged. Input transport records remain in their prior locations pending a separate
+ cleanup. Runtime validation remains green.
+
4. **Make the build graph express source ownership.** Stop linking production `.cs` files from `godot/` and
`tools/movie-corpus-gate/` into `Age.Engine.Tests`. Extract the platform-neutral frontend/movie/diagnostic
code into a small production project referenced by Godot, tests, and the corpus gate. Retain both existing
@@ -1158,9 +1164,8 @@ layer's rendering diverges from ADV; save layout.
## 8. Immediate next step
Continue step 3 of the **codebase consolidation** maintenance slice: clarify runtime contracts without changing
behavior or the aggregate host accepted by the VM. With diagnostic, lifecycle, audio, and movie contracts
-established beneath `IHost`, shared movie/image transport neutral, and the graphics contract now separated, move
-the input host contract next: modal text entry, ADV waits and callback servicing, input clock, cursor resources,
-and skip-state interaction. Preserve the existing overload/default chains and aggregate `IHost` entry point before
-separating the remaining ADV text/presentation contract.
+established beneath `IHost`, shared graphics transport neutral, and graphics/input contracts now separated, move
+the unchanged `FullwidthTextEditRequest`, `FullwidthTextEditResult`, and `AdvAutoWaitState` declarations from
+`Hosting` to `Model` next. Preserve all consumers before extracting the remaining ADV text/presentation contract.
Concrete playthrough blockers may still preempt this bounded maintenance work; the consolidation effort does
not replace Phase B gameplay validation or the open cross-platform gates.
diff --git a/engine/Age.Engine/Hosting/IHost.cs b/engine/Age.Engine/Hosting/IHost.cs
index b9b1822..dc7810b 100644
--- a/engine/Age.Engine/Hosting/IHost.cs
+++ b/engine/Age.Engine/Hosting/IHost.cs
@@ -27,11 +27,8 @@ public readonly record struct DiagnosticMessage(string Caption, string Text);
public readonly record struct FullwidthTextEditRequest(string CurrentText, string InitialText);
public readonly record struct FullwidthTextEditResult(bool Accepted, string Text);
-public interface IHost : IDiagnosticHost, ILifecycleHost, IAudioHost, IMovieHost, IGraphicsHost
+public interface IHost : IDiagnosticHost, ILifecycleHost, IAudioHost, IMovieHost, IGraphicsHost, IInputHost
{
- /// Present AGERc's modal full-width editor. Cancel preserves CurrentText.
- FullwidthTextEditResult EditFullwidthString(FullwidthTextEditRequest request)
- => new(false, request.CurrentText);
void ShowText(int offset, string text);
void ShowText(AdvLiveTextRun run, int glyphDelayMilliseconds)
=> ShowText(run.SourceOffset, run.Text);
@@ -89,33 +86,4 @@ public interface IHost : IDiagnosticHost, ILifecycleHost, IAudioHost, IMovieHost
void SetAdvPagePresentationSuspended(bool suspended) { }
void SetAdvPagePresentationSuspended(GfxState gfx, bool suspended)
=> SetAdvPagePresentationSuspended(suspended);
- void WaitForInput();
- void WaitForInput(int layoutSlot) => WaitForInput();
- // Interactive hosts service script callbacks on the VM thread while the enclosing ADV page remains
- // parked. The callback returns true while another queued input callback is ready to run.
- void WaitForInput(int layoutSlot, Func serviceInputCallback)
- {
- while (serviceInputCallback()) { }
- WaitForInput(layoutSlot);
- }
- void WaitForInput(int layoutSlot, Func serviceInputCallback,
- Func autoWaitState)
- => WaitForInput(layoutSlot, serviceInputCallback);
- void WakeInputCallbackService() { }
- void InputCallbackCompleted(GfxState gfx) { }
- // Generic AGE input-callback services (ops 0xcc/0xcd, 0xfb/0xff/0x100, 0x108).
- // Interactive hosts expose the same monotonic clock used by their frame scheduler.
- long InputClockMilliseconds => Environment.TickCount64;
- void SetCursorResource(long resourceId) { }
- void ClearCursorResource() { }
- // Native 0x1c7/0x1cc query two distinct ADV skip channels. Headless and non-interactive
- // hosts default to normal playback; the Godot host supplies the live interactive values.
- void SetMessageSkipActive(bool active) { }
- // Logical action 6 is the native hold-to-fast-forward channel. Keep it separate from the
- // persistent op-0x88 channel so releasing the key cannot turn off the user's Skip toggle.
- void SetPhysicalMessageSkipActive(bool active) { }
- bool IsMessageSkipActive => false;
- // Optional diagnostic override. Native ReadTextDB state is VM/profile-owned; interactive hosts
- // normally leave this false.
- bool IsAdvReadSkipActive => false;
}
diff --git a/engine/Age.Engine/Hosting/IInputHost.cs b/engine/Age.Engine/Hosting/IInputHost.cs
new file mode 100644
index 0000000..db03e0f
--- /dev/null
+++ b/engine/Age.Engine/Hosting/IInputHost.cs
@@ -0,0 +1,49 @@
+using Age.Engine.Model;
+
+namespace Age.Engine.Hosting;
+
+public interface IInputHost
+{
+ /// Present AGERc's modal full-width editor. Cancel preserves CurrentText.
+ FullwidthTextEditResult EditFullwidthString(FullwidthTextEditRequest request)
+ => new(false, request.CurrentText);
+
+ void WaitForInput();
+ void WaitForInput(int layoutSlot) => WaitForInput();
+
+ // Interactive hosts service script callbacks on the VM thread while the enclosing ADV page remains
+ // parked. The callback returns true while another queued input callback is ready to run.
+ void WaitForInput(int layoutSlot, Func serviceInputCallback)
+ {
+ while (serviceInputCallback()) { }
+ WaitForInput(layoutSlot);
+ }
+
+ void WaitForInput(int layoutSlot, Func serviceInputCallback,
+ Func autoWaitState)
+ => WaitForInput(layoutSlot, serviceInputCallback);
+
+ void WakeInputCallbackService() { }
+ void InputCallbackCompleted(GfxState gfx) { }
+
+ // Generic AGE input-callback services (ops 0xcc/0xcd, 0xfb/0xff/0x100, 0x108).
+ // Interactive hosts expose the same monotonic clock used by their frame scheduler.
+ long InputClockMilliseconds => Environment.TickCount64;
+
+ void SetCursorResource(long resourceId) { }
+ void ClearCursorResource() { }
+
+ // Native 0x1c7/0x1cc query two distinct ADV skip channels. Headless and non-interactive
+ // hosts default to normal playback; the Godot host supplies the live interactive values.
+ void SetMessageSkipActive(bool active) { }
+
+ // Logical action 6 is the native hold-to-fast-forward channel. Keep it separate from the
+ // persistent op-0x88 channel so releasing the key cannot turn off the user's Skip toggle.
+ void SetPhysicalMessageSkipActive(bool active) { }
+
+ bool IsMessageSkipActive => false;
+
+ // Optional diagnostic override. Native ReadTextDB state is VM/profile-owned; interactive hosts
+ // normally leave this false.
+ bool IsAdvReadSkipActive => false;
+}