fix(report): persist longform appendix on self-hosted upsert (BUG-576)

Engine calls already returned markdown; the worker failed because local postgres upsert required onConflict and the appendix write omitted it.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-07 14:19:21 +08:00
co-authored by Cursor
parent 4716fe46b3
commit e87c58d6e4
19 changed files with 382 additions and 70 deletions
@@ -39,6 +39,29 @@ function identifier(value: string): string {
return `"${normalized}"`;
}
export function upsertConflictColumns(options?: { onConflict?: string }): string[] {
return (options?.onConflict ?? "")
.split(",")
.map((column) => column.trim())
.filter(Boolean);
}
async function primaryKeyColumns(client: PoolClient, table: string): Promise<string[]> {
identifier(table);
const result = await client.query<{ column_name: string }>(
`
select a.attname as column_name
from pg_index i
join pg_attribute a on a.attrelid = i.indrelid and a.attnum = any(i.indkey)
where i.indrelid = format('%I.%I', 'public', $1::text)::regclass
and i.indisprimary
order by array_position(i.indkey, a.attnum)
`,
[table],
);
return result.rows.map((row) => row.column_name);
}
function queryError(error: unknown): QueryError {
const value = error as PostgresError;
return {
@@ -197,11 +220,9 @@ class LocalPostgresQueryBuilder implements PromiseLike<QueryResult> {
upsert(
value: Record<string, unknown> | readonly Record<string, unknown>[],
options: { onConflict: string },
options?: { onConflict?: string },
) {
const conflict = options.onConflict
.split(",")
.map((column) => column.trim());
const conflict = upsertConflictColumns(options);
conflict.forEach(identifier);
this.mutation = { kind: "upsert", rows: records(value), conflict };
return this;
@@ -402,13 +423,15 @@ class LocalPostgresQueryBuilder implements PromiseLike<QueryResult> {
);
sql = `insert into public.${identifier(this.table)} (${columns.map(identifier).join(", ")}) values ${valueGroups.join(", ")}`;
if (this.mutation.kind === "upsert") {
const updates = columns.filter(
(column) =>
!this.mutation ||
this.mutation.kind !== "upsert" ||
!this.mutation.conflict.includes(column),
);
sql += ` on conflict (${this.mutation.conflict.map(identifier).join(", ")}) do ${
let conflict = this.mutation.conflict;
if (conflict.length === 0) {
conflict = await primaryKeyColumns(client, this.table);
}
if (conflict.length === 0) {
throw new Error("upsert requires a conflict target");
}
const updates = columns.filter((column) => !conflict.includes(column));
sql += ` on conflict (${conflict.map(identifier).join(", ")}) do ${
updates.length === 0
? "nothing"
: `update set ${updates.map((column) => `${identifier(column)} = excluded.${identifier(column)}`).join(", ")}`