Skip to content

fix(feishu): bootstrap context for first thread mention(修复机器人第一次进入话题,读不到话题内容的问题) - #1627

Merged
chenhg5 merged 2 commits into
chenhg5:mainfrom
dongyusheng:test/lane/feishu-thread-bootstrap-context
Aug 13, 2026
Merged

fix(feishu): bootstrap context for first thread mention(修复机器人第一次进入话题,读不到话题内容的问题)#1627
chenhg5 merged 2 commits into
chenhg5:mainfrom
dongyusheng:test/lane/feishu-thread-bootstrap-context

Conversation

@dongyusheng

@dongyusheng dongyusheng commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix Feishu thread bootstrap when thread_isolation = true.

If a thread root is posted without mentioning the bot, cc-connect does not dispatch that message. When the bot is mentioned later in the same thread, the isolated agent session therefore starts empty and sees only the trigger text (for example, “看看这个”). This change detects the first accepted message in an existing isolated thread and injects its parent/root reply context once.

Later messages preserve the quote-skip behavior introduced for #764, avoiding repeated context and oversized quoted prefixes. This change is intentionally narrower than full thread-history synchronization: it bootstraps the parent/root context once and does not fetch every sibling message or other bots' replies.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing behavior to change)
  • Documentation only
  • Internal refactor / chore (no user-visible change)

Testing

  • go test ./... — passed for the functional change before the lint-only follow-up.
  • GitHub Actions lint — passes after simplifying the condition flagged by staticcheck QF1001.
  • The lint follow-up only applies De Morgan's law to an existing condition; behavior is unchanged.

Automated tests added in this PR

  • TestOnMessageThreadIsolationBootstrapsExistingThreadContext in platform/feishu/feishu_test.go
    • creates a pre-existing Feishu thread whose root did not mention the bot;
    • sends the first later @bot trigger with thread_isolation = true;
    • verifies the session key is scoped to the thread root;
    • verifies the trigger text remains the user content;
    • verifies the existing thread-root content is injected into ExtraContent.
  • TestMarkAndIsActiveThreadSession now verifies that only the first activation reports a bootstrap and subsequent activations do not.

For bug fixes only — regression test

  • Regression test name: TestOnMessageThreadIsolationBootstrapsExistingThreadContext
  • Manual verification this test catches the regression:
    • Reverted the fix locally; the regression test failed as expected.

Critical User Journeys (CUJ) impact

  • No CUJ touched (small refactor, doc change, etc.)
  • A — basic conversation
  • B — session lifecycle (/new /switch /list /history etc.)
  • C — agent execution control (/mode /cancel /stop permissions)
  • D — security & permissions (allow_from admin_from banned_words rate limits)
  • E — scheduled tasks (/cron /timer)
  • F — config switching (/lang /provider /model reload)
  • G — error handling & robustness (LLM failure, ws reconnect, agent crash)
  • H — multi-platform / multi-project isolation
  • I — UI rendering correctness (cards, streaming, display modes)

If any CUJ group is touched, confirm:

  • go test ./core/ -run TestCUJ passes locally.
  • If the change alters an existing user-visible flow, corresponding regression coverage was added.

Manual / user-visible behavior change

Before:

  1. A user posts the content or attachment that starts a Feishu topic without mentioning the bot.
  2. Later, the user sends @bot 看看这个 in that topic.
  3. The bot receives only “看看这个” and asks the user to send the content again.

After:

  1. The first accepted bot mention activates the isolated thread session.
  2. cc-connect fetches and injects the parent/root message context once.
  3. Subsequent messages reuse the agent session and continue to skip repeated quote injection.

thread_isolation = true remains enabled, so unrelated topics stay isolated.

Checklist (reviewer will verify)

  • go build ./... passes
  • go test ./... passes (with -race if touching concurrency)
  • AGENTS.md Pre-Commit Checklist items are satisfied
  • No new hardcoded platform/agent names in core/
  • i18n strings have all-language translations (no new user-facing strings)
  • No secrets / credentials in source

Related

Unlike #525, this implementation reuses the existing activeThreadSessions state and is narrowly scoped to bootstrapping the first accepted mention in an unengaged isolated thread; it does not introduce separate message-to-session or root-context maps.

@dongyusheng
dongyusheng requested a review from chenhg5 as a code owner August 1, 2026 08:44
@dongyusheng

Copy link
Copy Markdown
Contributor Author

@chenhg5
Related to #1644 / #1648, but orthogonal in scope:

#1648 does not inject prior topic context, so it does not address #1453.
If #1648 lands first, I will rebase and preserve both replyContext.rootID and
replyContext.bootstrapThread.

@chenhg5 chenhg5 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

结论: Approve

