Staging PostgREST rejects the executiveSummary JSON-path alias, so GET /api/reports 500s. Persist a plain card_summary column from the Markdown excerpt instead. Co-authored-by: Cursor <cursoragent@cursor.com>
167 lines
6.4 KiB
PL/PgSQL
167 lines
6.4 KiB
PL/PgSQL
-- Dedicated list-card excerpt. PostgREST JSON-path aliases on report_document
|
|
-- are rejected by the self-hosted PostgREST used in staging; this plain text
|
|
-- column is the only list-select form. Worker completion copies the Markdown
|
|
-- 摘要 excerpt already stored on executiveSummary.summary. Old rows stay null.
|
|
|
|
begin;
|
|
|
|
do $migration$
|
|
begin
|
|
if current_user <> 'schema_owner' then
|
|
raise exception 'personal_report_card_summary_requires_schema_owner'
|
|
using errcode = '42501';
|
|
end if;
|
|
end
|
|
$migration$;
|
|
|
|
alter table public.personal_reports
|
|
add column if not exists card_summary text;
|
|
|
|
alter table public.personal_reports
|
|
drop constraint if exists personal_reports_card_summary_length_check;
|
|
|
|
alter table public.personal_reports
|
|
add constraint personal_reports_card_summary_length_check
|
|
check (card_summary is null or char_length(card_summary) <= 500);
|
|
|
|
comment on column public.personal_reports.card_summary is
|
|
'Optional list-card excerpt, at most 500 characters. Owner SELECT only; writes are service_role via complete_personal_report_job. Not backfilled.';
|
|
|
|
-- Same signature as 20260814040000. Adds card_summary from the cover excerpt.
|
|
create or replace function public.complete_personal_report_job(
|
|
p_job_id uuid,
|
|
p_lease_token uuid,
|
|
p_user_id uuid,
|
|
p_report_id uuid,
|
|
p_request_id uuid,
|
|
p_request_fingerprint text,
|
|
p_schema_version text,
|
|
p_report_document jsonb,
|
|
p_calculation_hash text,
|
|
p_evidence_hash text,
|
|
p_skill_name text,
|
|
p_skill_version text,
|
|
p_skill_source_commit text,
|
|
p_skill_snapshot_sha256 text
|
|
)
|
|
returns setof public.personal_report_jobs
|
|
language plpgsql
|
|
set search_path = pg_catalog, public
|
|
as $$
|
|
declare
|
|
v_job public.personal_report_jobs%rowtype;
|
|
v_report public.personal_reports%rowtype;
|
|
v_completed_at timestamptz;
|
|
begin
|
|
if p_schema_version <> 'report_document.v2'
|
|
or p_request_fingerprint !~ '^[0-9a-f]{64}$'
|
|
or p_calculation_hash !~ '^[0-9a-f]{64}$'
|
|
or p_evidence_hash !~ '^[0-9a-f]{64}$'
|
|
or p_skill_name is null
|
|
or p_skill_name !~ '^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$'
|
|
or p_skill_version is null
|
|
or length(p_skill_version) not between 5 and 80
|
|
or p_skill_version !~ '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'
|
|
or p_skill_snapshot_sha256 !~ '^[0-9a-f]{64}$'
|
|
or (p_skill_source_commit is not null and p_skill_source_commit !~ '^[0-9a-f]{40}$') then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_payload_invalid';
|
|
end if;
|
|
|
|
select job.*
|
|
into v_job
|
|
from public.personal_report_jobs as job
|
|
where job.id = p_job_id
|
|
for update;
|
|
|
|
if not found then
|
|
raise exception using errcode = 'P0002', message = 'personal_report_job_not_found';
|
|
end if;
|
|
v_completed_at := clock_timestamp();
|
|
if v_job.user_id is distinct from p_user_id
|
|
or v_job.request_id is distinct from p_request_id
|
|
or v_job.request_fingerprint is distinct from p_request_fingerprint then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_identity_mismatch';
|
|
end if;
|
|
if v_job.status <> 'running'
|
|
or v_job.lease_token is distinct from p_lease_token
|
|
or v_job.lease_expires_at <= v_completed_at then
|
|
raise exception using errcode = '55000', message = 'personal_report_job_lease_lost';
|
|
end if;
|
|
|
|
select report.*
|
|
into v_report
|
|
from public.personal_reports as report
|
|
where report.id = p_report_id
|
|
and report.user_id = p_user_id
|
|
and report.request_id = p_request_id
|
|
for update;
|
|
|
|
if not found then
|
|
raise exception using errcode = 'P0002', message = 'personal_report_not_found';
|
|
end if;
|
|
if v_report.status <> 'generating' then
|
|
raise exception using errcode = '55000', message = 'personal_report_completion_invalid_state';
|
|
end if;
|
|
if v_report.request_fingerprint is distinct from p_request_fingerprint
|
|
or v_report.skill_name is distinct from p_skill_name
|
|
or v_report.skill_version is distinct from p_skill_version
|
|
or v_report.skill_source_commit is distinct from p_skill_source_commit
|
|
or v_report.skill_snapshot_sha256 is distinct from p_skill_snapshot_sha256 then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_identity_mismatch';
|
|
end if;
|
|
if p_report_document is null
|
|
or p_report_document ->> 'schemaVersion' <> p_schema_version
|
|
or p_report_document ->> 'reportId' <> p_report_id::text
|
|
or p_report_document #>> '{provenance,calculationHash}' <> p_calculation_hash
|
|
or p_report_document #>> '{provenance,evidenceHash}' <> p_evidence_hash
|
|
or p_report_document #>> '{provenance,skillName}' <> p_skill_name
|
|
or p_report_document #>> '{provenance,skillVersion}' <> p_skill_version
|
|
or p_report_document #>> '{provenance,skillSnapshotSha256}' <> p_skill_snapshot_sha256
|
|
or (p_report_document #>> '{provenance,skillSourceCommit}') is distinct from p_skill_source_commit then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_document_mismatch';
|
|
end if;
|
|
if v_report.calculation_hash is not null
|
|
and v_report.calculation_hash is distinct from p_calculation_hash then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_hash_mismatch';
|
|
end if;
|
|
if v_report.evidence_hash is not null
|
|
and v_report.evidence_hash is distinct from p_evidence_hash then
|
|
raise exception using errcode = '22023', message = 'personal_report_completion_hash_mismatch';
|
|
end if;
|
|
|
|
update public.personal_reports as report
|
|
set status = 'ready',
|
|
schema_version = p_schema_version,
|
|
report_document = p_report_document,
|
|
calculation_hash = p_calculation_hash,
|
|
evidence_hash = p_evidence_hash,
|
|
failure_code = null,
|
|
completed_at = v_completed_at,
|
|
updated_at = v_completed_at,
|
|
card_summary = nullif(
|
|
left(btrim(coalesce(p_report_document #>> '{executiveSummary,summary}', '')), 500),
|
|
''
|
|
)
|
|
where report.id = p_report_id;
|
|
|
|
return query
|
|
update public.personal_report_jobs as job
|
|
set status = 'ready',
|
|
lease_token = null,
|
|
lease_owner = null,
|
|
lease_acquired_at = null,
|
|
lease_expires_at = null,
|
|
heartbeat_at = v_completed_at,
|
|
next_attempt_at = null,
|
|
progress_phase = 'ready',
|
|
progress_percent = 100,
|
|
last_error_code = null,
|
|
last_error_at = null,
|
|
finished_at = v_completed_at
|
|
where job.id = p_job_id
|
|
returning job.*;
|
|
end;
|
|
$$;
|
|
|
|
commit;
|