job-forge 2.14.36 → 2.14.38

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.
@@ -17,7 +17,8 @@ Call 3: geometra_connect({
17
17
  pageUrl: "<the URL from the orchestrator's task>",
18
18
  isolated: true,
19
19
  headless: true,
20
- slowMo: 350
20
+ slowMo: 350,
21
+ stealth: true
21
22
  })
22
23
  ```
23
24
 
@@ -25,7 +26,7 @@ Call 3: geometra_connect({
25
26
 
26
27
  1. **Always run Call 1 and Call 2.** Do not skip Call 2 even if Call 1 returns an empty session list. `geometra_disconnect({ closeBrowser: true })` is a safe no-op on an empty pool.
27
28
  2. **Do not reason about Call 1's output.** Don't look at it and decide "the pool looks clean, I'll skip Call 2". Just always call Call 2 next. The small cost of a fresh browser is cheaper than the retry loop when the pool IS poisoned.
28
- 3. **Always use `isolated: true, headless: true, slowMo: 350`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
29
+ 3. **Always use `isolated: true, headless: true, slowMo: 350, stealth: true`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
29
30
  4. **One exception — skip ALL three calls:** if the orchestrator's task prompt says literally "attach to sessionId X" or "use existing session X", do not run Calls 1-3. Go straight to `geometra_page_model({ sessionId: "X" })` and proceed.
30
31
 
31
32
  ### Read Why This Exists
@@ -17,7 +17,7 @@ model_provider = "openai"
17
17
 
18
18
  [mcp_servers.geometra]
19
19
  command = "npx"
20
- args = ["-y", "@geometra/mcp"]
20
+ args = ["-y", "@geometra/mcp@1.61.3"]
21
21
 
22
22
  [mcp_servers.gmail]
23
23
  command = "npx"
package/.cursor/mcp.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@geometra/mcp"
7
+ "@geometra/mcp@1.61.3"
8
8
  ]
9
9
  },
10
10
  "gmail": {
@@ -16,7 +16,8 @@ Call 3: geometra_connect({
16
16
  pageUrl: "<the URL from the orchestrator's task>",
17
17
  isolated: true,
18
18
  headless: true,
19
- slowMo: 350
19
+ slowMo: 350,
20
+ stealth: true
20
21
  })
21
22
  ```
22
23
 
@@ -24,7 +25,7 @@ Call 3: geometra_connect({
24
25
 
25
26
  1. **Always run Call 1 and Call 2.** Do not skip Call 2 even if Call 1 returns an empty session list. `geometra_disconnect({ closeBrowser: true })` is a safe no-op on an empty pool.
26
27
  2. **Do not reason about Call 1's output.** Don't look at it and decide "the pool looks clean, I'll skip Call 2". Just always call Call 2 next. The small cost of a fresh browser is cheaper than the retry loop when the pool IS poisoned.
27
- 3. **Always use `isolated: true, headless: true, slowMo: 350`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
28
+ 3. **Always use `isolated: true, headless: true, slowMo: 350, stealth: true`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
28
29
  4. **One exception — skip ALL three calls:** if the orchestrator's task prompt says literally "attach to sessionId X" or "use existing session X", do not run Calls 1-3. Go straight to `geometra_page_model({ sessionId: "X" })` and proceed.
29
30
 
30
31
  ### Read Why This Exists
@@ -33,8 +33,8 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
33
33
  - [H7] Load-bearing facts passed to downstream subagents must originate from a file, not from prior subagent prose. Authoritative sources: `data/pipeline.md`, `data/scan-history.tsv`, `batch/scan-output-*.md`, `reports/{num}-*.md` with `**URL:**` / `**Score:**` headers, emitted score JSON validated by `npx job-forge score:check --input ...`, `batch/tracker-additions/*.tsv`, cached JD content returned by `npx job-forge cache:get --url ...`, source path/line pointers returned by `npx job-forge index:query ...`, materialized fact records returned by `npx job-forge facts:query ...`, selected next actions returned by `npx job-forge prioritize:select ...`, and lineage records returned by `npx job-forge lineage:explain ...`.
34
34
  why: 2026-04-18 scan subagent returned 30 fabricated Greenhouse IDs in prose (plausible-looking, non-existent); orchestrator dispatched 30 downstream subagents that all 404'd. Subagents can hallucinate IDs, scores, and confirmation text — round-trip through a file or don't trust the value
35
35
 
36
- - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
37
- why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions
36
+ - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object plus `stealth: true` to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
37
+ why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions. Geometra MCP >=1.61.3 can launch CloakBrowser stealth Chromium via `stealth: true`, which belongs with JobForge portal sessions instead of stock Playwright Chromium
38
38
 
39
39
  ## Defaults
40
40
 
@@ -67,7 +67,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
67
67
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
68
68
  2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
69
69
  3. Read the active mode file [D3]. Use local helpers when they can replace broad file reads, prose math, manual policy checks, or artifact reuse decisions [D8]. Decide inline vs delegated work [D1].
70
- 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy prompt hygiene [H8].
70
+ 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy/stealth prompt hygiene [H8].
71
71
  5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b], then settle the round with postflight status [D8].
72
72
  6. Keep multi-job form-filling out of the orchestrator [H4].
73
73
  7. Cross-check subagent facts against authoritative files [H7].
package/.mcp.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "command": "npx",
5
5
  "args": [
6
6
  "-y",
7
- "@geometra/mcp"
7
+ "@geometra/mcp@1.61.3"
8
8
  ]
9
9
  },
