Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Run Node tests
run: npm test

- name: Verify strict US portal coverage
run: npm run verify:diversity -- --strict

- name: ISO source smoke checks
run: npm run smoke:iso

Expand Down
7 changes: 5 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ Before opening a PR, from the harness repo root:
```bash
npm run verify
npm run smoke:iso
npm test
npm run build:dashboard
```

(`npm run build:dashboard` is the same as `(cd dashboard && go build .)` — requires Go on PATH.)

`npm run verify` runs `verify-pipeline.mjs`. It exits successfully when no tracker exists (fresh clone).
`npm run verify` runs `verify-pipeline.mjs` followed by the offline US portal-policy and source-coverage check. The tracker portion exits successfully when no tracker exists (fresh clone).

> **Contributor note:** Inside the harness repo, scripts resolve PROJECT_DIR via `process.cwd()`, so running `npm run verify` from the repo root operates on your local `data/` directory (gitignored). If you have a personal project that you want to test changes against, use `JOB_FORGE_PROJECT=/path/to/personal/project npm run verify`.

Expand All @@ -58,12 +59,14 @@ Other npm scripts:
| `npm run lint:helpers` | `scripts/check-helper-integration.mjs` | Verifies local helper deps, CLI aliases, scaffolder scripts, migrations, generated ignores, templates, docs, and helper reference stay aligned |
| `npm run build:dashboard` | `go build` in `dashboard/` | Optional TUI; same check as manual `cd dashboard && go build .` |
| `npm run sync-check` | `cv-sync-check.mjs` | Optional setup lint: `cv.md`, `config/profile.yml`, hardcoded-metric scan; fails if those files are missing |
| `npm run verify:diversity` | `scripts/verify-portal-diversity.mjs` | Offline check for the US-only policy, US location queries, and US-tagged catalog coverage |
| `npm run balance:scan -- candidates.json portals.yml` | `scripts/balance-scan-results.mjs` | Deterministic US-eligibility and company/location-balanced shortlist JSON |
| `npm run normalize` | `normalize-statuses.mjs` | Maps non-canonical statuses in tracker files; no-op if no tracker exists |
| `npm run dedup` | `dedup-tracker.mjs` | Merges duplicate company/role rows; no-op if no tracker exists |
| `npm run merge` | `merge-tracker.mjs` | Merges `batch/tracker-additions/*.tsv` into the tracker (day-based or single-file); no-op if no TSVs exist; creates tracker when no tracker file exists yet |
| `npm run pdf` | `generate-pdf.mjs` | Pass HTML and PDF paths after `--`, e.g. `npm run pdf -- output/cv.html output/cv.pdf`; missing parent dirs for the PDF path are created |

Utility scripts `verify`, `merge`, `normalize`, `dedup`, `sync-check`, and `pdf` accept `--help` / `-h` (e.g. `npm run verify -- --help`, `npm run sync-check -- --help`, `npm run pdf -- --help`).
Utility scripts `verify`, `verify:diversity`, `balance:scan`, `merge`, `normalize`, `dedup`, `sync-check`, and `pdf` accept `--help` / `-h` (e.g. `npm run verify:diversity -- --help`, `npm run balance:scan -- --help`, `npm run pdf -- --help`).

You can run the same `.mjs` files with `node <script>.mjs` from the repo root if you prefer.

Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ Then edit the three personal files the scaffolder creates:
| File | What to put there |
|------|-------------------|
| `cv.md` | Your CV in markdown. This is the source for matching and PDF generation. |
| `config/profile.yml` | Your identity, target roles, location constraints, compensation, and proof points. |
| `portals.yml` | Companies, search queries, and title filters for portal scanning. |
| `config/profile.yml` | Your identity, target roles, US work eligibility and location constraints, compensation, and proof points. |
| `portals.yml` | Companies, search queries, title filters, and US coverage policy for portal scanning. |

Optional: add `article-digest.md` with portfolio links, case studies, or extra proof points.

