# Urcrib: case study by Yusuf Ibrahim (Saint)

> Your home at its real size. Measure the room with what you have, place real furniture at real size, and find out what won't fit before you pay for it.

Page: https://madebysaint.xyz/work/urcrib

- Role: Designed and built solo, end to end
- Timeline: Apr 2025 – present
- Status: Live, in preview
- Shipped: Planner, delivery check, 3D, vendor portal and console
- Product: https://urcrib.com
- Tags: Solo · Product Design & Engineering · Web App, Vendor Portal & Console

## At a glance

- Problem: Furnishing a home is a run of purchases you can't take back, made without measurements. Planners assume a tape measure, foreign catalogues and pretty pictures.
- What he did: Designed and built it alone: the brand, three apps, and a geometry engine where every length carries how it was measured.
- Outcome: Live in preview. The planner, delivery check and 3D work fully, offline; ordering and the vendor side are interfaces waiting on a backend.

Hi, this is Urcrib, the one I designed and built entirely on my own. It turns a room you already live in into a plan you can buy against. It's live in preview, and I've been strict about what it claims. Ask away, or just scroll.

## So what is Urcrib?

Furnishing here is a run of irreversible purchases made with no information: a wardrobe built to a guess, carried across town, stuck at the door. Urcrib lets you measure with what you have (floor tiles, paces, a tape), builds a scaled plan, places real furniture at its real size, and tells you what won't fit and why, before you pay.

![Urcrib landing page](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/site-00.png)

- **204**: pieces of furniture at real size
- **26**: fit rules checked on every move
- **33**: layouts to start from
- **±5%**: when you count floor tiles

## What did you do on it?

All of it. I designed and built Urcrib end to end: the name and brand, the product decisions, the customer app, the vendor portal, the staff console, and the geometry engine underneath them. I wrote the product spec and the engineering standards first, then built with AI coding agents working from those specs, reviewing every change against them.

- **3**: apps: planner, vendor portal, console
- **1,275**: automated tests
- **11**: invariants, each with a check in CI
- **1**: person

## What does planning a room look like?

You pick what you're furnishing (a flat, a bungalow, a storey building or a shop), start from a layout, and measure each room the way you can. Then you add pieces from the catalogue at true size and the plan tells you, in plain words, what fits and what to look at. It works on any phone, offline once opened.

![What are you furnishing?](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-1start.png)
![Measure your mini flat](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-2template.png)
![The editor with the furniture catalogue](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-3editor.png)
![A wardrobe and armchair placed: everything fits](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-5placed.png)

![Start on a phone](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_780/casestudies/ur/app-m-1start.png)
![Measuring by tiles on a phone](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_780/casestudies/ur/app-m-2template.png)
![The plan on a phone](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_780/casestudies/ur/app-m-5placed.png)
![3D on a phone](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_780/casestudies/ur/app-m-8full3d.png)
*On a phone, where most plans are made.*

## How do you measure a room without a tape?

Not everyone has a tape measure, but a lot of floors are tiled. So you can count the tiles along a wall, say whether the end tile is cut, and the plan works out the length. The rule that holds it together: every length carries how it was measured, and how sure it is comes from that, never typed in. Pick a method and watch the plan's confidence change.

