Openable, lockable, and door
openable (openable, adjective). It has two states, open and closed,
both owned by the platform. startsOpen defaults to closed, and from Chord the
adjective composes closed with starts open on the composition line
seeding it open (ADR-231; chord-language.md). A required tool
composes too: openable with the crowbar. The biscuit tin and
sea chest are this trait at work. The remaining settings are
TypeScript territory today: canClose: false (one-way openable),
closeRequirements (a named obstacle), openSound/closeSound (ride
along in events), and open/closedDescription (swap description text).
lockable (lockable, optionally with the <key>, adjective).
It carries a locked/unlocked state, starting unlocked everywhere except on a door,
where a lockable door starts locked until the author says starts unlocked (ADR-234's one kind-scoped default); Chord also seeds starts locked on any composition line (ADR-231), as the reliquary does.
The key contract: with the <key> in Chord (keyId in TypeScript;
several keys via keyIds, TypeScript only), and a lock without keys is a
latch anyone can turn. lockSound / unlockSound decorate the events.
Two declared fields are inert today: autoLock (relock-on-close is
implemented but never invoked) and acceptsMasterKey (never read).
Don't build a puzzle on them; they are flagged as not yet functional.
door (a door, kind noun, since ADR-234). A door is an entity that
is the connection, but the connection is written on the room's exit
line, not the door's block: down to the Cellar through the cellar door.
The reverse exit is inferred (opposite direction, no far-room line
needed; a mirrored line is legal but must agree exactly), and the door
block itself is pure declaration: a door composes scenery and openable
automatically, starting closed (starts open overrides) and, when
lockable, locked (starts unlocked overrides). The loader wires
everything through the one platform path (connectRooms with a door id,
ADR-237), so going refuses door_closed / door_locked with no
story code at all, and the door answers from both of its rooms,
ADR-238's two-sided presence, without leaking the far room.
The author writes:
create the Landing
a room
down to the Cellar through the cellar door
A stone landing at the top of a worn stair.
create the Cellar
a room
Casks and cobwebs, and a smell of wet earth.
create the cellar door
a door, lockable with the iron key
Oak planks strapped with iron, set flush in the floor.
create the iron key
aka key
A heavy iron key, cold in the hand.
create the player
starts in the Landing
carries the iron key
The player sees:
> down
The cellar door is locked.
> unlock the cellar door with the iron key
You unlock the cellar door with the iron key.
> down
The cellar door is closed.
> open the cellar door
You open the cellar door.
> down
Cellar
Casks and cobwebs, and a smell of wet earth.
> close the cellar door
You close the cellar door.
One exit line and one declaration buy the whole protocol: the lockable
door starts locked by the kind-scoped default, going refuses
door_locked then door_closed with no story code, the standard
lock-and-key actions work on the door itself, and the final command
closes the same door from its far side, demonstrating its two-sided presence.
through never creates a door, and the compiler refuses what it can't
answer, each with its own diagnostic: a declared door that no exit line
connects, a placement line on a door block (its location IS its room
pair), through naming a non-door, and conflicting room pairs.
Gaps: under the trait, room1, room2, bidirectional: one-way doors
(bidirectional: false, traversing room1 → room2 only) are
TypeScript-only, and the , one-way exit-line modifier is reserved but a
parse error today.