Skip to content

Latest commit

 

History

History
114 lines (63 loc) · 11.3 KB

FAQ.md

File metadata and controls

114 lines (63 loc) · 11.3 KB

Frequently Asked Questions

Common questions from the community, answered in one place. For setup details see docs/SETUP.md. For anything not covered here, ask in Discord or open a GitHub Discussion.


1. Skills aren't loading on Windows — symlink error on install

Windows does not create symlinks by default, so Git checks out the CLI skill entrypoints (.claude/skills/, .opencode/skills/, etc.) as plain pointer files instead of real symlinks. The installer and updater both detect this automatically: run node update-system.mjs apply --confirm (or npx @santifer/career-ops init on a fresh install) and the materializeSkillEntrypoints step will replace the pointer files with the full canonical skill content. From a linked Git worktree, apply makes this repair in your main checkout; run git merge main in the worktree to pick it up (new worktrees get it automatically). With CAREER_OPS_UPDATE_IN_WORKTREE=1 set, apply repairs only that worktree's branch instead, and main stays unrepaired. No manual mklink or Developer Mode changes are needed.

2. What is the difference between scan and scan:full?

npm run scan is the standard portal scanner — it reads the companies you have configured in portals.yml, hits their ATS APIs (Greenhouse, Ashby, Lever, PrevueAPS) directly, and consumes zero LLM tokens. Use it for your regular daily or weekly discovery run. npm run scan:full inverts the direction: instead of scanning only your curated list, it walks public ATS company directories. Pass --history-seeds when you also want it to derive board seeds from posting URLs already present in your local tracker and data/scan-history.tsv; those history files remain local and are read-only inputs. Fresh postings are still filtered through your title_filter / location_filter, so you catch roles from companies you haven't manually added to portals.yml. Run scan:full when you want broader discovery beyond your tracked list.

3. How do I avoid hitting token or rate limits during a batch run?

Pass --limit <N> to batch-runner.sh to cap the number of offers processed in a single run (e.g. ./batch/batch-runner.sh --limit 5) — this lets you inspect output quality before committing to a larger run. If a run stops part-way, do not start over. Run the same command again: the runner keeps each offer's state in batch/batch-state.tsv, skips offers that already completed, and picks up the rest, retrying a failed offer up to --max-retries (default 2). A run that hit a Claude session or usage limit is different: it marks the offer that hit the limit paused_rate_limit and stops scheduling new ones. After the limit resets, ./batch/batch-runner.sh --resume-paused re-runs only those paused offers, so follow it with a plain run for the offers that had not started yet.

4. Can I run career-ops on a cheaper or local model?

Yes — career-ops is fully AI-agnostic and works with any AI coding CLI or standalone script. See docs/RUNNING_ON_A_BUDGET.md for a full guide covering OpenCode, Qwen CLI, DeepSeek, OpenRouter, Ollama, and other local or low-cost providers, along with recommended model sizes and token-saving best practices.

For zero cost specifically, see docs/FREE_TIER.md: career-ops runs on Antigravity CLI's free tier with no API key and no paid subscription, within Google's daily caps.

And if you already pay for a plan but are being billed per token anyway, that is usually an ANTHROPIC_API_KEY in your environment taking precedence over your subscription: see the subscription section of the budget guide.

5. What does the "possible cross-listing" warning mean during a scan?

When the scanner shows a warning like:

⚠ Possible cross-listing: Acme Corp / Senior AI Engineer ↔ TalentBridge / Senior AI Engineer (similarity 0.96)

it means the job description text of two listings from different companies is nearly identical — typically because a recruitment agency has re-posted a direct employer's role with the employer name removed or replaced.

Why it matters: if you apply through both channels, both the agency and the employer will see your application independently. This is known as a double-submission and it can damage your relationship with the hiring team.

What to do:

  1. Read both listings and confirm one is a direct company post and the other is an agency re-post.
  2. Choose ONE channel to apply through. Applying direct is usually safer; applying via an agency can be useful if the agency has a relationship with the hiring manager.
  3. If the two listings turn out to be genuinely different roles that happen to share boilerplate text (e.g. a generic engineering role template), the warning is a false positive — you can ignore it and apply to both.

Technical note: the scanner computes a 64-bit SimHash fingerprint of each JD body and stores it in the 8th column of data/scan-history.tsv (jd_fingerprint). Fingerprints are computed locally from text already returned by the ATS API — no extra network request is made. Postings without a usable description never receive a fingerprint and are never flagged. See docs/SCRIPTS.md for the full column reference.

6. Can I use my own CV template?

