job-forge 2.14.11 → 2.14.12

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.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: general-free
3
- description: Procedural worker on free-tier model. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
3
+ description: Procedural worker on the low-cost DeepSeek V4 Flash OpenCode route. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
4
4
  model: claude-haiku-4-5
5
5
  ---
6
6
 
7
- You are the @general-free subagent. You run on a free-tier model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
7
+ You are the @general-free subagent. You run on JobForge's low-cost procedural model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
8
8
 
9
9
  ## Run This Pre-Flight First Every Time
10
10
 
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: general-paid
3
- description: Quality-sensitive worker on the strongest free-tier OpenCode model by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
3
+ description: Quality-sensitive worker on the low-cost DeepSeek V4 Flash OpenCode route by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
4
4
  model: claude-opus-4-7
5
5
  ---
6
6
 
7
7
  You are the @general-paid subagent. The orchestrator delegated this task to you because it requires quality writing or judgment — the kind of work `@general-free` isn't well-suited for.
8
8
 
9
- On OpenCode, this agent now defaults to a free OpenRouter model. On other
10
- harnesses, the same role may still resolve to a premium model. Your job is
11
- still the same: produce the best final writing you can from the context you
12
- were given.
9
+ On OpenCode, this agent defaults to DeepSeek V4 Flash so application work
10
+ does not fall back into overloaded free OpenRouter pools. On other harnesses,
11
+ the same role may still resolve to a premium model. Your job is still the
12
+ same: produce the best final writing you can from the context you were given.
13
13
 
14
14
  ## Do These Tasks
15
15
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: glm-minimal
3
- description: Narrow-scope extractor on free-tier model. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
3
+ description: Narrow-scope extractor on the low-cost DeepSeek V4 Flash OpenCode route. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
4
4
  model: claude-haiku-4-5
5
5
  ---
6
6
 
@@ -1,9 +1,9 @@
1
1
  ---
2
- description: Procedural worker on free-tier model. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
2
+ description: Procedural worker on the low-cost DeepSeek V4 Flash OpenCode route. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
3
3
  alwaysApply: false
4
4
  ---
5
5
 
6
- You are the @general-free subagent. You run on a free-tier model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
6
+ You are the @general-free subagent. You run on JobForge's low-cost procedural model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
7
7
 
8
8
  ## Run This Pre-Flight First Every Time
9
9
 
@@ -1,14 +1,14 @@
1
1
  ---
2
- description: Quality-sensitive worker on the strongest free-tier OpenCode model by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
2
+ description: Quality-sensitive worker on the low-cost DeepSeek V4 Flash OpenCode route by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
3
3
  alwaysApply: false
4
4
  ---
5
5
 
6
6
  You are the @general-paid subagent. The orchestrator delegated this task to you because it requires quality writing or judgment — the kind of work `@general-free` isn't well-suited for.
7
7
 
8
- On OpenCode, this agent now defaults to a free OpenRouter model. On other
9
- harnesses, the same role may still resolve to a premium model. Your job is
10
- still the same: produce the best final writing you can from the context you
11
- were given.
8
+ On OpenCode, this agent defaults to DeepSeek V4 Flash so application work
9
+ does not fall back into overloaded free OpenRouter pools. On other harnesses,
10
+ the same role may still resolve to a premium model. Your job is still the
11
+ same: produce the best final writing you can from the context you were given.
12
12
 
13
13
  ## Do These Tasks
14
14
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Narrow-scope extractor on free-tier model. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
2
+ description: Narrow-scope extractor on the low-cost DeepSeek V4 Flash OpenCode route. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
3
3
  alwaysApply: false
4
4
  ---
5
5
 
@@ -10,7 +10,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
10
10
  ## Hard limits
11
11
 
12
12
  - [H1] Max 2 parallel `task` dispatches per message. For N jobs, run `ceil(N/2)` sequential rounds of 2. A round is not complete until both subagents return a final outcome (`APPLIED`, `APPLY FAILED`, `SKIP`, `Discarded`, or a written TSV path). A `task` tool result that only gives a session id / title is a launch acknowledgement, not completion. Applies in all modes, for all user phrasings ("urgent", "apply to 10 jobs now").
13
- why: higher parallelism blows through free-tier rate limits; each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider-fallback recoveries
13
+ why: each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider recoveries
14
14
 
15
15
  - [H2] Max 1 application per company+role. Before every `apply` dispatch, grep all four sources for the URL and for `company+role`: `data/pipeline.md`, all `data/applications/*.md` day files, `batch/tracker-additions/*.tsv`, `batch/tracker-additions/merged/*.tsv`. If any source shows APPLIED / Applied, skip the dispatch.
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
@@ -42,7 +42,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
42
42
  why: iso-trace showed 0.25% Agent calls across 5174 turns under a prior over-broad "delegate before 2nd tool call" rule — the rule was ignored in practice; narrowing matches the original cache-bust incident
43
43
 
44
44
  - [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.
45
- 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)
45
+ why: OpenCode routes all JobForge tiers through DeepSeek V4 Flash by default now; recent traces showed free OpenRouter fallbacks freezing or hitting provider balance errors during applications
46
46
 
47
47
  - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
48
48
  why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: Procedural worker on free-tier model. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
2
+ description: Procedural worker on the low-cost DeepSeek V4 Flash OpenCode route. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
3
3
  mode: subagent
4
- model: opencode/big-pickle
4
+ model: opencode-go/deepseek-v4-flash
5
5
  tools:
6
6
  geometra_connect: true
7
7
  geometra_page_model: true
@@ -17,14 +17,9 @@ tools:
17
17
  task: false
18
18
  temperature: 0.1
19
19
  reasoningEffort: minimal
20
- fallback_models:
21
- - openrouter/z-ai/glm-4.5-air:free
22
- - openrouter/openai/gpt-oss-20b:free
23
- - openrouter/nvidia/nemotron-3-nano-30b-a3b:free
24
- - openrouter/qwen/qwen3-coder:free
25
20
  ---
26
21
 
27
- You are the @general-free subagent. You run on a free-tier model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
22
+ You are the @general-free subagent. You run on JobForge's low-cost procedural model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
28
23
 
29
24
  ## Run This Pre-Flight First Every Time
30
25
 
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: Quality-sensitive worker on the strongest free-tier OpenCode model by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
2
+ description: Quality-sensitive worker on the low-cost DeepSeek V4 Flash OpenCode route by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
3
3
  mode: subagent
4
- model: openrouter/qwen/qwen3-next-80b-a3b-instruct:free
4
+ model: opencode-go/deepseek-v4-flash
5
5
  tools:
6
6
  geometra_connect: true
7
7
  geometra_page_model: true
@@ -17,21 +17,14 @@ tools:
17
17
  task: false
18
18
  temperature: 0.3
19
19
  reasoningEffort: medium
20
- fallback_models:
21
- - openrouter/openai/gpt-oss-120b:free
22
- - openrouter/nvidia/nemotron-3-super-120b-a12b:free
23
- - openrouter/z-ai/glm-4.5-air:free
24
- - openrouter/qwen/qwen3-coder:free
25
- - openrouter/google/gemma-4-31b-it:free
26
- - openrouter/meta-llama/llama-3.3-70b-instruct:free
27
20
  ---
28
21
 
29
22
  You are the @general-paid subagent. The orchestrator delegated this task to you because it requires quality writing or judgment — the kind of work `@general-free` isn't well-suited for.
30
23
 
31
- On OpenCode, this agent now defaults to a free OpenRouter model. On other
32
- harnesses, the same role may still resolve to a premium model. Your job is
33
- still the same: produce the best final writing you can from the context you
34
- were given.
24
+ On OpenCode, this agent defaults to DeepSeek V4 Flash so application work
25
+ does not fall back into overloaded free OpenRouter pools. On other harnesses,
26
+ the same role may still resolve to a premium model. Your job is still the
27
+ same: produce the best final writing you can from the context you were given.
35
28
 
