Imports:
  - Types:
      - Config
      - BuildConfig
      - TaskExecutorConfig
      - load_config
    From: goga/config
  - Types:
      - resolve_wrapper_path
    Usages:
      - resolve-wrapper-path
    From: goga/agents
  - Types:
      - ensure_in_docker
    Usages:
      - ensure-in-docker
    From: goga/docker

Usages:
  conventions: .goga/usages/conventions.md
  ralphex: .goga/usages/cooks/ralphex.md
  agent-wrappers: .goga/usages/cooks/agent-as-claude-wrappers.md

Annotations: |
  Use the `conventions` practice for development and testing.
  Use the `ralphex` practice for builds.
  Use the `agent-wrappers` practice for the in-container wrapper naming
  convention referenced in the ralphex config generation step.
  Write all output to sys.stderr (click is not used).
  Run external commands via subprocess.

---

"build(plan: str, config: Config, cli_options: dict) -> exit_code:int":
  location: build.py
  annotations: |
    Orchestrates code builds through `ralphex`. The function prepares the
    execution environment and launches the build runner.

    `plan`: path to the plan file (markdown)
    `config`: loaded project configuration object
    `cli_options`: dictionary of CLI options (dry_run, worktree, skip_finalize,
                   skip_manifest_check, session_timeout, idle_timeout,
                   wait, max_iterations, review_patience)
    `exit_code`: process exit code (0 = success, 1 = failure)

    Algorithm:
    0. (pre-check) When skip_manifest_check is not set:
       - Verify all project CODEMANIFEST files are committed to git
       - Reject with exit code 1 if any uncommitted manifests are found
    1. Resolve the agent wrapper path by calling `resolve_wrapper_path` with
       the agent field of `TaskExecutorConfig`, per the `resolve-wrapper-path`
       practice. The result is the absolute in-container path
       /home/goga/bin/<name>-as-claude.sh defined by the `agent-wrappers`
       practice. Do not validate the agent value — absence of the wrapper
       file is surfaced by `ralphex` itself.
    2. Create the .ralphex/config file with defaults populated for the
       resolved agent:
       - set the claude_command key to the resolved wrapper path
       - apply claude_args defaults when missing
       - set codex_enabled from `BuildConfig`
       - set preserve_anthropic_api_key to true so the ralphex runner keeps
         ANTHROPIC_API_KEY in the agent subprocess env (the agent wrapper
         no longer remaps ANTHROPIC_API_TOKEN)
    3. Copy default prompts and agents from the goga package to .ralphex/,
       using the directory paths specified in `BuildConfig`
    4. Assemble the ralphex command:
       - Base command: ["ralphex", plan, "--config-dir", ".ralphex/"]
       - Apply flags with precedence: CLI options > `Config` > omit
    5. On dry_run: print the assembled command and return 0
    6. Execute `ralphex` via subprocess.call with
       env = {**os.environ, **`TaskExecutorConfig`.env} (build env delivered through
       the process environment, not via a settings file) and propagate its exit code

    Apply the `conventions` practice for docstring style and intra-package imports.
    Apply the `ralphex` practice for ralphex CLI contract and config layout.
    Apply the `agent-wrappers` practice for the wrapper path semantics referenced
    in step 3.
    Apply the `resolve-wrapper-path` practice when calling
    `resolve_wrapper_path` in step 2.

    Requirements:
    - `ralphex` must be available on PATH; fail otherwise
    - Minimal output: log each step to sys.stderr
    - Return code: `ralphex` exit code on success, 1 on error
    - The claude_command key in .ralphex/config MUST be set to the resolved
      wrapper path (absolute, /home/goga/bin/<name>-as-claude.sh)
    - The preserve_anthropic_api_key key in .ralphex/config MUST be set to
      true so the agent wrapper receives ANTHROPIC_API_KEY from the
      ralphex subprocess env without remapping

    Constraints:
    - Wrappers live in the image at /home/goga/bin/ and are referenced by
      absolute path — do not generate wrapper scripts under .ralphex/
    - Agent resolution is uniform — do not branch the algorithm by agent name
    - Wrapper-script bodies are owned by the image (scripts/claude-as-claude.sh)
      and the upstream ralphex source (codex-as-claude.sh, opencode-as-claude.sh)
      — do not inline wrapper bodies into this cell
    - The .ralphex/ directory lifecycle is owned by the host launcher
      (goga/commands/build): the in-container build() receives .ralphex/ as a
      prepared mount and reuses any ralphex state present in it. The host wipes
      the directory only when goga build --clean is passed before launch

"main() -> exit_code:int":
  location: __main__.py
  annotations: |
    Entry point for python -m goga.build execution inside a Docker container.

    `exit_code`: process exit code (0 = success, 1 = failure)

    Algorithm:
    0. Call `ensure_in_docker` as the very first statement — refuse to proceed
       when the process is not running inside the goga Docker image (per the
       `ensure-in-docker` practice)
    1. Parse CLI arguments via argparse (plan + options)
    2. Load project configuration via `load_config`
    3. Build cli_options from the parsed argparse results
    4. Invoke `build`(plan, `Config`, cli_options)
    5. Return the resulting `exit_code`

    Requirements:
    - The guard at step 0 MUST be covered by tests for both branches:
      success path with GOGA_DOCKER=1 proceeds to argparse; refusal path
      without the marker writes to stderr and exits with code 1 before any
      filesystem or process work

    Apply the `ensure-in-docker` practice at step 0.

---

Author: Mikhail Trifonov
CreatedAt: 15/05/26

Description: |
  Manifest describing the code build orchestration logic through ralphex
