Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ pack):
- QA / Test Engineer
- DevOps / SRE
- Data Engineer
- Mobile Engineer

**Want to add your role?** Use the [New Archetype issue template](.github/ISSUE_TEMPLATE/new_archetype.md) and submit a PR. Each archetype needs 15+ keywords, evaluation weights, action verbs, and anti-patterns.

Expand All @@ -206,7 +207,7 @@ pack):

### v0.1 (Current Sprint — MVP)
- [x] Core evaluation engine with 6 dimensions
- [x] 7 role archetypes
- [x] 8 role archetypes
- [x] Universal anti-pattern detection
- [x] CLI: basic evaluate command
- [x] Web UI: paste and score screen (local demo)
Expand Down
3 changes: 2 additions & 1 deletion docs/ARCHETYPE_GAP_AUDIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ registries being out of sync — is filed as a follow-up issue and
| `software-engineer` | Software Engineer | ❌ | ✅ | ❌ (eval fixtures only) | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ (eval fixture `swe-strong`) | Lives only in `intelligence`. Not exposed in the live CLI/Web UI. |
| `product-manager` | Product Manager | ❌ | ✅ | ❌ (eval fixtures only) | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ (eval fixture `pm-strong`) | Lives only in `intelligence`. **Plain PM detection is broken in the live system** — see *Highest-priority gap* below. |
| `data-ml-engineer` | Data & ML Engineer | ❌ | ✅ | ❌ (eval fixtures only) | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ (eval fixture `data-ml-strong`) | Lives only in `intelligence`. Different id from `core.data-engineer`. |
| `mobile-engineer` | Mobile Engineer | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ (`core.__tests__/archetypes.test.ts`, `intelligence` detect test) | Closes issue #1. Added to both registries with matching content, keeping the two in sync rather than widening the gap. No `intelligence` eval fixture yet. |

**Notes on the table:**

Expand Down Expand Up @@ -123,7 +124,7 @@ registries being out of sync — is filed as a follow-up issue and
| AI Engineer | Issue #81, PR #69 (rubric v1 base) | ✅ Implemented in `core` as `ai-engineer` | `intelligence` has `software-engineer` (broader) but not `ai-engineer`. |
| QA / Test Engineer | Issue #5 (closed), PR #28 | ✅ Implemented in `core` as `qa-test-engineer` | |
| Machine Learning Engineer | Issue #3 | ❌ Missing | Closest existing: `intelligence.data-ml-engineer` (and `core.ai-engineer`). No plain `ml-engineer` (scikit-learn, classical ML) anywhere. |
| Mobile Engineer (iOS/Android) | Issue #1 | ❌ Missing | From `scripts/create-issues.sh` seed list. |
| Mobile Engineer (iOS/Android) | Issue #1 | ✅ Implemented in both `core` (`mobile-engineer`) and `intelligence` (`mobile-engineer`) | Closed by the follow-up PR to this audit. |
| Security Engineer | Issue #2 | ❌ Missing | From `scripts/create-issues.sh` seed list. |
| Full-Stack Engineer | Issue #4 | ❌ Missing | From `scripts/create-issues.sh` seed list. |
| Technical Program Manager | Issue #6 | ❌ Missing | From `scripts/create-issues.sh` seed list. |
Expand Down
49 changes: 49 additions & 0 deletions packages/core/src/__tests__/archetypes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, expect, it } from "vitest";
import { detectArchetype, getArchetype } from "../archetypes/index.js";

describe("mobile-engineer archetype", () => {
const archetype = getArchetype("mobile-engineer");

it("is registered under the core registry", () => {
expect(archetype.name).toBe("Mobile Engineer");
});

it("has 15-30 keywords", () => {
expect(archetype.keywords.length).toBeGreaterThanOrEqual(15);
expect(archetype.keywords.length).toBeLessThanOrEqual(30);
});

it("has 10+ action verbs", () => {
expect(archetype.actionVerbs.length).toBeGreaterThanOrEqual(10);
});

it("has 5+ anti-patterns", () => {
expect(archetype.antiPatterns.length).toBeGreaterThanOrEqual(5);
});

it("has evaluation weights summing to 1.0", () => {
const sum = Object.values(archetype.evaluationWeights).reduce((a, b) => a + b, 0);
expect(sum).toBeCloseTo(1, 5);
});

it("is detected from a representative mobile resume", () => {
const text =
"Built iOS features in Swift and SwiftUI, shipped Android screens with Kotlin " +
"and Jetpack Compose, released through TestFlight and Google Play Console.";
expect(detectArchetype(text).id).toBe("mobile-engineer");
});

it("is detected from platform-only mentions", () => {
expect(detectArchetype("Shipped iOS apps for the retail team.").id).toBe(
"mobile-engineer"
);
expect(detectArchetype("Shipped Android apps for the retail team.").id).toBe(
"mobile-engineer"
);
});

it("does not treat 'swiftly' as a swift keyword match", () => {
const text = "Swiftly delivered backend services using Node and Postgres.";
expect(detectArchetype(text).id).not.toBe("mobile-engineer");
});
});
76 changes: 75 additions & 1 deletion packages/core/src/archetypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,74 @@ ARCHETYPES.set("data-engineer", {
],
});

