Skip to content

/map-efficient: parallel-wave execution shipped but dark — sequential by default at every gate #303

Description

@azalio

Summary

The full parallel-execution machinery from #284 (per-subtask worktree isolation, wave planner, merge_wave_worktrees coordinator) is built and tested, but it is opt-in at every layer. For a typical run, /map-efficient executes strictly sequentially even when the plan is perfectly parallelizable (a wave with ≥2 independent, disjoint-file subtasks). The feature is effectively dark.

mapify_version: 3.20.0

Observed

A decomposition that yields a wave of ≥2 independent subtasks still runs one Actor at a time. Parallelism never engages on the default path.

Root cause is a chain of three closed gates, each defaulting to sequential, plus two latent causes:

Gate 1 — default loop is the sequential walker; wave-loop is opt-in.

  • Main loop drives off get_next_step (single-subtask walker): src/mapify_cli/templates_src/skills/map-efficient/SKILL.md.jinja:135,409,446,464
  • Explicit: "the skill defaults to the sequential walker for simplicity. Switch to the wave-loop when (a) waves have ≥2 subtasks AND (b) … disjoint files"SKILL.md.jinja:217-225
  • So even when set_waves produced a ≥2 wave, the default path never reads it back via get_wave_step as a parallel batch.

Gate 2 — concurrent Actor dispatch is hard-coupled to worktree.isolation, which is OFF.

  • Default: worktree.isolation"false"map_step_runner.py.jinja:15325
  • When off, create_subtask_worktree returns {"status":"disabled","reason":"worktree.isolation is off"}map_step_runner.py.jinja:15420
  • Concurrent dispatch requires mode:"parallel" AND isolation enabled — efficient-reference.md.jinja:572-573
  • This coupling is correct in principle: Claude Code gives subagents context isolation but NOT filesystem isolation — two Actors writing the same working tree concurrently corrupt it. So parallelism is structurally gated on the opt-in worktree feature.

Gate 3 — policy text repeatedly biases toward sequential.

  • parallel_tool_policy: guarded_wave_only (SKILL.md.jinja:23), "Keep execution sequential by default" (:27), "Sequential subtask execution is default" (:35), "Default to sequential execution … or explicit user-requested parallel execution" (:210).

Latent A — decomposer over-serialization. Parallelism is derived post-hoc from dependencies via Kahn (dependency_graph.py::compute_waves). Any false "logical-ordering" dependency collapses waves to size 1 → mode always "sequential". Guardrails exist (task-decomposer.md "Minimize Dependencies for Parallelism") but it's an LLM judgment, not a guarantee.

Latent B — no explicit multi-Task() dispatch mechanism. Reference says "dispatch the Actors concurrently (separate Task agents)" (efficient-reference.md.jinja:574) but never states the actual mechanism: emit one Task(actor) tool_use per subtask in a single assistant message. Claude Code has no special syntax for this — concurrency is purely the model emitting multiple tool calls in one turn. After reading "sequential by default" five times, the operator model won't.

Expected

When a wave legitimately contains ≥2 independent, disjoint-file subtasks, /map-efficient should engage parallel execution without the operator manually flipping config + switching dispatchers.

Suggested levers (each can be a slice)

  1. Auto-switch to the wave-loop when set_waves yields any multi-element wave (currently opt-in "for simplicity") — opens Gate 1.
  2. Reconsider the worktree.isolation default (or auto-enable it when a parallel wave is about to dispatch) — opens Gate 2. Needs a fallback when not in a git repo.
  3. Add an explicit SKILL instruction + example: "emit one Task(actor) per wave subtask in one message" — closes Latent B.
  4. Optionally surface a one-line diagnostic when a parallelizable wave is detected but executed sequentially (so the dark path is observable, not silent).

Notes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions