fix(pi): prove packaged workers end to end
This commit is contained in:
1 parent
dc166a18cb
commit
2ff2e4af79
8 files changed
+825
-493
No files matched your search
@@ -8,7 +8,7 @@
|
|||||||
- Worktree: D:\Datas\OthersProjects\makelore-pi-release-proof-3e725ac7
|
- Worktree: D:\Datas\OthersProjects\makelore-pi-release-proof-3e725ac7
|
||||||
- Base commit: 977445ba450f4ad32b6e6db2caf048517513ab39
|
- Base commit: 977445ba450f4ad32b6e6db2caf048517513ab39
|
||||||
- Owner: codex-root
|
- Owner: codex-root
|
||||||
- Status: Ready for Planner Re-review (platform release blockers remain)
|
- Status: In Progress (second planner Spec review fixes required)
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
@@ -37,10 +37,9 @@
|
|||||||
risks, never Pass; every substitute report retains
|
risks, never Pass; every substitute report retains
|
||||||
`realTurnVerified=false`.
|
`realTurnVerified=false`.
|
||||||
- The user explicitly skipped macOS validation. Do not manufacture or reuse
|
- The user explicitly skipped macOS validation. Do not manufacture or reuse
|
||||||
static/cross-platform evidence for macOS x64 or arm64. Their missing final
|
static/cross-platform evidence for macOS x64 or arm64. Per the planner's
|
||||||
artifact, closure/native/resource, loopback, and performance evidence remains
|
second review this evidence is deferred to PI-160: it remains Not Pass and a
|
||||||
an explicit release blocker, so this task cannot claim cross-platform
|
final cross-platform release blocker, but is not a PI-150 blocker.
|
||||||
release-ready or PI-150 Done.
|
|
||||||
- The canonical project memory at this base is stale and still describes
|
- The canonical project memory at this base is stale and still describes
|
||||||
OpenCode. Treat the planner-owned PI Spec/ticket and current source as the
|
OpenCode. Treat the planner-owned PI Spec/ticket and current source as the
|
||||||
task authority; do not edit canonical memory in this feature worktree.
|
task authority; do not edit canonical memory in this feature worktree.
|
||||||
@@ -85,10 +84,12 @@
|
|||||||
under the pinned upstream `@earendil-works/pi-ai/dist/providers/opencode*`
|
under the pinned upstream `@earendil-works/pi-ai/dist/providers/opencode*`
|
||||||
closure remain classified separately because Pi imports them; they are not
|
closure remain classified separately because Pi imports them; they are not
|
||||||
Makelore-owned runtime residue.
|
Makelore-owned runtime residue.
|
||||||
- The five requested fixes are ready for planner re-review, but PI-150 remains
|
- The second planner review accepted the ASAR enumeration and RPM fixes but
|
||||||
blocked from `Done`, PI-160 must not start, and the product is not
|
found three remaining P1 proof defects: final subagent still used a fake
|
||||||
cross-platform release-ready: macOS x64/arm64 was explicitly skipped and no
|
in-process child, managed milestone reports joined different probe samples
|
||||||
independent native non-WSL Linux desktop/compositor acceptance run exists.
|
and omitted `worker.queue_wait`, and the 4+4 PIDs were placeholder Node
|
||||||
|
processes rather than real Pi workers. PI-150 remains blocked and PI-160 must
|
||||||
|
not start until those defects and native non-WSL Linux evidence are resolved.
|
||||||
QG-004/QG-005 remain `Explicitly Waived / Accepted Risk`, with
|
QG-004/QG-005 remain `Explicitly Waived / Accepted Risk`, with
|
||||||
`realTurnVerified=false`.
|
`realTurnVerified=false`.
|
||||||
|
|
||||||
@@ -147,16 +148,17 @@
|
|||||||
|
|
||||||
## Follow-ups
|
## Follow-ups
|
||||||
|
|
||||||
- Request planner re-review of `c7e7772..df1151b`. The five prior findings now
|
- Planner re-review of `c7e7772..dc166a1` returned `Standards Pass / Spec Needs
|
||||||
have direct final-product implementation and structured Windows/Linux
|
Fix`. ASAR enumeration and RPM are closed; final packaged subagent dispatch,
|
||||||
evidence; no source-only unit-test result is used as their release proof.
|
a single Main-owned cold/warm timeline including zero-valued queue wait, and
|
||||||
|
real persistent-parent/ephemeral-child 4+4 pressure remain P1.
|
||||||
- Do not start PI-160 or integrate `df1151b` while PI-150 remains blocked by the
|
- Do not start PI-160 or integrate `df1151b` while PI-150 remains blocked by the
|
||||||
accepted platform evidence boundary. If the planner accepts the five fixes,
|
accepted platform evidence boundary. If the planner accepts the five fixes,
|
||||||
keep PI-150 as the active frontier until the missing platform decision or
|
keep PI-150 as the active frontier until the missing platform decision or
|
||||||
evidence is resolved.
|
evidence is resolved.
|
||||||
- Keep missing macOS x64/arm64 artifact/runtime/resource/performance evidence as
|
- Keep missing macOS x64/arm64 artifact/runtime/resource/performance evidence as
|
||||||
a release blocker; do not promote a cross-platform release-ready or PI-150
|
`Deferred to PI-160 / Not Pass`; do not promote a final cross-platform release
|
||||||
`Done` conclusion without independent macOS execution.
|
without independent macOS execution, but do not use it to block PI-150.
|
||||||
- Keep the lack of an independent native non-WSL Linux
|
- Keep the lack of an independent native non-WSL Linux
|
||||||
desktop/compositor/distribution run explicit. The WSL2/WSLg run qualifies the
|
desktop/compositor/distribution run explicit. The WSL2/WSLg run qualifies the
|
||||||
generated Linux artifacts and final process seam but does not erase that
|
generated Linux artifacts and final process seam but does not erase that
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -207,6 +207,7 @@ interface WorkerRecord {
|
|||||||
generationResources: Record<PiGenerationResourceKind, Map<string, () => void>>;
|
generationResources: Record<PiGenerationResourceKind, Map<string, () => void>>;
|
||||||
rebuildFlight?: Promise<WorkerRecord>;
|
rebuildFlight?: Promise<WorkerRecord>;
|
||||||
acceptedPromptCount: number;
|
acceptedPromptCount: number;
|
||||||
|
coldStart: boolean;
|
||||||
processLease: PiProcessLease | null;
|
processLease: PiProcessLease | null;
|
||||||
processStopFlight?: Promise<void>;
|
processStopFlight?: Promise<void>;
|
||||||
reconfigureAfterSettled: boolean;
|
reconfigureAfterSettled: boolean;
|
||||||
@@ -281,7 +282,15 @@ export class PiWorkerPool {
|
|||||||
revision,
|
revision,
|
||||||
})
|
})
|
||||||
.then(async ({ opened: { worker, session }, lease }) => {
|
.then(async ({ opened: { worker, session }, lease }) => {
|
||||||
const record = this.createRecord(conversation, worker, session, generation, revision, lease);
|
const record = this.createRecord(
|
||||||
|
conversation,
|
||||||
|
worker,
|
||||||
|
session,
|
||||||
|
generation,
|
||||||
|
revision,
|
||||||
|
lease,
|
||||||
|
true,
|
||||||
|
);
|
||||||
this.workers.set(conversation.conversationId, record);
|
this.workers.set(conversation.conversationId, record);
|
||||||
this.notifyReclaimableWorker();
|
this.notifyReclaimableWorker();
|
||||||
const state = this.publicState(record);
|
const state = this.publicState(record);
|
||||||
@@ -336,6 +345,7 @@ export class PiWorkerPool {
|
|||||||
generation,
|
generation,
|
||||||
revision,
|
revision,
|
||||||
lease,
|
lease,
|
||||||
|
true,
|
||||||
);
|
);
|
||||||
this.workers.set(conversation.conversationId, record);
|
this.workers.set(conversation.conversationId, record);
|
||||||
this.notifyReclaimableWorker();
|
this.notifyReclaimableWorker();
|
||||||
@@ -536,8 +546,8 @@ export class PiWorkerPool {
|
|||||||
this.launchTopLevel(record, pending);
|
this.launchTopLevel(record, pending);
|
||||||
return { accepted };
|
return { accepted };
|
||||||
}
|
}
|
||||||
this.waitingRuns.push(pending);
|
|
||||||
pending.queuedAt = this.now();
|
pending.queuedAt = this.now();
|
||||||
|
this.waitingRuns.push(pending);
|
||||||
record.state = 'queued';
|
record.state = 'queued';
|
||||||
this.notifyReclaimableWorker();
|
this.notifyReclaimableWorker();
|
||||||
return { queuePosition: this.waitingRuns.length, accepted };
|
return { queuePosition: this.waitingRuns.length, accepted };
|
||||||
@@ -619,7 +629,7 @@ export class PiWorkerPool {
|
|||||||
this.activeRuns.set(run.conversationId, {
|
this.activeRuns.set(run.conversationId, {
|
||||||
runId: run.runId,
|
runId: run.runId,
|
||||||
generation: record.generation,
|
generation: record.generation,
|
||||||
cold: record.acceptedPromptCount === 0,
|
cold: record.coldStart && record.acceptedPromptCount === 0,
|
||||||
});
|
});
|
||||||
void this.acceptTopLevel(record, run);
|
void this.acceptTopLevel(record, run);
|
||||||
}
|
}
|
||||||
@@ -632,12 +642,16 @@ export class PiWorkerPool {
|
|||||||
const active = this.activeRuns.get(run.conversationId);
|
const active = this.activeRuns.get(run.conversationId);
|
||||||
if (!active) throw new Error('Top-level run was cancelled before acceptance');
|
if (!active) throw new Error('Top-level run was cancelled before acceptance');
|
||||||
active.generation = current.generation;
|
active.generation = current.generation;
|
||||||
|
active.cold = current.coldStart && current.acceptedPromptCount === 0;
|
||||||
current.state = 'running';
|
current.state = 'running';
|
||||||
this.revisions.beginRun(current.revisionWorkerId);
|
this.revisions.beginRun(current.revisionWorkerId);
|
||||||
beganRun = true;
|
beganRun = true;
|
||||||
if (run.queuedAt !== undefined) {
|
this.recordMilestone(
|
||||||
this.recordMilestone(current, run, 'worker.queue_wait', this.now() - run.queuedAt);
|
current,
|
||||||
}
|
run,
|
||||||
|
'worker.queue_wait',
|
||||||
|
run.queuedAt === undefined ? 0 : this.now() - run.queuedAt,
|
||||||
|
);
|
||||||
const acceptedAt = this.now();
|
const acceptedAt = this.now();
|
||||||
const response = await current.worker.request(run.command);
|
const response = await current.worker.request(run.command);
|
||||||
if (run.command.type === 'prompt') {
|
if (run.command.type === 'prompt') {
|
||||||
@@ -792,6 +806,7 @@ export class PiWorkerPool {
|
|||||||
generation,
|
generation,
|
||||||
revision,
|
revision,
|
||||||
lease,
|
lease,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
replacement.state = record.state === 'spawning' && this.activeRuns.has(conversationId)
|
replacement.state = record.state === 'spawning' && this.activeRuns.has(conversationId)
|
||||||
? 'running'
|
? 'running'
|
||||||
@@ -822,6 +837,7 @@ export class PiWorkerPool {
|
|||||||
generation: number,
|
generation: number,
|
||||||
revision: PiManagedInputRevision,
|
revision: PiManagedInputRevision,
|
||||||
processLease: PiProcessLease,
|
processLease: PiProcessLease,
|
||||||
|
coldStart: boolean,
|
||||||
): WorkerRecord {
|
): WorkerRecord {
|
||||||
const revisionWorkerId = `${conversation.conversationId}:${generation}`;
|
const revisionWorkerId = `${conversation.conversationId}:${generation}`;
|
||||||
this.revisions.registerWorker(revisionWorkerId, revision);
|
this.revisions.registerWorker(revisionWorkerId, revision);
|
||||||
@@ -841,6 +857,7 @@ export class PiWorkerPool {
|
|||||||
child: new Map(),
|
child: new Map(),
|
||||||
},
|
},
|
||||||
acceptedPromptCount: 0,
|
acceptedPromptCount: 0,
|
||||||
|
coldStart,
|
||||||
processLease,
|
processLease,
|
||||||
reconfigureAfterSettled: false,
|
reconfigureAfterSettled: false,
|
||||||
};
|
};
|
||||||
@@ -1058,7 +1075,7 @@ export class PiWorkerPool {
|
|||||||
run: Pick<PendingTopLevelRun, 'runId'>,
|
run: Pick<PendingTopLevelRun, 'runId'>,
|
||||||
milestone: 'worker.queue_wait' | 'prompt.accepted' | 'agent.settled',
|
milestone: 'worker.queue_wait' | 'prompt.accepted' | 'agent.settled',
|
||||||
durationMs: number,
|
durationMs: number,
|
||||||
cold = record.acceptedPromptCount === 0,
|
cold = record.coldStart && record.acceptedPromptCount === 0,
|
||||||
): void {
|
): void {
|
||||||
if (!this.onTelemetry) return;
|
if (!this.onTelemetry) return;
|
||||||
this.onTelemetry(createPiRuntimeTelemetryEvent({
|
this.onTelemetry(createPiRuntimeTelemetryEvent({
|
||||||
|
|||||||
@@ -208,6 +208,16 @@ export class PiWorkerProcess {
|
|||||||
return this.generationValue;
|
return this.generationValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get processId(): number | undefined {
|
||||||
|
return this.child?.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRunning(): boolean {
|
||||||
|
return this.child !== null
|
||||||
|
&& this.child.exitCode === null
|
||||||
|
&& this.child.signalCode === null;
|
||||||
|
}
|
||||||
|
|
||||||
get pendingCommandCount(): number {
|
get pendingCommandCount(): number {
|
||||||
return this.rpc?.pendingCount ?? 0;
|
return this.rpc?.pendingCount ?? 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,8 +140,10 @@ async function runProductProofSamples(artifact, projectRoot, samples) {
|
|||||||
|
|
||||||
function summarizeManagedMilestone(reports, cold, milestone) {
|
function summarizeManagedMilestone(reports, cold, milestone) {
|
||||||
return summarizeMeasurements(reports.flatMap(({ extension }) => (
|
return summarizeMeasurements(reports.flatMap(({ extension }) => (
|
||||||
extension.managedWorkerMilestones
|
extension.managedTurns
|
||||||
.filter((event) => event.cold === cold && event.milestone === milestone)
|
.filter((turn) => turn.cold === cold)
|
||||||
|
.flatMap((turn) => turn.milestones)
|
||||||
|
.filter((event) => event.milestone === milestone)
|
||||||
.map(({ durationMs }) => durationMs)
|
.map(({ durationMs }) => durationMs)
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@@ -180,32 +182,29 @@ export async function runPiReleasePerformance(options) {
|
|||||||
options.projectRoot,
|
options.projectRoot,
|
||||||
options.samples,
|
options.samples,
|
||||||
);
|
);
|
||||||
const providerMetric = (temperature, field) => summarizeMeasurements(
|
|
||||||
providerContracts.protocols.flatMap(
|
|
||||||
({ qualification }) => qualification[temperature][field],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
const managedMilestones = {
|
const managedMilestones = {
|
||||||
definition: 'Actual createPiManagedWorkerOpener execution from final app.asar Main against final pi-runtime; cold creates a session, warm reopens it.',
|
definition: 'Each sample is one Main-owned correlated final-ASAR turn against the final pi-runtime; cold creates a session and invokes a real packaged subagent, warm reopens the same session. No milestone is joined from an external probe.',
|
||||||
cold: {
|
cold: {
|
||||||
|
workerQueueWaitMs: summarizeManagedMilestone(productProofs, true, 'worker.queue_wait'),
|
||||||
resourcesReadyMs: summarizeManagedMilestone(productProofs, true, 'resources.ready'),
|
resourcesReadyMs: summarizeManagedMilestone(productProofs, true, 'resources.ready'),
|
||||||
workerSpawnMs: summarizeManagedMilestone(productProofs, true, 'worker.spawn'),
|
workerSpawnMs: summarizeManagedMilestone(productProofs, true, 'worker.spawn'),
|
||||||
rpcReadyMs: summarizeManagedMilestone(productProofs, true, 'rpc.ready'),
|
rpcReadyMs: summarizeManagedMilestone(productProofs, true, 'rpc.ready'),
|
||||||
sessionOpenMs: summarizeManagedMilestone(productProofs, true, 'session.open'),
|
sessionOpenMs: summarizeManagedMilestone(productProofs, true, 'session.open'),
|
||||||
promptAcceptedMs: providerMetric('cold', 'promptAcceptedSamplesMs'),
|
promptAcceptedMs: summarizeManagedMilestone(productProofs, true, 'prompt.accepted'),
|
||||||
agentStartMs: providerMetric('cold', 'agentStartSamplesMs'),
|
agentStartMs: summarizeManagedMilestone(productProofs, true, 'agent.start'),
|
||||||
providerFirstEventMs: providerMetric('cold', 'providerFirstEventSamplesMs'),
|
providerFirstEventMs: summarizeManagedMilestone(productProofs, true, 'provider.first_event'),
|
||||||
agentSettledMs: providerMetric('cold', 'agentSettledSamplesMs'),
|
agentSettledMs: summarizeManagedMilestone(productProofs, true, 'agent.settled'),
|
||||||
},
|
},
|
||||||
warm: {
|
warm: {
|
||||||
|
workerQueueWaitMs: summarizeManagedMilestone(productProofs, false, 'worker.queue_wait'),
|
||||||
resourcesReadyMs: summarizeManagedMilestone(productProofs, false, 'resources.ready'),
|
resourcesReadyMs: summarizeManagedMilestone(productProofs, false, 'resources.ready'),
|
||||||
workerSpawnMs: summarizeManagedMilestone(productProofs, false, 'worker.spawn'),
|
workerSpawnMs: summarizeManagedMilestone(productProofs, false, 'worker.spawn'),
|
||||||
rpcReadyMs: summarizeManagedMilestone(productProofs, false, 'rpc.ready'),
|
rpcReadyMs: summarizeManagedMilestone(productProofs, false, 'rpc.ready'),
|
||||||
sessionOpenMs: summarizeManagedMilestone(productProofs, false, 'session.open'),
|
sessionOpenMs: summarizeManagedMilestone(productProofs, false, 'session.open'),
|
||||||
promptAcceptedMs: providerMetric('warm', 'promptAcceptedSamplesMs'),
|
promptAcceptedMs: summarizeManagedMilestone(productProofs, false, 'prompt.accepted'),
|
||||||
agentStartMs: providerMetric('warm', 'agentStartSamplesMs'),
|
agentStartMs: summarizeManagedMilestone(productProofs, false, 'agent.start'),
|
||||||
providerFirstEventMs: providerMetric('warm', 'providerFirstEventSamplesMs'),
|
providerFirstEventMs: summarizeManagedMilestone(productProofs, false, 'provider.first_event'),
|
||||||
agentSettledMs: providerMetric('warm', 'agentSettledSamplesMs'),
|
agentSettledMs: summarizeManagedMilestone(productProofs, false, 'agent.settled'),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const pressureUiInteractiveMs = summarizeMeasurements(
|
const pressureUiInteractiveMs = summarizeMeasurements(
|
||||||
@@ -230,7 +229,7 @@ export async function runPiReleasePerformance(options) {
|
|||||||
const git = await gitEvidence(options.projectRoot);
|
const git = await gitEvidence(options.projectRoot);
|
||||||
const scenarios = [
|
const scenarios = [
|
||||||
{ id: 1, name: 'fresh userData metadata and Composer', evidence: ['metadata fragments', 'Electron Composer samples'], result: 'pass' },
|
{ id: 1, name: 'fresh userData metadata and Composer', evidence: ['metadata fragments', 'Electron Composer samples'], result: 'pass' },
|
||||||
{ id: 2, name: 'first prompt milestone split', evidence: ['final-ASAR resources.ready/worker.spawn/rpc.ready/session.open', 'separate cold prompt.accepted/agent.start/provider.first_event/agent.settled', 'Renderer commit'], result: 'pass' },
|
{ id: 2, name: 'first prompt milestone split', evidence: ['single correlated final-ASAR worker.queue_wait/resources.ready/worker.spawn/rpc.ready/session.open/prompt.accepted/agent.start/provider.first_event/agent.settled timeline per turn', 'Renderer commit'], result: 'pass' },
|
||||||
{ id: 3, name: 'warm Conversation restore', evidence: ['packaged session stop/reopen/get_entries', 'warm rpc.ready samples'], result: 'pass' },
|
{ id: 3, name: 'warm Conversation restore', evidence: ['packaged session stop/reopen/get_entries', 'warm rpc.ready samples'], result: 'pass' },
|
||||||
{ id: 4, name: 'two projects provider-shaped overlap and abort isolation', evidence: providerContracts.protocols.map(({ protocol }) => protocol), result: 'pass' },
|
{ id: 4, name: 'two projects provider-shaped overlap and abort isolation', evidence: providerContracts.protocols.map(({ protocol }) => protocol), result: 'pass' },
|
||||||
{ id: 5, name: 'same-project read-only concurrency', evidence: ['pi-worker-pool.test.ts', 'pi-subagent.test.ts'], result: 'pass' },
|
{ id: 5, name: 'same-project read-only concurrency', evidence: ['pi-worker-pool.test.ts', 'pi-subagent.test.ts'], result: 'pass' },
|
||||||
@@ -302,8 +301,11 @@ export async function runPiReleasePerformance(options) {
|
|||||||
budgets,
|
budgets,
|
||||||
result: 'pass',
|
result: 'pass',
|
||||||
crossPlatformReleaseReady: false,
|
crossPlatformReleaseReady: false,
|
||||||
releaseBlockers: [
|
releaseBlockers: platform() === 'linux' && !release().toLowerCase().includes('microsoft')
|
||||||
'macOS x64 and macOS arm64 PI-150 performance/final-artifact validation was skipped by explicit user direction',
|
? []
|
||||||
|
: ['Native non-WSL Linux desktop/compositor PI-150 evidence is not established by this report'],
|
||||||
|
deferredToPi160: [
|
||||||
|
'macOS x64 and macOS arm64 final release validation was skipped by explicit user direction and remains Not Pass',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (options.reportPath) {
|
if (options.reportPath) {
|
||||||
|
|||||||
@@ -56,7 +56,11 @@ function assertActivePressure(pressure) {
|
|||||||
const expected = {
|
const expected = {
|
||||||
parentWorkers: 4,
|
parentWorkers: 4,
|
||||||
childWorkers: 4,
|
childWorkers: 4,
|
||||||
|
parentProcesses: 4,
|
||||||
|
childProcesses: 4,
|
||||||
liveProcesses: 8,
|
liveProcesses: 8,
|
||||||
|
parentProviderRequests: 4,
|
||||||
|
childProviderRequests: 4,
|
||||||
processBudget: 8,
|
processBudget: 8,
|
||||||
childPermits: 4,
|
childPermits: 4,
|
||||||
dispatches: 4,
|
dispatches: 4,
|
||||||
@@ -65,7 +69,11 @@ function assertActivePressure(pressure) {
|
|||||||
const actual = {
|
const actual = {
|
||||||
parentWorkers: pressure?.parentWorkers,
|
parentWorkers: pressure?.parentWorkers,
|
||||||
childWorkers: pressure?.childWorkers,
|
childWorkers: pressure?.childWorkers,
|
||||||
|
parentProcesses: pressure?.parentProcessIds?.length,
|
||||||
|
childProcesses: pressure?.childProcessIds?.length,
|
||||||
liveProcesses: pressure?.liveProcessIds?.length,
|
liveProcesses: pressure?.liveProcessIds?.length,
|
||||||
|
parentProviderRequests: pressure?.providerRequests?.parent,
|
||||||
|
childProviderRequests: pressure?.providerRequests?.child,
|
||||||
processBudget: pressure?.processBudget?.active,
|
processBudget: pressure?.processBudget?.active,
|
||||||
childPermits: pressure?.childPermits?.active,
|
childPermits: pressure?.childPermits?.active,
|
||||||
dispatches: pressure?.dispatches?.active,
|
dispatches: pressure?.dispatches?.active,
|
||||||
@@ -80,7 +88,11 @@ function assertReleasedPressure(pressure) {
|
|||||||
const counts = [
|
const counts = [
|
||||||
pressure?.parentWorkers,
|
pressure?.parentWorkers,
|
||||||
pressure?.childWorkers,
|
pressure?.childWorkers,
|
||||||
|
pressure?.parentProcessIds?.length,
|
||||||
|
pressure?.childProcessIds?.length,
|
||||||
pressure?.liveProcessIds?.length,
|
pressure?.liveProcessIds?.length,
|
||||||
|
pressure?.providerRequests?.parent,
|
||||||
|
pressure?.providerRequests?.child,
|
||||||
pressure?.processBudget?.active,
|
pressure?.processBudget?.active,
|
||||||
pressure?.processBudget?.waiting,
|
pressure?.processBudget?.waiting,
|
||||||
pressure?.childPermits?.active,
|
pressure?.childPermits?.active,
|
||||||
@@ -147,10 +159,16 @@ export async function runPackagedProductProof(options) {
|
|||||||
const extension = await evaluateProof(electronApplication, 'extension');
|
const extension = await evaluateProof(electronApplication, 'extension');
|
||||||
assertPackagedMain(extension);
|
assertPackagedMain(extension);
|
||||||
if (extension.extension?.subagentStatus !== 'complete'
|
if (extension.extension?.subagentStatus !== 'complete'
|
||||||
|| extension.extension?.childToolNames?.length !== 0
|
|| extension.extension?.childToolNames?.join(',') !== 'find,grep,ls,read'
|
||||||
|| !extension.extension?.parentToolNames?.includes('subagent')
|
|| !extension.extension?.parentToolNames?.includes('subagent')
|
||||||
|| extension.extension?.managedWorkerMilestones?.filter(({ cold }) => cold).length !== 4
|
|| extension.extension?.parentProcessIds?.length < 2
|
||||||
|| extension.extension?.managedWorkerMilestones?.filter(({ cold }) => !cold).length !== 4) {
|
|| extension.extension?.childProcessIds?.length !== 1
|
||||||
|
|| extension.extension?.providerRequests?.child !== 1
|
||||||
|
|| extension.extension?.managedTurns?.length !== 2
|
||||||
|
|| extension.extension.managedTurns.some(({ milestones }) => (
|
||||||
|
milestones.map(({ milestone }) => milestone).join(',')
|
||||||
|
!== 'worker.queue_wait,resources.ready,worker.spawn,rpc.ready,session.open,prompt.accepted,agent.start,provider.first_event,agent.settled'
|
||||||
|
))) {
|
||||||
throw new Error(`Final ASAR extension/subagent proof failed: ${JSON.stringify(extension.extension)}`);
|
throw new Error(`Final ASAR extension/subagent proof failed: ${JSON.stringify(extension.extension)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
import { execFile } from 'node:child_process';
|
||||||
import { mkdir, writeFile } from 'node:fs/promises';
|
import { mkdir, writeFile } from 'node:fs/promises';
|
||||||
import { arch, platform } from 'node:os';
|
import { arch, platform, release } from 'node:os';
|
||||||
import { dirname, resolve } from 'node:path';
|
import { dirname, resolve } from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
import { pathToFileURL } from 'node:url';
|
||||||
|
|
||||||
@@ -11,6 +12,23 @@ import { parsePiArtifactVerifierArgs } from './verify-pi-product-artifact.mjs';
|
|||||||
import { runProbe } from './probe-pi-runtime.mjs';
|
import { runProbe } from './probe-pi-runtime.mjs';
|
||||||
import { runPackagedProductProof } from './run-pi-subagent-packaged-smoke.mjs';
|
import { runPackagedProductProof } from './run-pi-subagent-packaged-smoke.mjs';
|
||||||
|
|
||||||
|
function runGit(projectRoot, args) {
|
||||||
|
return new Promise((resolvePromise, reject) => {
|
||||||
|
execFile('git', args, { cwd: projectRoot, windowsHide: true }, (error, stdout) => {
|
||||||
|
if (error) reject(error);
|
||||||
|
else resolvePromise(stdout.trim());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function gitEvidence(projectRoot) {
|
||||||
|
const [commit, status] = await Promise.all([
|
||||||
|
runGit(projectRoot, ['rev-parse', 'HEAD']),
|
||||||
|
runGit(projectRoot, ['status', '--porcelain']),
|
||||||
|
]);
|
||||||
|
return { commit, dirty: Boolean(status) };
|
||||||
|
}
|
||||||
|
|
||||||
async function runExtensionSmoke(projectRoot, artifact) {
|
async function runExtensionSmoke(projectRoot, artifact) {
|
||||||
return await runPackagedProductProof({
|
return await runPackagedProductProof({
|
||||||
projectRoot,
|
projectRoot,
|
||||||
@@ -21,6 +39,7 @@ async function runExtensionSmoke(projectRoot, artifact) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function runRealPiSmoke(options) {
|
export async function runRealPiSmoke(options) {
|
||||||
|
const git = await gitEvidence(options.projectRoot);
|
||||||
const artifact = await verifyPiProductArtifact(options);
|
const artifact = await verifyPiProductArtifact(options);
|
||||||
const common = {
|
const common = {
|
||||||
samples: options.samples,
|
samples: options.samples,
|
||||||
@@ -41,7 +60,8 @@ export async function runRealPiSmoke(options) {
|
|||||||
const report = {
|
const report = {
|
||||||
schemaVersion: 1,
|
schemaVersion: 1,
|
||||||
generatedAt: new Date().toISOString(),
|
generatedAt: new Date().toISOString(),
|
||||||
commit: process.env.MAKELORE_BUILD_COMMIT ?? null,
|
commit: git.commit,
|
||||||
|
git,
|
||||||
platform: { platform: platform(), arch: arch() },
|
platform: { platform: platform(), arch: arch() },
|
||||||
scope: {
|
scope: {
|
||||||
finalProductArtifact: true,
|
finalProductArtifact: true,
|
||||||
@@ -76,8 +96,11 @@ export async function runRealPiSmoke(options) {
|
|||||||
},
|
},
|
||||||
result: 'pass',
|
result: 'pass',
|
||||||
crossPlatformReleaseReady: false,
|
crossPlatformReleaseReady: false,
|
||||||
releaseBlockers: [
|
releaseBlockers: platform() === 'linux' && !release().toLowerCase().includes('microsoft')
|
||||||
'macOS x64 and macOS arm64 PI-150 final-artifact/runtime/resource/performance validation was skipped by explicit user direction',
|
? []
|
||||||
|
: ['Native non-WSL Linux desktop/compositor PI-150 evidence is not established by this report'],
|
||||||
|
deferredToPi160: [
|
||||||
|
'macOS x64 and macOS arm64 final release validation was skipped by explicit user direction and remains Not Pass',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (Object.values(report.coverage).some((value) => value !== true)) {
|
if (Object.values(report.coverage).some((value) => value !== true)) {
|
||||||
|
|||||||
@@ -467,8 +467,10 @@ describe('Pi worker pool', () => {
|
|||||||
it('rebuilds stale idle workers before prompt and lets running workers settle first', async () => {
|
it('rebuilds stale idle workers before prompt and lets running workers settle first', async () => {
|
||||||
const workers = new Map<string, FakeWorker[]>();
|
const workers = new Map<string, FakeWorker[]>();
|
||||||
const revisions: Array<{ conversationId: string; provider: number; resources: number }> = [];
|
const revisions: Array<{ conversationId: string; provider: number; resources: number }> = [];
|
||||||
|
const telemetry: PiRuntimeTelemetryEvent[] = [];
|
||||||
const pool = new PiWorkerPool({
|
const pool = new PiWorkerPool({
|
||||||
maxIdle: 4,
|
maxIdle: 4,
|
||||||
|
onTelemetry: (event) => telemetry.push(event),
|
||||||
openWorker: async ({ conversation: input, revision }) => {
|
openWorker: async ({ conversation: input, revision }) => {
|
||||||
const worker = new FakeWorker(`worker-${input.conversationId}-${(workers.get(input.conversationId)?.length ?? 0) + 1}`);
|
const worker = new FakeWorker(`worker-${input.conversationId}-${(workers.get(input.conversationId)?.length ?? 0) + 1}`);
|
||||||
workers.set(input.conversationId, [...(workers.get(input.conversationId) ?? []), worker]);
|
workers.set(input.conversationId, [...(workers.get(input.conversationId) ?? []), worker]);
|
||||||
@@ -494,7 +496,7 @@ describe('Pi worker pool', () => {
|
|||||||
await expect.poll(() => workers.get('conversation-running')![0]!.requests.length).toBe(1);
|
await expect.poll(() => workers.get('conversation-running')![0]!.requests.length).toBe(1);
|
||||||
|
|
||||||
pool.markProviderStale();
|
pool.markProviderStale();
|
||||||
pool.startTopLevel({
|
const idleTicket = pool.startTopLevel({
|
||||||
conversationId: 'conversation-idle',
|
conversationId: 'conversation-idle',
|
||||||
runId: 'run-idle',
|
runId: 'run-idle',
|
||||||
command: { type: 'prompt', message: 'idle' },
|
command: { type: 'prompt', message: 'idle' },
|
||||||
@@ -503,6 +505,11 @@ describe('Pi worker pool', () => {
|
|||||||
await expect.poll(() => workers.get('conversation-idle')?.length).toBe(2);
|
await expect.poll(() => workers.get('conversation-idle')?.length).toBe(2);
|
||||||
expect(workers.get('conversation-idle')![0]!.stopped).toBe(true);
|
expect(workers.get('conversation-idle')![0]!.stopped).toBe(true);
|
||||||
await expect.poll(() => workers.get('conversation-idle')![1]!.requests.length).toBe(1);
|
await expect.poll(() => workers.get('conversation-idle')![1]!.requests.length).toBe(1);
|
||||||
|
await idleTicket.accepted;
|
||||||
|
expect(telemetry.filter(({ runRef }) => runRef === 'runidle')).toEqual([
|
||||||
|
expect.objectContaining({ milestone: 'worker.queue_wait', workerGeneration: 2, cold: false }),
|
||||||
|
expect.objectContaining({ milestone: 'prompt.accepted', workerGeneration: 2, cold: false }),
|
||||||
|
]);
|
||||||
expect(workers.get('conversation-running')).toHaveLength(1);
|
expect(workers.get('conversation-running')).toHaveLength(1);
|
||||||
expect(workers.get('conversation-running')![0]!.stopped).toBe(false);
|
expect(workers.get('conversation-running')![0]!.stopped).toBe(false);
|
||||||
|
|
||||||
@@ -756,15 +763,17 @@ describe('Pi worker pool', () => {
|
|||||||
await second.accepted;
|
await second.accepted;
|
||||||
|
|
||||||
expect(telemetry.map(({ milestone }) => milestone)).toEqual([
|
expect(telemetry.map(({ milestone }) => milestone)).toEqual([
|
||||||
|
'worker.queue_wait',
|
||||||
'prompt.accepted',
|
'prompt.accepted',
|
||||||
'agent.settled',
|
'agent.settled',
|
||||||
'worker.queue_wait',
|
'worker.queue_wait',
|
||||||
'prompt.accepted',
|
'prompt.accepted',
|
||||||
]);
|
]);
|
||||||
expect(telemetry[1]).toMatchObject({
|
expect(telemetry[0]).toMatchObject({ durationMs: 0, workerGeneration: 1, cold: true });
|
||||||
|
expect(telemetry[2]).toMatchObject({
|
||||||
milestone: 'agent.settled', durationMs: 20, workerGeneration: 1, cold: true,
|
milestone: 'agent.settled', durationMs: 20, workerGeneration: 1, cold: true,
|
||||||
});
|
});
|
||||||
expect(telemetry[2]).toMatchObject({ durationMs: 20, workerGeneration: 1, cold: true });
|
expect(telemetry[3]).toMatchObject({ durationMs: 20, workerGeneration: 1, cold: true });
|
||||||
const serialized = JSON.stringify(telemetry);
|
const serialized = JSON.stringify(telemetry);
|
||||||
expect(serialized).not.toContain('private first prompt');
|
expect(serialized).not.toContain('private first prompt');
|
||||||
expect(serialized).not.toContain('private second prompt');
|
expect(serialized).not.toContain('private second prompt');
|
||||||
|
|||||||
Reference in new issue
Block a user