Merge final budget and CI follow-ups into dashboard stack

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin FoleyandPaperclip committed 2026-10-03 21:24:20 -07:00
commit 6af7db0166
9 files changed
+17 -6

No files matched your search

+3 -1
View File
@@ -166,7 +166,9 @@ provider adapters with missing prices still do.
Budget policy updates may omit unchanged settings, including the amount for an
existing policy. Omitted settings are read under the accounting transaction lock.
Creating a policy still requires an amount. Generic agent and company budget
Creating a policy still requires an amount. Partial edits preserve an explicitly
disabled policy; the legacy monthly-cap endpoints explicitly enable a positive
cap and disable a zero cap. Generic agent and company budget
edits deliver hard-stop cancellation after their transaction commits.
Status-card update costs retain the ledger's fractional-cent storage precision.
+1
View File
@@ -9,6 +9,7 @@ const artifactDirectory = path.join(root, "coverage/accounting/mutations");
const targets = { deduplication: "deduplicates a retried receipt", company: "isolates company reporting", projection: "conserves agent projections", threshold: "stops at the exact budget boundary" };
const evidence = [];
const persist = () => writeFile(path.join(root, "coverage/accounting/mutations.json"), JSON.stringify({ generatedAt: new Date().toISOString(), evidence }, null, 2));
await rm(artifactDirectory, { recursive: true, force: true });
await mkdir(artifactDirectory, { recursive: true });
try {
for (const name of ["baseline", ...Object.keys(targets)]) {
@@ -311,6 +311,10 @@ databaseDescribe("cost accounting reliability (PostgreSQL)", () => {
await budgets.upsertPolicy(f.company.id, { ...identity, amount: 200, hardStopEnabled: false, warnPercent: 60, notifyEnabled: false }, "other-operator");
const saved = await budgets.upsertPolicy(f.company.id, upsertBudgetPolicySchema.parse({ ...identity, reservationCents: "10" }), "board");
expect(saved).toMatchObject({ amount: 200, hardStopEnabled: false, warnPercent: 60, notifyEnabled: false, reservationCents: "10.0000000" });
await budgets.upsertPolicy(f.company.id, { ...identity, isActive: false }, "other-operator");
const disabled = await budgets.upsertPolicy(f.company.id, upsertBudgetPolicySchema.parse({ ...identity, amount: 300 }), "board");
expect(await db.select({ amount: budgetPolicies.amount, isActive: budgetPolicies.isActive }).from(budgetPolicies).where(eq(budgetPolicies.id, disabled.policyId))).toEqual([{ amount: 300, isActive: false }]);
expect(disabled).toMatchObject({ amount: 0, isActive: false, hardStopEnabled: false, warnPercent: 60, notifyEnabled: false, reservationCents: "10.0000000" });
await expect(budgets.upsertPolicy(f.company.id, { scopeType: "project", scopeId: f.project.id, reservationCents: "1" }, "board")).rejects.toThrow("Amount is required");
});
@@ -398,6 +398,7 @@ describe("cost routes", () => {
scopeType: "agent",
scopeId: "agent-1",
amount: 2500,
isActive: true,
windowKind: "calendar_month_utc",
},
"board-user",
+3 -2
View File
@@ -279,8 +279,9 @@ export function companyRoutes(db: Db, storage?: StorageService, options?: Compan
const router = Router();
const importTransferSpoolRoot =
options?.importTransferSpoolRoot ?? resolveDefaultImportTransferSpoolRoot();
const heartbeat = heartbeatService(db);
const svc = companyService(db, { cancelWorkForScope: heartbeat.cancelBudgetScopeWork });
const svc = companyService(db, {
cancelWorkForScope: (scope) => heartbeatService(db).cancelBudgetScopeWork(scope),
});
const agents = agentService(db);
const portability = companyPortabilityService(db, storage);
const access = accessService(db);
+2
View File
@@ -325,6 +325,7 @@ export function costRoutes(
scopeType: "company",
scopeId: companyId,
amount: req.body.budgetMonthlyCents,
isActive: req.body.budgetMonthlyCents > 0,
windowKind: "calendar_month_utc",
},
req.actor.userId ?? "board",
@@ -346,6 +347,7 @@ export function costRoutes(
scopeType: "agent",
scopeId: agent.id,
amount: req.body.budgetMonthlyCents,
isActive: req.body.budgetMonthlyCents > 0,
windowKind: "calendar_month_utc",
},
req.actor.type === "board" ? req.actor.userId ?? "board" : null,
+1 -1
View File
@@ -831,7 +831,7 @@ export function agentService(db: Db, budgetHooks: BudgetServiceHooks = {}) {
if (normalizedPatch.budgetMonthlyCents !== undefined) {
await budgetServiceInTransaction(txDb, publications).upsertPolicy(existing.companyId, {
scopeType: "agent", scopeId: id, amount: normalizedPatch.budgetMonthlyCents, windowKind: "calendar_month_utc",
scopeType: "agent", scopeId: id, amount: normalizedPatch.budgetMonthlyCents, isActive: normalizedPatch.budgetMonthlyCents > 0, windowKind: "calendar_month_utc",
}, options?.recordRevision?.createdByUserId ?? null);
}
const normalizedUpdated = await agentService(txDb).getById(updated.id);
+1 -1
View File
@@ -627,7 +627,7 @@ export function budgetServiceInTransaction(db: Db, publications: ActivityPublica
if (!existing && input.amount === undefined) throw unprocessable("Amount is required for a new budget policy");
const amount = Math.max(0, Math.floor(input.amount ?? existing!.amount));
const nextIsActive = amount > 0 && (input.isActive ?? (input.amount !== undefined ? true : existing?.isActive ?? true));
const nextIsActive = amount > 0 && (input.isActive ?? existing?.isActive ?? true);
const now = new Date();
const row = existing
? await db
+1 -1
View File
@@ -439,7 +439,7 @@ export function companyService(db: Db, budgetHooks: BudgetServiceHooks = {}) {
if (data.budgetMonthlyCents !== undefined) {
await budgetServiceInTransaction(tx as unknown as Db, budgetPublications).upsertPolicy(id, {
scopeType: "company", scopeId: id, amount: data.budgetMonthlyCents, windowKind: "calendar_month_utc",
scopeType: "company", scopeId: id, amount: data.budgetMonthlyCents, isActive: data.budgetMonthlyCents > 0, windowKind: "calendar_month_utc",
}, actor.actorType === "user" ? actor.actorId : null);
const [budgetUpdated] = await tx.select().from(companies).where(eq(companies.id, id));
Object.assign(updated, budgetUpdated);