job-forge 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -90,22 +90,23 @@ The harness ships three subagents (see `.opencode/agents/`). The orchestrator MU
90
90
 
91
91
  **When to break this rule:** if the user explicitly asks for "quality over cost" or flags a high-stakes application (top-tier company, offer-stage negotiation, executive search), route everything through `@general-paid`. Document the exception in the session.
92
92
 
93
- ### Pre-flight delegation (HARD RULE)
93
+ ### When to delegate
94
94
 
95
- For any task that will involve **more than one tool call** i.e., anything beyond a one-shot answer the orchestrator's **first tool call MUST be `task`** (dispatching to a subagent). Not `Read`, not `Bash`, not `geometra_connect`, not `Grep`. The orchestrator plans and dispatches; subagents execute.
95
+ **Delegate (`task` out) when the work involves repeated tool-heavy steps that bloat the orchestrator's cache prefix.** The concrete failure mode this prevents: a 341-message "apply to 20 jobs" session where repeated `geometra_fill_form` / `geometra_page_model` calls accumulated in history, forcing each new message to re-process 100K+ tokens of fresh input instead of reading from cache.
96
96
 
97
- **Why this is absolute:** every tool call in the orchestrator accumulates in the top-level session's history and pollutes the cache prefix. Once the orchestrator has read three files and made two Geometra calls, delegating to a subagent no longer helps — the subagent inherits the bloated context. The only way to keep the orchestrator lean is to delegate *before* doing anything else.
97
+ **Delegate when:**
98
+ - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
99
+ - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
100
+ - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
98
101
 
99
- **What counts as "more than one tool call":**
100
- - Evaluating any offer (always ≥3 steps: fetch JD, score, write report)
101
- - Any `/job-forge` mode invocation except `tracker` (read-only)
102
- - Applying to a job
103
- - Scanning portals
104
- - Any batch operation
102
+ **Do NOT delegate orchestrate inline:**
103
+ - Single-offer evaluation (text-heavy, not tool-heavy)
104
+ - Development / bug-fix / file-editing tasks
105
+ - `tracker` and other read-only modes
106
+ - Single-company scan, single-URL check
107
+ - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
105
108
 
106
- **Explicit exception:** trivial one-shot answers "what does this error mean?", "read this file and summarize", "what's my next report number?" — can stay in the orchestrator. If the question can be answered in ≤1 tool call, do not delegate.
107
-
108
- **Detection signal:** if you (orchestrator) find yourself about to make your 2nd tool call in a session that wasn't a trivial one-shot, STOP. Instead, `task` out the remaining work as a single delegated job.
109
+ **Detection signal:** if you're about to call `geometra_fill_form` for a second *different* job in the same session, STOP and delegate the remainder. For everything else, in-session execution is the expected default.
109
110
 
110
111
  ---
111
112
 
@@ -373,14 +374,28 @@ These blocks come from two distinct root causes and require different responses:
373
374
 
374
375
  **Known-block Ashby tenants (2026-04-19 empirical observations).** These tenants fired class B on every attempted submit from a headless datacenter-IP proxy. Orchestrators planning apply dispatches should assume these tenants will Fail in headless — prioritize other portals, or skip same-tenant siblings after a confirmed class B to avoid burning subagent slots:
375
376
 
376
- - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS
377
+ - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS, **Ashby (self-tenant)**, **Perplexity**
377
378
 
378
379
  **Known class-A-compatible Ashby tenants (same observations).** These tenants accepted headless submits cleanly, often with `imeFriendly: true` making the difference on the text-field subset:
379
380
 
380
- - Supabase, LangChain, Poolside, Runway Financial
381
+ - Supabase, LangChain, Poolside, Runway Financial, **Sentry**, **Cognition**
381
382
 
382
383
  The pattern is tenant configuration, not role or company size. Lists drift as tenants tune their anti-bot — treat as probabilistic priors, not hard rules.
383
384
 
