Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion dashboard/site/dashboard-fragments/entities.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"from": "domains",
"select": [{ "field": "domain" }, { "field": "run-link" }]
},
{
"name": "domain-run-records",
"subject": "Retained run-linked observations for the selected domain.",
"objective": "Find related runs without returning the unscoped domain collection alongside the paginated view.",
"acceptance": "Preserve every domain observation field, exact route scope, filtering, ordering, total count, provenance and related-run links; only the bounded view payload crosses the worker boundary.",
"from": "domains"
},
{
"name": "tool-route-record",
"subject": "Project the selected MCP tool header and one retained run link.",
Expand Down Expand Up @@ -262,7 +269,7 @@
"title": "Related runs",
"description": "Run records related to this domain.",
"data": {
"source": "domains",
"source": "domain-run-records",
"arguments": [
{
"name": "domain",
Expand Down
29 changes: 28 additions & 1 deletion dashboard/site/src/agent/catalog.generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,8 @@
}
],
"queries": [
"domain-route-record"
"domain-route-record",
"domain-run-records"
]
},
{
Expand Down Expand Up @@ -14902,6 +14903,32 @@
]
}
},
{
"id": "domain-run-records",
"subject": "Retained run-linked observations for the selected domain.",
"objective": "Find related runs without returning the unscoped domain collection alongside the paginated view.",
"acceptance": "Preserve every domain observation field, exact route scope, filtering, ordering, total count, provenance and related-run links; only the bounded view payload crosses the worker boundary.",
"description": "",
"parameters": [
{
"name": "domain",
"field": "domain"
}
],
"sources": [
"domains"
],
"used-by-pages": [
"domain-runs"
],
"execution": {
"local": true,
"backend": "sqlite",
"requirements": [
"domains"
]
}
},
{
"id": "tool-route-record",
"subject": "Project the selected MCP tool header and one retained run link.",
Expand Down
2 changes: 1 addition & 1 deletion dashboard/site/src/agent/queries.generated.json

Large diffs are not rendered by default.

37 changes: 35 additions & 2 deletions dashboard/site/src/data/queries/database.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,11 +325,12 @@ export async function queryIndexedDatabaseSources(indexedDB, logicalSources, def
if (unavailable) {
return [name, executeRowLocalRecordGraph(plan.queries, { [source]: unavailable }, name, budget)];
}
const keyReadId = `${plan.store}:${plan.indexName}`;
const keyReadId = JSON.stringify([plan.store, plan.indexName, plan.exactKey]);
let keyRead = keyReads.get(keyReadId);
if (!keyRead) {
keyRead = readCollectionQueryKeys(indexedDB, plan.store, plan.indexName, {
maxKeys: DASHBOARD_QUERY_LIMITS['max-input-rows'], checkpoint: () => budget.spend(1)
maxKeys: DASHBOARD_QUERY_LIMITS['max-input-rows'], checkpoint: () => budget.spend(1),
exactKey: plan.exactKey
});
keyReads.set(keyReadId, keyRead);
}
Expand Down Expand Up @@ -742,11 +743,43 @@ function indexedRecordKeySelectionPlan(index, definition) {
});
return {
source, store, indexName: candidate.indexName, keyField, queries,
exactKey: exactRecordSelectionKey(recordProjection, stages, candidate.covered),
projection: projection ? pruned[0] : undefined,
keyQueries: projection ? pruned.slice(1) : pruned
};
}

/**
* Only invert unchanged scalar projections, never computed labels. Storage
* additionally proves that all indexed tuples contain strings before using
* this shortcut; coercible or structured values retain dictionary evaluation.
* @param {import('./declarative.js').DashboardQuery} projection
* @param {Array<{ definition: import('./declarative.js').DashboardQuery }>} stages
* @param {Set<string>} covered
*/
function exactRecordSelectionKey(projection, stages, covered) {
if (covered.size !== 1) return undefined;
const [rawField] = covered;
const computed = new Set((projection.compute ?? []).map((field) => field.as));
let fields = new Set((projection.select ?? []).filter((field) => (
field.field === rawField && !computed.has(field.field)
)).map((field) => field.as ?? field.field));
for (const { definition } of stages) {
for (const field of definition.compute ?? []) fields.delete(field.as);
const predicate = definition.filter?.predicates?.find((predicate) => (
fields.has(predicate.field) && !predicate.optional
&& typeof predicate.equals === 'string' && !predicate.in
&& predicate.includes === undefined && predicate.gte === undefined && predicate.lt === undefined
));
if (predicate) return JSON.stringify([predicate.equals]);
if (definition.select) {
fields = new Set(definition.select.filter((field) => fields.has(field.field))
.map((field) => field.as ?? field.field));
}
}
return undefined;
}

