Aidan DavisHarmony
Full-stack engineer · housing inspection platforms
I compile messy real-world workflows into reliable systems with typed contracts, explicit failure modes, and interfaces that preserve context.
Profile
I have been at RamRod Software since 2022, working across a housing inspection business while studying computer science at Swinburne. I started as the maintainer of the legacy system — nineteen C# projects, three Office add-ins, and SQLite files carried over a file share as the only transport between the office and the field.
Since 2025 I have been the principal author of its replacement: a Convex, Next.js, and React Native platform where I have written 1,100 of 1,150 commits. It carries scheduling, offline field capture, contractor payroll, and a document compiler that turns a typed model into Typst rather than string templates.
I care about the unglamorous parts — replay-safe sync, injection-safe emitters, permission boundaries — because that is where software quietly earns or loses trust. Most of my day is spent deciding what to build and where an abstraction has stopped paying for itself.
Selected work
Housing inspection platform
I rebuilt a legacy Office toolchain as one typed, multi-tenant platform.
The business ran on a nineteen-project C# solution: three VSTO Office add-ins, a Xamarin field app, and SQLite files passed over a share. I was asked to build its replacement. Since June 2025 I have written 1,100 of its 1,150 commits — roughly 372,000 lines of TypeScript across a fifteen-package monorepo.
Convex is the system of record: 131 tables, 304 indexes, and 452 functions behind a Next.js web app and a React Native field client. Clerk supplies multi-organisation identity, and roles resolve through a parsed token grammar in one tested ACL package rather than through checks scattered across call sites.
Two complete stacks were built and rejected before this one — a Blazor Server prototype in 2024 and a Next.js prototype in 2025. Both still exist in git as working code, which keeps the architecture argument concrete instead of retrospective.
Scheduling, contractor payroll, a versioned knowledge base, audit trails, and migration tooling for the legacy data all live in the same model, so an inspector, an administrator, and an accountant read consistent state without a separate integration layer.
Highlights
- Principal author
- I wrote 1,100 of 1,150 commits across thirteen months of continuous development.
- Typed backend
- A 131-table Convex schema with 304 indexes exposes 452 typed functions to web and mobile.
- Parsed permissions
- Organisation-scoped role tokens resolve in a single ACL package covered by its own tests.
- Legacy migration
- Import sessions and identifier backfill tooling carry two decades of records across.
- Scheduling model
- Leave carries timezone and part-day hours; public holidays are scoped per Australian state.
- Contractor payroll
- Rate tiers, per-inspector overrides, pay runs, and purchase orders share the same schema.
Stack: Convex · Next.js · React Native · Clerk · TypeScript · Bun
Choosing the infrastructure
I built three stacks before one of them earned production.
The replacement platform did not start as the platform. It started as a Blazor Server prototype in 2024 — MudBlazor components, a SignalR hub driving the calendar, Auth0 for identity, and role attributes guarding admin, customer, and inspector fragments. It answered the domain questions and lost on the delivery ones.
The second attempt was Next.js and Convex in mid-2025: 101 commits and 23 tables. Beside it I tested whether a relational store could carry offline field work at all, trying PGlite — Postgres compiled to WebAssembly — along with libSQL and Drizzle. That line of enquiry is why the production system ended up on a sync engine rather than an ORM.
Only then came the monorepo that shipped. Even inside it the permission layer arrived in waves rather than at once: a backend-first foundation, then the web and mobile surfaces built against it, so authorisation was never retrofitted onto screens that had already shipped without it.
Every discarded stack is still in git as working code. I would rather argue an architecture from three things I built and measured than from preference, and the rejections are the part of the record that makes the final choice mean anything.
Highlights
- Blazor first
- A 2024 Server prototype with MudBlazor, a SignalR calendar hub, and Auth0 identity.
- Relational trial
- PGlite, libSQL, and Drizzle tested whether Postgres could carry offline field work.
- Staged permissions
- The ACL landed backend-first, then web and mobile, so nothing shipped unguarded.
- Rejections kept
- Each discarded stack remains in git as working code rather than a recollection.
Stack: Blazor · Auth0 · PGlite · libSQL · Drizzle · Convex
Typed document compiler
I replaced a document vendor with an IR that compiles to Typst.
Reports were generated through a third-party service. I replaced it with an in-house pipeline: a typed intermediate representation that compiles to Typst source and renders to PDF and DOCX, so the output is ours to reason about and version.
The IR is a Zod-typed model with bounded constraints, a visual AST carrying source spans, a one-way emitter, a linter, and content fingerprinting for cache keys. The editor boundary is explicit and documented — the rich-text editor’s serialised state is never allowed to cross into the document package.
User text can never emit as a bare Typst identifier. Every insertion point is drawn from a closed vocabulary of scope references, which makes injection a type error rather than a runtime hazard.
Rendering runs in a separate Bun service. Requests carry an HMAC-SHA256 signature over the exact raw body, verified in constant time, with excessive clock skew rejected and a missing signing secret failing closed rather than open.
Highlights
- Typed IR
- A Zod document model makes units, nesting, limits, and required images explicit.
- One-way emitter
- A visual AST with source spans compiles in a single direction to Typst source.
- Injection safety
- A closed scope-reference vocabulary stops user text emitting as a bare identifier.
- Signed requests
- HMAC-SHA256 covers the raw body, verified constant-time, and fails closed without a secret.
- Editor boundary
- Serialised editor state is documented as never crossing into the document package.
- Vendor removed
- An in-house Typst and DOCX service replaced the paid renderer it was benchmarked against.
Stack: Typst · Zod · Bun · Elysia · Effect · TypeScript
Offline field capture
I kept field edits durable across weak networks and whole disconnected days.
Inspectors work in buildings with no usable signal and reconcile at the end of the day. The field client is React Native, backed by a self-hosted PowerSync deployment streaming a subset of the Convex tables into on-device SQLite.
I ran the sync tier as infrastructure rather than a dependency: authoring the sync rules, and operating the replica set and its deployment. Reconciliation is explicit — conflicts, tombstones, and upload checkpoints are first-class tables, not implicit library behaviour.
Photo capture is the part that has to survive failure. Upload intents persist across reconnects so a dropped session does not duplicate files or lose evidence that cannot be recaptured once the inspector has left the site.
Highlights
- Self-hosted sync
- I authored the sync rules and operated the replica set rather than adopting a hosted tier.
- Explicit conflicts
- Conflicts, tombstones, and upload checkpoints are tables, not implicit library behaviour.
- Durable uploads
- Persisted upload intents stop reconnects duplicating files or losing captured evidence.
- On-device store
- A subset of the backend streams into SQLite so the app stays usable with no signal.
Stack: PowerSync · SQLite · React Native · Expo · Convex
Office and SQLite transport
I maintained a distributed system whose wire format was a SQLite file.
Before the rebuild I was the main maintainer of the system it replaced: nineteen projects and roughly 84,000 lines of C# spanning seven .NET target frameworks, first written in 2019 and still running the business daily.
There was no system of record and no server arbitrating writes. An Outlook add-in sliced a per-inspector, per-day SQLite database out of the office copy; it travelled over an Azure Files share to a Xamarin Android client; inspectors edited it offline all day; then a hand-written merge folded it back, remapping primary keys so child photo and attachment rows re-attached to reassigned parents.
That is an offline-first replication problem solved without any of the tooling that normally solves it — the entire protocol was a status column on every table. Its failure mode is not a crash but a silently wrong report, so correctness of the merge was the whole job.
I delivered photo commenting across four layers — schema, capture UI, sync path, and the add-in upload — and added crash and usage telemetry to an application that had previously been unobservable. Upload reliability was shared work with the engineer who had originally authored the system.
Highlights
- Nineteen projects
- One solution spanning seven .NET target frameworks built in a single pass.
- Office automation
- Three VSTO add-ins drove Word and Outlook over COM interop to generate and deliver reports.
- Hand-rolled sync
- Per-inspector extraction and row-level merge remapped primary keys on re-import.
- Made observable
- I instrumented a field app that had shipped for years with no crash or usage telemetry.
- Photo commenting
- I delivered one feature end to end across schema, capture UI, sync, and add-in upload.
Stack: C# · .NET · VSTO · Xamarin · SQLite · Azure Files
In-house agent runtime
I built the agent platform into the product rather than around it.
Roughly 22,900 lines of an agent runtime live inside the product. Threads form a delegation tree with parent, root, and depth, so an agent can spawn sub-agents under a contract. Each thread gets its own remote sandbox for running real commands, with lifecycle, exit codes, and produced files tracked as data. Profiles are gated by the same ACL as the rest of the system, and a realtime voice session runs on the mobile client.
Highlights
- Delegation tree
- Threads carry parent, root, and depth so sub-agents run under an explicit contract.
- Sandboxed execution
- Per-thread remote sandboxes record command status, exit codes, and output files.
- Gated profiles
- Agent capability resolves through the same permission layer as every other surface.
Stack: Convex · Azure OpenAI · Daytona · React Native · TypeScript
Database I/O reduction
I traced a month of database bandwidth back to one query and a cursor bug.
One list query consumed 74 GB against a 50 GB monthly database-I/O budget. Tracing it produced a documented root cause rather than a guess: a server-preloaded query and its client equivalent both subscribing after hydration, per-row relation fanout, reactive invalidation from writes to widely-subscribed documents, and an overfetch-filter-slice pagination path deriving its cursor from the wrong batch — which could silently skip rows between pages.
Highlights
- Measured first
- Per-function bandwidth and cache hit rates identified the cost before anything changed.
- Correctness bug
- The investigation surfaced a pagination cursor that could skip rows between pages.
- Documented cause
- Duplicate subscriptions, relation fanout, and invalidation were written up, not guessed.
Stack: Convex · React · TypeScript
