santifer/career-ops · warning

web/ not present in this checkout — skipping the pdf…

Error message

web/ not present in this checkout — skipping the pdf write-scope freeze (#2185)

What it means

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.

Solutions

  1. If the data-only install is intentional, do nothing — the warning documents an expected skip.
  2. If the pdf write-scope freeze must run, clone/copy the web/ directory into the repo root so web/src/lib exists.
  3. In CI, ensure the checkout step includes web/ (no sparse-checkout exclusion) when web tests should run.
  4. If web/ is permanently gone from your fork, delete or gate the freeze section so the log isn't noisy.

Example fix

// CI before
- uses: actions/checkout@v4
  with:
    sparse-checkout: modes scripts
// after — include web so the freeze runs
- uses: actions/checkout@v4
Defensive patterns

Strategy: fallback

Validate before calling

const hasWeb = existsSync(join(ROOT, 'web', 'src', 'lib'));
const checks = hasWeb ? fullSuite : coreOnlySuite;

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@e7abd431fc (2026-09-16). Data as JSON: /api/errors/662c3f721d5c7c3a. Report an issue: GitHub.

Appendix: source

Thrown at test-all.mjs:16249

  // The web's "pdf" agent tailors content and nothing else: it emits the CV
  // through a <<cv-html>> envelope and the BACKEND writes every file. A write
  // grant here would be unscoped, so a prompt injection in a posting or report
  // (both enter that agent's context) could redirect it at cv.md.
  //
  // Asserted on VALUES — the built argv and the built prompt. FIVE source-text
  // versions of this guard were defeated by rewriting route.ts around them (see
  // web/src/lib/claude-invocation.mjs's header). The one structural rule left is
  // that route.ts may not spell a tool flag itself, which is what stops an inline
  // argv from hiding beside a legitimate claudeCliArgs() call.
  //
  // In the REQUIRED suite on purpose: web-ci.yml is informative-only, so asserting
  // this only there would gate nothing. Importing is safe — these are
  // dependency-free ESM modules and the root suite runs on Node >= 18.
  const webLib = join(ROOT, 'web', 'src', 'lib');
  const runRoutePath = join(ROOT, 'web', 'src', 'app', 'api', 'run', 'route.ts');
  if (!existsSync(webLib)) {
    // Expected for a data-only install: web/ is in no SYSTEM_PATHS entry.
    warn('web/ not present in this checkout — skipping the pdf write-scope freeze (#2185)');
  } else {
    // web/ IS here, so a missing file means a move, not an absence — fail rather
    // than skip, because a skip is how this freeze would silently stop guarding.
    const required = {
      'claude-invocation.mjs': join(webLib, 'claude-invocation.mjs'),
      'worker-capabilities.mjs': join(webLib, 'worker-capabilities.mjs'),
      'cv-envelope.mjs': join(webLib, 'cv-envelope.mjs'),
      'run-prompts.mjs': join(webLib, 'run-prompts.mjs'),
      'api/run/route.ts': runRoutePath,
    };
    const missing = Object.entries(required).filter(([, f]) => !existsSync(f)).map(([name]) => name);
    if (missing.length > 0) {
      fail(`web/ exists but ${missing.join(', ')} is missing — the #2185 write-scope freeze cannot verify (was it moved?)`);
    } else {
      let invocation;
      let capabilities;
      let prompts;
      try {

View on GitHub (pinned to e7abd431fc)