Skip to content

fix(F070): governance banner self-heals when server reports healthy - #41

Open
bouillipx wants to merge 13 commits into
developfrom
fix/F070-governance-banner-self-validate
Open

fix(F070): governance banner self-heals when server reports healthy#41
bouillipx wants to merge 13 commits into
developfrom
fix/F070-governance-banner-self-validate

Conversation

@bouillipx

Copy link
Copy Markdown

Closes zts212653#1045

Summary

  • GovernanceBlockedCard now probes GET /api/governance/health on mount and asks the parent to remove the underlying transient message when the server reports status: 'healthy' for its projectPath.
  • ChatMessage wires the new onSelfClear callback to removeThreadMessage(currentThreadId, message.id).
  • Banner stays visible if the probe fails (network error) or governance is genuinely not-healthy — no regression for the original needs_bootstrap path.

Why

governance_blocked is a transient client-only message (added via addMessageToThread in useAgentMessages.ts, persisted into IDB by saveMessagesSnapshot, never persisted on the server). After POST /api/governance/confirm succeeds, the banner stays in store/IDB forever — every thread reload first-paints from IDB and the banner reappears.

Symptom users hit: amber "尚未初始化治理" banner for a project that was actually initialized; clicking "重试" returns Cannot retry invocation with status 'succeeded' because the original invocation completed long ago. Reproduced in thread thread_mqxva63tuebxj9sn where registry.confirmedByUser=true, CLAUDE.md has the managed block, preflight returns ready: true, and all 37 invocations show 0 GOVERNANCE errors.

Test plan

  • pnpm vitest run src/components/__tests__/governance-blocked-card.test.ts — 12/12 pass (5 new cases covering: healthy → onSelfClear called; not-healthy → not called; missing project → not called; network error → not called; no onSelfClear → no probe)
  • pnpm tsc --noEmit on packages/web — no new errors in GovernanceBlockedCard.tsx / ChatMessage.tsx (pre-existing errors in hub-cat-editor.test.tsx unrelated)
  • pnpm biome check on changed files — no new findings (pre-existing complexity/img/non-null warnings in ChatMessage.tsx unchanged)
  • Manual: hit affected thread in browser → banner disappears within a render cycle (verified the API via direct call: checkGovernancePreflight returns ready for the production case)

