Skip to content

Fix shipped evidence scoring#94

Open
SaharPak wants to merge 1 commit into
mainfrom
fix/shipped-evidence-scoring
Open

Fix shipped evidence scoring#94
SaharPak wants to merge 1 commit into
mainfrom
fix/shipped-evidence-scoring

Conversation

@SaharPak

@SaharPak SaharPak commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Problem

scoreShippedEvidence in packages/core/src/evaluator/index.ts was
capped at a 7-pattern keyword list:

const shippedIndicators = [
  /shipped/i, /launched/i, /deployed/i,
  /built and released/i, /in production/i, /live users/i, /released to/i,
];

Three of those (built and released, in production, live users,
released to) are exact multi-word phrases that almost no real CV
contains. Strong CVs that describe real shipped work with verbs like
owned, led, drove, built, migrated, designed
but never happen to use the literal word "shipped" or "launched" — got
floored to 1/5. A strong backend CV using only "Migrated from
Postgres 13 → 16", "Built the routing engine", "Designed the failover"
scored 1/5 because none of the four single-token regexes fired.

Root cause

  • Only 7 patterns, 4 of them near-impossible-to-match literal phrases.
  • The score was Math.min(5, max(1, distinctMatches + 1)), so missing
    all 4 single-token verbs floored at 1 even if the rest of the CV was
    filled with shipped work.
  • The matcher never recognized leadership verbs, improvement verbs,
    concrete shipped objects (API, pipeline, microservice, funnel,
    guardrail, …), quantified adoption signals ("180k MAU", "used by
    600k patients/month"), outcome metrics (latency, retention, NPS,
    error rate, p99, …), or production / open-source signals.

What changed

Replace the keyword matcher in
packages/core/src/evaluator/index.ts with a multi-signal additive
heuristic
, clamped to [1, 5]. Signals are grouped into positive
categories and one negative category.

