test: add PI release proof harness

This commit is contained in:
2026-08-24 13:44:39 +08:00
parent 977445ba45
commit 8820e82530
19 changed files with 1407 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
import { act, render } from '@testing-library/react';
import { mkdtemp, rm } from 'node:fs/promises';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { createServer } from 'node:http';
import { tmpdir } from 'node:os';
import path from 'node:path';
@@ -36,6 +36,16 @@ function percentile95(values: number[]): number {
return ordered[Math.ceil(ordered.length * 0.95) - 1] ?? Number.POSITIVE_INFINITY;
}
function summarize(values: number[]): { samples: number; p50: number; p95: number; max: number } {
const ordered = [...values].sort((left, right) => left - right);
return {
samples: ordered.length,
p50: ordered[Math.ceil(ordered.length * 0.5) - 1] ?? Number.POSITIVE_INFINITY,
p95: percentile95(ordered),
max: ordered.at(-1) ?? Number.POSITIVE_INFINITY,
};
}
function pressurePatch(seq: number): ConversationPatch {
if (seq === 1) {
return {
@@ -203,7 +213,7 @@ describe('REN-008 coding timeline pressure', () => {
latencies.push(performance.now() - startedAt);
}
const measuredReactCommits = reactCommits - initialCommits;
const p95Ms = percentile95(latencies);
const mainToReactMs = summarize(latencies);
const metrics = {
runtimePatchItems,
patchBatches,
@@ -211,7 +221,8 @@ describe('REN-008 coding timeline pressure', () => {
rendererTransactions,
reactCommits: measuredReactCommits,
wireBytes,
mainToReactP95Ms: p95Ms,
mainToReactMs,
mainToReactP95Ms: mainToReactMs.p95,
};
console.info('REN-008 metrics', metrics);
expect(metrics.runtimePatchItems).toBe(100);
@@ -225,6 +236,8 @@ describe('REN-008 coding timeline pressure', () => {
.entriesByConversationId[conversation.id]?.reducer.snapshot?.cursor.seq).toBe(100);
view.unmount();
unsubscribe();
const reportPath = process.env.MAKELORE_PI_PERF_PRESSURE_FRAGMENT;
if (reportPath) await writeFile(reportPath, `${JSON.stringify(metrics, null, 2)}\n`);
} finally {
controller.abort();
await reader.cancel().catch(() => undefined);

View File

@@ -0,0 +1,100 @@
// @vitest-environment node
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import {
assertNodeEngineCompatible,
collectAbsoluteManifestValues,
collectForbiddenResourcePaths,
defaultProductExecutable,
validatePiArtifactMetadata,
} from '../../scripts/lib/pi-product-artifact.mjs';
import { parsePiArtifactVerifierArgs } from '../../scripts/verify-pi-product-artifact.mjs';
const roots: string[] = [];
const PI_PACKAGE = '@earendil-works/pi-coding-agent';
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
function matchingMetadata() {
return {
rootPackage: { dependencies: { [PI_PACKAGE]: '0.84.2' } },
lockfile: {
importers: {
'.': { dependencies: { [PI_PACKAGE]: { specifier: '0.84.2', version: '0.84.2(ws@8.20.0)' } } },
},
},
packagedPackage: { dependencies: { [PI_PACKAGE]: '0.84.2' } },
runtimePackage: {
name: PI_PACKAGE,
version: '0.84.2',
bin: { pi: 'dist/cli.js' },
},
manifest: {
runtime: {
packageName: PI_PACKAGE,
version: '0.84.2',
cliEntry: 'dist/cli.js',
nodeEngine: '>=22.19.0',
},
target: { platform: 'win32', arch: 'x64' },
},
runtimePlatform: { platform: 'win32', arch: 'x64', node: '24.18.1' },
};
}
describe('final Pi product artifact verification', () => {
it('requires the same pinned Pi version across package, lock, app, runtime, and manifest', () => {
expect(validatePiArtifactMetadata(matchingMetadata())).toMatchObject({
expected: '0.84.2',
rootDependency: '0.84.2',
lockedVersion: '0.84.2',
manifest: '0.84.2',
});
const mismatch = matchingMetadata();
mismatch.packagedPackage.dependencies[PI_PACKAGE] = '0.84.1';
expect(() => validatePiArtifactMetadata(mismatch)).toThrow('versions do not match');
});
it('checks the packaged Node version against the exact supported engine shape', () => {
expect(() => assertNodeEngineCompatible('>=22.19.0', '24.18.1')).not.toThrow();
expect(() => assertNodeEngineCompatible('>=22.19.0', '22.18.9')).toThrow('does not satisfy');
expect(() => assertNodeEngineCompatible('^22.19.0', '24.18.1')).toThrow('Unsupported');
});
it('rejects absolute manifest values and OpenCode-named artifact resources', async () => {
expect(collectAbsoluteManifestValues({
entry: 'dist/cli.js',
asset: 'node_modules/example/file.wasm',
leaked: 'D:\\work\\pi-runtime',
})).toEqual([{ at: '$.leaked', value: 'D:\\work\\pi-runtime' }]);
const root = await mkdtemp(path.join(tmpdir(), 'makelore-pi-artifact-test-'));
roots.push(root);
await mkdir(path.join(root, 'pi-runtime', 'node_modules', 'opencode-ai'), { recursive: true });
await writeFile(path.join(root, 'pi-runtime', 'node_modules', 'opencode-ai', 'package.json'), '{}');
expect(await collectForbiddenResourcePaths(root)).toEqual([
'pi-runtime/node_modules/opencode-ai',
]);
});
it('uses final unpacked-product paths and strictly parses verifier options', () => {
expect(defaultProductExecutable('D:\\repo', 'win32'))
.toBe(path.resolve('D:\\repo', 'release', 'win-unpacked', 'Makelore.exe'));
expect(parsePiArtifactVerifierArgs([
'--app-exe', 'release/custom/Makelore.exe',
'--samples', '5',
'--timeout-ms', '12000',
'--report', 'release/evidence/pi.json',
], 'D:\\repo')).toMatchObject({ samples: 5, timeoutMs: 12_000 });
expect(() => parsePiArtifactVerifierArgs(['--samples', '0'], 'D:\\repo'))
.toThrow('--samples must be a positive integer');
expect(() => parsePiArtifactVerifierArgs(['--unknown'], 'D:\\repo'))
.toThrow('Unknown argument');
});
});

View File

@@ -0,0 +1,31 @@
import { readFile } from 'node:fs/promises';
import { describe, expect, it } from 'vitest';
const root = process.cwd();
describe('Pi release documentation', () => {
it('keeps final-product commands and the external Provider waiver distinct', async () => {
const [readme, runbook] = await Promise.all([
readFile(`${root}/README.md`, 'utf8'),
readFile(`${root}/docs/pi-runtime-release-runbook.md`, 'utf8'),
]);
expect(readme).toContain('pnpm run verify:artifact:pi');
expect(readme).toContain('pnpm run smoke:pi:real');
expect(readme).toContain('pnpm run perf:pi:release');
expect(runbook).toContain('realTurnVerified=false');
expect(runbook).toContain('Explicitly Waived / Accepted Risk');
expect(runbook).toContain('不得写成 Pass');
});
it('requires all release targets and documents the incompatible rollback boundary', async () => {
const runbook = await readFile(`${root}/docs/pi-runtime-release-runbook.md`, 'utf8');
for (const target of ['Windows x64', 'Linux x64', 'macOS x64', 'macOS arm64']) {
expect(runbook).toContain(target);
}
expect(runbook).toContain('缺少任一目标的独立证据时,结论只能是 `Blocked`');
expect(runbook).toContain('旧版本不能读取或续写它们');
expect(runbook).toContain('只支持完整应用版本回滚');
});
});

View File

@@ -0,0 +1,84 @@
// @vitest-environment node
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { createCodingConversationStore } from '../../electron/coding-projects/conversation-store';
import { createCodingProjectAgent } from '../../electron/coding-projects/project-config';
import {
createCodingProjectStore,
createLocalCodingProject,
createMemoryCodingProjectStorage,
} from '../../electron/coding-projects/project-store';
import { summarizeMeasurements } from '../../scripts/probe-pi-runtime.mjs';
const roots: string[] = [];
const samples = Number.parseInt(process.env.MAKELORE_PI_PERF_SAMPLES ?? '10', 10);
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
describe('PI-150 local metadata performance fragments', () => {
it('reports project, Agent, and Conversation metadata distributions', async () => {
expect(samples).toBeGreaterThan(0);
const projectMs: number[] = [];
const agentMs: number[] = [];
const conversationMs: number[] = [];
for (let index = 0; index < samples; index += 1) {
const projectPath = await mkdtemp(path.join(tmpdir(), 'makelore-pi-release-perf-'));
roots.push(projectPath);
const store = createCodingProjectStore(createMemoryCodingProjectStorage(), {
createId: () => `project-${index}`,
now: () => '2026-08-24T00:00:00.000Z',
});
let startedAt = performance.now();
await createLocalCodingProject({
projectPath,
now: '2026-08-24T00:00:00.000Z',
}, store);
projectMs.push(performance.now() - startedAt);
startedAt = performance.now();
const agent = await createCodingProjectAgent(projectPath, {
id: `builder-${index}`,
avatarId: 'avatar-01',
roleName: '实现者',
name: `Builder ${index}`,
model: { accountId: 'account-a', modelId: 'model-a', thinkingLevel: 'medium' },
modelResolution: 'resolved',
responsibility: {
mission: 'Implement', owns: [], boundaries: [], collaborators: [], principles: [],
},
}, { now: '2026-08-24T00:00:00.000Z' });
agentMs.push(performance.now() - startedAt);
const conversations = createCodingConversationStore(projectPath, {
createId: () => `00000000-0000-4000-8000-${String(index).padStart(12, '0')}`,
now: () => '2026-08-24T00:00:00.000Z',
});
startedAt = performance.now();
await conversations.create({
agentId: agent.id,
title: `Conversation ${index}`,
model: agent.model,
modelResolution: agent.modelResolution,
});
conversationMs.push(performance.now() - startedAt);
}
const report = {
projectMetadataMs: summarizeMeasurements(projectMs),
agentMetadataMs: summarizeMeasurements(agentMs),
conversationMetadataMs: summarizeMeasurements(conversationMs),
};
expect(report.projectMetadataMs.p95).toBeLessThanOrEqual(1_000);
expect(report.agentMetadataMs.p95).toBeLessThanOrEqual(500);
expect(report.conversationMetadataMs.p95).toBeLessThanOrEqual(500);
const reportPath = process.env.MAKELORE_PI_PERF_METADATA_FRAGMENT;
if (reportPath) await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`);
});
});

View File

@@ -103,6 +103,12 @@ describe('Pi runtime production bundler', () => {
expect(packageJson.scripts.package).toContain('bundle-pi-runtime.mjs --release-targets');
expect(packageJson.scripts['package:stage:win-x64'])
.toContain('bundle-pi-runtime.mjs --target win32-x64');
expect(packageJson.scripts['verify:artifact:pi'])
.toBe('node scripts/verify-pi-product-artifact.mjs');
expect(packageJson.scripts['smoke:pi:real'])
.toBe('node scripts/smoke-pi-real.mjs');
expect(packageJson.scripts['perf:pi:release'])
.toBe('node scripts/run-pi-release-performance.mjs');
expect(builder.mac.extraResources).toContainEqual({
from: 'build/pi-runtime/darwin-${arch}',
to: 'pi-runtime',

View File

@@ -16,6 +16,11 @@ import {
const scratchRoots: string[] = [];
const packagedRuntimeRoot = process.env.MAKELORE_PI_STAGED_RUNTIME_ROOT;
function electronExecutableFromEnvironment(): string {
const requireFromProject = createRequire(resolve('package.json'));
return process.env.MAKELORE_PI_ELECTRON_EXECUTABLE ?? requireFromProject('electron') as string;
}
async function materializeActiveToolsProbe(root: string): Promise<{
extensionPath: string;
resultPath: string;
@@ -56,8 +61,7 @@ afterEach(async () => {
describe('locked Pi worker process smoke', () => {
it('loads the real Pi 0.84.2 entry through Electron Node and exits by closing stdin', async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const electronExecutable = electronExecutableFromEnvironment();
const packageRoot = realpathSync(resolve(
'node_modules',
'@earendil-works',
@@ -124,8 +128,7 @@ describe('locked Pi worker process smoke', () => {
}, 15_000);
it('starts a real ephemeral read-only child with the child extension role', async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const electronExecutable = electronExecutableFromEnvironment();
const packageRoot = realpathSync(resolve(
'node_modules',
'@earendil-works',
@@ -181,8 +184,7 @@ describe('locked Pi worker process smoke', () => {
it.skipIf(!packagedRuntimeRoot)(
'starts the staged production-closure runtime as an ephemeral read-only child',
async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const electronExecutable = electronExecutableFromEnvironment();
const packageRoot = realpathSync(packagedRuntimeRoot as string);
const manifest = JSON.parse(await readFile(join(packageRoot, PI_RUNTIME_MANIFEST), 'utf8')) as {
runtime?: { version?: string; cliEntry?: string };
@@ -246,8 +248,7 @@ describe('locked Pi worker process smoke', () => {
it.skipIf(!packagedRuntimeRoot)(
'loads product tools in the staged production-closure parent runtime',
async () => {
const requireFromProject = createRequire(resolve('package.json'));
const electronExecutable = requireFromProject('electron') as string;
const electronExecutable = electronExecutableFromEnvironment();
const packageRoot = realpathSync(packagedRuntimeRoot as string);
const root = await mkdtemp(join(tmpdir(), 'makelore-pi-staged-product-tools-'));
scratchRoots.push(root);