{"record":{"id":"1d47f0852a5cc805","repo":"santifer/career-ops","slug":"web-src-lib-career-ops-ts-not-found-web-layer-moved-update","errorCode":null,"errorMessage":"web/src/lib/career-ops.ts not found — web layer moved? update contract freeze section","messagePattern":"web/src/lib/career-ops\\.ts not found — web layer moved\\? update contract freeze section","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"test-all.mjs","lineNumber":16227,"sourceCode":"    fail(\n      'batch/batch-prompt.md broke the Block B two-pass ordering (#2330): ' +\n      `hoistsCvRead=${hoistsCvRead} defersCvRead=${defersCvRead} namesLoadPoint=${namesLoadPoint} cvRowDefers=${cvRowDefers}`,\n    );\n  }\n\n  // 55.5 cross-check: the web parser still speaks the same column names\n  const webParserPath = join(ROOT, 'web', 'src', 'lib', 'career-ops.ts');\n  if (existsSync(webParserPath)) {\n    const webSrc = readFileSync(webParserPath, 'utf-8');\n    const ESSENTIAL_COLS = ['Company', 'Role', 'Score', 'Status'];\n    const missingCols = ESSENTIAL_COLS.filter((c) => !webSrc.toLowerCase().includes(c.toLowerCase()));\n    if (missingCols.length === 0) {\n      pass('web/src/lib/career-ops.ts still references the essential tracker columns');\n    } else {\n      fail(`web parser no longer references column(s): ${missingCols.join(', ')} — core and web drifted`);\n    }\n  } else {\n    warn('web/src/lib/career-ops.ts not found — web layer moved? update contract freeze section');\n  }\n\n  // 55.6 pdf mode must never hand the agent write access (#2185).\n  // The web's \"pdf\" agent tailors content and nothing else: it emits the CV\n  // through a <<cv-html>> envelope and the BACKEND writes every file. A write\n  // grant here would be unscoped, so a prompt injection in a posting or report\n  // (both enter that agent's context) could redirect it at cv.md.\n  //\n  // Asserted on VALUES — the built argv and the built prompt. FIVE source-text\n  // versions of this guard were defeated by rewriting route.ts around them (see\n  // web/src/lib/claude-invocation.mjs's header). The one structural rule left is\n  // that route.ts may not spell a tool flag itself, which is what stops an inline\n  // argv from hiding beside a legitimate claudeCliArgs() call.\n  //\n  // In the REQUIRED suite on purpose: web-ci.yml is informative-only, so asserting\n  // this only there would gate nothing. Importing is safe — these are\n  // dependency-free ESM modules and the root suite runs on Node >= 18.\n  const webLib = join(ROOT, 'web', 'src', 'lib');","sourceCodeStart":16209,"sourceCodeEnd":16245,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/test-all.mjs#L16209-L16245","documentation":"A warning emitted by the test-all.mjs contract-freeze suite when web/src/lib/career-ops.ts (the web layer's tracker-column parser) does not exist, so the check that it still references the essential tracker columns cannot run. The suite distinguishes absence of the whole web/ tree (expected for data-only installs, see #591) from web/ existing but this specific file being missing or moved. It indicates the web layer's file layout changed and the contract-freeze section is pointing at a stale path.","triggerScenarios":"Running `node test-all.mjs` (or the full suite) in a checkout where web/ exists but web/src/lib/career-ops.ts has been renamed, relocated (e.g. into web/src/lib/core/), or deleted, so the suite's existsSync check on that exact path fails.","commonSituations":"A refactor of the web layer moved the parser module; a contributor deleted the file believing it dead code; pulling a repo state where web/ was partially migrated; maintaining a fork where the web layer was restructured without updating the test's hardcoded path.","solutions":["Locate where the web tracker parser now lives (e.g. `fd career-ops web/src`) and update the path in test-all.mjs's contract-freeze section.","If the parser was intentionally removed, update the contract freeze section to guard the new module that owns the tracker-column references.","If web/ should not be in this checkout at all, remove the stale web/ directory so the suite takes the documented data-only-install skip path (#591) instead.","Re-run `node test-all.mjs` to confirm the check now passes or is intentionally skipped."],"exampleFix":"// before (test-all.mjs)\nconst parserPath = join(ROOT, 'web', 'src', 'lib', 'career-ops.ts');\n// after — parser moved into lib/core\nconst parserPath = join(ROOT, 'web', 'src', 'lib', 'core', 'career-ops.ts');","handlingStrategy":"validation","validationCode":"import { existsSync } from 'node:fs';\nconst p = join(ROOT, 'web', 'src', 'lib', 'career-ops.ts');\nif (existsSync(p)) runContractFreeze(p); else console.warn('parser path moved — locate before updating freeze');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize hardcoded web paths in one SYSTEM_PATHS-style constant shared by all freeze checks.","When moving web files, grep test-all.mjs for the old path before deleting it.","Prefer deriving the parser path from a package export or glob rather than a literal."],"tags":["file-not-found","test-suite","web-layer","contract-freeze"],"backgroundTag":"file-not-found","analyzedSha":"e7abd431fce9348a95261acac9e0c14779c35df8","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}