Positive signals (each category adds a small amount at one or two
thresholds so weak CVs aren't accidentally inflated):

  • Strong shipping verbs — shipped, launched, released, delivered,
    deployed, rolled out.
  • Medium engineering verbs — built, implemented, migrated,
    integrated, automated, redesigned, created, developed, designed,
    authored, coded, programmed, engineered. Each pattern requires the
    past/present-participle form so job titles like "software engineer"
    don't match.
  • Leadership verbs — owned, led, drove, spearheaded, orchestrated,
    championed, headed.
  • Improvement verbs — improved, reduced, increased, optimized, cut,
    grew, scaled, doubled, tripled, boosted, saved, accelerated,
    streamlined, hardened, lowered, shortened, raised, expanded, widened,
    lifted.
  • Evidence objects — feature, product, dashboard, API, workflow,
    integration, system, service, platform, module, experiment, release,
    rollout, MVP, pipeline, library, app, repo, microservice, funnel,
    guardrail, assistant, migration, upgrade, redesign, prototype, squad,
    … Generic terms (tools, frontend, backend, jobs) are intentionally
    excluded.
  • Quantified impact — percentages, dollar amounts, "N users /
    customers / teams / MAU / DAU / req/s", "N k/m users" scaled
    numbers.
  • Outcome metrics — conversion, activation, retention, latency,
    uptime, adoption, churn, engagement, funnel, throughput, MAU, DAU,
    WAU, NPS, hallucination, p99/p95/p90, SLO, SLA, error rate, CSAT.
    Generic words like "performance", "cost", "usage" are excluded
    so "Improved performance" doesn't count as evidence.
  • Production / live signals — in production, live users,
    publicly available, open source, deployed to, shipped to, launched
    to, used by, adopted by, customer use, public release.

Negative signals:

  • Vague patterns — worked on, helped (with|the), participated in,
    was involved in, assisted (with|in), tasked with, in charge of,
    worked with, collaborated with, "some (code|things|work|…)", did
    some, did stuff, responsible for. 3+ vague patterns subtract
    1; 5+ subtract 0.5 more.

A small countMatches helper is added at the bottom of the file.

Test cases added

packages/core/src/__tests__/shipped-evidence.test.ts plus three
synthetic fixtures under
packages/core/src/__fixtures__/synthetic/:

  • strong-ai-pm.md — Senior AI PM, "Shipped an LLM assistant to
    180k MAU", "Owned the prompt evaluation pipeline", "Built
    guardrails", "shipped 14 experiments", etc.
  • strong-backend.md — Staff Backend Engineer, "Owned the ledger
    service rewrite in Go; p99 latency 1.4s → 220ms", "Led the
    migration from Postgres 13 → 16", "Built Kafka-backed event
    sourcing layer", etc.
  • weak-generic.md — vague claims ("Hardworking and passionate
    software engineer", "Worked on various projects", "Responsible
    for backend things", "Wrote some code").

Assertions (8 cases):

  1. Strong AI PM scores 4-5/5.
  2. Strong backend scores 4-5/5.
  3. Weak generic scores 1-2/5.
  4. Existing sample-cv-strong.md stays at 4-5/5 (was 3/5; allowed
    to rise, not fall).
  5. Existing sample-cv-weak.md stays at 1-2/5.
  6. Strong AI PM > weak generic.
  7. Strong backend > weak generic.
  8. Other dimensions for the strong AI PM unchanged
    (Quantified Impact 5, ATS 5, Public Proof 3) — no regression outside
    Shipped Evidence.

Before / after scores on synthetic CVs

                                  before   after
strong-ai-pm  Shipped Evidence:    2/5  →  5/5   ✓
strong-backend Shipped Evidence:   1/5  →  5/5   ✓
weak-generic  Shipped Evidence:    1/5  →  2/5   ✓

core/sample-cv-strong  Shipped Evidence:  3/5  →  5/5   ✓
core/sample-cv-weak    Shipped Evidence:  1/5  →  2/5   ✓

Other dimensions (Quantified Impact, Tooling Visibility, ATS
Compatibility, Keyword Match, Public Proof) are byte-identical
before/after on every fixture above. Only scoreShippedEvidence
was modified; the other scorers are untouched.

Validation

  • pnpm lint — passes (one pre-existing biome.json deprecation
    info, unrelated).
  • pnpm test --force — 57 tests pass across 8 packages (29 in
    @cv-builder/core including the 8 new ones, 21 in
    @cv-builder/eval).
  • pnpm build --force — all 6 build targets succeed fresh.
  • pnpm eval — golden fixtures still pass.

Privacy confirmation

  • The personal CV at tmp/sahar-cv.md was not read, modified,
    committed, or used as a fixture.
  • Only synthetic CVs (synth-strong-ai-pm.md,
    synth-strong-backend.md, synth-weak-generic.md) were used.
    These live under packages/core/src/__fixtures__/synthetic/ and
    are public-safe, fictional profiles.
  • The synthetic fixtures are derived from no real user data — names,
    companies, and metrics are invented.

PR #37 (feat/cli-color-output) is untouched.

Summary by CodeRabbit

  • New Features

    • Improved resume/CV scoring for “shipped evidence” with a more nuanced assessment of delivery, impact, and production experience.
  • Bug Fixes

    • Refined scoring consistency so strong examples rank higher than weak ones more reliably, with better handling of quantified outcomes and real-world results.

The previous heuristic scored on a narrow 7-pattern keyword list
(shipped | launched | deployed | built and released | in production |
live users | released to). A strong AI PM CV describing real shipped
work with verbs like 'owned', 'led', 'drove', 'built' would floor at
1/5. A strong backend CV using 'migrated', 'designed', 'led', 'owned'
also scored 1/5 because none of the literal phrases appeared.

Replace the keyword matcher with a multi-signal additive scorer:
strong shipping verbs (shipped/launched/released/...), medium
engineering verbs (built/implemented/migrated/...), leadership
verbs (owned/led/drove/...), improvement verbs (improved/reduced/
cut/scaled/...), concrete evidence objects (feature/api/pipeline/
service/...), quantified impact (percentages, dollar amounts,
'180k MAU' style scaled users), outcome metrics (conversion/latency/
NPS/MAU/DAU/...), and production signals (in production/used by/
open source). Subtract for vague patterns ('worked on', 'helped with',
'responsible for', ...). Final score clamps to [1, 5].

Generic terms (tools, frontend, backend, jobs) and bare noun forms
(software 'engineer') are excluded so weak CVs stay at the floor.

Adds three synthetic fixtures under
packages/core/src/__fixtures__/synthetic/ and a focused
shipped-evidence.test.ts asserting the strong AI PM and strong
backend score 4-5/5, the weak generic CV scores 1-2/5, and the
existing core/sample-cv-strong and core/sample-cv-weak fixtures are
not regressed.
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds three new synthetic markdown CV fixtures (strong-ai-pm, strong-backend, weak-generic), rewrites the scoreShippedEvidence heuristic in the evaluator with categorized regex matching and threshold-based scoring, and adds a Vitest test suite validating shipped-evidence scoring across fixtures.

Changes

Shipped Evidence Scoring

Layer / File(s) Summary
Synthetic CV fixtures
packages/core/src/__fixtures__/synthetic/strong-ai-pm.md, packages/core/src/__fixtures__/synthetic/strong-backend.md, packages/core/src/__fixtures__/synthetic/weak-generic.md
New markdown fixtures for a strong AI PM CV, a strong backend engineer CV, and a weak generic profile, each with headers, experience, skills, and links sections.
scoreShippedEvidence heuristic rewrite
packages/core/src/evaluator/index.ts
Replaces keyword-count scoring with categorized regex groups (strong/medium/leadership/improvement/vague verbs), added counts for evidence objects, quantified impact, outcome metrics, and production signals, threshold-based score adjustments, a countMatches helper, and clamped/rounded final score.
Shipped Evidence test suite
packages/core/src/__tests__/shipped-evidence.test.ts
New Vitest suite reading fixtures, extracting the Shipped Evidence dimension score via a helper, asserting expected score ranges, verifying strong-vs-weak ranking, and checking non-regression of other dimension scores.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Test as shipped-evidence.test.ts
  participant Evaluate as evaluate()
  participant Scorer as scoreShippedEvidence()
  participant Counter as countMatches()
  Test->>Evaluate: evaluate(cvText)
  Evaluate->>Scorer: scoreShippedEvidence(cvText)
  Scorer->>Counter: countMatches(text, pattern) for each category
  Counter-->>Scorer: match counts
  Scorer->>Scorer: apply threshold adjustments
  Scorer->>Scorer: clamp and round score to [1,5]
  Scorer-->>Evaluate: score
  Evaluate-->>Test: dimensions with Shipped Evidence score
Loading

Possibly related issues

Possibly related PRs

  • TechImmigrants/cv-builder#84: Both PRs touch shippedEvidence scoring—this PR rewrites scoreShippedEvidence, while the referenced PR adjusts shippedEvidence evaluation weights for the AI Product Manager archetype.

Suggested reviewers: rfatideh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: improving shipped evidence scoring.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/shipped-evidence-scoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/core/src/evaluator/index.ts (1)

262-284: 🎯 Functional Correctness | 🔵 Trivial | ⚖️ Poor tradeoff

Additive scoring easily clamps to ceiling regardless of shipping-verb signal.

With strongCount/mediumCount/leadershipCount contributing at most ~3 combined points, and objectsCount, quantifiedCount, outcomeCount, productionCount each able to add up to ~1.5–2 more, a CV with rich quantified metrics and production language but zero shipping/leadership verbs can still reach the 5-point ceiling. This dilutes "Shipped Evidence" as a distinct signal from "Quantified Impact"/"Public Proof" (already separately scored dimensions). Given the case-sensitivity issue above further suppresses the verb-based counts, in practice this dimension is currently driven mostly by objects/metrics/production phrases rather than the verbs called out in the PR description.

Consider tightening the ceiling contribution from non-verb signals, or requiring a minimum combined verb signal before applying the outcome/production bonuses, so "Shipped Evidence" measures verb-based shipping evidence more directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/evaluator/index.ts` around lines 262 - 284, The Shipped
Evidence score in evaluator/index.ts is being driven too much by non-verb
signals, so a resume can hit the ceiling without strong shipping/leadership
verbs. Update the scoring logic in the function that computes score to make
verb-based counts such as strongCount, mediumCount, and leadershipCount the
primary gate, and reduce or condition the objectsCount, quantifiedCount,
outcomeCount, and productionCount bonuses unless enough verb evidence is
present. Keep the final clamp, but adjust the additive weights so this dimension
reflects shipping verbs more directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/core/src/evaluator/index.ts`:
- Around line 142-219: The verb and vague-language matchers in the evaluator are
missing capitalized resume-bullet forms, so the scorer undercounts common
signals. Update the regexes in the strongVerbs, mediumVerbs, leadershipVerbs,
improvementVerbs, and vaguePatterns arrays to be case-insensitive so matches
like Owned, Led, Built, Worked on, and Responsible for are counted consistently.

---

Nitpick comments:
In `@packages/core/src/evaluator/index.ts`:
- Around line 262-284: The Shipped Evidence score in evaluator/index.ts is being
driven too much by non-verb signals, so a resume can hit the ceiling without
strong shipping/leadership verbs. Update the scoring logic in the function that
computes score to make verb-based counts such as strongCount, mediumCount, and
leadershipCount the primary gate, and reduce or condition the objectsCount,
quantifiedCount, outcomeCount, and productionCount bonuses unless enough verb
evidence is present. Keep the final clamp, but adjust the additive weights so
this dimension reflects shipping verbs more directly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 47fc6c19-a4c6-4a88-b498-2a72bb61ff58

📥 Commits

Reviewing files that changed from the base of the PR and between 37deb6e and 0190b31.

📒 Files selected for processing (5)
  • packages/core/src/__fixtures__/synthetic/strong-ai-pm.md
  • packages/core/src/__fixtures__/synthetic/strong-backend.md
  • packages/core/src/__fixtures__/synthetic/weak-generic.md
  • packages/core/src/__tests__/shipped-evidence.test.ts
  • packages/core/src/evaluator/index.ts

Comment on lines +142 to +219
// Strong shipping verbs (each pattern matched counts once).
const strongVerbs = [
/\bshipped\b/,
/\blaunched\b/,
/\breleased\b/,
/\bdelivered\b/,
/\bdeployed\b/,
/\brolled\s+out\b/,
];
const matches = shippedIndicators.filter((r) => r.test(cv)).length;
return Math.min(5, Math.max(1, matches + 1));
// Medium shipping verbs (engineering work verbs). Each pattern requires
// the verb in past/present-participle form (no bare noun match) so that
// job titles like "software engineer" don't count as shipped work.
const mediumVerbs = [
/\bbuilt\b/,
/\bimplement(?:ed|s|ing)\b/,
/\bmigrat(?:ed|ing|e|ion)\b/,
/\bintegrat(?:ed|ing|e|ion)\b/,
/\bautomat(?:ed|ing|e|ion)\b/,
/\bredesign(?:ed|ing)\b/,
/\bcreat(?:ed|ing|e|ion)\b/,
/\bdevelop(?:ed|ing|ment)\b/,
/\bdesign(?:ed|ing)\b/,
/\bauthor(?:ed|ing)\b/,
/\bcod(?:ed|ing)\b/,
/\bprogram(?:med|ming)\b/,
/\bengineer(?:ed|ing)\b/,
];
// Leadership verbs (ownership signals).
const leadershipVerbs = [
/\bowned\b/,
/\bled\b/,
/\bdrove\b/,
/\bspearheaded\b/,
/\borchestrated\b/,
/\bchampioned\b/,
/\bheaded\b/,
];
// Improvement verbs (measurable change). Each pattern requires the verb
// in past/present-participle form.
const improvementVerbs = [
/\bimprov(?:ed|ing|ement)\b/,
/\breduc(?:ed|ing|tion)\b/,
/\bincreas(?:ed|ing)\b/,
/\boptimi[sz](?:ed|ing|ation)\b/,
/\bcut\b/,
/\bgrew\b/,
/\bscal(?:ed|ing)\b/,
/\bdoubled\b/,
/\btripled\b/,
/\bboost(?:ed|ing)\b/,
/\bsav(?:ed|ing)\b/,
/\baccelerat(?:ed|ing|ion)\b/,
/\bstreamlin(?:ed|ing|e)\b/,
/\bharden(?:ed|ing)\b/,
/\blower(?:ed|ing)\b/,
/\bshorten(?:ed|ing)\b/,
/\brais(?:ed|ing)\b/,
/\bexpand(?:ed|ing)\b/,
/\bwiden(?:ed|ing)\b/,
/\blift(?:ed|ing)\b/,
];
// Vague patterns that look like shipped work but rarely indicate
// ownership of a shipped artifact.
const vaguePatterns = [
/\bworked on\b/,
/\bhelped (?:with|the)\b/,
/\bparticipated in\b/,
/\bwas involved in\b/,
/\bassisted (?:with|in)\b/,
/\btasked with\b/,
/\bin charge of\b/,
/\bworked with\b/,
/\bcollaborated with\b/,
/\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/,
/\bdid some\b/,
/\bdid stuff\b/,
/\bresponsible for\b/,
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== File inventory ==\n'
git ls-files 'packages/core/src/evaluator/index.ts' 'packages/core/**' | sed -n '1,200p'

printf '\n== Outline evaluator file ==\n'
ast-grep outline packages/core/src/evaluator/index.ts --view expanded || true

printf '\n== Search fixtures/tests ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'strong-ai-pm|strong-backend|weak-generic|leadershipCount|vagueCount|strongVerbs|mediumVerbs|improvementVerbs|vaguePatterns' packages/core -S || true

Repository: TechImmigrants/cv-builder

Length of output: 3410


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Evaluator excerpt ==\n'
sed -n '135,290p' packages/core/src/evaluator/index.ts

printf '\n== Shipped evidence tests ==\n'
sed -n '1,240p' packages/core/src/__tests__/shipped-evidence.test.ts

printf '\n== Synthetic fixtures ==\n'
for f in \
  packages/core/src/__fixtures__/synthetic/strong-ai-pm.md \
  packages/core/src/__fixtures__/synthetic/strong-backend.md \
  packages/core/src/__fixtures__/synthetic/weak-generic.md
do
  printf '\n--- %s ---\n' "$f"
  sed -n '1,220p' "$f"
done

Repository: TechImmigrants/cv-builder

Length of output: 14801


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node <<'JS'
const fs = require('fs');
const path = require('path');

const root = process.cwd();
const fixtures = {
  'strong-ai-pm.md': fs.readFileSync(path.join(root, 'packages/core/src/__fixtures__/synthetic/strong-ai-pm.md'), 'utf8'),
  'strong-backend.md': fs.readFileSync(path.join(root, 'packages/core/src/__fixtures__/synthetic/strong-backend.md'), 'utf8'),
  'weak-generic.md': fs.readFileSync(path.join(root, 'packages/core/src/__fixtures__/synthetic/weak-generic.md'), 'utf8'),
};

const sets = {
  strongVerbs: [
    /\bshipped\b/,
    /\blaunched\b/,
    /\breleased\b/,
    /\bdelivered\b/,
    /\bdeployed\b/,
    /\brolled\s+out\b/,
  ],
  strongVerbs_i: [
    /\bshipped\b/i,
    /\blaunched\b/i,
    /\breleased\b/i,
    /\bdelivered\b/i,
    /\bdeployed\b/i,
    /\brolled\s+out\b/i,
  ],
  mediumVerbs: [
    /\bbuilt\b/,
    /\bimplement(?:ed|s|ing)\b/,
    /\bmigrat(?:ed|ing|e|ion)\b/,
    /\bintegrat(?:ed|ing|e|ion)\b/,
    /\bautomat(?:ed|ing|e|ion)\b/,
    /\bredesign(?:ed|ing)\b/,
    /\bcreat(?:ed|ing|e|ion)\b/,
    /\bdevelop(?:ed|ing|ment)\b/,
    /\bdesign(?:ed|ing)\b/,
    /\bauthor(?:ed|ing)\b/,
    /\bcod(?:ed|ing)\b/,
    /\bprogram(?:med|ming)\b/,
    /\bengineer(?:ed|ing)\b/,
  ],
  mediumVerbs_i: [
    /\bbuilt\b/i,
    /\bimplement(?:ed|s|ing)\b/i,
    /\bmigrat(?:ed|ing|e|ion)\b/i,
    /\bintegrat(?:ed|ing|e|ion)\b/i,
    /\bautomat(?:ed|ing|e|ion)\b/i,
    /\bredesign(?:ed|ing)\b/i,
    /\bcreat(?:ed|ing|e|ion)\b/i,
    /\bdevelop(?:ed|ing|ment)\b/i,
    /\bdesign(?:ed|ing)\b/i,
    /\bauthor(?:ed|ing)\b/i,
    /\bcod(?:ed|ing)\b/i,
    /\bprogram(?:med|ming)\b/i,
    /\bengineer(?:ed|ing)\b/i,
  ],
  leadershipVerbs: [
    /\bowned\b/,
    /\bled\b/,
    /\bdrove\b/,
    /\bspearheaded\b/,
    /\borchestrated\b/,
    /\bchampioned\b/,
    /\bheaded\b/,
  ],
  leadershipVerbs_i: [
    /\bowned\b/i,
    /\bled\b/i,
    /\bdrove\b/i,
    /\bspearheaded\b/i,
    /\borchestrated\b/i,
    /\bchampioned\b/i,
    /\bheaded\b/i,
  ],
  improvementVerbs: [
    /\bimprov(?:ed|ing|ement)\b/,
    /\breduc(?:ed|ing|tion)\b/,
    /\bincreas(?:ed|ing)\b/,
    /\boptimi[sz](?:ed|ing|ation)\b/,
    /\bcut\b/,
    /\bgrew\b/,
    /\bscal(?:ed|ing)\b/,
    /\bdoubled\b/,
    /\btripled\b/,
    /\bboost(?:ed|ing)\b/,
    /\bsav(?:ed|ing)\b/,
    /\baccelerat(?:ed|ing|ion)\b/,
    /\bstreamlin(?:ed|ing|e)\b/,
    /\bharden(?:ed|ing)\b/,
    /\blower(?:ed|ing)\b/,
    /\bshorten(?:ed|ing)\b/,
    /\brais(?:ed|ing)\b/,
    /\bexpand(?:ed|ing)\b/,
    /\bwiden(?:ed|ing)\b/,
    /\blift(?:ed|ing)\b/,
  ],
  improvementVerbs_i: [
    /\bimprov(?:ed|ing|ement)\b/i,
    /\breduc(?:ed|ing|tion)\b/i,
    /\bincreas(?:ed|ing)\b/i,
    /\boptimi[sz](?:ed|ing|ation)\b/i,
    /\bcut\b/i,
    /\bgrew\b/i,
    /\bscal(?:ed|ing)\b/i,
    /\bdoubled\b/i,
    /\btripled\b/i,
    /\bboost(?:ed|ing)\b/i,
    /\bsav(?:ed|ing)\b/i,
    /\baccelerat(?:ed|ing|ion)\b/i,
    /\bstreamlin(?:ed|ing|e)\b/i,
    /\bharden(?:ed|ing)\b/i,
    /\blower(?:ed|ing)\b/i,
    /\bshorten(?:ed|ing)\b/i,
    /\brais(?:ed|ing)\b/i,
    /\bexpand(?:ed|ing)\b/i,
    /\bwiden(?:ed|ing)\b/i,
    /\blift(?:ed|ing)\b/i,
  ],
  vaguePatterns: [
    /\bworked on\b/,
    /\bhelped (?:with|the)\b/,
    /\bparticipated in\b/,
    /\bwas involved in\b/,
    /\bassisted (?:with|in)\b/,
    /\btasked with\b/,
    /\bin charge of\b/,
    /\bworked with\b/,
    /\bcollaborated with\b/,
    /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/,
    /\bdid some\b/,
    /\bdid stuff\b/,
    /\bresponsible for\b/,
  ],
  vaguePatterns_i: [
    /\bworked on\b/i,
    /\bhelped (?:with|the)\b/i,
    /\bparticipated in\b/i,
    /\bwas involved in\b/i,
    /\bassisted (?:with|in)\b/i,
    /\btasked with\b/i,
    /\bin charge of\b/i,
    /\bworked with\b/i,
    /\bcollaborated with\b/i,
    /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/i,
    /\bdid some\b/i,
    /\bdid stuff\b/i,
    /\bresponsible for\b/i,
  ],
};

for (const [name, text] of Object.entries(fixtures)) {
  console.log(`\n## ${name}`);
  for (const [setName, patterns] of Object.entries(sets)) {
    const matches = patterns.filter((p) => p.test(text)).length;
    if (/^.*_i$/.test(setName) || ['strongVerbs','mediumVerbs','leadershipVerbs','improvementVerbs','vaguePatterns'].includes(setName)) {
      console.log(`${setName}: ${matches}`);
    }
  }
}
JS

Repository: TechImmigrants/cv-builder

Length of output: 771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Evaluator excerpt ==\n'
sed -n '135,290p' packages/core/src/evaluator/index.ts

printf '\n== Shipped evidence tests ==\n'
sed -n '1,240p' packages/core/src/__tests__/shipped-evidence.test.ts

printf '\n== Synthetic fixtures ==\n'
for f in \
  packages/core/src/__fixtures__/synthetic/strong-ai-pm.md \
  packages/core/src/__fixtures__/synthetic/strong-backend.md \
  packages/core/src/__fixtures__/synthetic/weak-generic.md
do
  printf '\n--- %s ---\n' "$f"
  sed -n '1,220p' "$f"
done

printf '\n== Regex count probe ==\n'
node <<'JS'
const fs = require('fs');
const path = require('path');

const fixtures = {
  'strong-ai-pm.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/strong-ai-pm.md'), 'utf8'),
  'strong-backend.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/strong-backend.md'), 'utf8'),
  'weak-generic.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/weak-generic.md'), 'utf8'),
};

