veratown-prototype

Bondage Club API Plane

This document describes the prototype’s typed interface between Veratown intent, BC wire packets, and available acknowledgement/read-back signals. The exhaustive request/response classification is in BC-SOCKET-API-ACKNOWLEDGEMENTS.md.

For the end-to-end composition, plug-in order, action flow, and data-authority boundaries, see BOT-ARCHITECTURE.md.

Protocol baseline: Bondage-College 51df6acc01f8fa185079d8deebf591fe3642a148 (R133). The complete BC event and payload declarations are vendored in src/adapters/bc/bc-messages.d.ts; src/adapters/bc/bc-messages-patch.d.ts supplies browser-only external aliases and precise map-configuration, tether, map-doodad-reference, asset-category, and color types. These declarations are type-only; no browser runtime or bc-bot implementation is included.

The 46-commit update since the previous pin adds four assets and changes metadata on 87 existing assets, including lock/color and eligibility metadata. The catalog contains 85 groups and 2,662 assets; unsupported callbacks and dynamic getters remain unexecuted and mutation stays guarded. The map catalog remains at 170 tiles and 372 objects and now retains BC’s CanBeTetherPoint flag. Wire declarations add tether state/configuration and a MapViewTether chat-message variant; this is not a Socket.IO event, so the 26/30 event maps and acknowledgement classifications remain unchanged. The prototype does not implement tether actions. See the 2026-11-03 compatibility review, the 2026-10-29 review, and the shared map contract.

Implementation Status

The Phase 1 appearance foundation adds BcAssetCatalog, BcAppearanceCodec, BcAppearancePrerequisiteEvaluator, BcLockCapabilityCatalog, feature-neutral appearance API contracts, and the shared BC adapter BcAppearanceAction. It uses guarded BcClient methods, not nested BcApiPlane.dispatch() calls, so each mutation has one journal owner. AppearanceItemSetService loads versioned database definitions on each call and uses the shared per-character sequence queue. Set key, revision and piece index are journaled with each mutation; partial and unconfirmed results remain explicit. The adapter evaluator implements pinned BC asset and selected-option prerequisites against observed character state; unavailable evidence remains unknown and fails closed. The composition root supplies its check to the feature-owned itemset service. This does not add a transport-independent prerequisite contract to core/. The shared mutation contract includes a JSON-safe craft snapshot for restoration workflows to preserve exact observed BC Craft metadata. The shower uses this only for supported catalogued clothing; itemsets and chat commands cannot provide the snapshot, and its contents are not journaled. Its optional per-piece callback runs inside that queue after each journaled outcome. An optional caller signal cancels pending observation waits and skips not-yet-dispatched pieces without bypassing the BC send queue. Already-emitted writes remain unconfirmed unless a matching BC observation arrives; cancelled queued pieces have failed journal outcomes, not synthetic observations. Location Entry Behavior consumes the core location-transition event contract and reads its strict policy from room-scoped prototypeGameConfig; it applies standard appearance itemsets without changing their lock configuration. Any configured timer lock and removeItemOnExpiry value are passed through to BC’s native appearance behavior. The feature does not persist application progress or manage expiry.

BcAppearanceAction also takes the Mongo-backed prototypeCharacterActionLeases per-room/member lease in production composition. The local queue remains, while the lease serializes the same target’s appearance mutations across supervisor, helper, and ABCL processes. Lease loss aborts the guarded write; lease records are coordination state, not action-journal outcomes or evidence that BC state changed.

BcApiPlane and the exhaustive BC_OUTGOING_CONTRACTS catalog are implemented in src/adapters/bc/api-plane.ts. The production composition root at src/app/start.ts constructs one plane using the shared BcClient and ActionCoordinator, then injects it into CommunicationPlugin. Friend reciprocity uses it for the OnlineFriends read-back. Other feature actions continue to use dedicated actions and guarded BcClient methods; the plane is not a replacement for those domain APIs. src/index.ts only launches the composition root. Unit tests cover the plane contract, and an integration test covers the communication plug-in through the real client adapter and a fake socket.

The optional ABCL feature uses a narrowly typed BC-mod extension dictionary adapter for ABCL:sync, ABCLMsg, and ECHO_INFO2. These mod-owned fields are not part of BC’s pinned standard ChatMessageDictionary union; they are validated at the adapter boundary and sent only through BcClient’s existing rate-limited ChatRoomChat queue. AbclModAction owns the journal record and stores only the fixed operation name and target, never the serialized mod payload. The extension has no direct BC acknowledgement; dispatch remains dispatched_unconfirmed, and a later ABCL mod response is bounded input for a new decision, not proof that an earlier command succeeded. Administrator ABCL tests request a new sync through the same action. A change-specific test uses a bounded, session-local response window and real thresholds; a matching later response remains loosely correlated, not a protocol acknowledgement.

The plane supports typed packet dispatch, raw BC event subscriptions, and requests waiting for one selected response event. Contracts distinguish listed progress and terminal-failure events; callers can classify success/failure variants within a shared response event, and may provide a current-state getter for already-satisfied checks. The caveats below describe the remaining limits.

BcClient also subscribes to ChatRoomSyncArousal and retains only a validated 0–100 progress projection on the current room character. Pose and relationship facts use the existing pose and full-character syncs; the snapshot exposes only owner/lover booleans, not relationship details. Validated title and reputation are projected from full character syncs. Appearance facts remain item-scoped: expression, lock presence, and removal timer are derived from the exact observed item property, without exposing lock-holder identity. Veratown credits conditions read the economy service’s available balance because BC excludes private Money from room syncs.

UML Map

