Skip to content

First-class task dependency graph (blocks / depends-on) — design issue, marinating #104

@atomikpanda

Description

@atomikpanda

Status

Marinating. This issue captures a structural gap identified during 20k-ft review, not a ready-to-build feature. The design questions below need answers before a plan or implementation.

Problem

Tasks in mship are islands. There is no first-class representation that "task B depends on task A" — the dependency lives in the user's head and expresses itself through ad-hoc coordination:

  • Shared lib refactor in task A → consumer update in task B: user manually checks A's PR status, manually rebases/merges before starting B.
  • API change in task A → consumer update in task B: same pattern.
  • Schema migration → app code → docs: three-step chain the user sequences by memory.
  • Spec work in task A produces a decision that task B depends on: no linkage beyond journal prose.

This is the same class of "state hell" mship is built to prevent, but at the cross-task level instead of the cross-repo level. Worktree isolation solved the intra-task version; the inter-task version is still manual.

Why this is a v1 substrate primitive, not a v2 concern

Multi-agent orchestration (v2) is the obvious case that needs a dependency graph, but the primitive is also useful today for a single agent or human doing sequential coupled work. Shipping it as v1 substrate:

  • Removes a present-value pain point (user-held coordination graph).
  • Lays the graph that v2 multi-agent coordination will sit on top of, without speculative design.
  • Extends the mship positioning (opinionated substrate for agentic SWE) — any methodology needs this; it's domain-neutral within the agentic-SWE assumption.

Crucially, dependencies is a new primitive (writing state) — unlike readability gaps (which downstream tools can fill by consuming mship journal --json / #101) or rollback gaps (git-level tools exist), task dependencies cannot be filled from above. Either mship models the graph or no one does.

How it composes with existing primitives

If the graph exists as state, each existing command becomes more powerful without redesign:

  • mship spawn --depends-on <slug>[,<slug>...] — declare the dependency at creation time. The task's state records upstream edges.
  • mship status — shows the DAG (or subgraph for the current task), not just a flat list. "What's blocking me" becomes one command.
  • mship finish — refuses to finish a task whose upstream is not yet merged, or offers a --cascade mode. Mirrors the reconcile UX.
  • mship dispatch — includes "task X is blocked on task Y — pick up Y first" in the dispatch payload, turning the graph into actionable context for the agent.
  • mship reconcile — already does cross-task drift detection; extends naturally to detecting dependency staleness (upstream changed, downstream needs rebase).
  • mship close — warns (or refuses) if there are active downstream tasks depending on the one being closed.

Design questions to resolve during brainstorm

These are the decisions that need answers. Not attempting to answer them in this issue.

  1. Dependency target: does a task depend on another task slug (internal graph only) or on a PR URL (lets you depend on work done outside mship too)? Probably both — slug for internal edges, PR URL for external ones. What's the unification?

  2. Hard vs. soft edges: is the default a hard block (finish refuses when upstream unmerged) or a soft advisory (warn but allow)? Per-edge override? Per-task default?

  3. Fan-in semantics: if task C depends on A and B, what counts as "ready"? All merged? Any merged? User-configurable?

  4. Cascade behavior: when an upstream task is abandoned or closed without merging, do downstream tasks (a) cascade-close, (b) prompt for decision, (c) dangle with a warning? Probably (b) interactively and (c) in non-TTY mode.

  5. Dispatch autonomy: does mship dispatch merely surface blocked-ness, or does it let the agent switch tasks autonomously to an upstream when the current one is blocked? This is the point where substrate decisions start to shade into methodology — may belong in the skill rather than the CLI.

  6. Cycle detection: do we refuse to create cycles at spawn time? (Probably yes — cycles are always a bug.)

  7. Migration: how do existing tasks adopt the new field? Presumably dependency-less by default; mship task edit --add-depends-on <slug> to retrofit.

  8. Cross-repo edges: are dependencies purely task-to-task, or can they be repo-scoped (this task's api-gateway changes depend on that task's shared changes)? Almost certainly task-to-task in v1 — don't add axes until needed.

Scope cuts (likely)

  • No critical-path / scheduling logic. mship records the graph; downstream tools can compute critical path if they want it.
  • No parallel execution orchestration. That's v2 multi-agent territory. v1 is "representation + one-agent-respects-the-graph."
  • No automatic rebasing of downstream branches when upstream moves. Detect staleness (via reconcile), don't auto-rebase.
  • No GitHub-level synchronization. mship's graph is mship's; if GitHub linked issues or "Depends on #N" comments exist, that's a downstream integration concern.

Related / differentiation

Next step

When ready to advance this past marinating: full brainstorming → spec → plan flow (per working-with-mothership skill), not inline design. This issue is the seed for that session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestquestionFurther information is requested

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions