veratown-prototype

Scoped & Permission-Aware Help Generation — Feature Proposal

Status: Implemented 2026-10-17. This archived proposal records the design; current behavior and contracts are documented in the command and runtime architecture guides.

Purpose and scope

Propose one shared help-generation capability for /bot help, !help, and /bot <feature> help. Generate help from structured command metadata and the requester’s current context, rather than exposing every registered command or maintaining separate, potentially inconsistent command lists.

Help must answer: What can this player do here, with their current permissions? A non-admin must never see admin-only command names, syntax, descriptions, examples, aliases, or navigation hints in bot-generated help. An administrator sees administrative help only for scopes where their actual permissions apply; being an admin in one room grants nothing in another.

This is a shared command-system capability, not an independent game. It does not grant permissions, enable disabled features, perform command actions, or replace authorization at execution time. All types and components introduced below are proposed contracts, not claims about existing implementation.

Product rules

Scope and permission examples

These are intended outcomes, not a new list of enabled commands.

Request / context Proposed result
Non-admin requests /bot help Available player-facing feature summaries; no world, Bunny, or door administrative command details.
Room admin requests !help Player summaries plus administrative scopes they may access in this room; private reply only.
Non-admin requests /bot door help Only authorized player-facing door/keypad guidance; no management, group, or access-management syntax.
Non-admin requests /bot world help Neutral unavailable-scope response, without admin syntax or hints.
Player requests /bot casino help outside a configured game location Available wallet/general information; no location-restricted game commands.
Player requests casino help at an authorized game location Only enabled, configured games available to that player at that location.
Admin loses room-admin status before requesting another page Recomputed player-only help; no reuse of a previously privileged page.

Administrative management help may remain available to an authorized admin when the corresponding player feature is disabled, if the management command itself is available. Conversely, public informational commands such as wallet or fairness help need not disappear merely because wagering is unavailable. Availability belongs to individual capabilities, not a blanket feature gate.

Command metadata and policy ownership

Command suites declare bounded, typed help descriptors alongside their registrations. Use one authoritative descriptor for each documented command path, including subcommands handled internally by a parent handler. A parser registration alone is not a complete catalog of such subcommands.

Proposed field / contract Validation / intent
id, commandPath Stable unique ID and normalized tokenized command path; references a live handler or a declared branch of one.
featureId, scopePath Explicit feature ownership and scope hierarchy; no feature inference from arbitrary strings.
summary, usage, examples Bounded reviewed text; examples belong to one visible descriptor and use placeholders, never real secrets.
aliases Supported aliases mapped to the same descriptor and policy; no separate weaker visibility path.
accessPolicy Explicit feature-owned permission predicate, reused with execution checks where practical.
availabilityPolicy Read-only predicate for effective configuration, feature gates, channel, location, or relevant session state.
overviewSummary Safe player/admin summaries selected from visible capabilities; no administrative text in a shared player summary.
displayOrder, localeKey Stable sorting and optional localization; localization must not bypass filtering.

Missing or malformed help metadata means not discoverable, with an operator validation diagnostic. It never defaults to public visibility. Migration must complete the descriptor inventory before the new renderer is enabled, so fail-closed metadata does not silently strand legitimate commands.

Feature adapters own channel-specific authorization. The shared help service calls injected policy contracts but does not implement feature permission rules or import feature services directly. The composition root wires the existing room-admin and observation capabilities into these contracts. Predicates must be bounded, side-effect-free, and deny visibility on errors or missing context.

Permission and availability predicates may be separate so an authorized admin can manage a disabled feature. Transient argument validation or insufficient wallet balance need not hide a command: help describes accessible capabilities, not a guarantee that every possible invocation will succeed.

No new MongoDB collection is required for the baseline. Static command syntax and reviewed descriptions belong with command registrations; live configuration and access observations remain owned by existing services. Optional future DB-managed explanatory text must never define permissions, add executable commands, or enable a feature. Any presentation settings must preserve private replies and mandatory permission filtering.