36
29
  ## Do These Tasks
37
30
 
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: Narrow-scope extractor on free-tier model. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
2
+ description: Narrow-scope extractor on the low-cost DeepSeek V4 Flash OpenCode route. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
3
3
  mode: subagent
4
- model: openrouter/openai/gpt-oss-20b:free
4
+ model: opencode-go/deepseek-v4-flash
5
5
  tools:
6
6
  geometra_*: false
7
7
  gmail_*: false
@@ -13,11 +13,6 @@ tools:
13
13
  task: false
14
14
  temperature: 0
15
15
  reasoningEffort: none
16
- fallback_models:
17
- - openrouter/google/gemma-4-26b-a4b-it:free
18
- - openrouter/nvidia/nemotron-nano-9b-v2:free
19
- - openrouter/google/gemma-4-31b-it:free
20
- - openrouter/z-ai/glm-4.5-air:free
21
16
  ---
22
17
 
23
18
  You are the @glm-minimal subagent. You handle narrow, one-shot extractions where the orchestrator has pre-digested the context and just needs you to do a specific transform.
package/AGENTS.md CHANGED
@@ -5,7 +5,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
5
5
  ## Hard limits
6
6
 
7
7
  - [H1] Max 2 parallel `task` dispatches per message. For N jobs, run `ceil(N/2)` sequential rounds of 2. A round is not complete until both subagents return a final outcome (`APPLIED`, `APPLY FAILED`, `SKIP`, `Discarded`, or a written TSV path). A `task` tool result that only gives a session id / title is a launch acknowledgement, not completion. Applies in all modes, for all user phrasings ("urgent", "apply to 10 jobs now").
8
- why: higher parallelism blows through free-tier rate limits; each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider-fallback recoveries
8
+ why: each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider recoveries
9
9
 
10
10
  - [H2] Max 1 application per company+role. Before every `apply` dispatch, grep all four sources for the URL and for `company+role`: `data/pipeline.md`, all `data/applications/*.md` day files, `batch/tracker-additions/*.tsv`, `batch/tracker-additions/merged/*.tsv`. If any source shows APPLIED / Applied, skip the dispatch.
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
@@ -37,7 +37,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
37
37
  why: iso-trace showed 0.25% Agent calls across 5174 turns under a prior over-broad "delegate before 2nd tool call" rule — the rule was ignored in practice; narrowing matches the original cache-bust incident
38
38
 
