myKaarma · 2026 · Case study · 8 min

One price per line was the whole problem.

RecLink is the recommendation-and-estimate tool a service advisor works from on the shop floor, and the part of ServiceCart most often called confusing. I rebuilt it as a working prototype: Good/Better/Best pricing tiers, itemised parts and tire sales on a single estimate, with every change traced through the three screens that render the same data.

Role
Senior product designer, UX lead
Did
Research, interaction design, prototype, test scripts
With
An AI pair-engineer, dealership users, myKaarma CS and product
Status
Shipped prototype, Sep–Oct 2026

Pricing

1 → 3

price tiers per recommendation, tagged in the shop's own vocabulary

Surfaces kept in sync

3

advisor, technician and parts views of the same data

Shipped

19

commits, each with the reasoning written in

Regression

40

browser-driven test scripts written along the way

If you only read one section: what I decided the world how it was built against the live tool what happened differently

The redesigned RecLink recommendation tab: a FAIL concern with Good, Aftermarket and Best priced options, each with labour hours and parts

01 · The world

One set of data, three people reading it.

A multi-point inspection produces a list of recommendations. The technician edits them, the advisor prices and presents them, and the parts counter sources what gets approved. myKaarma shows each of those people a different tab, but the tabs are all rendering the same underlying record.

That is what made the project interesting and what made it dangerous. A change that reads correctly on the advisor's tab has to stay correct on the other two. Most of the bugs in this story were only visible by holding two tabs next to each other.

RecommendationService advisorPrices the work and presents it to the customer
↔
Edit MPITechnicianRecords findings and pre-approved work
↔
PartsParts counterSources and prices what was approved
One record underneath. Change the shape of a recommendation anywhere and all three have to agree.
myKaarma's live Customer MPI screen with left rail, pinned details, customer and vehicle info, and a recommendations list where every concern has one price
The live screen. Every concern carries exactly one price; status has its own column and vocabulary.

Scoped on purpose

Drop the shell, keep the workflow.

The real screen carries a lot of operational chrome. The prototype deliberately leaves it behind so the estimate model could be iterated fast, in one file, without dragging the rest of the app along for every change.

–ShopChat, RO and MPI history, OEM lookup
–Pinned details, video-grader score, QR code
–Internal team panel (advisor, parts rep, owner, video taker)
+Recommendation, Edit MPI and Parts tabs
+The estimate, status and pre-approved model under them

02 · How it was built

Feedback in the morning, shipped by the afternoon.

I designed in the live prototype with an AI pair-engineer rather than in static mocks. Every change moved through the same loop: a specific note, sometimes a screenshot with an arrow at the exact pixel, implemented directly, verified in a headless browser, committed with the reasoning written out.

NoteA sentence, or a screenshot with an arrow
ChangeMade in the running prototype, not a mock
VerifyHeadless browser screenshot, zero console errors
CommitMessage says why, not just what
A review note: 'this line should be a little thinner' with an arrow pointing at a 2px divider between Good, Aftermarket and Best options
A real note from review.
The same options after the fix: a 1px connector line running flush to the rows above and below
Same day: 2px to 1px, run flush to the rows, confirmed with a fresh screenshot before moving on.
19

feature and fix commits, each with its reasoning in the message

3

parallel initiatives on isolated branches

40

automated browser-driven regression scripts

0

console errors tolerated in a shippable state, checked every time

e40bf00Sep 23Replicate Recommendation tab's Pre-Approved click behaviour in Edit MPI
80a00aeSep 23Multi-select tags, default-option-first, itemised parts toggle
5689a27Sep 24Wire the Parts tab to real recommendation and Pre-Approved data
15c27d2Sep 29Add Dealer Tire integration: "Add tires?" entry, side-panel selection flow
0ed6748Sep 29Rework tire panel to mirror MCI mobile's actual Recommend Tires screen
6828f52Sep 30Fix grey coach-mark backdrop bug; rework tire panel per feedback
c6a6318Oct 1Hide axle target CTAs when not staggered; auto-advance front → rear
533c050Oct 1Actually hide Estimate Options breakdown in Phase 1 lists