classDiagram
    direction LR

    class ClientToServerEvents {
        <<BC wire interface>>
        +AccountLogin
        +AccountCreate
        +PasswordReset
        +PasswordResetProcess
        +AccountUpdate
        +AccountUpdateEmail
        +AccountQuery
        +AccountBeep
        +AccountOwnership
        +AccountLovership
        +AccountDifficulty
        +AccountDisconnect
        +ChatRoomSearch
        +ChatRoomCreate
        +ChatRoomJoin
        +ChatRoomLeave
        +ChatRoomChat
        +ChatRoomCharacterUpdate
        +ChatRoomCharacterExpressionUpdate
        +ChatRoomCharacterPoseUpdate
        +ChatRoomCharacterArousalUpdate
        +ChatRoomCharacterItemUpdate
        +ChatRoomCharacterMapDataUpdate
        +ChatRoomAdmin
        +ChatRoomAllowItem
        +ChatRoomGame
    }

    class ServerToClientEvents {
        <<BC wire interface>>
        +LoginResponse
        +CreationResponse
        +PasswordResetResponse
        +AccountQueryResult
        +AccountOwnership
        +AccountLovership
        +AccountBeep
        +ChatRoomSearchResult
        +ChatRoomCreateResponse
        +ChatRoomSearchResponse
        +ChatRoomMessage
        +ChatRoomGameResponse
        +ChatRoomSync
        +ChatRoomSyncCharacter
        +ChatRoomSyncMemberJoin
        +ChatRoomSyncMemberLeave
        +ChatRoomSyncRoomProperties
        +ChatRoomSyncReorderPlayers
        +ChatRoomSyncSingle
        +ChatRoomSyncExpression
        +ChatRoomSyncPose
        +ChatRoomSyncArousal
        +ChatRoomSyncItem
        +ChatRoomSyncMapData
        +ChatRoomUpdateResponse
        +ChatRoomAllowItem
        +ForceDisconnect
        +LoginQueue
        +ServerInfo
        +ServerMessage
    }

    class BcClient {
        <<transport adapter>>
        +start() Promise~void~
        +close() void
        +currentPlayer: BcCharacterSnapshot?
        +currentRoom: BcRoomSnapshot?
        +getMapObservation() currentConnectionMap?
        +setRoomCreationDefinition(definition?) void
        +isFriend(memberNumber) boolean
        +dispatchPacket~Event~(event, payload) Promise~void~
        +onServerEvent~Event~(event, listener) Unsubscribe
        +queryOnlineFriends(timeoutMs) Promise~BcOnlineFriend[]~
        +updateMapObject(position, style) Promise~void~
        +updateRoomSettings(settings) Promise~void~
        +updateOwnMapPosition(position) Promise~void~
        +getAppearanceObservation(memberNumber) BcAppearanceObservation?
        +getCharacterSnapshot(memberNumber) BcCharacterSnapshot?
        +queryItemAccess(memberNumber, timeoutMs, signal) Promise~BcItemAccess~
        +updateCharacterAppearance(expected, appearance, signal) Promise~void~
        +events: ready / characterEntered / room / roomObserved / roomUpdateResponse / mapPosition / message / transportState / failure
    }

    class BcClientConfig {
        <<interface>>
        +serverUrl: string
        +origin: string
        +username: string
        +password: string
        +room: string
        +roomDefinition?: BcRoomDefinition
        +readyTimeoutMs?: number
    }

    class BcSocketLike {
        <<transport port>>
        +on(event, listener)
        +off(event, listener)
        +emit(event, payload)
        +connect()
        +disconnect()
        +connected: boolean
    }

    class BcRoomDefinition {
        <<BC-derived type>>
        +Name
        +Description
        +Background
        +Space
        +Admin
        +Ban
        +Access
        +Visibility
        +Limit
        +BlockCategory
        +Game
        +Language
        +MapData
    }

    class BcRoomSnapshot {
        <<runtime projection>>
        +Name
        +Character: BcCharacterSnapshot[]
        +Admin
        +Whitelist
        +Ban
        +Description
        +Background
        +Limit
        +Game
        +Visibility
        +Access
        +BlockCategory
        +Language
        +Space
        +Custom
        +MapData
    }

    class BcCharacterSnapshot {
        <<runtime projection>>
        +MemberNumber
        +Name
        +Nickname
        +Title
        +Reputation
        +FriendList
        +FriendNames
        +MapData
        +Appearance
        +ActivePose
        +ArousalProgress
        +HasOwner
        +HasLover
    }

    class BcMapPosition {
        <<interface>>
        +X: number
        +Y: number
    }

    class BcChatRoomMessage {
        <<BC-derived type>>
        +Sender
        +Target
        +Type
        +Content
        +Dictionary
    }

    class CharacterCommunicationApi {
        <<feature API>>
        +whisper(memberNumber, text) ActionOutcome
        +motd(memberNumber, text) ActionOutcome
        +chat(text) ActionOutcome
        +chatRedacted(text) ActionOutcome
        +emote(text) ActionOutcome
        +beep(memberNumber, text) ActionOutcome
    }

    class HelperTaskNotifier {
        <<core wake-up port>>
        +notifyTaskAvailable(agentId) dispatched_or_unavailable
    }
    class HelperTaskWakeups {
        <<core subscription port>>
        +subscribeTaskAvailable(listener) Dispose
    }
    class SupervisorPositionObservationSession
    class HelperPositionObservationSession
    class HelperBotsPlugin
    class TrackedTasks {
        <<transport-neutral shutdown drain>>
        +track(promise)
        +drain()
    }
    SupervisorPositionObservationSession ..|> HelperTaskNotifier
    HelperPositionObservationSession ..|> HelperTaskWakeups
    HelperBotsPlugin --> HelperTaskNotifier : after durable enqueue
    HelperBotsPlugin --> HelperTaskWakeups : wake existing Mongo poller
    HelperBotsPlugin --> TrackedTasks : wait for accepted work before Mongo closes

    class FriendReciprocationAction {
        <<incoming-message action>>
        +handleMessage(message) Promise~ActionOutcome?~
        +resetConnectionState() void
    }

    class MotdService {
        <<incoming-presence action>>
        +onCharacterEntered(memberNumber) Promise~ActionOutcome?~
        +resetConnectionState() void
    }

    class SendRoomMessageAction {
        <<dispatch-only action>>
        +execute(type, content) ActionOutcome
        +executeRedacted(type, content) ActionOutcome
    }

    class SendBeepAction {
        <<dispatch-only action>>
        +execute(memberNumber, message) ActionOutcome
    }

    class BcApiPlane {
        <<public typed API>>
        -client: BcClient
        -coordinator: ActionCoordinator
        -pendingResponseEvents: Map
        +contract~Event~(event) BcEventContract
        +subscribe~Event~(event, listener) Unsubscribe
        +dispatch~Event~(event, payload, metadata) Promise~ActionOutcome~
        +request~Request, Response~(spec) Promise~BcRequestResult~
    }

    class BcEventContract {
        <<interface>>
        +model: AckModel
        +responseEvents: BcIncomingEventName[]
        +progressResponseEvents?: BcIncomingEventName[]
        +failureResponseEvents?: BcIncomingEventName[]
        +directResponseEvents?: BcIncomingEventName[]
        +correlation: string
        +limitation: string
    }

    class BcAcknowledgementModel {
        <<union>>
        direct_response
        conditional_response
        state_observation
        dispatch_only
    }

    class BcIncomingEventName {
        <<keyof>>
        names in ServerToClientEvents
    }

    class BcOutgoingEventName {
        <<keyof>>
        names in ClientToServerEvents
    }

    class BcIncomingPayload {
        <<mapped type>>
        payload selected by inbound event name
    }

    class BcOutgoingPayload {
        <<mapped type>>
        payload selected by outbound event name
    }

    class BcActionMetadata {
        <<interface>>
        +action?: string
        +target?: Record~string, number|string~
        +desired?: Record~string, unknown~
    }

    class BcRequestSpec {
        <<interface>>
        +requestEvent: BcOutgoingEventName
        +payload: BcOutgoingPayload~Request~
        +responseEvent: BcIncomingEventName
        +matches(response) boolean
        +current?() Response
        +classifyResponse?(response) Classification
        +timeoutMs: number
    }

    class BcRequestResult {
        <<interface>>
        +outcome: ActionOutcome
        +response?: BcIncomingPayload~Response~
    }

    class ObservationChannel {
        <<interface>>
        +current() Observation
        +subscribe(listener) Unsubscribe
    }

    class ObservedActionRequest {
        <<interface>>
        +action: string
        +target: Record
        +desired: Record
        +timeoutMs: number
        +signal?: AbortSignal
        +channel: ObservationChannel
        +matches(observation) boolean
        +evidenceForCurrent?(observation) Record
        +evidenceForObservation?(observation) Record
        +dispatch() Promise~void~
    }

    class DispatchOnlyActionRequest {
        <<interface>>
        +action: string
        +target: Record
        +desired: Record
        +dispatch() Promise~void~
        +verify?() Promise~Readback~
    }

    class ActionCoordinator {
        <<lifecycle>>
        +executeDispatchOnly(request) ActionOutcome
        +executeObserved(request) ActionOutcome
        +recordAlreadySatisfied(...) ActionOutcome
    }

    class CharacterAppearanceApi {
        <<shared contract>>
        +execute(memberNumber, mutation) Promise~ActionOutcome~
    }

    class AppearanceMutation {
        <<shared contract>>
        +craftSnapshot?: JSON-safe observed Craft metadata
    }

    CharacterAppearanceApi ..> AppearanceMutation

    class CharacterAppearanceSequenceApi {
        <<ordered shared contract>>
        +executeSequence(memberNumber, mutations, source, onPieceOutcome?, signal?) Promise~ActionOutcome[]~
    }

    class CharacterMutationLeasePort {
        <<cross-process core contract>>
        +acquire(memberNumber, signal?) Promise~LeaseHandle?~
    }

    class CharacterMutationLeaseHandle {
        +signal: AbortSignal
        +assertOwned()
        +release()
    }

    class ShowerAdminPort {
        <<transport-neutral core command contract>>
        +showSettings()
        +updateSettings(settings)
        +listSongs()
        +addSong(song)
        +editSong(id, song)
        +setSongEnabled(id, enabled)
    }

    class ShowerAdminService
    class WorldAdminCommandSuite

    class BcAppearanceAction {
        <<per-character journaled action>>
        +execute(memberNumber, mutation) Promise~ActionOutcome~
        +executeGuarded(memberNumber, mutation, beforeDispatch) Promise~ActionOutcome~
        +executeLock(memberNumber, request, secrets?) Promise~ActionOutcome~
        +executeSequence(memberNumber, mutations, source, onPieceOutcome?, signal?) Promise~ActionOutcome[]~
        +stop() void
    }

    class CharacterAppearanceLockApi {
        <<ephemeral credential port>>
        +executeLock(memberNumber, request, secrets?) Promise~ActionOutcome~
    }

    class AppearanceItemSetService {
        +validateDefinition(definition) AppearanceItemSet
        +applyDefinition(memberNumber, definition, options) Promise~Result~
    }

    class BcAppearancePrerequisiteEvaluator {
        <<pinned BC semantics>>
        +evaluateItem(character, group, name, configuration?, targetIsPlayer?) BcPrerequisiteEvaluation
        +evaluateList(value, character, target, targetIsPlayer?) BcPrerequisiteEvaluation
    }

    class BcPrerequisiteEvaluation {
        <<adapter result>>
        +status: satisfied | unsatisfied | unknown | unsupported
        +prerequisite?: string
    }

    class BcAppearanceCodec {
        <<headless adapter>>
        +plan(character, mutation, actor) AppearanceBundle
        +checkPrerequisites(character, group, name, configuration?, targetIsPlayer?) BcPrerequisiteEvaluation
        +effectiveProperties(item) Record
        +matches(observed, desired) boolean
    }

    class BcAssetCatalog {
        <<pinned metadata adapter>>
        +get(group, name) AssetDefinition?
        +list() AssetDefinition[]
        +classify(group, name) Classification
    }

    class BcLockCapabilityCatalog {
        <<all source locks; reviewed mutation policy>>
        +list() LockCapability[]
        +get(type) LockCapability?
    }

    class MongoCharacterMutationLease

    BcAppearanceAction ..|> CharacterAppearanceApi
    BcAppearanceAction ..|> CharacterAppearanceLockApi
    BcAppearanceAction ..|> CharacterAppearanceSequenceApi
    AppearanceItemSetService --> CharacterAppearanceSequenceApi
    AppearanceItemSetService ..> BcAppearanceCodec : prerequisite callback injected by composition root
    WorldAdminCommandSuite --> ShowerAdminPort
    ShowerAdminService ..|> ShowerAdminPort
    BcAppearanceAction --> ActionCoordinator
    BcAppearanceAction --> CharacterMutationLeasePort
    CharacterMutationLeasePort ..> CharacterMutationLeaseHandle
    MongoCharacterMutationLease ..|> CharacterMutationLeasePort
    BcAppearanceAction --> BcClient
    BcAppearanceAction --> BcAppearanceCodec
    BcAppearanceCodec --> BcAppearancePrerequisiteEvaluator
    BcAppearancePrerequisiteEvaluator ..> BcPrerequisiteEvaluation
    BcAppearanceCodec --> BcAssetCatalog
    BcAppearanceCodec --> BcLockCapabilityCatalog

    class ActionOutcome {
        <<interface>>
        +operationId: string
        +action: string
        +status: ActionStatus
        +requestedAt: number
        +completedAt: number
        +target: Record
        +desired: Record
        +evidence?: Record
        +error?: string
    }

    class ActionStatus {
        <<union>>
        requested
        dispatched
        acknowledged
        observed
        persisted_observed
        timed_out_unobserved
        persistence_not_observed
        dispatched_unconfirmed
        dispatch_unrecorded
        already_satisfied
        failed
    }

    class ActionTransition {
        <<interface>>
        +status: ActionStatus
        +at: number
        +evidence?: Record
        +error?: string
    }

    class ActionJournalDocument {
        <<interface>>
        +_id: string
        +action: string
        +target: Record
        +desired: Record
        +status: ActionStatus
        +requestedAt: number
        +updatedAt: number
        +createdAt: Date
        +expiresAt: Date
        +history: ActionTransition[]
    }

    class AttemptResult~T~ {
        <<discriminated union>>
        +ok: boolean
        +value?: T
        +error?: Error
    }

    class ActionJournal {
        <<port interface>>
        +start(record) Promise~void~
        +transition(operationId, transition) Promise~void~
    }

    class MongoActionJournal {
        <<Mongo adapter>>
        +initialize() Promise~void~
    }

    class SetMapObjectAction {
        <<dedicated action>>
        +execute(position, style) Promise~ActionOutcome~
    }

    class ApplyRoomSettingsAction {
        <<dedicated action>>
        +execute(roomName, revision, settings, botMemberNumber) Promise~ActionOutcome~
    }

    class SendWhisperAction {
        <<dedicated action>>
        +execute(memberNumber, text) Promise~ActionOutcome~
    }

    ClientToServerEvents --> BcApiPlane : typed outbound payloads
    ServerToClientEvents --> BcApiPlane : typed inbound payloads
    BcApiPlane --> BcOutgoingPayload : dispatch payload typing
    BcApiPlane --> BcIncomingPayload : response/listener typing
    BcEventContract --> BcAcknowledgementModel
    BcIncomingEventName --> BcIncomingPayload
    BcOutgoingEventName --> BcOutgoingPayload
    BcApiPlane --> BcClient : dispatch / subscribe
    BcClient --> BcSocketLike : owns transport
    BcClient --> BcClientConfig : configured by
    BcClient --> BcRoomSnapshot : maintains observed state
    BcRoomSnapshot --> BcCharacterSnapshot
    BcRoomDefinition --> ClientToServerEvents : room create payload
    BcChatRoomMessage --> ServerToClientEvents : message payload
    BcMapPosition --> BcCharacterSnapshot : map state
    CharacterCommunicationApi --> SendWhisperAction
    CharacterCommunicationApi --> SendRoomMessageAction
    CharacterCommunicationApi --> SendBeepAction
    SendWhisperAction --> BcClient : ChatRoomChat Hidden
    SendRoomMessageAction --> BcClient : ChatRoomChat
    SendBeepAction --> BcClient : AccountBeep
    MotdService --> CharacterCommunicationApi : characterEntered whisper
    FriendReciprocationAction --> BcClient : ChatRoomMessage incoming
    FriendReciprocationAction --> BcClient : AccountUpdate and hidden request
    FriendReciprocationAction --> BcApiPlane : OnlineFriends read-back
    SetMapObjectAction --> BcClient : ChatRoomAdmin map observation
    ApplyRoomSettingsAction --> BcClient : guarded ChatRoomAdmin room update
    ApplyRoomSettingsAction --> ActionCoordinator : journal room-settings reconciliation
    BcApiPlane --> ActionCoordinator : journal operation lifecycle
    CharacterCommunicationApi --> ActionCoordinator : journal operation lifecycle
    MotdService --> ActionCoordinator : journal operation lifecycle
    FriendReciprocationAction --> ActionCoordinator : journal operation lifecycle
    ActionCoordinator --> ActionJournal : persist transitions
    ActionCoordinator --> DispatchOnlyActionRequest
    ActionCoordinator --> ObservedActionRequest
    ObservedActionRequest --> ObservationChannel
    ActionCoordinator --> AttemptResult
    ActionCoordinator --> ActionTransition
    ActionOutcome --> ActionStatus
    MongoActionJournal ..|> ActionJournal
    ActionJournalDocument --> ActionTransition
    SetMapObjectAction --> ActionCoordinator
    ApplyRoomSettingsAction --> ActionCoordinator
    SendWhisperAction --> ActionCoordinator

