veratown-prototype

Appearance foundation (Phase 1)

This foundation provides source-driven BC asset discovery, validated room appearance observations, permission-aware planning, and journaled mutations. It does not own location-entry behavior or Release sessions.

Source and capability boundaries

The asset catalog is generated from Bondage-College at 51df6acc01f8fa185079d8deebf591fe3642a148 (R133), the same revision as the wire declarations. It records source hashes and schema version: 85 groups and 2,662 assets. BC now defines preset colors on individual layers; the extractor derives each asset’s default color vector from those layer presets, including shared color indices. The catalog preserves source data but does not execute rendering or item callbacks. Dynamic baseline and module-option getters are retained as unresolved metadata; the generator never executes upstream code. BarStool’s dynamic decal options remain visible in raw metadata but have no statically enumerated options, so headless mutation is rejected. TentacleSuit’s d0/d1 option meanings changed upstream; review operator-owned itemsets using these selectors and itemsets pinned to older catalog revisions before use. The update does not rewrite stored itemsets or widen lock support. The refreshed catalog adds four assets and changes metadata on 87 existing assets. LegSpreadDisplayStand now carries CanBackElbowTouch and CanKneelingSpread prerequisites, which the headless pose evaluator supports. Its Gender: "F" metadata follows BC’s room-space gender filtering; Veratown does not emulate that browser UI filter, so review any itemset using it against the target room before activation. See the 2026-11-03 compatibility review, the 2026-10-29 review, and the previous source review. Regenerate with:

BC_REPO=/path/to/Bondage-College npm run sync:bc-assets

Generation reads the pinned Git blobs, not the checkout’s potentially different HEAD. Asset/group definitions, extended configurations, display names, layer and color labels, and asset strings are data, not browser runtime imports. No socket endpoint provides the entire asset definition catalog. Definitions are refreshed by deliberate generation; character state and access are live BC observations.

Discovery is broader than mutation support. The codec has no item-name allowlist: it interprets catalogued basic, typed, modular and noarch assets. Itemsets select assets, colors, configurations, catalog-baseline configurable properties, craft text and lock types in the database, not in feature code. The BC lock capability catalog discovers all source-defined locks but enables only reviewed types; requests use semantic options and duration bounds derived from each lock’s MaxTimer. Mutation-critical properties cannot be injected through a set. Every prerequisite used by an asset or selected option in the pinned catalog has a headless evaluator. Unavailable evidence remains unknown and blocks mutation; unrecognized prerequisites, unresolved definitions, dependent removals, exclusive relationship permissions, and arbitrary property writes fail explicitly. Resolved typed and modular choices, including shock levels and the Futuristic Crate’s modules, use the same catalog-backed selectors in the appearance-set builder. Assets whose configuration metadata contains unresolved callbacks are not offered for itemset mutation; this includes some vibrating archetypes and the Modular Chastity Belt in the current pinned catalog. The catalog’s numeric Difficulty fields do not establish a validated range or item-level editing semantics, so itemsets do not accept an independent difficulty override.

Appearance mutations can carry a JSON-safe craftSnapshot when a restoration workflow needs to preserve the exact Craft metadata observed from BC. The shower uses this only for catalogued, otherwise supported clothing; crafted metadata is kept in bounded process memory and is not written to journals or persistence. Itemsets and chat commands do not accept this snapshot field.

Appearance comparison canonicalizes omitted typed or modular TypeRecord selections as BC’s default option (index zero). This handles equivalent minimized and expanded server representations; non-default selections and unrelated appearance properties remain part of the comparison.

The lock management proposal remains active for policy expansion; the implemented mutation contract is summarized below.

Required appearance/body groups cannot be removed; other body changes require full wardrobe access. Clothing and cosplay classifications can overlap. Owner/relationship and ambiguous locks are preserved. Removing another supported lock requires explicit allowNonOwnerLock authorization and BC interaction access; that flag is not a permission bypass.

Prerequisite evaluation mirrors the pinned Inventory.js semantics for body traits, pose availability, effects, occupied groups, exposure/blocking clothing, gag layering, equipment attributes, and the remaining catalogued inventory rules. The target’s current item is removed from its group before evaluation, as BC does. Body traits use observed BodyUpper and Pussy assets; pose and item facts use the observed character plus pinned catalog properties. The evaluator does not execute custom inventory scripts or browser callbacks.