ARCHETYPES.set("mobile-engineer", {
id: "mobile-engineer",
name: "Mobile Engineer",
description: "iOS/Android engineers building native and cross-platform mobile apps",
keywords: [
"ios",
"android",
"swift",
"swiftui",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"objective-c",
"uikit",
"kotlin",
"jetpack compose",
"android sdk",
"xcode",
"android studio",
Comment thread
coderabbitai[bot] marked this conversation as resolved.
"react native",
"flutter",
"dart",
"kotlin multiplatform",
"core data",
"room",
"coroutines",
"combine",
"mvvm",
"ci/cd",
"fastlane",
"app store connect",
"google play console",
"testflight",
"crashlytics",
"firebase",
"rest api",
"push notifications",
"deep linking",
],
evaluationWeights: {
shippedEvidence: 0.3,
quantifiedImpact: 0.2,
toolingVisibility: 0.2,
atsCompatibility: 0.1,
keywordMatch: 0.1,
publicProof: 0.1,
},
actionVerbs: [
"Shipped",
"Launched",
"Released",
"Migrated",
"Optimized",
"Reduced",
"Refactored",
"Integrated",
"Automated",
"Debugged",
"Instrumented",
"Profiled",
],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
antiPatterns: [
"familiar with mobile development",
"worked on the app",
"various mobile frameworks",
"responsible for app maintenance",
"passionate about mobile technologies",
"experience with cross-platform development",
],
});

// --- Registry API ---