Yes. Set cv.template (and/or cover_letter.template) in config/profile.yml to the kebab-case name of a template file in templates/ — a value of modern resolves to templates/cv-template.modern.html (cover letters use templates/cover-letter-template.<name>.html). Leave the field unset and career-ops falls back to the built-in default template (templates/cv-template.html). You can also pick a template per generation just by asking (e.g. "use the modern template"). See the commented cv.template / cover_letter.template fields in config/profile.example.yml for the full reference.

Why does career-ops refuse to use a number from my story bank?

Because a plausible number is not necessarily a verified one. Interview-prep documents are often drafted to match a particular job description, and their phrasing can later be absorbed into interview-prep/story-bank.md. Without a provenance check, a figure invented in one prep session can be repeated in the next, gradually turning into an apparent fact.

career-ops therefore uses two trust tiers:

  • Primary, user-authored sources such as cv.md, article-digest.md, config/profile.yml, modes/_profile.md, and writing-samples/ are the ground truth for factual claims.
  • Derived, accumulated sources such as the story bank and company-specific interview prep can supply narrative and phrasing, but a quantified claim must trace back to a primary source or carry a supported marker: **Provenance:** user-stated YYYY-MM-DD or **Provenance:** source: cv.md counts as verified; **Provenance:** derived-unverified, **Provenance:** user-cannot-confirm, and arbitrary values do not.

Audit the story bank locally, without an LLM call:

node story-provenance-check.mjs --summary

The checker classifies each numeric claim as existing, supportedByResume, derived-unverified, or user-cannot-confirm. It is read-only: for a flagged claim, verify it against a primary source, provide the correct figure, remove the number and keep the story as narrative, or say "I don't know." That last answer is intentionally first-class — a **Provenance:** user-cannot-confirm marker keeps the number from being treated as verified on a later scan, so an honest unknown is never laundered into a confident fact through repetition.

How do I stop a company from showing up in scans?

Copy templates/blacklist.example.md to data/blacklist.md, delete its example rows, and add one table row per company:

| Company | Since | Scope | Reason |
|---------|-------|-------|--------|
| Acme Corp | 2026-01-15 | company | what happened |

The scanner reads only table rows, so a plain list of names, one per line, filters nothing. Scope is company (match the company name, ignoring case and punctuation) or domain (put a hostname such as ibm.com in the Company column to match postings on jobs.ibm.com).

If a listed company is encountered, the scan reports that it was skipped (never silently). You can bypass the filter with --include-blacklisted if you want to audit matching postings.

See the Company blacklist section in docs/SCRIPTS.md for the full behavior and supported workflow.

What's the difference between Discarded and SKIP?

From templates/states.yml: Discarded is "discarded by candidate or offer closed" (you considered it and stopped), SKIP is "doesn't fit, don't apply" (never a candidate). They land in different dashboard groups, so they count differently in your funnel: SKIP is filtering, Discarded is dropping out.

Is there only one CV template?

No. templates/ has cv-template.html, cv-template.tex (LaTeX/Overleaf), cv-template.zh-minimal.html, resume-template.html, cover-letter-template.html, plus templates/sections/. See also "Can I use my own CV template?" above.

Why does Chrome open during some scans but not others?

Chromium only launches with --verify. A normal scan reads public ATS APIs and needs no browser; --verify checks a posting is genuinely still live, which needs a real page load. If it fails, the error asks you to run npx playwright install chromium.

Why does every company in a scan fail with fetch failed, or every posting come back uncertain?

Usually a DNS filter. Sandboxed agent environments, and some corporate or home DNS filters, answer 0.0.0.0 or :: for any host outside their allowlist. career-ops refuses to connect to a non-public address, so a scan lists each filtered company under Network errors as fetch failed, and with --verify a posting whose own host is filtered reads uncertain (dns_sinkhole) and is kept (see liveness). To check, resolve a board host: if node -e "require('dns').lookup('boards-api.greenhouse.io', console.log)" prints 0.0.0.0 or ::, the lookup is being filtered. Run the scan from a normal shell, or allow the board hosts in your sandbox or filter.

Can I run career-ops in Docker / self-hosted?

Yes: there's a Dockerfile and docker-compose.yml in the repo root. Note that career-ops is local-first and human-in-the-loop, so Docker packages the environment: it does not turn career-ops into a service that runs on its own or applies to jobs for you.

Does career-ops sync across devices?

No. Everything is files in your checkout, and there is no cloud component. People who want this put the directory in a synced folder.

How do I get an issue assigned to me?

Comment on it and we'll assign it (this is in CONTRIBUTING.md). A PR with no prior issue is welcome for bug fixes, zero-auth scanner providers, docs and translations. Issue-first applies only to new features, new modes and architecture changes.

Can I use career-ops without a terminal?

Yes: see docs/COWORK.md which covers running it inside Claude Cowork, verified end to end. One step people trip on: Cowork's shell has no npm network access, so clone and npm install in a terminal before opening the folder.