Work / BadaBhai
The one-page contract: résumés that fail closed
A résumé generator for workers who have never had one. The sheet is printed and handed to a supervisor, so "it rendered" is not good enough — it has to fit one page, use the right fonts and never invent a fact.
Measured outcomes
- 56 / 56
- fixture sheets on exactly one A4 page
- 0 → 11.4 mm
- worst-case spare space at the foot of the page
- 6 → 0
- sheets spilling onto a second page
- 32
- tests that fail if a fact is invented
The problem
A CNC operator with eight years on the shop floor may never have had a résumé. BadaBhai builds one from a chat in Hinglish. The result is printed, photocopied and handed to a supervisor, so a sheet that spills onto a second page is a product failure, not a cosmetic one.
The content rules came from RVM CAD’s trade experts: 14 distinct résumé shapes (fresher, experienced operator, programmer and so on), each produced in two copies — one for the worker and one for employers. Some items can never be dropped, however crowded the page: name, availability, expected salary and the top ITI or NCVT qualification among them.
What I built
Render pipeline
- Worker answersFrom the Hinglish profiling chat
- Deterministic mapperReviewed labels; nine-row capability budget
- Degradation ladderFixed drop order; never-droppable items asserted
- RenderWeasyPrint, Noto Sans + Devanagari
Gates before a PDF leaves
- One pageMeasured on the rendered PDF
- Fonts embeddedAllowlist of required faces
- No invented factsFabrication tests on all 14 shapes
- PDF to the worker
- A deterministic mapper from the worker’s answers to reviewed English labels, so the same answers always produce the same sheet.
- A fixed capability budget of nine rows, with a drop order taken from how decisive each item is to an employer.
- A degradation ladder that removes lower-priority content in a fixed order when space runs out, with the never-droppable items asserted positively in tests rather than assumed.
- Name auto-fit: a long name drops to an 18-point floor and then wraps. A test pins 18 points as the minimum heading size anywhere on the sheet.
- A fabrication gate: 32 tests across all 14 shapes and both audiences, which fail if an adjective is injected, a tenure is rounded up or a raw internal token reaches the page.
- A font contract: the PDF is refused unless the bytes actually embed the required typefaces.
WeasyPrint exits 0 with the wrong font
This is the defect that justified the font contract. I rendered the same résumé in three containers:
| Container | Fonts embedded in the PDF | What the worker gets |
|---|---|---|
| The shipped image | Noto Sans, Noto Sans Bold, Noto Sans Devanagari | The correct sheet |
| Without the main Noto package | DejaVu Sans, DejaVu Sans Bold | A name rendered as empty boxes |
| Without DejaVu Sans as well | DejaVu Serif, DejaVu Serif Bold | A serif sheet; five shapes spill onto two pages |
In all three, the exit code, file size and page count looked identical to a correct render. Nothing in the normal pipeline would have noticed.
The guard now inspects which faces the produced bytes embed and refuses the render otherwise. It uses an allowlist of Noto faces rather than a denylist of known-bad ones. I deliberately did not require the bold face: a synthesised bold is a small visual regression, while every extra required face is another way to block a real worker’s résumé.
Measuring the artifact, not the proxy
| Measure | Before | After |
|---|---|---|
| Fixture sheets on one page (round 1) | 22 / 28 | 28 / 28 |
| Fixture sheets on one page (round 2, both audiences) | 28 / 28 | 56 / 56 |
| Worst-case spare space at the foot of the page | 0.00 mm | 11.42 mm |
| Worst case with work history and credentials injected | 3 sheets would overflow | 17.44 mm spare |
| Variance across three WeasyPrint versions | — | 0.00 mm |
Every number comes from rendering real PDFs and measuring them. The tempting proxies — counting page markers in the PDF bytes, or estimating line width from character counts — look right and are not: page markers in a compressed PDF count as zero.
Defects the fixtures found
None of these were on the original task list:
- An empty “Work history” heading for freshers. The CSS rule meant to hide the empty section could never match, so an ITI fresher got a heading with nothing under it — exactly what the design rules forbid.
- Three shapes spilled onto two pages because of the footer. Every fixture had been rendered without the QR code that production prints in the footer. The content budgets held; the footer did not fit.
- The name auto-fit had never been implemented. One shape’s worker copy was two pages at 20 points and one page at 18.
- An availability value printed raw (“Available from immediate”).
- A lookup called with its arguments reversed, which only a run against a real Postgres database exposed.
What is not verified
Glyph-level fidelity has not been checked across all 56 sheets. The Devanagari name line is not exercised by the fixtures. File-size and filename rules sit outside the render test. These are stated limits, not hidden ones.
What I learned
A budget checked only against the design’s easy examples is not checked. Render the worst case, measure the artifact that ships, and treat a silent fallback as a failure.
Where the numbers come from
- 56 / 56 fixture sheets on exactly one A4 page: Résumé engine journal, round 2 (28 Aug 2026).
- 0 → 11.4 mm worst-case spare space at the foot of the page: Same; measured on rendered PDFs.
- 6 → 0 sheets spilling onto a second page: Résumé engine journal, round 1.
- 32 tests that fail if a fact is invented: Fabrication gate, round 1.