{"record":{"id":"2140ace8d53c317b","repo":"santifer/career-ops","slug":"payload-cv-requires-section-entry","errorCode":null,"errorMessage":"payload.cv requires { section, entry }","messagePattern":"payload\\.cv requires (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"add-entry.mjs","lineNumber":156,"sourceCode":"}\n\n/**\n * Pure core: given the current file contents and a payload, compute the new\n * contents and a per-target status. No I/O — this is what the tests exercise.\n * @returns {{ cv: string, articleDigest: string, result: object }}\n */\nexport function applyAdd(payload, { cvText = null, articleText = null } = {}) {\n  if (!payload || typeof payload !== 'object' || (!payload.cv && !payload.articleDigest)) {\n    throw new Error('payload must include at least one of: cv, articleDigest');\n  }\n\n  const result = {};\n  let cv = cvText;\n  let articleDigest = articleText;\n\n  if (payload.cv) {\n    const { section, dedupKey, entry } = payload.cv;\n    if (!section || !entry) throw new Error('payload.cv requires { section, entry }');\n    // dedupKey is what makes the insert idempotent — refuse to add without one\n    // rather than silently allowing duplicate re-runs.\n    if (!normalizeKey(dedupKey)) throw new Error('payload.cv requires a non-empty dedupKey (used for dedup/idempotency)');\n    if (cvText === null) throw new Error(`cv.md not found — cannot add to a CV that does not exist`);\n    if (cvHasEntry(cvText, section, dedupKey)) {\n      result.cv = { status: 'duplicate', section };\n    } else {\n      cv = insertIntoCvSection(cvText, section, entry);\n      result.cv = { status: 'added', section };\n    }\n  }\n\n  if (payload.articleDigest) {\n    const { dedupKey, entry } = payload.articleDigest;\n    if (!entry) throw new Error('payload.articleDigest requires { entry }');\n    if (!normalizeKey(dedupKey)) throw new Error('payload.articleDigest requires a non-empty dedupKey (used for dedup/idempotency)');\n    // article-digest.md is optional; create it from a header when missing.\n    const current = articleText === null","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/add-entry.mjs#L138-L174","documentation":"Per-target validation inside `applyAdd` for the `cv` payload: when `payload.cv` is provided, it must contain both `section` and `entry`. Thrown before any dedup or I/O, because without a target section and content there is nothing to insert into `cv.md`.","triggerScenarios":"Calling `applyAdd({ cv: { dedupKey: 'x', entry: '...' } })` (missing `section`); `applyAdd({ cv: { section: 'Skills' } })` (missing `entry`); `applyAdd({ cv: {} })`; or any cv sub-object where one of the two is an empty/falsy value.","commonSituations":"Caller builds the cv object dynamically and forgets the section key; entry text is generated conditionally and comes back empty; schema drift between the agent's mental model and the function; passing the whole entry string as `section` by mistake.","solutions":["Ensure `payload.cv` includes both `section` (e.g. 'Experience', 'Projects', 'Skills') and a non-empty `entry` string.","Add a caller-side assert: `if (!p.cv.section || !p.cv.entry) throw ...` with a clearer message before calling applyAdd.","Double-check the section name matches an existing heading in `cv.md` so `insertIntoCvSection` can find it.","Generate the entry string eagerly — never pass a value that may be conditionally empty.","Cover the missing-section and missing-entry cases in unit tests."],"exampleFix":"// before\napplyAdd({ cv: { dedupKey: 'k', entry: '- Built X' } }); // throws — no section\n// after\napplyAdd({ cv: { section: 'Projects', dedupKey: 'x', entry: '- Built X (2024)' } });","handlingStrategy":"validation","validationCode":"function validateCvPayload(cv) {\n  if (!cv || typeof cv !== 'object') return false;\n  return typeof cv.section === 'string' && cv.section.trim() !== '' &&\n         typeof cv.entry === 'string' && cv.entry.trim() !== '';\n}\nif (payload.cv && !validateCvPayload(payload.cv)) {\n  throw new Error('payload.cv needs non-empty { section, entry }');\n}","typeGuard":"function isCvEntry(c) {\n  return !!c && typeof c === 'object' &&\n    typeof c.section === 'string' && c.section.trim() !== '' &&\n    typeof c.entry === 'string' && c.entry.trim() !== '';\n}","tryCatchPattern":null,"preventionTips":["Build the cv object with a typed helper that requires section + entry.","Confirm the section name matches a real heading in cv.md so insertion succeeds.","Generate entry text eagerly — never pass possibly-empty strings.","Add tests covering missing-section and missing-entry."],"tags":["validation","schema","add-entry","cv"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}