/**
* Fuse only row-local ancestors with their selection, retaining one shared
* budget. Sorting and limiting still operate once on the complete result.
Expand Down
14 changes: 13 additions & 1 deletion dashboard/site/src/data/storage/indexeddb.js
Original file line number Diff line number Diff line change
Expand Up @@ -1375,7 +1375,7 @@ export async function queryCollectionCountGroups(indexedDB, storeName, plan) {
* @param {IDBFactory} indexedDB
* @param {typeof ENTITY_STORES[number]} storeName
* @param {string} indexName
* @param {{ maxKeys: number, checkpoint?: () => void }} options
* @param {{ maxKeys: number, checkpoint?: () => void, exactKey?: string }} options
* @returns {Promise<Array<{ key: string, row: Record<string, unknown> }> | null>}
*/
export async function readCollectionQueryKeys(indexedDB, storeName, indexName, options) {
Expand All @@ -1392,6 +1392,18 @@ export async function readCollectionQueryKeys(indexedDB, storeName, indexName, o
return null;
}
const fields = CANONICAL_QUERY_INDEX_FIELDS[indexName];
if (options.exactKey !== undefined && fields.length === 1) {
// JSON string tuples sort before every non-string tuple. Check the tail
// without scanning the string dictionary, retaining coercion semantics.
const nonString = await requestResult(index.openKeyCursor(IDBKeyRange.lowerBound('[#')));
options.checkpoint?.();
if (!nonString) {
await done;
if (options.maxKeys < 1) throw new Error(`IndexedDB query keys exceeded max-input-rows of ${options.maxKeys}`);
const [value] = JSON.parse(options.exactKey);
return [{ key: options.exactKey, row: { [fields[0]]: value } }];
}
}
/** @type {Array<{ key: string, row: Record<string, unknown> }>} */
const rows = [];
try {
Expand Down
52 changes: 52 additions & 0 deletions dashboard/site/test/e2e/canonical-data.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,58 @@ test('workflow route queries return bounded records and subscribed aggregates th
expect(Object.values(result.missing).every((source) => source.rows.length === 0)).toBe(true);
});

test('Domain runs returns bounded aliases and refreshes its abort-scoped subscription', async ({ page, context }) => {
const initial = databaseTables();
initial.domains.rows.push({ ...initial.domains.rows[0], event: 'unrelated-domain', domain: 'other.example' });
await context.route('http://dashboard.test/domain-initial.json', (route) => route.fulfill({
contentType: 'application/json', body: JSON.stringify(initial)
}));
await context.route('http://dashboard.test/domain-updated.json', (route) => route.fulfill({
contentType: 'application/json', body: JSON.stringify(databaseTables('domain-refresh', '12346'))
}));
const result = await page.evaluate(async () => {
const processor = await import(`${location.origin}/src/data-processor.js`);
const { dashboard } = await fetch(`${location.origin}/dashboard.json`).then((response) => response.json());
const source = 'domain-run-records';
const alias = 'view:domain-runs:domain-runs-table:domain-run-records';
const options = {
pageId: 'domain-runs', viewId: 'domain-runs-table',
routeParameters: { domain: 'api.github.com' }
};
const pagination = { [alias]: { limit: 1 } };
const initial = await processor.loadCanonicalDashboardSources(
`${location.origin}/domain-initial.json`, [source], dashboard, pagination, options
);
const lifetime = new AbortController();
let deliveries = 0;
try {
const refreshed = await new Promise((resolve, reject) => {
processor.subscribeCanonicalDashboardView('domain-runs-refresh', [source], dashboard, (/** @type {Record<string, import('../../src/presenter.js').LogicalSourceInput>} */ payload) => {
deliveries++;
if (deliveries === 1) {
processor.refreshCanonicalDashboardSources(
`${location.origin}/domain-updated.json`, [source], dashboard, pagination, options
).catch(reject);
} else if (payload[alias]?.metadata['total-row-count'] === 4) resolve(payload);
}, pagination, { ...options, signal: lifetime.signal, onError: reject });
});
return { initial, refreshed };
} finally { lifetime.abort(); }
});
const alias = 'view:domain-runs:domain-runs-table:domain-run-records';
for (const payload of [result.initial, result.refreshed]) {
expect(Object.keys(payload)).toEqual([alias]);
expect(payload[alias].rows).toHaveLength(1);
expect(payload[alias].rows[0]).toMatchObject({
domain: 'api.github.com', repository: 'gh-cao',
'run-attempt': 2, 'run-link': { href: expect.stringMatching(/actions\/runs\/1234[56]/) }
});
expect(payload[alias].continuationToken).toBeTruthy();
}
expect(result.initial[alias].metadata['total-row-count']).toBe(2);
expect(result.refreshed[alias].metadata['total-row-count']).toBe(4);
});

test('data worker avoids unavailable legacy boundaries on initial and navigated requests', async ({ context, page }) => {
await context.route('http://dashboard.test/canonical-warning-sources.json', async (route) => {
await route.fulfill({
Expand Down
18 changes: 18 additions & 0 deletions dashboard/site/test/fixtures/detail-query-contracts.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@
"missing-tool/"
],
"sources": {
"domain-run-records": [
"id", "organization", "repository", "workflow", "run", "run-attempt",
"event", "event-timestamp", "event-source", "event-type", "event-summary",
"event-status", "request-count",
"correlation-id", "payload-ref", "domain", "decision", "source-sequence",
"observed-at", "run-link", "engine", "engine-version", "requested-model",
"resolved-model", "model-id", "rollout-mode", "gh-aw-version", "run-conclusion",
"agent-id", "agent-version", "agent-runtime", "organization-link",
"repository-link", "workflow-link"
],
"entity-events": [
"organization", "repository", "workflow", "run", "run-attempt",
"event", "event-source", "event-type", "event-summary", "event-status",
Expand Down Expand Up @@ -48,6 +58,14 @@
],
"temporalPanelFields": ["campaign", "value-workflow", "series-repository", "plot-title", "plot-window-start-ms", "plot-window-end-ms", "operational-value-definition", "operational-value-name", "operational-value-unit", "operational-value-direction", "plot-evidence-mode", "plot-comparison-note", "metric", "metric-key", "metric-name", "metric-kind", "metric-group", "series"],
"views": [
{
"page": "domain-runs",
"id": "domain-runs-table",
"source": "domain-run-records",
"identity": "event",
"arguments": ["domain"],
"lazy-list": true
},
{
"page": "run-events",
"id": "run-events",
Expand Down
18 changes: 18 additions & 0 deletions dashboard/site/test/unit/data-sqlite-native-selection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ afterEach(() => {
});

describe('real SQLite native record selection', () => {
it('retains the ordinary SQLite path when tuple-key cursors are unsupported', async () => {
const factory = sqliteFactory();
vi.stubGlobal('IDBKeyRange', SqliteIDBKeyRange);
const canonical = evidence();
canonical.audits = Array.from({ length: 101 }, (_, index) => ({
id: `audit:${index}`, runId: 'run:42', summary: index === 0 ? 'selected' : `other-${index}`
}));
await upsertCanonicalBatch(factory, canonical);
const query = {
name: 'selected-audit', from: 'audits',
filter: { predicates: [{ field: 'event-summary', equals: 'selected' }] },
select: [{ field: 'event' }]
};
expect(await queryIndexedDatabaseSources(factory, sources, [query], [query.name])).toEqual({});
const result = await loadDatabaseQuerySources(factory, sources, { queries: [query], sourceNames: [query.name] });
expect(result[query.name].rows).toEqual([{ event: 'audit:0' }]);
});

it('reduces production repository run statistics in bounded SQLite scans before joins', async () => {
const factory = sqliteFactory();
vi.stubGlobal('IDBKeyRange', SqliteIDBKeyRange);
Expand Down
Loading
Loading