385
+ **Ashby choice-group with `optionCount: 1` and no labels (Sentry pattern).** Some Ashby tenants render Yes/No work-authorization questions as `role="button" name="Application"` pill toggles where the accessibility tree exposes neither `Yes` nor `No` labels. `fill_fields` with `choiceType: "group"` silently no-ops; `geometra_click` by `id` also fails to toggle. Fix: fall back to `geometra_click` with RAW x,y coordinates at the button centers (Yes is typically the left button, No is the right). Confirmed on Sentry Staff Platform #845, 2026-04-19.
386
+
387
+ ### Other Portal Failure Classes
388
+
389
+ **Typeform applications are Geometra-unsupported.** Some companies (Better Stack confirmed, 2026-04-19) route the Apply link to a Typeform wizard (`*.typeform.com/apply-*`). Typeform renders questions via a custom React/canvas layer that does NOT expose input fields to the accessibility tree — `geometra_form_schema` returns "No forms found", `geometra_query role=textbox` returns empty, blind `geometra_type` produces no semantic change. Mark `Failed` with reason "Typeform portal — Geometra unsupported" on detection; do not burn the 9-minute budget attempting blind input.
390
+
391
+ **Avature multi-step wizards have a native-`<select>` validation lag (Bloomberg pattern).** Bloomberg's careers site redirects to `bloomberg.avature.net` with a 4-step wizard. On Step 2, native `<select>` elements ("Is Current Position? / No") accept the value but keep `invalid: true` persistently — neither Tab, re-submit, nor re-pick clears it. `imeFriendly` has no effect because the field is a native `<select>`, not React-controlled text. There is no documented recovery. Mark `Failed` with reason "Avature native-select validation lag"; account creation up to that point is preserved for any future manual path. Confirmed on Bloomberg Sr SWE Auth #828, 2026-04-19.
392
+
393
+ **Cloudflare / ATS-vendor blocks on Dropbox-class portals.** Dropbox's real apply flow lives behind `happydance.website` (ATS vendor), which Cloudflare-fingerprints headless Chromium + datacenter IPs and returns "Sorry, you have been blocked". `job-boards.greenhouse.io/dropbox` does not mirror — there is no public Greenhouse fallback. Symptom-wise indistinguishable from Ashby class B but at a different layer. Mark `Failed` with reason "ATS vendor Cloudflare block (happydance.website or equivalent)". Confirmed on Dropbox Sr FS Product #831, 2026-04-19.
394
+
395
+ **Greenhouse OTP-on-fill variant (Instacart pattern).** Most Greenhouse OTP flows fire on Submit. A minority (Instacart Staff FoodStorm #827, 2026-04-19) fire the 8-cell security-code gate mid-fill, BEFORE the user clicks Submit. Detection: watch for an 8-cell OTP input surfacing after resume upload or the first listbox commit. Fetch from Gmail (`from:greenhouse newer_than:10m`) immediately when it appears — do not wait for Submit.
396
+
397
+ **`geometra_fill_otp` char-drop on first fill.** Occasionally `fill_otp` lands only the first character of an 8-char code (seen on Instacart, 2026-04-19). Recovery: click the first cell to focus, then re-issue `fill_otp` with `perCharDelayMs: 120`. The form usually auto-submits once all 8 cells are populated.
398
+
384
399
  ### Greenhouse Bot-Detection Honeypots
385
400
 
386
401
  Some Greenhouse tenants (Grafana Labs confirmed, 2026-04-19) inject a honeypot-style single-pick question on the application form, rendered as a listbox labeled something like "Which of the following best describes you?" with options resembling "I am a human being / I am a bot / I am a robot".
package/AGENTS.md CHANGED
@@ -85,22 +85,23 @@ The harness ships three subagents (see `.opencode/agents/`). The orchestrator MU
85
85
 
86
86
  **When to break this rule:** if the user explicitly asks for "quality over cost" or flags a high-stakes application (top-tier company, offer-stage negotiation, executive search), route everything through `@general-paid`. Document the exception in the session.
87
87
 
88
- ### Pre-flight delegation (HARD RULE)
88
+ ### When to delegate
89
89
 
90
- For any task that will involve **more than one tool call** i.e., anything beyond a one-shot answer the orchestrator's **first tool call MUST be `task`** (dispatching to a subagent). Not `Read`, not `Bash`, not `geometra_connect`, not `Grep`. The orchestrator plans and dispatches; subagents execute.
90
+ **Delegate (`task` out) when the work involves repeated tool-heavy steps that bloat the orchestrator's cache prefix.** The concrete failure mode this prevents: a 341-message "apply to 20 jobs" session where repeated `geometra_fill_form` / `geometra_page_model` calls accumulated in history, forcing each new message to re-process 100K+ tokens of fresh input instead of reading from cache.
91
91
 
92
- **Why this is absolute:** every tool call in the orchestrator accumulates in the top-level session's history and pollutes the cache prefix. Once the orchestrator has read three files and made two Geometra calls, delegating to a subagent no longer helps — the subagent inherits the bloated context. The only way to keep the orchestrator lean is to delegate *before* doing anything else.
92
+ **Delegate when:**
93
+ - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
94
+ - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
95
+ - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
93
96
 
94
- **What counts as "more than one tool call":**
95
- - Evaluating any offer (always ≥3 steps: fetch JD, score, write report)
96
- - Any `/job-forge` mode invocation except `tracker` (read-only)
97
- - Applying to a job
98
- - Scanning portals
99
- - Any batch operation
97
+ **Do NOT delegate orchestrate inline:**
98
+ - Single-offer evaluation (text-heavy, not tool-heavy)
99
+ - Development / bug-fix / file-editing tasks
100
+ - `tracker` and other read-only modes
101
+ - Single-company scan, single-URL check
102
+ - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
100
103
 
101
- **Explicit exception:** trivial one-shot answers "what does this error mean?", "read this file and summarize", "what's my next report number?" — can stay in the orchestrator. If the question can be answered in ≤1 tool call, do not delegate.
102
-
103
- **Detection signal:** if you (orchestrator) find yourself about to make your 2nd tool call in a session that wasn't a trivial one-shot, STOP. Instead, `task` out the remaining work as a single delegated job.
104
+ **Detection signal:** if you're about to call `geometra_fill_form` for a second *different* job in the same session, STOP and delegate the remainder. For everything else, in-session execution is the expected default.
104
105
 
105
106
  ---
106
107
 
@@ -368,14 +369,28 @@ These blocks come from two distinct root causes and require different responses:
368
369
 
369
370
  **Known-block Ashby tenants (2026-04-19 empirical observations).** These tenants fired class B on every attempted submit from a headless datacenter-IP proxy. Orchestrators planning apply dispatches should assume these tenants will Fail in headless — prioritize other portals, or skip same-tenant siblings after a confirmed class B to avoid burning subagent slots:
370
371
 
371
- - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS
372
+ - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS, **Ashby (self-tenant)**, **Perplexity**
372
373
 
373
374
  **Known class-A-compatible Ashby tenants (same observations).** These tenants accepted headless submits cleanly, often with `imeFriendly: true` making the difference on the text-field subset:
374
375
 
375
- - Supabase, LangChain, Poolside, Runway Financial
376
+ - Supabase, LangChain, Poolside, Runway Financial, **Sentry**, **Cognition**
376
377
 
377
378
  The pattern is tenant configuration, not role or company size. Lists drift as tenants tune their anti-bot — treat as probabilistic priors, not hard rules.
378
379
 
380
+ **Ashby choice-group with `optionCount: 1` and no labels (Sentry pattern).** Some Ashby tenants render Yes/No work-authorization questions as `role="button" name="Application"` pill toggles where the accessibility tree exposes neither `Yes` nor `No` labels. `fill_fields` with `choiceType: "group"` silently no-ops; `geometra_click` by `id` also fails to toggle. Fix: fall back to `geometra_click` with RAW x,y coordinates at the button centers (Yes is typically the left button, No is the right). Confirmed on Sentry Staff Platform #845, 2026-04-19.
381
+
382
+ ### Other Portal Failure Classes
383
+
384
+ **Typeform applications are Geometra-unsupported.** Some companies (Better Stack confirmed, 2026-04-19) route the Apply link to a Typeform wizard (`*.typeform.com/apply-*`). Typeform renders questions via a custom React/canvas layer that does NOT expose input fields to the accessibility tree — `geometra_form_schema` returns "No forms found", `geometra_query role=textbox` returns empty, blind `geometra_type` produces no semantic change. Mark `Failed` with reason "Typeform portal — Geometra unsupported" on detection; do not burn the 9-minute budget attempting blind input.
385
+
386
+ **Avature multi-step wizards have a native-`<select>` validation lag (Bloomberg pattern).** Bloomberg's careers site redirects to `bloomberg.avature.net` with a 4-step wizard. On Step 2, native `<select>` elements ("Is Current Position? / No") accept the value but keep `invalid: true` persistently — neither Tab, re-submit, nor re-pick clears it. `imeFriendly` has no effect because the field is a native `<select>`, not React-controlled text. There is no documented recovery. Mark `Failed` with reason "Avature native-select validation lag"; account creation up to that point is preserved for any future manual path. Confirmed on Bloomberg Sr SWE Auth #828, 2026-04-19.
387
+
388
+ **Cloudflare / ATS-vendor blocks on Dropbox-class portals.** Dropbox's real apply flow lives behind `happydance.website` (ATS vendor), which Cloudflare-fingerprints headless Chromium + datacenter IPs and returns "Sorry, you have been blocked". `job-boards.greenhouse.io/dropbox` does not mirror — there is no public Greenhouse fallback. Symptom-wise indistinguishable from Ashby class B but at a different layer. Mark `Failed` with reason "ATS vendor Cloudflare block (happydance.website or equivalent)". Confirmed on Dropbox Sr FS Product #831, 2026-04-19.
389
+
390
+ **Greenhouse OTP-on-fill variant (Instacart pattern).** Most Greenhouse OTP flows fire on Submit. A minority (Instacart Staff FoodStorm #827, 2026-04-19) fire the 8-cell security-code gate mid-fill, BEFORE the user clicks Submit. Detection: watch for an 8-cell OTP input surfacing after resume upload or the first listbox commit. Fetch from Gmail (`from:greenhouse newer_than:10m`) immediately when it appears — do not wait for Submit.
391
+
392
+ **`geometra_fill_otp` char-drop on first fill.** Occasionally `fill_otp` lands only the first character of an 8-char code (seen on Instacart, 2026-04-19). Recovery: click the first cell to focus, then re-issue `fill_otp` with `perCharDelayMs: 120`. The form usually auto-submits once all 8 cells are populated.
393
+
379
394
  ### Greenhouse Bot-Detection Honeypots
380
395
 
381
396
  Some Greenhouse tenants (Grafana Labs confirmed, 2026-04-19) inject a honeypot-style single-pick question on the application form, rendered as a listbox labeled something like "Which of the following best describes you?" with options resembling "I am a human being / I am a bot / I am a robot".
package/CLAUDE.md CHANGED
@@ -85,22 +85,23 @@ The harness ships three subagents (see `.opencode/agents/`). The orchestrator MU
85
85
 
86
86
  **When to break this rule:** if the user explicitly asks for "quality over cost" or flags a high-stakes application (top-tier company, offer-stage negotiation, executive search), route everything through `@general-paid`. Document the exception in the session.
87
87
 
88
- ### Pre-flight delegation (HARD RULE)
88
+ ### When to delegate
89
89
 
90
- For any task that will involve **more than one tool call** i.e., anything beyond a one-shot answer the orchestrator's **first tool call MUST be `task`** (dispatching to a subagent). Not `Read`, not `Bash`, not `geometra_connect`, not `Grep`. The orchestrator plans and dispatches; subagents execute.
90
+ **Delegate (`task` out) when the work involves repeated tool-heavy steps that bloat the orchestrator's cache prefix.** The concrete failure mode this prevents: a 341-message "apply to 20 jobs" session where repeated `geometra_fill_form` / `geometra_page_model` calls accumulated in history, forcing each new message to re-process 100K+ tokens of fresh input instead of reading from cache.
91
91
 
92
- **Why this is absolute:** every tool call in the orchestrator accumulates in the top-level session's history and pollutes the cache prefix. Once the orchestrator has read three files and made two Geometra calls, delegating to a subagent no longer helps — the subagent inherits the bloated context. The only way to keep the orchestrator lean is to delegate *before* doing anything else.
92
+ **Delegate when:**
93
+ - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
94
+ - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
95
+ - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
93
96
 
94
- **What counts as "more than one tool call":**
95
- - Evaluating any offer (always ≥3 steps: fetch JD, score, write report)
96
- - Any `/job-forge` mode invocation except `tracker` (read-only)
97
- - Applying to a job
98
- - Scanning portals
99
- - Any batch operation
97
+ **Do NOT delegate orchestrate inline:**
98
+ - Single-offer evaluation (text-heavy, not tool-heavy)
99
+ - Development / bug-fix / file-editing tasks
100
+ - `tracker` and other read-only modes
101
+ - Single-company scan, single-URL check
102
+ - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
100
103
 
101
- **Explicit exception:** trivial one-shot answers "what does this error mean?", "read this file and summarize", "what's my next report number?" — can stay in the orchestrator. If the question can be answered in ≤1 tool call, do not delegate.
102
-
103
- **Detection signal:** if you (orchestrator) find yourself about to make your 2nd tool call in a session that wasn't a trivial one-shot, STOP. Instead, `task` out the remaining work as a single delegated job.
104
+ **Detection signal:** if you're about to call `geometra_fill_form` for a second *different* job in the same session, STOP and delegate the remainder. For everything else, in-session execution is the expected default.
104
105
 
105
106
  ---
106
107
 
@@ -368,14 +369,28 @@ These blocks come from two distinct root causes and require different responses:
368
369
 
369
370
  **Known-block Ashby tenants (2026-04-19 empirical observations).** These tenants fired class B on every attempted submit from a headless datacenter-IP proxy. Orchestrators planning apply dispatches should assume these tenants will Fail in headless — prioritize other portals, or skip same-tenant siblings after a confirmed class B to avoid burning subagent slots:
370
371
 
371
- - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS
372
+ - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS, **Ashby (self-tenant)**, **Perplexity**
372
373
 
373
374
  **Known class-A-compatible Ashby tenants (same observations).** These tenants accepted headless submits cleanly, often with `imeFriendly: true` making the difference on the text-field subset:
374
375
 
375
- - Supabase, LangChain, Poolside, Runway Financial
376
+ - Supabase, LangChain, Poolside, Runway Financial, **Sentry**, **Cognition**
376
377
 
377
378
  The pattern is tenant configuration, not role or company size. Lists drift as tenants tune their anti-bot — treat as probabilistic priors, not hard rules.
378
379
 
380
+ **Ashby choice-group with `optionCount: 1` and no labels (Sentry pattern).** Some Ashby tenants render Yes/No work-authorization questions as `role="button" name="Application"` pill toggles where the accessibility tree exposes neither `Yes` nor `No` labels. `fill_fields` with `choiceType: "group"` silently no-ops; `geometra_click` by `id` also fails to toggle. Fix: fall back to `geometra_click` with RAW x,y coordinates at the button centers (Yes is typically the left button, No is the right). Confirmed on Sentry Staff Platform #845, 2026-04-19.
381
+
382
+ ### Other Portal Failure Classes
383
+
384
+ **Typeform applications are Geometra-unsupported.** Some companies (Better Stack confirmed, 2026-04-19) route the Apply link to a Typeform wizard (`*.typeform.com/apply-*`). Typeform renders questions via a custom React/canvas layer that does NOT expose input fields to the accessibility tree — `geometra_form_schema` returns "No forms found", `geometra_query role=textbox` returns empty, blind `geometra_type` produces no semantic change. Mark `Failed` with reason "Typeform portal — Geometra unsupported" on detection; do not burn the 9-minute budget attempting blind input.
385
+
386
+ **Avature multi-step wizards have a native-`<select>` validation lag (Bloomberg pattern).** Bloomberg's careers site redirects to `bloomberg.avature.net` with a 4-step wizard. On Step 2, native `<select>` elements ("Is Current Position? / No") accept the value but keep `invalid: true` persistently — neither Tab, re-submit, nor re-pick clears it. `imeFriendly` has no effect because the field is a native `<select>`, not React-controlled text. There is no documented recovery. Mark `Failed` with reason "Avature native-select validation lag"; account creation up to that point is preserved for any future manual path. Confirmed on Bloomberg Sr SWE Auth #828, 2026-04-19.
387
+
388
+ **Cloudflare / ATS-vendor blocks on Dropbox-class portals.** Dropbox's real apply flow lives behind `happydance.website` (ATS vendor), which Cloudflare-fingerprints headless Chromium + datacenter IPs and returns "Sorry, you have been blocked". `job-boards.greenhouse.io/dropbox` does not mirror — there is no public Greenhouse fallback. Symptom-wise indistinguishable from Ashby class B but at a different layer. Mark `Failed` with reason "ATS vendor Cloudflare block (happydance.website or equivalent)". Confirmed on Dropbox Sr FS Product #831, 2026-04-19.
389
+
390
+ **Greenhouse OTP-on-fill variant (Instacart pattern).** Most Greenhouse OTP flows fire on Submit. A minority (Instacart Staff FoodStorm #827, 2026-04-19) fire the 8-cell security-code gate mid-fill, BEFORE the user clicks Submit. Detection: watch for an 8-cell OTP input surfacing after resume upload or the first listbox commit. Fetch from Gmail (`from:greenhouse newer_than:10m`) immediately when it appears — do not wait for Submit.
391
+
392
+ **`geometra_fill_otp` char-drop on first fill.** Occasionally `fill_otp` lands only the first character of an 8-char code (seen on Instacart, 2026-04-19). Recovery: click the first cell to focus, then re-issue `fill_otp` with `perCharDelayMs: 120`. The form usually auto-submits once all 8 cells are populated.
393
+
379
394
  ### Greenhouse Bot-Detection Honeypots
380
395
 
381
396
  Some Greenhouse tenants (Grafana Labs confirmed, 2026-04-19) inject a honeypot-style single-pick question on the application form, rendered as a listbox labeled something like "Which of the following best describes you?" with options resembling "I am a human being / I am a bot / I am a robot".
@@ -85,22 +85,23 @@ The harness ships three subagents (see `.opencode/agents/`). The orchestrator MU
85
85
 
86
86
  **When to break this rule:** if the user explicitly asks for "quality over cost" or flags a high-stakes application (top-tier company, offer-stage negotiation, executive search), route everything through `@general-paid`. Document the exception in the session.
87
87
 
88
- ### Pre-flight delegation (HARD RULE)
88
+ ### When to delegate
89
89
 
90
- For any task that will involve **more than one tool call** i.e., anything beyond a one-shot answer the orchestrator's **first tool call MUST be `task`** (dispatching to a subagent). Not `Read`, not `Bash`, not `geometra_connect`, not `Grep`. The orchestrator plans and dispatches; subagents execute.
90
+ **Delegate (`task` out) when the work involves repeated tool-heavy steps that bloat the orchestrator's cache prefix.** The concrete failure mode this prevents: a 341-message "apply to 20 jobs" session where repeated `geometra_fill_form` / `geometra_page_model` calls accumulated in history, forcing each new message to re-process 100K+ tokens of fresh input instead of reading from cache.
91
91
 
92
- **Why this is absolute:** every tool call in the orchestrator accumulates in the top-level session's history and pollutes the cache prefix. Once the orchestrator has read three files and made two Geometra calls, delegating to a subagent no longer helps — the subagent inherits the bloated context. The only way to keep the orchestrator lean is to delegate *before* doing anything else.
92
+ **Delegate when:**
93
+ - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
94
+ - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
95
+ - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
93
96
 
94
- **What counts as "more than one tool call":**
95
- - Evaluating any offer (always ≥3 steps: fetch JD, score, write report)
96
- - Any `/job-forge` mode invocation except `tracker` (read-only)
97
- - Applying to a job
98
- - Scanning portals
99
- - Any batch operation
97
+ **Do NOT delegate orchestrate inline:**
98
+ - Single-offer evaluation (text-heavy, not tool-heavy)
99
+ - Development / bug-fix / file-editing tasks
100
+ - `tracker` and other read-only modes
101
+ - Single-company scan, single-URL check
102
+ - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
100
103
 
101
- **Explicit exception:** trivial one-shot answers "what does this error mean?", "read this file and summarize", "what's my next report number?" — can stay in the orchestrator. If the question can be answered in ≤1 tool call, do not delegate.
102
-
103
- **Detection signal:** if you (orchestrator) find yourself about to make your 2nd tool call in a session that wasn't a trivial one-shot, STOP. Instead, `task` out the remaining work as a single delegated job.
104
+ **Detection signal:** if you're about to call `geometra_fill_form` for a second *different* job in the same session, STOP and delegate the remainder. For everything else, in-session execution is the expected default.
104
105
 
105
106
  ---
106
107
 
@@ -368,14 +369,28 @@ These blocks come from two distinct root causes and require different responses:
368
369
 
369
370
  **Known-block Ashby tenants (2026-04-19 empirical observations).** These tenants fired class B on every attempted submit from a headless datacenter-IP proxy. Orchestrators planning apply dispatches should assume these tenants will Fail in headless — prioritize other portals, or skip same-tenant siblings after a confirmed class B to avoid burning subagent slots:
370
371
 
371
- - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS
372
+ - Vellum, Linear, Vanta, River Financial, Higharc, Trace Labs, Solace Health, Unstructured, ClickUp, Zapier, Deepgram, Ramp, WorkOS, **Ashby (self-tenant)**, **Perplexity**
372
373
 
373
374
  **Known class-A-compatible Ashby tenants (same observations).** These tenants accepted headless submits cleanly, often with `imeFriendly: true` making the difference on the text-field subset:
374
375
 
375
- - Supabase, LangChain, Poolside, Runway Financial
376
+ - Supabase, LangChain, Poolside, Runway Financial, **Sentry**, **Cognition**
376
377
 
377
378
  The pattern is tenant configuration, not role or company size. Lists drift as tenants tune their anti-bot — treat as probabilistic priors, not hard rules.
378
379
 
380
+ **Ashby choice-group with `optionCount: 1` and no labels (Sentry pattern).** Some Ashby tenants render Yes/No work-authorization questions as `role="button" name="Application"` pill toggles where the accessibility tree exposes neither `Yes` nor `No` labels. `fill_fields` with `choiceType: "group"` silently no-ops; `geometra_click` by `id` also fails to toggle. Fix: fall back to `geometra_click` with RAW x,y coordinates at the button centers (Yes is typically the left button, No is the right). Confirmed on Sentry Staff Platform #845, 2026-04-19.
381
+
382
+ ### Other Portal Failure Classes
383
+
384
+ **Typeform applications are Geometra-unsupported.** Some companies (Better Stack confirmed, 2026-04-19) route the Apply link to a Typeform wizard (`*.typeform.com/apply-*`). Typeform renders questions via a custom React/canvas layer that does NOT expose input fields to the accessibility tree — `geometra_form_schema` returns "No forms found", `geometra_query role=textbox` returns empty, blind `geometra_type` produces no semantic change. Mark `Failed` with reason "Typeform portal — Geometra unsupported" on detection; do not burn the 9-minute budget attempting blind input.
385
+
386
+ **Avature multi-step wizards have a native-`<select>` validation lag (Bloomberg pattern).** Bloomberg's careers site redirects to `bloomberg.avature.net` with a 4-step wizard. On Step 2, native `<select>` elements ("Is Current Position? / No") accept the value but keep `invalid: true` persistently — neither Tab, re-submit, nor re-pick clears it. `imeFriendly` has no effect because the field is a native `<select>`, not React-controlled text. There is no documented recovery. Mark `Failed` with reason "Avature native-select validation lag"; account creation up to that point is preserved for any future manual path. Confirmed on Bloomberg Sr SWE Auth #828, 2026-04-19.
387
+
388
+ **Cloudflare / ATS-vendor blocks on Dropbox-class portals.** Dropbox's real apply flow lives behind `happydance.website` (ATS vendor), which Cloudflare-fingerprints headless Chromium + datacenter IPs and returns "Sorry, you have been blocked". `job-boards.greenhouse.io/dropbox` does not mirror — there is no public Greenhouse fallback. Symptom-wise indistinguishable from Ashby class B but at a different layer. Mark `Failed` with reason "ATS vendor Cloudflare block (happydance.website or equivalent)". Confirmed on Dropbox Sr FS Product #831, 2026-04-19.
389
+
390
+ **Greenhouse OTP-on-fill variant (Instacart pattern).** Most Greenhouse OTP flows fire on Submit. A minority (Instacart Staff FoodStorm #827, 2026-04-19) fire the 8-cell security-code gate mid-fill, BEFORE the user clicks Submit. Detection: watch for an 8-cell OTP input surfacing after resume upload or the first listbox commit. Fetch from Gmail (`from:greenhouse newer_than:10m`) immediately when it appears — do not wait for Submit.
391
+
392
+ **`geometra_fill_otp` char-drop on first fill.** Occasionally `fill_otp` lands only the first character of an 8-char code (seen on Instacart, 2026-04-19). Recovery: click the first cell to focus, then re-issue `fill_otp` with `perCharDelayMs: 120`. The form usually auto-submits once all 8 cells are populated.
393
+
379
394
  ### Greenhouse Bot-Detection Honeypots
380
395
 
381
396
  Some Greenhouse tenants (Grafana Labs confirmed, 2026-04-19) inject a honeypot-style single-pick question on the application form, rendered as a listbox labeled something like "Which of the following best describes you?" with options resembling "I am a human being / I am a bot / I am a robot".
package/merge-tracker.mjs CHANGED
@@ -109,9 +109,31 @@ function normalizeCompany(name) {
109
109
  return name.toLowerCase().replace(/[^a-z0-9]/g, '');
110
110
  }
111
111
 
112
+ // Generic seniority + engineering words that appear across most SWE roles
113
+ // and carry no role-specialty signal. A "discriminator" is any remaining
114
+ // word longer than 3 chars (e.g. "Observability", "Telemetry", "Platform").
115
+ const ROLE_STOPWORDS = new Set([
116
+ 'staff', 'senior', 'principal', 'lead', 'junior',
117
+ 'software', 'engineer', 'engineering', 'developer',
118
+ 'backend', 'frontend', 'fullstack', 'full-stack', 'full', 'stack',
119
+ 'technical', 'applied',
120
+ ]);
121
+
112
122
  function roleFuzzyMatch(a, b) {
113
- const wordsA = a.toLowerCase().split(/\s+/).filter(w => w.length > 3);
114
- const wordsB = b.toLowerCase().split(/\s+/).filter(w => w.length > 3);
123
+ // Split on whitespace AND role punctuation (commas, colons, dashes, parens)
124
+ // so "Staff SWE, Observability K8s" tokenizes past the comma.
125
+ const split = (s) => s.toLowerCase()
126
+ .split(/[\s,:\-()\/]+/)
127
+ .map(w => w.trim())
128
+ .filter(w => w.length > 3 && !ROLE_STOPWORDS.has(w));
129
+
130
+ const wordsA = split(a);
131
+ const wordsB = split(b);
132
+
133
+ // Match on discriminator-word overlap only. Prevents "Staff Software
134
+ // Engineer, ML Observability" and "Staff Backend Engineer, Adaptive
135
+ // Telemetry" from colliding (same company, different specialty) while
136
+ // still collapsing re-evaluations of the same role (same discriminators).
115
137
  const overlap = wordsA.filter(w => wordsB.some(wb => wb.includes(w) || w.includes(wb)));
116
138
  return overlap.length >= 2;
117
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-forge",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "AI-powered job search pipeline built on opencode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,8 @@
18
18
  "tokens": "node scripts/token-usage-report.mjs",
19
19
  "tokens:today": "node scripts/token-usage-report.mjs --days 1",
20
20
  "tokens:log": "node scripts/token-usage-report.mjs --days 1 --append",
21
+ "trace:list": "iso-trace list --since 7d --cwd .",
22
+ "trace:stats": "iso-trace stats --since 7d --cwd .",
21
23
  "build:config": "iso build .",
22
24
  "prepack": "iso build .",
23
25
  "release:check-source": "node ./scripts/release/check-source.mjs",
@@ -75,6 +77,7 @@
75
77
  },
76
78
  "devDependencies": {
77
79
  "@razroo/iso": "^0.1.1",
78
- "@razroo/iso-harness": "^0.1.3"
80
+ "@razroo/iso-harness": "^0.1.3",
81
+ "@razroo/iso-trace": "^0.1.0"
79
82
  }
80
83
  }