job-forge 2.14.7 → 2.14.8

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.
@@ -23,3 +23,8 @@ args = ["-y", "@geometra/mcp"]
23
23
  command = "npx"
24
24
  args = ["-y", "@razroo/gmail-mcp"]
25
25
  env = { DISABLE_HTTP = "true" }
26
+
27
+ [mcp_servers.state-trace]
28
+ command = "uvx"
29
+ args = ["--from", "state-trace[mcp]", "state-trace-mcp"]
30
+ env = { STATE_TRACE_STORAGE_PATH = ".state-trace/memory.db", STATE_TRACE_NAMESPACE = "job-forge", STATE_TRACE_CAPACITY_LIMIT = "256" }
package/.cursor/mcp.json CHANGED
@@ -16,6 +16,19 @@
16
16
  "env": {
17
17
  "DISABLE_HTTP": "true"
18
18
  }
19
+ },
20
+ "state-trace": {
21
+ "command": "uvx",
22
+ "args": [
23
+ "--from",
24
+ "state-trace[mcp]",
25
+ "state-trace-mcp"
26
+ ],
27
+ "env": {
28
+ "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
29
+ "STATE_TRACE_NAMESPACE": "job-forge",
30
+ "STATE_TRACE_CAPACITY_LIMIT": "256"
31
+ }
19
32
  }
20
33
  }
21
34
  }
