Skip to content

fix(exec): track background exec liveness with CLI tasks#59719

Closed
ringlochid wants to merge 1 commit into
openclaw:mainfrom
ringlochid:fix/background-coding-task-liveness
Closed

fix(exec): track background exec liveness with CLI tasks#59719
ringlochid wants to merge 1 commit into
openclaw:mainfrom
ringlochid:fix/background-coding-task-liveness

Conversation

@ringlochid

@ringlochid ringlochid commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Describe the problem and fix in 2–5 bullets:

If this PR fixes a plugin beta-release blocker, title it fix(<plugin-id>): beta blocker - <summary> and link the matching Beta blocker: <plugin-name> - <summary> issue labeled beta-blocker. Contributors cannot label PRs, so the title is the PR-side signal for maintainers and automation.

  • Problem: background exec / process runs could continue after the parent lane looked idle/reset, but there was no first-class linked runtime:"cli" task keeping liveness, cancellation, and terminal state visible.
  • Why it matters: users could see a session or /status surface that looked done or dead while repo-mutating background work was still running, stalled, or only partially cancelled.
  • What changed: backgrounded exec runs now create/update linked CLI task records, process remove preserves visibility until actual exit, session rows/status surfaces map linked CLI task state, and task maintenance reconciles orphaned CLI runs through a runtime hook with a CLI-specific grace period.
  • What did NOT change (scope boundary): this does not redesign ACP streaming, add a new dashboard/UI, or change broader gateway restart policy / transport reliability behavior.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause / Regression History (if applicable)

For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write N/A. If the cause is unclear, write Unknown.

  • Root cause: the plain background execprocess path was not wired into the task registry/status surfaces, so background work could outlive the parent lane without a durable CLI task reflecting running/stalled/cancelled/completed state.
  • Missing detection / guardrail: there was no regression coverage spanning background exec lifecycle, task-registry updates, process remove/kill semantics, and session-row/status derivation from linked CLI work.
  • Prior context (git blame, prior PR, issue, or refactor if known): #58810 fixed stale task rows in status cards, but it intentionally did not add CLI task creation/progress plumbing; #59672 hardened task maintenance, which made the missing CLI-runtime seam more important to handle explicitly.
  • Why this regressed now: long-running coding-agent workflows increasingly rely on the plain background exec/process path rather than ACP parent-stream handling, so the missing linkage became visible once parent chat lanes timed out/reset while the worker kept running.
  • If unknown, what was ruled out: this was not only a full gateway crash; the original repro showed the gateway still sending messages while the parent lane timed out/reset and the background worker kept progressing.

Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write N/A.

  • Coverage level that should have caught this:
  • Unit test
  • Seam / integration test
  • End-to-end test
  • Existing coverage already sufficient
  • Target test or file: src/agents/bash-tools.test.ts, src/agents/bash-tools.exec-runtime.test.ts, src/agents/bash-tools.process.supervisor.test.ts, src/tasks/task-registry.test.ts, src/gateway/session-utils.test.ts, src/agents/openclaw-tools.session-status.test.ts
  • Scenario the test should lock in: a backgrounded exec creates a linked CLI task, updates progress/stall state, remains inspectable during remove/cancel until exit, reconciles correctly if its backing run disappears, and drives session/status surfaces from linked task state instead of flattening to plain done.
  • Why this is the smallest reliable guardrail: the bug lives at the seam between exec runtime, process lifecycle, task maintenance, and status rendering, so single-function unit coverage alone would miss the user-visible drift.
  • Existing test that already covers this (if any): pre-existing session_status and task-registry coverage helped validate the status surface once the new CLI-task path was linked in, but did not previously cover the background-exec seam itself.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

