feat(agent): 'Vad din agent vet' page rendering the ledger context (P2) (#935)

* feat(agent): 'Vad din agent vet' page rendering the ledger context (P2)

The human-facing surface for the openwiki ledger-context: a read-only page
that renders the exact payload the AI agent reads (Accounted://ledger/context
+ the briefing digest) as a legible profile of how this company books.

- Route app/(dashboard)/agent-knowledge (server component) calls the shared
  buildLedgerContext(supabase, companyId) directly: one payload, two
  renderers, no new API or data path.
- Sections mirror the payload 1:1: coverage/freshness strip, counterparty
  patterns (monochrome confidence bars + seen/agree evidence), supplier
  patterns, explicit rules shown as authoritative instructions distinct from
  observed patterns, account usage, VAT profile, conventions.
- Nav entry in the Analys group (icon Brain), ungated so it doubles as an
  upsell; flip requiredCapability to paywall.
- Design per .claude/rules/design.md (PageHeader, Card, Table, Badge,
  AccountNumber BAS tooltips); sv + en strings (agentKnowledge namespace).
  VAT/BAS labels stay Swedish in both locales per i18n rules.

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

* feat(agent): deep entity-resolved analysis + radial graph for the knowledge page

Reworks the 'Vad din agent vet' page from tables into a radial-hub graph
driven by a new full-history deep analysis, per founder feedback.

- fix(rpc): median_booking_lag_days now measures real posting promptness via
  committed_at, not entry_date (which the bank flow sets to the transaction
  date, giving a ~0 tautology: 151/152 on prod). migration 20260708120000.
- feat(rpc): get_ledger_deep_context (migration 20260708130000): full-history,
  deterministic, read-side. Merges counterparties by normalize_counterparty_key
  (e.g. Claude = 14 bookings across 12 name variants, weekly, 9 710 kr, always
  5420), mines booked verifikat for SEK spend (coalesce amount_sek), detects
  recurrence cadence, dominant account + share, plus supplier entities. Storno
  excluded, corrections kept; 19xx/26xx excluded from the dominant contra.
- LedgerGraph: radial SVG (company center, accounts inner ring, payees outer
  ring), hover/focus reveals variants + spend + cadence + account. Keyboard
  focusable nodes with per-node accessible names + a screen-reader data table.
- Page fetches the deep context alongside the light context; coverage strip
  gains tracked-payee / recurring / tracked-spend stats. sv + en strings.
- 14 pg tests (light + deep) green; both RPCs applied to prod + version-matched.

Reviewed by an adversarial multi-lens pass (accounting/SQL, frontend/a11y,
prod-fact verification); all four verified findings fixed (SEK currency,
storno-lag guard, keyboard a11y, spacing tokens).

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

* feat(agent): gentle mount fade-in for the radial map (reduced-motion safe)

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

* fix(agent): render the page for a rules-only company (empty-state edge case)

isEmpty ignored explicit_rules, so a company with configured mapping rules
but no posted transactions hit the 'hasn't learned anything' empty state and
lost its rules section. Rules are independent of bookings. (CodeRabbit)

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

* feat(agent): show Kompetens (skills) + Fakta (memory) on the knowledge page

The 'Vad din agent vet' page now shows the full picture of what the agent
knows: alongside the booking map, a compact read-only view of its Kompetens
(the Swedish accounting/tax knowledge atoms it ships with, grouped by
tier as chips with active/dormant state) and the Fakta it remembers (top
learned facts with kind + source), each linking to /settings/assistant for
full management. Server-rendered via a new buildAgentCompetence() that
mirrors GET /api/agent/skills + /api/agent/memory. Also renders in the
no-bookings case so a new company still sees its agent's competence.
sv + en strings.

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

* refactor(agent): restructure knowledge page - hero graph + tabbed detail

Declutters the page per feedback: the booking map is the always-visible hero,
and the supporting detail (Kompetens · Minne · Regler & profil) moves into
tabs so only one view shows at a time instead of a long card stack. Split
AgentCompetenceSections into standalone CompetenceCard + FactsCard for the
tabs; removed the top stat row on request. sv + en.

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

* feat(agent): Reconciliation Aurora rewrite of the ledger knowledge graph

Full rewrite of LedgerGraph: node area = sqrt(spend), colour = cadence,
shape = supplier/counterparty, confidence = depth-of-field; on-mount
descriptor-collapse animation with xN badge; cadence pulse veins;
deterministic seeded layout; framer-motion only (no new deps); keyboard
navigation, reduced-motion and sr-only support. Build-verified; 3-lens
adversarial review findings fixed.

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

* fix(agent): sample-size-honest confidence in the ledger knowledge graph

dominant_account_share was raw cnt/total, so a counterparty with a single
booking rendered as '100% säkerhet': fake certainty by construction (the
data_quality_master Item-C / P3 finding). New migration replaces
get_ledger_deep_context with a Laplace-smoothed share (cnt+1)/(total+2)
(1/1 -> 0.67, 3/3 -> 0.80) and exposes the raw evidence as
dominant_account_count / dominant_account_total. The detail card now shows
'Bokförd hit i k av n fall' under the confidence bar; the existing focus
buckets, stroke widths and percent labels inherit the honest value
unchanged. pg-real test updated to guard the n=1 case.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-12 21:25:34 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent 64ea0fef02
commit 4b51af3d80
13 changed files with 1914 additions and 1 deletions
+111
View File
@@ -67,6 +67,7 @@
"dashboard": "Overview",
"home": "Home",
"assistant": "Assistant",
"agent_knowledge": "What your agent knows",
"bureau": "Bureau",
"kpi": "KPIs",
"invoice_inbox": "Documents",
@@ -128,6 +129,116 @@
"ext_tic": "Company profile",
"ext_invoice_inbox": "Document inbox"
},
"agentKnowledge": {
"title": "What your agent knows",
"description": "How your company books, derived from your own accounting. This is the context your assistant reads before categorizing or creating vouchers.",
"meta_window": "Period",
"meta_posted_entries": "Posted vouchers",
"meta_lag": "Typical booking lag",
"meta_lag_value": "{days} days",
"meta_computed": "Updated",
"empty_title": "Your assistant hasn't learned anything yet",
"empty_description": "As you book transactions, the assistant learns how your company codes each counterparty, supplier and account.",
"empty_action": "Book transactions",
"graph_title": "Booking map",
"graph_description": "How the assistant sees your counterparties and suppliers flow into the chart of accounts. Colour shows rhythm, size the amount, and sharpness how sure the booking is. Hover a node for details.",
"graph_hint": "Hover a node to see how it's booked.",
"graph_account_caption": "{count} counterparties",
"graph_payee_caption": "seen {seen}× · {share}% agree",
"graph_aria": "Radial map: {payees} counterparties linked to {accounts} accounts.",
"graph_truncated": "showing the most frequent",
"legend_account": "Account",
"legend_counterparty": "Counterparty",
"legend_supplier": "Supplier",
"legend_edge": "Line strength = confidence",
"meta_entities": "Tracked payees",
"meta_recurring": "Recurring",
"meta_tracked_spend": "Tracked amount",
"cap_bookings": "{n} bookings",
"cap_variants": "{n} variants",
"cadence_weekly": "~weekly",
"cadence_monthly": "~monthly",
"cadence_quarterly": "~quarterly",
"cadence_irregular": "irregular",
"graph_replay": "Replay",
"graph_tally": "transactions · {payees} counterparties · {accounts} accounts, understood",
"legend_size": "Size = amount",
"legend_focus": "Sharpness = confidence",
"card_variants": "Name variants",
"card_confidence": "{share}% confidence",
"card_evidence": "Booked here in {k} of {n} cases",
"comp_title": "Competence",
"comp_desc": "Knowledge areas of Swedish bookkeeping and tax the assistant builds on. Core competence applies to everyone; industry and company adaptation is chosen for your company.",
"comp_empty": "No knowledge areas yet.",
"comp_count": "{total} knowledge areas · {active} active",
"comp_manage": "Read more",
"tier_horizontal": "Core competence",
"tier_vertical": "Tailored to your industry",
"tier_modifier": "Your company situation",
"badge_dormant": "Dormant",
"facts_title": "Facts the assistant remembers",
"facts_desc": "Notes the assistant uses to give you the right advice. Pinned facts are always included.",
"facts_empty": "No memories yet.",
"facts_more": "+{n} more",
"facts_manage": "Manage memory",
"facts_pinned": "Pinned",
"kind_fact": "Fact",
"kind_preference": "Preference",
"kind_pattern": "Pattern",
"kind_correction": "Correction",
"source_composer": "Loaded at startup",
"source_user_taught": "You taught me",
"source_agent_learned": "I noted",
"source_derived": "Derived",
"tab_competence": "Competence",
"tab_memory": "Memory",
"tab_config": "Rules & profile",
"footer_basis": "Based on {entries} posted vouchers · updated {date}",
"cp_title": "Booking patterns by counterparty",
"cp_description": "The most frequent counterparties from the bank feed and how they are usually booked. The evidence is historical frequency, not a guarantee: the assistant suggests, you approve.",
"sup_title": "Patterns by supplier",
"sup_description": "Derived from supplier invoices: the dominant expense account and VAT treatment per supplier.",
"rules_title": "Your rules",
"rules_description": "Rules you set yourself. They are instructions and take precedence over observed patterns.",
"acc_title": "Account usage",
"acc_description": "Accounts actually in use, the 20 most frequent over the last 12 months.",
"vat_title": "VAT profile",
"vat_description": "Observed VAT handling.",
"conv_title": "Conventions",
"conv_description": "How your company is set up.",
"col_counterparty": "Counterparty",
"col_supplier": "Supplier",
"col_account": "Account",
"col_vat": "VAT",
"col_evidence": "Confidence",
"col_last": "Last",
"col_rule": "Rule",
"col_match": "Match",
"col_postings": "Postings",
"col_last_used": "Last used",
"ev_counts": "{agree} of {seen} times",
"src_observed": "Observed",
"src_template": "Learned",
"src_rule": "Rule",
"none_cp": "No counterparty patterns yet.",
"vat_registered_label": "VAT registered",
"vat_period_label": "Reporting period",
"vat_treatments_label": "VAT types used",
"vat_no_treatments": "None yet",
"conv_method_label": "Accounting method",
"conv_series_label": "Voucher series",
"conv_salary_label": "Payroll run active",
"conv_lag_label": "Typical booking lag",
"method_accrual": "Accrual (invoice) method",
"method_cash": "Cash method",
"method_unknown": "Not set",
"period_monthly": "Monthly",
"period_quarterly": "Quarterly",
"period_yearly": "Yearly",
"unknown": "Not set",
"yes": "Yes",
"no": "No"
},
"auth": {
"login_title": "Sign in",
"login_subtitle": "Sign in to manage your finances",
+111
View File
@@ -67,6 +67,7 @@
"dashboard": "Översikt",
"home": "Hem",
"assistant": "Assistent",
"agent_knowledge": "Vad din agent vet",
"bureau": "Byrå",
"kpi": "Nyckeltal",
"invoice_inbox": "Underlag",
@@ -128,6 +129,116 @@
"ext_tic": "Företagsprofil",
"ext_invoice_inbox": "Dokumentinkorg"
},
"agentKnowledge": {
"title": "Vad din agent vet",
"description": "Så här bokför ditt företag, härlett ur din egen bokföring. Det här är sammanhanget din assistent läser innan den kategoriserar eller skapar verifikationer.",
"meta_window": "Period",
"meta_posted_entries": "Bokförda verifikationer",
"meta_lag": "Typisk bokföringstid",
"meta_lag_value": "{days} dagar",
"meta_computed": "Uppdaterad",
"empty_title": "Din assistent har inte lärt sig något än",
"empty_description": "När du bokför transaktioner lär sig assistenten hur ditt företag konterar varje motpart, leverantör och konto.",
"empty_action": "Bokför transaktioner",
"graph_title": "Konteringskarta",
"graph_description": "Så här ser assistenten dina motparter och leverantörer flöda in i kontoplanen. Färgen visar rytmen, storleken beloppet och skärpan hur säker konteringen är. Peka på en nod för detaljer.",
"graph_hint": "Peka på en nod för att se hur den bokförs.",
"graph_account_caption": "{count} motparter",
"graph_payee_caption": "sedd {seen} ggr · {share}% lika",
"graph_aria": "Radiell karta: {payees} motparter kopplade till {accounts} konton.",
"graph_truncated": "visar de vanligaste",
"legend_account": "Konto",
"legend_counterparty": "Motpart",
"legend_supplier": "Leverantör",
"legend_edge": "Linjestyrka = träffsäkerhet",
"meta_entities": "Spårade motparter",
"meta_recurring": "Återkommande",
"meta_tracked_spend": "Spårat belopp",
"cap_bookings": "{n} bokningar",
"cap_variants": "{n} varianter",
"cadence_weekly": "~veckovis",
"cadence_monthly": "~månadsvis",
"cadence_quarterly": "~kvartalsvis",
"cadence_irregular": "oregelbundet",
"graph_replay": "Spela upp igen",
"graph_tally": "transaktioner · {payees} motparter · {accounts} konton, förstådda",
"legend_size": "Storlek = belopp",
"legend_focus": "Skärpa = säkerhet",
"card_variants": "Namnvarianter",
"card_confidence": "{share}% säkerhet",
"card_evidence": "Bokförd hit i {k} av {n} fall",
"comp_title": "Kompetens",
"comp_desc": "Kunskapsområden om svensk bokföring och skatt som assistenten bygger på. Kärnkompetensen gäller alla; bransch- och bolagsanpassningen väljs utifrån ditt företag.",
"comp_empty": "Inga kunskapsområden ännu.",
"comp_count": "{total} kunskapsområden · {active} aktiva",
"comp_manage": "Läs mer",
"tier_horizontal": "Kärnkompetens",
"tier_vertical": "Anpassat för din bransch",
"tier_modifier": "Din bolagssituation",
"badge_dormant": "Vilande",
"facts_title": "Fakta assistenten minns",
"facts_desc": "Anteckningar assistenten använder för att ge dig rätt råd. Fästa fakta skickas alltid med.",
"facts_empty": "Inga minnen ännu.",
"facts_more": "+{n} till",
"facts_manage": "Hantera minne",
"facts_pinned": "Fäst",
"kind_fact": "Fakta",
"kind_preference": "Preferens",
"kind_pattern": "Mönster",
"kind_correction": "Korrigering",
"source_composer": "Inläst vid uppstart",
"source_user_taught": "Du lärde mig",
"source_agent_learned": "Jag noterade",
"source_derived": "Härlett",
"tab_competence": "Kompetens",
"tab_memory": "Minne",
"tab_config": "Regler & profil",
"footer_basis": "Baserat på {entries} bokförda verifikat · uppdaterad {date}",
"cp_title": "Konteringsmönster per motpart",
"cp_description": "De vanligaste motparterna från bankflödet och hur de brukar bokföras. Underlaget är historisk frekvens, inte en garanti: assistenten föreslår, du godkänner.",
"sup_title": "Mönster per leverantör",
"sup_description": "Härlett ur leverantörsfakturor: dominerande kostnadskonto och momstyp per leverantör.",
"rules_title": "Dina regler",
"rules_description": "Regler du själv satt. De är instruktioner och går före observerade mönster.",
"acc_title": "Kontoanvändning",
"acc_description": "Konton som faktiskt används, de 20 vanligaste de senaste 12 månaderna.",
"vat_title": "Momsprofil",
"vat_description": "Observerad momshantering.",
"conv_title": "Konventioner",
"conv_description": "Hur ditt företag är inställt.",
"col_counterparty": "Motpart",
"col_supplier": "Leverantör",
"col_account": "Konto",
"col_vat": "Moms",
"col_evidence": "Träffsäkerhet",
"col_last": "Senast",
"col_rule": "Regel",
"col_match": "Matchning",
"col_postings": "Bokningar",
"col_last_used": "Senast använt",
"ev_counts": "{agree} av {seen} ggr",
"src_observed": "Observerat",
"src_template": "Inlärt",
"src_rule": "Regel",
"none_cp": "Inga motpartsmönster än.",
"vat_registered_label": "Momsregistrerad",
"vat_period_label": "Redovisningsperiod",
"vat_treatments_label": "Momstyper som använts",
"vat_no_treatments": "Inga än",
"conv_method_label": "Bokföringsmetod",
"conv_series_label": "Verifikationsserier",
"conv_salary_label": "Lönekörning aktiv",
"conv_lag_label": "Typisk bokföringstid",
"method_accrual": "Fakturametod",
"method_cash": "Kontantmetod",
"method_unknown": "Ej angiven",
"period_monthly": "Månadsvis",
"period_quarterly": "Kvartalsvis",
"period_yearly": "Årsvis",
"unknown": "Ej angiven",
"yes": "Ja",
"no": "Nej"
},
"auth": {
"login_title": "Logga in",
"login_subtitle": "Logga in för att hantera din ekonomi",