@@ -16,7 +16,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
16
16
  why: 2026-04 same-day batch collision — when two batches target the same role, `npx job-forge merge` updates the existing day-file row rather than appending, so grepping day files alone misses earlier-batch applies; merged/*.tsv is the only place the breadcrumb remains
17
17
 
18
18
  - [H3] Before every batch of `task` dispatches that will use Geometra, call `geometra_list_sessions` then `geometra_disconnect({closeBrowser: true})`. Every round, no exceptions. Name this cleanup as an explicit "step 0" in your first-response plan for any multi-apply request — it is the most frequently skipped guardrail in practice, and skipping it produces cascade "Not connected" failures on the next dispatch.
19
- why: if any prior subagent aborted mid-flow, its Chromium session stays stuck in the MCP pool and the next `geometra_connect` fails with "Not connected"; the disconnect is a no-op when the pool is empty but a poison-cure when it isn't; vocalizing it up-front doubles the odds it actually runs
19
+ why: aborted subagents can leave Chromium sessions stuck in the MCP pool. Forced disconnect is a safe no-op on an empty pool and prevents the next connect from failing. Naming it up front improves compliance
20
20
 
21
21
  - [H4] In multi-job mode, the orchestrator session MUST NOT call `geometra_fill_form`, `geometra_run_actions`, `geometra_pick_listbox_option`, or `geometra_fill_otp` directly. Your first-response plan must name the `task` dispatches explicitly ("dispatch subagent for job 1, subagent for job 2, …") — do not describe the work in first person ("I'll visit each job, fill each form") when it will be delegated.
22
22
  why: repeated Geometra calls in the orchestrator bloat the cache prefix — this is the 2026-04 "apply to 20 jobs" 341-msg incident where each turn re-processed 100K+ fresh tokens instead of reading from cache; first-person narration is a leading indicator that the agent is mentally queueing work for itself rather than a subagent
@@ -38,13 +38,10 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
38
38
  - [D2] Route subagent work by cost tier. `@general-free`: procedural — form-fill, TSV merge, verify, OTP retrieval, portal scan metadata extraction, one-shot structured-field transforms. `@general-paid`: quality-sensitive — offer evaluation narrative Blocks A-F, cover letters, "Why X?" answers, STAR interview stories, LinkedIn outreach. `@glm-minimal`: narrow ≤5K-input one-shot extract/classify jobs that do not need context.
39
39
  why: GLM 5.1 doesn't discount cache reads so procedural work there costs ~10×; free-tier models handle procedural work fine empirically (`opencode/big-pickle` processed 1000+ messages at $0)
40
40
 
41
- - [D3] Upgrade `apply` routing to `@general-paid` when offer score ≥ 4.0/5, when user flags "top-tier / dream job / high-stakes", or when late-stage pipeline (post-screens).
42
- why: form-fill flows are 6+ steps; free-tier sometimes aborts mid-flow on large Greenhouse/Workday schemas; paid tier has more headroom
41
+ - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
42
+ why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
43
43
 
44
- - [D3f] **Provider-failure downgrade on `apply` (all harnesses; OpenCode + OpenRouter especially):** If you dispatched `@general-paid` per [D3] and that subagent fails or exhausts retries with provider-side errors copy mentioning Venice / Diem / Chutes, "insufficient" USD/credits/funds/balance, HTTP 402/429, overload / temporarily unavailable re-dispatch the **same apply URL** once on `@general-free` before marking FAILED. Do not abandon the role solely because the upgraded tier hit a pool-specific limit.
45
- why: `@general-paid` on OpenCode still uses free OpenRouter model ids; Venice-style balance errors are a backend-route issue, not proof that procedural `@general-free` cannot complete the same Greenhouse-style flow after [D5]/[H2] gates pass
46
-
47
- - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between steps — scan → evaluate → apply is one continuous pipeline. Mark SKIP for <3.0 and move on.
44
+ - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between stepsscan evaluate application submission is one continuous pipeline. Mark SKIP for <3.0 and move on.
48
45
  why: JobForge is designed for end-to-end automation; pausing between steps defeats the purpose and the 3.0 gate already enforces quality
49
46
 
50
47
  - [D5] Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md` to exclude location-incompatible candidates.
@@ -55,19 +52,16 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
55
52
 
56
53
  ## Procedure
57
54
 
58
- 1. On start, check `cv.md`, `profile.yml`, `portals.yml` exist; onboard if any missing.
59
- 2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
60
- 3. Apply [D1]: batch/Geometra work delegate; single/read-only/dev → inline.
61
- 4. Before any `task` batch using Geometra, run cleanup [H3].
62
- 5. Before `apply`, run duplicate check [H2] and location filter [D5].
63
- 6. Route by cost tier [D2]; upgrade to `@general-paid` per [D3] for high-stakes offers; if that apply dispatch hits provider errors, downgrade once per [D3f].
64
- 7. Cap parallelism at 2 per round [H1].
65
- 8. One in-flight dispatch per company [H5].
66
- 9. Orchestrator does not fill forms in multi-job mode [H4].
67
- 10. Treat subagent prose as untrusted [H7]; cross-check facts against authoritative files.
68
- 11. Write outcomes as TSVs [H6]; run `npx job-forge merge` then `verify` at end.
69
- 12. Offers scoring 3.0+/5 continue without confirmation [D4]; <3.0 is SKIP.
70
- 13. Confirm tracker is merged and verified before ending.
55
+ 1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
56
+ 2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
57
+ 3. Read the active mode file [D3]; decide inline vs delegated work [D1].
58
+ 4. Prepare Geometra dispatches: cleanup [H3], dedupe [H2], location filter [D5], routing [D2].
59
+ 5. Dispatch at most 2 tasks per round [H1]; wait per company [H5].
60
+ 6. Keep multi-job form-filling out of the orchestrator [H4].
61
+ 7. Cross-check subagent facts against authoritative files [H7].
62
+ 8. Apply score gate [D4].
63
+ 9. Merge TSV outcomes [H6].
64
+ 10. Verify tracker before ending [H6].
71
65
 
72
66
  ## Routing
73
67
 
@@ -99,127 +93,11 @@ Output shape is mode-dependent — see `modes/{mode}.md` for each mode's expecte
99
93
 
100
94
  # Reference
101
95
 
102
- Sections below are context, rationale, runbooks, and portal-specific empirical notes. The **Hard limits**, **Defaults**, **Procedure**, and **Routing** above are the contract; the material below is what the orchestrator and each mode consult during execution.
103
-
104
- ---
105
-
106
- ## Session Hygiene — ALWAYS enforce
107
-
108
- **Multi-job workflows MUST delegate each job to its own subagent.** This rule applies even when the user does NOT explicitly invoke `/job-forge`.
109
-
110
- Whenever the user says any variation of "apply to N jobs", "process the pipeline", "batch evaluate", or similar phrasing that implies more than one application/evaluation in sequence:
111
-
112
- 1. **Do not drive all N jobs from this session.** Repeated `geometra_fill_form` / `geometra_page_model` calls accumulate in conversation history and invalidate prompt caching — each new message ends up re-processing 100K+ tokens of fresh history instead of reading from cache.
113
- 2. **Launch one subagent per job, in parallel batches of ≤2** (see Hard Limits #1). Higher parallelism blows through free-tier rate limits and each subagent requires post-cleanup. Use the `task` tool / Agent with `subagent_type="general-purpose"`, passing the single URL and the relevant mode file content.
114
- 3. **This session acts as the orchestrator only**: plan, pick the jobs, dispatch subagents, aggregate results. No Geometra form-filling in this session unless it's a single one-off application.
115
-
116
- **Why:** observed on a real run — a 341-msg "apply to 20 jobs" session had `cache_read ~1.8K` on 5 messages where input ballooned to 100K-144K tokens. A 40-msg orchestrator session that delegates instead stays under 40K input max with cache reads at full 100K+. Same work, ~5× fewer effective tokens.
117
-
118
- **Verify after running:** `npx job-forge tokens --session <id>` — any message with `cache_read < 5K` and `input > 50K` is a cache-bust; next time split that work across subagents.
119
-
120
- **Exception:** evaluation-only or tracker-only work (no Geometra, no repeated tool calls) can proceed in a single session. The rule targets tool-heavy multi-step loops.
121
-
122
- **Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md`** to exclude location-incompatible candidates. Catches the common case where an evaluated role has the right role-shape but a deal-breaking location that profile.yml already rules out.
123
-
124
- ---
125
-
126
- ## Subagent Routing — which agent for which task
127
-
128
- The harness ships three subagents (see `.opencode/agents/`). The orchestrator MUST route work by cost tier, not pick the default for everything. **GLM 5.1 does not discount cache reads**, so running procedural work on it costs ~10× what it would on a cache-discounting model. Free-tier models handle procedural work fine (confirmed empirically: `opencode/big-pickle` processed 1000+ messages at $0 in prior runs).
129
-
130
- | Task type | Subagent | Why |
131
- |-----------|----------|-----|
132
- | Drive Geometra form-fill / submit (atomic `run_actions`) | `@general-free` | Procedural; label-driven; deterministic |
133
- | Merge TSVs, run `verify-pipeline.mjs`, dedup | `@general-free` | Script-driven; no writing quality needed |
134
- | OTP retrieval via Gmail MCP + `geometra_fill_otp` | `@general-free` | Fixed-shape lookup + input |
135
- | Scan portals, extract offer metadata, return structured records (see schema below) | `@general-free` | Structured output; no judgment |
136
- | Evaluation narrative — Blocks A-F per `modes/offer.md` | `@general-paid` | Judgment + writing quality |
137
- | Cover letter, "Why X?" answers, Section G drafts | `@general-paid` | Tone and specificity matter |
138
- | STAR+R interview stories, story-bank curation | `@general-paid` | Quality signals seniority |
139
- | LinkedIn outreach messages (`modes/contact.md`) | `@general-paid` | First impression |
140
- | "Extract N fields from this text → JSON" (≤5K input) | `@glm-minimal` | One-shot transform; no context needed |
141
- | "Classify this JD as archetype X/Y/Z" | `@glm-minimal` | Narrow, structured output |
142
-
143
- **Example JSON shape for the "extract / emit JSON" subagent rows above** (use this exact key set when delegating a portal-scan / extract task):
144
-
145
- ```json
146
- {
147
- "company": "Acme",
148
- "role": "Senior Backend Engineer",
149
- "location": "Remote (US)",
150
- "comp_range_usd": "180000-220000",
151
- "archetype": "backend-platform",
152
- "url": "https://..."
153
- }
154
- ```
155
-
156
- **Rule:** when you (the orchestrator) delegate a task, pick the cheapest agent that can do it well. Do NOT route every subagent through the same tier. Auto-pipeline mode MUST split a single job across `@general-paid` (evaluation) and `@general-free` (PDF gen + tracker + apply), not run it all on one model.
157
-
158
- **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.
159
-
160
- ### When to delegate
161
-
162
- **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.
163
-
164
- **Delegate when:**
165
- - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
166
- - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
167
- - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
168
-
169
- **Do NOT delegate — orchestrate inline:**
170
- - Single-offer evaluation (text-heavy, not tool-heavy)
171
- - Development / bug-fix / file-editing tasks
172
- - `tracker` and other read-only modes
173
- - Single-company scan, single-URL check
174
- - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
175
-
176
- **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.
177
-
178
- ---
179
-
180
- ## Ethical Use -- CRITICAL
181
-
182
- **Applying to jobs via Geometra MCP is NOT impersonation.** The agent uses the user's credentials, CV, and information to apply on their behalf. This is the same as the user applying themselves — the agent is an extension of the user's will. Think of it as a browser automation tool that fills forms with the user's data.
183
-
184
- **This system is designed for quality, not quantity.** The goal is to help the user find and apply to roles where there is a genuine match -- not to spam companies with mass applications.
185
-
186
- - **Continuous pipeline for 3.0+ offers:** When scanning, evaluating, and applying — run the full pipeline end-to-end without pausing for confirmation. Scan → evaluate → fill form → submit for any offer scoring 3.0/5 or above. Do NOT stop between steps to ask "want me to continue?" — just do it.
187
- - **Auto-submit:** For offers scoring 3.0+/5, fill and submit the application automatically. For offers below 3.0/5, mark as SKIP and move on.
188
- - **Still respect quality:** Only apply where there is a genuine match (3.0+ ensures this). Auto-SKIP anything below 3.0.
189
- - **Respect recruiters' time.** Every application a human reads costs someone's attention. Only send what's worth reading.
190
-
191
- ---
192
-
193
- ## Offer Verification -- MANDATORY
194
-
195
- **Read local artifacts before the network.** If `reports/` already contains this posting URL (or company+role with a full JD in the body), **Read** that report for verification or evaluation instead of WebFetch/Geometra. If `data/pipeline.md` or `jds/` points at frozen JD text (`local:jds/{file}` or pasted blocks), **Read** that first. Reuse JD text already in the same conversation — do not fetch the same URL twice. (The JD extraction section at the top of `modes/auto-pipeline.md` and its "at most once per session" rule are the detailed contract.)
196
-
197
- **When Geometra MCP is available** (interactive sessions), ALWAYS use it to verify offers:
198
- 1. `geometra_connect` to the URL (via proxy)
199
- 2. `geometra_page_model` to read structured page content
200
- 3. Only footer/navbar without JD = closed. Title + description + Apply = active.
201
-
202
- **When Geometra MCP is NOT available** (batch workers via `opencode run`, headless environments):
203
- 1. Use WebFetch to retrieve the page content
204
- 2. Check for JD text, job title, and apply button/link in the response
205
- 3. If WebFetch returns only a shell/navbar (no JD content), mark the offer as `**Verification: unconfirmed**` in the report header
206
- 4. Do NOT skip the evaluation — proceed but flag the uncertainty so the user can verify manually before applying
207
-
208
- The goal is to never waste time on closed offers, but also never silently assume a role is active when verification was incomplete.
209
-
210
- ### Canonical MCP tools (quick reference)
211
-
212
- Pick tools by name directly — reduces unnecessary tool discovery:
213
-
214
- | Task | Preferred tools |
215
- |------|------------------|
216
- | JD from URL | Greenhouse boards API when the URL matches (see JD extraction in `modes/auto-pipeline.md`) → else `geometra_connect` + `geometra_page_model` → else WebFetch → WebSearch last |
217
- | Offer still live? | Same as JD when Geometra is available; else WebFetch per above |
218
- | One apply subagent (single job) | One `geometra_connect` per job URL; reuse `sessionId` through schema + fill; submit via atomic `geometra_run_actions` per `modes/apply.md` [H1]. Do **not** `geometra_disconnect` between `geometra_form_schema` and submit on the same form unless recovery requires it |
219
- | Chromium pool between orchestrator dispatch rounds | `geometra_list_sessions` + `geometra_disconnect({ closeBrowser: true })` per Hard limit [H3] — orchestrator-only; not a substitute for finishing the in-subagent form flow |
220
-
221
- @modes/reference-setup.md
96
+ The sections above are the shared contract. Load detailed context on demand:
222
97
 
223
- @modes/reference-portals.md
98
+ - `modes/{mode}.md` for the active mode procedure, output shape, and mode-specific routing.
99
+ - `modes/reference-setup.md` for onboarding, tracker layout, states, and profile/CV setup.
100
+ - `modes/reference-portals.md` for OTP, residential proxy, and MCP configuration.
101
+ - `modes/reference-geometra.md` for form-fill patterns, portal failures, cleanup runbooks, and session recovery.
224
102
 
225
- @modes/reference-geometra.md
103
+ Do not pre-load all reference files. Read only the active mode file and the reference file needed for the current blocker.
package/.mcp.json CHANGED
@@ -16,6 +16,19 @@
16
16
  "env": {
17
17
  "DISABLE_HTTP": "true"
18
18
  }
19
+ },
20
+ "state-trace": {
21
+ "command": "uvx",
22
+ "args": [
23
+ "--from",
24
+ "state-trace[mcp]",
25
+ "state-trace-mcp"
26
+ ],
27
+ "env": {
28
+ "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
29
+ "STATE_TRACE_NAMESPACE": "job-forge",
30
+ "STATE_TRACE_CAPACITY_LIMIT": "256"
31
+ }
19
32
  }
20
33
  }
21
34
  }
package/AGENTS.md CHANGED
@@ -11,7 +11,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
11
11
  why: 2026-04 same-day batch collision — when two batches target the same role, `npx job-forge merge` updates the existing day-file row rather than appending, so grepping day files alone misses earlier-batch applies; merged/*.tsv is the only place the breadcrumb remains
12
12
 
13
13
  - [H3] Before every batch of `task` dispatches that will use Geometra, call `geometra_list_sessions` then `geometra_disconnect({closeBrowser: true})`. Every round, no exceptions. Name this cleanup as an explicit "step 0" in your first-response plan for any multi-apply request — it is the most frequently skipped guardrail in practice, and skipping it produces cascade "Not connected" failures on the next dispatch.
14
- why: if any prior subagent aborted mid-flow, its Chromium session stays stuck in the MCP pool and the next `geometra_connect` fails with "Not connected"; the disconnect is a no-op when the pool is empty but a poison-cure when it isn't; vocalizing it up-front doubles the odds it actually runs
14
+ why: aborted subagents can leave Chromium sessions stuck in the MCP pool. Forced disconnect is a safe no-op on an empty pool and prevents the next connect from failing. Naming it up front improves compliance
15
15
 
16
16
  - [H4] In multi-job mode, the orchestrator session MUST NOT call `geometra_fill_form`, `geometra_run_actions`, `geometra_pick_listbox_option`, or `geometra_fill_otp` directly. Your first-response plan must name the `task` dispatches explicitly ("dispatch subagent for job 1, subagent for job 2, …") — do not describe the work in first person ("I'll visit each job, fill each form") when it will be delegated.
17
17
  why: repeated Geometra calls in the orchestrator bloat the cache prefix — this is the 2026-04 "apply to 20 jobs" 341-msg incident where each turn re-processed 100K+ fresh tokens instead of reading from cache; first-person narration is a leading indicator that the agent is mentally queueing work for itself rather than a subagent
@@ -33,13 +33,10 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
33
33
  - [D2] Route subagent work by cost tier. `@general-free`: procedural — form-fill, TSV merge, verify, OTP retrieval, portal scan metadata extraction, one-shot structured-field transforms. `@general-paid`: quality-sensitive — offer evaluation narrative Blocks A-F, cover letters, "Why X?" answers, STAR interview stories, LinkedIn outreach. `@glm-minimal`: narrow ≤5K-input one-shot extract/classify jobs that do not need context.
34
34
  why: GLM 5.1 doesn't discount cache reads so procedural work there costs ~10×; free-tier models handle procedural work fine empirically (`opencode/big-pickle` processed 1000+ messages at $0)
35
35
 
36
- - [D3] Upgrade `apply` routing to `@general-paid` when offer score ≥ 4.0/5, when user flags "top-tier / dream job / high-stakes", or when late-stage pipeline (post-screens).
37
- why: form-fill flows are 6+ steps; free-tier sometimes aborts mid-flow on large Greenhouse/Workday schemas; paid tier has more headroom
36
+ - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
37
+ why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
38
38
 
39
- - [D3f] **Provider-failure downgrade on `apply` (all harnesses; OpenCode + OpenRouter especially):** If you dispatched `@general-paid` per [D3] and that subagent fails or exhausts retries with provider-side errors copy mentioning Venice / Diem / Chutes, "insufficient" USD/credits/funds/balance, HTTP 402/429, overload / temporarily unavailable re-dispatch the **same apply URL** once on `@general-free` before marking FAILED. Do not abandon the role solely because the upgraded tier hit a pool-specific limit.
40
- why: `@general-paid` on OpenCode still uses free OpenRouter model ids; Venice-style balance errors are a backend-route issue, not proof that procedural `@general-free` cannot complete the same Greenhouse-style flow after [D5]/[H2] gates pass
41
-
42
- - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between steps — scan → evaluate → apply is one continuous pipeline. Mark SKIP for <3.0 and move on.
39
+ - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between stepsscan evaluate application submission is one continuous pipeline. Mark SKIP for <3.0 and move on.
43
40
  why: JobForge is designed for end-to-end automation; pausing between steps defeats the purpose and the 3.0 gate already enforces quality
44
41
 
45
42
  - [D5] Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md` to exclude location-incompatible candidates.
@@ -50,19 +47,16 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
50
47
 
51
48
  ## Procedure
52
49
 
53
- 1. On start, check `cv.md`, `profile.yml`, `portals.yml` exist; onboard if any missing.
54
- 2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
55
- 3. Apply [D1]: batch/Geometra work delegate; single/read-only/dev → inline.
56
- 4. Before any `task` batch using Geometra, run cleanup [H3].
57
- 5. Before `apply`, run duplicate check [H2] and location filter [D5].
58
- 6. Route by cost tier [D2]; upgrade to `@general-paid` per [D3] for high-stakes offers; if that apply dispatch hits provider errors, downgrade once per [D3f].
59
- 7. Cap parallelism at 2 per round [H1].
60
- 8. One in-flight dispatch per company [H5].
61
- 9. Orchestrator does not fill forms in multi-job mode [H4].
62
- 10. Treat subagent prose as untrusted [H7]; cross-check facts against authoritative files.
63
- 11. Write outcomes as TSVs [H6]; run `npx job-forge merge` then `verify` at end.
64
- 12. Offers scoring 3.0+/5 continue without confirmation [D4]; <3.0 is SKIP.
65
- 13. Confirm tracker is merged and verified before ending.
50
+ 1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
51
+ 2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
52
+ 3. Read the active mode file [D3]; decide inline vs delegated work [D1].
53
+ 4. Prepare Geometra dispatches: cleanup [H3], dedupe [H2], location filter [D5], routing [D2].
54
+ 5. Dispatch at most 2 tasks per round [H1]; wait per company [H5].
55
+ 6. Keep multi-job form-filling out of the orchestrator [H4].
56
+ 7. Cross-check subagent facts against authoritative files [H7].
57
+ 8. Apply score gate [D4].
58
+ 9. Merge TSV outcomes [H6].
59
+ 10. Verify tracker before ending [H6].
66
60
 
67
61
  ## Routing
68
62
 
@@ -94,127 +88,11 @@ Output shape is mode-dependent — see `modes/{mode}.md` for each mode's expecte
94
88
 
95
89
  # Reference
96
90
 
97
- Sections below are context, rationale, runbooks, and portal-specific empirical notes. The **Hard limits**, **Defaults**, **Procedure**, and **Routing** above are the contract; the material below is what the orchestrator and each mode consult during execution.
98
-
99
- ---
100
-
101
- ## Session Hygiene — ALWAYS enforce
102
-
103
- **Multi-job workflows MUST delegate each job to its own subagent.** This rule applies even when the user does NOT explicitly invoke `/job-forge`.
104
-
105
- Whenever the user says any variation of "apply to N jobs", "process the pipeline", "batch evaluate", or similar phrasing that implies more than one application/evaluation in sequence:
106
-
107
- 1. **Do not drive all N jobs from this session.** Repeated `geometra_fill_form` / `geometra_page_model` calls accumulate in conversation history and invalidate prompt caching — each new message ends up re-processing 100K+ tokens of fresh history instead of reading from cache.
108
- 2. **Launch one subagent per job, in parallel batches of ≤2** (see Hard Limits #1). Higher parallelism blows through free-tier rate limits and each subagent requires post-cleanup. Use the `task` tool / Agent with `subagent_type="general-purpose"`, passing the single URL and the relevant mode file content.
109
- 3. **This session acts as the orchestrator only**: plan, pick the jobs, dispatch subagents, aggregate results. No Geometra form-filling in this session unless it's a single one-off application.
110
-
111
- **Why:** observed on a real run — a 341-msg "apply to 20 jobs" session had `cache_read ~1.8K` on 5 messages where input ballooned to 100K-144K tokens. A 40-msg orchestrator session that delegates instead stays under 40K input max with cache reads at full 100K+. Same work, ~5× fewer effective tokens.
112
-
113
- **Verify after running:** `npx job-forge tokens --session <id>` — any message with `cache_read < 5K` and `input > 50K` is a cache-bust; next time split that work across subagents.
114
-
115
- **Exception:** evaluation-only or tracker-only work (no Geometra, no repeated tool calls) can proceed in a single session. The rule targets tool-heavy multi-step loops.
116
-
117
- **Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md`** to exclude location-incompatible candidates. Catches the common case where an evaluated role has the right role-shape but a deal-breaking location that profile.yml already rules out.
118
-
119
- ---
120
-
121
- ## Subagent Routing — which agent for which task
122
-
123
- The harness ships three subagents (see `.opencode/agents/`). The orchestrator MUST route work by cost tier, not pick the default for everything. **GLM 5.1 does not discount cache reads**, so running procedural work on it costs ~10× what it would on a cache-discounting model. Free-tier models handle procedural work fine (confirmed empirically: `opencode/big-pickle` processed 1000+ messages at $0 in prior runs).
124
-
125
- | Task type | Subagent | Why |
126
- |-----------|----------|-----|
127
- | Drive Geometra form-fill / submit (atomic `run_actions`) | `@general-free` | Procedural; label-driven; deterministic |
128
- | Merge TSVs, run `verify-pipeline.mjs`, dedup | `@general-free` | Script-driven; no writing quality needed |
129
- | OTP retrieval via Gmail MCP + `geometra_fill_otp` | `@general-free` | Fixed-shape lookup + input |
130
- | Scan portals, extract offer metadata, return structured records (see schema below) | `@general-free` | Structured output; no judgment |
131
- | Evaluation narrative — Blocks A-F per `modes/offer.md` | `@general-paid` | Judgment + writing quality |
132
- | Cover letter, "Why X?" answers, Section G drafts | `@general-paid` | Tone and specificity matter |
133
- | STAR+R interview stories, story-bank curation | `@general-paid` | Quality signals seniority |
134
- | LinkedIn outreach messages (`modes/contact.md`) | `@general-paid` | First impression |
135
- | "Extract N fields from this text → JSON" (≤5K input) | `@glm-minimal` | One-shot transform; no context needed |
136
- | "Classify this JD as archetype X/Y/Z" | `@glm-minimal` | Narrow, structured output |
137
-
138
- **Example JSON shape for the "extract / emit JSON" subagent rows above** (use this exact key set when delegating a portal-scan / extract task):
139
-
140
- ```json
141
- {
142
- "company": "Acme",
143
- "role": "Senior Backend Engineer",
144
- "location": "Remote (US)",
145
- "comp_range_usd": "180000-220000",
146
- "archetype": "backend-platform",
147
- "url": "https://..."
148
- }
149
- ```
150
-
151
- **Rule:** when you (the orchestrator) delegate a task, pick the cheapest agent that can do it well. Do NOT route every subagent through the same tier. Auto-pipeline mode MUST split a single job across `@general-paid` (evaluation) and `@general-free` (PDF gen + tracker + apply), not run it all on one model.
152
-
153
- **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.
154
-
155
- ### When to delegate
156
-
157
- **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.
158
-
159
- **Delegate when:**
160
- - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
161
- - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
162
- - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
163
-
164
- **Do NOT delegate — orchestrate inline:**
165
- - Single-offer evaluation (text-heavy, not tool-heavy)
166
- - Development / bug-fix / file-editing tasks
167
- - `tracker` and other read-only modes
168
- - Single-company scan, single-URL check
169
- - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
170
-
171
- **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.
172
-
173
- ---
174
-
175
- ## Ethical Use -- CRITICAL
176
-
177
- **Applying to jobs via Geometra MCP is NOT impersonation.** The agent uses the user's credentials, CV, and information to apply on their behalf. This is the same as the user applying themselves — the agent is an extension of the user's will. Think of it as a browser automation tool that fills forms with the user's data.
178
-
179
- **This system is designed for quality, not quantity.** The goal is to help the user find and apply to roles where there is a genuine match -- not to spam companies with mass applications.
180
-
181
- - **Continuous pipeline for 3.0+ offers:** When scanning, evaluating, and applying — run the full pipeline end-to-end without pausing for confirmation. Scan → evaluate → fill form → submit for any offer scoring 3.0/5 or above. Do NOT stop between steps to ask "want me to continue?" — just do it.
182
- - **Auto-submit:** For offers scoring 3.0+/5, fill and submit the application automatically. For offers below 3.0/5, mark as SKIP and move on.
183
- - **Still respect quality:** Only apply where there is a genuine match (3.0+ ensures this). Auto-SKIP anything below 3.0.
184
- - **Respect recruiters' time.** Every application a human reads costs someone's attention. Only send what's worth reading.
185
-
186
- ---
187
-
188
- ## Offer Verification -- MANDATORY
189
-
190
- **Read local artifacts before the network.** If `reports/` already contains this posting URL (or company+role with a full JD in the body), **Read** that report for verification or evaluation instead of WebFetch/Geometra. If `data/pipeline.md` or `jds/` points at frozen JD text (`local:jds/{file}` or pasted blocks), **Read** that first. Reuse JD text already in the same conversation — do not fetch the same URL twice. (The JD extraction section at the top of `modes/auto-pipeline.md` and its "at most once per session" rule are the detailed contract.)
191
-
192
- **When Geometra MCP is available** (interactive sessions), ALWAYS use it to verify offers:
193
- 1. `geometra_connect` to the URL (via proxy)
194
- 2. `geometra_page_model` to read structured page content
195
- 3. Only footer/navbar without JD = closed. Title + description + Apply = active.
196
-
197
- **When Geometra MCP is NOT available** (batch workers via `opencode run`, headless environments):
198
- 1. Use WebFetch to retrieve the page content
199
- 2. Check for JD text, job title, and apply button/link in the response
200
- 3. If WebFetch returns only a shell/navbar (no JD content), mark the offer as `**Verification: unconfirmed**` in the report header
201
- 4. Do NOT skip the evaluation — proceed but flag the uncertainty so the user can verify manually before applying
202
-
203
- The goal is to never waste time on closed offers, but also never silently assume a role is active when verification was incomplete.
204
-
205
- ### Canonical MCP tools (quick reference)
206
-
207
- Pick tools by name directly — reduces unnecessary tool discovery:
208
-
209
- | Task | Preferred tools |
210
- |------|------------------|
211
- | JD from URL | Greenhouse boards API when the URL matches (see JD extraction in `modes/auto-pipeline.md`) → else `geometra_connect` + `geometra_page_model` → else WebFetch → WebSearch last |
212
- | Offer still live? | Same as JD when Geometra is available; else WebFetch per above |
213
- | One apply subagent (single job) | One `geometra_connect` per job URL; reuse `sessionId` through schema + fill; submit via atomic `geometra_run_actions` per `modes/apply.md` [H1]. Do **not** `geometra_disconnect` between `geometra_form_schema` and submit on the same form unless recovery requires it |
214
- | Chromium pool between orchestrator dispatch rounds | `geometra_list_sessions` + `geometra_disconnect({ closeBrowser: true })` per Hard limit [H3] — orchestrator-only; not a substitute for finishing the in-subagent form flow |
215
-
216
- @modes/reference-setup.md
91
+ The sections above are the shared contract. Load detailed context on demand:
217
92
 
218
- @modes/reference-portals.md
93
+ - `modes/{mode}.md` for the active mode procedure, output shape, and mode-specific routing.
94
+ - `modes/reference-setup.md` for onboarding, tracker layout, states, and profile/CV setup.
95
+ - `modes/reference-portals.md` for OTP, residential proxy, and MCP configuration.
96
+ - `modes/reference-geometra.md` for form-fill patterns, portal failures, cleanup runbooks, and session recovery.
219
97
 
220
- @modes/reference-geometra.md
98
+ Do not pre-load all reference files. Read only the active mode file and the reference file needed for the current blocker.
package/CLAUDE.md CHANGED
@@ -11,7 +11,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
11
11
  why: 2026-04 same-day batch collision — when two batches target the same role, `npx job-forge merge` updates the existing day-file row rather than appending, so grepping day files alone misses earlier-batch applies; merged/*.tsv is the only place the breadcrumb remains
12
12
 
13
13
  - [H3] Before every batch of `task` dispatches that will use Geometra, call `geometra_list_sessions` then `geometra_disconnect({closeBrowser: true})`. Every round, no exceptions. Name this cleanup as an explicit "step 0" in your first-response plan for any multi-apply request — it is the most frequently skipped guardrail in practice, and skipping it produces cascade "Not connected" failures on the next dispatch.
14
- why: if any prior subagent aborted mid-flow, its Chromium session stays stuck in the MCP pool and the next `geometra_connect` fails with "Not connected"; the disconnect is a no-op when the pool is empty but a poison-cure when it isn't; vocalizing it up-front doubles the odds it actually runs
14
+ why: aborted subagents can leave Chromium sessions stuck in the MCP pool. Forced disconnect is a safe no-op on an empty pool and prevents the next connect from failing. Naming it up front improves compliance
15
15
 
16
16
  - [H4] In multi-job mode, the orchestrator session MUST NOT call `geometra_fill_form`, `geometra_run_actions`, `geometra_pick_listbox_option`, or `geometra_fill_otp` directly. Your first-response plan must name the `task` dispatches explicitly ("dispatch subagent for job 1, subagent for job 2, …") — do not describe the work in first person ("I'll visit each job, fill each form") when it will be delegated.
17
17
  why: repeated Geometra calls in the orchestrator bloat the cache prefix — this is the 2026-04 "apply to 20 jobs" 341-msg incident where each turn re-processed 100K+ fresh tokens instead of reading from cache; first-person narration is a leading indicator that the agent is mentally queueing work for itself rather than a subagent
@@ -33,13 +33,10 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
33
33
  - [D2] Route subagent work by cost tier. `@general-free`: procedural — form-fill, TSV merge, verify, OTP retrieval, portal scan metadata extraction, one-shot structured-field transforms. `@general-paid`: quality-sensitive — offer evaluation narrative Blocks A-F, cover letters, "Why X?" answers, STAR interview stories, LinkedIn outreach. `@glm-minimal`: narrow ≤5K-input one-shot extract/classify jobs that do not need context.
34
34
  why: GLM 5.1 doesn't discount cache reads so procedural work there costs ~10×; free-tier models handle procedural work fine empirically (`opencode/big-pickle` processed 1000+ messages at $0)
35
35
 
36
- - [D3] Upgrade `apply` routing to `@general-paid` when offer score ≥ 4.0/5, when user flags "top-tier / dream job / high-stakes", or when late-stage pipeline (post-screens).
37
- why: form-fill flows are 6+ steps; free-tier sometimes aborts mid-flow on large Greenhouse/Workday schemas; paid tier has more headroom
36
+ - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
37
+ why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
38
38
 
39
- - [D3f] **Provider-failure downgrade on `apply` (all harnesses; OpenCode + OpenRouter especially):** If you dispatched `@general-paid` per [D3] and that subagent fails or exhausts retries with provider-side errors copy mentioning Venice / Diem / Chutes, "insufficient" USD/credits/funds/balance, HTTP 402/429, overload / temporarily unavailable re-dispatch the **same apply URL** once on `@general-free` before marking FAILED. Do not abandon the role solely because the upgraded tier hit a pool-specific limit.
40
- why: `@general-paid` on OpenCode still uses free OpenRouter model ids; Venice-style balance errors are a backend-route issue, not proof that procedural `@general-free` cannot complete the same Greenhouse-style flow after [D5]/[H2] gates pass
41
-
42
- - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between steps — scan → evaluate → apply is one continuous pipeline. Mark SKIP for <3.0 and move on.
39
+ - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between stepsscan evaluate application submission is one continuous pipeline. Mark SKIP for <3.0 and move on.
43
40
  why: JobForge is designed for end-to-end automation; pausing between steps defeats the purpose and the 3.0 gate already enforces quality
44
41
 
45
42
  - [D5] Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md` to exclude location-incompatible candidates.
@@ -50,19 +47,16 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
50
47
 
51
48
  ## Procedure
52
49
 
53
- 1. On start, check `cv.md`, `profile.yml`, `portals.yml` exist; onboard if any missing.
54
- 2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
55
- 3. Apply [D1]: batch/Geometra work delegate; single/read-only/dev → inline.
56
- 4. Before any `task` batch using Geometra, run cleanup [H3].
57
- 5. Before `apply`, run duplicate check [H2] and location filter [D5].
58
- 6. Route by cost tier [D2]; upgrade to `@general-paid` per [D3] for high-stakes offers; if that apply dispatch hits provider errors, downgrade once per [D3f].
59
- 7. Cap parallelism at 2 per round [H1].
60
- 8. One in-flight dispatch per company [H5].
61
- 9. Orchestrator does not fill forms in multi-job mode [H4].
62
- 10. Treat subagent prose as untrusted [H7]; cross-check facts against authoritative files.
63
- 11. Write outcomes as TSVs [H6]; run `npx job-forge merge` then `verify` at end.
64
- 12. Offers scoring 3.0+/5 continue without confirmation [D4]; <3.0 is SKIP.
65
- 13. Confirm tracker is merged and verified before ending.
50
+ 1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
51
+ 2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
52
+ 3. Read the active mode file [D3]; decide inline vs delegated work [D1].
53
+ 4. Prepare Geometra dispatches: cleanup [H3], dedupe [H2], location filter [D5], routing [D2].
54
+ 5. Dispatch at most 2 tasks per round [H1]; wait per company [H5].
55
+ 6. Keep multi-job form-filling out of the orchestrator [H4].
56
+ 7. Cross-check subagent facts against authoritative files [H7].
57
+ 8. Apply score gate [D4].
58
+ 9. Merge TSV outcomes [H6].
59
+ 10. Verify tracker before ending [H6].
66
60
 
67
61
  ## Routing
68
62
 
@@ -94,127 +88,11 @@ Output shape is mode-dependent — see `modes/{mode}.md` for each mode's expecte
94
88
 
95
89
  # Reference
96
90
 
97
- Sections below are context, rationale, runbooks, and portal-specific empirical notes. The **Hard limits**, **Defaults**, **Procedure**, and **Routing** above are the contract; the material below is what the orchestrator and each mode consult during execution.
98
-
99
- ---
100
-
101
- ## Session Hygiene — ALWAYS enforce
102
-
103
- **Multi-job workflows MUST delegate each job to its own subagent.** This rule applies even when the user does NOT explicitly invoke `/job-forge`.
104
-
105
- Whenever the user says any variation of "apply to N jobs", "process the pipeline", "batch evaluate", or similar phrasing that implies more than one application/evaluation in sequence:
106
-
107
- 1. **Do not drive all N jobs from this session.** Repeated `geometra_fill_form` / `geometra_page_model` calls accumulate in conversation history and invalidate prompt caching — each new message ends up re-processing 100K+ tokens of fresh history instead of reading from cache.
108
- 2. **Launch one subagent per job, in parallel batches of ≤2** (see Hard Limits #1). Higher parallelism blows through free-tier rate limits and each subagent requires post-cleanup. Use the `task` tool / Agent with `subagent_type="general-purpose"`, passing the single URL and the relevant mode file content.
109
- 3. **This session acts as the orchestrator only**: plan, pick the jobs, dispatch subagents, aggregate results. No Geometra form-filling in this session unless it's a single one-off application.
110
-
111
- **Why:** observed on a real run — a 341-msg "apply to 20 jobs" session had `cache_read ~1.8K` on 5 messages where input ballooned to 100K-144K tokens. A 40-msg orchestrator session that delegates instead stays under 40K input max with cache reads at full 100K+. Same work, ~5× fewer effective tokens.
112
-
113
- **Verify after running:** `npx job-forge tokens --session <id>` — any message with `cache_read < 5K` and `input > 50K` is a cache-bust; next time split that work across subagents.
114
-
115
- **Exception:** evaluation-only or tracker-only work (no Geometra, no repeated tool calls) can proceed in a single session. The rule targets tool-heavy multi-step loops.
116
-
117
- **Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md`** to exclude location-incompatible candidates. Catches the common case where an evaluated role has the right role-shape but a deal-breaking location that profile.yml already rules out.
118
-
119
- ---
120
-
121
- ## Subagent Routing — which agent for which task
122
-
123
- The harness ships three subagents (see `.opencode/agents/`). The orchestrator MUST route work by cost tier, not pick the default for everything. **GLM 5.1 does not discount cache reads**, so running procedural work on it costs ~10× what it would on a cache-discounting model. Free-tier models handle procedural work fine (confirmed empirically: `opencode/big-pickle` processed 1000+ messages at $0 in prior runs).
124
-
125
- | Task type | Subagent | Why |
126
- |-----------|----------|-----|
127
- | Drive Geometra form-fill / submit (atomic `run_actions`) | `@general-free` | Procedural; label-driven; deterministic |
128
- | Merge TSVs, run `verify-pipeline.mjs`, dedup | `@general-free` | Script-driven; no writing quality needed |
129
- | OTP retrieval via Gmail MCP + `geometra_fill_otp` | `@general-free` | Fixed-shape lookup + input |
130
- | Scan portals, extract offer metadata, return structured records (see schema below) | `@general-free` | Structured output; no judgment |
131
- | Evaluation narrative — Blocks A-F per `modes/offer.md` | `@general-paid` | Judgment + writing quality |
132
- | Cover letter, "Why X?" answers, Section G drafts | `@general-paid` | Tone and specificity matter |
133
- | STAR+R interview stories, story-bank curation | `@general-paid` | Quality signals seniority |
134
- | LinkedIn outreach messages (`modes/contact.md`) | `@general-paid` | First impression |
135
- | "Extract N fields from this text → JSON" (≤5K input) | `@glm-minimal` | One-shot transform; no context needed |
136
- | "Classify this JD as archetype X/Y/Z" | `@glm-minimal` | Narrow, structured output |
137
-
138
- **Example JSON shape for the "extract / emit JSON" subagent rows above** (use this exact key set when delegating a portal-scan / extract task):
139
-
140
- ```json
141
- {
142
- "company": "Acme",
143
- "role": "Senior Backend Engineer",
144
- "location": "Remote (US)",
145
- "comp_range_usd": "180000-220000",
146
- "archetype": "backend-platform",
147
- "url": "https://..."
148
- }
149
- ```
150
-
151
- **Rule:** when you (the orchestrator) delegate a task, pick the cheapest agent that can do it well. Do NOT route every subagent through the same tier. Auto-pipeline mode MUST split a single job across `@general-paid` (evaluation) and `@general-free` (PDF gen + tracker + apply), not run it all on one model.
152
-
153
- **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.
154
-
155
- ### When to delegate
156
-
157
- **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.
158
-
159
- **Delegate when:**
160
- - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
161
- - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
162
- - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
163
-
164
- **Do NOT delegate — orchestrate inline:**
165
- - Single-offer evaluation (text-heavy, not tool-heavy)
166
- - Development / bug-fix / file-editing tasks
167
- - `tracker` and other read-only modes
168
- - Single-company scan, single-URL check
169
- - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
170
-
171
- **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.
172
-
173
- ---
174
-
175
- ## Ethical Use -- CRITICAL
176
-
177
- **Applying to jobs via Geometra MCP is NOT impersonation.** The agent uses the user's credentials, CV, and information to apply on their behalf. This is the same as the user applying themselves — the agent is an extension of the user's will. Think of it as a browser automation tool that fills forms with the user's data.
178
-
179
- **This system is designed for quality, not quantity.** The goal is to help the user find and apply to roles where there is a genuine match -- not to spam companies with mass applications.
180
-
181
- - **Continuous pipeline for 3.0+ offers:** When scanning, evaluating, and applying — run the full pipeline end-to-end without pausing for confirmation. Scan → evaluate → fill form → submit for any offer scoring 3.0/5 or above. Do NOT stop between steps to ask "want me to continue?" — just do it.
182
- - **Auto-submit:** For offers scoring 3.0+/5, fill and submit the application automatically. For offers below 3.0/5, mark as SKIP and move on.
183
- - **Still respect quality:** Only apply where there is a genuine match (3.0+ ensures this). Auto-SKIP anything below 3.0.
184
- - **Respect recruiters' time.** Every application a human reads costs someone's attention. Only send what's worth reading.
185
-
186
- ---
187
-
188
- ## Offer Verification -- MANDATORY
189
-
190
- **Read local artifacts before the network.** If `reports/` already contains this posting URL (or company+role with a full JD in the body), **Read** that report for verification or evaluation instead of WebFetch/Geometra. If `data/pipeline.md` or `jds/` points at frozen JD text (`local:jds/{file}` or pasted blocks), **Read** that first. Reuse JD text already in the same conversation — do not fetch the same URL twice. (The JD extraction section at the top of `modes/auto-pipeline.md` and its "at most once per session" rule are the detailed contract.)
191
-
192
- **When Geometra MCP is available** (interactive sessions), ALWAYS use it to verify offers:
193
- 1. `geometra_connect` to the URL (via proxy)
194
- 2. `geometra_page_model` to read structured page content
195
- 3. Only footer/navbar without JD = closed. Title + description + Apply = active.
196
-
197
- **When Geometra MCP is NOT available** (batch workers via `opencode run`, headless environments):
198
- 1. Use WebFetch to retrieve the page content
199
- 2. Check for JD text, job title, and apply button/link in the response
200
- 3. If WebFetch returns only a shell/navbar (no JD content), mark the offer as `**Verification: unconfirmed**` in the report header
201
- 4. Do NOT skip the evaluation — proceed but flag the uncertainty so the user can verify manually before applying
202
-
203
- The goal is to never waste time on closed offers, but also never silently assume a role is active when verification was incomplete.
204
-
205
- ### Canonical MCP tools (quick reference)
206
-
207
- Pick tools by name directly — reduces unnecessary tool discovery:
208
-
209
- | Task | Preferred tools |
210
- |------|------------------|
211
- | JD from URL | Greenhouse boards API when the URL matches (see JD extraction in `modes/auto-pipeline.md`) → else `geometra_connect` + `geometra_page_model` → else WebFetch → WebSearch last |
212
- | Offer still live? | Same as JD when Geometra is available; else WebFetch per above |
213
- | One apply subagent (single job) | One `geometra_connect` per job URL; reuse `sessionId` through schema + fill; submit via atomic `geometra_run_actions` per `modes/apply.md` [H1]. Do **not** `geometra_disconnect` between `geometra_form_schema` and submit on the same form unless recovery requires it |
214
- | Chromium pool between orchestrator dispatch rounds | `geometra_list_sessions` + `geometra_disconnect({ closeBrowser: true })` per Hard limit [H3] — orchestrator-only; not a substitute for finishing the in-subagent form flow |
215
-
216
- @modes/reference-setup.md
91
+ The sections above are the shared contract. Load detailed context on demand:
217
92
 
218
- @modes/reference-portals.md
93
+ - `modes/{mode}.md` for the active mode procedure, output shape, and mode-specific routing.
94
+ - `modes/reference-setup.md` for onboarding, tracker layout, states, and profile/CV setup.
95
+ - `modes/reference-portals.md` for OTP, residential proxy, and MCP configuration.
96
+ - `modes/reference-geometra.md` for form-fill patterns, portal failures, cleanup runbooks, and session recovery.
219
97
 
220
- @modes/reference-geometra.md
98
+ Do not pre-load all reference files. Read only the active mode file and the reference file needed for the current blocker.
package/README.md CHANGED
@@ -25,7 +25,13 @@ npm install
25
25
  opencode
26
26
  ```
27
27
 
28
- The scaffolded `opencode.json` already has the Geometra MCP (browser automation + PDF) and Gmail MCP (reading replies) wired up — they launch automatically the first time opencode starts. `npm install` also materializes symlinks for every supported agent harness — OpenCode, Cursor, Claude Code, and Codex — so you can run `opencode`, `cursor`, `claude`, or `codex` in the same project and each picks up the shared MCP config and instructions.
28
+ The scaffolded `opencode.json` already has three MCPs wired up — they launch automatically the first time opencode starts:
29
+
30
+ - **Geometra** — browser automation + PDF generation
31
+ - **Gmail** — reads replies from recruiters
32
+ - **state-trace** — typed working memory for cross-session context (resumed batches, recent decisions, repeated portal quirks). Spawned via `uvx`; install once with `brew install uv` (or `pipx install uv`) — no other setup.
33
+
34
+ `npm install` also materializes symlinks for every supported agent harness — OpenCode, Cursor, Claude Code, and Codex — so you can run `opencode`, `cursor`, `claude`, or `codex` in the same project and each picks up the shared MCP config and instructions.
29
35
 
30
36
  Then fill in `cv.md`, `config/profile.yml`, and `portals.yml` with your personal data, paste a job URL into opencode, and JobForge evaluates + tracks it.
31
37
 
package/docs/SETUP.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  - [opencode](https://opencode.ai) installed and configured
6
6
  - Node.js 18+ (for the CLI, PDF generation, and tracker scripts)
7
+ - [`uv`](https://docs.astral.sh/uv/) installed (`brew install uv` on macOS, or `pipx install uv`). Used by the state-trace MCP to spawn its Python entry point on demand via `uvx`. Without `uv`, the state-trace MCP fails to start; the rest of JobForge keeps working.
7
8
  - (Optional) Go (for the dashboard TUI) — use a toolchain that satisfies the `go` directive in [`dashboard/go.mod`](../dashboard/go.mod)
8
9
 
9
10
  ## Quick Start (two paths)
@@ -11,7 +11,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
11
11
  why: 2026-04 same-day batch collision — when two batches target the same role, `npx job-forge merge` updates the existing day-file row rather than appending, so grepping day files alone misses earlier-batch applies; merged/*.tsv is the only place the breadcrumb remains
12
12
 
13
13
  - [H3] Before every batch of `task` dispatches that will use Geometra, call `geometra_list_sessions` then `geometra_disconnect({closeBrowser: true})`. Every round, no exceptions. Name this cleanup as an explicit "step 0" in your first-response plan for any multi-apply request — it is the most frequently skipped guardrail in practice, and skipping it produces cascade "Not connected" failures on the next dispatch.
14
- why: if any prior subagent aborted mid-flow, its Chromium session stays stuck in the MCP pool and the next `geometra_connect` fails with "Not connected"; the disconnect is a no-op when the pool is empty but a poison-cure when it isn't; vocalizing it up-front doubles the odds it actually runs
14
+ why: aborted subagents can leave Chromium sessions stuck in the MCP pool. Forced disconnect is a safe no-op on an empty pool and prevents the next connect from failing. Naming it up front improves compliance
15
15
 
16
16
  - [H4] In multi-job mode, the orchestrator session MUST NOT call `geometra_fill_form`, `geometra_run_actions`, `geometra_pick_listbox_option`, or `geometra_fill_otp` directly. Your first-response plan must name the `task` dispatches explicitly ("dispatch subagent for job 1, subagent for job 2, …") — do not describe the work in first person ("I'll visit each job, fill each form") when it will be delegated.
17
17
  why: repeated Geometra calls in the orchestrator bloat the cache prefix — this is the 2026-04 "apply to 20 jobs" 341-msg incident where each turn re-processed 100K+ fresh tokens instead of reading from cache; first-person narration is a leading indicator that the agent is mentally queueing work for itself rather than a subagent
@@ -33,13 +33,10 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
33
33
  - [D2] Route subagent work by cost tier. `@general-free`: procedural — form-fill, TSV merge, verify, OTP retrieval, portal scan metadata extraction, one-shot structured-field transforms. `@general-paid`: quality-sensitive — offer evaluation narrative Blocks A-F, cover letters, "Why X?" answers, STAR interview stories, LinkedIn outreach. `@glm-minimal`: narrow ≤5K-input one-shot extract/classify jobs that do not need context.
34
34
  why: GLM 5.1 doesn't discount cache reads so procedural work there costs ~10×; free-tier models handle procedural work fine empirically (`opencode/big-pickle` processed 1000+ messages at $0)
35
35
 
36
- - [D3] Upgrade `apply` routing to `@general-paid` when offer score ≥ 4.0/5, when user flags "top-tier / dream job / high-stakes", or when late-stage pipeline (post-screens).
37
- why: form-fill flows are 6+ steps; free-tier sometimes aborts mid-flow on large Greenhouse/Workday schemas; paid tier has more headroom
36
+ - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
37
+ why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
38
38
 
39
- - [D3f] **Provider-failure downgrade on `apply` (all harnesses; OpenCode + OpenRouter especially):** If you dispatched `@general-paid` per [D3] and that subagent fails or exhausts retries with provider-side errors copy mentioning Venice / Diem / Chutes, "insufficient" USD/credits/funds/balance, HTTP 402/429, overload / temporarily unavailable re-dispatch the **same apply URL** once on `@general-free` before marking FAILED. Do not abandon the role solely because the upgraded tier hit a pool-specific limit.
40
- why: `@general-paid` on OpenCode still uses free OpenRouter model ids; Venice-style balance errors are a backend-route issue, not proof that procedural `@general-free` cannot complete the same Greenhouse-style flow after [D5]/[H2] gates pass
41
-
42
- - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between steps — scan → evaluate → apply is one continuous pipeline. Mark SKIP for <3.0 and move on.
39
+ - [D4] Auto-submit for offers scoring 3.0+/5 without pausing for confirmation between stepsscan evaluate application submission is one continuous pipeline. Mark SKIP for <3.0 and move on.
43
40
  why: JobForge is designed for end-to-end automation; pausing between steps defeats the purpose and the 3.0 gate already enforces quality
44
41
 
45
42
  - [D5] Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md` to exclude location-incompatible candidates.
@@ -50,19 +47,16 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
50
47
 
51
48
  ## Procedure
52
49
 
53
- 1. On start, check `cv.md`, `profile.yml`, `portals.yml` exist; onboard if any missing.
54
- 2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
55
- 3. Apply [D1]: batch/Geometra work delegate; single/read-only/dev → inline.
56
- 4. Before any `task` batch using Geometra, run cleanup [H3].
57
- 5. Before `apply`, run duplicate check [H2] and location filter [D5].
58
- 6. Route by cost tier [D2]; upgrade to `@general-paid` per [D3] for high-stakes offers; if that apply dispatch hits provider errors, downgrade once per [D3f].
59
- 7. Cap parallelism at 2 per round [H1].
60
- 8. One in-flight dispatch per company [H5].
61
- 9. Orchestrator does not fill forms in multi-job mode [H4].
62
- 10. Treat subagent prose as untrusted [H7]; cross-check facts against authoritative files.
63
- 11. Write outcomes as TSVs [H6]; run `npx job-forge merge` then `verify` at end.
64
- 12. Offers scoring 3.0+/5 continue without confirmation [D4]; <3.0 is SKIP.
65
- 13. Confirm tracker is merged and verified before ending.
50
+ 1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
51
+ 2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
52
+ 3. Read the active mode file [D3]; decide inline vs delegated work [D1].
53
+ 4. Prepare Geometra dispatches: cleanup [H3], dedupe [H2], location filter [D5], routing [D2].
54
+ 5. Dispatch at most 2 tasks per round [H1]; wait per company [H5].
55
+ 6. Keep multi-job form-filling out of the orchestrator [H4].
56
+ 7. Cross-check subagent facts against authoritative files [H7].
57
+ 8. Apply score gate [D4].
58
+ 9. Merge TSV outcomes [H6].
59
+ 10. Verify tracker before ending [H6].
66
60
 
67
61
  ## Routing
68
62
 
@@ -94,127 +88,11 @@ Output shape is mode-dependent — see `modes/{mode}.md` for each mode's expecte
94
88
 
95
89
  # Reference
96
90
 
97
- Sections below are context, rationale, runbooks, and portal-specific empirical notes. The **Hard limits**, **Defaults**, **Procedure**, and **Routing** above are the contract; the material below is what the orchestrator and each mode consult during execution.
98
-
99
- ---
100
-
101
- ## Session Hygiene — ALWAYS enforce
102
-
103
- **Multi-job workflows MUST delegate each job to its own subagent.** This rule applies even when the user does NOT explicitly invoke `/job-forge`.
104
-
105
- Whenever the user says any variation of "apply to N jobs", "process the pipeline", "batch evaluate", or similar phrasing that implies more than one application/evaluation in sequence:
106
-
107
- 1. **Do not drive all N jobs from this session.** Repeated `geometra_fill_form` / `geometra_page_model` calls accumulate in conversation history and invalidate prompt caching — each new message ends up re-processing 100K+ tokens of fresh history instead of reading from cache.
108
- 2. **Launch one subagent per job, in parallel batches of ≤2** (see Hard Limits #1). Higher parallelism blows through free-tier rate limits and each subagent requires post-cleanup. Use the `task` tool / Agent with `subagent_type="general-purpose"`, passing the single URL and the relevant mode file content.
109
- 3. **This session acts as the orchestrator only**: plan, pick the jobs, dispatch subagents, aggregate results. No Geometra form-filling in this session unless it's a single one-off application.
110
-
111
- **Why:** observed on a real run — a 341-msg "apply to 20 jobs" session had `cache_read ~1.8K` on 5 messages where input ballooned to 100K-144K tokens. A 40-msg orchestrator session that delegates instead stays under 40K input max with cache reads at full 100K+. Same work, ~5× fewer effective tokens.
112
-
113
- **Verify after running:** `npx job-forge tokens --session <id>` — any message with `cache_read < 5K` and `input > 50K` is a cache-bust; next time split that work across subagents.
114
-
115
- **Exception:** evaluation-only or tracker-only work (no Geometra, no repeated tool calls) can proceed in a single session. The rule targets tool-heavy multi-step loops.
116
-
117
- **Before any batch-apply dispatch, run the Apply Preflight location filter from `modes/apply.md`** to exclude location-incompatible candidates. Catches the common case where an evaluated role has the right role-shape but a deal-breaking location that profile.yml already rules out.
118
-
119
- ---
120
-
121
- ## Subagent Routing — which agent for which task
122
-
123
- The harness ships three subagents (see `.opencode/agents/`). The orchestrator MUST route work by cost tier, not pick the default for everything. **GLM 5.1 does not discount cache reads**, so running procedural work on it costs ~10× what it would on a cache-discounting model. Free-tier models handle procedural work fine (confirmed empirically: `opencode/big-pickle` processed 1000+ messages at $0 in prior runs).
124
-
125
- | Task type | Subagent | Why |
126
- |-----------|----------|-----|
127
- | Drive Geometra form-fill / submit (atomic `run_actions`) | `@general-free` | Procedural; label-driven; deterministic |
128
- | Merge TSVs, run `verify-pipeline.mjs`, dedup | `@general-free` | Script-driven; no writing quality needed |
129
- | OTP retrieval via Gmail MCP + `geometra_fill_otp` | `@general-free` | Fixed-shape lookup + input |
130
- | Scan portals, extract offer metadata, return structured records (see schema below) | `@general-free` | Structured output; no judgment |
131
- | Evaluation narrative — Blocks A-F per `modes/offer.md` | `@general-paid` | Judgment + writing quality |
132
- | Cover letter, "Why X?" answers, Section G drafts | `@general-paid` | Tone and specificity matter |
133
- | STAR+R interview stories, story-bank curation | `@general-paid` | Quality signals seniority |
134
- | LinkedIn outreach messages (`modes/contact.md`) | `@general-paid` | First impression |
135
- | "Extract N fields from this text → JSON" (≤5K input) | `@glm-minimal` | One-shot transform; no context needed |
136
- | "Classify this JD as archetype X/Y/Z" | `@glm-minimal` | Narrow, structured output |
137
-
138
- **Example JSON shape for the "extract / emit JSON" subagent rows above** (use this exact key set when delegating a portal-scan / extract task):
139
-
140
- ```json
141
- {
142
- "company": "Acme",
143
- "role": "Senior Backend Engineer",
144
- "location": "Remote (US)",
145
- "comp_range_usd": "180000-220000",
146
- "archetype": "backend-platform",
147
- "url": "https://..."
148
- }
149
- ```
150
-
151
- **Rule:** when you (the orchestrator) delegate a task, pick the cheapest agent that can do it well. Do NOT route every subagent through the same tier. Auto-pipeline mode MUST split a single job across `@general-paid` (evaluation) and `@general-free` (PDF gen + tracker + apply), not run it all on one model.
152
-
153
- **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.
154
-
155
- ### When to delegate
156
-
157
- **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.
158
-
159
- **Delegate when:**
160
- - Applying to N≥2 jobs (repeated Geometra form-fill — the original cache-bust scenario)
161
- - Batch portal scans hitting ≥3 companies (API loops + page-model reads stack up)
162
- - Any explicit "apply to... / process pipeline / batch evaluate" phrasing from the user (multi-job intent)
163
-
164
- **Do NOT delegate — orchestrate inline:**
165
- - Single-offer evaluation (text-heavy, not tool-heavy)
166
- - Development / bug-fix / file-editing tasks
167
- - `tracker` and other read-only modes
168
- - Single-company scan, single-URL check
169
- - One-shot questions — "what does this mean?", "read X and summarize", "what's my next report number?"
170
-
171
- **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.
172
-
173
- ---
174
-
175
- ## Ethical Use -- CRITICAL
176
-
177
- **Applying to jobs via Geometra MCP is NOT impersonation.** The agent uses the user's credentials, CV, and information to apply on their behalf. This is the same as the user applying themselves — the agent is an extension of the user's will. Think of it as a browser automation tool that fills forms with the user's data.
178
-
179
- **This system is designed for quality, not quantity.** The goal is to help the user find and apply to roles where there is a genuine match -- not to spam companies with mass applications.
180
-
181
- - **Continuous pipeline for 3.0+ offers:** When scanning, evaluating, and applying — run the full pipeline end-to-end without pausing for confirmation. Scan → evaluate → fill form → submit for any offer scoring 3.0/5 or above. Do NOT stop between steps to ask "want me to continue?" — just do it.
182
- - **Auto-submit:** For offers scoring 3.0+/5, fill and submit the application automatically. For offers below 3.0/5, mark as SKIP and move on.
183
- - **Still respect quality:** Only apply where there is a genuine match (3.0+ ensures this). Auto-SKIP anything below 3.0.
184
- - **Respect recruiters' time.** Every application a human reads costs someone's attention. Only send what's worth reading.
185
-
186
- ---
187
-
188
- ## Offer Verification -- MANDATORY
189
-
190
- **Read local artifacts before the network.** If `reports/` already contains this posting URL (or company+role with a full JD in the body), **Read** that report for verification or evaluation instead of WebFetch/Geometra. If `data/pipeline.md` or `jds/` points at frozen JD text (`local:jds/{file}` or pasted blocks), **Read** that first. Reuse JD text already in the same conversation — do not fetch the same URL twice. (The JD extraction section at the top of `modes/auto-pipeline.md` and its "at most once per session" rule are the detailed contract.)
191
-
192
- **When Geometra MCP is available** (interactive sessions), ALWAYS use it to verify offers:
193
- 1. `geometra_connect` to the URL (via proxy)
194
- 2. `geometra_page_model` to read structured page content
195
- 3. Only footer/navbar without JD = closed. Title + description + Apply = active.
196
-
197
- **When Geometra MCP is NOT available** (batch workers via `opencode run`, headless environments):
198
- 1. Use WebFetch to retrieve the page content
199
- 2. Check for JD text, job title, and apply button/link in the response
200
- 3. If WebFetch returns only a shell/navbar (no JD content), mark the offer as `**Verification: unconfirmed**` in the report header
201
- 4. Do NOT skip the evaluation — proceed but flag the uncertainty so the user can verify manually before applying
202
-
203
- The goal is to never waste time on closed offers, but also never silently assume a role is active when verification was incomplete.
204
-
205
- ### Canonical MCP tools (quick reference)
206
-
207
- Pick tools by name directly — reduces unnecessary tool discovery:
208
-
209
- | Task | Preferred tools |
210
- |------|------------------|
211
- | JD from URL | Greenhouse boards API when the URL matches (see JD extraction in `modes/auto-pipeline.md`) → else `geometra_connect` + `geometra_page_model` → else WebFetch → WebSearch last |
212
- | Offer still live? | Same as JD when Geometra is available; else WebFetch per above |
213
- | One apply subagent (single job) | One `geometra_connect` per job URL; reuse `sessionId` through schema + fill; submit via atomic `geometra_run_actions` per `modes/apply.md` [H1]. Do **not** `geometra_disconnect` between `geometra_form_schema` and submit on the same form unless recovery requires it |
214
- | Chromium pool between orchestrator dispatch rounds | `geometra_list_sessions` + `geometra_disconnect({ closeBrowser: true })` per Hard limit [H3] — orchestrator-only; not a substitute for finishing the in-subagent form flow |
215
-
216
- @modes/reference-setup.md
91
+ The sections above are the shared contract. Load detailed context on demand:
217
92
 
218
- @modes/reference-portals.md
93
+ - `modes/{mode}.md` for the active mode procedure, output shape, and mode-specific routing.
94
+ - `modes/reference-setup.md` for onboarding, tracker layout, states, and profile/CV setup.
95
+ - `modes/reference-portals.md` for OTP, residential proxy, and MCP configuration.
96
+ - `modes/reference-geometra.md` for form-fill patterns, portal failures, cleanup runbooks, and session recovery.
219
97
 
220
- @modes/reference-geometra.md
98
+ Do not pre-load all reference files. Read only the active mode file and the reference file needed for the current blocker.
package/iso/mcp.json CHANGED
@@ -10,6 +10,15 @@
10
10
  "env": {
11
11
  "DISABLE_HTTP": "true"
12
12
  }
13
+ },
14
+ "state-trace": {
15
+ "command": "uvx",
16
+ "args": ["--from", "state-trace[mcp]", "state-trace-mcp"],
17
+ "env": {
18
+ "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
19
+ "STATE_TRACE_NAMESPACE": "job-forge",
20
+ "STATE_TRACE_CAPACITY_LIMIT": "256"
21
+ }
13
22
  }
14
23
  }
15
24
  }
package/modes/apply.md CHANGED
@@ -43,7 +43,13 @@ Live application assistant. Reads the active application form in Chrome (via Geo
43
43
  why: labels are stable across DOM refreshes; IDs are regenerated
44
44
 
45
45
  - [D7] If the orchestrator's task prompt includes a `proxy` object (sourced from `config/profile.yml`), pass it verbatim into every `geometra_connect` call — including Call 3 of the recovery sequence. If absent, run without one; never invent a proxy URL.
46
- why: class-B Ashby / Cloudflare-fronted portals need a residential outbound IP; the fix is wired in Geometra MCP v1.59.0 but the orchestrator owns the config pipe. See "BYO Residential Proxy" in iso/instructions.md.
46
+ why: class-B Ashby / Cloudflare-fronted portals need a residential outbound IP; the fix is wired in Geometra MCP v1.59.0 but the orchestrator owns the config pipe. See "BYO Residential Proxy" in modes/reference-portals.md.
47
+
48
+ - [D8] Upgrade application routing to `@general-paid` when the offer score is ≥ 4.0/5, the user flags "top-tier", "dream job", or "high-stakes", or the candidate is late-stage/post-screen.
49
+ why: form-fill flows are 6+ steps; free-tier sometimes aborts mid-flow on large Greenhouse/Workday schemas; paid tier has more headroom
50
+
51
+ - [D9] If an upgraded `@general-paid` subagent fails with provider-side errors, re-dispatch the same URL once on `@general-free` before marking FAILED. Provider-side errors include Venice, Diem, Chutes, HTTP 402/429, insufficient credits/funds/balance, overload, and temporarily unavailable.
52
+ why: OpenCode paid-tier routing can still use free OpenRouter model IDs; backend pool limits are not evidence that a procedural free-tier worker cannot complete the same form after preflight gates pass
47
53
 
48
54
  ## Procedure
49
55
 
@@ -54,14 +60,16 @@ Live application assistant. Reads the active application form in Chrome (via Geo
54
60
  5. Compare role on screen vs evaluated role [D3].
55
61
  6. If different, pause for the candidate's decision [D3].
56
62
  7. Before dispatch, run Geometra cleanup [H4] and location filter [D1].
57
- 8. Extract form questions; classify each Section-G vs new.
58
- 9. Generate answers from Block B + Block F + Section G + JD.
59
- 10. Submit as ONE `run_actions` call [H1] using labels [D6] with `imeFriendly: true` [D4].
60
- 11. On session error, run the 4-step recovery; only one retry [H2].
61
- 12. On OTP prompt, fetch the code from Gmail via `gmail_get_message`.
62
- 13. Submit the OTP with `geometra_fill_otp` and click Submit.
63
- 14. Write outcome as `batch/tracker-additions/*.tsv` [H3].
64
- 15. Cap parallelism at 2 per round [H5]; one in-flight per company.
63
+ 8. Route high-stakes applications through `@general-paid` [D8].
64
+ 9. Extract form questions; classify each Section-G vs new.
65
+ 10. Generate answers from Block B + Block F + Section G + JD.
66
+ 11. Submit as ONE `run_actions` call [H1] using labels [D6] with `imeFriendly: true` [D4].
67
+ 12. On session error, run the 4-step recovery; only one retry [H2].
68
+ 13. On upgraded-provider failure, downgrade once to `@general-free` [D9].
69
+ 14. On OTP prompt, fetch the code from Gmail via `gmail_get_message`.
70
+ 15. Submit the OTP with `geometra_fill_otp` and click Submit.
71
+ 16. Write outcome as `batch/tracker-additions/*.tsv` [H3].
72
+ 17. Cap parallelism at 2 per round [H5]; one in-flight per company.
65
73
 
66
74
  ## Routing
67
75
 
package/opencode.json CHANGED
@@ -50,6 +50,20 @@
50
50
  "environment": {
51
51
  "DISABLE_HTTP": "true"
52
52
  }
53
+ },
54
+ "state-trace": {
55
+ "type": "local",
56
+ "command": [
57
+ "uvx",
58
+ "--from",
59
+ "state-trace[mcp]",
60
+ "state-trace-mcp"
61
+ ],
62
+ "environment": {
63
+ "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
64
+ "STATE_TRACE_NAMESPACE": "job-forge",
65
+ "STATE_TRACE_CAPACITY_LIMIT": "256"
66
+ }
53
67
  }
54
68
  },
55
69
  "plugin": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-forge",
3
- "version": "2.14.7",
3
+ "version": "2.14.8",
4
4
  "description": "AI-powered job search pipeline built on opencode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,6 +28,7 @@
28
28
  "test:agentmd:baseline": "agentmd test iso/instructions.md --fixtures fixtures/instructions.yml --via claude-code --model claude-haiku-4-5 --concurrency 2 --trials 3 --format json --out fixtures/baseline.json",
29
29
  "test:agentmd:apply": "agentmd test modes/apply.md --fixtures fixtures/modes/apply.yml --via claude-code --model claude-haiku-4-5 --concurrency 2 --trials 3",
30
30
  "lint:agentmd:modes": "agentmd lint modes/apply.md",
31
+ "smoke:iso": "iso plan . && iso build . --dry-run && iso-route verify models.yaml && node scripts/check-iso-smoke.mjs . && JOBFORGE_ROOT=$PWD iso-eval run fixtures/iso-smoke/eval.yml",
31
32
  "build:config": "iso build .",
32
33
  "prepack": "iso build .",
33
34
  "release:check-source": "node ./scripts/release/check-source.mjs",
@@ -87,10 +88,12 @@
87
88
  "playwright": "^1.58.1"
88
89
  },
89
90
  "devDependencies": {
91
+ "@razroo/agentmd": "^0.3.0",
90
92
  "@razroo/iso": "^0.2.5",
93
+ "@razroo/iso-eval": "^0.4.0",
91
94
  "@razroo/iso-harness": "^0.6.1",
92
- "@razroo/iso-route": "^0.5.2",
93
- "@razroo/iso-trace": "^0.3.1",
95
+ "@razroo/iso-route": "^0.5.3",
96
+ "@razroo/iso-trace": "^0.4.0",
94
97
  "@razroo/opencode-model-fallback": "^0.3.1"
95
98
  }
96
99
  }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ const root = resolve(process.argv[2] ?? ".");
6
+ const files = {
7
+ instructions: readFileSync(resolve(root, "iso/instructions.md"), "utf8"),
8
+ apply: readFileSync(resolve(root, "modes/apply.md"), "utf8"),
9
+ models: readFileSync(resolve(root, "models.yaml"), "utf8"),
10
+ config: readFileSync(resolve(root, "iso/config.json"), "utf8"),
11
+ };
12
+
13
+ const checks = [
14
+ ["root defines H1-H7", () => every(files.instructions, ["[H1]", "[H2]", "[H3]", "[H4]", "[H5]", "[H6]", "[H7]"])],
15
+ ["H1 caps dispatches at 2", () => /Max 2 parallel `task` dispatches/.test(files.instructions)],
16
+ ["H2 checks all duplicate sources", () => every(files.instructions, ["data/pipeline.md", "data/applications/*.md", "batch/tracker-additions/*.tsv", "batch/tracker-additions/merged/*.tsv"])],
17
+ ["H3 names Geometra cleanup calls", () => every(files.instructions, ["geometra_list_sessions", "geometra_disconnect({closeBrowser: true})"])],
18
+ ["H4 blocks orchestrator form filling", () => every(files.instructions, ["MUST NOT call `geometra_fill_form`", "`geometra_run_actions`", "`geometra_fill_otp`"])],
19
+ ["H5 blocks same-company concurrent retry", () => every(files.instructions, ["Re-dispatch the same company only AFTER", "previous subagent returns"])],
20
+ ["H6 requires merge and verify", () => every(files.instructions, ["batch/tracker-additions/*.tsv", "npx job-forge merge", "npx job-forge verify"])],
21
+ ["H7 distrusts subagent prose", () => every(files.instructions, ["must originate from a file", "not from prior subagent prose"])],
22
+ ["shared prompt points to on-demand references", () => every(files.instructions, ["modes/{mode}.md", "modes/reference-setup.md", "modes/reference-portals.md", "modes/reference-geometra.md"])],
23
+ ["apply mode owns high-stakes upgrade", () => every(files.apply, ["[D8]", "@general-paid", "4.0/5", "high-stakes"])],
24
+ ["apply mode owns provider downgrade", () => every(files.apply, ["[D9]", "@general-free", "HTTP 402/429", "insufficient credits/funds/balance"])],
25
+ ["models policy extends free OpenRouter preset", () => /extends:\s*openrouter-free/.test(files.models)],
26
+ ["OpenCode fallback plugin is configured", () => every(files.config, ["opencodeModelFallback", "@razroo/opencode-model-fallback"])],
27
+ ];
28
+
29
+ const failures = checks
30
+ .filter(([, check]) => !check())
31
+ .map(([name]) => name);
32
+
33
+ if (failures.length > 0) {
34
+ console.error("JobForge iso smoke failed:");
35
+ for (const failure of failures) console.error(`- ${failure}`);
36
+ process.exit(1);
37
+ }
38
+
39
+ console.log(`JobForge iso smoke passed (${checks.length} checks).`);
40
+
41
+ function every(source, needles) {
42
+ return needles.every((needle) => source.includes(needle));
43
+ }