This repository was archived by the owner on Oct 13, 2026. It is now read-only.
Repository navigation
feat: planner-dispatch mode and dispatcher-mode comparison (#146) - #152
Merged
Merged
Conversation
`submit --dispatcher planner` runs a job with the planner thread as its dispatcher. No dispatcher thread starts: each decision point (first dispatch, worker result, answer, refused completion, envelope repair) is posted into the planner thread, keyed by the action seq with its message id saved before the post, so a restarted controller watches the same post. A reply records planner_dispatch_reply with wait_secs; no reply blocks as planner_dispatch_pending, which recover continues. The planner's protocol carries #147's router-defect duty. A planner_question waits for the user (`answer` plus `recover`). Cancel never interrupts the planner thread. `luna` stays the default and its protocol text is unchanged. scripts/compare_dispatch.py reads jobs.db and Agent Observer's ledger read-only and reports, per mode, success rate, time to first worker and to a reviewed PR, and dispatch tokens by counter semantics. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What:
submit --dispatcher plannerruns a Prism job with the planner thread as its dispatcher, andscripts/compare_dispatch.pycompares both modes from the runner and Agent Observer ledgers;lunastays the default.Why: Dispatch took 6-11 minutes before the first worker on most jobs, and nobody has measured whether the planner routing the job itself is faster or cheaper (#146); the planner then also owns #147's router-defect duty.
So what: Review and merge; to expose the mode in Prism, Chromeria's
prism_submitneeds adispatcherargument (described below, not in this PR); the user then runs at least five matched jobs per mode and picks the default from the comparison.Closes #146 (mode and comparison method; the comparison on real jobs and the default stay with the user). Refs #147 (planner-dispatch half).
Change
submit --dispatcher luna|planner(CLI andcore.submit, defaultluna). Newjobs.dispatchercolumn; rows from before it read asluna. The mode is part of the idempotent payload, is in thesubmittedevent and the submit ack.runner/controller.py): no dispatcher thread starts. Every decision point a Luna dispatcher gets (first dispatch, worker result, planner answer, refused completion, envelope repair) is posted into the planner thread, headedPRISM DISPATCH DECISION for job <id> (<key>), and the planner's reply carries the envelope. Each decision is keyed by the actionseq; its message id is saved before the post (planner_dispatch_posted), so a restarted controller watches the same post instead of asking twice. The reply recordsplanner_dispatch_replywithwait_secs. No reply blocks asplanner_dispatch_pending(recover-owned): recover re-watches the same post, or posts afresh after an errored or interrupted turn. Workers and reviewers become children of the planner thread. Cancel never interrupts the planner thread (it is not saved as a job thread). The Prism Dispatcher list is not required in this mode.planner_questionfrom the planner is for the user: stored, not posted back to the planner, and the job blocks asplanner_question_pendinguntilanswerplusrecover. The runner's own escalation question still reaches the planner as today.runner/adapters.py:PLANNER_ACTION_PROTOCOLshares the worker duties, theROUTER DEFECTSparagraph (Dispatcher reports router defects as Issues (planner does it in planner-dispatch mode) #147) and the reply shapes with Luna's; the role and the packet-mistake route differ (the planner corrects it in the instructions, or cancels and resubmits).LUNA_ACTION_PROTOCOLis byte-identical to main.scripts/compare_dispatch.pyand RUNNER.md "Comparing dispatcher modes": per mode, success rate, median seconds to first worker (submittedto the firstimpl_*t3_threadevent), to a reviewed PR (firstreview_verdictapprove), and dispatch plus question tokens from Observerresponsesby counter semantics (Luna: the dispatcher thread's session; planner: the planner session inside each decision window).Pending and gaps
prism_submitcannot pass the mode. Needed: an optionaldispatcher: "luna" | "planner"inPrismSubmitInput(apps/server/src/mcp/toolkits/prism/tools.ts) and...(input.dispatcher ? ["--dispatcher", input.dispatcher] : [])insubmitArgs(apps/server/src/mcp/toolkits/prism/handlers.ts). Until then the mode is reachable through the CLI only.prism_answerrunsanswerwithoutrecover;answersets the job back torunning, but a job with no live controller still needsrecover(true in both modes today).jobs.dispatchernor event payloads, so mode grouping and decision windows come fromjobs.db;task show --task router:<id>reports costunknownfor T3-path jobs, so cost per mode stays unknown until Observer attributes those sessions.prism_submitturn still runs is the same path planner questions use today; not exercised live here. The comparison itself (five matched jobs per mode) is behavioral and after release.Proof
tests/test_issue146.pyagainst the base runner fails at import (module 'runner.adapters' has no attribute 'PLANNER_ACTION_PROTOCOL'); the new behavior did not exist.e2ab553:python3 scripts/test.py:Ran 402 tests ... OK;python3 -m compileall -q runner scripts testsclean;versionctl release-check:release candidate OK: v0.44.0 at e2ab553977b6f37d05cbdbfb27147ffff1de23ab.tests/test_issue146.py(17 tests): default and planner submit, invalid mode, cross-mode resubmission conflict, legacy rows, CLI flag and ack; a planner-mode job runs tosucceededwith one worker thread under the planner thread, no dispatcher thread, exactly two decision posts carrying the planner protocol; Luna mode still starts its dispatcher thread; no Dispatcher list needed; no-reply block then adoption of the same post; one envelope repair; planner question waits for the user; cancel does not interrupt the planner thread; protocol pins for the router-defect duty and packet mistakes; the comparison script's timings, windowed tokens and summary on synthetic ledgers.scripts/compare_dispatch.py --since 2026-09-25on the real ledgers: 30 terminal Luna jobs, 10 succeeded, median 439 s to first worker, median 1695 s to a reviewed PR (the Luna baseline).Elon record
jobs.dbandobserver.db; checked thattask show --task router:<id>reports cost unknown and that Chromeria'ssubmitArgshas no dispatcher argument.🤖 Generated with Claude Code