fix(desktop): pin complete Pi runtime closure (#2439)
## Summary - pin `pi-agent-core`, `pi-ai`, `pi-coding-agent`, and `pi-tui` to one exact 0.82.0 workspace override set - extend the Pi version policy guard to reject missing, ranged, or mismatched desktop runtime closure overrides - add a patch changeset for the legacy desktop packaging fix ## Test plan - `node --test scripts/__tests__/check-pi-versions-pinned.test.mjs` (5 passed) - `node scripts/check-pi-versions-pinned.mjs` - `corepack pnpm check:changesets --strict` - focused engine fixtures: 4 files / 47 tests passed - GitHub: Desktop packaging, Lint, Typecheck, Build, Gate, and Greptile Review passed
This commit is contained in:
7
.changeset/desktop-pi-runtime-closure.md
Normal file
7
.changeset/desktop-pi-runtime-closure.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Keep legacy desktop builds on one packageable Pi runtime dependency closure.
|
||||
category: fix
|
||||
dev: Pins pi-agent-core and pi-tui with the 0.82.0 Pi runtime pair and extends the dependency policy guard to workspace overrides.
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -7,8 +7,10 @@ settings:
|
||||
overrides:
|
||||
'@types/node': ^25.5.2
|
||||
protobufjs: ^7.5.8
|
||||
'@earendil-works/pi-agent-core': 0.82.0
|
||||
'@earendil-works/pi-ai': 0.82.0
|
||||
'@earendil-works/pi-coding-agent': 0.82.0
|
||||
'@earendil-works/pi-tui': 0.82.0
|
||||
|
||||
importers:
|
||||
|
||||
|
||||
@@ -25,8 +25,13 @@ overrides:
|
||||
# to match guarded package manifests (cli/core/engine/dashboard/pi-claude-cli).
|
||||
# FNXC:ModelCatalog 2026-07-24-12:00: FN-8564 advances the matched runtime pair
|
||||
# to 0.82.0. Wildcard consumers must resolve the exact guarded Pi API surface.
|
||||
# FNXC:DesktopPackaging 2026-07-25-17:15: Legacy `pnpm deploy` resolves pi-coding-agent's
|
||||
# transitive ranges without the workspace lockfile. Pin the complete Pi runtime closure to one
|
||||
# exact version so new agent-core or tui patches cannot split the packageable desktop tree.
|
||||
'@earendil-works/pi-agent-core': 0.82.0
|
||||
'@earendil-works/pi-ai': 0.82.0
|
||||
'@earendil-works/pi-coding-agent': 0.82.0
|
||||
'@earendil-works/pi-tui': 0.82.0
|
||||
# FNXC:DesktopEmbeddedPostgres 2026-07-14-09:30:
|
||||
# Desktop release jobs cross-build Intel and ARM64 artifacts on one host. Install
|
||||
# optional native payloads for both CPU families on the current OS so each
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import {
|
||||
PI_RUNTIME_PACKAGES,
|
||||
scanTrackedManifests,
|
||||
validateManifestSet,
|
||||
validateWorkspaceOverrides,
|
||||
} from "../check-pi-versions-pinned.mjs";
|
||||
|
||||
const pinnedManifest = {
|
||||
@@ -42,4 +44,32 @@ describe("check-pi-versions-pinned", () => {
|
||||
it("accepts a clean exact matched pair", () => {
|
||||
assert.deepEqual(validate(pinnedManifest), []);
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:DesktopPackaging 2026-07-25-17:15:
|
||||
Legacy desktop deploy resolves transitive pi-mono ranges without the workspace
|
||||
lockfile. Guard every Pi runtime package in the staged closure, not only the
|
||||
direct pi-ai and pi-coding-agent declarations, so a newly published patch
|
||||
cannot split agent-core or tui from the exact runtime version.
|
||||
*/
|
||||
it("requires one exact workspace override for every staged Pi runtime package", () => {
|
||||
const coherentOverrides = Object.fromEntries(PI_RUNTIME_PACKAGES.map((packageName) => [packageName, "0.82.0"]));
|
||||
|
||||
assert.deepEqual(validateWorkspaceOverrides(coherentOverrides), []);
|
||||
assert.equal(
|
||||
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-tui": undefined })
|
||||
.some((violation) => violation.includes("pi-tui") && violation.includes("must be pinned")),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-agent-core": "^0.82.0" })
|
||||
.some((violation) => violation.includes("pi-agent-core") && violation.includes("exact semver")),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
validateWorkspaceOverrides({ ...coherentOverrides, "@earendil-works/pi-tui": "0.82.1" })
|
||||
.some((violation) => violation.includes("one exact version")),
|
||||
true,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,12 +9,27 @@ manifest declaration must be an exact semver and all declarations must agree.
|
||||
import { readFileSync } from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
|
||||
export const PI_DEPENDENCIES = [
|
||||
"@earendil-works/pi-ai",
|
||||
"@earendil-works/pi-coding-agent",
|
||||
];
|
||||
|
||||
/*
|
||||
FNXC:DesktopPackaging 2026-07-25-17:15:
|
||||
The legacy desktop deploy resolves pi-coding-agent's transitive pi-mono ranges
|
||||
without the workspace lockfile. Keep every Pi package in that staged runtime
|
||||
closure on one exact override so a new agent-core or tui patch cannot be hoisted
|
||||
beside older direct ai/coding-agent dependencies.
|
||||
*/
|
||||
export const PI_RUNTIME_PACKAGES = [
|
||||
"@earendil-works/pi-agent-core",
|
||||
"@earendil-works/pi-ai",
|
||||
"@earendil-works/pi-coding-agent",
|
||||
"@earendil-works/pi-tui",
|
||||
];
|
||||
|
||||
export const GUARDED_MANIFESTS = [
|
||||
"packages/cli/package.json",
|
||||
"packages/core/package.json",
|
||||
@@ -30,6 +45,31 @@ export function isExactSemver(version) {
|
||||
return typeof version === "string" && EXACT_SEMVER.test(version);
|
||||
}
|
||||
|
||||
export function validateWorkspaceOverrides(overrides = {}) {
|
||||
const violations = [];
|
||||
const versions = [];
|
||||
|
||||
for (const packageName of PI_RUNTIME_PACKAGES) {
|
||||
const version = overrides?.[packageName];
|
||||
if (version === undefined) {
|
||||
violations.push(`pnpm-workspace.yaml: overrides.${packageName} must be pinned for the staged desktop closure`);
|
||||
continue;
|
||||
}
|
||||
if (!isExactSemver(version)) {
|
||||
violations.push(`pnpm-workspace.yaml: overrides.${packageName} must be an exact semver, found ${JSON.stringify(version)}`);
|
||||
continue;
|
||||
}
|
||||
versions.push(version);
|
||||
}
|
||||
|
||||
const uniqueVersions = [...new Set(versions)];
|
||||
if (uniqueVersions.length > 1) {
|
||||
violations.push(`${PI_RUNTIME_PACKAGES.join(", ")} must use one exact version in pnpm-workspace.yaml overrides; found ${uniqueVersions.join(", ")}`);
|
||||
}
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
function listTrackedManifests() {
|
||||
const result = spawnSync("git", ["ls-files", "--", ...GUARDED_MANIFESTS], {
|
||||
encoding: "utf8",
|
||||
@@ -99,13 +139,37 @@ export function scanTrackedManifests(files = listTrackedManifests(), options = {
|
||||
return [`${filePath}: invalid JSON (${error instanceof Error ? error.message : String(error)})`];
|
||||
}
|
||||
}
|
||||
return validateManifestSet(manifests);
|
||||
const violations = validateManifestSet(manifests);
|
||||
let workspaceConfig;
|
||||
try {
|
||||
workspaceConfig = parseYaml(readFile("pnpm-workspace.yaml", "utf8"));
|
||||
} catch (error) {
|
||||
return [...violations, `pnpm-workspace.yaml: invalid YAML (${error instanceof Error ? error.message : String(error)})`];
|
||||
}
|
||||
|
||||
const workspaceOverrides = workspaceConfig?.overrides ?? {};
|
||||
violations.push(...validateWorkspaceOverrides(workspaceOverrides));
|
||||
|
||||
const manifestVersions = new Set(
|
||||
manifests.flatMap(({ manifest }) =>
|
||||
DEPENDENCY_BLOCKS.flatMap((blockName) =>
|
||||
PI_DEPENDENCIES.map((packageName) => manifest?.[blockName]?.[packageName]).filter(isExactSemver),
|
||||
),
|
||||
),
|
||||
);
|
||||
const overrideVersions = new Set(PI_RUNTIME_PACKAGES.map((packageName) => workspaceOverrides[packageName]).filter(isExactSemver));
|
||||
const allRuntimeVersions = [...new Set([...manifestVersions, ...overrideVersions])];
|
||||
if (allRuntimeVersions.length > 1) {
|
||||
violations.push(`guarded Pi manifests and workspace overrides must use one exact runtime version; found ${allRuntimeVersions.join(", ")}`);
|
||||
}
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
export function formatFailureMessage(violations) {
|
||||
return [
|
||||
"[check-pi-versions-pinned] pi runtime dependencies must be exact, matched versions.",
|
||||
"npm global installs do not use pnpm-lock.yaml; ranges can resolve an incompatible pi-mono patch set.",
|
||||
"[check-pi-versions-pinned] Pi runtime dependencies and workspace overrides must be exact, matched versions.",
|
||||
"npm global installs and legacy desktop deploy do not reliably use pnpm-lock.yaml; ranges can resolve an incompatible pi-mono patch set.",
|
||||
...violations.map((violation) => `- ${violation}`),
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user