Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix(runtime-host): preserve Windows startup diagnostics by M4n5ter · Pull Request #3238 · apache/maka · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -47,7 +47,7 @@
"release:cli:eval": "node scripts/release-cli-eval-package.mjs",
"generate:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs",
"check:windows-cargo-notices": "node scripts/generate-windows-cargo-notices.mjs --check",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"check:release": "npm run check:stale && npm run check:third-party-notices && npm run check:cli-third-party-notices && node --test scripts/release-cli-file-policy.test.mjs scripts/release-cli-artifact-policy.test.mjs scripts/release-cli-eval-support.test.mjs scripts/release-cli-publication.test.mjs scripts/release-cli-runtime-host-diagnostics.test.mjs scripts/release-cli-workflow-policy.test.mjs",
"package:macos-arm64": "node scripts/package-macos-arm64.mjs",
"verify:macos-arm64": "node scripts/verify-macos-arm64-dmg.mjs",
"package:windows-x64": "node scripts/package-windows-x64.mjs",
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-host/src/__tests__/candidate-cli.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,18 +3,22 @@ import test from 'node:test';
import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js';

const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('parses the production candidate flags without a desktop E2E override', () => {
const parsed = parseInteractiveRuntimeHostCandidateArguments([
'--root',
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--idle-grace-ms',
'10000',
]);
assert.equal(parsed.rootPath, '/tmp/workspace');
assert.equal(parsed.expectedRootId, ROOT_ID);
assert.equal(parsed.startupAttemptId, STARTUP_ATTEMPT_ID);
assert.equal(parsed.idleGraceMs, 10_000);
assert.equal(parsed.desktopE2e, undefined);
});
Expand All@@ -25,6 +29,8 @@ test('parses the desktop E2E composition flag', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'1',
]);
Expand All@@ -39,6 +45,8 @@ test('rejects an unknown desktop E2E flag value', () => {
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
]),
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,6 +4,7 @@ import {
candidateStartupFailureExitCode,
candidateStartupFailureForExitCode,
classifyCandidateStartupFailure,
isPermanentCandidateStartupFailure,
} from '../candidate-startup-failure.js';

test('preserves the primary startup classification through cleanup aggregation', () => {
Expand DownExpand Up@@ -63,6 +64,25 @@ test('does not infer workspace ownership from a generic filesystem error', () =>
});
});

test('preserves a retryable Local IPC security failure across the Candidate boundary', () => {
const endpointError = Object.assign(new Error('private Windows ACL detail'), {
code: 'insecure_endpoint_directory',
});
const failure = classifyCandidateStartupFailure(
new AggregateError([endpointError, new Error('cleanup failed')], 'startup failed', {
cause: endpointError,
}),
);

assert.deepEqual(failure, { reason: 'local_ipc_security_failed' });
assert.deepEqual(
candidateStartupFailureForExitCode(candidateStartupFailureExitCode(failure)),
failure,
);
assert.equal(isPermanentCandidateStartupFailure(failure), false);
assert.equal(JSON.stringify(failure).includes('private'), false);
});

test('classifies unknown startup failures without serializing their message', () => {
const failure = classifyCandidateStartupFailure(new Error('private provider or workspace data'));
assert.deepEqual(failure, { reason: 'internal_startup_failure' });
Expand Down
32 changes: 32 additions & 0 deletions packages/runtime-host/src/__tests__/control-endpoint.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,6 +7,7 @@ import {
prepareRuntimeHostEndpoint,
RuntimeHostEndpointError,
windowsPipeAclFailure,
windowsPipeAclFailureDiagnostic,
} from '../control/endpoint.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';

Expand All@@ -21,6 +22,37 @@ function execFileException(overrides: Partial<ExecFileException>): ExecFileExcep
});
}

test('bounds Windows pipe ACL helper diagnostics without serializing the command', () => {
const error = Object.assign(new Error('helper failed'), {
code: 1,
signal: 'SIGTERM',
killed: true,
cmd: 'private PowerShell command',
});
const diagnostic = windowsPipeAclFailureDiagnostic(error, `ACL failure ${'x'.repeat(8_192)}`);
const parsed = JSON.parse(diagnostic);

assert.deepEqual(
{
schemaVersion: parsed.schemaVersion,
helper: parsed.helper,
exitCode: parsed.exitCode,
signal: parsed.signal,
killed: parsed.killed,
},
{
schemaVersion: 1,
helper: 'windows_pipe_acl',
exitCode: 1,
signal: 'SIGTERM',
killed: true,
},
);
assert.equal(typeof parsed.stderr, 'string');
assert.ok(Buffer.byteLength(parsed.stderr, 'utf8') <= 4 * 1024);
assert.equal(diagnostic.includes('private PowerShell command'), false);
});

function rootTag(): string {
return Buffer.from(ROOT_ID, 'hex').toString('base64url');
}
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
import { resolveStorageRoot } from '@maka/storage/root-authority';
import {
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
} from '../control/startup-diagnostic.js';

const CANDIDATE_ENTRYPOINT = fileURLToPath(
new URL('../execution-candidate-main.js', import.meta.url),
);
const ROOT_ID = 'a'.repeat(64);
const STARTUP_ATTEMPT_ID = '00000000-0000-4000-8000-000000000001';

