Skip to content

fix(web): consume provider_capability telemetry silently (refs #939 part A)#943

Merged
zts212653 merged 3 commits into
zts212653:mainfrom
enihcam:fix/web-render-system-info
Jun 17, 2026
Merged

fix(web): consume provider_capability telemetry silently (refs #939 part A)#943
zts212653 merged 3 commits into
zts212653:mainfrom
enihcam:fix/web-render-system-info

Conversation

@enihcam

@enihcam enihcam commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

When kimi-cli (and future providers) emit system_info with type: provider_capability, the chat dispatch chain had no specific branch for this telemetry. The message fell through to the default render path, displaying the raw JSON as a system bubble — the user saw "thinking → unavailable" as if it were an error.

This PR is the part A of the 3-way split for #939 (kimi auth dual-path + telemetry/UX fixups). It mirrors the F210-H1 pattern (agy_trajectory_progress): consume the telemetry silently and store it on the invocation snapshot for any future UI surface that wants to surface capability status.

Three-way split for #939

Part Scope Status
C kimi-cli auth dual-path (CAT_CAFE_KIMI_API_KEY / CAT_CAFE_KIMI_OAUTH_TOKEN / native credential file) 🔄 PR #942 (open, CI red, awaiting fix)
B Caller-attribution for unrecognised CLI errors (未识别的CLI错误) TBD
A provider_capability misrendered as error This PR

Changes

  • packages/web/src/stores/chat-types.ts (+18 lines)

    • New ProviderCapabilityReport type (status, reason, provider, receivedAt)
    • New providerCapabilities?: Record<string, ProviderCapabilityReport> field on CatInvocationInfo
  • packages/web/src/hooks/useAgentMessages.ts (+30 lines)

    • New dispatch branch for parsed.type === 'provider_capability' (placed after agy_trajectory_progress)
    • Read-merge-write into invocation.providerCapabilities so multiple capabilities coexist and later reports replace earlier ones (latest-wins)
    • Marks the message consumed = true → no addMessage → no user-visible bubble
    • Defensive type coercion: unknown status'unavailable'; missing fields → 'unknown' / ''
  • packages/web/src/hooks/__tests__/useAgentMessages-provider-capability.test.tsx (+274 lines, new file)

    • 5 test cases:
      1. kimi thinking: unavailable → no addMessage, setCatInvocation called with providerCapabilities.thinking
      2. image_input: limited → no addMessage, snapshot stored under image_input
      3. Multiple capabilities merge into same map without clobbering
      4. Later reports for same capability replace earlier (latest-wins)
      5. Unknown status coerces to 'unavailable' rather than rendering a bubble

Test results

$ node node_modules/vitest/vitest.mjs run src/hooks/__tests__/useAgentMessages-provider-capability.test.tsx
✓ 5 tests passed (30ms)

$ # also ran the full useAgentMessages family:
✓ 24 test files, 276 tests passed

Biome lint

Found 23 warnings.  (all pre-existing complexity warnings on
                      useAgentMessages.ts dispatch chain; my
                      addition does not introduce new errors)
Found 2 infos.
0 errors.

What this does NOT do

  • It does not add any user-visible UI for capability status. The data is stored on the invocation snapshot for later consumers (e.g., a debug panel, a per-provider settings page). Part A's job is only to stop misrendering the telemetry as an error.
  • It does not touch the backend emission (KimiAgentService.ts already emits provider_capability correctly — line 321-338 for image_input, line 402-418 for thinking).

Reviewer checklist

  • Verify parsed.catId ?? msg.catId resolution matches existing patterns (agy-progress branch).
  • Confirm read-merge-write against useChatStore.getState() is correct (the existing agy_trajectory_progress branch also uses useChatStore.getState().catInvocations?.[catId]?...).
  • Verify Date.now() for receivedAt is acceptable for an in-memory snapshot (no clock skew concerns).

Test plan for reviewer

cd packages/web
node node_modules/vitest/vitest.mjs run src/hooks/__tests__/useAgentMessages-provider-capability.test.tsx

Refs #939 (part A only). The umbrella issue #939 stays open until parts B and C are also addressed.


Maintainer note (2026-06-17): the status table below incorrectly listed PR #942 as merged; in reality #942 is still open with red CI. PR #944 (Kimi CLI binary fallback) is the only #939-cluster PR merged so far. Closing keyword in title/body changed from fixes to refs so #939 does not auto-close on this part-A merge.

…2653#939 part A)

When kimi-cli (and future providers) emit system_info with
`type: provider_capability`, the chat dispatch chain had no
specific branch. The message fell through to the default render
path, displaying the raw JSON as a system bubble — the user saw
"thinking → unavailable" as if it were an error.

This mirrors the F210-H1 pattern (agy_trajectory_progress):
consume the telemetry silently and store it on the invocation
snapshot for any future UI surface that wants to show capability
status.

Changes:
  - Add ProviderCapabilityReport type + providerCapabilities field
    on CatInvocationInfo (chat-types.ts).
  - Dispatch branch for type === 'provider_capability' that
    read-merges into invocation.providerCapabilities and marks
    the message consumed (no addMessage).
  - 5-case vitest suite covering: silent consumption, multi-cap
    merge, latest-wins, status coercion.

Part of the 3-way split for zts212653#939 (sub-issues A/B/C):
  - C: PR zts212653#942 (kimi auth dual-path) — merged.
  - B: caller-attribution for unrecognised errors — TBD.
  - A: this PR (telemetry misrendered as error).
@enihcam enihcam requested a review from zts212653 as a code owner June 16, 2026 13:59
@zts212653

Copy link
Copy Markdown
Owner

Thanks for taking the front-end Part A. First-pass inbound gate from our side:

Next pass will verify the current head against the Part A contract: provider_capability is consumed only as system_info telemetry, real error events still render, invocation snapshot merging preserves existing fields/multiple capabilities, and the targeted useAgentMessages tests plus the relevant hook test family pass.

[砚砚/gpt-5.5🐾]

@zts212653 zts212653 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.

Code review pass for the Part A implementation at 7057a29c.

What I verified:

  • provider_capability is consumed only as system telemetry and no longer falls through to addMessage.
  • Real system_info payloads that are not provider_capability keep the existing rendering path.
  • Multiple capabilities for the same cat are read-merge-written into providerCapabilities without clobbering each other.
  • parsed.catId ?? msg.catId matches nearby telemetry handler patterns.

Local verification in an isolated worktree:

cd packages/web
node node_modules/vitest/vitest.mjs run src/hooks/__tests__/useAgentMessages-provider-capability.test.tsx
# 5 tests passed

node node_modules/vitest/vitest.mjs run src/hooks/__tests__/useAgentMessages-*.test.ts*
# 24 files / 276 tests passed

This is still not merge-gateable until the PR title/body stop using closing keywords for the umbrella issue. Please change fixes #939 / Closes #939 to Refs #939 or Part of #939, and update the status table entry that says #942 is merged. After that wording fix, I do not see a code blocker for this PR.

[砚砚/gpt-5.5🐾]

@zts212653 zts212653 changed the title fix(web): consume provider_capability telemetry silently (fixes #939 part A) fix(web): consume provider_capability telemetry silently (refs #939 part A) Jun 17, 2026

@zts212653 zts212653 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.

Approving — second pass after the maintainer wording cleanup + branch update.

Verification:

  • Code review pass for the Part A implementation at 7057a29c was completed in my earlier comment chain (砚砚/gpt-5.5 review at #943 (review)... — provider_capability consumed only as system telemetry, multiple capabilities merged without clobbering, parsed.catId resolution matches existing patterns, useAgentMessages-* family 24 files / 276 tests pass).
  • Commit chain since: 7057a29c (initial, reviewed) → 9a700196 (merge from main by author) → 9b8a621c (merge from main via maintainer update-branch). No code delta since the reviewed SHA — both intermediate commits are pure main-integration merge commits.
  • Title/body wording cleanup at this iteration: fixes #939 part Arefs #939 part A (avoids premature umbrella close), and the status table line that incorrectly said ✅ PR #942 (merged) is now 🔄 PR #942 (open, CI red, awaiting fix) — the only PR in the cluster actually merged so far is #944 (Kimi CLI binary fallback, today).

CI on 9b8a621c is currently blocked on the first-time-contributor workflow approval (CI + Windows Smoke). Approving the runs now. Will squash-merge once CI passes.

[宪宪/opus-4.7🐾]

@zts212653 zts212653 merged commit 347330f into zts212653:main Jun 17, 2026
5 checks passed
mindfn pushed a commit to mindfn/clowder-ai that referenced this pull request Jun 18, 2026
…653#939 part A) (zts212653#943)

Refs zts212653#939 (part A only). The umbrella issue zts212653#939 stays open until parts B and C are also addressed.

Inbound review chain (community PR by @enihcam, FIRST_TIME_CONTRIBUTOR; self-review API blocks formal state on shared maintainer account so comments are the trail):
- 砚砚/gpt-5.5 code review pass on 7057a29 — provider_capability consumed only as system telemetry, multiple capabilities merge without clobbering, parsed.catId resolution matches existing patterns, useAgentMessages-* family 24 files / 276 tests pass
- 宪宪/opus-4.7 second-pass APPROVED on 9b8a621 — metadata wording cleanup (fixes zts212653#939 → refs zts212653#939; status table corrected — zts212653#944 not zts212653#942 is the merged member of the zts212653#939 cluster); no code delta since 7057a29 (newer commits are pure merge-from-main commits)

CI 5/5 pass: Build / Lint / Test (Public) / Test (Windows) / Directory Size Guard.

Opensource-ops B Patch self-merge: 4 conditions all met (accepted zts212653#939, safe-cherry-pick scope, CI 5/5 green, no toolchain/security impact — web frontend telemetry rendering only).

Thanks @enihcam for the careful F210-H1 pattern reuse and the targeted 5-case test file.

Co-authored-by: enihcam <enihcam@users.noreply.github.com>
mindfn added a commit to mindfn/clowder-ai that referenced this pull request Jun 22, 2026
zts212653#991 — drift-resolve sync skips new skills due to preserveGlobalCascade.
The condition in updateConfigAfterSync incorrectly skipped ALL skills
without local policy, including newly discovered ones. Add
existingProjectSkills guard so only skills already in the project config
are eligible for the cascade-preserve skip.

zts212653#966 — provider_capability missing in background dispatch chain.
PR zts212653#943 added the foreground handler for provider_capability system_info
but missed the background mirror in consumeBackgroundSystemInfo. Kimi
invocations running through background callbacks (A2A handoffs, unviewed
threads) still surfaced raw-JSON "thinking → unavailable" bubbles.
Add the matching background handler following the F210-H1 dual-handler
pattern.

Skill tab UI — sync status and batch toggle on the same line.
The "✓ Skill 同步一致" text and the batch enable/disable toggle were on
separate lines with redundant label text. Combine them into one flex row
(sync status left, toggle right) and unify the element order between the
global and project tabs so the project tab matches the global layout
(only adding the project selector dropdown).

Closes zts212653#991
Closes zts212653#966

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mindfn added a commit that referenced this pull request Jun 22, 2026
The condition in updateConfigAfterSync incorrectly skipped ALL skills
without local policy, including newly discovered ones. Add
existingProjectSkills guard so only skills already in the project config
are eligible for the cascade-preserve skip.

PR #943 added the foreground handler for provider_capability system_info
but missed the background mirror in consumeBackgroundSystemInfo. Kimi
invocations running through background callbacks (A2A handoffs, unviewed
threads) still surfaced raw-JSON "thinking → unavailable" bubbles.
Add the matching background handler following the F210-H1 dual-handler
pattern.

Skill tab UI — sync status and batch toggle on the same line.
The "✓ Skill 同步一致" text and the batch enable/disable toggle were on
separate lines with redundant label text. Combine them into one flex row
(sync status left, toggle right) and unify the element order between the
global and project tabs so the project tab matches the global layout
(only adding the project selector dropdown).

Closes #991
Closes #966

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mindfn added a commit that referenced this pull request Jun 22, 2026
The condition in updateConfigAfterSync incorrectly skipped ALL skills
without local policy, including newly discovered ones. Add
existingProjectSkills guard so only skills already in the project config
are eligible for the cascade-preserve skip.

PR #943 added the foreground handler for provider_capability system_info
but missed the background mirror in consumeBackgroundSystemInfo. Kimi
invocations running through background callbacks (A2A handoffs, unviewed
threads) still surfaced raw-JSON "thinking → unavailable" bubbles.
Add the matching background handler following the F210-H1 dual-handler
pattern.

Skill tab UI — sync status and batch toggle on the same line.
The "✓ Skill 同步一致" text and the batch enable/disable toggle were on
separate lines with redundant label text. Combine them into one flex row
(sync status left, toggle right) and unify the element order between the
global and project tabs so the project tab matches the global layout
(only adding the project selector dropdown).

Closes #991
Closes #966

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
zts212653 added a commit that referenced this pull request Jun 23, 2026
…stency (#993)

* fix: batch fix #991 + #966 + Skill tab UI consistency

The condition in updateConfigAfterSync incorrectly skipped ALL skills
without local policy, including newly discovered ones. Add
existingProjectSkills guard so only skills already in the project config
are eligible for the cascade-preserve skip.

PR #943 added the foreground handler for provider_capability system_info
but missed the background mirror in consumeBackgroundSystemInfo. Kimi
invocations running through background callbacks (A2A handoffs, unviewed
threads) still surfaced raw-JSON "thinking → unavailable" bubbles.
Add the matching background handler following the F210-H1 dual-handler
pattern.

Skill tab UI — sync status and batch toggle on the same line.
The "✓ Skill 同步一致" text and the batch enable/disable toggle were on
separate lines with redundant label text. Combine them into one flex row
(sync status left, toggle right) and unify the element order between the
global and project tabs so the project tab matches the global layout
(only adding the project selector dropdown).

Closes #991
Closes #966

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

* fix(#991): cascade-safe new skill registration + #966 background tests

P1 fix: Restore original preserveGlobalCascade condition for ALL
inherited-only skills (not just existing ones). New skills are collected
into cascadeNewSkills and registered in capabilities.json WITHOUT
mountPaths — so resolveEffectiveSkillMountPaths falls through to global
policy, preserving #962 cascade intent. mount-rules-route.test.js 23/23.

P2 fix: Add 4 background regression tests for #966 provider_capability
handler in consumeBackgroundSystemInfo:
- consumed=true, no addMessageToThread (no raw JSON bubble)
- Multi-capability merge without clobbering (read-merge-write)
- Unknown status coerces to 'unavailable'
- Empty-string catId fallback via || (msg.catId used)

Closes #991

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

* fix(#995): connector plugin routes use allowMissingOwner for single-user mode

requirePluginWriteAccess and requirePluginListAccess used requireConfiguredOwner: true,
which blocked IM connector install/list in local single-user mode (no DEFAULT_OWNER_USER_ID).
Changed to allowMissingOwner: true — consistent with plugin-routes.ts and the unified
owner gate pattern from #794.

Closes #995

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

* test(#995): flip connector plugin auth tests for allowMissingOwner

Old tests asserted 403 when DEFAULT_OWNER_USER_ID was unset — encoding the
regression behavior. New tests assert pass-through: install reaches file
validation (400 No file uploaded), list returns 200 with plugin data.

Existing coverage for session-auth, same-origin, cross-origin, and
configured non-owner rejection is preserved (13 other tests unchanged).

Red→Green: 15/15 pass.

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

* fix: resolve pre-existing brand guard violations + orphaned F207 ROADMAP ref

- Remove F207 row from ROADMAP (feature doc deleted for PII in #988,
  ROADMAP reference left behind → check:features failure on every branch)
- Port upstream brand strings to fork values: "Clowder AI" → "Cat Café"
  in layout.tsx, SplitPaneView.tsx, manifest.json, ChatContainerHeader.tsx,
  api-client.ts comment
- Fix connector-gateway-bootstrap.ts frontend port fallback: 3003 → 3001
  (fork uses 3001, upstream uses 3003)
- Fix brand expectation typo in intake-from-opensource.sh: must_contain
  was checking for 3003 instead of 3001

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

* fix: add missing check:biome-version script + JSDoc for owner gate pattern

Two pre-existing gaps resolved:

1. package.json: add check:biome-version script referenced by
   .githooks/pre-commit (introduced in sync #956 but never added to
   upstream package.json, breaking local commits).

2. capability-write-guards.ts: promote inline comments to JSDoc on
   requireCapabilityWriteOwner, documenting the #794 unified owner gate
   pattern (allowMissingOwner for writes vs requireConfiguredOwner for
   data-visibility). Prevents future recurrence of #995.

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

* fix(#992): recover stale ACP lease on session re-acquire instead of throwing

On Windows, closing the console during an active ACP prompt leaves the
child process alive but the lease unreleased (async generator finally
block never runs). The next acquire with the same sessionId hit
"already active on its owning process" and blocked forever.

Fix: when acquire() finds a session-owned entry with leaseCount > 0 on
a non-multiplexing carrier, force-release the orphaned lease instead of
throwing. This is safe because the same sessionId being re-acquired
proves the previous consumer is gone — the lease is a zombie.

Red→Green: new test simulates the zombie scenario (acquire + remember
session + skip release + re-acquire same sessionId). 24/24 pool tests
pass.

Closes #992

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

* fix(#992): add lease generation guard against late stale release

Review P1 from @codex: the force-release path reset leaseCount but the
old lease's release() closure still held a reference to the same entry.
A late-arriving release (async generator finally) would decrement the
new lease's count, triggering premature idle eviction.

Fix: add leaseGeneration counter to PoolEntry. createLease captures the
current generation at creation time; release() checks for mismatch and
becomes a no-op if the generation has been bumped by a force-release.
Also properly transitions through idle state in the force-release path
so idleProcessCount stays balanced.

Red→Green: new test simulates late release after stale recovery —
verifies metrics stay non-negative, new lease survives idle TTL, and
normal release still works. 25/25 pool tests pass.

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

* fix(#794): add requireLocalCapabilityWriteRequest to connector-plugins

The connector-plugins routes were missing layer 1 (loopback guard) of the
#794 three-layer security pattern. Both requirePluginWriteAccess and
requirePluginListAccess now match the reference implementation in
plugin-routes.ts: loopback guard → session auth → owner gate.

Tests updated to reflect the tightened security boundary:
- Remote-IP tests now expect 403 (loopback rejection)
- Cross-origin tests match the loopback guard error
- List endpoint tests provide loopback-valid origin headers
- Two new tests verify proxy-forwarded loopback rejection (X-Forwarded-For)

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

* revert(brand): rollback user-visible brand strings from 2eeb332

Reverts the brand changes from commit 2eeb332 that incorrectly replaced
"Clowder AI" with "Cat Café" in the clowder-ai repo. Per maintainer review
on PR #993, the correct brand mapping is: cat-cafe repo → "Cat Café",
clowder-ai repo → "Clowder AI".

Reverted files: manifest.json, layout.tsx, ChatContainerHeader.tsx,
SplitPaneView.tsx, api-client.ts, connector-gateway-bootstrap.ts.

Also disables conflicting BRAND_EXPECTATIONS entries in intake-from-opensource.sh
that enforced cat-cafe brand terms in the clowder-ai repo (mirrored verbatim
without per-repo parameterization), and fixes a bug in the Phase 2 brand guard
skip logic where `echo "${array[*]}"` joined all entries on one line, causing
the `^` anchor to only match the first array element.

Brand guard parameterization tracked for a separate follow-up PR.

[宪宪/claude-opus-4-6🐾]

* fix(#1002): deliver maintainer PR reviews instead of silently dropping them

ReviewFeedbackTaskSpec applied decideDelivery() from community-delivery-policy,
which silences OWNER/MEMBER activity — correct for Repo Inbox (F168) where
own team's activity is noise, but wrong for PR review tracking where the cat
explicitly registered to receive ALL reviewer feedback including maintainers.

Remove decideDelivery() calls from both comment and review filtering paths.
The existing isEchoComment + isNoiseComment + isEchoReview filters are
sufficient — they correctly filter self-authored echoes without silencing
maintainer reviews.

Closes #1002

[宪宪/claude-opus-4-6🐾]

* test(#1002): fix thread-rotation test for OWNER delivery change

Update review-feedback-thread-rotation test to expect OWNER comments
in newComments (no longer filtered after #1002 decideDelivery removal).
The routing audit behavior is unchanged — OWNER feedback is now
delivered alongside it.

[宪宪/claude-opus-4-6🐾]

* fix(brand-guard): align test with disabled 3003 port expectation

The BRAND_EXPECTATIONS entry for connector-gateway-bootstrap.ts was
disabled in this PR (3003 is the correct public frontend port for the
clowder-ai repo, not cat-cafe contamination). The test still asserted
3003 should be flagged — flip it to assert brand guard now allows it.

Addresses codex P2 review finding.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lysander Su <773678591@qq.com>
buproof pushed a commit to buproof/clowder-ai that referenced this pull request Jun 29, 2026
…653#939 part A) (zts212653#943)

Refs zts212653#939 (part A only). The umbrella issue zts212653#939 stays open until parts B and C are also addressed.

Inbound review chain (community PR by @enihcam, FIRST_TIME_CONTRIBUTOR; self-review API blocks formal state on shared maintainer account so comments are the trail):
- 砚砚/gpt-5.5 code review pass on 7057a29 — provider_capability consumed only as system telemetry, multiple capabilities merge without clobbering, parsed.catId resolution matches existing patterns, useAgentMessages-* family 24 files / 276 tests pass
- 宪宪/opus-4.7 second-pass APPROVED on 9b8a621 — metadata wording cleanup (fixes zts212653#939 → refs zts212653#939; status table corrected — zts212653#944 not zts212653#942 is the merged member of the zts212653#939 cluster); no code delta since 7057a29 (newer commits are pure merge-from-main commits)

CI 5/5 pass: Build / Lint / Test (Public) / Test (Windows) / Directory Size Guard.

Opensource-ops B Patch self-merge: 4 conditions all met (accepted zts212653#939, safe-cherry-pick scope, CI 5/5 green, no toolchain/security impact — web frontend telemetry rendering only).

Thanks @enihcam for the careful F210-H1 pattern reuse and the targeted 5-case test file.

Co-authored-by: enihcam <enihcam@users.noreply.github.com>
buproof pushed a commit to buproof/clowder-ai that referenced this pull request Jun 29, 2026
…653#995 + zts212653#1002 + Skill tab UI consistency (zts212653#993)

* fix: batch fix zts212653#991 + zts212653#966 + Skill tab UI consistency

The condition in updateConfigAfterSync incorrectly skipped ALL skills
without local policy, including newly discovered ones. Add
existingProjectSkills guard so only skills already in the project config
are eligible for the cascade-preserve skip.

PR zts212653#943 added the foreground handler for provider_capability system_info
but missed the background mirror in consumeBackgroundSystemInfo. Kimi
invocations running through background callbacks (A2A handoffs, unviewed
threads) still surfaced raw-JSON "thinking → unavailable" bubbles.
Add the matching background handler following the F210-H1 dual-handler
pattern.

Skill tab UI — sync status and batch toggle on the same line.
The "✓ Skill 同步一致" text and the batch enable/disable toggle were on
separate lines with redundant label text. Combine them into one flex row
(sync status left, toggle right) and unify the element order between the
global and project tabs so the project tab matches the global layout
(only adding the project selector dropdown).

Closes zts212653#991
Closes zts212653#966

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

* fix(zts212653#991): cascade-safe new skill registration + zts212653#966 background tests

P1 fix: Restore original preserveGlobalCascade condition for ALL
inherited-only skills (not just existing ones). New skills are collected
into cascadeNewSkills and registered in capabilities.json WITHOUT
mountPaths — so resolveEffectiveSkillMountPaths falls through to global
policy, preserving zts212653#962 cascade intent. mount-rules-route.test.js 23/23.

P2 fix: Add 4 background regression tests for zts212653#966 provider_capability
handler in consumeBackgroundSystemInfo:
- consumed=true, no addMessageToThread (no raw JSON bubble)
- Multi-capability merge without clobbering (read-merge-write)
- Unknown status coerces to 'unavailable'
- Empty-string catId fallback via || (msg.catId used)

Closes zts212653#991

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

* fix(zts212653#995): connector plugin routes use allowMissingOwner for single-user mode

requirePluginWriteAccess and requirePluginListAccess used requireConfiguredOwner: true,
which blocked IM connector install/list in local single-user mode (no DEFAULT_OWNER_USER_ID).
Changed to allowMissingOwner: true — consistent with plugin-routes.ts and the unified
owner gate pattern from zts212653#794.

Closes zts212653#995

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

* test(zts212653#995): flip connector plugin auth tests for allowMissingOwner

Old tests asserted 403 when DEFAULT_OWNER_USER_ID was unset — encoding the
regression behavior. New tests assert pass-through: install reaches file
validation (400 No file uploaded), list returns 200 with plugin data.

Existing coverage for session-auth, same-origin, cross-origin, and
configured non-owner rejection is preserved (13 other tests unchanged).

Red→Green: 15/15 pass.

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

* fix: resolve pre-existing brand guard violations + orphaned F207 ROADMAP ref

- Remove F207 row from ROADMAP (feature doc deleted for PII in zts212653#988,
  ROADMAP reference left behind → check:features failure on every branch)
- Port upstream brand strings to fork values: "Clowder AI" → "Cat Café"
  in layout.tsx, SplitPaneView.tsx, manifest.json, ChatContainerHeader.tsx,
  api-client.ts comment
- Fix connector-gateway-bootstrap.ts frontend port fallback: 3003 → 3001
  (fork uses 3001, upstream uses 3003)
- Fix brand expectation typo in intake-from-opensource.sh: must_contain
  was checking for 3003 instead of 3001

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

* fix: add missing check:biome-version script + JSDoc for owner gate pattern

Two pre-existing gaps resolved:

1. package.json: add check:biome-version script referenced by
   .githooks/pre-commit (introduced in sync zts212653#956 but never added to
   upstream package.json, breaking local commits).

2. capability-write-guards.ts: promote inline comments to JSDoc on
   requireCapabilityWriteOwner, documenting the zts212653#794 unified owner gate
   pattern (allowMissingOwner for writes vs requireConfiguredOwner for
   data-visibility). Prevents future recurrence of zts212653#995.

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

* fix(zts212653#992): recover stale ACP lease on session re-acquire instead of throwing

On Windows, closing the console during an active ACP prompt leaves the
child process alive but the lease unreleased (async generator finally
block never runs). The next acquire with the same sessionId hit
"already active on its owning process" and blocked forever.

Fix: when acquire() finds a session-owned entry with leaseCount > 0 on
a non-multiplexing carrier, force-release the orphaned lease instead of
throwing. This is safe because the same sessionId being re-acquired
proves the previous consumer is gone — the lease is a zombie.

Red→Green: new test simulates the zombie scenario (acquire + remember
session + skip release + re-acquire same sessionId). 24/24 pool tests
pass.

Closes zts212653#992

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

* fix(zts212653#992): add lease generation guard against late stale release

Review P1 from @codex: the force-release path reset leaseCount but the
old lease's release() closure still held a reference to the same entry.
A late-arriving release (async generator finally) would decrement the
new lease's count, triggering premature idle eviction.

Fix: add leaseGeneration counter to PoolEntry. createLease captures the
current generation at creation time; release() checks for mismatch and
becomes a no-op if the generation has been bumped by a force-release.
Also properly transitions through idle state in the force-release path
so idleProcessCount stays balanced.

Red→Green: new test simulates late release after stale recovery —
verifies metrics stay non-negative, new lease survives idle TTL, and
normal release still works. 25/25 pool tests pass.

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

* fix(zts212653#794): add requireLocalCapabilityWriteRequest to connector-plugins

The connector-plugins routes were missing layer 1 (loopback guard) of the
zts212653#794 three-layer security pattern. Both requirePluginWriteAccess and
requirePluginListAccess now match the reference implementation in
plugin-routes.ts: loopback guard → session auth → owner gate.

Tests updated to reflect the tightened security boundary:
- Remote-IP tests now expect 403 (loopback rejection)
- Cross-origin tests match the loopback guard error
- List endpoint tests provide loopback-valid origin headers
- Two new tests verify proxy-forwarded loopback rejection (X-Forwarded-For)

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

* revert(brand): rollback user-visible brand strings from 2eeb332

Reverts the brand changes from commit 2eeb332 that incorrectly replaced
"Clowder AI" with "Cat Café" in the clowder-ai repo. Per maintainer review
on PR zts212653#993, the correct brand mapping is: cat-cafe repo → "Cat Café",
clowder-ai repo → "Clowder AI".

Reverted files: manifest.json, layout.tsx, ChatContainerHeader.tsx,
SplitPaneView.tsx, api-client.ts, connector-gateway-bootstrap.ts.

Also disables conflicting BRAND_EXPECTATIONS entries in intake-from-opensource.sh
that enforced cat-cafe brand terms in the clowder-ai repo (mirrored verbatim
without per-repo parameterization), and fixes a bug in the Phase 2 brand guard
skip logic where `echo "${array[*]}"` joined all entries on one line, causing
the `^` anchor to only match the first array element.

Brand guard parameterization tracked for a separate follow-up PR.

[宪宪/claude-opus-4-6🐾]

* fix(zts212653#1002): deliver maintainer PR reviews instead of silently dropping them

ReviewFeedbackTaskSpec applied decideDelivery() from community-delivery-policy,
which silences OWNER/MEMBER activity — correct for Repo Inbox (F168) where
own team's activity is noise, but wrong for PR review tracking where the cat
explicitly registered to receive ALL reviewer feedback including maintainers.

Remove decideDelivery() calls from both comment and review filtering paths.
The existing isEchoComment + isNoiseComment + isEchoReview filters are
sufficient — they correctly filter self-authored echoes without silencing
maintainer reviews.

Closes zts212653#1002

[宪宪/claude-opus-4-6🐾]

* test(zts212653#1002): fix thread-rotation test for OWNER delivery change

Update review-feedback-thread-rotation test to expect OWNER comments
in newComments (no longer filtered after zts212653#1002 decideDelivery removal).
The routing audit behavior is unchanged — OWNER feedback is now
delivered alongside it.

[宪宪/claude-opus-4-6🐾]

* fix(brand-guard): align test with disabled 3003 port expectation

The BRAND_EXPECTATIONS entry for connector-gateway-bootstrap.ts was
disabled in this PR (3003 is the correct public frontend port for the
clowder-ai repo, not cat-cafe contamination). The test still asserted
3003 should be flagged — flip it to assert brand guard now allows it.

Addresses codex P2 review finding.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Lysander Su <773678591@qq.com>
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.

2 participants