When a generative AI chooses its own path, there are no flows to define and map. I built a modular component system the AI assembles on its own, and tested it against real scenarios.
Roofstock is a real estate investment platform for buying, managing, and selling rental properties. Its property management arm, Mynd, is a full-service management company handling tenant placement, maintenance, rent collection, and day-to-day operations for thousands of single-family rentals across multiple markets. When something breaks in one of those homes, the resident needs a fast way to report it, and Mynd needs enough detail to dispatch the right fix without a round of follow-up questions.
Residents reach Mynd mainly through ResBot, an AI chatbot introduced not long before this project, which was already bringing case volume down. Mynd wanted to expand what the bot could resolve on its own instead of passing along.
ResBot handled routine questions well but couldn’t intake service requests. When a resident had a leaky faucet or a broken air conditioner, ResBot handed them off to a third-party service called Mezo.
Mezo’s intake was a rigid decision tree with a lot of button-clicking. Residents had to complete several steps before they could describe the problem in their own words, and there was no way to report more than one issue at a time. Many picked “I don’t know” just to get through the flow, and the requests came in shaped by whatever they had clicked rather than what was actually wrong.
Every gap in the intake became manual work for someone else.
Requests arrived without photos, so maintenance project coordinators (MPCs) had to go back to the resident and ask.
Many service requests arrived with no record of troubleshooting, which meant another follow-up.
Duplicate service requests were common, since residents couldn’t see whether their issue was already open.
Resident-submitted requests didn’t follow the internal team’s flow at all, so the data looked different depending on who created it.
The handoff between the two automated services was disjointed, and Mezo was expensive. We wanted to replace it with an in-house solution.
We had two months from kickoff to shipped, and the Mezo contract had to end on the same date. There was no phased rollout to fall back on and no option to launch part of the experience. Whatever we designed had to replace Mezo completely on the day it went live.
That ruled out learning as we went, which meant I had to know what a complete service request required before designing how the bot would collect it.
Finding out what a complete service request required meant asking the people who work them. Through stakeholder interviews with MPCs and the maintenance team, I cataloged 32 distinct pieces of information the AI might need to collect during any given intake conversation.
What surprised me was how much of that list was contextual. A clogged toilet in a unit with two bathrooms is routine. A clogged toilet in a unit with one bathroom, occupied by a family, is urgent. Whether to ask about Section 8 status, HUD standards, or move-out timing depends on metadata the system may or may not have. The information needed shifted based on the conversation.
That created a tension between speed of reporting and depth of information. Residents want to report an issue in a few taps and have it resolved fast. Mynd wants enough detail that an MPC can send the request straight to a vendor without touching it. A stakeholder described the “magic wand” version as enough information that nobody has to follow up with the resident after submission.
Explain to residents why providing detail benefits them: “Answer now and we can fix things quicker, with the right parts on the first visit.”
Traditional UX for a chatbot means mapping conversational flows. If the user says X, go to branch Y. But ResBot wasn’t a decision tree. It was a generative AI that evaluated context and chose its next action dynamically, so mapping every branch would break the moment it deviated from the script. The 32 information points weren’t a linear checklist either. Different conversations would need different subsets, in different orders, depending on what the resident described. I wasn’t designing a flow. I was designing a system of parts that could create any flow.
Creating a system of parts meant defining each piece well enough for the AI to use, and ruling out mapped branches.
The governing design principle was modular component design. Build the individual pieces the AI can use, then let it assemble them dynamically based on the conversation.
Map the conversational paths instead. “If the resident mentions water, go to the plumbing branch.” That’s how Mezo worked, and how most chatbot design works. Rigid flows would constrain the contextual judgement that makes a generative AI useful, and recreate the experience we were replacing.
I created 22 components in three groups: what the AI uses to collect information, what it uses to present information, and special workflows it may need to run. For each one, the spec captured what it’s for, when the AI should use it, what data it takes in and produces, and whether it mapped to an existing ResBot UI element or required a new build.
Once the AI understands the issue, four actions can follow, and the order matters.
A stakeholder raised a concern that too many diagnostic questions could slow things down and block service request creation. The proposal was to create the work order first and ask diagnostic questions as follow-ups. It made sense at first, get the request into the system fast, then enrich it.
Reviewing the full sequence turned up a structural problem. After work order creation, the next step was scheduling, which linked the resident out of the bot. A resident who clicked through to book an appointment had no reason to come back to the conversation. Every diagnostic question after that point would likely go unanswered, and the data Mynd needed most would be the data it lost (what the technician should bring, how severe the issue is, whether the resident had already tried something). Creating first and asking later optimized for the speed of making a record at the cost of the information that makes the record useful.
We split intake questions in two. Required questions captured the minimum needed to create a work order. Optional questions enriched it but never blocked it, so even if a resident abandoned halfway through the process, we had enough for an informed dispatch decision.
Engineering flagged that the bot could take 40 to 60 seconds to respond and the loading spinner would time out. They asked me for messaging to show the resident while it worked.
The first attempt used task-specific messages like “understanding your issue,” but we couldn’t predict which step was slow, so the message could often be wrong. The second offered a handoff to a human, but that capability didn’t exist. That’s when I stepped back and reconsidered the ask. We were treating this as a messaging problem, but the messaging was covering for a performance problem.
Frequent updates draw more attention to a delay, so we went with fewer messages and longer silent gaps. We also avoided apology framing. Apologizing at 30 seconds implies something has gone wrong, when a longer wait sometimes means the bot is working through a detailed description more thoroughly. The final copy set an expectation instead of apologizing, and tied the wait to the resident’s own input. I documented the whole thing as interim, so nobody would mistake polished delay copy for a fixed performance problem.
A long intake gives a resident more chances to step away, and some of what they’d already answered was expensive to collect twice. I designed a tiered resume system based on how long they were gone:
Auto-continue exactly where they left off.
Resume with confirmation, preserve expensive to recapture data.
Smart summary with options to continue, report a new issue, or check status.
With no flow diagram, I needed a validation method that proved the components worked together without prescribing how they’d be combined. I worked with the team to define six scenarios covering the situations we expected most often, plus a few to test the harder paths. For each one, I mapped which components could appear, in what order, and what data would flow between them.
Nine components carried most of the intake conversation. Each one had to make sense the moment it appeared, since the resident had no way of knowing what the AI would ask next.
The project had to do three things: fix the disjointed handoff, make the data consistent, and close the mismatch between a decision-tree design and a generative AI.
The design replaced Mezo completely, which gave us control over the intake experience and its data. There was no partial version to fall back on, so the new intake stood on its own from day one.
Residents now report issues inside the bot they were already talking to. There is no handoff to a second interface, and no context lost between the two systems.
Resident-submitted and internal requests run through one system, so the data no longer depends on who created it. The maintenance team gets the same request either way.
The component library and spec outlast this project. Any generative AI interaction can use the same structure, and a new component needs no redesign of the existing ones.
Looking back, I’d speak up on the order we built things in.
I prioritized components by engineering effort, dependencies, and what the system couldn’t function without. The team built scheduling, media upload, and availability capture first, based on perceived impact. Core workflow components like duplicate detection and emergency handling shipped later, and the full library was never finished.