fix(audit): retain exact model fallback reason

This commit is contained in:
joshavant
2026-08-25 03:48:15 -05:00
parent 3e6c8c92a4
commit 548d3405f9
5 changed files with 13 additions and 12 deletions
+2 -2
View File
@@ -277,7 +277,7 @@ describe("prepareCliRunContext", () => {
requestedProvider: "openai",
requestedModel: "mock-1",
stage: "fallback",
fallbackReason: "overloaded",
fallbackReason: "rate_limit",
},
}),
).finally(preparedRunAdmission.close);
@@ -285,7 +285,7 @@ describe("prepareCliRunContext", () => {
expect(decisionWork).toHaveLength(1);
expect(decisionWork[0]?.receipt).toMatchObject({
action: { summary: "Requested openai/mock-1; selected test-cli/mock-2." },
decision: { reasonCode: "model_route_selected_after_fallback" },
decision: { reasonCode: "rate_limit" },
});
});
@@ -226,8 +226,9 @@ export async function prepareAndDispatchEmbeddedRunAttempt(input: {
selectionMode:
runtime.lastProfileId && runtime.lastProfileId === lockedProfileId ? "explicit" : "automatic",
credentialProfileId: runtime.lastProfileId,
fallbackSelected: params.modelRoutingProvenance?.stage === "fallback",
fallbackReason: params.modelRoutingProvenance?.fallbackReason ?? fallbackReason,
fallbackSelected:
params.modelRoutingProvenance?.stage === "fallback" || Boolean(fallbackReason),
fallbackReason: params.modelRoutingProvenance?.fallbackReason,
});
const dispatchedAttempt = await dispatchEmbeddedRunAttempt({
params,
@@ -563,7 +563,7 @@ describe("runWithModelFallback + runEmbeddedAgent failover behavior", () => {
},
{
action: { summary: "Requested openai/mock-1; selected groq/mock-2." },
decision: { reasonCode: "model_route_selected_after_fallback" },
decision: { reasonCode: "overloaded" },
},
]);
expect(observedModelRoutingProvenance).toMatchObject([
+1 -1
View File
@@ -45,7 +45,7 @@ describe("admitted model routing decisions", () => {
token,
receipt: {
action: { family: "model-routing", operation: "explicit-selection" },
decision: { reasonCode: "model_route_selected_after_fallback" },
decision: { reasonCode: "rate_limit" },
enforcement: { coverageState: "attribution-only" },
},
refs: {
+6 -6
View File
@@ -4,6 +4,7 @@ import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { recordExecutionDecisionWork } from "../audit/execution-decision-work.js";
import type { ExecutionIdentityAdmissionToken } from "../audit/execution-identity-admission.js";
import { redactSensitiveText } from "../logging/redact.js";
import type { FailoverReason } from "./failover/signal.js";
type ModelRoutingSelectionMode = "automatic" | "explicit";
@@ -21,7 +22,7 @@ export function recordAdmittedModelRoutingDecision(params: {
selectionMode: ModelRoutingSelectionMode;
credentialProfileId?: string;
fallbackSelected?: boolean;
fallbackReason?: string | null;
fallbackReason?: FailoverReason | null;
occurredAt?: number;
}): boolean {
if (!params.token) {
@@ -32,8 +33,9 @@ export function recordAdmittedModelRoutingDecision(params: {
const selectedRef = boundedModelRef(params.selectedProvider, params.selectedModel);
const credentialProfileId = params.credentialProfileId?.trim();
const hasCredentialOwner = Boolean(credentialProfileId);
const fallbackSelected =
params.fallbackSelected === true || Boolean(params.fallbackReason?.trim());
const reasonCode =
params.fallbackReason ??
(params.fallbackSelected ? "model_route_selected_after_fallback" : "model_route_selected");
return recordExecutionDecisionWork({
workVersion: 1,
token: params.token,
@@ -48,9 +50,7 @@ export function recordAdmittedModelRoutingDecision(params: {
},
decision: {
outcome: "allowed",
reasonCode: fallbackSelected
? "model_route_selected_after_fallback"
: "model_route_selected",
reasonCode,
},
enforcement: {
coverageState: hasCredentialOwner ? "attribution-only" : "unknown",