aaronwood.dev

Aaron Wood

  • Senior Software Engineer
  • Greater Toronto Area, or remote
  • Open to staff-level roles

I build the platforms other engineers build on. Payments first, then AI.

What I do

Twenty-plus years of shipping, seven of them inside one enterprise platform

Most of my work is infrastructure with an internal user base: the layer a product team injects and stops thinking about. I like the part where nothing exists yet and the shape of the thing is still an argument. I also spend a lot of time in rooms with executives, product managers and customers, which is why I care whether the abstraction survives contact with a real business. The newer work is in Python: multi-agent orchestration on Google's ADK, a coordinator that decomposes a request and delegates to specialist sub-agents, and agents wired to MCP tool servers.

  • LLM platform architecture
  • Multi-tenant SaaS
  • Payments integration
  • Distributed systems
  • Multi-agent orchestration
  • API design
  • PHP
  • TypeScript
  • React
  • Python
  • BigQuery
  • GCP
  • Google ADK
  • MCP

Platforms

Architecture only. No employer code, no client names, no internal identifiers.

A provider-agnostic AI platform

In production

One gateway. Every product team's model calls run through it, and none of them name a vendor.

Problem
Model calls were going to spread across a large monolith, each with its own client, its own error handling, and no way to answer "what is this costing us, and for which tenant?"
Design
A single injectable service behind a narrow provider interface. Callers pass a model variation such as "normal" or "pro" rather than a vendor model name, so no calling code anywhere names a vendor. Requests persist per tenant with input and output token counts, giving cost attribution and full history for free.
Result
Customer summaries, AI-generated preferences across two generations, message generation with brand-guideline enforcement, and the dashboard generator below all ship on the same foundation. Every rollout is gated per tenant.
callers web app iOS app React Native services that inject it request endpoints one surface, every client internal API job queue the gateway model variation in per-tenant tokens out the only code that names a vendor Gemini second vendor drops in without touching a caller Web, mobile and every internal service reach models through one seam, so cost and history land in one place. Provider-agnostic by design, and today only one provider is implemented.
Callers pass "normal" or "pro", never a model name, so the second vendor is a config change rather than a migration.

PHP Vertex AI Gemini response schemas token accounting feature flags job queue

An agentic dashboard generator

In production

A sentence in, a working analytics dashboard out.

Problem
Every customer wants a slightly different view of their own data, and each one was a ticket: someone writes the warehouse SQL, someone lays out a screen, someone ships a release. That does not scale past the first handful.
Design
A generation job turns a plain-language request into a spec: warehouse queries, computed fields, and the UI components bound to them. The spec is data rather than code, so the same definition can be validated, repaired, versioned, localized, and rendered on web and mobile.
Result
A dashboard that used to be a release cycle is now a generation job, with a human approving each one before it goes live. Built with a second backend engineer: I designed the generation, validation and repair pipeline, and wrote most of the renderer.
spec a single failing query spliced back in repair budget runs out generation job plain-language request deterministic repair 14, each idempotent validate against the warehouse asks a person before it goes live targeted fix spliced back in
the generation, validation and repair pipeline repair loop stops and asks a person

PHP BigQuery Gemini spec-driven UI job queue React Native renderer server-sent events

A message system that learns your voice

In production

Drafts that start closer to how you actually write, and get closer over time.

Problem
A generated customer message is only useful if it sounds like the person sending it. A draft that reads like nobody gets rewritten from scratch, which is slower than not having it. On top of that, every brand has rules about tone, banned words, signatures and whether emoji are allowed, and those rules differ by channel.
Design
Prompt assembly is layered and ordered: who the message is for, then the sender, then the location, then the brand rules, then the channel. Above that sit personas at three levels of specificity, with the most specific winning. Every layer is separately gated, so any of it can be switched off for one tenant without disturbing the rest.
Result
Drafts that land closer to the sender's own voice, with brand rules enforced per channel underneath. Mine since the first version, and still mine: I have maintained and extended it through every generation it has had.
least specific most specific employee customer relationship most specific wins prompt assembly layered, then brand rules draft person edits, sends the signal nobody read diff generated vs sent async, off the send path persona evolves from the delta shared style glossary, fixed value ranges
The loop only works because the vocabulary is pinned: a free-form analysis would drift until the personas stopped meaning anything.

