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.
- package/.codex/config.toml +5 -0
- package/.cursor/mcp.json +13 -0
- package/.cursor/rules/main.mdc +20 -142
- package/.mcp.json +13 -0
- package/AGENTS.md +20 -142
- package/CLAUDE.md +20 -142
- package/README.md +7 -1
- package/docs/SETUP.md +1 -0
- package/iso/instructions.md +20 -142
- package/iso/mcp.json +9 -0
- package/modes/apply.md +17 -9
- package/opencode.json +14 -0
- package/package.json +6 -3
- package/scripts/check-iso-smoke.mjs +43 -0
package/.codex/config.toml
CHANGED
|
@@ -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
|
}
|
package/.cursor/rules/main.mdc
CHANGED
|
@@ -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:
|
|
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]
|
|
42
|
-
why:
|
|
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
|
-
- [
|
|
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 steps — scan → 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.
|
|
59
|
-
2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
60
|
-
3.
|
|
61
|
-
4.
|
|
62
|
-
5.
|
|
63
|
-
6.
|
|
64
|
-
7.
|
|
65
|
-
8.
|
|
66
|
-
9.
|
|
67
|
-
10.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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]
|
|
37
|
-
why:
|
|
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
|
-
- [
|
|
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 steps — scan → 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.
|
|
54
|
-
2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
59
|
-
7.
|
|
60
|
-
8.
|
|
61
|
-
9.
|
|
62
|
-
10.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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]
|
|
37
|
-
why:
|
|
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
|
-
- [
|
|
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 steps — scan → 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.
|
|
54
|
-
2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
59
|
-
7.
|
|
60
|
-
8.
|
|
61
|
-
9.
|
|
62
|
-
10.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
package/iso/instructions.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:
|
|
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]
|
|
37
|
-
why:
|
|
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
|
-
- [
|
|
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 steps — scan → 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.
|
|
54
|
-
2. Pick the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
55
|
-
3.
|
|
56
|
-
4.
|
|
57
|
-
5.
|
|
58
|
-
6.
|
|
59
|
-
7.
|
|
60
|
-
8.
|
|
61
|
-
9.
|
|
62
|
-
10.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
58
|
-
9.
|
|
59
|
-
10.
|
|
60
|
-
11.
|
|
61
|
-
12. On
|
|
62
|
-
13.
|
|
63
|
-
14.
|
|
64
|
-
15.
|
|
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.
|
|
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.
|
|
93
|
-
"@razroo/iso-trace": "^0.
|
|
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
|
+
}
|