export function getArchetype(id: string): RoleArchetype {
Expand All @@ -512,6 +580,12 @@ export function registerArchetype(archetype: RoleArchetype): void {
ARCHETYPES.set(archetype.id, archetype);
}

function hasKeyword(text: string, keyword: string): boolean {
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
// hyphen counts as a word char so "go" doesn't match "go-to-market"
return new RegExp(`(?<![\\w-])${escaped}(?![\\w-])`).test(text);
}

/**
* Auto-detect the best archetype based on CV content and optional JD.
* Uses keyword frequency matching to determine the closest fit.
Expand All @@ -524,7 +598,7 @@ export function detectArchetype(cvContent: string, jdContent?: string): RoleArch

for (const archetype of ARCHETYPES.values()) {
const matchCount = archetype.keywords.filter((kw) =>
text.includes(kw.toLowerCase())
hasKeyword(text, kw.toLowerCase())
).length;
const score = matchCount / archetype.keywords.length;

Expand Down
16 changes: 16 additions & 0 deletions packages/intelligence/src/__tests__/intelligence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ describe("detectArchetype", () => {
expect(detectArchetype(text).id).toBe("product-manager");
});

it("detects a mobile engineer resume", () => {
const text =
"Built iOS features in Swift and SwiftUI, shipped Android screens with Kotlin " +
"and Jetpack Compose, released through TestFlight and Google Play Console.";
expect(detectArchetype(text).id).toBe("mobile-engineer");
});

it("detects platform-only mobile mentions", () => {
expect(detectArchetype("Shipped iOS apps for the retail team.").id).toBe(
"mobile-engineer"
);
expect(detectArchetype("Shipped Android apps for the retail team.").id).toBe(
"mobile-engineer"
);
});

it("falls back to the default on no signal", () => {
expect(detectArchetype("hello world").id).toBe("software-engineer");
});
Expand Down
10 changes: 8 additions & 2 deletions packages/intelligence/src/archetypes/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import type { Archetype } from "@cv-builder/schemas";
import { dataMlEngineer } from "./data-ml-engineer.js";
import { mobileEngineer } from "./mobile-engineer.js";
import { productManager } from "./product-manager.js";
import { softwareEngineer } from "./software-engineer.js";

// Register new archetypes here. Software Engineer is the fallback when nothing
// else clearly matches.
export const ARCHETYPES: Archetype[] = [softwareEngineer, productManager, dataMlEngineer];
export const ARCHETYPES: Archetype[] = [
softwareEngineer,
productManager,
dataMlEngineer,
mobileEngineer,
];

export const DEFAULT_ARCHETYPE = softwareEngineer;

Expand All @@ -17,4 +23,4 @@ export function listArchetypes(): Archetype[] {
return ARCHETYPES;
}

export { dataMlEngineer, productManager, softwareEngineer };
export { dataMlEngineer, mobileEngineer, productManager, softwareEngineer };
70 changes: 70 additions & 0 deletions packages/intelligence/src/archetypes/mobile-engineer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import type { Archetype } from "@cv-builder/schemas";

export const mobileEngineer: Archetype = {
id: "mobile-engineer",
name: "Mobile Engineer",
description: "iOS/Android engineers building native and cross-platform mobile apps",
keywords: [
"ios",
"android",
"swift",
"swiftui",
"objective-c",
"uikit",
"kotlin",
"jetpack compose",
"android sdk",
"xcode",
"android studio",
"react native",
"flutter",
"dart",
"kotlin multiplatform",
"core data",
"room",
"coroutines",
"combine",
"mvvm",
"ci/cd",
"fastlane",
"app store connect",
"google play console",
"testflight",
"crashlytics",
"firebase",
"rest api",
"push notifications",
"deep linking",
],
evaluationWeights: {
shippedEvidence: 0.3,
quantifiedImpact: 0.2,
toolingVisibility: 0.2,
atsCompatibility: 0.1,
keywordMatch: 0.1,
publicProof: 0.1,
},
actionVerbs: [
"Shipped",
"Launched",
"Released",
"Migrated",
"Optimized",
"Reduced",
"Refactored",
"Integrated",
"Automated",
"Debugged",
"Instrumented",
"Profiled",
],
antiPatterns: [
"familiar with mobile development",
"worked on the app",
"various mobile frameworks",
"responsible for app maintenance",
"passionate about mobile technologies",
"experience with cross-platform development",
],
version: "1.0.0",
};
7 changes: 6 additions & 1 deletion research/sources.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All research sources used to build CV Builder's rules and scoring engine.

Last updated: May 2026
Last updated: July 30, 2026

## Market Data

Expand All @@ -27,6 +27,11 @@ Last updated: May 2026
| Machine Learning Engineer | ML engineers build, evaluate, productionize, optimize, monitor, and improve ML models; core areas include model architecture, data/ML pipelines, metrics interpretation, MLOps, and data engineering. | Google Cloud Professional Machine Learning Engineer Exam Guide | https://cloud.google.com/learn/certification/guides/machine-learning-engineer | 2026-06-01 |
| Machine Learning Engineer | scikit-learn covers model selection, cross-validation, hyperparameter tuning, metrics, pipelines, preprocessing, feature extraction, feature selection, and model persistence. | scikit-learn User Guide | https://scikit-learn.org/stable/user_guide | 2026-06-01 |
| Machine Learning Engineer | XGBoost's Python API supports scikit-learn-style estimators, evaluation sets, early stopping, feature weights, and feature importance for tree boosters. | XGBoost Python API Reference | https://xgboost.readthedocs.io/en/stable/python/python_api.html | 2026-06-01 |
| Mobile Engineer | Senior iOS postings now explicitly require Combine and Swift Concurrency (async/await, actors, structured concurrency) alongside UIKit/SwiftUI and MVVM-style architecture, not just legacy UIKit. (§ "About You") | Bumble Inc. — Lead iOS Engineer (Date) | https://jobs.lever.co/bumbleinc/9dd631a9-ba49-4e71-b2d5-b655c943bed9 | 2026-07-30 |
| Mobile Engineer | iOS postings expect 3+ years shipping Swift-based apps with MVVM (or similar) architecture, SwiftUI + Auto Layout UI work, and a track record of publishing and maintaining live App Store apps. (§ "Does this sound like you") | Airalo — iOS Developer | https://jobs.lever.co/airalo/4c06be41-69bc-4b28-9f8d-df75027e0eda | 2026-07-30 |
| Mobile Engineer | Android postings expect Kotlin, Jetpack Compose, coroutines, Room, and Hilt, plus hands-on experience publishing at least one app to the Google Play Store. (§ "What We're Looking For") | Match Group (Hinge) — Android Engineer II | https://jobs.lever.co/matchgroup/eeb31906-5f8f-4599-a77a-dbee6c2079c9 | 2026-07-30 |
| Mobile Engineer | Cross-platform Flutter roles require naming a specific state-management approach (Provider, Bloc, Riverpod, or MobX) and tie mobile performance to concrete metrics like startup time and crash rate. (§ "Requirements") | HighLevel — SDE III, Content Hub (Mobile Flutter) | https://jobs.lever.co/gohighlevel/d792b13b-f415-4164-b295-2d37f122ba32 | 2026-07-30 |
| Mobile Engineer | Recruiters for senior iOS roles screen for shipped native apps in production, Fastlane/CI tooling, and disciplined release practices backed by crash and performance monitoring. (§ "QUALIFICATIONS") | Whoop — Senior iOS Engineer (Healthcare) | https://jobs.lever.co/whoop/e3fc699a-e921-4015-8f0a-652af1cdb0a2 | 2026-07-30 |

## Expert Opinions

Expand Down
Loading