test('classifies invalid candidate arguments as an internal startup failure', () => {
const result = spawnSync(
Expand All@@ -17,6 +27,8 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
'/tmp/workspace',
'--expected-root-id',
ROOT_ID,
'--startup-attempt-id',
STARTUP_ATTEMPT_ID,
'--desktop-e2e',
'true',
],
Expand All@@ -27,3 +39,39 @@ test('classifies invalid candidate arguments as an internal startup failure', ()
assert.match(result.stderr, /\[runtime-host\] startup failed:/);
assert.match(result.stderr, /Invalid --desktop-e2e/);
});

test('preserves a valid Candidate invocation failure across the detached stderr boundary', async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-candidate-diagnostic-'));
const mismatchedRootId = createHash('sha256').update(randomUUID()).digest('hex');
const startupAttemptId = randomUUID();
const diagnosticPath = resolveCandidateStartupDiagnosticPath(mismatchedRootId, startupAttemptId);
const controlDirectory = dirname(diagnosticPath);
try {
await resolveStorageRoot({ path: root, kind: 'interactive' });
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
const result = spawnSync(
process.execPath,
[
CANDIDATE_ENTRYPOINT,
'--root',
root,
'--expected-root-id',
mismatchedRootId,
'--startup-attempt-id',
startupAttemptId,
],
{ encoding: 'utf8', timeout: 10_000 },
);

assert.equal(result.status, 70, result.stderr);
const diagnostic = await readCandidateStartupDiagnostic(mismatchedRootId, startupAttemptId);
assert.ok(diagnostic);
assert.equal(diagnostic.reason, 'internal_startup_failure');
assert.equal(diagnostic.startupAttemptId, startupAttemptId);
assert.ok(diagnostic.logs.every((entry) => !entry.includes('startup failed')));
assert.ok(diagnostic.errorChain.some((entry) => entry.code === 'root_identity_changed'));
} finally {
await rm(root, { recursive: true, force: true });
await rm(controlDirectory, { recursive: true, force: true });
}
});
67 changes: 65 additions & 2 deletions packages/runtime-host/src/__tests__/host-kernel.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,10 @@ import {
type DetachedCandidateInput,
} from '../client/launcher.js';
import { readHostRegistration } from '../control/registration.js';
import {
readCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';
import { removePosixEndpointDirectories } from './fixtures/endpoint-hygiene.js';
import {
decodeHostFrame,
Expand DownExpand Up@@ -81,6 +85,8 @@ const CURRENT_PROTOCOL = {
max: RUNTIME_HOST_PROTOCOL_VERSION,
} as const;
const LEGACY_PROTOCOL = { min: 1, max: 1 } as const;
const STARTUP_ATTEMPT_A = '00000000-0000-4000-8000-000000000001';
const STARTUP_ATTEMPT_B = '00000000-0000-4000-8000-000000000002';
const KERNEL_CANDIDATE_ENTRYPOINT = new URL('./fixtures/kernel-candidate.js', import.meta.url);
const KERNEL_COMPOSITION = defineInteractiveRuntimeHostComposition(async () => ({
handlers: createUnavailableDomainOperationHandlers(),
Expand DownExpand Up@@ -162,6 +168,7 @@ describe('non-serving Runtime Host kernel', () => {
pid: process.pid,
startupFailure: Promise.resolve({
reason: 'operational_state_migration_blocked' as const,
startupAttemptId: STARTUP_ATTEMPT_A,
}),
}),
};
Expand DownExpand Up@@ -200,11 +207,64 @@ describe('non-serving Runtime Host kernel', () => {
});
});

test('publishes the diagnostic from the Candidate failure selected by the election', async () => {
await withHostPaths(async (paths) => {
const capability = await resolveStorageRoot({ path: paths.root, kind: 'interactive' });
let launches = 0;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
rootPath: paths.root,
surface: 'desktop',
protocol: CURRENT_PROTOCOL,
compositionId: KERNEL_COMPOSITION.descriptor.id,
candidateEntrypoint: KERNEL_CANDIDATE_ENTRYPOINT,
electionDeadlineMs: 800,
},
{
random: () => 0.5,
launchCandidate: () => {
launches += 1;
if (launches > 2) return { spawned: new Promise(() => undefined) };
const startupAttemptId = launches === 1 ? STARTUP_ATTEMPT_A : STARTUP_ATTEMPT_B;
const reason =
launches === 1
? ('local_ipc_security_failed' as const)
: ('internal_startup_failure' as const);
return {
spawned: writeCandidateStartupDiagnostic({
rootId: capability.rootId,
startupAttemptId,
failure: { reason },
error: new Error(`Candidate ${launches} failed`),
}).then(() => ({
pid: process.pid,
startupFailure: Promise.resolve({ reason, startupAttemptId }),
})),
};
},
},
);

assert.deepEqual(result, { kind: 'failed', reason: 'local_ipc_security_failed' });
assert.equal(
(await readCandidateStartupDiagnostic(capability.rootId))?.startupAttemptId,
STARTUP_ATTEMPT_A,
);
assert.equal(
await readCandidateStartupDiagnostic(capability.rootId, STARTUP_ATTEMPT_B),
undefined,
);
});
});

