feat(audit): log trusted-role committed_at overrides to behandlingshistorik (#1490)

* feat(audit): log trusted-role committed_at overrides to behandlingshistorik (#1444)

Migrations 20260806150000/160000 let backend writers (service_role,
no-claims direct SQL) preserve a preset committed_at on the draft-to-posted
transition, but nothing recorded that the transition timestamp was
overridden: BFNAR 2013:2 kap 8 wants a log of who did what, when. If a
trusted-role connection is ever used against a live company, there is now
an audit row separating real from preset commit time.

- audit_log accepts action COMMITTED_AT_OVERRIDE (types + Zod filter too)
- log_committed_at_override() SECURITY DEFINER writer: audit_log has RLS
  with no INSERT policy and service_role is not guaranteed BYPASSRLS in
  every harness; EXECUTE revoked from anon/authenticated so PostgREST
  cannot expose it as an RPC
- set_committed_at() calls the writer in the preserve branch; the stamping
  branch is unchanged from 20260806160000
- pg tests: override row content (preset value, wall clock, jwt role) for
  postgres and service_role writers, absence on the stamp paths, and the
  writer's privilege lockdown

Closes #1444

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review(audit): stamp wall_clock with clock_timestamp(), not now()

Seeding flows post many entries inside one transaction; now() would pin
every override row's wall_clock to the BEGIN instead of the actual
transition moment. Captured once so new_state and description agree.
Test posts inside an explicit transaction after pg_sleep and asserts
wall_clock moved past the transaction start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-10 14:11:09 +02:00
committed by GitHub
co-authored by Claude Fable 5 Jakob Wennberg
parent fe1ff8649b
commit c77367ac1a
5 changed files with 243 additions and 0 deletions
+1
View File
@@ -853,3 +853,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
[2026-08-10] RC VAT template fix scoped to template-library.ts (applyTemplate + deriveTemplateLinesFromBooking): counterparty/SIE learned patterns already strip fiktiv-moms legs and regenerate them via generateReverseChargeLines with the gross base, so mapping-engine/counterparty paths were already correct and stay untouched.
[2026-08-10] Receipt hunt cron keeps searchMail=false: the mailbox leg stays manual until its time budget is proven. A sweep of one 172-message mailbox took over 600s while the route's maxDuration is 300, so enabling it nightly would time out mid-run. Flip both this flag and RECEIPT_HUNT_COMPANY_IDS together once the per-company budget is measured.
[2026-08-10] Account 2012 reverted from BAS_REFERENCE and the EF F-skatt template moved to 2013 (#1409): the primary source (bas.se BAS 2026 v2 PDF) has no 2012 anywhere; the EF equity block is 2010/2011/2013/2017/2018/2019. 2012 "Avräkning för skatter och avgifter" is a Visma/Bokio/BL program convention that the swedish-year-end-closing skill had absorbed as if standard. Kept per-company charts that already have 2012 untouched (their history is legal and the account is a valid free slot); only the reference, the seeded template and the skill guidance change.
[2026-08-10] committed_at overrides audited via a dedicated COMMITTED_AT_OVERRIDE action and a SECURITY DEFINER writer (#1444): reusing SECURITY_EVENT would overload attack semantics onto a sanctioned backdate, and a plain INSERT inside set_committed_at() fails under the pg harness where SET ROLE service_role has no BYPASSRLS and audit_log has no INSERT policy. EXECUTE revoked from anon/authenticated so PostgREST cannot expose the writer as an audit-noise RPC.