fix: QuickEntry agent outside-click + quarantine CLI full-suite cascade (#2291)

## Summary
Latest Full Suite after #2290 was green on shards 1–2 and nearly green
on 3–4:

- **Shard 3:** QuickEntry agent picker outside click left the portal
open (product) — capture-phase mousedown + open-token so late
`fetchAgents` cannot re-open a dismissed picker
- **Shard 4:** `@runfusion/fusion` package-lane cascade (87 failures
from `extension-dist-barrel` hookTimeout + lock-retry timeouts under
load) — quarantine the 14 observed files on sight (ledger + vitest
exclude), no timeout appeasement

Also hardens the agent-picker outside-click test.

## Test plan
- [x] Local agent picker portal tests green
- [ ] PR gate
- [ ] Post-merge Full Suite green

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Fixed the Quick Add agent picker so it reliably closes when clicking
outside.
- Prevented delayed agent-loading results from reopening the picker
after it has been dismissed.
- Improved the picker’s loading behavior by displaying it immediately
while agents are being retrieved.

- **Tests**
- Added coverage for dismissing the agent picker with an outside click.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
gsxdsm
2026-07-18 01:54:27 -07:00
committed by GitHub
parent d88e4f9c90
commit c0cce18cfb
5 changed files with 137 additions and 9 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Close the Quick Add agent picker when clicking outside it.
category: fix
dev: Capture-phase mousedown listener plus open-token so late fetchAgents cannot re-open a dismissed portal.

View File

@@ -98,6 +98,27 @@ const quarantinedCliTests: string[] = [
FNXC:CliTests 2026-07-16-09:00:
FN-8077 removed project-context.test.ts from this list and the ledger in lockstep. Its CentralCore coverage now uses the external PostgreSQL test harness under pgDescribe rather than launching an embedded postmaster for each test in forked loaded lanes; pure formatting coverage remains ungated.
*/
/*
FNXC:CliTests 2026-07-18-08:50:
Full-suite shard 4 (runs 29637256028, 29637376023) re-exposed package-lane cascade:
extension-dist-barrel beforeAll hookTimeout under PG load, plus lock-retry timeouts,
cascading 87 failures. Quarantine the observed integration-heavy files on sight in
lockstep with scripts/lib/test-quarantine.json — do not raise hookTimeout/testTimeout.
*/
"src/__tests__/bin.test.ts",
"src/__tests__/extension-agent-update.test.ts",
"src/__tests__/extension-dist-barrel.test.ts",
"src/__tests__/extension-goal-tools.test.ts",
"src/__tests__/extension-mission-goal-tools.test.ts",
"src/__tests__/extension-tool-timeout.test.ts",
"src/__tests__/extension-workflow-tools.test.ts",
"src/__tests__/extension.test.ts",
"src/__tests__/task-plan.test.ts",
"src/commands/__tests__/mcp-lock-retry.test.ts",
"src/commands/__tests__/plugin.test.ts",
"src/commands/__tests__/task-lock-retry.test.ts",
"src/commands/dashboard-tui/__tests__/app.test.tsx",
"src/commands/dashboard-tui/__tests__/terminal-attach.test.ts",
];
export default defineConfig({

View File

@@ -191,6 +191,8 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
const modelMenuPortalRef = useRef<HTMLDivElement>(null);
const agentPickerRef = useRef<HTMLDivElement>(null);
const agentPickerPortalRef = useRef<HTMLDivElement>(null);
/** Bumps on open/close so a late fetchAgents resolution cannot re-open a dismissed picker. */
const agentPickerOpenTokenRef = useRef(0);
const nodePickerRef = useRef<HTMLDivElement>(null);
const nodePickerPortalRef = useRef<HTMLDivElement>(null);
const priorityPickerRef = useRef<HTMLDivElement>(null);
@@ -554,17 +556,25 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
useEffect(() => {
if (!showAgentPicker) return;
/*
FNXC:QuickEntry 2026-07-18-08:45:
Listen in the capture phase so outside mousedown closes the agent portal even
when a nested control calls preventDefault/stopPropagation on bubble. Full-
suite + local tests observed the bubble-only listener leaving "Select agent"
mounted after a true outside click.
*/
const handleClickOutside = (e: MouseEvent) => {
const target = e.target as Node;
// Check both the trigger button and the portaled dropdown
if (agentPickerRef.current?.contains(target)) return;
if (agentPickerPortalRef.current?.contains(target)) return;
agentPickerOpenTokenRef.current += 1;
setShowAgentPicker(false);
setAgentPickerPosition(null);
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
document.addEventListener("mousedown", handleClickOutside, true);
return () => document.removeEventListener("mousedown", handleClickOutside, true);
}, [showAgentPicker]);
useEffect(() => {
@@ -1637,24 +1647,38 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
const loadAgents = useCallback(async () => {
if (agents.length > 0 && agentsProjectId === projectId) {
agentPickerOpenTokenRef.current += 1;
setShowAgentPicker(true);
updateAgentPickerPosition();
return;
}
/*
FNXC:QuickEntry 2026-07-18-08:45:
Open the picker immediately (with loading) so outside-click listeners arm
before fetchAgents resolves. Bump a token on open/close so a late fetch
does not re-open the portal after the operator dismissed it.
*/
const openToken = ++agentPickerOpenTokenRef.current;
setAgentsLoading(true);
setShowAgentPicker(true);
updateAgentPickerPosition();
try {
const result = await fetchAgents(undefined, projectId);
if (openToken !== agentPickerOpenTokenRef.current) return;
setAgents(result);
setAgentsProjectId(projectId);
setShowAgentPicker(true);
updateAgentPickerPosition();
} catch (err) {
if (openToken !== agentPickerOpenTokenRef.current) return;
const msg = getErrorMessage(err);
addToast(msg ? t("tasks.loadAgentsFailed", "Failed to load agents: {{msg}}", { msg }) : t("tasks.loadAgentsFailedGeneric", "Failed to load agents"), "error");
setShowAgentPicker(false);
} finally {
setAgentsLoading(false);
if (openToken === agentPickerOpenTokenRef.current) {
setAgentsLoading(false);
}
}
}, [agents.length, agentsProjectId, projectId, addToast, updateAgentPickerPosition]);

View File

@@ -4955,13 +4955,19 @@ describe("QuickEntryBox", () => {
const outsideElement = document.createElement("div");
document.body.appendChild(outsideElement);
try {
fireEvent.mouseDown(outsideElement);
/*
FNXC:DashboardTests 2026-07-18-08:45:
Dispatch a bubbling native MouseEvent so the document mousedown listener
in QuickEntryBox sees the outside target (RTL fireEvent alone was flaky
under full-suite and failed locally for this agent portal).
*/
outsideElement.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true }));
await waitFor(() => {
expect(screen.queryByText("Select agent")).toBeNull();
});
} finally {
document.body.removeChild(outsideElement);
if (outsideElement.parentNode) document.body.removeChild(outsideElement);
}
// Picker should be closed
expect(screen.queryByText("Select agent")).toBeNull();
});
it("repositions portaled picker on window resize while open", async () => {

View File

@@ -1,5 +1,5 @@
{
"$comment": "Flaky-test quarantine ledger (deletion ratchet — see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date — the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config is the enforcement.",
"$comment": "Flaky-test quarantine ledger (deletion ratchet \u2014 see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date \u2014 the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config is the enforcement.",
"entries": [
{
"file": "packages/engine/src/__tests__/backlog-pressure-reporter.test.ts",
@@ -40,6 +40,76 @@
"file": "packages/engine/src/__tests__/heartbeat-error-recovery.test.ts",
"reason": "Full-suite shard 1 timeout (30s) on rotation-shaped 401 recovery under load without product bug evidence. Failing run: https://github.com/Runfusion/Fusion/actions/runs/29636550951. Mirrored in packages/engine/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/bin.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-agent-update.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-dist-barrel.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-goal-tools.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-mission-goal-tools.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-tool-timeout.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension-workflow-tools.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/extension.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/__tests__/task-plan.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/commands/__tests__/mcp-lock-retry.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/commands/__tests__/plugin.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/commands/__tests__/task-lock-retry.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/commands/dashboard-tui/__tests__/app.test.tsx",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
},
{
"file": "packages/cli/src/commands/dashboard-tui/__tests__/terminal-attach.test.ts",
"reason": "Full-suite shard 4 package-lane cascade (run 29637376023 / 29637256028): extension-dist-barrel beforeAll hookTimeout 10s + lock-retry 5s timeouts under shard load, cascading 87 failures across CLI integration suites without product-bug evidence. Quarantine on sight per AGENTS.md. Mirrored in packages/cli/vitest.config.ts.",
"quarantinedAt": "2026-07-18"
}
]
}