# Request and result guide

Every example below is an executable fixture from `tests/cases.json`. The same cases run as MoonBit white-box tests on every backend (`fixtures_wbtest.mbt`) and through the Node CLI (`scripts/check.mjs`). Assertions cover the listed result fields; additional output fields are documented by the API and other fixtures. Error cases intentionally reject the request. This file is generated by `scripts/fixtures.py`.

## initial ready wave

Tasks are reported in execution order. The first wave contains every task whose dependencies are satisfied, up to the concurrency bound; each task carries the reason for its status.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  }
}
```

Expected result fields:

```json
{
  "ready/0/id": "a",
  "tasks/0/reason": "no cache entry",
  "tasks/1/status": "waiting",
  "tasks/1/reason": "waiting for: a",
  "terminal": false,
  "summary/ready": [
    "a"
  ],
  "summary/waiting": [
    "b"
  ]
}
```

## failure blocks downstream

rerun lists the failed and blocked tasks that the next run will attempt.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  },
  "completed": {
    "a": "failed"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "failed",
  "tasks/1/status": "blocked",
  "tasks/1/reason": "dependency failed: a",
  "terminal": true,
  "success": false,
  "summary/rerun": [
    "a",
    "b"
  ]
}
```

## missing input fails

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    }
  ]
}
```

Expected result fields:

```json
{
  "tasks/0/status": "failed",
  "tasks/0/reason": "missing input: in.txt",
  "tasks/1/status": "blocked",
  "terminal": true
}
```

## topological cycle

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "deps": [
        "b"
      ]
    },
    {
      "id": "b",
      "command": [
        "x"
      ],
      "deps": [
        "a"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## output conflict

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "outputs": [
        "same"
      ]
    },
    {
      "id": "b",
      "command": [
        "x"
      ],
      "outputs": [
        "same"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## case-insensitive output conflict

Output ownership is compared case-insensitively so the graph is valid on case-insensitive file systems too.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "outputs": [
        "Out.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "x"
      ],
      "outputs": [
        "out.txt"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## nested output conflict

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "outputs": [
        "dist"
      ]
    },
    {
      "id": "b",
      "command": [
        "x"
      ],
      "outputs": [
        "dist/index.html"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## unsafe inputs

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "inputs": [
        "../x"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## producer dependency required

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "outputs": [
        "a"
      ]
    },
    {
      "id": "b",
      "command": [
        "x"
      ],
      "inputs": [
        "a"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## unknown dependency

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "deps": [
        "ghost"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## empty command

```json
{
  "tasks": [
    {
      "id": "a",
      "command": []
    }
  ]
}
```

Expected: nonzero exit with an input error.

## invalid timeout

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "timeout_ms": 0
    }
  ]
}
```

Expected: nonzero exit with an input error.

## invalid retries

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "retries": 11
    }
  ]
}
```

Expected: nonzero exit with an input error.

## invalid concurrency

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ]
    }
  ],
  "concurrency": 0
}
```

Expected: nonzero exit with an input error.

## concurrency bound

```json
{
  "tasks": [
    {
      "id": "0",
      "command": [
        "x"
      ]
    },
    {
      "id": "1",
      "command": [
        "x"
      ]
    },
    {
      "id": "2",
      "command": [
        "x"
      ]
    },
    {
      "id": "3",
      "command": [
        "x"
      ]
    }
  ],
  "concurrency": 2
}
```

Expected result fields:

```json
{
  "tasks/2/status": "waiting",
  "tasks/2/reason": "concurrency limit reached",
  "ready/1/id": "1"
}
```

## deterministic order by id

Independent tasks are ordered by id in UTF-16 code unit order, so plans are reproducible.

```json
{
  "tasks": [
    {
      "id": "b",
      "command": [
        "x"
      ]
    },
    {
      "id": "10",
      "command": [
        "x"
      ]
    },
    {
      "id": "a",
      "command": [
        "x"
      ]
    },
    {
      "id": "9",
      "command": [
        "x"
      ]
    }
  ]
}
```

Expected result fields:

```json
{
  "tasks/0/id": "10",
  "tasks/1/id": "9",
  "tasks/2/id": "a",
  "tasks/3/id": "b"
}
```

## cache hit verified

A cache entry is reused only when the fingerprint matches and every declared output still has the recorded digest.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "cached",
  "tasks/0/reason": "fingerprint and outputs verified",
  "terminal": true,
  "success": true,
  "cache/a/outputs/a.txt": "ha"
}
```

## input change invalidates

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h2",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "fingerprint changed: inputs"
}
```

## command change invalidates

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "changed"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "fingerprint changed: command"
}
```

## environment change invalidates

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  },
  "environment": {
    "MODE": "release"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "fingerprint changed: environment"
}
```

## executable digest invalidates

When the host supplies a digest for a task's program it becomes part of the fingerprint, so a changed executable invalidates the task; hosts that do not hash executables keep their fingerprints.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  },
  "executables": {
    "echo": "e1"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "fingerprint changed: executable",
  "tasks/0/fingerprint/executable": "e1"
}
```

## executable digest round trip

Fingerprints are compared structurally: the cached entry here lists its fields in a different order.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "digest": "bff1a7e5518e2896c7832b606da21a51fecbf6ba5703c424b9fb18f87b62bec3",
        "dependencies": {},
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "executable": "e1",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "version": 2
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  },
  "executables": {
    "echo": "e1"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "cached"
}
```

## executable digests must be strings

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "executables": {
    "echo": 1
  }
}
```

Expected: nonzero exit with an input error.

## forged cache outputs rejected

A cache entry whose recorded output digest no longer matches the file is not trusted; the task is executed again.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "tampered"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "outputs changed"
}
```

