job-forge 2.14.13 → 2.14.15

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.
@@ -25,6 +25,5 @@ args = ["-y", "@razroo/gmail-mcp"]
25
25
  env = { DISABLE_HTTP = "true" }
26
26
 
27
27
  [mcp_servers.state-trace]
28
- command = "uvx"
29
- args = ["--from", "state-trace[mcp]", "state-trace-mcp"]
28
+ command = "state-trace-mcp"
30
29
  env = { STATE_TRACE_STORAGE_PATH = ".state-trace/memory.db", STATE_TRACE_NAMESPACE = "job-forge", STATE_TRACE_CAPACITY_LIMIT = "256" }
package/.cursor/mcp.json CHANGED
@@ -18,12 +18,7 @@
18
18
  }
19
19
  },
20
20
  "state-trace": {
21
- "command": "uvx",
22
- "args": [
23
- "--from",
24
- "state-trace[mcp]",
25
- "state-trace-mcp"
26
- ],
21
+ "command": "state-trace-mcp",
27
22
  "env": {
28
23
  "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
29
24
  "STATE_TRACE_NAMESPACE": "job-forge",
@@ -56,6 +56,9 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
56
56
  - [D6] Pick the mode from the **Routing** table below AND name it explicitly in your first response (e.g., "running auto-pipeline mode", "this is a `compare` request"). If no row matches the user's intent, ask which mode fits; do not guess.
57
57
  why: silent mode picks mis-route work (a "negotiation" question answered in `offer` mode produces the wrong report shape); naming the mode out loud makes the routing decision reviewable and gives downstream dispatches a reliable anchor
58
58
 
59
+ - [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@razroo/iso-orchestrator`, persists workflow records in `.jobforge-runs/`, caps bundle fan-out, and mutexes state/report-number writes. Use `JOBFORGE_LEGACY_BATCH_RUNNER=1` only as a fallback.
60
+ why: the old Bash loop encoded resumability and parallelism manually; the iso-orchestrator path makes the durable control state inspectable and prevents report-number collisions under parallel bundles
61
+
59
62
  ## Procedure
60
63
 
61
64
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
package/.mcp.json CHANGED
@@ -18,12 +18,7 @@
18
18
  }
19
19
  },
20
20
  "state-trace": {
21
- "command": "uvx",
22
- "args": [
23
- "--from",
24
- "state-trace[mcp]",
25
- "state-trace-mcp"
26
- ],
21
+ "command": "state-trace-mcp",
27
22
  "env": {
28
23
  "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
29
24
  "STATE_TRACE_NAMESPACE": "job-forge",
package/AGENTS.md CHANGED
@@ -51,6 +51,9 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
51
51
  - [D6] Pick the mode from the **Routing** table below AND name it explicitly in your first response (e.g., "running auto-pipeline mode", "this is a `compare` request"). If no row matches the user's intent, ask which mode fits; do not guess.
52
52
  why: silent mode picks mis-route work (a "negotiation" question answered in `offer` mode produces the wrong report shape); naming the mode out loud makes the routing decision reviewable and gives downstream dispatches a reliable anchor
53
53
 
54
+ - [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@razroo/iso-orchestrator`, persists workflow records in `.jobforge-runs/`, caps bundle fan-out, and mutexes state/report-number writes. Use `JOBFORGE_LEGACY_BATCH_RUNNER=1` only as a fallback.
55
+ why: the old Bash loop encoded resumability and parallelism manually; the iso-orchestrator path makes the durable control state inspectable and prevents report-number collisions under parallel bundles
56
+
54
57
  ## Procedure
55
58
 
56
59
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
package/CLAUDE.md CHANGED
@@ -51,6 +51,9 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
51
51
  - [D6] Pick the mode from the **Routing** table below AND name it explicitly in your first response (e.g., "running auto-pipeline mode", "this is a `compare` request"). If no row matches the user's intent, ask which mode fits; do not guess.
52
52
  why: silent mode picks mis-route work (a "negotiation" question answered in `offer` mode produces the wrong report shape); naming the mode out loud makes the routing decision reviewable and gives downstream dispatches a reliable anchor
53
53
 
54
+ - [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@razroo/iso-orchestrator`, persists workflow records in `.jobforge-runs/`, caps bundle fan-out, and mutexes state/report-number writes. Use `JOBFORGE_LEGACY_BATCH_RUNNER=1` only as a fallback.
55
+ why: the old Bash loop encoded resumability and parallelism manually; the iso-orchestrator path makes the durable control state inspectable and prevents report-number collisions under parallel bundles
56
+
54
57
  ## Procedure
55
58
 
56
59
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
package/README.md CHANGED
@@ -29,7 +29,7 @@ The scaffolded `opencode.json` already has three MCPs wired up — they launch a
29
29
 
30
30
  - **Geometra** — browser automation + PDF generation
31
31
  - **Gmail** — reads replies from recruiters
32
- - **state-trace** — typed working memory for cross-session context (resumed batches, recent decisions, repeated portal quirks). Spawned via `uvx`; install once with `brew install uv` (or `pipx install uv`) no other setup.
32
+ - **state-trace** — typed working memory for cross-session context (resumed batches, recent decisions, repeated portal quirks). Install once with `python3 -m pip install "state-trace[mcp]"`; the MCP command is `state-trace-mcp`.
33
33
 
34
34
  `npm install` also materializes symlinks for every supported agent harness — OpenCode, Cursor, Claude Code, and Codex — so you can run `opencode`, `cursor`, `claude`, or `codex` in the same project and each picks up the shared MCP config and instructions.
35
35
 
@@ -73,9 +73,10 @@ JobForge turns opencode into a full job search command center. Instead of manual
73
73
  | **Smart LinkedIn Outreach** | Reads evaluation reports to craft targeted messages using top proof points |
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
+ | **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/`. |
76
77
  | **Pipeline Integrity** | Automated merge, dedup, status normalization, health checks |
77
78
  | **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
+ | **Trace + Telemetry + Guard** | `job-forge trace:*` exposes local OpenCode transcripts, `job-forge telemetry:*` summarizes runs, and `job-forge guard:*` audits deterministic JobForge policy rules with `@razroo/iso-guard`. |
79
80
  | **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
81
 
81
82
  ## Usage
@@ -144,6 +145,7 @@ my-search/
144
145
  ├── data/ # applications, pipeline, scan history (personal, gitignored)
145
146
  ├── reports/ # generated evaluation reports (personal, gitignored)
146
147
  ├── batch/{batch-input,batch-state}.tsv, tracker-additions/, logs/ # personal
148
+ ├── .jobforge-runs/ # durable batch workflow records (generated)
147
149
  ├── AGENTS.md # personal overrides (opencode + codex)
148
150
  ├── CLAUDE.md # personal overrides (Claude Code), @-imports CLAUDE.harness.md
149
151
 
@@ -187,6 +189,7 @@ JobForge/
187
189
  ├── config/profile.example.yml # template for consumer's profile.yml
188
190
  ├── batch/{batch-prompt.md,batch-runner.sh} # batch orchestrator
189
191
  ├── scripts/
192
+ │ ├── batch-orchestrator.mjs # iso-orchestrator-backed batch control loop
190
193
  │ ├── token-usage-report.mjs # opencode cost analyzer
191
194
  │ └── release/check-source.mjs # version gate for npm publish
192
195
  ├── tracker-lib.mjs / merge-tracker.mjs / dedup-tracker.mjs / verify-pipeline.mjs
package/batch/README.md CHANGED
@@ -6,13 +6,20 @@ The `batch/` folder holds the **parallel batch runner** for processing 10+ job U
6
6
 
7
7
  | Path | Role |
8
8
  |------|------|
9
- | `batch-runner.sh` | Orchestrator: parallelism, state, retries, resume |
9
+ | `batch-runner.sh` | Compatibility entrypoint; delegates to the durable Node orchestrator by default |
10
10
  | `batch-prompt.md` | Prompt template passed to each worker (keep evaluation and scoring instructions aligned with the canonical model in [`modes/_shared.md`](../modes/_shared.md) so batch scores match single-offer runs) |
11
11
  | `README.md` | This file |
12
12
 
13
13
  ## Local-only files (gitignored when present)
14
14
 
15
- Per [`.gitignore`](../.gitignore): `batch-input.tsv`, `batch-state.tsv`, `logs/*`, and `tracker-additions/*.tsv`. Empty dirs (`logs/`, `tracker-additions/`) use `.gitkeep` so the tree exists in a fresh clone.
15
+ Per [`.gitignore`](../.gitignore): `batch-input.tsv`, `batch-state.tsv`, `logs/*`, `tracker-additions/*.tsv`, and `.jobforge-runs/`. Empty dirs (`logs/`, `tracker-additions/`) use `.gitkeep` so the tree exists in a fresh clone.
16
+
17
+ The default runner uses `@razroo/iso-orchestrator` through
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.
16
23
 
17
24
  ## Input: `batch-input.tsv`
18
25
 
@@ -6,8 +6,24 @@ set -euo pipefail
6
6
  # tracks state in batch-state.tsv for resumability.
7
7
 
8
8
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
- PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
10
- BATCH_DIR="$SCRIPT_DIR"
9
+ PROJECT_DIR="${JOB_FORGE_PROJECT:-$(cd "$SCRIPT_DIR/.." && pwd)}"
10
+
11
+ # Default path: delegate to the durable Node orchestrator. Keep the legacy
12
+ # shell implementation below as an escape hatch while the new runner settles.
13
+ SOURCE="${BASH_SOURCE[0]}"
14
+ while [[ -L "$SOURCE" ]]; do
15
+ SOURCE_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
16
+ SOURCE="$(readlink "$SOURCE")"
17
+ [[ "$SOURCE" != /* ]] && SOURCE="$SOURCE_DIR/$SOURCE"
18
+ done
19
+ HARNESS_BATCH_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
20
+ HARNESS_DIR="$(cd "$HARNESS_BATCH_DIR/.." && pwd)"
21
+ if [[ "${JOBFORGE_LEGACY_BATCH_RUNNER:-}" != "1" && -f "$HARNESS_DIR/scripts/batch-orchestrator.mjs" ]]; then
22
+ export JOB_FORGE_PROJECT="$PROJECT_DIR"
23
+ exec node "$HARNESS_DIR/scripts/batch-orchestrator.mjs" "$@"
24
+ fi
25
+
26
+ BATCH_DIR="$PROJECT_DIR/batch"
11
27
  INPUT_FILE="$BATCH_DIR/batch-input.tsv"
12
28
  STATE_FILE="$BATCH_DIR/batch-state.tsv"
13
29
  PROMPT_FILE="$BATCH_DIR/batch-prompt.md"
@@ -117,6 +117,8 @@ 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
+ 'guard:audit': 'job-forge guard:audit',
121
+ 'guard:explain': 'job-forge guard:explain',
120
122
  // One command to pull the latest harness and any locally-pinned MCP
121
123
  // packages. npm update is a no-op on packages not in package.json, so
122
124
  // listing @razroo/gmail-mcp + @geometra/mcp is safe for consumers that
package/bin/job-forge.mjs CHANGED
@@ -19,6 +19,7 @@
19
19
  * tokens Run scripts/token-usage-report.mjs
20
20
  * trace:* Inspect local agent transcripts via iso-trace
21
21
  * telemetry:* Summarize JobForge pipeline status from traces + tracker files
22
+ * guard:* Audit JobForge trace policy with iso-guard
22
23
  * sync Re-run the harness symlink sync (bin/sync.mjs)
23
24
  * help, --help Show this message
24
25
  */
@@ -68,6 +69,11 @@ const telemetryAliases = {
68
69
  'telemetry:watch': 'watch',
69
70
  };
70
71
 
72
+ const guardAliases = {
73
+ 'guard:audit': 'audit',
74
+ 'guard:explain': 'explain',
75
+ };
76
+
71
77
  const [, , cmd, ...rest] = process.argv;
72
78
 
73
79
  function printHelp() {
@@ -92,6 +98,8 @@ Commands:
92
98
  telemetry:status Show latest JobForge run + pending tracker state
93
99
  telemetry:show ID Show one run with child sessions, provider errors, next actions
94
100
  telemetry:watch Watch latest run status
101
+ guard:audit Audit latest/local trace policy with iso-guard
102
+ guard:explain Show the active iso-guard policy
95
103
  sync Re-create harness symlinks in the current project
96
104
 
97
105
  Deterministic helpers (prefer these over LLM-derived values):
@@ -118,6 +126,8 @@ Pass --help after a command to see its own flags, e.g.:
118
126
  job-forge trace:show ses_...
119
127
  job-forge telemetry:status
120
128
  job-forge telemetry:show ses_...
129
+ job-forge guard:audit
130
+ job-forge guard:explain
121
131
 
122
132
  Project directory resolves to $JOB_FORGE_PROJECT or cwd.`);
123
133
  }
@@ -157,6 +167,21 @@ if (cmd === 'telemetry' || telemetryAliases[cmd]) {
157
167
  process.exit(result.status ?? 1);
158
168
  }
159
169
 
170
+ if (cmd === 'guard' || guardAliases[cmd]) {
171
+ const guardArgs = cmd === 'guard'
172
+ ? (rest.length === 0 ? ['help'] : rest)
173
+ : [guardAliases[cmd], ...rest];
174
+
175
+ const scriptPath = join(PKG_ROOT, 'scripts/guard.mjs');
176
+ const result = spawnSync(process.execPath, [scriptPath, ...guardArgs], {
177
+ stdio: 'inherit',
178
+ cwd: PROJECT_DIR,
179
+ env: process.env,
180
+ });
181
+
182
+ process.exit(result.status ?? 1);
183
+ }
184
+
160
185
  const rel = commands[cmd];
161
186
  if (!rel) {
162
187
  console.error(`Unknown command: ${cmd}\n`);
@@ -131,11 +131,11 @@ For customization (archetypes, weights, tone), start with `_shared.md` and [CUST
131
131
  The batch system processes multiple offers in parallel:
132
132
 
133
133
  ```
134
- batch-input.tsv batch-runner.sh N × opencode run workers
135
- (id, url, source, notes) (orchestrator) (self-contained prompt)
136
-
137
- batch-state.tsv
138
- (tracks progress)
134
+ batch-input.tsv -> batch-runner.sh -> N x opencode run workers
135
+ (id, url, source, notes) (iso-orchestrator) (self-contained prompt)
136
+ |
137
+ batch-state.tsv + .jobforge-runs/
138
+ (progress + durable workflow record)
139
139
  ```
140
140
 
141
141
  Each worker is a headless opencode instance (`opencode run`) that receives the full `batch-prompt.md` as context. Workers produce:
@@ -143,9 +143,13 @@ Each worker is a headless opencode instance (`opencode run`) that receives the f
143
143
  - PDF
144
144
  - Tracker TSV line
145
145
 
146
- The orchestrator manages parallelism, state, retries, and resume.
146
+ The orchestrator manages parallelism, state, retries, and resume. The default
147
+ runner delegates to `scripts/batch-orchestrator.mjs`, which uses
148
+ `@razroo/iso-orchestrator` for bounded bundle fan-out, idempotent bundle steps,
149
+ and mutexed report-number/state writes. Set `JOBFORGE_LEGACY_BATCH_RUNNER=1`
150
+ only if you need the old shell loop.
147
151
 
148
- **Local batch artifacts:** `batch/batch-input.tsv`, `batch/batch-state.tsv`, `batch/logs/`, and `batch/tracker-additions/*.tsv` 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.
152
+ **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.
149
153
 
150
154
  ## Data Flow
151
155
 
@@ -205,6 +209,7 @@ Scripts maintain data consistency. In a consumer project they're invoked via the
205
209
  | `scripts/token-usage-report.mjs` | `npx job-forge tokens` | Per-session opencode token/cost report from the SQLite DB |
206
210
  | `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 |
207
211
  | `scripts/telemetry.mjs` | `npx job-forge telemetry:status` / `telemetry:show` | JobForge operational telemetry derived from OpenCode traces plus tracker TSV state |
212
+ | `scripts/guard.mjs` | `npx job-forge guard:audit` / `guard:explain` | Deterministic `@razroo/iso-guard` policy audits over local OpenCode traces |
208
213
  | `tracker-lib.mjs` | _(library)_ | Shared helpers for reading/writing day-based tracker files — imported by merge/dedup/verify/normalize |
209
214
  | `bin/sync.mjs` | `npx job-forge sync` | Creates the harness symlinks in a consumer project (also runs as `postinstall`) |
210
215
  | `bin/create-job-forge.mjs` | `npx create-job-forge <dir>` | Scaffolds a new personal project |
@@ -125,6 +125,18 @@ npx job-forge telemetry:watch
125
125
 
126
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.
127
127
 
128
+ ## JobForge guard audits
129
+
130
+ 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.
131
+
132
+ ```bash
133
+ npx job-forge guard:audit
134
+ npx job-forge guard:audit <session-id-or-prefix>
135
+ npx job-forge guard:explain
136
+ ```
137
+
138
+ 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.
139
+
128
140
  **Where Claude Code writes JSONL:** `~/.claude/projects/<encoded-cwd>/*.jsonl`.
129
141
 
130
142
  **Direct CLI fallback:** `npx -y @razroo/iso-trace@latest stats --source "$HOME/.claude/projects/<encoded-dir>/<session>.jsonl"`
package/docs/README.md CHANGED
@@ -31,7 +31,7 @@ The harness exposes a single CLI (`job-forge`) installed as a `bin` entry. In a
31
31
 
32
32
  | What you need | Where to read |
33
33
  |---------------|---------------|
34
- | Full command list (`verify`, `merge`, `dedup`, `normalize`, `pdf`, `sync-check`, `tokens`, `sync`). | [SETUP.md — Tracker and scripts (terminal)](SETUP.md#tracker-and-scripts-terminal). |
34
+ | Full command list (`verify`, `merge`, `dedup`, `normalize`, `pdf`, `sync-check`, `tokens`, `trace`, `telemetry`, `guard`, `sync`). | [SETUP.md — Tracker and scripts (terminal)](SETUP.md#tracker-and-scripts-terminal). |
35
35
  | What each harness `.mjs` script does. | [ARCHITECTURE.md — Pipeline integrity](ARCHITECTURE.md#pipeline-integrity) and the scripts table underneath. |
36
36
  | Batch runner, TSV layout, and `batch/tracker-additions/` merge flow. | [batch/README.md](../batch/README.md). |
37
37
  | PR gate for harness contributions (`npm run verify` + `npm run build:dashboard`). | [CONTRIBUTING.md — Development](../CONTRIBUTING.md#development). |
package/docs/SETUP.md CHANGED
@@ -3,7 +3,7 @@
3
3
  ## Prerequisites
4
4
 
5
5
  - [opencode](https://opencode.ai) installed and configured
6
- - Node.js 18+ (for the CLI, PDF generation, and tracker scripts)
6
+ - Node.js 20.6+ (for the CLI, PDF generation, tracker scripts, and durable batch orchestration)
7
7
  - [`uv`](https://docs.astral.sh/uv/) installed (`brew install uv` on macOS, or `pipx install uv`). Used by the state-trace MCP to spawn its Python entry point on demand via `uvx`. Without `uv`, the state-trace MCP fails to start; the rest of JobForge keeps working.
8
8
  - (Optional) Go (for the dashboard TUI) — use a toolchain that satisfies the `go` directive in [`dashboard/go.mod`](../dashboard/go.mod)
9
9
 
@@ -136,6 +136,8 @@ From your project root, these commands maintain the tracker and pipeline checks.
136
136
  | List recent JobForge runs with outcomes/issues | `npx job-forge telemetry:list` | `npm run telemetry:list` |
137
137
  | Show latest run status + pending TSVs | `npx job-forge telemetry:status` | `npm run telemetry:status` |
138
138
  | Show one JobForge run by session id/prefix | `npx job-forge telemetry:show <id>` | `npm run telemetry:show -- <id>` |
139
+ | Audit latest JobForge trace policy | `npx job-forge guard:audit` | `npm run guard:audit` |
140
+ | Show the active guard policy | `npx job-forge guard:explain` | `npm run guard:explain` |
139
141
  | Re-create harness symlinks | `npx job-forge sync` | `npm run sync` |
140
142
  | Build optional dashboard TUI (Go on `PATH`) | `(cd node_modules/job-forge/dashboard && go build .)` | `npm run build:dashboard` (harness repo only) |
141
143
 
@@ -51,6 +51,9 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
51
51
  - [D6] Pick the mode from the **Routing** table below AND name it explicitly in your first response (e.g., "running auto-pipeline mode", "this is a `compare` request"). If no row matches the user's intent, ask which mode fits; do not guess.
52
52
  why: silent mode picks mis-route work (a "negotiation" question answered in `offer` mode produces the wrong report shape); naming the mode out loud makes the routing decision reviewable and gives downstream dispatches a reliable anchor
53
53
 
54
+ - [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@razroo/iso-orchestrator`, persists workflow records in `.jobforge-runs/`, caps bundle fan-out, and mutexes state/report-number writes. Use `JOBFORGE_LEGACY_BATCH_RUNNER=1` only as a fallback.
55
+ why: the old Bash loop encoded resumability and parallelism manually; the iso-orchestrator path makes the durable control state inspectable and prevents report-number collisions under parallel bundles
56
+
54
57
  ## Procedure
55
58
 
56
59
  1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
package/iso/mcp.json CHANGED
@@ -12,8 +12,7 @@
12
12
  }
13
13
  },
14
14
  "state-trace": {
15
- "command": "uvx",
16
- "args": ["--from", "state-trace[mcp]", "state-trace-mcp"],
15
+ "command": "state-trace-mcp",
17
16
  "env": {
18
17
  "STATE_TRACE_STORAGE_PATH": ".state-trace/memory.db",
19
18
  "STATE_TRACE_NAMESPACE": "job-forge",
package/modes/batch.md CHANGED
@@ -30,6 +30,7 @@ Each worker is a child `opencode run` with a clean 200K token context. The condu
30
30
  ## Read These Files
31
31
 
32
32
  ```
33
+ .jobforge-runs/ # Durable iso-orchestrator records (gitignored)
33
34
  batch/
34
35
  batch-input.tsv # URLs (from conductor or manual)
35
36
  batch-state.tsv # Progress (auto-generated, gitignored)
@@ -66,12 +67,19 @@ d. Execute via Bash:
66
67
  batch/batch-runner.sh [OPTIONS]
67
68
  ```
68
69
 
70
+ `batch-runner.sh` delegates to `scripts/batch-orchestrator.mjs` by default.
71
+ That Node runner uses `@razroo/iso-orchestrator` to persist workflow records in
72
+ `.jobforge-runs/`, cap bundle fan-out with `workflow.forEach`, and serialize
73
+ report-number/state writes while workers run in parallel. If a regression
74
+ requires the old shell loop, run with `JOBFORGE_LEGACY_BATCH_RUNNER=1`.
75
+
69
76
  Options:
70
77
  - `--dry-run` — list pending without executing
71
78
  - `--retry-failed` — only retry failed ones
72
79
  - `--start-from N` — start from ID N
73
80
  - `--parallel N` — N workers in parallel
74
81
  - `--max-retries N` — attempts per offer (default: 2)
82
+ - `--workflow-id ID` — durable workflow id (default: `jobforge-batch`)
75
83
 
76
84
  ## Read batch-state.tsv Format
77
85
 
@@ -85,6 +93,7 @@ id url status started_at completed_at report_num score error retries
85
93
  ## Use Resumability
86
94
 
87
95
  - If it dies → re-run → reads `batch-state.tsv` → skips completed
96
+ - `.jobforge-runs/` keeps the durable run record, step outcomes, and bundle events
88
97
  - Lock file (`batch-runner.pid`) prevents double execution
89
98
  - Each worker is independent: failure on offer #47 does not affect the rest
90
99
 
package/opencode.json CHANGED
@@ -36,9 +36,6 @@
36
36
  "state-trace": {
37
37
  "type": "local",
38
38
  "command": [
39
- "uvx",
40
- "--from",
41
- "state-trace[mcp]",
42
39
  "state-trace-mcp"
43
40
  ],
44
41
  "environment": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "job-forge",
3
- "version": "2.14.13",
3
+ "version": "2.14.15",
4
4
  "description": "AI-powered job search pipeline built on opencode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,6 +25,8 @@
25
25
  "telemetry:status": "node bin/job-forge.mjs telemetry:status",
26
26
  "telemetry:show": "node bin/job-forge.mjs telemetry:show",
27
27
  "telemetry:watch": "node bin/job-forge.mjs telemetry:watch",
28
+ "guard:audit": "node bin/job-forge.mjs guard:audit",
29
+ "guard:explain": "node bin/job-forge.mjs guard:explain",
28
30
  "plan": "iso plan .",
29
31
  "lint:agentmd": "agentmd lint iso/instructions.md",
30
32
  "lint:modes": "isolint lint modes/",
@@ -86,9 +88,11 @@
86
88
  },
87
89
  "license": "MIT",
88
90
  "engines": {
89
- "node": ">=18"
91
+ "node": ">=20.6.0"
90
92
  },
91
93
  "dependencies": {
94
+ "@razroo/iso-guard": "^0.1.0",
95
+ "@razroo/iso-orchestrator": "^0.1.0",
92
96
  "@razroo/iso-trace": "^0.4.0",
93
97
  "playwright": "^1.58.1"
94
98
  },