Skip to content

Add per-state concurrency caps #72

@VincentShipsIt

Description

@VincentShipsIt

PRD: per-state-concurrency-caps

Executive Summary

Adopt Symphony's per-state concurrency cap. Front-matter map `agent.max_concurrent_agents_by_state` limits how many pipelines may run in each tracker state at once. Useful for capping expensive verify-loop pipelines while allowing more lightweight plan-phase work. Falls back to the global cap when no per-state value exists.

Implementation Checklist

  • WORKFLOW.md front matter accepts `agent.max_concurrent_agents_by_state: { state_name: int }`
  • Loader normalizes state keys to lowercase and rejects non-positive values silently
  • Dispatcher computes `runningInState[s]` from the `running` map keyed by current effective state
  • Per-state slot check: `runningInState[state] < perStateCap[state]` when key present, otherwise the global slot check applies
  • Both global and per-state caps must pass for dispatch

Problem Statement

Today's dispatcher only enforces a single global concurrency cap. We cannot say "only 1 verify at a time but up to 5 plan-phase pipelines". When verify is slow and expensive, this matters: starving the queue or oversubscribing the verify lane both waste resources.

Goals

  • New front-matter key `agent.max_concurrent_agents_by_state` (map of state → positive int).
  • State key normalized to lowercase for lookup.
  • Dispatcher checks per-state slot before global slot when key present.
  • Invalid entries (non-positive, non-numeric) ignored without crash.
  • Global cap remains the upper bound when no per-state value exists.

Non-Goals

User Stories

  • As a solo founder running shipcode under daemon mode, I want max 1 pipeline in verify at a time but up to 3 in plan, so my expensive verify lane does not flood the API.
    Acceptance:
    • With 5 candidate issues all in verify and per-state cap 1, only 1 pipeline dispatches.
    • With per-state cap absent, global cap of 3 applies.
    • Negative values in the map are ignored without affecting other entries.

Functional Requirements

  1. WORKFLOW.md front matter accepts `agent.max_concurrent_agents_by_state: { state_name: int }`.
  2. Loader normalizes state keys to lowercase and rejects non-positive values silently.
  3. Dispatcher computes `runningInState[s]` from the `running` map keyed by current effective state.
  4. Per-state slot check: `runningInState[state] < perStateCap[state]` when key present, otherwise the global slot check applies.
  5. Both global and per-state caps must pass for dispatch.

Non-Functional Requirements

  • Slot accounting must be O(running size) per dispatch tick.
  • State key matching must be case-insensitive.

Success Criteria

  • Unit test: per-state cap 1 with 3 candidates in same state → only 1 dispatches.
  • Mixed test: per-state cap 1 in verify, 5 candidates split 3 verify + 2 plan with global cap 4 → dispatches 1 verify + 2 plan.
  • Invalid entries ignored test: `{ verify: 0, plan: -1, review: 2 }` → only `review: 2` honored.
  • Global cap fallback test: no per-state key for plan; global cap 3 applied.

Out of Scope

  • Per-state caps for stall thresholds or retry budgets.
  • Dynamic cap derivation from system load.
  • UI surface for editing caps without WORKFLOW.md.

Dependencies

Verification Plan

  • tests: new `packages/pipeline/src/issue-group-scheduler.test.ts` cases for per-state slot accounting, fallback to global, invalid entry handling.
  • manual: WORKFLOW.md with `{ verify: 1 }`; verify only one verify pipeline runs concurrently when multiple are eligible.

Risks & Open Questions

  • "State" definition: shipcode does not have explicit GH state for plan/review/execute/verify — we map phase to state for this purpose. Confirm the dispatcher knows the active phase per running thread.
  • Edge case: pipeline transitions phase mid-tick — slot accounting must observe the new phase consistently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request
    No fields configured for Feature.

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions