{"record":{"id":"bf1734712550e56b","repo":"santifer/career-ops","slug":"name-must-be-a-percentage-e-g-70-or-70-g","errorCode":null,"errorMessage":"--${name} must be a percentage (e.g. 70 or 70%), got \"${v}\"","messagePattern":"--(.+?) must be a percentage \\(e\\.g\\. 70 or 70%\\), got \"(.+?)\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assessment-log.mjs","lineNumber":138,"sourceCode":"}\n\n// --- Append (`add` subcommand) ---\nexport function buildRow(fields, today) {\n  const req = (name) => {\n    const v = String(fields[name] ?? '').trim();\n    if (!v) throw new Error(`--${name} is required`);\n    if (v.includes('\\t') || v.includes('\\n')) throw new Error(`--${name} must not contain tabs or newlines`);\n    return v;\n  };\n  const opt = (name) => {\n    const v = String(fields[name] ?? '').trim();\n    if (v.includes('\\t') || v.includes('\\n')) throw new Error(`--${name} must not contain tabs or newlines`);\n    return v || '-';\n  };\n  const optPct = (name) => {\n    const v = String(fields[name] ?? '').trim();\n    if (!v) return '-';\n    if (parsePct(v) === null) throw new Error(`--${name} must be a percentage (e.g. 70 or 70%), got \"${v}\"`);\n    return v;\n  };\n  return [\n    today, req('company'), opt('report'), req('platform'), req('subject'),\n    optPct('threshold'), optPct('score'), opt('stale') === '-' ? '' : opt('stale'),\n  ].join('\\t');\n}\n\nfunction addEntry(args) {\n  const fields = {};\n  for (let i = 0; i < args.length; i++) {\n    const m = args[i].match(/^--(company|report|platform|subject|threshold|score|stale)$/);\n    if (m) { fields[m[1]] = args[i + 1] ?? ''; i++; }\n  }\n  // LOCAL day: this is the date written into the appended assessments.tsv row,\n  // and the UTC day stamped a user record with a day that had not happened yet\n  // for anyone west of Greenwich (#3070).\n  const today = localToday();","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/assessment-log.mjs#L120-L156","documentation":"optPct() in buildRow() validates the --threshold and --score fields, which must be percentages (70 or 70%). It delegates to parsePct(); when parsePct returns null (unparseable value), the error is thrown quoting the offending input. This keeps the TSV columns machine-parseable so assessment-log.mjs --summary can compute percentage statistics.","triggerScenarios":"Calling buildRow() with --threshold or --score set to a non-percentage string, e.g. `--threshold seventy`, `--score 70/100`, `--score \"70 %\"`, `--threshold 1.5x`, or any value parsePct() cannot interpret as a bare number or number-with-percent-sign.","commonSituations":"Entering a fraction (0.7) where the tool expects 70; writing '70 percent' in words; a score out of the platform's 0-100 convention like 'A' or 'pass'; locale-decimal input ('70,5'); typos ('7O' with letter O); passing other numeric scales (Codewars kyu, HackerRank stars) that are not percentages.","solutions":["Re-run with a valid percentage form: a bare number (70) or number with % (70%).","Convert non-percent scales first: fraction 0.7 → 70; 'A' grade → find the numeric percentage on the platform.","Use a decimal point, not a comma, if the value is fractional (check parsePct's accepted form).","Omit the flag entirely if no percentage exists — optPct() returns '-' when empty instead of throwing."],"exampleFix":"// before\nnode assessment-log.mjs add --company Acme --platform leetcode --subject dp --score 8/10\n// Error: --score must be a percentage (e.g. 70 or 70%), got \"8/10\"\n// after\nnode assessment-log.mjs add --company Acme --platform leetcode --subject dp --score 80%","handlingStrategy":"validation","validationCode":"const isPct = (v) => { const s = String(v ?? '').trim(); return /^\\d+(\\.\\d+)?%?$/.test(s); };\nif (fields.threshold != null && fields.threshold !== '' && !isPct(fields.threshold)) throw new Error(`--threshold must be a percentage, got \"${fields.threshold}\"`);\nif (fields.score != null && fields.score !== '' && !isPct(fields.score)) throw new Error(`--score must be a percentage, got \"${fields.score}\"`);","typeGuard":"const isPercentage = (v) => /^\\d+(\\.\\d+)?%?$/.test(String(v).trim());","tryCatchPattern":"try {\n  const row = buildRow(fields, today);\n} catch (err) {\n  const m = err.message.match(/^--(\\w+) must be a percentage .* got \"(.*)\"$/);\n  if (m) {\n    const [, flag, bad] = m;\n    console.error(`Convert ${flag} value \"${bad}\" to a number out of 100 (e.g. 70 or 70%).`);\n  } else throw err;\n}","preventionTips":["Always express assessment results on a 0-100 scale (70, not 0.7, not 8/10, not letter grades).","Use a decimal point, never a comma, for fractional percentages.","Omit --threshold/--score when no percentage exists rather than improvising a value.","Validate with a quick regex before scripting bulk `add` calls."],"tags":["cli","validation","parsing"],"backgroundTag":"invalid-argument-value","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"}