Sharpee
Chord / Standard Library / Containers & Openables / Openable, lockable, door

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.