DEV Community

Akrm Al-Qubati
Akrm Al-Qubati

Posted on

Daftar Closing Agent: Gemini plans the shop’s day; the phone commits the books

Daftar Closing Agent: Gemini plans the shop's day; the phone commits the books

This piece was created for the purpose of entering the All Things Agentic Hackathon.

Yemeni and MENA shops still close the day on paper. Balances live in memory. Collections wait until someone remembers who owes. That chore is the friction I built for — Taskmaster, not a chatbot.

Daftar Closing Agent (وكيل إغلاق الدفتر) treats the merchant's business day as the long-running task. Each Cloud Run POST /run is fast on purpose. Cloud Run may scale to zero. The durable work sits on the phone in Drift.

What the agent actually does

Mid-day. Voice or text (Mohamed owes 500 sugar). Gemini 3.5 Flash on Vertex AI, via Google ADK on Cloud Run, emits a proposal. Money hits Drift only after Confirm.

Close. One plan. One tap: Confirm & Send Statements. Then a device-owned ritual: Drift localDay summary → Google Drive backup → FIFO aging → Collections Desk → POST /v1/email/send-batchsmtp.gmail.com.

Aging ranks who is emailed (min(shortlist, 20)). MIME PDF statements go to the ranked Top 5. The rest get a fixed text-only reminder. Gemini does not pick recipients and does not send mail. Confirm without sending still closes the books; send-batch is never called.

Contest architecture: Merchant and Flutter HITL on the left path; Cloud Run ADK agent and eight frozen tools; Drift source of truth to Drive backup and aging desk; send-batch to smtp.gmail.com; Architecture HUD observes SMTP

Figure 1. Contest architecture. Gemini proposes; the merchant confirms; Drift commits. POST /v1/email/send-batch is a sibling FastAPI route — not an ADK tool. The Architecture HUD observes SMTP; it does not feed the confirm gate.

Why this stack (Architectural Discipline)

Official ask What Daftar does
Decouple /run emits proposals only. Drift commits money. SMTP is a sibling FastAPI route, not a FunctionTool.
State Drift + device ClosingAgentPhase. Scoped daftarContext. No vector database — a debt ledger is a financial source of truth, not a RAG corpus.
Credentials Cloud Run IAM + ID tokens with custom audiences. No allUsers. Gmail App Password in Secret Manager only (gmail-smtp-app-password), never in Flutter.
Failures Confirm-gate lock per proposalId. Drive skip does not abort close. Per-row SMTP failed / skipped. Cloud Run down → ledger intact. Durable send key is device batchId.

One LlmAgent named closing_agent. Eight frozen FunctionTools. No send-email tool. No SequentialAgent. Mid-day capture is one intent-dependent hop. The close ritual is device-deterministic after one plan confirm.

The offline Drift ledger, Drive backup, and Khazna UI are pre-existing substrate (disclosed). The agentic close path — ADK, Gemini 3.5, HITL, Gmail SMTP, Architecture HUD — was built during the Submission Period (3–31 August 2026).

Proof of Action

SMTP 250 means Gmail accepted the message. It is not mailbox-delivered, not a read receipt, and not a delivery webhook. Gmail SMTP has none. I did not fake delivered. I did not film WhatsApp as the send.

Proof is Cloud Logging daftar.agent.email (unique Message-ID + 250) plus the recipient inbox: MIME PDF on the ranked Top 5, text-only remainder by design.

Google Cloud Logging: jsonPayload event daftar.agent.email, smtpCode 250, status sent, masked recipient, Message-ID at gmail.com, Cloud Run stdout on project daftar-closing-agent

Figure 2. Cloud Run logs on project daftar-closing-agent. smtpCode: 250 and status: sent are server accept. toMasked keeps the inbox private. This is not proof the mailbox received the mail.

Daftar statement PDF for Omar in USD: FIFO invoices for rice, sugar, and groceries; net balance −150.00 USD

Figure 3. Device-rendered statement for **Omar* (integer USD cents, shown as dollars). On close-the-day, Omar is in the text-only remainder. MIME PDFs attach only for the ranked Top 5 (for example Mohamed). Gemini does not choose who gets a PDF.*

The on-device Architecture HUD shows the same close: model gemini-3.5-flash, tool propose_closing_plan, HITL rail (Propose → Confirm → Commit → Rank), and Sent · Message-ID last-8. The HUD observes; it does not confirm money or send mail.

Architecture HUD over Day closed: Cloud Run gemini-3.5-flash, propose_closing_plan, HITL rail, Sent Message-ID last-8, 13 tasks sealed, Drive backup, 7 overdue

Figure 4. After close. Thirteen ritual tasks sealed, backup on Drive, seven overdue in the send set. Sent · is Message-ID last-8, matching logs. Today's Drift localDay on this frame is two small debts (USD 3.50) — the sample-store today strip, not the filmed Mohamed $500 capture.

What I learned

The agent should be a clerk who proposes, not a cashier who writes the books. For an offline merchant, the phone is the system of record. Frozen tools are cheaper to reason about than a ninth “just send it” tool.

Repo: github.com/akrmcodes/daftar-closing-agent

Hackathon: All Things Agentic · Track: Taskmaster · Individual

Top comments (0)