address greptile review feedback (greploop iteration 1)

Add modeMatches() helper so array guardrail_mode values (e.g.
["pre_call", "post_call"]) place the entry in all matching timeline
buckets, not just the first. Updated test to verify both buckets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
yuneng-jiang
2026-03-18 12:29:57 -07:00
co-authored by Claude Opus 4.6
parent b13a7c6790
commit 1c8b5f77c9
2 changed files with 33 additions and 10 deletions
@@ -170,14 +170,18 @@ describe("GuardrailViewer", () => {
expect(screen.getByText("PRE-CALL")).toBeInTheDocument();
});
it("renders without crashing when guardrail_mode is an array", () => {
it("renders without crashing when guardrail_mode is an array and shows in both timeline buckets", () => {
const data = makeGuardrailInformation({
guardrail_mode: ["pre_call", "post_call"],
});
renderWithProviders(<GuardrailViewer data={data} />);
expect(screen.getByText("Guardrails & Policy Compliance")).toBeInTheDocument();
// Mode badge shows first element formatted
expect(screen.getByText("PRE-CALL")).toBeInTheDocument();
// Entry should appear in both pre-call and post-call timeline sections
expect(screen.getByText(/Pre-call guardrail:/)).toBeInTheDocument();
expect(screen.getByText(/Post-call guardrail:/)).toBeInTheDocument();
});
it("integration: renders with real Bedrock details without mocks", async () => {
@@ -78,8 +78,8 @@ const PROVIDERS_WITH_CUSTOM_RENDERERS = new Set([
]);
/**
* Extracts a plain string from guardrail_mode, which may be a string,
* an array of strings, an object with a "default" key, or null.
* Extracts a plain string from guardrail_mode for display purposes.
* Returns the first mode when multiple are present.
*/
const resolveMode = (mode: GuardrailInformation["guardrail_mode"]): string | null => {
if (mode == null) return null;
@@ -93,6 +93,25 @@ const resolveMode = (mode: GuardrailInformation["guardrail_mode"]): string | nul
return null;
};
/**
* Checks whether guardrail_mode includes the given target stage.
* Handles arrays (multi-stage guardrails) by checking all elements.
*/
const modeMatches = (
mode: GuardrailInformation["guardrail_mode"],
target: string,
): boolean => {
if (mode == null) return false;
if (typeof mode === "string") return mode === target;
if (Array.isArray(mode)) return mode.includes(target);
if (typeof mode === "object" && "default" in mode) {
const def = mode.default;
if (typeof def === "string") return def === target;
if (Array.isArray(def)) return (def as string[]).includes(target);
}
return false;
};
const formatMode = (mode: GuardrailInformation["guardrail_mode"]): string => {
const s = resolveMode(mode);
if (s == null || s === "") return "—";
@@ -317,13 +336,13 @@ const RequestLifecycle = ({ entries }: { entries: GuardrailInformation[] }) => {
// Request received
items.push({ type: "request", label: "Request received", offsetMs: 0 });
// Pre-call guardrails
const preCalls = sorted.filter((e) => resolveMode(e.guardrail_mode) === "pre_call");
const postCalls = sorted.filter((e) => {
const m = resolveMode(e.guardrail_mode);
return m === "post_call" || m === "logging_only";
});
const duringCalls = sorted.filter((e) => resolveMode(e.guardrail_mode) === "during_call");
// Pre-call guardrails — use modeMatches so array modes (e.g. ["pre_call", "post_call"])
// place the entry in every matching bucket.
const preCalls = sorted.filter((e) => modeMatches(e.guardrail_mode, "pre_call"));
const postCalls = sorted.filter(
(e) => modeMatches(e.guardrail_mode, "post_call") || modeMatches(e.guardrail_mode, "logging_only"),
);
const duringCalls = sorted.filter((e) => modeMatches(e.guardrail_mode, "during_call"));
for (const e of preCalls) {
const offsetMs = Math.round((e.end_time - baseTime) * 1000);