Follow-up (tracked in zts212653#1045, NOT in this PR)

  • Phase B: server emits governance_unblocked event on confirm success → frontend clears banners across all threads with that projectPath
  • Phase C: stop persisting governance_blocked to IDB; derive banner from (last invocation errorCode, server health) instead of a stored message

🤖 Generated with Claude Code

@bouillipx
bouillipx requested a review from zts212653 as a code owner June 29, 2026 01:35

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

Copy link
Copy Markdown

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: 36e84384c7

ℹ️ 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".

Comment thread packages/web/src/components/GovernanceBlockedCard.tsx Outdated

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

Copy link
Copy Markdown

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: 570a4119b7

ℹ️ 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".

Comment thread packages/web/src/stores/chatStore.ts
bouillipx added a commit that referenced this pull request Jul 2, 2026
Why: PR #41 was blocked only by Next build treating unused mock implementation parameters as errors. Vitest records call arguments even when the mock implementation declares no parameters, so the test can keep its assertions without tripping no-unused-vars.\n\n[砚砚/GPT-5.5🐾]\nThread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex
bouillipx and others added 3 commits July 7, 2026 23:09
…ts212653#1045)

Why: After `POST /api/governance/confirm` succeeds, the amber "尚未初始化治理"
banner stays in zustand store + IDB snapshot — server never persisted it and
no path clears it. Users see stale banners on every thread reload; clicking
"重试" hits 409 "Cannot retry invocation with status 'succeeded'" because
the underlying invocation completed long ago.

Fix (Phase A — minimal self-healing):
- GovernanceBlockedCard probes `GET /api/governance/health` on mount
- When server reports projectPath as `status: 'healthy'`, fire `onSelfClear`
- ChatMessage wires it to `removeThreadMessage(currentThreadId, message.id)`
- Only fires while `state === 'idle'` (don't fight the in-flight bootstrap
  state machine if user already clicked the button)
- Network failure → banner stays visible; user can still bootstrap manually

Follow-ups (NOT in this PR, tracked in zts212653#1045):
- Phase B: server emits `governance_unblocked` event on confirm success
  → frontend clears banners across all threads with that projectPath
- Phase C: stop persisting `governance_blocked` to IDB; derive from
  (last invocation errorCode, server health) instead of stored message

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…n (砚砚 R1)

Address砚砚 R1 P1/P2:

P1-1 — thread scoping: ChatMessage used global currentThreadId for the banner
self-clear, but ChatContainer already renders non-current threads via
useThreadMessages(threadId) (split-pane / background panes). With currentThreadId
clearing could silently target the wrong thread on message-id collisions and
fail to clear stale banners outside the active pane.
 - Add `threadId?: string` prop on ChatMessage (back-compat: fall back to
   currentThreadId when omitted).
 - ChatContainer.renderSingleMessage now passes `threadId={threadId}`.
 - Banner self-clear targets the *render* thread, not the active one.

P1-2 — IDB persistence: removeThreadMessage only mutated memory. The whole
point of this PR is killing a stale banner persisted in IDB; without writing
the snapshot the banner returns on next first-paint hydrate.
 - Active-thread branch now mirrors to threadStates via mirrorActiveFlat
   (KD-2: threadStates is the writer source).
 - Both branches schedule saveMessagesSnapshot(threadId, nextMessages, hasMore)
   fire-and-forget so the deletion lands in IDB.
 - No-op early-returns (message absent / threadState absent) intentionally
   skip the snapshot write.

P2 — import sort: reorder `useCallback, type CSSProperties` so Biome's
organizeImports is satisfied (`type CSSProperties, useCallback`).

Tests:
 - New: stores/__tests__/chatStore-remove-thread-message.test.ts (4 cases:
   active thread mirrors+persists, background thread persists, no-op cases).
 - Existing 16 governance-blocked-card + removeThreadMessage caller tests
   (useChatSocketCallbacks-delete, message-actions, useAgentMessages
   cross-thread-handoff, chatStore-multithread, chatStore-thread-runtime-
   writer) — 97/97 pass, no regression.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Why: PR #41 was blocked only by Next build treating unused mock implementation parameters as errors. Vitest records call arguments even when the mock implementation declares no parameters, so the test can keep its assertions without tripping no-unused-vars.\n\n[砚砚/GPT-5.5🐾]\nThread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex
@bouillipx
bouillipx force-pushed the fix/F070-governance-banner-self-validate branch from c7af53f to d1339f6 Compare July 7, 2026 15:10
@bouillipx
bouillipx changed the base branch from main to develop July 7, 2026 15:10
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review


P2 Badge Honor manifest providerId during routeable admission

When an agentProvider manifest declares a providerId that differs from the resource name, this approval path admits descriptor.name instead of the manifest claim. For example, a plugin can claim reserved providerId: openai while using name: my-provider and a non-reserved catId, and the reserved/collision checks never see the openai identity; projection repeats the same candidate construction, so the red-line recheck will not catch it later. Use descriptor.providerId ?? descriptor.name consistently in admission and snapshots.


const existing = await this.readExistingAgentProviderDescriptor(manifest.id, capId);

P2 Badge Preserve approvals under the capability lock

This reads the existing agentProvider descriptor before upsertCapabilityEntry acquires withCapabilityLock, so a concurrent approve-routeable request can write routeableApproved=true and fresh health after this stale read but before the upsert writes. In that interleaving, activation rewrites the row from the stale existing snapshot and silently drops the operator's approval even when the descriptor hash did not change. Move the read/merge/write for agentProvider activation into one capability-lock critical section.


const trimmed = value.trim();
if (trimmed.length === 0) return;
if (seen.has(trimmed)) return;
seen.add(trimmed);
ordered.push(trimmed);

P2 Badge Normalize routeable identities before admission checks

The admission gate preserves the original casing for claims and then performs exact Set.has checks, but mention routing elsewhere strips @ and lowercases aliases before matching. With an active cat alias like @Codex, a plugin can approve @codex (or the reverse) because the admission sets differ by case, yet both resolve to the same runtime mention and can shadow each other. Normalize the claim and snapshot identities with the same casing rules used by mention routing before deduping and collision checks.

ℹ️ 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".

bouillipx added a commit that referenced this pull request Jul 7, 2026
Why:\n- Fix PR #41 cloud P2 findings by normalizing routeable identities, honoring manifest providerId, and preserving approved descriptors across capability-lock activation.\n- Carry the PR #40 Directory Size Guard unblock by splitting packages/api/src/utils and renewing the governance/public-test guard entries.\n- Sync ROADMAP with active F207 so feature truth checks stay green.\n\nValidation:\n- git diff --check\n- git diff --cached --check\n- pnpm check\n- Prior handoff validation: targeted API tests/build passed; pnpm --dir packages/api run test:public failed only in sync-skills.sh due local main-worktree skill symlink state outside this PR.\n\n[砚砚/gpt-5.5🐾]
Why:
- Fix PR #41 cloud P2 findings by normalizing routeable identities, honoring manifest providerId, and preserving approved descriptors across capability-lock activation.
- Carry the PR #40 Directory Size Guard unblock by splitting packages/api/src/utils and renewing the governance/public-test guard entries.
- Sync ROADMAP with active F207 so feature truth checks stay green.

Validation:
- git diff --check
- git diff --cached --check
- pnpm check
- Prior handoff validation: targeted API tests/build passed; pnpm --dir packages/api run test:public failed only in sync-skills.sh due local main-worktree skill symlink state outside this PR.

[砚砚/gpt-5.5🐾]
@bouillipx
bouillipx force-pushed the fix/F070-governance-banner-self-validate branch from cf58d99 to 0ea3f60 Compare July 7, 2026 16:46

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

import { execFile } from 'node:child_process';

P2 Badge Keep the orphan cleaner source import working

Because packages/api/test/orphan-chrome-cleaner.test.js imports ../src/utils/orphan-chrome-cleaner.ts directly after registering tsx/esm, moving the implementation here without updating that test or leaving a shim makes the full API test script fail before any assertions; packages/api/package.json includes root test/*.test.js, so this rename blocks pnpm --dir packages/api test even though product imports were updated.

ℹ️ 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".

Why:
- Fix PR #41 cloud P2 feedback: orphan-chrome-cleaner.test.js imported the pre-split utils path and failed before assertions.
- After restoring the source import, keep the existing orphan cleaner contract green for go-rod Chromium and cached macOS Chromium helper binaries.

Validation:
- RED: pnpm --dir packages/api exec node --import tsx/esm --test test/orphan-chrome-cleaner.test.js failed with ERR_MODULE_NOT_FOUND.
- GREEN: pnpm --dir packages/api exec node --import tsx/esm --test test/orphan-chrome-cleaner.test.js (29 pass, 0 fail).
- pnpm --dir packages/api run build
- git diff --check
- git diff --cached --check
- rg old split-utils direct imports in packages/api/test: no matches
- pnpm check
- pnpm --dir packages/api test: no orphan cleaner import failure; run later timed out in test/capabilities-route.test.js at 60s, with 16408 tests / 16310 pass / 55 fail / 9 cancelled.

[砚砚/gpt-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

Addressed the current-head Codex P2 in 7b8f760ca.

  • orphan-chrome-cleaner.test.js now imports the split source path: ../src/utils/process/orphan-chrome-cleaner.ts.
  • While validating that import, the test reached its existing assertions; I kept those green by covering bare cached Chromium and Chromium Framework.framework/.../Chromium Helper binary paths in the parser.

Validation:

  • RED: pnpm --dir packages/api exec node --import tsx/esm --test test/orphan-chrome-cleaner.test.js failed with ERR_MODULE_NOT_FOUND.
  • GREEN: pnpm --dir packages/api exec node --import tsx/esm --test test/orphan-chrome-cleaner.test.js (29 pass, 0 fail).
  • pnpm --dir packages/api run build
  • git diff --check
  • git diff --cached --check
  • old split-utils direct-import grep in packages/api/test: no matches
  • pnpm check
  • pnpm --dir packages/api test no longer failed at orphan cleaner import; it later timed out in test/capabilities-route.test.js at 60s (16408 tests / 16310 pass / 55 fail / 9 cancelled), so that full-suite result is tracked as a separate timeout, not this P2.

@codex review

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

Copy link
Copy Markdown

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: 7b8f760ca8

ℹ️ 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".

Comment thread packages/web/src/components/GovernanceBlockedCard.tsx Outdated
Comment thread packages/web/src/stores/chatStore.ts
Why:
- PR #41 targets develop under the corrected branching model, but CI and Windows Smoke only matched pull_request targets for main.
- The current head therefore had zero check-runs/check-suites even after cloud review was requested.
- Keep push-to-main validation unchanged for the sync bot path while allowing develop-base PR validation.

Validation:
- Red: gh check-runs for 7b8f760 returned total_count=0.
- ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts "ok #{f}" }' .github/workflows/ci.yml .github/workflows/windows-smoke.yml
- git diff --check
- git diff --cached --check
- pnpm check

[砚砚/gpt-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

PR #41 follow-up pushed: 75ed239

Root cause for the empty statusCheckRollup was the same develop-base branching-model gap: CI and Windows Smoke only matched pull_request targets for main, while this PR targets develop.

Fix:

  • .github/workflows/ci.yml: pull_request branches now include develop
  • .github/workflows/windows-smoke.yml: pull_request branches now include develop
  • push-to-main CI remains unchanged for the sync bot path

Validation:

  • Red: check-runs for 7b8f760 returned total_count=0
  • YAML parse for both workflow files -> pass
  • git diff --check -> pass
  • git diff --cached --check -> pass
  • pnpm check -> pass

@codex review

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

Copy link
Copy Markdown

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: 75ed239519

ℹ️ 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".

Comment thread packages/web/src/components/GovernanceBlockedCard.tsx Outdated

@bouillipx bouillipx left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Review: ✅ No P1/P2 — Ready to Merge

Reviewer: 布偶猫/宪宪 (@Opus, claude-opus-4-6)
Cannot GitHub-approve: same account constraint. This is a full non-author review with no blocking findings.

Scope Assessment (3 concerns, all clean)

1. F070 Governance Banner Self-Healing (core fix)

  • GovernanceBlockedCard: Health probe in useEffect is well-structured — proper canceled cleanup flag, state === 'idle' guard prevents conflict with in-flight bootstrap, graceful network error handling (banner stays visible). ✅
  • ChatMessage: handleGovernanceBannerSelfClear correctly wrapped in useCallback with stable deps — prevents effect re-fire from callback identity churn. ✅
  • Thread scoping via explicit threadId prop (fallback to currentThreadId) addresses split-pane/background thread race correctly (砚砚 R2 P1). ✅
  • chatStore.removeThreadMessage: Now properly (a) mirrors active-thread deletion to threadStates via mirrorActiveFlat, and (b) persists to IDB snapshot. The zustand set() + post-set snapshot pattern works because set is synchronous — acceptable. ✅
  • Test coverage: 5 card tests + 4 store tests cover positive, negative, no-op paths. ✅

2. Agent Provider Routing Identity Normalization (cloud P2)

  • normalizeRouteableIdentityClaim: Correctly handles trim → strip @ → lowercase → empty guard. ✅
  • providerId ?? name precedence correct per manifest semantics. ✅
  • PluginResourceActivator refactor: Previous code had TOCTOU race (read existing descriptor before lock, concurrent approval could change it). Refactored to factory function buildAgentProvider(existing) called under the lock. Genuine race condition fix confirmed by new test. ✅

3. Utils Directory Split + Housekeeping

  • 100+ mechanical import renames — CI green confirms correctness. ✅
  • Barrel utils/index.ts re-routes correctly. upload-paths.ts adjusts __dirname relative path after nesting. ✅
  • Orphan chrome cleaner: Two broader Chromium regex patterns. ✅
  • .dir-exceptions.json: Cleaned entries below threshold, renewed rest to 2026-07-31. ✅
  • CI develop branch filter: Necessary for branching model. ✅

Verdict

No P1/P2 issues. All behavioral changes have test coverage. CI green on head 75ed239519. Safe to merge.

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

Why:
- registry-level health can be green while a project's preflight is still blocked
- GovernanceBlockedCard should only self-clear when /api/governance/status reports ready for the specific project
- add regression coverage for the registry-healthy but project-blocked path
@bouillipx

Copy link
Copy Markdown
Author

Current head 0450cf117 fixes the GovernanceBlockedCard cloud P2 by switching stale-banner self-clear from /api/governance/health to per-project /api/governance/status?projectPath=... and requiring ready === true before clearing.

Evidence:

  • pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts → 13/13 pass
  • git diff --check → clean
  • Browser verification on a temporary harness: /tmp/ready self-cleared, /tmp/blocked stayed visible with the bootstrap CTA

@codex review

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

Copy link
Copy Markdown

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: 0450cf1175

ℹ️ 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".

Comment thread packages/web/src/components/GovernanceBlockedCard.tsx Outdated
@bouillipx

Copy link
Copy Markdown
Author

@codex review

Why: cloud P2 found that background governance_blocked replacement persisted the remove-before-add intermediate snapshot, so reload could lose the actionable banner while dispatch remained blocked.

Validation: pnpm --dir packages/web exec vitest run src/stores/__tests__/chatStore-remove-thread-message.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts; pnpm check; pnpm --filter @cat-cafe/web build; git diff --check.

[砚砚/gpt-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

Addressed the current-head cloud P2 in eae97c4.\n\nFix:\n- Background governance_blocked replacement no longer calls removeThreadMessage + addMessageToThread.\n- replaceThreadMessages now has an explicit persist option for durable system-bubble replacement, writing one final snapshot instead of the intermediate no-banner state.\n- New regression test covers final-only snapshot persistence.\n\nValidation:\n- pnpm --dir packages/web exec vitest run src/stores/tests/chatStore-remove-thread-message.test.ts src/hooks/tests/useAgentMessages-background.test.ts\n- pnpm check\n- pnpm --filter @cat-cafe/web build\n- git diff --check

@bouillipx

Copy link
Copy Markdown
Author

@codex review

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

Copy link
Copy Markdown

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: eae97c4ac6

ℹ️ 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".

Comment thread packages/web/src/hooks/useAgentMessages.ts Outdated
Why: cloud review found that governance self-clear probes reused an unscoped status check while dispatch preflight is provider-scoped, and refreshed background banners stayed in their old transcript slot without a new unread signal.

What: carry the blocked dispatch clientId through governance_blocked payloads into the card status probe, let /api/governance/status evaluate the same provider scope, and replace stale background banners by appending the fresh banner at the transcript end with one final persisted snapshot.

Validation: pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts; pnpm --dir packages/api lint; pnpm check; pnpm --filter @cat-cafe/web build; git diff --check.

[砚砚/gpt-5.5🐾]
@bouillipx

Copy link
Copy Markdown
Author

Cloud P2 fixes pushed in 6038aed8b.

Red→Green:

  • Provider-scoped self-clear probe: added failing coverage in packages/web/src/components/__tests__/governance-blocked-card.test.ts, now passes. governance_blocked carries clientId from dispatch preflight through store/rendering, and /api/governance/status evaluates the same provider/client scope.
  • Background refreshed banner ordering: added failing coverage in packages/web/src/hooks/__tests__/useAgentMessages-background.test.ts, now passes. Background refresh removes stale same-project governance banners, appends the fresh banner at transcript end, persists one final snapshot, and increments unread.

Validation:

  • pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts
  • pnpm --dir packages/api lint
  • pnpm check
  • pnpm --filter @cat-cafe/web build
  • git diff --check

Note: scripts/check-fallback-layers.mjs is not present in this worktree, so that receive-review advisory check could not be run.

@bouillipx

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 6038aed8b1

ℹ️ 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".

Why: same-project governance status can differ by provider, so banner replacement must not hide a still-blocked provider CTA when another provider refreshes.
@bouillipx

Copy link
Copy Markdown
Author

Current head 39c05751d addresses the provider-scope governance banner replacement P2 found on 6038aed8.

Fix:

  • governance_blocked replacement now keys by normalized {projectPath, clientId}.
  • Same-project banners for different providers can coexist, so one provider refresh no longer hides another provider's still-blocked CTA.
  • Legacy persisted banners without clientId remain project-scoped and are replaced by the next scoped banner for compatibility.
  • Active and background receive paths use the same scope helper.

Validation:

  • pnpm --dir packages/web exec vitest run src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts -> 84/84 pass
  • pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts -> 98/98 pass
  • pnpm check -> pass
  • pnpm --filter @cat-cafe/web build -> pass with existing warnings only
  • git diff --check -> clean

Note: scripts/check-fallback-layers.mjs is not present in this worktree, so that receive-review advisory check could not be run.

@bouillipx

Copy link
Copy Markdown
Author

@codex review

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

Copy link
Copy Markdown

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: 39c05751d8

ℹ️ 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".

(async () => {
try {
const params = new URLSearchParams({ projectPath });
if (clientId) params.set('clientId', clientId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Require provider scope before clearing banners

When clientId is absent, which is true for governance banners already persisted before this change, this still probes /api/governance/status without a provider scope. The backend checkGovernancePreflight then falls back to the Claude file/skills check, so a legacy OpenAI/Gemini/Kimi banner raised because AGENTS.md/GEMINI.md/KIMI.md was missing can be removed as soon as Claude governance is healthy, even though the next dispatch for that provider will still be blocked. Please either skip self-clear for unscoped legacy banners or make the unscoped status validate all provider files before returning ready.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in head 62b4e892e. GovernanceBlockedCard now requires a non-empty trimmed clientId before running the self-heal status probe, so legacy persisted banners without provider scope do not clear against the backend default provider. Provider-scoped banners still probe /api/governance/status?...&clientId=... and only clear on ready === true. Regression coverage added in packages/web/src/components/__tests__/governance-blocked-card.test.ts for the legacy unscoped no-probe/no-clear path.

Comment on lines 5186 to 5189
.messages.find((m) => isSameGovernanceBlockedScope(m, projectPath, clientId));
if (existingBlocked) {
removeMessage(existingBlocked.id);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist active banner refreshes atomically

When the active thread receives a refreshed governance_blocked event for the same project/provider, this path still removes and re-adds only the flat active message list. Unlike the background path added in this change, it does not mirror through threadStates or write the updated snapshot to IndexedDB until a later thread switch, so reloading right after the refresh can first-paint the old persisted banner (often without the new clientId) and resurrect stale UI. Use the thread-scoped replacement path with persist: true here as well.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in head 62b4e892e. The active governance_blocked receive path now builds the final scoped banner list and calls replaceThreadMessages(threadId, nextMessages, nextHasMore, { persist: true }) in one operation. With a resolvable thread id it no longer does removeMessage(...) followed by addMessage(...), so IndexedDB receives the fresh final snapshot instead of an intermediate/old banner state. Regression coverage added in packages/web/src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts asserts no remove/add calls and the persisted thread-scoped replacement.

Why: legacy unscoped governance banners cannot prove provider readiness, and active refreshes need an atomic persisted thread snapshot to avoid resurrecting stale retry prompts.
@bouillipx

Copy link
Copy Markdown
Author

Current-head cloud P2 fixes pushed in 62b4e892e4d352c7f0162e2077d1b8e3eecf502c.

Fixes:

  • GovernanceBlockedCard now skips stale-banner self-heal for legacy persisted banners without a non-empty clientId, because provider readiness cannot be proven safely from an unscoped status probe.
  • Active-thread governance_blocked refresh now writes one persisted thread-scoped replacement snapshot via replaceThreadMessages(..., { persist: true }) instead of remove/add, matching the durable background path.

Red to green evidence:

  • Red was confirmed before the production fix for both current-head P2s: legacy unscoped banners still probed/cleared, and active refresh still called remove before add.
  • Green: pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts src/stores/__tests__/chatStore-remove-thread-message.test.ts passed 105/105.
  • git diff --check clean.
  • pnpm check passed.
  • pnpm --filter @cat-cafe/web build passed with existing warnings only.
  • Fallback-layer advisory check was not run because scripts/check-fallback-layers.mjs is absent in this worktree.

Both current-head inline P2 threads were also replied with targeted fix notes.

@bouillipx

Copy link
Copy Markdown
Author

@codex review

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

Copy link
Copy Markdown

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: 62b4e892e4

ℹ️ 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".

useEffect(() => {
if (!onSelfClear || state !== 'idle') return;
const scopedClientId = clientId?.trim();
if (!scopedClientId) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Probe legacy banners before returning

For users who already have a stale governance_blocked banner in IndexedDB, this guard skips the self-heal entirely because clientId is a new field added by this change and older persisted messages only have projectPath/reasonKind/invocationId. In that upgrade/reload scenario no /api/governance/status request is made and removeThreadMessage is never called, so the exact stale banner this fix is meant to clear remains stuck until another governance event happens. Please migrate/infer the scope or otherwise handle legacy unscoped banners instead of returning here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in head c73696372. Legacy persisted banners without clientId no longer return without self-heal, but they also do not use the unsafe unscoped/default-provider status probe. The card now probes the four governance provider scopes (anthropic, openai, google, kimi) via scoped /api/governance/status?...&clientId=... requests and clears the legacy banner only when every scope reports ready === true. If any provider scope is still blocked or a probe fails, the banner stays visible. Regression coverage added for both all-ready legacy clear and one-provider-blocked no-clear paths.

Why: legacy governance_blocked messages lack provider scope, so self-heal must avoid default-provider false clears while still removing stale upgraded banners once every governance provider scope is ready.
@bouillipx

Copy link
Copy Markdown
Author

Current-head cloud P2 fix pushed in c73696372f69f20210c8c44f8b8cef4a2bd1453c.

Fix:

  • Legacy persisted governance_blocked banners without clientId no longer skip self-heal forever.
  • They also do not use an unsafe unscoped/default-provider status probe. The card probes anthropic, openai, google, and kimi scoped /api/governance/status?...&clientId=... and clears only when all scopes return ready === true.
  • Scoped banners with clientId still probe only their own provider scope.

Red→Green:

  • Red: governance-blocked-card.test.ts failed because legacy banners made zero status calls and never self-cleared.
  • Green: pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts16/16 pass.

Validation:

  • pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts src/stores/__tests__/chatStore-remove-thread-message.test.ts106/106 pass.
  • git diff --check clean.
  • pnpm check pass.
  • pnpm --filter @cat-cafe/web build pass with existing warnings only.
  • Fallback-layer advisory check was not run because scripts/check-fallback-layers.mjs is absent in this worktree.

Replied to the current-head inline P2 at #41 (comment).

@bouillipx

Copy link
Copy Markdown
Author

@codex review

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

Copy link
Copy Markdown

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: c73696372f

ℹ️ 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".

});
};
const nextMessages = [...nextMessagesWithoutStaleBanner, nextBanner];
options.store.replaceThreadMessages(msg.threadId, nextMessages, threadState.hasMore, { persist: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3 Badge Stamp background governance refreshes as activity

When a governance_blocked event arrives for an inactive/background thread, this new path rewrites the message list via replaceThreadMessages and then only calls incrementUnread. Unlike the previous removeThreadMessage + addMessageToThread flow, neither of these calls updates threadStates[threadId].lastActivity, while ThreadSidebar uses that live lastActivity to merge into lastActiveAt before sorting. The result is that a newly blocked background thread can get an unread badge but remain ordered by its old activity until the thread list is refetched; stamp lastActivity when this replacement actually adds/replaces the banner.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in head 2fdf09309. Background governance refreshes still replace/persist the final banner snapshot and increment unread, but incrementUnread now also stamps lastActivity, so inactive threads with fresh governance prompts move according to the new activity instead of retaining the stale sidebar sort timestamp. Red→Green coverage added in packages/web/src/hooks/__tests__/useAgentMessages-background.test.ts by seeding an old lastActivity and asserting the refreshed background banner advances it.

Why: Background governance refreshes now use replaceThreadMessages plus incrementUnread, bypassing the old addMessageToThread activity stamp. Updating unread activity at the store boundary keeps sidebar ordering aligned with newly blocked background threads.

Validation: pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts src/stores/__tests__/chatStore-remove-thread-message.test.ts

Validation: pnpm check

Validation: pnpm --filter @cat-cafe/web build

Validation: git diff --check

[砚砚/gpt-5.5🐾]

Thread-Context: threadId=thread_mqcj45byxoka2z7u catId=codex
@bouillipx

Copy link
Copy Markdown
Author

Current-head P3 fix pushed in 2fdf09309ad0dc7be0df0d90a4f984ebf27bd6cd.

Fix:

  • Background governance refreshes still use final-snapshot replaceThreadMessages(..., { persist: true }) plus unread bump.
  • incrementUnread now also stamps lastActivity, so inactive threads with fresh governance prompts sort by the new activity instead of retaining stale sidebar recency.

Red→Green:

  • Red: useAgentMessages-background.test.ts failed because refreshed background governance banners left lastActivity equal to the seeded stale timestamp.
  • Green: targeted F070 background test passes after the store activity stamp.

Validation:

  • pnpm --dir packages/web exec vitest run src/components/__tests__/governance-blocked-card.test.ts src/hooks/__tests__/useAgentMessages-background.test.ts src/hooks/__tests__/useAgentMessages-thread-dispatch.test.ts src/stores/__tests__/chatStore-remove-thread-message.test.ts106/106 pass.
  • pnpm check → pass.
  • pnpm --filter @cat-cafe/web build → pass with existing warnings only.
  • git diff --check → clean.
  • scripts/check-fallback-layers.mjs is absent in this worktree, so the advisory fallback-layer check could not be run.
  • pnpm check:architecture-ownership is not defined in this open-source worktree.

Replied to the current-head inline P3 at #41 (comment).

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 2fdf09309a

ℹ️ 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".

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.

F070: governance_blocked banner lingers after治理已就绪

1 participant