Plan: "Start on Vector" option on chat.justinsforge.com session launcher¶
URL: https://mkdocs.justinsforge.com/memory/plans/vector-session-launcher-2026-07-01/
Date: 2026-07-01
Approved spec: New Chat sheet on chat.justinsforge.com gets a "Start on Vector" checkbox. Checked, the launcher API drops a job JSON into a queue at forge/data/vector-launcher/queue/ instead of spawning locally. A Vector-side PowerShell daemon (logon scheduled task, installed with schtasks.exe) polls the queue every ~15s over Vector's existing standing SSH into Console (no new keys in either direction), writes a heartbeat each poll, atomically claims jobs, launches a visible Windows Terminal running claude --model <m> --remote-control <name> --debug-file <log> in C:\Users\Justi\forge-vector-local\, fires a Windows toast, parses the bridge URL from the debug log, and writes a result JSON back to Console. The API surfaces the claude.ai/code URL through the existing job-polling UI. Stale heartbeat (>60s) fails fast with "Vector not reachable". Daemon launches Claude only, never arbitrary shell; every launch toasts and logs.
Out of scope:
- Wake-on-LAN when Vector is asleep/off
- Warm pool on Vector (cold boot only)
- Listing or killing Vector sessions from the launcher UI (spawn-time URL only; sessions won't appear in the Active list, which reads Console tmux sockets)
- Sol support (queue format stays machine-generic: jobs carry a machine field so Sol can bolt on later)
Contracts (shared by all tasks)¶
Queue dir layout on Console: forge/data/vector-launcher/{queue,claimed,results}/ + heartbeat.json.
Job file queue/<job_id>.json:
{"id": "<uuid>", "machine": "vector", "model": "opus|fable|sonnet|haiku",
"name": "<slug or empty>", "prompt": "<string or empty>", "created_at": "<iso8601>"}
Result file results/<job_id>.json:
{"id": "<uuid>", "status": "ready|error", "url": "https://claude.ai/code/<id>",
"session_name": "<name>", "error": "<msg if error>", "finished_at": "<iso8601>"}
Heartbeat heartbeat.json: {"ts": "<iso8601>", "host": "vector", "daemon_pid": <int>} rewritten every poll. API treats >60s stale as offline.
Claim = mv queue/<id>.json claimed/<id>.json over SSH (atomic on same filesystem; daemon restart cannot double-launch).
Task list¶
Task 1: Create queue directory structure and contract doc¶
- Files:
/home/justinwieb/forge/data/vector-launcher/queue/.gitkeep,/home/justinwieb/forge/data/vector-launcher/claimed/.gitkeep,/home/justinwieb/forge/data/vector-launcher/results/.gitkeep,/home/justinwieb/forge/data/vector-launcher/README.md - What: Create the three queue dirs with
.gitkeep(per feedback_git_stash_empty_dir_wipe) and a README documenting the job/result/heartbeat JSON contracts above. Bare path,data/is not mkdocs-indexed. - Verification:
ls /home/justinwieb/forge/data/vector-launcher/queue /home/justinwieb/forge/data/vector-launcher/claimed /home/justinwieb/forge/data/vector-launcher/results - Commit:
feat(vector-launcher): queue dir structure + job contract
Task 2: API accepts target:"vector" and queues jobs¶
- Files:
/home/justinwieb/forge/scripts/forge_session_launcher_api.py - What: Add
target: str = "console"toSpawnRequest(validator:console|vector). Inspawn_session, routetarget == "vector"to a new_spawn_vector_job(job_id, req): reject immediately ifheartbeat.jsonmissing/stale (>60s) witherror: "Vector not reachable (daemon heartbeat stale)"; else write the job file, then pollresults/<job_id>.json(2s interval, 180s timeout) and map it onto the existing JOBS statuses (booting->ready/error). Rejectresume/advancedcombined withtarget: "vector"(unsupported, fail loud). Fully async, no threads. - Verification:
curl -fsS -X POST http://127.0.0.1:7365/api/sessions -H "Authorization: Bearer $SESSION_LAUNCHER_TOKEN" -H 'Content-Type: application/json' -d '{"model":"opus","target":"vector"}' | jq .thencurl .../api/jobs/<id>shows the stale-heartbeat error (daemon not deployed yet) - Commit:
feat(session-launcher): target:"vector" job queue path
Task 3: UI checkbox "Start on Vector"¶
- Files:
/home/justinwieb/forge/sites/justinsforge.com/chat/launcher.js,/home/justinwieb/forge/sites/justinsforge.com/chat/launcher.css - What: Add a
fl-checkcheckboxid="fl-vector"labeled "Start on Vector" to the New Session sheet (next to the existing Advanced checkbox,launcher.js~line 452); includetarget: "vector"in the POST body when checked (~line 142). When checked, grey out the warm-pool count line (cold boot only) and disable the Advanced checkbox. Bump the versioned asset URL inindex.htmlif that is the current cache-bust mechanism. - Verification:
/screenshot https://chat.justinsforge.comafter opening the sheet locally viahttp://127.0.0.1:7365/ui/; checkbox visible, POST body carriestarget(confirm inforge/logs/session-launcher.log) - Commit:
feat(chat-ui): Start on Vector checkbox in new-session sheet
Task 4: Vector daemon PowerShell script¶
- Files:
/home/justinwieb/forge/scripts/vector/session-launcher/forge_vector_session_launcher.ps1 - What: Poll loop (15s):
ssh consoleto write heartbeat and listqueue/*.json; claim via atomicmv; parse job; launchwt.exe(fallbackconhost) runningclaude --model <m> --dangerously-skip-permissions --remote-control <name> --debug-file C:\Users\Justi\forge-vector-local\logs\rc-<id>.log "<prompt>"with CWDC:\Users\Justi\forge-vector-local\; BurntToast-free toast viamsg-style fallback (plainNew-BurntToastNotificationonly if module present, else[System.Windows.Forms.NotifyIcon]); poll the debug log up to 120s for thev2 session URLline (same regex family asforge_spawn_session.sh:136); write result JSON back over SSH (PowerShell here-string pipe, NOT heredoc, per reference_vector_claude_code.md). Any failure writesstatus:"error"with the message; every job appended toC:\Users\Justi\forge-vector-local\logs\launcher-daemon.log. Session name defaultvector-<4char>when blank. - Verification: PowerShell syntax check on Console:
pwsh -NoProfile -Command "[scriptblock]::Create((Get-Content -Raw scripts/vector/session-launcher/forge_vector_session_launcher.ps1)) | Out-Null; 'parse ok'"(pwsh is installed on Console; full behavior verified in Task 6) - Commit:
feat(vector-launcher): Vector-side poll daemon (PowerShell)
Task 5: Installer using schtasks.exe¶
- Files:
/home/justinwieb/forge/scripts/vector/session-launcher/install.ps1 - What: Copies the daemon into
C:\Users\Justi\forge-vector-local\session-launcher\, creates logon scheduled taskForgeVectorSessionLauncherviaschtasks.exe /Create /SC ONLOGON(NEVER Register-ScheduledTask; stalls over SSH, per reference_sshrequest_vector_schtasks_gotcha), starts it immediately withschtasks.exe /Run, idempotent re-run (/Foverwrite). Include anuninstallswitch (/Delete /F). - Verification:
pwsh -NoProfileparse check as in Task 4 - Commit:
feat(vector-launcher): schtasks installer for Vector daemon
Task 6: Deploy to Vector and end-to-end test¶
- Files: none new (deployment + test)
- What:
/sshrequest vector for 1 hour to install session-launcher daemon(one-time push under the sanctioned grant model; Justin clicks Approve on Vector).scpthe two scripts over, runinstall.ps1over SSH, confirm heartbeat lands on Console within 30s. Then full path: check "Start on Vector" at chat.justinsforge.com, confirm terminal + toast on Vector, claude.ai/code URL returned in UI, session responds. Also verify fail-fast: stop the task (schtasks /End), wait 90s, spawn again, expect "Vector not reachable" in UI. Restart task after. - Verification:
cat /home/justinwieb/forge/data/vector-launcher/heartbeat.jsonfresh ts; result JSON withstatus:"ready"and working URL; UI error string on the stopped-daemon test - Commit:
chore(vector-launcher): deployed to Vector, e2e verified(any fixup edits included)
Task 7: Register and document¶
- Files:
/home/justinwieb/forge/memory/general/reference_vector_session_launcher.md(with mkdocs URL line),/home/justinwieb/.claude/projects/-home-justinwieb-forge/memory/MEMORY.md(one index line),/home/justinwieb/forge/memory/general/reference_session_launcher.md(add target:"vector" to the endpoint docs) - What: Topic file: architecture (pull model + why), file paths both sides, contracts, install/uninstall commands, known limitations (no Active-list visibility, no WoL), security posture. Update the launcher reference and MEMORY.md index. Run
bash scripts/forge_eval_run.sh. - Verification:
bash /home/justinwieb/forge/scripts/forge_eval_run.shpasses;grep -n "vector" /home/justinwieb/forge/memory/general/reference_session_launcher.md - Commit:
docs(vector-launcher): register + document Vector session launcher