An ongoing engagement. We work in fixed-time, variable-scope stages — each one starting with a shaping conversation to define the smallest sensible next step. No retainers, no open-ended sprints. Each stage delivers something usable.
Aire Spaces
- 1
Property Rental Reports
Workflow Automation ReportingReplaced spreadsheet-driven monthly client reporting with an automated reporting application. CSV ingestion, report generation, dual delivery as a web page and PDF, Supabase-backed client portal with access control.
Read case study - 2
Hostaway Integration
Integration Workflow AutomationAutomated the manual data entry step by connecting directly to Hostaway, their property management platform. Built an admin report workbench for reviewing and confirming pulled data before report generation.
Read write-up -
Client Portal
Client Portal AuthSupabase-powered portal giving each client a login to access their reports directly. Replaced the email-a-link workflow. Includes a light client and property management interface.
Write-up in progress - 3
Reconciliation Agent
Agent ReconciliationRather than build a full bank reconciliation UI — which would have ballooned with edge cases — we explored an agent-based approach. A lightweight composable tool that handles the judgment-heavy parts of reconciliation without a complex interface.
Read experiment notes
Feeling stuck or unsure how to proceed?
With every project, we follow a fixed-time, variable-scope approach inspired by Basecamp's Shape Up. Book a short 30 minute call with us to help shape and reduce the scope of your problem until a sensible next step emerges. No obligation beyond that. You'll leave with a clearer framing and a concrete next step, whether or not we continue working together.
Property Rental Reports
Replaced spreadsheet-driven monthly client reporting with an automated reporting application. CSV ingestion, report generation, dual delivery as a web page and PDF, Supabase-backed client portal with access control.
A series of spreadsheets that were, honestly, a mess.
Aire Spaces came to us with a reporting problem. What they showed us was a series of interconnected Excel sheets — duplicated steps to move data between tabs, manual copy-paste between sources, and one sheet essentially acting as a database for eleven months of property revenue. It held together, in the way that hand-built spreadsheets tend to. But it was fragile, hard to hand off, and impossible to scale.
Their existing client report was functional but not pretty. No branding. Column labels that were overly clustered and hard to read. Little room for growth — things had been added over time in ways that left the layout brittle. As it turned out, some of the improvements we delivered later in the engagement would not have been possible in the format they started with.
On top of this, the reporting workflow crossed over with their invoicing process. Not cleanly — invoicing prep sat adjacent to reporting in a way that added time and friction to what was already a slow monthly cycle. That overlap became part of the brief.
The state of things
- • Interconnected Excel sheets with duplicated data movement
- • A single sheet acting as an 11-month revenue database
- • Client reports with hard-to-read columns
- • No room to extend the report format without breaking it
- • Reporting and invoicing prep intertwined, adding friction to both
Fitting into their workflow
The first thing we established was a constraint: whatever we built as a first step had to slot into how Aire Spaces already worked. We were not going to ask them to take a great leap from one month to the next — the system had to earn its place gradually. So we made a small but meaningful modification to their existing sheet workflow, producing a CSV template they could drop into a simple upload interface. First step: fill out this slightly modified template and drop it here. That was it. No disruption. No retraining.
The highlight of shaping this stage was realising that whatever we do first NEEDS to fit into their workflow. We modified as little as possible and built around what already existed.
Rapid prototyping the report
Before we wrote a line of application code, we redesigned their client-facing report. We used Figma Make to move fast — not static mocks, not a wired-together prototype, but something that behaved like a real product. This proved unexpectedly valuable.
As the client put it: it allowed us to explore a whole space of interactions that would not have been apparent from static mocks. We could sit down together with something that responded and clicked through, gather high-fidelity feedback at an early stage, and use it as the foundation for the codebase when we moved into build. The facelift also gave Aire Spaces an updated version of the document they were already sharing with clients — an immediate, tangible improvement before the application was anywhere near done.
Supabase: auth, history, and a light client portal
For user access, we reached for Supabase. Fast to spin up, a migrations-based schema, and a row-level security layer that meant clients did not need to receive a link in an email each month — they got a login to a portal where their reports lived. The time to ship this was minimal relative to the value it added.
Supabase also became the persistence layer for report history. Month after month, data accumulated without anyone having to manage it. Historical views built themselves. The scope naturally extended into a light client management interface — adding clients, assigning properties, controlling access — but we kept it tight and shipped it without letting it sprawl.
Supabase — fast to wire up, RLS for access control, and a clean persistence layer for historical data. A strong, malleable component. A recurring part of how we build.
- 01
A report that could grow
The redesigned format gave Aire Spaces room they did not have before. Additions made in later stages would not have been possible in the original layout. Rapid prototyping made this possible in days, not weeks.
- 02
A process that fit
We did not ask Aire Spaces to change how they worked in order to adopt the system. The system earned its place gradually — modifying as little as possible and building around what already existed.
- 03
History that builds itself
Supabase as the persistence layer means every month of data accumulates without anyone managing it. The historical view is a byproduct of normal operation, not a separate project.
- 04
Access without email chains
Clients log in to a portal. Reports live there, permanently. No more sending links, no more broken attachments, no more chasing the right version.
Hostaway Integration
Automated the manual data entry step by connecting directly to Hostaway, their property management platform. Built an admin report workbench for reviewing and confirming pulled data before report generation.
Cutting out the manual data entry entirely.
With the core application running, the next stage was eliminating the manual step that remained: each month, someone had to pull data from Hostaway — Aire Spaces’ operational platform — and populate the sheet before uploading. We built a Hostaway integration that does this automatically.
We also built an admin-side report workbench: a structured interface for reviewing the pulled data, supplementing it with the fields that still come from outside Hostaway, and confirming before reports are generated. This gave the team a clear and controlled place to handle edge cases without having to touch a spreadsheet.
On building the integration
- •Explored the Hostaway API surface before committing to any data model.
- •Drafted a CLI tool first — faster to validate the integration without the bulk of the application UI around it.
- •Used Figma Make again to prototype the admin workbench UI before coding it.
- •AI-assisted development for the integration layer — fast to write, but still required the exploration work up front to get it right.
CLI-first validation is a pattern worth repeating. Get confidence in the integration before adding the UI. Cheaper to throw away a script than a screen.
- 01
An integration validated cheaply
CLI-first development meant the Hostaway integration was working and tested before we invested in the UI around it. A pattern we now repeat.
- 02
A controlled place for edge cases
The admin workbench gives the team somewhere structured to review, supplement, and confirm pulled data — without ever touching a spreadsheet.
Reconciliation Agent
Rather than build a full bank reconciliation UI — which would have ballooned with edge cases — we explored an agent-based approach. A lightweight composable tool that handles the judgment-heavy parts of reconciliation without a complex interface.
When the obvious next step is the wrong one.
The natural extension of the integration work pointed toward a bank feed — automatically reconciling income data against actual payments, removing the last manual thread from the reporting cycle. We scoped it. And we decided not to build it.
Bank feed integrations for custom-built projects carry a level of compliance overhead that sits well outside the scope of a reporting tool. More importantly, the reconciliation UI required to communicate what an automated process has done — the edge cases, the mismatches, the exceptions — would have taken the project somewhere neither of us wanted it to go. We would have spent months building something that made the process more complicated to audit, not less.
Instead, we experimented with something smaller and more interesting. As we had started building AI-powered tools internally, we saw a different kind of opportunity — not a full automated reconciliation pipeline, but an agent-based approach that could handle the messy, judgment-heavy parts of reconciliation without requiring a complex UI to surface it. We built a Reconciliation Agent as an internal experiment: a lightweight, composable tool that could do meaningful lifting in a fraction of the time.
— Codemyriad on the Aire Spaces engagement”It was not just about using agents to get work done smarter. It was about finding ways to get something valuable into the client’s hands quickly — something that would have taken months to build properly as a traditional UI.”
The Reconciliation Agent is an ongoing internal experiment. A write-up is in progress.
- 01
A smarter scope decision
Choosing not to build the bank feed saved months of work on something that would have added complexity rather than clarity. The experiment with the Reconciliation Agent pointed toward a better path.
Case study components
Every reusable component used inside case studies, rendered out of narrative context with placeholder data. Use this to refine the components themselves, or as a copy-paste reference when authoring a new MDX case study.
Stage
Section wrapper with a sticky label column. Use to break a case study into named beats (Discovery, Build, Outcome…). Children render in the right column.
- label
- string — required
- heading
- string — optional
import Stage from "@components/case-study/Stage.astro";
Where the project started
The client came to us with a monthly reporting workflow held together by spreadsheets and exported PDFs. They knew it didn't scale; what they didn't yet know was which piece to replace first.
Two weeks of pairing with their ops lead surfaced the actual bottleneck — and a much smaller v1 than anyone had pitched.
Callout
Boxed note with a header and optional icon. Use for technical asides, architecture notes, or "why we chose X" interjections within prose.
- title
- string — required
- icon
- any Lucide icon — optional
- accent
- "teal" | "amber" — default "teal"
import Callout from "@components/case-study/Callout.astro";
import { Database } from "@lucide/astro";
Why local-first
Runtime trade-off
InlineNote
Quieter, italic aside with a neutral white left border. Use sparingly — for a single sentence of meta-commentary inside a stage. No configurable props beyond the slot (kept intentionally simple so accented sidebars stay distinct as the Quote component).
import InlineNote from "@components/case-study/InlineNote.astro";
In hindsight, the spreadsheet wasn't broken — the manual reconciliation on top of it was.
Quote
Pulled testimonial or framing line. Use sparingly to mark a beat that should land.
- attribution
- string — optional
- accent
- "teal" | "amber" — default "teal"
- size
- "default" | "large" — default "default"
import Quote from "@components/case-study/Quote.astro";
We used to lose half a day every month chasing missing rows. Now the report just… exists on the first.— Ops Lead, client team
You shipped the thing I'd been describing for three years.— Founder
StateList
Titled list of short, bulleted points. Good for "what we considered", "constraints", or "what changed".
- title
- string — required
- items
- string[] — required
- icon
- any Lucide icon — optional
- accent
- "teal" | "amber" — default "amber"
import StateList from "@components/case-study/StateList.astro";
What we ruled out
- • A new dashboard tool — the team already had three.
- • A spreadsheet plugin — solved the symptom, not the cause.
- • Outsourcing the monthly close — locks them in further.
What the v1 included
- • A single source of truth for monthly bookings.
- • Auto-generated PDF and web reports per property.
- • A read-only client portal with magic-link access.
MetricsBar
Inline strip of headline numbers. Lays out as 1/2/3/4 columns based on
the length of metrics — the grid responds
automatically.
- metrics
- { label: string; value: string }[] — required
import MetricsBar from "@components/case-study/MetricsBar.astro";
OutcomeGrid
Vertical list of outcomes (title + short paragraph) tagged with a short ID in a narrow left column. Pairs well after a MetricsBar — numbers above, narrative below.
- outcomes
- { title: string; description: string }[] — required
- heading
- string — optional
- numbering
- "numeric" | "alpha" — default "numeric"
import OutcomeGrid from "@components/case-study/OutcomeGrid.astro";
What changed for the team
- 01
Monthly close is now a checkbox
What used to be a half-day of reconciliation is a single click. The first of every month is no longer a dreaded date.
- 02
New properties onboard in an afternoon
Adding a property to the report set used to mean threading a new tab into every spreadsheet. Now it's a form.
- 03
Clients see what we see
The same report the ops team relies on is what each client gets in their inbox. No second source, no surprises.
- A
Reports survive the internet
Local-first sync means a flaky connection no longer means a missing month.
- B
Audit trail by default
Every value in every report is traceable back to a source row, with no extra effort from the team.
BeforeAfterSlider
Draggable comparison between two images (the classic "before / after"
wipe). Use to show a redesigned interface, a cleaned-up data view, or
any state change that reads visually. Source images can be passed as
props or via named slots (before /
after) for richer content.
- beforeSrc
- string (URL) — optional
- afterSrc
- string (URL) — optional
- beforeAlt
- string — default "Before"
- afterAlt
- string — default "After"
- beforeLabel
- string — default "Before"
- afterLabel
- string — default "After"
- aspectRatio
- string (CSS) — default "16 / 10"
import BeforeAfterSlider from "@components/case-study/BeforeAfterSlider.astro";