const groups = {
  strongVerbs: [/\bshipped\b/, /\blaunched\b/, /\breleased\b/, /\bdelivered\b/, /\bdeployed\b/, /\brolled\s+out\b/],
  strongVerbs_i: [/\bshipped\b/i, /\blaunched\b/i, /\breleased\b/i, /\bdelivered\b/i, /\bdeployed\b/i, /\brolled\s+out\b/i],
  mediumVerbs: [/\bbuilt\b/, /\bimplement(?:ed|s|ing)\b/, /\bmigrat(?:ed|ing|e|ion)\b/, /\bintegrat(?:ed|ing|e|ion)\b/, /\bautomat(?:ed|ing|e|ion)\b/, /\bredesign(?:ed|ing)\b/, /\bcreat(?:ed|ing|e|ion)\b/, /\bdevelop(?:ed|ing|ment)\b/, /\bdesign(?:ed|ing)\b/, /\bauthor(?:ed|ing)\b/, /\bcod(?:ed|ing)\b/, /\bprogram(?:med|ming)\b/, /\bengineer(?:ed|ing)\b/],
  mediumVerbs_i: [/\bbuilt\b/i, /\bimplement(?:ed|s|ing)\b/i, /\bmigrat(?:ed|ing|e|ion)\b/i, /\bintegrat(?:ed|ing|e|ion)\b/i, /\bautomat(?:ed|ing|e|ion)\b/i, /\bredesign(?:ed|ing)\b/i, /\bcreat(?:ed|ing|e|ion)\b/i, /\bdevelop(?:ed|ing|ment)\b/i, /\bdesign(?:ed|ing)\b/i, /\bauthor(?:ed|ing)\b/i, /\bcod(?:ed|ing)\b/i, /\bprogram(?:med|ming)\b/i, /\bengineer(?:ed|ing)\b/i],
  leadershipVerbs: [/\bowned\b/, /\bled\b/, /\bdrove\b/, /\bspearheaded\b/, /\borchestrated\b/, /\bchampioned\b/, /\bheaded\b/],
  leadershipVerbs_i: [/\bowned\b/i, /\bled\b/i, /\bdrove\b/i, /\bspearheaded\b/i, /\borchestrated\b/i, /\bchampioned\b/i, /\bheaded\b/i],
  improvementVerbs: [/\bimprov(?:ed|ing|ement)\b/, /\breduc(?:ed|ing|tion)\b/, /\bincreas(?:ed|ing)\b/, /\boptimi[sz](?:ed|ing|ation)\b/, /\bcut\b/, /\bgrew\b/, /\bscal(?:ed|ing)\b/, /\bdoubled\b/, /\btripled\b/, /\bboost(?:ed|ing)\b/, /\bsav(?:ed|ing)\b/, /\baccelerat(?:ed|ing|ion)\b/, /\bstreamlin(?:ed|ing|e)\b/, /\bharden(?:ed|ing)\b/, /\blower(?:ed|ing)\b/, /\bshorten(?:ed|ing)\b/, /\brais(?:ed|ing)\b/, /\bexpand(?:ed|ing)\b/, /\bwiden(?:ed|ing)\b/, /\blift(?:ed|ing)\b/],
  improvementVerbs_i: [/\bimprov(?:ed|ing|ement)\b/i, /\breduc(?:ed|ing|tion)\b/i, /\bincreas(?:ed|ing)\b/i, /\boptimi[sz](?:ed|ing|ation)\b/i, /\bcut\b/i, /\bgrew\b/i, /\bscal(?:ed|ing)\b/i, /\bdoubled\b/i, /\btripled\b/i, /\bboost(?:ed|ing)\b/i, /\bsav(?:ed|ing)\b/i, /\baccelerat(?:ed|ing|ion)\b/i, /\bstreamlin(?:ed|ing|e)\b/i, /\bharden(?:ed|ing)\b/i, /\blower(?:ed|ing)\b/i, /\bshorten(?:ed|ing)\b/i, /\brais(?:ed|ing)\b/i, /\bexpand(?:ed|ing)\b/i, /\bwiden(?:ed|ing)\b/i, /\blift(?:ed|ing)\b/i],
  vaguePatterns: [/\bworked on\b/, /\bhelped (?:with|the)\b/, /\bparticipated in\b/, /\bwas involved in\b/, /\bassisted (?:with|in)\b/, /\btasked with\b/, /\bin charge of\b/, /\bworked with\b/, /\bcollaborated with\b/, /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/, /\bdid some\b/, /\bdid stuff\b/, /\bresponsible for\b/],
  vaguePatterns_i: [/\bworked on\b/i, /\bhelped (?:with|the)\b/i, /\bparticipated in\b/i, /\bwas involved in\b/i, /\bassisted (?:with|in)\b/i, /\btasked with\b/i, /\bin charge of\b/i, /\bworked with\b/i, /\bcollaborated with\b/i, /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/i, /\bdid some\b/i, /\bdid stuff\b/i, /\bresponsible for\b/i],
};