List user-visible changes (including defaults/config).
If none, write None.

  • Backgrounded exec runs now surface as linked CLI tasks in task/status views instead of silently disappearing behind parent-lane state.
  • process remove no longer makes a live run immediately look gone; while termination is pending it remains inspectable and reports Termination requested; process still running.
  • Session rows can now surface linked CLI terminal states like killed / failed instead of flattening them to plain done.
  • Background execs without a notifying owner still create a system-scoped CLI task record so the run is not invisible to reconciliation/status plumbing.

Diagram (if applicable)

For UI changes or non-trivial logic flows, include a small ASCII diagram reviewers can scan quickly. Otherwise write N/A.

Before:
[background exec starts] -> [process session only] -> [parent lane resets/looks done] -> [no linked task state] -> [user cannot tell if work is alive, stalled, or cancelled]

After:
[background exec starts] -> [linked runtime:"cli" task created] -> [output/stall/cancel/exit update task] -> [session/status surfaces derive from task] -> [user sees running/stalled/killed/failed/completed state]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: Ubuntu 24.04 ARM64 (AWS EC2)
  • Runtime/container: local worktree / pnpm workspace
  • Model/provider: N/A
  • Integration/channel (if any): original repro came from a Telegram direct chat; verification here was local test/typecheck coverage
  • Relevant config (redacted): standard local dev config, no new feature flags

Steps

  1. Start a background exec run that yields/continues instead of finishing in the foreground.
  2. Inspect process behavior plus linked task/session status while the run is active, silent, or being removed/cancelled.
  3. Verify terminal state, orphan reconciliation, and session-row/status rendering from the linked CLI task.

Expected

  • Background exec creates a linked CLI task.
  • Progress/stall/terminal state remain visible while the run is alive or being cancelled.
  • Session/status surfaces reflect linked CLI state instead of plain stale parent-lane state.

Actual

  • Verified via targeted tests and local inspection after implementation; see Evidence and Human Verification below.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: linked CLI task creation for owned and no-owner background exec runs; pre-yield output marking; stalled no-output progress notice; remove/cancel keeping the process visible until exit; linked cancellation terminal state; session row/status derivation from linked CLI task state; CLI orphan maintenance grace behavior.
  • Edge cases checked: cancel-request ordering before supervisor termination, graceful remove-on-exit cleanup, terminal task mapping to killed / failed, and recent missing CLI runs staying running until the CLI-specific grace expires.
  • What you did not verify: a fresh live Telegram repro after the final rebase, a real gateway-restart/orphan recovery smoke on deployed infrastructure, or unrelated full-repo suites outside the touched regression slice.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: callers that mentally treated process remove as immediate disappearance now see a temporary running/termination-requested state until actual exit.
  • Mitigation: the new behavior preserves inspectability and avoids false “No session found” gaps; tests lock the user-visible text/state transition.
  • Risk: linked task status could over-drive session-row state if the wrong task is selected.
  • Mitigation: session-utils now derives from the focused linked task snapshot and has explicit regression coverage for running, failed, cancelled, and recent-miss cases.
  • Risk: orphan reconciliation could mark CLI runs lost too aggressively.
  • Mitigation: maintenance now consults an injected isCliRunActive(runId) hook and applies a CLI-specific grace window before marking lost.

Copilot AI review requested due to automatic review settings April 2, 2026 14:24
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime agents Agent runtime and tooling size: XL labels Apr 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a liveness/visibility gap for background exec/process runs by linking them to first-class runtime:"cli" task records, ensuring task maintenance, session rows, and status surfaces reflect ongoing (or cancelled/failed) background work.

Changes:

  • Create/update linked runtime:"cli" task records for background exec runs (including no-owner/system-scoped runs) and record progress/stall/terminal outcomes.
  • Adjust process remove/kill semantics to preserve inspectability until exit finalizes, and propagate cancellation state consistently.
  • Reconcile orphaned CLI tasks via a runtime hook (isCliRunActive) with a CLI-specific grace window, and derive gateway session row status/updatedAt from linked task state.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/tasks/task-registry.test.ts Adds regression coverage for CLI orphan reconciliation and grace behavior in maintenance.
