Files
openclaw/docs/channels/qa-channel.md
Ayaan Gazali 1f2e99832b fix(channels): bundled channels reject the documented responsePrefix override (#118148)
* fix(channels): bundled channels reject the documented responsePrefix override

* fix(feishu): pass channel and account to the reply prefix resolver

The ordinary reply dispatcher built the prefix context without channel or
accountId, so channels.feishu.responsePrefix and its account override fell
through to the global value. The comment dispatcher already passed them.

* fix(channels): accept twitch's root responsePrefix and type ClickClack's

Twitch validates as a two-branch union with both branches closed, so
responsePrefix declared only on the account shape left
channels.twitch.responsePrefix rejected whenever accounts is present. Also
declares responsePrefix on ClickClackAccountConfig so typed callers can express
what its schema already accepts.

* fix(clickclack): apply the configured responsePrefix in model mode

Model mode sends the completion straight to ClickClack instead of going through
the agent reply pipeline, so the documented responsePrefix parsed and then did
nothing. Resolve it through the public reply-pipeline seam so template
variables like {model} render from the completion's own provider and model.

* fix(clickclack): do not double the response prefix in model mode

systemPrompt is operator-owned, so a model can be instructed to emit the
configured prefix itself and the unconditional concatenation then sent it
twice. Match the shared reply normalizer's startsWith guard.

* test(clickclack): resolve inbound access through the real resolver

Main now requires access.channelIngress, which the hand-built fixture did not
provide, so the model-mode messages never dispatched. Drop the fixture and let
handleClickClackInbound resolve access from a DM-shaped message, matching the
sibling bot-loop tests.

* test(channels): reject a key missing from any composed branch

A union alternative is a configuration mode an operator can pick, so a key
present in only one mode is unusable in the other even though the union still
validates. Treat any rejecting alternative as a rejection, and walk allOf when
collecting account schemas.

* fix(channels): offer responsePrefix only where the reply path applies it

Applying the prefix to an outbound reply is per-channel wiring, not a shared
step. Buzz, Nostr, QA Channel, Raft, Reef and SMS never read it, so accepting
the key there validated a setting their delivery path ignores. Anchor the
contract to the channels that consume the prefix.

* fix(channels): complete shared response prefix delivery

Co-authored-by: ayaangazali <ayaangazali.work@gmail.com>

* docs(config): refresh response prefix baseline

* test(tlon): use canonical prefix fixture helpers

* test(channels): prove Twitch reply prefix delivery

Exercise schema admission and the real shared reply dispatcher at the Twitch native send boundary, replacing redundant schema-only coverage. Keep Tlon fixtures on canonical imports and recording options.

* docs(channels): clarify explicit message prefix behavior

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 17:08:28 -07:00

4.4 KiB

summary, title, read_when
summary title read_when
Synthetic Slack-class channel plugin for deterministic OpenClaw QA scenarios QA channel
You are wiring the synthetic QA transport into a local or CI test run
You need the bundled qa-channel config surface
You are iterating on end-to-end QA automation

qa-channel is a repo-local synthetic message transport for automated OpenClaw QA (extensions/qa-channel, private package, excluded from packaged installs). It is not a production channel - it exists to exercise the same channel plugin boundary used by real transports while keeping state deterministic and fully inspectable.

What it does

  • Slack-class target grammar:
    • dm:<user>
    • channel:<room>
    • group:<room>
    • thread:<room>/<thread>
  • Shared channel: and group: conversations are surfaced to agents as group/channel room turns, so they exercise the same visible-reply and message-tool routing policy used by Discord, Slack, Telegram, and similar transports.
  • HTTP-backed synthetic bus for inbound message injection, outbound transcript capture, thread creation, reactions, edits, deletes, and search/read actions.
  • Host-side self-check runner that writes a Markdown report to .artifacts/qa-e2e/.

Config

{
  "channels": {
    "qa-channel": {
      "baseUrl": "http://127.0.0.1:43123",
      "botUserId": "openclaw",
      "botDisplayName": "OpenClaw QA",
      "allowFrom": ["*"],
      "pollTimeoutMs": 1000
    }
  }
}

Account keys:

  • enabled - master toggle for this account.
  • name - optional display label.
  • responsePrefix - automatic reply prefix; account overrides win. Accepts a literal, "auto" for the agent identity name, a template such as "[{model}]", or "" to disable an inherited prefix.
  • baseUrl - synthetic bus URL. The account counts as configured once this is set.
  • botUserId - synthetic bot user id used in target grammar (default: openclaw).
  • botDisplayName - display name for outbound messages (default: OpenClaw QA).
  • pollTimeoutMs - long-poll wait window. Integer between 100 and 30000 (default: 1000).
  • allowFrom - sender allowlist (user ids or "*"; default: ["*"]). DMs are always open policy; allowlisted group policy also uses these synthetic sender ids.
  • groupPolicy - shared-room policy: "open" (default), "allowlist", or "disabled".
  • groupAllowFrom - optional shared-room sender allowlist. When omitted under "allowlist", QA Channel falls back to allowFrom.
  • groups.<room>.requireMention - require a bot mention before replying in a specific group/channel room (default: false). groups."*" sets the default; per-room tools / toolsBySender set tool policy overrides.
  • defaultTo - fallback target when none is supplied.
  • actions.messages / actions.reactions / actions.search / actions.threads - per-action tool gating.

Multi-account keys at the top level:

  • accounts - record of named per-account overrides keyed by account id.
  • defaultAccount - preferred account id when multiple are configured.

Runners

Host-side self-check (writes a Markdown report under .artifacts/qa-e2e/):

pnpm qa:e2e

This routes through qa-lab, starts the in-repo QA bus, boots the qa-channel runtime slice, and runs a deterministic self-check.

Full repo-backed scenario suite:

pnpm openclaw qa suite

The isolated channel-participant-identity-inspection scenario enables execution identity before startup, exercises DM, group, senderless, same- and mixed-participant collect paths, proves an ingress rejection creates no audit rows, and compares JSON plus human CLI inspection across Gateway restart:

pnpm openclaw qa suite --scenario channel-participant-identity-inspection

Runs scenarios in parallel against the QA gateway lane. See QA overview for scenarios, profiles, and provider modes.

Docker-backed QA site (gateway + QA Lab debugger UI in one stack):

pnpm qa:lab:up

Builds the QA site, starts the Docker-backed gateway + QA Lab stack, and prints the QA Lab URL. From there you can pick scenarios, choose the model lane, launch individual runs, and watch results live. The QA Lab debugger is separate from the shipped Control UI bundle.