Three MCP feedback reports (seq 265062, 288474, 288577; three companies) hit
the same hole: link_document_to_voucher attaches the document to the
verifikat but never stamps the inbox item it came from, and both inbox read
surfaces derive "handled" from the inbox row's own link columns, never from
document_attachments.journal_entry_id. So an attached document stayed
"unprocessed" forever: agents re-saw it as missing underlag (one reporter
paged 750 rows to find the ~15 real ones), and one user read the five
leftover rows as duplicates and was about to delete the only copies of
underlag sitting on posted verifikat.
- commitLinkDocumentToVoucher and the bulk twin now stamp
invoice_inbox_items.created_journal_entry_id, keyed on document_id, CAS on
both link columns, 23505 tolerated (samlingsverifikat). Same shape as the
create_voucher + inbox_item_id stamp; best-effort so inbox bookkeeping
never rolls back a committed link.
- gnubok_list_unmatched_documents returns file_name (same embed
list_inbox_items uses) and the extraction's page coverage, so an agent can
tell "no total on this document" from "we read 3 of 38 pages" and does not
have to fetch each document to learn what it is (seq 265062, 288574).
- gnubok_list_transactions_without_documents no longer echoes the column
default "uncategorized" on rows that are booked by construction:
list_uncategorized_transactions uses the same word for "no journal entry
yet", and an agent read the label and tried to re-book an already-booked
share-capital deposit (seq 288574).
No tools/list payload change: the unmatched-documents item schema is
untyped and the category field already allowed null.
Claude-Session: https://claude.ai/code/session_013yw62FMXGSzo6icFDiBwP3
Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.
Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.
Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.
Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(mcp): unify missing-document surfaces on one predicate (P1-3)
The two MCP surfaces told different truths: the transactions tool keyed
'has underlag' on transactions.document_id while the verifikat tool
keyed on document_attachments — and neither respected the source-type
semantics, version chains, or journal_entry_no_doc_required waivers
that lib/worklist's canonical count applies. Measured on prod: 22,046
waived verifikat still listed to agents, 2,370 doc-exempt source types
listed, ~87 docs attached to transactions but never propagated to the
verifikat, 1,100 transactions flagged missing-receipt although their
verifikat HAS the underlag.
One predicate now lives in SQL — posted, needs-doc source type
(mirrors NEEDS_DOC_SOURCE_TYPES), no current-version doc, no waiver:
- verifikat_without_documents RPC v2 adopts the canonical predicate.
- New transactions_without_documents RPC: the bank-driven subset of the
same predicate, joined through transactions.journal_entry_id — a
strict subset of the verifikat surface by construction. Rows expose
qualified transaction_id (P1-2 forward-compat); bare id deprecated.
- Both tools become thin RPC wrappers; descriptions state the actual
set relationship.
- lib/worklist countVerifikatMissingDocument delegates to the RPC
(previously three full-table pulls set-differenced client-side) —
badge count and agent surfaces can no longer drift.
- Backfill: propagate transaction-attached docs to their verifikat
where the attachment was never linked (open periods only; never
steals a doc linked to another verifikat).
pg-real: fixture matrix (no-doc/with-doc/waived/stale-version/
doc-exempt-source/import), strict-subset assertion, per-source-type pin
of the SQL list against the TS constant, tenant guard.
Part of dev_docs/mcp_optimization_plan.md (P1-3).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(mcp): explicit grants restated + count-call comment (#876 review)
- Restate REVOKE/GRANT on verifikat_without_documents so the migration
is self-contained (CREATE OR REPLACE preserves the 20260703130000
grants — verified on prod: authenticated + service_role only).
- Comment on the p_limit:1 count call: total_count is computed over the
full filtered set, independent of page size.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>