Generation, lifecycle, and consistency

  1. Normalize supported native/bang input using the shared parser, preserving existing sender/target validation and longest registered-prefix dispatch.
  2. Resolve a declared help request, without treating an arbitrary trailing help inside free-form arguments as a request. Document reserved help paths and detect collisions during registration.
  3. Build a request context from authoritative observations: member number, room key, channel, membership, permission revision/freshness, effective configuration revision, and relevant location/session observations.
  4. Read a consistent catalog snapshot, select the requested scope, and evaluate permission and availability for each candidate descriptor.
  5. Remove denied descriptors before grouping, composing examples, counting entries, rendering headings, or generating suggestions/navigation. Parent headings are shown only when visible descendants remain.
  6. Render deterministic, bounded output. Split oversized help on descriptor boundaries, with continuation links containing only visible scopes. Apply per-member rate limits and existing outbound queue limits.
  7. Revalidate requester presence and relevant context revisions immediately before enqueueing each private chunk. Recompute or suppress stale privileged content when permissions, room, configuration, location, or catalog change.

Initial implementation should recompute filtered results per request, not cache rendered help globally. Immutable syntax/localization templates may be cached. If filtered caching is introduced later, its key and invalidation must include member, room, channel, permissions, configuration, location/session, locale, and catalog revision. Never share privileged output between users.

Registration and disposal update handler/help metadata consistently. Plugin stop removes its descriptors and prevents pending help work from emitting stale entries. Startup validates duplicate IDs, unresolved aliases, scope cycles, reserved help-path collisions, and descriptors without a dispatch owner. A help failure yields a neutral private response, not the raw catalog or a policy exception containing sensitive command details.

Delivery uses the existing communication capability. Enqueue-time checks cannot retract a whisper already dispatched; describe delivery as unconfirmed unless supported by existing communication evidence, and do not claim atomic permission checking across the external transport.

Privacy, authorization, and migration

The existing parser’s private helpText() renders all registrations without a requester context and is also appended to unknown-command responses. Existing feature suites additionally own local help strings; casino help already filters some game entries by member/location. Door administration’s service currently returns help before its admin check. These are migration surfaces, not a claim that every existing execution path lacks authorization.

Inventory parser-level help, nested feature help, bare-feature fallbacks, usage errors, guided-session prompts, and legacy message listeners. Replace or guard all bot-facing help paths with the shared visibility policy. Preserve existing casino location/configuration restrictions and do not infer admin permission from descriptive text. Operator-only CLI documentation is outside player help.

UML

Component diagram

flowchart LR
    Root["src/app/start.ts"] --> Parser["BotCommandParser"]
    Root --> Help["ScopedHelpService"]
    Suites["Feature command suites"] --> Parser
    Suites --> Catalog["CommandHelpCatalog"]
    Suites --> Policies["Feature-owned access and availability policies"]
    Parser --> Help
    Help --> Catalog
    Help --> Context["Injected HelpContextPort"]
    Help --> Policies
    Help --> Renderer["HelpRenderer"]
    Help --> Comms["Injected CharacterCommunicationApi"]
    Context --> Observations["Room membership / permissions / configuration / location"]
    Comms --> BC["Existing BcClient actions"]

Class diagram

classDiagram
    class CommandHelpDescriptor {
        +id
        +commandPath
        +scopePath
        +usage
        +examples
        +accessPolicy
        +availabilityPolicy
    }
    class CommandHelpCatalog {
        +register(descriptor) Dispose
        +snapshot()
        +validate()
    }
    class ScopedHelpService {
        +request(sender, scope, source)
        +visibleDescriptors(context, scope)
        +stop()
    }
    class HelpContextPort {
        <<injected contract>>
        +current(sender, source)
        +isCurrent(context)
    }
    class HelpPolicy {
        <<feature-owned contract>>
        +isAllowed(context)
        +isAvailable(context)
    }
    class HelpRenderer {
        +overview(visibleDescriptors)
        +scope(visibleDescriptors)
        +chunks(renderedHelp)
    }
    CommandHelpCatalog o-- CommandHelpDescriptor
    ScopedHelpService --> CommandHelpCatalog
    ScopedHelpService --> HelpContextPort
    ScopedHelpService --> HelpPolicy
    ScopedHelpService --> HelpRenderer

