{"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":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"add-entry.mjs","lineNumber":180,"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":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/add-entry.mjs#L162-L198","documentation":"When payload.cv is present, applyAdd destructures { section, dedupKey, entry } from it and requires at minimum a non-empty `section` (which cv.md section to insert into) and an `entry` (the markdown content to add). A truthy payload.cv missing either means the caller shaped the CV part of the payload incorrectly, so there is no well-defined insertion to perform.","triggerScenarios":"applyAdd({ cv: { section: 'Projects' } }) (no entry), applyAdd({ cv: { entry: '...' } }) (no section), or applyAdd({ cv: { section: '', entry: '...' } }) — truthy payload.cv but empty/absent section or entry.","commonSituations":"Hand-writing the payload and forgetting a field, constructing { cv: 'my entry text' } (a string instead of the required object), or a refactor that moved `section`/`entry` to a different nesting level.","solutions":["Provide both fields: applyAdd({ cv: { section: 'Projects', dedupKey: '...', entry: '- ...' } }).","If passing a raw string, wrap it: { cv: { section: <targetHeading>, dedupKey: <uniqueKey>, entry: <markdownText> } }.","Pick a section name that exactly matches an existing heading in cv.md so insertIntoCvSection can find it."],"exampleFix":"// before\nawait applyAdd({ cv: 'Built cv-santiago portfolio' }, { cvText });\n\n// after\nawait applyAdd(\n  { cv: { section: 'Projects', dedupKey: 'proj:cv-santiago', entry: '- **cv-santiago** — open-source portfolio' } },\n  { cvText }\n);","handlingStrategy":"validation","validationCode":"function isValidCvPayload(cv) {\n  return Boolean(cv && cv.section && cv.entry);\n}\nif (payload.cv && !isValidCvPayload(payload.cv)) throw new Error('cv payload needs { section, entry }');","typeGuard":"function isCvTarget(cv) {\n  return typeof cv === 'object' && cv !== null && typeof cv.section === 'string' && cv.section.length > 0 && typeof cv.entry === 'string' && cv.entry.length > 0;\n}","tryCatchPattern":"try {\n  applyAdd({ cv: cvTarget }, { cvText });\n} catch (e) {\n  if (e.message.includes('payload.cv requires')) {\n    console.error(`Malformed cv payload: ${JSON.stringify(cvTarget)}`);\n  } else throw e;\n}","preventionTips":["Wrap payload construction in a helper enforcing the { section, dedupKey, entry } shape.","Validate with a JSON schema or zod schema before calling applyAdd.","Keep the target section name copied from an actual cv.md heading."],"tags":["validation","schema","payload-shape"],"backgroundTag":"schema-validation-failed","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}