NoErection means the character lacks the ForcedErection effect; arousal progress is not a substitute. The pinned AccessFullPenis implementation checks ItemArms against an empty list and therefore always passes; the headless evaluator preserves that source-specific behavior. RemotesAllowed passes for remote characters, but remains unknown for the bot’s own character because the local BlockRemoteSelf owner setting is not part of the observed snapshot. Unknown evidence is never treated as permission to apply an item.

Lock capabilities

BcLockCapabilityCatalog discovers all 18 lock assets declared by the pinned BC source. Discovery does not authorize mutation: only these seven types have a reviewed policy with no unresolved reasons. The admin itemset editor lists all discovered lock types, but disables types that are unsupported or not approved for shared itemsets, with the policy reason shown:

Lock type Supported semantic options Credential path Itemset support
MetalPadlock None None Yes
IntricatePadlock None None Yes
ExclusivePadlock None None Yes
TimerPadlock durationMs, removeItemOnExpiry None Yes
CombinationPadlock None Required ephemeral combination No
PasswordPadlock hint, removeOnUnlock Required ephemeral password No
TimerPasswordPadlock durationMs, removeItemOnExpiry, showTimer Optional ephemeral/generated password Yes

The adapter maps semantic options to BC properties and validates them against the selected type’s source metadata, including MaxTimer. Itemsets cannot hold credentials; timer-password itemsets may use a generated ephemeral password. The bot applies Intricate and Exclusive locks through the normal appearance mutation path; their specialized key and unlock behavior remains BC-owned. Live application qualification succeeded for both types; account-profile persistence remains subject to separate qualification. The appearance lock qualification command is a TimerPasswordPadlock convenience, not a generic command for selecting any of the seven types.

Owner-, lover-, family-, high-security-, safeword-, and specialized browser-hook locks other than IntricatePadlock and ExclusivePadlock remain unsupported. Arbitrary BC Property fields are not accepted, and the existence of a source asset never implies that its server-side authorization or inventory behavior is supported.

classDiagram
    class CharacterAppearanceApi {
        <<core port>>
        +execute(memberNumber, mutation)
    }
    class CharacterAppearanceLockApi {
        <<core port; ephemeral credentials>>
        +executeLock(memberNumber, request, secrets?)
    }
    class BcAppearanceAction {
        <<shared BC implementation>>
        +execute(memberNumber, mutation)
        +executeLock(memberNumber, request, secrets?)
    }
    class BcLockCapabilityCatalog {
        +list()
        +get(type)
    }
    class BcAppearanceCodec {
        +plan()
        +matches()
    }
    class ActionCoordinator
    class BcClient
    CharacterAppearanceApi <|.. BcAppearanceAction
    CharacterAppearanceLockApi <|.. BcAppearanceAction
    BcAppearanceAction --> BcLockCapabilityCatalog
    BcAppearanceAction --> BcAppearanceCodec
    BcAppearanceAction --> ActionCoordinator
    BcAppearanceAction --> BcClient
sequenceDiagram
    participant Feature
    participant API as CharacterAppearanceLockApi
    participant Action as BcAppearanceAction
    participant Catalog as BcLockCapabilityCatalog
    participant Codec as BcAppearanceCodec
    participant Journal as ActionCoordinator
    participant BC as BcClient / BC
    Feature->>API: executeLock(request, ephemeral secrets)
    API->>Action: validated request
    Action->>BC: check interaction permission and read current appearance
    Action->>Catalog: resolve reviewed type policy
    Catalog-->>Action: supported options or rejection
    Action->>Codec: plan against observed item and lock state
    Codec-->>Action: mutation plan or rejection
    Action->>Journal: record requested lifecycle
    Action->>BC: guarded appearance update
    BC-->>Action: later matching observation, if received
    Action->>Journal: observed / timed_out_unobserved / failed
    Action-->>Feature: ActionOutcome

Database-driven itemsets