for (const [name, text] of Object.entries(fixtures)) {
  console.log(`\n${name}`);
  for (const [group, patterns] of Object.entries(groups)) {
    const c = patterns.filter((r) => r.test(text)).length;
    console.log(`${group}: ${c}`);
  }
}
JS

Repository: TechImmigrants/cv-builder

Length of output: 15425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== Evaluator excerpt ==\n'
sed -n '135,290p' packages/core/src/evaluator/index.ts

printf '\n== Shipped evidence tests ==\n'
sed -n '1,240p' packages/core/src/__tests__/shipped-evidence.test.ts

printf '\n== Synthetic fixtures ==\n'
for f in \
  packages/core/src/__fixtures__/synthetic/strong-ai-pm.md \
  packages/core/src/__fixtures__/synthetic/strong-backend.md \
  packages/core/src/__fixtures__/synthetic/weak-generic.md
do
  printf '\n--- %s ---\n' "$f"
  sed -n '1,220p' "$f"
done

printf '\n== Regex count probe ==\n'
node <<'JS'
const fs = require('fs');
const path = require('path');

const fixtures = {
  'strong-ai-pm.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/strong-ai-pm.md'), 'utf8'),
  'strong-backend.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/strong-backend.md'), 'utf8'),
  'weak-generic.md': fs.readFileSync(path.join(process.cwd(), 'packages/core/src/__fixtures__/synthetic/weak-generic.md'), 'utf8'),
};