getMapObservation() clones only the map of the configured room when the current connection is synchronized. World map saves reject disconnected/stale state and never substitute Mongo data. setRoomCreationDefinition() changes only the template used after CannotFindRoom; clearing it disables creation after a failed database reload. Neither API emits a BC mutation or changes observed snapshots, so saving a map has a Mongo persistence result, not a BC acknowledgement.

Feature Plug-in UML

Built-in features are statically assembled by src/app/start.ts; src/index.ts is only the process entrypoint. Plug-ins are not dynamically loaded from files or packages. The host gives each feature a lifecycle, while feature code continues to use the existing action/adapter/contract APIs. The composition root injects BcApiPlane into CommunicationPlugin for the OnlineFriends read-back; other plug-ins continue using the existing domain actions and client events.

classDiagram
    direction LR

    class BotPlugin {
        <<lifecycle contract>>
        +id: string
        +start()
        +stop()
    }

    class BotPluginHost {
        -plugins: BotPlugin[]
        -startedPlugins: BotPlugin[]
        +start() Promise~void~
        +stop() Promise~void~
    }

    class BotCommandParser {
        <<command registry>>
        +register(name, description, handler, helpDescriptors?) Dispose
        +handleMessage(input: BotCommandInput) Promise~boolean~
    }

    class BcCommandParser {
        <<BC protocol adapter>>
        +handleMessage(message: BcChatRoomMessage) Promise~boolean~
    }

    class KeyedDeadlines {
        <<transport-neutral scheduler>>
        +schedule(key, delayMs, callback)
        +cancel(key)
        +has(key) boolean
        +clear()
    }

    class BotCommandContext {
        +command
        +args
        +alias?
    }

    class CommandAliasResolver {
        <<single-pass fallback>>
        +resolve(parts, commandPaths, descriptors)
        +availableAliases(commandPaths, descriptors)
    }

    class GameConfigRuntime {
        +getForRoom(roomKey, key)
        +current()
    }

    class CommandHelpDescriptor {
        <<reviewed capability metadata>>
        +id
        +commandPath
        +featureId
        +scopePath
        +summary
        +access policy
        +feature availability policy
        +capability availability policy
    }

    class CommandHelpCatalog {
        <<disposable live catalog>>
        +register(descriptors) Dispose
        +snapshot() CommandHelpSnapshot
        +validate()
    }

    class ScopedHelpService {
        <<read-only private help>>
        +request(sender, scope, channel)
    }

    class HelpContextPort {
        <<observed requester context>>
        +current(sender, channel)
        +isCurrent(context)
    }

    class CommandSuite {
        <<command registration contract>>
        +register() Dispose
    }

    class CommandRouterPlugin {
        <<transport bridge>>
        +start() void
        +stop() void
    }

    class AppearancePlugin {
        <<qualification feature plugin>>
        +start() void
        +stop() void
    }

    class AppearanceCommandSuite {
        <<qualification commands>>
        +register() Dispose
    }

    class DoorFeaturePlugin {
        <<feature plugin>>
        -runtime: VeratownMapRuntime
        -admin: DoorAdminService
        +start() Promise~void~
        +stop() Promise~void~
    }

    class LocationEntryBehaviorPlugin {
        <<database-driven feature>>
        +start() Promise~void~
        +stop() Promise~void~
    }

    class LocationEntryBehaviorService {
        +apply(memberNumber) Promise~LocationEntryBehaviorResult~
    }

    class LocationEntryBehaviorRepository {
        +loadConfig() Promise~LocationEntryBehaviorConfig~
        +loadItemSet(key) Promise~AppearanceItemSet~
    }

    class DoorCommandSuite {
        <<runtime commands>>
        +register() Dispose
    }

    class DoorAdminCommandSuite {
        <<admin commands>>
        +register() Dispose
    }

    class CommunicationPlugin {
        <<feature plugin>>
        -friendReciprocation: FriendReciprocationAction
        -motd: MotdService
        -apiPlane: BcApiPlane
        +start() void
        +stop() void
    }

    class CommunicationTestCommandSuite {
        <<diagnostic command>>
        +register() Dispose
    }

    class PrototypeStatusPlugin {
        <<command plugin>>
        +start() void
        +stop() void
    }

    class PrototypeCommandSuite {
        <<status command>>
        +register() Dispose
    }

    class HotSeatPlugin {
        <<room-scoped game plugin>>
        +start() void
        +stop() void
    }

    class HotSeatCommandSuite {
        <<private-only game commands>>
        +register() Dispose
    }

    class BcClient
    class BcApiPlane
    class CharacterCommunicationApi
    class ActionCoordinator
    class MongoActionJournal
    class VeratownMapRuntime
    class MapLocationEventBus {
        <<core event contract>>
        +locationEntered(memberNumber, location, firstObservation, cause?)
    }
    class LocationEntryFeatureHandler {
        <<generic core lifecycle and de-duplication>>
        +start()
        +stop()
    }
    class TileGeometry {
        <<transport-independent core contract>>
        +point
        +inclusive rectangle
        +canonical tile row spans
        +containsTile(geometry, position) boolean
    }
    class LocationAdministration {
        <<core admin port>>
        +find(key) location with optional region
        +create(draft with optional region)
        +update(key, edit with region/null)
    }
    class HelperTaskExecutor
    class PositionObservations {
        <<read-only core contract>>
        +getLatestObservation(memberNumber)
        +subscribe(listener) Dispose
    }
    class MapPositionObservationBus {
        <<bounded five-second process cache>>
        +publish(observation)
        +forget(memberNumber)
        +clear()
    }
    class PositionObservationSession {
        <<optional transport lifecycle>>
        +start() Promise~void~
        +stop() Promise~void~
    }
    class MapTeleportAction {
        +teleport(roomKey, locationKey) Promise~ActionOutcome~
        +stop() Promise~void~
    }
    class AppearanceItemSetService
    class BcAppearanceAction
    class DoorAdminService
    class SetMapObjectAction
    class FriendReciprocationAction
    class MotdService
    class MapRuntimeRepository {
        <<Mongo module>>
        +loadMapRuntimeConfiguration(db, roomKey) Promise~Configuration~
        +watchMapRuntimeConfiguration(db, reload, onError) Dispose
    }

    class EconomyPort {
        <<core shared capability>>
        +balance(memberNumber)
        +grantOnce(operationId, memberNumber, amount, reason, maximumAvailableCredits?)
        +reserve(operationId, memberNumber, amount, sessionId)
        +settle(operationId, reservationId, result)
        +refund(operationId, reservationId)
        +forfeitAvailable(operationId, memberNumber)
    }

    class EconomyService
    class EconomyRepository {
        <<feature persistence port>>
        +initialize()
        +applyTransaction()
    }
    class MongoEconomyRepository
    class CasinoFeaturePlugin
    class AnonymousWhispererPlugin
    class WhispererCommandSuite
    class WhispererSubmissionHandler
    class WhispererService
    class HotSeatPlugin
    class HotSeatCommandSuite
    class HotSeatService

    EconomyService ..|> EconomyPort
    EconomyService --> EconomyRepository
    MongoEconomyRepository ..|> EconomyRepository
    CasinoFeaturePlugin --> EconomyPort : injected by start.ts
    AnonymousWhispererPlugin ..|> BotPlugin
    AnonymousWhispererPlugin --> WhispererCommandSuite : owns
    AnonymousWhispererPlugin --> WhispererSubmissionHandler : owns redacted input
    AnonymousWhispererPlugin --> WhispererService
    AnonymousWhispererPlugin --> BcClient : message and presence lifecycle
    WhispererService --> EconomyPort : injected by start.ts
    WhispererService --> CharacterCommunicationApi : injected communication port
    HotSeatPlugin ..|> BotPlugin
    HotSeatPlugin --> EconomyPort : injected by start.ts
    HotSeatPlugin --> HotSeatService
    HotSeatPlugin --> HotSeatCommandSuite
    HotSeatCommandSuite --> BotCommandParser

    BotPluginHost o-- BotPlugin : owns lifecycle
    DoorFeaturePlugin ..|> BotPlugin
    VeratownMapRuntime --> KeyedDeadlines : safe BC map deadlines
    LocationEntryBehaviorPlugin ..|> BotPlugin
    CommunicationPlugin ..|> BotPlugin
    PrototypeStatusPlugin ..|> BotPlugin
    AppearancePlugin ..|> BotPlugin
    HotSeatPlugin ..|> BotPlugin
    CommandRouterPlugin ..|> BotPlugin
    DoorCommandSuite ..|> CommandSuite
    DoorAdminCommandSuite ..|> CommandSuite
    CommunicationTestCommandSuite ..|> CommandSuite
    PrototypeCommandSuite ..|> CommandSuite
    AppearanceCommandSuite ..|> CommandSuite
    WhispererCommandSuite ..|> CommandSuite
    WhispererCommandSuite --> WhispererService : game actions
    DoorFeaturePlugin --> DoorCommandSuite : owns
    DoorFeaturePlugin --> DoorAdminCommandSuite : owns
    CommunicationPlugin --> CommunicationTestCommandSuite : owns
    PrototypeStatusPlugin --> PrototypeCommandSuite : owns
    AppearancePlugin --> AppearanceCommandSuite : registers qualification commands
    WhispererCommandSuite --> BotCommandParser : registers game commands
    WhispererSubmissionHandler --> CharacterCommunicationApi : private replies
    WhispererSubmissionHandler --> WhispererService : consented ephemeral input
    DoorCommandSuite --> BotCommandParser : registers door/code
    DoorAdminCommandSuite --> BotCommandParser : registers admin subcommands
    CommunicationTestCommandSuite --> BotCommandParser : registers testcomms
    PrototypeCommandSuite --> BotCommandParser : registers prototype
    AppearanceCommandSuite --> BotCommandParser : registers qualification commands
    CommandRouterPlugin --> BcCommandParser : passes BC messages
    BcCommandParser --|> BotCommandParser : dispatches normalized inputs
    CommandRouterPlugin --> BcClient : subscribes message
    BotCommandParser --> CommandHelpCatalog : registers/disposes descriptors
    BotCommandParser --> CommandAliasResolver : no-match fallback
    BotCommandParser --> ScopedHelpService : global/scoped help
    CommandAliasResolver --> GameConfigRuntime : reads room aliases by revision
    CommandAliasResolver --> CommandHelpCatalog : validates live targets
    ScopedHelpService --> CommandHelpCatalog : consistent snapshot
    ScopedHelpService --> HelpContextPort : membership/permission freshness
    ScopedHelpService --> CharacterCommunicationApi : private replies
    DoorFeaturePlugin --> BcClient : map/room events
    DoorFeaturePlugin --> VeratownMapRuntime
    VeratownMapRuntime --> TileGeometry : exact location and door membership
    HelperTaskExecutor --> TileGeometry : same location membership
    LocationAdministration --> TileGeometry : region contract
    DoorFeaturePlugin --> MapLocationEventBus : publishes transitions
    LocationEntryFeatureHandler --> MapLocationEventBus : subscribes to entries
    DoorFeaturePlugin --> MapPositionObservationBus : publishes BC coordinates/invalidation
    MapPositionObservationBus ..|> PositionObservations
    MapTeleportAction --> PositionObservations : optional fresh shared evidence
    MapTeleportAction --> ActionCoordinator
    MapTeleportAction --> BcClient : guarded position update
    PositionObservationSession --> MapPositionObservationBus : scoped live stream
    LocationEntryBehaviorPlugin --> MapLocationEventBus : subscribes; ignores snapshots
    LocationEntryBehaviorPlugin --> LocationEntryBehaviorService
    LocationEntryBehaviorPlugin --> LocationEntryBehaviorRepository
    LocationEntryBehaviorService --> LocationEntryBehaviorRepository : loads current revisions
    LocationEntryBehaviorService --> AppearanceItemSetService : applies selected standard set
    AppearanceItemSetService --> BcAppearanceAction : observed ordered mutations
    AppearancePlugin --> AppearanceItemSetService : applies itemsets
    AppearancePlugin --> BcAppearanceAction : single appearance mutations
    DoorAdminCommandSuite --> DoorAdminService
    DoorFeaturePlugin --> SetMapObjectAction
    DoorFeaturePlugin --> MapRuntimeRepository : reloads configuration
    CommunicationPlugin --> BcClient : message/presence events
    CommunicationPlugin --> BcApiPlane : OnlineFriends query
    CommunicationPlugin --> FriendReciprocationAction
    CommunicationPlugin --> MotdService
    CommunicationTestCommandSuite --> CharacterCommunicationApi
    PrototypeCommandSuite --> CharacterCommunicationApi
    SetMapObjectAction --> ActionCoordinator
    FriendReciprocationAction --> ActionCoordinator
    MotdService --> CharacterCommunicationApi
    ActionCoordinator --> MongoActionJournal

