task_service

One-shot script with a timeout. Runs to completion (or kills on timeout); does not stay alive. Sibling to process but modeled as a condition = "completed" upstream rather than "healthy".

Use this for migrations, fixture loaders, batch-prep steps that need to finish before app services start.

Manifest knobs

[services.migrate]
type = "task_service"
command = "python migrate.py"
cwd = "."
timeout_seconds = 300                 # default; SIGTERM then SIGKILL
depends_on = [
    { service = "postgres", condition = "healthy" },
]

[services.api]
type = "process"
command = "python app.py"
depends_on = [
    { service = "migrate", condition = "completed" },
]

Lifecycle

  • Spawns under the same env / lockfile rules as process.
  • On clean exit (exit 0): the orchestrator writes a done marker into the per-service data dir; subsequent project opens skip the re-run unless unibench start --fresh or --seed.
  • On non-zero exit or timeout: marked Failed. Dependents that declared condition = "completed" get blocked (Failed, by the blocked-dependent rule).

Common gotchas

  • Idempotency is yours. The orchestrator skips re-running a successful task across project restarts. If your migration script isn't safely re-runnable when --fresh strips the done marker, you'll see the same problem you'd see in production.
  • Timeout vs slow CI. Default 300s is generous for local dev, tight for first-run model downloads. Bump per-task when you hit it.