chore(ui): sync Lighthouse context core

This commit is contained in:
alejandrobailo
2026-07-22 17:18:33 +02:00
4 changed files with 132 additions and 19 deletions
@@ -199,7 +199,7 @@ describe("createLighthouseChatStore", () => {
displayText: "Prioritize findings",
context: {
...context,
items: context.items.slice(0, 2),
items: context.items.slice(0, 3),
},
}),
);
+19 -16
View File
@@ -41,27 +41,15 @@ export function compileLighthouseContext(
const seen = new Set<string>();
const items = parsedItems
.sort((left, right) => getItemOrder(left) - getItemOrder(right))
.filter((item) => {
const key = `${item.kind}:${item.id}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
})
.sort((left, right) => getItemOrder(left) - getItemOrder(right));
});
const completeContext = buildEnvelopeWithinLimits(items);
if (completeContext) return completeContext;
const withoutSummaries = items.filter(
(item) =>
item.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE ||
item.source !== LIGHTHOUSE_CONTEXT_SOURCE.AUTOMATIC,
);
const selectionContext = buildEnvelopeWithinLimits(withoutSummaries);
if (selectionContext) return selectionContext;
const page = items.find((item) => item.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE);
return page ? buildEnvelopeWithinLimits([page]) : undefined;
return buildEnvelopeWithProgressiveDegradation(items);
}
function hasDifferentScope(candidate: unknown, scopeKey: string): boolean {
@@ -76,7 +64,8 @@ function hasDifferentScope(candidate: unknown, scopeKey: string): boolean {
function getItemOrder(item: LighthouseContextItem): number {
if (item.kind === LIGHTHOUSE_CONTEXT_KIND.PAGE) return 0;
return item.source === LIGHTHOUSE_CONTEXT_SOURCE.AUTOMATIC ? 2 : 1;
if (item.source === LIGHTHOUSE_CONTEXT_SOURCE.FOCUSED) return 1;
return item.source === LIGHTHOUSE_CONTEXT_SOURCE.AUTOMATIC ? 3 : 2;
}
function buildEnvelopeWithinLimits(
@@ -94,3 +83,17 @@ function buildEnvelopeWithinLimits(
const byteLength = getApiLighthouseContextByteLength(result.data);
return byteLength <= LIGHTHOUSE_CONTEXT_MAX_BYTES ? result.data : undefined;
}
function buildEnvelopeWithProgressiveDegradation(
items: LighthouseContextItem[],
): LighthouseContextEnvelope | undefined {
const retainedItems = items.slice(0, 8);
while (retainedItems.length > 0) {
const context = buildEnvelopeWithinLimits(retainedItems);
if (context) return context;
retainedItems.pop();
}
return undefined;
}
+111 -2
View File
@@ -244,10 +244,51 @@ describe("compileLighthouseContext", () => {
"finding:finding-1",
]);
});
it("should retain the highest-priority duplicate", () => {
// Given
const scopeKey = "findings:/findings";
const items = [
{
kind: "page",
id: "findings",
source: "automatic",
scopeKey,
label: "Findings",
path: "/findings",
},
{
kind: "finding",
id: "finding-1",
source: "selection",
scopeKey,
label: "Selected finding",
findingId: "finding-1",
},
{
kind: "finding",
id: "finding-1",
source: "focused",
scopeKey,
label: "Focused finding",
findingId: "finding-1",
},
];
// When
const context = compileLighthouseContext(items, scopeKey);
// Then
expect(context?.items[1]).toMatchObject({
id: "finding-1",
source: "focused",
label: "Focused finding",
});
});
});
describe("when contributors arrive in render order", () => {
it("should order page, selection, and summary items deterministically", () => {
it("should order page, focused, selection, and summary items deterministically", () => {
// Given
const scopeKey = "findings:/findings";
const items = [
@@ -268,6 +309,14 @@ describe("compileLighthouseContext", () => {
label: "Selected finding",
findingId: "finding-1",
},
{
kind: "finding",
id: "finding-focused",
source: "focused",
scopeKey,
label: "Focused finding",
findingId: "finding-focused",
},
{
kind: "page",
id: "findings",
@@ -284,6 +333,7 @@ describe("compileLighthouseContext", () => {
// Then
expect(context?.items.map((item) => item.id)).toEqual([
"findings",
"finding-focused",
"finding-1",
"findings-summary",
]);
@@ -291,7 +341,7 @@ describe("compileLighthouseContext", () => {
});
describe("when serialized context exceeds 2 KiB", () => {
it("should remove automatic summaries before dropping the selection", () => {
it("should drop lowest-priority items until the context fits", () => {
// Given
const scopeKey = "findings:/findings";
const page = {
@@ -332,6 +382,7 @@ describe("compileLighthouseContext", () => {
expect(context?.items.map((item) => item.id)).toEqual([
"findings",
"finding-1",
"summary-0",
]);
});
@@ -369,6 +420,64 @@ describe("compileLighthouseContext", () => {
});
});
describe("when context exceeds the eight-item limit", () => {
it("should progressively drop only the lowest-priority items", () => {
// Given
const scopeKey = "findings:/findings";
const page = {
kind: "page",
id: "findings",
source: "automatic",
scopeKey,
label: "Findings",
path: "/findings",
};
const focused = {
kind: "finding",
id: "focused",
source: "focused",
scopeKey,
label: "Focused finding",
findingId: "focused",
};
const selections = Array.from({ length: 2 }, (_, index) => ({
kind: "finding",
id: `selection-${index}`,
source: "selection",
scopeKey,
label: `Selected finding ${index}`,
findingId: `selection-${index}`,
}));
const summaries = Array.from({ length: 6 }, (_, index) => ({
kind: "finding",
id: `summary-${index}`,
source: "automatic",
scopeKey,
label: `Summary ${index}`,
findingId: `summary-${index}`,
total: index,
}));
// When
const context = compileLighthouseContext(
[page, ...summaries, ...selections, focused],
scopeKey,
);
// Then
expect(context?.items.map((item) => item.id)).toEqual([
"findings",
"focused",
"selection-0",
"selection-1",
"summary-0",
"summary-1",
"summary-2",
"summary-3",
]);
});
});
describe("when contributors belong to another page", () => {
it("should ignore stale scoped data", () => {
// Given / When
+1
View File
@@ -13,6 +13,7 @@ export type LighthouseContextKind =
export const LIGHTHOUSE_CONTEXT_SOURCE = {
AUTOMATIC: "automatic",
FOCUSED: "focused",
SELECTION: "selection",
MANUAL: "manual",
} as const;