Interface Descriptions

Interface/type Responsibility
ClientToServerEvents Pinned BC declarations for all 26 client-originated packet names and payloads.
ServerToClientEvents Pinned BC declarations for all 30 server-originated event names and payloads.
BcIncomingPayload<Event> / BcOutgoingPayload<Event> Type-level lookup of the exact payload associated with a BC event name.
BcIncomingEventName / BcOutgoingEventName Keys of the pinned server-to-client and client-to-server event maps.
BcClient Sole production Socket.IO owner; manages connection/readiness, the global send queue, typed packet dispatch, and BC observations.
BcApiPlane Typed adapter for outbound contracts, raw inbound subscriptions, single-response requests, payload-summary redaction, and lifecycle recording. The app injects it into CommunicationPlugin for OnlineFriends read-back; dedicated feature actions remain in use for other operations.
BC_OUTGOING_CONTRACTS Exhaustive 26-event contract catalog describing acknowledgement model, candidate responses, direct responses, correlation, and limitations.
BotPlugin / BotPluginHost Lifecycle contract and static host; starts features in order, rolls back partial startup, and stops started features in reverse order.
CommandSuite Feature command-registration contract; register() returns a disposer owned by its plug-in lifecycle.
BotCommandParser Transport-neutral longest-prefix registry for validated BotCommandInput; registered paths win before single-pass alias resolution and scoped help.
BcCommandParser BC-specific Hidden /bot and ! Chat/Emote/targeted-Whisper validation and normalization; raw BC content does not enter the core registry.
CommandAliasResolver Core-only guarded parser for room-scoped aliases; reads command.aliases by game-config revision, rejects malformed/sensitive aliases, and ignores targets no longer present in the live help catalog.
CommandHelpDescriptor / CommandHelpCatalog Reviewed feature-owned capability metadata with explicit public/admin access, feature-wide enabled-state, and per-capability availability rules; registrations share command disposer lifetimes and reject duplicate or reserved paths.
ScopedHelpService / HelpContextPort Recomputes global/scoped help from server-observed requester context, hides all descriptors for disabled features, denies stale/unknown scopes, bounds and rate-limits private output, and rechecks context/catalog revisions before each chunk.
CommandRouterPlugin Connects BC message events to the command registry and owns that event subscription’s disposal.
DoorFeaturePlugin Owns keypad/map subscriptions, door runtime, admin service, configuration watcher, and door command-suite lifecycles.
DoorCommandSuite Registers runtime door/door open/door close and code commands.
DoorAdminCommandSuite Registers door CRUD, group, and access command paths and returns admin-service results privately.
TileGeometry / MapRegionDefinition Core-only exact tile membership for points, legacy inclusive rectangles and canonical tile row spans; map schemas validate persisted forms at the feature boundary.
LocationAdministration Core command port for room-admin location CRUD, including optional region definitions and explicit region clearing.
CommunicationPlugin Owns friend-reciprocation and MOTD event subscriptions plus the communication-test command-suite lifecycle.
EconomyPort / EconomyService / MongoEconomyRepository Shared credits capability: core contract, application policy, and economy-owned Mongo account/ledger persistence. The composition root constructs one service and injects it into economy consumers; it is not a plug-in or BC API-plane dependency.
CommunicationTestCommandSuite Validates admin test requests, invokes journaled channel actions, and returns per-channel outcomes privately.
PrototypeStatusPlugin / PrototypeCommandSuite Plug-in wrapper and suite for the prototype status command.
BcRoomMapData / BcRoomSettings BC-derived room map and full room-settings types.
BcMapPositionUpdate / BcRoomAdminRequest / BcRoomPropertiesUpdate / BcRoomUpdateResponse BC-derived payloads for map, room-admin updates, room-property sync, and update result signals.
BcAccountUpdate / BcAccountBeepRequest BC-derived account-update and beep request payloads.
BcOnlineFriend / BcOnlineFriendsQueryResponse Typed account-query result entries used for server read-back; this query includes online friends only.
BcCharacterMapData / BcAppearanceItem Validated feature-facing character position and appearance projections.
BcMsgIdDictionaryEntry BC message localization dictionary entry used by chat packets.
BcClientConfig Connection origin/server, dedicated account credentials, room name, and optional room-creation definition. Credentials are never journaled.
BcSocketLike Typed Socket.IO transport port owned by BcClient; it keeps features away from raw socket construction and the global rate-limit queue.
BcRoomDefinition Validated BC room settings used by join-or-create.
BcRoomSnapshot Latest BC-observed room settings/map and room roster; it is not proof that this client caused those values.
BcCharacterSnapshot Feature-facing character view including member number, appearance, optional map state, and friend-list snapshot.
BcMapPosition BC room-map coordinate used by location and map-object actions.
BcChatRoomMessage BC chat/hidden/emote/etc. packet payload. Generic action journaling stores its summary, not full message contents.
BcEventContract Per-outbound-event model, candidate response events, optional progress/failure event lists, direct-response subset, correlation hint, and protocol limitation. The catalog is exhaustive over keyof ClientToServerEvents; progress events cannot complete requests, and declared failure events produce failed outcomes.
BcAcknowledgementModel The four completion classes: direct response, conditional response, state observation, and dispatch only.
BcActionMetadata Safe operation name, target identity, and caller-provided desired-state summary. Do not place passwords, full chat text, appearance payloads, or private account data in metadata.
BcRequestSpec<Request, Response> Typed outbound packet, one selected inbound event, matcher, timeout, optional current-state getter, optional success/failure classifier, and safe metadata. Progress events are rejected as completion events; a classifier handles result variants sharing one event.
BcRequestResult<Response> Action lifecycle outcome plus the matched response payload when observed before timeout.
ObservationChannel<T> Supplies the latest known state and a disposable subscription to future observations for ActionCoordinator. BcApiPlane.request() adapts its optional current() provider to this channel; without one, the channel has no cached value.
ObservedActionRequest<T> Desired-state action specification; completes on a matching observation or timeout. Optional AbortSignal cancels waits; before dispatch it prevents emission, and after dispatch it remains timed_out_unobserved unless matching evidence already arrived. Optional evidence projectors add safe capture/provenance details without changing outcome semantics.
DispatchOnlyActionRequest One-way action specification; may optionally run a separate read-back verifier.
ActionOutcome Stable operation ID, lifecycle status, timing, target, desired-state summary, evidence, and optional failure detail.
ActionTransition One timestamped lifecycle transition persisted to the journal.
ActionJournal Persistence port for action start and status transitions.
ActionJournalDocument Full action record with desired state, target, lifecycle history, and 30-day expiry.
AttemptResult<T> Discriminated success/error result used at adapter boundaries instead of scattered exception handling.
ActionCoordinator Enforces lifecycle ordering, separates dispatch from acknowledgement/observation, and persists each transition through ActionJournal.
MongoActionJournal Mongo adapter writing prototypeActions; records use a TTL index for 30-day retention.
CharacterCommunicationApi Feature API for room chat, redacted one-shot chat, emotes, action/activity/status messages, targeted hidden messages, whispers/MOTD, and beeps; each delegates to a journaled action.
FriendReciprocationAction Consumes incoming hidden friend-request messages, dispatches reciprocal account/chat packets, and optionally verifies through OnlineFriends read-back.
MotdService Deduplicates MOTD whispers per entrant and connection state.
SendRoomMessageAction Journaled Chat, Emote, Action, Activity, Hidden, and Status ChatRoomChat variants, with optional target and localization dictionary. The sensitive-text variant replaces transport exceptions before journaling.
SendWhisperAction / SendBeepAction Journal dispatch attempts and return dispatched_unconfirmed; BC exposes no sender-side delivery receipt for these packets.
SetMapObjectAction Serializes map writes; resolves style plus optional BC Type to the exact object ID and accepts ChatRoomUpdateResponse: "Updated" as weak acknowledgement or a matching room snapshot as observed state. Neither provides request-ID-level causality.
ApplyRoomSettingsAction Serializes room-setting updates with map-admin updates and accepts only a matching inbound room sync as completion; a successful ChatRoomUpdateResponse is not sufficient.

