Skip to content

ADR-310: Concurrent task scheduler, concurrent-orchestrate skill, and /implement multi-task dispatch - #81

Merged
jodavis merged 8 commits into
feature/ADR-296-concurrencyfrom
dev/claude/ADR-310
Jul 23, 2026
Merged

ADR-310: Concurrent task scheduler, concurrent-orchestrate skill, and /implement multi-task dispatch#81
jodavis merged 8 commits into
feature/ADR-296-concurrencyfrom
dev/claude/ADR-310

Conversation

@jodavis-claude

Copy link
Copy Markdown
Collaborator

Work item

ADR-310: Implement the concurrent scheduler (concurrent_schedule.py), the extended /implement argument parser, and the new concurrent-orchestrate skill — together these let a human queue up multiple dependency-ordered task-work-items (an inclusive "up to" target or an explicit list) and have them run as parallel workflow-orchestrate pipelines, respecting a repo-wide concurrency cap, without any single-task pipeline internals changing.

Changes

  • plugins/dev-team/skills/workflow-orchestrate/scripts/concurrent_schedule.py (new) — compute_next_batch(target) -> dict computes the "up to" target's transitive dependency closure (or takes an explicit list as-is, no expansion), validates an explicit list's dependencies upfront (raising ConcurrentScheduleError naming the offending task/dependency), persists a per-target data file at ~/.dev-team/<repo-slug>/concurrent-<target-slug>.json, refreshes each task's status via the existing Task readiness checker (dependency_status/is_task_eligible), enforces the concurrency cap repo-wide (scanning every concurrent-*.json file under the state dir), and returns "complete" / "blocked" (naming blocked_tasks) / "waiting". main() is a thin argparse CLI wrapper (--up-to <key> / --list <key1,key2,...>, mutually exclusive).
  • plugins/dev-team/skills/workflow-orchestrate/scripts/test_concurrent_schedule.py (new) — 17 unit tests covering closure computation, list-mode no-expansion, upfront list validation (reject/accept), "complete", "blocked" (immediate and deferred-until-active-spawns-reach-terminal), repo-wide cap enforcement (including across multiple target files), max-parallel-tasks default/override, and CLI wrapper happy/error paths.
  • plugins/dev-team/skills/concurrent-orchestrate/SKILL.md (new) — sibling Orchestrator skill to workflow-orchestrate: a thin loop that invokes concurrent_schedule.py, pre-populates a spawned task's base_branch via use-context-file only when it isn't None, spawns each newly-eligible task as a backgrounded, isolated workflow-orchestrate run (isolation: "worktree"), records the returned worktree path/branch back into that task's context file, re-invokes the script every 60 seconds or on spawn completion, and stops/reports on "complete" or "blocked".
  • plugins/dev-team/commands/implement.md (modified) — step 1 now recognizes, in order: the case-insensitive up to <key> phrase (dispatches to concurrent-orchestrate with --target-mode up-to), two-or-more comma/"and"-separated keys (dispatches with --target-mode list), or a single key (unchanged dispatch to workflow-orchestrate).
  • plugins/dev-team/skills/get-project-configuration/assets/default-config.yaml (modified) — added concurrency: max-parallel-tasks: 3.
  • plugins/dev-team/skills/get-project-configuration/SKILL.md (modified) — documented the new concurrency.max-parallel-tasks key, following the existing testing.test-file-patterns style.

Design decisions

  • Implemented concurrent_schedule.py directly via TDD (test-first, confirmed red, then green) rather than the heavier implement-tdd sub-agent trio, given the component's moderate size.
  • No E2E/Gherkin scenarios were added — no BDD harness exists anywhere in this repo, consistent with the existing workflow-orchestrate/ensure-working-branch precedents. The two Given/When/Then exit criteria are covered by concurrent_schedule.py's own unit tests instead.
  • CLI shape for concurrent_schedule.py: argparse with a mutually-exclusive --up-to/--list group; the spec file is located via dev_team.find_spec_file(), never passed on the CLI.
  • Re-invocation interval for concurrent-orchestrate chosen as 60 seconds (not specified in the spec).
  • _max_parallel_tasks explicitly converts the configured value to int with a fallback to the default, since merge_config.py's minimal YAML parser returns scalars as strings.
  • The upstream isolation:"worktree" parent-HEAD-leak risk (Claude Code issue #55708) is accepted as residual, out-of-scope risk, consistent with the spec's own risk write-up.

Testing completed

  • plugins/dev-team/skills/workflow-orchestrate/scripts/test_concurrent_schedule.py: 17 new tests, all passing.
  • Full workflow-orchestrate/scripts suite (182 tests) and get-project-configuration/scripts suite (81 tests) both still pass unmodified.

…TDD (closure computation for "up to" targets, explicit-list upfront validation, repo-wide concurrency-cap enforcement across multiple target files, waiting/complete/blocked status computation)
…current_schedule.py to isolated workflow-orchestrate runs)
…rget and an explicit multi-task list, dispatching either to concurrent-orchestrate
…schema (default 3), documented alongside testing.test-file-patterns
@github-actions

Copy link
Copy Markdown

build-and-test: Python test results

Status: ✅ Passed

Test log

@jodavis-claude jodavis-claude left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: ADR-310 — Concurrent scheduler, /implement argument parser, concurrent-orchestrate

Reviewed against _spec_ConcurrentDevelopment.md's exit criteria (lines 826-851), CONTRIBUTING.md, and the existing task_readiness.py/task_dependencies.py/dev_team.py conventions this PR builds on.

