From 23c2113f78b405e6aa8d90e955ba7d01c5b373fa Mon Sep 17 00:00:00 2001 From: jackwener Date: Tue, 11 Aug 2026 18:56:39 +0800 Subject: [PATCH] test: remove obsolete compatibility and script tests --- .../__tests__/artifact-visibility.test.ts | 13 +- .../src/main/__tests__/chat-readiness.test.ts | 59 -- .../__tests__/project-root-controller.test.ts | 43 -- .../__tests__/session-workbar-tabs.test.ts | 28 +- .../src/main/__tests__/stale-sessions.test.ts | 151 ----- ...d-architecture-astryx-review-2026-08-09.md | 1 - package.json | 4 +- packages/cli/src/__tests__/cli.test.ts | 2 - .../agent-run-continuation-source.test.ts | 12 - .../core/src/__tests__/orchestration.test.ts | 2 +- .../src/__tests__/sandbox-boundary.test.ts | 14 - .../core/src/__tests__/task-ledger.test.ts | 94 ---- .../task-submission-readiness.test.ts | 20 - .../src/__tests__/candidate-cli.test.ts | 35 -- .../legacy-runtime-policy-migration.test.ts | 346 ------------ .../history-compact-checkpoint.test.ts | 37 +- .../__tests__/history-compact-cleanup.test.ts | 300 ---------- .../__tests__/runtime-event-backfill.test.ts | 529 ------------------ .../src/__tests__/connection-store.test.ts | 28 - .../src/__tests__/project-catalog.test.ts | 88 --- .../__tests__/sqlite-runtime-store.test.ts | 187 ------- .../sqlite-scheduling-schema.test.ts | 101 ---- scripts/check-astryx-alignment.test.mjs | 22 - .../check-astryx-surface-inventory.test.mjs | 75 --- scripts/check-dead-css.test.mjs | 130 ----- scripts/check-story-annotations.test.mjs | 90 --- scripts/ci-test-plan.mjs | 3 - scripts/ci-test-plan.test.mjs | 1 - scripts/cli-build-order.test.mjs | 55 -- scripts/code-mode-build-order.test.mjs | 33 -- scripts/cu-process-restart-harness.test.mjs | 70 --- scripts/dependency-audit-workflow.test.mjs | 67 --- scripts/fixture-env.test.mjs | 46 -- scripts/storybook-visual-smoke.test.mjs | 58 -- scripts/windows-baseline-workflow.test.mjs | 171 ------ scripts/windows-recovery-workflow.test.mjs | 48 -- scripts/windows-smoke.test.mjs | 59 -- scripts/windows-test-inventory.mjs | 1 - scripts/windows-test-inventory.test.mjs | 101 ---- 39 files changed, 12 insertions(+), 3112 deletions(-) delete mode 100644 apps/desktop/src/main/__tests__/stale-sessions.test.ts delete mode 100644 packages/runtime-host/src/__tests__/candidate-cli.test.ts delete mode 100644 packages/runtime-host/src/__tests__/legacy-runtime-policy-migration.test.ts delete mode 100644 packages/runtime/src/__tests__/history-compact-cleanup.test.ts delete mode 100644 packages/runtime/src/__tests__/runtime-event-backfill.test.ts delete mode 100644 packages/storage/src/__tests__/sqlite-scheduling-schema.test.ts delete mode 100644 scripts/check-astryx-alignment.test.mjs delete mode 100644 scripts/check-astryx-surface-inventory.test.mjs delete mode 100644 scripts/check-dead-css.test.mjs delete mode 100644 scripts/check-story-annotations.test.mjs delete mode 100644 scripts/cli-build-order.test.mjs delete mode 100644 scripts/code-mode-build-order.test.mjs delete mode 100644 scripts/cu-process-restart-harness.test.mjs delete mode 100644 scripts/dependency-audit-workflow.test.mjs delete mode 100644 scripts/fixture-env.test.mjs delete mode 100644 scripts/storybook-visual-smoke.test.mjs delete mode 100644 scripts/windows-baseline-workflow.test.mjs delete mode 100644 scripts/windows-recovery-workflow.test.mjs delete mode 100644 scripts/windows-smoke.test.mjs delete mode 100644 scripts/windows-test-inventory.test.mjs diff --git a/apps/desktop/src/main/__tests__/artifact-visibility.test.ts b/apps/desktop/src/main/__tests__/artifact-visibility.test.ts index ad912dbf95..000b905555 100644 --- a/apps/desktop/src/main/__tests__/artifact-visibility.test.ts +++ b/apps/desktop/src/main/__tests__/artifact-visibility.test.ts @@ -3,15 +3,15 @@ import { describe, it } from 'node:test'; import type { ArtifactRecord, ArtifactSource } from '@maka/core'; import { filterUserVisibleArtifacts } from '../../renderer/artifact-visibility.js'; -function artifact(source?: ArtifactSource): ArtifactRecord { +function artifact(source: ArtifactSource): ArtifactRecord { return { - id: source ?? 'legacy', + id: source, sessionId: 'session-1', turnId: 'turn-1', source, kind: 'file', - name: `${source ?? 'legacy'}.json`, - relativePath: `${source ?? 'legacy'}.json`, + name: `${source}.json`, + relativePath: `${source}.json`, sizeBytes: 4096, createdAt: 1, status: 'live', @@ -41,14 +41,13 @@ describe('generated artifact visibility', () => { assert.deepEqual(filterUserVisibleArtifacts(hiddenSources.map(artifact)), []); }); - it('preserves user-facing generated files and legacy records without a source', () => { - const visibleSources: Array = [ + it('preserves user-facing generated files', () => { + const visibleSources: ArtifactSource[] = [ 'subagent_writeback', 'deep_research', 'export', 'snapshot', 'fixture', - undefined, ]; const records = visibleSources.map(artifact); diff --git a/apps/desktop/src/main/__tests__/chat-readiness.test.ts b/apps/desktop/src/main/__tests__/chat-readiness.test.ts index 510fa202ff..45dfb60378 100644 --- a/apps/desktop/src/main/__tests__/chat-readiness.test.ts +++ b/apps/desktop/src/main/__tests__/chat-readiness.test.ts @@ -343,65 +343,6 @@ describe('chat readiness guard', () => { assert.deepEqual(updates, []); }); - test('does not rebind locked legacy fake sessions', async () => { - const updates: unknown[] = []; - - await assertRejectsReadiness( - 'locked fake session', - () => ensureSessionCanSendOrRebind( - 'session-locked-fake', - header({ backend: 'fake', llmConnectionSlug: 'fake', model: 'fake-model', connectionLocked: true }), - { - readyConnectionDeps: keyedDeps({ - anthropic: { connection: connection(), apiKey: 'sk-test' }, - }), - async getDefaultSlug() { - return 'anthropic'; - }, - async listConnectionSlugs() { - return []; - }, - async updateSession(_sessionId, patch) { - updates.push(patch); - }, - }, - ), - '旧的本地模拟连接', - 'fake_backend', - ); - - assert.deepEqual(updates, []); - }); - - test('rebinds old fake sessions to a ready default connection before send', async () => { - const updates: unknown[] = []; - const result = await ensureSessionCanSendOrRebind( - 'session-1', - header({ backend: 'fake', llmConnectionSlug: 'fake', model: 'fake-model' }), - { - readyConnectionDeps: keyedDeps({ - anthropic: { connection: connection(), apiKey: 'sk-test' }, - }), - async getDefaultSlug() { - return 'anthropic'; - }, - async listConnectionSlugs() { - return []; - }, - async updateSession(_sessionId, patch) { - updates.push(patch); - }, - }, - ); - - assert.deepEqual(result, { - rebound: true, - connectionSlug: 'anthropic', - modelId: 'claude-3-5-sonnet-20241022', - }); - assert.equal(updates.length, 1); - }); - test('rebinds an unknown-provider session to the first existing ready connection', async () => { const updates: unknown[] = []; const rebindDeps = { diff --git a/apps/desktop/src/main/__tests__/project-root-controller.test.ts b/apps/desktop/src/main/__tests__/project-root-controller.test.ts index 673a11c5a9..d0b6aebb27 100644 --- a/apps/desktop/src/main/__tests__/project-root-controller.test.ts +++ b/apps/desktop/src/main/__tests__/project-root-controller.test.ts @@ -46,49 +46,6 @@ test('serializes rapid selections without losing another Runtime Host root', asy } }); -test('migrates the legacy path selection once and removes the legacy file', async () => { - const base = await mkdtemp(join(tmpdir(), 'maka-project-preference-migration-')); - const project = join(base, 'project'); - await mkdir(project); - const legacy = join(base, 'last-project-path.json'); - await writeFile(legacy, JSON.stringify({ projectId: 'project-1', projectPath: project })); - try { - const selection = await controller(base, project, 'root-a').currentSelection(); - assert.equal(selection.projectId, 'project-1'); - assert.equal(selection.path, project); - await assert.rejects(() => readFile(legacy), /ENOENT/); - assert.equal( - JSON.parse(await readFile(join(base, 'project-preferences.json'), 'utf8')).selections[ - 'root-a' - ], - 'project-1', - ); - } finally { - await rm(base, { recursive: true, force: true }); - } -}); - -test('keeps a legacy path selection until it can be represented by a Project id', async () => { - const base = await mkdtemp(join(tmpdir(), 'maka-project-preference-path-migration-')); - const fallback = join(base, 'fallback'); - const project = join(base, 'project'); - await mkdir(fallback); - await mkdir(project); - const legacy = join(base, 'last-project-path.json'); - await writeFile(legacy, JSON.stringify({ projectPath: project })); - try { - for (let attempt = 0; attempt < 2; attempt += 1) { - assert.deepEqual(await controller(base, fallback, 'root-a').currentSelection(), { - projectId: undefined, - path: project, - }); - } - assert.equal(JSON.parse(await readFile(legacy, 'utf8')).projectPath, project); - } finally { - await rm(base, { recursive: true, force: true }); - } -}); - test('does not reuse a preference from another Runtime Host root', async () => { const base = await mkdtemp(join(tmpdir(), 'maka-project-preference-scope-')); const fallback = join(base, 'fallback'); diff --git a/apps/desktop/src/main/__tests__/session-workbar-tabs.test.ts b/apps/desktop/src/main/__tests__/session-workbar-tabs.test.ts index c601114b1a..275e496b0e 100644 --- a/apps/desktop/src/main/__tests__/session-workbar-tabs.test.ts +++ b/apps/desktop/src/main/__tests__/session-workbar-tabs.test.ts @@ -319,15 +319,7 @@ describe('session workbar tab persistence', () => { assert.equal(state.activeTabId, 'workbar:inspector'); }); - it('migrates the legacy active tab and otherwise starts on the New Tab page', () => { - (globalThis as { localStorage?: unknown }).localStorage = { - getItem: (key: string) => - key === 'maka-session-workbar-tab-v1' ? 'browser' : null, - }; - assert.deepEqual(readSessionWorkbarTabs().tabs, [ - { id: 'workbar:browser', kind: 'browser' }, - ]); - + it('starts on the New Tab page without persisted state', () => { (globalThis as { localStorage?: unknown }).localStorage = { getItem: () => null, }; @@ -460,7 +452,7 @@ describe('session workbar panel topology', () => { }); }); - it('reads v3 topology and migrates v2 tabs into the right panel', () => { + it('reads v3 topology', () => { (globalThis as { localStorage?: unknown }).localStorage = { getItem: (key: string) => key === 'maka-session-workbar-panels-v3' @@ -484,21 +476,5 @@ describe('session workbar panel topology', () => { { id: 'workbar:files', kind: 'files' }, ]); assert.equal(readSessionWorkbarPanels().focusedPanel, 'bottom'); - - (globalThis as { localStorage?: unknown }).localStorage = { - getItem: (key: string) => - key === 'maka-session-workbar-tabs-v2' - ? JSON.stringify({ - version: 2, - tabs: [{ id: 'workbar:tasks', kind: 'tasks' }], - activeTabId: 'workbar:tasks', - }) - : null, - }; - const migrated = readSessionWorkbarPanels(); - assert.deepEqual(migrated.right.tabs, [ - { id: 'workbar:tasks', kind: 'tasks' }, - ]); - assert.deepEqual(migrated.bottom.tabs, []); }); }); diff --git a/apps/desktop/src/main/__tests__/stale-sessions.test.ts b/apps/desktop/src/main/__tests__/stale-sessions.test.ts deleted file mode 100644 index 6e6e07a189..0000000000 --- a/apps/desktop/src/main/__tests__/stale-sessions.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -/** - * Tests for the stale-session classifier (sidebar pill, PR108g). - * - * The renderer derives `staleSessionIds: Set` from `sessions` x - * `connections` and passes it to SessionListPanel; rows with matching ids - * get a dim treatment + "已过期" pill. We lock the classifier down here - * so future edits don't drift on what counts as stale. - */ - -import { strict as assert } from 'node:assert'; -import { describe, it } from 'node:test'; -import { deriveStaleSessionIds } from '../../renderer/stale-sessions.js'; - -function session(partial: { id: string; backend?: string; slug?: string }): { - id: string; - backend: string; - llmConnectionSlug: string; -} { - return { - id: partial.id, - backend: partial.backend ?? 'ai-sdk', - llmConnectionSlug: partial.slug ?? 'zai-coding-plan', - }; -} - -describe('deriveStaleSessionIds', () => { - it('returns empty set when no sessions', () => { - const result = deriveStaleSessionIds({ - sessions: [], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.equal(result.size, 0); - }); - - it('flags sessions with backend="fake"', () => { - const result = deriveStaleSessionIds({ - sessions: [ - session({ id: 'a', backend: 'fake', slug: 'fake' }), - session({ id: 'b', backend: 'ai-sdk', slug: 'zai-coding-plan' }), - ], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.deepEqual([...result], ['a']); - }); - - it('flags sessions whose slug is not in the known connections set', () => { - const result = deriveStaleSessionIds({ - sessions: [ - session({ id: 'a', backend: 'ai-sdk', slug: 'fake-claude' }), - session({ id: 'b', backend: 'ai-sdk', slug: 'zai-coding-plan' }), - ], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.deepEqual([...result], ['a']); - }); - - it('flags legacy backend kinds (e.g. "claude") if connection also missing', () => { - const result = deriveStaleSessionIds({ - sessions: [session({ id: 'a', backend: 'claude', slug: 'fake-claude' })], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.deepEqual([...result], ['a']); - }); - - it('does NOT flag a session whose backend is unknown but slug resolves', () => { - // We don't penalize "future backend kind we don't know about" if the - // user's connection still exists. The chat-header banner + send-path - // guard handle the real readiness check. - const result = deriveStaleSessionIds({ - sessions: [session({ id: 'a', backend: 'future-backend', slug: 'zai-coding-plan' })], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.equal(result.size, 0); - }); - - it('reproduces the @WAWQAQ workspace scenario', () => { - // The on-disk state that triggered the P0 — defaultSlug + apiKey are - // correct in `llm-connections.json`, but two legacy sessions in - // sessions/ still reference dead backends: - // - // 3b76ea22 backend=claude slug=fake-claude ← stale - // 7280e103 backend=ai-sdk slug=zai-coding-plan ← OK - // fff5cb61 backend=fake slug=fake ← stale - // - // Without this classifier the user has to click into each session and - // see the chat-header banner to know which ones are broken. - const result = deriveStaleSessionIds({ - sessions: [ - session({ id: '3b76ea22', backend: 'claude', slug: 'fake-claude' }), - session({ id: '7280e103', backend: 'ai-sdk', slug: 'zai-coding-plan' }), - session({ id: 'fff5cb61', backend: 'fake', slug: 'fake' }), - ], - knownConnectionSlugs: new Set(['zai-coding-plan']), - }); - assert.deepEqual([...result].sort(), ['3b76ea22', 'fff5cb61']); - }); - - it('flags everything when the connection store is empty', () => { - const result = deriveStaleSessionIds({ - sessions: [ - session({ id: 'a', backend: 'ai-sdk', slug: 'zai-coding-plan' }), - session({ id: 'b', backend: 'ai-sdk', slug: 'anthropic' }), - ], - knownConnectionSlugs: new Set(), - }); - assert.deepEqual([...result].sort(), ['a', 'b']); - }); -}); - -describe('stale session list wiring', () => { - // Pill visibility on active rows is a product invariant; assert it through - // the panel render, not by grepping Astryx SideNav CSS selectors. - - it('staleSessionIds wires data-stale and pill only on marked sessions', async () => { - const { makeSessionSummary, renderSessionListPanel } = await import( - './session-list-render-helpers.js' - ); - const stale = makeSessionSummary({ id: 'stale-session', name: 'Stale' }); - const healthy = makeSessionSummary({ id: 'healthy-session', name: 'Healthy' }); - const html = renderSessionListPanel({ - sessions: [stale, healthy], - activeId: healthy.id, - staleSessionIds: new Set(['stale-session']), - }); - - const staleChunk = html.match( - /data-session-id="stale-session"[\s\S]*?(?=data-session-id="healthy-session"|$)/, - )?.[0]; - const healthyChunk = html.match( - /data-session-id="healthy-session"[\s\S]*?(?=data-session-id=|$)/, - )?.[0]; - assert.ok(staleChunk, 'expected stale session row markup'); - assert.ok(healthyChunk, 'expected healthy session row markup'); - assert.match(staleChunk, /data-stale="true"/, 'stale session must set data-stale'); - assert.match( - staleChunk, - /maka-list-row-stale-pill/, - 'stale session must render the stale pill', - ); - assert.doesNotMatch( - healthyChunk, - /data-stale="true"/, - 'healthy session must not inherit data-stale', - ); - assert.doesNotMatch( - healthyChunk, - /maka-list-row-stale-pill/, - 'healthy session must not render a stale pill', - ); - }); -}); diff --git a/docs/frontend-architecture-astryx-review-2026-08-09.md b/docs/frontend-architecture-astryx-review-2026-08-09.md index 7f7655fa20..60e73eaf7f 100644 --- a/docs/frontend-architecture-astryx-review-2026-08-09.md +++ b/docs/frontend-architecture-astryx-review-2026-08-09.md @@ -295,7 +295,6 @@ Claims re-checked on disk at review time: | Module kit | `primitives/module-page.tsx` | pass | | Inventory after debt fix | regen | `blocker=0 polish=0 aligned=183` | -Inventory unit tests: `node --test scripts/check-astryx-surface-inventory.test.mjs scripts/check-astryx-alignment.test.mjs` → **5/5 pass**. --- diff --git a/package.json b/package.json index f15ed1f3e2..0492648045 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "test:dist": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --concurrency=3", "test:dist:serial": "npm run test:scripts:full && node scripts/run-workspace-tests-parallel.mjs --serial", "test:fast": "npm run build:test && npm run test:scripts && node scripts/run-workspace-tests-parallel.mjs --concurrency=3", - "test:scripts": "node --test scripts/electron-builder-config.test.mjs scripts/install-electron-with-retry.test.mjs scripts/sync-model-metadata.test.mjs scripts/fixture-env.test.mjs scripts/electron-lifecycle.test.mjs scripts/check-story-annotations.test.mjs scripts/check-dead-css.test.mjs scripts/check-astryx-alignment.test.mjs scripts/check-astryx-surface-inventory.test.mjs scripts/build-astryx-theme.test.mjs scripts/ci-test-plan.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/storybook-visual-smoke.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/cu-trace-analyse.test.mjs scripts/prepare-maka-cu-provenance.test.mjs scripts/prepare-bundled-git.test.mjs scripts/prepare-bundled-git-source.test.mjs scripts/bundled-skill-catalog.test.mjs scripts/windows-test-inventory.test.mjs scripts/windows-smoke.test.mjs scripts/windows-baseline-workflow.test.mjs scripts/windows-recovery-workflow.test.mjs scripts/code-mode-build-order.test.mjs scripts/cli-build-order.test.mjs scripts/dependency-audit-workflow.test.mjs apps/desktop/scripts/dev-app-runtime.test.mjs", - "test:scripts:extended": "node --test scripts/cu-provider-matrix.test.mjs scripts/cu-process-restart-harness.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/windows-x64-release.test.mjs", + "test:scripts": "node --test scripts/electron-builder-config.test.mjs scripts/install-electron-with-retry.test.mjs scripts/sync-model-metadata.test.mjs scripts/electron-lifecycle.test.mjs scripts/ci-test-plan.test.mjs scripts/run-workspace-tests-parallel.test.mjs scripts/cu-e2e-scenarios.test.mjs scripts/cu-report-sanitize.test.mjs scripts/cu-trace-analyse.test.mjs scripts/prepare-maka-cu-provenance.test.mjs scripts/prepare-bundled-git.test.mjs scripts/prepare-bundled-git-source.test.mjs scripts/bundled-skill-catalog.test.mjs apps/desktop/scripts/dev-app-runtime.test.mjs", + "test:scripts:extended": "node --test scripts/cu-provider-matrix.test.mjs scripts/cu-real-model-launcher.test.mjs scripts/macos-arm64-release.test.mjs scripts/windows-x64-release.test.mjs", "test:scripts:full": "npm run test:scripts && npm run test:scripts:extended", "dev": "npm --workspace @maka/desktop run dev:hmr --", "dev:full": "npm run build && npm --workspace @maka/desktop run start", diff --git a/packages/cli/src/__tests__/cli.test.ts b/packages/cli/src/__tests__/cli.test.ts index 4a00ea8ded..77117c89e9 100644 --- a/packages/cli/src/__tests__/cli.test.ts +++ b/packages/cli/src/__tests__/cli.test.ts @@ -115,7 +115,6 @@ describe('Maka CLI args', () => { [['run', 'hello', '--max-steps', '3'], { kind: 'run', args: ['hello', '--max-steps', '3'] }], [['-p', 'hello', '--max-steps', '3'], { kind: 'run', args: ['hello', '--max-steps', '3'] }], [['--version'], { kind: 'version', text: '0.1.0' }], - [['headless'], { kind: 'error', message: 'Unexpected argument: headless', exitCode: 2 }], [['--resume', 'abc'], { kind: 'tui', resumeSessionId: 'abc' }], [ ['--resume', 'abc', '--cwd', '../moved repo'], @@ -147,7 +146,6 @@ describe('Maka CLI args', () => { assert.equal(help.kind, 'help'); if (help.kind === 'help') assert.match(help.text, /Usage: maka/); if (help.kind === 'help') assert.match(help.text, /--resume --cwd /); - if (help.kind === 'help') assert.doesNotMatch(help.text, /inspect|autonomous|headless/i); }); test('preserves an established process exit code', () => { diff --git a/packages/core/src/__tests__/agent-run-continuation-source.test.ts b/packages/core/src/__tests__/agent-run-continuation-source.test.ts index 171e7b47e7..9047e837ee 100644 --- a/packages/core/src/__tests__/agent-run-continuation-source.test.ts +++ b/packages/core/src/__tests__/agent-run-continuation-source.test.ts @@ -57,18 +57,6 @@ describe('AgentRun continuation source decoding', () => { ); }); - it('preserves legacy V1 compatibility for a zero source high-water', () => { - const decoded = decodeAgentRunHeader( - headerWithContinuation({ - sourceInvocationId: 'legacy-source-invocation', - sourceRunId: 'legacy-source-run', - sourceTurnId: 'legacy-source-turn', - sourceRuntimeEventHighWater: 0, - }), - ); - - assert.equal(decoded.continuationSource?.sourceRuntimeEventHighWater, 0); - }); }); function headerWithContinuation( diff --git a/packages/core/src/__tests__/orchestration.test.ts b/packages/core/src/__tests__/orchestration.test.ts index 47fa18dfca..5675a1b461 100644 --- a/packages/core/src/__tests__/orchestration.test.ts +++ b/packages/core/src/__tests__/orchestration.test.ts @@ -8,7 +8,7 @@ import { } from '../orchestration.js'; describe('orchestration contract', () => { - test('legacy sessions resolve to the compatible default mode', () => { + test('sessions without an explicit mode use the default mode', () => { assert.deepEqual(resolveEffectiveOrchestration(undefined, undefined), { mode: 'default', source: 'session', diff --git a/packages/core/src/__tests__/sandbox-boundary.test.ts b/packages/core/src/__tests__/sandbox-boundary.test.ts index 4ad7cef29c..24d5cec8cc 100644 --- a/packages/core/src/__tests__/sandbox-boundary.test.ts +++ b/packages/core/src/__tests__/sandbox-boundary.test.ts @@ -308,20 +308,6 @@ describe('SandboxBoundaryExpansion', () => { }); describe('ExecutionBoundary', () => { - test('migrates every legacy permission mode to a deterministic revision-zero boundary', () => { - for (const mode of ['ask', 'execute'] as const) { - const boundary = createGenesisExecutionBoundary(mode); - expect(boundary.kind).toBe('managed'); - expect(boundary.revision).toBe(0); - if (boundary.kind === 'managed') expect(boundary.profile.name).toBe('workspace-write'); - } - - const explore = createGenesisExecutionBoundary('explore'); - expect(explore.kind).toBe('managed'); - if (explore.kind === 'managed') expect(explore.profile.name).toBe('read-only'); - expect(createGenesisExecutionBoundary('bypass')).toEqual({ kind: 'bypass', revision: 0 }); - }); - test('decodes only a complete full boundary snapshot', () => { const managed = createGenesisExecutionBoundary('ask'); expect(decodeExecutionBoundary(JSON.parse(JSON.stringify(managed)))).toEqual(managed); diff --git a/packages/core/src/__tests__/task-ledger.test.ts b/packages/core/src/__tests__/task-ledger.test.ts index efe1dd2a4e..d47f79bdc6 100644 --- a/packages/core/src/__tests__/task-ledger.test.ts +++ b/packages/core/src/__tests__/task-ledger.test.ts @@ -379,100 +379,6 @@ describe('task lifecycle validators', () => { }); describe('task ledger events', () => { - test('backfills legacy keys and terminal timestamps deterministically', () => { - const legacyRoot = { - id: 'legacy-root', - subject: 'root', - status: 'completed' as const, - createdAt: 1, - updatedAt: 4, - completionEvidence: 'done', - }; - const legacyChild = { - id: 'legacy-child', - subject: 'child', - status: 'pending' as const, - createdAt: 2, - updatedAt: 2, - parentId: legacyRoot.id, - }; - const projection = projectTaskLedgerEvents([ - { - eventId: 'legacy-1', - type: 'task_imported', - ts: 1, - sessionId: 'session-1', - taskId: legacyRoot.id, - nextStatus: legacyRoot.status, - task: legacyRoot, - }, - { - eventId: 'legacy-2', - type: 'task_imported', - ts: 2, - sessionId: 'session-1', - taskId: legacyChild.id, - nextStatus: legacyChild.status, - task: legacyChild, - }, - ]); - assert.deepEqual(projection.diagnostics, []); - assert.deepEqual( - projection.tasks.map((item) => item.key), - ['T1', 'T1.1'], - ); - assert.equal(projection.tasks[0]?.endedAt, 4); - assert.deepEqual( - new Set(projection.backfilledTaskIds), - new Set(['legacy-root', 'legacy-child']), - ); - }); - - test('backfills legacy keys by creation-event order even when timestamps are non-monotonic', () => { - const first = { - id: 'first-event', - subject: 'first', - status: 'pending' as const, - createdAt: 20, - updatedAt: 20, - }; - const second = { - id: 'second-event', - subject: 'second', - status: 'pending' as const, - createdAt: 10, - updatedAt: 10, - }; - const projection = projectTaskLedgerEvents([ - { - eventId: 'legacy-first', - type: 'task_imported', - ts: 20, - sessionId: 'session-1', - taskId: first.id, - nextStatus: first.status, - task: first, - }, - { - eventId: 'legacy-second', - type: 'task_imported', - ts: 10, - sessionId: 'session-1', - taskId: second.id, - nextStatus: second.status, - task: second, - }, - ]); - assert.deepEqual(projection.diagnostics, []); - assert.deepEqual( - projection.tasks.map((task) => [task.id, task.key]), - [ - ['first-event', 'T1'], - ['second-event', 'T2'], - ], - ); - }); - test('diagnoses duplicate and structurally invalid hierarchy keys', () => { const root: Task = { id: 'root', diff --git a/packages/core/src/__tests__/task-submission-readiness.test.ts b/packages/core/src/__tests__/task-submission-readiness.test.ts index 0fa8089799..8bc24d9cd8 100644 --- a/packages/core/src/__tests__/task-submission-readiness.test.ts +++ b/packages/core/src/__tests__/task-submission-readiness.test.ts @@ -47,26 +47,6 @@ describe('task submission readiness', () => { expect(snapshot.blockers[0]?.repairTarget).toBe(undefined); }); - test('normalizes a legacy Codex session model exactly like the send path', () => { - const input = readyInput(); - input.modelTarget = { - kind: 'resolved', - connection: { - ...connection(), - slug: 'codex-sub', - providerType: 'openai-codex', - defaultModel: 'gpt-5.6-sol', - enabledModelIds: ['gpt-5.6-sol'], - models: [{ id: 'gpt-5.6-sol' }], - }, - hasSecret: true, - requestedModel: 'gpt-5-codex', - checkedAt: 90, - }; - - expect(deriveTaskSubmissionReadiness(input).state).toBe('ready'); - }); - test('distinguishes unavailable runtime and workspace from repairable setup', () => { const runtimeInput = readyInput(); runtimeInput.runtime.state = 'unavailable'; diff --git a/packages/runtime-host/src/__tests__/candidate-cli.test.ts b/packages/runtime-host/src/__tests__/candidate-cli.test.ts deleted file mode 100644 index 3175e133f1..0000000000 --- a/packages/runtime-host/src/__tests__/candidate-cli.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import assert from 'node:assert/strict'; -import { test } from 'node:test'; -import { parseInteractiveRuntimeHostCandidateArguments } from '../candidate-cli.js'; - -test('accepts an absolute legacy configuration root for the elected Candidate', () => { - assert.deepEqual( - parseInteractiveRuntimeHostCandidateArguments([ - '--root', - '/runtime-host-root', - '--expected-root-id', - 'a'.repeat(64), - '--legacy-configuration-root', - '/legacy-configuration', - ]), - { - rootPath: '/runtime-host-root', - expectedRootId: 'a'.repeat(64), - legacyConfigurationRoot: '/legacy-configuration', - idleGraceMs: undefined, - handshakeTimeoutMs: undefined, - }, - ); - assert.throws( - () => - parseInteractiveRuntimeHostCandidateArguments([ - '--root', - '/runtime-host-root', - '--expected-root-id', - 'a'.repeat(64), - '--legacy-configuration-root', - 'relative/configuration', - ]), - /Invalid --legacy-configuration-root/, - ); -}); diff --git a/packages/runtime-host/src/__tests__/legacy-runtime-policy-migration.test.ts b/packages/runtime-host/src/__tests__/legacy-runtime-policy-migration.test.ts deleted file mode 100644 index c2e95da98a..0000000000 --- a/packages/runtime-host/src/__tests__/legacy-runtime-policy-migration.test.ts +++ /dev/null @@ -1,346 +0,0 @@ -import assert from 'node:assert/strict'; -import { access, mkdtemp, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { test } from 'node:test'; -import { createDefaultRuntimePolicy } from '@maka/core/runtime-policy'; -import { - createConnectionStore, - createFileCredentialStore, - createSettingsStore, -} from '@maka/storage'; -import { openInteractiveRuntimePolicyStoresForWrite } from '@maka/storage/runtime-policy-stores'; -import { resolveStorageRoot, tryAcquireInteractiveRootOwner } from '@maka/storage/root-authority'; -import { migrateLegacyRuntimePolicy } from '../server/legacy-runtime-policy-migration.js'; - -const JOURNAL_FILE = '.runtime-host-m5-migration.json'; - -test('imports legacy execution policy from its configuration root', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const legacy = createConnectionStore(legacyConfigurationRoot); - await legacy.create({ - slug: 'legacy-openai', - name: 'Legacy OpenAI', - providerType: 'openai', - defaultModel: 'gpt-4.1', - }); - await legacy.update('legacy-openai', { - models: [{ id: 'gpt-4.1' }, { id: 'gpt-4.1-mini' }], - modelSource: 'fetched', - modelsFetchedAt: 1_800_000_000_000, - lastTestStatus: 'verified', - lastTestAt: '2027-01-15T08:00:01.000Z', - }); - await legacy.setDefault('legacy-openai'); - await createFileCredentialStore(legacyConfigurationRoot).setSecret( - 'legacy-openai', - 'api_key', - 'legacy-api-key', - ); - await createSettingsStore(legacyConfigurationRoot).update({ - personalization: { displayName: 'Legacy User', assistantTone: 'precise' }, - network: { - proxy: { - enabled: true, - protocol: 'http', - host: '127.0.0.1', - port: 8080, - authEnabled: true, - username: 'legacy-user', - password: 'legacy-proxy-password', - }, - }, - webSearch: { - enabled: true, - defaultProvider: 'tavily', - providers: { tavily: { apiKey: 'legacy-tavily-key' } }, - }, - subagents: { - presets: [ - { - id: 'legacy-reader', - name: 'Legacy reader', - description: 'Read through the migrated route', - profile: 'local_read', - connectionSlug: 'legacy-openai', - model: 'gpt-4.1', - enabled: true, - }, - ], - }, - }); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const catalog = await stores.connectionCatalog.getSnapshot(); - assert.equal(catalog.connections.length, 1); - const connection = catalog.connections[0]!; - assert.equal(connection.slug, 'legacy-openai'); - assert.deepEqual(connection.models, [{ id: 'gpt-4.1' }, { id: 'gpt-4.1-mini' }]); - assert.equal(connection.lastTest?.status, 'verified'); - assert.deepEqual(catalog.defaultTarget, { - connectionId: connection.connectionId, - modelId: 'gpt-4.1', - }); - - const execution = await stores.operations.resolveExecutionConnection('legacy-openai'); - assert.equal(execution.kind, 'ready'); - if (execution.kind === 'ready') { - assert.equal(execution.secretMaterial.connection?.secret, 'legacy-api-key'); - } - const webSearch = await stores.operations.resolveWebSearchExecution(); - assert.equal(webSearch.kind, 'ready'); - if (webSearch.kind === 'ready') { - assert.equal(webSearch.secretMaterial.webSearch.secret, 'legacy-tavily-key'); - assert.equal(webSearch.secretMaterial.networkProxy?.secret, 'legacy-proxy-password'); - } - const policy = await stores.runtimePolicy.getSnapshot(); - assert.deepEqual(policy.policy.personalization, { - displayName: 'Legacy User', - assistantTone: 'precise', - }); - assert.equal(policy.policy.networkProxy.host, '127.0.0.1'); - assert.equal(policy.policy.webSearch.defaultProvider, 'tavily'); - assert.deepEqual(policy.policy.subagents.presets, [ - { - id: 'legacy-reader', - name: 'Legacy reader', - description: 'Read through the migrated route', - profile: 'local_read', - connectionSlug: 'legacy-openai', - model: 'gpt-4.1', - enabled: true, - }, - ]); - await assertJournalRemoved(root); - }); -}); - -test('keeps an established Runtime Host policy authoritative over legacy files', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - await createConnectionStore(legacyConfigurationRoot).create({ - slug: 'legacy-openai', - name: 'Legacy OpenAI', - providerType: 'openai', - defaultModel: 'gpt-4.1', - }); - const created = await stores.connectionCatalog.create({ - expectedCatalogRevision: 0, - connection: { - slug: 'canonical-deepseek', - name: 'Canonical DeepSeek', - providerType: 'deepseek', - enabled: true, - enabledModelIds: ['deepseek-chat'], - }, - }); - assert.equal(created.kind, 'committed'); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const catalog = await stores.connectionCatalog.getSnapshot(); - assert.deepEqual( - catalog.connections.map(({ slug }) => slug), - ['canonical-deepseek'], - ); - await assertJournalRemoved(root); - }); -}); - -test('imports legacy subagent presets into an established Runtime Host policy', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const { subagents: _subagents, ...versionOnePolicy } = createDefaultRuntimePolicy(); - await writeFile( - join(root, 'runtime-policy.json'), - `${JSON.stringify({ schemaVersion: 1, revision: 3, policy: versionOnePolicy })}\n`, - 'utf8', - ); - await createSettingsStore(legacyConfigurationRoot).update({ - subagents: { - presets: [ - { - id: 'legacy-reader', - name: 'Legacy reader', - description: 'Preserve the configured subagent during policy migration', - profile: 'local_read', - connectionSlug: 'openrouter', - model: 'openrouter/free', - enabled: true, - }, - ], - }, - }); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const policy = await stores.runtimePolicy.getSnapshot(); - assert.equal(policy.revision, 4); - assert.deepEqual( - policy.policy.subagents.presets.map(({ id }) => id), - ['legacy-reader'], - ); - }); -}); - -test('resumes a journaled migration without duplicating committed Connections', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const legacy = createConnectionStore(legacyConfigurationRoot); - await legacy.create({ - slug: 'legacy-openai', - name: 'Legacy OpenAI', - providerType: 'openai', - defaultModel: 'gpt-4.1', - }); - await legacy.setDefault('legacy-openai'); - await createFileCredentialStore(legacyConfigurationRoot).setSecret( - 'legacy-openai', - 'api_key', - 'legacy-api-key', - ); - const partiallyImported = await stores.connectionCatalog.create({ - expectedCatalogRevision: 0, - connection: { - slug: 'legacy-openai', - name: 'Legacy OpenAI', - providerType: 'openai', - enabled: true, - enabledModelIds: ['gpt-4.1'], - }, - }); - assert.equal(partiallyImported.kind, 'committed'); - await writeFile( - join(root, JOURNAL_FILE), - `${JSON.stringify({ version: 1, state: 'importing' })}\n`, - 'utf8', - ); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const catalog = await stores.connectionCatalog.getSnapshot(); - assert.equal(catalog.connections.length, 1); - assert.equal(catalog.connections[0]?.slug, 'legacy-openai'); - assert.deepEqual(catalog.connections[0]?.models, [{ id: 'gpt-4.1' }]); - assert.equal(catalog.connections[0]?.modelSource, 'fallback'); - assert.equal(catalog.defaultTarget?.modelId, 'gpt-4.1'); - const execution = await stores.operations.resolveExecutionConnection('legacy-openai'); - assert.equal(execution.kind, 'ready'); - if (execution.kind === 'ready') { - assert.equal(execution.secretMaterial.connection?.secret, 'legacy-api-key'); - } - await assertJournalRemoved(root); - }); -}); - -test('drops credential-dependent legacy effects when their credential is unavailable', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const legacy = createConnectionStore(legacyConfigurationRoot); - await legacy.create({ - slug: 'codex-subscription', - name: 'OpenAI Codex', - providerType: 'openai-codex', - defaultModel: 'gpt-5.5', - }); - await legacy.update('codex-subscription', { - models: [{ id: 'gpt-5.5' }], - modelSource: 'fetched', - modelsFetchedAt: 1_800_000_000_000, - lastTestStatus: 'needs_reauth', - lastTestAt: '2027-01-15T08:00:01.000Z', - }); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const catalog = await stores.connectionCatalog.getSnapshot(); - assert.equal(catalog.connections[0]?.slug, 'codex-subscription'); - assert.deepEqual(catalog.connections[0]?.models, []); - assert.equal(catalog.connections[0]?.lastTest, undefined); - await assertJournalRemoved(root); - }); -}); - -test('imports legacy interactive OAuth credentials through migration authority', async () => { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const legacy = createConnectionStore(legacyConfigurationRoot); - await legacy.create({ - slug: 'codex-subscription', - name: 'OpenAI Codex', - providerType: 'openai-codex', - defaultModel: 'gpt-5.6-luna', - }); - await createFileCredentialStore(legacyConfigurationRoot).setSecret( - 'codex-subscription', - 'oauth_token', - 'legacy-oauth-token', - ); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const execution = await stores.operations.resolveExecutionConnection('codex-subscription'); - assert.equal(execution.kind, 'ready'); - if (execution.kind === 'ready') { - assert.equal(execution.secretMaterial.connection?.secret, 'legacy-oauth-token'); - } - await assertJournalRemoved(root); - }); -}); - -test('upgrades only the untouched historical free bootstrap during import', async () => { - for (const seed of [ - { - defaultModel: 'big-pickle', - enabledModelIds: ['big-pickle'], - }, - { - defaultModel: 'nemotron-3-ultra-free', - enabledModelIds: ['nemotron-3-ultra-free'], - extras: { makaBootstrap: { id: 'opencode-free', version: 2 } }, - }, - ]) { - await withMigrationRoot(async ({ root, legacyConfigurationRoot, stores }) => { - const legacy = createConnectionStore(legacyConfigurationRoot); - await legacy.create({ - slug: 'opencode-free', - name: 'OpenCode Free', - providerType: 'opencode-free', - ...seed, - }); - await legacy.setDefault('opencode-free'); - - await migrateLegacyRuntimePolicy({ workspaceRoot: root, legacyConfigurationRoot, stores }); - - const catalog = await stores.connectionCatalog.getSnapshot(); - assert.deepEqual(catalog.connections[0]?.enabledModelIds, [ - 'nemotron-3-ultra-free', - 'mimo-v2.5-free', - 'deepseek-v4-flash-free', - ]); - assert.equal(catalog.defaultTarget?.modelId, 'nemotron-3-ultra-free'); - }); - } -}); - -async function withMigrationRoot( - run: (input: { - root: string; - legacyConfigurationRoot: string; - stores: Awaited>; - }) => Promise, -): Promise { - const base = await mkdtemp(join(tmpdir(), 'maka-runtime-policy-migration-')); - const root = join(base, 'workspace'); - const legacyConfigurationRoot = join(base, 'configuration'); - const capability = await resolveStorageRoot({ path: root, kind: 'interactive' }); - const owner = await tryAcquireInteractiveRootOwner(capability); - assert.ok(owner); - try { - const stores = await openInteractiveRuntimePolicyStoresForWrite(owner.lease); - await run({ root, legacyConfigurationRoot, stores }); - } finally { - await owner.close(); - await rm(base, { recursive: true, force: true }); - } -} - -async function assertJournalRemoved(root: string): Promise { - await assert.rejects(access(join(root, JOURNAL_FILE)), { code: 'ENOENT' }); -} diff --git a/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts b/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts index 0cd93b62a3..6f2cf3cb14 100644 --- a/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts +++ b/packages/runtime/src/__tests__/history-compact-checkpoint.test.ts @@ -128,20 +128,13 @@ describe('history compact checkpoint', () => { ); }); - test('keeps legacy V2 checkpoints readable but rejects inconsistent projection cursors', () => { + test('rejects inconsistent projection cursors', () => { const events = [textEvent(0), textEvent(1)]; const checkpoint = buildHistoryCompactCheckpoint({ sessionId: 'session-1', coveredRuntimeEvents: events, summary: 'source-bound', }); - const { source: _source, ...legacy } = checkpoint; - assert.equal(validateHistoryCompactCheckpointShape(legacy, 'session-1'), true); - assert.equal( - matchHistoryCompactCheckpointPrefix(legacy as typeof checkpoint, events).coveredEventCount, - events.length, - ); - const invalid = { ...checkpoint, source: { @@ -252,34 +245,6 @@ describe('history compact checkpoint', () => { assert.equal(loaded?.checkpointId, furthest.checkpointId); }); - test('prefers source-bound recovery over a legacy checkpoint that cannot prove cursor ordering', async () => { - const sourceBound = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: [textEvent(0), textEvent(1)], - summary: 'bound', - }); - const legacyBuilt = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: [textEvent(0), textEvent(1), textEvent(2)], - summary: 'legacy', - }); - const { source: _source, ...legacy } = legacyBuilt; - const store = new StubAgentRunStore( - [run('run-bound', 10), run('run-legacy', 20)], - new Map([ - ['run-bound', [checkpointEvent('ledger-bound', 'run-bound', sourceBound, 10)]], - [ - 'run-legacy', - [checkpointEvent('ledger-legacy', 'run-legacy', legacy as typeof legacyBuilt, 20)], - ], - ]), - ); - - const loaded = await loadLatestHistoryCompactCheckpointFromRunLedger(store, 'session-1'); - - assert.equal(loaded?.checkpointId, sourceBound.checkpointId); - }); - test('recovers the tip of an out-of-order same-coverage successor chain across runs', async () => { const source = [textEvent(0), textEvent(1)]; const first = buildHistoryCompactCheckpoint({ diff --git a/packages/runtime/src/__tests__/history-compact-cleanup.test.ts b/packages/runtime/src/__tests__/history-compact-cleanup.test.ts deleted file mode 100644 index 6f76b69f05..0000000000 --- a/packages/runtime/src/__tests__/history-compact-cleanup.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import assert from 'node:assert/strict'; -import { describe, test } from 'node:test'; -import type { RuntimeEvent } from '@maka/core/runtime-event'; -import { buildHistoryCompactBlockFromSummary } from '../context-budget.js'; -import { cleanupLegacyHistoryCompactArtifacts } from '../history-compact-cleanup.js'; -import { persistHistoryCompactBlocksToArtifacts } from '../history-compact-artifacts.js'; -import { buildHistoryCompactCheckpoint } from '../history-compact-checkpoint.js'; -import { memoryArtifactStore } from './memory-artifact-store.js'; - -describe('legacy history compact cleanup', () => { - test('purges a verified V1 block and its sources after a later V2 checkpoint', async () => { - const store = memoryArtifactStore(); - const legacyEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, legacyEvents); - const runtimeEvents = [...legacyEvents, textEvent(3)]; - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'V2 covers the legacy prefix.', - }); - - const result = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - - assert.equal(result.purgedArtifactIds.length, 4); - assert.deepEqual(result.skipped, []); - assert.deepEqual(await store.list('session-1', { includeDeleted: true }), []); - }); - - test('ignores non-compactable ledger facts when validating V2 coverage', async () => { - const store = memoryArtifactStore(); - const compactableEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, compactableEvents); - const heartbeat: RuntimeEvent = { - ...textEvent(99), - id: 'tool-heartbeat', - partial: true, - role: 'tool', - author: 'tool', - content: undefined, - refs: { toolCallId: 'tool-call-1' }, - }; - const runtimeEvents = [compactableEvents[0]!, heartbeat, ...compactableEvents.slice(1)]; - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: compactableEvents, - summary: 'V2 compactable prefix', - }); - - const result = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - - assert.equal(result.purgedArtifactIds.length, 4); - assert.deepEqual(result.skipped, []); - }); - - test('preserves every legacy artifact when the V2 checkpoint no longer matches the ledger', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, runtimeEvents); - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'checkpoint before corruption', - }); - const changedEvents = [...runtimeEvents]; - changedEvents[1] = { ...changedEvents[1]!, content: { kind: 'text', text: 'changed' } }; - - const result = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents: changedEvents, - artifactStore: store, - }); - - assert.deepEqual(result.purgedArtifactIds, []); - assert.equal(result.skipped.length, 4); - assert.ok(result.skipped.every((item) => item.reason === 'checkpoint_source_hash_mismatch')); - assert.equal((await store.list('session-1', { includeDeleted: true })).length, 4); - }); - - test('preserves V1 data that extends beyond the valid V2 checkpoint prefix', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, runtimeEvents); - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents.slice(0, 2), - summary: 'shorter V2 checkpoint', - }); - - const result = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - - assert.deepEqual(result.purgedArtifactIds, []); - assert.ok(result.skipped.some((item) => item.reason === 'block_coverage_mismatch')); - assert.equal((await store.list('session-1', { includeDeleted: true })).length, 4); - }); - - test('preserves a corrupt V1 block and its unverified source artifacts', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, runtimeEvents); - const records = await store.list('session-1', { includeDeleted: true }); - const block = records.find((record) => record.source === 'history_compact_block')!; - await store.create({ - id: block.id, - sessionId: block.sessionId, - turnId: block.turnId, - name: block.name, - kind: 'file', - content: '{invalid json', - source: 'history_compact_block', - }); - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'valid V2 checkpoint', - }); - const diagnostics: unknown[] = []; - const cleanupInput = { - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - onDiagnostic: (diagnostic: unknown) => diagnostics.push(diagnostic), - }; - - const result = await cleanupLegacyHistoryCompactArtifacts(cleanupInput); - - assert.deepEqual(result.purgedArtifactIds, []); - assert.equal( - result.skipped.find((item) => item.artifactId === block.id)?.reason, - 'block_invalid_json', - ); - assert.equal(result.skipped.filter((item) => item.reason === 'source_unlinked').length, 3); - assert.equal((await store.list('session-1', { includeDeleted: true })).length, 4); - assert.deepEqual(diagnostics, [ - { - kind: 'skipped', - artifactCount: 4, - reasonCounts: { - block_invalid_json: 1, - source_unlinked: 3, - }, - }, - ]); - }); - - test('preserves a V1 group when a linked source no longer matches the canonical event', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, runtimeEvents); - const source = (await store.list('session-1', { includeDeleted: true })).find( - (record) => record.source === 'history_compact_source', - )!; - await store.create({ - id: source.id, - sessionId: source.sessionId, - turnId: source.turnId, - name: source.name, - kind: 'file', - content: JSON.stringify({ ...runtimeEvents[0], content: { kind: 'text', text: 'tampered' } }), - source: 'history_compact_source', - }); - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'valid V2 checkpoint', - }); - - const result = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - - assert.deepEqual(result.purgedArtifactIds, []); - assert.ok(result.skipped.some((item) => item.reason === 'source_content_mismatch')); - assert.equal((await store.list('session-1', { includeDeleted: true })).length, 4); - }); - - test('purges a safely linked group after one source was soft deleted', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0), textEvent(1), textEvent(2)]; - await writeLegacyArtifacts(store, runtimeEvents); - const source = (await store.list('session-1', { includeDeleted: true })).find( - (record) => record.source === 'history_compact_source', - )!; - await store.delete(source.id); - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'valid V2 checkpoint', - }); - - const first = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - const repeated = await cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: store, - }); - - assert.equal(first.purgedArtifactIds.length, 4); - assert.deepEqual(first.skipped, []); - assert.deepEqual(repeated, { purgedArtifactIds: [], skipped: [] }); - }); - - test('reports one cleanup failure without changing the thrown error', async () => { - const store = memoryArtifactStore(); - const runtimeEvents = [textEvent(0)]; - const checkpoint = buildHistoryCompactCheckpoint({ - sessionId: 'session-1', - coveredRuntimeEvents: runtimeEvents, - summary: 'valid V2 checkpoint', - }); - const diagnostics: unknown[] = []; - - await assert.rejects( - () => - cleanupLegacyHistoryCompactArtifacts({ - sessionId: 'session-1', - checkpoint, - runtimeEvents, - artifactStore: { - ...store, - async list() { - throw new Error('metadata unreadable'); - }, - }, - onDiagnostic: (diagnostic) => diagnostics.push(diagnostic), - }), - /metadata unreadable/, - ); - assert.deepEqual(diagnostics, [ - { - kind: 'failed', - message: 'metadata unreadable', - }, - ]); - }); -}); - -async function writeLegacyArtifacts( - store: ReturnType, - events: readonly RuntimeEvent[], -): Promise { - await persistHistoryCompactBlocksToArtifacts(store, { - sessionId: 'session-1', - turnId: 'turn-write', - source: { - draftBlock: buildHistoryCompactBlockFromSummary({ - sessionId: 'session-1', - foldedRuntimeEvents: events, - summary: 'legacy summary', - }), - foldedRuntimeEvents: [...events], - }, - limits: { - maxBlocks: 1, - maxBlockEstimatedTokens: 1_024, - maxEstimatedTokens: 2_048, - charsPerToken: 4, - }, - }); -} - -function textEvent(index: number): RuntimeEvent { - return { - id: `event-${index}`, - invocationId: `invocation-${index}`, - runId: `run-${index}`, - sessionId: 'session-1', - turnId: `turn-${index}`, - ts: index + 1, - partial: false, - role: 'user', - author: 'user', - content: { kind: 'text', text: `fact ${index}` }, - }; -} diff --git a/packages/runtime/src/__tests__/runtime-event-backfill.test.ts b/packages/runtime/src/__tests__/runtime-event-backfill.test.ts deleted file mode 100644 index 7505248d91..0000000000 --- a/packages/runtime/src/__tests__/runtime-event-backfill.test.ts +++ /dev/null @@ -1,529 +0,0 @@ -import { describe, test } from 'node:test'; -import { mkdtemp, rm } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import type { AgentRunHeader, RuntimeEvent, StoredMessage } from '@maka/core'; -import { createSqliteRuntimeStore } from '@maka/storage'; -import { expect } from '../test-helpers.js'; -import { - RUNTIME_EVENT_BACKFILL_STATE_KEY, - backfillRuntimeEventsFromStoredMessages, -} from '../runtime-event-backfill.js'; - -const run: AgentRunHeader = { - runId: 'run-1', - sessionId: 'session-1', - turnId: 'turn-1', - status: 'completed', - backendKind: 'fake', - llmConnectionSlug: 'fake', - modelId: 'fake-model', - cwd: '/tmp/cwd', - permissionMode: 'ask', - createdAt: 100, - updatedAt: 180, - completedAt: 180, -}; - -function nextIds(): () => string { - let index = 0; - return () => { - index += 1; - return `rt-backfill-${index}`; - }; -} - -function recoveryMarker(event: RuntimeEvent): Record | undefined { - return event.actions?.stateDelta?.[RUNTIME_EVENT_BACKFILL_STATE_KEY] as - | Record - | undefined; -} - -describe('runtime event backfill', () => { - test('persists legacy tool history through the canonical generic RuntimeEvent writer', async () => { - const root = await mkdtemp(join(tmpdir(), 'maka-runtime-backfill-sqlite-')); - const store = createSqliteRuntimeStore(join(root, 'runtime.sqlite')); - try { - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages: [ - { - type: 'tool_call', - id: 'legacy-tool-call', - turnId: 'turn-1', - ts: 120, - toolName: 'Read', - activityKind: 'read', - displayName: 'Read file', - intent: 'inspect', - args: { path: 'README.md' }, - }, - { - type: 'tool_result', - id: 'legacy-tool-result', - turnId: 'turn-1', - ts: 130, - toolUseId: 'legacy-tool-call', - isError: false, - content: { kind: 'text', text: 'file body' }, - durationMs: 42, - }, - { - type: 'turn_state', - id: 'legacy-state', - turnId: 'turn-1', - ts: 180, - status: 'completed', - partialOutputRetained: true, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - for (const event of result.events) { - await store.appendRuntimeEvent(event.sessionId, event.runId, event); - } - - const persisted = await store.readImmutableRuntimeEvents(run.sessionId, run.runId); - expect(persisted.map((event) => event.content?.kind ?? event.status)).toEqual([ - 'function_call', - 'function_response', - 'completed', - ]); - expect(persisted[0]?.refs).toEqual({ - storedMessageId: 'legacy-tool-call', - toolCallId: 'legacy-tool-call', - }); - expect(persisted[1]?.refs).toEqual({ - storedMessageId: 'legacy-tool-result', - toolCallId: 'legacy-tool-call', - }); - expect(persisted.some((event) => event.actions?.toolDispatch !== undefined)).toBe(false); - expect(persisted.some((event) => event.refs?.operationId !== undefined)).toBe(false); - } finally { - store.close(); - await rm(root, { recursive: true, force: true }); - } - }); - - test('prefers the persisted Run invocation identity over a caller fallback', () => { - const result = backfillRuntimeEventsFromStoredMessages({ - run: { ...run, invocationId: 'persisted-invocation' }, - invocationId: 'caller-fallback', - messages: [ - { - type: 'turn_state', - id: 'legacy-state', - turnId: 'turn-1', - ts: 180, - status: 'completed', - partialOutputRetained: false, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - expect(result.events.map((event) => event.invocationId)).toEqual(['persisted-invocation']); - }); - - test('preserves provider-native identity in StoredMessage fallback backfill', () => { - const providerOutput = [ - { - type: 'web_search_result', - url: 'https://maka.example/', - title: 'Maka', - pageAge: null, - encryptedContent: 'encrypted-result', - }, - ]; - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages: [ - { - type: 'tool_call', - id: 'search-1', - turnId: 'turn-1', - ts: 120, - toolName: 'WebSearch', - args: { query: 'latest Maka' }, - providerExecuted: true, - }, - { - type: 'tool_result', - id: 'search-result-1', - turnId: 'turn-1', - ts: 130, - toolUseId: 'search-1', - isError: false, - content: { kind: 'web_search', provider: 'model', query: 'latest Maka', rows: [] }, - providerExecuted: true, - providerOutput, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - expect(result.events[0]?.content).toMatchObject({ - kind: 'function_call', - providerExecuted: true, - }); - expect(result.events[1]?.content).toMatchObject({ - kind: 'function_response', - providerExecuted: true, - providerOutput, - }); - }); - - test('drops provider-native fallback history when opaque replay output is unavailable', () => { - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages: [ - { - type: 'tool_call', - id: 'search-legacy', - turnId: 'turn-1', - ts: 120, - toolName: 'WebSearch', - args: { query: 'latest Maka' }, - providerExecuted: true, - }, - { - type: 'tool_result', - id: 'search-result-legacy', - turnId: 'turn-1', - ts: 130, - toolUseId: 'search-legacy', - isError: false, - content: { kind: 'web_search', provider: 'model', query: 'latest Maka', rows: [] }, - providerExecuted: true, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - expect(result.events.map((event) => event.content?.kind ?? event.status)).toEqual([ - 'completed', - ]); - expect(result.diagnostics).toEqual([ - { - code: 'skipped_provider_native_replay_gap', - message: - 'provider-native tool history requires the opaque provider output for lossless recovery', - detail: { messageId: 'search-legacy', toolUseId: 'search-legacy' }, - }, - ]); - }); - - test('backfills a host-authored graph wake without attributing it to the user', () => { - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages: [ - { - type: 'user', - id: 'legacy-graph-wake', - turnId: 'turn-1', - ts: 101, - text: 'graph checkpoint', - origin: { - kind: 'agent_graph', - graphId: 'graph-1', - wakeId: 'wake-1', - attemptId: 'attempt-1', - }, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - expect(result.events[0]?.role).toBe('user'); - expect(result.events[0]?.author).toBe('host'); - expect(result.events[0]?.content).toMatchObject({ - kind: 'text', - origin: { - kind: 'agent_graph', - graphId: 'graph-1', - wakeId: 'wake-1', - attemptId: 'attempt-1', - }, - }); - }); - - test('backfills nested CodeMode tool rows without making them model-visible', () => { - const identity = { - origin: 'code_mode' as const, - modelVisibility: 'hidden' as const, - parentToolCallId: 'exec-1', - parentOperationId: 'exec-op-1', - }; - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages: [ - { - type: 'tool_call', - id: 'nested-1', - turnId: 'turn-1', - ts: 120, - toolName: 'Read', - args: {}, - ...identity, - }, - { - type: 'tool_result', - id: 'nested-result-1', - turnId: 'turn-1', - ts: 130, - toolUseId: 'nested-1', - isError: false, - content: { kind: 'text', text: 'ok' }, - ...identity, - }, - ], - newId: nextIds(), - now: () => 999, - }); - - const toolEvents = result.events.filter( - (event) => - event.content?.kind === 'function_call' || event.content?.kind === 'function_response', - ); - expect(toolEvents).toHaveLength(2); - for (const event of toolEvents) { - expect(event).toMatchObject({ origin: 'code_mode', modelVisibility: 'hidden' }); - expect(event.refs).toMatchObject({ - parentToolCallId: 'exec-1', - parentOperationId: 'exec-op-1', - }); - } - }); - - test('backfills only low-risk RuntimeEvents from legacy StoredMessage rows', () => { - const messages: StoredMessage[] = [ - { - type: 'user', - id: 'legacy-user', - turnId: 'turn-1', - ts: 101, - text: 'hello', - attachments: [ - { - kind: 'other', - name: 'note.txt', - mimeType: 'text/plain', - bytes: 12, - ref: { - kind: 'session_file', - sessionId: 'session-1', - relativePath: 'attachments/note.txt', - }, - }, - ], - }, - { - type: 'assistant', - id: 'legacy-assistant', - turnId: 'turn-1', - ts: 110, - text: 'answer', - modelId: 'fake-model', - thinking: { text: 'reasoning', signature: 'sig-1' }, - }, - { - type: 'tool_call', - id: 'tool-1', - turnId: 'turn-1', - ts: 120, - toolName: 'Read', - activityKind: 'read', - displayName: 'Read file', - intent: 'inspect', - args: { path: 'README.md' }, - stepId: 'step-1', - }, - { - type: 'tool_result', - id: 'legacy-tool-result', - turnId: 'turn-1', - ts: 130, - toolUseId: 'tool-1', - isError: false, - content: { kind: 'text', text: 'file body' }, - durationMs: 42, - }, - { - type: 'permission_decision', - id: 'perm-1', - turnId: 'turn-1', - ts: 140, - toolUseId: 'tool-1', - toolName: 'Read', - decision: 'allow', - rememberForTurn: true, - }, - { - type: 'token_usage', - id: 'usage-1', - turnId: 'turn-1', - ts: 150, - input: 10, - output: 5, - total: 15, - }, - { - type: 'turn_state', - id: 'legacy-state', - turnId: 'turn-1', - ts: 180, - status: 'completed', - partialOutputRetained: true, - }, - ]; - - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages, - newId: nextIds(), - now: () => 999, - }); - - expect(result.diagnostics).toEqual([]); - expect(result.events.map((event) => event.id)).toEqual([ - 'rt-backfill-1', - 'rt-backfill-2', - 'rt-backfill-3', - 'rt-backfill-4', - 'rt-backfill-5', - 'rt-backfill-6', - 'rt-backfill-7', - 'rt-backfill-8', - ]); - expect(result.events.map((event) => event.invocationId)).toEqual( - Array(8).fill('backfill-run-1'), - ); - expect(result.events.map((event) => event.partial)).toEqual(Array(8).fill(false)); - expect(result.events[0]?.content).toEqual({ - kind: 'text', - text: 'hello', - attachments: [ - { - kind: 'other', - name: 'note.txt', - mimeType: 'text/plain', - bytes: 12, - ref: { - kind: 'session_file', - sessionId: 'session-1', - relativePath: 'attachments/note.txt', - }, - }, - ], - }); - expect(result.events[1]?.content).toEqual({ kind: 'text', text: 'answer' }); - expect(result.events[2]?.content).toEqual({ - kind: 'thinking', - text: 'reasoning', - signature: 'sig-1', - }); - expect(result.events[3]?.content).toEqual({ - kind: 'function_call', - id: 'tool-1', - name: 'Read', - args: { path: 'README.md' }, - }); - expect(result.events[3]?.actions?.stateDelta?.displayName).toBe('Read file'); - expect(result.events[3]?.actions?.stateDelta?.activityKind).toBe('read'); - expect(result.events[3]?.actions?.stateDelta?.intent).toBe('inspect'); - expect(result.events[3]?.refs).toEqual({ - storedMessageId: 'tool-1', - toolCallId: 'tool-1', - stepId: 'step-1', - }); - expect(result.events[4]?.content).toEqual({ - kind: 'function_response', - id: 'tool-1', - name: 'Read', - result: { kind: 'text', text: 'file body' }, - isError: false, - }); - expect(result.events[4]?.actions?.stateDelta?.durationMs).toBe(42); - expect(result.events[5]?.actions?.permissionDecision).toEqual({ - requestId: 'perm-1', - decision: 'allow', - rememberForTurn: true, - }); - expect(result.events[5]?.refs).toEqual({ storedMessageId: 'perm-1', toolCallId: 'tool-1' }); - expect(result.events[6]?.actions?.tokenUsage).toEqual({ input: 10, output: 5, total: 15 }); - expect(result.events[7]?.status).toBe('completed'); - expect(result.events[7]?.actions?.endInvocation).toBe(true); - expect(result.events[7]?.refs).toEqual({ storedMessageId: 'legacy-state' }); - - expect(recoveryMarker(result.events[3]!)).toBe(undefined); - expect(recoveryMarker(result.events[4]!)).toBe(undefined); - for (const index of [0, 1, 2, 5, 6, 7]) { - expect(recoveryMarker(result.events[index]!)).toMatchObject({ - kind: 'runtime_event_backfill', - source: 'legacy_stored_message', - reason: 'missing_runtime_event_ledger', - confidence: 'lossless', - generatedAt: 999, - version: 1, - }); - } - }); - - test('skips high-risk legacy rows that cannot be reconstructed safely', () => { - const messages: StoredMessage[] = [ - { - type: 'tool_result', - id: 'orphan-result', - turnId: 'turn-1', - ts: 120, - toolUseId: 'missing-tool', - isError: false, - content: { kind: 'text', text: 'orphan' }, - }, - { - type: 'permission_decision', - id: 'orphan-permission', - turnId: 'turn-1', - ts: 130, - toolUseId: 'missing-tool', - toolName: 'Write', - decision: 'deny', - }, - { - type: 'system_note', - id: 'session-note', - turnId: 'turn-1', - ts: 140, - kind: 'session_resume', - }, - { - type: 'turn_state', - id: 'legacy-state', - turnId: 'turn-1', - ts: 180, - status: 'completed', - partialOutputRetained: false, - }, - ]; - - const result = backfillRuntimeEventsFromStoredMessages({ - run, - messages, - newId: nextIds(), - now: () => 999, - }); - - expect(result.events.map((event) => event.status)).toEqual(['completed']); - expect(result.diagnostics.map((diagnostic) => diagnostic.code)).toEqual([ - 'skipped_unmatched_tool_result', - 'skipped_unmatched_permission_decision', - 'skipped_high_risk_message', - ]); - }); -}); diff --git a/packages/storage/src/__tests__/connection-store.test.ts b/packages/storage/src/__tests__/connection-store.test.ts index 94e0d6d548..fa08e8d2b6 100644 --- a/packages/storage/src/__tests__/connection-store.test.ts +++ b/packages/storage/src/__tests__/connection-store.test.ts @@ -72,34 +72,6 @@ describe('FileConnectionStore', () => { }); }); - test('migrates a legacy connection to only its default model enabled', async () => { - await withConnectionStore(async (store, dir) => { - await writeFile( - join(dir, 'llm-connections.json'), - JSON.stringify({ - defaultSlug: 'openrouter-main', - connections: [ - { - slug: 'openrouter-main', - name: 'OpenRouter', - providerType: 'openrouter', - defaultModel: 'openrouter/auto', - enabled: true, - models: [{ id: 'openrouter/auto' }, { id: 'anthropic/claude-opus-4.8' }], - createdAt: 1, - updatedAt: 1, - }, - ], - }), - 'utf8', - ); - - const migrated = await store.get('openrouter-main'); - - assert.deepEqual(migrated?.enabledModelIds, ['openrouter/auto']); - }); - }); - test('relay profiles sanitize on create and prune when a model is disabled', async () => { await withConnectionStore(async (store) => { const created = await store.create({ diff --git a/packages/storage/src/__tests__/project-catalog.test.ts b/packages/storage/src/__tests__/project-catalog.test.ts index 1a1d622705..c0e4d922f9 100644 --- a/packages/storage/src/__tests__/project-catalog.test.ts +++ b/packages/storage/src/__tests__/project-catalog.test.ts @@ -508,94 +508,6 @@ test('selecting a project returns its most recent available location and rejects } }); -test('a malformed legacy catalog is reported and preserved without blocking the catalog', async () => { - const base = await mkdtemp(join(tmpdir(), 'maka-project-corrupt-')); - try { - const workspace = join(base, 'workspace'); - const storage = join(base, 'storage'); - const catalogPath = join(storage, 'projects.json'); - await mkdir(workspace); - await mkdir(storage); - const original = '{"schemaVersion":1,"projects":[{}]}\n'; - await writeFile(catalogPath, original, 'utf8'); - const failures: unknown[] = []; - const catalog = createProjectCatalog(storage, { - onLegacyImportFailure: (error) => failures.push(error), - }); - - // SQLite is the authority: a legacy file that cannot be read must not take - // the catalog down with it, and it must stay on disk to recover by hand. - const project = await catalog.register(workspace); - - assert.equal((await catalog.list()).length, 1); - assert.equal((await catalog.list())[0]?.id, project.id); - assert.equal(await readFile(catalogPath, 'utf8'), original); - assert.equal(failures.length, 1); - assert.match(String(failures[0]), /Invalid project catalog/); - } finally { - await rm(base, { recursive: true, force: true }); - } -}); - -test('a legacy catalog is imported once and then set aside', async () => { - const base = await mkdtemp(join(tmpdir(), 'maka-project-import-')); - try { - const storage = join(base, 'storage'); - await mkdir(storage); - await writeFile( - join(storage, 'projects.json'), - JSON.stringify({ - schemaVersion: 1, - projects: [ - { - id: 'legacy-project', - aliases: ['merged-away'], - name: 'Renamed By Hand', - identity: 'folder:/gone', - locations: [{ path: '/gone', isWorktree: false, lastUsedAt: 5 }], - lastUsedAt: 7, - archivedAt: 9, - }, - ], - }), - 'utf8', - ); - const failures: unknown[] = []; - const catalog = createProjectCatalog(storage, { - now: () => 1_000, - onLegacyImportFailure: (error) => failures.push(error), - }); - - const projects = await catalog.list(); - - assert.deepEqual(failures, []); - // The user's name, relink aliases and archive state only ever lived in this - // file; losing them on upgrade would be indistinguishable from data loss. - assert.equal(projects.length, 1); - assert.equal(projects[0]?.id, 'legacy-project'); - assert.equal(projects[0]?.name, 'Renamed By Hand'); - assert.deepEqual(projects[0]?.aliases, ['merged-away']); - assert.equal(projects[0]?.archivedAt, 9); - await assert.rejects(() => readFile(join(storage, 'projects.json'), 'utf8'), { - code: 'ENOENT', - }); - const setAside = JSON.parse( - await readFile(join(storage, 'projects.json.imported-1000'), 'utf8'), - ) as { projects: Array<{ id: string }> }; - assert.deepEqual( - setAside.projects.map((project) => project.id), - ['legacy-project'], - 'the imported file is kept verbatim so a bad upgrade stays recoverable', - ); - - // A catalog opened later must not re-import and must not lose the state. - catalog.close(); - assert.equal((await createProjectCatalog(storage).list()).length, 1); - } finally { - await rm(base, { recursive: true, force: true }); - } -}); - test('registering a filesystem root writes a project that a fresh catalog can read', async () => { const base = await mkdtemp(join(tmpdir(), 'maka-project-root-')); const storage = join(base, 'storage'); diff --git a/packages/storage/src/__tests__/sqlite-runtime-store.test.ts b/packages/storage/src/__tests__/sqlite-runtime-store.test.ts index cfb195b35f..ff91dcc215 100644 --- a/packages/storage/src/__tests__/sqlite-runtime-store.test.ts +++ b/packages/storage/src/__tests__/sqlite-runtime-store.test.ts @@ -41,193 +41,6 @@ describe('SqliteRuntimeStore', () => { }); }); - it('upgrades a populated mainline schema 6 database without rewriting RuntimeEvents', async () => { - await withStore(async (store, dbPath) => { - const historical = functionCallEvent({ - id: 'schema-6-historical-event', - content: { kind: 'text', text: 'preserve me across v6 to v7' }, - }); - await store.appendRuntimeEvent(historical.sessionId, historical.runId, historical); - store.close(); - - const legacy = new DatabaseSync(dbPath); - legacy.exec(` - DROP TABLE runtime_partial_segments; - DROP TABLE runtime_session_event_ordinals; - DROP TABLE runtime_storage_root_binding; - DROP TABLE runtime_workspace_heads; - DROP TABLE runtime_workspace_versions; - DROP TABLE runtime_workspace_epochs; - DELETE FROM runtime_capabilities - WHERE capability = 'runtime_workspace_version_authority'; - PRAGMA user_version = 6; - `); - legacy.close(); - - const upgraded = createSqliteRuntimeStore(dbPath); - try { - assert.equal(upgraded.schemaVersion(), SQLITE_RUNTIME_SCHEMA_VERSION); - assert.deepEqual( - await upgraded.readImmutableRuntimeEvents(historical.sessionId, historical.runId), - [historical], - ); - assert.deepEqual(await upgraded.readSessionRuntimeEventEntries(historical.sessionId), [ - { ordinal: 1, event: historical }, - ]); - const inspect = new DatabaseSync(dbPath); - try { - const columns = inspect - .prepare('PRAGMA table_info(runtime_continuation_claims)') - .all() as Array<{ name: string }>; - assert.ok(columns.some((column) => column.name === 'start_kind')); - } finally { - inspect.close(); - } - } finally { - upgraded.close(); - } - }); - }); - - it('drops the retired TaskRun event table during migration', async () => { - await withStore(async (store, dbPath) => { - store.close(); - - const legacy = new DatabaseSync(dbPath); - legacy.exec(` - CREATE TABLE headless_task_run_events ( - task_run_id TEXT NOT NULL, - sequence INTEGER NOT NULL, - event_id TEXT NOT NULL, - record_json TEXT NOT NULL, - PRIMARY KEY (task_run_id, sequence) - ); - PRAGMA user_version = 11; - `); - legacy.close(); - - const upgraded = createSqliteRuntimeStore(dbPath); - try { - assert.equal(upgraded.schemaVersion(), SQLITE_RUNTIME_SCHEMA_VERSION); - const inspect = new DatabaseSync(dbPath); - try { - assert.equal( - inspect - .prepare( - "SELECT COUNT(*) AS count FROM sqlite_master WHERE type = 'table' AND name = 'headless_task_run_events'", - ) - .get()?.count, - 0, - ); - assert.deepEqual( - inspect - .prepare('PRAGMA table_info(runtime_storage_root_binding)') - .all() - .map((row) => (row as { name: string }).name), - ['singleton', 'root_id', 'protocol_version'], - ); - } finally { - inspect.close(); - } - } finally { - upgraded.close(); - } - }); - }); - - it('upgrades a schema 9 partial snapshot and appends new segments without rewriting it', async () => { - await withStore(async (store, dbPath) => { - const partial = (id: string, ts: number, text: string): RuntimeEvent => - functionCallEvent({ - id, - ts, - partial: true, - role: 'model', - author: 'agent', - content: { kind: 'text', text }, - refs: { providerEventId: 'message-1' }, - }); - await store.appendRuntimeEvent('session-1', 'run-1', partial('partial-old', 1, 'old')); - store.close(); - - const legacy = new DatabaseSync(dbPath); - legacy.prepare(`UPDATE runtime_partial_snapshots SET text_content = 'old'`).run(); - legacy.exec(` - DROP TABLE runtime_partial_segments; - DROP TABLE runtime_session_event_ordinals; - PRAGMA user_version = 9; - `); - legacy.close(); - - const upgraded = createSqliteRuntimeStore(dbPath); - try { - const before = await upgraded.readRuntimeEvents('session-1', 'run-1'); - assert.equal( - before[0]?.content?.kind === 'text' ? before[0].content.text : undefined, - 'old', - ); - await upgraded.appendRuntimeEvent('session-1', 'run-1', partial('partial-new', 2, 'new')); - const after = await upgraded.readRuntimeEvents('session-1', 'run-1'); - assert.equal( - after[0]?.content?.kind === 'text' ? after[0].content.text : undefined, - 'oldnew', - ); - } finally { - upgraded.close(); - } - }); - }); - - it('backfills schema 10 Session ordinals in SQLite insertion order', async () => { - await withStore(async (store, dbPath) => { - const first = functionCallEvent({ id: 'legacy-first', ts: 20 }); - const second = functionCallEvent({ - id: 'legacy-second', - invocationId: 'invocation-2', - runId: 'run-2', - turnId: 'turn-2', - ts: 10, - }); - await store.appendRuntimeEvent(first.sessionId, first.runId, first); - await store.appendRuntimeEvent(second.sessionId, second.runId, second); - store.close(); - - const legacy = new DatabaseSync(dbPath); - legacy.exec(` - DROP TABLE runtime_session_event_ordinals; - PRAGMA user_version = 10; - `); - legacy.close(); - - const upgraded = createSqliteRuntimeStore(dbPath); - try { - assert.deepEqual( - (await upgraded.readSessionRuntimeEventEntries(first.sessionId)).map( - ({ ordinal, event }) => ({ ordinal, eventId: event.id }), - ), - [ - { ordinal: 1, eventId: first.id }, - { ordinal: 2, eventId: second.id }, - ], - ); - const third = functionCallEvent({ - id: 'legacy-third', - invocationId: 'invocation-3', - runId: 'run-3', - turnId: 'turn-3', - ts: 5, - }); - await upgraded.appendRuntimeEvent(third.sessionId, third.runId, third); - assert.equal( - (await upgraded.readSessionRuntimeEventEntries(first.sessionId)).at(-1)?.ordinal, - 3, - ); - } finally { - upgraded.close(); - } - }); - }); - it('assigns stable Session ordinals in commit order across Runs', async () => { await withStore(async (store, dbPath) => { const first = functionCallEvent({ id: 'ordinal-1', ts: 20 }); diff --git a/packages/storage/src/__tests__/sqlite-scheduling-schema.test.ts b/packages/storage/src/__tests__/sqlite-scheduling-schema.test.ts deleted file mode 100644 index adcca8ccbf..0000000000 --- a/packages/storage/src/__tests__/sqlite-scheduling-schema.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import assert from 'node:assert/strict'; -import { DatabaseSync } from 'node:sqlite'; -import { describe, test } from 'node:test'; -import { migrateSqliteAutomationDatabase } from '../sqlite-automation-schema.js'; -import { migrateSqliteWorkflowDatabase } from '../sqlite-workflow-schema.js'; - -describe('SQLite scheduling schema', () => { - test('replaces the legacy durable Automation catalog with the heartbeat schema', () => { - const database = new DatabaseSync(':memory:'); - try { - database.exec(` - CREATE TABLE automation_authority_state ( - singleton INTEGER PRIMARY KEY, - revision INTEGER NOT NULL - ); - INSERT INTO automation_authority_state VALUES (1, 9); - CREATE TABLE automation_definitions ( - automation_id TEXT PRIMARY KEY, - session_id TEXT NOT NULL, - created_at INTEGER NOT NULL, - status TEXT NOT NULL, - durable INTEGER NOT NULL, - record_json TEXT NOT NULL - ); - INSERT INTO automation_definitions - VALUES ('legacy-cron', 'session-1', 1, 'active', 1, '{}'); - CREATE TABLE automation_pending_fires ( - fire_id TEXT PRIMARY KEY, - automation_id TEXT NOT NULL, - target_session_id TEXT NOT NULL, - admitted_at INTEGER NOT NULL, - record_json TEXT NOT NULL - ); - `); - - migrateSqliteAutomationDatabase(database); - - assert.deepEqual( - database - .prepare('PRAGMA table_info(automation_definitions)') - .all() - .map((row) => row.name), - ['automation_id', 'session_id', 'created_at', 'status', 'record_json'], - ); - assert.equal( - ( - database.prepare('SELECT COUNT(*) AS count FROM automation_definitions').get() as { - count: number; - } - ).count, - 0, - ); - assert.equal( - ( - database.prepare('SELECT revision FROM automation_authority_state').get() as { - revision: number; - } - ).revision, - 0, - ); - } finally { - database.close(); - } - }); - - test('drops the legacy plan-reminder catalog instead of migrating it', () => { - const database = new DatabaseSync(':memory:'); - try { - database.exec(` - CREATE TABLE workflow_plan_reminders ( - reminder_id TEXT PRIMARY KEY, - record_json TEXT NOT NULL - ); - CREATE INDEX workflow_plan_reminders_order - ON workflow_plan_reminders(reminder_id); - INSERT INTO workflow_plan_reminders VALUES ('legacy-reminder', '{}'); - `); - - migrateSqliteWorkflowDatabase(database); - - assert.equal( - database - .prepare( - "SELECT COUNT(*) AS count FROM sqlite_master WHERE name = 'workflow_plan_reminders'", - ) - .get()?.count, - 0, - ); - assert.equal( - database - .prepare( - "SELECT COUNT(*) AS count FROM sqlite_master WHERE name = 'workflow_plan_reminders_order'", - ) - .get()?.count, - 0, - ); - } finally { - database.close(); - } - }); -}); diff --git a/scripts/check-astryx-alignment.test.mjs b/scripts/check-astryx-alignment.test.mjs deleted file mode 100644 index 91f668adf1..0000000000 --- a/scripts/check-astryx-alignment.test.mjs +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Drives the real alignment gate script (not a reimplementation). - */ -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { join } from 'node:path'; -import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; - -const root = join(fileURLToPath(new URL('..', import.meta.url))); -const script = join(root, 'scripts/check-astryx-alignment.mjs'); - -describe('check-astryx-alignment', () => { - it('passes against the current product tree', () => { - const result = spawnSync(process.execPath, [script], { - cwd: root, - encoding: 'utf8', - }); - assert.equal(result.status, 0, result.stdout + result.stderr); - assert.match(result.stdout, /astryx alignment check: ok/); - }); -}); diff --git a/scripts/check-astryx-surface-inventory.test.mjs b/scripts/check-astryx-surface-inventory.test.mjs deleted file mode 100644 index f467ad983b..0000000000 --- a/scripts/check-astryx-surface-inventory.test.mjs +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Drives the real coverage gate (disk path set vs inventory artifacts). - */ -import assert from 'node:assert/strict'; -import { spawnSync } from 'node:child_process'; -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { describe, it } from 'node:test'; -import { fileURLToPath } from 'node:url'; - -const root = join(fileURLToPath(new URL('..', import.meta.url))); - -describe('astryx surface file inventory coverage', () => { - it('passes the coverage gate against on-disk product trees', () => { - const result = spawnSync( - process.execPath, - [join(root, 'scripts/check-astryx-surface-inventory.mjs')], - { cwd: root, encoding: 'utf8' }, - ); - assert.equal(result.status, 0, result.stdout + result.stderr); - assert.match(result.stdout, /astryx surface inventory coverage: ok/); - }); - - it('lists every settings page/modal as its own markdown row', () => { - const md = readFileSync(join(root, 'docs/astryx-surface-file-inventory.md'), 'utf8'); - const required = [ - 'apps/desktop/src/renderer/settings/general-settings-page.tsx', - 'apps/desktop/src/renderer/settings/appearance-settings-page.tsx', - 'apps/desktop/src/renderer/settings/settings-modal.tsx', - 'apps/desktop/src/renderer/mcp-page.tsx', - 'packages/ui/src/skills-panel.tsx', - 'packages/ui/src/scheduled-task-panel.tsx', - 'packages/ui/src/daily-review-panel.tsx', - 'packages/ui/src/primitives/module-page.tsx', - ]; - for (const path of required) { - assert.match(md, new RegExp(`\`${path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\``)); - } - // No family-batch collapse of settings pages - assert.doesNotMatch(md, /General, Appearance, Data, Providers, Bot, Memory.*already aligned/); - }); - - it('paths file line count matches markdown file rows for inventoried files', () => { - const paths = readFileSync(join(root, 'docs/astryx-surface-file-inventory.paths'), 'utf8') - .trim() - .split('\n') - .filter(Boolean); - const md = readFileSync(join(root, 'docs/astryx-surface-file-inventory.md'), 'utf8'); - const filesSection = md.split('## Files')[1] ?? ''; - const rowCount = [...filesSection.matchAll(/^\| `[^`]+` \|/gm)].length; - assert.equal(rowCount, paths.length, `md rows ${rowCount} vs paths ${paths.length}`); - }); - - it('does not claim Astryx usage from comment-only name matches', () => { - const md = readFileSync(join(root, 'docs/astryx-surface-file-inventory.md'), 'utf8'); - // These files historically false-positive'd when the analyzer scanned comments. - const commentOnly = [ - 'packages/ui/src/icons.tsx', - 'apps/desktop/src/renderer/live-turn-reconciler.tsx', - 'apps/desktop/src/renderer/settings/settings-rows.tsx', - 'packages/ui/src/primitives/chat.tsx', - 'packages/ui/src/primitives/page-header.tsx', - 'apps/desktop/src/renderer/settings/settings-metric-card.tsx', - ]; - for (const path of commentOnly) { - const line = md.split('\n').find((l) => l.includes(`\`${path}\``)); - assert.ok(line, `missing inventory row for ${path}`); - // Table columns: Path | Role | Astryx used | Gap | Severity - const cols = line.split('|').map((c) => c.trim()); - // cols[0] empty, [1]=path, [2]=role, [3]=astryx, [4]=gap, [5]=severity - assert.equal(cols[3], 'none', `${path} Astryx used must be none, got: ${cols[3]}`); - assert.doesNotMatch(cols[4] ?? '', /uses Astryx/); - } - }); -}); diff --git a/scripts/check-dead-css.test.mjs b/scripts/check-dead-css.test.mjs deleted file mode 100644 index 7636fca49d..0000000000 --- a/scripts/check-dead-css.test.mjs +++ /dev/null @@ -1,130 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { - analyzeTokenSheet, - collectClassSelectors, - hasExactConsumer, - parseLeafRules, - resolveLiveTokens, -} from './check-dead-css.mjs'; - -// #1980 blind spot 2: `maka-shell` was reported live because the substring -// check found it inside `maka-shell-astryx`. Class names extend across -// hyphens, so a prefix hit in either direction is not a consumer. -test('a longer hyphenated class name is not a consumer of its prefix', () => { - assert.equal(hasExactConsumer('className="maka-shell-astryx"', 'maka-shell'), false); - assert.equal(hasExactConsumer('className="maka-shell"', 'maka-shell-astryx'), false); -}); - -test('exact matches count as consumers regardless of surrounding syntax', () => { - assert.equal(hasExactConsumer('className="maka-shell chat"', 'maka-shell'), true); - assert.equal(hasExactConsumer("classes.push('maka-shell')", 'maka-shell'), true); - assert.equal(hasExactConsumer('
', 'maka-shell'), true); -}); - -test('class selectors are collected from compact and nested rules', () => { - const css = ` - .maka-shell { display: grid; } - @media (min-width: 600px) { .maka-sidebar:hover, .maka-titlebar strong { color: red; } } - /* .maka-commented { } */ - `; - assert.deepEqual([...collectClassSelectors(css)].sort(), [ - 'maka-shell', - 'maka-sidebar', - 'maka-titlebar', - ]); -}); - -test('parseLeafRules flattens at-rule wrappers to selector/body pairs', () => { - const rules = parseLeafRules( - '@layer components { .a { color: red; } @media (x) { .b { color: blue; } } }', - ); - assert.deepEqual( - rules.map((rule) => rule.selector), - ['.a', '.b'], - ); -}); - -// #1980 blind spot 3: --w-rail / --w-sidebar were reported live because dead -// shell rules inside the token sheet itself read them. -test('a token read only by a dead rule in the token sheet is dead', () => { - const sheet = ` - :root { --w-rail: 240px; } - .maka-shell { grid-template-columns: var(--w-rail) 1fr; } - `; - const analysis = analyzeTokenSheet(sheet, () => false); - assert.equal(resolveLiveTokens(new Set(), analysis).has('--w-rail'), false); -}); - -test('the same read counts once the rule class has a consumer', () => { - const sheet = ` - :root { --w-rail: 240px; } - .maka-shell { grid-template-columns: var(--w-rail) 1fr; } - `; - const analysis = analyzeTokenSheet(sheet, (cls) => cls === 'maka-shell'); - assert.equal(resolveLiveTokens(new Set(), analysis).has('--w-rail'), true); -}); - -// A comma group applies when any branch matches, so one live branch must -// keep the whole rule's reads alive — and all-dead branches must not. -test('a comma group with one live branch keeps its token reads', () => { - const sheet = '.maka-dead, .maka-live { width: var(--w-shared); }'; - const live = (isLive) => - resolveLiveTokens(new Set(), analyzeTokenSheet(sheet, isLive)).has('--w-shared'); - assert.equal( - live((cls) => cls === 'maka-live'), - true, - ); - assert.equal( - live(() => false), - false, - ); -}); - -test('token-to-token derivation keeps a base token alive only via a live head', () => { - const sheet = ':root { --derived: var(--base); --base: 4px; }'; - const analysis = analyzeTokenSheet(sheet, () => false); - assert.equal(resolveLiveTokens(new Set(['--derived']), analysis).has('--base'), true); - assert.equal(resolveLiveTokens(new Set(), analysis).has('--base'), false); -}); - -test('derivation chains resolve transitively', () => { - const sheet = ':root { --a: var(--b); --b: var(--c); --c: 1px; }'; - const analysis = analyzeTokenSheet(sheet, () => false); - const live = resolveLiveTokens(new Set(['--a']), analysis); - assert.equal(live.has('--c'), true); -}); - -test('a self-referencing token does not loop and does not revive itself', () => { - const sheet = ':root { --a: var(--a, 4px); }'; - const analysis = analyzeTokenSheet(sheet, () => false); - assert.equal(resolveLiveTokens(new Set(), analysis).has('--a'), false); - assert.equal(resolveLiveTokens(new Set(['--a']), analysis).has('--a'), true); -}); - -test('a derivation cycle terminates and lives or dies as one unit', () => { - const sheet = ':root { --a: var(--b); --b: var(--a); }'; - const analysis = analyzeTokenSheet(sheet, () => false); - const dead = resolveLiveTokens(new Set(), analysis); - assert.equal(dead.has('--a'), false); - assert.equal(dead.has('--b'), false); - const live = resolveLiveTokens(new Set(['--a']), analysis); - assert.equal(live.has('--a'), true); - assert.equal(live.has('--b'), true); -}); - -test('a diamond of derivations resolves every path to the shared base', () => { - const sheet = - ':root { --top: var(--left) var(--right); --left: var(--base); --right: var(--base); --base: 1px; }'; - const analysis = analyzeTokenSheet(sheet, () => false); - const live = resolveLiveTokens(new Set(['--top']), analysis); - for (const token of ['--left', '--right', '--base']) { - assert.equal(live.has(token), true, `${token} should be live via the diamond`); - } -}); - -test('reads in rules without class selectors always count', () => { - const sheet = 'body { margin: var(--space-page); }'; - const analysis = analyzeTokenSheet(sheet, () => false); - assert.equal(resolveLiveTokens(new Set(), analysis).has('--space-page'), true); -}); diff --git a/scripts/check-story-annotations.test.mjs b/scripts/check-story-annotations.test.mjs deleted file mode 100644 index 034f13db6e..0000000000 --- a/scripts/check-story-annotations.test.mjs +++ /dev/null @@ -1,90 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { checkFile, checkStorybookRoots } from './check-story-annotations.mjs'; - -function problemsFor(source) { - const problems = []; - checkFile('stories/example.stories.tsx', source, problems); - return problems; -} - -const PRODUCT_META = `const meta = { title: 'Product/Example' } satisfies Meta;\n`; - -test('a Product story without a Real path comment is reported', () => { - const problems = problemsFor(`${PRODUCT_META} -export const Annotated: Story = { render: () => null }; -`); - assert.equal(problems.length, 1); - assert.match(problems[0], /Annotated has no `\/\/ Real path:` comment/); -}); - -test('an unsupported export shape fails instead of being skipped', () => { - const problems = problemsFor(`${PRODUCT_META} -// Real path: sidebar → 扩展 → 技能. -export const Sneaky = { render: () => null }; -`); - assert.equal(problems.length, 1); - assert.match(problems[0], /Sneaky is not `export const : Story = …`/); -}); - -// TSX allows the break after `export`, so a line-based scanner sees no export -// on either line. This shape used to slip through unannotated while the check -// reported that every story names its path. -test('an export broken across lines is reported, not skipped', () => { - const problems = problemsFor(`${PRODUCT_META} -export -const Sneaky: Story = { render: () => null }; -`); - assert.equal(problems.length, 1); - assert.match(problems[0], /is not `export const : Story = …`/); -}); - -test('an empty `// Real path:` is not an annotation', () => { - const problems = problemsFor(`${PRODUCT_META} -// Real path: -export const Bare: Story = { render: () => null }; -`); - assert.equal(problems.length, 1); -}); - -test('a title in no known namespace is reported rather than silently skipped', () => { - const problems = problemsFor(`const meta = { title: 'Scratch/Thing' } satisfies Meta; -export const Unannotated: Story = { render: () => null }; -`); - assert.equal(problems.length, 1); - assert.match(problems[0], /neither Product\/\* nor an exempt namespace/); -}); - -test('a file with no meta title is reported', () => { - assert.equal(problemsFor('export const Orphan: Story = {};\n').length, 1); -}); - -const BOTH_ROOTS = - "stories: ['../../../packages/ui/stories/**/*.stories.@(ts|tsx)', " + - "resolve(REPO_ROOT, 'apps/desktop/stories/**/*.stories.@(ts|tsx)')]"; - -function rootProblems(config) { - const problems = []; - checkStorybookRoots(config, problems); - return problems; -} - -// Both story roots end in `stories`, so matching only the last path segment -// stays satisfied by whichever root is left and passes in silence. -test('dropping either story root from the Storybook config is caught', () => { - for (const dropped of ['packages/ui/stories', 'apps/desktop/stories']) { - const problems = rootProblems(BOTH_ROOTS.replace(`${dropped}/**/*.stories.`, 'elsewhere/')); - assert.equal(problems.length, 1, dropped); - assert.match(problems[0], new RegExp(`no longer loads ${dropped}`)); - } -}); - -// The other direction: a root Storybook loads but this scanner never opens is -// unchecked coverage, which is what the guard claims cannot happen. -test('adding a story root to the Storybook config is caught', () => { - const problems = rootProblems( - BOTH_ROOTS.replace(']', ", 'apps/desktop/e2e-stories/**/*.stories.@(ts|tsx)']"), - ); - assert.equal(problems.length, 1); - assert.match(problems[0], /loads apps\/desktop\/e2e-stories, which is not in STORY_ROOTS/); -}); diff --git a/scripts/ci-test-plan.mjs b/scripts/ci-test-plan.mjs index b8ebf857f3..f580b3319b 100644 --- a/scripts/ci-test-plan.mjs +++ b/scripts/ci-test-plan.mjs @@ -26,10 +26,8 @@ const TYPECHECK_ONLY_FILES = new Set([ const WINDOWS_BASELINE_FILES = new Set([ '.github/workflows/windows-baseline.yml', - 'scripts/windows-baseline-workflow.test.mjs', 'scripts/windows-process-identity.ps1', 'scripts/windows-smoke.mjs', - 'scripts/windows-smoke.test.mjs', ]); /** @@ -120,7 +118,6 @@ function isE2eProductPath(path) { const EXTENDED_SCRIPT_FILES = new Set([ 'scripts/cu-process-restart-e2e.mjs', - 'scripts/cu-process-restart-harness.test.mjs', 'scripts/cu-provider-matrix.mjs', 'scripts/cu-provider-matrix.test.mjs', 'scripts/cu-real-model-fixture.mjs', diff --git a/scripts/ci-test-plan.test.mjs b/scripts/ci-test-plan.test.mjs index 35ed712861..b5d6247214 100644 --- a/scripts/ci-test-plan.test.mjs +++ b/scripts/ci-test-plan.test.mjs @@ -99,7 +99,6 @@ test('Windows planning runs workflow changes fully and helper changes narrowly', assert.equal(workflow.windowsStorage, true); for (const path of [ - 'scripts/windows-baseline-workflow.test.mjs', 'scripts/windows-process-identity.ps1', 'scripts/windows-smoke.mjs', ]) { diff --git a/scripts/cli-build-order.test.mjs b/scripts/cli-build-order.test.mjs deleted file mode 100644 index 0bc5a2abe4..0000000000 --- a/scripts/cli-build-order.test.mjs +++ /dev/null @@ -1,55 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -const repoRoot = new URL('../', import.meta.url); - -function workspaceBuild(name) { - return `npm --workspace ${name} run build`; -} - -async function readManifest(path) { - return JSON.parse(await readFile(new URL(path, repoRoot), 'utf8')); -} - -test('CLI pretest builds direct workspace dependencies and orders Eval', async () => { - const root = await readManifest('package.json'); - const cli = await readManifest('packages/cli/package.json'); - const evalPackage = await readManifest('packages/eval/package.json'); - const workspaceNames = new Set(); - - for (const workspacePath of root.workspaces) { - const manifest = await readManifest(`${workspacePath}/package.json`); - workspaceNames.add(manifest.name); - } - - const pretest = cli.scripts.pretest; - const directWorkspaceDependencies = Object.keys(cli.dependencies ?? {}).filter((name) => - workspaceNames.has(name), - ); - - for (const dependency of directWorkspaceDependencies) { - assert.notEqual( - pretest.indexOf(workspaceBuild(dependency)), - -1, - `CLI pretest must build ${dependency}`, - ); - } - - const evalIndex = pretest.indexOf(workspaceBuild(evalPackage.name)); - const evalWorkspaceDependencies = Object.keys(evalPackage.dependencies ?? {}).filter((name) => - workspaceNames.has(name), - ); - for (const dependency of evalWorkspaceDependencies) { - const dependencyIndex = pretest.indexOf(workspaceBuild(dependency)); - assert.notEqual( - dependencyIndex, - -1, - `CLI pretest must build ${dependency} before ${evalPackage.name}`, - ); - assert.ok( - dependencyIndex < evalIndex, - `CLI pretest must build ${dependency} before ${evalPackage.name}`, - ); - } -}); diff --git a/scripts/code-mode-build-order.test.mjs b/scripts/code-mode-build-order.test.mjs deleted file mode 100644 index 82a17208a8..0000000000 --- a/scripts/code-mode-build-order.test.mjs +++ /dev/null @@ -1,33 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -const repoRoot = new URL('../', import.meta.url); -const codeModeBuild = 'npm --workspace @maka/code-mode run build'; -const runtimeBuild = 'npm --workspace @maka/runtime run build'; - -async function readManifest(path) { - return JSON.parse(await readFile(new URL(path, repoRoot), 'utf8')); -} - -test('product build paths build CodeMode before Runtime', async () => { - const root = await readManifest('package.json'); - const desktop = await readManifest('apps/desktop/package.json'); - const cli = await readManifest('packages/cli/package.json'); - const manifests = [ - ['root', root], - ['desktop', desktop], - ['CLI', cli], - ]; - - for (const [manifestLabel, manifest] of manifests) { - for (const [scriptName, script] of Object.entries(manifest.scripts)) { - const runtimeIndex = script.indexOf(runtimeBuild); - if (runtimeIndex === -1) continue; - const label = `${manifestLabel} ${scriptName}`; - const codeModeIndex = script.indexOf(codeModeBuild); - assert.notEqual(codeModeIndex, -1, `${label} must build CodeMode`); - assert.ok(codeModeIndex < runtimeIndex, `${label} must build CodeMode before Runtime`); - } - } -}); diff --git a/scripts/cu-process-restart-harness.test.mjs b/scripts/cu-process-restart-harness.test.mjs deleted file mode 100644 index d81df57760..0000000000 --- a/scripts/cu-process-restart-harness.test.mjs +++ /dev/null @@ -1,70 +0,0 @@ -// The real-machine soak harness calls methods on the executor backend, and it -// needs a signed maka-cu and a live desktop to run at all — so nothing checked -// that the methods it calls exist. -// -// They did not. `backend.serviceState()` was cua-driver's two-role snapshot; -// maka-cu has one child and exposes `executorState()`. The call threw a -// TypeError on round 1, inside the try, AFTER the round's real work had -// succeeded — so the harness caught it and wrote an `ok: false` report. A -// passing run on a real machine was reported as a failure, and the only way to -// find out was to have the machine. -// -// This reads the harness's own source for the calls it makes and asks a real -// backend whether it has them. -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -test('every backend method the process-restart soak calls exists on the backend', async () => { - const source = await readFile(new URL('cu-process-restart-e2e.mjs', import.meta.url), 'utf8'); - // Comment lines are dropped first: the harness explains in prose which call - // it replaced, and matching that would report the bug as still present. - const code = source - .split('\n') - .filter((line) => !line.trimStart().startsWith('//')) - .join('\n'); - const called = [...code.matchAll(/\bbackend\.([A-Za-z_$][\w$]*)\s*\(/g)].map((match) => match[1]); - assert.ok(called.length > 0, 'the harness calls nothing on the backend, so this test is inert'); - - const { createMakaCuBackend } = await import( - new URL('../packages/computer-use/dist/index.js', import.meta.url) - ); - // Never started: constructing does not spawn, and every method below is only - // asked for by name. - const backend = createMakaCuBackend({ - binaryPath: '/nonexistent/maka-cu', - imageDir: '/tmp', - }); - try { - for (const method of new Set(called)) { - assert.equal( - typeof backend[method], - 'function', - `the soak harness calls backend.${method}(), which the backend does not have`, - ); - } - } finally { - backend.dispose(); - } -}); - -test('the soak harness reads the executor snapshot maka-cu actually returns', async () => { - // One child, one generation. The two-role read it replaced would have found - // `undefined.restartAttempts` even if `serviceState` had existed. - const { createMakaCuBackend } = await import( - new URL('../packages/computer-use/dist/index.js', import.meta.url) - ); - const backend = createMakaCuBackend({ - binaryPath: '/nonexistent/maka-cu', - imageDir: '/tmp', - }); - try { - const snapshot = backend.executorState(); - assert.equal(typeof snapshot.generation, 'number'); - assert.equal(typeof snapshot.restartAttempts, 'number'); - assert.ok(!('action' in snapshot), 'maka-cu has no action role to read'); - assert.ok(!('capture' in snapshot), 'maka-cu has no capture role to read'); - } finally { - backend.dispose(); - } -}); diff --git a/scripts/dependency-audit-workflow.test.mjs b/scripts/dependency-audit-workflow.test.mjs deleted file mode 100644 index 834511268c..0000000000 --- a/scripts/dependency-audit-workflow.test.mjs +++ /dev/null @@ -1,67 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -const auditWorkflowUrl = new URL('../.github/workflows/dependency-audit.yml', import.meta.url); -const releaseWorkflowUrl = new URL('../.github/workflows/release-desktop.yml', import.meta.url); - -test('dependency audit runs daily and when its security inputs change', async () => { - const workflow = await readFile(auditWorkflowUrl, 'utf8'); - - assert.match(workflow, /^\s+- cron: '17 3 \* \* \*'$/mu); - assert.match(workflow, /^\s+workflow_dispatch:$/mu); - assert.match(workflow, /^\s+pull_request:$/mu); - assert.match(workflow, /^\s+push:\n\s+branches: \[main\]$/mu); - const pullRequestTrigger = workflow.slice( - workflow.indexOf(' pull_request:'), - workflow.indexOf(' push:'), - ); - assert.match(pullRequestTrigger, /\.github\/workflows\/dependency-audit\.yml/u); -}); - -test('dependency audit cancels superseded runs for the same ref', async () => { - const workflow = await readFile(auditWorkflowUrl, 'utf8'); - - assert.match( - workflow, - /^concurrency:\n group: dependency-audit-\$\{\{ github\.ref \}\}\n cancel-in-progress: true$/mu, - ); -}); - -test('dependency audit has only read-only repository access', async () => { - const workflow = await readFile(auditWorkflowUrl, 'utf8'); - - assert.equal([...workflow.matchAll(/^\s*permissions:/gmu)].length, 1); - assert.match(workflow, /^permissions:\n contents: read$/mu); - assert.doesNotMatch(workflow, /^\s+(?:env|environment):|\$\{\{\s*(?:secrets|vars)\./mu); - - const actionReferences = [...workflow.matchAll(/^\s+uses: [^@\s]+@([^\s#]+)/gmu)].map( - (match) => match[1], - ); - assert.ok(actionReferences.length > 0); - assert.ok(actionReferences.every((reference) => /^[0-9a-f]{40}$/u.test(reference))); - assert.match(workflow, /^\s+persist-credentials: false$/mu); -}); - -test('dependency audit installs only production packages without lifecycle scripts', async () => { - const workflow = await readFile(auditWorkflowUrl, 'utf8'); - - assert.match(workflow, /^\s+run: npm ci --ignore-scripts --omit=dev$/mu); - assert.match(workflow, /^\s+run: npm audit --omit=dev --audit-level=moderate$/mu); - assert.match(workflow, /^\s+run: npm audit signatures --omit=dev$/mu); - assert.doesNotMatch(workflow, /^\s+continue-on-error: true$/mu); -}); - -test('desktop release blocks moderate advisories before signing credentials and packaging', async () => { - const workflow = await readFile(releaseWorkflowUrl, 'utf8'); - const audit = workflow.indexOf('npm audit --omit=dev --audit-level=moderate'); - - assert.notEqual(audit, -1); - for (const laterStep of [ - 'Write App Store Connect API key', - 'Package notarized app and signed DMG', - 'Package the Windows installer and ZIP', - ]) { - assert.ok(audit < workflow.indexOf(laterStep), laterStep); - } -}); diff --git a/scripts/fixture-env.test.mjs b/scripts/fixture-env.test.mjs deleted file mode 100644 index 6243945b24..0000000000 --- a/scripts/fixture-env.test.mjs +++ /dev/null @@ -1,46 +0,0 @@ -import { strict as assert } from 'node:assert'; -import { afterEach, test } from 'node:test'; -import { buildFixtureEnv } from './fixture-env.mjs'; - -const originals = new Map(); -function setEnv(key, value) { - if (!originals.has(key)) originals.set(key, process.env[key]); - process.env[key] = value; -} - -afterEach(() => { - for (const [key, value] of originals) { - if (value === undefined) delete process.env[key]; - else process.env[key] = value; - } - originals.clear(); -}); - -test('fixture launch state comes only from explicit arguments', () => { - setEnv('CI', 'true'); - setEnv('MAKA_E2E_SHOW_WINDOW', '1'); - setEnv('MAKA_E2E_FIXTURE_THEME', 'dark'); - setEnv('MAKA_E2E_FIXTURE_LOCALE', 'en'); - setEnv('ANTHROPIC_API_KEY', 'sk-real-key'); - setEnv('SOME_API_TOKEN', 'secret-token'); - setEnv('X_API_SECRET', 'secret-value'); - - const env = buildFixtureEnv('/tmp/data', '/tmp/data/home', { - scenario: 'fetched-empty', - theme: 'light', - timezone: 'UTC', - }); - - assert.equal(env.MAKA_E2E, '1'); - assert.equal(env.MAKA_SKIP_SHELL_ENV, '1'); - assert.equal(env.MAKA_E2E_SHOW_WINDOW, undefined); - assert.equal(env.MAKA_E2E_FIXTURE_THEME, 'light'); - assert.equal(env.MAKA_E2E_FIXTURE_LOCALE, undefined); - assert.equal(env.ANTHROPIC_API_KEY, undefined); - assert.equal(env.SOME_API_TOKEN, undefined); - assert.equal(env.X_API_SECRET, undefined); - assert.equal(env.HOME, '/tmp/data/home'); - assert.equal(env.USERPROFILE, '/tmp/data/home'); - assert.equal(env.MAKA_E2E_USER_DATA_DIR, '/tmp/data'); - assert.equal(env.MAKA_E2E_FIXTURE_TIMEZONE, 'UTC'); -}); diff --git a/scripts/storybook-visual-smoke.test.mjs b/scripts/storybook-visual-smoke.test.mjs deleted file mode 100644 index 83912dd225..0000000000 --- a/scripts/storybook-visual-smoke.test.mjs +++ /dev/null @@ -1,58 +0,0 @@ -import assert from 'node:assert/strict'; -import { mkdtemp, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import test from 'node:test'; - -import { catalogJobs, startStaticServer, storyUrl } from './storybook-visual-smoke.mjs'; - -test('catalog jobs include every story once and reject an empty catalog', () => { - assert.deepEqual( - catalogJobs({ - entries: { - docs: { id: 'docs', type: 'docs' }, - first: { id: 'product-first--default', type: 'story' }, - second: { id: 'product-second--default', type: 'story' }, - }, - }), - [{ storyId: 'product-first--default' }, { storyId: 'product-second--default' }], - ); - assert.throws(() => catalogJobs({ entries: {} }), /index has no stories/); - assert.throws(() => catalogJobs(null), /index has no entries/); -}); - -test('render URLs disable play functions in embedded Storybook', () => { - const url = new URL(storyUrl('http://127.0.0.1:6006', 'product-example--default')); - assert.equal(url.pathname, '/iframe.html'); - assert.equal(url.searchParams.get('id'), 'product-example--default'); - assert.equal(url.searchParams.get('viewMode'), 'story'); - assert.equal(url.searchParams.get('embed'), 'true'); - assert.equal(url.searchParams.get('globals'), 'colorScheme:light'); -}); - -test('the Storybook static server handles only the optional favicon specially', async () => { - const staticDir = await mkdtemp(join(tmpdir(), 'maka-storybook-smoke-')); - await writeFile(join(staticDir, 'index.html'), 'fixture'); - const server = await startStaticServer(staticDir); - - try { - const [indexResponse, missingFaviconResponse, missingAssetResponse] = await Promise.all([ - fetch(`${server.baseUrl}/`), - fetch(`${server.baseUrl}/favicon.ico`), - fetch(`${server.baseUrl}/missing-story-asset.js`), - ]); - - assert.equal(indexResponse.status, 200); - assert.equal(missingFaviconResponse.status, 204); - assert.equal(await missingFaviconResponse.text(), ''); - assert.equal(missingAssetResponse.status, 404); - - await writeFile(join(staticDir, 'favicon.ico'), 'fixture-icon'); - const presentFaviconResponse = await fetch(`${server.baseUrl}/favicon.ico`); - assert.equal(presentFaviconResponse.status, 200); - assert.equal(await presentFaviconResponse.text(), 'fixture-icon'); - } finally { - await server.close(); - await rm(staticDir, { recursive: true, force: true }); - } -}); diff --git a/scripts/windows-baseline-workflow.test.mjs b/scripts/windows-baseline-workflow.test.mjs deleted file mode 100644 index 018895ad6b..0000000000 --- a/scripts/windows-baseline-workflow.test.mjs +++ /dev/null @@ -1,171 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import { spawnSync } from 'node:child_process'; -import test from 'node:test'; -import { fileURLToPath } from 'node:url'; - -const workflowUrl = new URL('../.github/workflows/windows-baseline.yml', import.meta.url); -const processIdentityScriptUrl = new URL('./windows-process-identity.ps1', import.meta.url); - -function powerShellArgs(command) { - return ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', command]; -} - -function resolvePowerShellExecutable(spawn = spawnSync) { - const failures = []; - for (const executable of ['pwsh.exe', 'powershell.exe']) { - const probe = spawn(executable, powerShellArgs('exit 0'), { - encoding: 'utf8', - }); - if (probe.status === 0) return executable; - failures.push( - `${executable}: ${probe.error?.message || probe.stderr || probe.stdout || `exit ${probe.status}`}`, - ); - } - assert.fail(`No usable PowerShell executable (${failures.join('; ')})`); -} - -test('PowerShell resolution covers the documented Windows PowerShell fallback', () => { - for (const pwshFailure of [ - { - status: null, - error: Object.assign(new Error('spawnSync pwsh.exe ENOENT'), { code: 'ENOENT' }), - }, - { status: 1, error: undefined }, - ]) { - const calls = []; - const executable = resolvePowerShellExecutable((command, args, options) => { - calls.push({ command, args, options }); - return command === 'pwsh.exe' - ? { ...pwshFailure, stdout: '', stderr: '' } - : { status: 0, error: undefined, stdout: '', stderr: '' }; - }); - - assert.equal(executable, 'powershell.exe'); - assert.deepEqual( - calls.map(({ command }) => command), - ['pwsh.exe', 'powershell.exe'], - ); - assert.deepEqual(calls[0].args, powerShellArgs('exit 0')); - assert.equal(calls[0].options.encoding, 'utf8'); - } -}); - -test('Windows baseline workflow keeps its non-blocking evidence contract', async () => { - const workflow = await readFile(workflowUrl, 'utf8'); - - assert.match(workflow, /^\s+runs-on: windows-latest$/mu); - assert.match(workflow, /^\s+continue-on-error: true$/mu); - assert.match(workflow, /^\s+timeout-minutes: 45$/mu); - assert.match(workflow, /^\s+needs: changes$/mu); - assert.match(workflow, /if: needs\.changes\.outputs\.windows == 'true'/u); - assert.match(workflow, /windows_runtime: \$\{\{ steps\.plan\.outputs\.windows_runtime \}\}/u); - assert.match(workflow, /windows_storage: \$\{\{ steps\.plan\.outputs\.windows_storage \}\}/u); - assert.match( - workflow, - /windows_storage_full: \$\{\{ steps\.full\.outputs\.windows_storage_full \}\}/u, - ); - assert.match(workflow, /schedule:\n\s+- cron:/u); - assert.match(workflow, /full_storage:/u); - - const stepIds = [...workflow.matchAll(/^\s+- id: ([a-z_]+)$/gmu)] - .map((match) => match[1]) - .filter((stepId) => stepId !== 'plan' && stepId !== 'full'); - assert.deepEqual(stepIds, [ - 'install', - 'build', - 'inventory', - 'scripts', - 'smoke', - 'runtime_pty_input', - 'storage', - 'storage_full', - 'processes', - ]); - for (const stepId of stepIds) { - assert.match(workflow, new RegExp(`\\$\\{\\{ steps\\.${stepId}\\.outcome \\}\\}`, 'u')); - } - - for (const command of [ - 'npm.cmd ci', - 'npm.cmd run build:test', - 'npm.cmd run windows:inventory', - 'npm.cmd run test:scripts', - 'npm.cmd --workspace @maka/desktop run build:smoke', - 'npm.cmd run smoke:windows:dist', - // Curated OS-sensitive gate set for PR/push. - 'node.exe --test --test-concurrency=2 --test-force-exit --test-timeout=60000', - 'packages/storage/dist/__tests__/root-authority.test.js', - 'packages/storage/dist/__tests__/sqlite-recovery-concurrency.test.js', - 'packages/storage/dist/__tests__/managed-workspace-owner.test.js', - 'node.exe --test --test-force-exit --test-timeout=15000 --test-reporter=tap --test-concurrency=1 --test-name-pattern="semantic text and Enter actions|terminal mode parsed before the control cut" packages/runtime/dist/__tests__/shell-run-manager.test.js', - // Full suite only via storage_full (nightly / manual), not the PR storage step. - 'node.exe scripts/run-workspace-tests-parallel.mjs --concurrency=1 --workspaces=packages/storage', - ]) { - assert.ok(workflow.includes(command), command); - } - assert.match(workflow, /needs\.changes\.outputs\.windows_storage_full == 'true'/u); - assert.equal(workflow.match(/needs\.changes\.outputs\.windows_storage == 'true'/gu)?.length, 1); - assert.equal(workflow.match(/needs\.changes\.outputs\.windows_runtime == 'true'/gu)?.length, 1); - assert.match(workflow, /Runtime PTY input gate did not run exactly two passing tests/u); - assert.match(workflow, /name: Capture full storage suite baseline/u); - assert.match(workflow, /storage-full\.log/u); - - assert.match(workflow, /Get-CimInstance Win32_Process/u); - assert.match(workflow, /name: Capture process baseline/u); - assert.match(workflow, /process-baseline\.json/u); - assert.match(workflow, /CreationDate/u); - assert.match(workflow, /\. \.\/scripts\/windows-process-identity\.ps1/u); - assert.equal(workflow.match(/Get-WindowsProcessIdentityKey/gmu)?.length, 4); - assert.match(workflow, /HashSet\[string\]/u); - assert.match(workflow, /HashSet\[int\]/u); - assert.doesNotMatch(workflow, /CommandLine -match/u); - assert.match(workflow, /\$treeProcessIds\.Contains\(\$process\.ParentProcessId\)/u); - assert.match(workflow, /residual-process-tree\.json/u); - assert.match(workflow, /taskkill\.exe \/PID \$process\.ProcessId \/T \/F/u); - assert.match(workflow, /residual-processes-after-cleanup\.json/u); - assert.match(workflow, /\$unreaped\.Count -gt 0/u); - assert.match(workflow, /\$exitCode = \$LASTEXITCODE/u); - assert.match(workflow, /Tee-Object -FilePath "\$env:WINDOWS_BASELINE_LOG_DIR\/storage\.log"/u); - assert.match(workflow, /name: Run Windows storage path and lock gates/u); - for (const blockingRecoveryArtifact of [ - 'sqlite-runtime-crash.test.js', - 'sqlite-long-term-memory-crash.test.js', - 'managed-workspace-baseline.test.js', - 'git-workspace-service.test.js', - ]) { - assert.ok(!workflow.includes(blockingRecoveryArtifact), blockingRecoveryArtifact); - } - // Pin-short-comment contract: the workflow must pin upload-artifact to a - // full SHA and annotate it with the exact version it resolves to. The major - // is intentionally not asserted — dependabot may bump it — but the - // annotation must stay truthful. - assert.match(workflow, /actions\/upload-artifact@[0-9a-f]{40} # v\d+\.\d+\.\d+/u); - assert.match(workflow, /name: windows-baseline/u); - assert.match(workflow, /retention-days: 14/u); -}); - -test('Windows process identity matches a non-empty JSON baseline to a live process object', { - skip: process.platform !== 'win32', -}, () => { - const fixture = String.raw` - . '${fileURLToPath(processIdentityScriptUrl).replaceAll("'", "''")}' - $captured = [pscustomobject]@{ - Processes = @([pscustomobject]@{ - ProcessId = 4242 - CreationDate = [DateTimeOffset]::Parse('2026-08-05T08:09:10.1234567+08:00').ToUniversalTime().ToString('o') - }) - } | ConvertTo-Json -Depth 3 | ConvertFrom-Json - $live = [pscustomobject]@{ - ProcessId = 4242 - CreationDate = [DateTime]::Parse('2026-08-05T00:09:10.1234567Z').ToUniversalTime() - } - if ((Get-WindowsProcessIdentityKey $captured.Processes) -ne (Get-WindowsProcessIdentityKey $live)) { - exit 1 - } - `; - const result = spawnSync(resolvePowerShellExecutable(), powerShellArgs(fixture), { - encoding: 'utf8', - }); - assert.equal(result.status, 0, result.error?.message || result.stderr || result.stdout); -}); diff --git a/scripts/windows-recovery-workflow.test.mjs b/scripts/windows-recovery-workflow.test.mjs deleted file mode 100644 index 11035d4bf3..0000000000 --- a/scripts/windows-recovery-workflow.test.mjs +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import test from 'node:test'; - -const workflowUrl = new URL('../.github/workflows/windows-recovery.yml', import.meta.url); - -test('Windows recovery workflow is a bounded release-blocking evidence gate', async () => { - const workflow = await readFile(workflowUrl, 'utf8'); - - assert.match(workflow, /^\s+windows_recovery:$/mu); - assert.match(workflow, /^\s+name: windows_recovery$/mu); - assert.match(workflow, /^\s+runs-on: windows-latest$/mu); - assert.match(workflow, /^\s+timeout-minutes: 30$/mu); - assert.doesNotMatch(workflow, /continue-on-error/u); - - for (const command of ['npm.cmd ci', 'npm.cmd run build:test']) { - assert.ok(workflow.includes(command), command); - } - - for (const testArtifact of [ - 'packages/storage/dist/__tests__/sqlite-runtime-crash.test.js', - 'packages/storage/dist/__tests__/sqlite-long-term-memory-crash.test.js', - 'packages/runtime/dist/__tests__/runtime-resume-crash.test.js', - 'packages/runtime/dist/__tests__/runtime-continuation-crash.test.js', - 'packages/runtime-host/dist/__tests__/artifact-two-client-uds.test.js', - 'packages/runtime-host/dist/__tests__/execution-host-queue.test.js', - 'packages/storage/dist/__tests__/managed-workspace-baseline.test.js', - 'packages/storage/dist/__tests__/git-workspace-service.test.js', - ]) { - assert.ok(workflow.includes(testArtifact), testArtifact); - } - - assert.match(workflow, /\$env:MAKA_STORAGE_STRESS = '1'/u); - assert.match(workflow, /owner death\|a killed Host is recovered exactly once/u); - assert.match(workflow, /real process crash\|real crash\|real-process crash/u); - assert.equal(workflow.match(/--test-reporter=tap/gu)?.length, 2); - for (const evidence of [ - '# tests 2', - '# pass 2', - '# tests 12', - '# pass 12', - '# skipped 0', - 'Runtime Host recovery gate did not run exactly two passing tests', - 'Managed workspace recovery gate did not run exactly twelve passing tests', - ]) { - assert.ok(workflow.includes(evidence), evidence); - } -}); diff --git a/scripts/windows-smoke.test.mjs b/scripts/windows-smoke.test.mjs deleted file mode 100644 index a2f17547c6..0000000000 --- a/scripts/windows-smoke.test.mjs +++ /dev/null @@ -1,59 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; -import { runWindowsSmoke } from './windows-smoke.mjs'; - -test('Windows smoke checks CLI entry points before the real Electron window', () => { - const calls = []; - runWindowsSmoke({ - platform: 'win32', - existsSync: () => true, - spawnSync(command, args, options) { - calls.push([command, args, options]); - return { - status: 0, - stdout: args.includes('--startup-only') ? '[real-window-smoke] report: report.md\n' : '', - stderr: '', - }; - }, - }); - assert.equal(calls.length, 3); - assert.deepEqual(calls[0][1].slice(-1), ['--version']); - assert.deepEqual(calls[1][1].slice(-1), ['--help']); - assert.ok(calls[2][1].includes('--startup-only')); - assert.equal(calls[0][2].timeout, 15_000); - assert.equal(calls[1][2].timeout, 15_000); - assert.equal(calls[2][2].timeout, 45_000); -}); - -test('Windows smoke refuses to claim evidence on another platform', () => { - assert.throws(() => runWindowsSmoke({ platform: 'linux' }), /must run on Windows/u); -}); - -test('Windows smoke requires renderer and runtime resource artifacts', () => { - for (const missingSuffix of [ - 'apps/desktop/dist-renderer/index.html', - 'apps/desktop/resources/workers/filesystem-worker.js', - ]) { - assert.throws( - () => - runWindowsSmoke({ - platform: 'win32', - existsSync: (path) => !path.replaceAll('\\', '/').endsWith(missingSuffix), - }), - new RegExp(`Missing build artifact: .*${missingSuffix.replaceAll('/', '.*')}`, 'u'), - missingSuffix, - ); - } -}); - -test('Windows smoke rejects an Electron command that exits without exercising a window', () => { - assert.throws( - () => - runWindowsSmoke({ - platform: 'win32', - existsSync: () => true, - spawnSync: () => ({ status: 0, stdout: '', stderr: '' }), - }), - /without producing a report/u, - ); -}); diff --git a/scripts/windows-test-inventory.mjs b/scripts/windows-test-inventory.mjs index 639e9f97ec..ca70db9f70 100644 --- a/scripts/windows-test-inventory.mjs +++ b/scripts/windows-test-inventory.mjs @@ -20,7 +20,6 @@ export async function collectWindowsTestSkips(root = REPO_ROOT) { const entries = []; for (const file of files.sort()) { const path = relative(root, file).replaceAll('\\', '/'); - if (path === 'scripts/windows-test-inventory.test.mjs') continue; const sourceText = await readFile(file, 'utf8'); const sourceLines = sourceText.split(/\r?\n/u); for (const skip of findSkipExpressions(sourceText)) { diff --git a/scripts/windows-test-inventory.test.mjs b/scripts/windows-test-inventory.test.mjs deleted file mode 100644 index 132230c773..0000000000 --- a/scripts/windows-test-inventory.test.mjs +++ /dev/null @@ -1,101 +0,0 @@ -import assert from 'node:assert/strict'; -import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import test from 'node:test'; -import { - collectWindowsTestSkips, - findSkipExpressions, - renderWindowsTestInventory, - windowsTestInventoriesMatch, -} from './windows-test-inventory.mjs'; - -test('compares generated inventories independently of checkout line endings', () => { - const rendered = '# Windows test skip inventory\n\nEntry\n'; - - assert.equal(windowsTestInventoriesMatch(rendered.replaceAll('\n', '\r\n'), rendered), true); - assert.equal(windowsTestInventoriesMatch(rendered.replaceAll('\n', '\r'), rendered), true); - assert.equal(windowsTestInventoriesMatch(`${rendered}Changed\n`, rendered), false); -}); - -test('reads multiline skip expressions through their property delimiter', () => { - const source = ` -// skip: process.platform === 'win32', -const example = "skip: process.platform === 'win32',"; -test('direct multiline', { - skip: - process.platform === 'win32' - ? 'not on Windows' - : false, -}, () => {}); -test('logical multiline', { - skip: - process.platform === 'win32' || - anotherCondition, - timeout: 1000, -}, () => {}); -`; - - assert.deepEqual( - findSkipExpressions(source).map(({ expression }) => expression), - [ - "process.platform === 'win32'\n ? 'not on Windows'\n : false", - "process.platform === 'win32' ||\n anotherCondition", - ], - ); -}); - -test('collects and classifies every test declaration that excludes Windows', async () => { - const root = await mkdtemp(join(tmpdir(), 'maka-windows-inventory-')); - try { - await mkdir(join(root, 'packages', 'runtime-host'), { recursive: true }); - await mkdir(join(root, 'apps', 'desktop'), { recursive: true }); - await writeFile( - join(root, 'packages', 'runtime-host', 'host.test.ts'), - `test('named pipe peer', { - skip: - process.platform === 'win32' - ? 'not on Windows' - : false, -}, () => {}); -test('named pipe reconnect', { - skip: - process.platform === 'win32' || - anotherCondition, -}, () => {}); -`, - ); - await writeFile( - join(root, 'apps', 'desktop', 'window.test.mjs'), - 'test(\'macOS window probe\', { skip: process.platform !== "darwin" }, () => {});\n', - ); - - const entries = await collectWindowsTestSkips(root); - assert.deepEqual( - entries.map(({ path, title, classification }) => ({ path, title, classification })), - [ - { - path: 'apps/desktop/window.test.mjs', - title: 'macOS window probe', - classification: 'platform-contract', - }, - { - path: 'packages/runtime-host/host.test.ts', - title: 'named pipe peer', - classification: 'windows-backend-gap', - }, - { - path: 'packages/runtime-host/host.test.ts', - title: 'named pipe reconnect', - classification: 'windows-backend-gap', - }, - ], - ); - const rendered = renderWindowsTestInventory(entries); - assert.match(rendered, /Total Windows-excluded declarations: \*\*3\*\*/u); - assert.match(rendered, /windows-backend-gap \| 2/u); - assert.doesNotMatch(rendered, /window\.test\.mjs:\d+/u); - } finally { - await rm(root, { recursive: true, force: true }); - } -});