Skip to content

fix: resolve agent-tools.cy.ts E2E test flakiness#2042

Merged
nick-inkeep merged 1 commit intomainfrom
fix/cypress-agent-tools-flaky
Feb 16, 2026
Merged

fix: resolve agent-tools.cy.ts E2E test flakiness#2042
nick-inkeep merged 1 commit intomainfrom
fix/cypress-agent-tools-flaky

Conversation

@nick-inkeep
Copy link
Collaborator

@nick-inkeep nick-inkeep commented Feb 16, 2026

Summary

Fixes the flaky "Editing sub-agent ID should not removes linked tools" test in agent-tools.cy.ts which failed ~30% of the time in CI with Found '2', expected '3' after save+reload.

Root Cause

Three contributing factors:

  1. No verification of drag-and-drop successdragNode() triggered drag events but never asserted a new node was created. Silent drag failures meant the test proceeded with fewer nodes than expected.
  2. No wait for save completion before reloadsaveAndAssert() waited for the "Agent saved" toast but immediately called cy.reload() without ensuring the server action response was fully processed. The save uses Next.js server actions (POST to current page URL), not a direct PUT to the API.
  3. Insufficient timeout after reload — the default 4s Cypress timeout was not enough for CI to load JS, fetch agent data, and render React Flow nodes.

Changes

  • Assert node count after each dragNode() call (1→2→3) to catch drag failures early
  • Use cy.intercept('POST', '**/agents/*') + cy.wait('@saveAgent') to deterministically wait for the Next.js server action save response before reloading
  • Wait for "Agent saved" toast after the intercept to confirm UI processed the response
  • Increase post-reload assertion timeout to 10s

Test plan

  • agent-tools.cy.ts passes consistently in CI (all 3 retry attempts)
  • No other E2E tests regressed
  • Format/JSON and Format/JavaScript tests still pass

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Feb 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agents-api Ready Ready Preview, Comment Feb 16, 2026 6:25pm
agents-manage-ui Ready Ready Preview, Comment Feb 16, 2026 6:25pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agents-docs Skipped Skipped Feb 16, 2026 6:25pm

Request Review

@changeset-bot
Copy link

changeset-bot bot commented Feb 16, 2026

⚠️ No Changeset found

Latest commit: 302aaf2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(0) Total Issues | Risk: Low

This is a well-executed fix for E2E test flakiness. The changes correctly address all three identified root causes:

  1. Explicit node count assertions after each dragNode() call catch silent drag failures early (1→2→3)
  2. API response waiting via cy.intercept + cy.wait('@saveAgent') ensures data is persisted before reload
  3. Increased timeouts (10s) accommodate CI environment variability

The implementation follows Cypress best practices and is minimal/targeted.

💭 Consider (1) 💭

💭 1) agent-tools.cy.ts:45 Add response status assertion to API wait

Issue: The cy.wait('@saveAgent') doesn't validate the response status code. If the PUT returns a 500 error, the test proceeds to reload and fails with a confusing "expected 3 nodes" error rather than indicating the actual API failure.

Why: Better error messages during future debugging — API failures would be caught at the source rather than manifesting as downstream assertion failures.

Fix: Consider adding status validation:

cy.wait('@saveAgent').its('response.statusCode').should('eq', 200);

Refs: Cypress wait assertions docs


✅ APPROVE

Summary: Clean, targeted fix that addresses the root causes of E2E flakiness with appropriate Cypress patterns. The single "Consider" item is a minor improvement for debugging ergonomics — the current implementation is already robust. Ship it! 🚀

Discarded (2)
Location Issue Reason Discarded
agent-tools.cy.ts:43 Intercept setup inside function vs beforeEach Theoretical concern only — the intercept is registered synchronously before the click. Reviewer acknowledged "current placement works."
agent-tools.cy.ts:65 Format/JavaScript test lacks post-drag assertion Out of scope — this PR fixes a specific flaky test. The Format test has implicit verification via subsequent Monaco typing.
Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-standards 0 0 0 0 0 0 0
pr-review-tests 3 0 1 0 0 0 2
Total 3 0 1 0 0 0 2

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(1) Total Issues | Risk: Medium

🟠⚠️ Major (1) 🟠⚠️

Inline Comments:

  • 🟠 Major: agent-tools.cy.ts:45 Hardcoded cy.wait(500) instead of documented cy.intercept + cy.wait('@saveAgent') approach

Summary of Concerns

The PR description explicitly states that the fix uses cy.intercept('PUT', '**/agent/**') + cy.wait('@saveAgent') to wait for the save API response before reloading. However, the actual implementation uses cy.wait(500) — a hardcoded delay that is a known Cypress anti-pattern.

