mirror of
https://gh-proxy.org/https://github.com/klinsmaya/multica.git
synced 2026-11-09 18:06:45 +08:00
* fix(agents): exclude cancelled runs from success metrics * refactor(agents): require activity outcome counts from the back-end The previous commit carried a fallback for a new client talking to an older back-end: optional `completed_count` / `cancelled_count`, null propagation through the bucket series, and a "—" success rate when the counts were absent. Multica only supports the other direction — an older daemon or client against a newer back-end — so that branch is an unrequested legacy adapter. Outcome counts are now required at the schema boundary, so the bucket series and window summary carry plain numbers. A response without them is contract drift and `parseWithFallback` degrades the whole list to `[]`, the same as every other list endpoint. Unchanged: a window holding nothing but cancellations still has no success rate and renders "—" rather than claiming 100%, and the sparkline still draws cancellations neutral. Both are the point of the fix, not compatibility. Copy for the unavailable state now says the period has no completed or failed run, which is the only way it is reachable. Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: J <[email protected]> Co-authored-by: multica-agent <[email protected]>
46 lines
1.4 KiB
TypeScript
46 lines
1.4 KiB
TypeScript
// @vitest-environment node
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { ApiClient } from "./client";
|
|
|
|
const bucket = {
|
|
agent_id: "a",
|
|
bucket_at: "2026-09-15T00:00:00Z",
|
|
task_count: 10,
|
|
failed_count: 1,
|
|
};
|
|
|
|
afterEach(() => vi.unstubAllGlobals());
|
|
|
|
async function read(body: unknown) {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockResolvedValue(new Response(JSON.stringify(body), { status: 200 })),
|
|
);
|
|
return new ApiClient("https://api.example.test").getWorkspaceAgentActivity30d();
|
|
}
|
|
|
|
describe("agent activity API outcomes", () => {
|
|
it("preserves explicit outcome counts, including zero", async () => {
|
|
expect(await read([{ ...bucket, completed_count: 0, cancelled_count: 9 }])).toEqual([
|
|
{ ...bucket, completed_count: 0, cancelled_count: 9 },
|
|
]);
|
|
});
|
|
|
|
// Outcome counts are part of the contract, so a bucket without usable
|
|
// ones is drift, not an older peer to accommodate. Degrading the whole
|
|
// response is the intended failure mode: an empty activity panel is
|
|
// honest, a half-populated one silently misreports success.
|
|
it.each([undefined, null, "1", -1, 1.5])(
|
|
"drops the response when an outcome count is %s",
|
|
async (bad) => {
|
|
expect(
|
|
await read([{ ...bucket, completed_count: bad, cancelled_count: bad }]),
|
|
).toEqual([]);
|
|
},
|
|
);
|
|
|
|
it("falls back for a malformed list", async () => {
|
|
expect(await read({ buckets: [] })).toEqual([]);
|
|
});
|
|
});
|