Ilya — owner, NW Maids. Written 5 August 2026. All figures below are as of that date.
NW Maids does roughly $5M a year in residential cleaning across five cities, with more than 100 cleaners in the field. The back office is three people, all remote. I run the company remotely too. Founded 2013.
That ratio is the whole reason any of this exists. A field-service business at this size normally carries a dispatch desk, a support desk, and someone whose entire job is chasing reporting. We don't. The gap gets filled by a deterministic automation layer that has existed for years, and — since late May 2026 — by an AI operations layer sitting on top of it.
The AI layer has been in production for about ten weeks. The self-hosted infrastructure underneath it has been in production for about four (VPS provisioned 6 July 2026). That is recent, and I am not going to pretend otherwise. Anyone evaluating this should weight it accordingly.
I'm not a traditional engineer. I built this through AI-assisted development, running privileged commands myself, with Claude as the technical guide.
Let me kill the obvious misreading first, because the honest version is more interesting than the hype version.
This is not an AI that runs my company. It doesn't answer customers. It doesn't assign cleaners. It doesn't screen applicants. It doesn't resolve complaints. It doesn't set prices. Four entire functions of this business have essentially zero AI involvement, by design.
What it is: an operations analyst and chief-of-staff that reads nearly every system in the company on a schedule, notices things, and stages decisions for a human. The internal file that governs it is called CEO_INSTRUCTIONS.md, which — I'll say it before you do — oversells what it is. It's a constitution for an analyst, not a mandate for an executive.
The interesting engineering here isn't the capability. Capability is cheap now. The interesting part is the constraint: what a system like this is stopped from doing, how that stop is actually enforced, and what happens the four times the stop didn't hold.
BUSINESS INPUTS booking system · CRM/email engine · call tracking · SMS ·
shared inbox · Google Business Profiles · Local Services Ads ·
rank tools · web analytics · time tracking · accounting
│
▼
WORKFLOW / DATA LAYER ~150+ deterministic automations (no model in the loop):
inbox routing & labeling · form→booking→CRM enrollment ·
hiring pipeline glue · job lifecycle logging · SOP checklists ·
drip/win-back/referral sends
│
▼
AI REASONING LAYER 23 recurring scheduled jobs. Each one reads the constitution
first, then its own data sources, then decides whether
anything a human needs to see actually happened.
│
▼
STAGED ACTIONS inbox_for_you.md — a plain markdown approval queue.
Fixed format: Decision needed / Options A–C / My recommendation.
Plus done_for_review.md and state/decisions_log.md.
│
▼
DASHBOARDS & ALERTS CSV trackers (ranks, leads, bookings) · internal Slack for two
job types only · email for new website leads
│
▼
HUMAN ESCALATION Me, for anything touching the five hard-stops.
Staffing coordinator for off-policy cleaner invites.
Support lead for negative feedback. Listings coordinator
for profile and photo work.
Note what the approval surface actually is: a markdown file. There is no console, no approval GUI, no workflow product. I open a file and read it. That's a real limitation and I'll come back to it in the section on what transfers.
The constitution stops the system on five classes of action. Each one routes to me instead of executing.
Now the part most write-ups of this kind leave out.
Mostly by written policy the model follows — not by permission walls. I want to be exact about this, because it's the claim a serious evaluator will push on hardest, and because overstating it would poison everything else in this document.
There are three real technical controls:
| Control | Type | Scope |
|---|---|---|
| Credential scoping | Technical | The system can only act through connectors and browser sessions I'm already logged into. It never enters passwords. I type privileged and server credentials myself. What it doesn't hold, it cannot use. |
| CRM API write-block | Technical | Direct API writes to the CRM return 403. Any CRM change has to route through a human-driven UI path. |
| No payment instrument | Technical, by omission | It holds no payment method. The ads job is explicitly forbidden from entering or modifying payment methods and budgets — and also has nothing to enter one with. |
Everything else is behavioral. The booking system, the Google Business Profiles, and the live website are all technically writable by this system. It refrains because the constitution says to. That's it.
If you want the one-sentence honest version: read-only is enforced primarily by a written constitution the model follows plus credential scoping, not by hard read-only permissions on each system.
The 7/20 incident below is the proof that this distinction is not academic.
Three of the five stops carry a standing pre-approval I granted deliberately. I'd rather publish these than have someone discover them:
| Stop | Exception | Granted |
|---|---|---|
| Messaging a real person | The Local Services Ads job can send review requests to satisfied, already-booked customers. Internal Slack posts to my own team are also permitted — team-facing, not customer-facing. | 15 July 2026 |
| Publishing public content | The quarterly testimonials job publishes refreshed review content directly to live website pages and a microsite. It then asks me only for a CDN cache purge. | Standing |
| Changing booking/customer data | The Local Services Ads job marks leads as booked and files valid lead disputes in the ad console. | Standing |
So the accurate claim is not "it never messages customers or publishes content." The accurate claim is: every action of those classes is blocked by default, and three narrow, dated, single-purpose carve-outs exist that I granted explicitly and can revoke. That's a weaker sentence and a truer one.
inbox_for_you.md took roughly 40–70 appended items per week through late July, peaking at 12–13 in a day, tapering to 3–7 per day in early August.
Caveat that matters: those entries mix genuine decision-needed escalations with status and FYI notes. The pure "blocked action awaiting approval" subset is smaller and I don't tally it separately. I could give you a clean approvals-per-week number only by tagging inbox entries by type, which I don't currently do. So I won't give you one.
Exact count as of today: 23 recurring, enabled AI jobs, plus 5 dated one-time tasks and a retained tail of ~30 completed one-offs. I had been saying "about 22" from a late-July internal count; the live number is 23. Every one of these is AI-assisted or draft-and-check. None are fully autonomous except where the standing exceptions above apply.
| # | Job | Cadence | What it does | Department | Class |
|---|---|---|---|---|---|
| 1 | Daily operations run | Daily | Works unblocked tasks, stages drafts, logs decisions | Cross-functional | Draft+check |
| 2 | PR / media scan | Daily | Drafts pitches in my voice from journalist-request feeds | Marketing/PR | Draft+check |
| 3 | Local PR & backlinks | Daily | Verifies targets, stages outreach drafts. Never sends | Marketing/PR | Draft+check |
| 4 | Job-invites review | Daily | Posts to Slack only when an invite needs changing | Scheduling | AI-assisted |
| 5 | Review allocation | Weekly | Re-runs value-per-review model across 4 profiles | Marketing/SEO | AI-assisted |
| 6 | Testimonials refresh | Quarterly | Publishes review content to live pages | Marketing/SEO | Standing exception |
| 7 | Security advisories | Monthly | CVE report for the self-hosted apps | IT/Ops | AI-assisted |
| 8 | Booking attribution | Daily | Yesterday's bookings + rolling 7-day intent | Reporting | AI-assisted |
| 9 | Price-increase churn watch | Daily | Flags objections, stages rate-freeze offers | Retention | Draft+check |
| 10 | Win-back reply tracking | Daily | Logs and surfaces responders | Retention | AI-assisted |
| 11 | Lead-alert health check | Daily | Silent unless the alert path itself is broken | Lead intake | AI-assisted |
| 12 | Comms watch (daily) | Daily | Silent incident log across email, SMS, calls | Inbound messages | AI-assisted |
| 13 | Comms watch (weekly) | Weekly | Patterns, surfaced only if actionable | Inbound messages | Draft+check |
| 14 | Local Services Ads maintenance | Daily | Matches leads; marks booked, files disputes, sends review requests | Ads/Lead intake | Standing exception |
| 15 | Listing-violation tracking | Weekly | Flags previously-reported listings still in violation | SEO | AI-assisted |
| 16 | Payroll hours pull | Weekly | Reports hours to pay. The pay run is human | Payments | AI-assisted |
| 17 | Portland profile recovery watch | Daily | Watches a delisted profile until restored | SEO | AI-assisted |
| 18 | Commercial lead tracker | Daily | Logs new B2B leads to CSV + Slack | Lead intake (B2B) | AI-assisted |
| 19 | Directory listing check | Daily | Presence check on a third-party list | SEO | AI-assisted |
| 20 | Property-manager watch | Weekdays | Recommends actions on B2B/vendor-portal activity | B2B | AI-assisted |
| 21 | Map-pack rankings | Daily | Appends to rankings CSV. Read-only, silent | SEO | AI-assisted |
| 22 | Organic rankings | Weekly | 25-pair snapshot + week-over-week summary | SEO | AI-assisted |
| 23 | Proven-roster refresh | Weekly | Rebuilds per-city proven-cleaner roster from history | Scheduling | AI-assisted |
Two of those jobs are worth calling out for what they don't do. Job 4 reviews cleaner invites and posts to Slack only when one is off-policy — it never sends an invite. Job 23 rebuilds the roster of proven cleaners per city — and never assigns one to a job.
The AI jobs sit on top of a much larger rule-based layer with no model in it at all. Counts below were verified in a 29–31 July pass and have not been re-counted live; treat them as accurate as of late July.
| Platform | Count | What it does |
|---|---|---|
| Chatwoot automations | ~116 | Inbound routing, labeling, auto-acknowledgement |
| Chatwoot canned replies | 179 | Suggested answers — a library, not an autoresponder |
| Activepieces flows | 14 | Form→booking→CRM enrollment, hiring glue, job lifecycle logging |
| Donetick recurring checklists | ~49 | Onboarding and support SOPs |
| Donetick conditional checklists | 8 | Branching SOPs |
| ActiveCampaign automations | Uncounted | Drip, win-back, referral, review-request sends |
So: on the order of 150+ deterministic automations, with one platform's set never enumerated. I'd rather report an honest "uncounted" than round it into the headline.
The Chatwoot set is the one most likely to be misread. Those 116 automations tag, route, and suggest. They do not compose customer answers. There is no AI autoresponder on my support inbox.
(The self-hosting migration that produced this stack — replacing $573/month of SaaS with a $51/month box, and the security grounds on which I rejected n8n, FreeScout, and Vikunja — is a separate write-up.)
Eight departments, four class labels, gaps included.
| Department | Rule-based | AI-assisted | Draft+check | Still fully human |
|---|---|---|---|---|
| Lead intake | Form→booking→CRM; lead alerts; call attribution | Booking-attribution report; commercial-lead tracker; alert health check | — | Answering, quoting, closing. The conversion is 100% human |
| Scheduling | Invite-blast mechanism (human-operated) | Invite policy review; proven-roster rebuild | — | Coverage confirmation. No AI assigns a cleaner |
| Inbound messages | Routing, labeling, auto-ack, canned suggestions | Comms watch (24h no-reply safety net) | — | Every substantive reply |
| Marketing | Drip sends once live; review requests (9–5 PT window) | — | Campaign and win-back copy, staged for send approval | Strategy, offers, final send authorization |
| Hiring | Application→sheet→CRM; scheduler redirect; e-sign webhook | — | — | Sourcing, screening, every decision. Deliberately low-AI |
| Job problems | Cancelled-booking logging only | 24h no-reply flags | — | No-shows, complaints, re-cleans, credits. No AI resolves a complaint |
| Reporting | — | Most of the 23 jobs | Weekly comms review | Financial reporting — still partial, accounting audit open |
| Payments | Charge at booking via the booking platform | Weekly payroll hours pull | — | The pay run, bookkeeping, B2B invoicing. No automated collections |
Where the AI layer does essentially nothing: hiring decisions, complaint resolution, cleaner assignment, and supplies/inventory — the last of which is entirely off-system. Nothing tracks or reorders supplies. That's not automation restraint, that's just a gap, and it's the one I'd fix first.
The pattern, if you want it in one line: AI covers observation and analysis. Humans cover judgment, money, and anything a customer will read.
Guardrails only mean something if you say what happened when they didn't hold.
Working near a send-hot campaign editor triggered a real win-back send that wasn't meant to go out. Root cause: the email platform treats an in-progress edit as sendable, with no confirmation gate. Caught in the same session. The fix is a standing rule — verify campaign status is 0 before and after any edit, never work near "Send now."
That guardrail is a behavioral rule. The platform still has no technical block. This is the incident that proves the enforcement model: a policy-level stop failed, and the only thing that caught it was a human noticing.
An orphaned proxied subdomain record, left over from a migration, resolved into a different tenant's space. Found during unrelated DNS work. I don't know how long it was exposed. Fix: a DNS-hygiene rule to delete or edge-redirect stale proxied records. Also policy, not an automated check.
The ads console renders timestamps in UTC+7. An analysis read those raw and concluded that leads were arriving after hours. They weren't. A related version of the same bug misdated a support thread as new because a stray reply bumped an old one. Retracted on review. Fix: always derive true Pacific time explicitly, and date incidents by the substantive message rather than the thread timestamp.
I include this one because it's the least dramatic and the most representative. An AI ops layer's most common failure isn't a rogue action — it's a confidently wrong conclusion drawn from a correctly-read but misinterpreted field. Most of the real work is defending against that.
A mailbox consolidation done before transferring Google Business Profile ownership knocked the Portland listing off Maps. The profile's owner was the mailbox. Detected when the listing dropped. Still under daily recovery watch as of today — this one is not resolved. Fix: transfer profile ownership to the primary account before touching any city mailbox.
All four guardrails are policy, not technical controls. The safety of this system comes from a followed constitution and four hard-won operating rules, not from permission walls. State that plainly or don't state it at all.
Social auto-posting — built it, deleted it. Reddit auto-answering — killed before launch. Both for the same reason: they put a model in direct contact with a public audience with no human in the path, which is precisely the thing the constitution exists to prevent. Building them was a lapse in my own judgment, not a technical failure.
Published deliberately, because the absence of these numbers is the most common tell that a system like this is being oversold.
If someone shows you an AI operations write-up with confident numbers in all six of those categories, ask how they were instrumented.
This is the question I'd ask if I were on the other side of the table.
Transfers cleanly:
Does not transfer, and I won't pretend otherwise:
What I'd build first if I were doing this across a rollup portfolio: typed approval routing, technical write-blocks replacing the three policy-only stops, scoped service credentials replacing browser sessions, and a pre-deployment baseline capture so the second site can prove what the first one only asserts.
A three-person remote back office runs a $5M, five-city, 100+ cleaner field-service operation. Underneath sits 150-plus deterministic automations. On top sit 23 AI jobs that watch nearly every system in the business and stage decisions in a markdown file.
The AI does not answer customers, assign cleaners, screen applicants, resolve complaints, or set prices. It is stopped from five classes of action by a written constitution, three real technical controls, and three documented standing exceptions I granted on purpose.
Ten weeks in production. Four incidents worth publishing. Six things I can't measure.
That's the whole system. The constraint list is longer than the capability list, and that's not an accident — it's the design.
I'm currently available for operations leadership roles, and I consult with remote-run, multi-location field-service businesses on this kind of build. If you're evaluating whether any of the above would survive contact with your operation, I'm happy to tell you which parts wouldn't.