Use execution workspace review semantics during bottom-up pin upgrades

This commit is contained in:
Timothy J. Aveni
2026-10-08 07:43:32 -07:00
parent 31a48e746d
commit a935273913
2 changed files with 124 additions and 6 deletions
+43 -5
View File
@@ -15,7 +15,12 @@ import {
} from "./assembly.js";
import { createGitCapabilityResolver } from "./git-resolver.js";
import { snapshotCommit, buildImmutableCandidate } from "./checked-build.js";
import { planEvolution, type WorkspaceRevision, type EvolutionReview } from "../capability-model/index.js";
import {
planEvolution,
reviewSemanticMajors,
type WorkspaceRevision,
type EvolutionReview,
} from "../capability-model/index.js";
import { withFileLock } from "./file-lock.js";
const execFile = promisify(callback);
type Source = { repository: string; commit: string };
@@ -187,6 +192,34 @@ export type UpgradeEffects = {
snapshot(root: string): Promise<string>;
publish(root: string, commit: string): Promise<void>;
};
/** Review a successfully built immutable candidate using the same boundary as
* ordinary authoring checks. Execution storage compatibility is checked against
* the current graph at activation; the legacy evolution model is not its schema.
*/
export async function reviewUpgradeCandidate({
artifact,
baseline,
candidate,
reviews = [],
}: {
artifact: string;
baseline: WorkspaceRevision | null;
candidate: WorkspaceRevision;
reviews?: EvolutionReview[];
}) {
let execution = false;
try {
await fs.access(path.join(artifact, "execution-plan.json"));
execution = true;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
return execution
? { filename: "semantic-review.json", review: reviewSemanticMajors(baseline, candidate, reviews) }
: { filename: "evolution.json", review: planEvolution(baseline, candidate, { reviews }) };
}
const effects: UpgradeEffects = {
async check(node, root, output, spec) {
if (node.kind === "workspace" && !spec.baseline && !spec.bootstrap)
@@ -206,10 +239,15 @@ const effects: UpgradeEffects = {
? (JSON.parse(await fs.readFile(spec.baseline, "utf8")) as WorkspaceRevision)
: null;
const reviews = spec.reviews ? (JSON.parse(await fs.readFile(spec.reviews, "utf8")) as EvolutionReview[]) : [];
const evolution = planEvolution(baseline, JSON.parse(candidate), { reviews });
await fs.writeFile(path.join(output, "evolution.json"), JSON.stringify(evolution, null, 2));
if (evolution.blockers.length)
throw new Error(`Refactor required in ${node.directory}: ${evolution.blockers.join("; ")}`);
const { filename, review } = await reviewUpgradeCandidate({
artifact,
baseline,
candidate: JSON.parse(candidate),
reviews,
});
await fs.writeFile(path.join(output, filename), JSON.stringify(review, null, 2));
if (review.blockers.length)
throw new Error(`Refactor required in ${node.directory}: ${review.blockers.join("; ")}`);
}
},
async snapshot(root) {