Usages:
  conventions: .goga/usages/conventions.md
  afm: .goga/usages/cooks/afm.md

Annotations: |
  The `conventions` practice is used for:
  - Working with the codebase
  - Organizing the REPL development cycle
  - Debugging and testing
  - Organizing the test infrastructure
  - Understanding the general principles and rules of development and testing in the project

  This cell is a thin subprocess-only wrapper over the external `afm` binary —
  its sole responsibility is to invoke "afm run" with an absolute pipeline-file
  path and a port, then propagate the subprocess exit code. Discovery, path
  resolution, port allocation, and the pipeline-file entity live in other cells:
  this cell performs no discovery or path resolution.

  Use the `afm` practice to interact with the external afm binary
  (CLI contract, PATH-resolved invocation, exit-code propagation).

  The standard library subprocess and pathlib.Path modules are used for invoking
  the binary and receiving arguments.

---

"run_flow(flow_path: Path, port: int, max_parallel: int | None = None) -> exit_code: int":
  location: run_flow.py
  annotations: |
    Launch the external `afm` binary to run the pipeline file at the given
    absolute path and bind its dashboard to the given `port`. Optionally cap the
    number of concurrently executing stages. This is the goga-side entry point
    to afm; it performs no discovery, path resolution, or port allocation.

    `flow_path`: absolute path to the pipeline file (passed verbatim to
                 "afm run" as the positional argument)
    `port`: TCP port forwarded to "afm run --port" (allocated by the caller)
    `max_parallel`: optional cap on concurrently executing stages, forwarded to
                    afm as "--max-parallel <max_parallel>" ONLY when not None.
                    When None — the "--max-parallel" flag is OMITTED and afm
                    applies its own default. Decided by
                    the caller; `run_flow` only passes it through.
    `exit_code`: 0 on success, 127 when afm is missing from PATH, 126 when afm
                 is present but not executable (other OSError), otherwise afm's
                 own exit code

    Algorithm:
    1. Receive `flow_path` (absolute), `port` (integer), and the optional
       `max_parallel`
    2. Invoke "afm run" via the `afm` practice with --port <port>, the absolute
       `flow_path` as positional argument, and — when `max_parallel` is not None
       — the --max-parallel <max_parallel> flag; resolve afm through PATH
    3. On binary-not-found (FileNotFoundError, per the `afm` practice) — return
       `exit_code` 127 with a clear message
    4. On other OSError (present but not executable, permission denied, etc.) —
       return `exit_code` 126 with a clear message
    5. Return afm's exit code as `exit_code`

    Apply the `conventions` practice for error-handling style and docstring formatting.

    Requirements:
    - Always pass `flow_path` to "afm run" as the positional argument — never a
      bare name
    - Always forward `port` to afm via the --port flag — never omit it
    - Forward `max_parallel` via --max-parallel ONLY when it is not None — None
      means "do not limit" and MUST omit the flag (never substitute a default
      such as 0)
    - Invoke afm through PATH — do not hard-code /srv/afm
    - Apply the `afm` practice's error-handling rules verbatim
    - Accept absolute paths only; do not resolve or construct paths internally

    Constraints:
    - Do not resolve names to paths — the caller's responsibility
    - Do not allocate the port — the caller allocates it
    - Do not decide or default `max_parallel` — the caller decides; None ⇒ omit
      the flag, never infer a value
    - Do not discover pipeline files — discovery is the caller's concern
    - Do not hard-code the afm binary path — rely on PATH inside the container
    - Do not modify or parse pipeline-file contents

---

Author: Goga
CreatedAt: 28/06/26
Description: |
  Thin subprocess-only wrapper over the external `afm` binary. Launches
  "afm run" with an absolute pipeline-file path and a dashboard port, then
  propagates the subprocess exit code. Discovery, path resolution, port
  allocation, and the pipeline-file entity live in other cells.
