Tejas Patel
Tejas Patel
write to
tjs13.9@gmail.com
theme

All work

MirrorFactory OS

A shop-floor board I built for a framing workshop's iPads. The floor kept its TV, and that taught me more than the board did.

Archived. Its pipeline synced the workshop's orders to Airtable from 1 July to 2 August.

my part
Designed and built, self-initiated
when
January to July 2026
context
The Framing Project, Sydney
team
Solo, with coding agents doing much of the typingThe Framing Project photographs of the workshop and the mirror
status
Archived
The workshop floor in black and white, felt-topped benches with tools and tape in front, moulding stacked against the back wall, coiled air lines hanging from the roof, and two framers at work.
Photograph: The Framing Project

outcomes

  1. 1

    paid sync service marked cancelled in July, the month my pipeline began syncing orders to Airtablereported · sourceMy operations guide for the workshop (the TFP Operations Guide, v2.1, 'Business at a Glance' table, as read on 1 July 2026: the bridge's monthly cost struck through, 'Cancelled (July 2026)'), and the MirrorFactory OS commit of 1 July 2026 that made the Airtable sync a step of the pipeline.

  2. 3 s

    Firestore query behind the mirror portal's data, replaced by a lookup cached in memorymeasured · sourceTimed by my AI assistant, and recorded in my MirrorFactory OS commit of 3 June 2026 (ffde98b): its message gives the 3 s, and its code comment says the mirror orders are now served from an in-memory cache instead of a 3 s Firestore query. The commit keeps no benchmark.

On 10 February I had my assistant check nine overdue invoices against a copy of the workshop’s order data. None had a glass order recorded, and one was still a quote. Out on the floor, mirrors got moved from trolley to trolley again and again while someone searched for stock. I’d been building the system behind it since 12 January, first as an invoice reader for the workshop’s Airtable, and by May it was a board for the floor’s iPads. This is what I designed into it, what the floor did with it, and the lesson I took into Reframed.

February: nine overdue, no glass

Drawing · 10 February

Nine overdue invoices · glass order on record?

  1. Invoice 1: no glass order on record
  2. Invoice 2: no glass order on record
  3. Invoice 3: no glass order on record
  4. Invoice 4: no glass order on record
  5. Invoice 5: no glass order on record
  6. Invoice 6: no glass order on record
  7. Invoice 7: no glass order on record
  8. Invoice 8: no glass order on record
  9. Invoice 9, still a quote: no glass order on record

0 of 9

The workshop tracked its orders in Airtable, and when one changed, nobody on the floor was told. Stock got ordered twice, jobsheets were out of date, and the CNC sat three metres from the spray area in the same room, so its dust landed on frames waiting for paint.

After the check I set up an alert that messaged me at 8 each morning with the jobs due within two days. It ran for four mornings, and on the 14th I asked for it to stop. When the ownership changed later that month, my assistant drafted the floor up from what I told it as a four-phase proposal: a partition for the dust, an inventory overhaul, jobsheets and assembly, and a print and cut station.

One card, one line

decision 1

One card for each line of an order

Drawing · sample data

First board: a card per part

  1. Mirror glassOrdered
  2. Metal shelfOrdered
  3. Flat barOrdered
  4. LED stripQueue
  5. LED driverQueue

qty shown5ordered 1

Rebuild: a card per line

Metal-framed LED mirror

line 1 · copies 1

Ordered

5 parts underneath: glass, shelf, flat bar, LED strip, driver

My first board made a card for every part. A metal-framed LED mirror is glass, two frame parts, an LED strip and a driver, so one mirror put five cards on the board and the job got lost among them. The count was wrong too: it added up the parts and showed 5 where the order said 1. On 31 May I rebuilt the board around the line of the work order, with the parts folded underneath and the count taken from the order’s copies. That’s how the floor already talked about a job.

5 → 1cards per order linereported · sourceMirrorFactory OS HANDOFF.md, resolved bugs, 31 May 2026: '5 BOM cards per sub-line', fixed by production units, '1 card per sub-line'; 'Quantity showing 5 instead of 1', fixed by line_copies = max(Copies).after the rebuild of 31 May

decision 2

The glass moves the card

A tall mirror in a thin black metal frame with rounded corners, hung from the ceiling on two black poles in the corner of a grey plastered room, with two glass wall lights to its left.

the glass moves the cardsourcelib/productionUnits.ts, pickDriverComponent: the glass if there is one, else the frame

flange and polessourcescripts/consolidate.py: a suspended mirror's parts include a flange and poles, made in the workshop

the glass moves the cardsourcelib/productionUnits.ts, pickDriverComponent: the glass if there is one, else the frame

flange and polessourcescripts/consolidate.py: a suspended mirror's parts include a flange and poles, made in the workshop

A suspended mirror on site. Photograph by The Framing Project.

Each card follows the route for its kind of mirror. A suspended one like this goes Queue, Cutting, Welding, Fitting, Spraying, Assembly, Pack Together, Ready. One part decides where the whole card sits: the glass if there is any, else the frame. The glass comes from a supplier, so a mirror’s card waits at Ordered until it’s in, and the manager’s view keeps a tab for jobs with an item on hold, cancelled or not yet ordered.

on the photograph

  • the glass moves the cardlib/productionUnits.ts, pickDriverComponent: the glass if there is one, else the frame
  • flange and polesscripts/consolidate.py: a suspended mirror's parts include a flange and poles, made in the workshop

Here is that card as a drawing you can use, with sample data and the same rules as the code. Claim it when the glass is in, and every move after that writes a row to its history. At Ready the floor’s own flag goes on and the job joins the office’s queue. Its quote, ordered or invoiced status doesn’t change, because finishing a mirror and invoicing it are different facts.

Drawing · sample data · tap to move it

Its route

  1. Ordered
  2. Queue
  3. Cutting
  4. Welding
  5. Fitting
  6. Spraying
  7. Assembly
  8. Pack Together
  9. Ready

Suspended mirror, black frame

line 1copies 1driver: glass

Ordered
floor complete
no
order status
ordered, not invoiced

Its history, one row a move

  1. nothing yet: waiting on the glass

The size is in the words

decision 3

Read a mirror's size from its description

Drawing · sample text

The order line, as the office typed it

Suspended mirror, 600mm wide x 1500mm high, silver, rounded corners, black metal frame, on two ceiling poles

The size fields

source
framing software
trusted
not for a mirror

The parser, first

width
600 mm
height
1500 mm

The batch pass AI

shape
rectangle, rounded
tint
silver
lighting
none
then
it can go to a review tab

Mirrors were quoted in framing software, so the size fields on a mirror’s order couldn’t be trusted. The real size was in the description, written as 1400mm wide x 1000mm high. My pipeline reads the size from those words, and a batch AI pass reads shape, tint and lighting from the same line. Anything the AI read is badged AI in the mirror list. From 3 June the board had a review tab where someone in the office could check a mirror’s specs and confirm them, and a read with low confidence was flagged there with its percentage.

A dusty floor, and no hover

decision 4

Big targets, and nothing that waits for a hover

Drawing · actual size on screen
24the June audit’s estimatea supplier select in the mirror portal, from its styling
44the minimum from 2 Juneon most of the portal’s controls
the manager’s Done buttonno hover on an iPad

At the start of June I ran a floor audit over the mirror portal’s code. It put its selects at about 24 pixels high, going by their styling, against a 44 pixel minimum, and its line on the Wi-Fi was blunt: when it drops, the screen goes blank and the worker goes back to paper. Finding a job meant typing its number into another view. On 2 June most of the portal’s controls, the selects the audit named among them, got a 44 pixel minimum and the mirror list got a stage column. The manager’s Done button was the same kind of mistake, and I’d fixed it on 31 May: it only appeared on hover, and an iPad has no hover.

24 → 44pxreported · sourceFloor UX audit, June 2026 (.understand-anything/proposals/phase-c-workshop-ux.md: selects at about 24 px, estimated from their CSS classes, against a 44 px minimum); commit of 2 June 2026, 'Touch: min-h-touch/min-w-touch across all mirror-portal components', min-h-touch = 2.75rem in tailwind.config.js.a supplier select in the mirror portal, from the June audit's estimate to the minimum I set

The floor kept its TV

The framers never trialled the board. The mirror team had an iPad and the framing team had the TV. On 30 June my own operations guide for the workshop listed the iPads as not in use. The framers worked from an Airtable view on that TV, where they saw new jobs and picked their own. A paid monthly service copied orders from the quoting software into that Airtable.

On 1 July the sync became a step of my pipeline: scrape the orders, consolidate them and push them to a new table in the workshop’s Airtable, at 6, 12 and 4 every day. The TV’s Airtable view read from that table, so my pipeline fed the floor’s TV. My operations guide marks the paid bridge as cancelled in July. The pipeline ran until 2 August, and the board’s service is switched off now.

Drawing · from my operations guide

To 30 June

  1. Quoting software
  2. struck out: A paid bridgea monthly feemarked cancelled in July
  3. Airtable
  4. The TV on the floorframers pick their own jobs

From 1 July

  1. Quoting software
  2. My pipelinescrape, consolidate, sync · 06:00, 12:00, 16:00
  3. A new Airtable table

Beside them

  1. My board, built for iPadsiPads “not in use”, 30 June · pipeline paused after 2 August

I built the board for iPads on a floor that already had its screen. Mine was one more place a job lived, beside the quoting software, the CRM, Airtable and the TV. That’s the lesson I took into Reframed, which is built to put the floor on the same record as the order.

sources

  1. 11 paid sync service marked cancelled in July, the month my pipeline began syncing orders to Airtable. My operations guide for the workshop (the TFP Operations Guide, v2.1, 'Business at a Glance' table, as read on 1 July 2026: the bridge's monthly cost struck through, 'Cancelled (July 2026)'), and the MirrorFactory OS commit of 1 July 2026 that made the Airtable sync a step of the pipeline.
  2. 23 s Firestore query behind the mirror portal's data, replaced by a lookup cached in memory. Timed by my AI assistant, and recorded in my MirrorFactory OS commit of 3 June 2026 (ffde98b): its message gives the 3 s, and its code comment says the mirror orders are now served from an in-memory cache instead of a 3 s Firestore query. The commit keeps no benchmark.

permission

The Framing Project: granted. The owners agreed when I told them about this study. I offered to show it to them before publishing, and they said they were fine with anything I publish. The photographs are The Framing Project's own. The drawings are made from the code with sample data; there are no screens of the app here, because it sits behind a login and its data holds client names.

related

  • ReframedA legacy-run workshop given one structure, and the site, studios and floor screens that make it visible. Launched 27 October 2026.
  • Mini CyberdeckA pocket terminal for answering Claude Code away from the desk: an ESP32-S3, a two-inch screen and eight keys under the thumb.
Next: Mini CyberdeckA pocket terminal for answering Claude Code away from the desk: an ESP32-S3, a two-inch screen and eight keys under the thumb.3 of 6