{"record":{"id":"55d11e87885c88b3","repo":"santifer/career-ops","slug":"from-from-must-not-be-after-to-to","errorCode":null,"errorMessage":"--from (${from}) must not be after --to (${to}).","messagePattern":"--from \\((.+?)\\) must not be after --to \\((.+?)\\)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"weekly-digest.mjs","lineNumber":303,"sourceCode":"  from,\n  to,\n  sessionsDir = DEFAULT_SESSIONS_DIR,\n  questionBankPath = DEFAULT_QUESTION_BANK_PATH,\n} = {}) {\n  // Range resolution has three cases, not two:\n  //   - neither --from nor --to given -> default current-week range (unchanged)\n  //   - exactly one of --from/--to given -> ambiguous, hard error (was:\n  //     silently fell back to the default range, which quietly discarded\n  //     the one bound the caller did supply)\n  //   - both given but from > to -> hard error (was: silently returned an\n  //     empty digest, indistinguishable from \"no interviews this week\")\n  let range;\n  if (from === undefined && to === undefined) {\n    range = computeDefaultRange();\n  } else if (from === undefined || to === undefined) {\n    throw new Error('--from and --to must both be supplied together (or neither, to use the default current-week range).');\n  } else if (from > to) {\n    throw new Error(`--from (${from}) must not be after --to (${to}).`);\n  } else {\n    range = { from, to };\n  }\n\n  const allSessions = loadSessions(sessionsDir);\n  const sessionsInRange = allSessions.filter((s) => inRange(s.date, range.from, range.to));\n\n  const companyNames = [...new Set(sessionsInRange.map((s) => s.company))];\n  // \"File exists\" and \"file has usable content\" are independent questions —\n  // an existing-but-empty question-bank.md is a different state than a\n  // missing one, and the metadata (and printSummary's \"present but\n  // unmatched\" branch) needs to be able to tell them apart.\n  const questionBankFound = existsSync(questionBankPath);\n  // existsSync() succeeding doesn't guarantee readFileSync() will: the path\n  // could be a directory, permissions could block the read, or the file\n  // could be deleted between the two calls (TOCTOU). Any of those is an\n  // optional-data problem, not a reason to abort the whole digest — degrade\n  // to \"no usable content\" the same way a missing file does, but keep","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/weekly-digest.mjs#L285-L321","documentation":"weekly-digest.mjs rejects an inverted explicit range: when both --from and --to are supplied but from > to, it throws instead of silently returning an empty digest (which was previously indistinguishable from 'no interviews this week'). Dates compare lexicographically as ISO YYYY-MM-DD strings, which is correct only when the strict format is used.","triggerScenarios":"`node weekly-digest.mjs --from 2026-08-20 --to 2026-08-10`. Also triggered by mixed formats (e.g. --to 08/16/2026) because non-ISO strings compare unpredictably and can land in the from > to branch.","commonSituations":"Swapping start/end when copying a command; mixed locale date habits (MM/DD vs DD/MM); typing the newer date first intending 'between these two'.","solutions":["Swap the flags so --from is the earlier ISO date","Always use strict YYYY-MM-DD for both bounds","Re-run with the corrected pair; the digest is read-only so there is nothing to undo"],"exampleFix":"# before\nnode weekly-digest.mjs --from 2026-08-20 --to 2026-08-10\n# after\nnode weekly-digest.mjs --from 2026-08-10 --to 2026-08-20","handlingStrategy":"validation","validationCode":"if (from !== undefined && to !== undefined && from > to) {\n  [from, to] = [to, from]; // or hard-stop: throw new Error('inverted range')\n}\n// Also normalize: enforce /^\\d{4}-\\d{2}-\\d{2}$/ on both before comparing","typeGuard":"function isOrderedIsoRange(from, to) {\n  const ISO = /^\\d{4}-\\d{2}-\\d{2}$/;\n  return ISO.test(from) && ISO.test(to) && from <= to;\n}","tryCatchPattern":"try {\n  await runDigest({ from, to });\n} catch (err) {\n  if (/must not be after/.test(err.message)) {\n    console.error('inverted range — swap the two dates');\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Always pass strict YYYY-MM-DD strings; other formats compare unpredictably as strings","Convention: --from is the older date (range start)","The tool deliberately rejects instead of returning an empty digest — do not paper over with retries"],"tags":["cli","date-range","validation","weekly-digest"],"backgroundTag":"invalid-date-range","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}