src/tasks/task-registry.store.ts Extends task registry hooks with isCliRunActive(runId) for CLI liveness checks.
src/tasks/task-registry.maintenance.ts Uses CLI hook + CLI-specific grace when reconciling “lost” CLI tasks; makes reconcile helper accept now.
src/gateway/session-utils.ts Drives session row status/timestamps from linked task snapshot (when no subagent run).
src/gateway/session-utils.test.ts Adds tests ensuring session rows reflect active/stale/missing CLI-linked task state.
src/gateway/server.impl.ts Wires isCliRunActive from process registry into task registry runtime hooks at gateway startup.
src/agents/bash-tools.test.ts Adds exec backgrounding tests asserting CLI task creation/progress/cancel/terminal behavior.
src/agents/bash-tools.process.ts Keeps removed sessions inspectable until exit, adds “termination requested” messaging, and surfaces killed status.
src/agents/bash-tools.process.supervisor.test.ts Updates tests for new remove semantics (inspectable until exit) and killed terminal behavior.
src/agents/bash-tools.exec.ts Creates linked CLI task on background yield (session-owned or system-scoped) via dynamic task-executor import.
src/agents/bash-tools.exec-runtime.ts Records CLI task progress/stall notices and writes terminal state on completion/failure/cancel.
src/agents/bash-tools.exec-runtime.test.ts Adds unit test for CLI stall notice (No output for 120s.) behavior.
src/agents/bash-process-registry.ts Tracks output timestamps, removal/cancel flags, and exposes isCliRunActive for liveness checks.
src/agents/bash-process-registry.test.ts Adds coverage for “removed but still alive” sessions: hidden from tools, still live for CLI liveness.
src/agents/bash-process-registry.test-helpers.ts Extends process session fixtures with new CLI-task lifecycle fields.

return;
}
yielded = true;
markBackgrounded(run.session);
await createBackgroundCliTaskIfNeeded();

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

onYieldNow awaits createBackgroundCliTaskIfNeeded() before resolving the tool result. Because createBackgroundCliTaskIfNeeded does a dynamic import + task write, this can delay the yield/"background" response beyond the configured yield window. Consider kicking off task creation without awaiting it (while still storing cliTaskCreationPromise so runExecProcess can await it on exit), then call resolveRunning() immediately to keep yield latency predictable.

Suggested change
await createBackgroundCliTaskIfNeeded();
void createBackgroundCliTaskIfNeeded();

Copilot uses AI. Check for mistakes.
Comment on lines +1216 to +1243
const ownerTaskSnapshot = buildTaskStatusSnapshot(
listTasksForOwnerKey(key).map((task) => reconcileTaskRecordForOperatorInspection(task, now)),
{ now },
);
const activeOwnerTask = ownerTaskSnapshot.active[0];
const taskDrivenFocus = !subagentRun ? activeOwnerTask ?? ownerTaskSnapshot.focus : undefined;
const taskDrivenStatus = !subagentRun
? mapTaskStatusToSessionRunStatus(taskDrivenFocus?.status)
: undefined;
const taskDrivenStartedAt =
typeof taskDrivenStatus === "string"
? (taskDrivenFocus?.startedAt ?? taskDrivenFocus?.createdAt)
: undefined;
const taskDrivenEndedAt =
typeof taskDrivenStatus === "string" && taskDrivenStatus !== "running"
? (taskDrivenFocus?.endedAt ?? taskDrivenFocus?.lastEventAt ?? now)
: undefined;
const taskDrivenRuntimeMs =
typeof taskDrivenStartedAt === "number"
? Math.max(
0,
(taskDrivenStatus === "running" ? now : (taskDrivenEndedAt ?? now)) - taskDrivenStartedAt,
)
: undefined;
const taskDrivenUpdatedAt =
typeof taskDrivenStatus === "string"
? (taskDrivenFocus?.lastEventAt ?? taskDrivenFocus?.endedAt ?? taskDrivenFocus?.startedAt ?? taskDrivenFocus?.createdAt)
: undefined;

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