Exit criteria — all satisfied:

  • /implement recognizes a single key (unchanged, byte-for-byte identical step-2 body), the case-insensitive up to <key> phrase, and an explicit comma/"and"-separated list of two-or-more keys; either multi-item form dispatches to concurrent-orchestrate.
  • concurrent_schedule.py's compute_next_batch(target) computes the "up to" closure or takes the explicit list as-is, persists to ~/.dev-team/<repo-slug>/concurrent-<target-slug>.json.
  • Explicit-list upfront validation (_validate_explicit_list) rejects a dependency neither in the list nor already done, naming the offending task/dependency, before anything is persisted or spawned.
  • Concurrency cap is enforced repo-wide (_repo_wide_active_spawn_count scans every concurrent-*.json under the state dir, excluding terminal spawns) — confirmed correct via the multi-target-file tests.
  • get-project-configuration gains concurrency.max-parallel-tasks (default 3), documented consistently with the existing testing.test-file-patterns style; _max_parallel_tasks defensively converts the merged config's string scalar to int, with a fallback — a real bug this test suite would have caught if the conversion were missing.
  • "complete"/"blocked"/"waiting" semantics match the spec precisely, including the two blocked sub-cases (immediate vs. deferred-until-active-spawns-reach-terminal) — both explicitly tested.
  • concurrent-orchestrate/SKILL.md is a thin, spec-faithful spawn loop: pre-populates base_branch only when not None, spawns with isolation: "worktree", records worktree path/branch, re-invokes every 60s or on spawn completion, stops on "blocked"/"complete".

Testing — 17 new unit tests in test_concurrent_schedule.py, following the established test_<scenario>_<expected_result> / AAA / one-Test<Behavior>-class-per-scenario-group conventions (mirrors test_task_readiness.py closely). Ran the full suite locally: all 17 new tests pass, the full workflow-orchestrate/scripts suite (182 tests) and get-project-configuration/scripts suite (81 tests) both still pass, and scripts/validate.sh is clean.

Documentation — no _doc_*.md update needed for this task; the spec itself defers _doc_ConcurrentDevelopment.md authorship to "the final documentation task once implementation completes" (not this task).

One minor, non-blocking fault-tolerance note left inline. Also noting for the record (style only, not blocking): concurrent-orchestrate/SKILL.md's Agent(subagent_type: "claude", isolation: "worktree", ...) example uses colon-style key syntax, where the sibling workflow-orchestrate/SKILL.md uses subagent_type="claude" (equals-sign). Purely cosmetic, doesn't affect the instructions' meaning.

Overall: solid, well-tested implementation that matches the spec's interfaces and design decisions closely, including sensible resolutions to all five "Known ambiguities" the brief flagged. Approved.

Comment thread plugins/dev-team/skills/workflow-orchestrate/scripts/concurrent_schedule.py Outdated
@jodavis-claude
jodavis-claude marked this pull request as ready for review July 22, 2026 04:57
@jodavis-claude
jodavis-claude requested a review from jodavis July 22, 2026 04:57
Comment thread plugins/dev-team/skills/concurrent-orchestrate/SKILL.md Outdated
Comment thread plugins/dev-team/commands/implement.md
Comment thread plugins/dev-team/skills/get-project-configuration/SKILL.md Outdated
…n scripts/ folder

Addresses PR #81 review comment: concurrent-orchestrate shouldn't reach into
workflow-orchestrate's scripts/ folder for its own script. Moved
concurrent_schedule.py and test_concurrent_schedule.py into
plugins/dev-team/skills/concurrent-orchestrate/scripts/, updated the script's
internal sys.path insertion to point at the sibling workflow-orchestrate/scripts/
directory for its shared-module imports (dev_team, get_context_path,
task_dependencies, task_readiness), and updated concurrent-orchestrate/SKILL.md's
invocation path to <skill-dir>/scripts/concurrent_schedule.py.
Addresses PR #81 review comment: without an explicit stop, the agent could
fall through from step 2 (single-task workflow-orchestrate dispatch) into
step 3 (concurrent-orchestrate) and try to invoke a list of tasks it doesn't
have. Mirrors the existing 'Then stop.' already used in step 1's no-match
branch.
…ent-orchestrate

Addresses PR #81 review comment: the general-purpose get-project-configuration
skill doc shouldn't make every agent that reads project configuration think
about task concurrency. Removed the concurrency.max-parallel-tasks subsection
from get-project-configuration/SKILL.md and added it to concurrent-orchestrate/
SKILL.md instead, next to concurrent_schedule.py where the key is actually
consumed. The schema/default itself stays unchanged in
get-project-configuration/assets/default-config.yaml, since merge_config.py's
structural merge still needs it there.
… its docstring

Addresses non-blocking self-review comment: main()'s docstring promises a
clean 'Error: ...' message on 'any other failure', but the except tuple didn't
cover json.JSONDecodeError (a corrupted concurrent-*.json data file) or
subprocess.TimeoutExpired (a merge_config.py subprocess timeout inside
_max_parallel_tasks) — either would still exit non-zero but with a raw
traceback. Added both to the except tuple. Added two new failing-first unit
tests (test_main_corrupted_data_file_prints_clean_error_and_exits_nonzero,
test_main_merge_config_subprocess_timeout_prints_clean_error_and_exits_nonzero)
that exercise main() directly (not via subprocess) with monkeypatched
sys.argv, confirmed red before the fix, green after.
@jodavis
jodavis merged commit c229722 into feature/ADR-296-concurrency Jul 23, 2026
3 checks passed
@jodavis
jodavis deleted the dev/claude/ADR-310 branch July 23, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants