Skip to content

fix(task): align background delegate-task output with OpenCode TUI session metadata contract#2309

Merged
code-yeongyu merged 3 commits intodevfrom
fix/task-tui-session-metadata-sync
Mar 5, 2026
Merged

fix(task): align background delegate-task output with OpenCode TUI session metadata contract#2309
code-yeongyu merged 3 commits intodevfrom
fix/task-tui-session-metadata-sync

Conversation

@code-yeongyu
Copy link
Copy Markdown
Owner

@code-yeongyu code-yeongyu commented Mar 5, 2026

Summary

Fix 3 contract mismatches between delegate-task background output metadata and OpenCode TUI's Task component expectations, preventing broken session syncing and ambiguous task ID semantics.

Problem

When delegate-task launches a background task, the output metadata sent to OpenCode's TUI had several issues:

  1. Fake sessionId: "pending" — When the background manager hasn't resolved the subagent session yet, we emitted metadata.sessionId = "pending", causing TUI to call sync.session.sync("pending") on a non-existent session.
  2. Missing task_id field — OpenCode's built-in task tool emits task_id: <sessionId> in its output, but our background path omitted it entirely.
  3. Ambiguous task_id semantics — Our background flow has two distinct IDs (background manager task ID vs session ID), but the output conflated them.

Root Cause Analysis (with OpenCode source references)

TUI Contract: metadata.sessionId

OpenCode's TUI Task component uses props.metadata.sessionId to sync the child session and count toolcalls:

  • Sync trigger (index.tsx:1952-1955):

    onMount(() => {
      if (props.metadata.sessionId && !sync.data.message[props.metadata.sessionId]?.length)
        sync.session.sync(props.metadata.sessionId)
    })
  • Toolcall counting (index.tsx:1957-1965):

    const messages = createMemo(() => sync.data.message[props.metadata.sessionId ?? ""] ?? [])
    const tools = createMemo(() => {
      return messages().flatMap((msg) =>
        (sync.data.part[msg.id] ?? [])
          .filter((part): part is ToolPart => part.type === "tool")
          .map((part) => ({ tool: part.tool, state: part.state })),
      )
    })

TUI Contract: task_id output

OpenCode's built-in task tool emits task_id: <sessionId> as a session resumption key (task.ts:147-153):

`task_id: ${session.id} (for resuming to continue this task if needed)`,
"",
"<task_result>",
result,
"</task_result>",

TUI Permission rendering

TUI permission dialog reads data.subagent_type for task display (permission.tsx:300-313).

Changes

Commit 1: fix(task): avoid pending sessionId metadata in background delegate output

  • Problem: metadata.sessionId was set to "pending" when session wasn't yet created, causing TUI sync failures.
  • Fix: Only include sessionId in metadata when a real session ID is available. Conditionally emit <task_metadata> block.
  • Files: src/tools/delegate-task/background-task.ts, src/tools/delegate-task/background-task.test.ts (new)

Commit 2: fix(task): align background output task_id with opencode contract

  • Problem: Output lacked task_id field that OpenCode's TUI and agents expect for session resumption.
  • Fix: Add task_id: <sessionId> to <task_metadata> block (matching upstream contract). Rename human-readable label to Background Task ID to distinguish from session-based task_id.
  • Files: same as above

Commit 3: fix(task): disambiguate background task_id metadata

  • Problem: task_id (session ID) vs background manager task ID were ambiguous in output.
  • Fix: Add background_task_id: <bg_task_id> to <task_metadata> block alongside task_id: <sessionId>, making both IDs explicit.
  • Files: same as above

Test Coverage

New test file src/tools/delegate-task/background-task.test.ts with 3 test cases:

Test What it verifies
does not emit synthetic pending session metadata when session id is unresolved No fake sessionId, no <task_metadata> block when session unavailable
emits task metadata session_id when real session id is available session_id, task_id, background_task_id all present with correct values
captures late-resolved session id and emits synced metadata Session ID that appears mid-polling still propagates to metadata correctly

Verification

  • bun test src/tools/delegate-task/background-task.test.ts src/tools/delegate-task/tools.test.ts — 112 pass, 0 fail
  • bun run typecheck — clean
  • bun run build — clean

Summary by cubic

Aligns delegate-task background output with OpenCode TUI’s Task contract to fix session syncing and clarify task ID semantics. Background output now emits session metadata only when a real or late-resolved session is available and distinguishes between session and background task IDs.

  • Bug Fixes
    • Stop emitting metadata.sessionId="pending"; only include sessionId when resolved (including late-resolved via polling).
    • Emit <task_metadata> only when sessionId is present; add task_id: to match TUI’s resume contract.
    • Add background_task_id: <bg_task_id> and rename visible label to “Background Task ID” to avoid confusion.

Written for commit 39d94a4. Summary will update on new commits.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 2 files

Confidence score: 4/5

  • This PR is likely safe to merge, with minimal runtime risk since the reported issue is confined to a test file rather than production logic.
  • The main concern is in src/tools/delegate-task/background-task.test.ts: using an exact millisecond timeout with setTimeout can cause intermittent CI failures when execution runs slightly slower than expected.
  • Given the issue severity (7/10) and high confidence, increasing the test timeout should reduce flaky failures without changing feature behavior.
  • Pay close attention to src/tools/delegate-task/background-task.test.ts - timeout sensitivity may cause non-deterministic test outcomes.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tools/delegate-task/background-task.test.ts">

<violation number="1" location="src/tools/delegate-task/background-task.test.ts:16">
P1: Increase the timeout value to prevent flaky test failures caused by `setTimeout` taking longer than the exact millisecond duration specified.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

//#given - reduce waiting to keep tests fast
__setTimingConfig({
WAIT_FOR_SESSION_INTERVAL_MS: 1,
WAIT_FOR_SESSION_TIMEOUT_MS: 2,
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Mar 5, 2026

Choose a reason for hiding this comment

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

P1: Increase the timeout value to prevent flaky test failures caused by setTimeout taking longer than the exact millisecond duration specified.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/delegate-task/background-task.test.ts, line 16:

<comment>Increase the timeout value to prevent flaky test failures caused by `setTimeout` taking longer than the exact millisecond duration specified.</comment>

<file context>
@@ -0,0 +1,158 @@
+    //#given - reduce waiting to keep tests fast
+    __setTimingConfig({
+      WAIT_FOR_SESSION_INTERVAL_MS: 1,
+      WAIT_FOR_SESSION_TIMEOUT_MS: 2,
+    })
+  })
</file context>
Fix with Cubic

@code-yeongyu code-yeongyu force-pushed the fix/task-tui-session-metadata-sync branch from 72722f7 to 39d94a4 Compare March 5, 2026 02:02
@code-yeongyu code-yeongyu merged commit fc41a38 into dev Mar 5, 2026
7 checks passed
@code-yeongyu code-yeongyu deleted the fix/task-tui-session-metadata-sync branch March 5, 2026 02:06
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