Skip to content
← vishwa moraAI systems · portfolio

Every system here
has something that can say no.

Seven things I built that now run a piece of a business on their own. Each one does real work every week. Each one also has a point where it stops and refuses, because software that quietly does the wrong thing is far more expensive than software that does nothing at all.

If your team repeats something more than twice a week, or you want AI touching customers without the risk of it inventing things, this is the kind of work I do.

~240hours of manual work removedPlus 12 hours a month recurring, measured.
1,922posts, leads and jobs processed87 posts, 830 leads, 1,005 jobs.
11hard stops across seven systemsPlaces the work can be refused.
6 of 7running unattendedOn schedules, without me.
Running in production, unattendedBuilt works, run when needed marks a hard stop

Posting without me

Running
Before

Writing and scheduling social posts ate most of an afternoon every week. In the weeks I was busy, nothing went out at all.

Now

Now I talk into my phone, or paste in a rough paragraph. It comes back as a finished post in my own voice, waiting for a yes. I press approve and it goes out at the right time, whether my laptop is open or not.

Where it stops

It cannot make up a number, and it cannot publish itself

Every figure in a draft is checked against what I actually said. If a number turns up that I did not give it, the post is stopped before I even see it. And nothing reaches the internet without me pressing approve. If a scheduled post never gets approved, it goes quietly back to drafts and messages me rather than publishing anyway.

One of the four steps scores the writing for voice. It is deliberately not trusted to decide what is true, because a model marking its own honesty is not a safeguard.

Draft 91 · held for approval
  • “We cut their response time by 40% in six weeks.”
  • Voice and structure
  • “40%” does not appear in what you recorded
  • “six weeks” does not appear in what you recorded
Held. Not shown to you, not published.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Capture
what you saidvoice or paste
Shape
anglebrief + hook type
draftwritten in your voice
voice checktone and structure
Gate
fact checkevery figure matched
your approvalor it waits
Out
published18:00, unattended

Launching a campaign end to end

Running
Before

Every campaign was about forty small jobs spread across four different tools. Miss one and the customers are the ones who find out.

Now

One instruction now runs the whole sequence: the idea, the copy, the artwork, the landing page, the discount codes and the emails. A person stays in the loop for the decisions that matter and nothing else.

Where it stops

Two checks that are allowed to stop the launch

Nothing goes live until one check confirms the discount code really works and the emails are really switched on. A second then signs itself up as a test customer, confirms the email actually arrived and the code actually worked, and deletes its own test data afterwards. If either check fails, the campaign does not launch.

Both exist because a campaign once went out with the emails left switched off. Nine people signed up, seven finished it, and not one of them heard anything back. Every rule in here is something that has already gone wrong once.

Pre-launch check · 4 items
  • Discount code live, 20% off, expires 14 Aug
  • Landing page deployed and reachable
  • Signup form writing to the database
  • Welcome email is still a DRAFT, not switched on
Launch stopped. 1 of 4 checks failed.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Build
ideamechanic + narrative
copyemails and posts
artworkfrom brand templates
Wire
landing pagerebuilt for this one
codes + emailsstore and lifecycle
Gate
pre-launch checklive, not draft
live testreal signup, real email
Out
launchannounce it

Outreach that cannot oversell

Running
Before

A consultancy needed to reach senior decision-makers who can spot a template from the subject line, and who would end the conversation over one wrong number.

Now

Built and run for them on a monthly retainer. It finds the right companies, reads each one's own website, and writes an opening line about that specific business rather than a mail-merged name.

Where it stops

It physically cannot invent a client result

Every statistic and every currency figure in an outgoing email is checked against the client's real, documented results. Anything that does not match is rewritten before it is sent, not flagged for someone to catch afterwards. The check at the other end drops companies that were never a fit, before they cost anything.

This exists because the client spotted a made-up profit figure in a draft. One invented number in an email to a pricing expert ends the relationship.

Outgoing email · fact check
  • Rejected: “clients typically see a 28% margin uplift”
  • No documented result matches that figure
  • Rewritten from the client’s real case studies
  • Sent: “your pricing page lists three tiers, and the top one has no price on it”
Sent. No unverifiable claim left in the email.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Find
find companiessourced by profile
right-fit checkbad fits dropped early
Learn
read their sitetheir own words
Write
write the openerabout that business
fact checkmatched to real results
Out
sendwarmed, rotated
sort the repliesclassified on arrival

Applying without exaggerating

Running
Before

Finding relevant roles across Europe and tailoring a CV to each one is a full-time job by itself, and the shortcut everyone takes is to stretch the truth.

Now

Eight scrapers I wrote cover company job boards, investor portfolios and government databases. Everything found gets scored, and the ones worth applying to get a CV written for that specific role.

Where it stops

It is not allowed to be the source of a fact about me

The CV writer can only use facts from one file it has no permission to change. A separate check then scores the finished CV and blocks anything that does not clear the bar. Not a warning to be ignored. A block.

Written after it started quietly rounding my experience up. The fix was not better instructions, it was removing its ability to be the source of a fact about me in the first place.

Generated CV · quality bar
  • Every claim traced to the locked facts file
  • One page, correct format
  • Score 61 out of 100, the bar is 75
  • Missing 4 of 9 terms the role asks for
