chore: replace personal seed data with generic demo profiles

Seeds now insert generic Demo User personas (INSERT OR IGNORE, so
existing databases keep their current profile data). Drops the
every-boot UPDATE backfills that re-asserted personal values, and
removes the personal cron env example templates in favor of the
generic scripts/jobber-cron.env.example.
This commit is contained in:
2026-07-26 15:54:12 -04:00
parent af3d1c56b0
commit 285b094bce
14 changed files with 67 additions and 236 deletions
@@ -362,8 +362,8 @@ export const ManualImportFlow: React.FC<ManualImportFlowProps> = ({
</Button>
</div>
<p className="text-[11px] text-muted-foreground">
Synthetic postings for quick manual-import testing (Ilia vs
Cherepaha-style roles).
Synthetic postings for quick manual-import testing (SDET vs
Guidewire QA-style roles).
</p>
</div>
@@ -124,10 +124,8 @@ export const PageHeader: React.FC<PageHeaderProps> = ({
only visible to this login
</span>
sign out to use another user (e.g.{" "}
<span className="font-mono text-foreground">ilia</span> vs{" "}
<span className="font-mono text-foreground">
cherepaha
</span>
<span className="font-mono text-foreground">demo</span> vs{" "}
<span className="font-mono text-foreground">demo-qa</span>
).
</p>
<Button
@@ -175,7 +175,7 @@ describe.sequential("Stats proxy routes", () => {
await stopServer({ server, closeDb, tempDir });
({ server, baseUrl, closeDb, tempDir } = await startServer({
env: {
BASIC_AUTH_USER: "ilia",
BASIC_AUTH_USER: "demo",
BASIC_AUTH_PASSWORD: "secret",
},
}));
@@ -228,7 +228,7 @@ describe.sequential("Tracer links routes", () => {
({ server, baseUrl, closeDb, tempDir } = await startServer({
env: {
// Must match a seeded search profile `basicAuthUser` (see db/migrate.ts).
BASIC_AUTH_USER: "ilia",
BASIC_AUTH_USER: "demo",
BASIC_AUTH_PASSWORD: "secret",
},
}));
@@ -236,7 +236,7 @@ describe.sequential("Tracer links routes", () => {
const unauthorized = await fetch(`${baseUrl}/api/tracer-links/analytics`);
expect(unauthorized.status).toBe(401);
const credentials = Buffer.from("ilia:secret").toString("base64");
const credentials = Buffer.from("demo:secret").toString("base64");
const authorized = await fetch(`${baseUrl}/api/tracer-links/analytics`, {
headers: {
Authorization: `Basic ${credentials}`,
+42 -110
View File
@@ -34,7 +34,7 @@ function sqlJsonLiteral(values: string[]): string {
}
/** Canada / remote — SDET & QA automation title variants for discovery + scoring. */
const ILIA_DOBKIN_CA_TARGET_ROLES = [
const SDET_CA_TARGET_ROLES = [
"SDET",
"Senior SDET",
"Lead SDET",
@@ -61,8 +61,8 @@ const ILIA_DOBKIN_CA_TARGET_ROLES = [
];
/** Job-board search terms — title variants plus common stack/tool pairings. */
const ILIA_DOBKIN_CA_KEYWORD_TERMS = [
...ILIA_DOBKIN_CA_TARGET_ROLES.map((role) => role.toLowerCase()),
const SDET_CA_KEYWORD_TERMS = [
...SDET_CA_TARGET_ROLES.map((role) => role.toLowerCase()),
"playwright SDET",
"SDET playwright",
"SDET java",
@@ -78,14 +78,14 @@ const ILIA_DOBKIN_CA_KEYWORD_TERMS = [
"software engineer test",
];
const ILIA_DOBKIN_US_TARGET_ROLES = [
...ILIA_DOBKIN_CA_TARGET_ROLES,
const SDET_US_TARGET_ROLES = [
...SDET_CA_TARGET_ROLES,
"Computer Systems Analyst",
"Quality Assurance Engineer",
];
const ILIA_DOBKIN_US_KEYWORD_TERMS = [
...ILIA_DOBKIN_CA_KEYWORD_TERMS,
const SDET_US_KEYWORD_TERMS = [
...SDET_CA_KEYWORD_TERMS,
"computer systems analyst",
"systems analyst",
"computer systems analyst QA",
@@ -99,7 +99,7 @@ const ILIA_DOBKIN_US_KEYWORD_TERMS = [
];
/** AI engineering / AI enablement — agents, MCP, RAG, evals, local LLM serving. */
const ILIA_DOBKIN_AI_TARGET_ROLES = [
const AI_ENGINEER_TARGET_ROLES = [
"AI Engineer",
"Senior AI Engineer",
"Staff AI Engineer",
@@ -119,8 +119,8 @@ const ILIA_DOBKIN_AI_TARGET_ROLES = [
"RAG Engineer",
];
const ILIA_DOBKIN_AI_KEYWORD_TERMS = [
...ILIA_DOBKIN_AI_TARGET_ROLES.map((role) => role.toLowerCase()),
const AI_ENGINEER_KEYWORD_TERMS = [
...AI_ENGINEER_TARGET_ROLES.map((role) => role.toLowerCase()),
"LLM agents",
"MCP engineer",
"LangGraph",
@@ -919,12 +919,13 @@ const migrations = [
SELECT 1 FROM keyword_sets WHERE owner_profile_id = '__default__'
)`,
// Seed default job-search personas (INSERT OR IGNORE — safe on existing DBs).
// Seed default job-search personas (INSERT OR IGNORE — safe on existing DBs;
// existing databases keep whatever profile data they already have).
sqlInsertSearchProfileSeed({
id: "685b0000-0000-4000-8000-000000000001",
name: "Ilia Dobkin",
name: "Demo User",
profile: {
targetRoles: ILIA_DOBKIN_CA_TARGET_ROLES,
targetRoles: SDET_CA_TARGET_ROLES,
experienceLevel: "Senior",
mustHaveSkills: [
"Playwright",
@@ -957,9 +958,9 @@ const migrations = [
industriesToTarget: ["Software", "FinTech", "iGaming", "Enterprise SaaS"],
industriesToAvoid: [],
aboutMe:
"Ilia Dobkin — Senior SDET / test automation (Canada & remote). Paired login user `ilia` activates this profile and local resume ilia-dobkin.json (unset JOBOPS_LOCAL_RESUME_PATH so Settings path applies).",
basicAuthUser: "ilia,dobkin",
resumeLocalPath: "../data/resumes/ilia-dobkin.json",
"Demo User — Senior SDET / test automation (Canada & remote). Paired login user `demo` activates this profile and local resume demo-user.json (unset JOBOPS_LOCAL_RESUME_PATH so Settings path applies).",
basicAuthUser: "demo,demo-user",
resumeLocalPath: "../data/resumes/demo-user.json",
},
}),
`UPDATE keyword_sets
@@ -976,17 +977,12 @@ const migrations = [
WHERE sp.id = keyword_sets.owner_profile_id
AND json_array_length(json_extract(sp.data, '$.targetRoles')) > 0
)`,
`UPDATE search_profiles
SET data = json_set(data, '$.basicAuthUser', 'ilia,dobkin'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000001'
AND json_extract(data, '$.basicAuthUser') = 'ilia'`,
`INSERT INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
SELECT
'ilia-dobkin-keyword-set',
'demo-sdet-keyword-set',
'685b0000-0000-4000-8000-000000000001',
'SDET',
'${sqlJsonLiteral(ILIA_DOBKIN_CA_KEYWORD_TERMS)}',
'${sqlJsonLiteral(SDET_CA_KEYWORD_TERMS)}',
1,
datetime('now'),
datetime('now')
@@ -1020,7 +1016,7 @@ const migrations = [
)`,
`INSERT OR IGNORE INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
VALUES (
'ilia-dobkin-caseware',
'demo-caseware',
'685b0000-0000-4000-8000-000000000001',
'Caseware',
'["Caseware","Caseware template developer","Caseware template specialist","Caseware engagement template","Caseware working papers","Caseware Assurance","Caseware consultant","CaseWare"]',
@@ -1028,13 +1024,9 @@ const migrations = [
datetime('now'),
datetime('now')
)`,
`UPDATE keyword_sets
SET terms = '["Caseware","Caseware template developer","Caseware template specialist","Caseware engagement template","Caseware working papers","Caseware Assurance","Caseware consultant","CaseWare"]',
updated_at = datetime('now')
WHERE id = 'ilia-dobkin-caseware'`,
`INSERT OR IGNORE INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
VALUES (
'ilia-dobkin-lead',
'demo-lead',
'685b0000-0000-4000-8000-000000000001',
'Lead',
'["lead SDET","lead QA","test lead","QA manager","engineering manager test"]',
@@ -1045,7 +1037,7 @@ const migrations = [
`ALTER TABLE jobs ADD COLUMN sponsorship_signals TEXT`,
sqlInsertSearchProfileSeed({
id: "685b0000-0000-4000-8000-000000000002",
name: "Iliya Cherepaha",
name: "Demo User (QA)",
profile: {
targetRoles: [
"Senior QA Analyst",
@@ -1080,26 +1072,16 @@ const migrations = [
],
industriesToAvoid: [],
aboutMe:
"Iliya Cherepaha — Senior QA (Guidewire, OPS, accessibility); paired login user `cherepaha` activates this profile and local resume cherepaha.json (unset JOBOPS_LOCAL_RESUME_PATH so Settings path applies).",
basicAuthUser: "cherepaha",
resumeLocalPath: "../data/resumes/cherepaha.json",
"Demo User (QA) — Senior QA (Guidewire, accessibility, UAT); paired login user `demo-qa` activates this profile and local resume demo-qa.json (unset JOBOPS_LOCAL_RESUME_PATH so Settings path applies).",
basicAuthUser: "demo-qa",
resumeLocalPath: "../data/resumes/demo-qa.json",
},
}),
`UPDATE search_profiles
SET data = json_set(data, '$.resumeLocalPath', '../data/resumes/ilia-dobkin.json'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000001'
AND coalesce(json_extract(data, '$.resumeLocalPath'), '') = ''`,
`UPDATE search_profiles
SET data = json_set(data, '$.resumeLocalPath', '../data/resumes/cherepaha.json'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000002'
AND coalesce(json_extract(data, '$.resumeLocalPath'), '') = ''`,
sqlInsertSearchProfileSeed({
id: "685b0000-0000-4000-8000-000000000003",
name: "Ilia Dobkin (US)",
name: "Demo User (US)",
profile: {
targetRoles: ILIA_DOBKIN_US_TARGET_ROLES,
targetRoles: SDET_US_TARGET_ROLES,
experienceLevel: "Senior",
mustHaveSkills: [
"Playwright",
@@ -1130,46 +1112,22 @@ const migrations = [
],
industriesToAvoid: [],
aboutMe:
"Ilia Dobkin (US) — Senior SDET / QA automation; Canadian citizen, TN-eligible (USMCA), open to U.S. relocation. Computer Systems Analyst-aligned framing. Switch to this profile in the Jobs page picker (same login as Canada).",
basicAuthUser: "ilia,dobkin",
resumeLocalPath: "../data/resumes/ilia-dobkin-us.json",
"Demo User (US) — Senior SDET / QA automation targeting U.S. remote roles. Computer Systems Analyst-aligned framing. Switch to this profile in the Jobs page picker (same login as Canada).",
basicAuthUser: "demo,demo-user",
resumeLocalPath: "../data/resumes/demo-user-us.json",
},
}),
`UPDATE search_profiles
SET data = json_set(data, '$.targetRoles', '${sqlJsonLiteral(ILIA_DOBKIN_CA_TARGET_ROLES)}'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000001'`,
`UPDATE keyword_sets
SET terms = '${sqlJsonLiteral(ILIA_DOBKIN_CA_KEYWORD_TERMS)}',
updated_at = datetime('now')
WHERE id = 'ilia-dobkin-keyword-set'`,
`INSERT OR IGNORE INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
VALUES (
'ilia-dobkin-us-keyword-set',
'demo-sdet-us-keyword-set',
'685b0000-0000-4000-8000-000000000003',
'SDET',
'${sqlJsonLiteral(ILIA_DOBKIN_US_KEYWORD_TERMS)}',
'${sqlJsonLiteral(SDET_US_KEYWORD_TERMS)}',
1,
datetime('now'),
datetime('now')
)`,
`UPDATE search_profiles
SET data = json_set(
json_set(data, '$.basicAuthUser', 'ilia,dobkin'),
'$.aboutMe',
'Ilia Dobkin (US) — Senior SDET / QA automation; Canadian citizen, TN-eligible (USMCA), open to U.S. relocation. Computer Systems Analyst-aligned framing. Switch to this profile in the Jobs page picker (same login as Canada).'
),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000003'`,
`UPDATE search_profiles
SET data = json_set(data, '$.targetRoles', '${sqlJsonLiteral(ILIA_DOBKIN_US_TARGET_ROLES)}'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000003'`,
`UPDATE keyword_sets
SET terms = '${sqlJsonLiteral(ILIA_DOBKIN_US_KEYWORD_TERMS)}',
updated_at = datetime('now')
WHERE id = 'ilia-dobkin-us-keyword-set'`,
// Remove duplicate Ilia profiles left by repository tests (same names, test UUIDs).
// Remove duplicate seed profiles left by repository tests (same names, test UUIDs).
`DELETE FROM keyword_sets
WHERE owner_profile_id IN (
'685b0000-0000-4000-8000-000000009901',
@@ -1183,9 +1141,9 @@ const migrations = [
// AI engineering profile — agents, MCP, RAG, evals (paired with same login as Canada/US).
sqlInsertSearchProfileSeed({
id: "685b0000-0000-4000-8000-000000000004",
name: "Ilia Dobkin (AI)",
name: "Demo User (AI)",
profile: {
targetRoles: ILIA_DOBKIN_AI_TARGET_ROLES,
targetRoles: AI_ENGINEER_TARGET_ROLES,
experienceLevel: "Senior",
mustHaveSkills: [
"Python",
@@ -1226,59 +1184,33 @@ const migrations = [
],
industriesToAvoid: [],
aboutMe:
"Ilia Dobkin (AI) — AI Automation Engineer / LLM agents, MCP, RAG & evals; AI enablement lead experience. Switch to this profile for AI-role discovery (same login as Canada/US). Resume: ilia-dobkin-ai.json.",
basicAuthUser: "ilia,dobkin",
resumeLocalPath: "../data/resumes/ilia-dobkin-ai.json",
"Demo User (AI) — AI Automation Engineer / LLM agents, MCP, RAG & evals; AI enablement lead experience. Switch to this profile for AI-role discovery (same login as Canada/US). Resume: demo-user-ai.json.",
basicAuthUser: "demo,demo-user",
resumeLocalPath: "../data/resumes/demo-user-ai.json",
},
}),
`INSERT OR IGNORE INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
VALUES (
'ilia-dobkin-ai-keyword-set',
'demo-ai-keyword-set',
'685b0000-0000-4000-8000-000000000004',
'AI Engineer',
'${sqlJsonLiteral(ILIA_DOBKIN_AI_KEYWORD_TERMS)}',
'${sqlJsonLiteral(AI_ENGINEER_KEYWORD_TERMS)}',
1,
datetime('now'),
datetime('now')
)`,
`UPDATE search_profiles
SET data = json_set(
json_set(
json_set(data, '$.basicAuthUser', 'ilia,dobkin'),
'$.resumeLocalPath',
'../data/resumes/ilia-dobkin-ai.json'
),
'$.aboutMe',
'Ilia Dobkin (AI) — AI Automation Engineer / LLM agents, MCP, RAG & evals; AI enablement lead experience. Switch to this profile for AI-role discovery (same login as Canada/US). Resume: ilia-dobkin-ai.json.'
),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000004'`,
`UPDATE search_profiles
SET data = json_set(data, '$.targetRoles', '${sqlJsonLiteral(ILIA_DOBKIN_AI_TARGET_ROLES)}'),
updated_at = datetime('now')
WHERE id = '685b0000-0000-4000-8000-000000000004'`,
`UPDATE keyword_sets
SET terms = '${sqlJsonLiteral(ILIA_DOBKIN_AI_KEYWORD_TERMS)}',
name = 'AI Engineer',
is_active = 1,
updated_at = datetime('now')
WHERE id = 'ilia-dobkin-ai-keyword-set'`,
// Also add AI Engineer as an inactive keyword set on the Canada SDET profile
// (same pattern as Caseware / Lead) so it can be flipped without switching profiles.
`INSERT OR IGNORE INTO keyword_sets (id, owner_profile_id, name, terms, is_active, created_at, updated_at)
VALUES (
'ilia-dobkin-ai',
'demo-ai',
'685b0000-0000-4000-8000-000000000001',
'AI Engineer',
'${sqlJsonLiteral(ILIA_DOBKIN_AI_KEYWORD_TERMS)}',
'${sqlJsonLiteral(AI_ENGINEER_KEYWORD_TERMS)}',
0,
datetime('now'),
datetime('now')
)`,
`UPDATE keyword_sets
SET terms = '${sqlJsonLiteral(ILIA_DOBKIN_AI_KEYWORD_TERMS)}',
updated_at = datetime('now')
WHERE id = 'ilia-dobkin-ai'`,
];
console.log("🔧 Running database migrations...");
@@ -45,7 +45,7 @@ describe("profiles repository", () => {
data: JSON.stringify({
...sharedProfileData,
preferredLocations: ["Canada"],
resumeLocalPath: "../data/resumes/ilia-dobkin.json",
resumeLocalPath: "../data/resumes/demo-user.json",
}),
},
{
@@ -54,16 +54,20 @@ describe("profiles repository", () => {
data: JSON.stringify({
...sharedProfileData,
preferredLocations: ["United States"],
resumeLocalPath: "../data/resumes/ilia-dobkin-us.json",
resumeLocalPath: "../data/resumes/demo-user-us.json",
}),
},
]);
});
it("matches comma-separated basic auth aliases", () => {
expect(profileMatchesBasicAuthUser("ilia,dobkin", "ILIA")).toBe(true);
expect(profileMatchesBasicAuthUser("ilia,dobkin", "dobkin")).toBe(true);
expect(profileMatchesBasicAuthUser("ilia,dobkin", "cherepaha")).toBe(false);
expect(profileMatchesBasicAuthUser("demo,demo-user", "DEMO")).toBe(true);
expect(profileMatchesBasicAuthUser("demo,demo-user", "demo-user")).toBe(
true,
);
expect(profileMatchesBasicAuthUser("demo,demo-user", "someone-else")).toBe(
false,
);
});
it("lists every profile visible to the login", async () => {
@@ -152,7 +152,7 @@ function basicAuthAliases(basicAuthUser: string | null | undefined): string[] {
/**
* When one login maps to multiple search profiles, remember which profile is active.
* Writes the same profile id for every alias on that profile (e.g. ilia + dobkin).
* Writes the same profile id for every alias on that profile (e.g. demo + demo-user).
*/
export async function setActiveSearchProfileIdForBasicAuthUser(
username: string,