fix: close monitor ingest review gaps

This commit is contained in:
AgentRun Codex
2026-07-12 21:45:33 +00:00
parent 9b9a460d37
commit 3f711cd38a
7 changed files with 55 additions and 26 deletions
+4 -1
View File
@@ -17,11 +17,12 @@ function positive(name: string): number {
return value;
}
let store: ReturnType<typeof createPostgresMonitorCentralStore> | null = null;
try {
const snapshotPath = process.argv[2];
if (!snapshotPath) throw Object.assign(new Error("snapshot path is required"), { code: "monitor_migration_snapshot_missing" });
const snapshot = JSON.parse(readFileSync(snapshotPath, "utf8")) as MonitorSqliteSnapshot;
const store = createPostgresMonitorCentralStore(required("DATABASE_URL"), { poolMax: positive("MONITOR_CENTRAL_PG_POOL_MAX"), idleTimeoutSeconds: positive("MONITOR_CENTRAL_PG_IDLE_TIMEOUT_SECONDS") });
store = createPostgresMonitorCentralStore(required("DATABASE_URL"), { poolMax: positive("MONITOR_CENTRAL_PG_POOL_MAX"), idleTimeoutSeconds: positive("MONITOR_CENTRAL_PG_IDLE_TIMEOUT_SECONDS") });
await store.ping();
if (await store.schemaVersion() !== MONITOR_CENTRAL_SCHEMA_VERSION) throw Object.assign(new Error("central monitor schema is not ready"), { code: "monitor_migration_schema_unready" });
if (!(await store.capability()).ingest) throw Object.assign(new Error("central monitor ingest capability is unavailable"), { code: "monitor_migration_capability_unavailable" });
@@ -29,4 +30,6 @@ try {
} catch (error) {
console.log(JSON.stringify({ ok: false, error: structuredMonitorCentralError(error), valuesRedacted: true }));
process.exitCode = 2;
} finally {
await store?.close();
}
@@ -1033,7 +1033,7 @@ function submitCentralTerminalIngest(state: SentinelCicdState, payload: Record<s
const report = { ...analysis, updatedAt: stringAtNullable(analysis, "reportUpdatedAt") ?? stringAtNullable(payload, "updatedAt"), findings: Array.isArray(payload.findings) ? payload.findings.map(record) : [], summary, views, valuesRedacted: true };
const artifacts = [{ relativePath: "analysis/report.json", kind: "report-json", sha256: stringAtNullable(payload, "reportJsonSha256") ?? stringAtNullable(analysis, "reportJsonSha256") ?? "", sizeBytes: numberAtNullable(analysis, "reportJsonBytes") ?? -1 }];
const ingest = buildMonitorTerminalIngest({ sentinelId: state.sentinelId, node: state.spec.nodeId, lane: state.spec.lane, runId: stringAt(payload, "runId") }, report, artifacts, { ownerNode: state.spec.nodeId, ownerLane: state.spec.lane, ownerPvc: stringAt(state.runtime, "pvcName"), stateDir: stateDir ?? "" });
const result = runCommand(["bun", "scripts/monitor-terminal-ingest-submit.ts"], repoRoot, { timeoutMs: config.timeoutMs + config.retry.maxAttempts * config.retry.delayMs + 1_000, input: JSON.stringify({ config, ingest }) });
const result = runCommand(["bun", "scripts/monitor-terminal-ingest-submit.ts"], repoRoot, { timeoutMs: config.retry.maxAttempts * config.timeoutMs + (config.retry.maxAttempts - 1) * config.retry.delayMs + 1_000, input: JSON.stringify({ config, ingest }) });
const response = parseJsonObject(result.stdout);
if (result.exitCode === 0 && response?.ok === true) return { ...response, writer: "central", valuesRedacted: true };
return { ok: false, writer: "central", error: "monitor-ingest-failed", response, stderr: short(result.stderr), payloadHash: ingest.payloadHash, valuesRedacted: true };
@@ -1406,10 +1406,11 @@ function boundQuickVerifyRecordText(value: unknown, maxChars: number): string |
return `${value.slice(0, maxChars)}\n[truncated ${value.length - maxChars} chars]`;
}
function analysisSummaryFromAnalyzeResult(analysis: ChildCliResult, fallbackStateDir: string | null): Record<string, unknown> | null {
export function analysisSummaryFromAnalyzeResult(analysis: ChildCliResult, fallbackStateDir: string | null): Record<string, unknown> | null {
const payload = cliDataPayload(analysis.parsed);
const source = record(payload.analysis);
const reportJsonSha256 = stringAtNullable(source, "reportJsonSha256");
const analyzer = record(source.analyzer);
const reportJsonSha256 = stringAtNullable(source, "reportJsonSha256") ?? stringAtNullable(analyzer, "reportJsonSha256");
if (reportJsonSha256 === null) return null;
const counts = record(source.counts);
const archiveSummary = record(source.archiveSummary);
@@ -1428,13 +1429,13 @@ function analysisSummaryFromAnalyzeResult(analysis: ChildCliResult, fallbackStat
source: "observe-analyze-raw",
reason: null,
reportOk: source.ok === true,
stateDir: stringAtNullable(source, "stateDir") ?? fallbackStateDir,
reportJsonPath: stringAtNullable(source, "reportJsonPath"),
stateDir: stringAtNullable(source, "stateDir") ?? stringAtNullable(analyzer, "stateDir") ?? fallbackStateDir,
reportJsonPath: stringAtNullable(source, "reportJsonPath") ?? stringAtNullable(analyzer, "reportJsonPath"),
reportJsonSha256,
reportJsonBytes: numberAtNullable(source, "reportJsonBytes"),
reportUpdatedAt: stringAtNullable(source, "reportUpdatedAt"),
reportMdPath: stringAtNullable(source, "reportMdPath"),
reportMdSha256: stringAtNullable(source, "reportMdSha256"),
reportJsonBytes: numberAtNullable(source, "reportJsonBytes") ?? numberAtNullable(analyzer, "reportJsonBytes"),
reportUpdatedAt: stringAtNullable(source, "reportUpdatedAt") ?? stringAtNullable(analyzer, "reportUpdatedAt"),
reportMdPath: stringAtNullable(source, "reportMdPath") ?? stringAtNullable(analyzer, "reportMdPath"),
reportMdSha256: stringAtNullable(source, "reportMdSha256") ?? stringAtNullable(analyzer, "reportMdSha256"),
findingCount,
artifactCount: numberAtNullable(source, "artifactCount") ?? numberAtNullable(counts, "artifacts") ?? 0,
findings,
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { test } from "bun:test";
import { createInMemoryMonitorCentralStore, createMonitorCentralService, mapPostgresFindingRow, MONITOR_CENTRAL_MIGRATIONS, MonitorCentralConfigurationError, structuredMonitorCentralError } from "./monitor-central-persistence";
import { createInMemoryMonitorCentralStore, createMonitorCentralService, mapPostgresFindingRow, MONITOR_CENTRAL_MIGRATIONS, MonitorCentralConfigurationError, monitorCentralStableJson, normalizeMonitorCentralIngest, structuredMonitorCentralError } from "./monitor-central-persistence";
function service() { return createMonitorCentralService({ store: createInMemoryMonitorCentralStore(), pageSize: 50 }); }
function locator(runId: string) { return { ownerNode: "NC01", ownerLane: "v03", ownerPvc: "sentinel-pvc", stateDir: `/state/${runId}`, relativePath: "analysis/report.json", sha256: "sha256:abc", sizeBytes: 12, kind: "report", reachable: true }; }
@@ -44,6 +44,14 @@ test("full terminal envelope controls hash and concurrent same identity converge
assert.equal(detail.run.artifactLocators[0].reachable, false);
});
test("stable JSON accepts optional fields and in-memory view matches the PG contract", async () => {
assert.equal(monitorCentralStableJson({ optional: undefined, value: "ok", list: [undefined] }), '{"list":[null],"value":"ok"}');
const store = createInMemoryMonitorCentralStore();
const normalized = normalizeMonitorCentralIngest({ ...input("top-level-view"), payload: { views: { summary: { state: "ready" } }, optional: undefined } });
await store.ingest(normalized);
assert.deepEqual(await store.view({ sentinelId: "sentinel-a", node: "NC01", lane: "v03" }, "top-level-view", "summary"), { run: { sentinelId: "sentinel-a", node: "NC01", lane: "v03", runId: "top-level-view", updatedAt: normalized.updatedAt, status: normalized.status, payloadHash: normalized.payloadHash }, view: "summary", value: { state: "ready" } });
});
test("global and scoped latest use updatedAt then runId descending", async () => {
const target = service();
await ingest(target, "run-a", "2026-07-12T10:00:00.000Z");
+12 -8
View File
@@ -46,6 +46,7 @@ export interface MonitorRun extends Required<MonitorScope> {
}
export interface MonitorCentralStore {
close(): Promise<void>;
migrate(): Promise<void>;
ping(): Promise<void>;
schemaVersion(): Promise<string | null>;
@@ -176,6 +177,7 @@ export function createMonitorCentralService(options: MonitorCentralServiceOption
export function createPostgresMonitorCentralStore(databaseUrl: string, options: PostgresMonitorCentralStoreOptions): MonitorCentralStore {
const sql = postgres(databaseUrl, { max: requirePositiveInteger(options.poolMax, "monitor_central_pg_pool_max_missing"), idle_timeout: requirePositiveInteger(options.idleTimeoutSeconds, "monitor_central_pg_idle_timeout_missing") });
return {
async close() { await sql.end({ timeout: 0 }); },
async migrate() {
for (const statement of MONITOR_CENTRAL_MIGRATIONS) await sql.unsafe(statement);
await sql`insert into monitor.schema_migrations (version, applied_at) values (${MONITOR_CENTRAL_SCHEMA_VERSION}, now()) on conflict (version) do nothing`;
@@ -197,9 +199,13 @@ export function createPostgresMonitorCentralStore(databaseUrl: string, options:
},
async importSource(manifest, inputs) {
return sql.begin(async (transaction) => {
const existing = await transaction<{ source_fingerprint: string; row_count: string | number }[]>`
select source_fingerprint, row_count from monitor.import_manifests where manifest_id = ${manifest.manifestId} for update`;
if (existing[0]) {
const claimed = await transaction<{ manifest_id: string }[]>`
insert into monitor.import_manifests (manifest_id, source_fingerprint, row_count, imported_at)
values (${manifest.manifestId}, ${manifest.sourceFingerprint}, ${manifest.rowCount}, now())
on conflict (manifest_id) do nothing returning manifest_id`;
if (!claimed[0]) {
const existing = await transaction<{ source_fingerprint: string; row_count: string | number }[]>`
select source_fingerprint, row_count from monitor.import_manifests where manifest_id = ${manifest.manifestId}`;
if (existing[0].source_fingerprint !== manifest.sourceFingerprint || Number(existing[0].row_count) !== manifest.rowCount) {
throw Object.assign(new Error("monitor import manifest conflict"), { code: "monitor_import_manifest_conflict" });
}
@@ -207,9 +213,6 @@ export function createPostgresMonitorCentralStore(databaseUrl: string, options:
}
const runs = [] as { disposition: "inserted" | "idempotent" }[];
for (const input of inputs) runs.push(await writePostgresIngest(transaction, input));
await transaction`
insert into monitor.import_manifests (manifest_id, source_fingerprint, row_count, imported_at)
values (${manifest.manifestId}, ${manifest.sourceFingerprint}, ${manifest.rowCount}, now())`;
return { disposition: "inserted" as const, runs };
});
},
@@ -307,6 +310,7 @@ export function createInMemoryMonitorCentralStore(): MonitorCentralStore {
const records = new Map<string, NormalizedMonitorCentralIngest>();
const manifests = new Map<string, MonitorImportManifest>();
return {
async close() {},
async migrate() {},
async ping() {},
async schemaVersion() { return MONITOR_CENTRAL_SCHEMA_VERSION; },
@@ -361,7 +365,7 @@ export function createInMemoryMonitorCentralStore(): MonitorCentralStore {
const detail = await this.run(scope, runId);
const views = detail?.payload && typeof detail.payload === "object" ? (detail.payload as Record<string, unknown>).views : null;
const value = views && typeof views === "object" ? (views as Record<string, unknown>)[view] : undefined;
return value === undefined ? null : { runId, name: view, value };
return value === undefined || !detail ? null : { run: runFrom(detail as MonitorRun), view, value };
},
async findings(scope, options) {
return sortRuns([...records.values()].filter((item) => matches(scope, item)).map(runFrom)).flatMap((run) => {
@@ -416,7 +420,7 @@ function sortRuns(runs: readonly MonitorRun[]): MonitorRun[] { return [...runs].
function afterRunCursor(run: MonitorRun, cursorValue: RunCursor | null): boolean { return !cursorValue || run.updatedAt < cursorValue.updatedAt || (run.updatedAt === cursorValue.updatedAt && run.runId < cursorValue.runId); }
function compareFindings(left: Record<string, unknown>, right: Record<string, unknown>): number { return String(right.updatedAt).localeCompare(String(left.updatedAt)) || String(right.runId).localeCompare(String(left.runId)) || Number(right.findingIndex) - Number(left.findingIndex); }
function afterFindingCursor(finding: Record<string, unknown>, cursorValue: FindingCursor | null): boolean { if (!cursorValue) return true; return String(finding.updatedAt) < cursorValue.updatedAt || (finding.updatedAt === cursorValue.updatedAt && (String(finding.runId) < cursorValue.runId || (finding.runId === cursorValue.runId && Number(finding.findingIndex) < cursorValue.findingIndex))); }
export function monitorCentralStableJson(value: unknown): string { if (Array.isArray(value)) return `[${value.map(monitorCentralStableJson).join(",")}]`; if (value && typeof value === "object") return `{${Object.entries(value as Record<string, unknown>).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${monitorCentralStableJson(item)}`).join(",")}}`; return JSON.stringify(value); }
export function monitorCentralStableJson(value: unknown): string { if (value === undefined) return "null"; if (Array.isArray(value)) return `[${value.map(monitorCentralStableJson).join(",")}]`; if (value && typeof value === "object") return `{${Object.entries(value as Record<string, unknown>).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${monitorCentralStableJson(item)}`).join(",")}}`; const json = JSON.stringify(value); return json === undefined ? "null" : json; }
function scopeWhere(scope: MonitorScope): { readonly text: string; readonly values: readonly string[] } { const entries = Object.entries({ sentinel_id: scope.sentinelId, node: scope.node, lane: scope.lane }).filter(([, value]) => value); return { text: entries.length ? entries.map(([key], index) => `r.${key} = $${index + 1}`).join(" and ") : "true", values: entries.map(([, value]) => String(value)) }; }
function identityWhere(scope: Required<MonitorScope>, runId: string): { readonly text: string; readonly values: readonly string[] } { return { text: "r.sentinel_id = $1 and r.node = $2 and r.lane = $3 and r.run_id = $4", values: [scope.sentinelId, scope.node, scope.lane, runId] }; }
async function queryRuns(sql: postgres.Sql, scope: MonitorScope, options: RunQueryOptions): Promise<MonitorRun[]> { const where = scopeWhere(scope); const values: unknown[] = [...where.values]; let cursorClause = ""; if (options.cursor) { values.push(options.cursor.updatedAt, options.cursor.runId); cursorClause = `and (r.updated_at, r.run_id) < ($${values.length - 1}, $${values.length})`; } values.push(options.limit); const rows = await sql.unsafe(`select r.sentinel_id, r.node, r.lane, r.run_id, r.updated_at, r.status, r.payload_hash from monitor.runs r where ${where.text} ${cursorClause} order by r.updated_at desc, r.run_id desc limit $${values.length}`, values); return rows.map(runRow); }
+10 -3
View File
@@ -27,15 +27,20 @@ export function exportMonitorSqliteSnapshot(source: MonitorSqliteSource, outputP
const path = assertReadOnlySqliteSource(source.path);
const sourceBytes = readFileSync(path);
const database = new Database(path, { readonly: true });
let transactionOpen = false;
try {
database.exec("BEGIN");
transactionOpen = true;
const integrity = String(database.query("PRAGMA integrity_check").get()?.integrity_check ?? "unknown");
if (integrity !== "ok") throw new Error(`legacy SQLite integrity_check failed: ${integrity}`);
const tables = (database.query("SELECT name, sql FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name").all() as { name: string; sql: string }[]).map((table) => ({ name: table.name, sql: table.sql, rowCount: Number(database.query(`SELECT COUNT(*) AS count FROM ${quoteIdentifier(table.name)}`).get()?.count ?? 0), rows: database.query(`SELECT * FROM ${quoteIdentifier(table.name)}`).all().map(normalizeRow) }));
const unsigned = { schema: MONITOR_SQLITE_MIGRATION_SCHEMA, createdAt: new Date().toISOString(), source: { ...source, path }, fingerprint: `sha256:${sha256(sourceBytes)}`, integrity, byteLength: sourceBytes.byteLength, tables, artifactLocator: { path, sha256: `sha256:${sha256(sourceBytes)}`, sizeBytes: sourceBytes.byteLength, kind: "legacy-sqlite" } };
const snapshot: MonitorSqliteSnapshot = { ...unsigned, manifestContentHash: monitorSnapshotContentHash(unsigned) };
writeSnapshot(outputPath, `${monitorCentralStableJson(snapshot)}\n`);
database.exec("COMMIT");
transactionOpen = false;
return snapshot;
} finally { database.close(); }
} finally { if (transactionOpen) database.exec("ROLLBACK"); database.close(); }
}
export function verifyMonitorSqliteSnapshot(snapshot: MonitorSqliteSnapshot): Record<string, unknown> {
@@ -76,9 +81,11 @@ function canonicalIngests(snapshot: MonitorSqliteSnapshot): readonly ReturnType<
const report = metadataValue(metadata, `run.report.${runId}`);
const rowLocators = locators.filter((item) => stringField(item, ["run_id"]) === runId).map((item) => locatorFromRow(item, snapshot));
const reportLocator = reportLocatorFromRun(row, snapshot);
const payload = jsonField(payloads.find((item) => stringField(item, ["run_id"]) === runId), ["payload", "value_json"]) ?? { ...(report ?? {}), legacy: { run: row, sourceFingerprint: snapshot.fingerprint } };
const storedPayload = jsonField(payloads.find((item) => stringField(item, ["run_id"]) === runId), ["payload", "value_json"]);
const payload = { ...(storedPayload ?? {}), ...(report ?? {}), legacy: { run: row, sourceFingerprint: snapshot.fingerprint } };
const reportFindings = Array.isArray(report?.findings) ? report.findings.filter(isRecord) : [];
return normalizeMonitorCentralIngest({ sentinelId: stringField(row, ["sentinel_id", "sentinelId"]) || snapshot.source.sentinelId, node: stringField(row, ["node"]) || snapshot.source.node, lane: stringField(row, ["lane"]) || snapshot.source.lane, runId, updatedAt: stringField(row, ["updated_at", "updatedAt", "created_at", "createdAt"]) || snapshot.createdAt, status: stringField(row, ["status"]) || "legacy", payload, findings: rowFindings.length ? rowFindings : reportFindings, artifactLocators: reportLocator ? [...rowLocators, reportLocator] : rowLocators, timeline: [] });
const canonicalFindings = reportFindings.length ? [...reportFindings, ...rowFindings.filter((item) => !reportFindings.some((reportFinding) => reportFinding.id === item.id))] : rowFindings;
return normalizeMonitorCentralIngest({ sentinelId: stringField(row, ["sentinel_id", "sentinelId"]) || snapshot.source.sentinelId, node: stringField(row, ["node"]) || snapshot.source.node, lane: stringField(row, ["lane"]) || snapshot.source.lane, runId, updatedAt: stringField(row, ["updated_at", "updatedAt", "created_at", "createdAt"]) || snapshot.createdAt, status: stringField(row, ["status"]) || "legacy", payload, findings: canonicalFindings, artifactLocators: reportLocator ? [...rowLocators, reportLocator] : rowLocators, timeline: [] });
});
}
function locatorFromRow(row: Record<string, unknown>, source: MonitorSqliteSnapshot): MonitorArtifactLocator { const ownerPvc = stringField(row, ["owner_pvc", "ownerPvc"]); const relativePath = stringField(row, ["relative_path", "relativePath"]); const hash = stringField(row, ["sha256"]); const sizeBytes = numberField(row, ["size_bytes", "sizeBytes"]); if (!ownerPvc || !relativePath || !hash || sizeBytes === null) throw Object.assign(new Error("legacy artifact locator is incomplete"), { code: "monitor-migration-locator-invalid" }); return { ownerNode: stringField(row, ["owner_node", "ownerNode"]) || source.source.node, ownerLane: stringField(row, ["owner_lane", "ownerLane"]) || source.source.lane, ownerPvc, stateDir: stringField(row, ["state_dir", "stateDir"]), relativePath, sha256: hash, sizeBytes, kind: stringField(row, ["kind"]) || "artifact", reachable: row.reachable !== false }; }
@@ -9,6 +9,7 @@ import { Database } from "bun:sqlite";
import { createInMemoryMonitorCentralStore } from "./monitor-central-persistence";
import { exportMonitorSqliteSnapshot, importMonitorSqliteSnapshot, planMonitorSqliteSources, verifyMonitorSqliteSnapshot } from "./monitor-sqlite-migration";
import { buildMonitorTerminalIngest, MonitorIngestFailedError, resolveMonitorTerminalWriter, submitMonitorTerminalIngest } from "./monitor-terminal-ingest";
import { analysisSummaryFromAnalyzeResult } from "./hwlab-node-web-sentinel-p5-observe";
test("terminal ingest payload is immutable and retries bounded failures", async () => {
const report = { ok: false, findings: [{ id: "blocked" }], updatedAt: "2026-07-12T00:00:00.000Z" };
@@ -35,6 +36,11 @@ test("terminal writer requires explicit mutually exclusive authority", () => {
assert.throws(() => resolveMonitorTerminalWriter({ sqlite: { path: "/state/legacy.sqlite" }, monitor: { terminalWriter: { mode: "central", ingest: { endpoint: "https://monitor.example.test/api/ingest", timeoutMs: 1_000, retry: { maxAttempts: 2, delayMs: 0 } } } } }));
});
test("runner-shaped analyze result projects nested report artifact facts", () => {
const summary = analysisSummaryFromAnalyzeResult({ ok: true, parsed: { data: { analysis: { analyzer: { stateDir: "run-1", reportJsonPath: "/state/run-1/analysis/report.json", reportJsonSha256: `sha256:${"c".repeat(64)}`, reportJsonBytes: 12, reportUpdatedAt: "2026-07-12T00:00:00.000Z" } } } }, result: {} } as never, null);
assert.deepEqual(summary && { stateDir: summary.stateDir, reportJsonSha256: summary.reportJsonSha256, reportJsonBytes: summary.reportJsonBytes, reportUpdatedAt: summary.reportUpdatedAt }, { stateDir: "run-1", reportJsonSha256: `sha256:${"c".repeat(64)}`, reportJsonBytes: 12, reportUpdatedAt: "2026-07-12T00:00:00.000Z" });
});
test("SQLite snapshot reconciles the 228/242/234 fixture shape without production assumptions", async () => {
const directory = mkdtempSync(join(tmpdir(), "monitor-migration-"));
try {
@@ -47,11 +53,11 @@ test("SQLite snapshot reconciles the 228/242/234 fixture shape without productio
writeFileSync(reportPath, JSON.stringify({ updatedAt: "2026-07-12T00:00:00.000Z", views: { summary: { title: "legacy summary" } }, findings: [{ id: "report-finding" }] }));
const reportHash = `sha256:${Bun.CryptoHasher.hash("sha256", readFileSync(reportPath), "hex")}`;
database.run("CREATE TABLE runs (id TEXT, state_dir TEXT, report_json_sha256 TEXT, updated_at TEXT)");
database.run("CREATE TABLE findings (id TEXT)");
database.run("CREATE TABLE findings (run_id TEXT, finding TEXT)");
database.run("CREATE TABLE metadata (id TEXT, key TEXT, value_json TEXT)");
for (let index = 0; index < 228; index += 1) database.run("INSERT INTO runs VALUES (?, ?, ?, ?)", [`run-${index}`, index === 0 ? "run-0" : null, index === 0 ? reportHash : null, "2026-07-12T00:00:00.000Z"]);
for (let index = 0; index < 242; index += 1) database.run("INSERT INTO findings VALUES (?)", [`finding-${index}`]);
for (let index = 0; index < 234; index += 1) database.run("INSERT INTO metadata VALUES (?, ?, ?)", [`metadata-${index}`, index === 0 ? "run.report.run-0" : `metadata-${index}`, index === 0 ? JSON.stringify({ views: { summary: { title: "legacy summary" } }, findings: [{ id: "report-finding" }] }) : "{}"]);
for (let index = 0; index < 242; index += 1) database.run("INSERT INTO findings VALUES (?, ?)", [index === 0 ? "run-0" : `run-${index % 228}`, JSON.stringify({ id: index === 0 ? "report-finding" : `finding-${index}`, summary: "indexed" })]);
for (let index = 0; index < 234; index += 1) database.run("INSERT INTO metadata VALUES (?, ?, ?)", [`metadata-${index}`, index === 0 ? "run.report.run-0" : `metadata-${index}`, index === 0 ? JSON.stringify({ views: { summary: { title: "legacy summary" } }, findings: [{ id: "report-finding", rootCause: "complete metadata detail" }] }) : "{}"]);
database.close();
const source = { sentinelId: "fixture", node: "NC01", lane: "v03", path: sqlitePath, runtimeConfigRef: "fixture#runtime", stateRoot, ownerPvc: "fixture-pvc" };
const plan = planMonitorSqliteSources([source]);
+1 -1
View File
@@ -23,7 +23,7 @@ export function buildMonitorTerminalIngest(identity: MonitorTerminalIdentity, re
...identity,
updatedAt,
status: stringValue(immutableReport.status) ?? (immutableReport.ok === true ? "ok" : "blocked"),
payload: { report: immutableReport, artifacts: artifacts.map((artifact) => ({ ...artifact })) },
payload: { ...immutableReport, report: immutableReport, artifacts: artifacts.map((artifact) => ({ ...artifact })) },
findings: recordArray(immutableReport.findings),
timeline: recordArray(immutableReport.timeline),
artifactLocators: artifacts.map((artifact) => ({ ...locatorOwner, relativePath: artifact.relativePath, kind: artifact.kind, sha256: artifact.sha256, sizeBytes: artifact.sizeBytes, reachable: true })),