BcClient remains the only owner of Socket.IO and the global 14-packet/1.2-second send queue. Its generic dispatchPacket and onServerEvent methods are the narrow typed transport ports. The API plane adds policy, serialization, metadata redaction, and lifecycle semantics; it does not open a second connection or bypass the queue.

Feature plug-ins use the shared client, typed action APIs, repositories, and lifecycle journal provided by the composition root. They must dispose every event subscription and command registration in stop(). New plug-ins are statically imported and listed in src/app/start.ts; dynamic code loading is intentionally not supported. The composition root constructs BcApiPlane once and injects it into CommunicationPlugin for the OnlineFriends read-back. Other plug-ins continue to use domain-specific actions and client events.

Do not create a second socket or duplicate the client’s connection lifecycle: BcClient.start() owns login, room join-or-create, and the corresponding state synchronization. The plane’s generic API complements this lifecycle; it does not replace it.

Existing feature actions use ActionCoordinator and client methods that apply domain-specific checks or transformations. BcApiPlane.dispatch() also journals through that coordinator. Calling dispatch() from inside an already-journaled action would create a second lifecycle record for one operation. Keep dedicated actions as the lifecycle owner, or redesign the transport boundary so exactly one layer journals each operation. Do not replace methods such as sendWhisper, sendBeep, or updateMapObject with generic packet dispatch without preserving their validation, formatting, and friend/map-state safeguards.