Blocked. This CV cannot be sent.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Find
8 sourcesboards, portfolios, gov APIs
Filter
score the fitagainst a locked box
suppressalready seen, already closed
Build
locked factsit cannot edit the source
Gate
quality barunder it, nothing sends
Out
applysend it

Proposals in two minutes

Running
Before

Writing a partnership proposal by hand took twenty to thirty minutes, and no two ever came out looking the same.

Now

Give it a partner name and it produces the finished, on-brand document and files it where the team can actually find it.

Where it stops

Shaky facts never reach a document someone opens

Before anything goes into the document, every fact is scored on how good the source is, how recent it is, and whether it can be independently checked. Anything weak is blocked, rather than slipped in with a caveat nobody reads. The person receiving it never sees a wrong number about their own business.

An audience figure lifted from one social media bio, then quoted back to the person it describes, is the fastest way to lose a partner.

Research · before it enters the document
  • City, Amsterdam, official website
  • Founded 2019, two independent sources
  • Members, 12,000, one social media bio, unchecked
The member count is dropped. The proposal never claims it.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Research
research thempublic sources only
Gate
is this solid?source, age, checkable
Build
build the documentbrand template filled
Out
file itwhere the team looks

Something that improves itself

Built
Before

Some tools need dozens of small improvements, and testing each one by hand is slow enough that you stop bothering.

Now

It makes one change, runs it, scores the result, and keeps the change only if the score went up. Nobody sits and watches it.

Where it stops

It is not allowed to mark its own homework

How success gets measured is fixed before it starts, and it has no permission to change that. A change survives only if the number went up. Every attempt is saved, so you can go back and see exactly what it tried, and why each one lived or died.

The honest limit: this only produces real gains when the score comes from running actual code. Pointed at writing, the judgement becomes circular and it talks itself into nonsense. That cost me a weekend to learn, and it is why the tool is kept deliberately narrow.

Run log · iterations 3 to 6
  • 3 · score 0.71 → 0.68 · undone
  • 4 · score 0.71 → 0.79 · kept
  • 5 · score 0.79 → 0.74 · undone
  • 6 · score 0.79 → 0.83 · kept
Two of four changes survived. The rest were undone automatically.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Set
the goalfixed before it starts
Try
try a changeone at a time
run itactually executed
Score
score itby the fixed rule
Gate
keep or undoonly if the number rose

Finding the right communities

Running
Before

A brand wanted to reach running and cycling clubs across Europe. There is no list of them. They are scattered across social profiles, club sites and event pages, and most of what you find is either dead or the wrong size.

Now

Agents search city by city and sport by sport to build the pool. Everything found is checked against six criteria before anyone is contacted, and each approach opens with one specific detail proving somebody actually looked at them.

Where it stops

Six checks before anyone gets contacted

Country, sport, city, size, activity and reply rules are all confirmed before a community enters the outreach list. Volume comes last, on purpose. Sending more messages to a badly built list is how you burn a market you only get one shot at.

Capped at ten to fifteen approaches a day, by choice. This is relationship outreach into small communities where being seen as a spammer once is permanent.

Community 44 · six-check gate
  • Country and sport match the brief
  • City is a target market
  • Posted an event in the last 30 days
  • Member count comes from one bio, unverified
Held. Not contacted until the size can be confirmed.
An example of the check doing its job. Written to show the behaviour, not a screenshot of one particular run.
How it runs can refuse the work
Scope
scope the marketcountry and sport rules
Find
discovercity by city
verifyat least one real signal
Gate
6-check gatebefore anyone is contacted
Reach
find the hookone specific detail
send10 to 15 a day, capped
Out
call briefwarm reply to a call
Earlier, in n8n

Where these started,
and why they left the canvas.

Before any of the above, I built the same ideas as visual workflows in n8n. They work, and prototyping there is genuinely faster. They are all switched off now, for one reason: a canvas can route, branch and call a model, but it cannot hold a read-only truth file, cannot regenerate a figure that fails a check, and cannot refuse to send. The moment a workflow needed something that could say no, it had to become code.

Reddit research

Pulls a thread and its comments, maps the fields, and runs them through an agent with a reasoning tool and a structured output parser before anything reaches the sheet. A second branch searches for posts and loops over the results.

thread + commentslimitmap fieldsagent + think toolschema parsersheet

The schema parser is the closest thing here to a gate. It enforces shape, not truth, which is exactly the limit that pushed the later systems into code.

Support triage

A Gmail trigger feeds a text classifier. Anything it recognises goes to an agent that labels the thread. Anything it does not falls through to a no-op rather than being guessed at.

gmail triggerclassifierrouteagentlabel

Routing unrecognised mail to a deliberate no-op is the right instinct. Do nothing beats do something wrong.

Research to sales page

The largest of them. Reads a company list, writes its own search queries, runs parallel research across a search API and a crawler, combines the findings, then generates a sales page and an email sequence from what it found.

company listgenerate queriessearch + crawlcombineanalysispage + sequencequality analyserarchive

A quality analyser sits before the archive step, but a model scoring its own output is not a control. That lesson is why the systems above check figures against a source instead.

Also on the shelf

An open-source GTM operating system published to npm under MIT, CLI-first, which plans campaigns and qualifies leads from the terminal. And a nine-agent system for running an X account that is specified in full and deliberately not built, because the honest version of this page is that a spec is not a system.

Something in your business
that should refuse bad input?

vishwahithhmora@gmail.com

How I work with clientsBack to everything else