The read-only runtime repository uses prototypeItemSets in the database selected by the fixed runtime target (veratown_prototype for production or veratown_prototype_test for testing). Runtime startup never writes this collection. The dry-run-first party-game provisioner seeds six validated, strip-only Staged Table stage itemsets if absent; all other itemsets, including emergency.release.clothing, remain operator-authored. An operator can create or edit definitions in that collection:

{
    "_id": "appearance.restraints",
    "schemaVersion": 1,
    "revision": 1,
    "enabled": true,
    "pieces": [
        {
            "group": "ItemArms",
            "asset": "HempRope",
            "configuration": { "kind": "typed", "option": "BoxTie" },
            "color": "#FF69B4",
            "lock": { "type": "TimerPasswordPadlock", "durationMs": 300000 }
        }
    ]
}

This is an example shared appearance itemset. Replacing the asset or selection requires changing only the document and incrementing its revision. The same service and codec load the new definition on the next application, without code changes or a restart. New upstream asset definitions still require deliberately regenerating the pinned catalog.

Definitions are versioned and validated: at most 128 pieces, one per group, nonempty group identifiers, JSON-safe properties, and timer durations no greater than the selected lock’s source-defined maximum. An itemset may also specify unique stripGroups from the pinned catalog. Before applying pieces, it removes the target’s currently observed item from each selected, removable group using the same permission, lock, dependency, observation, and journal checks as an explicit removal. Selected removals are ordered around catalog-declared dependencies; a dependency cycle is rejected before any mutation. A set can be strip-only, and selected groups with no current item need no removal mutation. Observed mod items missing from the pinned catalog remain in the strip sequence and are reported as skipped; other set operations continue.

Conditional variants

An itemset may carry up to 16 optional variants, each with a when condition group, optional stripGroups, and pieces. Conditions compare a supported fact to true or false with equals. Body facts hasBreasts, hasFlatChest, hasVagina and hasPenis come only from the target’s observed BC appearance (BodyUpper Small/Normal/Large/XLarge → breasts, FlatSmall/FlatMedium → flat chest; Pussy Pussy1-3 → vagina, Penis → penis). Missing or unrecognized body assets leave those facts unknown.

prerequisitesSatisfied:<Group>:<Asset> evaluates the selected BC asset’s catalogued prerequisites against the target’s latest observed character state. If that asset is included in the candidate variant, its selected configuration and option prerequisites are checked too. If required evidence or an extended configuration is unavailable, the condition is unknown and does not match. Evaluation follows BC’s replacement semantics: the current item in the candidate asset’s group is excluded before its prerequisites are checked. Only an observed character snapshot can select a variant; unavailable evidence never counts as satisfied. The Admin Web editor uses the same pinned catalog for the group and asset selectors; it does not connect to BC or preview live eligibility.

At apply time the first matching variant is merged over the base set: strip groups are unioned and the variant’s pieces replace base pieces in the same group. With no match the base set applies; a set with an empty base and no matching variant fails with no mutation. Every variant is validated with the same catalog, lock, and policy rules as the base. Features with stricter itemset policies (Trick-or-Treat costumes, favors, emergency release, and Staged Table appearance) reject sets that contain variants.

The admin set builder can filter the item picker to items BC restricts to breasts/vagina (“women’s”) or flat chest/penis (“men’s”) through the Body requirement filter, and edits these conditions in the Conditional variants section. The first matching variant applies; if none match, the base set applies, or an empty-base itemset fails without mutation.

Stripping does not bypass required-group safeguards or infer appearance from database state. The optional allowNonOwnerLockRemoval itemset flag requests removal of supported non-owner locks during stripping. It does not remove owner or relationship locks, resolve ambiguous lock metadata, or bypass BC permissions. The flag is shown in the admin itemset editor and defaults to off. Itemsets may specify removeItemOnExpiry and showTimer only where that lock’s reviewed capability allows it. An optional catalogRevision pins a definition to one catalog. BC’s empty-name body assets are valid asset identities, not item removals. Typed selection uses the option’s catalog name; modular selection supplies every module key and its zero-based option index. A noarch selection needs no options. properties can override fields declared by the extended baseline with matching types; unspecified fields inherit their catalog defaults. Effects, poses, permission selectors and lock internals are protected.