buildGatewaySessionRow always builds an owner-task snapshot (including reconciliation) even when subagentRun is present, but then discards the task-driven fields. This adds unnecessary task-registry work per row. Wrap the listTasksForOwnerKey/buildTaskStatusSnapshot block in a if (!subagentRun) guard to avoid extra work when subagent metadata already drives the row.

Suggested change
const ownerTaskSnapshot = buildTaskStatusSnapshot(
listTasksForOwnerKey(key).map((task) => reconcileTaskRecordForOperatorInspection(task, now)),
{ now },
);
const activeOwnerTask = ownerTaskSnapshot.active[0];
const taskDrivenFocus = !subagentRun ? activeOwnerTask ?? ownerTaskSnapshot.focus : undefined;
const taskDrivenStatus = !subagentRun
? mapTaskStatusToSessionRunStatus(taskDrivenFocus?.status)
: undefined;
const taskDrivenStartedAt =
typeof taskDrivenStatus === "string"
? (taskDrivenFocus?.startedAt ?? taskDrivenFocus?.createdAt)
: undefined;
const taskDrivenEndedAt =
typeof taskDrivenStatus === "string" && taskDrivenStatus !== "running"
? (taskDrivenFocus?.endedAt ?? taskDrivenFocus?.lastEventAt ?? now)
: undefined;
const taskDrivenRuntimeMs =
typeof taskDrivenStartedAt === "number"
? Math.max(
0,
(taskDrivenStatus === "running" ? now : (taskDrivenEndedAt ?? now)) - taskDrivenStartedAt,
)
: undefined;
const taskDrivenUpdatedAt =
typeof taskDrivenStatus === "string"
? (taskDrivenFocus?.lastEventAt ?? taskDrivenFocus?.endedAt ?? taskDrivenFocus?.startedAt ?? taskDrivenFocus?.createdAt)
: undefined;
let taskDrivenFocus:
| ReturnType<typeof buildTaskStatusSnapshot>["focus"]
| ReturnType<typeof buildTaskStatusSnapshot>["active"][number]
| undefined;
let taskDrivenStatus: SessionRunStatus | undefined;
let taskDrivenStartedAt: number | undefined;
let taskDrivenEndedAt: number | undefined;
let taskDrivenRuntimeMs: number | undefined;
let taskDrivenUpdatedAt: number | undefined;
if (!subagentRun) {
const ownerTaskSnapshot = buildTaskStatusSnapshot(
listTasksForOwnerKey(key).map((task) =>
reconcileTaskRecordForOperatorInspection(task, now),
),
{ now },
);
const activeOwnerTask = ownerTaskSnapshot.active[0];
taskDrivenFocus = activeOwnerTask ?? ownerTaskSnapshot.focus;
taskDrivenStatus = mapTaskStatusToSessionRunStatus(taskDrivenFocus?.status);
taskDrivenStartedAt =
typeof taskDrivenStatus === "string"
? taskDrivenFocus?.startedAt ?? taskDrivenFocus?.createdAt
: undefined;
taskDrivenEndedAt =
typeof taskDrivenStatus === "string" && taskDrivenStatus !== "running"
? taskDrivenFocus?.endedAt ?? taskDrivenFocus?.lastEventAt ?? now
: undefined;
taskDrivenRuntimeMs =
typeof taskDrivenStartedAt === "number"
? Math.max(
0,
(taskDrivenStatus === "running" ? now : taskDrivenEndedAt ?? now) -
taskDrivenStartedAt,
)
: undefined;
taskDrivenUpdatedAt =
typeof taskDrivenStatus === "string"
? (taskDrivenFocus?.lastEventAt ??
taskDrivenFocus?.endedAt ??
taskDrivenFocus?.startedAt ??
taskDrivenFocus?.createdAt)
: undefined;
}

