Skip to content

F267: Channel-level single identity binding (mechanism layer) - #1196

Open
kaverjody wants to merge 1 commit into
zts212653:mainfrom
kaverjody:F267-channel-single-identity
Open

F267: Channel-level single identity binding (mechanism layer)#1196
kaverjody wants to merge 1 commit into
zts212653:mainfrom
kaverjody:F267-channel-single-identity

Conversation

@kaverjody

Copy link
Copy Markdown

What

This PR introduces the mechanism layer (Phase 1) of F267: per-(connector, chat) channel identity override, enforced at the OutboundDeliveryHook adapter edge.

For every cat that posts to an external IM channel (feishu / dingtalk / wechat / telegram groups) which contains non-roster members, the natural display name (砚砚 / 小狸 / 宪宪 / 小捷 / 金哥 / 烁烁) is replaced at the adapter edge with the channel-configured identity. Default for feishu groups is 咖啡猫 🐱; DMs explicitly opt out (KD-2) so the home-environment multi-cat authoring experience is preserved.

Why

External collaborators only ever opted in to a single identity. They cannot tell 砚砚 from 宪宪, and 60% of every message in a busy group is signature noise. This blocks real collaboration with openclaw / hermes / humans who do not share the cat-cafe context. Layer B (prompt pin) and Layer C (audit dashboard) are scoped for follow-up PRs — this PR is the harness-level floor that makes Layer A work without trusting the model.

Files

docs/README.md                                                 | +1
docs/features/F267-channel-single-identity.md                  | new (spec)
docs/references/im-channel-public-contract.md                  | new (public contract)
packages/api/src/infrastructure/connectors/channel-identity.ts | new (registry)
packages/api/src/infrastructure/connectors/OutboundDeliveryHook.ts | +~80
packages/api/test/channel-identity-override.test.js            | new (9 cases)
packages/api/test/outbound-identity-override.test.js           | new (5 cases)

7 files changed. 5 new, 2 modified.

Acceptance Criteria covered

  • AC-1A: OutboundDeliveryHook.executeDelivery 增加 channelIdentityOverride 解析 → resolveBindingIdentity 纯函数解析
  • AC-1B: 当 binding 配 displayName='咖啡猫' → 出站 envelope 强制为 【咖啡猫🐱】,即使 cat agent 输出 【砚砚🐱】
  • AC-1C: 当 binding 没配 override → 旧行为(透传 cat 自带 displayName),overridden: false
  • AC-1D: 默认配置 feishu 群(chatType='group')→ fallback displayName='咖啡猫';飞书 DM 不走 override
  • AC-1E: IM Hub 飞书群面板加「对外显名」开关(follow-up,UI 改动单独 PR)
  • AC-1F: 现有 F134 / F157 / F252 行为不变(只在 catDisplayName 推导前多一步 resolve)
  • AC-1G: 单元测试:channel-identity-override.test.js 9 cases + outbound-identity-override.test.js 5 cases

Test plan

  1. cd packages/api && pnpm install && pnpm build — 编译通过(channel-identity.ts + OutboundDeliveryHook.ts 强类型契约)
  2. node --test packages/api/test/channel-identity-override.test.js — 9 cases 全绿
  3. node --test packages/api/test/outbound-identity-override.test.js — 5 cases 全绿
  4. 集成验证:在 IM Hub 飞书群发 1 条触发消息 → 群成员看到 【咖啡猫🐱】 前缀,看到 【砚砚🐱】/【小狸🐱】
  5. 回归:DM 内 cat 自带 displayName 仍正常(KD-2 验证)
  6. 回归:cat-cafe 家内 thread 不走 connector,无 override(unaffected)

Out of scope (follow-up PRs)

Key Decisions

KD 决策 理由
KD-1 双层机制(connector 强制 + prompt pin) F167 已证 prompt 层天花板(opus-47 仍句中 @ 复现),需要 harness 兜底
KD-2 DM 保留 cat 自带 displayName 用户对 DM 有「一对一 cat」期望,群才暴露
KD-5 exposeInternalNames: false 默认 内部 cat 名泄漏是协作障碍根因
KD-6 cat agent body 仍允许引用「砚砚」做内部提及 不强制改 body 内容,只改 adapter 出口

Risk

风险 缓解
用户看「咖啡猫」但实际是「砚砚」在干活 → 困惑 Phase 2 prompt identity pin + IM Hub audit 段显示当前 cat ID
DM 行为改变 DM 不走 override(AC-1D)
跨 connector 不一致(飞书=咖啡猫,Telegram=其他) 默认值 operator 可配(IM Hub 面板)

Closes #1195

When a cat-cafe deployment connects to external IM channels (feishu/
dingtalk/wechat/telegram groups) that contain non-roster members
(external agents, bots, or humans), every cat currently prefixes its
own roster display name (砚砚/小狸/宪宪/小捷/金哥/烁烁 + 🐱) to
outbound messages. External members cannot distinguish these names —
they only ever opted in to one identity (e.g. "咖啡猫"). The
resulting noise (60% of message length is signature), the inability
to recognize the actor, and the leakage of internal shorthand are
all collaboration blockers for cross-roster scenarios.

This PR introduces the mechanism layer (Phase 1) of a three-phase
fix: each (connector, chat) pair can declare a single outbound
display identity; OutboundDeliveryHook enforces it at the adapter
edge, so any cat's natural display name is replaced before delivery.
DMs and cat-cafe home threads are explicitly excluded from the
override so the multi-cat authoring experience is preserved inside
the home environment.

Layer B (agent prompt identity pin) and Layer C (IM Hub audit
dashboard) are scoped for follow-up PRs and tracked in F267.

Changes:

  * packages/api/src/infrastructure/connectors/channel-identity.ts
    New: ChannelIdentityRegistry + CHANNEL_IDENTITY_DEFAULTS, with
    explicit set/unset/list/resolve and resolveForChatType honoring
    DM behavior (KD-2).
  * packages/api/src/infrastructure/connectors/OutboundDeliveryHook.ts
    New: channelIdentityRegistry option, resolveBindingIdentity pure
    resolver, per-binding identity collapse in executeDelivery, and
    an F267 audit log line when override fires.
  * packages/api/test/channel-identity-override.test.js
    New: 9 unit tests covering the registry contract.
  * packages/api/test/outbound-identity-override.test.js
    New: 5 unit tests covering the pure resolver, including the
    "no registry" and "empty natural name" edges.
  * docs/features/F267-channel-single-identity.md
    New: full spec with AC (Phase 1/2/3), KD log, dependencies, risk
    matrix, behavioral evidence cases.
  * docs/references/im-channel-public-contract.md
    New: public contract for external collaborators — which
    cat-cafe internal shorthands they can safely ignore, which
    formats are guaranteed, and which patterns are noise to filter.
  * docs/README.md
    Reference index updated to point at the new IM Channel Public
    Contract doc and at the F267 spec.

Out of scope (intentional, for follow-up PRs):
  - SystemPromptBuilder identity pin injection (Phase 2)
  - IM Hub audit dashboard (Phase 3)
  - FeishuAdapter in-group gate, fingerprint loopback guard, full
    OutboundSanitizationLayer (P1 items in the issue body)

Refs: issue zts212653#1195 (Channel-level single identity binding for IM
connectors).
@kaverjody
kaverjody requested a review from zts212653 as a code owner July 21, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Channel-level single identity binding for IM connectors (feishu/dingtalk/wechat/telegram groups)

1 participant