The separate configuration dashboard provides an authenticated, audited editor for creating and updating shared itemsets. Its selectors use a compact projection of the pinned BC catalog and omit assets with unresolved or unsupported configuration metadata. Search across the full catalog by group, category, asset name, or description, then select a blueprint and configure its supported options. The editor shows catalog-declared instance-property defaults and persists only customized overrides on each set item. It also supports typed and modular selections, color layers, reviewed itemset-compatible locks, and selecting groups to strip before applying pieces. Unsupported lock types remain visible but cannot be selected; credentials and unreviewed lock behavior are not accepted. From a location in the map workspace, operators can open or create its referenced appearance set directly in this editor. Strip-only sets are supported. It does not connect to BC or apply appearance changes. Set keys remain stable for location-entry behavior and shop references, and the dashboard does not expose deletion.

Each application snapshots one document revision and holds the per-character queue for the whole set. Strip removals run before apply pieces; catalog-declared dependencies order selected removals, while apply pieces follow document order. A denied, occupied or unsupported operation is reported as skipped; an emitted but unobserved write is unconfirmed. The result is completed, partial or failed across both strip and apply operations. No skipped operation is silently treated as success. Each mutation has its own journaled action and records the set key, revision and sequence index; there is no second journal owner wrapping those mutations. Shop favor sets remain apply-only. Worn mod items absent from the pinned catalog are preserved and ignored when checking catalog-defined blocking, enclosure and prerequisite metadata.

Set edits do not replace already occupied slots, retroactively alter active punishments or schedule releases. Clean up the previous feature session before applying a changed set. Automated session ownership and recovery remain later phases.

Actions and observations

stateDiagram-v2
    [*] --> Queued
    Queued --> PermissionQuery
    PermissionQuery --> Failed: denied / unknown / cancelled
    PermissionQuery --> Plan: allowed
    Plan --> Failed: unsupported / occupied / protected
    Plan --> AlreadySatisfied: current BC observation matches
    Plan --> JournalRequested
    JournalRequested --> Failed: journal unavailable
    JournalRequested --> Dispatch: current revision still matches
    Dispatch --> Failed: cancelled / stale revision
    Dispatch --> AwaitObservation
    AwaitObservation --> Observed: later matching full appearance
    AwaitObservation --> TimedOutUnobserved: no matching evidence
    AwaitObservation --> Failed: disconnect / leave / stop

One per-character queue serializes mutations across callers. All packets use the existing BcClient rate limiter. Full updates preserve the character ID, active pose, and unrelated appearance items. The input revision and cancellation signal are checked again immediately before the rate-limited queue emits the packet. There is no optimistic snapshot patch, strip-and-restore, or automatic retry. After an unconfirmed emitted write, later bundle writes are refused until a fresh authoritative BC appearance observation arrives. This prevents subsequent pieces from overwriting an earlier, possibly successful write using a stale snapshot. An emitted permission query that times out or is cancelled quarantines that member’s permission channel until reconnect, preventing a late response from being mistaken for the result of a retry.

BC observations come from full room/character syncs, single-item patches, pose updates, and expression updates. Connection epochs invalidate stale snapshots. Malformed appearance events are reported without logging their payloads. Omitted optional sync fields use the pinned browser’s documented defaults: empty active pose and permission lists, full wardrobe access off, and body cosplay blocking off. Malformed supplied fields are not treated as defaults. Omitted AssetFamily uses Female3DCG, matching the pinned browser’s CharacterCreatePlayer and CharacterLoadOnline initialization. Supplied malformed or unsupported families are rejected rather than replaced with that default.

The pinned BC browser source explicitly states that single-item updates are not echoed to the sender. Phase 1 therefore uses the full-character update path and requires a later, matching appearance observation. This is sufficient convergence evidence for bot operation: local inbound BC room updates do not need independent helper corroboration, a particular source member, or a second confirmation. Features such as shower consume the action result directly. Evidence retains temporal_match_only attribution, not causal acknowledgement or proof of account persistence. No inbound signal means timed_out_unobserved, not success.

Generic action journal metadata contains asset identifiers and catalog revision, plus non-secret lock intent; it never contains appearance bundles, colors, craft text, or credentials. Explicit password/combination locks take credentials on a separate ephemeral API path; they are never accepted from itemsets or chat. TimerPasswordPadlock itemsets use an internally generated ephemeral password. The feature-specific durable session/projection repositories belong to later phases.