Copilot uses AI. Check for mistakes.
Comment thread src/tasks/task-registry.maintenance.ts Outdated
if (!runId) {
return true;
}
return getTaskRegistryHooks()?.isCliRunActive?.(runId) ?? true;

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

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

hasBackingSession calls the configured isCliRunActive hook directly. If a hook implementation throws, it will bubble up and can break reconciliation/maintenance runs. Consider wrapping the hook call in a try/catch (similar to emitTaskRegistryHookEvent in task-registry.ts) and defaulting to true (or logging + true) on errors so maintenance remains resilient.

Suggested change
return getTaskRegistryHooks()?.isCliRunActive?.(runId) ?? true;
const hooks = getTaskRegistryHooks();
if (!hooks?.isCliRunActive) {
return true;
}
try {
return hooks.isCliRunActive(runId) ?? true;
} catch (error) {
// Ensure hook failures do not break maintenance/reconciliation runs.
// eslint-disable-next-line no-console
console.warn("task-registry: isCliRunActive hook threw, treating run as active", {
runId,
error,
});
return true;
}

Copilot uses AI. Check for mistakes.
@greptile-apps

greptile-apps Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires background exec / process runs into the task registry by creating linked runtime:"cli" task records on yield, propagating output/stall/cancel/exit states through the task executor, and threading isCliRunActive as a hook into task maintenance so orphaned CLI runs respect a CLI-specific 5-second grace window before being marked lost. Session rows now derive status from the focused linked CLI task rather than flattening it to plain done, and process remove preserves process visibility until actual exit instead of making the session immediately disappear.

One minor style issue was found (redundant second call to markCancelRequested in the kill action), with no functional impact.

Confidence Score: 5/5

  • Safe to merge; the lifecycle wiring is correct and all identified findings are P2 style issues.
  • Thorough review found no P0/P1 issues. The double-markExited paths in the kill/remove fallback routes are idempotent and result in correct final state. The cliTaskCreationPromise await pattern correctly handles early-exit races. The isCliRunActive hook correctly returns true for removed-but-not-yet-exited sessions and false after actual exit. All remaining findings are P2 (style/readability).
  • No files require special attention; src/agents/bash-tools.process.ts has the minor redundant call worth cleaning up.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/agents/bash-tools.process.ts
Line: 613-627

Comment:
**Redundant second `markCancelRequested` call**

`markCancelRequested` is already called on line 613 (before `cancelManagedSession`). The `else` branch on line 626 calls it again when `canceled` is `true`, but at that point `cancelRequestedByUser` is already `true` and the `stallTimer` was already cleared by the first call, so the second call is a no-op. The `else` branch can simply be removed.

