You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by as...@apache.org on 2026/09/22 05:23:14 UTC
(maka) branch main updated: fix(runtime-host): preserve managed startup failure diagnostics (#5579)
This is an automated email from the ASF dual-hosted git repository.
Astro-Han pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/maka.git
The following commit(s) were added to refs/heads/main by this push:
new e6db756890 fix(runtime-host): preserve managed startup failure diagnostics (#5579)
e6db756890 is described below
commit e6db756890c36a8d4396241cc4f3a6f180529d20
Author: Nyvo <cz...@gmail.com>
AuthorDate: Tue Sep 22 13:23:09 2026 +0800
fix(runtime-host): preserve managed startup failure diagnostics (#5579)
Enrich the managed-Host readiness failure with the supervisor-observable evidence: service state, PID, last exit code, and a bounded redacted log tail. The diagnostic rides the existing repair-handoff seam, so a Host that crashes before listening now names its actual failure instead of surfacing MANAGED_ROOT_REQUIRES_OPERATOR.
Closes #5425.
---
.../runtime-host-local-remote-access.test.ts | 44 ++++++++++++++++++++
.../__tests__/runtime-host-service-manager.test.ts | 20 +++++++++
packages/cli/src/runtime-host-service-manager.ts | 48 +++++++++++++++++++++-
3 files changed, 111 insertions(+), 1 deletion(-)
diff --git a/apps/desktop/src/main/__tests__/runtime-host-local-remote-access.test.ts b/apps/desktop/src/main/__tests__/runtime-host-local-remote-access.test.ts
index 65f58c2f2e..9991253df0 100644
--- a/apps/desktop/src/main/__tests__/runtime-host-local-remote-access.test.ts
+++ b/apps/desktop/src/main/__tests__/runtime-host-local-remote-access.test.ts
@@ -391,6 +391,50 @@ test('repairs an existing managed Host with the current setup package and restar
assert.deepEqual(phases, ['checking', 'staging', 'restart']);
});
+test('preserves service readiness evidence in the managed repair blocker', async (t) => {
+ const base = await mkdtemp(join(tmpdir(), 'maka-local-managed-repair-diagnostic-'));
+ t.after(() => rm(base, { recursive: true, force: true }));
+ const clientDataRoot = join(base, 'client');
+ const rootPath = join(clientDataRoot, 'workspaces', 'default');
+ const rootId = 'a'.repeat(64);
+ await mkdir(rootPath, { recursive: true });
+ await writeManagedLifecycle(clientDataRoot, rootPath, rootId);
+ const diagnostic = [
+ 'Runtime Host service did not become ready: Host state is failed',
+ 'service state: failed; active: false; pid: none; last exit code: 78',
+ 'service logs (tail):\nstartup failed: [redacted]',
+ ].join('\n');
+ const service = createDesktopLocalRuntimeHostRemoteAccess({
+ ipcMain: { handle() {}, removeHandler() {} },
+ clientDataRoot,
+ rootPath,
+ rootId,
+ directPeerAvailable: true,
+ manager: () => undefined,
+ resolveSetupPackage: async () => ({ kind: 'npm', specifier: 'maka-agent@0.2.0' }),
+ resolveManagedDeploymentAuthority: async () => ({
+ kind: 'active',
+ lifecycleMode: 'supervised',
+ deploymentRoot: join(base, 'deployment'),
+ target: {
+ schemaVersion: 2,
+ serviceId: 'b'.repeat(64),
+ rootPath,
+ rootId,
+ operator: testOperator(join(base, 'operator.mjs')),
+ deploymentId: RECOVERY_DEPLOYMENT_ID,
+ },
+ }),
+ inspectHost: async () => ({ kind: 'unavailable' as const, reason: 'connect_failed' as const }),
+ operator: { async close() {} } as unknown as ReturnType<typeof createDesktopRuntimeHostLocalOperator>,
+ });
+ t.after(() => service.close());
+
+ const blocker = await service.resolveStartupRepair(new Error(diagnostic), new AbortController().signal);
+ assert.equal(blocker?.reason, 'repair');
+ assert.equal(blocker?.diagnostic, diagnostic);
+});
+
test('replaces a conflicting supervised Host with the requested active-work policy', async (t) => {
const base = await mkdtemp(join(tmpdir(), 'maka-local-managed-conflict-'));
t.after(() => rm(base, { recursive: true, force: true }));
diff --git a/packages/cli/src/__tests__/runtime-host-service-manager.test.ts b/packages/cli/src/__tests__/runtime-host-service-manager.test.ts
index 4afe87e038..5ad9084e66 100644
--- a/packages/cli/src/__tests__/runtime-host-service-manager.test.ts
+++ b/packages/cli/src/__tests__/runtime-host-service-manager.test.ts
@@ -59,6 +59,7 @@ import { runManagedRuntimeHostUpdateCli } from '../runtime-host-update-command.j
import {
cleanupRuntimeHostManagedDeployment,
effectiveRuntimeHostProjectDirectoryRoots,
+ formatRuntimeHostServiceReadinessDiagnostic,
manageRuntimeHostService,
replaceRuntimeHostManagedService,
resolveRuntimeHostManagedServiceConfigPath,
@@ -3167,6 +3168,25 @@ describe('managed Runtime Host service', () => {
await installing;
assert.notEqual((await status).service.config, null);
});
+
+ it('formats bounded, redacted readiness evidence for a failed managed Host', () => {
+ const diagnostic = formatRuntimeHostServiceReadinessDiagnostic({
+ lastFailure: 'Host state is failed; token=connection-secret',
+ status: {
+ state: 'failed',
+ active: false,
+ pid: null,
+ lastExitCode: 78,
+ },
+ logs: `${'x'.repeat(8_000)}\nstartup failed: {"apiKey":"fixture-secret"}`,
+ });
+
+ assert.match(diagnostic, /Host state is failed/u);
+ assert.match(diagnostic, /last exit code: 78/u);
+ assert.match(diagnostic, /service logs \(tail\):/u);
+ assert.doesNotMatch(diagnostic, /connection-secret|fixture-secret/u);
+ assert.ok(Buffer.byteLength(diagnostic, 'utf8') <= 2_048);
+ });
});
function legacySystemdUnitFixture(config: RuntimeHostManagedServiceConfig): string {
diff --git a/packages/cli/src/runtime-host-service-manager.ts b/packages/cli/src/runtime-host-service-manager.ts
index 598febd393..1447e8a810 100644
--- a/packages/cli/src/runtime-host-service-manager.ts
+++ b/packages/cli/src/runtime-host-service-manager.ts
@@ -35,6 +35,7 @@ import {
import { homedir } from 'node:os';
import { dirname, isAbsolute, join, resolve } from 'node:path';
import { truncateUtf8 } from '@maka/core/diagnostic-log';
+import { redactSecrets } from '@maka/core/redaction';
import {
canonicalProjectDirectoryRootSpec,
isCanonicalRuntimeHostWebSocketPath,
@@ -49,6 +50,7 @@ import {
import {
resolveRuntimeHostManagedServiceId,
RUNTIME_HOST_SERVICE_LOG_MAX_BYTES,
+ RUNTIME_HOST_SERVICE_ERROR_MESSAGE_MAX_BYTES,
type RuntimeHostReconciliationProvider,
type RuntimeHostServiceErrorCode,
type RuntimeHostSupervisorProvider,
@@ -78,6 +80,7 @@ const DEFAULT_WEBSOCKET_PATH = '/runtime-host';
const SERVICE_OPERATION_LOCK_TIMEOUT_MS = 60_000;
const SERVICE_READY_TIMEOUT_MS = 45_000;
const SERVICE_READY_POLL_MS = 50;
+const SERVICE_READINESS_LOG_MAX_BYTES = 1_024;
export interface RuntimeHostManagedServiceConfig {
readonly schemaVersion: 1 | 2;
@@ -928,6 +931,34 @@ export function formatRuntimeHostServiceLogs(
.join('\n');
}
+export function formatRuntimeHostServiceReadinessDiagnostic(input: {
+ readonly lastFailure: string;
+ readonly status?: Pick<
+ RuntimeHostServiceBackendStatus,
+ 'state' | 'active' | 'pid' | 'lastExitCode'
+ >;
+ readonly logs?: string;
+}): string {
+ const lines = [`Runtime Host service did not become ready: ${redactSecrets(input.lastFailure)}`];
+ if (input.status) {
+ lines.push(
+ `service state: ${input.status.state}; active: ${String(input.status.active)}; pid: ${input.status.pid ?? 'none'}; last exit code: ${input.status.lastExitCode ?? 'unknown'}`,
+ );
+ }
+ if (input.logs?.trim()) {
+ const safeLogs = takeUtf8Tail(
+ redactSecrets(input.logs.trim()),
+ SERVICE_READINESS_LOG_MAX_BYTES,
+ );
+ lines.push(`service logs (tail):\n${safeLogs}`);
+ }
+ return truncateUtf8(
+ lines.join('\n'),
+ RUNTIME_HOST_SERVICE_ERROR_MESSAGE_MAX_BYTES,
+ '\n<diagnostic truncated>',
+ );
+}
+
function takeUtf8Tail(value: string, maximumBytes: number): string {
const encoded = Buffer.from(value);
if (encoded.byteLength <= maximumBytes) return value;
@@ -1313,10 +1344,25 @@ export async function verifyRuntimeHostManagedServiceReady(
}
throw new RuntimeHostServiceManagerError(
'service_manager_operation_failed',
- `Runtime Host service did not become ready: ${lastFailure}`,
+ await formatRuntimeHostServiceReadinessFailure(lastFailure, backend),
);
}
+async function formatRuntimeHostServiceReadinessFailure(
+ lastFailure: string,
+ backend: RuntimeHostServiceBackend,
+): Promise<string> {
+ const [status, logs] = await Promise.all([
+ backend.status().catch(() => undefined),
+ backend.logs().catch(() => undefined),
+ ]);
+ return formatRuntimeHostServiceReadinessDiagnostic({
+ lastFailure,
+ status,
+ logs,
+ });
+}
+
async function prepareRuntimeHostRetirement(
config: RuntimeHostManagedServiceConfig,
expectedPid: number,
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org