Skip to content

fix: isUnstableAgent checks default model instead of resolved model, crashing visual-engineering and writing categories#2297

Closed
guazi04 wants to merge 1 commit into
code-yeongyu:devfrom
guazi04:fix/2287-isUnstableAgent-category-crash
Closed

fix: isUnstableAgent checks default model instead of resolved model, crashing visual-engineering and writing categories#2297
guazi04 wants to merge 1 commit into
code-yeongyu:devfrom
guazi04:fix/2287-isUnstableAgent-category-crash

Conversation

@guazi04

@guazi04 guazi04 commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #2287visual-engineering and writing task categories always crash with "Task not found" regardless of user model configuration.

Root Cause

Two bugs in src/tools/delegate-task/:

1. category-resolver.tsisUnstableAgent checks hardcoded default model instead of resolved model

// Before: checks BOTH actualModel AND default config model
const categoryConfigModel = resolved.config.model?.toLowerCase()
const isUnstableAgent = ... || [unstableModel, categoryConfigModel].some(m => ...)
  • visual-engineering defaults to google/gemini-3.1-pro → contains "gemini" → always unstable
  • writing defaults to kimi-for-coding/k2p5 → contains "kimi" → always unstable
  • Even user overrides to anthropic/claude-sonnet-4-6 don't help because categoryConfigModel still checks the default

2. unstable-agent-task.ts — race condition in polling loop

manager.getTask(task.id) returns undefined when task completes and gets cleaned up before the polling loop catches it. The code falls through and keeps polling until timeout → "Task not found".

Fix

Bug 1: Only check actualModel (the resolved, post-override model) for unstable model names. Remove categoryConfigModel from the check entirely.

// After: only checks the resolved actualModel
const isUnstableAgent = resolved.config.is_unstable_agent === true 
  || (unstableModel != null && (unstableModel.includes("gemini") || unstableModel.includes("minimax") || unstableModel.includes("kimi")))

Bug 2: Add null guard — when getTask() returns undefined, treat as task-completed instead of continuing to poll.

Behavior Matrix

Scenario isUnstableAgent
Default google/gemini-3.1-pro (no override) true
User overrides to anthropic/claude-sonnet-4-6 false ✅ (was true ❌)
Default kimi-for-coding/k2p5 (no override) true
is_unstable_agent: true explicit config true
Stable model like anthropic/claude-haiku-4-5 false

Tests

  • 6 new tests in category-resolver.test.ts covering all override scenarios
  • 1 new test in unstable-agent-task.test.ts for the race condition
  • All existing tests pass, build and typecheck clean

Summary by cubic

Fixes #2287 by stopping crashes in visual-engineering and writing tasks. Detection now uses the resolved model only, the polling loop treats cleaned-up tasks as completed, and tests cover overrides and the race condition.

  • Bug Fixes
    • Detect unstable agents from the resolved actualModel instead of the category default, so user model overrides work as expected.
    • Treat getTask(...) === undefined as task completed in the polling loop to avoid "Task not found" timeouts.

Written for commit acb257a. Summary will update on new commits.

@github-actions

github-actions Bot commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA. Thank you! ✅
Posted by the CLA Assistant Lite bot.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Auto-approved: Fixes two logical bugs (incorrect model check and polling race condition) with comprehensive test coverage, ensuring user overrides are respected without regressions.

…crashing visual-engineering and writing categories

- Remove categoryConfigModel from isUnstableAgent check; only evaluate
  the resolved actualModel (post user-override) for unstable model names
- Add null guard in unstable-agent-task polling loop: treat undefined
  getTask() result as task-completed instead of polling until timeout
- Add 7 tests covering override scenarios and race condition

Fixes code-yeongyu#2287
@guazi04 guazi04 force-pushed the fix/2287-isUnstableAgent-category-crash branch from be28b0c to acb257a Compare March 4, 2026 15:49
@acamq acamq self-assigned this Mar 7, 2026
@acamq

acamq commented Mar 7, 2026

Copy link
Copy Markdown
Collaborator

Analysis: Relationship Between #2293 and #2297

I've done a deep dive into both PRs and the related issue (#2287). These PRs are solving overlapping problems and should be combined.

The Core Issue

Both PRs address why tasks "disappear" causing "Task not found" errors, but they approach it differently:

PR Approach Scope
#2293 Fixes root cause: unify terminal-state lifecycle 5 bugs
#2297 Fixes symptom: handle missing task in polling loop 2 bugs

PR #2297's Bug 2 Has an Edge Case

The fix treats getTask() === undefined as success:

if (!currentTask) {
  completedDuringMonitoring = true  // Assumes success
  break
}

However, during the polling window (10 min), undefined most likely means error, not success:

Event Cleanup Timing Result During Polling
Success 10-minute delay Task observable with status="completed"
Error Immediate undefined

So if a task errors and gets cleaned up before the polling loop sees it, this fix would incorrectly report success.

PR #2293 Fixes This Root Cause

PR #2293's Bug #2 fix addresses this directly:

Root Cause: tasks.delete() called with no notification or delay
Fix: Mark status → notify parent → scheduleDelayedCleanup()

With this fix, error paths also have a 10-minute query window, so the polling loop can observe status="error" instead of seeing undefined.

Recommendation: Combine These PRs

Keep from #2297:

  • ✅ Bug 1 fix: isUnstableAgent checks resolved model only (not default)

Keep from #2293:

  • ✅ Unified scheduleDelayedCleanup() for all terminal states
  • ✅ Error notification to parent session
  • ✅ Prune skips terminal-state tasks
  • ✅ The other 4 bugs it fixes

Consider removing from #2297:

  • ⚠️ Bug 2 fix becomes a fallback safeguard rather than the primary solution. If you keep it, consider checking session status when task is missing:
if (!currentTask) {
  // Task cleaned up - verify it wasn't an error
  const statusResult = await client.session.status()
  const sessionStatus = statusResult[sessionID]
  if (sessionStatus?.type === "error") {
    terminalStatus = { status: "error", error: sessionStatus.message }
  } else {
    completedDuringMonitoring = true
  }
  break
}

Summary

#2293 is the more complete fix. Combining it with #2297's isUnstableAgent fix would give you the best of both PRs and ensure tasks are always queryable regardless of outcome.

guazi04 pushed a commit to guazi04/oh-my-opencode that referenced this pull request Mar 8, 2026
@guazi04

guazi04 commented Mar 8, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of #2293, which now includes both fixes.

Bug 1 (isUnstableAgent fix): Cherry-picked into #2293category-resolver.ts and category-resolver.test.ts (6 new tests) are now part of the lifecycle PR.

Bug 2 (polling null guard): No longer needed. #2293's scheduleDelayedCleanup() ensures all terminal-state tasks remain queryable for 10 minutes, so getTask() will never return undefined for a task that just completed/errored. The root cause (immediate task deletion) is fixed by the unified lifecycle approach.

See acamq's analysis in the #2297 review comments for the rationale behind combining these PRs.

@guazi04 guazi04 closed this Mar 8, 2026
guazi04 added a commit to guazi04/oh-my-opencode that referenced this pull request Mar 8, 2026
guazi04 added a commit to guazi04/oh-my-opencode that referenced this pull request Mar 9, 2026
guazi04 added a commit to guazi04/oh-my-opencode that referenced this pull request Mar 10, 2026
guazi04 added a commit to guazi04/oh-my-opencode that referenced this pull request May 28, 2026
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.

visual-engineering and writing categories crash with "Task not found" due to isUnstableAgent model name check

2 participants