mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(release): accept Kova collector-only phases (#104008)
This commit is contained in:
@@ -96,6 +96,23 @@ function validateCommandResult(value, label) {
|
||||
check(result.status === 0 && result.timedOut === false, `${label} failed`);
|
||||
}
|
||||
|
||||
function validateCollectorOnlyPhase(phase) {
|
||||
check(
|
||||
phase.driverKind === "none" && phase.collectionIntent === "post-ready-health",
|
||||
"commandless phase was not a collector-only phase",
|
||||
);
|
||||
const evidence = array(phase.evidence, "collector-only phase evidence");
|
||||
check(
|
||||
evidence.length > 0 &&
|
||||
evidence.every((entry) => typeof entry === "string" && entry.trim().length > 0),
|
||||
"collector-only phase evidence was invalid",
|
||||
);
|
||||
check(
|
||||
object(phase.metrics, "collector-only phase metrics").schemaVersion === "kova.envMetrics.v1",
|
||||
"collector-only phase metrics schema was invalid",
|
||||
);
|
||||
}
|
||||
|
||||
function alreadyAbsent(result, noun) {
|
||||
check(
|
||||
Number.isSafeInteger(result.status) && result.status !== 0,
|
||||
@@ -128,9 +145,23 @@ function validateRecord(recordValue) {
|
||||
const phases = array(record.phases, "record phases");
|
||||
check(phases.length > 0, "record had no phases");
|
||||
for (const phaseValue of phases) {
|
||||
const results = array(object(phaseValue, "phase").results, "phase results");
|
||||
check(results.length > 0, "phase had no command results");
|
||||
results.forEach((result, index) => validateCommandResult(result, `phase result ${index}`));
|
||||
const phase = object(phaseValue, "phase");
|
||||
const commands = array(phase.commands, "phase commands");
|
||||
const results = array(phase.results, "phase results");
|
||||
check(commands.length === results.length, "phase command/result counts did not match");
|
||||
if (commands.length === 0) {
|
||||
validateCollectorOnlyPhase(phase);
|
||||
continue;
|
||||
}
|
||||
results.forEach((resultValue, index) => {
|
||||
const result = object(resultValue, `phase result ${index}`);
|
||||
check(
|
||||
text(result.command, `phase result ${index} command`) ===
|
||||
text(commands[index], `phase command ${index}`),
|
||||
`phase command ${index} did not match its result`,
|
||||
);
|
||||
validateCommandResult(result, `phase result ${index}`);
|
||||
});
|
||||
}
|
||||
validateCleanup(
|
||||
record.cleanup,
|
||||
|
||||
@@ -206,7 +206,13 @@ function partialReport(): JsonObject {
|
||||
cpuPercentMax: 80,
|
||||
peakRssMb: 650,
|
||||
},
|
||||
phases: [{ id: "agent-turn", results: [commandResult()] }],
|
||||
phases: [
|
||||
{
|
||||
commands: [commandResult().command],
|
||||
id: "agent-turn",
|
||||
results: [commandResult()],
|
||||
},
|
||||
],
|
||||
profiling: normalProfiling(),
|
||||
scenario: SCENARIO,
|
||||
state: { id: STATE },
|
||||
@@ -300,7 +306,13 @@ function profiledResourceReport(): JsonObject {
|
||||
"agent-process": { maxCpuPercent: 156.2, peakRssMb: 923.7 },
|
||||
},
|
||||
},
|
||||
phases: [{ id: "agent-turn", results: [commandResult()] }],
|
||||
phases: [
|
||||
{
|
||||
commands: [commandResult().command],
|
||||
id: "agent-turn",
|
||||
results: [commandResult()],
|
||||
},
|
||||
],
|
||||
profiling: deepProfiling(),
|
||||
scenario: SCENARIO,
|
||||
state: { id: STATE },
|
||||
@@ -417,6 +429,21 @@ describe("scripts/lib/kova-report-gate.mjs", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("accepts a declared collector-only phase without commands", () => {
|
||||
const report = partialReport();
|
||||
setAt(report, ["records", 0, "phases", 0], {
|
||||
collectionIntent: "post-ready-health",
|
||||
commands: [],
|
||||
driverKind: "none",
|
||||
evidence: ["startup logs"],
|
||||
id: "logs",
|
||||
metrics: { schemaVersion: "kova.envMetrics.v1" },
|
||||
results: [],
|
||||
});
|
||||
|
||||
expect(evaluateToleratedPartialKovaReport(report)).toEqual({ ok: true });
|
||||
});
|
||||
|
||||
it("accepts an exact deep-profile resource-only rejection", () => {
|
||||
expect(evaluateToleratedProfiledKovaReport(profiledResourceReport())).toEqual({
|
||||
ok: true,
|
||||
@@ -549,6 +576,15 @@ describe("scripts/lib/kova-report-gate.mjs", () => {
|
||||
"rejects timed-out phase commands",
|
||||
(report) => setAt(report, ["records", 0, "phases", 0, "results", 0, "timedOut"], true),
|
||||
],
|
||||
[
|
||||
"rejects phase command/result count drift",
|
||||
(report) => setAt(report, ["records", 0, "phases", 0, "results"], []),
|
||||
],
|
||||
[
|
||||
"rejects phase command/result identity drift",
|
||||
(report) =>
|
||||
setAt(report, ["records", 0, "phases", 0, "results", 0, "command"], "different command"),
|
||||
],
|
||||
[
|
||||
"rejects retained record cleanup",
|
||||
(report) => setAt(report, ["records", 0, "cleanup"], "retained"),
|
||||
@@ -832,6 +868,13 @@ describe("scripts/lib/kova-report-gate.mjs", () => {
|
||||
"rejects PARTIAL phase failures",
|
||||
(report) => setAt(report, ["records", 0, "phases", 0, "results", 0, "status"], 1),
|
||||
],
|
||||
[
|
||||
"rejects unmarked commandless PARTIAL phases",
|
||||
(report) => {
|
||||
setAt(report, ["records", 0, "phases", 0, "commands"], []);
|
||||
setAt(report, ["records", 0, "phases", 0, "results"], []);
|
||||
},
|
||||
],
|
||||
[
|
||||
"rejects PARTIAL cleanup failures",
|
||||
(report) => setAt(report, ["records", 0, "cleanup"], "destroy-failed"),
|
||||
|
||||
Reference in New Issue
Block a user