Admin qualification

Qualification is disabled by default:

APPEARANCE_QUALIFICATION_ENABLED=false

Enable it explicitly only with the dedicated prototype account in a test room. Commands work through native /bot appearance ... and !appearance .... The sender must be a room administrator. Mutations can target only the sender’s own character, so administrators cannot use this qualification surface to modify another player.

/bot appearance inspect <memberId>
/bot appearance applyset <memberId> <itemsetKey>
/bot appearance apply <memberId> Cloth <simpleAsset>
/bot appearance apply <memberId> ItemArms HeavyYoke
/bot appearance apply <memberId> ItemFeet HeavySpreaderMetal typed Wide
/bot appearance apply <memberId> ItemDevices FuturisticCrate modular w=2,l=3,a=3,d=1,t=1,h=4
/bot appearance configure <memberId> ItemFeet HeavySpreaderMetal typed Narrow
/bot appearance lock <memberId> ItemArms HeavyYoke 5
/bot appearance remove <memberId> ItemArms HeavyYoke non-owner

There are no asset-specific command shortcuts. The direct lock command is a TimerPasswordPadlock qualification convenience (1-240 minutes); database itemsets select supported lock types and catalog-approved options generically. Occupied slots are not implicitly replaced. Keep manual BC/admin cleanup available: Phase 1 does not schedule feature release workers.

Before enabling automated features, live qualification must demonstrate:

  1. Full-update observation on this single connection, including client corrections.
  2. Supported clothing, typed spreader, modular crate, and timer-lock round trips.
  3. Preservation of unrelated items and owner locks.
  4. Honest timeout behavior when updates are not echoed.
  5. Cleanup after cancellation, disconnect, target departure, and shutdown.

Fake-socket tests cover the contracts but cannot establish real-server delivery, browser acceptance, or durable account persistence.

Automated two-account fault qualification

The opt-in harness uses TESTVERATOWN2 as the driver and subject, and the primary account as the system under test. It sends native self-targeted commands through the existing admin gates; each account has its own client and connection. It does not modify operator characters, grant room-admin access, seed itemsets, or enable separate Cage or Release automation. Location Entry Behavior has its own room-scoped opt-in configuration.

Implementation or offline testing does not authorize a live run. Obtain explicit approval before connecting the accounts or stopping the normal primary. Do not leave a browser logged into either bot account during testing.

  1. Copy .env.appearance-qualification.example to the git-ignored .env.appearance-qualification.local and supply the dedicated test password locally. Never commit credentials. The existing .env supplies primary credentials and the room; qualification must already be enabled.
  2. The dedicated account must have room-admin access and permit native interaction. Its Cloth slot must be empty; existing clothing is never replaced.
  3. After approval, stop the exact normal primary process. The harness refuses to start its primary if that account is still observed in the room.
  4. Run with Node 24:

     npm run qualify:appearance:faults -- --confirm-live --confirm-primary-stopped --primary-member 261407
    

The harness tests withheld observations and the unconfirmed-write guard, cancellation before and after emission, queued work after stop, actual socket disconnect, actual target departure, and SIGTERM shutdown. Observation withholding is local fault injection, not evidence that BC naturally failed to echo. One unlocked Cloth/BusinessSuit is used; there are no restraint or lock tests in this runner. Fault windows are bounded. After each scenario, the primary is restarted to obtain fresh room observations before cleanup; stale snapshots are never replayed.

Success requires exact update counts, terminal outcomes, observer cleanup, independent target-side room observations, appearance preservation, and terminal journal read-back in veratown_prototype.prototypeActions. The JSON summary is printed only after verification. A failed run exits nonzero and warns when clothing may remain; inspect the dedicated account before any retry. The harness closes its connections when finished. Restart the normal primary afterward, including after failure. It does not prove durable BC account persistence or browser rendering.

Offline validation:

npm run test:appearance-faults

Automated real-browser correction qualification

qualify:appearance:browser-correction runs TESTVERATOWN2 in a fresh, headless Chromium browser using the actual BC web application. It does not also start the headless Socket.IO driver for that account. The primary still runs the normal prototype composition with explicit, test-only socket interception.