What's good about this PR:

  • ✅ Explicit node count assertions after each dragNode() call (1→2→3) — this is a solid improvement
  • ✅ Increased timeouts (10s) for CI environment variability

What needs addressing:

  • ❌ The cy.wait(500) hardcoded delay may still cause flakiness if the save API takes >500ms in slow CI environments
  • ❌ The implementation doesn't match the documented fix approach

Recommendation

Replace the hardcoded wait with the intercept-based approach as documented in the PR description:

function saveAndAssert() {
  cy.intercept('PUT', '**/agents/**').as('saveAgent');
  cy.contains('Save changes').click();
  cy.wait('@saveAgent').its('response.statusCode').should('eq', 200);
  cy.reload();
  cy.get('.react-flow__node', { timeout: 10000 }).should('have.length', 3);
}

🚫 REQUEST CHANGES

Summary: The PR makes good improvements (explicit assertions, increased timeouts), but the core fix for the save-before-reload race condition uses a hardcoded delay instead of the documented API intercept approach. This may reduce flakiness but doesn't eliminate it. Please implement the intercept pattern as stated in the PR description.

Discarded (0)

No findings were discarded.

Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-tests 1 0 0 0 1 0 0
pr-review-standards 1 0 0 0 0 0 0
Total 2 0 0 0 1 0 0

Note: Both reviewers identified the same issue; findings were merged per semantic deduplication.

cy.contains('Save changes').click();
cy.contains('Agent saved').should('exist');
cy.contains('Agent saved', { timeout: 10000 }).should('exist');
cy.wait(500);
Copy link
Contributor

Choose a reason for hiding this comment

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

🟠 MAJOR: Hardcoded wait vs documented intercept approach

Issue: The PR description states: "Use cy.intercept('PUT', '**/agent/**') + cy.wait('@saveAgent') to wait for the save response before reloading" — but the actual implementation uses cy.wait(500), a hardcoded delay.

Why: Hardcoded waits are a Cypress anti-pattern that introduces residual flakiness:

  • 500ms may be insufficient in slow CI environments under load, causing the original Found '2', expected '3' failure to recur
  • It's wastefully slow in fast environments
  • Fails silently if the save takes longer than expected

Fix: Implement the approach documented in the PR description:

function saveAndAssert() {
  cy.intercept('PUT', '**/agents/**').as('saveAgent');
  cy.contains('Save changes').click();
  cy.wait('@saveAgent').its('response.statusCode').should('eq', 200);
  cy.reload();
  cy.get('.react-flow__node', { timeout: 10000 }).should('have.length', 3);
}

This deterministically waits for the API response and fails fast with a clear error if the API returns an error status.

Refs:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in the latest commit (302aaf2 → now rebased). Replaced cy.wait(500) with cy.intercept('POST', '**/agents/*').as('saveAgent') + cy.wait('@saveAgent') to deterministically wait for the save server action response before reloading. Both CI runs passed.

Copy link
Contributor

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(1) Total Issues | Risk: Medium

This is a re-review triggered by a synchronize event. No new commits have been pushed since the last review, and the previously raised issue remains unaddressed.

🕐 Pending Recommendations (1)

  • 🟠 agent-tools.cy.ts:45 — Hardcoded cy.wait(500) instead of documented cy.intercept + cy.wait('@saveAgent') approach. The PR description explicitly states this pattern but implementation differs.

🚫 REQUEST CHANGES

Summary: The prior review identified a discrepancy between the PR description (which states cy.intercept + cy.wait('@saveAgent') is used) and the actual implementation (which uses cy.wait(500)). This issue has not been addressed in the current PR state. Please either:

  1. Implement the documented approach — Replace cy.wait(500) with cy.intercept('PUT', '**/agents/**').as('saveAgent') + cy.wait('@saveAgent') as stated in the PR description, OR
  2. Update the PR description — If the cy.wait(500) approach is intentional (e.g., because cy.intercept is not used elsewhere in the codebase), update the PR description to accurately reflect the implementation.

The other changes in this PR (explicit node count assertions, increased timeouts) are solid improvements that address the stated flakiness root causes.

Discarded (0)

No findings were discarded.

Reviewers (2)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-tests 0 0 0 0 0 0 0
pr-review-standards 0 0 0 0 0 0 0
Total 0 0 0 0 0 0 0

Note: Both reviewers confirmed the prior issue was already flagged and found no additional concerns worth raising.

@github-actions github-actions bot deleted a comment from claude bot Feb 16, 2026
The "Editing sub-agent ID should not removes linked tools" test failed
~30% of the time with "Found '2', expected '3'" after save+reload.

Root causes:
1. No verification that drag-and-drop operations created nodes — if a
   drag failed silently, the test continued with fewer nodes than expected
2. No wait for the save API response before reloading — cy.reload() could
   fire before the PUT response was fully processed
