Initial curated projectbrain source from 8a7b1e0651dbdab97d744cbd2e653ab3970ac6c5
This commit is contained in:
commit
5aa3e8d685
700 files changed
+132801
No files matched your search
@@ -0,0 +1,99 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { AutonomyProjectRuntime } from "../src/services/autonomyRuntime.js";
|
||||
import { buildAutoHumanAttentionPriority, PORTFOLIO_CATALOG_CACHE_MS, shouldRefreshPortfolioCatalog, summarizeDeliveryActivity, summarizeLiveExecution } from "../src/services/portfolioControlPlane.js";
|
||||
|
||||
const autoProject = (patch: Partial<AutonomyProjectRuntime> = {}): AutonomyProjectRuntime => ({
|
||||
projectId: "project-db-id",
|
||||
projectSlug: "demo",
|
||||
projectName: "Demo",
|
||||
enabled: true,
|
||||
status: "running",
|
||||
lifecycle: "ready",
|
||||
pendingWorkCount: 3,
|
||||
completedWorkCount: 1,
|
||||
maxWorkItemsPerRun: 5,
|
||||
maxRunMinutes: 30,
|
||||
stalled: false,
|
||||
...patch,
|
||||
});
|
||||
|
||||
describe("portfolio delivery activity semantics", () => {
|
||||
it("counts stored delivery phases separately from executor liveness", () => {
|
||||
expect(summarizeDeliveryActivity({
|
||||
draft: 2,
|
||||
approved: 3,
|
||||
executing: 4,
|
||||
validating: 5,
|
||||
pull_request_open: 6,
|
||||
merged: 7,
|
||||
verified: 8,
|
||||
released: 9,
|
||||
})).toEqual({
|
||||
active: 9,
|
||||
queued: 5,
|
||||
waitingExternal: 6,
|
||||
mergedAwaitingVerification: 7,
|
||||
completed: 17,
|
||||
});
|
||||
});
|
||||
|
||||
it("defaults missing delivery buckets to zero", () => {
|
||||
expect(summarizeDeliveryActivity({})).toEqual({
|
||||
active: 0,
|
||||
queued: 0,
|
||||
waitingExternal: 0,
|
||||
mergedAwaitingVerification: 0,
|
||||
completed: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("confirmed execution", () => {
|
||||
const now = Date.parse("2026-09-09T12:00:00Z");
|
||||
const working = autoProject({ activeWorkKind: "implement", activeRunId: "run-1", executorRunId: "run-1", executorStatus: "working", executorLastSeenAt: new Date(now - 1000).toISOString(), leaseExpiresAt: new Date(now + 60000).toISOString() });
|
||||
it("requires enabled state, valid lease and fresh working heartbeat together", () => {
|
||||
expect(summarizeLiveExecution([working], 120, now).verifiedActiveProjects).toBe(1);
|
||||
for (const patch of [{ enabled: false }, { executorStatus: "stale" as const }, { executorStatus: "idle" as const }, { activeWorkKind: undefined }, { leaseExpiresAt: new Date(now).toISOString() }, { executorLastSeenAt: new Date(now - 121000).toISOString() }, { executorLastSeenAt: "invalid" }, { executorLastSeenAt: new Date(now + 1000).toISOString() }]) {
|
||||
expect(summarizeLiveExecution([{ ...working, ...patch }], 120, now).verifiedActiveProjects).toBe(0);
|
||||
}
|
||||
});
|
||||
it("distinguishes paused automation from orphaned claims", () => {
|
||||
expect(summarizeLiveExecution([{ ...working, executorRunId: "different-run" }], 120, now).verifiedActiveProjects).toBe(0);
|
||||
expect(summarizeLiveExecution([{ ...working, activeRunId: undefined }], 120, now).verifiedActiveProjects).toBe(0);
|
||||
expect(summarizeLiveExecution([{ ...working, enabled: false }], 120, now)).toMatchObject({ verifiedActiveProjects: 0, enabledProjects: 0, unconfirmedLeaseProjects: 0 });
|
||||
expect(summarizeLiveExecution([{ ...working, executorStatus: "offline" }], 120, now).unconfirmedLeaseProjects).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("portfolio Auto human attention", () => {
|
||||
it("surfaces true human Auto blockers as portfolio attention", () => {
|
||||
const priority = buildAutoHumanAttentionPriority([
|
||||
autoProject({ status: "blocked", blockerKind: "human", blockerReason: "Owner decision required" }),
|
||||
autoProject({ projectId: "retry-db", projectSlug: "retry", projectName: "Retry", status: "blocked", blockerKind: "retryable", blockerReason: "Runner timeout" }),
|
||||
]);
|
||||
|
||||
expect(priority).toMatchObject({
|
||||
id: "auto-human-blockers",
|
||||
projectId: "project-db-id",
|
||||
requiresHumanAttention: true,
|
||||
count: 1,
|
||||
route: "/projects/demo/attention",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not turn retryable or disabled Auto state into human attention", () => {
|
||||
expect(buildAutoHumanAttentionPriority([
|
||||
autoProject({ status: "blocked", blockerKind: "retryable" }),
|
||||
autoProject({ enabled: false, status: "blocked", blockerKind: "human" }),
|
||||
])).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("portfolio catalog freshness", () => {
|
||||
it("serves a recent durable catalog before asking Gitea for another inventory", () => {
|
||||
const now = Date.parse("2026-09-13T12:00:00.000Z");
|
||||
expect(shouldRefreshPortfolioCatalog(new Date(now - PORTFOLIO_CATALOG_CACHE_MS), now)).toBe(false);
|
||||
expect(shouldRefreshPortfolioCatalog(new Date(now - PORTFOLIO_CATALOG_CACHE_MS - 1), now)).toBe(true);
|
||||
expect(shouldRefreshPortfolioCatalog(undefined, now)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user