Embalo is a web application (PWA) for professional drivers — Uber, 99, iFood, taxi, and private clients — who want to see the day’s profit, not only the platform’s revenue. It brings together revenue and expenses, the vehicle, the shift, routes, receipts, vouchers, and reports. It runs in the phone or computer browser, in Portuguese, on the domain embalomotorista.com.br. The code is vanilla JavaScript with Supabase (PostgreSQL, Auth, and Storage) and deploys on Vercel.

This technical case shows what the code does, the decisions that shaped it, and the real failures that became rules. The numbers were checked in the repository at the version of October 28, 2026 (commit c07ce82).

Transparency: Embalo was designed and is developed by Jeremias Cardoso, the author of this blog. The product is under implementation; this text does not claim a paying fleet or user metrics.

Update note (November 2, 2026): this post was rewritten as a technical case. The October 1, 2026 version was a product presentation. This one brings architecture, numbers, incidents, limits, and sources checked in the repository.

The problem: the platform statement is not the profit

The fare amount is not what stays with the driver. Fuel, maintenance, tolls, and idle time between calls come out of the same account, but they do not appear on the Uber or 99 statement. Without those numbers side by side, the week ends with revenue known and the result unknown.

A spreadsheet works while it fits one person and a file that does not disappear. The problem starts when the entry has to happen on the phone, on a bad network, with a photo of the receipt, a receipt for a company, and a history that survives changing devices. It is the same boundary at which control spreadsheets stop being enough and a system with a login and rules starts to make sense — the same logic that appears in EscalaOper for shift scheduling.

The system in numbers

Local measurement on November 2, 2026 of commit c07ce82 (the repository’s latest commit on October 28, 2026).

The system in numbers
MeasureValue
Application (main JS/CSS/HTML, without tests/docs/assets)28 files; about 22.7 thousand lines of code (non-blank and without comments)
Serverless API (api/)12 files; about 1.5 thousand lines of code
tests/ folder232 files (includes fixtures and reports; do not use the raw line total as “test code”)
Git historyabout 681 commits
Public domain nameembalomotorista.com.br

The count excludes node_modules, documentation, state, mockups, and assets. The central file app.js alone is over 500 KB on disk.

One account, two access points

The product decision is to treat the application and the portal as the same product: the same account, the same data, the same rules. The phone is not a reduced version. The stack chosen was deliberately thin on the front end: HTML, CSS with light/dark tokens, and JavaScript without a framework, so it fits in a pocket and on 4G.

The data lives in Supabase. Authentication, tables, and receipt photos go through it. Part of the plan rules and of monthly usage was moved to serverless functions and routes in api/, because leaving the browser as the only source of truth for the plan opens a hole: you can turn yourself Pro from the browser console. The June 2026 audit already marked that as a blocker before charging.

Embalo does not automatically import earnings from the platforms. The driver enters them by hand, by voice, or with help from reading the amount on the receipt photo. The advantage is gathering, in the same place, what each platform pays and what the car consumes.

What the code delivers

Financial. Revenue, expenses, and net result by period (month, last 30 days, year). A daily target, filters by category, and a photo attachment. The totals come from the driver’s own entries.

My Vehicle. Registration, mileage, maintenance with a forecast of the next replacement, and a daily checklist. The forecast starts from the latest record of each service type.

Shift and productivity. Start and end of shift with mileage; earnings per hour, cost per kilometer, hours worked, and the revenue category that earned the most in the month. Charts for the last seven days and by shift.

Routes. Stops that can be reordered; opening in Google Maps or Waze; order optimization in a beta version, which can fail when geocoding an address.

Receipts, clients, and vouchers. Numbered receipts in PDF, shareable on WhatsApp; vouchers with a status (pending, sent, under review, paid). The same axis of “finished artifact, human relationship” appears in ZapOrca and in the post about the cost of WhatsApp with AI.

Free, Pro, and Max plans. Limits on entries, photos, receipts, vouchers, and vehicles; a message in Portuguese when the ceiling is hit; a trial and a Pix subscription in the current design.

Architecture under constraint: what fits on the phone

The front end does not use React or Vue on purpose. The business logic lives in app.js and in satellite modules (voz-parser.js, assistente-voz.js, business-ui.js, report-ui.js). The service worker and the manifest close the PWA. The choice reduces the payload and avoids a build layer on the driver’s critical path; the price is a large monolithic file and the discipline of not mixing session state between accounts.

On the server, the routes in api/ concentrate what cannot trust the client: consuming the voice-command quota, reconciling monthly usage, and hardening the plan. The SQL versioned in sql/ documents the reconstruction of the database and the rollbacks of the plan migrations. The June audit already separated the “free-trial phase” (approvable with reservations) from “start charging” (failed until the server is the source of truth and account deletion exists in the app).

São Paulo appears in the product design because the UTC−3 timezone and the night driver are the typical audience for the date bug — not as city marketing. The public site embalomotorista.com.br is the storefront and the manual; this case describes the repository, not a finding about what is deployed at each moment in the Vercel dashboard.

Incidents that became rules

1. The plan limit that turned into “Something went wrong”

When Free hit the ceiling, the app threw an error with clear text in Portuguese (“Limite atingido… faça upgrade”). The catch went through traduzirErro(), built for Supabase’s English messages. No match hit, and the driver saw the generic fallback. The person who most needed to see the upgrade invitation did not see it. The fix marked the app’s own errors with jaAmigavel and began returning the original message. In app.js, the LIMITE_PLANO:* keys and the err.jaAmigavel branch are still there.

2. Double tap, a second vehicle, a ghost shift

On the phone, a second tap in the 700–1500 ms between success and the screen change resubmitted the form. On vehicle, the second insert broke the tab: carregarVeiculo() uses .maybeSingle() and the screen stayed in a permanent error. On shift, an open “ghost” shift was born. The rule that stayed: do not re-enable the button in the success finally while navigation has not finished, and set edit mode as soon as the vehicle is created.

3. After 21:00 in São Paulo, the day became tomorrow

Calendar dates used toISOString().slice(0,10) (UTC). In São Paulo (UTC−3), after 21:00 that is already the next day — exactly when the night driver closes the day. The seven-day chart and the shift reports shifted. The dataISOLocal / dataHojeISO helper began using the device timezone. The comment in app.js records the classic São Paulo bug on purpose.

4. A thousand lines with no warning

Queries that add up money asked for “everything” without .limit(). PostgREST returns at most 1,000 lines and does not warn about the cut. A driver who enters every trip passes that ceiling in about a year, and the annual report lies in silence. The minimum is to detect the ceiling and warn; real pagination was left as debt.

5. Voice copilot: offline, period, and quota

Three lessons checked in the code in October 2026: (a) “offline resilience” applies only to interpreting the text — speech recognition and saving to Supabase need a network, and there is no resend queue; (b) the query period by exclusion list accepted “em 2025” (“in 2025”) or “sábado” (“Saturday”) and landed on the wrong day — the safe guard is by inclusion of what the app knows how to calculate; (c) a command quota in localStorage is not the source of truth — the AI cost and the quota go through the server.

6. “Done” and “100% of the tests” with no proof

The task of making the server the source of truth for the plans arrived marked done without meeting the criterion. “100% of the tests” applied only to one copilot suite; the baseline of the whole suite produced 18 PASS and 21 FAIL that already existed. The rule that stayed in the project memory: a claim without evidence you can point to is a hypothesis.

7. A keep-alive that queried a column that does not exist

The robot that warmed Supabase every three days queried configuracoes?select=id, but the table’s key is chave, and the anon role does not read that table. The 4xx response was treated as success. The query was replaced; activity on the dashboard is not proved by a misleading HTTP status.

Tests: the machine matters

At the R19 check (October 28, 2026), run-all --fechamento on the owner’s machine with Edge produced 43/44 (1,060 PASS cases; one FAIL from TEMPO_CASO / Edge that did not finish in time; the same case passed on a 16/16 retest). In the cloud, the same round produced 38/44: the cloud does not have Edge and substitutes Chromium with a shim. The project memory records: a cloud suite is not the machine’s suite for screen and zoom.

This does not prove that Embalo is “ready to charge.” It proves that interface proof depends on the environment, and that a browser timeout does not become a product defect without repeating the case.

What is still missing / what this case does not prove

  • Adoption. The repository and this text do not state how many drivers use the product in production. Recruiting testers and store access appear as blocked tasks in the project state, not as completed facts.
  • Full charging. Before actually charging, the audit and the tasks still treat the server as the source of truth for the plans, account deletion through the app (LGPD), and a legal review of the policy and the terms as go-live pending items. The free-trial phase and the charging phase are different verdicts in the June report.
  • Platform import. It remains manual. There is no Uber/99/iFood connector in the code of this version.
  • Real offline. There is no queue of entries for when the network comes back. The copilot interprets text locally; saving and listening depend on the internet.
  • Pagination of the totals. PostgREST’s ceiling of 1,000 lines remains a structural risk if usage grows without .range() or a sum in the database.
  • Secrets and dashboards. Details of keys, full RLS policies, and SMTP configuration stay out of this text on purpose.
  • PDF and spreadsheet libraries. The memory records versions and known warnings (jsPDF, SheetJS); this case does not re-read the supply chain of each front-end dependency.

Lessons that hold outside the driver app

  1. A friendly message that the catch swallows does not exist. Mark the app’s own error, or the user sees a generic one.
  2. Timezone is a product requirement for people who work at night. toISOString on a calendar date lies after 21:00 in São Paulo.
  3. A silent API ceiling is an accounting lie. If the sum can be cut, the cut has to show up.
  4. “Offline” and “100% of the tests” require a written definition. Without one, they become internal marketing.
  5. Screen proof in the cloud ≠ proof on the user’s machine. The environment is part of the evidence.

Sources

  • Code, documentation, the pre-production audit, project memory, and history: the motorista-pro repository (Embalo) at commit c07ce82 (October 28, 2026), read by the author; extract with passages, the counting method, and the limits, verified on 2026-11-02. The full suite was not rerun for this writing; the suite results cited come from the R19 report of October 28, 2026 versioned in the repository.
  • Public product manual at embalomotorista.com.br (presentation and demonstration screenshots), checked at the launch version of the original post.