Use-case diagram

flowchart LR
    Player((Player)) --> Global([Request global help])
    Player --> Scoped([Request feature help])
    Admin((Authorized room admin)) --> AdminHelp([See permitted administrative help])
    Owner((Feature maintainer)) --> Register([Declare metadata and policies])
    Global --> Filter([Filter using current context])
    Scoped --> Filter
    AdminHelp --> Filter
    Register --> Catalog[(Live command catalog)]
    Catalog --> Filter
    Filter --> Private([Privately show visible capabilities only])

Activity diagram: scoped help request

flowchart TD
    Start([Help requested]) --> Validate{Valid sender and target?}
    Validate -->|No| Ignore([Ignore])
    Validate -->|Yes| Context[Load current requester context]
    Context --> Select[Select declared scope from catalog snapshot]
    Select --> Filter[Evaluate access and availability; omit denied entries]
    Filter --> Any{Visible entries?}
    Any -->|No| Neutral[Prepare neutral unavailable-scope reply]
    Any -->|Yes| Render[Build safe headings, usage, examples and chunks]
    Neutral --> Fresh{Context still current?}
    Render --> Fresh
    Fresh -->|No| Recompute[Recompute or suppress stale response]
    Recompute --> Context
    Fresh -->|Yes| Send[Enqueue private reply only]
    Send --> End([Dispatch unconfirmed; no public fallback])

State machine

stateDiagram-v2
    [*] --> Requested
    Requested --> Ignored: invalid sender or target
    Requested --> Evaluating: valid help request
    Evaluating --> Rendering: visible entries
    Evaluating --> NeutralReply: unknown or inaccessible scope
    Rendering --> Revalidating
    NeutralReply --> Revalidating
    Revalidating --> Evaluating: changed context, bounded recomputation
    Revalidating --> Suppressed: departed, stopped, or retry bound reached
    Revalidating --> Dispatching: context current
    Dispatching --> Complete: private chunks enqueued
    Dispatching --> Suppressed: delivery failure or changed context
    Complete --> [*]
    Suppressed --> [*]
    Ignored --> [*]

Sequence / communication diagram

sequenceDiagram
    participant Player
    participant Parser as BotCommandParser
    participant Help as ScopedHelpService
    participant Context as HelpContextPort
    participant Catalog as CommandHelpCatalog
    participant Policy as Feature-owned policies
    participant Renderer as HelpRenderer
    participant Comms as CharacterCommunicationApi
    Player->>Parser: /bot help, !help, or /bot feature help
    Parser->>Help: validated sender, declared scope, source
    Help->>Context: current requester context
    Context-->>Help: membership, permissions and availability revisions
    Help->>Catalog: consistent scope snapshot
    Catalog-->>Help: candidate descriptors
    loop Candidate descriptors
        Help->>Policy: evaluate read-only access and availability
        Policy-->>Help: visible or denied
    end
    Help->>Renderer: visible descriptors only
    Renderer-->>Help: bounded private help chunks or neutral reply
    loop Before each chunk
        Help->>Context: revalidate relevant context
        Context-->>Help: current or changed
        alt Context current
            Help->>Comms: whisper requester only
        else Context changed
            Help->>Help: recompute or suppress; never send stale privileged text
        end
    end

Decisions and acceptance criteria

Approve the global overview/detail split, supported nested help scopes, reserved help-path conventions, command-level availability semantics, neutral inaccessible-scope wording, output/rate bounds, and the inventory/migration plan. Filtering and private-only delivery are mandatory, not configurable relaxations. Do not activate centralized generation until legacy bot-facing help routes are migrated or guarded.

Acceptance requires tests covering:

The implementation adds the shared catalog, scoped visibility filtering, bounded private rendering, request-context revalidation, and command-suite metadata migration. Runtime documentation and regression tests describe the implemented contract. Existing command-time authorization remains independent of help visibility.

References