test('accepts a ready successor launched before a migration blocker is observed', async () => {
await withHostPaths(async (paths) => {
let launches = 0;
let reportBlocker:
| ((failure: { reason: 'operational_state_migration_blocked' }) => void)
| ((failure: {
reason: 'operational_state_migration_blocked';
startupAttemptId: string;
}) => void)
| undefined;
const result = await connectOrSpawnRuntimeHostWithDependencies(
{
Expand All@@ -229,7 +289,10 @@ describe('non-serving Runtime Host kernel', () => {
}),
};
}
reportBlocker?.({ reason: 'operational_state_migration_blocked' });
reportBlocker?.({
reason: 'operational_state_migration_blocked',
startupAttemptId: STARTUP_ATTEMPT_A,
});
return launchTestRuntimeHostCandidate(paths, {
...input,
});
Expand Down
79 changes: 79 additions & 0 deletions packages/runtime-host/src/__tests__/startup-diagnostic.test.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
import assert from 'node:assert/strict';
import { createHash, randomUUID } from 'node:crypto';
import { mkdir, rm, stat, utimes } from 'node:fs/promises';
import { dirname } from 'node:path';
import test from 'node:test';
import {
clearCandidateStartupDiagnostic,
clearSelectedCandidateStartupDiagnostic,
readCandidateStartupDiagnostic,
resolveCandidateStartupDiagnosticPath,
selectCandidateStartupDiagnostic,
writeCandidateStartupDiagnostic,
} from '../control/startup-diagnostic.js';

test('preserves a bounded redacted Candidate startup diagnostic in the private control root', async () => {
const rootId = createHash('sha256').update(randomUUID()).digest('hex');
const selectedAttemptId = randomUUID();
const otherAttemptId = randomUUID();
const attemptPath = resolveCandidateStartupDiagnosticPath(rootId, selectedAttemptId);
const selectedPath = resolveCandidateStartupDiagnosticPath(rootId);
const controlDirectory = dirname(attemptPath);
await mkdir(controlDirectory, { recursive: true, mode: 0o700 });
try {
const helperDiagnostic = JSON.stringify({
schemaVersion: 1,
helper: 'windows_pipe_acl',
stderr: JSON.stringify({ stage: 'acl_apply', hresult: -2_147_024_891 }),
});
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: selectedAttemptId,
failure: { reason: 'local_ipc_security_failed' },
error: new Error('Unable to secure endpoint', { cause: new Error(helperDiagnostic) }),
logs: [`startup token=sk-${'a'.repeat(24)}`, 'endpoint setup failed'],
});

await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: otherAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Different Candidate failure'),
});
await selectCandidateStartupDiagnostic(rootId, selectedAttemptId);

const diagnostic = await readCandidateStartupDiagnostic(rootId);
assert.ok(diagnostic);
assert.equal(diagnostic.rootId, rootId);
assert.equal(diagnostic.startupAttemptId, selectedAttemptId);
assert.equal(diagnostic.reason, 'local_ipc_security_failed');
assert.match(diagnostic.errorChain[1]?.message ?? '', /windows_pipe_acl/u);
assert.match(diagnostic.errorChain[1]?.message ?? '', /acl_apply/u);
assert.deepEqual(diagnostic.logs, ['startup token=[redacted]', 'endpoint setup failed']);
assert.equal((await stat(selectedPath)).mode & 0o077, 0);

const otherDiagnostic = await readCandidateStartupDiagnostic(rootId, otherAttemptId);
assert.equal(otherDiagnostic?.reason, 'internal_startup_failure');

await utimes(
resolveCandidateStartupDiagnosticPath(rootId, otherAttemptId),
new Date(0),
new Date(0),
);
const freshAttemptId = randomUUID();
await writeCandidateStartupDiagnostic({
rootId,
startupAttemptId: freshAttemptId,
failure: { reason: 'internal_startup_failure' },
error: new Error('Fresh Candidate failure'),
});
assert.equal(await readCandidateStartupDiagnostic(rootId, otherAttemptId), undefined);

assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, otherAttemptId), false);
assert.equal(await clearSelectedCandidateStartupDiagnostic(rootId, selectedAttemptId), true);
await clearCandidateStartupDiagnostic(rootId, freshAttemptId);
assert.equal(await readCandidateStartupDiagnostic(rootId), undefined);
} finally {
await rm(controlDirectory, { recursive: true, force: true });
}
});
6 changes: 6 additions & 0 deletions packages/runtime-host/src/__tests__/startup-error.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,3 +27,9 @@ test('keeps internal startup failures retryable', () => {
const error = runtimeHostStartupError('internal_startup_failure');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
});

test('presents Local IPC security failures distinctly without making them permanent', () => {
const error = runtimeHostStartupError('local_ipc_security_failed');
assert.equal(error instanceof RuntimeHostPermanentReconnectError, false);
assert.match(error.message, /LOCAL_IPC_SECURITY_FAILED/u);
});
Loading
Loading