03 · What I decided

Five decisions, and the bug each one surfaced.

Each change below traces one idea through every place the data already lived. The pattern repeats: a first pass that looked right in one tab, caught by comparison with another.

Decision 1 · Pricing

From one flat price to Good / Better / Best.

Every recommendation could only ever carry a single price. The estimate section gained a proper multi-option mode: a tab strip of priced alternatives, each with its own labour, parts and tags, tagged against the shop's real vocabulary (Recommended, On your car now, Good, Better, Best, Best warranty, OEM, Aftermarket) with multi-select.

One option is always the default the customer sees first. It's tracked by its own id, not its tab position, so it survives a tab being reordered or deleted, and there's an explicit "Make default" control rather than leaving it implicit.

An early review surfaced a second bug by accident: a leftover coach-mark overlay dimmed the whole estimate panel every time an existing rec was opened. Fixed in the same pass.

ShippedChoice architectureDealer interviews
Early estimate editor: a single tag, no default control, and a grey coach-mark overlay dimming the panel
Before: single tag, no default control, and the stray overlay.
Estimate editor with Good, Better and Best option tabs, multi-select tags and a visible Default badge
After: full tag set, explicit default.

Decision 2 · Parts

Flat or itemised, as a deliberate choice.

A dealer can keep the simple flat "parts estimate $" field, or switch the whole shop to line-by-line itemised parts. The toggle lives in the settings menu next to the existing Phase 1 / Phase 2 switch it actually relates to, not as a new piece of chrome.

Following the data further: pre-approved lines gained the same itemised option, with their total deferring to the itemised subtotal the moment it has real rows. And the Parts tab, previously a static mock that rendered two blank rows no matter what, got wired to the same real data with its own tab per pricing tier.

ShippedProgressive disclosure
Flat parts mode: one parts estimate field and an in-stock flag
Flat mode, unchanged for simpler shops.
Settings kebab menu with Phase toggle, Itemised Parts and Dealer Tire integration side by side
The toggle, next to the Phase switch.
Parts tab with Good, Aftermarket and Best tabs each holding their own itemised parts rows
Parts tab wired to real data, one tab per tier.

Decision 3 · New capability

Sell tires without leaving the estimate.

The brief was simple to state and easy to get wrong: let an advisor add tires suggested from the vehicle's tread-depth readings, without inventing a parallel "tire card" system that the Parts tab, the customer total and the estimate maths don't know about. Every version kept one constraint: a selected tire becomes a normal itemised parts row.

The first version worked but invented its own interaction pattern. Once pointed at the mobile "Recommend Tires" screen the techs already use, it was rebuilt to mirror that directly: per-axle size and quantity, front/rear install pricing, real SKUs and rebates. Selecting a tire also seeds the ancillary parts a tire sale needs (valve stems, disposal fee, TPMS kit) so one choice produces a complete, editable ticket instead of one suspiciously round number.

ShippedMatch the mental modelTech shadowing
First tire selection panel: four-position toggle, one tire size, click-to-select cards and a separate Compare options view
v1: functional, but its own pattern.
Rebuilt tire flow with a Tires added block inside the estimate, per-axle sizes and quantities
Rebuilt against the real reference; tires in their own block.

Decision 4 · Interaction

Two questions, two pieces of state.

The trickiest interaction problem was small. When front and rear tires differ, how does an advisor say which axle they're choosing for right now, versus see which axle already has a tire? The first pass tangled those into one value, so a button could read "selected" while describing the wrong thing.

They became two independent states: one drives the target button and where a catalogue click lands, the other drives whether an axle card renders as a dashed placeholder or a solid, priced block. Picking a front tire now hands targeting to Rear automatically, and the card control lets you switch back by hand.

ShippedVisibility of system status
Early axle controls where 'Selecting for Front' described both the target and whether a tire was chosen
Early pass: one state doing two jobs.
Final axle design: front card solid and priced, rear card targeted, with a 1-or-2 quantity control
Final: target and selection rendered independently.

