Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e162dc3013 | ||
|
|
a935273913 | ||
|
|
e65ddad798 | ||
|
|
31a48e746d |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"sourceRepo": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos",
|
||||
"sourceCommit": "abc91ab1cbbd61ffd7f0a49dbc5768598ac8745c",
|
||||
"sourceCommit": "e4075003e669be0e307ec8c0585cccb1bd08276a",
|
||||
"sourcePath": "quixos-protocol",
|
||||
"exportName": "quixos-protocol",
|
||||
"mirrorRemote": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -11,7 +11,7 @@ export interface ArtifactFile {
|
||||
export interface ArtifactImplementation {
|
||||
id: string;
|
||||
target: string;
|
||||
compatibility: "quixos-service-v1" | "quixos-js-module-v1";
|
||||
compatibility: "quixos-service-v1" | "quixos-js-module-v1" | "quixos-restricted-module-v1";
|
||||
path: string;
|
||||
ports: "quixos-transaction-ports-v1";
|
||||
registryExport?: string;
|
||||
@@ -225,7 +225,11 @@ export function parseArtifactGroup(value: unknown): ArtifactGroup {
|
||||
["registryExport"],
|
||||
path,
|
||||
);
|
||||
if (entry.compatibility !== "quixos-service-v1" && entry.compatibility !== "quixos-js-module-v1")
|
||||
if (
|
||||
!["quixos-service-v1", "quixos-js-module-v1", "quixos-restricted-module-v1"].includes(
|
||||
String(entry.compatibility),
|
||||
)
|
||||
)
|
||||
fail(path, "Unsupported artifact ABI");
|
||||
if (entry.ports !== "quixos-transaction-ports-v1") fail(path, "Unsupported transaction port ABI");
|
||||
if (
|
||||
@@ -233,7 +237,9 @@ export function parseArtifactGroup(value: unknown): ArtifactGroup {
|
||||
(entry.target !== "server" || entry.registryExport !== undefined)
|
||||
)
|
||||
fail(path, "Service must target server and has no JS registry export");
|
||||
if (entry.compatibility === "quixos-js-module-v1") text(entry.registryExport, `${path}.registryExport`);
|
||||
if (entry.compatibility !== "quixos-service-v1") text(entry.registryExport, `${path}.registryExport`);
|
||||
if (entry.compatibility === "quixos-restricted-module-v1" && entry.target !== "server")
|
||||
fail(path, "Restricted modules must target server");
|
||||
const exports = array(entry.exports, `${path}.exports`).map((value) => text(value, `${path}.exports`));
|
||||
unique(exports, `${path}.exports`);
|
||||
const files = parseArtifactFiles(entry.files, `${path}.files`);
|
||||
@@ -300,6 +306,11 @@ export function checkPackageCoverage(
|
||||
for (const id of implementation.exports) {
|
||||
const entry = expected.exports.find((e) => e.id === id);
|
||||
if (!entry) fail(implementation.id, `Unknown export ${id}`);
|
||||
if (
|
||||
implementation.compatibility === "quixos-restricted-module-v1" &&
|
||||
(entry!.execution.kind === "remote" || !entry!.execution.targets.includes("server"))
|
||||
)
|
||||
fail(implementation.id, `Export ${id} does not promise restricted server computation`);
|
||||
if (
|
||||
implementation.target !== "server" &&
|
||||
(!entry?.execution ||
|
||||
|
||||
@@ -113,6 +113,8 @@ export interface ExecutionInstallation {
|
||||
readonly root: string;
|
||||
readonly service?: ArtifactImplementation;
|
||||
readonly module?: ArtifactImplementation;
|
||||
/** Checked candidate only; the qualified host verifies its import surface and SDK. */
|
||||
readonly restricted?: ArtifactImplementation;
|
||||
readonly browserImplementation?: ArtifactImplementation;
|
||||
readonly browserURL?: string;
|
||||
readonly browserRegistry?: string;
|
||||
@@ -251,6 +253,11 @@ function assembleExecutionInstallation(
|
||||
exports: exports.map((entry) => entry.id),
|
||||
})
|
||||
: undefined;
|
||||
const restrictedCandidates = manifest.implementations.filter(
|
||||
(entry) => entry.compatibility === "quixos-restricted-module-v1",
|
||||
);
|
||||
if (restrictedCandidates.length > 1) fail(revision, "Ambiguous restricted module candidates");
|
||||
const restricted = restrictedCandidates[0];
|
||||
const browserExports = exports.filter(
|
||||
(entry) =>
|
||||
entry.execution.kind !== "remote" &&
|
||||
@@ -258,7 +265,13 @@ function assembleExecutionInstallation(
|
||||
);
|
||||
if (!browserExports.length) {
|
||||
if (selection.browser) fail(revision, "Package has no browser exports to select");
|
||||
packages.push({ revision, root, ...(service ? { service } : {}), ...(module ? { module } : {}) });
|
||||
packages.push({
|
||||
revision,
|
||||
root,
|
||||
...(service ? { service } : {}),
|
||||
...(module ? { module } : {}),
|
||||
...(restricted ? { restricted } : {}),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const browser = selectArtifactImplementation(manifest, {
|
||||
@@ -289,6 +302,7 @@ function assembleExecutionInstallation(
|
||||
root,
|
||||
...(service ? { service } : {}),
|
||||
...(module ? { module } : {}),
|
||||
...(restricted ? { restricted } : {}),
|
||||
browserImplementation: browser,
|
||||
browserURL: contentArtifactUrl(entry),
|
||||
browserRegistry: browser.registryExport,
|
||||
|
||||
@@ -23,6 +23,39 @@ export interface ImportFinding {
|
||||
readonly detail: string;
|
||||
readonly line: number | null;
|
||||
}
|
||||
export const restrictedSDKSpecifier = "quixos:restricted-sdk/v1";
|
||||
export const restrictedSDKPath = "share/quixos/restricted/core.mjs";
|
||||
|
||||
/** Mechanical candidate checks, not authorization or an engine-safety proof.
|
||||
* The caller must first verify the manifest and all declared file digests. */
|
||||
export async function inspectRestrictedImplementation(
|
||||
revision: string,
|
||||
implementation: ArtifactImplementation,
|
||||
read: ArtifactReader,
|
||||
qualifiedSDKDigest?: string,
|
||||
): Promise<ImportFinding[]> {
|
||||
const entry = implementation.files.find((file) => file.path === implementation.path);
|
||||
const sdk = implementation.files.find((file) => file.path === restrictedSDKPath);
|
||||
if (
|
||||
implementation.compatibility !== "quixos-restricted-module-v1" ||
|
||||
implementation.target !== "server" ||
|
||||
implementation.files.length !== 2 ||
|
||||
!entry ||
|
||||
!sdk ||
|
||||
entry === sdk ||
|
||||
entry.mediaType !== "text/javascript" ||
|
||||
sdk.mediaType !== "text/javascript" ||
|
||||
!/^[$A-Z_a-z][$\w]*$/.test(implementation.registryExport ?? "")
|
||||
)
|
||||
return [{ code: "RESTRICTED_CLOSURE", detail: "Expected one closed module and the restricted SDK", line: null }];
|
||||
if (qualifiedSDKDigest && sdk.digest !== qualifiedSDKDigest)
|
||||
return [{ code: "RESTRICTED_SDK_UNSUPPORTED", detail: "SDK differs from the qualified host profile", line: null }];
|
||||
if (entry.bytes > 1024 * 1024 || sdk.bytes > 2 * 1024 * 1024)
|
||||
return [{ code: "ANALYSIS_LIMIT", detail: "Restricted closure exceeds the source budget", line: null }];
|
||||
return inspectJavaScriptSurface(Buffer.from(await read(revision, entry.path, entry.bytes)).toString("utf8"), [
|
||||
restrictedSDKSpecifier,
|
||||
]);
|
||||
}
|
||||
const nativeImports = new Set(builtinModules.flatMap((name) => [name, name.replace(/^node:/, "")]));
|
||||
const ambient = new Set([
|
||||
"process",
|
||||
@@ -136,10 +169,8 @@ export function inspectJavaScriptSurface(source: string, checkedSDKImports: read
|
||||
return findings;
|
||||
}
|
||||
|
||||
/** Derived metadata only. Browser/server portability and available targets say
|
||||
* nothing about sandbox eligibility. Build reports have no platform approval
|
||||
* catalog, so they conservatively require VM placement. A host can override
|
||||
* placement only for independently reviewed, exact restricted-code approvals. */
|
||||
/** Derived metadata only. A build report can describe a closed candidate but
|
||||
* cannot qualify the host's engine, SDK, OS sandbox or resource controller. */
|
||||
export async function reportExecutionPlacement(
|
||||
plan: ExecutionPlan,
|
||||
installation: ExecutionInstallation,
|
||||
@@ -156,11 +187,13 @@ export async function reportExecutionPlacement(
|
||||
const packages = await Promise.all(
|
||||
installation.packages.map(async (pkg) => {
|
||||
const implementations: { id: string; digest: string; compatibility: string; findings: ImportFinding[] }[] = [];
|
||||
for (const implementation of [pkg.service, pkg.module].filter(
|
||||
for (const implementation of [pkg.service, pkg.module, pkg.restricted].filter(
|
||||
(entry): entry is ArtifactImplementation => !!entry,
|
||||
)) {
|
||||
let findings: ImportFinding[];
|
||||
if (implementation.compatibility === "quixos-js-module-v1") {
|
||||
if (implementation.compatibility === "quixos-restricted-module-v1")
|
||||
findings = await inspectRestrictedImplementation(pkg.revision, implementation, read);
|
||||
else if (implementation.compatibility === "quixos-js-module-v1") {
|
||||
const file = implementation.files.find((file) => file.path === implementation.path)!;
|
||||
if (!file || file.bytes > 1024 * 1024)
|
||||
findings = [
|
||||
@@ -192,7 +225,14 @@ export async function reportExecutionPlacement(
|
||||
revision: pkg.revision,
|
||||
requiresVM: implementations.length > 0,
|
||||
restrictedEligible: false as const,
|
||||
reason: "NO_QUALIFIED_RESTRICTED_RUNTIME",
|
||||
reason: pkg.restricted ? "HOST_PROFILE_QUALIFICATION_REQUIRED" : "NO_RESTRICTED_IMPLEMENTATION",
|
||||
restrictedCandidate: pkg.restricted
|
||||
? {
|
||||
digest: pkg.restricted.contentDigest,
|
||||
exports: pkg.restricted.exports,
|
||||
compatible: implementations.find((entry) => entry.id === pkg.restricted!.id)!.findings.length === 0,
|
||||
}
|
||||
: null,
|
||||
implementations,
|
||||
};
|
||||
}),
|
||||
|
||||
@@ -5,9 +5,89 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { execFile as callback } from "node:child_process";
|
||||
import { planPinUpgrades, applyPinUpgrades, type UpgradeEffects } from "../src/capability-language/pin-upgrades.js";
|
||||
import {
|
||||
planPinUpgrades,
|
||||
applyPinUpgrades,
|
||||
reviewUpgradeCandidate,
|
||||
type UpgradeEffects,
|
||||
} from "../src/capability-language/pin-upgrades.js";
|
||||
import { capabilityId } from "../src/capability-model/index.js";
|
||||
import { makeValidCapabilityWorkspace } from "./fixtures/capability-model.js";
|
||||
const execFile = promisify(callback);
|
||||
|
||||
test("execution pin upgrades accept unchanged shared attachments without legacy migration planning", async (context) => {
|
||||
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-execution-review-"));
|
||||
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
|
||||
await fs.writeFile(path.join(artifact, "execution-plan.json"), "{}");
|
||||
const baseline = makeValidCapabilityWorkspace();
|
||||
assert.ok(baseline.sharedAttachments.length);
|
||||
const candidate = structuredClone(baseline);
|
||||
candidate.sourceRootCommit = "f".repeat(40);
|
||||
const { filename, review } = await reviewUpgradeCandidate({ artifact, baseline, candidate });
|
||||
assert.equal(filename, "semantic-review.json");
|
||||
assert.equal(review.checkerVersion, "quixos-semantic-review-v1");
|
||||
assert.deepEqual(review.blockers, []);
|
||||
assert.equal("migrationRequired" in review, false);
|
||||
assert.equal("runtimeActions" in review, false);
|
||||
});
|
||||
|
||||
test("execution pin upgrades require exact semantic-major receipts and reject foreign workspaces", async (context) => {
|
||||
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-semantic-review-"));
|
||||
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
|
||||
await fs.writeFile(path.join(artifact, "execution-plan.json"), "{}");
|
||||
const baseline = makeValidCapabilityWorkspace();
|
||||
baseline.conformances[0]!.id = capabilityId.conformance("conformance:project:named");
|
||||
const candidate = structuredClone(baseline);
|
||||
candidate.conformances[0]!.semanticMajor = 2;
|
||||
const { review } = await reviewUpgradeCandidate({ artifact, baseline, candidate });
|
||||
assert.equal(review.reviews.length, 1);
|
||||
assert.equal(review.reviews[0]!.accepted, false);
|
||||
assert.match(review.blockers[0]!, /Semantic-major review required/);
|
||||
const receipt = {
|
||||
requirementDigest: review.reviews[0]!.requirementDigest,
|
||||
decision: "accepted-unchanged" as const,
|
||||
rationale: "Reviewed the consumer against the provider's unchanged label behavior",
|
||||
agentId: "upgrade-fixture-agent",
|
||||
};
|
||||
assert.deepEqual(
|
||||
(await reviewUpgradeCandidate({ artifact, baseline, candidate, reviews: [receipt] })).review.blockers,
|
||||
[],
|
||||
);
|
||||
candidate.packageImports[0]!.source.commit = "f".repeat(40);
|
||||
assert.equal(
|
||||
(await reviewUpgradeCandidate({ artifact, baseline, candidate, reviews: [receipt] })).review.reviews[0]!.accepted,
|
||||
false,
|
||||
);
|
||||
candidate.workspaceId = capabilityId.workspace("workspace:foreign");
|
||||
await assert.rejects(() => reviewUpgradeCandidate({ artifact, baseline, candidate }), /different workspace/);
|
||||
});
|
||||
|
||||
test("nonexecution pin upgrades retain their evolution checks", async (context) => {
|
||||
const artifact = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-evolution-review-"));
|
||||
context.after(() => fs.rm(artifact, { recursive: true, force: true }));
|
||||
const baseline = makeValidCapabilityWorkspace();
|
||||
const { filename, review } = await reviewUpgradeCandidate({
|
||||
artifact,
|
||||
baseline,
|
||||
candidate: structuredClone(baseline),
|
||||
});
|
||||
assert.equal(filename, "evolution.json");
|
||||
assert.equal(review.checkerVersion, "quixos-evolution-v1");
|
||||
assert.ok(review.blockers.some((message) => message.includes("legacy workspace-shared attachments")));
|
||||
assert.equal("migrationRequired" in review, true);
|
||||
});
|
||||
|
||||
test("unexpected artifact access failures do not select nonexecution checks", async (context) => {
|
||||
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "qx-upgrade-review-failure-"));
|
||||
context.after(() => fs.rm(directory, { recursive: true, force: true }));
|
||||
const artifact = path.join(directory, "not-a-directory");
|
||||
await fs.writeFile(artifact, "not an artifact");
|
||||
const candidate = makeValidCapabilityWorkspace();
|
||||
await assert.rejects(() => reviewUpgradeCandidate({ artifact, baseline: candidate, candidate }), {
|
||||
code: "ENOTDIR",
|
||||
});
|
||||
});
|
||||
|
||||
test(
|
||||
"real jj snapshots and immutable Git publication propagate a changed interface into the root",
|
||||
{ skip: !process.env.QX_CHECK_GENERATOR },
|
||||
|
||||
Reference in New Issue
Block a user