const groups = {
  strongVerbs: [/\bshipped\b/, /\blaunched\b/, /\breleased\b/, /\bdelivered\b/, /\bdeployed\b/, /\brolled\s+out\b/],
  strongVerbs_i: [/\bshipped\b/i, /\blaunched\b/i, /\breleased\b/i, /\bdelivered\b/i, /\bdeployed\b/i, /\brolled\s+out\b/i],
  mediumVerbs: [/\bbuilt\b/, /\bimplement(?:ed|s|ing)\b/, /\bmigrat(?:ed|ing|e|ion)\b/, /\bintegrat(?:ed|ing|e|ion)\b/, /\bautomat(?:ed|ing|e|ion)\b/, /\bredesign(?:ed|ing)\b/, /\bcreat(?:ed|ing|e|ion)\b/, /\bdevelop(?:ed|ing|ment)\b/, /\bdesign(?:ed|ing)\b/, /\bauthor(?:ed|ing)\b/, /\bcod(?:ed|ing)\b/, /\bprogram(?:med|ming)\b/, /\bengineer(?:ed|ing)\b/],
  mediumVerbs_i: [/\bbuilt\b/i, /\bimplement(?:ed|s|ing)\b/i, /\bmigrat(?:ed|ing|e|ion)\b/i, /\bintegrat(?:ed|ing|e|ion)\b/i, /\bautomat(?:ed|ing|e|ion)\b/i, /\bredesign(?:ed|ing)\b/i, /\bcreat(?:ed|ing|e|ion)\b/i, /\bdevelop(?:ed|ing|ment)\b/i, /\bdesign(?:ed|ing)\b/i, /\bauthor(?:ed|ing)\b/i, /\bcod(?:ed|ing)\b/i, /\bprogram(?:med|ming)\b/i, /\bengineer(?:ed|ing)\b/i],
  leadershipVerbs: [/\bowned\b/, /\bled\b/, /\bdrove\b/, /\bspearheaded\b/, /\borchestrated\b/, /\bchampioned\b/, /\bheaded\b/],
  leadershipVerbs_i: [/\bowned\b/i, /\bled\b/i, /\bdrove\b/i, /\bspearheaded\b/i, /\borchestrated\b/i, /\bchampioned\b/i, /\bheaded\b/i],
  improvementVerbs: [/\bimprov(?:ed|ing|ement)\b/, /\breduc(?:ed|ing|tion)\b/, /\bincreas(?:ed|ing)\b/, /\boptimi[sz](?:ed|ing|ation)\b/, /\bcut\b/, /\bgrew\b/, /\bscal(?:ed|ing)\b/, /\bdoubled\b/, /\btripled\b/, /\bboost(?:ed|ing)\b/, /\bsav(?:ed|ing)\b/, /\baccelerat(?:ed|ing|ion)\b/, /\bstreamlin(?:ed|ing|e)\b/, /\bharden(?:ed|ing)\b/, /\blower(?:ed|ing)\b/, /\bshorten(?:ed|ing)\b/, /\brais(?:ed|ing)\b/, /\bexpand(?:ed|ing)\b/, /\bwiden(?:ed|ing)\b/, /\blift(?:ed|ing)\b/],
  improvementVerbs_i: [/\bimprov(?:ed|ing|ement)\b/i, /\breduc(?:ed|ing|tion)\b/i, /\bincreas(?:ed|ing)\b/i, /\boptimi[sz](?:ed|ing|ation)\b/i, /\bcut\b/i, /\bgrew\b/i, /\bscal(?:ed|ing)\b/i, /\bdoubled\b/i, /\btripled\b/i, /\bboost(?:ed|ing)\b/i, /\bsav(?:ed|ing)\b/i, /\baccelerat(?:ed|ing|ion)\b/i, /\bstreamlin(?:ed|ing|e)\b/i, /\bharden(?:ed|ing)\b/i, /\blower(?:ed|ing)\b/i, /\bshorten(?:ed|ing)\b/i, /\brais(?:ed|ing)\b/i, /\bexpand(?:ed|ing)\b/i, /\bwiden(?:ed|ing)\b/i, /\blift(?:ed|ing)\b/i],
  vaguePatterns: [/\bworked on\b/, /\bhelped (?:with|the)\b/, /\bparticipated in\b/, /\bwas involved in\b/, /\bassisted (?:with|in)\b/, /\btasked with\b/, /\bin charge of\b/, /\bworked with\b/, /\bcollaborated with\b/, /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/, /\bdid some\b/, /\bdid stuff\b/, /\bresponsible for\b/],
  vaguePatterns_i: [/\bworked on\b/i, /\bhelped (?:with|the)\b/i, /\bparticipated in\b/i, /\bwas involved in\b/i, /\bassisted (?:with|in)\b/i, /\btasked with\b/i, /\bin charge of\b/i, /\bworked with\b/i, /\bcollaborated with\b/i, /\bsome\s+(?:code|things|work|stuff|projects|cloud)\b/i, /\bdid some\b/i, /\bdid stuff\b/i, /\bresponsible for\b/i],
};