## outputs missing reruns

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "outputs missing"
}
```

## no outputs always runs

```json
{
  "tasks": [
    {
      "id": "lint",
      "command": [
        "echo",
        "lint"
      ],
      "inputs": [
        "in.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "no outputs declared"
}
```

## transitive invalidation

Dependents cannot be judged until the changed producer has run again; their fingerprints include the producer's new fingerprint.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b.txt"
      ],
      "outputs": [
        "c.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h2",
    "a.txt": "ha",
    "b.txt": "hb",
    "c.txt": "hc"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/0/reason": "fingerprint changed: inputs",
  "tasks/1/status": "waiting",
  "tasks/1/reason": "waiting for: a",
  "tasks/2/reason": "waiting for: b",
  "terminal": false
}
```

## fingerprint propagation rebuilds dependents of a rebuilt producer

By default a dependent's fingerprint includes its producers' fingerprints, so a producer that ran again invalidates it even when the produced bytes are identical.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h2",
    "a.txt": "ha",
    "b.txt": "hb"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    },
    "b": {
      "fingerprint": {
        "version": 2,
        "command": "e70e2ac27552caa5e241886591183f7eda2a64080cf12a89e2bd91cd88103d10",
        "inputs": "cead3f8c7613b27789643b8871c4c1c4aa3452c59a7d1e98162ff1ba59b4e188",
        "outputs": "bcf50bedc636f90fe982fdae45c361415942ec6b912bc74392b808075f6bcfaf",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {
          "a": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
        },
        "digest": "0c509e213e951359247e01ac373d31743e322514b65a9c8f39f0c773a893c611"
      },
      "outputs": {
        "b.txt": "hb"
      }
    }
  },
  "completed": {
    "a": "success"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "success",
  "tasks/1/status": "ready",
  "tasks/1/reason": "fingerprint changed: dependencies"
}
```

## content propagation stops at identical outputs

With propagate: content, dependencies only order execution; invalidation follows the declared files, so a producer that reproduced identical outputs leaves its dependents cached (early cutoff).

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h2",
    "a.txt": "ha",
    "b.txt": "hb"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    },
    "b": {
      "fingerprint": {
        "version": 2,
        "command": "e70e2ac27552caa5e241886591183f7eda2a64080cf12a89e2bd91cd88103d10",
        "inputs": "cead3f8c7613b27789643b8871c4c1c4aa3452c59a7d1e98162ff1ba59b4e188",
        "outputs": "bcf50bedc636f90fe982fdae45c361415942ec6b912bc74392b808075f6bcfaf",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "93b018d2a750b6faca5920a8ca021183d580ad9388a6b323a34f64eb4a281370"
      },
      "outputs": {
        "b.txt": "hb"
      }
    }
  },
  "completed": {
    "a": "success"
  },
  "propagate": "content"
}
```

Expected result fields:

```json
{
  "tasks/0/status": "success",
  "tasks/1/status": "cached",
  "tasks/1/reason": "fingerprint and outputs verified"
}
```

## unknown propagation mode

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "propagate": "magic"
}
```

Expected: nonzero exit with an input error.

## paths with spaces

```json
{
  "tasks": [
    {
      "id": "render",
      "command": [
        "render"
      ],
      "inputs": [
        "my input.txt"
      ],
      "outputs": [
        "out dir/result.txt"
      ]
    }
  ],
  "hashes": {
    "my input.txt": "h"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "ready/0/outputs/0": "out dir/result.txt"
}
```

## summary groups

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h",
    "a.txt": "ha"
  },
  "cache": {
    "a": {
      "fingerprint": {
        "version": 2,
        "command": "8660d5d12a2de6a799d4fbf13dd577991ccb2a84779b1c0502863eb599a186e4",
        "inputs": "c0a33e57511793ecf260f2ed34e72f52afccd5094212d877752c04d8d6e762e9",
        "outputs": "3169ba9e29527082ac0a22ae78e46eca5efc1f58ff2b47d42c36100b4d9252a8",
        "environment": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
        "dependencies": {},
        "digest": "f74b7952fadcd18580b578ed9b34d3705ed4f833f7e37fbd69eba79d313c2e9c"
      },
      "outputs": {
        "a.txt": "ha"
      }
    }
  },
  "completed": {
    "b": "failed"
  }
}
```

Expected result fields:

```json
{
  "summary/cached": [
    "a"
  ],
  "summary/failed": [
    "b"
  ],
  "summary/blocked": [
    "c"
  ],
  "summary/rerun": [
    "b",
    "c"
  ],
  "terminal": true,
  "success": false
}
```

## targets select a dependency closure

targets limits the run to the named tasks and their transitive dependencies; everything else is skipped and never scheduled.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a.txt"
      ],
      "outputs": [
        "b.txt"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b.txt"
      ],
      "outputs": [
        "c.txt"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  },
  "targets": [
    "b"
  ]
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/1/status": "waiting",
  "tasks/2/status": "skipped",
  "tasks/2/reason": "not required by targets",
  "tasks/3/status": "skipped",
  "summary/skipped": [
    "c",
    "d"
  ],
  "terminal": false
}
```

## skipped tasks do not block completion

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  },
  "targets": [
    "d"
  ],
  "completed": {
    "d": "success"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "skipped",
  "tasks/1/status": "success",
  "terminal": true,
  "success": true
}
```

