job-forge 2.14.45 → 2.14.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/general-free.md +4 -2
- package/.codex/config.toml +1 -1
- package/.cursor/mcp.json +2 -2
- package/.cursor/rules/agent-general-free.mdc +4 -2
- package/.cursor/rules/main.mdc +6 -6
- package/.mcp.json +2 -2
- package/.opencode/agents/general-free.md +4 -2
- package/.opencode/skills/job-forge.md +2 -1
- package/AGENTS.md +6 -6
- package/CLAUDE.md +6 -6
- package/README.md +2 -1
- package/bin/create-job-forge.mjs +10 -2
- package/bin/geometra-mcp-launcher.mjs +1 -1
- package/bin/job-forge.mjs +32 -0
- package/config/profile.example.yml +16 -14
- package/docs/ARCHITECTURE.md +3 -0
- package/docs/CUSTOMIZATION.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +3 -1
- package/iso/agents/general-free.md +4 -2
- package/iso/commands/job-forge.md +2 -1
- package/iso/instructions.md +6 -6
- package/iso/mcp.json +2 -2
- package/modes/apply.md +6 -4
- package/modes/auto-pipeline.md +1 -1
- package/modes/pipeline.md +1 -1
- package/modes/reference-geometra.md +19 -10
- package/modes/reference-local-helpers.md +3 -0
- package/modes/reference-portals.md +20 -19
- package/modes/scan.md +3 -3
- package/opencode.json +2 -2
- package/package.json +9 -2
- package/scripts/check-helper-integration.mjs +1 -0
- package/scripts/check-iso-smoke.mjs +2 -2
- package/scripts/portal.mjs +36 -6
- package/scripts/receipts.mjs +488 -0
- package/templates/migrations.json +7 -0
|
@@ -18,7 +18,9 @@ Call 3: geometra_connect({
|
|
|
18
18
|
isolated: true,
|
|
19
19
|
headless: true,
|
|
20
20
|
slowMo: 350,
|
|
21
|
-
|
|
21
|
+
browserMode: "stock",
|
|
22
|
+
blockDetection: true,
|
|
23
|
+
blockedSitePolicy: "manual-handoff"
|
|
22
24
|
})
|
|
23
25
|
```
|
|
24
26
|
|
|
@@ -26,7 +28,7 @@ Call 3: geometra_connect({
|
|
|
26
28
|
|
|
27
29
|
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.
|
|
28
30
|
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.
|
|
29
|
-
3. **Always use `isolated: true, headless: true, slowMo: 350,
|
|
31
|
+
3. **Always use `isolated: true, headless: true, slowMo: 350, browserMode: "stock", blockDetection: true, blockedSitePolicy: "manual-handoff"`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
|
|
30
32
|
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.
|
|
31
33
|
|
|
32
34
|
### Read Why This Exists
|
package/.codex/config.toml
CHANGED
|
@@ -18,7 +18,7 @@ model_provider = "openai"
|
|
|
18
18
|
[mcp_servers.geometra]
|
|
19
19
|
command = "npx"
|
|
20
20
|
args = ["--no-install", "job-forge", "mcp:geometra"]
|
|
21
|
-
env = { GEOMETRA_STEALTH = "
|
|
21
|
+
env = { GEOMETRA_STEALTH = "0", GEOMETRA_BROWSER = "stock" }
|
|
22
22
|
|
|
23
23
|
[mcp_servers.gmail]
|
|
24
24
|
command = "npx"
|
package/.cursor/mcp.json
CHANGED
|
@@ -17,7 +17,9 @@ Call 3: geometra_connect({
|
|
|
17
17
|
isolated: true,
|
|
18
18
|
headless: true,
|
|
19
19
|
slowMo: 350,
|
|
20
|
-
|
|
20
|
+
browserMode: "stock",
|
|
21
|
+
blockDetection: true,
|
|
22
|
+
blockedSitePolicy: "manual-handoff"
|
|
21
23
|
})
|
|
22
24
|
```
|
|
23
25
|
|
|
@@ -25,7 +27,7 @@ Call 3: geometra_connect({
|
|
|
25
27
|
|
|
26
28
|
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
29
|
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,
|
|
30
|
+
3. **Always use `isolated: true, headless: true, slowMo: 350, browserMode: "stock", blockDetection: true, blockedSitePolicy: "manual-handoff"`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
|
|
29
31
|
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
32
|
|
|
31
33
|
### Read Why This Exists
|
package/.cursor/rules/main.mdc
CHANGED
|
@@ -30,11 +30,11 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
30
30
|
- [H6] Application outcomes flow through `batch/tracker-additions/*.tsv`, not `data/pipeline.md`. After any multi-apply run, the orchestrator MUST run `npx job-forge merge` then `npx job-forge verify` before ending the session.
|
|
31
31
|
why: `pipeline.md` is the URL inbox (`[ ]` pending → `[x]` processed); `data/applications/YYYY-MM-DD.md` is the outcome log; the TSV pathway is the only safe bridge because `merge` handles column order and duplicate detection
|
|
32
32
|
|
|
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 ...`,
|
|
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 ...`, lineage records returned by `npx job-forge lineage:explain ...`, and verified `.jobforge-receipts/*.agent.zip` paths checked with `npx job-forge receipts:verify ...`.
|
|
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 plus `headless: true`
|
|
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 `headless: true`, `browserMode: \"stock\"`, `blockDetection: true`, and `blockedSitePolicy: \"manual-handoff\"` to every `geometra_connect` call and every Geometra auto-connect call that passes `pageUrl` or `url`." 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. JobForge keeps Chromium headless by passing `headless: true`, uses Geometra MCP >=1.62.3's stock browser mode by default, and surfaces `blockedSite` metadata instead of trying to work around server-side blocks silently
|
|
38
38
|
|
|
39
39
|
- [H9] If Geometra MCP disappears, becomes unresponsive, or returns a cascade of `Not connected` after a live form-fill, inspect `.jobforge-mcp/geometra-mcp.jsonl` before guessing. Report the last `launcher_start`, `child_spawn`, `heartbeat`, `signal_received`, `child_stderr`, and `child_exit` events plus the timestamp gap from the last heartbeat. If the last event is an old heartbeat with no `signal_received` / `child_exit`, treat it as likely host SIGKILL or external process death.
|
|
40
40
|
why: OpenCode or the OS can kill the MCP server without stderr, crash logs, or core dumps. JobForge's MCP launcher writes durable lifecycle events outside MCP stdout, so silent disappearances still leave enough evidence to distinguish host kill, child crash, stderr failure, and wrapper health
|
|
@@ -62,7 +62,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
62
62
|
- [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@agent-pattern-labs/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.
|
|
63
63
|
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
|
|
64
64
|
|
|
65
|
-
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
65
|
+
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, receipts, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
66
66
|
why: the helper ecosystem is now broad enough that repeating every command in the shared prefix wastes cache budget; the reference keeps operational details on demand while `npm run lint:helpers` enforces integration drift in code
|
|
67
67
|
|
|
68
68
|
## Procedure
|
|
@@ -70,13 +70,13 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
70
70
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
71
71
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
72
72
|
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].
|
|
73
|
-
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/headless/
|
|
73
|
+
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/headless/browser-mode prompt hygiene [H8], MCP lifecycle log awareness [H9].
|
|
74
74
|
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].
|
|
75
75
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
76
76
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
77
77
|
8. Apply score gate [D4, D8].
|
|
78
78
|
9. Merge contract-validated TSV outcomes [H6, D8].
|
|
79
|
-
10. Verify tracker and run postflight check before ending [H6, D8].
|
|
79
|
+
10. Verify tracker and run postflight check before ending [H6, D8]. For irreversible or trust-sensitive boundaries only (application submitted, blocked-site manual handoff, release, repro, inter-agent handoff), create and verify a receipt from the relevant artifacts with `npx job-forge receipts:create ...` and `npx job-forge receipts:verify ...` [D8].
|
|
80
80
|
|
|
81
81
|
## Routing
|
|
82
82
|
|
package/.mcp.json
CHANGED
|
@@ -33,7 +33,9 @@ Call 3: geometra_connect({
|
|
|
33
33
|
isolated: true,
|
|
34
34
|
headless: true,
|
|
35
35
|
slowMo: 350,
|
|
36
|
-
|
|
36
|
+
browserMode: "stock",
|
|
37
|
+
blockDetection: true,
|
|
38
|
+
blockedSitePolicy: "manual-handoff"
|
|
37
39
|
})
|
|
38
40
|
```
|
|
39
41
|
|
|
@@ -41,7 +43,7 @@ Call 3: geometra_connect({
|
|
|
41
43
|
|
|
42
44
|
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.
|
|
43
45
|
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.
|
|
44
|
-
3. **Always use `isolated: true, headless: true, slowMo: 350,
|
|
46
|
+
3. **Always use `isolated: true, headless: true, slowMo: 350, browserMode: "stock", blockDetection: true, blockedSitePolicy: "manual-handoff"`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
|
|
45
47
|
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.
|
|
46
48
|
|
|
47
49
|
### Read Why This Exists
|
|
@@ -231,7 +231,8 @@ 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 plus headless: true
|
|
234
|
+
# top-level proxy object plus headless: true, browserMode: "stock",
|
|
235
|
+
# blockDetection: true, and blockedSitePolicy: "manual-handoff" to every
|
|
235
236
|
# Geometra connect or auto-connect call."
|
|
236
237
|
# Dispatch 1 or 2 task() calls in ONE message (never 3+)
|
|
237
238
|
task(subagent_type=<tier per AGENTS.md routing>, prompt=<apply prompt for pair[0]>)
|
package/AGENTS.md
CHANGED
|
@@ -25,11 +25,11 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
25
25
|
- [H6] Application outcomes flow through `batch/tracker-additions/*.tsv`, not `data/pipeline.md`. After any multi-apply run, the orchestrator MUST run `npx job-forge merge` then `npx job-forge verify` before ending the session.
|
|
26
26
|
why: `pipeline.md` is the URL inbox (`[ ]` pending → `[x]` processed); `data/applications/YYYY-MM-DD.md` is the outcome log; the TSV pathway is the only safe bridge because `merge` handles column order and duplicate detection
|
|
27
27
|
|
|
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 ...`,
|
|
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 ...`, lineage records returned by `npx job-forge lineage:explain ...`, and verified `.jobforge-receipts/*.agent.zip` paths checked with `npx job-forge receipts:verify ...`.
|
|
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 plus `headless: true`
|
|
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 `headless: true`, `browserMode: \"stock\"`, `blockDetection: true`, and `blockedSitePolicy: \"manual-handoff\"` to every `geometra_connect` call and every Geometra auto-connect call that passes `pageUrl` or `url`." 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. JobForge keeps Chromium headless by passing `headless: true`, uses Geometra MCP >=1.62.3's stock browser mode by default, and surfaces `blockedSite` metadata instead of trying to work around server-side blocks silently
|
|
33
33
|
|
|
34
34
|
- [H9] If Geometra MCP disappears, becomes unresponsive, or returns a cascade of `Not connected` after a live form-fill, inspect `.jobforge-mcp/geometra-mcp.jsonl` before guessing. Report the last `launcher_start`, `child_spawn`, `heartbeat`, `signal_received`, `child_stderr`, and `child_exit` events plus the timestamp gap from the last heartbeat. If the last event is an old heartbeat with no `signal_received` / `child_exit`, treat it as likely host SIGKILL or external process death.
|
|
35
35
|
why: OpenCode or the OS can kill the MCP server without stderr, crash logs, or core dumps. JobForge's MCP launcher writes durable lifecycle events outside MCP stdout, so silent disappearances still leave enough evidence to distinguish host kill, child crash, stderr failure, and wrapper health
|
|
@@ -57,7 +57,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
57
57
|
- [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@agent-pattern-labs/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.
|
|
58
58
|
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
|
|
59
59
|
|
|
60
|
-
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
60
|
+
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, receipts, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
61
61
|
why: the helper ecosystem is now broad enough that repeating every command in the shared prefix wastes cache budget; the reference keeps operational details on demand while `npm run lint:helpers` enforces integration drift in code
|
|
62
62
|
|
|
63
63
|
## Procedure
|
|
@@ -65,13 +65,13 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
65
65
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
66
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
67
|
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].
|
|
68
|
-
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/headless/
|
|
68
|
+
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/headless/browser-mode prompt hygiene [H8], MCP lifecycle log awareness [H9].
|
|
69
69
|
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].
|
|
70
70
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
71
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
72
72
|
8. Apply score gate [D4, D8].
|
|
73
73
|
9. Merge contract-validated TSV outcomes [H6, D8].
|
|
74
|
-
10. Verify tracker and run postflight check before ending [H6, D8].
|
|
74
|
+
10. Verify tracker and run postflight check before ending [H6, D8]. For irreversible or trust-sensitive boundaries only (application submitted, blocked-site manual handoff, release, repro, inter-agent handoff), create and verify a receipt from the relevant artifacts with `npx job-forge receipts:create ...` and `npx job-forge receipts:verify ...` [D8].
|
|
75
75
|
|
|
76
76
|
## Routing
|
|
77
77
|
|
package/CLAUDE.md
CHANGED
|
@@ -25,11 +25,11 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
25
25
|
- [H6] Application outcomes flow through `batch/tracker-additions/*.tsv`, not `data/pipeline.md`. After any multi-apply run, the orchestrator MUST run `npx job-forge merge` then `npx job-forge verify` before ending the session.
|
|
26
26
|
why: `pipeline.md` is the URL inbox (`[ ]` pending → `[x]` processed); `data/applications/YYYY-MM-DD.md` is the outcome log; the TSV pathway is the only safe bridge because `merge` handles column order and duplicate detection
|
|
27
27
|
|
|
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 ...`,
|
|
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 ...`, lineage records returned by `npx job-forge lineage:explain ...`, and verified `.jobforge-receipts/*.agent.zip` paths checked with `npx job-forge receipts:verify ...`.
|
|
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 plus `headless: true`
|
|
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 `headless: true`, `browserMode: \"stock\"`, `blockDetection: true`, and `blockedSitePolicy: \"manual-handoff\"` to every `geometra_connect` call and every Geometra auto-connect call that passes `pageUrl` or `url`." 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. JobForge keeps Chromium headless by passing `headless: true`, uses Geometra MCP >=1.62.3's stock browser mode by default, and surfaces `blockedSite` metadata instead of trying to work around server-side blocks silently
|
|
33
33
|
|
|
34
34
|
- [H9] If Geometra MCP disappears, becomes unresponsive, or returns a cascade of `Not connected` after a live form-fill, inspect `.jobforge-mcp/geometra-mcp.jsonl` before guessing. Report the last `launcher_start`, `child_spawn`, `heartbeat`, `signal_received`, `child_stderr`, and `child_exit` events plus the timestamp gap from the last heartbeat. If the last event is an old heartbeat with no `signal_received` / `child_exit`, treat it as likely host SIGKILL or external process death.
|
|
35
35
|
why: OpenCode or the OS can kill the MCP server without stderr, crash logs, or core dumps. JobForge's MCP launcher writes durable lifecycle events outside MCP stdout, so silent disappearances still leave enough evidence to distinguish host kill, child crash, stderr failure, and wrapper health
|
|
@@ -57,7 +57,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
57
57
|
- [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@agent-pattern-labs/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.
|
|
58
58
|
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
|
|
59
59
|
|
|
60
|
-
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
60
|
+
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, receipts, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
61
61
|
why: the helper ecosystem is now broad enough that repeating every command in the shared prefix wastes cache budget; the reference keeps operational details on demand while `npm run lint:helpers` enforces integration drift in code
|
|
62
62
|
|
|
63
63
|
## Procedure
|
|
@@ -65,13 +65,13 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
65
65
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
66
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
67
|
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].
|
|
68
|
-
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/headless/
|
|
68
|
+
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/headless/browser-mode prompt hygiene [H8], MCP lifecycle log awareness [H9].
|
|
69
69
|
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].
|
|
70
70
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
71
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
72
72
|
8. Apply score gate [D4, D8].
|
|
73
73
|
9. Merge contract-validated TSV outcomes [H6, D8].
|
|
74
|
-
10. Verify tracker and run postflight check before ending [H6, D8].
|
|
74
|
+
10. Verify tracker and run postflight check before ending [H6, D8]. For irreversible or trust-sensitive boundaries only (application submitted, blocked-site manual handoff, release, repro, inter-agent handoff), create and verify a receipt from the relevant artifacts with `npx job-forge receipts:create ...` and `npx job-forge receipts:verify ...` [D8].
|
|
75
75
|
|
|
76
76
|
## Routing
|
|
77
77
|
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ JobForge is built for selective, high-fit applications. It is not intended for s
|
|
|
65
65
|
- Scans configured company portals and job boards.
|
|
66
66
|
- Tracks applications, follow-ups, rejections, offers, reports, and PDFs.
|
|
67
67
|
- Supports batch evaluation and application work through bounded subagents.
|
|
68
|
-
- Uses local helper CLIs for dedupe, scoring, lineage, preflight, postflight, and tracker integrity.
|
|
68
|
+
- Uses local helper CLIs for dedupe, scoring, lineage, preflight, postflight, receipts, and tracker integrity.
|
|
69
69
|
|
|
70
70
|
## Common Commands
|
|
71
71
|
|
|
@@ -78,6 +78,7 @@ Run these from your personal project root after `npm install`.
|
|
|
78
78
|
| Merge batch tracker additions | `npx job-forge merge` |
|
|
79
79
|
| Generate a CV PDF from the current project | `npx job-forge pdf` |
|
|
80
80
|
| Show token usage | `npx job-forge tokens --days 1` |
|
|
81
|
+
| Create a verifiable evidence receipt | `npx job-forge receipts:create --artifact <file>` |
|
|
81
82
|
| Rebuild harness symlinks | `npx job-forge sync` |
|
|
82
83
|
| Upgrade the harness | `npm run update-harness` |
|
|
83
84
|
|
package/bin/create-job-forge.mjs
CHANGED
|
@@ -198,6 +198,12 @@ const consumerPkg = {
|
|
|
198
198
|
'redact:verify': 'job-forge redact:verify',
|
|
199
199
|
'redact:apply': 'job-forge redact:apply',
|
|
200
200
|
'redact:explain': 'job-forge redact:explain',
|
|
201
|
+
'receipts:create': 'job-forge receipts:create',
|
|
202
|
+
'receipts:capture': 'job-forge receipts:capture',
|
|
203
|
+
'receipts:verify': 'job-forge receipts:verify',
|
|
204
|
+
'receipts:inspect': 'job-forge receipts:inspect',
|
|
205
|
+
'receipts:redact': 'job-forge receipts:redact',
|
|
206
|
+
'receipts:path': 'job-forge receipts:path',
|
|
201
207
|
'migrate:plan': 'job-forge migrate:plan',
|
|
202
208
|
'migrate:apply': 'job-forge migrate:apply',
|
|
203
209
|
'migrate:check': 'job-forge migrate:check',
|
|
@@ -245,8 +251,8 @@ const opencodeCfg = {
|
|
|
245
251
|
type: 'local',
|
|
246
252
|
command: ['npx', '--no-install', 'job-forge', 'mcp:geometra'],
|
|
247
253
|
environment: {
|
|
248
|
-
GEOMETRA_STEALTH: '
|
|
249
|
-
GEOMETRA_BROWSER: '
|
|
254
|
+
GEOMETRA_STEALTH: '0',
|
|
255
|
+
GEOMETRA_BROWSER: 'stock',
|
|
250
256
|
},
|
|
251
257
|
enabled: true,
|
|
252
258
|
},
|
|
@@ -317,6 +323,7 @@ Before doing any work, remember where things live in *this* project:
|
|
|
317
323
|
| Dispatch preflight policy | \`templates/preflight.json\` | Safe apply rounds/gates; use \`job-forge preflight:*\` |
|
|
318
324
|
| Dispatch postflight policy | \`templates/postflight.json\` | Safe apply settlement; use \`job-forge postflight:*\` |
|
|
319
325
|
| Consumer migrations | \`templates/migrations.json\` | Safe script/gitignore upgrades; use \`job-forge migrate:*\` |
|
|
326
|
+
| Evidence receipts | \`.jobforge-receipts/\` | Portable verifiable work receipts; use \`job-forge receipts:*\` |
|
|
320
327
|
| Scanner config | \`portals.yml\` (project root) | Company configs |
|
|
321
328
|
| Profile / identity | \`config/profile.yml\` | Candidate name, email, target roles |
|
|
322
329
|
| CV | \`cv.md\` (project root) | Markdown, source of truth |
|
|
@@ -417,6 +424,7 @@ data/timeline-events.jsonl
|
|
|
417
424
|
.jobforge-mcp/
|
|
418
425
|
.jobforge-runs/
|
|
419
426
|
.jobforge-redacted/
|
|
427
|
+
.jobforge-receipts/
|
|
420
428
|
reports/
|
|
421
429
|
!reports/.gitkeep
|
|
422
430
|
batch/batch-state.tsv
|
|
@@ -5,7 +5,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
7
7
|
|
|
8
|
-
const DEFAULT_FALLBACK_PACKAGE = '@geometra/mcp@1.62.
|
|
8
|
+
const DEFAULT_FALLBACK_PACKAGE = '@geometra/mcp@1.62.3';
|
|
9
9
|
const RESOLVE_ONLY_FLAG = '--job-forge-resolve-target';
|
|
10
10
|
const DEFAULT_LOG_RELATIVE_PATH = '.jobforge-mcp/geometra-mcp.jsonl';
|
|
11
11
|
const DEFAULT_HEARTBEAT_MS = 15_000;
|
package/bin/job-forge.mjs
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
* prioritize:* Rank local next-action queues via iso-prioritize
|
|
37
37
|
* lineage:* Record artifact lineage and stale outputs via iso-lineage
|
|
38
38
|
* redact:* Sanitize local exports via iso-redact
|
|
39
|
+
* receipts:* Create/verify/redact portable JobForge evidence receipts
|
|
39
40
|
* migrate:* Apply deterministic consumer-project migrations via iso-migrate
|
|
40
41
|
* sync Re-run the harness symlink sync (bin/sync.mjs)
|
|
41
42
|
* help, --help Show this message
|
|
@@ -230,6 +231,15 @@ const redactAliases = {
|
|
|
230
231
|
'redact:path': 'path',
|
|
231
232
|
};
|
|
232
233
|
|
|
234
|
+
const receiptsAliases = {
|
|
235
|
+
'receipts:create': 'create',
|
|
236
|
+
'receipts:capture': 'capture',
|
|
237
|
+
'receipts:verify': 'verify',
|
|
238
|
+
'receipts:inspect': 'inspect',
|
|
239
|
+
'receipts:redact': 'redact',
|
|
240
|
+
'receipts:path': 'path',
|
|
241
|
+
};
|
|
242
|
+
|
|
233
243
|
const migrateAliases = {
|
|
234
244
|
'migrate:plan': 'plan',
|
|
235
245
|
'migrate:apply': 'apply',
|
|
@@ -332,6 +342,11 @@ Commands:
|
|
|
332
342
|
redact:verify Fail if local text still contains sensitive values
|
|
333
343
|
redact:apply Write a sanitized copy of local text
|
|
334
344
|
redact:explain Show the active redaction policy
|
|
345
|
+
receipts:create Create a portable receipt from JobForge artifacts
|
|
346
|
+
receipts:capture Capture a command's stdout/stderr into a receipt
|
|
347
|
+
receipts:verify Verify a receipt manifest and artifact hashes
|
|
348
|
+
receipts:inspect Summarize a receipt without unpacking it
|
|
349
|
+
receipts:redact Redact a receipt with templates/redact.json
|
|
335
350
|
migrate:plan Preview deterministic consumer-project migrations
|
|
336
351
|
migrate:apply Apply deterministic consumer-project migrations
|
|
337
352
|
migrate:check Fail if migrations are pending
|
|
@@ -394,6 +409,8 @@ Pass --help after a command to see its own flags, e.g.:
|
|
|
394
409
|
job-forge lineage:check --artifact reports/123-acme-2026-04-27.md
|
|
395
410
|
job-forge redact:scan --input raw-session.jsonl
|
|
396
411
|
job-forge redact:apply --input raw-session.jsonl --output .jobforge-redacted/session.jsonl
|
|
412
|
+
job-forge receipts:create --company "Acme" --role "Staff Engineer" --artifact batch/tracker-additions/123-acme.tsv --include-ledger
|
|
413
|
+
job-forge receipts:verify .jobforge-receipts/receipt.agent.zip
|
|
397
414
|
job-forge migrate:check
|
|
398
415
|
job-forge migrate:apply
|
|
399
416
|
|
|
@@ -675,6 +692,21 @@ if (cmd === 'redact' || redactAliases[cmd]) {
|
|
|
675
692
|
process.exit(result.status ?? 1);
|
|
676
693
|
}
|
|
677
694
|
|
|
695
|
+
if (cmd === 'receipts' || receiptsAliases[cmd]) {
|
|
696
|
+
const receiptsArgs = cmd === 'receipts'
|
|
697
|
+
? (rest.length === 0 ? ['help'] : rest)
|
|
698
|
+
: [receiptsAliases[cmd], ...rest];
|
|
699
|
+
|
|
700
|
+
const scriptPath = join(PKG_ROOT, 'scripts/receipts.mjs');
|
|
701
|
+
const result = spawnSync(process.execPath, [scriptPath, ...receiptsArgs], {
|
|
702
|
+
stdio: 'inherit',
|
|
703
|
+
cwd: PROJECT_DIR,
|
|
704
|
+
env: process.env,
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
process.exit(result.status ?? 1);
|
|
708
|
+
}
|
|
709
|
+
|
|
678
710
|
if (cmd === 'migrate' || migrateAliases[cmd]) {
|
|
679
711
|
const migrateArgs = cmd === 'migrate'
|
|
680
712
|
? (rest.length === 0 ? ['help'] : rest)
|
|
@@ -87,23 +87,25 @@ 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
|
|
91
|
-
# Uncomment and fill in to route
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# `
|
|
96
|
-
#
|
|
97
|
-
#
|
|
90
|
+
# Optional outbound proxy for Geometra browser sessions.
|
|
91
|
+
# Uncomment and fill in to route browser traffic through a proxy you already
|
|
92
|
+
# control. JobForge never bundles proxy bandwidth and never prints these values
|
|
93
|
+
# in prompts or status text. JobForge passes `headless: true`,
|
|
94
|
+
# `browserMode: "stock"`, `blockDetection: true`, and
|
|
95
|
+
# `blockedSitePolicy: "manual-handoff"` by default so Geometra MCP >= 1.62.3
|
|
96
|
+
# keeps browser windows hidden and returns structured `blockedSite` metadata
|
|
97
|
+
# when a portal serves a challenge, CAPTCHA, access-denied page, or other
|
|
98
|
+
# manual-review state.
|
|
98
99
|
#
|
|
99
|
-
# BYO — JobForge does NOT bundle or resell proxy bandwidth.
|
|
100
|
-
#
|
|
101
|
-
# mobile hotspot, or your own SOCKS relay. Required: Geometra MCP >= 1.61.3.
|
|
100
|
+
# BYO — JobForge does NOT bundle or resell proxy bandwidth. Required:
|
|
101
|
+
# Geometra MCP >= 1.62.3.
|
|
102
102
|
#
|
|
103
103
|
# When present, the apply / scan / auto-pipeline modes thread this into every
|
|
104
|
-
# `geometra_connect` call as `proxy: {...}` alongside `headless: true
|
|
105
|
-
# `
|
|
106
|
-
#
|
|
104
|
+
# `geometra_connect` call as `proxy: {...}` alongside `headless: true`,
|
|
105
|
+
# `browserMode: "stock"`, `blockDetection: true`, and
|
|
106
|
+
# `blockedSitePolicy: "manual-handoff"`. Pooling is partitioned by proxy
|
|
107
|
+
# identity and browser mode so direct and proxied sessions never share a
|
|
108
|
+
# Chromium instance.
|
|
107
109
|
#
|
|
108
110
|
# proxy:
|
|
109
111
|
# server: "http://residential.example.com:8080" # http://, https://, or socks5://
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -169,6 +169,7 @@ data/pipeline.md → Pending URLs and `local:jds/...` inbox (see modes/p
|
|
|
169
169
|
.jobforge-timeline.json → Deterministic follow-up action plan built from templates/timeline.json
|
|
170
170
|
.jobforge-prioritize.json → Deterministic next-action priority queue built from templates/prioritize.json
|
|
171
171
|
.jobforge-lineage.json → Artifact lineage graph for stale report/PDF checks
|
|
172
|
+
.jobforge-receipts/ → Portable evidence receipts for side-effect, handoff, release, blocked-site, and repro boundaries
|
|
172
173
|
jds/*.md → Saved job descriptions referenced from the pipeline (`local:jds/{file}`)
|
|
173
174
|
templates/states.yml → Canonical status values
|
|
174
175
|
templates/canon.json → Canonical URL/company/role identity keys
|
|
@@ -197,6 +198,7 @@ Create `data/pipeline.md` when you start using the URL inbox (`/job-forge pipeli
|
|
|
197
198
|
- Timeline: `.jobforge-timeline.json` (created on demand by `job-forge timeline:*`; gitignored local next-action state)
|
|
198
199
|
- Prioritize: `.jobforge-prioritize.json` and `.jobforge-prioritize-items.json` (created on demand by `job-forge prioritize:*`; gitignored local ranking state)
|
|
199
200
|
- Lineage: `.jobforge-lineage.json` (created by `job-forge lineage:record`; gitignored local stale-output state)
|
|
201
|
+
- Receipts: `.jobforge-receipts/*.agent.zip` (created by `job-forge receipts:create` / `receipts:capture`; gitignored local evidence bundles)
|
|
200
202
|
- Canon: `templates/canon.json` (identity rules inspected with `job-forge canon:*`)
|
|
201
203
|
- Score: `templates/score.json` (weighted rubric and gates inspected with `job-forge score:*`)
|
|
202
204
|
- Timeline policy: `templates/timeline.json` (follow-up windows inspected with `job-forge timeline:*`)
|
|
@@ -266,6 +268,7 @@ Scripts maintain data consistency. In a consumer project they're invoked via the
|
|
|
266
268
|
| `scripts/preflight.mjs` | `npx job-forge preflight:plan` / `preflight:check` / `preflight:explain` | Deterministic `@agent-pattern-labs/iso-preflight` dispatch planning for file-backed candidate facts and gates |
|
|
267
269
|
| `scripts/postflight.mjs` | `npx job-forge postflight:status` / `postflight:check` / `postflight:explain` | Deterministic `@agent-pattern-labs/iso-postflight` settlement for dispatch outcomes, required tracker TSV artifacts, and merge/verify post-steps |
|
|
268
270
|
| `scripts/redact.mjs` | `npx job-forge redact:scan` / `redact:apply` / `redact:verify` | Deterministic `@agent-pattern-labs/iso-redact` safe-export scanning and sanitization for traces, prompts, reports, and fixtures |
|
|
271
|
+
| `scripts/receipts.mjs` | `npx job-forge receipts:create` / `receipts:verify` / `receipts:redact` | Portable JobForge evidence receipts for side-effect, release, blocked-site, repro, and inter-agent handoff boundaries |
|
|
269
272
|
| `scripts/migrate.mjs` | `npx job-forge migrate:plan` / `migrate:apply` / `migrate:check` | Deterministic `@agent-pattern-labs/iso-migrate` consumer-project upgrades for scripts and generated-artifact ignores |
|
|
270
273
|
| `scripts/check-helper-integration.mjs` | `npm run lint:helpers` | Integration lint that keeps helper packages, scripts, scaffolder defaults, migrations, generated ignores, docs, and `modes/reference-local-helpers.md` aligned |
|
|
271
274
|
| `tracker-lib.mjs` | _(library)_ | Shared helpers for reading/writing day-based tracker files — imported by merge/dedup/verify/normalize |
|
package/docs/CUSTOMIZATION.md
CHANGED
|
@@ -190,6 +190,10 @@ Application settlement policy lives in `templates/postflight.json` and is checke
|
|
|
190
190
|
|
|
191
191
|
Safe-export redaction rules live in `templates/redact.json` and are enforced locally by `@agent-pattern-labs/iso-redact`. Use `job-forge redact:scan --input <file>` before sharing traces, prompts, reports, or fixtures outside the project, `job-forge redact:apply --input <file> --output .jobforge-redacted/<file>` to write a sanitized copy, and `job-forge redact:verify --input <file>` to fail if secrets or configured PII remain. Findings never print matched values. This is not an MCP and does not add prompt or tool-schema tokens.
|
|
192
192
|
|
|
193
|
+
## JobForge evidence receipts
|
|
194
|
+
|
|
195
|
+
Evidence receipts live under `.jobforge-receipts/` and are created locally by `job-forge receipts:*`. Use them at side-effect boundaries such as application submission, blocked-site manual handoff, release, repro, or inter-agent handoff. `receipts:create` can attach tracker TSVs, reports, portal snapshots/form schemas, Geometra replay files, filtered ledger events, proof payloads, and verdicts; `receipts:verify` checks manifest hashes; `receipts:redact` applies `templates/redact.json` before sharing. Routine reads and ordinary local edits do not need receipts.
|
|
196
|
+
|
|
193
197
|
## JobForge consumer migrations
|
|
194
198
|
|
|
195
199
|
Consumer-project migrations live in `templates/migrations.json` and are applied locally by `@agent-pattern-labs/iso-migrate`. `job-forge sync` applies safe migrations automatically after refreshing symlinks; use `JOB_FORGE_SKIP_MIGRATIONS=1` to opt out. Use `job-forge migrate:plan`, `job-forge migrate:apply`, and `job-forge migrate:check` to inspect or enforce script/gitignore drift explicitly. This is not an MCP and does not add prompt or tool-schema tokens.
|
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`, `trace`, `telemetry`, `guard`, `ledger`, `capabilities`, `context`, `cache`, `index`, `facts`, `score`, `canon`, `timeline`, `prioritize`, `lineage`, `preflight`, `postflight`, `redact`, `migrate`, `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`, `ledger`, `capabilities`, `context`, `cache`, `index`, `facts`, `score`, `canon`, `timeline`, `prioritize`, `lineage`, `preflight`, `postflight`, `redact`, `receipts`, `migrate`, `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 smoke:iso` + `npm run build:dashboard`). | [CONTRIBUTING.md — Development](../CONTRIBUTING.md#development). |
|
package/docs/SETUP.md
CHANGED
|
@@ -145,6 +145,8 @@ From your project root, these commands maintain the tracker and pipeline checks.
|
|
|
145
145
|
| Fail unless dispatch outcomes and post-steps are complete | `npx job-forge postflight:check --plan batch/preflight-plan.json --outcomes batch/postflight-outcomes.json` | `npm run postflight:check -- --plan ... --outcomes ...` |
|
|
146
146
|
| Sanitize local text before exporting it | `npx job-forge redact:apply --input raw-session.jsonl --output .jobforge-redacted/session.jsonl` | `npm run redact:apply -- --input ... --output ...` |
|
|
147
147
|
| Verify local text is safe to export | `npx job-forge redact:verify --input .jobforge-redacted/session.jsonl` | `npm run redact:verify -- --input ...` |
|
|
148
|
+
| Create a verifiable work receipt | `npx job-forge receipts:create --company "Acme" --role "Staff Engineer" --artifact batch/tracker-additions/example.tsv --include-ledger` | `npm run receipts:create -- --company ... --role ... --artifact ... --include-ledger` |
|
|
149
|
+
| Verify or redact a receipt | `npx job-forge receipts:verify .jobforge-receipts/example.agent.zip` | `npm run receipts:verify -- .jobforge-receipts/example.agent.zip` |
|
|
148
150
|
| Inspect pending consumer migrations | `npx job-forge migrate:plan` | `npm run migrate:plan` |
|
|
149
151
|
| Map status column to canonical labels | `npx job-forge normalize` | `npm run normalize` |
|
|
150
152
|
| Merge duplicate company/role rows | `npx job-forge dedup` | `npm run dedup` |
|
|
@@ -214,7 +216,7 @@ Use it to identify which sessions or models are consuming the most tokens. The `
|
|
|
214
216
|
`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
217
|
|
|
216
218
|
**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", "--no-install", "job-forge", "mcp:geometra"]`, `enabled: true`, and its `environment` should include `GEOMETRA_STEALTH=
|
|
219
|
+
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", "--no-install", "job-forge", "mcp:geometra"]`, `enabled: true`, and its `environment` should include `GEOMETRA_STEALTH=0` plus `GEOMETRA_BROWSER=stock`. `job-forge mcp:geometra` resolves Geometra in this order: `JOB_FORGE_GEOMETRA_MCP_PATH`, then a consumer-project override from `package.json -> jobForge.geometraMcpPath`, then `opencode.json -> mcp.geometra.environment.JOB_FORGE_GEOMETRA_MCP_PATH`, then a sibling `../geometra/mcp/dist/index.js` checkout for local JobForge development, and finally the pinned npm package. Geometra manages Chromium via its built-in proxy. JobForge passes `headless: true`, `browserMode: "stock"`, `blockDetection: true`, and `blockedSitePolicy: "manual-handoff"` for portal sessions explicitly, keeping browser windows hidden while surfacing structured blocked-site metadata. For standalone CLI usage (outside opencode), `generate-pdf.mjs` also works with standalone Playwright/Chromium — install with `npx playwright install chromium`.
|
|
218
220
|
|
|
219
221
|
`job-forge mcp:geometra` writes MCP launcher diagnostics to `.jobforge-mcp/geometra-mcp.jsonl`. If the server silently vanishes with empty stderr, run `tail -40 .jobforge-mcp/geometra-mcp.jsonl`. A final `signal_received` event means the host sent a catchable signal, a final `child_exit` means Geometra exited, and an old final `heartbeat` with no exit/signal usually means SIGKILL or external process death. Set `JOB_FORGE_GEOMETRA_MCP_LOG_PATH` to move the log, `JOB_FORGE_GEOMETRA_MCP_LOG=0` to disable it, or `JOB_FORGE_GEOMETRA_MCP_HEARTBEAT_MS` to tune the heartbeat interval.
|
|
220
222
|
|
|
@@ -40,7 +40,9 @@ Call 3: geometra_connect({
|
|
|
40
40
|
isolated: true,
|
|
41
41
|
headless: true,
|
|
42
42
|
slowMo: 350,
|
|
43
|
-
|
|
43
|
+
browserMode: "stock",
|
|
44
|
+
blockDetection: true,
|
|
45
|
+
blockedSitePolicy: "manual-handoff"
|
|
44
46
|
})
|
|
45
47
|
```
|
|
46
48
|
|
|
@@ -48,7 +50,7 @@ Call 3: geometra_connect({
|
|
|
48
50
|
|
|
49
51
|
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.
|
|
50
52
|
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.
|
|
51
|
-
3. **Always use `isolated: true, headless: true, slowMo: 350,
|
|
53
|
+
3. **Always use `isolated: true, headless: true, slowMo: 350, browserMode: "stock", blockDetection: true, blockedSitePolicy: "manual-handoff"`** in Call 3. No other values. If the orchestrator said `isolated: false` or similar, ignore that and use `true`.
|
|
52
54
|
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.
|
|
53
55
|
|
|
54
56
|
### Read Why This Exists
|
|
@@ -234,7 +234,8 @@ 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 plus headless: true
|
|
237
|
+
# top-level proxy object plus headless: true, browserMode: "stock",
|
|
238
|
+
# blockDetection: true, and blockedSitePolicy: "manual-handoff" to every
|
|
238
239
|
# Geometra connect or auto-connect call."
|
|
239
240
|
# Dispatch 1 or 2 task() calls in ONE message (never 3+)
|
|
240
241
|
task(subagent_type=<tier per AGENTS.md routing>, prompt=<apply prompt for pair[0]>)
|
package/iso/instructions.md
CHANGED
|
@@ -25,11 +25,11 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
25
25
|
- [H6] Application outcomes flow through `batch/tracker-additions/*.tsv`, not `data/pipeline.md`. After any multi-apply run, the orchestrator MUST run `npx job-forge merge` then `npx job-forge verify` before ending the session.
|
|
26
26
|
why: `pipeline.md` is the URL inbox (`[ ]` pending → `[x]` processed); `data/applications/YYYY-MM-DD.md` is the outcome log; the TSV pathway is the only safe bridge because `merge` handles column order and duplicate detection
|
|
27
27
|
|
|
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 ...`,
|
|
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 ...`, lineage records returned by `npx job-forge lineage:explain ...`, and verified `.jobforge-receipts/*.agent.zip` paths checked with `npx job-forge receipts:verify ...`.
|
|
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 plus `headless: true`
|
|
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 `headless: true`, `browserMode: \"stock\"`, `blockDetection: true`, and `blockedSitePolicy: \"manual-handoff\"` to every `geometra_connect` call and every Geometra auto-connect call that passes `pageUrl` or `url`." 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. JobForge keeps Chromium headless by passing `headless: true`, uses Geometra MCP >=1.62.3's stock browser mode by default, and surfaces `blockedSite` metadata instead of trying to work around server-side blocks silently
|
|
33
33
|
|
|
34
34
|
- [H9] If Geometra MCP disappears, becomes unresponsive, or returns a cascade of `Not connected` after a live form-fill, inspect `.jobforge-mcp/geometra-mcp.jsonl` before guessing. Report the last `launcher_start`, `child_spawn`, `heartbeat`, `signal_received`, `child_stderr`, and `child_exit` events plus the timestamp gap from the last heartbeat. If the last event is an old heartbeat with no `signal_received` / `child_exit`, treat it as likely host SIGKILL or external process death.
|
|
35
35
|
why: OpenCode or the OS can kill the MCP server without stderr, crash logs, or core dumps. JobForge's MCP launcher writes durable lifecycle events outside MCP stdout, so silent disappearances still leave enough evidence to distinguish host kill, child crash, stderr failure, and wrapper health
|
|
@@ -57,7 +57,7 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
57
57
|
- [D7] For standalone `batch` runs, prefer `batch/batch-runner.sh` instead of hand-rolling the loop. It delegates to `@agent-pattern-labs/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.
|
|
58
58
|
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
|
|
59
59
|
|
|
60
|
-
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
60
|
+
- [D8] Use deterministic local helpers instead of prose when they can answer or validate state, identity, policy, scoring, timing, dispatch, priority, lineage, migration, receipts, or safe-export questions. Read `modes/reference-local-helpers.md` when choosing a helper or changing helper wiring.
|
|
61
61
|
why: the helper ecosystem is now broad enough that repeating every command in the shared prefix wastes cache budget; the reference keeps operational details on demand while `npm run lint:helpers` enforces integration drift in code
|
|
62
62
|
|
|
63
63
|
## Procedure
|
|
@@ -65,13 +65,13 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
65
65
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
66
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
67
|
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].
|
|
68
|
-
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/headless/
|
|
68
|
+
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/headless/browser-mode prompt hygiene [H8], MCP lifecycle log awareness [H9].
|
|
69
69
|
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].
|
|
70
70
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
71
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
72
72
|
8. Apply score gate [D4, D8].
|
|
73
73
|
9. Merge contract-validated TSV outcomes [H6, D8].
|
|
74
|
-
10. Verify tracker and run postflight check before ending [H6, D8].
|
|
74
|
+
10. Verify tracker and run postflight check before ending [H6, D8]. For irreversible or trust-sensitive boundaries only (application submitted, blocked-site manual handoff, release, repro, inter-agent handoff), create and verify a receipt from the relevant artifacts with `npx job-forge receipts:create ...` and `npx job-forge receipts:verify ...` [D8].
|
|
75
75
|
|
|
76
76
|
## Routing
|
|
77
77
|
|