Install the development dependencies and browser binary before offline validation:

npm install
npx playwright install chromium
npm run test:appearance-faults

The same dedicated-account prerequisites and separate live approval apply. Stop the normal primary and log out the dedicated account elsewhere before executing:

npm run qualify:appearance:browser-correction -- --confirm-live --confirm-primary-stopped --primary-member 261407

By default the browser opens ${BC_ORIGIN}/R133/BondageClub/, matching the selected source’s GameVersion. A successful run requires the browser to report that version. If the configured BC host uses a different game path, set BC_TEST_BROWSER_URL in the ignored local qualification environment file. The URL must remain HTTPS on the configured BC origin; the browser’s actual server endpoint must match BC_SERVER_URL before credentials are sent. Credentials are not traced, screenshotted or printed.

Appearance qualification runners derive this URL and exact version check from the shared source pin. Custom same-origin paths cannot bypass that check. The Bunny manual observation sequence records the actual displayed version; R132 and R133Beta2 observations do not qualify R133. This repin includes offline verification only and does not renew any live approval.

The test uses one unlocked Cloth/BusinessSuit and requires that slot initially empty. After the real browser receives the requested clothing, automation invokes BC’s native InventoryRemove, refreshes the real character canvas without requesting an account save, and calls native ChatRoomCharacterUpdate. This is a native browser wardrobe edit, not a claim that a simulated mouse click or an automatic asset-validation repair was tested.

To keep the action pending deterministically, the primary locally withholds the initial matching echo. It forwards, rather than fabricates, the actual corrected full appearance received from BC, bound to the dedicated character and its native source member. Passing requires:

The report includes the actual browser game version, pinned catalog revision, operation ID, corrective event and sequence. Offline tests exercise Chromium against a local fixture and cannot substitute for live BC qualification. Restore the normal primary after the run. Durable BC account persistence remains outside this qualification.

Live qualification record (2026-10-08)

The approved automated run completed successfully in Veratown2, using TESTVERATOWN2 (261575) as the dedicated subject and TestVeraTown (261407) as the primary. Only the dedicated subject’s unlocked Cloth/BusinessSuit was changed.

Check Result Appearance updates
Withheld-observation timeout Passed 1
Unconfirmed-write guard Passed 0 additional
Cancellation before write Passed 0
Cancellation after write Passed 1
Queued work after stop Passed 0 additional
Actual socket disconnect Passed 1
Actual target departure Passed 1
SIGTERM shutdown Passed 1

Timeout, cancellation, disconnect and departure produced the expected unsuccessful action outcomes, not false success. The runner verified observer cleanup, independent target-side observations, cleanup after fresh primary connections, restoration of the subject’s initial appearance, and terminal status read-back for all 13 action records in veratown_prototype.prototypeActions. The run exited with code 0. Both harness connections closed, and the normal primary was restarted and observed ready in Veratown2. Existing operator itemsets were not changed.

Previously guided live tests also demonstrated basic, clothing, cosmetic cosplay, typed and modular round trips; active timer-lock removal; occupied-slot refusal; protected owner-lock preservation; and a database-only HeavyYoke-to-HempRope revision change without restarting the bot, with explicit partial outcomes and idempotency. All added test items were removed; the dedicated itemset document was deleted and the operator’s temporary native cosplay permission change was restored.

The separately approved real-browser run also passed against the official R132 BC application, using the same dedicated subject and primary. The actual target-origin ChatRoomSyncSingle correction advanced the primary’s normal appearance reducer to sequence 4 while the action was pending. It restored the initial appearance but did not falsely confirm the requested clothing. Operation 9b57e121-a172-493a-8e0c-39676b8bc57d ended timed_out_unobserved, with emitted-write evidence, exactly one primary appearance write and no retry. Browser baseline, observer cleanup and terminal MongoDB journal status were verified; the runner exited with code 0 and closed its connections. The normal primary was rebuilt, restarted and observed ready in Veratown2 after this run.

Observation withholding was local injection in both automated scenarios. The browser test performed a native wardrobe edit and canvas refresh; it did not simulate a mouse click or prove spontaneous asset-validator repair, visual rendering quality or durable BC account persistence.