SetMapObjectAction uses the guarded BcClient.updateMapObject() method, which requires the synchronized configured room and current room-admin membership. ApplyRoomSettingsAction uses the guarded BcClient.updateRoomSettings() method and the same serialization queue as map-admin writes. It preserves the inbound map snapshot while sending settings through the normal BC queue. The action treats BC’s update response as a rejection signal only; it completes as observed only after a matching inbound room-settings sync. The supervisor runs this reconciliation after the initial room sync, using the latest prototypeRoomConfig revision. Admin-Map does not open a BC connection or mutate the live room.

Friend reciprocity intentionally creates two related records: the feature action records the friend-add/notification workflow, while the plane records the distinct OnlineFriends read-back request. The client still owns FriendList construction and updates its server-observed friend cache when that response arrives. Avoid issuing the same AccountQuery concurrently through another request helper; requests sharing AccountQueryResult are serialized by the plane.

request() accepts one selected response event and serializes concurrent requests waiting for that same event. It rejects events listed as progress, such as LoginQueue, as completion events. Events listed as terminal failures, such as ForceDisconnect for login, produce a failed action outcome. For success/failure variants on one event, provide classifyResponse; its matcher must accept the relevant variants so the classifier can distinguish them. A caller may provide current() for that response type: if the current value matches and is not classified as a failure, the plane records already_satisfied and does not dispatch. Without a provider, no cached current-state check is performed. Multi-step login and room readiness remain owned by BcClient, not by a single request() call.

