How I stopped my AI resume writer from inventing achievements A developer built a free AI resume builder that constrains a language model to reword user-supplied bullets without adding facts, enforcing the rule with a strict prompt, JSON output mode, low temperature, and a validation step that rejects any response where the number of rewritten bullets does not match the input. Testing showed vague inputs like "worked on the website" produced clearer but still truthful rewrites with no invented numbers or tools, and an automated check flags outputs containing digits, percentages, or technologies absent from the input. The developer also found that uppercase headings with wide letter-spacing caused PDF text extraction to split words like "SKILLS" into fragments, fixed by removing the letter-spacing. Ask most AI resume tools to "improve" a bullet like "worked on the website" and you'll get something like "Spearheaded a website redesign that boosted conversions by 35%." It sounds impressive. It's also made up. And the first time an interviewer asks "how did you measure that 35%?", it falls apart. I recently built a free resume builder, and the one rule I cared about most was: the AI may reword what you wrote, but it may never add facts. Here's how I tried to enforce that, what I tested, and a strange PDF bug I found along the way. Language models are very good at producing text that sounds like a strong resume. Strong resumes have numbers, tools and outcomes, so when a model sees a vague bullet, the most "resume-like" completion includes a number, a tool and an outcome, whether or not they're true. So "be helpful" and "be honest" pull in different directions. You have to make the honest option the only acceptable one. Vague instructions like "don't make things up" aren't enough. I listed the specific kinds of invention I'd seen: js const HONESTY RULES = Rules you must follow: - Only use facts the user provided. Never invent employers, job titles, dates, numbers, percentages, tools, technologies, awards or achievements. - If a bullet has no measurable result, do not add one. Make it clear and specific with the facts given instead. - Do not use placeholders like X% or "N users". - Write in plain, professional English. No buzzwords like "synergy", "rockstar" or "results-driven". Two details mattered more than I expected: X % for you to fill in, which nudges users to invent a number themselves. Prompts are guidance, not guarantees. So the cof the response before showing it to the user: js const out = await complete systemPrompt, JSON.stringify { role, company, current, bullets } const improved = Array.isArray out.bullets ? .map b: unknown = clean b, 600 .filter Boolean // One output per input, or it's not a safe dro if improved.length == bullets.length throw new Error "Unexpected AI response" Requiring exactly one rewritten bullet per ins two useful things: If the check fails, the user sees an error instead of a questionable rewrite. I'd rather fail loudly than show something plausible but wrong. I also use JSON output mode response format: so parsing is reliable, and a low temperature 0.4 to keep rewrites conservative. The best test cases are vague bullets that almost beg for a made-up number: | Input | Output | |---|---| | worked on the website | Developed features for the website. | | helped with customer support tickets | Assistickets. | | fixed bugs in the checkout page using React | Resolved bugs in the checkout page using React. | | made the product page load faster | Optimizeder loading. | No numbers, no new tools, no invented outcomes.and that's the point: they're clearer, but still true. I also ran an automated check that flags any output containing digits, % or technology names that weren't in the input. It's crude, but it catches the most common kind The UI still tells users to read the result bt is perfect, and the person whose name is on the resume should have the final say. The PDF export is simply the browser's print-to-PDF of a single-column HTML preview. That gives real, selectable text, which matters because many employers run resumeng systems ATS that read the text. To check that, I extracted the text from a gene text EDUCATION SKI L LS CERTI F ICATIONS The cause was my section headings: uppercase wi . With wide tracking, PDF text extraction sometimes decides the gaps between letters are word breaks. A human sees "SKILLS"; a parser may see three words. The fix was one line: remove the letter-spacing from the headings and add a thin divider so they still stand out . After that, extraction gave clean "SKILLS" and "CERTI Lesson: if machines will read your output, te, not just what it looks like. Every AI action costs money, so there's a limit per day. I store the counter in the user's server-only metadata Supabase app metadata , which users can't edit , and only successful actions count against the limit: js const usage = aiUsageToday user if usage.remaining <= 0 { return NextResponse.json { error: "You've used all 20 AI actions for today." }, { status: 429 } } // ...call the model... const ai = await recordAiUse user // only afte Editing, saving and PDF export aren't limited at all. Only the part that costs money is. If you want to try it, the resume builder is free at nokku.payanai.com/resume-builder https://nokker . I'd genuinely like to hear if you can getit to invent something. That's the kind of bug report I want.