总体判断: 一个窄而准的 bug fix——修复 Feishu topic 里机器人第一次被 @ 时读不到 topic root 上下文的问题。设计克制,没有全量同步 thread history,只在「第一次接受到的 thread mention」一次性 bootstrap 父消息,避免引入新的 map / 新状态。建议合入。

Review 范围:

  • 看了 platform/feishu/feishu.gomarkThreadSessionActive 的语义变化(无返回值 → 返回 bool)、replyContext.bootstrapThread 新字段、以及 onMessageparentID 兜底逻辑。
  • 看了新增测试 TestOnMessageThreadIsolationBootstrapsExistingThreadContext 和更新的 TestMarkAndIsActiveThreadSession
  • 旁路核对 isThreadSessionKeyactiveThreadSessions 的现有用法,确认 LoadOrStore 替换原 Store 的语义差异(避免 concurrent first-mark 误判)。

✅ 做得好的地方:

  • 设计克制:刻意比「全量 thread history 同步」窄,只在 bootstrapThread && parentID == "" 时把 RootId 当作 parentID 传给 fetchQuotedMessage,复用现有 quote-injection 通道,不另起 thread-history fetcher。这与 #525(未合并)的失败原因形成对比——本 PR 不引入新的 message→session / root-context map,复用 activeThreadSessions 单状态机。
  • API 演进合理markThreadSessionActive 从无返回改为 bool,并更新所有调用点测试;bool 语义清晰(首次激活 / 重复 / 关闭隔离 / 非 thread key),没有破坏现有调用者。
  • 测试覆盖两个独立维度:(1) 新 bootstrap 路径的端到端(SessionKey、Content、ExtraContent 三件事),(2) markThreadSessionActive bool 契约(disabled / non-thread / first / subsequent 四种 case)。回归测试覆盖明确,作者列出的「if I reverted the fix locally this test fails」路径符合预期。

🚨/🔴 必须处理:

  • 未发现。

🟠 建议改进:

  • markThreadSessionActive 内部这段:loaded := p.activeThreadSessions.LoadOrStore(sessionKey, time.Now()); if loaded { p.activeThreadSessions.Store(sessionKey, time.Now()) } 实际上是「总是覆盖时间戳」+「区分是否首次」。如果意图只是区分首次激活,不应每次都覆盖时间戳(LoadOrStore 已写入了首次时间,后续 Store 会刷新它)。建议二选一:
    • 若希望 isActiveThreadSession 反映「最近一次互动时间」:保留双写,但加注释说明「store 刷新是有意为之,避免 stale」。
    • 若只关心「是否曾经被 @ 过」:删掉 if loaded 那段,后续 LoadOrStore 直接丢弃 loaded,函数仍返回 !loaded
    • 当前写法功能正确,但语义有歧义,对 future reader 不友好。
  • 不阻塞,但建议在本 PR 或 follow-up 里加一行注释明确意图。

🔵 可选优化:

  • 新测试里 mock server 的 default 分支用 t.Fatalf("unexpected path %s", r.URL.Path),对 SDK 自带的轮询 / 健康检查路径(如果有)可能误伤;建议加一个 t.Logf + return 200 的更宽容分支,便于后续 SDK 升级不破测试。低优先

❓ 需要确认:

  • 无。

Testing / Risk:

  • 已看到的验证: CI 全绿(lint / unit / smoke / regression / performance);新增 TestOnMessageThreadIsolationBootstrapsExistingThreadContext 覆盖 happy path;TestMarkAndIsActiveThreadSession 覆盖 bool 契约。
  • 未覆盖风险: 真实飞书环境根消息被删除 / 不可见的情况下 fetchQuotedMessage 会拿到什么(与现有 quote 路径行为一致,应该 OK,但作者没显式断言)。低优先,因为本 PR 走的就是现有 quote 通道,行为与原有 quote 处理同源。
  • 并发安全: 改为 LoadOrStore 之后,markThreadSessionActive 在并发第一次 mark 时只有一个 goroutine 会拿到 false(首次),其余拿到 true(重复),bool 返回仍然 deterministic。已确认。

Next step:

  • 建议 owner 直接 merge。scope 极小,fix 真实用户痛点(飞书 topic 第一次进入机器人看不到上下文),设计合理,测试充分。可以现在合,也可以攒到下次 batch。

@chenhg5
chenhg5 merged commit 94e2154 into chenhg5:main Aug 13, 2026
5 checks passed
@dongyusheng

Copy link
Copy Markdown
Contributor Author

@chenhg5 您好,感谢合并这个修复!请问包含 #1627 的下一个 cc-connect 版本预计什么时候发布?它会进入下一个 beta 版本还是稳定版?谢谢!

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.

[Bug] 飞书@机器人后,机器人无法读取到群消息里的消息

2 participants