for (const [name, text] of Object.entries(fixtures)) {
  console.log(`\n${name}`);
  for (const [group, patterns] of Object.entries(groups)) {
    console.log(`${group}: ${patterns.filter((r) => r.test(text)).length}`);
  }
}
JS

Repository: TechImmigrants/cv-builder

Length of output: 15425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '135,290p' packages/core/src/evaluator/index.ts
printf '\n====\n'
sed -n '1,240p' packages/core/src/__tests__/shipped-evidence.test.ts
printf '\n==== strong-ai-pm.md ====\n'
sed -n '1,220p' packages/core/src/__fixtures__/synthetic/strong-ai-pm.md
printf '\n==== strong-backend.md ====\n'
sed -n '1,220p' packages/core/src/__fixtures__/synthetic/strong-backend.md
printf '\n==== weak-generic.md ====\n'
sed -n '1,220p' packages/core/src/__fixtures__/synthetic/weak-generic.md

Repository: TechImmigrants/cv-builder

Length of output: 14609


Make the verb/vague regexes case-insensitive
strongVerbs, mediumVerbs, leadershipVerbs, improvementVerbs, and vaguePatterns miss the common capitalized resume-bullet form (Owned, Led, Built, Worked on, Responsible for). Add /i so the scorer counts those signals consistently; otherwise it undercounts leadership and vague-language matches on realistic CVs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/evaluator/index.ts` around lines 142 - 219, The verb and
vague-language matchers in the evaluator are missing capitalized resume-bullet
forms, so the scorer undercounts common signals. Update the regexes in the
strongVerbs, mediumVerbs, leadershipVerbs, improvementVerbs, and vaguePatterns
arrays to be case-insensitive so matches like Owned, Led, Built, Worked on, and
Responsible for are counted consistently.

Source: Path instructions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant