Insp## Inspiration

A nail salon in Mexico has an AI employee. It answers customers on WhatsApp, books them, reads the transfer receipts they send, proposes the day's reconciliation for the owner to confirm, and every Monday tells her the three things worth money this week and which clients have stopped coming. It has done this every day since June 2026.

That platform is mine and it pre-existed this hackathon. I am saying so in the first paragraph because the honest version of this submission is more interesting than the flattering one.

Standing that one business up took a one-hour interview and a forty-field configuration file I wrote by hand. That is why it serves one business and not fifty.

What it does

What I built during the submission period is the thing that removes the hand.

An owner describes their business in one paragraph, the way they would describe it to a friend. The system produces the business profile and the advisory brain for their trade: which numbers matter in that trade, what counts as healthy, and which advice is worth the owner's Monday morning.

It generates that brain, validates it, rehearses it against two real months, refuses it when it is wrong, and holds it until a person signs their name.

Anyone can generate a chatbot. This generates the consultant.

Three things separate it from a form:

  • It infers instead of collecting. The input is prose, not fields. Timezone from the city, language from how the owner writes, industry and hours from context.
  • It decides in code what may block. A hand-written list blocks on three things plus prices. Everything else is asked without stopping the owner, and every assumption is shown back to them.
  • It manufactures an artefact and then argues with it. The generator proposes a brain, a validator refuses it, and a rehearsal runs the proposed rules against two months of real numbers before a human ever sees them.

How I built it

Python 3.11, Flask and gunicorn on Cloud Run (scales to zero), in front of Cloud SQL for PostgreSQL over a mounted unix socket that never touches the public internet, with Gemini 3.5 Flash on Vertex AI through the google-genai SDK, and Secret Manager referenced in the deploy manifest so no password is ever an environment variable.

The design decision everything else follows from: the model proposes data, never code. A rule is a JSON object evaluated by an interpreter I wrote, so there is no eval anywhere in the project. Between the model's proposal and the owner sit three refusals:

  • a rule citing a metric the system does not compute is rejected, because it could never fire;
  • a benchmark without a source is rejected;
  • a rule claiming a bigger gain than the business makes in a month is rejected, because the advisor ranks by impact and one inflated figure buries every honest rule beneath it.

python tests/test_rules.py runs with no credentials and no database, in under a second, and prints those refusals firing.

One script stands the whole system up on an empty Google Cloud project in about fifteen minutes: APIs, database, schema, demo data, IAM, build and deploy. Seventeen tables, every business row scoped to the business it belongs to.

Challenges I ran into

The headline claim was inverted in the code, and the bug damaged people in plural. A brain belongs to a trade, shared by every business in it. Onboarding the second veterinary clinic regenerated it, and ratifying it overwrote the brain the first clinic's owner had read and signed. Owner One approved five recommendations and ended up running the single one Owner Two approved, under Owner Two's name. The code guarded the individual business with a 409 and guarded the shared template not at all, which is exactly backwards: a name collision hurts one business, a template overwrite silently rewrites the advice every business in that trade receives.

The worst failures do not error. The tenant travels in the page URL as ?t=, and the dashboard's own API calls dropped it, so the server fell back to the default tenant and served the seeded demo business to whoever was looking. Opening another tenant's dashboard rendered someone else's clients, services and revenue, with a 200 and a clean log. It survived because every request carried the correct tenant in its Referer header while the request line had none, so a log skim reads as healthy. Twenty-eight calls across six screens now carry it explicitly.

Credential checks can answer correctly everywhere except production. The code asked whether GOOGLE_APPLICATION_CREDENTIALS was set. Cloud Run hands the attached service account's credentials to the process through the metadata server and sets no such variable, so the check answered "no credentials" precisely in production while all three wizard steps worked perfectly on my machine.

A JavaScript syntax error does not break a button, it kills the file. A malformed onclick left four string literals glued together with no operator, the browser abandoned the entire <script> block, and discovery, generation and ratification were all dead on a page whose HTML rendered perfectly. tests/test_js_syntax.py is now the compiler those pages never had.

What I learned

Letting the model decide what is mandatory kills the funnel. Discovery originally asked Gemini to mark which missing fields were blocking. It marked ten. Nobody finishes that wizard. The fix was not a better prompt: the decision moved into a constant in Python. The model is good at inferring what a business is and bad at knowing how much friction an owner will tolerate.

One reference month is not enough to judge a generated rule. A rule that never fires is easy to catch. Its opposite is not: clients_at_risk > 0 is true of every business that has ever had one customer drift, so it greets the owner every week regardless of performance. That is a banner, not advice, and against a struggling month it is indistinguishable from a rule working correctly. Catching it needs a second month where the business is doing well, and that month has to be good and not perfect, because nothing fires on a flawless month and every rule then looks well behaved.

An explicit null is not an absent key. Leaving the optional "Owner" box blank sent owner_name: null. Defaults only apply when a key is missing, so the most ordinary thing an owner can do, skipping an optional field, permanently broke the dashboard they were sent to next. It broke on read, so it kept breaking long after the moment that caused it.

What's next

A second paying business in a trade the platform has never served. The wizard is the part that made that possible; the prize is not the point. iration

What it does

How we built it

Challenges we ran into

Accomplishments that we're proud of

What we learned

What's next for VuDeja: the agent that provisions agents

Built With

Share this project:

Updates

Submission history