from __future__ import annotations

def execute(payload: dict) -> dict:
    machines = [{**item, "assigned_workload": 0.0, "tasks": []} for item in payload.get("machines", [])]
    if not machines or any(float(m["capacity"]) <= 0 for m in machines):
        raise ValueError("positive-capacity machines are required")
    for task in sorted(payload.get("tasks", []), key=lambda item: float(item["workload"]), reverse=True):
        machine = min(machines, key=lambda item: item["assigned_workload"] / float(item["capacity"]))
        machine["tasks"].append(task["id"])
        machine["assigned_workload"] += float(task["workload"])
    allocations = [{"machine_id": m["id"], "tasks": m["tasks"], "estimated_time": round(m["assigned_workload"] / float(m["capacity"]), 3)} for m in machines]
    return {"allocations": allocations, "makespan": max(item["estimated_time"] for item in allocations)}
