|
6 | 6 | countPackageResources, |
7 | 7 | extensionEntryName, |
8 | 8 | findShadowedSkills, |
| 9 | + isFlowLoaded, |
9 | 10 | isUnderAnyRoot, |
10 | 11 | owningRoot, |
11 | 12 | partitionAdminExtensions, |
@@ -313,3 +314,35 @@ test("nothing admin-like means nothing dropped, and no input shape throws", () = |
313 | 314 | // Tools may arrive as plain names too -- the decision must not depend on pi's container type. |
314 | 315 | assert.equal(adminExtensionReason({ path: WORKSPACE_EXT, tools: ["dispatch_run"] }), "admin-tools"); |
315 | 316 | }); |
| 317 | + |
| 318 | +// --- isFlowLoaded (issue #189): the flow-resolves-somewhere check, against the LOADED set --- |
| 319 | + |
| 320 | +test("a flow that names a loaded skill is loaded, and a near-miss is not", () => { |
| 321 | + const skills = [{ name: "review", filePath: "/job/pi/skills/review/SKILL.md" }, { name: "deploy" }]; |
| 322 | + assert.equal(isFlowLoaded("review", skills), true); |
| 323 | + // Exact equality, no normalisation: pi's own catalogue is exact-name, so a check that fuzzed |
| 324 | + // ("revieww", case folds) would report loaded for a skill pi will never surface. |
| 325 | + assert.equal(isFlowLoaded("revieww", skills), false); |
| 326 | + assert.equal(isFlowLoaded("Review", skills), false); |
| 327 | +}); |
| 328 | + |
| 329 | +test("no flow means nothing to verify: null, undefined and empty all pass without a report", () => { |
| 330 | + // A bare run.task cron job carries no flow. That is the normal state, not a miss -- reporting it |
| 331 | + // would teach operators to ignore the line that matters. |
| 332 | + for (const flow of [null, undefined, ""]) { |
| 333 | + assert.equal(isFlowLoaded(flow, []), true, `flow ${JSON.stringify(flow)} must not report`); |
| 334 | + } |
| 335 | +}); |
| 336 | + |
| 337 | +test("a disableModelInvocation skill still counts as loaded", () => { |
| 338 | + // It is absent from the system-prompt catalogue but present in the session (invocable as |
| 339 | + // /skill:name), so the flow it names is not the silent no-op this check exists to catch. |
| 340 | + assert.equal(isFlowLoaded("quiet", [{ name: "quiet", disableModelInvocation: true }]), true); |
| 341 | +}); |
| 342 | + |
| 343 | +test("malformed skill entries and an absent list are skipped, never a crash", () => { |
| 344 | + // This runs inside every job; a diagnostic input must not take the job down with it. |
| 345 | + assert.equal(isFlowLoaded("review", [null, {}, { filePath: "/x" }]), false); |
| 346 | + assert.equal(isFlowLoaded("review", undefined), false); |
| 347 | + assert.equal(isFlowLoaded("review", []), false); |
| 348 | +}); |
0 commit comments