## unknown target

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "targets": [
    "ghost"
  ]
}
```

Expected: nonzero exit with an input error.

## deep chain waits link by link

```json
{
  "tasks": [
    {
      "id": "t0",
      "command": [
        "build",
        "0"
      ],
      "inputs": [
        "src.txt"
      ],
      "outputs": [
        "o0"
      ]
    },
    {
      "id": "t1",
      "command": [
        "build",
        "1"
      ],
      "deps": [
        "t0"
      ],
      "inputs": [
        "o0"
      ],
      "outputs": [
        "o1"
      ]
    },
    {
      "id": "t2",
      "command": [
        "build",
        "2"
      ],
      "deps": [
        "t1"
      ],
      "inputs": [
        "o1"
      ],
      "outputs": [
        "o2"
      ]
    },
    {
      "id": "t3",
      "command": [
        "build",
        "3"
      ],
      "deps": [
        "t2"
      ],
      "inputs": [
        "o2"
      ],
      "outputs": [
        "o3"
      ]
    },
    {
      "id": "t4",
      "command": [
        "build",
        "4"
      ],
      "deps": [
        "t3"
      ],
      "inputs": [
        "o3"
      ],
      "outputs": [
        "o4"
      ]
    },
    {
      "id": "t5",
      "command": [
        "build",
        "5"
      ],
      "deps": [
        "t4"
      ],
      "inputs": [
        "o4"
      ],
      "outputs": [
        "o5"
      ]
    },
    {
      "id": "t6",
      "command": [
        "build",
        "6"
      ],
      "deps": [
        "t5"
      ],
      "inputs": [
        "o5"
      ],
      "outputs": [
        "o6"
      ]
    },
    {
      "id": "t7",
      "command": [
        "build",
        "7"
      ],
      "deps": [
        "t6"
      ],
      "inputs": [
        "o6"
      ],
      "outputs": [
        "o7"
      ]
    }
  ],
  "hashes": {
    "src.txt": "h"
  }
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "tasks/1/reason": "waiting for: t0",
  "tasks/7/reason": "waiting for: t6",
  "summary/waiting": [
    "t1",
    "t2",
    "t3",
    "t4",
    "t5",
    "t6",
    "t7"
  ],
  "terminal": false
}
```

## wide fan-in respects concurrency

Producers fill the wave up to the bound; the consumer waits for every producer and names them.

```json
{
  "tasks": [
    {
      "id": "p0",
      "command": [
        "make",
        "0"
      ],
      "outputs": [
        "p0.out"
      ]
    },
    {
      "id": "p1",
      "command": [
        "make",
        "1"
      ],
      "outputs": [
        "p1.out"
      ]
    },
    {
      "id": "p2",
      "command": [
        "make",
        "2"
      ],
      "outputs": [
        "p2.out"
      ]
    },
    {
      "id": "p3",
      "command": [
        "make",
        "3"
      ],
      "outputs": [
        "p3.out"
      ]
    },
    {
      "id": "p4",
      "command": [
        "make",
        "4"
      ],
      "outputs": [
        "p4.out"
      ]
    },
    {
      "id": "p5",
      "command": [
        "make",
        "5"
      ],
      "outputs": [
        "p5.out"
      ]
    },
    {
      "id": "join",
      "command": [
        "join"
      ],
      "deps": [
        "p0",
        "p1",
        "p2",
        "p3",
        "p4",
        "p5"
      ],
      "inputs": [
        "p0.out",
        "p1.out",
        "p2.out",
        "p3.out",
        "p4.out",
        "p5.out"
      ],
      "outputs": [
        "joined"
      ]
    }
  ],
  "concurrency": 4
}
```

Expected result fields:

```json
{
  "summary/ready": [
    "p0",
    "p1",
    "p2",
    "p3"
  ],
  "tasks/4/reason": "concurrency limit reached",
  "tasks/6/id": "join",
  "tasks/6/reason": "waiting for: p0, p1, p2, p3, p4, p5"
}
```

## timeout upper bound accepted

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "timeout_ms": 86400000,
      "retries": 10
    }
  ],
  "concurrency": 256
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "ready/0/timeout_ms": 86400000,
  "ready/0/retries": 10
}
```

## grace period is passed to the executor

grace_ms (0-60000, default 0) is how long the executor waits after SIGTERM before SIGKILL when a task times out; a timed-out task fails regardless.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "timeout_ms": 1000,
      "grace_ms": 500
    }
  ]
}
```

Expected result fields:

```json
{
  "ready/0/grace_ms": 500,
  "ready/0/timeout_ms": 1000
}
```

## grace above one minute rejected

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "grace_ms": 60001
    }
  ]
}
```

Expected: nonzero exit with an input error.

## timeout above one day rejected

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "timeout_ms": 86400001
    }
  ]
}
```

Expected: nonzero exit with an input error.

## fractional timeout rejected

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "timeout_ms": 1.5
    }
  ]
}
```

Expected: nonzero exit with an input error.

