Enhanced Agent Routing And Cadence Design
May 7, 2026 · View on GitHub
Summary
Revise .github/workflows/weights.json from a two-step scheduled policy of "pick one workflow, then let that workflow choose any action agent" to a single scheduled routing policy that selects workflow + agent together.
The revised design keeps the current repo split intact:
- scheduled action routing is separate from follow-up routing
- manual
agentoverrides remain available to humans - scheduled selection becomes a concrete workflow/agent pair chosen by the scheduler
- the policy names actual repo agents consistently:
copilot,codex,qwen,gemini, andmini-max
The routing model stays flat and auditable. Scheduled workflows declare task type, base weight, and complexity. Scheduled agents declare their own weight, cadence, task-type eligibility, and optional complexity bounds.
Only three scheduled task categories exist:
code: normal scheduled code, lint, style, documentation, clarity, and maintenance workflowsmerging: PR merge-conflict repair workflowsregressions: PR test-regression repair workflows
Do not add tags, capabilities, lane allowlists, or per-workflow agent maps unless the routing policy grows beyond these three categories.
Final Config Shape
Use one authoritative schema in .github/workflows/weights.json:
agents: scheduled action agents used only by the scheduler and manual blank-agent action dispatch fallbackagentPools.followUps: non-scheduled follow-up pools for automerge, regression comments, conflict-repair follow-ups, and similar PR-specific retry flowsworkflows: scheduled task workflow weights and routing metadata
Scheduled action routing does not keep agentPools.actions in the final schema. The old action-pool path is removed rather than preserved in parallel.
Sample .github/workflows/weights.json:
{
"agents": [
{
"name": "copilot",
"weight": 1.0,
"category": ["code", "merging", "regressions"]
},
{
"name": "codex",
"weight": 1.0,
"cadenceTicks": 4,
"category": ["code"],
"minComplexity": 3
},
{
"name": "qwen",
"weight": 0.0,
"cadenceTicks": 2,
"category": ["code"],
"maxComplexity": 2
},
{
"name": "gemini",
"weight": 0.0,
"cadenceTicks": 4,
"category": ["code"],
"maxComplexity": 2
},
{
"name": "mini-max",
"weight": 0.5,
"cadenceTicks": 4,
"category": ["code"],
"maxComplexity": 2
}
],
"agentPools": {
"followUps": {
"default": "copilot",
"agents": [
{"weight": 0, "name": "codex"},
{"weight": 1, "name": "copilot"},
{"weight": 0, "name": "gemini"}
]
}
},
"workflows": [
{
"name": "agent-02-resolve-merge-conflicts",
"weight": 1.0,
"category": "merging",
"complexity": 3
},
{
"name": "agent-04-style",
"weight": 0.2,
"category": "code",
"complexity": 1
},
{
"name": "agent-18-clarity",
"weight": 0.25,
"category": "code",
"complexity": 2
},
{
"name": "agent-23-lint",
"weight": 0.33,
"category": "code",
"complexity": 3
},
{
"name": "agent-41-test-failure",
"weight": 0.5,
"category": "regressions",
"complexity": 3
}
]
}
Field semantics:
agents[*].name: concrete value passed to workflowagentinputsagents[*].weight: scheduled routing weight for the agent; non-positive weights disable scheduled selection without invalidating the fileagents[*].cadenceTicks: optional positive integer count of scheduler ticks between due evaluations for that agent; defaults to1when omittedagents[*].category: non-empty list of allowed scheduled task categoriesagents[*].minComplexity: optional inclusive lower complexity boundagents[*].maxComplexity: optional inclusive upper complexity boundworkflows[*].name: scheduled workflow name without the.ymlsuffixworkflows[*].weight: base task-selection weightworkflows[*].category: one ofcode,merging, orregressionsworkflows[*].complexity: required integer routing-complexity score in the inclusive range1..3
Complexity is mandatory on every scheduled workflow in the final schema. Do not rely on implicit defaults.
Complexity represents routing risk and expected judgment, not runtime duration. Use only three levels:
1: low-complexity routine and recoverable work2: normal medium-complexity work3: highest-complexity scheduled work
Merge repair, regression repair, broad refactors, and high-judgment maintenance tasks should use 3 rather than expanding the scale further.
Routing Semantics
_scheduler.yml continues to run on its fixed 15-minute cron. That wake-up interval remains owned by the workflow file itself rather than duplicated in weights.json.
The scheduler loads weights.json, validates the final schema, and computes eligible workflow/agent pairs for the current tick.
An agent is due on a scheduler tick when:
currentTickNumber % (agent.cadenceTicks ?? 1) === 0
A workflow/agent pair is eligible when:
agent.weight > 0workflow.weight > 0- the agent is due on the current scheduler tick
workflow.categoryis included inagent.categoryworkflow.complexity >= agent.minComplexitywhenminComplexityis presentworkflow.complexity <= agent.maxComplexitywhenmaxComplexityis present
Candidate pair weight is:
workflow.weight * agent.weight
Selection remains deterministic and uses the same general run-number weighted cycle approach the repo already uses today:
- use
GITHUB_RUN_NUMBERas the selection input - build the candidate set only after cadence and eligibility filtering
- sort deterministically before building the weighted cycle
- select the candidate pair by deterministic weighted cycle position
The same config, candidate set, and GITHUB_RUN_NUMBER must always produce the same selected workflow/agent pair.
The scheduler dispatches exactly one eligible workflow/agent pair per run, preserving one shared scheduled dispatch surface while still allowing faster due evaluation for agents with shorter cadence.
If a scheduler tick has no eligible positive-weight workflow/agent pair, log and skip without failing the scheduler.
Manual and follow-up behavior:
- Manual
workflow_dispatch.inputs.agentremains an unrestricted human override. - Blank manual dispatch in
_agent-open-pr-and-ping.ymlmust still resolve through the scheduledagentspolicy source defined inweights.json. agentPools.followUpsremains the only policy source for automerge comments, regression comments, conflict-repair follow-ups, and similar PR-specific retry paths.- Scheduled restrictions apply only to automated scheduler selection and blank manual fallback selection, not to explicit human overrides.
Policy Defaults
Initial intended policy:
copilot: enabled forcode,merging, andregressions; omittingcadenceTickskeeps it on the default every-run cadencecodex: enabled for higher-complexitycodework on the standard hourly cadenceqwen: reserved for low-complexitycodework on a faster 30-minute cadence, initially disabled withweight: 0.0until rollout is readygemini: retained as a named repo agent, but disabled for scheduled routing initially withweight: 0.0mini-max: retained in scheduled routing as a low-complexitycodeoption rather than silently removed
Cadence defaults under the fixed 15-minute scheduler tick:
cadenceTicks: 4-> every 60 minutescadenceTicks: 2-> every 30 minutes- omitted
cadenceTicks-> every scheduler run
This document does not introduce a qwen-local scheduled identity. The repo’s existing local-Qwen identity is qwen, and the routing plan should continue to use that name unless a separate rename proposal is approved later.
Validation Rules
Scheduler parsing should fail clearly when the configuration is internally inconsistent:
- scheduled agent names must be unique
- workflow names must be unique
- scheduled agent and workflow weights must be finite numbers
- scheduled agent records must have
name,weight, and non-emptycategory - workflow records must have
name,weight,category, andcomplexity categoryvalues must be one ofcode,merging, orregressionscomplexity,minComplexity, andmaxComplexitymust be integers in the inclusive range1..3cadenceTicks, when present, must be an integercadenceTicks, when present, must be positiveminComplexitymust be less than or equal tomaxComplexitywhen both are present- follow-up pools must continue to satisfy the existing weighted-agent pool shape used by current non-scheduled workflows
- non-positive scheduled agent and workflow weights should be ignored during selection, not treated as validation failures
Validation should also include explicit policy guards for intentionally restricted scheduled agents:
qwenmust have no eligiblemergingorregressionsroute unless intentionally added latergeminimust have no eligiblemergingorregressionsroute unless intentionally added later- any future removal of
mini-maxfrom scheduled routing must be expressed as an explicit config change, not a silent omission from the document
Migration Impact
This document describes a real schema migration from the repo’s current scheduler shape, so the implementation notes must stay explicit about affected surfaces.
Conceptual files affected:
.github/workflows/_scheduler.yml.github/workflows/_agent-open-pr-and-ping.ymltest/agent-weight-routing.test.ts
Required migration changes:
- update
_scheduler.ymlto load scheduled agents from top-levelagents, enumerate eligible workflow/agent pairs, and dispatch the selected concreteagent - update
_agent-open-pr-and-ping.ymlso blank manual action dispatch selects from scheduledagentsinstead of the removedagentPools.actions - preserve
agentPools.followUpsbehavior in follow-up workflows such as automerge and conflict-repair flows - remove old scheduled action-pool routing logic rather than keeping parallel legacy paths
- keep manual explicit
agentoverrides intact for human-triggered runs
This plan intentionally preserves follow-up routing as a separate policy surface. It does not redesign non-scheduled follow-up flows beyond keeping them compatible with the updated weight file.
Test Plan
Update test/agent-weight-routing.test.ts and related workflow tests to assert:
- schema validation checks only the final field set described in this document
- the only valid scheduled task categories are
code,merging, andregressions - workflow
complexityis required on every scheduled workflow - workflow and agent complexity values are limited to
1..3 - non-positive weights disable candidates without invalidating the file
- scheduled routing remains separate from
agentPools.followUps - deterministic pair selection remains based on
GITHUB_RUN_NUMBER - candidate pair weights combine
workflow.weight * agent.weight - cadence filtering uses
cadenceTicks - complexity bounds filter eligible pairs before weighted selection
- task-category eligibility filters eligible pairs before weighted selection
- the scheduler dispatch payload includes the selected concrete
agent - manual explicit
agentoverride bypasses scheduled eligibility rules - blank manual dispatch still selects from the scheduled
agentspolicy source qwenhas no eligiblemergingorregressionsroute unless intentionally enabled latergeminihas no eligiblemergingorregressionsroute unless intentionally enabled later
Run after implementation:
- targeted root workflow tests
pnpm run build:tspnpm run lint:quiet
Assumptions And Defaults
- the current repo shape is the source of truth unless this document explicitly proposes a migration
qwenis the existing local-Qwen identity in the repo and remains the default name used by this planagentPools.followUpsstays as-is unless there is a strong reason to redesign non-scheduled routing too- the scheduler remains a fixed 15-minute tick
- deterministic weighted selection remains the default because current workflows and tests already rely on it
- "actions" in this document means scheduled task workflows such as
agent-23-lint, not GitHub marketplace actions