procman 0.23.2

A process supervisor with a dependency DAG and a typed .pman language
# Process Output

Every job and service receives a `PROCMAN_OUTPUT` environment variable pointing
to a per-process output file at `logs/procman/<name>.output`. Jobs (one-shot
processes) write data here; downstream jobs and services reference it with
`@job.KEY` expressions.

## Output File Format

The output file supports two formats:

**Simple key-value lines** — one per line, first `=` splits key from value:

```
DATABASE_URL=postgres://localhost:5432/mydb
API_KEY=secret123
```

**Heredoc blocks** for multi-line values:

```
CERT<<EOF
-----BEGIN CERTIFICATE-----
MIIBxTCCAWugAwIBAgIJALP...
-----END CERTIFICATE-----
EOF
```

The heredoc delimiter is arbitrary — `KEY<<DELIM` starts a block and a line
containing only `DELIM` ends it.

## Referencing Output

Reference another job's output with `@job.KEY` in `env` bindings. Values flow
into shell via environment variables — procman never interpolates inside shell
strings.

~~~
job migrate {
  run """
    ./run-migrations
    echo "DATABASE_URL=postgres://localhost:5432/mydb" > $PROCMAN_OUTPUT
  """
}

service api {
  env DB_URL = @migrate.DATABASE_URL

  wait {
    after @migrate
  }

  run "api-server --db $DB_URL"
}
~~~

The sequence:

1. `migrate` starts and runs migrations.
2. `migrate` writes `DATABASE_URL=postgres://...` to its `$PROCMAN_OUTPUT` file.
3. `migrate` exits with code 0 — procman marks it as complete.
4. `api`'s `after @migrate` condition is satisfied.
5. Procman resolves `@migrate.DATABASE_URL` by reading `migrate`'s output file.
6. `api` starts with `DB_URL` set in its environment.

## Resolution

Output resolution happens **at spawn time**, after all `wait` conditions for the
job are satisfied. The resolver:

1. Reads the referenced job's output file (`logs/procman/<job>.output`).
2. Parses it into a key-value map.
3. Substitutes each `@job.KEY` reference with the corresponding value.

If a referenced key is not found in the output file, resolution fails and the
job is not started.

## Validation Rules

Procman enforces three rules at parse time to catch output reference errors
before any job starts:

### Rule 1: Referenced process must exist

```
job app {
  env KEY = @nonexistent.KEY  # Error: process 'nonexistent' does not exist
  run "echo $KEY"
}
```

### Rule 2: Referenced process must be a `job`

Only jobs (one-shot processes) produce output that is guaranteed to be available.
Referencing a service is rejected:

```
service server {
  run "start-server"
}

job app {
  env PORT = @server.PORT  # Error: 'server' is not a job
  run "echo $PORT"
}
```

### Rule 3: Referencing process must have `after @job` in its `wait` block

The referencing job or service must have an `after` condition (direct or
transitive) on the referenced job. This guarantees the output file exists when
references are resolved:

```
job setup {
  run "echo KEY=value > $PROCMAN_OUTPUT"
}

service app {
  env KEY = @setup.KEY  # Error: no 'after @setup' in wait block
  run "echo $KEY"
}
```

Transitive dependencies are followed — if `app` waits on `middle` and `middle`
waits on `setup`, then `app` can reference `setup`'s output.