```suggestion
          markCancelRequested(scopedSession);
          const canceled = cancelManagedSession(scopedSession.id);
          if (!canceled) {
            const terminated = terminateSessionFallback(scopedSession);
            if (!terminated) {
              scopedSession.cancelRequestedByUser = false;
              return failText(
                `Unable to terminate session ${params.sessionId}: no active supervisor run or process id.`,
              );
            }
            markExited(scopedSession, null, "SIGKILL", "killed");
            notifyExecSessionExit(scopedSession, "killed");
          }
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "Track background exec liveness with CLI ..." | Re-trigger Greptile

Comment on lines +613 to 627
markCancelRequested(scopedSession);
const canceled = cancelManagedSession(scopedSession.id);
if (!canceled) {
const terminated = terminateSessionFallback(scopedSession);
if (!terminated) {
scopedSession.cancelRequestedByUser = false;
return failText(
`Unable to terminate session ${params.sessionId}: no active supervisor run or process id.`,
);
}
markExited(scopedSession, null, "SIGKILL", "failed");
markExited(scopedSession, null, "SIGKILL", "killed");
notifyExecSessionExit(scopedSession, "killed");
} else {
markCancelRequested(scopedSession);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Redundant second markCancelRequested call

markCancelRequested is already called on line 613 (before cancelManagedSession). The else branch on line 626 calls it again when canceled is true, but at that point cancelRequestedByUser is already true and the stallTimer was already cleared by the first call, so the second call is a no-op. The else branch can simply be removed.

Suggested change
markCancelRequested(scopedSession);
const canceled = cancelManagedSession(scopedSession.id);
if (!canceled) {
const terminated = terminateSessionFallback(scopedSession);
if (!terminated) {
scopedSession.cancelRequestedByUser = false;
return failText(
`Unable to terminate session ${params.sessionId}: no active supervisor run or process id.`,
);
}
markExited(scopedSession, null, "SIGKILL", "failed");
markExited(scopedSession, null, "SIGKILL", "killed");
notifyExecSessionExit(scopedSession, "killed");
} else {
markCancelRequested(scopedSession);
}
markCancelRequested(scopedSession);
const canceled = cancelManagedSession(scopedSession.id);
if (!canceled) {
const terminated = terminateSessionFallback(scopedSession);
if (!terminated) {
scopedSession.cancelRequestedByUser = false;
return failText(
`Unable to terminate session ${params.sessionId}: no active supervisor run or process id.`,
);
}
markExited(scopedSession, null, "SIGKILL", "killed");
notifyExecSessionExit(scopedSession, "killed");
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/bash-tools.process.ts
Line: 613-627

Comment:
**Redundant second `markCancelRequested` call**

`markCancelRequested` is already called on line 613 (before `cancelManagedSession`). The `else` branch on line 626 calls it again when `canceled` is `true`, but at that point `cancelRequestedByUser` is already `true` and the `stallTimer` was already cleared by the first call, so the second call is a no-op. The `else` branch can simply be removed.

```suggestion
          markCancelRequested(scopedSession);
          const canceled = cancelManagedSession(scopedSession.id);
          if (!canceled) {
            const terminated = terminateSessionFallback(scopedSession);
            if (!terminated) {
              scopedSession.cancelRequestedByUser = false;
              return failText(
                `Unable to terminate session ${params.sessionId}: no active supervisor run or process id.`,
              );
            }
            markExited(scopedSession, null, "SIGKILL", "killed");
            notifyExecSessionExit(scopedSession, "killed");
          }
