{"record":{"id":"dc04c8c68f9fad72","repo":"santifer/career-ops","slug":"additions-carry-a-url-but-this-tracker-has-no","errorCode":null,"errorMessage":"⚠️  Additions carry a URL but this tracker has no URL column — URL dedup is INACTIVE (fuzzy fallback). Add a `URL` header column to enable it.","messagePattern":"⚠️  Additions carry a URL but this tracker has no URL column — URL dedup is INACTIVE \\(fuzzy fallback\\)\\. Add a `URL` header column to enable it\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"merge-tracker.mjs","lineNumber":1378,"sourceCode":"  const content = readFileSync(join(ADDITIONS_DIR, file), 'utf-8');\n  const addition = parseTsvContent(content, file);\n  if (!addition) { skipped++; continue; }\n\n  // A via= tag can only be stored if the tracker has a Via column — warn\n  // instead of dropping the channel silently (#1596). Clear the value too:\n  // existing rows parse with via='' on this layout, so a set addition.via would\n  // make the cross-channel duplicate guard see a channel mismatch and add a\n  // second ? row instead of updating the same-agency re-blast.\n  if (addition.via && COLMAP.via == null) {\n    console.warn(`⚠️  ${file}: carries via=${addition.via} but the tracker has no Via column — value dropped. Add it with: node merge-tracker.mjs --migrate-via`);\n    addition.via = '';\n  }\n\n  // If additions carry a URL but the tracker has no URL\n  // column, deterministic Pass 0 cannot engage and dedup silently falls back to\n  // the fuzzy tiers. Warn once rather than degrade invisibly.\n  if (addition.url && COLMAP.url == null && !warnedNoUrlCol) {\n    console.warn('⚠️  Additions carry a URL but this tracker has no URL column — URL dedup is INACTIVE (fuzzy fallback). Add a `URL` header column to enable it.');\n    warnedNoUrlCol = true;\n  }\n\n  // Normalize the report link to be relative to the tracker file's directory.\n  // The TSV convention carries a root-relative `reports/...` link; rewrite it\n  // so it resolves correctly when clicked from applications.md (see #760).\n  addition.report = normalizeReportLink(addition.report);\n\n  // Derive the key from the linked report when the TSV did not carry one, so a\n  // row added through the normal nine-column flow is born WITH its key. This\n  // must run before the dedup below reads addition.url — deriving it afterwards\n  // would populate the column while leaving Pass 0 nothing to match on, which is\n  // the original bug with extra steps.\n  if (!addition.url) addition.url = resolveReportUrl(addition.report).url;\n\n  // Check for duplicate by:\n  // 0. Exact normalized posting URL (deterministic, authoritative)\n  // 1. Exact report number match","sourceCodeStart":1360,"sourceCodeEnd":1396,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/merge-tracker.mjs#L1360-L1396","documentation":"URL-based dedup (Pass 0) needs a URL column in the tracker header to store and compare normalized posting URLs. When additions carry URLs but the tracker lacks the column, merge-tracker.mjs cannot engage the deterministic tier and dedup silently degrades to fuzzy company+role matching — so it warns once (warnedNoUrlCol) instead of degrading invisibly.","triggerScenarios":"Any merge where addition.url is set (the TSV's url column or a trailing http(s):// field) and COLMAP.url is null — i.e. data/applications.md has no 'URL' header column.","commonSituations":"Tracker created before the URL column feature; a hand-maintained minimal tracker; an old template lacking the column — same-position reposts then dedup poorly.","solutions":["Add a 'URL' header column to data/applications.md (empty for old rows is fine) and re-run the merge.","Run node merge-tracker.mjs --backfill-urls to backfill URLs from existing tracker reports, then merge.","Accept fuzzy dedup if your pipeline has no duplicate URLs — the warning is informational.","Standardize batch workers to always emit the url column so dedup stays deterministic."],"exampleFix":"# before (tracker header)\n| # | Date | Company | Role | Score | Status | PDF | Report | Notes |\n# after\n| # | Date | Company | Role | Score | Status | PDF | Report | Notes | URL |","handlingStrategy":"validation","validationCode":"const trackerHeader = fs.readFileSync('data/applications.md','utf8').split('\\n')[0];\nif (addition.url && !/\\bURL\\b/i.test(trackerHeader)) console.warn('Add a `URL` header column to data/applications.md to enable URL dedup');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Include a URL column in the tracker header from day one.","Emit the url field in every batch TSV so deterministic Pass 0 dedup engages.","Run node merge-tracker.mjs --backfill-urls after adding the column to hydrate old rows.","Re-check the header after any manual tracker edit."],"tags":["dedup","tsv","degradation"],"backgroundTag":"feature-not-enabled","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"}