Three flows in, three flows out
Original diagrams of the primary user journeys. Each step shows what the user does, what the system does in response, and every decision point. Nothing on this page is a screenshot.
From a flagged transaction table to a PDF in Slack.
Ask the report a question without generating a new one.
Change report.tex safely, with automatic rollback on failure.
Primary report generation
The user starts the conversation in Slack. PyGuard captures the thread identifier, decides whether it is a brand-new request or a follow-up, and (for new requests) orchestrates the fraud sub-agents to generate a PDF that lands back in the thread.
flowchart TD
A["User posts in Slack: Generate a fraud report"] --> B["slack service: capture thread_ts and channel_id"]
B --> C["process_chat_message stores the message, loads history"]
C --> D{"thread_ts already maps to a fraud_reports row?"}
D -->|yes| E["Phase 0: ReportFollowupAgent (Q&A or edit)"]
D -->|no| F["Phase 1: MemoryAgent classifies intent"]
F --> G{"Intent is fraud_analysis?"}
G -->|no| H["Other orchestrator path"]
G -->|yes| I["Phase 2-F: FraudOrchestratorAgent"]
I --> J["FraudDataAnalyst pulls SQL summaries"]
J --> K["FraudPatternAgent interprets the patterns"]
K --> L["FraudReportAgent renders LaTeX and compiles PDF"]
L --> M["Bundle written to backend/reports/fraud_report_<ts>/"]
M --> N["fraud_reports row inserted keyed by thread_ts"]
N --> O["Slack service uploads the PDF into the thread"]- Step 1
- User
- Posts a short message in Slack asking for a report.
- System
- The Slack service captures thread_ts and channel_id, stores the message, and forwards the request to the backend.
- Decision / outcome
- Does this thread already own a fraud_reports row? If yes, jump to the follow-up path; if no, continue to the Memory Agent.
- Step 2
- User
- Waits while the orchestration runs (typically 30-60 seconds).
- System
- FraudDataAnalyst pulls deterministic SQL summaries, FraudPatternAgent interprets them, FraudReportAgent builds the LaTeX document and compiles it with tectonic.
- Decision / outcome
- Bundle is written to backend/reports/fraud_report_<ts>/ and a fraud_reports row is inserted for the thread.
- Step 3
- User
- Opens the PDF in Slack and reviews it.
- System
- Slack service posts a short summary into the channel and uploads the PDF into the thread under that summary.
- Decision / outcome
- All subsequent replies in that thread will match the stored thread_ts.
Slack thread Q&A
Follow-up questions never generate a new report. A tiny classifier decides between question, edit, or a brand-new report, and binds the existing report as context for the Q&A agent.
flowchart TD
A["User replies in the PDF thread with a question"] --> B["slack service captures the same thread_ts"]
B --> C["process_chat_message receives thread_ts"]
C --> D{"fraud_reports row exists for this thread_ts?"}
D -->|no| E["Fall through to MemoryAgent (new report path)"]
D -->|yes| F["ReportFollowupAgent classifier picks qa / edit / new_report"]
F -->|qa| G["ReportQAAgent binds the report context"]
G --> H["Agent reads report tex, sections, data snapshot, live data tools"]
H --> I["Slack service posts narrative answer in the thread"]
F -->|new_report| E
F -->|edit| J["ReportEditorAgent path (see next diagram)"]- Step 1
- User
- Clicks 'Reply in thread' on the PDF message and asks a question.
- System
- process_chat_message receives the same thread_ts and finds the matching fraud_reports row. Phase 0 takes over before the Memory Agent runs.
- Decision / outcome
- ReportFollowupAgent's classifier picks qa, edit, or new_report from the user's wording.
- Step 2
- User
- Reads the answer and can keep asking follow-ups.
- System
- ReportQAAgent binds the report context, reads report.tex, sections, and the captured data snapshot, and optionally queries the live data tools.
- Decision / outcome
- No PDF is uploaded. The answer is plain text rendered in the Slack thread.
LaTeX edit with compile and rollback
Edits are the most sensitive path. PyGuard takes a per-report asyncio lock, copies the current tex and PDF into history, applies structured edits, and recompiles. If tectonic fails, the previous version is restored before the user sees anything.
flowchart TD
A["User replies in thread with an edit request"] --> B["ReportFollowupAgent classifier picks edit"]
B --> C["Per-report asyncio lock acquired for this fraud_reports.id"]
C --> D["ReportEditorAgent builds structured edits (JSON)"]
D --> E["apply_edits_and_recompile: back up to history/v{n}/"]
E --> F["Apply edits in memory, validate LaTeX structure"]
F --> G["Write report.tex atomically, run tectonic"]
G --> H{"tectonic compile succeeds?"}
H -->|yes| I["metadata.json bumped to new version"]
I --> J["fraud_reports row updated with new pdf_path and version"]
J --> K["Slack service uploads new PDF into the same thread"]
H -->|no| L["Restore previous report.tex and PDF from history/v{n}/"]
L --> M["Agent replies in thread with tectonic stderr tail"]
M --> N["Human decides whether to retry with a smaller edit"]- Step 1
- User
- Asks in the thread for a targeted change (rewrite, add, remove, tweak).
- System
- ReportFollowupAgent classifier picks edit. A per-report asyncio.Lock is acquired so concurrent edits on the same thread are serialised.
- Decision / outcome
- ReportEditorAgent builds a JSON array of structured edits: replace_section_body, insert_section_after, delete_section, or replace_text with a uniqueness guard.
- Step 2
- User
- Waits for confirmation.
- System
- apply_edits_and_recompile backs up the current tex and PDF into history/v{n}/, validates LaTeX structure, writes the new tex atomically, and compiles with tectonic (120-second timeout).
- Decision / outcome
- Compile success -> metadata.json version bumps, the fraud_reports row updates, Slack uploads the new PDF into the same thread. Compile failure -> rollback restores history/v{n}/ and the user sees the tectonic stderr tail.
- Step 3
- User
- Receives the updated report or a short failure note.
- System
- On success: 'Here is your updated Fraud Intelligence Report (v2):'. On failure: the prior PDF is still the active version and the user can retry.
- Decision / outcome
- Humans decide whether to retry, refine the request, or accept the current draft.