10
10
  "gmail": {
@@ -32,7 +32,8 @@ Call 3: geometra_connect({
32
32
  pageUrl: "<the URL from the orchestrator's task>",
33
33
  isolated: true,
34
34
  headless: true,
35
- slowMo: 350
35
+ slowMo: 350,
36
+ stealth: true
36
37
  })
37
38
  ```
38
39
 
@@ -40,7 +41,7 @@ Call 3: geometra_connect({
40
41
 
41
42
  1. **Always run Call 1 and Call 2.** Do not skip Call 2 even if Call 1 returns an empty session list. `geometra_disconnect({ closeBrowser: true })` is a safe no-op on an empty pool.
42
43
  2. **Do not reason about Call 1's output.** Don't look at it and decide "the pool looks clean, I'll skip Call 2". Just always call Call 2 next. The small cost of a fresh browser is cheaper than the retry loop when the pool IS poisoned.
43
- 3. **Always use `isolated: true, headless: true, slowMo: 350`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
44
+ 3. **Always use `isolated: true, headless: true, slowMo: 350, stealth: true`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
44
45
  4. **One exception — skip ALL three calls:** if the orchestrator's task prompt says literally "attach to sessionId X" or "use existing session X", do not run Calls 1-3. Go straight to `geometra_page_model({ sessionId: "X" })` and proceed.
45
46
 
46
47
  ### Read Why This Exists
@@ -0,0 +1,8 @@
1
+ ## OpenCode Addendum
2
+
3
+ These notes apply only inside OpenCode sessions.
4
+
5
+ - The OpenCode subagent dispatch primitive is `task`.
6
+ - A `task` result that only returns a session id/title is a launch acknowledgement, not a completed application outcome.
7
+ - Do not use `task` to poll status. Inspect tracker files, workflow artifacts, or `iso-trace` instead.
8
+ - When JobForge says "dispatch" or "subagent" in the shared contract, map that to OpenCode `task` calls here.
@@ -231,7 +231,7 @@ Step 5 — Loop in rounds of 2 (Hard Limit #1)
231
231
  pair = candidates[round*2 : round*2 + 2]
232
232
  # If proxy is configured, do not paste proxy values into prompts.
233
233
  # Say: "Proxy is configured; read config/profile.yml and pass its
234
- # top-level proxy object to every geometra_connect call."
234
+ # top-level proxy object plus stealth: true to every geometra_connect call."
235
235
  # Dispatch 1 or 2 task() calls in ONE message (never 3+)
236
236
  task(subagent_type=<tier per AGENTS.md routing>, prompt=<apply prompt for pair[0]>)
237
237
  task(subagent_type=<tier>, prompt=<apply prompt for pair[1]>) # only if pair has 2
package/AGENTS.md CHANGED
@@ -28,8 +28,8 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
28
28
  - [H7] Load-bearing facts passed to downstream subagents must originate from a file, not from prior subagent prose. Authoritative sources: `data/pipeline.md`, `data/scan-history.tsv`, `batch/scan-output-*.md`, `reports/{num}-*.md` with `**URL:**` / `**Score:**` headers, emitted score JSON validated by `npx job-forge score:check --input ...`, `batch/tracker-additions/*.tsv`, cached JD content returned by `npx job-forge cache:get --url ...`, source path/line pointers returned by `npx job-forge index:query ...`, materialized fact records returned by `npx job-forge facts:query ...`, selected next actions returned by `npx job-forge prioritize:select ...`, and lineage records returned by `npx job-forge lineage:explain ...`.
29
29
  why: 2026-04-18 scan subagent returned 30 fabricated Greenhouse IDs in prose (plausible-looking, non-existent); orchestrator dispatched 30 downstream subagents that all 404'd. Subagents can hallucinate IDs, scores, and confirmation text — round-trip through a file or don't trust the value
30
30
 
31
- - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
- why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions
31
+ - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object plus `stealth: true` to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
+ why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions. Geometra MCP >=1.61.3 can launch CloakBrowser stealth Chromium via `stealth: true`, which belongs with JobForge portal sessions instead of stock Playwright Chromium
33
33
 
34
34
  ## Defaults
35
35
 
@@ -62,7 +62,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
62
62
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
63
63
  2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
64
64
  3. Read the active mode file [D3]. Use local helpers when they can replace broad file reads, prose math, manual policy checks, or artifact reuse decisions [D8]. Decide inline vs delegated work [D1].
65
- 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy prompt hygiene [H8].
65
+ 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy/stealth prompt hygiene [H8].
66
66
  5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b], then settle the round with postflight status [D8].
67
67
  6. Keep multi-job form-filling out of the orchestrator [H4].
68
68
  7. Cross-check subagent facts against authoritative files [H7].
package/CLAUDE.md CHANGED
@@ -28,8 +28,8 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
28
28
  - [H7] Load-bearing facts passed to downstream subagents must originate from a file, not from prior subagent prose. Authoritative sources: `data/pipeline.md`, `data/scan-history.tsv`, `batch/scan-output-*.md`, `reports/{num}-*.md` with `**URL:**` / `**Score:**` headers, emitted score JSON validated by `npx job-forge score:check --input ...`, `batch/tracker-additions/*.tsv`, cached JD content returned by `npx job-forge cache:get --url ...`, source path/line pointers returned by `npx job-forge index:query ...`, materialized fact records returned by `npx job-forge facts:query ...`, selected next actions returned by `npx job-forge prioritize:select ...`, and lineage records returned by `npx job-forge lineage:explain ...`.
29
29
  why: 2026-04-18 scan subagent returned 30 fabricated Greenhouse IDs in prose (plausible-looking, non-existent); orchestrator dispatched 30 downstream subagents that all 404'd. Subagents can hallucinate IDs, scores, and confirmation text — round-trip through a file or don't trust the value
30
30
 
31
- - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
- why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions
31
+ - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object plus `stealth: true` to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
+ why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions. Geometra MCP >=1.61.3 can launch CloakBrowser stealth Chromium via `stealth: true`, which belongs with JobForge portal sessions instead of stock Playwright Chromium
33
33
 
34
34
  ## Defaults
35
35
 
@@ -62,7 +62,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
62
62
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
63
63
  2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
64
64
  3. Read the active mode file [D3]. Use local helpers when they can replace broad file reads, prose math, manual policy checks, or artifact reuse decisions [D8]. Decide inline vs delegated work [D1].
65
- 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy prompt hygiene [H8].
65
+ 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy/stealth prompt hygiene [H8].
66
66
  5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b], then settle the round with postflight status [D8].
67
67
  6. Keep multi-job form-filling out of the orchestrator [H4].
68
68
  7. Cross-check subagent facts against authoritative files [H7].
package/README.md CHANGED
@@ -74,11 +74,11 @@ JobForge turns opencode into a full job search command center. Instead of manual
74
74
  | **Deep Research** | Company research that feeds back into scores and interview prep |
75
75
  | **Smart LinkedIn Outreach** | Reads evaluation reports to craft targeted messages using top proof points |
76
76
  | **Portal Scanner** | 45+ companies pre-configured with fuzzy dedup for reposts |
77
- | **Batch Processing** | Parallel evaluation with `opencode run` workers, with honest verification flagging |
77
+ | **Batch Processing** | Parallel evaluation with headless AI CLI workers (`opencode run` or `codex exec`), with honest verification flagging |
78
78
  | **Durable Batch Orchestration** | `batch-runner.sh` uses `@razroo/iso-orchestrator` for resumable bundle execution, bounded fan-out, mutexed state writes, and workflow records in `.jobforge-runs/`. |
79
79
  | **Pipeline Integrity** | Automated merge, dedup, status normalization, health checks |
80
80
  | **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. |
81
- | **Trace + Telemetry + Guard + Contract + Score + Canon + Ledger + Capabilities + Context + Cache + Index + Facts + Timeline + Prioritize + Lineage + Preflight + Postflight + Redact + Migrate** | `job-forge trace:*` exposes local OpenCode transcripts, `job-forge telemetry:*` summarizes runs, `job-forge guard:*` audits deterministic policy rules, `templates/contracts.json` enforces artifact shape with `iso-contract`, `job-forge score:*` computes/checks weighted offer scores, `job-forge canon:*` derives stable URL/company/role identity keys, `job-forge ledger:*` queries append-only workflow state, `job-forge capabilities:*` checks role boundaries, `job-forge context:*` plans mode/reference context bundles, `job-forge cache:*` reuses fetched JD/artifact content, `job-forge index:*` queries compact source pointers, `job-forge facts:*` materializes source-backed job/application/candidate facts, `job-forge timeline:*` computes due/overdue follow-up actions, `job-forge prioritize:*` ranks local apply/follow-up candidates, `job-forge lineage:*` detects stale reports/PDFs after source changes, `job-forge preflight:*` plans bounded apply dispatch rounds from file-backed candidate facts, `job-forge postflight:*` settles dispatch outcomes/artifacts/post-steps, `job-forge redact:*` sanitizes local exports, and `job-forge migrate:*` applies safe consumer-project upgrades without MCP/tool-schema overhead. |
81
+ | **Trace + Telemetry + Guard + Contract + Score + Canon + Ledger + Capabilities + Context + Cache + Index + Facts + Timeline + Prioritize + Lineage + Preflight + Postflight + Redact + Migrate** | `job-forge trace:*` exposes local harness transcripts, `job-forge telemetry:*` summarizes runs, `job-forge guard:*` audits deterministic policy rules, `templates/contracts.json` enforces artifact shape with `iso-contract`, `job-forge score:*` computes/checks weighted offer scores, `job-forge canon:*` derives stable URL/company/role identity keys, `job-forge ledger:*` queries append-only workflow state, `job-forge capabilities:*` checks role boundaries, `job-forge context:*` plans mode/reference context bundles, `job-forge cache:*` reuses fetched JD/artifact content, `job-forge index:*` queries compact source pointers, `job-forge facts:*` materializes source-backed job/application/candidate facts, `job-forge timeline:*` computes due/overdue follow-up actions, `job-forge prioritize:*` ranks local apply/follow-up candidates, `job-forge lineage:*` detects stale reports/PDFs after source changes, `job-forge preflight:*` plans bounded apply dispatch rounds from file-backed candidate facts, `job-forge postflight:*` settles dispatch outcomes/artifacts/post-steps, `job-forge redact:*` sanitizes local exports, and `job-forge migrate:*` applies safe consumer-project upgrades without MCP/tool-schema overhead. |
82
82
  | **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. |
83
83
 
84
84
  ## Usage
package/batch/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Batch evaluation
2
2
 
3
- The `batch/` folder holds the **parallel batch runner** for processing 10+ job URLs with headless `opencode run` workers. For how batch fits into the rest of JobForge, see [docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md).
3
+ The `batch/` folder holds the **parallel batch runner** for processing 10+ job URLs with headless AI CLI workers (`opencode run` or `codex exec`). For how batch fits into the rest of JobForge, see [docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md).
4
4
 
5
5
  ## What ships in git
6
6
 
@@ -16,10 +16,11 @@ Per [`.gitignore`](../.gitignore): `batch-input.tsv`, `batch-state.tsv`, `logs/*
16
16
 
17
17
  The default runner uses `@razroo/iso-orchestrator` through
18
18
  `scripts/batch-orchestrator.mjs`. It persists bundle steps and events in
19
- `.jobforge-runs/`, caps worker fan-out with `workflow.forEach`, and serializes
20
- state/report-number writes while parallel bundles run. Use
21
- `JOBFORGE_LEGACY_BATCH_RUNNER=1 ./batch/batch-runner.sh` only to fall back to
22
- the old shell loop.
19
+ `.jobforge-runs/`, caps worker fan-out with `workflow.forEach`, serializes
20
+ state/report-number writes while parallel bundles run, and records worker
21
+ leases/heartbeats for liveness inspection. Use `./batch/batch-runner.sh --runner codex`
22
+ to switch the worker CLI, or `JOBFORGE_LEGACY_BATCH_RUNNER=1 ./batch/batch-runner.sh`
23
+ only to fall back to the old shell loop.
23
24
 
24
25
  ## Input: `batch-input.tsv`
25
26
 
@@ -64,4 +65,4 @@ After a successful merge, each processed file is moved to **`batch/tracker-addit
64
65
 
65
66
  ## Prerequisites
66
67
 
67
- The runner expects the `opencode` CLI on `PATH` and a valid `batch-prompt.md`. It creates `reports/` and tracker paths when they do not exist; ensure your usual JobForge setup (`cv.md`, `config/profile.yml`, `portals.yml`) matches what `batch-prompt.md` assumes.
68
+ The runner expects the selected worker CLI (`opencode` by default, `codex` when `--runner codex` is used) on `PATH` and a valid `batch-prompt.md`. It creates `reports/` and tracker paths when they do not exist; ensure your usual JobForge setup (`cv.md`, `config/profile.yml`, `portals.yml`) matches what `batch-prompt.md` assumes.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- # job-forge batch runner — standalone orchestrator for opencode run workers
5
- # Reads batch-input.tsv, delegates each offer to an opencode run worker,
4
+ # job-forge batch runner — standalone orchestrator for headless AI CLI workers
5
+ # Reads batch-input.tsv, delegates each offer to a worker CLI invocation,
6
6
  # tracks state in batch-state.tsv for resumability.
7
7
 
8
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -202,7 +202,7 @@ const consumerPkg = {
202
202
  // One command to pull the latest harness and any locally-pinned MCP
203
203
  // packages. npm update is a no-op on packages not in package.json, so
204
204
  // listing @razroo/gmail-mcp + @geometra/mcp is safe for consumers that
205
- // invoke them via `npx -y` without pinning.
205
+ // invoke them via npx without local package pins.
206
206
  '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)"',
207
207
  },
208
208
  dependencies: {
@@ -224,12 +224,15 @@ const opencodeCfg = {
224
224
  // Files listed here load into every session's cached prefix, so they're
225
225
  // cached once (on Anthropic) instead of Read-as-tool-call on every session.
226
226
  // AGENTS.harness.md → symlink to node_modules/job-forge/AGENTS.md (harness rules)
227
+ // .opencode/instructions.md → symlink to node_modules/job-forge/.opencode/instructions.md
228
+ // (OpenCode-only harness addendum)
227
229
  // modes/_shared.md → symlink into node_modules; canonical scoring model
228
230
  // cv.md → candidate's CV (personal, created during onboarding)
229
231
  // templates/states.yml → canonical application states (validated by merge-tracker.mjs)
230
232
  // Ordering matters for cache prefix stability: put most-stable files first.
231
233
  instructions: [
232
234
  'AGENTS.harness.md',
235
+ '.opencode/instructions.md',
233
236
  'templates/states.yml',
234
237
  'modes/_shared.md',
235
238
  'cv.md',
@@ -237,7 +240,7 @@ const opencodeCfg = {
237
240
  mcp: {
238
241
  geometra: {
239
242
  type: 'local',
240
- command: ['npx', '-y', '@geometra/mcp'],
243
+ command: ['npx', '-y', '@geometra/mcp@1.61.3'],
241
244
  enabled: true,
242
245
  },
243
246
  gmail: {
@@ -285,7 +288,7 @@ write('AGENTS.md', `# AGENTS — ${name}
285
288
 
286
289
  Personal job search project using the [job-forge](https://github.com/razroo/JobForge) harness. The harness lives in \`node_modules/job-forge/\`; most files you need are accessible through symlinks at the project root.
287
290
 
288
- **How context loads in this project:** opencode auto-loads *this* file as the project-root AGENTS.md, and also loads \`AGENTS.harness.md\` via \`opencode.json:instructions\` — that second file is a symlink to \`node_modules/job-forge/AGENTS.md\` and carries the shared operational rules (Session Hygiene, OTP handling, batch best practices, scoring). Keep *this* file for personal overrides — anything you want to diverge from or add on top.
291
+ **How context loads in this project:** opencode auto-loads *this* file as the project-root AGENTS.md, then loads \`AGENTS.harness.md\` plus \`.opencode/instructions.md\` via \`opencode.json:instructions\`. \`AGENTS.harness.md\` is the shared cross-harness contract; \`.opencode/instructions.md\` is reserved for OpenCode-only addenda. Keep *this* file for personal overrides — anything you want to diverge from or add on top.
289
292
 
290
293
  ---
291
294
 
package/bin/sync.mjs CHANGED
@@ -20,7 +20,7 @@
20
20
  * whether the cwd contains the harness's own package.json with name=job-forge).
21
21
  */
22
22
 
23
- import { existsSync, lstatSync, readlinkSync, symlinkSync, mkdirSync, readFileSync } from 'fs';
23
+ import { existsSync, lstatSync, readlinkSync, symlinkSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
24
24
  import { dirname, join, resolve, relative } from 'path';
25
25
  import { fileURLToPath } from 'url';
26
26
  import {
@@ -80,6 +80,7 @@ const links = [
80
80
 
81
81
  // OpenCode: skill router + subagent definitions. Users can override any
82
82
  // single subagent by replacing its symlink with a local file.
83
+ { src: '.opencode/instructions.md', dst: '.opencode/instructions.md' },
83
84
  { src: '.opencode/skills/job-forge.md', dst: '.opencode/skills/job-forge.md' },
84
85
  { src: '.opencode/agents', dst: '.opencode/agents' },
85
86
 
@@ -154,6 +155,15 @@ for (const { src, dst } of links) {
154
155
  }
155
156
  }
156
157
 
158
+ try {
159
+ if (ensureOpencodeInstructionRef()) {
160
+ created++;
161
+ }
162
+ } catch (error) {
163
+ console.warn(` warn: failed to patch opencode.json instructions: ${error instanceof Error ? error.message : String(error)}`);
164
+ warned++;
165
+ }
166
+
157
167
  try {
158
168
  const migrationResult = applyConsumerMigrations();
159
169
  if (migrationResult?.changeCount > 0) {
@@ -166,6 +176,30 @@ try {
166
176
 
167
177
  console.log(`\njob-forge sync: ${created} created, ${skipped} up-to-date, ${warned} warnings (project: ${PROJECT_DIR})`);
168
178
 
179
+ function ensureOpencodeInstructionRef() {
180
+ const configPath = join(PROJECT_DIR, 'opencode.json');
181
+ if (!existsSync(configPath)) return false;
182
+
183
+ const raw = readFileSync(configPath, 'utf8');
184
+ const config = JSON.parse(raw);
185
+ const current = Array.isArray(config.instructions)
186
+ ? config.instructions.slice()
187
+ : config.instructions
188
+ ? [config.instructions]
189
+ : [];
190
+ const required = '.opencode/instructions.md';
191
+ if (current.includes(required)) return false;
192
+
193
+ const next = current.slice();
194
+ const anchor = next.indexOf('AGENTS.harness.md');
195
+ if (anchor === -1) next.unshift(required);
196
+ else next.splice(anchor + 1, 0, required);
197
+ config.instructions = [...new Set(next)];
198
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
199
+ console.log(` updated: opencode.json instructions += ${required}`);
200
+ return true;
201
+ }
202
+
169
203
  function applyConsumerMigrations() {
170
204
  const skip = process.env.JOB_FORGE_SKIP_MIGRATIONS;
171
205
  if (skip === '1' || skip === 'true') return null;
@@ -87,19 +87,22 @@ location_constraints:
87
87
  requires_visa_sponsorship: false # true → roles in non-authorized countries are blocked unless
88
88
  # the JD explicitly mentions visa sponsorship
89
89
 
90
- # Optional outbound proxy for the Chromium that Geometra MCP spawns.
90
+ # Optional outbound proxy for the stealth Chromium that Geometra MCP spawns.
91
91
  # Uncomment and fill in to route ALL browser traffic through a residential /
92
92
  # mobile / SOCKS proxy you already pay for. Bypasses the datacenter-IP
93
93
  # fingerprinting that drives ~80-90% of Ashby / Lever / Cloudflare-fronted
94
- # "flagged as possible spam" submit failures in headless mode.
94
+ # "flagged as possible spam" submit failures in headless mode. JobForge passes
95
+ # `stealth: true` by default so Geometra MCP >= 1.61.3 launches CloakBrowser's
96
+ # patched Chromium for portal sessions.
95
97
  #
96
98
  # BYO — JobForge does NOT bundle or resell proxy bandwidth. Pick a residential
97
99
  # or mobile provider (Bright Data, Oxylabs, SOAX, Smartproxy, etc.), or a
98
- # mobile hotspot, or your own SOCKS relay. Required: Geometra MCP >= 1.59.0.
100
+ # mobile hotspot, or your own SOCKS relay. Required: Geometra MCP >= 1.61.3.
99
101
  #
100
102
  # When present, the apply / scan / auto-pipeline modes thread this into every
101
- # `geometra_connect` call as `proxy: {...}`. Pool is partitioned by proxy
102
- # identity so direct and proxied sessions never share a Chromium.
103
+ # `geometra_connect` call as `proxy: {...}` alongside `stealth: true`. Pool is
104
+ # partitioned by proxy identity and stealth mode so direct and proxied sessions
105
+ # never share a Chromium.
103
106
  #
104
107
  # proxy:
105
108
  # server: "http://residential.example.com:8080" # http://, https://, or socks5://
@@ -134,14 +134,14 @@ For customization (archetypes, weights, tone), start with `_shared.md` and [CUST
134
134
  The batch system processes multiple offers in parallel:
135
135
 
136
136
  ```
137
- batch-input.tsv -> batch-runner.sh -> N x opencode run workers
138
- (id, url, source, notes) (iso-orchestrator) (self-contained prompt)
137
+ batch-input.tsv -> batch-runner.sh -> N x AI CLI workers
138
+ (id, url, source, notes) (iso-orchestrator) (opencode run / codex exec)
139
139
  |
140
140
  batch-state.tsv + .jobforge-runs/
141
141
  (progress + durable workflow record)
142
142
  ```
143
143
 
144
- Each worker is a headless opencode instance (`opencode run`) that receives the full `batch-prompt.md` as context. Workers produce:
144
+ Each worker is a headless CLI agent (`opencode run` or `codex exec`) that receives the full `batch-prompt.md` context. Workers produce:
145
145
  - Report .md
146
146
  - PDF
147
147
  - Tracker TSV line
@@ -149,8 +149,9 @@ Each worker is a headless opencode instance (`opencode run`) that receives the f
149
149
  The orchestrator manages parallelism, state, retries, and resume. The default
150
150
  runner delegates to `scripts/batch-orchestrator.mjs`, which uses
151
151
  `@razroo/iso-orchestrator` for bounded bundle fan-out, idempotent bundle steps,
152
- and mutexed report-number/state writes. Set `JOBFORGE_LEGACY_BATCH_RUNNER=1`
153
- only if you need the old shell loop.
152
+ mutexed report-number/state writes, and worker leases/heartbeats. Pass
153
+ `--runner codex` to use Codex workers instead of OpenCode. Set
154
+ `JOBFORGE_LEGACY_BATCH_RUNNER=1` only if you need the old shell loop.
154
155
 
155
156
  **Local batch artifacts:** `batch/batch-input.tsv`, `batch/batch-state.tsv`, `batch/logs/`, `batch/tracker-additions/*.tsv`, and `.jobforge-runs/` are created when you run the runner; they are gitignored (with `.gitkeep` in `batch/logs/` and `batch/tracker-additions/`). A fresh clone ships `batch/batch-runner.sh` and `batch/batch-prompt.md` only until you add an input file — see [`batch/README.md`](../batch/README.md) and `batch/batch-runner.sh --help` for the TSV layout and workflow.
156
157
 
@@ -247,9 +248,9 @@ Scripts maintain data consistency. In a consumer project they're invoked via the
247
248
  | `generate-pdf.mjs` | `npx job-forge pdf` | Renders HTML to PDF via Geometra MCP (`geometra_generate_pdf`) or standalone Playwright/Chromium (`npx job-forge pdf <input.html> <output.pdf>`) |
248
249
  | `cv-sync-check.mjs` | `npx job-forge sync-check` | Setup lint: `cv.md` + `config/profile.yml`, hardcoded-metric scan on `modes/_shared.md` and `batch/batch-prompt.md`, optional `article-digest.md` freshness |
249
250
  | `scripts/token-usage-report.mjs` | `npx job-forge tokens` | Per-session opencode token/cost report from the SQLite DB |
250
- | `scripts/trace.mjs` | `npx job-forge trace:list` / `trace:stats` / `trace:show` | Local transcript observability via `@razroo/iso-trace`; common commands default to OpenCode sessions for the consumer project |
251
- | `scripts/telemetry.mjs` | `npx job-forge telemetry:status` / `telemetry:show` | JobForge operational telemetry derived from OpenCode traces plus tracker TSV state |
252
- | `scripts/guard.mjs` | `npx job-forge guard:audit` / `guard:explain` | Deterministic `@razroo/iso-guard` policy audits over local OpenCode traces |
251
+ | `scripts/trace.mjs` | `npx job-forge trace:list` / `trace:stats` / `trace:show` | Local transcript observability via `@razroo/iso-trace`; common commands default to project-local sessions across supported harnesses |
252
+ | `scripts/telemetry.mjs` | `npx job-forge telemetry:status` / `telemetry:show` | JobForge operational telemetry derived from normalized local traces plus tracker TSV state |
253
+ | `scripts/guard.mjs` | `npx job-forge guard:audit` / `guard:explain` | Deterministic `@razroo/iso-guard` policy audits over local normalized traces (with OpenCode `task` rules still available where relevant) |
253
254
  | `scripts/ledger.mjs` | `npx job-forge ledger:status` / `ledger:has` / `ledger:rebuild` | Deterministic `@razroo/iso-ledger` state over tracker, TSV, and pipeline files |
254
255
  | `scripts/capabilities.mjs` | `npx job-forge capabilities:check` / `capabilities:explain` | Deterministic `@razroo/iso-capabilities` role boundary checks for tools, MCPs, commands, filesystem, and network access |
255
256
  | `scripts/cache.mjs` | `npx job-forge cache:has` / `cache:get` / `cache:put` | Deterministic `@razroo/iso-cache` JD and artifact reuse keyed by stable job/url inputs |
@@ -100,7 +100,7 @@ Some forks use a **single** `data/applications.md` instead. That is fine if you
100
100
 
101
101
  To inspect real agent sessions locally (tool mix, redundant fetches, Geometra churn) without uploading transcripts, use the `job-forge trace:*` commands. JobForge depends on Razroo's [`@razroo/iso-trace`](https://github.com/razroo/iso/tree/main/packages/iso-trace), so consumer projects do not need to install it separately.
102
102
 
103
- Common commands default to OpenCode sessions for the current project and use a 7-day window:
103
+ Common commands default to sessions for the current project and use a 7-day window:
104
104
 
105
105
  ```bash
106
106
  npx job-forge trace:list
@@ -114,7 +114,7 @@ For raw iso-trace commands, use `npx job-forge trace sources`, `npx job-forge tr
114
114
 
115
115
  ## JobForge telemetry
116
116
 
117
- Trace is the raw transcript view. Telemetry is the JobForge operational view: it summarizes task dispatches, child session outcomes, provider errors, policy issues, and pending tracker TSVs.
117
+ Trace is the raw transcript view. Telemetry is the JobForge operational view: it summarizes dispatches, child session outcomes, provider errors, policy issues, and pending tracker TSVs.
118
118
 
119
119
  ```bash
120
120
  npx job-forge telemetry:list
@@ -123,7 +123,7 @@ npx job-forge telemetry:show <session-id-or-prefix>
123
123
  npx job-forge telemetry:watch
124
124
  ```
125
125
 
126
- Telemetry is also local-only and passive. It reads OpenCode's SQLite DB and files under `batch/tracker-additions/`; agents do not need to remember to emit custom events.
126
+ Telemetry is also local-only and passive. It reads normalized local harness traces plus files under `batch/tracker-additions/`; agents do not need to remember to emit custom events.
127
127
 
128
128
  ## JobForge ledger
129
129
 
@@ -196,7 +196,7 @@ Consumer-project migrations live in `templates/migrations.json` and are applied
196
196
 
197
197
  ## JobForge guard audits
198
198
 
199
- Guard audits run deterministic `@razroo/iso-guard` policies over the same local OpenCode traces. The default policy lives at `templates/guards/jobforge-baseline.yaml` and checks rules that are reliable from transcript data, including max two task dispatches per assistant message, no task-status polling via `task`, no raw proxy configuration in task prompts, and no child session task recursion.
199
+ Guard audits run deterministic `@razroo/iso-guard` policies over the same local traces. The default policy lives at `templates/guards/jobforge-baseline.yaml` and checks rules that are reliable from transcript data, including max two OpenCode `task` dispatches per assistant message, no task-status polling via `task`, no raw proxy configuration in task prompts, and no child session task recursion.
200
200
 
201
201
  ```bash
202
202
  npx job-forge guard:audit
@@ -204,13 +204,13 @@ npx job-forge guard:audit <session-id-or-prefix>
204
204
  npx job-forge guard:explain
205
205
  ```
206
206
 
207
- Use `--policy <path>` to audit with a custom policy. This does not add prompt, token, or MCP overhead; JobForge converts local trace rows into guard events inside the CLI process.
207
+ Use `--policy <path>` to audit with a custom policy. This does not add prompt, token, or MCP overhead; JobForge converts local normalized trace events into guard events inside the CLI process.
208
208
 
209
209
  **Where Claude Code writes JSONL:** `~/.claude/projects/<encoded-cwd>/*.jsonl`.
210
210
 
211
211
  **Direct CLI fallback:** `npx -y @razroo/iso-trace@latest stats --source "$HOME/.claude/projects/<encoded-dir>/<session>.jsonl"`
212
212
 
213
- **Performance:** `iso-trace list --cwd /path/to/repo` walks all of `~/.claude/projects` before filtering; on large machines prefer `stats --source <one.jsonl>` or the library's `discoverSessions({ roots: ["<one encoded project dir>"] })` (see the iso-trace README).
213
+ **Performance:** JobForge narrows discovery to project-local roots where possible, but raw `iso-trace list --cwd /path/to/repo` may still walk large transcript trees on busy machines. Prefer `stats --source <one.jsonl>` or project-scoped roots when you need direct `iso-trace` usage.
214
214
 
215
215
  ## States (templates/states.yml)
216
216
 
package/docs/SETUP.md CHANGED
@@ -214,7 +214,7 @@ Use it to identify which sessions or models are consuming the most tokens. The `
214
214
  `sync-check` requires `cv.md` and `config/profile.yml` with the fields checked in `cv-sync-check.mjs`. Until you finish the profile and CV steps, that is normal.
215
215
 
216
216
  **PDF generation fails**
217
- The scaffolded `opencode.json` already registers Geometra MCP; if it's not running, check `opencode mcp list` and verify the scaffolded config under the `mcp.geometra` key — its `command` MUST be `["npx", "-y", "@geometra/mcp"]` and `enabled: true`. Geometra manages Chromium via its built-in proxy. For standalone CLI usage (outside opencode), `generate-pdf.mjs` also works with standalone Playwright/Chromium — install with `npx playwright install chromium`.
217
+ The scaffolded `opencode.json` already registers Geometra MCP; if it's not running, check `opencode mcp list` and verify the scaffolded config under the `mcp.geometra` key — its `command` MUST be `["npx", "-y", "@geometra/mcp@1.61.3"]` and `enabled: true`. Geometra manages Chromium via its built-in proxy. JobForge passes `stealth: true` for portal sessions so Geometra launches CloakBrowser's patched Chromium. For standalone CLI usage (outside opencode), `generate-pdf.mjs` also works with standalone Playwright/Chromium — install with `npx playwright install chromium`.
218
218
 
219
219
  **Symlinks are missing or pointing to a stale path**
220
220
  Run `npx job-forge sync` (or `npm run sync`) to recreate them. This happens if you move the project directory after installing, or if `postinstall` didn't run (rare — check `npm install` output for errors).
@@ -39,7 +39,8 @@ Call 3: geometra_connect({
39
39
  pageUrl: "<the URL from the orchestrator's task>",
40
40
  isolated: true,
41
41
  headless: true,
42
- slowMo: 350
42
+ slowMo: 350,
43
+ stealth: true
43
44
  })
44
45
  ```
45
46
 
@@ -47,7 +48,7 @@ Call 3: geometra_connect({
47
48
 
48
49
  1. **Always run Call 1 and Call 2.** Do not skip Call 2 even if Call 1 returns an empty session list. `geometra_disconnect({ closeBrowser: true })` is a safe no-op on an empty pool.
49
50
  2. **Do not reason about Call 1's output.** Don't look at it and decide "the pool looks clean, I'll skip Call 2". Just always call Call 2 next. The small cost of a fresh browser is cheaper than the retry loop when the pool IS poisoned.
50
- 3. **Always use `isolated: true, headless: true, slowMo: 350`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
51
+ 3. **Always use `isolated: true, headless: true, slowMo: 350, stealth: true`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
51
52
  4. **One exception — skip ALL three calls:** if the orchestrator's task prompt says literally "attach to sessionId X" or "use existing session X", do not run Calls 1-3. Go straight to `geometra_page_model({ sessionId: "X" })` and proceed.
52
53
 
53
54
  ### Read Why This Exists
@@ -234,7 +234,7 @@ Step 5 — Loop in rounds of 2 (Hard Limit #1)
234
234
  pair = candidates[round*2 : round*2 + 2]
235
235
  # If proxy is configured, do not paste proxy values into prompts.
236
236
  # Say: "Proxy is configured; read config/profile.yml and pass its
237
- # top-level proxy object to every geometra_connect call."
237
+ # top-level proxy object plus stealth: true to every geometra_connect call."
238
238
  # Dispatch 1 or 2 task() calls in ONE message (never 3+)
239
239
  task(subagent_type=<tier per AGENTS.md routing>, prompt=<apply prompt for pair[0]>)
240
240
  task(subagent_type=<tier>, prompt=<apply prompt for pair[1]>) # only if pair has 2
@@ -28,8 +28,8 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
28
28
  - [H7] Load-bearing facts passed to downstream subagents must originate from a file, not from prior subagent prose. Authoritative sources: `data/pipeline.md`, `data/scan-history.tsv`, `batch/scan-output-*.md`, `reports/{num}-*.md` with `**URL:**` / `**Score:**` headers, emitted score JSON validated by `npx job-forge score:check --input ...`, `batch/tracker-additions/*.tsv`, cached JD content returned by `npx job-forge cache:get --url ...`, source path/line pointers returned by `npx job-forge index:query ...`, materialized fact records returned by `npx job-forge facts:query ...`, selected next actions returned by `npx job-forge prioritize:select ...`, and lineage records returned by `npx job-forge lineage:explain ...`.
29
29
  why: 2026-04-18 scan subagent returned 30 fabricated Greenhouse IDs in prose (plausible-looking, non-existent); orchestrator dispatched 30 downstream subagents that all 404'd. Subagents can hallucinate IDs, scores, and confirmation text — round-trip through a file or don't trust the value
30
30
 
31
- - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
- why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions
31
+ - [H8] Never paste proxy values from `config/profile.yml` into `task` prompts, status text, or summaries. If a proxy is configured, tell the subagent exactly: "Proxy is configured; read `config/profile.yml` and pass its top-level `proxy:` object plus `stealth: true` to every `geometra_connect` call." Do not transcribe `server`, `username`, `password`, or `bypass`, even if you just read them from disk.
32
+ why: a 2026-04-25 OpenCode trace showed raw proxy credentials copied into an apply subagent prompt; trace logs are local, but prompts must still avoid replicating secrets across subagent sessions. Geometra MCP >=1.61.3 can launch CloakBrowser stealth Chromium via `stealth: true`, which belongs with JobForge portal sessions instead of stock Playwright Chromium
33
33
 
34
34
  ## Defaults
35
35
 
@@ -62,7 +62,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
62
62
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
63
63
  2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
64
64
  3. Read the active mode file [D3]. Use local helpers when they can replace broad file reads, prose math, manual policy checks, or artifact reuse decisions [D8]. Decide inline vs delegated work [D1].
65
- 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy prompt hygiene [H8].
65
+ 4. Prepare Geometra dispatches: cleanup [H3], local-helper prefilters when useful [D8], dedupe [H2], location filter [D5], file-backed preflight plan/check [D8], routing [D2], proxy/stealth prompt hygiene [H8].
66
66
  5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b], then settle the round with postflight status [D8].
67
67
  6. Keep multi-job form-filling out of the orchestrator [H4].
68
68
  7. Cross-check subagent facts against authoritative files [H7].
@@ -0,0 +1,8 @@
1
+ ## OpenCode Addendum
2
+
3
+ These notes apply only inside OpenCode sessions.
4
+
5
+ - The OpenCode subagent dispatch primitive is `task`.
6
+ - A `task` result that only returns a session id/title is a launch acknowledgement, not a completed application outcome.
7
+ - Do not use `task` to poll status. Inspect tracker files, workflow artifacts, or `iso-trace` instead.
8
+ - When JobForge says "dispatch" or "subagent" in the shared contract, map that to OpenCode `task` calls here.
package/iso/mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "servers": {
3
3
  "geometra": {
4
4
  "command": "npx",
5
- "args": ["-y", "@geometra/mcp"]
5
+ "args": ["-y", "@geometra/mcp@1.61.3"]
6
6
  },
7
7
  "gmail": {
8
8
  "command": "npx",