From 9f29699bd8ee723f671f3af327191ee55325fc87 Mon Sep 17 00:00:00 2001
From: Cleanup Bot
Date: Thu, 2 Jul 2026 18:38:38 +0300
Subject: [PATCH 1/2] release: prepare CV Builder MVP for community testing
- Rewrite home page copy to honestly describe the product as a CV evaluator
- Render issues[], ATS verdict, and detected archetype on /results
- Add visual dimension bars and CTA back to evaluator on /results
- Add /feedback page with anonymization guidance and sample feedback
- Stop advertising PDF support; remove .pdf from accept list; show inline
status messages instead of alerts in FileUpload
- Add docs/LOCAL_DEMO.md, docs/FEEDBACK_GUIDE.md, docs/COMMUNITY_ANNOUNCEMENT.md
- Update README with privacy note, MVP framing, and link to local demo guide
---
README.md | 37 ++-
.../src/app/components/EvaluateForm.tsx | 4 +-
apps/web-ui/src/app/components/FileUpload.tsx | 35 ++-
apps/web-ui/src/app/feedback/page.tsx | 147 ++++++++++
apps/web-ui/src/app/page.tsx | 80 ++++--
apps/web-ui/src/app/results/page.tsx | 199 +++++++++++--
docs/COMMUNITY_ANNOUNCEMENT.md | 157 ++++++++++
docs/FEEDBACK_GUIDE.md | 119 ++++++++
docs/LOCAL_DEMO.md | 272 ++++++++++++++++++
9 files changed, 989 insertions(+), 61 deletions(-)
create mode 100644 apps/web-ui/src/app/feedback/page.tsx
create mode 100644 docs/COMMUNITY_ANNOUNCEMENT.md
create mode 100644 docs/FEEDBACK_GUIDE.md
create mode 100644 docs/LOCAL_DEMO.md
diff --git a/README.md b/README.md
index 913c917..8236340 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# CV Builder
-**Paste a job description + your CV. Get a scored evaluation, specific rewrites, and a tailored PDF.**
+**Paste your CV and an optional job description. Get a 0–5 evaluation across six dimensions, with a prioritised list of issues and fixes.**
-An open source, privacy-first CV builder for tech professionals. Built by the [Tech Immigrants](https://youtube.com/c/TechImmigrants) community.
+This is the **first community MVP**: it scores an existing resume, it does not generate, tailor, or rewrite one. An open source, privacy-first CV evaluator for tech professionals. Built by the [Tech Immigrants](https://youtube.com/c/TechImmigrants) community.
[](https://github.com/TechImmigrants/cv-builder/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
@@ -75,9 +75,30 @@ Clone the repo, open [Claude Code](https://claude.com/claude-code) at the root,
See [apps/cli/README.md](apps/cli/README.md) for the full guide.
-### Web UI (coming soon)
+### Web UI (MVP — local only)
-A browser-based interface where you paste your CV and JD, get instant feedback, and export a tailored PDF. No sign-up required, no data leaves your browser.
+```bash
+pnpm dev # boots at http://localhost:3000
+```
+
+A browser-based interface where you paste your CV and JD, get instant
+feedback, and read the full dimension breakdown. No sign-up, no data leaves
+your browser. PDF upload is not supported yet — use `.txt` or `.md`.
+
+Full guide: [docs/LOCAL_DEMO.md](docs/LOCAL_DEMO.md).
+
+---
+
+## Privacy
+
+- The web UI runs entirely in your browser. Nothing is sent to a server.
+- The CLI reads files locally and prints to stdout. Nothing is uploaded.
+- There is no telemetry, no analytics, no cookies.
+- Results in the web UI are stored in your browser's `localStorage` only.
+
+**Do not paste your real CV into GitHub issues.** Issues are public. Before
+sharing an example, anonymize it first — see
+[docs/FEEDBACK_GUIDE.md](docs/FEEDBACK_GUIDE.md).
---
@@ -158,13 +179,17 @@ Currently built-in:
## Roadmap
-### v0.1 (Current Sprint)
+### v0.1 (Current Sprint — MVP)
- [x] Core evaluation engine with 6 dimensions
- [x] 8 role archetypes
- [x] Universal anti-pattern detection
- [x] CLI: basic evaluate command
+- [x] Web UI: paste and score screen (local demo)
+- [x] Eval harness with golden fixtures
+- [x] Localization-ready code structure (no strings in wrong place)
+- [x] Privacy model: no telemetry, no upload
- [ ] Unit tests for scoring logic
-- [ ] Web UI: paste and score screen
+- [ ] PDF text extraction
### v0.2
- [ ] LLM-enhanced mode (rewrite suggestions)
diff --git a/apps/web-ui/src/app/components/EvaluateForm.tsx b/apps/web-ui/src/app/components/EvaluateForm.tsx
index 92fd453..34c0da8 100644
--- a/apps/web-ui/src/app/components/EvaluateForm.tsx
+++ b/apps/web-ui/src/app/components/EvaluateForm.tsx
@@ -61,7 +61,7 @@ export function EvaluateForm() {
/>
- setCv(content)} />
+ setCv(content)} />
diff --git a/apps/web-ui/src/app/components/FileUpload.tsx b/apps/web-ui/src/app/components/FileUpload.tsx
index 298f2f3..defe6c6 100644
--- a/apps/web-ui/src/app/components/FileUpload.tsx
+++ b/apps/web-ui/src/app/components/FileUpload.tsx
@@ -1,37 +1,46 @@
"use client";
-import { type DragEvent, useRef, useState } from "react";
+import { type DragEvent, useRef } from "react";
interface FileUploadProps {
+ inputId: string;
onContentLoaded: (content: string) => void;
}
-export function FileUpload({ onContentLoaded }: FileUploadProps) {
- const [fileName, setFileName] = useState("");
+export function FileUpload({ inputId, onContentLoaded }: FileUploadProps) {
const inputRef = useRef(null);
+ function setStatus(message: string) {
+ const el = document.getElementById(`${inputId}-message`);
+ if (el) {
+ el.textContent = message;
+ }
+ }
+
async function processFile(file: File) {
+ setStatus("");
const extension = file.name.split(".").pop()?.toLowerCase() || "";
if (extension === "txt" || extension === "md") {
try {
const text = await file.text();
onContentLoaded(text);
- setFileName(file.name);
+ setStatus(`Loaded ${file.name}`);
} catch (error) {
console.error("Failed to read file:", error);
- alert("Failed to read file. Please try again.");
- setFileName("");
+ setStatus("Failed to read file. Please try again.");
}
return;
}
if (extension === "pdf") {
- alert("PDF text extraction is not yet supported. Please use .txt or .md files.");
+ setStatus(
+ "PDF text extraction isn't supported yet. Please paste the content or upload a .txt or .md file."
+ );
return;
}
- alert("Unsupported file type.");
+ setStatus("Unsupported file type. Please use .txt or .md.");
}
async function handleFiles(files: FileList | null) {
@@ -65,13 +74,19 @@ export function FileUpload({ onContentLoaded }: FileUploadProps) {
Supports .txt and .md
- {fileName &&
Loaded: {fileName}
}
+
handleFiles(e.target.files)}
/>
diff --git a/apps/web-ui/src/app/feedback/page.tsx b/apps/web-ui/src/app/feedback/page.tsx
new file mode 100644
index 0000000..05665f2
--- /dev/null
+++ b/apps/web-ui/src/app/feedback/page.tsx
@@ -0,0 +1,147 @@
+import Link from "next/link";
+import { Header } from "../components/layout/Header";
+
+export default function FeedbackPage() {
+ return (
+
+
+
+
+
+ Feedback Guide
+
+
+
+ Tell us what worked and what didn't.
+
+
+
+ This MVP is the first cut. Real-world feedback is the only thing that gets us to
+ a useful tool. Skim this page before you open an issue so your feedback lands in
+ the right place.
+
+ Use the{" "}
+
+ feedback
+ {" "}
+ channel in the Tech Immigrants community.
+
+
+ If something is broken, use a{" "}
+
+ bug report
+ {" "}
+ instead.
+
+
+
+
+
+
What to include
+
+
+
What you were testing (web UI, CLI, eval harness).
+
What you expected vs. what happened.
+
The role or archetype you were aiming for.
+
Your browser / OS / Node version (for web UI / CLI bugs).
+
+
+
+
+
+ ⚠ Privacy: don't paste your real CV
+
+
+
+ GitHub issues are public. Before sharing an example,{" "}
+ anonymize it:
+
+
+
+
Replace your name with "Candidate A".
+
Replace company names with "Company X".
+
Remove phone, address, and email.
+
Keep the bullet structure and metrics — that's what we need to see.
+
+
+
+ If you accidentally paste a real CV, edit or delete the comment immediately.
+ We can't un-share what you post on GitHub.
+
+
+
+
+
Sample anonymized feedback
+
+
+ {`Browser: Chrome 124 on macOS 14
+What I tried: A 1-page SWE CV against a Python backend JD
+Result: Score 2.1/5
+Surprise: Keyword Match scored 5/5 but Tooling Visibility scored 1/5,
+even though I named every tool in the CV. The two should agree.
+Anonymized snippet:
+ - "Built Candidate A service in Company X serving 50M requests/day"
+ - "Migrated Postgres to a sharded cluster, scaling writes 5x"
+Suggestion: weight Tooling Visibility by exact JD keywords, not just
+archetype keywords.`}
+
+ );
+}
diff --git a/apps/web-ui/src/app/page.tsx b/apps/web-ui/src/app/page.tsx
index ad86d88..d9f7b6d 100644
--- a/apps/web-ui/src/app/page.tsx
+++ b/apps/web-ui/src/app/page.tsx
@@ -1,3 +1,4 @@
+import Link from "next/link";
import { EvaluateForm } from "./components/EvaluateForm";
import { Header } from "./components/layout/Header";
@@ -8,40 +9,64 @@ export default function Home() {
-
CV Builder
+
+ CV Builder · MVP
+
+
- Tailor your resume to the role without starting from scratch.
+ Score your resume against the role before you apply.
-
- Paste a job description, highlight the experience that matters, and shape
- a cleaner CV for tech roles in a few quick steps.
+
+
+ Paste your CV and an optional job description. CV Builder runs six quality
+ checks — Shipped Evidence, Quantified Impact, Tooling Visibility, ATS
+ Compatibility, Keyword Match, Public Proof — and shows you what to fix
+ first. Everything runs in your browser; nothing is uploaded.
+
+
+
+ This is a community MVP. It scores an existing resume — it does not
+ generate, tailor, or rewrite one. Feedback and bug reports are welcome at
+ the bottom of{" "}
+
+ a results page
+ {" "}
+ after you evaluate.
-
1. Paste the job post
-
- Start with the exact role description so the app can focus the CV around
- the right skills, tools, and experience.
+
1. Paste a CV
+
+
+ Plain text or Markdown works best. Most real-world resumes can be pasted
+ directly from your editor.
-
2. Shape the story
-
- Refine summaries, impact statements, and project bullets to match the role
- while keeping your background clear and honest.
+
+ 2. Add a job description (optional)
+
+
+
+ If you add a JD, the Keyword Match dimension becomes active and you get a
+ focused score against that specific role.
-
3. Export a focused CV
-
- End with a cleaner resume that is easier to review and more aligned with
- the position you are targeting.
+
3. Read the result
+
+
+ Overall score, per-dimension bars, the issues worth fixing first, and a
+ list of strengths you can lean into in the interview.
@@ -49,10 +74,23 @@ export default function Home() {
-
- This starter page is intentionally simple. It gives the web package an
- app-specific home screen instead of the default Next.js template and leaves
- room for the real workflow to grow from here.
+
Privacy
+
+
+ The web app runs entirely in your browser via{" "}
+
+ @cv-builder/core
+
+ . Nothing is sent to a server. Local demo only — no telemetry, no analytics,
+ no cookies. Do not paste real personal information (phone, address) into
+ GitHub issues; see{" "}
+
+ feedback guide
+
+ .
diff --git a/apps/web-ui/src/app/results/page.tsx b/apps/web-ui/src/app/results/page.tsx
index f4d5663..69fe679 100644
--- a/apps/web-ui/src/app/results/page.tsx
+++ b/apps/web-ui/src/app/results/page.tsx
@@ -1,59 +1,214 @@
"use client";
import type { EvaluationResult } from "@cv-builder/core";
+import Link from "next/link";
import { useEffect, useState } from "react";
import { ScoreCard } from "../components/ScoreCard";
import { getEvaluationResult } from "../lib/evaluation-storage";
+const SEVERITY_STYLES = {
+ critical: "border-red-500 bg-red-50 text-red-900 dark:bg-red-950/40 dark:text-red-200",
+ major:
+ "border-amber-500 bg-amber-50 text-amber-900 dark:bg-amber-950/40 dark:text-amber-200",
+ minor: "border-zinc-400 bg-zinc-50 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-200",
+} as const;
+
export default function ResultsPage() {
const [result, setResult] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const data = getEvaluationResult();
-
setResult(data as EvaluationResult | null);
setLoading(false);
}, []);
if (loading) {
- return Loading...;
+ return (
+
+
Loading your evaluation…
+
+ );
}
if (!result) {
- return No evaluation found;
+ return (
+
+
No evaluation found
+
+
+ It looks like you landed on this page directly. Run a CV through the evaluator
+ first — your last result is stored in this browser only.
+
+ {result.atsCompatible
+ ? "No tables, no smart quotes — applicant tracking systems should parse this CV cleanly."
+ : "Your CV contains formatting (tables or smart quotes) that many ATS tools can't parse. Plain text or Markdown works best."}
+
+ This is the first community MVP. Feedback shapes what we build next. See the{" "}
+
+ feedback guide
+ {" "}
+ for what to share and how to share it without exposing your CV.
+
);
diff --git a/docs/COMMUNITY_ANNOUNCEMENT.md b/docs/COMMUNITY_ANNOUNCEMENT.md
new file mode 100644
index 0000000..f0c4b66
--- /dev/null
+++ b/docs/COMMUNITY_ANNOUNCEMENT.md
@@ -0,0 +1,157 @@
+# Community Announcement — CV Builder MVP
+
+> Ready-to-post announcements for the Tech Immigrants community.
+> Pick the version that fits the channel. Personalize the closing line.
+
+---
+
+## Short — Persian (Telegram)
+
+```text
+🚀 اولین MVP ابزار CV Builder آمادهی تست شد.
+
+چی هست؟
+ابزاری برای ارزیابی رزومه. رزومهات رو paste میکنی (بهعلاوهی آگهی شغلی اختیاری)،
+ابزار در ۶ بُعد امتیاز میده و مشکلات مهم رو با راهحل نشون میده.
+
+چی نیست؟
+سازندهی رزومه نیست. رزومه رو نمینویسه و بازنویسی نمیکنه. فقط ارزیابی میکنه.
+
+حریم خصوصی:
+همهچیز توی مرورگر خودت اجرا میشه. چیزی به سرور فرستاده نمیشه.
+
+نصب و اجرا:
+docs/LOCAL_DEMO.md رو بخون. کل فرایند کمتر از ۵ دقیقهست.
+
+نظر و فیدبک:
+اگه تست کردی، توی issues گیتهاب بگو چی دیدی.
+⚠️ رزومهی واقعیات رو paste نکن — اول بینامش کن (راهنما: docs/FEEDBACK_GUIDE.md).
+
+تشکر از همهی کسانی که توی این نسخه مشارکت کردن.
+```
+
+---
+
+## Longer — Persian (Telegram / LinkedIn)
+
+```text
+🎉 اولین نسخهی MVP پروژهی CV Builder آمادهی تست توسط جامعهی Tech Immigrants شد.
+
+📌 این ابزار چی هست؟
+CV Builder یک ارزیاب رایگان و open-source برای رزومهست.
+- رزومهات رو paste میکنی (متن ساده یا Markdown)، اختیاری هم آگهی شغلی رو اضافه میکنی.
+- ابزار شش بُعد رو بررسی میکنه:
+ Shipped Evidence، Quantified Impact، Tooling Visibility، ATS Compatibility،
+ Keyword Match، Public Proof.
+- یک امتیاز کلی از ۰ تا ۵ میده، بهعلاوهی لیستی از مشکلات با اولویت بالا و
+ راهحل پیشنهادی برای هرکدوم.
+- archetype (نقش) شما رو هم تشخیص میده (Backend Engineer، Frontend Engineer،
+ AI Product Manager و ...).
+
+🔐 حریم خصوصی
+همهچیز توی مرورگر خودت یا روی کامپیوترت اجرا میشه. هیچ دادهای به سرور
+فرستاده نمیشه. هیچ telemetry یا analytics نداریم. هیچ cookie نمیگذاریم.
+نتایج فقط توی localStorage مرورگر ذخیره میشن.
+
+⚠️ این ابزار چی نیست؟
+- سازندهی رزومه نیست — رزومهی موجودت رو ارزیابی میکنه، نمیسازه.
+- بازنویسی یا tailor نمیکنه.
+- PDF رو پشتیبانی نمیکنه (فقط .txt و .md).
+- هنوز hosted version نداره. فقط لوکال.
+
+🚀 چطور تست کنم؟
+کل فرایند کمتر از ۵ دقیقهست:
+1. مخزن رو clone کن: github.com/TechImmigrants/cv-builder
+2. pnpm install
+3. pnpm dev → http://localhost:3000
+راهنمای کامل: docs/LOCAL_DEMO.md
+
+همچنین میتونی CLI رو امتحان کنی:
+node packages/cli/dist/cli.js evaluate path/to/cv.md
+(بعد از pnpm --filter @cv-builder/cli build)
+
+📝 فیدبک
+از همهی تستکنندهها میخوایم که بعد از امتحان کردن، توی issues گیتهاب
+بنویسن. این پنج سوال بیشترین کمک رو میکنه:
+1. آیا فیدبکها مفید بودن؟
+2. آیا امتیاز قابلفهم بود؟
+3. چه چیزی از نتیجه کم بود؟
+4. آیا برای رزومهی خودت به این ابزار اعتماد میکنی؟
+5. چه نقش / archetype دیگهای باید اضافه کنیم؟
+
+⚠️ نکتهی مهم: لطفاً رزومهی واقعیات رو توی issues گیتهاب paste نکن.
+Issues عمومی هستن. اول بینامش کن:
+- اسم خودت → "Candidate A"
+- اسم شرکتها → "Company X"
+- شماره تماس، ایمیل، آدرس → حذف
+راهنمای کامل: docs/FEEDBACK_GUIDE.md
+
+🙏 تشکر
+این نسخه حاصل کار جمعی از اعضای جامعهی Tech Immigrants و contributorهای
+open-source هست. تشکر ویژه از همهی کسانی که PR، rule، archetype، skill، یا
+doc اضافه کردن. مشارکت شما دلیل اینکه این پروژه به اینجا رسیده.
+
+منتظر فیدبکهاتون هستیم. 🙌
+```
+
+---
+
+## Short — English (Telegram / Discord)
+
+```text
+🚀 First MVP of CV Builder is ready for community testing.
+
+What it is: an open-source CV evaluator. Paste a CV (and optionally a JD),
+get a 0–5 score across six dimensions, plus a prioritised list of issues
+with concrete fixes.
+
+What it isn't: it does not generate, tailor, or rewrite a CV. It scores
+what you have.
+
+Privacy: everything runs in your browser or on your machine. No server,
+no telemetry, no analytics, no cookies.
+
+How to test:
+1. Clone github.com/TechImmigrants/cv-builder
+2. pnpm install
+3. pnpm dev → http://localhost:3000
+Full guide: docs/LOCAL_DEMO.md
+
+Feedback welcome. Please anonymize your CV before sharing examples in
+GitHub issues (see docs/FEEDBACK_GUIDE.md).
+
+Thanks to every contributor who made this possible. 🙌
+```
+
+---
+
+## How to personalize
+
+Replace the closing line with one of:
+
+- "Sahar از Tech Immigrants" — for a maintainer-signed post
+- Your handle + "Tech Immigrants community manager" — for a community-manager post
+- Nothing — for an automated bot announcement
+
+If posting on LinkedIn, add a short paragraph about why this matters to
+immigrant tech professionals (English-language CVs are an uneven playing
+field; a privacy-first local tool lowers the bar to getting useful feedback
+on a CV).
+
+## Posting checklist
+
+Before posting:
+
+- [ ] Confirm `pnpm install && pnpm dev` works on a fresh clone
+- [ ] Confirm `pnpm test && pnpm lint && pnpm build` all pass
+- [ ] Confirm the home page copy is honest (matches docs/MVP_DEMO_PLAN.md)
+- [ ] Confirm the `/feedback` page exists and renders correctly
+- [ ] Confirm the privacy note is visible on the home page
+- [ ] Pick the announcement length that fits the channel
+- [ ] Replace the closing line with your sign-off
+
+After posting:
+
+- [ ] Pin the announcement in the channel
+- [ ] Link to docs/LOCAL_DEMO.md and docs/FEEDBACK_GUIDE.md
+- [ ] Watch the issues feed for the first 24 hours
\ No newline at end of file
diff --git a/docs/FEEDBACK_GUIDE.md b/docs/FEEDBACK_GUIDE.md
new file mode 100644
index 0000000..9f5e671
--- /dev/null
+++ b/docs/FEEDBACK_GUIDE.md
@@ -0,0 +1,119 @@
+# Feedback Guide
+
+> How to send useful feedback without exposing your CV.
+
+GitHub issues are public. Before you open one, read this page so your
+feedback lands in the right place and doesn't leak personal information.
+
+## Where to send feedback
+
+- **General feedback:** open a GitHub issue with the `feedback` label.
+- **Bug reports:** use the **Bug report** issue template (it asks the right
+ questions).
+- **Feature requests:** use the **Feature request** issue template.
+- **New archetype / role:** use the **New archetype** issue template.
+- **New rule:** use the **New rule** issue template.
+
+Links:
+
+- New issue: https://github.com/TechImmigrants/cv-builder/issues/new
+- Templates: https://github.com/TechImmigrants/cv-builder/issues/new/choose
+
+## What to include
+
+For feedback that helps us improve:
+
+- **What you were testing** (web UI, CLI, eval harness)
+- **What you expected vs. what happened**
+- **The role or archetype you were aiming for**
+- **Your browser / OS / Node version** for web UI / CLI bugs
+
+For bug reports, the template will ask for additional details (reproduction
+steps, expected vs. actual output).
+
+## What NOT to include
+
+This is the most important section. **Do not paste your full CV into a GitHub
+issue.**
+
+Why:
+
+- GitHub issues are public. They show up in search engines.
+- Your CV contains your name, your work history, contact details, sometimes
+ your address and phone number.
+- Once posted, the only way to remove it is to edit or delete the comment —
+ but anyone who saw it can save a copy.
+- Your current / past employers can find it.
+
+## How to anonymize a CV before sharing
+
+Before you paste anything, do this:
+
+1. **Replace your name** with `Candidate A`.
+2. **Replace company names** with `Company X`, `Company Y`, etc.
+3. **Remove phone numbers, addresses, and emails.**
+4. **Keep the bullet structure and the metrics.** That's what we need to
+ see to improve the evaluator. A line like
+ `"Migrated Postgres to a sharded cluster, scaling writes 5x"` is exactly
+ what we want — there's no PII in it.
+
+## Sample anonymized feedback
+
+```text
+Browser: Chrome 124 on macOS 14
+What I tried: A 1-page SWE CV against a Python backend JD
+Result: Score 2.1/5
+Surprise: Keyword Match scored 5/5 but Tooling Visibility scored 1/5,
+even though I named every tool in the CV. The two should agree.
+Anonymized snippet:
+ - "Built Candidate A service in Company X serving 50M requests/day"
+ - "Migrated Postgres to a sharded cluster, scaling writes 5x"
+Suggestion: weight Tooling Visibility by exact JD keywords, not just
+archetype keywords.
+```
+
+This is the shape we want — concrete observation, anonymized evidence,
+actionable suggestion. We can act on this without ever seeing who you are.
+
+## If you accidentally paste your real CV
+
+Edit or delete the comment immediately. Then:
+
+1. Open the comment, click the three-dot menu, choose **Edit** or **Delete**.
+2. If a screenshot of the issue has already been taken (e.g. shared in
+ Discord), let us know in the issue and we'll add a pinned note saying it
+ should be ignored.
+
+We can't un-share what gets posted on GitHub, so please anonymize **before**
+you post.
+
+## Five questions that help
+
+If you only have two minutes, answer these in any order:
+
+1. Was the feedback useful?
+2. Was the score understandable?
+3. What was missing from the result?
+4. Would you trust this for your own CV today?
+5. What role or archetype should we support next?
+
+The first four help us judge what to invest in next. The fifth helps us
+prioritize the archetype backlog.
+
+## Other ways to give feedback
+
+- **Tech Immigrants community:** the `#feedback` channel on Discord / Telegram.
+- **Code review:** if you're a developer, the easiest way to influence the
+ product is to open a PR. See [CONTRIBUTING.md](../CONTRIBUTING.md).
+- **Email:** if GitHub feels too public, ping a maintainer through the
+ community channels first to find a private path.
+
+## Privacy promise
+
+This MVP runs entirely in your browser for the web UI, and entirely on your
+machine for the CLI. We have no server-side log of your CV. We have no
+telemetry. We have no analytics.
+
+When a hosted version eventually lands, the privacy posture will be
+documented before launch. Don't trust a hosted version that doesn't tell
+you what it does with your data.
\ No newline at end of file
diff --git a/docs/LOCAL_DEMO.md b/docs/LOCAL_DEMO.md
new file mode 100644
index 0000000..95e6edf
--- /dev/null
+++ b/docs/LOCAL_DEMO.md
@@ -0,0 +1,272 @@
+# Local Demo Guide
+
+> For testers who want to run the MVP locally without deploying anything.
+
+## What this MVP does
+
+CV Builder is a privacy-first CV evaluator. You give it a resume (and
+optionally a job description). It scores the resume across six dimensions and
+surfaces the most impactful issues to fix:
+
+1. **Shipped Evidence** — does the CV show shipped work?
+2. **Quantified Impact** — are outcomes measured (numbers, percentages,
+ scale)?
+3. **Tooling Visibility** — are the right tools named for the role?
+4. **ATS Compatibility** — will applicant tracking systems parse it cleanly?
+5. **Keyword Match** — when a JD is provided, does the CV mention the role's
+ keywords?
+6. **Public Proof** — does the CV link to verifiable work (GitHub, blog,
+ portfolio)?
+
+You get:
+
+- An overall score from 0 to 5
+- A bar for each dimension
+- A list of issues with severity, the why, and a concrete fix
+- A list of strengths you can lean into in the interview
+- An ATS verdict (compatible or blockers detected)
+- The detected role archetype (e.g. Backend Engineer, AI Product Manager)
+
+## What this MVP does NOT do (yet)
+
+- **It does not generate a CV.** No rewriting, no tailoring, no autofill.
+- **It does not parse PDFs.** Paste plain text or Markdown. Upload `.txt` or
+ `.md` only.
+- **It does not have a hosted version.** You run it locally.
+- **It does not store anything on a server.** Results live in your browser's
+ `localStorage` only.
+- **It does not cover every role.** Eight archetypes are built-in (Backend
+ Engineer, AI Engineer, AI Product Manager, Frontend Engineer, QA, DevOps,
+ Data Engineer, plus a default). If your role isn't there, the detector
+ falls back to Backend Engineer.
+
+If any of the above is what you came looking for, see the [Roadmap](../ROADMAP.md)
+or open a [feedback issue](https://github.com/TechImmigrants/cv-builder/issues/new).
+
+## How to install
+
+### Prerequisites
+
+- **Node.js 22 or newer** (matches the GitHub Actions workflow)
+- **pnpm 9.15 or newer** (`npm install -g pnpm@9.15.0`)
+- **git**
+
+### One-time setup
+
+```bash
+git clone https://github.com/TechImmigrants/cv-builder
+cd cv-builder
+pnpm install
+```
+
+`pnpm install` resolves all 9 workspaces and downloads dev dependencies
+(Next.js, Vitest, Biome, TypeScript). Takes about 30–60 seconds.
+
+## How to run the web UI
+
+```bash
+pnpm dev
+```
+
+This boots Next.js at **http://localhost:3000**. Open it in your browser.
+
+The home page shows two textareas:
+
+- **CV** — paste your resume. Plain text or Markdown.
+- **Job Description** — optional. Paste the JD here to activate the Keyword
+ Match dimension.
+
+Click **Evaluate**. The evaluator runs in your browser (no server roundtrip).
+The results page shows your score, dimension breakdown, issues, and
+strengths. Click **Evaluate another CV** to start over.
+
+### File upload
+
+Below each textarea is a drag-and-drop area. You can drop a `.txt` or `.md`
+file and it will load the content into the textarea. PDF upload is not yet
+supported — the UI now shows a friendly inline message instead of throwing
+an alert.
+
+## How to run the CLI evaluation
+
+The CLI is a thin wrapper around the same evaluator. Useful for batch testing
+or for piping output into other tools.
+
+```bash
+# Build the CLI once
+pnpm --filter @cv-builder/cli build
+
+# Score a CV
+node packages/cli/dist/cli.js evaluate /path/to/your-cv.md
+
+# Score against a JD
+node packages/cli/dist/cli.js evaluate /path/to/your-cv.md --jd /path/to/jd.md
+
+# Machine-readable JSON output
+node packages/cli/dist/cli.js evaluate /path/to/your-cv.md --format json
+
+# List the archetypes the evaluator can detect
+node packages/cli/dist/cli.js archetypes
+
+# Help
+node packages/cli/dist/cli.js help
+```
+
+The CLI uses the same color-coded scoring the web UI uses. Use `--format json`
+to pipe into `jq` or other tools.
+
+## Sample CV to test with
+
+The repo ships five golden fixtures under `packages/eval/fixtures/`. To demo
+with a known-good CV:
+
+```bash
+cat packages/eval/fixtures/swe-strong/resume.md
+```
+
+To demo with a weak CV (tables, no metrics):
+
+```bash
+cat packages/eval/fixtures/swe-weak-table/resume.md
+```
+
+Or copy one into the web UI's CV textarea and click Evaluate.
+
+## Expected result shape
+
+The evaluator returns this shape:
+
+```ts
+type EvaluationResult = {
+ score: number; // 0–5, weighted average across dimensions
+ dimensions: Array<{ // 6 dimensions, score/maxScore/weight/feedback
+ name: string;
+ score: number; // 0–5
+ maxScore: number; // always 5 today
+ weight: number; // archetype-specific, sums to ~1.0
+ feedback: string; // optional human-readable note
+ }>;
+ strengths: string[]; // bullet points to lean into in the interview
+ issues: Array<{ // things to fix, severity-ordered
+ element: string; // the rule name (e.g. "Excessive CV length")
+ why: string; // 1-sentence reasoning
+ fix: string; // concrete suggested fix
+ severity: "critical" | "major" | "minor";
+ }>;
+ archetype: { // detected role, used to weight dimensions
+ id: string;
+ name: string;
+ };
+ atsCompatible: boolean; // false if tables or smart quotes detected
+ rewrites: []; // reserved for future use
+};
+```
+
+A typical score breakdown looks like:
+
+```text
+CV Score: 2.3/5
+Archetype: Backend Engineer
+
+Dimensions:
+ Shipped Evidence █░░░░ 1/5
+ Quantified Impact ██░░░ 2/5
+ Tooling Visibility ██░░░ 2/5
+ ATS Compatibility █████ 5/5
+ Keyword Match ███░░ 3/5
+ Public Proof ██░░░ 2/5
+
+Strengths:
+ ✓ Uses quantified metrics
+ ✓ Links to public code
+
+ATS Compatible: ✓ Yes
+```
+
+A weak CV scores lower on the same dimensions, and typically shows ATS
+incompatibility plus 1–3 issues (e.g. "Excessive CV length", "Inconsistent
+date formats", "Outdated tool without modern stack").
+
+## Running the test suite
+
+```bash
+pnpm test # all packages
+pnpm --filter @cv-builder/core test # 21 evaluator + rules tests
+pnpm --filter @cv-builder/eval test # 21 fixture-driven tests
+pnpm lint # biome check, expect 0 errors
+pnpm build # all 6 packages build
+```
+
+If all five commands pass, the MVP is healthy.
+
+## Troubleshooting
+
+### `pnpm install` complains about lockfile
+
+The lockfile is `pnpm-lock.yaml`. If you've switched pnpm versions you may
+need:
+
+```bash
+pnpm install --no-frozen-lockfile
+```
+
+Don't commit the regenerated lockfile unless the change is intentional.
+
+### `pnpm dev` fails with "Cannot find module"
+
+Run `pnpm install` again. If that doesn't help, `rm -rf node_modules && pnpm install`.
+
+### Web UI loads but Evaluate returns 0 dimensions or NaN
+
+Check the browser console. Common causes:
+
+- The CV is empty (the form rejects empty CVs with an alert)
+- The CV has unsupported characters that crash the parser (very rare;
+ `TextStats` shows char/word counts so you can confirm the textarea is
+ populated)
+
+### CLI says "Cannot find module @cv-builder/core"
+
+The CLI uses workspace dependencies. Make sure you ran `pnpm install` from
+the repo root, not from inside `packages/cli/`.
+
+### Lint reports errors
+
+Run `pnpm exec biome check --write` to auto-fix formatting. If real lint
+errors remain (not formatting), read the message — they're usually about
+unused imports or non-null assertions.
+
+### Build fails on the web UI
+
+Check that `apps/web-ui/out/` is writable. If you have an old `out/` from a
+previous build, delete it.
+
+## Privacy reminders
+
+- **The web UI does not upload anything.** Everything runs in your browser.
+- **The CLI does not upload anything.** It reads files locally and prints to
+ stdout.
+- **Do not paste your real CV into GitHub issues.** GitHub issues are public.
+ Anonymize first — see [FEEDBACK_GUIDE.md](FEEDBACK_GUIDE.md).
+- **Results in the web UI live in `localStorage`.** Clearing browser data
+ wipes them. There is no cloud sync.
+
+## What to test
+
+If you want to help, here are a few high-value things to try:
+
+1. **Run the same CV twice with different JDs.** Does the Keyword Match
+ score change? Does the archetype stay the same?
+2. **Try a CV in your native language.** The keyword detector is
+ English-first; non-English CVs may give odd scores.
+3. **Drag a `.txt` of your own CV.** Does the file upload work? Does the
+ score feel right?
+4. **Drag a PDF.** You should see a friendly inline message instead of an
+ alert.
+5. **Refresh the page after evaluating.** Does the result survive a reload?
+ (It should — it's in `localStorage`.)
+6. **Open `/results` directly in the URL bar.** Do you see the empty state
+ with a back link?
+
+Report what you find at
+https://github.com/TechImmigrants/cv-builder/issues/new
\ No newline at end of file
From 2bc34a4390be9a395d6384acbefc20491e322c59 Mon Sep 17 00:00:00 2001
From: Cleanup Bot
Date: Thu, 2 Jul 2026 19:27:09 +0300
Subject: [PATCH 2/2] fix(mvp): address CodeRabbit review on PR #87
- results/page.tsx: default dimensions/issues/strengths to [] on stale
localStorage records; guard divide-by-zero when dimension.maxScore is 0;
render shared Header so the page matches the home and feedback shell.
- FileUpload.tsx: replace imperative document.getElementById status updates
with a React useState field rendered inside the aria-live status element.
- docs/LOCAL_DEMO.md: lower Node prerequisite to 20+ (matches package.json),
use the repo's pnpm format script for auto-fix, document rewrites as
Rewrite[] in the current EvaluationResult shape.
- docs/FEEDBACK_GUIDE.md: align feedback channel wording with the rest of
the repo (Tech Immigrants community, not Discord/Telegram).
Validates: pnpm test (12/12 tasks), pnpm lint (0 errors), pnpm build (6/6).
---
apps/web-ui/src/app/components/FileUpload.tsx | 14 +-
apps/web-ui/src/app/results/page.tsx | 327 +++++++++---------
docs/FEEDBACK_GUIDE.md | 2 +-
docs/LOCAL_DEMO.md | 6 +-
4 files changed, 182 insertions(+), 167 deletions(-)
diff --git a/apps/web-ui/src/app/components/FileUpload.tsx b/apps/web-ui/src/app/components/FileUpload.tsx
index defe6c6..dc0b080 100644
--- a/apps/web-ui/src/app/components/FileUpload.tsx
+++ b/apps/web-ui/src/app/components/FileUpload.tsx
@@ -1,6 +1,6 @@
"use client";
-import { type DragEvent, useRef } from "react";
+import { type DragEvent, useRef, useState } from "react";
interface FileUploadProps {
inputId: string;
@@ -9,13 +9,7 @@ interface FileUploadProps {
export function FileUpload({ inputId, onContentLoaded }: FileUploadProps) {
const inputRef = useRef(null);
-
- function setStatus(message: string) {
- const el = document.getElementById(`${inputId}-message`);
- if (el) {
- el.textContent = message;
- }
- }
+ const [status, setStatus] = useState("");
async function processFile(file: File) {
setStatus("");
@@ -79,7 +73,9 @@ export function FileUpload({ inputId, onContentLoaded }: FileUploadProps) {
role="status"
aria-live="polite"
className="mt-2 min-h-[1rem] text-xs text-amber-600"
- />
+ >
+ {status}
+
-
Loading your evaluation…
-
+
+
+
+
+
Loading your evaluation…
+
+
);
}
if (!result) {
return (
-
-
No evaluation found
-
-
- It looks like you landed on this page directly. Run a CV through the evaluator
- first — your last result is stored in this browser only.
-
-
-
- ← Back to evaluator
-
-
+
+
+
+
+
No evaluation found
+
+
+ It looks like you landed on this page directly. Run a CV through the evaluator
+ first — your last result is stored in this browser only.
+
- {result.atsCompatible
- ? "No tables, no smart quotes — applicant tracking systems should parse this CV cleanly."
- : "Your CV contains formatting (tables or smart quotes) that many ATS tools can't parse. Plain text or Markdown works best."}
-
+ {result.atsCompatible
+ ? "No tables, no smart quotes — applicant tracking systems should parse this CV cleanly."
+ : "Your CV contains formatting (tables or smart quotes) that many ATS tools can't parse. Plain text or Markdown works best."}
+
+ No specific strengths detected yet. As your CV adds quantified outcomes,
+ public links, and shipped work, more bullets will appear here.
+
+ ) : (
+
+ {strengths.map((strength: string) => (
+
+ ✓ {strength}
+
+ ))}
+
+ )}
+
-
- This is the first community MVP. Feedback shapes what we build next. See the{" "}
-
- feedback guide
- {" "}
- for what to share and how to share it without exposing your CV.
-
-
-
+
+
+ Was this useful?
+
+
+
+ This is the first community MVP. Feedback shapes what we build next. See the{" "}
+
+ feedback guide
+ {" "}
+ for what to share and how to share it without exposing your CV.
+
+
+
+
);
}
diff --git a/docs/FEEDBACK_GUIDE.md b/docs/FEEDBACK_GUIDE.md
index 9f5e671..7657e6b 100644
--- a/docs/FEEDBACK_GUIDE.md
+++ b/docs/FEEDBACK_GUIDE.md
@@ -102,7 +102,7 @@ prioritize the archetype backlog.
## Other ways to give feedback
-- **Tech Immigrants community:** the `#feedback` channel on Discord / Telegram.
+- **Tech Immigrants community:** the `#feedback` channel in the Tech Immigrants community.
- **Code review:** if you're a developer, the easiest way to influence the
product is to open a PR. See [CONTRIBUTING.md](../CONTRIBUTING.md).
- **Email:** if GitHub feels too public, ping a maintainer through the
diff --git a/docs/LOCAL_DEMO.md b/docs/LOCAL_DEMO.md
index 95e6edf..dd2e9a4 100644
--- a/docs/LOCAL_DEMO.md
+++ b/docs/LOCAL_DEMO.md
@@ -47,7 +47,7 @@ or open a [feedback issue](https://github.com/TechImmigrants/cv-builder/issues/n
### Prerequisites
-- **Node.js 22 or newer** (matches the GitHub Actions workflow)
+- **Node.js 20 or newer** (matches `package.json`)
- **pnpm 9.15 or newer** (`npm install -g pnpm@9.15.0`)
- **git**
@@ -158,7 +158,7 @@ type EvaluationResult = {
name: string;
};
atsCompatible: boolean; // false if tables or smart quotes detected
- rewrites: []; // reserved for future use
+ rewrites: Rewrite[]; // current evaluator output
};
```
@@ -232,7 +232,7 @@ the repo root, not from inside `packages/cli/`.
### Lint reports errors
-Run `pnpm exec biome check --write` to auto-fix formatting. If real lint
+Run `pnpm format` to auto-fix formatting. If real lint
errors remain (not formatting), read the message — they're usually about
unused imports or non-null assertions.