```

How can I resolve this? If you propose a fix, please make it concise.

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

Copy link
Copy Markdown
Contributor

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: 3abd6f2f51

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

@@ -1190,6 +1213,39 @@ export function buildGatewaySessionRow(params: {
const subagentStartedAt = subagentRun ? getSubagentSessionStartedAt(subagentRun) : undefined;
const subagentEndedAt = subagentRun ? subagentRun.endedAt : undefined;
const subagentRuntimeMs = subagentRun ? resolveSessionRuntimeMs(subagentRun, now) : undefined;
const ownerTaskSnapshot = buildTaskStatusSnapshot(
listTasksForOwnerKey(key).map((task) => reconcileTaskRecordForOperatorInspection(task, now)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Restrict session status override to CLI-linked tasks

The new task-driven session mapping now pulls from listTasksForOwnerKey(key) across all runtimes, so any recent/active non-CLI task (for example ACP/subagent tasks owned by the same session) can overwrite the row's status, timing fields, and updatedAt. In practice this can make /sessions show a session as failed or running based on an unrelated task and keep it in activeMinutes results even when the session itself is done; the override should be scoped to the linked background CLI task(s) this change is targeting.

Useful? React with 👍 / 👎.

@ringlochid
ringlochid force-pushed the fix/background-coding-task-liveness branch from 3abd6f2 to 723c6fe Compare April 3, 2026 01:16

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

Copy link
Copy Markdown
Contributor

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: 723c6fec85

ℹ️ 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 on lines +71 to +75
const runId = task.runId?.trim();
if (!runId) {
return true;
}
return getTaskRegistryObservers()?.isCliRunActive?.(runId) ?? true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Restrict CLI orphan checks to exec-backed run IDs

This branch now treats every runtime:"cli" task as backed only by isCliRunActive(runId), but the observer wired in src/gateway/server.impl.ts points to bash-process-registry and only knows exec/process sessions. Other CLI tasks (for example the gateway agent task created in src/gateway/server-methods/agent.ts:197-213) use different run IDs and will be reported missing, then reconciled to lost after the 5s CLI grace even while their real work is still active. That can incorrectly flip task/session status to failure.

Useful? React with 👍 / 👎.

Comment on lines +678 to +682
if (summary) {
const now = Date.now();
session.lastTaskUpdateAt = now;
void loadTaskExecutorModule()
.then(({ recordTaskRunProgressByRunId }) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Update CLI task heartbeat on all output, not summaries only

lastEventAt is only pushed to the task registry when summary is present, but callers pass undefined for normal ongoing output after the first chunk (only first output and stall-resume are summarized). For long-running commands that keep producing output, the task heartbeat stops advancing, so task-driven updatedAt becomes stale and active session filtering/status views can treat a live run as inactive.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

Keep this PR open because the background exec liveness problem is still meaningful and current main only partially covers adjacent stale-task maintenance, but this branch is not merge-ready: it conflicts with current main, lacks real behavior proof, and has correctness bugs in CLI task reconciliation and session-row status derivation.

Canonical path: Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

So I’m closing this here because the remaining work is already tracked in the canonical issue.

Review details

Best possible solution:

Close this stale PR. The latest review rated it F, the branch still lacks merge-ready proof, and there has been no human follow-up after the durable review.

Do we have a high-confidence way to reproduce the issue?

No high-confidence live reproduction was established in this read-only review. Source inspection shows current main still lacks linked CLI task creation for background execs, while the PR provides targeted tests but not real-run proof.

Is this the best way to solve the issue?

No. The requested direction is valid, but this PR is currently too broad in task maintenance and session status projection; the safer fix must preserve current main's runtime-specific reconciliation and scope new status behavior to linked background exec tasks.

Security review:

Security review cleared: The diff changes existing exec/process and task-registry plumbing but adds no dependencies, workflows, secret handling, network calls, or new external code execution path beyond the existing exec tool surface.

AGENTS.md: found and applied where relevant.

What I checked:

  • stale F-rated PR: PR was opened 2026-04-02T14:24:38Z, is older than 60 days, and the latest review rated it F.
  • proof blocker: real behavior proof is mock_only and proof tier is F, so this branch is not merge-ready without contributor follow-up.
  • no human follow-up: live comments and timeline hydrated by apply contain no non-automation activity after the ClawSweeper review.

Likely related people:

  • steipete: Authored and merged the current main task-maintenance cleanup for stale cron/chat-backed CLI tasks and recently touched exec timeout guidance adjacent to background exec semantics. (role: recent area contributor; confidence: high; commits: 7d1575b5df79, 7fc3197ecb86; files: src/tasks/task-registry.maintenance.ts, src/tasks/task-registry.maintenance.issue-60299.test.ts, src/agents/bash-tools.exec-runtime.ts)
  • vincentkoc: Authored the merged status snapshot and task-flow maintenance PRs that this branch builds on and references. (role: recent area contributor; confidence: high; commits: eb3893c5c282, 2025b73963fa, e1c85f9e7cb0; files: src/tasks/task-registry.maintenance.ts, src/tasks/task-registry.test.ts, src/gateway/session-utils.ts)

Codex review notes: model internal, reasoning high; reviewed against db5e415888ae.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels May 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 19, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 10, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 10, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jun 11, 2026
@clawsweeper clawsweeper Bot removed the rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. label Jun 14, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 14, 2026
@clawsweeper

clawsweeper Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper applied the proposed close for this PR.

@clawsweeper clawsweeper Bot closed this Jun 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling gateway Gateway runtime merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. size: XL status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants