Guide4 min read2 sections

Garden ferry playtest

docs/GARDEN_FERRY.mdSource updated 2026-10-24
Markdown
On this page 2 sections

Run npm run dev:ferry, then open http://localhost:5180 in two browser profiles (or a normal and private window). This launches a separate relay on port 8788 with room storage in .data/garden-ferry.4. The existing beta on port 8787, its content, saved journeys and .data/hearth are retained.

You start at the lit landing in the Rain Garden. Wait for the wooden ferry, walk aboard with W, then release the keys. It pauses for four seconds at each end and takes ten seconds to cross. A second traveler can ride alongside you. Open the menu while riding, close the host's window, reload while aboard, or walk/jump into the shallow channel. The other rider and ferry should continue; the lawn beneath the water prevents a fall into the void. The house and sanctuary remain reachable from the garden.

For Coolify, create a separate application from codex/engine-foundations using the Docker Compose build pack and /compose.ferry.yaml. Assign a separate HTTPS domain to darkwind-ferry, port 8787, and set DW_ALLOWED_ORIGINS to that exact origin. Optionally set DW_ROOM_PASSWORD. Compose creates the independent garden-ferry-v4-room persistent volume. The build argument selects matching client content; the runtime setting selects matching relay content. No deployment is performed by this change.

The current playtest is garden-ferry.4. It uses new local save/session keys and a new room directory/Compose volume because the authored content changed. Version 1, 2 and 3 data remain on disk; automatic release migration is not implemented. An existing preview process must be restarted with npm run dev:ferry after updating the branch.

A lantern crate now waits along the west garden wall. Stand behind it and press E, walk forward to follow it, and repeat until it reaches the green plinth. The fourth push lights the lantern and changes the shared objective. A second traveler sees each accepted position. Stand at the opposite end to push it back; standing in front blocks a push. Close the host's window and rejoin to check that the position and light remain. The bounded sliding-prop contract is documented in SLIDING_PROPS.md.

Mara, the garden keeper, now walks the central path. Approach and press E to speak. Both travelers should see her stop; she stays while the conversation is open and resumes when it closes. Only one traveler speaks with her at a time. Closing the speaker's tab lets the conversation lease expire after at most 12 seconds. Host handoff does not reset the patrol. The scripted patrol contract describes the scope and lifecycle.

A traveling lantern now rests on a low stand beside the eastern landing. Take it with E, press I to open your satchel, then choose Carry lantern. Your friend should see it follow your hand as you walk. Stow it, reload, or close the host’s window to check shared continuity. The equipment contract records the current scope.

#Implemented contract

The relay persists a route start timestamp before admitting riders. It broadcasts elapsed route time at 10 Hz; elected-browser host handoff does not own or restart this clock. During a process lifetime, progression uses monotonic time. A relay restart uses the persisted origin and elapsed wall-clock time, including downtime. This assumes the server clock remains accurate across restarts; clock rollback recovery is not implemented.

Clients run native platform carry at their normal simulation rate. Network samples drive bounded forward phase correction; local platform movement stays inside the existing per-step sweep budget. A menu pauses input, while shared routes and riders continue. More than one second without a fresh route sample freezes the shared simulation until a new baseline can align it. On initial admission, place entry and reconnect, collision is rebuilt at the room phase, preserving an existing rider's local deck offset. Local saves keep phase and player position together.

A rider's collision sweep still protects that character. An obstructed rider cannot stop the platform for other clients: the platform continues and the obstructed character loses support. Solo routes retain their existing stop-on-obstruction behavior. This is a cooperative authored-route slice, not general replicated dynamic physics, server-validated character movement, or completed per-place authority. Network latency can produce small differences between clients' displayed phases. Motor-relative locomotion now keeps standing riders idle and shows a blended airborne pose when they jump. Temporary character models still need the planned final polish.

#Verification

npm run check, npm test, and npm run test:browser:ferry cover the implementation. The browser journey uses actual keyboard boarding, two independent browser contexts, native collision and rendering, host departure, pause, saved reload and safe dismount. Headless tests also cover obstructed riders, cross-place alignment, absent samples and a real relay process restart.

End of Garden ferry example