mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
@@ -12,24 +12,28 @@ Follow these steps **in order**.
|
|||||||
|
|
||||||
`$ARGUMENTS` may contain:
|
`$ARGUMENTS` may contain:
|
||||||
|
|
||||||
- Nothing → rank all jobs with status `new` in `job_scraper/seen_jobs.json`
|
- Nothing → rank up to 10 jobs with status `new` in `job_scraper/seen_jobs.json`
|
||||||
- A focus area (e.g. `/rank data science`) → rank only jobs whose title or stored fit-notes match the focus
|
- A focus area (e.g. `/rank data science`) → rank only jobs whose title or stored fit-notes match the focus
|
||||||
- `--all` → re-rank every job that has not been applied to, including previously ranked ones (useful after the profile changes)
|
- `--all` → re-rank every job that has not been applied to, including previously ranked ones (useful after the profile changes)
|
||||||
|
- `--limit <N>` → maximum number of jobs to score this run (default 10)
|
||||||
- `--top <N>` → shortlist size (default 5)
|
- `--top <N>` → shortlist size (default 5)
|
||||||
|
|
||||||
|
`--limit` bounds the expensive fetch-and-score work; `--top` only bounds how many scored jobs appear in the shortlist. They are independent: jobs beyond `--limit` are deferred, not silently discarded.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 1: Load State
|
## Step 1: Load State
|
||||||
|
|
||||||
1. Read `job_scraper/seen_jobs.json`. If the file is missing or has no entries, tell the user to run `/scrape` first and stop.
|
1. Read `job_scraper/seen_jobs.json`. If the file is missing or has no entries, tell the user to run `/scrape` first and stop.
|
||||||
2. Read `job_search_tracker.csv`. Build the exclusion set: any company+role already in the tracker is out of scope regardless of flags - it has been applied to or consciously tracked.
|
2. Read `job_search_tracker.csv`. Build the exclusion set: any company+role already in the tracker is out of scope regardless of flags - it has been applied to or consciously tracked.
|
||||||
3. Select candidates: entries with status `new` (or entries of any status with `--all`), minus the exclusion set, filtered by the focus area if one was given.
|
3. Select eligible candidates: entries with status `new` (or entries of any status with `--all`), minus the exclusion set, filtered by the focus area if one was given.
|
||||||
4. If no candidates remain, say so ("Nothing new to rank - run /scrape to find fresh postings") and stop.
|
4. Apply `--limit` after those filters. Keep at most N eligible candidates for this run and count every remaining eligible candidate as deferred. Deferred jobs keep their current status so a later `/rank` run continues the backlog.
|
||||||
5. Read the scoring framework and profile **once**:
|
5. If no candidates remain, say so ("Nothing new to rank - run /scrape to find fresh postings") and stop.
|
||||||
|
6. Read the scoring framework and profile **once**:
|
||||||
- `.claude/skills/job-application-assistant/04-job-evaluation.md`
|
- `.claude/skills/job-application-assistant/04-job-evaluation.md`
|
||||||
- `.claude/skills/job-application-assistant/01-candidate-profile.md`
|
- `.claude/skills/job-application-assistant/01-candidate-profile.md`
|
||||||
|
|
||||||
State how many jobs will be ranked before proceeding.
|
State how many jobs will be ranked and how many are deferred before proceeding.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -117,6 +121,7 @@ Do not modify `job_search_tracker.csv` - that file records applications, and `/r
|
|||||||
|
|
||||||
Ranked <N> new postings (<X> shortlisted, <Y> below threshold, <Z> expired/vetoed).
|
Ranked <N> new postings (<X> shortlisted, <Y> below threshold, <Z> expired/vetoed).
|
||||||
Swept <S> previously ranked entries (<E> newly expired, <C> closing soon).
|
Swept <S> previously ranked entries (<E> newly expired, <C> closing soon).
|
||||||
|
<D> jobs deferred to the next run - re-run `/rank` to continue.
|
||||||
|
|
||||||
### Shortlist
|
### Shortlist
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **`/rank` now bounds each scoring batch** (#395) - a bare run scores at most 10
|
||||||
|
eligible jobs instead of attempting the entire backlog. `--limit <N>` controls
|
||||||
|
scoring independently of `--top`, and the report makes deferred work visible so
|
||||||
|
re-running `/rank` can continue it.
|
||||||
|
|
||||||
- **The portal CLIs' unknown-flag guard no longer lets a single-dash flag through** (#426) -
|
- **The portal CLIs' unknown-flag guard no longer lets a single-dash flag through** (#426) -
|
||||||
the guard in the four bunli-based CLIs (`jobnet`, `jobbank`, `jobindex`, `jobdanmark`) inspected
|
the guard in the four bunli-based CLIs (`jobnet`, `jobbank`, `jobindex`, `jobdanmark`) inspected
|
||||||
only tokens starting with `--`, so an undefined *short* flag bypassed it entirely: bunli
|
only tokens starting with `--`, so an undefined *short* flag bypassed it entirely: bunli
|
||||||
|
|||||||
@@ -484,5 +484,38 @@ class PostedDateStalenessSpec(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RankBatchLimitSpec(unittest.TestCase):
|
||||||
|
"""The expensive fetch-and-score batch is bounded independently of output."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.sections = _sections(COMMAND.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
def test_step0_documents_default_limit_distinct_from_top(self):
|
||||||
|
step0 = self.sections.get("Step 0: Parse Input", "")
|
||||||
|
self.assertIn("`--limit <N>`", step0)
|
||||||
|
self.assertIn("default 10", step0)
|
||||||
|
self.assertIn("`--top <N>`", step0)
|
||||||
|
self.assertIn(
|
||||||
|
"They are independent",
|
||||||
|
step0,
|
||||||
|
"--limit must bound scoring without being confused with shortlist size",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_step1_applies_limit_after_eligibility_filters(self):
|
||||||
|
step1 = self.sections.get("Step 1: Load State", "")
|
||||||
|
self.assertIn("Apply `--limit` after those filters", step1)
|
||||||
|
self.assertIn("count every remaining eligible candidate as deferred", step1)
|
||||||
|
self.assertIn(
|
||||||
|
"keep their current status",
|
||||||
|
step1,
|
||||||
|
"deferred jobs must remain eligible for a later run",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_step5_reports_deferral_and_how_to_continue(self):
|
||||||
|
report = self.sections.get("Job Ranking - YYYY-MM-DD", "")
|
||||||
|
self.assertIn("jobs deferred", report)
|
||||||
|
self.assertIn("re-run `/rank` to continue", report)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user