{"record":{"id":"662c3f721d5c7c3a","repo":"santifer/career-ops","slug":"web-not-present-in-this-checkout-skipping-the-pdf-write","errorCode":null,"errorMessage":"web/ not present in this checkout — skipping the pdf write-scope freeze (#2185)","messagePattern":"web/ not present in this checkout — skipping the pdf write-scope freeze \\(#2185\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"test-all.mjs","lineNumber":16249,"sourceCode":"  // 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');\n  const runRoutePath = join(ROOT, 'web', 'src', 'app', 'api', 'run', 'route.ts');\n  if (!existsSync(webLib)) {\n    // Expected for a data-only install: web/ is in no SYSTEM_PATHS entry.\n    warn('web/ not present in this checkout — skipping the pdf write-scope freeze (#2185)');\n  } else {\n    // web/ IS here, so a missing file means a move, not an absence — fail rather\n    // than skip, because a skip is how this freeze would silently stop guarding.\n    const required = {\n      'claude-invocation.mjs': join(webLib, 'claude-invocation.mjs'),\n      'worker-capabilities.mjs': join(webLib, 'worker-capabilities.mjs'),\n      'cv-envelope.mjs': join(webLib, 'cv-envelope.mjs'),\n      'run-prompts.mjs': join(webLib, 'run-prompts.mjs'),\n      'api/run/route.ts': runRoutePath,\n    };\n    const missing = Object.entries(required).filter(([, f]) => !existsSync(f)).map(([name]) => name);\n    if (missing.length > 0) {\n      fail(`web/ exists but ${missing.join(', ')} is missing — the #2185 write-scope freeze cannot verify (was it moved?)`);\n    } else {\n      let invocation;\n      let capabilities;\n      let prompts;\n      try {","sourceCodeStart":16231,"sourceCodeEnd":16267,"githubUrl":"https://github.com/santifer/career-ops/blob/e7abd431fce9348a95261acac9e0c14779c35df8/test-all.mjs#L16231-L16267","documentation":"A warning from test-all.mjs's pdf write-scope freeze check (#2185) when the entire web/ directory is absent from the checkout. The freeze verifies that web/src/lib/worker-capabilities.mjs never grants the web 'pdf' agent write access (the backend writes all files). Without web/, the check is intentionally skipped — expected for a data-only install where web/ is in no SYSTEM_PATHS entry. It is only a problem if you expected the web layer to be present.","triggerScenarios":"Running the root test suite on a data-only install (no web/ directory cloned); running it from a shallow or partial checkout; running it after deleting web/; CI jobs that checkout only core paths.","commonSituations":"Deploying career-ops as a data-only workspace without the web UI; sparse git checkouts; contributors running tests before cloning submodules/web assets; a cleanup step removing web/ from a server install.","solutions":["If the data-only install is intentional, do nothing — the warning documents an expected skip.","If the pdf write-scope freeze must run, clone/copy the web/ directory into the repo root so web/src/lib exists.","In CI, ensure the checkout step includes web/ (no sparse-checkout exclusion) when web tests should run.","If web/ is permanently gone from your fork, delete or gate the freeze section so the log isn't noisy."],"exampleFix":"// CI before\n- uses: actions/checkout@v4\n  with:\n    sparse-checkout: modes scripts\n// after — include web so the freeze runs\n- uses: actions/checkout@v4","handlingStrategy":"fallback","validationCode":"const hasWeb = existsSync(join(ROOT, 'web', 'src', 'lib'));\nconst checks = hasWeb ? fullSuite : coreOnlySuite;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Document data-only vs full installs in CI and gate web checks on a hasWeb flag.","Ensure CI checkout includes web/ when web tests are required.","Treat this warning as informational noise on data-only installs rather than a failure."],"tags":["missing-directory","test-suite","optional-dependency","skip"],"backgroundTag":"missing-dependency","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"}