feat: redesign health check to triangulate projects.json, issue label, and session state (#143) (#145)
## Changes - Remove `activeSessions` parameter from health check (was never populated) - Add gateway session lookup via `openclaw gateway call status` - Add issue label lookup via `provider.getIssue(issueId)` - Implement detection matrix with 6 issue types: - session_dead: active worker but session missing in gateway - label_mismatch: active worker but issue not in Doing/Testing - stale_worker: active for >2h - stuck_label: inactive but issue has Doing/Testing label - orphan_issue_id: inactive but issueId set - issue_gone: active but issue deleted/closed ## Files - lib/services/health.ts — complete rewrite with three-source triangulation - lib/tools/health.ts — remove activeSessions param, fetch sessions from gateway - lib/services/heartbeat.ts — remove empty activeSessions calls, pass sessions map
This commit is contained in:
@@ -15,7 +15,7 @@ import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { readProjects } from "../projects.js";
|
||||
import { log as auditLog } from "../audit.js";
|
||||
import { checkWorkerHealth } from "./health.js";
|
||||
import { checkWorkerHealth, fetchGatewaySessions, type SessionLookup } from "./health.js";
|
||||
import { projectTick } from "./tick.js";
|
||||
import { createProvider } from "../providers/index.js";
|
||||
import { notifyTickPickups, getNotificationConfig } from "../notify.js";
|
||||
@@ -184,12 +184,16 @@ async function processAllAgents(
|
||||
totalSkipped: 0,
|
||||
};
|
||||
|
||||
// Fetch gateway sessions once for all agents/projects
|
||||
const sessions = await fetchGatewaySessions();
|
||||
|
||||
for (const { agentId, workspace } of agents) {
|
||||
const agentResult = await tick({
|
||||
workspaceDir: workspace,
|
||||
agentId,
|
||||
config,
|
||||
pluginConfig,
|
||||
sessions,
|
||||
logger,
|
||||
});
|
||||
|
||||
@@ -221,9 +225,10 @@ export async function tick(opts: {
|
||||
agentId?: string;
|
||||
config: HeartbeatConfig;
|
||||
pluginConfig?: Record<string, unknown>;
|
||||
sessions: SessionLookup;
|
||||
logger: { info(msg: string): void; warn(msg: string): void };
|
||||
}): Promise<TickResult> {
|
||||
const { workspaceDir, agentId, config, pluginConfig } = opts;
|
||||
const { workspaceDir, agentId, config, pluginConfig, sessions } = opts;
|
||||
|
||||
const data = await readProjects(workspaceDir);
|
||||
const projectIds = Object.keys(data.projects);
|
||||
@@ -250,6 +255,7 @@ export async function tick(opts: {
|
||||
workspaceDir,
|
||||
groupId,
|
||||
project,
|
||||
sessions,
|
||||
);
|
||||
|
||||
// Budget check: stop if we've hit the limit
|
||||
@@ -304,6 +310,7 @@ async function performHealthPass(
|
||||
workspaceDir: string,
|
||||
groupId: string,
|
||||
project: any,
|
||||
sessions: SessionLookup,
|
||||
): Promise<number> {
|
||||
const { provider } = await createProvider({ repo: project.repo });
|
||||
let fixedCount = 0;
|
||||
@@ -314,7 +321,7 @@ async function performHealthPass(
|
||||
groupId,
|
||||
project,
|
||||
role,
|
||||
activeSessions: [],
|
||||
sessions,
|
||||
autoFix: true,
|
||||
provider,
|
||||
});
|
||||
@@ -332,5 +339,3 @@ async function checkProjectActive(workspaceDir: string, groupId: string): Promis
|
||||
if (!fresh) return false;
|
||||
return fresh.dev.active || fresh.qa.active;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user