Decision 5 · Tiers of complexity

Keep Phase 1 simple, actually.

RecLink already draws a line between a Phase 1 experience for simpler shops and a fuller Phase 2. Estimate Options were built as Phase 2. The first pass only hid the entry point for creating options; a rec that already carried saved options still rendered its full breakdown in Phase 1's lists, because the list view had its own ungated check.

Same pattern as the Pre-Approved bug in Edit MPI, which opened a plausible-looking modal with the wrong data because its click handler passed arguments in the wrong order and dropped a flag. Both were invisible in isolation and obvious by comparison.

ShippedCross-surface review
Review note: Phase 1 is still showing estimate options
Caught in review.
Phase 1 list view collapsed back to a single-line summary
Phase 1 collapses to the plain summary regardless of what's saved.
Edit MPI's Pre-Approved pill opening the same Pre-Approved Details editor as the Recommendation tab
Edit MPI now opens the same editor, with the same data.

Stripping colour out of the rows and only adding it back on headers and status was the last change, and the one people commented on most. Nobody said "it's calmer." They said "I can see what matters."

04 · Against the live tool

What the prototype adds, and what it still owes.

The real per-line editor is one unified modal for every status. Splitting it into a Recommendation editor and a Pre-Approved editor is what made room for tiering. The trade-offs are listed honestly, because the next step is closing them.

myKaarma's live recommendation modal: one form with Fail, Caution and Pre-approved picker, Original Approved Amount block and inline parts table with Core Charge and BIN
The live editor: one form for every status, Original Labour / Parts on every line.
myKaarma's live Parts tab with Core Charge, BIN, per-line DMS push status and timestamps, and a 'No parts needed' checkbox
The live Parts tab, already richer on fields the prototype doesn't reproduce.

Prototype adds

  • Good / Better / Best option tabs per recommendation
  • Multi-select tags in the shop's vocabulary, explicit default
  • Itemised parts as a shop-wide toggle, per tier
  • Dealer Tire flow that lands as normal parts rows
  • Phase 1 / Phase 2 gating that actually holds

Still owed to the live tool

  • Inline "Mark As" status control inside the modal
  • Service Operation search against the shop catalogue
  • Separate Title and Description fields
  • Core Charge, BIN, DMS push status and timestamps on parts
  • "No parts needed ($0)" checkbox state

05 · What happened

Some dealers loved it. One group didn't.

Tested with dealerships and myKaarma's own customer-service staff, who have been asking when it ships. One test group responded poorly; the signal pointed at recent onboarding and change fatigue rather than the design, and didn't replicate. Reported as it is.

Shipped as

Working prototype

Tiered pricing, itemised parts and tire sales on one estimate, across all three tabs, with 40 regression scripts protecting it. The thing people clicked in testing was the thing that would ship.

Dealers

Loved it

in several pilot dealerships, especially the tier tabs.

Internally

"When?"

Customer-service staff kept asking when it would go live.

Test groups

[n]

dealerships in usability testing; one poor result, not replicated.

Iterations

100s

of design iterations across several directions before the build.

Methods: user interviews, usability tests, research review of support tickets and dealership visits, Playwright regression on every change. Figures in brackets are for Ruth to confirm.

06 · Differently

What I'd change with hindsight.

I'd have written the cross-surface check before the first feature, not after the first bug. Every defect in this project had the same shape: correct in one tab, wrong in another. A script that opens the same rec in all three tabs and diffs what it sees would have caught the Pre-Approved and Phase 1 bugs before anyone did.

I'd also have started the tire flow from the techs' mobile screen on day one instead of designing a fresh pattern first. The rebuild cost a day; the comparison to the real reference was the design.

And I'd have asked the test group that struggled what else had changed for them that month, before reading their results as a verdict on the screen.

Sections 05 and 06 are drafts in Ruth's voice to confirm or rewrite. Screens are from the prototype and from myKaarma's live tool; the live-tool screens may need redacting before publishing.