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
22 changes: 6 additions & 16 deletions docs/windows-test-inventory.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ Locations intentionally omit line numbers so unrelated edits do not invalidate t

| Classification | Count |
|---|---:|
| windows-backend-gap | 30 |
| windows-backend-gap | 20 |
| portable-candidate | 0 |
| platform-contract | 35 |

Total Windows-excluded declarations: **65**
Total Windows-excluded declarations: **55**

## Inventory

Expand All @@ -32,30 +32,22 @@ Total Windows-excluded declarations: **65**
| platform-contract | `apps/desktop/src/main/__tests__/shell-env.test.ts` kills login-shell descendants when capture times out | `process.platform === 'win32'` |
| platform-contract | `apps/desktop/src/main/__tests__/shell-env.test.ts` bounds shell output instead of buffering until the global timeout | `process.platform === 'win32'` |
| windows-backend-gap | `packages/headless/src/__tests__/sandbox.test.ts` copies portable workspace evidence while skipping process-local special files | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/agent-graph-two-client-uds.test.ts` two UDS Clients query and control one Agent graph through Session invalidation | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/artifact-two-client-uds.test.ts` production Host recovers Artifact publication and preserves deletes across owner death | `process.platform === 'win32' ? 'POSIX process death gate' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/automation-two-client-uds.test.ts` two UDS Clients share one revision-pinned Automation authority | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/control-endpoint.test.ts` runtime host control endpoint | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/daily-review-two-client-uds.test.ts` two UDS Clients share Daily Review config, generation, and restart recovery | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/deep-research-two-client-uds.test.ts` two UDS Clients and a restarted production Host share one Deep Research projection | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/execution-host-queue.test.ts` a killed Host is recovered exactly once before its successor becomes ready | `process.platform === 'win32' ? 'POSIX process death gate' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/execution-inspect-uds.test.ts` a live Host serves Interactive inspection over its real UDS while retaining exclusive ownership | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/execution-inspect-uds.test.ts` a live Host serves Interactive inspection over its real endpoint while retaining exclusive ownership | `process.platform === 'win32' ? 'Windows execution Host startup lifecycle' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/host-kernel.test.ts` an automatic failed liveness check is connection-fatal and Client close stays local | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/host-kernel.test.ts` bounded election does not launch a Candidate after handshake exhausts the deadline | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/host-kernel.test.ts` a non-reading Client overload is isolated to its connection | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/host-kernel.test.ts` reports one shutdown failure through close and closed while releasing ownership | `process.platform === 'win32'` |
| platform-contract | `packages/runtime-host/src/__tests__/host-kernel.test.ts` publishes private POSIX endpoint and registration permissions | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/memory-two-client-uds.test.ts` two UDS clients share one recoverable Memory authority across Host death | `process.platform === 'win32' ? 'POSIX process death gate' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/oauth-two-client-uds.test.ts` OAuth enrollment presents only on the initiating Client over real UDS | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/oauth-two-client-uds.test.ts` OAuth enrollment honors Claude and Codex opt-out flags over real UDS | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/plan-two-client-uds.test.ts` two UDS Clients and a restarted production Host share one retry-safe Plan authority | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/runtime-policy-coordinator.test.ts` invalidates when a real published mutation loses its commit reply | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/runtime-resource-process.test.ts` real Host Runtime Resource process lifecycle | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/runtime-resource-two-client-uds.test.ts` a Host-owned PTY survives Desktop disconnect and transfers control to TUI | `process.platform === 'win32' ? 'POSIX UDS and shell integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-catalog-two-client-uds.test.ts` two UDS Clients share stable Session creation, CAS configuration, and catalog continuity | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-catalog-two-client-uds.test.ts` stable Session creation survives response loss and Host restart | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-effect-two-client-uds.test.ts` two UDS Clients share one durable Session recap effect | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts` two UDS Clients share exact retryable Session branch and revision authority | `process.platform === 'win32' ? 'POSIX UDS integration' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-catalog-two-client-uds.test.ts` two Clients share stable Session creation, CAS configuration, and catalog continuity | `process.platform === 'win32' ? 'Windows SQLite shutdown lifecycle' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-catalog-two-client-uds.test.ts` stable Session creation survives response loss and Host restart | `process.platform === 'win32' ? 'Windows SQLite shutdown lifecycle' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/session-revision-two-client-uds.test.ts` two Clients share exact retryable Session branch and revision authority | `process.platform === 'win32' ? 'Windows SQLite shutdown lifecycle' : false` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/skill-catalog-two-client-uds.test.ts` two UDS clients converge on one lease-bound Skill catalog authority | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/usage-pricing-client-correlation.test.ts` fails the connection for a canonical response with mismatched ${mismatch.name} | `process.platform === 'win32'` |
| windows-backend-gap | `packages/runtime-host/src/__tests__/usage-pricing-client-correlation.test.ts` rejects local invalid input without poisoning transport and correlates a private canonical copy | `process.platform === 'win32'` |
Expand Down Expand Up @@ -86,7 +78,5 @@ Total Windows-excluded declarations: **65**
| platform-contract | `packages/storage/src/__tests__/runtime-policy-stores.test.ts` preserves unknown commit semantics and consumes the completion ticket | `process.platform === 'win32' ? 'POSIX permissions are required to inject a persistence failure' : false` |
| platform-contract | `packages/storage/src/__tests__/runtime-policy-stores.test.ts` successor recovery removes credentials orphaned by an interrupted connection removal | `process.platform === 'win32' ? 'POSIX permissions are required to inject a persistence failure' : false` |
| platform-contract | `packages/storage/src/__tests__/runtime-policy-stores.test.ts` fails closed on final symlinks, FIFOs, and oversized documents without changing bytes | `process.platform === 'win32'` |
| windows-backend-gap | `packages/storage/src/__tests__/sqlite-long-term-memory-crash.test.ts` retains one committed Item and its receipt when killed between COMMIT and return | `process.platform === 'win32'` |
| windows-backend-gap | `packages/storage/src/__tests__/sqlite-runtime-crash.test.ts` SqliteRuntimeStore real-process crash boundaries | `process.platform === 'win32'` |
| platform-contract | `packages/storage/src/__tests__/usage-stores.test.ts` classifies a renamed or replaced live root as a draining persistence failure | `process.platform === 'win32' ? 'Windows does not permit renaming a directory with an open SQLite database' : false` |
| platform-contract | `packages/storage/src/__tests__/workspace-identity.test.ts` an unmarked read-only workspace fails without leaving marker state | `process.platform === 'win32' ? 'POSIX permissions are required to create a read-only workspace fixture' : false` |
15 changes: 13 additions & 2 deletions packages/runtime/src/__tests__/runtime-continuation-crash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { createSqliteAgentRunStore } from '@maka/storage';
import { type RuntimeContinuationFailpoint } from '../agent-run.js';
import { BackendRegistry, SessionManager } from '../session-manager.js';
import { FakeBackend } from '../fake-backend.js';
import { terminateChildProcessTree } from '../process-tree-terminator.js';

const CRASH_CHILD_ENV = 'MAKA_RUNTIME_CONTINUATION_CRASH_CHILD';
const FAILPOINTS: readonly RuntimeContinuationFailpoint[] = [
Expand Down Expand Up @@ -60,6 +61,7 @@ if (process.env[CRASH_CHILD_ENV] === '1') {
await store.close?.();
const {
manager,
agentRunStore: recoveryAgentRunStore,
runtimeEventStore: recoveryRuntimeStore,
sessionStore: recoverySessionStore,
} = createManager(workspaceRoot);
Expand Down Expand Up @@ -102,6 +104,8 @@ if (process.env[CRASH_CHILD_ENV] === '1') {
`${failpoint} left the continuation non-terminal`,
);
}
recoveryAgentRunStore.close?.();
runStore.close?.();
recoveryRuntimeStore.close();
await recoverySessionStore.close?.();
}
Expand Down Expand Up @@ -212,6 +216,7 @@ async function suspendCrashChild(

function createManager(workspaceRoot: string): {
manager: SessionManager;
agentRunStore: ReturnType<typeof createSqliteAgentRunStore>;
runtimeEventStore: ReturnType<typeof createSqliteRuntimeStore>;
sessionStore: ReturnType<typeof createSessionStore>;
} {
Expand All @@ -231,6 +236,7 @@ function createManager(workspaceRoot: string): {
);
let id = 100;
return {
agentRunStore: runStore,
runtimeEventStore,
sessionStore: store,
manager: new SessionManager({
Expand Down Expand Up @@ -288,15 +294,20 @@ async function crashContinuationAt(
await new Promise((resolve) => setTimeout(resolve, 10));
}
if (!stdout.includes(`READY:${failpoint}\n`)) {
child.kill('SIGKILL');
await killCrashChild(child);
await closed;
throw new Error(`${failpoint} child did not reach boundary: ${stderr || stdout}`);
}
assert.equal(child.kill('SIGKILL'), true);
assert.equal(await killCrashChild(child), true);
const [exitCode, signal] = await closed;
assert.ok(exitCode !== 0 || signal !== null);
}

function killCrashChild(child: ReturnType<typeof spawn>): Promise<boolean> {
if (process.platform === 'win32') return terminateChildProcessTree(child, 'SIGKILL');
return Promise.resolve(child.kill('SIGKILL'));
}

function assertPrefix(
failpoint: RuntimeContinuationFailpoint,
header: AgentRunHeader | undefined,
Expand Down
21 changes: 15 additions & 6 deletions packages/runtime/src/__tests__/runtime-resume-crash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
buildResumePlanFromRuntimeEvents,
type RuntimeResumeCommittedPrefix,
} from '../runtime-resume.js';
import { terminateChildProcessTree } from '../process-tree-terminator.js';

const CRASH_CHILD_ENV = 'MAKA_RUNTIME_RESUME_CRASH_CHILD';

Expand All @@ -41,7 +42,8 @@ if (process.env[CRASH_CHILD_ENV] === '1') {
// Production creates the run header before any RuntimeEvent append. Keep the
// crash boundary focused on the child event writer while preserving the
// storage identity contract used when the ledger is reopened.
await createSqliteAgentRunStore(workspaceRoot).createRun({
const runStore = createSqliteAgentRunStore(workspaceRoot);
await runStore.createRun({
runId,
invocationId: `invocation-${runId}`,
sessionId,
Expand All @@ -55,6 +57,7 @@ if (process.env[CRASH_CHILD_ENV] === '1') {
createdAt: 1,
updatedAt: 1,
});
runStore.close?.();

await crashWriterAfterCommit({
workspaceRoot,
Expand Down Expand Up @@ -86,6 +89,7 @@ if (process.env[CRASH_CHILD_ENV] === '1') {
recoveredEvents,
`${failpoint.id} projection mutated the durable ledger`,
);
reopened.close();
}
} finally {
await rm(root, { recursive: true, force: true });
Expand Down Expand Up @@ -150,25 +154,30 @@ async function crashWriterAfterCommit(input: {
stderr += chunk;
});

const exited = once(child, 'exit') as Promise<[number | null, NodeJS.Signals | null]>;
const closed = once(child, 'close') as Promise<[number | null, NodeJS.Signals | null]>;
const deadline = Date.now() + 10_000;
while (!stdout.includes('READY\n') && child.exitCode === null && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 10));
}
if (!stdout.includes('READY\n')) {
child.kill('SIGKILL');
await exited;
await killCrashChild(child);
await closed;
throw new Error(`crash child did not reach committed boundary: ${stderr || stdout}`);
}

assert.equal(child.kill('SIGKILL'), true);
const [exitCode, signal] = await exited;
assert.equal(await killCrashChild(child), true);
const [exitCode, signal] = await closed;
assert.ok(
exitCode !== 0 || signal !== null,
'crash child exited successfully instead of being killed',
);
}

function killCrashChild(child: ReturnType<typeof spawn>): Promise<boolean> {
if (process.platform === 'win32') return terminateChildProcessTree(child, 'SIGKILL');
return Promise.resolve(child.kill('SIGKILL'));
}

function ledgerEvents(sessionId: string, runId: string): RuntimeEvent[] {
const identity = {
sessionId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ if (childMode) {
await runCrashChild(childMode);
} else {
test('retains one committed Item and its receipt when killed between COMMIT and return', {
skip: process.platform === 'win32',
timeout: 30_000,
}, async () => {
const root = await mkdtemp(join(tmpdir(), 'maka-long-term-memory-crash-'));
Expand Down
4 changes: 1 addition & 3 deletions packages/storage/src/__tests__/sqlite-runtime-crash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ const childMode = process.env.MAKA_SQLITE_CRASH_CHILD;
if (childMode) {
await runCrashChild(childMode);
} else {
describe('SqliteRuntimeStore real-process crash boundaries', {
skip: process.platform === 'win32',
}, () => {
describe('SqliteRuntimeStore real-process crash boundaries', () => {
it('rolls back a process killed inside T1', { timeout: 30_000 }, async () => {
await withKilledChild('inside_t1', async (store) => {
assert.deepEqual(await store.readRuntimeEvents('session-1', 'run-1'), []);
Expand Down
Loading