*(An interactive demo is on the page: https://madebysaint.xyz.)*

![Every length says how it was measured](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/site-10.png)

```ts title="packages/geometry/src/dimension.ts"
/** The only constructor. It cannot be called without a source. */
export function dimension(
  value: Millimetres,
  source: Source,
  detail?: DimensionDetail,
): Dimension {
  if ((RESERVED_SOURCES as readonly Source[]).includes(source)) {
    throw new SourceReservedError(source);
  }
  return {
    value,
    source,
    toleranceMm: toleranceFor(source, value, detail?.knownDistance),
  };
}
```

Lengths are stored as whole millimetres everywhere, with no floats or unit strings. A wall you taped is surer than one you paced, and the plan never pretends otherwise.

There's even a source called nominal, for the size a product is sold as: a bed sold as "6 by 6". It isn't a measurement, but it isn't a guess either, and filing it as either would have been a lie the fit checks then believed.

## How does it decide what fits?

A rules engine of 26 rules checks every move: walkways, chairs pulling out from a dining table, a wardrobe door swinging open, a bed you can get round, a TV at a sensible distance. Every threshold lives in config, not in the code, so a market can be re-tuned without a release. And nothing it says blocks a placement: it explains, you decide.

```ts title="packages/geometry/src/rules/config.ts"
/**
 * INVARIANT 4.4 — rules are data.
 *
 * Every clearance threshold in the product lives in this file and nowhere
 * else. The evaluator reads them; it never contains one.
 */
export type RuleId =
  | 'primary_walkway'
  | 'dining_chair_pullout'
  | 'wardrobe_door_swing'
  | 'bed_side_access'
  | 'carried_up_the_stairs'
  // …and 21 more
```

Each rule has a test that fires and one that doesn't, so a rule that quietly stopped working would fail the build.

## Will it actually get through the door?

A three-seater that fits the parlour is still a wasted delivery if it won't pass the front door, and here that's found out on the day, by four men on a staircase. So Urcrib checks the doorway, the door head and the turn in from a corridor, using measurements you've already taken. It also works on its own at /delivery, with no account. Try it with a wardrobe.

*(An interactive demo is on the page: https://madebysaint.xyz.)*

![Will it fit through the door? on the landing page](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/site-03.png)
![The delivery check's answer, saying what it did and didn't check](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/delivery-02.png)

The answer always says what it didn't check: if you didn't give a door height, it tells you the head wasn't checked.

Angling a piece diagonally in the doorway was built and then taken out again. For a rectangle it can never squeeze a piece through an opening narrower than its own smallest side, and the six faces already try every orientation that leads with that side. The reasoning is written into the code so nobody reopens it on intuition.

## Can you see it in 3D?

Yes, two ways. A flat 3D drawing that runs anywhere, and a full three.js view you can turn and look inside, across every floor and the stairs between. It's massing only, on purpose: photorealism is a non-goal, because a pretty render isn't a measurement. You can export the building as a 3D file for other tools.

![The flat 3D view](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-7lite3d.png)
![The full 3D view](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/app-d-8full3d.png)

## Is all of it actually working?

Not yet, and the product says so. The planner is real; everything that needs a server is an interface. I keep a ledger of every promise the site makes against what actually works, checked against the code. Before showing it publicly I changed the site from reading as finished to a clear Preview: everything in the planner is open and free, and what isn't ready says “soon”.

- **Before the preview:** Free with an account. Sign in and create an account. Plans kept in your account.
- **Since 13 Oct 2026:** Everything in the planner open, no account. Plans kept on this phone. Accounts and paying open soon.

Generated pictures of furniture are never used as a measurement: the image and dimension pipelines share no types, so a pretty picture can't sneak into a fit verdict.

## How did you build three apps alone?

By writing the rules down first. The engineering standards list eleven invariants, each with a check in CI, and are written to be pasted into an AI coding agent's context. Every task names the invariant it touches, its boundary, the test that proves it, and when it's done. Then I verify before accepting, ending on one question: does the test fail when the code is reverted?

- **Which invariant it touches:** Integer millimetres, provenance, rules as data, prices with a vendor and date…
- **The boundary:** Which package or app, and what not to touch.
- **The test that proves it:** Named before the code is written.
- **Done, in observable terms:** Not "implemented", but what a person can now do.

> Does the test fail when the code is reverted? (the last check before accepting any change)

## What does a furniture seller see?

A portal at vendors.urcrib.com where a quote request arrives already measured: the room, the piece, the doorway and the stairs it has to get through. The vendor answers with a quote, talks to the customer in messages, and lists their own pieces at the size they are sold. I designed it as a work queue, not a shop dashboard: what needs an answer today comes first. It runs on sample data, and says so on every screen.

![Vendor overview: what needs an answer today](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-overview.png)
![Quote requests, newest first](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-requests.png)
![A quote request with the room, the piece and the doorway already measured](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-request.png)
![Writing a quote against the measured request](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-quote.png)
![Messages with the customer](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-messages.png)
![The vendor's own pieces, listed at the size they are sold](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-v-pieces.png)
*The vendor portal on its sample data.*

- **3**: personas in the scripted test: customer, vendor, owner
- **70**: findings, ranked before anything was fixed

I tested the hand-offs between the three apps with a scripted run as three people: a customer who plans and asks for a quote, a vendor who answers it, and the owner who checks both. It found 70 problems, which I ranked before fixing any of them.

## And the people who run Urcrib?

They get a console. A Today queue of what's waiting, catalogue checks where a size is verified before it's trusted, vendor checks (the call, the visit, the registration, the bank account), order decisions and disputes, trust and safety, support and a team page. The part I'm proudest of is invisible: every action goes through one door. A screen never changes anything itself. It asks one function, attempt, which checks the person's role, wants a reason for anything that touches someone else, needs a second person to approve refunds and price changes, and stops people changing their own access. Then it writes the audit log, whether the action went through or was refused.

![Console Today: everything waiting, by area](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-today.png)
![Catalogue review: sizes verified before they are trusted](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-catalogue.png)
![Vendor checks: the call, the visit, registration and bank](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-vendor-checks.png)
![Order decisions: late orders, cancellations and disputes](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-order-decisions.png)
![Team and roles: what each role may do](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-team.png)
![The audit log: who did it, and who tried](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-audit.png)
*The console on its sample records.*

1. **Role:** Is this person switched on, and does their role allow it?
2. **Reason:** If one is required, is it worth reading? Kept in the log.
3. **Second person:** Refunds, price changes, one-to-one emails: someone other than whoever asked.
4. **Not to self:** Nobody changes their own role or switches themselves off.
5. **Audit log:** Written either way: done, or refused and why.

```ts title="apps/admin/src/access/actions.ts"
'refund.approve': { area: 'payments', needs: 'payments.refund.approve',
  summary: 'approved a refund', reason: 'optional', secondPerson: true },
'staff.role': { area: 'team', needs: 'team.manage',
  summary: 'changed somebody’s role', reason: 'required', notToSelf: true },

// The only door. Pure, so the rules are tested without a screen.
if (!actor.active) return 'Your access is switched off.';
if (!can(actor.role, action.needs)) return 'Your role does not allow this.';
if (reasonMissing(attempt)) return 'Say why, in a few words. It is kept in the audit log.';
```

Every action is a row of data: the permission it takes, whether it needs a reason, whether it needs a second person. So adding an action means adding a row, and the rules are unit-tested without rendering a screen. Someone allowed to act who hasn't yet said why hasn't tried anything; they have an unfinished form, so the log stays clean.

## Who explains all this to people?

I wrote it, and I built the pages it lives on. There's a customer guide and a vendor guide at urcrib.com/guide and /vendor-guide: one long page on screen that prints as an A4 book, chapter by chapter. Every price, limit and tolerance in them is read from the same code the product uses, so the guide can't teach a figure the product doesn't have, and the pictures are the product's own screens, retaken by script when the UI changes. The blog is written in the console: drafts and published posts, five categories, a live preview of how a post will read. Publishing needs a reason, like everything else there. The site reads those posts through a small client package, next to the articles written in code. The brand kit at urcrib.com/brand reads the logo and colours from the design system itself, so it can't drift from the product, and builds its downloads in the browser.

![The Urcrib customer guide: from an empty room to a list you can buy from](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-m-guide-0.png)
![The vendor guide: sell to customers who already know the size](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-m-vendor-guide-0.png)
*The guides, live at urcrib.com.*

![The console blog: drafts and published posts](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-blog.png)
![Writing a post with a live preview of how it will read](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-c-blog-edit.png)
*Writing in the console.*

![The public blog: notes on measuring, fitting and furnishing](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-m-blog-0.png)
![The brand kit: the mark, the lockup and how to use them](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/ops-m-brand-0.png)
*What it publishes to.*

- **Usually:** Help pages written once, with screenshots and prices copied in by hand. They go stale the first time the product changes.
- **Here:** Figures come from the pricing and geometry code, pictures are retaken by script, and the brand kit reads the design tokens. When the product changes, the words follow.

## Did you design the brand too?

Yes: the mark, the lockup, the colours and the type, then the social launch. The posts follow the same rule as the product: show a measurement where others show an adjective. Every graphic uses real screens from the planner, and I wrote down what the account never does before making any of them: no prices, no made-up users or reviews, no promising features that aren't open (the scan import says "soon" on the site, so it never appears in a post).

![The bed is in, and the door will not close](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-1.png)
![Count the floor tiles](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-2.png)
![Place it at the size it is sold](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-3.png)
![Know what blocks a door before delivery day](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-4.png)
![Will it get through the door?](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-5.png)
![See if it fits before you pay](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-carousel-6.png)
*The launch carousel, six slides.*

![Every piece now has a picture](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-catalogue.png)
![A long one-bed flat: will everything fit?](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-plan-a.png)
![A round living room: can you still furnish it?](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-plan-b.png)
![The piece in hand, always one tap away](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-remove.png)
![No tape? Count the floor tiles](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_900/casestudies/ur/soc-tiles.png)
*Single posts, each built on a real screen.*

![The Urcrib X banner: your home, at its real size](https://res.cloudinary.com/dljwhrpqh/image/upload/f_auto,q_auto,w_1600/casestudies/ur/soc-x-banner.png)
*The X banner, with the sides kept clear for the phone crop.*

## If you started over tomorrow, what would you do differently?

I'd keep the claims ledger from the first public page, not add it after launch. And I'd put the planner in front of someone who has never seen it sooner: that's the exit condition I set for the very first phase, and it's still the test that matters most.

- **Write the invariants first:** They're what let agents build fast without breaking the product.
- **Say only what's true:** Track every claim against working code from day one.
- **Measure with what people have:** Tiles and paces, honestly labelled, beat waiting for a tape.
- **Test with strangers early:** A scripted run finds broken hand-offs; only a real user finds confusion.
