Capy-Bearer Documentation

11. Tasks and jobs

A task runs a Capy handler outside the current request. A job runs an operating-system command. Both return immediately with a handle that the caller can inspect later.

Read status data

Task status is a dval because fields can vary with the state:

function state_label(status : dval) string {
    -> string(status.state, {fallback: "missing"})
}

function RENDER(request : dval) {
    print(state_label({state: "queued"}), "\n")
}

Output

queued

Real task states are queued, running, succeeded, failed, canceled, or missing.

Define a task handler

A named task handler receives one request value:

function TASK:STORE(request : dval) {
    var props := request.props
    var key := string(props.key)
    if key == "" {
        trap()
    }
    // Store the application result under key.
}

function RENDER(request : dval) {
    print("task handler ready\n")
}

The task boundary copies props as BRRB. dval identity does not cross it. The worker does not receive the caller's request, session, connections, open handles, or memory state.

Submit a task

task returns an opaque string ID after Bearer admits the task:

function RENDER(request : dval) {
    var id := task("/doc/examples/task_worker.capy:STORE", {key: "report-42"})
    print(id, "\n")
}

The call traps if Bearer cannot admit the task. Use task_status(id) to poll. Use task_await(id, timeout_ms) to wait for a state:

function show_failure(id : string) {
    var status := task_await(id, 5000)
    if string(status.state) == "failed" {
        print(string(status.failure_code, {fallback: "unknown"}))
    }
}

function RENDER(request : dval) {
    print("failure helper ready\n")
}

A wait timeout does not cancel active work. task_cancel(id) requests cancellation, but a running task can finish first. Cancellation does not undo an external effect.

A successful task means that its handler finished. Task status does not carry an application result. Store results in application-owned persistent storage.

Run a command

The two-argument shell_exec form starts a bounded background operating-system job:

function RENDER(request : dval) {
    var job := shell_exec("printf ready", {background: true, timeout_ms: 1000})
    var status := job_await(job, 3000)
    print(string(status.state, {fallback: "missing"}), "\n")
}

Use job_status, job_await, job_result, and job_cancel with the returned handle. Always set a deliberate command timeout. Validate the final state before reading result data.

The shell interprets the command string. Do not build it from request data or other untrusted input.

Failure and retries

Bearer does not retry a failed task automatically. Submit a new task only when the application can repeat the operation safely. Use an application idempotency key for work that changes external state.

Do not wait for long work in a page request unless the response requires it. Treat task IDs and job handles as opaque values.

Keep task input small

Pass a dval that contains only stable identifiers and simple options. Do not pass a full request snapshot to a task. Request data can contain cookies, headers, and uploaded-file metadata that the task does not need.

Use a database row, file path, or application key when work needs large input. Validate that reference again inside the task. A task can run after the user request has ended.

Use a visible status page for long work. It should show queued, running, done, failed, and canceled states in user terms.