Status: The initial helper control plane and location-scoped public Chat/Emote tasks are implemented. Optional, observed same-room startup teleport to an enabled point location is implemented; general movement and shared-map mutation remain out of scope. See the Helper Bots guide for runtime behavior and operator setup.
Run each BC account as an independently configured bot process. Keep one supervisor (the existing main bot) as the only authority that accepts operator requests and creates, assigns, cancels, and reviews helper tasks. Helpers execute only tasks assigned to their configured identity and may not delegate to peers or create child tasks.
Coordinate delegation through a durable, room-scoped MongoDB task queue in the
database already selected by RUNTIME_TARGET. Each process owns exactly one
BcClient, one Socket.IO connection, and that client’s existing outbound send
queue. Do not put multiple clients in the main process or add a second transport
path. Give each helper a dedicated BC account and deployment, but use the same
BC server, room, and validated room-map revision when it is intended to appear
on the same map.
This is a one-level hierarchy for the first release:
Operator / authorized room admin
│ requests
▼
Supervisor bot
┌─────────┼─────────┐
▼ ▼ ▼
Helper A Helper B Helper C
Helpers can ask the supervisor for clarification or escalation. They cannot assign work to another helper, widen their own permissions, or treat peer messages as instructions.
The implemented runtime has one statically composed BcClient and
BcApiPlane; BcClient owns login, room join-or-create, observations,
reconnection, and the 14-packet / 1,200 ms send queue. Its observations are
server-driven, and the BC protocol has no general request ID for many state
changes. See bot runtime architecture,
BC API plane, and
socket acknowledgement rules.
The map movement proposal documents the observed startup teleport slice and deferred graph movement. A helper may only use its operator-configured startup location; arbitrary task destinations are not accepted. A Mongo position record never proves that a character moved. Helper positioning is journaled and must be verified from BC observations.
parentAgentId is immutable for a helper.assignedAgentId matches its
own configured identity. It may update progress/outcome for that task only.These are application-level controls for trusted runtime code, not a defense against an operator with direct database or BC-account credentials. If helpers must be isolated from each other’s database writes even when a helper is compromised, replace direct queue access with a separately authenticated supervisor control service before deployment; do not claim Mongo collection credentials provide document-level authorization.
| Option | Appropriate use | Limitations | Decision |
|---|---|---|---|
| Durable Mongo task queue, polled by each helper | Assignments, claims, progress, cancellation requests, results, and recovery | Requires schema validation, compare-and-set transitions, leases, and target-database isolation | Recommended control plane. |
| Supervisor-mediated BC whisper or room chat | Human-visible notification that a task was assigned or completed | BC chat has no reliable receipt acknowledgement; whispers may be hidden, delayed, or unavailable; content is not an authorization token | Optional notification only. The durable task record remains authoritative. |
| Direct helper-to-helper BC messages | None in the initial release | Creates peer authority paths, duplicate command handling, ambiguous delivery, and no reliable request correlation | Prohibited for control. |
| In-process worker objects | Unit tests or later non-BC local work | They are not separate BC accounts and must not create sockets; a local worker cannot stand in for a helper identity | Not the helper deployment model. |
| External message broker or public HTTP API | A future fleet-scale control plane | Adds operational surface, credentials, dependencies, and another failure domain | Defer; no need is established for the initial fleet. |
The supervisor polls helper records and presents status through its existing authorized command surface. Helpers poll only their own assigned work. Poll intervals use bounded backoff and jitter; task claims use atomic compare-and-set updates so two processes cannot both claim the same queued task.
Each process uses the existing startup/configuration path with one dedicated BC
account, one BcClient, one BcApiPlane, one ActionCoordinator, and one
Mongo client for the fixed runtime target. A helper does not import or construct
another bot’s client, and no feature touches socket.emit.
flowchart LR
Operator[Authorized operator] --> Supervisor[Supervisor process]
Supervisor -->|validated task records| Queue[(Helper task control plane)]
HelperA[Helper A process] -->|claim / report| Queue
HelperB[Helper B process] -->|claim / report| Queue
Supervisor --> SClient[BcClient A<br/>one socket + local queue]
HelperA --> AClient[BcClient B<br/>one socket + local queue]
HelperB --> BClient[BcClient C<br/>one socket + local queue]
SClient -->|BC protocol| BC[Bondage Club server<br/>shared room and map]
AClient -->|BC protocol| BC
BClient -->|BC protocol| BC
SClient --> Journal[(prototypeActions)]
AClient --> Journal
BClient --> Journal
Queue -. same fixed runtime database .- Journal
The diagram’s separate queues are intentional: each process retains the existing local rate limiter and request serialization. Do not reinterpret a response observed by one connection as an acknowledgement for another connection’s action. A task result and an action journal entry are separate records; the action outcome is determined by the executor’s own supported BC response or observation.
The local BcClient limiter is not evidence of a documented fleet-wide BC
quota. Before enabling multiple accounts, qualify server limits with dedicated
test accounts. If aggregate room traffic needs a cap, implement an optional
fleet dispatch admission gate in the action path while retaining every local
queue. It must fail closed when the gate is unavailable, never dispatch by raw
Socket.IO, and never claim to enforce an undocumented server quota. Start with
low per-helper action budgets and staggered polling.
BcApiPlane.request() serialization and
action-level observed-state reconciliation inside each process. For BC
packets without request IDs, wait for that process’s valid response or
observation and report timed_out_unobserved when evidence is absent. Never
blindly retry a non-idempotent operation.All agents configured for one coordinated map use the same validated
roomKey, BC server, runtime target, and map configuration revision. Each
dedicated BC account joins that room as a separate character. The supervisor
may assign distinct logical locations to helpers, for example:
same BC room / same map revision
┌─────────────────────────────────────────┐
│ north station: Supervisor │
│ west entrance: Helper A │
│ garden point: Helper B │
└─────────────────────────────────────────┘
The BC room map (shared room objects/settings) is distinct from each character’s
map position/state. Never represent one helper’s position by overwriting room
map data. The bot’s latest valid BC sync remains authoritative; a task’s desired
location, a stored location label, or a heartbeat is not proof of arrival.
Logical location labels may map to BC coordinates only through the validated
map/movement subsystem. If the movement action or matching BC observation is
not available, report unsupported or unknown; do not simulate movement by
patching snapshots or writing an authoritative-looking position document.
For independent positions, helpers may execute separate per-character movement tasks once movement support is implemented and qualified. For paths or shared location capacity, the supervisor validates the same room-map revision and occupancy policy before assignment, then rechecks immediately before the movement action. If configuration changes during a task, stop and reconcile against the new observed state rather than silently completing a stale route.
Use collections in the currently selected fixed database only. Do not add a configurable database name or touch the source/legacy database.
prototypeHelperAgentsValidated, operator-managed records keyed by stable agentId:
| Field | Purpose |
|---|---|
agentId |
Stable logical process identity; never a BC credential. |
role |
supervisor or helper; exactly one supervisor per coordinated room. |
parentAgentId |
null for the supervisor; supervisor ID for every v1 helper. |
memberNumber |
Expected BC character identity, checked against the live account/room observation. |
roomKey, runtimeTarget |
Scope the agent to one map and the existing test/production database pairing. |
startLocationKey |
Optional enabled point location for observed same-room startup teleport only. |
capabilities |
Reviewed allowlist of task types and permitted target scope. |
enabled, maxActiveTasks |
Operator control and bounded concurrency; v1 default is one active task. |
No password, Mongo URI, token, chat text, or arbitrary BC payload belongs in this document. Credentials remain in the deployment’s existing secret store. An agent whose live BC member number, room, or target does not match its configuration fails closed and cannot claim work.
prototypeHelperTasksOne validated record per delegation. Suggested fields:
taskId, parentAgentId, rootTaskId, assignedAgentId, createdByMemberNumber,
taskType, roomKey, mapRevision, targets, arguments, requiredCapabilities,
resourceKeys, status, createdAt, updatedAt, notBefore, expiresAt,
lease { ownerAgentId, epoch, leaseUntil }, cancellationRequestedAt,
resultCode, resultSummary, actionOperationIds
The task contains typed, bounded, allowlisted arguments rather than arbitrary
BC packets or executable instructions. The envelope records the authority
chain and intended scope. resultSummary is a redacted status summary only;
it excludes secrets, private communications, full chat/whisper text, and
appearance payloads. Do not store private message text in task arguments;
delegated communications should use reviewed template identifiers or another
approved, non-sensitive reference. Detailed evidence and lifecycle transitions
remain in the existing prototypeActions journal owned by the executor.
Create and verify indexes before connecting agents:
{ taskId: 1 }.{ assignedAgentId: 1, status: 1, notBefore: 1, createdAt: 1 }.{ status: 1, "lease.leaseUntil": 1 }.expiresAt only after the
audit-retention policy is approved; never expire active or reconciliation
records.Use atomic filters on taskId, expected status, assigned agent, lease epoch,
and unexpired lease for each transition. Increment lease.epoch on a new claim
to detect stale writers. Lease fencing protects task-record updates; it cannot
retract a packet already sent to BC. Therefore an expired task that may have
dispatched is reconciled, not automatically replayed.
Task control status describes delegation; ActionOutcome describes one BC
operation. A successful task requires the result contract declared by its task
type:
succeeded: task goal verified by supported BC observation/read-back.completed_unconfirmed: task definition explicitly allows dispatch-only
completion, and the stored result says delivery/persistence is unconfirmed.failed: a supported, definitive rejection or validation failure.reconciliation_required: dispatch may have occurred but the goal is
unknown; no automatic retry.cancelled: cancellation was accepted before any irreversible dispatch.Do not convert a journal status, task transition, helper report, or chat echo into a stronger BC acknowledgement.
flowchart LR
Operator((Authorized operator))
Supervisor((Supervisor bot))
Helper((Helper bot))
BC((Bondage Club))
DB[(Task control plane<br/>fixed runtime database)]
subgraph System[Hierarchical helper-bot system]
UC1([Request delegated task])
UC2([Authorize and bound scope])
UC3([Assign to configured helper])
UC4([Claim and execute assigned task])
UC5([Report progress and outcome])
UC6([Cancel or escalate])
UC7([Reconcile uncertain BC action])
UC8([Observe helper presence and location])
UC9([Review task and action audit])
end
Operator --> UC1
Operator --> UC6
Operator --> UC9
Supervisor --> UC2
Supervisor --> UC3
Supervisor --> UC6
Supervisor --> UC7
Supervisor --> UC8
Supervisor --> UC9
Helper --> UC4
Helper --> UC5
Helper --> UC6
Helper --> UC8
BC --> UC4
BC --> UC7
UC3 --> DB
UC4 --> DB
UC5 --> DB
UC6 --> DB
UC7 --> DB
UC9 --> DB
stateDiagram-v2
[*] --> queued: supervisor validates and assigns
queued --> claimed: assigned helper atomically claims
queued --> cancelled: supervisor cancels before claim
queued --> expired: not claimed before expiry
claimed --> running: helper validates lease, identity, room and capability
claimed --> queued: helper exits before starting / lease safely released
claimed --> cancelled: cancelled before any dispatch
claimed --> expired: lease lost before start
running --> cancel_requested: supervisor requests cancellation
running --> awaiting_observation: BC packet dispatched; observation required
running --> succeeded: task contract has definitive synchronous evidence
running --> completed_unconfirmed: task contract permits dispatch-only result
running --> failed: definitive validation or BC failure
running --> cancelled: cancelled before dispatch
running --> reconciliation_required: cancellation or lease loss after possible dispatch
cancel_requested --> cancelled: helper confirms no dispatch occurred
cancel_requested --> reconciliation_required: dispatch may have occurred
awaiting_observation --> succeeded: matching supported observation/read-back
awaiting_observation --> failed: definitive failure observed
awaiting_observation --> reconciliation_required: timeout / process loss / ambiguous state
reconciliation_required --> succeeded: supervisor confirms desired observed state
reconciliation_required --> failed: supervisor confirms no effect or rejection
reconciliation_required --> queued: supervisor approves safe, idempotent retry
reconciliation_required --> cancelled: supervisor closes without replay
succeeded --> [*]
completed_unconfirmed --> [*]
failed --> [*]
cancelled --> [*]
expired --> [*]
completed_unconfirmed is terminal but explicitly weaker than succeeded.
Only the supervisor may resolve reconciliation_required; a helper cannot
declare its own ambiguous action safe to retry. Transition history should
preserve actor, timestamp, prior/new status, and a redacted reason.
stateDiagram-v2
[*] --> starting
starting --> connecting: config, DB and indexes validated
starting --> fenced: identity, room or runtime target mismatch
connecting --> ready: BC room sync and own member identity observed
connecting --> degraded: connection or control-plane unavailable
ready --> polling: poll interval reached
polling --> ready: no eligible task
polling --> claiming: eligible task found
claiming --> executing: atomic claim and scope checks pass
claiming --> ready: task already claimed or claim rejected
executing --> reporting: task action returns outcome
reporting --> ready: result stored under current lease
executing --> degraded: lease lost / connection lost / ambiguous dispatch
reporting --> fenced: stale lease cannot be renewed
ready --> degraded: BC or control-plane disconnect
degraded --> recovering: retry/backoff window reached
recovering --> ready: identity and room revalidated
recovering --> fenced: identity or scope no longer valid
starting --> draining: shutdown requested
connecting --> draining: shutdown requested
ready --> draining: shutdown requested
degraded --> draining: shutdown requested
fenced --> draining: shutdown requested
executing --> draining: shutdown requested
reporting --> draining: shutdown requested
draining --> reconciling: an action may have dispatched
draining --> [*]: no in-flight action remains
reconciling --> [*]: uncertain work persisted for supervisor review
fenced means the process stops claiming or dispatching; it is not an
authorization to mutate another helper’s lease. Reconnection requires fresh
account identity and room observations before task polling resumes.
sequenceDiagram
actor Admin as Authorized operator
participant Root as Supervisor bot
participant Queue as prototypeHelperTasks
participant Helper as Helper process
participant Action as Helper ActionCoordinator
participant BC as BC server
Admin->>Root: request task (command)
Root->>Root: authorize; check agent, room, capability, target and limits
Root->>Queue: insert validated queued task
Helper->>Queue: poll assigned tasks
Helper->>Queue: atomic claim (agent ID + status + lease epoch)
Queue-->>Helper: claimed task
Helper->>Helper: validate config identity, lease, map revision and task scope
Helper->>Action: execute dedicated action with taskId metadata
Action->>BC: guarded packet via this helper's BcClient queue
BC-->>Action: declared response and/or later state sync
Action-->>Helper: honest ActionOutcome
Helper->>Queue: CAS progress/final result under current lease
Helper-->>Root: status becomes visible on next poll
Root-->>Admin: report verified / unconfirmed / failed result
If the helper loses its lease or process connection after dispatch, it records
or leaves the task in reconciliation_required; it does not resend the packet.
If BC response semantics do not support a definitive result, the supervisor
reports that uncertainty without upgrading the outcome.
BcApiPlane.dispatch() nested inside an already journaled action.reconciliation_required rather than a fabricated success.| Risk | Mitigation |
|---|---|
| A helper interprets a task as authority to do more | Strict typed task catalog, static capability intersection, fixed agent identity, target allowlists, execution-time policy checks. |
| Two bots process one command or task | Supervisor-only command subscriptions; atomic task claim; assigned-agent filter and lease epoch. |
| Cross-socket action collision on shared BC state | One writer for shared room resources; per-character ownership; serialize same-target work; no lease-expiry assumption for already-sent packets. |
| Fleet traffic exceeds server limits | Retain each local BcClient limiter; test actual multi-account behavior; conservative budgets and optional fail-closed fleet admission. |
| Lost response causes duplicate non-idempotent action | Explicit uncertain state, observation/reconciliation before any supervisor-approved safe retry. |
| Helper reports a false location | BC sync is authority; task and heartbeat positions are telemetry only; don’t enable movement before the movement contract is qualified. |
| Helper credentials or logs leak private data | Separate secret-store entries; no secrets in Mongo task documents, action metadata, logs, or chat-based control. |
| Helper process is compromised | Separate BC accounts and deployments reduce blast radius; v1 assumes trusted runtime code. Use a separately authenticated control service if DB write isolation against a compromised helper is required. |
/bot helpers commands are the primary operator surface.
There is no additional supervisor-mediated BC notification channel.