39
39
  - [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.
40
- 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
+ why: OpenCode routes all JobForge tiers through DeepSeek V4 Flash by default now; recent traces showed free OpenRouter fallbacks freezing or hitting provider balance errors during applications
41
41
 
42
42
  - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
43
43
  why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
package/CLAUDE.md CHANGED
@@ -5,7 +5,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
5
5
  ## Hard limits
6
6
 
7
7
  - [H1] Max 2 parallel `task` dispatches per message. For N jobs, run `ceil(N/2)` sequential rounds of 2. A round is not complete until both subagents return a final outcome (`APPLIED`, `APPLY FAILED`, `SKIP`, `Discarded`, or a written TSV path). A `task` tool result that only gives a session id / title is a launch acknowledgement, not completion. Applies in all modes, for all user phrasings ("urgent", "apply to 10 jobs now").
8
- why: higher parallelism blows through free-tier rate limits; each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider-fallback recoveries
8
+ why: each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider recoveries
9
9
 
10
10
  - [H2] Max 1 application per company+role. Before every `apply` dispatch, grep all four sources for the URL and for `company+role`: `data/pipeline.md`, all `data/applications/*.md` day files, `batch/tracker-additions/*.tsv`, `batch/tracker-additions/merged/*.tsv`. If any source shows APPLIED / Applied, skip the dispatch.
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
@@ -37,7 +37,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
37
37
  why: iso-trace showed 0.25% Agent calls across 5174 turns under a prior over-broad "delegate before 2nd tool call" rule — the rule was ignored in practice; narrowing matches the original cache-bust incident
38
38
 
39
39
  - [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.
40
- 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
+ why: OpenCode routes all JobForge tiers through DeepSeek V4 Flash by default now; recent traces showed free OpenRouter fallbacks freezing or hitting provider balance errors during applications
41
41
 
42
42
  - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
43
43
  why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
package/README.md CHANGED
@@ -74,8 +74,8 @@ JobForge turns opencode into a full job search command center. Instead of manual
74
74
  | **Portal Scanner** | 45+ companies pre-configured with fuzzy dedup for reposts |
75
75
  | **Batch Processing** | Parallel evaluation with `opencode run` workers, with honest verification flagging |
76
76
  | **Pipeline Integrity** | Automated merge, dedup, status normalization, health checks |
77
- | **Cost-Aware Agent Routing** | Three subagents (`@general-free`, `@general-paid`, `@glm-minimal`) with per-task model tiers. On OpenCode, JobForge mixes native free models with free OpenRouter routes so the harness stays no-cost without forcing every task through the same provider. See [Subagent Routing in AGENTS.md](AGENTS.md) for the task-to-agent mapping. |
78
- | **Automatic Model Fallback** | When a model rate-limits or 5xx's, [`@razroo/opencode-model-fallback`](https://www.npmjs.com/package/@razroo/opencode-model-fallback) rotates the agent through a configured `fallback_models` chain and replays the request. JobForge's OpenCode defaults stay on free models for both primaries and fallbacks. |
77
+ | **Cost-Aware Agent Routing** | Three subagents (`@general-free`, `@general-paid`, `@glm-minimal`) with per-task tool surfaces. On OpenCode, JobForge pins all tiers to `opencode-go/deepseek-v4-flash` so application runs avoid overloaded free-model pools. See [Subagent Routing in AGENTS.md](AGENTS.md) for the task-to-agent mapping. |
78
+ | **Trace + Telemetry** | `job-forge trace:*` exposes local OpenCode transcripts, and `job-forge telemetry:*` summarizes runs, child outcomes, provider errors, and pending tracker TSVs. |
79
79
  | **Token Cost Visibility** | `job-forge tokens --days 1` for per-session breakdown; `job-forge session-report --since-minutes 60 --log` to flag sessions over budget and append history to `data/token-usage.tsv`. Auto-logged after every batch run. |
80
80
 
81
81
  ## Usage
@@ -117,22 +117,14 @@ const consumerPkg = {
117
117
  'telemetry:status': 'job-forge telemetry:status',
118
118
  'telemetry:show': 'job-forge telemetry:show',
119
119
  'telemetry:watch': 'job-forge telemetry:watch',
120
- // One command to pull the latest harness, companion plugin, and any
121
- // locally-pinned MCP packages. npm update is a no-op on packages not
122
- // in package.json, so listing @razroo/gmail-mcp + @geometra/mcp is
123
- // safe for consumers that invoke them via `npx -y` without pinning.
124
- 'update-harness': 'npm update job-forge @razroo/opencode-model-fallback @razroo/gmail-mcp @geometra/mcp && job-forge sync && node -e "console.log(\'✅ harness at\', require(\'./package-lock.json\').packages[\'node_modules/job-forge\'].resolved)"',
120
+ // One command to pull the latest harness and any locally-pinned MCP
121
+ // packages. npm update is a no-op on packages not in package.json, so
122
+ // listing @razroo/gmail-mcp + @geometra/mcp is safe for consumers that
123
+ // invoke them via `npx -y` without pinning.
124
+ 'update-harness': 'npm update job-forge @razroo/gmail-mcp @geometra/mcp && job-forge sync && node -e "console.log(\'✅ harness at\', require(\'./package-lock.json\').packages[\'node_modules/job-forge\'].resolved)"',
125
125
  },
126
126
  dependencies: {
127
127
  'job-forge': '^2.0.0',
128
- // Model-fallback plugin: rotates agents through their fallback_models
129
- // chain on rate-limit / 5xx errors so a rate-limited free-tier model
130
- // doesn't wedge the whole flow. The chains live upstream in each
131
- // agent's MD frontmatter (`.opencode/agents/*.md` in the harness);
132
- // consumers can override individual chains by adding their own
133
- // agent.<name>.fallback_models block to opencode.json. Requires
134
- // 0.3.1+ for the frontmatter-merge path.
135
- '@razroo/opencode-model-fallback': '^0.3.1',
136
128
  },
137
129
  engines: { node: '>=18' },
138
130
  };
@@ -142,16 +134,11 @@ write('package.json', JSON.stringify(consumerPkg, null, 2) + '\n');
142
134
 
143
135
  const opencodeCfg = {
144
136
  $schema: 'https://opencode.ai/config.json',
145
- // Keep the top-level orchestrator on a free model too. Subagents pin
146
- // their own models in .opencode/agents/*.md; this covers the main chat
147
- // session and any commands that don't hop to a subagent immediately.
148
- model: 'openrouter/qwen/qwen3-coder:free',
149
- small_model: 'openrouter/google/gemma-4-26b-a4b-it:free',
150
- // Model-fallback plugin: on rate-limit / 5xx / known provider errors,
151
- // rotates the agent's model to the next entry in its fallback_models
152
- // chain (see `agent` below) and replays the request. Without this, a
153
- // rate-limited free-tier model wedges the whole subagent flow.
154
- plugin: ['@razroo/opencode-model-fallback'],
137
+ // Keep the top-level orchestrator on JobForge's low-cost paid OpenCode
138
+ // route. Subagents pin the same route in .opencode/agents/*.md so job
139
+ // applications do not fall through overloaded free OpenRouter pools.
140
+ model: 'opencode-go/deepseek-v4-flash',
141
+ small_model: 'opencode-go/deepseek-v4-flash',
155
142
  // Files listed here load into every session's cached prefix, so they're
156
143
  // cached once (on Anthropic) instead of Read-as-tool-call on every session.
157
144
  // AGENTS.harness.md → symlink to node_modules/job-forge/AGENTS.md (harness rules)
@@ -182,31 +169,6 @@ const opencodeCfg = {
182
169
  environment: { DISABLE_HTTP: 'true' },
183
170
  },
184
171
  },
185
- // Register the exact OpenRouter free models the harness uses so they're
186
- // selectable even if they are not in OpenCode's built-in preloaded set.
187
- // This list is a superset: role primaries, per-agent fallback chains,
188
- // and the orchestrator fallback chain.
189
- provider: {
190
- openrouter: {
191
- models: {
192
- // Orchestrator + agentic coding (role default)
193
- 'qwen/qwen3-coder:free': {},
194
- // Role primaries
195
- 'z-ai/glm-4.5-air:free': {}, // fast
196
- 'qwen/qwen3-next-80b-a3b-instruct:free': {}, // quality
197
- 'openai/gpt-oss-20b:free': {}, // minimal
198
- // Common fallbacks
199
- 'openai/gpt-oss-120b:free': {},
200
- 'minimax/minimax-m2.5:free': {},
201
- 'nvidia/nemotron-3-super-120b-a12b:free': {},
202
- 'nvidia/nemotron-3-nano-30b-a3b:free': {},
203
- 'nvidia/nemotron-nano-9b-v2:free': {},
204
- 'google/gemma-4-26b-a4b-it:free': {},
205
- 'google/gemma-4-31b-it:free': {},
206
- 'meta-llama/llama-3.3-70b-instruct:free': {},
207
- },
208
- },
209
- },
210
172
  // Restrict the primary orchestrator to dispatching only the three harness
211
173
  // subagents. Prevents accidental self-calls or unregistered agents.
212
174
  // Override locally in opencode.json if you add project-specific agents.
package/bin/sync.mjs CHANGED
@@ -75,7 +75,6 @@ const links = [
75
75
 
76
76
  // OpenCode: skill router + subagent definitions. Users can override any
77
77
  // single subagent by replacing its symlink with a local file.
78
- { src: '.opencode/opencode-model-fallback.json', dst: '.opencode/opencode-model-fallback.json' },
79
78
  { src: '.opencode/skills/job-forge.md', dst: '.opencode/skills/job-forge.md' },
80
79
  { src: '.opencode/agents', dst: '.opencode/agents' },
81
80
 
@@ -43,11 +43,11 @@ The consumer's `opencode.json` loads a small set of stable files as always-prese
43
43
 
44
44
  The skill router (`.opencode/skills/job-forge.md`) loads mode and data files on demand, keeping per-session input tokens low (~20-40K for most modes instead of ~130-170K when everything was force-loaded).
45
45
 
46
- **Cost-tiered subagents** live in `.opencode/agents/` (`general-free`, `general-paid`, `glm-minimal`). On OpenCode, JobForge now uses a mix of native free models and free OpenRouter routes, with different quality/latency tiers per task shape. See [MODEL-ROUTING.md](MODEL-ROUTING.md) for the routing architecture, why it exists, and how to customize.
46
+ **Cost-tiered subagents** live in `.opencode/agents/` (`general-free`, `general-paid`, `glm-minimal`). On OpenCode, JobForge pins all three tiers to `opencode-go/deepseek-v4-flash` by default, while the tiers still differ by tool surface, reasoning budget, and task prompt. See [MODEL-ROUTING.md](MODEL-ROUTING.md) for the routing architecture, why it exists, and how to customize.
47
47
 
48
48
  **Multi-harness support.** Because `iso/` is the single source of truth, publishing ships config for OpenCode, Cursor, Claude Code, and Codex in one tarball. Consumers run any of `opencode`, `cursor`, `claude`, or `codex` in the project and each picks up the shared MCP config + instructions via the symlinks above.
49
49
 
50
- **Upgrading** the harness in a consumer project is `npm run update-harness` — pulls the latest `job-forge` from npm, refreshes the fallback plugin + pinned MCPs, re-runs symlink sync, and prints the resolved version.
50
+ **Upgrading** the harness in a consumer project is `npm run update-harness` — pulls the latest `job-forge` from npm, refreshes pinned MCPs, re-runs symlink sync, and prints the resolved version.
51
51
 
52
52
  ## System Overview
53
53
 
@@ -4,13 +4,13 @@ JobForge routes each piece of work to the cheapest model that can do it well, in
4
4
 
5
5
  ## Why routing matters (the cost math)
6
6
 
7
- A two-day trace early in development showed `$48` in spend, with **84% coming from GLM 5.1** despite the majority of the work being procedural (form fills, tracker updates, OTP retrieval). The root cause:
7
+ A two-day trace early in development showed `$48` in spend, with **84% coming from GLM 5.1** despite the majority of the work being procedural (form fills, tracker updates, OTP retrieval). Later traces showed the opposite failure mode: free OpenRouter routes hit shared-pool contention and Venice balance errors during application subagents.
8
8
 
9
9
  - **GLM 5.1's provider doesn't discount cache reads.** On Anthropic, a 10K-token cached prefix costs ~$0.03. On GLM 5.1 it bills near-full input rate (~$0.35). Every session that re-loads the prefix pays full price.
10
- - **Procedural work is the high-volume work.** 1000+ messages per day go to form filling, TSV merges, scan dedup. Running that on a paid model is unnecessary when current free models can handle the task.
11
- - **OpenCode no longer needs one provider for every role.** JobForge now pins the procedural `@general-free` worker to `opencode/big-pickle`, while the quality-sensitive writer tier stays on a free OpenRouter route.
10
+ - **Procedural work is the high-volume work.** 1000+ messages per day go to form filling, TSV merges, scan dedup. It needs a cheap reliable route, not the absolute strongest model.
11
+ - **Free pools are not reliable enough for application runs.** A 2026-04-25 trace showed `@general-paid` hitting `openrouter/qwen/...:free` Venice 402 errors, while `@general-free` fell through `opencode/big-pickle` into `z-ai/glm-4.5-air:free` and `gpt-oss-20b:free`.
12
12
 
13
- Conclusion: route procedural work to free tier, reserve paid models for tasks that actually need the quality.
13
+ Conclusion: keep the subagent split for tool permissions and prompting, but route OpenCode to `opencode-go/deepseek-v4-flash` across all JobForge tiers. It is the best current "affordable and reliable" default for applications.
14
14
 
15
15
  ## The three subagents
16
16
 
@@ -18,9 +18,9 @@ Defined in `.opencode/agents/*.md` (shipped in the harness, symlinked into consu
18
18
 
19
19
  | Agent | Model | Reasoning | Use for |
20
20
  |-------|-------|-----------|---------|
21
- | `@general-free` | `opencode/big-pickle` | `minimal` | Geometra form fills, tracker TSV merges, scan dedup, OTP retrieval via Gmail, scripted pipeline steps |
22
- | `@general-paid` | `openrouter/qwen/qwen3-next-80b-a3b-instruct:free` | `medium` | Offer evaluation narratives (Blocks A-F), cover letters, "Why X?" answers, STAR+R interview stories, LinkedIn outreach prose |
23
- | `@glm-minimal` | `openrouter/openai/gpt-oss-20b:free` | `none` | Narrow one-shot transforms: "extract these 8 fields from this JD text → JSON", "classify this archetype" |
21
+ | `@general-free` | `opencode-go/deepseek-v4-flash` | `minimal` | Geometra form fills, tracker TSV merges, scan dedup, OTP retrieval via Gmail, scripted pipeline steps |
22
+ | `@general-paid` | `opencode-go/deepseek-v4-flash` | `medium` | Offer evaluation narratives (Blocks A-F), cover letters, "Why X?" answers, STAR+R interview stories, LinkedIn outreach prose |
23
+ | `@glm-minimal` | `opencode-go/deepseek-v4-flash` | `none` | Narrow one-shot transforms: "extract these 8 fields from this JD text → JSON", "classify this archetype" |
24
24
 
25
25
  The full task-to-agent mapping lives in [AGENTS.md → Subagent Routing](../AGENTS.md#subagent-routing--which-agent-for-which-task). The orchestrator (your primary session) is expected to delegate before taking any multi-step action — see the **Pre-flight delegation** rule in AGENTS.md.
26
26
 
@@ -64,25 +64,35 @@ Disables ~30 MCP tool schemas globally; each agent re-enables only what it needs
64
64
 
65
65
  All three layers are designed to be edited — this is your search, your cost budget, your model preferences.
66
66
 
67
- ### Swap the paid model
67
+ ### Swap the quality model
68
68
 
69
- The default `@general-paid` is `openrouter/qwen/qwen3-next-80b-a3b-instruct:free`. To use Claude instead, edit `.opencode/agents/general-paid.md`:
69
+ The default `@general-paid` OpenCode model is `opencode-go/deepseek-v4-flash`. To use a stronger OpenCode route for quality writing, edit `models.yaml`:
70
70
 
71
71
  ```yaml
72
- ---
73
- model: opencode/claude-sonnet-4-6
74
- reasoningEffort: medium
75
- ---
72
+ roles:
73
+ quality:
74
+ targets:
75
+ opencode:
76
+ provider: opencode
77
+ model: opencode-go/deepseek-v4-pro
76
78
  ```
77
79
 
78
- The `.opencode/agents/general-paid.md` file is a symlink into `node_modules/job-forge/` by default. To customize locally without modifying the harness: delete the symlink and create a real file with the same name `job-forge sync` will skip it on future updates. Or override in `opencode.json` under `agent.general-paid.model`.
80
+ Run `npm run build:config` if you are editing a harness checkout. In a consumer project, keep local overrides in `opencode.json` or replace a symlinked `.opencode/agents/<name>.md` with a real file.
79
81
 
80
- ### Swap the free tier
82
+ ### Swap the procedural route
81
83
 
82
- The primary `@general-free` model is set in `models.yaml` via the `fast`
83
- role's `targets.opencode` override. Change that if you want a different
84
- default. The `fallback_models` list still lives in
85
- `.opencode/agents/general-free.md` / `iso/agents/general-free.md`.
84
+ The `@general-free` model is set in `models.yaml` via the `fast` role's
85
+ `targets.opencode` override. Change that if you want a different procedural
86
+ default:
87
+
88
+ ```yaml
89
+ roles:
90
+ fast:
91
+ targets:
92
+ opencode:
93
+ provider: opencode
94
+ model: opencode-go/deepseek-v4-flash
95
+ ```
86
96
 
87
97
  ### Add a custom agent
88
98
 
@@ -125,8 +135,8 @@ npx job-forge tokens --session <session-id>
125
135
  ```
126
136
 
127
137
  Healthy pattern after this architecture lands:
128
- - **Free OpenRouter defaults** now cover procedural, quality-sensitive, and extractor work on OpenCode
129
- - If you opt back into a paid model, do it deliberately and only for the role that needs it
138
+ - **DeepSeek V4 Flash defaults** cover procedural, quality-sensitive, and extractor work on OpenCode
139
+ - If you opt into a stronger model, do it deliberately and only for the role that needs it
130
140
  - Session titles prefixed `@general-free`, `@general-paid`, `@glm-minimal` appear in the list — confirms delegation actually happened
131
141
  - `cache_read` >> `cache_creation` on parallel subagent runs within a 5-min window
132
142
 
@@ -134,67 +144,24 @@ Failure pattern to watch for:
134
144
  - **All messages showing up under your primary model** (no `@general-*` titles) → orchestrator isn't delegating. Check the Pre-flight delegation rule in AGENTS.md is being followed; tighten wording if not.
135
145
  - **High cache-creation with near-zero cache-read across parallel workers** → workers aren't firing within the 5-min cache TTL, or the shared prefix isn't byte-identical (see [batch/README.md](../batch/README.md) for the prompt-cache-friendly batch pattern).
136
146
 
137
- ## Automatic fallback on rate limits / 5xx
138
-
139
- A rate-limited or overloaded free-tier model would otherwise wedge the whole subagent flow — the delegated task errors and the orchestrator sits stuck. The harness ships with [`@razroo/opencode-model-fallback`](https://www.npmjs.com/package/@razroo/opencode-model-fallback) (added as a dependency in the scaffolder) to rotate agents through a configured `fallback_models` chain automatically.
140
-
141
- Default chains ship upstream in each agent's YAML frontmatter (`node_modules/job-forge/.opencode/agents/*.md`, symlinked into your project's `.opencode/agents/`):
142
-
143
- | Agent | Primary | Fallback chain (in order) |
144
- |-------|---------|---------------------------|
145
- | `@general-free` | `opencode/big-pickle` | `openrouter/z-ai/glm-4.5-air:free` → `openrouter/openai/gpt-oss-20b:free` → `openrouter/nvidia/nemotron-3-nano-30b-a3b:free` → `openrouter/qwen/qwen3-coder:free` |
146
- | `@general-paid` | `openrouter/qwen/qwen3-next-80b-a3b-instruct:free` | `openrouter/openai/gpt-oss-120b:free` → `openrouter/nvidia/nemotron-3-super-120b-a12b:free` → `openrouter/z-ai/glm-4.5-air:free` → `openrouter/qwen/qwen3-coder:free` → `openrouter/google/gemma-4-31b-it:free` → `openrouter/meta-llama/llama-3.3-70b-instruct:free` |
147
- | `@glm-minimal` | `openrouter/openai/gpt-oss-20b:free` | `openrouter/google/gemma-4-26b-a4b-it:free` → `openrouter/nvidia/nemotron-nano-9b-v2:free` → `openrouter/google/gemma-4-31b-it:free` → `openrouter/z-ai/glm-4.5-air:free` |
148
-
149
- These chains are deliberately free-only so the default OpenCode path never needs to pay. **Note:** OpenCode model IDs must use the provider prefix it expects (`openrouter/...`, `opencode/...`, etc.). The raw OpenRouter model slug by itself is not enough.
150
-
151
- Consumers **do not need to configure anything** to get these defaults: the subagent chains arrive via the symlinked agent MD files, and the harness ships `.opencode/opencode-model-fallback.json` for the main orchestrator / any agent without its own list. That JSON is **generated by `@razroo/iso-harness`** from the top-level `opencodeModelFallback` object in the harness's `iso/config.json` (same `iso build` pass as `opencode.json`), so it stays in sync with published releases — not a hand-edited file in git. `@razroo/opencode-model-fallback` (≥0.3.1) reads per-agent chains from the frontmatter-derived `fallback_models` field and falls through to the global file when no per-agent list exists. The consumer's `opencode.json` only needs `"plugin": ["@razroo/opencode-model-fallback"]` — which the scaffolder sets automatically.
152
-
153
- **When fallback fires:** the plugin pattern-matches rate-limit / 5xx / quota / "overloaded" / insufficient-balance style errors (including Venice / Diem copy — see `retryable_error_patterns` in the harness source). Failed models enter a cooldown before they're retried. Every rotation logs to `~/.config/opencode/opencode-model-fallback.log` with the trigger error, original model, and target model — grep for `"Auto-retrying with fallback model"` to confirm it fired.
154
-
155
- **Orchestrator policy (all harnesses):** if an upgraded `@general-paid` apply still fails after model fallback, follow **[D3f]** in the shared instructions (`AGENTS.md` / `CLAUDE.md` / Cursor rules / Codex `AGENTS.md`) — re-dispatch the same URL once on `@general-free` before marking FAILED.
156
-
157
- ### Overriding an upstream chain
158
-
159
- Add an `agent.<name>.fallback_models` block to your project's `opencode.json`. Top-level entries win over upstream frontmatter:
160
-
161
- ```json
162
- {
163
- "agent": {
164
- "general-free": {
165
- "fallback_models": ["my/preferred-free", "my/preferred-paid"]
166
- }
167
- }
168
- }
169
- ```
170
-
171
- ### Global fallback chain (agents without their own)
172
-
173
- Source of truth: **`iso/config.json` → `opencodeModelFallback`** (iso-harness writes `.opencode/opencode-model-fallback.json` on `npm run build:config` / `prepack`). Applies to the orchestrator and any agent whose `fallback_models` is empty. Example shape:
174
-
175
- ```json
176
- {
177
- "cooldown_seconds": 60,
178
- "timeout_seconds": 30,
179
- "notify_on_fallback": true,
180
- "fallback_models": [
181
- "openrouter/openai/gpt-oss-120b:free",
182
- "openrouter/z-ai/glm-4.5-air:free"
183
- ],
184
- "retryable_error_patterns": ["(?i)venice.*insufficient"]
185
- }
186
- ```
187
-
188
- `retryable_error_patterns` extends the plugin's built-in matchers — use it for provider-specific strings (Venice Diem balance, etc.) that do not match the stock regexes.
147
+ ## Fallback policy
189
148
 
190
- ### Disabling fallback
149
+ JobForge no longer ships automatic free-model fallback for OpenCode. The
150
+ previous free fallback chain solved some transient model outages, but real
151
+ application traces showed it could also freeze runs upstream or route into
152
+ provider balance failures. The default is now simpler: each OpenCode role uses
153
+ `opencode-go/deepseek-v4-flash`, and errors surface directly in telemetry.
191
154
 
192
- Remove `"@razroo/opencode-model-fallback"` from `opencode.json:plugin` agents keep their `model:` primary and errors propagate normally.
155
+ Use `npm run telemetry:status` or `npm run telemetry:show -- <session>` to
156
+ inspect provider errors, child outcomes, and pending TSVs after a run. If you
157
+ want a local fallback chain anyway, add it explicitly in your own
158
+ `opencode.json` or agent frontmatter so the cost/reliability tradeoff is
159
+ visible in your project rather than hidden in the harness defaults.
193
160
 
194
161
  ## Known limitations
195
162
 
196
163
  - **opencode's 5-minute cache TTL is hardcoded.** The 1-hour cache (Anthropic beta, `extended-cache-ttl-2025-04-11`) is not plumbed through opencode as of 2026-04-15. Long batch runs (>5 min between workers) will miss cache every cycle. Upstream fix would be 2 lines in `packages/opencode/src/provider/`.
197
- - **`instructions` is top-level, not per-agent.** Files listed in `opencode.json:instructions` load for every agent including free-tier. This is fine for `cv.md` and `_shared.md` (they're small and useful everywhere), but means you can't hide heavy context from free agents via instructions — use per-agent `prompt:` files for that.
164
+ - **`instructions` is top-level, not per-agent.** Files listed in `opencode.json:instructions` load for every agent. This is fine for `cv.md` and `_shared.md` (they're small and useful everywhere), but means you can't hide heavy context from lower-cost agents via instructions — use per-agent `prompt:` files for that.
198
165
  - **`reasoningEffort` support varies by provider.** Anthropic accepts `thinking: { type: "disabled" }`; opencode-labs models may need the `variant` pattern. See the `reasoningEffort` values in the opencode docs.
199
166
 
200
167
  ## See also
package/docs/README.md CHANGED
@@ -20,7 +20,7 @@ See [SETUP.md](SETUP.md) for both paths.
20
20
  | [SETUP.md](SETUP.md) | Prerequisites, both install paths, profile and CV, portals, `npx job-forge verify`, optional Go dashboard, token usage tracking, [troubleshooting](SETUP.md#troubleshooting) |
21
21
  | [ARCHITECTURE.md](ARCHITECTURE.md) | [Package architecture](ARCHITECTURE.md#package-architecture-v200) (consumer vs harness split), modes under `modes/`, single-offer flow, batch runner, tracker and scripts, [contributor touchpoints](ARCHITECTURE.md#contributing-touchpoints) |
22
22
  | [CUSTOMIZATION.md](CUSTOMIZATION.md) | Profile, archetypes in `_shared.md`, `portals.yml`, CV template, canonical states, optional story bank and opencode hooks. Also: how to customize a symlinked mode file locally |
23
- | [MODEL-ROUTING.md](MODEL-ROUTING.md) | Why the harness uses three cost-tiered subagents (`@general-free`, `@general-paid`, `@glm-minimal`), how routing is enforced (permission + tool-surface trim + reasoningEffort), how to swap models or add agents, and the automatic fallback-on-rate-limit behavior via [`@razroo/opencode-model-fallback`](https://www.npmjs.com/package/@razroo/opencode-model-fallback) |
23
+ | [MODEL-ROUTING.md](MODEL-ROUTING.md) | Why the harness uses three cost-tiered subagents (`@general-free`, `@general-paid`, `@glm-minimal`), how routing is enforced (permission + tool-surface trim + reasoningEffort), and how to swap models or add agents |
24
24
  | [examples/README.md](../examples/README.md) | Fictional CV samples (per-role markdown), optional digest, fictional [`sample-jd.md`](../examples/sample-jd.md) for `local:jds/…` shape, and sample report — starting point for new `cv.md`, JD-on-disk layout, or contributor archetypes (cloud infrastructure, agent platform) |
25
25
  | [batch/README.md](../batch/README.md) | Batch TSV format, `batch-runner.sh`, `tracker-additions/` merge flow with `npx job-forge merge` |
26
26
  | [jds/README.md](../jds/README.md) | Markdown JDs on disk; `local:jds/{file}.md` lines in `data/pipeline.md` |
@@ -1,24 +1,16 @@
1
1
  ---
2
- description: Procedural worker on free-tier model. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
2
+ description: Procedural worker on the low-cost DeepSeek V4 Flash OpenCode route. Use for form filling via Geometra, tracker updates, TSV merges, scan dedup, OTP retrieval, and other mechanical/scripted tasks where quality-sensitive text generation is NOT required.
3
3
  role: fast
4
4
  targets:
5
- # No inline model: iso-route's "standard" preset maps role "fast" to
6
- # each harness's cheapest reliable model. Claude Code reads
5
+ # No inline model: JobForge's models.yaml maps role "fast" to each
6
+ # harness's cheapest reliable model. On OpenCode that is pinned to
7
+ # opencode-go/deepseek-v4-flash. Claude Code reads
7
8
  # .claude/iso-route.resolved.json; OpenCode reads opencode.json's
8
9
  # agent.fast.model (iso-harness 0.6.0+).
9
10
  opencode:
10
11
  mode: subagent
11
12
  temperature: 0.1
12
13
  reasoningEffort: minimal
13
- # Primary comes from models.yaml: opencode/big-pickle on OpenCode.
14
- # Fallback chain stays free-only and intentionally excludes
15
- # openrouter/minimax/minimax-m2.5:free because recent traces showed
16
- # repeated read({ path|file_path }) schema drift on that route.
17
- fallback_models:
18
- - openrouter/z-ai/glm-4.5-air:free
19
- - openrouter/openai/gpt-oss-20b:free
20
- - openrouter/nvidia/nemotron-3-nano-30b-a3b:free
21
- - openrouter/qwen/qwen3-coder:free
22
14
  tools:
23
15
  geometra_connect: true
24
16
  geometra_page_model: true
@@ -34,7 +26,7 @@ targets:
34
26
  task: false
35
27
  ---
36
28
 
37
- You are the @general-free subagent. You run on a free-tier model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
29
+ You are the @general-free subagent. You run on JobForge's low-cost procedural model, which means the orchestrator has delegated this task to you **specifically because the work is procedural**: deterministic steps, scripted outputs, no nuanced writing required.
38
30
 
39
31
  ## Run This Pre-Flight First Every Time
40
32
 
@@ -1,28 +1,16 @@
1
1
  ---
2
- description: Quality-sensitive worker on the strongest free-tier OpenCode model by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
2
+ description: Quality-sensitive worker on the low-cost DeepSeek V4 Flash OpenCode route by default. Use for offer evaluation narratives (Blocks A-F), cover letter generation, "Why X?" form answers, interview STAR stories, and other tasks where writing quality and judgment matter.
3
3
  role: quality
4
4
  targets:
5
- # No inline model: JobForge's models.yaml maps role "quality" to a
6
- # free OpenRouter model on OpenCode, while Claude/Codex keep their
7
- # quality-tier defaults from the standard preset. Claude Code reads
5
+ # No inline model: JobForge's models.yaml maps role "quality" to
6
+ # opencode-go/deepseek-v4-flash on OpenCode, while Claude/Codex keep
7
+ # their quality-tier defaults from the standard preset. Claude Code reads
8
8
  # .claude/iso-route.resolved.json; OpenCode reads opencode.json's
9
9
  # agent.quality.model (iso-harness 0.6.0+).
10
10
  opencode:
11
11
  mode: subagent
12
12
  temperature: 0.3
13
13
  reasoningEffort: medium
14
- # Primary (qwen/qwen3-next-80b-a3b-instruct:free) resolves from the
15
- # openrouter-free preset. First fallbacks intentionally avoid another
16
- # immediate hop through the same Venice/Qwen pool when OpenRouter
17
- # returns "[Venice] insufficient …" — gpt-oss-120b + nemotron are
18
- # usually different backends. Remaining picks stay free-only.
19
- fallback_models:
20
- - openrouter/openai/gpt-oss-120b:free
21
- - openrouter/nvidia/nemotron-3-super-120b-a12b:free
22
- - openrouter/z-ai/glm-4.5-air:free
23
- - openrouter/qwen/qwen3-coder:free
24
- - openrouter/google/gemma-4-31b-it:free
25
- - openrouter/meta-llama/llama-3.3-70b-instruct:free
26
14
  tools:
27
15
  geometra_connect: true
28
16
  geometra_page_model: true
@@ -40,10 +28,10 @@ targets:
40
28
 
41
29
  You are the @general-paid subagent. The orchestrator delegated this task to you because it requires quality writing or judgment — the kind of work `@general-free` isn't well-suited for.
42
30
 
43
- On OpenCode, this agent now defaults to a free OpenRouter model. On other
44
- harnesses, the same role may still resolve to a premium model. Your job is
45
- still the same: produce the best final writing you can from the context you
46
- were given.
31
+ On OpenCode, this agent defaults to DeepSeek V4 Flash so application work
32
+ does not fall back into overloaded free OpenRouter pools. On other harnesses,
33
+ the same role may still resolve to a premium model. Your job is still the
34
+ same: produce the best final writing you can from the context you were given.
47
35
 
48
36
  ## Do These Tasks
49
37
 
@@ -1,23 +1,16 @@
1
1
  ---
2
- description: Narrow-scope extractor on free-tier model. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
2
+ description: Narrow-scope extractor on the low-cost DeepSeek V4 Flash OpenCode route. Use for single-purpose tasks where the orchestrator passes the exact input and expects a small, structured output — e.g., "extract these 8 fields from this JD text" or "parse this form schema into a label→type map". NOT for multi-step workflows.
3
3
  role: minimal
4
4
  targets:
5
- # No inline model: iso-route's "standard" preset maps role "minimal" to
6
- # each harness's smallest credible model. Claude Code reads
5
+ # No inline model: JobForge's models.yaml maps role "minimal" to each
6
+ # harness's smallest credible model. On OpenCode that is pinned to
7
+ # opencode-go/deepseek-v4-flash. Claude Code reads
7
8
  # .claude/iso-route.resolved.json; OpenCode reads opencode.json's
8
9
  # agent.minimal.model (iso-harness 0.6.0+).
9
10
  opencode:
10
11
  mode: subagent
11
12
  temperature: 0
12
13
  reasoningEffort: none
13
- # Primary (openai/gpt-oss-20b:free) resolves from openrouter-free
14
- # preset. Fallback chain sticks to small dense models with reliable
15
- # structured-output behavior — no creative generation upstream.
16
- fallback_models:
17
- - openrouter/google/gemma-4-26b-a4b-it:free
18
- - openrouter/nvidia/nemotron-nano-9b-v2:free
19
- - openrouter/google/gemma-4-31b-it:free
20
- - openrouter/z-ai/glm-4.5-air:free
21
14
  tools:
22
15
  geometra_*: false
23
16
  gmail_*: false
package/iso/config.json CHANGED
@@ -1,49 +1,8 @@
1
1
  {
2
- "opencodeModelFallback": {
3
- "cooldown_seconds": 60,
4
- "timeout_seconds": 30,
5
- "notify_on_fallback": true,
6
- "fallback_models": [
7
- "openrouter/openai/gpt-oss-120b:free",
8
- "openrouter/z-ai/glm-4.5-air:free",
9
- "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
10
- "openrouter/qwen/qwen3-next-80b-a3b-instruct:free"
11
- ],
12
- "retryable_error_patterns": [
13
- "\\bvenice\\b",
14
- "insufficient\\s+usd",
15
- "insufficient\\s+.*\\s+diem",
16
- "diem\\s+balance",
17
- "add\\s+credits",
18
- "chutes",
19
- "insufficient\\s+(?:credits?|funds?|balance)",
20
- "credit.*balance.*too.*low",
21
- "(?:temporarily\\s+)?unavailable|overloaded|try\\s+again"
22
- ]
23
- },
24
2
  "targets": {
25
3
  "opencode": {
26
- "plugin": ["@razroo/opencode-model-fallback"],
27
4
  "instructions": ["templates/states.yml"],
28
- "small_model": "openrouter/google/gemma-4-26b-a4b-it:free",
29
- "provider": {
30
- "openrouter": {
31
- "models": {
32
- "qwen/qwen3-coder:free": {},
33
- "z-ai/glm-4.5-air:free": {},
34
- "qwen/qwen3-next-80b-a3b-instruct:free": {},
35
- "openai/gpt-oss-20b:free": {},
36
- "openai/gpt-oss-120b:free": {},
37
- "minimax/minimax-m2.5:free": {},
38
- "nvidia/nemotron-3-super-120b-a12b:free": {},
39
- "nvidia/nemotron-3-nano-30b-a3b:free": {},
40
- "nvidia/nemotron-nano-9b-v2:free": {},
41
- "google/gemma-4-26b-a4b-it:free": {},
42
- "google/gemma-4-31b-it:free": {},
43
- "meta-llama/llama-3.3-70b-instruct:free": {}
44
- }
45
- }
46
- }
5
+ "small_model": "opencode-go/deepseek-v4-flash"
47
6
  }
48
7
  }
49
8
  }
@@ -5,7 +5,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
5
5
  ## Hard limits
6
6
 
7
7
  - [H1] Max 2 parallel `task` dispatches per message. For N jobs, run `ceil(N/2)` sequential rounds of 2. A round is not complete until both subagents return a final outcome (`APPLIED`, `APPLY FAILED`, `SKIP`, `Discarded`, or a written TSV path). A `task` tool result that only gives a session id / title is a launch acknowledgement, not completion. Applies in all modes, for all user phrasings ("urgent", "apply to 10 jobs now").
8
- why: higher parallelism blows through free-tier rate limits; each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider-fallback recoveries
8
+ why: each subagent requires post-cleanup and racing more than 2 reliably loses at least one result. On 2026-04-25 the orchestrator launched round 2 while round 1 had only returned task ids, leaving four application subagents in flight and losing two provider recoveries
9
9
 
10
10
  - [H2] Max 1 application per company+role. Before every `apply` dispatch, grep all four sources for the URL and for `company+role`: `data/pipeline.md`, all `data/applications/*.md` day files, `batch/tracker-additions/*.tsv`, `batch/tracker-additions/merged/*.tsv`. If any source shows APPLIED / Applied, skip the dispatch.
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
@@ -37,7 +37,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
37
37
  why: iso-trace showed 0.25% Agent calls across 5174 turns under a prior over-broad "delegate before 2nd tool call" rule — the rule was ignored in practice; narrowing matches the original cache-bust incident
38
38
 
39
39
  - [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.
40
- 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
+ why: OpenCode routes all JobForge tiers through DeepSeek V4 Flash by default now; recent traces showed free OpenRouter fallbacks freezing or hitting provider balance errors during applications
41
41
 
42
42
  - [D3] Read the active mode file before dispatch. Mode files own score gates, provider fallback, portal runbooks, and output shape.
43
43
  why: mode-specific rules change faster than global orchestration rules; keeping them out of the shared prefix preserves cache efficiency and prevents stale branches
package/models.yaml CHANGED
@@ -1,17 +1,15 @@
1
1
  # JobForge model policy.
2
2
  #
3
- # Extends @razroo/iso-route's bundled "openrouter-free" preset a
4
- # curated zero-cost routing config that keeps Claude Code / Codex on
5
- # their native providers but routes OpenCode through explicit free
6
- # OpenRouter model IDs. Preset content lives in
7
- # node_modules/@razroo/iso-route/presets/openrouter-free.yaml; run
8
- # `npx iso-route plan models.yaml` to see the resolved policy.
3
+ # Extends @razroo/iso-route's bundled "standard" preset, then pins every
4
+ # OpenCode route to DeepSeek V4 Flash. Recent traces showed free OpenRouter
5
+ # routes freezing or falling through Venice balance errors, so JobForge's
6
+ # OpenCode default is now "best affordable paid" rather than "free".
9
7
  #
10
8
  # JobForge's subagents bind to preset roles via the `role:` field in
11
9
  # iso/agents/<slug>.md:
12
- # @general-free → role: fast (Haiku / OpenCode big-pickle / gpt-5.4-mini)
13
- # @general-paid → role: quality (Opus 4.7 / OpenRouter Qwen3 Next 80B free / gpt-5.4)
14
- # @glm-minimal → role: minimal (Haiku / OpenRouter GPT-OSS-20B free / gpt-5.4-nano)
10
+ # @general-free → role: fast (Haiku / DeepSeek V4 Flash / gpt-5.4-mini)
11
+ # @general-paid → role: quality (Opus 4.7 / DeepSeek V4 Flash / gpt-5.4)
12
+ # @glm-minimal → role: minimal (Haiku / DeepSeek V4 Flash / gpt-5.4-nano)
15
13
  #
16
14
  # Override anything by adding fields here. For example, to pin Opus on
17
15
  # Claude Code for the @general-paid (quality) role:
@@ -29,11 +27,27 @@
29
27
  # provider: openai
30
28
  # model: gpt-5.4
31
29
 
32
- extends: openrouter-free
30
+ extends: standard
31
+
32
+ default:
33
+ targets:
34
+ opencode:
35
+ provider: opencode
36
+ model: opencode-go/deepseek-v4-flash
33
37
 
34
38
  roles:
35
39
  fast:
36
40
  targets:
37
41
  opencode:
38
42
  provider: opencode
39
- model: opencode/big-pickle
43
+ model: opencode-go/deepseek-v4-flash
44
+ quality:
45
+ targets:
46
+ opencode:
47
+ provider: opencode
48
+ model: opencode-go/deepseek-v4-flash
49
+ minimal:
50
+ targets:
51
+ opencode:
52
+ provider: opencode
53
+ model: opencode-go/deepseek-v4-flash
package/modes/apply.md CHANGED
@@ -17,7 +17,7 @@ Live application assistant. Reads the active application form in Chrome (via Geo
17
17
  why: prior aborted subagents leave Chromium sessions stuck in the pool; next `geometra_connect` fails with "Not connected" (see root `[H3]`)
18
18
 
19
19
  - [H5] Max 2 parallel `task` dispatches per round. For N jobs, run `ceil(N/2)` sequential rounds of 2. Never emit 3+ dispatches in a single message. Do not start the next round until both current-round subagents return final outcomes (`APPLIED`, `APPLY FAILED`, `SKIP`, `Discarded`, or a written TSV path); task/session ids are only launch receipts.
20
- why: free-tier rate limits + subagent post-cleanup cost; racing more than 2 reliably loses at least one result (see root `[H1]`). A 2026-04-25 OpenCode trace launched round 2 while round 1 was still running, then lost two fallback recoveries
20
+ why: subagent post-cleanup cost and portal state make racing more than 2 unreliable (see root `[H1]`). A 2026-04-25 OpenCode trace launched round 2 while round 1 was still running, then lost two provider recoveries
21
21
 
22
22
  ## Defaults
23
23
 
@@ -46,10 +46,10 @@ Live application assistant. Reads the active application form in Chrome (via Geo
46
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
47
 
48
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
49
+ why: high-stakes applications need the quality-sensitive prompt and medium reasoning budget even though OpenCode now routes both application tiers through DeepSeek V4 Flash by default
50
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
51
+ - [D9] If a subagent fails with provider-side errors, do not auto-downgrade or re-dispatch the same URL. Report the provider failure, leave any TSV untouched unless there is a confirmed outcome, and inspect telemetry before retrying.
52
+ why: OpenCode now pins all JobForge application tiers to DeepSeek V4 Flash; switching `@general-paid` `@general-free` changes the prompt/tool budget but not the provider route, so automatic duplicate dispatches add risk without fixing provider availability
53
53
 
54
54
  ## Procedure
55
55
 
@@ -65,7 +65,7 @@ Live application assistant. Reads the active application form in Chrome (via Geo
65
65
  10. Generate answers from Block B + Block F + Section G + JD.
66
66
  11. Submit as ONE `run_actions` call [H1] using labels [D6] with `imeFriendly: true` [D4].
67
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].
68
+ 13. On provider failure, stop and inspect telemetry before any retry [D9].
69
69
  14. On OTP prompt, fetch the code from Gmail via `gmail_get_message`.
70
70
  15. Submit the OTP with `geometra_fill_otp` and click Submit.
71
71
  16. Write outcome as `batch/tracker-additions/*.tsv` [H3].
@@ -98,7 +98,7 @@ Or, on failure:
98
98
  APPLY FAILED AFTER RECOVERY: <url>
99
99
  Error 1: <first error>
100
100
  Error 2: <post-recovery error>
101
- Recommend: re-dispatch on @general-paid
101
+ Recommend: inspect telemetry before retrying this URL
102
102
  ```
103
103
 
104
104
  ---
@@ -359,10 +359,10 @@ Call 4: geometra_run_actions({
359
359
  APPLY FAILED AFTER RECOVERY: <URL>
360
360
  Error 1: <first error message>
361
361
  Error 2: <error after recovery>
362
- Recommend: re-dispatch on @general-paid
362
+ Recommend: inspect telemetry before retrying this URL
363
363
  ```
364
364
 
365
- Do NOT try a third time. Do NOT try a different approach. The orchestrator will decide whether to re-dispatch on a bigger model.
365
+ Do NOT try a third time. Do NOT try a different approach. The orchestrator will decide whether to retry after inspecting telemetry.
366
366
 
367
367
  ### Skip schema re-fetches mid-flow (Rule D)
368
368
 
package/opencode.json CHANGED
@@ -1,33 +1,15 @@
1
1
  {
2
2
  "$schema": "https://opencode.ai/config.json",
3
- "model": "openrouter/qwen/qwen3-coder:free",
3
+ "model": "opencode-go/deepseek-v4-flash",
4
4
  "agent": {
5
5
  "fast": {
6
- "model": "opencode/big-pickle"
6
+ "model": "opencode-go/deepseek-v4-flash"
7
7
  },
8
8
  "quality": {
9
- "model": "openrouter/qwen/qwen3-next-80b-a3b-instruct:free"
9
+ "model": "opencode-go/deepseek-v4-flash"
10
10
  },
11
11
  "minimal": {
12
- "model": "openrouter/openai/gpt-oss-20b:free"
13
- }
14
- },
15
- "provider": {
16
- "openrouter": {
17
- "models": {
18
- "qwen/qwen3-coder:free": {},
19
- "z-ai/glm-4.5-air:free": {},
20
- "qwen/qwen3-next-80b-a3b-instruct:free": {},
21
- "openai/gpt-oss-20b:free": {},
22
- "openai/gpt-oss-120b:free": {},
23
- "minimax/minimax-m2.5:free": {},
24
- "nvidia/nemotron-3-super-120b-a12b:free": {},
25
- "nvidia/nemotron-3-nano-30b-a3b:free": {},
26
- "nvidia/nemotron-nano-9b-v2:free": {},
27
- "google/gemma-4-26b-a4b-it:free": {},
28
- "google/gemma-4-31b-it:free": {},
29
- "meta-llama/llama-3.3-70b-instruct:free": {}
30
- }
12
+ "model": "opencode-go/deepseek-v4-flash"
31
13
  }
32
14
  },
33
15
  "mcp": {
@@ -66,11 +48,8 @@
66
48
  }
67
49
  }
68
50
  },
69
- "plugin": [
70
- "@razroo/opencode-model-fallback"
71
- ],
72
51
  "instructions": [
73
52
  "templates/states.yml"
74
53
  ],
75
- "small_model": "openrouter/google/gemma-4-26b-a4b-it:free"
54
+ "small_model": "opencode-go/deepseek-v4-flash"
76
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-forge",
3
- "version": "2.14.11",
3
+ "version": "2.14.12",
4
4
  "description": "AI-powered job search pipeline built on opencode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -97,7 +97,6 @@
97
97
  "@razroo/iso": "^0.2.5",
98
98
  "@razroo/iso-eval": "^0.4.0",
99
99
  "@razroo/iso-harness": "^0.6.1",
100
- "@razroo/iso-route": "^0.5.3",
101
- "@razroo/opencode-model-fallback": "^0.3.1"
100
+ "@razroo/iso-route": "^0.5.3"
102
101
  }
103
102
  }
@@ -21,9 +21,9 @@ const checks = [
21
21
  ["H7 distrusts subagent prose", () => every(files.instructions, ["must originate from a file", "not from prior subagent prose"])],
22
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
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"])],
24
+ ["apply mode blocks provider auto-downgrade", () => every(files.apply, ["[D9]", "do not auto-downgrade", "inspect telemetry before retrying"])],
25
+ ["models policy pins OpenCode to DeepSeek V4 Flash", () => /extends:\s*standard/.test(files.models) && count(files.models, "opencode-go/deepseek-v4-flash") >= 4],
26
+ ["OpenCode fallback plugin is not configured", () => !every(files.config, ["opencodeModelFallback", "@razroo/opencode-model-fallback"])],
27
27
  ];
28
28
 
29
29
  const failures = checks
@@ -41,3 +41,7 @@ console.log(`JobForge iso smoke passed (${checks.length} checks).`);
41
41
  function every(source, needles) {
42
42
  return needles.every((needle) => source.includes(needle));
43
43
  }
44
+
45
+ function count(source, needle) {
46
+ return source.split(needle).length - 1;
47
+ }
@@ -1,22 +0,0 @@
1
- {
2
- "cooldown_seconds": 60,
3
- "timeout_seconds": 30,
4
- "notify_on_fallback": true,
5
- "fallback_models": [
6
- "openrouter/openai/gpt-oss-120b:free",
7
- "openrouter/z-ai/glm-4.5-air:free",
8
- "openrouter/nvidia/nemotron-3-super-120b-a12b:free",
9
- "openrouter/qwen/qwen3-next-80b-a3b-instruct:free"
10
- ],
11
- "retryable_error_patterns": [
12
- "\\bvenice\\b",
13
- "insufficient\\s+usd",
14
- "insufficient\\s+.*\\s+diem",
15
- "diem\\s+balance",
16
- "add\\s+credits",
17
- "chutes",
18
- "insufficient\\s+(?:credits?|funds?|balance)",
19
- "credit.*balance.*too.*low",
20
- "(?:temporarily\\s+)?unavailable|overloaded|try\\s+again"
21
- ]
22
- }