santifer/career-ops · warning
cv.md not found — cannot add to a CV that does not exist
Error message
cv.md not found — cannot add to a CV that does not exist
What it means
Emitted by Check 11 of verify-pipeline.mjs. Each tracker row's Company cell is tested against CONFIDENTIAL_WORD_RE — confidential, vertraulich, confidentiel, confidencial, riservato, gizli, 機密, سري. A match means a locale-dependent confidentiality word was typed as the company name; the data contract requires the locale-invariant structural marker '?' instead, because such words can collide with a real firm name (a company actually called 'Confidential') and defeat exact string matching and dedup keys.
Source
Thrown at add-entry.mjs:181
* contents and a per-target status. No I/O — this is what the tests exercise.
* @returns {{ cv: string, articleDigest: string, result: object }}
*/
export function applyAdd(payload, { cvText = null, articleText = null } = {}) {
if (!payload || typeof payload !== 'object' || (!payload.cv && !payload.articleDigest)) {
throw new Error('payload must include at least one of: cv, articleDigest');
}
const result = {};
let cv = cvText;
let articleDigest = articleText;
if (payload.cv) {
const { section, dedupKey, entry } = payload.cv;
if (!section || !entry) throw new Error('payload.cv requires { section, entry }');
// dedupKey is what makes the insert idempotent — refuse to add without one
// rather than silently allowing duplicate re-runs.
if (!normalizeKey(dedupKey)) throw new Error('payload.cv requires a non-empty dedupKey (used for dedup/idempotency)');
if (cvText === null) throw new Error(`cv.md not found — cannot add to a CV that does not exist`);
if (cvHasEntry(cvText, section, dedupKey)) {
result.cv = { status: 'duplicate', section };
} else {
cv = insertIntoCvSection(cvText, section, entry);
result.cv = { status: 'added', section };
}
}
if (payload.articleDigest) {
const { dedupKey, entry } = payload.articleDigest;
if (!entry) throw new Error('payload.articleDigest requires { entry }');
if (!normalizeKey(dedupKey)) throw new Error('payload.articleDigest requires a non-empty dedupKey (used for dedup/idempotency)');
// article-digest.md is optional; create it from a header when missing.
const current = articleText === null
? '# Article Digest -- Proof Points\n\nCompact proof points from portfolio projects. Read by career-ops at evaluation time.\n'
: articleText;
if (articleDigestHasEntry(current, dedupKey)) {
result.articleDigest = { status: 'duplicate' };View on GitHub (pinned to 60398d6549)
Solutions
- Replace the Company cell with the structural marker '?'
- Record the agency in the Via column — add it first with `node merge-tracker.mjs --migrate-via` if the tracker lacks it; '?' with no Via channel is the error-level sibling finding
- Once the end employer becomes known, replace '?' with the real company name
Example fix
// before: | 17 | 2026-02-10 | Confidential | Backend Engineer | Applied | ... | // after: | 17 | 2026-02-10 | ? | Backend Engineer | Applied | ... | via=Hays recorded in Via |
Defensive patterns
Strategy: type-guard
Validate before calling
// Run BEFORE writing a Company cell: reject locale-dependent placeholder words.
const CONFIDENTIAL_WORD_RE = /^(confidential|vertraulich|confidentiel|confidencial|riservato|gizli|機密|سري)$/i;
export function validCompanyCell(company) {
const c = String(company ?? '').trim();
return c !== '' && !CONFIDENTIAL_WORD_RE.test(c);
} Type guard
const CONFIDENTIAL_WORD_RE = /^(confidential|vertraulich|confidentiel|confidencial|riservato|gizli|機密|سري)$/i; // Structural unknown-employer marker: locale-invariant, cannot collide with a real firm. const isStructuralUnknownEmployer = (company) => String(company ?? '').trim() === '?'; const isSafeCompanyCell = (company) => isStructuralUnknownEmployer(company) || !CONFIDENTIAL_WORD_RE.test(String(company ?? '').trim());
Prevention
- Use '?' the moment an agency hides the end client, not the word the email uses
- Pair every '?' with a Via channel so the row passes the error-level sibling check
- When localizing the tracker for DACH/JP/AR markets, remember the regex covers those placeholders too — the marker stays '?' in every locale
- Add the guard above to any script or workflow that appends tracker rows
When it happens
Trigger: Adding a tracker row by hand from an agency email that says 'Confidential client' and typing that into the Company cell; importing rows localized for the DACH ('Vertraulich'), Japanese ('機密'), or Arabic ('سري') market mode sets.
Common situations: Agency-mediated applications where the end employer is hidden; localizing the tracker into another market's vocabulary; copy-pasting the subject line of a blind recruiter email.
Related errors
- payload must include at least one of: cv, articleDigest
- payload.cv requires { section, entry }
- payload.cv requires a non-empty dedupKey (used for dedup/ide
- payload.articleDigest requires { entry }
- INVALID_DATE
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/79421732be2e3a22.
Report an issue: GitHub.