Status Board · Operations Ledger
Pick one counterpart and its KakaoTalk, email, and order sheets land on one calendar; the deal stage of hundreds of counterparts fits on one screen
Conversations with counterparts are split across KakaoTalk rooms, the order mailbox, and Slack, and Excel order sheets arrive separately as email attachments. The Status Board summarizes these records per day and places them in date order on a per-counterpart calendar. Pick one brand, vendor, or seller, and every record exchanged with that counterpart comes together on one screen. The Communication Status tab shows where each of hundreds of counterparts stands, from first contact to settlement. Records the rules could not match, and stages that are unclear, are decided by staff.
- The screen stays the same when the same record arrives again. Collectors resend the same record any number of times. It is stored with a single upsert statement that updates when (source, source_ref) matches. Rows edited by staff, and rows whose content is unchanged, are left alone. When content is unchanged the trigger does not bump the sequence number or timestamp either, so the screen stays the same after a resend.
- The author value is never taken from the caller. The intake function drops the author value the caller sends and rebuilds it as 'ingest:source'. The manual source is staff-only, so it is rejected. The 'edited by staff' flag is set by a trigger, not the app, and hidden records cannot be restored with UPDATE. The app role gets only execute permission on functions.
- When keys disagree, nothing is matched. The matching order is fixed: thread, business registration number, KakaoTalk room, email, phone, domain, account code, alias. If a business registration number or phone points to a different counterpart than the other evidence, it is flagged as a conflict and left in Unclassified. 13 public domains such as gmail and naver are not used as evidence.
- Concurrent collection does not create a counterpart twice. Only when creating a new counterpart, an advisory lock (a named lock shared across transactions) is taken per evidence value in sorted order, then the lookup runs again. If another transaction created the counterpart in the meantime, that one is used. Staff stage updates follow the same lock order as collection (counterpart → proposal), so there are no deadlocks.
- LLM output is checked against the original text. A stage evidence phrase is kept only if it is a substring of the masked body. Deal terms are stricter. Every number in the value must appear in the quote, and the keywords and units for that term type must match too. A company name is used only if its normalized name appears in the evidence line.
- Order-sheet tables are masked by column. Order sheets usually arrive as tables. Header columns such as 'recipient·orderer' are located, and values in those columns are masked only on rows with the same cell count as the header row. Product names and quantities therefore stay in the summary. A name next to a corporate marker such as '㈜' is treated as a company name and not masked.
- Cost is reserved before the call. The balance check and the reservation are one SQL statement, so two concurrent calls cannot both pass the limit. The reserved amount is the larger of an estimate based on maximum input/output and a floor (the minimum reservation). If settlement fails, later calls stop. A late retried summary fails the date comparison and cannot overwrite a newer summary.
- The mail cursor moves only after the ledger has received everything. Emails go into a local queue first and are then sent to the ledger. The Gmail history cursor moves only when every delivery in that round succeeds. If the cursor expires, the gap is backfilled by listing from the last successful time. An email that fails twice is quarantined and raises an alert.
- Hundreds counterparts on the board