Repository navigation
Description
Design (design-architect pass 19, 2026-10-30): pm-dispatch launch pipeline, staged. Full design doc: files/designs/code-pm-dispatch-launch-pipeline.md in the keep-the-four-repos-healthy goal workspace. No user/review — the four origin issues carry explicit acceptance, the codebase already embodies each rule locally (issue-creation warnings, the lane-ledger guard, the success-only JWKS cache), and the judgment calls are recorded in the doc. One timing call is flagged for Drew's attention rather than labelled: Phase 1 should land before the dispatch.security.cab tunnel hostname is added (README's remaining step), since it closes the two boundary holes below.
Summary
pm-dispatch (#2648, merged today) launches OpenCode sessions on the laptop from authenticated HTTP calls. Four bugs filed today — #2664, #2665, #2660, #2663 — are one per stage of its launch pipeline, with one root: the pipeline's stages and failure semantics were never stated, so each stage improvised. This design states them as three rules, generalising the patterns the code already uses locally:
- Boundary (Rule 1): everything the caller supplies is validated to its declared shape and scope before any side effect, and authorization binds the request's whole effect.
parseJwtrejects non-plain-object header/payload decodings (→AuthError, 401 — today a rawTypeError→ 500,auth.ts:53-67,101, server mappingserver.ts:376-379).directoryresolves by rule: absent/blank →REPO_DIRS[repo]; otherwise an absolute path that is the repo primary (config.ts:25-30) or inside it, else 400 — todayoptStringpasses it verbatim (validate.ts:93-98,server.ts:180) andassertCallerRepobinds only therepofield (auth.ts:190-201), so a repo-bound OIDC caller can launch a session anywhere on the laptop. The rule applies to all caller types uniformly. - Commit point (Rule 2): the claim PATCH is the launch's commit point. After it, steps are annotations: lane ledger (already guarded,
server.ts:148-158), issue comment and labels (unguarded today,server.ts:161-166) become individually guarded — failure = response warning + task note ("launch unaffected" shape), nevercancelTask, never 502, never adispatch launch failedstamp on a live session (today:wait:truecancels the live session's task;wait:falsemis-stamps it, pm-dispatch(sessions): a GitHub comment/label failure after the claim PATCH cancels a live session's queue task — the launched session keeps running untracked #2660). Cancel + 502 remains correct only at/before the commit point. README's flow section gains the stage/failure table. - Retryable init (Rule 3): memoized async initialization caches success only.
ensureWorker()(queue.ts:137-158, the service's only??=promise memo) clears its memo on rejection so the next launch retries the idempotent registration — thefetchGithubJwkssuccess-only-cache pattern already inauth.ts. Today one transient 500 wedges every launch until restart (pm-dispatch(queue): ensureWorker caches its first failure forever — one transient Supabase 500 permanently wedges every session launch until restart #2663).
Phases — adopted origins, no parallel issues filed
- Phase 1 — boundary: pm-dispatch(sessions): directory is never validated — empty string defeats the repo default, and any absolute path lets an OIDC caller launch a session outside its own repo #2664 + pm-dispatch(auth): a JWT whose header decodes to non-object JSON (null/array/scalar) throws a raw TypeError — malformed credentials get HTTP 500, not 401 #2665. Independent. Gate for public exposure (see top).
- Phase 2 — commit point: pm-dispatch(sessions): a GitHub comment/label failure after the claim PATCH cancels a live session's queue task — the launched session keeps running untracked #2660, including the README stage/failure table as its truth-up. Independent in code.
- Phase 3 — retryable init: pm-dispatch(queue): ensureWorker caches its first failure forever — one transient Supabase 500 permanently wedges every session launch until restart #2663. Independent.
Each origin is a sub-issue of this issue, keeps its own queued triage packet, and carries an agent note recording this structure — triage must not re-file, re-scope, or split them.
Alternatives (recorded in the doc)
Fix-the-four-bugs with no stated model (improvisation already diverging inside one function — loses); per-caller directory rules (two auth models for one field; a leaked bearer becomes launch-anywhere — loses to uniform rule + a recorded extra-roots question); directory exactly equal to the primary (kills legitimate inside-tree dirs); reorder annotation before the claim (impossible — it announces the claim); TypeError→401 mapping at the handler (wrong layer; parse-time shape check closes the class, including the post-signature payload hole); backoff/timer retry for ensureWorker (call-driven retry suffices for an idempotent upsert).
Open questions (in the doc, not filed)
Extra roots allowlist if a real outside-tree worktree consumer appears; /v1/tasks inheriting the directory rule if it ever gains the field; annotation-failure visibility beyond warnings/notes; JWKS success-cache staleness on key rotation (pre-existing, noted only).
agent-note:v1 — filed by the repo design architect (dedicated code job, pass 19). Design doc and central register entry (files/potential-projects.md, status: proposed) accompany this issue.
Acceptance criteria
- This design epic is complete when its three adopted phases land via their origin issues — no parallel issues are filed for the phases:
- Phase 1 (boundary): pm-dispatch(sessions): directory is never validated — empty string defeats the repo default, and any absolute path lets an OIDC caller launch a session outside its own repo #2664 + pm-dispatch(auth): a JWT whose header decodes to non-object JSON (null/array/scalar) throws a raw TypeError — malformed credentials get HTTP 500, not 401 #2665 — caller input validated to declared shape/scope before any side effect; non-object JWT header/payload decodings return 401, not a raw-TypeError 500;
directoryresolves by the stated rule uniformly for all caller types. - Phase 2 (commit point): pm-dispatch(sessions): a GitHub comment/label failure after the claim PATCH cancels a live session's queue task — the launched session keeps running untracked #2660 — post-claim annotation failures (lane ledger, issue comment, labels) degrade to response warnings + a task note; they never cancel a live session's task, never 502, and never stamp a live session as failed; the README flow section carries the stage/failure table.
- Phase 3 (retryable init): pm-dispatch(queue): ensureWorker caches its first failure forever — one transient Supabase 500 permanently wedges every session launch until restart #2663 —
ensureWorker()memoizes success only; a rejected registration clears the memo so the next launch retries.
- Phase 1 (boundary): pm-dispatch(sessions): directory is never validated — empty string defeats the repo default, and any absolute path lets an OIDC caller launch a session outside its own repo #2664 + pm-dispatch(auth): a JWT whose header decodes to non-object JSON (null/array/scalar) throws a raw TypeError — malformed credentials get HTTP 500, not 401 #2665 — caller input validated to declared shape/scope before any side effect; non-object JWT header/payload decodings return 401, not a raw-TypeError 500;
- Phase 1 lands before the
dispatch.security.cabtunnel hostname is added (the timing call flagged in the design). - Each origin issue keeps its own queued triage packet and records this structure in an agent note; triage does not re-file, re-scope, or split them.
Activity
dezren39 commentedon Oct 30, 2026
agent-note:v1 — design-architect origination (pass 19, 2026-10-30). This design adopts its four origin issues as phases — #2664 + #2665 (Phase 1, boundary), #2660 (Phase 2, commit point), #2663 (Phase 3, retryable init) — linked as sub-issues; no parallel implementation issues were filed and no new queue packets enqueued (each origin already carries a queued triage packet, and each carries an agent note recording its phase and the decisions above). Register entry added to
files/potential-projects.md(status: proposed); design doc atfiles/designs/code-pm-dispatch-launch-pipeline.mdin the keep-the-four-repos-healthy goal workspace. Audit base: origin/main 4b48bf1 (dispatch tree unchanged from 732ca26, the commit the origins cite).dezren39 commentedon Oct 30, 2026
Intake review
Fleet monitor intake pass (2026-10-30).
queue/queuedtriage packets this run. Nothing to close and nothing new to file; per the design's own instruction, triage must not re-file or split the phases.1 remaining item
developing-today commentedon Nov 3, 2026
agent-note:v1
state: reviewed (coverage loop-back)
Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 7347 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
Found via: coverage loop-back 2026-11-03
dezren39 commentedon Nov 3, 2026
Program tending — completion proposal (2026-11-03, via #1352)
All 4 sub-issues of this epic are now closed: #2660, #2663, #2664, #2665 (verified this run via the REST sub_issues read, cross-checked in a separate call; this thread's last note recorded only #2660's closure).
Proposing closure of the epic: its phases are complete per its own structure. If follow-on work remains, file it as its own issue rather than keeping this epic open.
dezren39 commentedon Nov 4, 2026
agent-note:v1
state: picked_up
Agent: repo-worker-code (OpenChamber scheduled lane)
Session / ID: ses_e5e5bb716caffeYpbml7XIQd71 / queue task 7347 (triage)
Found via: repo-health-code-pilot atomic claim, lane c8b9355e-319a-4640-8096-7ad19480d151
Task: Revalidate this design epic at current main, keeping the pm-dispatch JWKS/OIDC outage separate from the design record; verify the four phase issues and their completion state.
Expectation: Close with evidence if every phase is complete; otherwise name the exact remaining blocker or route the needed follow-up without duplicating existing work.
Links: queue task 7347; #2668
dezren39 commentedon Nov 4, 2026
agent-note:v1
state: done
Agent: repo-worker-code (OpenChamber scheduled lane)
Session / ID: ses_e5e5bb716caffeYpbml7XIQd71 / queue task 7347
Found via: repo-health-code-pilot atomic claim, lane c8b9355e-319a-4640-8096-7ad19480d151
Task: Triage the pm-dispatch launch-pipeline design epic, separately from the operational dispatch.security.cab JWKS/OIDC outage.
Result: All four design phases are complete: #2660 is closed with PR #4231 merged (9456d01); #2663 is closed with PR #2908 merged (f0a7b0a); #2664 is closed with PR #3507 merged (2115038); and #2665 is closed with PR #4241 merged (115fe4f). Current main is b519288. The existing completion proposal is therefore supported; no remaining phase blocker was found.
Caveat: PR #3483 remains open on closed issue #2664; it is assigned to dezren39 and is the stale duplicate of the merged #3507 work. It was left untouched under the Drew-owned PR restriction and does not block this design epic's completion.
Expectation: Met — this design epic is complete and is being closed; the operational outage remains separate and is not addressed by this closure.
Links: PR #4231 #4231 · PR #2908 #2908 · PR #3507 #3507 · PR #4241 #4241 · untouched PR #3483 #3483