3. Default 4s timeout insufficient for CI after page reload

Fixes:
- Assert node count after each dragNode() call (1→2→3) to catch silent
  drag failures immediately
- Use cy.intercept()+cy.wait() to wait for the PUT /agent/** response
  before reloading, ensuring data is persisted
- Increase post-reload assertion timeout to 10s for CI environments

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

@claude claude bot left a comment

Choose a reason for hiding this comment

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

PR Review Summary

(1) Total Issues | Risk: High

🔴❗ Critical (1) ❗🔴

Inline Comments:

  • 🔴 Critical: agent-tools.cy.ts:43-45 — Intercept pattern POST **/agents/* won't match the actual save API call (PUT /agent/{id})

Summary of the Issue

The PR correctly identifies three root causes of flakiness and implements good fixes for two of them:

  • Explicit node count assertions after each dragNode() call (1→2→3) — catches silent drag failures early
  • Increased timeouts (10s) — accommodates CI environment variability

However, the third fix — API response waiting — has an incorrect intercept pattern:

What the code does What it should do
cy.intercept('POST', '**/agents/*') cy.intercept('PUT', '**/agent/**')

The test creates an agent, adds tools, then saves. At save time, the agent already exists, so saveAgent() calls updateFullAgentAction() which uses PUT to /agent/{agentId} (singular). The current pattern expects POST to /agents/... (plural) — which never fires during the save operation.

PR Description vs Implementation

The PR description states:

"Use cy.intercept('PUT', '**/agent/**') + cy.wait('@saveAgent') to wait for the save response before reloading"

But the implementation uses:

cy.intercept('POST', '**/agents/*').as('saveAgent');

Please align the implementation with the documented approach.


🚫 REQUEST CHANGES

Summary: The fix is almost there! The explicit node assertions and increased timeouts are solid improvements. The intercept pattern just needs to be corrected from POST **/agents/* to PUT **/agent/** to actually wait for the save API response. See the inline suggestion for a 1-click fix.

Discarded (0)

No findings were discarded.

Reviewers (1)
Reviewer Returned Main Findings Consider While You're Here Inline Comments Pending Recs Discarded
pr-review-tests 1 0 0 0 1 0 0
Total 1 0 0 0 1 0 0

Comment on lines +43 to +45
cy.intercept('POST', '**/agents/*').as('saveAgent');
cy.contains('Save changes').click();
cy.contains('Agent saved').should('exist');
cy.wait('@saveAgent');
Copy link
Contributor

Choose a reason for hiding this comment

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

🔴 CRITICAL: Intercept pattern won't match actual API calls

Issue: The pattern POST **/agents/* won't match the save API call. The test flow is:

  1. Create agent → POST to /agents (no trailing segment)
  2. Add tools via drag-and-drop
  3. Save changes → PUT to /agent/{agentId} (singular, not plural)

The intercept expects POST to **/agents/*, but the save operation uses PUT to /agent/... (singular). This means cy.wait('@saveAgent') will never receive a matching request.

Why: The race condition this fix aims to solve will remain. cy.wait('@saveAgent') will either:

  • Timeout after the default command timeout, causing test failure, OR
  • If Cypress falls through (depending on version/config), reload will proceed without waiting for save, causing the original flakiness

Fix:

Suggested change
cy.intercept('POST', '**/agents/*').as('saveAgent');
cy.contains('Save changes').click();
cy.contains('Agent saved').should('exist');
cy.wait('@saveAgent');
cy.intercept('PUT', '**/agent/**').as('saveAgent');
cy.contains('Save changes').click();
cy.wait('@saveAgent').its('response.statusCode').should('eq', 200);

This pattern correctly matches the update flow (PUT to /agent/{id}) and adds status validation to catch API failures early.

Refs:

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The suggestion to use PUT is incorrect for this codebase. The save goes through Next.js server actions — the browser sends a POST request to the current page URL (with a Next-Action header), and the PUT /agent/{id} call happens server-side within the action handler. Cypress cy.intercept() only catches browser-initiated requests, so it can see the POST server action but NOT the server-side PUT.

This is exactly why the earlier attempt with cy.intercept('PUT', '**/agent/**') failed with No request ever occurred — the PUT never passes through the browser.

The POST **/agents/* pattern correctly matches the server action POST to URLs like /default/projects/my-weather-project/agents/<id>. Both CI runs (Cypress + main CI) passed with this approach, confirming the pattern works.

@github-actions github-actions bot deleted a comment from claude bot Feb 16, 2026
@nick-inkeep nick-inkeep merged commit bc9a369 into main Feb 16, 2026
21 checks passed
@nick-inkeep nick-inkeep deleted the fix/cypress-agent-tools-flaky branch February 16, 2026 18:43
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.

1 participant