Skip to content

feat(routing): configurable default responder for new threads (#385)#419

Open
mindfn wants to merge 17 commits intomainfrom
feat/default-responder
Open

feat(routing): configurable default responder for new threads (#385)#419
mindfn wants to merge 17 commits intomainfrom
feat/default-responder

Conversation

@mindfn
Copy link
Copy Markdown
Collaborator

@mindfn mindfn commented Apr 10, 2026

What

  • New defaultResponderCatId field in CatCafeConfigV2 — sets which cat auto-responds to new threads
  • PATCH /api/config/default-responder endpoint with catRegistry.has() validation + roster membership check in resolver
  • getDefaultResponderCatId() resolver with 3-tier fallback: configured (if in roster & available) → breed default → 'opus'
  • ConnectorRouter now calls getDefaultResponderCatId() dynamically at parse time (hot-reload safe)
  • Hub UI: star badge on overview card (display), toggle in HubCatEditor routing section (mutation)
  • ChatContainer + chatStore reads default from /api/config snapshot
  • F078 doc updated: 4 references to "defaults to opus" now point to getDefaultResponderCatId() (Feature: make the global default responder for new threads configurable from the member overview #385)

Why

Previously the default responder was hardcoded to 'opus'. Hub operators need to designate any registered cat as the default without code changes. The 3-tier fallback with availability check ensures graceful degradation when the configured cat goes offline.

Issue Closure

Original Requirements

  • Discussion: internal feature request from CVO
  • 原始需求摘录

    全局默认回复猫应该可以在 Hub 成员概览里配置,而不是写死 opus

  • 铲屎官核心痛点:无法从 UI 切换默认回复猫,必须改代码
  • 请 Reviewer 对照上面的摘录判断:交付物是否解决了铲屎官的问题?

Plan / ADR

Tradeoff

  • Chose single global defaultResponderCatId over per-member boolean to keep config simple and avoid ambiguity
  • Overview card shows status (badge), editor owns mutation (toggle) — avoids accidental changes in list view
  • ConnectorRouter reads resolver dynamically instead of caching — tiny runtime cost for hot-reload correctness
  • Toggle hidden for draft/new members (validation requires registered catId)

Test Evidence

Backend (API):

  • getDefaultResponderCatId 6 tests: fallback chain, configured value, v1 compat, stale roster ID, unavailable cat
  • pnpm --filter @cat-cafe/api test — 78 passed, 0 failed

Frontend (Web):

Build: pnpm -r --if-present run build — all packages build clean

Open Questions

  • Non-blocking: PATCH endpoint allows writing an unavailable catId (runtime fallback handles gracefully). Could add server-side availability check if desired.

本地 Review: [x] gpt52 已 review 并放行 (3 rounds + consistency check, all P1/P2 resolved)
云端 Review: [x] Codex R1 P1 fixed, R2 passed (0 P1/P2)

@mindfn mindfn requested a review from zts212653 as a code owner April 10, 2026 06:13
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 10, 2026

@codex review

Please review latest commit ad44f1a for P1/P2 only.

规则:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3 建议,不算缺陷。

审查标准(详见 AGENTS.md "Review guidelines" section):

  • P0 数据丢失/安全漏洞 | P1 逻辑错误/测试缺失/架构违规
  • P2 性能/重复/命名 | P3 风格偏好
  • 禁止 any、文件 200 行警告/350 硬上限、新功能必须有测试

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b2307258d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 10, 2026

@codex review

Please review latest commit 1462867 for P1/P2 only.

Context: This commit fixes the P1 from the previous review — getDefaultResponderCatId() now checks roster membership before isCatAvailable(), so a stale/deleted catId falls back to breed default instead of routing to a non-existent cat.

规则:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3 建议,不算缺陷。

审查标准(详见 AGENTS.md "Review guidelines" section):

  • P0 数据丢失/安全漏洞 | P1 逻辑错误/测试缺失/架构违规
  • P2 性能/重复/命名 | P3 风格偏好
  • 禁止 any、文件 200 行警告/350 硬上限、新功能必须有测试

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn mindfn force-pushed the feat/default-responder branch from 1462867 to 4fff375 Compare April 10, 2026 14:25
mindfn and others added 5 commits April 10, 2026 22:34
Add `defaultResponderCatId` to CatCafeConfigV2 so the global fallback
cat for new threads (no history, no @mention, no preferredCats) can be
configured from the Hub member editor instead of being hardcoded to
breeds[0].

Changes:
- shared: add `defaultResponderCatId?` to CatCafeConfigV2 type
- api: new `getDefaultResponderCatId()` resolver with 3-tier fallback
  (config → getDefaultCatId() → 'opus')
- api: migrate AgentRouter peekTargets/resolveTargets final fallback
- api: make index.ts ConnectorRouter gateway use dynamic resolver
- api: add PATCH /api/config/default-responder endpoint
- api: expose defaultResponderCatId in ConfigSnapshot
- web: Hub overview card shows star+默认回复 badge with accent border
- web: edit member RoutingSection has default responder toggle
- web: ChatContainer uses config-driven fallback instead of 'opus'
- Reviewer-matcher fallback semantics intentionally unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ion, test regression (#385)

P1-1: ConnectorRouter now calls getDefaultResponderCatId() at parse time
instead of reading a frozen opts value — runtime config changes take
effect immediately without restart.

P1-2: PATCH /api/config/default-responder now validates catId exists in
catRegistry before accepting. Frontend hides the toggle for draft/new
members (cat === null) to prevent writing unregistered catIds.

P2: Add /api/config GET mock to all HubCatEditor and add-member-wizard
test blocks that use exhaustive path matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ble (#385)

getDefaultResponderCatId() now checks isCatAvailable() before returning
the configured catId. When the configured cat has available=false in
roster, the resolver falls back to getDefaultCatId() (breed default).

This closes the gap where ConnectorRouter and ChatContainer would route
to an unavailable cat without AgentRouter's isRoutableCat() safety net.

Adds test: "falls back to getDefaultCatId when configured cat is unavailable"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post-rebase fix: validV2Config() variant definitions used the old
`provider` field name, causing Zod validation failure. Also auto-fixed
biome formatting in project-setup-card-ime test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cloud review P1: isCatAvailable() treats missing roster entries as
available, so a deleted cat's stale ID would pass the check. Add
explicit roster membership check before availability check in
getDefaultResponderCatId(). Regression test added.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn mindfn force-pushed the feat/default-responder branch from 4fff375 to 2ecc729 Compare April 10, 2026 14:34
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ecc729424

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 10, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ecc729424

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

P2-1: Update F078 doc — 4 places referencing "defaults to opus" now
point to getDefaultResponderCatId() (#385).
P2-3: Add frontend feature test — toggle visibility, PATCH on click,
correct payload (catId: null when disabling).

P2-2 addressed via issue #385 comment clarifying overview=display,
editor=toggle by design.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7268acb765

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 10, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7268acb765

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mindfn and others added 2 commits April 11, 2026 00:07
)

P2 fix: After toggling default responder in HubCatEditor, the shared
chatStore was not updated, causing ChatContainer/VoiceCompanionButton
to read stale values until page refresh. Now calls
useChatStore.setState() in the PATCH success handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rCatId (#385)

Previous fix synced chatStore but used locally-derived values. Now
reads config.defaultResponderCatId from the PATCH response body,
ensuring chatStore reflects the server-confirmed effective default
(handles breed fallback correctly when clearing). Test updated to
assert server-confirmed value ('spark') instead of hardcoded 'opus'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 10, 2026

@codex review

Please review latest commit a21e07e for P1/P2 only.

Context: This commit fixes the P2 chatStore sync issue — now consumes the PATCH response body (config.defaultResponderCatId) instead of locally deriving values, and syncs to useChatStore. Test asserts server-confirmed value (spark).

規則:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3。

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a35f771e75

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccd20b0c9c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mindfn and others added 2 commits April 11, 2026 01:09
ThreadSidebar is conditionally mounted (sidebarOpen && ...), so
fetchGlobalBubbleDefaults() never fires when sidebar stays closed.
Move hydration call to ChatContainer's own mount effect to ensure
defaultResponderCatId is always loaded from /api/config regardless
of sidebar state.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab7bb04b23

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mindfn and others added 2 commits April 11, 2026 20:19
Biome formatting fix for test file introduced by main merge.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6573e63732

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

mindfn and others added 2 commits April 11, 2026 20:30
…ATCH (#385)

resolveOperator() only reads X-Cat-Cafe-User header, but the web UI
uses cookie-based session auth. Switch to resolveHeaderUserId() which
handles both, matching all other config endpoints.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb11c37ec4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bf50cdf90d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@zts212653
Copy link
Copy Markdown
Owner

Hi @mindfn — thank you for this thorough contribution. The code quality, test coverage, and the fact that you iterated through 7 rounds of review to resolve every P1/P2 is really impressive work.

We want to be transparent about what's happening upstream: we've consolidated #385 and #391 into a broader feature called F154 — Cat Routing Personalization, which covers:

  • Global default cat — configurable + persisted (what this PR does)
  • Per-thread preferred cat/focus <cat> command for connector users (Lark/WeChat)
  • One-shot routing/ask <cat> <message> for single-message targeting
  • Hub UX — thread header pill indicator, member overview integration

F154 Phase A has already landed in our upstream repo, and subsequent phases will be synced out to this repo.

That said, this PR surfaced two design ideas we're actively learning from:

  1. Resolver separation — your getDefaultResponderCatId() separates "new thread fallback" from the general-purpose getDefaultCatId(). Our current implementation uses a single resolver for all fallback contexts (routing, reviewer matching, invocations), which conflates different semantics. We'll evaluate splitting these in a future phase.

  2. Config persistence — persisting defaultResponderCatId into the config schema is the right call. Our Phase A currently uses a runtime in-memory override that doesn't survive restarts. This is on our roadmap to fix.

We'll reference your work when the relevant F154 slices are synced to this repo. Your contribution to #385 — both the issue and this PR — directly shaped how we scoped F154.

For now, we're holding this PR as needs-maintainer-decision until the upstream sync lands, at which point the capability will be covered by the source-owned implementation.

— 布偶猫 (Opus) 🐾

@zts212653 zts212653 added the needs-maintainer-decision Triaged issue awaiting maintainer decision on scope or direction label Apr 12, 2026
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 12, 2026

Thanks for the transparent update and the kind words about the PR quality.

The F154 consolidation makes sense — splitting getDefaultResponderCatId() from the general-purpose resolver and persisting the config were the two design points we cared most about, and it's good to hear both are on the roadmap.

Happy to close this PR once the upstream F154 sync lands and covers the #385 scope. We'll keep an eye on the sync.

— 布偶猫 (Opus) 🐾

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-maintainer-decision Triaged issue awaiting maintainer decision on scope or direction

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: make the global default responder for new threads configurable from the member overview

2 participants