Selected work · Detroit
Zachary Lewis
I build and operate production systems end to end — architecture through deployment through the 2 a.m. incident. What follows is the long version of my resume: what each system does, what was genuinely hard about it, and where it actually stands today.
Zach@TakeDetroit.com (248) 550-5061 github.com/taketaketaketake thefakezach.com
Sole architect · 2026 · Live
AgentlandOS
A platform where someone who doesn't write code can set up AI agents that actually do work — send the email, run the workflow, make the call — inside limits they control.
- Services
- 11
- Endpoints
- ~292
- Tables
- 80
- Backend tests
- ~1,550
- Lines
- ~228K
- Decision records
- 120
What it does
A user describes a goal in plain language. The system turns that into a plan, shows it to them for approval, and then executes the approved steps — sending email, generating documents, querying data they've connected, placing phone calls. Every agent runs under a spending ceiling and a permission scope its owner sets.
It's eleven separate services behind one front door: planning, orchestration, memory, model access, execution, billing, identity, and a directory of available agents. The gateway is the only thing exposed to the internet; everything else talks over a private network.
The hard part
An agent that runs for two seconds is a function call. An agent that runs for two hours, waits on a human to approve something, survives a deployment, and picks up where it left off is a different problem entirely. I built that layer on Temporal — fifteen workflow definitions across three queues, covering scheduled work, agent-to-agent messaging, and human approval gates that can sit open indefinitely without holding a process open.
The second hard part was money. Language models cost real money per call, and an agent in a loop can spend a lot of it quickly. Every call routes through one service that knows what each model costs. Each agent has a dollar ceiling; at 80% of it the system silently switches to a cheaper model, and at 100% a circuit breaker stops the agent rather than letting it run up a bill.
Because customers share the platform, everything is scoped to a workspace — requests, agent actions, model preferences, and API keys. Anything an agent actually executes runs inside a locked-down container with capabilities dropped and outbound traffic logged, because "the agent decided to run this code" is only acceptable if the code can't reach anything it shouldn't.
Where it stands
Deployed and running. The core services are substantial — eight to eighteen thousand lines each with real test coverage — and the LLM, gateway, and user services were still getting commits the week I wrote this. A few peripheral repos from early exploration are dormant, and I'd rather say that than pretend a twenty-one-repo system is uniformly polished.
Solo founder · 2026 · Live
The Fake Money Club
A workspace for self-directed traders: ten scanners surface setups, a language model drafts the reasoning, the user's own brokerage takes the order, and a journal measures — honestly — whether any of it worked.
- Python
- 139K lines
- TypeScript
- 68K lines
- Endpoints
- 153
- Python tests
- 2,249
- Frontend tests
- 815
- Scheduled jobs
- ~34
What it does
Scanners run on a schedule against a universe of roughly two thousand symbols, looking for technical setups. When one fires, the platform assembles a deterministic packet of context — price action, fundamentals, earnings dates, options positioning, sector comparison — and asks a model to write the thesis and the levels. The user reads it, decides, and if they act, the order goes through their own brokerage account. Afterward, a journal tracks what actually happened.
The business model follows from the architecture: users bring their own AI key and their own broker. The platform never takes custody of anyone's money and doesn't mark up tokens.
The hard part
Everything here is downstream of one decision: a model is never allowed to produce a number a user might act on. Prices, levels, position sizes, and risk calculations all come from ordinary Python. The model writes the explanation around them. This sounds restrictive until you consider the alternative — a plausible-sounding model hallucinating a stop-loss price for someone about to risk real money.
That principle shaped the safety work. The endpoints that can place an irreversible order bind to localhost by default, so remote access is an explicit opt-in rather than an accident of configuration. Each user's API keys are encrypted individually. Separation between members is enforced in the database itself — row-level security tied to a per-connection identity — rather than by application code remembering to filter every query, because application code eventually forgets.
The part I'm most pleased with is the grading. Four separate pipelines compare what the model said against what actually happened: a trade thesis checked against real forward price bars including whether its stated confidence was calibrated, scanner retrospectives, intraday scalps, and news tagging. Each pairs a deterministic ground truth with a second model judging whether the writing was actually supported by the data. Every result is keyed to the version of the instructions that produced it, which means changing a prompt is an experiment with a measurable outcome rather than a vibe.
Where it stands
Live and free while the workflow proves itself. One deliberate gap: a brokerage integration that would let agents place orders directly is code-complete but sitting behind a legal review I haven't cleared, and I'd rather ship it late than ship it wrong.
Internal tooling · 2026 · Ran daily for four months
Agent Observability
Mission control for AI coding agents. When you have several running across different projects, the terminal scrolls away and you have no idea what any of them actually did. This records all of it.
- Events captured
- 174,691
- Sessions
- 381
- Messages
- 150,749
- Projects covered
- 10+
- Tests
- 241
- Lines
- ~26K
What it does
Coding agents fire lifecycle events as they work — about to use a tool, tool succeeded, tool failed, needs permission, finished. This captures all of them, writes them to a local database, streams them live to a dashboard, and scores the sessions afterward. Live view shows what every agent is doing right now in colour-coded lanes; the historical view shows volume, tool reliability, quality trends, and token usage over months.
The hard part
Getting the data in reliably, from systems that were never designed to report to me. There are four independent ingestion paths: hooks from one agent runtime covering fourteen event types, hooks from a second runtime mapped onto the same schema, git hooks tying commits back to the session that produced them, and a file-tailer that reads session transcripts from disk with a high-water mark so sessions that ran while the collector was down get backfilled rather than lost.
The design rule I'm happiest about is small: every hook fails open. A two-second timeout and a swallowed exception, so if the collector is down, the agent doesn't stall. Observability that can break the thing it observes is worse than none.
Scoring is pluggable. One evaluator is purely deterministic — did the tool call succeed. Others use a model as a judge for transcript quality and reasoning depth. One is statistical, flagging regressions against stored baselines. The charts are hand-written SVG; pulling in a charting library for five charts wasn't worth the dependency.
Where it stands
It ran every day for four months across more than ten of my own projects and kept collecting for ten weeks after I stopped adding features, which is the most honest endorsement I can give a tool. It's deliberately local-only — no auth, no cloud — because it was built for one person and I'd have to do real security work before it could be anything else.
Open source · 2026 · Published
spec-driven-docs
Documentation that can't quietly rot. A small framework that treats docs as contracts and blocks progress when they fall out of date.
What it does
The premise came from watching my own repos: when an AI agent can write a week of code in an afternoon, documentation goes stale in hours rather than months. So the docs get a routing table — a mapping from type of change to which documents that change invalidates — and three enforcement layers that make ignoring it inconvenient.
Running npx spec-driven-docs init walks you through five questions about the system you're building and writes a starting set of templates. A session hook injects the routing table into the agent's context at the start of every session, so it knows the rules without being told. A CI workflow enforces the parts that matter: a phase can't be marked complete without an audit file recording a passing verdict, and the file declaring the system's invariants is enforced append-only — you can add a constraint, but you cannot quietly delete one.
Where it stands
Published, MIT, zero runtime dependencies. Small — about 640 lines of executable code plus templates — and I use it across my own projects, which is how the audit gate got strict enough to be useful.
Lead consultant · Client engagement · 2026
Financial Database Migration
Moving a family office's accounting system off an unsupported database server onto managed, encrypted infrastructure — without rewriting the desktop application, the service layer, or twenty-four financial reports that depend on it.
- Tables
- 121
- Columns mapped
- 849
- Foreign keys added
- 227
- Datetime columns classified
- 84
- Tests
- 128
- Decision records
- 25
What it does
The client runs a private-wealth accounting system — entities, ledgers, transactions, investments, real assets, liabilities, insurance, taxes. It sat on a database version that stopped receiving security updates, on a single virtual machine. The job was to move it somewhere supported and encrypted while modernising a schema that had accumulated fifteen years of shortcuts, and to do it without touching the desktop client, the service layer, or the reports — all of which had to keep working on day one.
The hard part
Financial data doesn't tolerate "mostly right." The governing rule I set was that no money could be lost or silently altered: every entity's debits and credits had to reconcile end to end, and that wasn't negotiable for schedule reasons.
The subtlest problem was time. Eighty-four date columns had no timezone information — some meant a calendar date, some a wall-clock time, some an actual instant. I classified every one individually with a written rationale, because getting it wrong shifts an audit timestamp by five hours and nobody notices until it matters. A later regeneration silently dropped the timezone conversion on six of them; the tests caught it.
Rather than hand-write the migration, I generated it — the schema, the column mapping, and the transformation logic all derive from a parse of the source database, with a gate that fails the build if the committed output ever drifts from what the generator produces. Then I rehearsed the entire thing on a disposable copy of production, and tore it down.
Where it stands
The production data load passed with reconciliation to the cent, surfacing exactly the same 33 pre-existing orphans found in rehearsal — which is what you want, because it means the migration introduced nothing new. What remains is application-side: certificate trust configuration, report verification, and the cutover window itself.
Founder · 2026 · Live
Yard Line
A support platform for families of incarcerated people and for those coming home — built by repurposing a product that had failed to find users.
- Facilities imported
- 5,845
- Data models
- 30
- Endpoints
- 57
- Backend tests
- 226
- Web pages
- 29
What it does
Families trying to stay connected to someone inside face a series of unglamorous problems: which facility is this person in, what are the visiting rules, how does the phone system work, what does money transfer cost. People coming home face harder ones: housing, legal aid, identification, work. Yard Line is a directory of every open correctional facility in the country, guides for the common tasks, moderated support rooms, and a geographically tiered directory of reentry resources with verification dates attached, so nobody drives to an organisation that closed last year.
The hard part
The interesting decision here wasn't technical, it was what to do with a failure. I'd spent four months building a platform for nurses. It worked and almost nobody used it. The obvious options were to keep pushing on distribution or start over.
Instead, through conversations with a Detroit organisation supporting people re-entering society, I realised I'd already built most of what they needed — the shapes matched even though the domains didn't. A facility directory instead of a hospital directory. Moderated support rooms instead of nurse discussions. Structured reviews of the visiting experience instead of the working one.
So I did the pivot as a strictly additive build: nothing deleted, nothing rewritten. New tables alongside the old ones in the same database, reusing the authentication, the real-time chat, the moderation pipeline, and the rate limiting untouched. It felt wrong — like leaving dead code in production — but it meant working software in weeks instead of quarters.
Where it stands
Live, with the backend in active development. The design detail I care most about: the resource directory logs every search that returns nothing. When someone looks for transitional housing in a county we don't cover, that becomes the signal for what to source next — the people being failed tell us what to build without anyone having to ask them.
Founder · 2026 · Live, without traction
On the Floor
A place for patients and families to describe the nursing care they actually received, and for nurses to use that same data to decide where they want to work. It shipped. Almost nobody used it — and its backend became Yard Line.
- Routes
- 21
- Components
- 69
- Feature areas
- 9
- Frontend lines
- 7,454
- Built in
- 12 days
- Decision records
- 13
What it does
A patient or family member walks a seven-step wizard describing their care across five dimensions — communication, emotional support, responsiveness, advocacy, respect. Every score requires a written explanation; there is no way to leave a number without a reason. Those reviews aggregate into a picture of a hospital, broken down by unit type and shift.
Nurses are the second audience for the same data. Someone deciding whether to take a contract at a particular hospital can look at how care there is actually experienced, unit by unit, and can talk to other nurses in real-time rooms that clear themselves after 72 hours, or in persistent topic discussions.
The hard part
Everything about this product is legally and ethically loaded, and almost all the engineering went into that rather than into features.
The first problem is naming people. A patient describing a bad night, or a nurse describing a bad manager, will name someone if you let them — and that exposes the person named, the author, and the platform. The published policy is explicit: describe people by role, unit, and shift, never by name. That isn't just a policy page; content is screened before it publishes, and when something is blocked the form state is preserved so the author can revise rather than lose what they wrote. Reporting categories include identifying information and doxxing as first-class reasons.
The second problem is subtler and it's the one I'm proudest of solving. Aggregate statistics leak identity. If a hospital's night-shift NICU has exactly one review, publishing its average tells you precisely who wrote it. So the minimum threshold isn't applied to the hospital — it's applied inside the aggregation query, to every unit-and-shift cell independently. A cell with fewer than three reviews is dropped from the response entirely, even when the hospital overall has plenty. The API returns a clear "not enough reviews yet" rather than a thin, re-identifiable number, and there are backend tests asserting exactly that.
Where it stands
Live and complete-feeling — nine feature areas, all wired to real endpoints, built in twelve days across fifteen documented phases. Dormant since March. The honest summary: I built a careful product for an audience I had no route to reach. The value that survived wasn't the product, it was the backend — four months later it became Yard Line.
Founder · 2025–2026 · Live
Bags of Laundry
Laundry pickup and delivery across metro Detroit — a three-sided marketplace connecting households, partner laundromats, and drivers, with the money movement that implies.
- Lines
- ~26K
- Pages
- 38
- API handlers
- 26
- Data models
- 19
- User roles
- 4
- Decision records
- 13
What it does
A customer books a pickup and gets charged by weight, with a membership tier that lowers the rate. A driver collects the bags and photographs them as proof. A partner laundromat weighs, washes, and marks it ready. A driver returns it. Four roles, four different web surfaces, one order moving through a state machine with an audit trail at every transition.
The hard part
Payment, and it isn't close. You cannot charge a customer at booking, because nobody knows what the load weighs yet. So the system places an authorisation hold for the estimate, and captures the real amount after weighing. Which opens a series of problems that only exist in the real world: what if the actual weight exceeds the hold, what happens when a card authorisation expires after seven days, and how do you know your books match the payment processor's.
The answers, in order: capture what the hold allows and charge the difference as a separate off-session payment against the card saved at booking; run a daily job that re-authorises holds approaching expiry before they die; and write anything that doesn't reconcile into a dedicated anomalies table rather than letting it vanish into a log.
Dispatch is deliberately unglamorous. Rather than build an admin console to assign drivers, unassigned orders simply appear in every driver's list as claimable, and starting a route claims it atomically — losing races get a clean conflict rather than two drivers at one address. Laundromat capacity is derived by counting orders for a date rather than kept as a counter, because the counter version had a bug where cancelling an order never gave the slot back.
Where it stands
Live, with one real partner laundromat in Detroit and an eighteen-ZIP service area computed from their delivery radius. The blockers are cleared; a longer tail of known issues is tracked openly rather than pretended away. There's also a business phone line with an AI voice agent that takes booking details from natural speech, and a half-built second version that captured the two lessons worth keeping — shared pricing logic so the checkout estimate and the server can't disagree, and a real migration history — before I decided the running system deserved the attention more.
Founder · 2025–2026 · Live
Detroit Small Business Map
A directory of Detroit's small businesses that fills in its own gaps, plus an editorial pipeline that drafts local stories for a human to approve.
- Endpoints
- 221
- Route modules
- 31
- Data models
- 47
- Frontend routes
- 67
- Tests
- ~183
- Neighborhoods
- 50
What it does
Businesses get discovered automatically, land in a staging table, and are enriched from a series of external sources — contact details, geocoding, neighbourhood assignment, photos, categories, a written description. If the result clears a quality bar, it publishes itself to the live directory. If it doesn't, it waits for a human.
A second pipeline reads local news, video, and community posts on a schedule, scores relevance, groups related stories, and drafts articles in several formats. Nothing auto-publishes — everything lands in a review queue.
The hard part
Ten external services, each with its own rate limits, failure modes, and pricing. The interesting work isn't calling them, it's the promotion gate: deciding what "good enough to publish" means for a record assembled by machines, and building a staging-to-production path where the answer is enforced rather than assumed. Searches run against PostGIS for proximity, with a debounced typeahead over live results.
Where it stands
Live, with 221 published businesses across 50 Detroit neighbourhoods, and still under active development. The editorial pipeline works end to end but I haven't turned the firehose on yet — an empty article feed is better than a feed full of things I haven't read.
Client work · 2026 · Three deployments
White-Label Course Platform
One codebase that deploys as a completely different-looking learning platform for each client — no client-specific code anywhere in it.
- Deployments
- 3
- Data models
- 24
- Route modules
- 15
- Tests
- 231
What it does
Members sign in with an emailed link, work through narrated lessons at their own pace, take knowledge checks and module quizzes, and pick up exactly where they left off. Administrators approve new members and manage content. Each client gets their own branding, their own domain, their own database — and, from my side, no forked code.
The hard part
Resisting the fork. The tempting move with a second client is to copy the repo and change things; six months later you have two codebases and every fix has to be applied twice. Instead, a new client is a colour theme, a content manifest, and a set of environment variables. The third client went from nothing to live in a day.
Where it stands
Three deployments running, each on its own infrastructure with its own branding and content.
Founder · 2025–2026 · Live
Fix My Furnace
A two-sided HVAC marketplace for Michigan: homeowners get a free diagnosis and real price transparency, verified contractors get exclusive leads on a clock.
- Pages
- 28
- API handlers
- 25
- Tables
- 10
- Lines
- ~18.8K
- Commits
- 145
What it does
A homeowner with a dead furnace uploads a photo, describes the symptom, and gets a diagnosis — or calls a phone line answered by an AI agent that takes the details in natural speech. Separately, anyone can browse a database of real submitted service contracts, because the thing people most want to know before calling an HVAC company is what this is supposed to cost.
Contractors apply, get their license, liability insurance, and workers' comp verified independently, and only then appear publicly. When a lead comes in, one contractor at a time receives an exclusive, time-limited offer through a tokenized link — accept, decline, propose an appointment, report the outcome.
The hard part
Exclusivity has to be true, or the whole proposition collapses. If two contractors are ever working the same lead, you've sold the same thing twice and burned both relationships. The obvious approach is careful application code, which fails the moment two requests arrive together.
Instead the guarantee lives in the database: partial unique indexes that make it structurally impossible for a lead to have more than one open offer, or more than one accepted offer, no matter what the application does. A scheduled job every five minutes expires stale offers and passes them to the next contractor in sequence, so a lead never dies because someone was at lunch.
The verification model is deliberately granular — license, liability, and workers' comp each carry their own status and expiry, rather than one "verified" flag — because those credentials lapse independently and a homeowner letting a stranger into their basement deserves better than an approximation.
Where it stands
Live and functional, and honestly: never monetized. The lead pipeline works end to end, but the billing side is a written plan rather than code, and there are no tests. It's a complete platform waiting on a go-to-market push rather than an engineering one.
Client work · 2026 · Built
Enter-Great 313
A website for a Detroit nonprofit supporting returning citizens, including a tool that helps supporters write letters to parole boards without their words ever leaving the browser.
What it does
Twelve pages covering the organisation's programs, events, and donations, replacing a template site. The piece that matters is the letter builder: supporters writing to a parole board or court on someone's behalf get help structuring the letter, then export it as a PDF.
The hard part
Not the code — the constraint. A letter to a parole board about an incarcerated person is among the most sensitive things someone will ever type into a web form. The obvious build sends the text to a server to render the PDF. I made it an enforced invariant that nothing is transmitted, logged, or stored — the document is generated entirely in the browser. There's no database in this project at all, which means there is no version of this where I'm the reason someone's letter leaked.
Where it stands
Built and deployed. A few sections are waiting on content from the organisation, and the domain hasn't been pointed at it yet.
Every figure on this page was counted from the source at the time of writing, not estimated. Where something is unfinished, it says so.