API Usage

One-way dispatch

Use this when BC declares no reliable result, such as AccountUpdate, ChatRoomChat, AccountBeep, or AccountDifficulty.

const outcome = await api.dispatch(
    "AccountUpdate",
    { FriendList: [42] },
    {
        action: "bc.account.friend.add",
        target: { memberNumber: 42 },
        desired: { friend: true },
    },
);

A successful dispatch() resolves to dispatched_unconfirmed: the local Socket.IO queue emitted the packet. It does not mean BC stored the change. The API plane records only a redacted payload summary; credentials, chat text, and account values are not copied into action metadata.

Direct response

Use request() when the protocol defines a terminal response event. Its matcher must distinguish the intended response variant. Requests waiting on the same response event are serialized because BC packets do not include a generic request ID. The plane rejects catalogued progress events as completion events and records catalogued or caller-classified failures as failed.

const result = await api.request({
    action: "bc.account.online-friends.query",
    requestEvent: "AccountQuery",
    payload: { Query: "OnlineFriends" },
    responseEvent: "AccountQueryResult",
    matches: (response) => response.Query === "OnlineFriends",
    timeoutMs: 8_000,
});

A matched event listed in directResponseEvents returns outcome.status === "acknowledged", unless it is classified as a failure, which returns failed. A matching non-direct candidate event returns observed unless classified as a failure. Neither status proves causation because BC supplies no generic request ID. The typed response is included when received; a matching current value supplied by current() is returned with already_satisfied; timeout returns timed_out_unobserved.

