diff --git a/.claude/commands/rank.md b/.claude/commands/rank.md index f7022578..06d6cb13 100644 --- a/.claude/commands/rank.md +++ b/.claude/commands/rank.md @@ -59,7 +59,7 @@ Dispatch parallel **`posting-scorer`** agents via the **Agent tool** (`subagent_ - `expired` - **positive evidence the posting is closed**: the page says it is no longer available or the deadline has passed, the server answers 410 Gone, or the URL redirects to a listing page instead of the posting. - `unreachable` - **retrieval failed, so nothing is known about the posting**: 401/403 or a login wall, a 404, a timeout, a 5xx, or an empty or unparseable page. The agent adds `"fetch_error"`, one short plain-text line naming the failure (e.g. `"403 after curl retry"`). A 404 is `unreachable`, not `expired`: links break while postings stay live (#538 was one). - **Before returning `unreachable` or `expired`, the agent must exhaust the escalation order** in `09-web-research.md`: installed portal CLI first, then `WebFetch`, then `python3 tools/fetch_page.py` with browser headers (it enforces `robots.txt` itself), then search for the employer's own careers site. A `WebFetch` 403 is a rejected *client*, not a missing page, and retrying with browser headers via `fetch_page.py` recovers most corporate and bank domains. A stored URL ending in a `#fragment` points at a listing page rather than a posting, so the agent should search the employer's own careers site for the role by name before writing the job off. Include this instruction in every scoring agent's prompt. Neither status means "the first fetch was unhelpful". -- Scope is triage: posting text vs. rubric. **No company research, no salary lookup, no web searches** - that depth belongs to `/apply`. +- Scope is triage: posting text vs. rubric. A web search is allowed only for the escalation's last step above: finding the employer's own copy of the posting. **No company research or salary lookup** - that depth belongs to `/apply`. - **Every fetched body passes `tools/posting_guard.py` before it is scored.** Portal CLI output is piped (`... detail '' --format plain | python3 tools/posting_guard.py --text --json`), a `fetch_page.py --out` file is passed with `--in --html --json`, and `WebFetch` output goes through a quoted heredoc with `--text --json`. The agent scores from the **cleaned** text and returns the gate's first line verbatim as `"guard"` (`"clean"` when clean). A flagged posting is still scored - the flag is for the user, not a veto. Include this instruction in every scoring agent's prompt. Each agent returns a JSON array, one object per job: diff --git a/CHANGELOG.md b/CHANGELOG.md index 38b6a098..dc72b1ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -187,6 +187,12 @@ per-file diff commands. under `--rebuild`, and a first run with only such entries stops before creating the database. An expired posting with a tracker row still syncs as an ordinary row. +- **`/rank` Step 2 no longer forbids the web search its own escalation requires** (#638) (`.claude/commands/rank.md`) - the + escalation bullet ends with a search for the employer's own careers site (step 4 of `09-web-research.md` is `WebSearch`), + and the next bullet said "no web searches", in the same prompt every scoring agent receives. The scope bullet now allows a + web search only for that last escalation step, finding the employer's own copy of the posting; company research and salary + lookups stay out of `/rank`. + ## [1.7.3] - 2026-10-19 ### Added