Batch Scheduler (the parallel planner's sensor)
One pass that answers the parallel question the single-next sensor cannot:
which open issues can run together right now, in which lane, in which
order — emitted as the fixed batch-plan@2 machine envelope. The script is
the deterministic producer: scripts/batch-scheduler.mjs reads the live work
graph and prints the document. This skill runs the script, reads the JSON, and
interprets it against the plan grammar — it never
assembles the envelope by hand.
Turn contract
Load and verify the canonical Turn contract before ending every turn; the read-only profile substitutes boxes 1–5 (the one sanctioned side effect is the planner's own untracked state file, declared here). Skill-specific additions:
✓ The script was RUN — `bun scripts/batch-scheduler.mjs [--json-only] [--root
<dir>] [--issues-file <json>] [--no-state] [--remote <url>]` (node is the
fallback when bun is absent); the envelope is the script's stdout, never
assembled by the model
✓ Every degraded dimension is named in the envelope `degraded[]` and reported
to the human — a degraded plan is never presented as complete truth
✓ Every finding (cycle, orphaned-soft-dep, prose-native-mismatch) is reported
with its issue numbers — findings are never silently dropped, and never
auto-fixed by this skill
✓ `worktree` and `command` are REPORTED, never executed — the planner plans,
it does not launch (no worktree creation, no session spawn, ever)
✓ The human-readable summary is printed (unless `--json-only`), then the
machine envelope is the ABSOLUTE last output
✓ Nothing was edited, committed, pushed, or created except the script's own
state file (`.agentic-workflow/batches.json`, untracked) — read-only
everywhere else
When to use
- Before launching parallel work: "what can run together now, and on which
surfaces?" — replaces the hand-computed batch plan that died every session.
- When starting a work session with several open issues: batch 1 is the
runnable-now set; the per-issue
command starts its lane.
- Not for sequential next-step recommendation (
workflow-status owns
that), for quality judgment (review-change/audit-pr), or for launching
(you or the #295 panel own that).
Step 0 — Discover the project (always first)
Per the agent guide's Workflow conventions + documentation map. The
script reads what it needs itself — the location index (docs/ROADMAP.md, via
the shared index-document.mjs parsers), the open issues and their native
blocked-by relations from the forge, the prose ## Dependencies rows and
## Where/## In scope sections from issue bodies, and the unit docs under
docs/fix/ + docs/features/. Read them yourself only to interpret a field
the script emitted.
Process
- Run the script. Defaults read the current repository and the live forge and
persist the plan's claimed lanes to
.agentic-workflow/batches.json
(untracked, durable) so the next run recognizes what is already claimed or
launched. Flags:
--json-only — envelope alone (drivers, tests).
--issues-file <json> — inject a fetched issue cache instead of calling
the forge; a cache without native relations fetches them live (one
GraphQL call) or degrades declared.
--no-state — pure read; writes nothing. It cannot be combined with
--mark-launched (that pair is a usage error, exit 2).
--remote <url> — name the forge explicitly when origin is not a
GitHub URL (a scratch clone); it names it for the issue list and the
relations call alike.
--mark-launched <n> — record that you launched issue N's lane outside
the sensor (the only manual write).
- Read the envelope against the plan grammar:
batches[] in order (batch 1 = runnable now), each lane one surface with
its size hint and its issue rows (number, worktree, command,
reason.kind + detail, claimed, triaged), blocked[] with the open
blockers that gate each issue, launched[] with the evidence that marked
them, new_issues[] and closed_since[] with the lifecycle delta since
the previous plan, findings[] with the unresolvable spots, degraded[]
with anything that could not be read, and summary as the totals.
- Print the human summary: one line per lane in batch 1 (surface, size hint,
issues with their commands), then the blocked issues with their blockers,
then the untriaged issues (
triaged: false — scope undetermined;
co-scheduling provisional until their lane triage runs), then every
finding, then every degradation. Launch nothing yourself —
hand the per-issue command to the user or the panel, and let them decide
how many lanes to run (the size hints inform that choice; they are facts,
not a lane-count policy).
Guardrails
- Findings are findings.
cycle, orphaned-soft-dep and
prose-native-mismatch are work-graph defects the work-graph audit can
feed; report them with numbers and detail, never fix them here (the prose
rows belong to triage, per the #442 contract). A target that is open and in
flight (its unit doc is on disk) is a state, not a defect: blocked[]
names it as in flight and no finding is emitted for it.
- Untriaged is provisional, not launchable-as-usual. A row with
reason.kind: untriaged (or triaged: false) has no lane triage yet —
report it as co-scheduling-provisional in the summary, never as a plain
ready lane. soft-deps-open rows start before their declared inputs exist:
name the open deps and their batches when reporting.
- The prose parser fails closed. A non-standard
## Dependencies table
produces a declared degradation naming the issue and zero prose
dependencies — never false findings, never a guess (issue #460).
- Native relations are authoritative. A prose row declared
hard without
a native blocked-by is reported as a mismatch; it never gates on its own.
- No decision model. The sensor is fully deterministic with no model
configured; #444's optional signal layer may order groups later, never
replace this computation.
- Offline is declared. A failed forge call degrades (
degraded[]) and the
plan still prints — from the cache and the docs that did read.
Portability (agents other than Claude Code)
- no slash menu → open this
SKILL.md and follow it literally in a fresh
conversation; the skill is one script run plus interpretation.
- no bun → run the script with
node (the shebang's fallback).
- no forge access → run with
--issues-file carrying a cached issue export;
the envelope then degrades on the relation source only when the cache lacks
them.
Relationship to other skills
workflow-status is the sequential sibling: what is startable now and the
single recommended next command. This sensor answers the parallel question
over the same substrate; the two never write and never conflict.
unit-lane is the consumer of each row's command
(/unit-lane --from-issue <N>); it launches units, this sensor only names
them.
- #295's panel is the planned renderer: it reads this envelope as data.
- Schema owner:
orchestration-envelope's package
(agentic-workflow/batch-plan), validated by the script before printing.
Done when
- The script ran; every claim in the report comes from its envelope.
- Batch 1's lanes were printed with their issues' launch commands; blocked,
findings, and degraded dimensions were all reported, none silently dropped.
- Nothing was launched, spawned, or created except the script's own state
file; the state write was reported.
- The envelope is the last output (unless
--json-only).
→ Next: the per-issue command values of batch 1 — the user (or the #295
panel) picks how many lanes to launch from the size hints
· one issue only → run its /unit-lane --from-issue <N> directly
· a blocked or finding row first → /triage-issue <N> resolves the relation
defect before launching anything it gates
1---2name: batch-scheduler3user-invocable: true4version: 4.10.05argument-hint: "[--json-only] [--issues-file <json>] [--no-state] [--remote <url>]"6description: >7 Deterministic batch planner: read the live work graph and emit8 parallel-execution batches (order-respecting, collision-free) as the fixed9 `batch-plan@2` machine envelope — which issues can run together now, which10 are blocked and by what, which surface each lane occupies, what changed11 since the previous plan (new/closed issues, untriaged rows, open cross-lane12 soft deps). Triggers: "batch scheduler", "what can run in parallel", "plan13 the batches", "batch plan".14metadata:15 source: https://skillmd.com/skills/gtrabanco/batch-scheduler16---1718# Batch Scheduler (the parallel planner's sensor)1920One pass that answers the parallel question the single-next sensor cannot:21**which open issues can run together right now, in which lane, in which22order** — emitted as the fixed `batch-plan@2` machine envelope. The **script is23the deterministic producer**: `scripts/batch-scheduler.mjs` reads the live work24graph and prints the document. This skill runs the script, reads the JSON, and25interprets it against [the plan grammar](references/PLAN.md) — it never26assembles the envelope by hand.2728## Turn contract2930Load and verify the **canonical** [Turn contract](../orchestration-envelope/references/TURN_CONTRACT.md) before ending every turn; the **read-only profile** substitutes boxes 1–5 (the one sanctioned side effect is the planner's own untracked state file, declared here). Skill-specific additions:3132```33✓ The script was RUN — `bun scripts/batch-scheduler.mjs [--json-only] [--root34 <dir>] [--issues-file <json>] [--no-state] [--remote <url>]` (node is the35 fallback when bun is absent); the envelope is the script's stdout, never36 assembled by the model37✓ Every degraded dimension is named in the envelope `degraded[]` and reported38 to the human — a degraded plan is never presented as complete truth39✓ Every finding (cycle, orphaned-soft-dep, prose-native-mismatch) is reported40 with its issue numbers — findings are never silently dropped, and never41 auto-fixed by this skill42✓ `worktree` and `command` are REPORTED, never executed — the planner plans,43 it does not launch (no worktree creation, no session spawn, ever)44✓ The human-readable summary is printed (unless `--json-only`), then the45 machine envelope is the ABSOLUTE last output46✓ Nothing was edited, committed, pushed, or created except the script's own47 state file (`.agentic-workflow/batches.json`, untracked) — read-only48 everywhere else49```5051## When to use5253- Before launching parallel work: "what can run together now, and on which54 surfaces?" — replaces the hand-computed batch plan that died every session.55- When starting a work session with several open issues: batch 1 is the56 runnable-now set; the per-issue `command` starts its lane.57- **Not** for sequential next-step recommendation (`workflow-status` owns58 that), for quality judgment (`review-change`/`audit-pr`), or for launching59 (you or the #295 panel own that).6061## Step 0 — Discover the project (always first)6263Per the agent guide's **Workflow conventions** + **documentation map**. The64script reads what it needs itself — the location index (`docs/ROADMAP.md`, via65the shared `index-document.mjs` parsers), the open issues and their native66`blocked-by` relations from the forge, the prose `## Dependencies` rows and67`## Where`/`## In scope` sections from issue bodies, and the unit docs under68`docs/fix/` + `docs/features/`. Read them yourself only to interpret a field69the script emitted.7071## Process72731. Run the script. Defaults read the current repository and the live forge and74 persist the plan's claimed lanes to `.agentic-workflow/batches.json`75 (untracked, durable) so the next run recognizes what is already claimed or76 launched. Flags:77 - `--json-only` — envelope alone (drivers, tests).78 - `--issues-file <json>` — inject a fetched issue cache instead of calling79 the forge; a cache without native relations fetches them live (one80 GraphQL call) or degrades declared.81 - `--no-state` — pure read; writes nothing. It cannot be combined with82 `--mark-launched` (that pair is a usage error, exit 2).83 - `--remote <url>` — name the forge explicitly when `origin` is not a84 GitHub URL (a scratch clone); it names it for the issue list and the85 relations call alike.86 - `--mark-launched <n>` — record that you launched issue N's lane outside87 the sensor (the only manual write).882. Read the envelope against [the plan grammar](references/PLAN.md):89 `batches[]` in order (batch 1 = runnable now), each lane one surface with90 its size hint and its issue rows (`number`, `worktree`, `command`,91 `reason.kind` + `detail`, `claimed`, `triaged`), `blocked[]` with the open92 blockers that gate each issue, `launched[]` with the evidence that marked93 them, `new_issues[]` and `closed_since[]` with the lifecycle delta since94 the previous plan, `findings[]` with the unresolvable spots, `degraded[]`95 with anything that could not be read, and `summary` as the totals.963. Print the human summary: one line per lane in batch 1 (surface, size hint,97 issues with their commands), then the blocked issues with their blockers,98 then the untriaged issues (`triaged: false` — scope undetermined;99 co-scheduling provisional until their lane triage runs), then every100 finding, then every degradation. **Launch nothing yourself** —101 hand the per-issue `command` to the user or the panel, and let them decide102 how many lanes to run (the size hints inform that choice; they are facts,103 not a lane-count policy).104105## Guardrails106107- **Findings are findings.** `cycle`, `orphaned-soft-dep` and108 `prose-native-mismatch` are work-graph defects the work-graph audit can109 feed; report them with numbers and detail, never fix them here (the prose110 rows belong to triage, per the #442 contract). A target that is open and in111 flight (its unit doc is on disk) is a **state**, not a defect: `blocked[]`112 names it as in flight and no finding is emitted for it.113- **Untriaged is provisional, not launchable-as-usual.** A row with114 `reason.kind: untriaged` (or `triaged: false`) has no lane triage yet —115 report it as co-scheduling-provisional in the summary, never as a plain116 ready lane. `soft-deps-open` rows start before their declared inputs exist:117 name the open deps and their batches when reporting.118- **The prose parser fails closed.** A non-standard `## Dependencies` table119 produces a declared degradation naming the issue and zero prose120 dependencies — never false findings, never a guess (issue #460).121- **Native relations are authoritative.** A prose row declared `hard` without122 a native `blocked-by` is reported as a mismatch; it never gates on its own.123- **No decision model.** The sensor is fully deterministic with no model124 configured; #444's optional signal layer may order groups later, never125 replace this computation.126- **Offline is declared.** A failed forge call degrades (`degraded[]`) and the127 plan still prints — from the cache and the docs that did read.128129## Portability (agents other than Claude Code)130131- *no slash menu* → open this `SKILL.md` and follow it literally in a fresh132 conversation; the skill is one script run plus interpretation.133- *no bun* → run the script with `node` (the shebang's fallback).134- *no forge access* → run with `--issues-file` carrying a cached issue export;135 the envelope then degrades on the relation source only when the cache lacks136 them.137138## Relationship to other skills139140- `workflow-status` is the sequential sibling: what is startable now and the141 single recommended next command. This sensor answers the parallel question142 over the same substrate; the two never write and never conflict.143- `unit-lane` is the consumer of each row's `command`144 (`/unit-lane --from-issue <N>`); it launches units, this sensor only names145 them.146- #295's panel is the planned renderer: it reads this envelope as data.147- Schema owner: `orchestration-envelope`'s package148 (`agentic-workflow/batch-plan`), validated by the script before printing.149150## Done when151152- The script ran; every claim in the report comes from its envelope.153- Batch 1's lanes were printed with their issues' launch commands; blocked,154 findings, and degraded dimensions were all reported, none silently dropped.155- Nothing was launched, spawned, or created except the script's own state156 file; the state write was reported.157- The envelope is the last output (unless `--json-only`).158159→ Next: the per-issue `command` values of batch 1 — the user (or the #295160panel) picks how many lanes to launch from the size hints161 · one issue only → run its `/unit-lane --from-issue <N>` directly162 · a blocked or finding row first → `/triage-issue <N>` resolves the relation163 defect before launching anything it gates