Expand All @@ -62,7 +62,9 @@ JobForge is built for selective, high-fit applications. It is not intended for s

- Scores opportunities with a consistent weighted rubric.
- Generates tailored ATS-friendly CV PDFs.
- Scans configured company portals and job boards.
- Scans configured company portals and job boards, restricting discovery to postings that explicitly permit work in the United States.
- Balances eligible results across Northeast, Midwest, South, West, and nationwide/US-remote buckets while limiting per-company concentration.
- Blocks automatic outreach, form filling, and submission when US eligibility is ambiguous or absent.
- Tracks applications, follow-ups, rejections, offers, reports, and PDFs.
- Supports batch evaluation and application work through bounded subagents.
- Uses local helper CLIs for dedupe, scoring, lineage, preflight, postflight, receipts, and tracker integrity.
Expand Down
8 changes: 7 additions & 1 deletion batch/batch-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Read `{{URL}}`, `{{JD_FILE}}`, `{{REPORT_NUM}}`, `{{DATE}}`, `{{ID}}` from the u
| File | Absolute path | When |
|------|---------------|------|
| cv.md | `cv.md (project root)` | ALWAYS |
| profile.yml | `config/profile.yml` | ALWAYS (identity and location eligibility) |
| llms.txt | `llms.txt (if exists)` | ALWAYS |
| article-digest.md | `article-digest.md (project root)` | ALWAYS (proof points) |
| i18n.ts | `i18n.ts (if exists, optional)` | Only for interviews/deep |
Expand Down Expand Up @@ -72,6 +73,7 @@ Everywhere this prompt writes `{{URL}}`, `{{JD_FILE}}`, `{{REPORT_NUM}}`, `{{DAT
3. If the JD file is empty or missing, WebFetch `{{URL}}` **once**
4. If WebFetch returns content but it looks like a shell page (no JD text, just navbar/footer), add `**Verification: unconfirmed**` to the report header and proceed — the conductor or user will verify later
5. If both the file and WebFetch fail, report an error and stop
6. Extract the posting's location and remote hiring scope. Compare it with ISO country code `US` and the five US location bucket ids in `config/profile.yml`, then assign `location_status`: `eligible`, `exploratory`, `location_review`, or `ineligible`. `eligible` requires explicit evidence that the employer hires in the United States. Generic `Remote`, `Americas`, or `North America` without explicit US scope is not eligible; scanner-originated ambiguous roles are excluded under the default `unknown_location: exclude` policy.

**Never chain WebFetch + file read + second WebFetch for the same URL** — each fetch re-pulls the same tokens into context.

Expand Down Expand Up @@ -192,6 +194,10 @@ Where `{company-slug}` is the company name in lowercase, no spaces, with hyphens
**Archetype:** {detected}
**Score:** {X.X/5}
**URL:** {original offer URL}
**Location:** {posting location and remote scope}
**Country code:** {US | non-US/unknown}
**Region:** {US diversity_policy location bucket id or non-US/unknown}
**Location status:** {eligible | exploratory | location_review | ineligible}
**PDF:** job-forge/output/cv-candidate-{company-slug}-{{DATE}}.pdf
**Batch ID:** {{ID}}

Expand Down Expand Up @@ -335,7 +341,7 @@ TSV format (single line, no header, 9 tab-separated columns):
| 6 | score | X.XX/5 | `4.55/5` | Or `N/A` if not scorable |
| 7 | pdf | emoji | `✅` or `❌` | Whether PDF was generated |
| 8 | report | md link | `[647](reports/647-...)` | Link to report |
| 9 | notes | string | `APPLY HIGH...` | 1-sentence summary |
| 9 | notes | string | `APPLY HIGH...; Austin, TX; US; us_south; eligible` | Include location, ISO country code, US bucket id, and location status |

**IMPORTANT:** The TSV order has status BEFORE score (col 5→status, col 6→score). In the tracker day files the order is reversed (col 5→score, col 6→status). merge-tracker.mjs handles the conversion.

Expand Down
6 changes: 6 additions & 0 deletions bin/job-forge.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* merge Run merge-tracker.mjs
* dedup Run dedup-tracker.mjs
* verify Run verify-pipeline.mjs
* verify:diversity Validate US-only portal discovery policy
* balance:scan Balance extracted US scan candidates
* normalize Run normalize-statuses.mjs
* pdf Run generate-pdf.mjs
* sync-check Run cv-sync-check.mjs
Expand Down Expand Up @@ -55,6 +57,8 @@ const commands = {
merge: 'merge-tracker.mjs',
dedup: 'dedup-tracker.mjs',
verify: 'verify-pipeline.mjs',
'verify:diversity': 'scripts/verify-portal-diversity.mjs',
'balance:scan': 'scripts/balance-scan-results.mjs',
normalize: 'normalize-statuses.mjs',
pdf: 'generate-pdf.mjs',
'sync-check': 'cv-sync-check.mjs',
Expand Down Expand Up @@ -260,6 +264,8 @@ Commands:
merge Merge batch/tracker-additions/*.tsv into the tracker
dedup Remove duplicate entries from the tracker
verify Verify pipeline integrity (reports, URLs, dedup)
verify:diversity Validate the US-only portal policy and source coverage
balance:scan Balance extracted US candidates across locations and companies
normalize Normalize status values across the tracker
pdf Generate ATS-optimized CV PDF from cv.md
sync-check Lint: verify cv.md and profile.yml are filled in
Expand Down
27 changes: 19 additions & 8 deletions config/profile.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,22 @@ compensation:
location:
country: "United States"
city: "San Francisco"
timezone: "PST"
timezone: "America/Los_Angeles" # IANA timezone, not PST/PDT abbreviation
visa_status: "No sponsorship needed"
# For remote roles outside your country:
# onsite_availability: "1 week/month in any city"
# Hard US-only discovery constraints. A role must explicitly allow work in
# the United States; generic "Remote" and non-US postings are excluded.
# ISO 3166-1 alpha-2 country codes and diversity_policy region ids make
# matching deterministic.
eligible_work_country_codes: [US]
preferred_region_ids: [us_northeast, us_midwest, us_south, us_west, us_nationwide]
remote:
enabled: true
allowed_region_ids: [us_northeast, us_midwest, us_south, us_west, us_nationwide]
require_explicit_location_eligibility: true
include_exploratory_regions: false
willing_to_relocate: false
# Optional availability for hybrid/on-site roles within the US:
# onsite_availability: "1 week/month in a US office"

# Structured location constraints — consumed by the Apply Preflight location
# filter in modes/apply.md. The prose fields above (compensation.location_flexibility,
Expand All @@ -73,19 +85,18 @@ location:
# an apply subagent.
#
# City names are lowercase, hyphenated (e.g. "san-francisco", "new-york").
# Country codes are ISO-3166 alpha-2 uppercase (e.g. "US", "CA", "GB").
# Country codes are ISO-3166 alpha-2 uppercase. The distributable policy is
# US-only, so `US` is the only automated-application country.
location_constraints:
remote_us: true # open to US-remote roles
remote_global: false # open to non-US remote (visa / timezone permitting)
# No remote_global switch: legacy values are ignored by the US-only gate.
hybrid_cities: # cities where hybrid N-days-in-office is acceptable
- san-francisco
blocked_cities: # cities that are a hard No for relocation (even if hybrid)
- new-york
- london
authorized_countries: # countries where the candidate has right-to-work
- US
requires_visa_sponsorship: false # true → roles in non-authorized countries are blocked unless
# the JD explicitly mentions visa sponsorship
requires_visa_sponsorship: false # whether the candidate needs sponsorship to work in the US

# Optional outbound proxy for Geometra browser sessions.
# Uncomment and fill in to route browser traffic through a proxy you already
Expand Down
Loading