feat(observatory): verify exact published result reuse

This commit is contained in:
DCCONSTRUCTIONS
2026-09-03 09:14:54 +03:00
parent 80fc0058cb
commit 1e4ddc2cff
10 changed files with 910 additions and 42 deletions
@@ -1,4 +1,5 @@
import type {
ObservatoryLaboratoryPreservedResult,
ObservatoryLaboratoryRunDefinition,
ObservatoryLaboratorySetup,
ObservatoryLaboratorySetupCatalog,
@@ -23,13 +24,14 @@ export function decodePortableCatalog(value: unknown): ObservatoryLaboratorySetu
);
exact(row.schema_version, PORTABLE_CATALOG_SCHEMA, "schema_version portable-каталога");
observationAuthority(row.authority);
const sourceSessionId = text(row.source_session_id, "portable source_session_id");
return {
sourceSessionId: text(row.source_session_id, "portable source_session_id"),
setups: array(row.setups, "portable setups").map(decodePortableSetup),
sourceSessionId,
setups: array(row.setups, "portable setups").map((item) => decodePortableSetup(item, sourceSessionId)),
};
}
function decodePortableSetup(value: unknown): ObservatoryLaboratorySetup {
function decodePortableSetup(value: unknown, sourceSessionId: string): ObservatoryLaboratorySetup {
const row = record(value, "portable-сетап");
exactKeys(row, [
"authority", "description", "display_name", "executor", "existing_results",
@@ -39,6 +41,8 @@ function decodePortableSetup(value: unknown): ObservatoryLaboratorySetup {
exact(row.origin, "portable-definition", "portable origin");
observationAuthority(row.authority);
decodePortableSourceRequirements(row.source_requirements);
const setupId = text(row.setup_id, "portable setup_id");
const runDefinition = decodePortableRunDefinition(row.run_definition);
const compatibility = record(row.source_compatibility, "portable source_compatibility");
exactKeys(
@@ -96,12 +100,12 @@ function decodePortableSetup(value: unknown): ObservatoryLaboratorySetup {
], "portable preflight");
const preflightOutcome = oneOf(
preflight.outcome,
["ready", "blocked"] as const,
["existing", "ready", "blocked"] as const,
"portable preflight outcome",
);
const preflightAction = oneOf(
preflight.action,
["check", "blocked"] as const,
["open-existing", "check", "blocked"] as const,
"portable preflight action",
);
const submissionAllowed = boolean(
@@ -112,28 +116,37 @@ function decodePortableSetup(value: unknown): ObservatoryLaboratorySetup {
submissionAllowed !== (preflightOutcome === "ready")
|| (preflightOutcome === "ready" && preflightAction !== "check")
|| (preflightOutcome === "blocked" && preflightAction !== "blocked")
|| (preflightOutcome === "existing" && preflightAction !== "open-existing")
|| (submissionAllowed && (!compatible || !executorReady))
) {
throw new ObservatoryPortableSetupDecodeError(
"Portable preflight: состояние запуска противоречиво.",
);
}
const existingResults = array(row.existing_results, "portable existing_results");
const existingResults = array(row.existing_results, "portable existing_results").map(
(item) => decodePortableResult(item, sourceSessionId, setupId, runDefinition),
);
const existingResultIds = array(
preflight.existing_result_ids,
"portable existing_result_ids",
);
if (existingResults.length > 0 || existingResultIds.length > 0) {
).map((item) => text(item, "portable existing result id"));
if (
(existingResults.length > 0) !== (preflightOutcome === "existing")
|| existingResultIds.length !== existingResults.length
|| new Set(existingResultIds).size !== existingResultIds.length
|| existingResults.some((result, index) => result.resultId !== existingResultIds[index])
) {
throw new ObservatoryPortableSetupDecodeError(
"Portable result: проверяемая привязка результата к RunDefinition ещё не поддерживается.",
"Portable result: готовность не соответствует проверенным результатам.",
);
}
return {
setupId: text(row.setup_id, "portable setup_id"),
setupId,
displayName: text(row.display_name, "portable display_name"),
description: text(row.description, "portable description"),
origin: "portable-definition",
runDefinition: decodePortableRunDefinition(row.run_definition),
runDefinition,
compatibility: {
compatible,
reasons: compatible
@@ -146,17 +159,58 @@ function decodePortableSetup(value: unknown): ObservatoryLaboratorySetup {
reasonCode: executorReasonCode,
reason: executorReason,
},
preservedResults: [],
preservedResults: existingResults,
preflight: {
outcome: preflightOutcome,
action: preflightAction,
reason: text(preflight.reason, "portable preflight reason"),
submissionAllowed,
existingResultIds: [],
existingResultIds,
},
};
}
function decodePortableResult(
value: unknown, sourceSessionId: string, setupId: string,
definition: ObservatoryLaboratoryRunDefinition,
): ObservatoryLaboratoryPreservedResult {
const row = record(value, "portable result");
exactKeys(row, [
"result_id", "result_kind", "relation", "access", "created_at_utc",
"observatory_projection_available", "identity",
], "portable result");
exact(row.result_kind, definition.resultKind, "portable result kind");
exact(row.relation, "exact-recorded-computation", "portable result relation");
exact(row.access, "observatory", "portable result access");
exact(row.observatory_projection_available, true, "portable result availability");
const identity = record(row.identity, "portable result identity");
exactKeys(identity, [
"job_id", "source_session_id", "source_catalog_sha256", "source_bundle_sha256",
"source_capability_manifest_sha256", "setup_id", "definition_sha256",
"package_sha256", "artifact_manifest_id",
], "portable result identity");
exact(identity.source_session_id, sourceSessionId, "portable result source");
exact(identity.setup_id, setupId, "portable result setup");
exact(identity.definition_sha256, definition.definitionSha256, "portable result definition");
text(identity.job_id, "portable result job");
for (const key of [
"source_catalog_sha256", "source_bundle_sha256", "source_capability_manifest_sha256",
"definition_sha256", "package_sha256", "artifact_manifest_id",
]) {
if (!SHA256.test(text(identity[key], `portable result ${key}`))) {
throw new ObservatoryPortableSetupDecodeError(`Portable result: некорректный ${key}.`);
}
}
return {
resultId: text(row.result_id, "portable result id"),
resultKind: text(row.result_kind, "portable result kind"),
relation: "exact-recorded-computation",
access: "observatory",
createdAtUtc: text(row.created_at_utc, "portable result created_at_utc"),
observatoryProjectionAvailable: true,
};
}
function decodePortableSourceRequirements(value: unknown): void {
const row = record(value, "portable source_requirements");
exactKeys(row, [
@@ -323,7 +323,7 @@ test("portable LAB V1 rejects an unbound existing result projection", async () =
authority,
}), { status: 200 }),
}),
/значение изменилось|значение не поддерживается|проверяемая привязка результата/,
/значение изменилось|значение не поддерживается|обнаружены неизвестные поля/,
);
});
@@ -348,6 +348,76 @@ test("portable LAB V1 rejects heavyweight compatibility evidence", async () => {
);
});
function cachedPortableSetup() {
const setup = portableSetup();
setup.existing_results = [{
result_id: "portable-result-001",
result_kind: setup.run_definition.result_kind,
relation: "exact-recorded-computation",
access: "observatory",
created_at_utc: "2026-09-03T00:00:00Z",
observatory_projection_available: true,
identity: {
job_id: "observatory-run-001",
source_session_id: "source-a",
source_catalog_sha256: "a".repeat(64),
source_bundle_sha256: "b".repeat(64),
source_capability_manifest_sha256: "c".repeat(64),
setup_id: setup.setup_id,
definition_sha256: setup.run_definition.definition_sha256,
package_sha256: "d".repeat(64),
artifact_manifest_id: "e".repeat(64),
},
}];
setup.preflight = {
outcome: "existing", action: "open-existing",
reason: "Точный результат проверен.", submission_allowed: false,
existing_result_ids: [setup.existing_results[0].result_id],
};
return setup;
}
function fetchCachedPortable(setup) {
return fetchObservatoryPortableLaboratorySetups("source-a", {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.observatory-portable-setup-catalog/v2",
source_session_id: "source-a", setups: [setup], authority,
}), { status: 200 }),
});
}
test("portable exact cached result is readable without an installed executor", async () => {
const catalog = await fetchCachedPortable(cachedPortableSetup());
const setup = catalog.setups[0];
assert.equal(setup.executor.state, "not-installed");
assert.equal(setup.preflight.outcome, "existing");
assert.equal(setup.preflight.submissionAllowed, false);
assert.deepEqual(setup.preflight.existingResultIds, ["portable-result-001"]);
assert.equal(setup.preservedResults[0].access, "observatory");
});
for (const [label, change] of [
["another source", (s) => { s.existing_results[0].identity.source_session_id = "source-b"; }],
["another setup", (s) => { s.existing_results[0].identity.setup_id = "another-profile"; }],
["another version", (s) => { s.existing_results[0].identity.definition_sha256 = "f".repeat(64); }],
["bad package digest", (s) => { s.existing_results[0].identity.package_sha256 = "not-a-digest"; }],
["unavailable artifact", (s) => { s.existing_results[0].observatory_projection_available = false; }],
["missing binding", (s) => { delete s.existing_results[0].identity; }],
["unrelated result ID", (s) => { s.preflight.existing_result_ids = ["different-result"]; }],
["duplicate result", (s) => {
s.existing_results.push(s.existing_results[0]);
s.preflight.existing_result_ids.push(s.preflight.existing_result_ids[0]);
}],
["contradictory action", (s) => { s.preflight.action = "check"; }],
["cached but queueable", (s) => { s.preflight.submission_allowed = true; }],
]) {
test(`portable cached result rejects ${label}`, async () => {
const setup = cachedPortableSetup();
change(setup);
await assert.rejects(fetchCachedPortable(setup));
});
}
test("Observatory preflight sends the exact selected definition and never submits a run", async () => {
const selected = (await fetchObservatoryLaboratorySetups("source-a", {
fetcher: async () => new Response(JSON.stringify({