The Saint Line

Case study

Urcrib

Live, in preview

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.

The scripted part of this conversation carries the verified facts. Live answers only use what's on this site.

Role
Designed and built solo, end to end
Timeline
Apr 2025 – present
Status
Live, in preview

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?

Thought for 2s

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
pieces of furniture at real size
204pieces of furniture at real size
fit rules checked on every move
26fit rules checked on every move
layouts to start from
33layouts to start from
when you count floor tiles
±5%when you count floor tiles

What did you do on it?

Thought for 3s

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.

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

What does planning a room look like?

Thought for 4s

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?Measure your mini flatThe editor with the furniture catalogueA wardrobe and armchair placed: everything fits
Start on a phoneMeasuring by tiles on a phoneThe plan on a phone3D on a phone
On a phone, where most plans are made.

How do you measure a room without a tape?

Thought for 2s

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.

Tap to tryHow did you measure the wall?
Wall 3.60 m ±180 mm (counted tiles)Fits, even at the short end

The shaded band is how wrong the wall could be. A tape is sure to ±30 mm; counted tiles to ±5%. The plan says so instead of pretending. Sample room.

Every length says how it was measured
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.

Explain more

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?

Thought for 3s

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.

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?

Thought for 4s

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.

Tap to tryWardrobe, 3-door: 1.80 × 0.60 × 2.10 m
Leading with a 600 × 1800 mm faceGoes through

It tries all six faces, because a wardrobe can lead with its narrow side. Simplified: the real check also covers the turn in from a corridor, the stairs and the lift.

Will it fit through the door? on the landing pageThe delivery check's answer, saying what it did and didn't check

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.

Explain more

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?

Thought for 2s

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 viewThe full 3D view

Is all of it actually working?

Thought for 3s

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”.

Then · Before the preview

Free with an account. Sign in and create an account. Plans kept in your account.

Now · 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?

Thought for 4s

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 touchesInteger millimetres, provenance, rules as data, prices with a vendor and date…
  • The boundaryWhich package or app, and what not to touch.
  • The test that proves itNamed before the code is written.
  • Done, in observable termsNot "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?

Thought for 2s

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 todayQuote requests, newest firstA quote request with the room, the piece and the doorway already measuredWriting a quote against the measured requestMessages with the customerThe vendor's own pieces, listed at the size they are sold
The vendor portal on its sample data.
personas in the scripted test: customer, vendor, owner
3personas in the scripted test: customer, vendor, owner
findings, ranked before anything was fixed
70findings, ranked before anything was fixed
Explain more

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?

Thought for 3s

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 areaCatalogue review: sizes verified before they are trustedVendor checks: the call, the visit, registration and bankOrder decisions: late orders, cancellations and disputesTeam and roles: what each role may doThe audit log: who did it, and who tried
The console on its sample records.

Is this person switched on, and does their role allow it?

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.';
Explain more

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?

Thought for 4s

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 fromThe vendor guide: sell to customers who already know the size
The guides, live at urcrib.com.
The console blog: drafts and published postsWriting a post with a live preview of how it will read
Writing in the console.
The public blog: notes on measuring, fitting and furnishingThe brand kit: the mark, the lockup and how to use them
What it publishes to.

Then · Usually

Help pages written once, with screenshots and prices copied in by hand. They go stale the first time the product changes.

Now · 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?

Thought for 2s

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 closeCount the floor tilesPlace it at the size it is soldKnow what blocks a door before delivery dayWill it get through the door?See if it fits before you pay
The launch carousel, six slides.
Every piece now has a pictureA long one-bed flat: will everything fit?A round living room: can you still furnish it?The piece in hand, always one tap awayNo tape? Count the floor tiles
Single posts, each built on a real screen.
The Urcrib X banner: your home, at its real size
The X banner, with the sides kept clear for the phone crop.

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

Thought for 3s

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 firstThey're what let agents build fast without breaking the product.
  • Say only what's trueTrack every claim against working code from day one.
  • Measure with what people haveTiles and paces, honestly labelled, beat waiting for a tape.
  • Test with strangers earlyA scripted run finds broken hand-offs; only a real user finds confusion.

The scripted part of this conversation carries the verified facts. Live answers only use what's on this site.