## concurrency above limit rejected

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ]
    }
  ],
  "concurrency": 257
}
```

Expected: nonzero exit with an input error.

## unicode ids order by code unit

Identifiers may be any non-empty string; independent tasks are ordered by UTF-16 code units, so ASCII sorts before accented Latin before CJK.

```json
{
  "tasks": [
    {
      "id": "b",
      "command": [
        "x"
      ]
    },
    {
      "id": "构建",
      "command": [
        "x"
      ]
    },
    {
      "id": "a",
      "command": [
        "x"
      ]
    },
    {
      "id": "Ärger",
      "command": [
        "x"
      ]
    }
  ]
}
```

Expected result fields:

```json
{
  "tasks/0/id": "a",
  "tasks/1/id": "b",
  "tasks/2/id": "Ärger",
  "tasks/3/id": "构建"
}
```

## nested output directories

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "outputs": [
        "dist/a/b.txt",
        "dist/a/c.txt"
      ]
    }
  ]
}
```

Expected result fields:

```json
{
  "tasks/0/status": "ready",
  "ready/0/outputs": [
    "dist/a/b.txt",
    "dist/a/c.txt"
  ]
}
```

## self dependency rejected

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ],
      "deps": [
        "a"
      ]
    }
  ]
}
```

Expected: nonzero exit with an input error.

## environment values must be strings

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ]
    }
  ],
  "environment": {
    "N": 1
  }
}
```

Expected: nonzero exit with an input error.

## hash values must be strings

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": 1
  }
}
```

Expected: nonzero exit with an input error.

## invalid completed status

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "x"
      ]
    }
  ],
  "completed": {
    "a": "done"
  }
}
```

Expected: nonzero exit with an input error.

## success requires verified outputs

An executor may only report success after the declared outputs exist; the planner refuses inconsistent reports.

```json
{
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "in.txt"
      ],
      "outputs": [
        "a.txt"
      ]
    }
  ],
  "hashes": {
    "in.txt": "h"
  },
  "completed": {
    "a": "success"
  }
}
```

Expected: nonzero exit with an input error.

## followup input impact closure

```json
{
  "operation": "impact",
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "input"
      ],
      "outputs": [
        "a"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a"
      ],
      "outputs": [
        "b"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "changed_inputs": [
    "input"
  ]
}
```

Expected result fields:

```json
{
  "affected/0/id": "a",
  "affected/1/id": "b",
  "affected/2/id": "c",
  "unaffected": [
    "d"
  ],
  "plan_only": true
}
```

## followup intermediate impact closure

```json
{
  "operation": "impact",
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "input"
      ],
      "outputs": [
        "a"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a"
      ],
      "outputs": [
        "b"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "changed_inputs": [
    "a"
  ]
}
```

Expected result fields:

```json
{
  "affected/0/id": "b",
  "affected/1/id": "c",
  "unaffected": [
    "a",
    "d"
  ]
}
```

## followup irrelevant change

```json
{
  "operation": "impact",
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "input"
      ],
      "outputs": [
        "a"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a"
      ],
      "outputs": [
        "b"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "changed_inputs": [
    "other"
  ]
}
```

Expected result fields:

```json
{
  "affected": []
}
```

## followup unsafe changed path

```json
{
  "operation": "impact",
  "tasks": [
    {
      "id": "a",
      "command": [
        "echo",
        "a"
      ],
      "inputs": [
        "input"
      ],
      "outputs": [
        "a"
      ]
    },
    {
      "id": "b",
      "command": [
        "echo",
        "b"
      ],
      "deps": [
        "a"
      ],
      "inputs": [
        "a"
      ],
      "outputs": [
        "b"
      ]
    },
    {
      "id": "c",
      "command": [
        "echo",
        "c"
      ],
      "deps": [
        "b"
      ],
      "inputs": [
        "b"
      ]
    },
    {
      "id": "d",
      "command": [
        "echo",
        "d"
      ]
    }
  ],
  "changed_inputs": [
    "../bad"
  ]
}
```

Expected: nonzero exit with an input error.

## Host adapter

```sh
node scripts/execute.mjs WORKFLOW.json WORKSPACE_DIR
```

The executor hashes declared files with SHA-256, asks the planner for the next ready wave, runs those commands as child processes (argv arrays, `shell=false`) with timeouts and retries, and repeats until the plan is terminal. It writes `.taskforge-cache.json` and `.taskforge-journal.json` into the workspace. Read the current boundaries before using this adapter.
