feat(agent): one-tap "clear the proposals I didn't approve" (BoXon feedback) (#1798)

When the assistant stages several verifikat and the user approves only some,
the rest lingered as pending_operations in Granskning until the 30-day expiry —
manual per-item cleanup. Now:

- lib/agent/pending/reject-conversation-pending.ts rejects a conversation's
  still-pending proposals in one update (guarded on status='pending' so it never
  stamps over a committed verifikat; company-scoped; keyed on the conversation).
- POST /api/agent/conversations/[id]/reject-pending — the chat's "Rensa förslag
  som inte godkänts" button (appears when the thread has staged proposals; drops
  the cards from view).
- Auto-clear on archive: archiving a thread ("I'm done") clears its leftover
  proposals in the PATCH, best-effort.

Kept durable-by-default (proposals still come back on resume) — the button is
explicit user intent, not an auto-reject on every panel close, so resume still
works. 10 tests (helper + endpoint 401/404/404/happy); lint + guards + scoped
typecheck clean. UI button awaits founder visual sign-off.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-22 10:42:22 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Opus 4.8
parent 13b69a2056
commit 1f7acbf144
7 changed files with 243 additions and 0 deletions
+1
View File
@@ -1156,3 +1156,4 @@ One line per decision: `[YYYY-MM-DD] <decision>: <why>`. Appended by agents and
[2026-08-21] Confidence honesty fix, driven by a real backtest (scripts/backtest-categorize.ts, read-only: runs the real cascade on already-booked prod transactions and scores the model's pick vs the human's actual account). Backtest exposed the selector reporting 0.95 on pure category guesses → "säker" was a lie (high-conf picks only 52% accurate). Fix: confidence is now driven by DETERMINISTIC BACKING (the confidence of a candidate that independently points at the chosen account), not the model's verbalized confidence (which the backtest showed is ~always "high"). A backed pick takes the candidate confidence, reduced only when the model is unsure (BACKED_MODEL_FACTOR); an UNBACKED pick (category guess no candidate agreed with) is capped at 0.7 — below the säker band (0.8) — so a guess is never "säker". Re-backtest: säker accuracy 52% → 73%, and far fewer picks claim säker (only template-backed ones). Still not auto-book-grade (~73%, want ~95%); auto-book stays off until isotonic calibration on real approvals. Backtest caveats: exact-account match is strict (penalizes reasonable-but-different picks + companies' idiosyncratic charts), sample is established users (cold-start majority has no ground truth yet), backtest ran samples=1 (no self-consistency). Some confident-wrong cases are POISONED templates (a past mis-booking → wrong candidate the model correctly follows), a data-quality issue not fixable in the confidence math.
[2026-08-21] Behandlingshistorik ships as a report over existing stores (journal_entries.committed_at + audit_log + rattelse log + import tables) rather than on processing_history: that table only carries Document/BankTransaction/System events in prod, while audit_log is complete, immutable and already the archive's revision/behandlingshistorik.json. Event labels stay Swedish in both locales (räkenskapsinformation, archived 7 years, same rule as SIE/grundbok); only the view chrome is translated. Bokföringsposter come from journal_entries (not audit COMMIT rows) so entries predating the audit log or from the July SIE-import window are never missing.
[2026-08-21] Peppol access is granted per company by the operators, never self-served (peppol_access table, locked by default): every transmission is billed per document by Qvalia and every receiving identifier consumes a contracted tenant slot, so the company asks from Settings > Fakturering (request row + e-mail to support) and we enable it with scripts/peppol/access.ts, setting max_sends (null = no cap) and separately receive_enabled; the send route refuses PEPPOL_ACCESS_REQUIRED / PEPPOL_SEND_LIMIT_REACHED before touching the invoice, and registration refuses PEPPOL_RECEIVING_NOT_ENABLED. Founder call 2026-08-21 after the first open-for-all hour in prod.
[2026-08-22] BoXon feedback fix: leftover assistant proposals no longer need manual cleanup in Granskning. lib/agent/pending/reject-conversation-pending.ts rejects a conversation's still-pending pending_operations in one update (guarded on status='pending' so it never stamps 'rejected' over a committing/committed verifikat; company-scoped; filtered by agent_metadata->>conversation_id). Surfaced two ways: POST /api/agent/conversations/[id]/reject-pending (the chat's "Rensa förslag som inte godkänts" button) + auto-clear when the conversation is ARCHIVED (best-effort in the PATCH; archive = "I'm done with this thread"). Kept durable-by-default (proposals still resume) — the button is explicit user intent, not an auto-reject on every panel close, so resume still works. Chat button drops all staged cards from view on click (committed ones are already booked; the card was only a confirmation). UI PR: needs founder visual sign-off on the button.