PHP Gemini prompt layering job queue feature flags per-channel rules

A multi-provider payments layer

In production

Card-present terminals, gift cards, and online, behind one interface.

Problem
Multiple payment providers, physical terminals in retail stores, and real money. Every failure mode is a customer standing at a counter.
Design
A provider-abstraction layer with connectors for each processor, per-tenant transaction logging, metrics, and provider health checks, so adding a processor does not touch the transaction flow.
Result
Still in production years later. Other engineers took it over and built well past what I left them, which is the outcome you want from an abstraction: new processors plug in without touching the transaction flow, and it stopped needing me.

PHP card-present terminals gift cards distributed locking idempotency metrics

Darkwatch

The agent pipeline is the point. The game is what I pointed it at.

A real-time session manager for a tabletop role-playing game, built solo, to find out what one engineer plus a serious agent pipeline could actually ship. Nineteen custom automation skills plan, implement, review and ship the work; the app is the proof they hold up over five months and 1,300 commits rather than one impressive afternoon.

Nineteen skills that plan, implement, review and ship

In daily use, on a live codebase

Agents fan out across tickets in parallel git worktrees. Every gate that could merge something is deterministic, and every write to a shared system stops for a person.

Knowing when not to
Every gate that can let code through is deterministic, never a model's opinion: typecheck, lint, import-cycle and dead-code checks, 168 Playwright specs sharded in parallel, visual regression, performance budgets, supply-chain audit. A model proposes; only a test merges. Anything that writes to a shared system stops at a person first.
Measuring it
A reopen check flags any issue closed and then reopened inside seven days, the signature of a fix that satisfied the ticket but missed the intent. Building it taught me more than running it did: a separate audit found fourteen partial fixes on closed issues that the metric could never see, because the convention files those as new issues. A measure blind to its own target class reads green forever.
The app underneath
Real-time state synchronization across a table of players, live maps with fog of war, character and combat management, an image pipeline and a component library. React and TypeScript on Node and MySQL, 18 CI workflows, nightly deploys to self-hosted infrastructure.
ticket one issue plan halts for review agent, worktree isolated checkout agent, worktree isolated checkout agent, worktree isolated checkout parallel, no shared state gates deterministic tests, not opinions PR a person a red gate goes back to the agent, not to me
The worktree isolation is what makes the fan-out safe: concurrent agents never share a checkout, so one run cannot corrupt another.

React TypeScript Node MySQL Playwright self-hosted CI agent orchestration

to add screenshots, an architecture diagram, and a short walkthrough video.

Developer tooling

Nobody asked for this. I built it because the same twenty minutes kept disappearing at the start and end of every ticket.

Twelve tools, from ticket to published documentation

In daily use by me and by teammates

Each one removes a specific twenty minutes I was spending by hand. None of them decide anything on their own.

What it does
Takes a ticket from untouched to merged and documented: open it with the right branch, worktree and context already loaded; review the merge request; write and publish its description; triage reviewer comments into a work plan; keep track of what is in flight across projects.
Documentation that shows the thing
The documentation tool does not just write prose. It generates the diagrams and it drives the product to capture its own screenshots, on the web app and on the iOS app, walking the real interaction and embedding the result. The merge-request descriptions use the same capture path, so a reviewer sees the change rather than a paragraph claiming it happened.
Beyond me
Pieces of it have been packaged up and handed to other developers on the team.

Claude Code Python Playwright iOS simulator capture Jira REST GitLab API Confluence API

Open source

Workflow skills for a self-hosted repo

Ready to publish

The Darkwatch toolchain, genericized so anyone can adapt it.

What
File and triage issues, open a pull request with a test plan and watch CI, verify QA tickets against a live app with Playwright, fan parallel agents out across tickets in git worktrees, and drive exploratory playthroughs that end in a written report. Each one documents honestly how portable it is, because most of this shape of tooling is quietly welded to one repo.
Status
placeholder Repository link lands here.

Contact

Currently open to staff-level work

I am most useful where something does not exist yet and someone has to decide what shape it should be. Remote, or Greater Toronto Area.