fix(report): accept min host version floors (#17)
* fix(report): accept min host version floors * fix(inspector): treat bundled channel entries as channel coverage * fix(inspector): classify bundled channel probes * fix(policy): allow wildcard seam rules
This commit is contained in:
parent
2eda65a8a9
commit
06cc55ce51
@ -241,7 +241,9 @@ function executionChecks(executionResults, policy, options) {
|
||||
}
|
||||
|
||||
function findPolicyMatch(rules, item) {
|
||||
return rules.find((rule) => item.seam === rule.seam && item.reason?.includes(rule.reasonIncludes));
|
||||
return rules.find(
|
||||
(rule) => (rule.seam === "*" || item.seam === rule.seam) && item.reason?.includes(rule.reasonIncludes),
|
||||
);
|
||||
}
|
||||
|
||||
function failedExecutionEvidence(executionResults) {
|
||||
|
||||
@ -290,7 +290,7 @@ export function classifyPackageContracts({ fixture, inspection, fixtureReport })
|
||||
fixture: fixture.id,
|
||||
code: "package-min-host-version-drift",
|
||||
level: "warning",
|
||||
message: "package openclaw.install.minHostVersion does not match the target OpenClaw build version",
|
||||
message: "package openclaw.install.minHostVersion is not a semver floor for the target OpenClaw build version",
|
||||
evidence: [
|
||||
`minHostVersion:${packageSummary.openclaw.install.minHostVersion}`,
|
||||
`buildOpenClawVersion:${packageSummary.openclaw.buildOpenClawVersion}`,
|
||||
@ -300,7 +300,7 @@ export function classifyPackageContracts({ fixture, inspection, fixtureReport })
|
||||
fixture: fixture.id,
|
||||
decision: "plugin-upstream-fix",
|
||||
seam: "package-metadata",
|
||||
action: "Ask the plugin to keep install.minHostVersion aligned with the OpenClaw package surface it targets.",
|
||||
action: "Ask the plugin to publish install.minHostVersion as a semver floor for the OpenClaw package surface it targets.",
|
||||
evidence: packageSummary.path,
|
||||
});
|
||||
}
|
||||
@ -1090,11 +1090,15 @@ function packageNpmPackMissingMetadata(packageSummary, fixtureReport) {
|
||||
|
||||
function packageMinHostVersionDrift(packageSummary) {
|
||||
const openclaw = packageSummary.openclaw;
|
||||
return (
|
||||
nonEmptyString(openclaw?.install?.minHostVersion) &&
|
||||
nonEmptyString(openclaw?.buildOpenClawVersion) &&
|
||||
openclaw.install.minHostVersion !== openclaw.buildOpenClawVersion
|
||||
);
|
||||
if (!nonEmptyString(openclaw?.install?.minHostVersion) || !nonEmptyString(openclaw?.buildOpenClawVersion)) {
|
||||
return false;
|
||||
}
|
||||
return parseMinHostVersionFloor(openclaw.install.minHostVersion) !== openclaw.buildOpenClawVersion;
|
||||
}
|
||||
|
||||
function parseMinHostVersionFloor(value) {
|
||||
const match = /^>=([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/.exec(value);
|
||||
return match?.[1] ?? null;
|
||||
}
|
||||
|
||||
function repoPathIncludedInNpmPack(packageSummary, repoPath) {
|
||||
|
||||
@ -12,7 +12,7 @@ import { buildCompatibilityReport, buildReport } from "./report.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const registrationEquivalents = new Map([
|
||||
["registerChannel", new Set(["createChatChannelPlugin", "defineChannelPluginEntry", "registerChannel"])],
|
||||
["registerChannel", new Set(["createChatChannelPlugin", "defineBundledChannelEntry", "defineChannelPluginEntry", "registerChannel"])],
|
||||
]);
|
||||
|
||||
export async function inspectFixtureSet(config, options = {}) {
|
||||
@ -147,6 +147,7 @@ export function inspectSourceText(text, filePath = "source.js") {
|
||||
const hooks = collectDetailedMatches(searchableText, /\bapi\.on\(\s*["'`]([^"'`]+)["'`]/g, filePath, "name");
|
||||
const registrations = [
|
||||
...collectDetailedMatches(searchableText, /\bapi\.(register[A-Za-z0-9]+)\s*\(/g, filePath, "name"),
|
||||
...collectDetailedMatches(searchableText, /\b(defineBundledChannelEntry)\s*\(/g, filePath, "name"),
|
||||
...collectDetailedMatches(searchableText, /\b(defineChannelPluginEntry)\s*\(/g, filePath, "name"),
|
||||
...collectDetailedMatches(searchableText, /\b(createChatChannelPlugin)\s*\(/g, filePath, "name"),
|
||||
...collectDetailedMatches(searchableText, /\b(definePluginEntry)\s*\(/g, filePath, "name"),
|
||||
|
||||
@ -11,6 +11,11 @@ export const syntheticRegistrationExecutionProfiles = {
|
||||
callableProperties: [],
|
||||
reason: "entry wrapper metadata is captured before channel runtime execution",
|
||||
},
|
||||
defineBundledChannelEntry: {
|
||||
mode: "metadata-only",
|
||||
callableProperties: [],
|
||||
reason: "bundled channel entry metadata is captured before channel runtime execution",
|
||||
},
|
||||
definePluginEntry: {
|
||||
mode: "metadata-only",
|
||||
callableProperties: [],
|
||||
|
||||
@ -63,6 +63,41 @@ test("ci policy allows known blocked probes but fails unknown blockers", () => {
|
||||
assert.match(renderCiPolicyMarkdown(report), /Plugin Inspector CI Policy/);
|
||||
});
|
||||
|
||||
test("ci policy supports wildcard seam rules for generated surface blockers", () => {
|
||||
const report = buildCiPolicyReport({
|
||||
policy: {
|
||||
...policy,
|
||||
allowedBlocked: [
|
||||
...policy.allowedBlocked,
|
||||
{
|
||||
id: "generated-surface-runtime-gap",
|
||||
seam: "*",
|
||||
reasonIncludes: "generated surface has no callable runtime",
|
||||
decision: "allowed-blocked",
|
||||
until: "generated surface runtime harness lands",
|
||||
},
|
||||
],
|
||||
},
|
||||
compatibilityReport: compatibilityReport(),
|
||||
executionResults: executionResults([
|
||||
{
|
||||
seam: "before_tool_call",
|
||||
reason: "generated surface has no callable runtime",
|
||||
},
|
||||
{
|
||||
seam: "registerCommand",
|
||||
reason: "generated surface has no callable runtime",
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
assert.equal(report.status, "pass");
|
||||
assert.deepEqual(
|
||||
report.checks.filter((check) => check.id.startsWith("execution-results.blocked.")).map((check) => check.action),
|
||||
["warn", "warn"],
|
||||
);
|
||||
});
|
||||
|
||||
test("ci policy fails ref diff hard regressions", () => {
|
||||
const report = buildCiPolicyReport({
|
||||
policy,
|
||||
|
||||
@ -84,8 +84,10 @@ test("fixture set inspection treats channel factories as channel registration co
|
||||
path.join(dir, "fixture", "index.js"),
|
||||
[
|
||||
'import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";',
|
||||
'import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";',
|
||||
"",
|
||||
"export const channel = createChatChannelPlugin({ id: 'fixture-channel' });",
|
||||
"export default defineBundledChannelEntry({ id: 'bundled-channel' });",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
@ -110,7 +112,7 @@ test("fixture set inspection treats channel factories as channel registration co
|
||||
|
||||
assert.equal(report.status, "pass");
|
||||
assert.deepEqual(report.breakages, []);
|
||||
assert.deepEqual(report.fixtures[0].registrations, ["createChatChannelPlugin"]);
|
||||
assert.deepEqual(report.fixtures[0].registrations, ["createChatChannelPlugin", "defineBundledChannelEntry"]);
|
||||
});
|
||||
|
||||
test("capture entrypoint imports a local fixture and records registrations", async () => {
|
||||
|
||||
@ -538,7 +538,7 @@ test("compatibility fixture summary reads manifests and OpenClaw package metadat
|
||||
clawhubSpec: "clawhub:@openclaw/fixture-plugin",
|
||||
npmSpec: "@openclaw/fixture-plugin",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: "2026.5.2",
|
||||
minHostVersion: ">=2026.5.2",
|
||||
},
|
||||
release: {
|
||||
publishToClawHub: true,
|
||||
@ -601,7 +601,7 @@ test("compatibility fixture summary reads manifests and OpenClaw package metadat
|
||||
clawhubSpec: "clawhub:@openclaw/fixture-plugin",
|
||||
npmSpec: "@openclaw/fixture-plugin",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: "2026.5.2",
|
||||
minHostVersion: ">=2026.5.2",
|
||||
});
|
||||
assert.deepEqual(report.package.openclaw.release, {
|
||||
publishToClawHub: true,
|
||||
@ -737,7 +737,7 @@ test("package contract classifier reports broken install and release metadata",
|
||||
clawhubSpec: null,
|
||||
npmSpec: "fixture-plugin",
|
||||
defaultChoice: "clawhub",
|
||||
minHostVersion: "2026.5.1",
|
||||
minHostVersion: ">=2026.5.1",
|
||||
},
|
||||
release: {
|
||||
publishToClawHub: true,
|
||||
|
||||
@ -111,6 +111,7 @@ test("synthetic probe plan classifies generated kitchen-sink registrars", () =>
|
||||
"registerAgentToolResultMiddleware",
|
||||
"registerAutoEnableProbe",
|
||||
"registerChannel",
|
||||
"defineBundledChannelEntry",
|
||||
"registerCli",
|
||||
"registerCliBackend",
|
||||
"registerCodexAppServerExtensionFactory",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user