diff --git a/src/AcDream.App/Rendering/Walk/WalkEvents.cs b/src/AcDream.App/Rendering/Walk/WalkEvents.cs
new file mode 100644
index 00000000..b3aa43c0
--- /dev/null
+++ b/src/AcDream.App/Rendering/Walk/WalkEvents.cs
@@ -0,0 +1,58 @@
+namespace AcDream.App.Rendering.Walk;
+
+///
+/// Campaign FW1 — the frame walk's observable event stream. These events
+/// mirror the FW0 oracle-trace vocabulary one-to-one
+/// (docs/research/2026-08-30-fw-walk-oracle/README.md): the conformance
+/// contract is that RetailFrameWalk, fed the same world state and
+/// camera pose as a capture, emits the identical sequence. Richer draw
+/// commands (mesh/state/stage tuples for the FW2 ordered stream) layer on
+/// later; this vocabulary stays the conformance surface.
+///
+public enum WalkEventKind
+{
+ /// LScape::draw @0x00506330 ran (outdoor root, or drawn
+ /// through an interior frame's exit views).
+ Landscape,
+
+ /// RenderDeviceD3D::DrawBuilding @0x0059f2a0 entered for
+ /// one building (identified by its position cell id).
+ Building,
+
+ /// PView::DrawInside @0x005a5860 entered — the frame is
+ /// rooted at this interior cell.
+ DrawInside,
+
+ /// PView::DrawCells @0x005a4840 entered with the flood's
+ /// cell list (far-to-near iteration happens inside; the event carries the
+ /// list in storage order, matching the oracle dump).
+ DrawCells,
+}
+
+public readonly record struct WalkEvent(
+ WalkEventKind Kind,
+ uint CellId,
+ int OutsideViewCount,
+ IReadOnlyList Cells)
+{
+ public static WalkEvent Landscape()
+ => new(WalkEventKind.Landscape, 0, 0, Array.Empty());
+
+ public static WalkEvent Building(uint positionCellId)
+ => new(WalkEventKind.Building, positionCellId, 0, Array.Empty());
+
+ public static WalkEvent DrawInside(uint cellId)
+ => new(WalkEventKind.DrawInside, cellId, 0, Array.Empty());
+
+ public static WalkEvent DrawCells(int outsideViewCount, IReadOnlyList cells)
+ => new(WalkEventKind.DrawCells, 0, outsideViewCount, cells);
+}
+
+///
+/// The sink a walk emits into. FW1 conformance tests collect events; FW2's
+/// production sink will additionally receive the ordered draw stream.
+///
+public interface IWalkEventSink
+{
+ void Emit(in WalkEvent walkEvent);
+}