Unsolicited event

Subscribe to any declared raw BC server event with the exact payload type. This is distinct from the higher-level BcClient events such as ready, mapPosition, and message. The returned disposer must be called when the feature is disabled or the connection owner shuts down.

const unsubscribe = api.subscribe("ChatRoomSyncPose", (pose) => {
    console.info(pose.MemberNumber, pose.Pose);
});

State-changing action

Use a dedicated action and an observed-state predicate for effects whose result appears later as a room/character sync. Matching inbound BC state is sufficient operational evidence even though its correlation is loose: no causal proof, independent helper, or second confirmation is required. Preserve target/desired-state matching and connection/freshness guards; dispatch alone remains unconfirmed and room observation does not imply account persistence. SetMapObjectAction serializes room map updates and listens for both the uncorrelated ChatRoomUpdateResponse: "Updated" result and room syncs. The former returns acknowledged; the latter returns observed with temporal_match_only evidence. Door expiry begins after either result; timeout begins no timer and requires reconciliation before retrying.

Acknowledgement Models And Restrictions

Model API surface Completion meaning Examples
direct_response request() Matching BC response event observed. Login, room join/create, search, AccountQuery, item permission.
conditional_response request() with a matcher/classifier, or subscribe() A matching terminal response arrived; caller classifies result variants sharing the event. Contract-listed progress events cannot be selected as completion. Ownership/lovership, room admin, game messages.
state_observation A dedicated observed action, or request() with a sync matcher and optional current-state getter Desired state appeared in a BC sync, or was already present in the supplied current value; causation may remain uncertain. Character, pose, expression, item, map changes.
dispatch_only dispatch() Local queue emitted the packet; server receipt, delivery, or persistence is unknown. AccountUpdate, chat, beep, leave, difficulty.

Implemented restrictions include no synthetic success for missing events, no generic retry of non-idempotent actions, no request() for contracts classified dispatch_only, rejection of listed progress events as completion, terminal failure classification, one pending operation per selected response event, optional current-state short-circuiting, and no secrets in journal summaries. The plane is integrated for the communication feature’s OnlineFriends read-back; other generic requests should be added only with an explicit response classifier where needed and a clear single-owner journaling design. Full packet pairing and caveats are listed in BC-SOCKET-API-ACKNOWLEDGEMENTS.md.