setup:
  cleanup_on_exit: false
  default_mode: default
  env:
    pi_auth_json: RHO_PI_AUTH_JSON
    agent_fixture_mode: RHO_AGENT_FIXTURE_MODE
    agent_fixture_root: RHO_AGENT_FIXTURE_ROOT
    middleware_adapter: RHO_MIDDLEWARE_ADAPTER
    middleware_fixture_file: RHO_MIDDLEWARE_FIXTURE_FILE
    telegram_profile: RHO_TELEGRAM_PROFILE
    telegram_bot_token: RHO_TELEGRAM_BOT_TOKEN
    telegram_chat_id: RHO_TELEGRAM_CHAT_ID
    telegram_user_id: RHO_TELEGRAM_USER_ID
  generate:
    dataset_uuid: uuid
  vars:
    owner: test-agent1
    peer: test-agent2
    request_id: "req-{{dataset_uuid}}"
    run_action_id: "act-run-real-{{dataset_uuid}}"
    release_action_id: "act-release-results-{{dataset_uuid}}"
  paths:
    sandbox_dir: "{{repo_root}}/sandbox/two-console-demo-agent-approval"
    users_root: "{{sandbox_dir}}/users"
    shared_root: "{{sandbox_dir}}/shared"
    control_root: "{{sandbox_dir}}/control"
    control_outbox: "{{control_root}}/outbox"
    control_inbox: "{{control_root}}/inbox"
    share_stage_dir: "{{users_root}}/{{owner}}/datasets/share/{{dataset_uuid}}"
    private_stage_dir: "{{users_root}}/{{owner}}/datasets/private/{{dataset_uuid}}"
    published_dir: "{{shared_root}}/datasets/{{dataset_uuid}}"
    workspace_dir: "{{shared_root}}/workspace"
    workspace_script: "{{workspace_dir}}/sum_prices.py"
    mock_csv: "{{published_dir}}/mock/prices-mock.csv"
    real_csv: "{{private_stage_dir}}/real/prices-real.csv"
    request_path: "{{shared_root}}/.rho/requests/{{request_id}}.yaml"
    inbox_agent1: "{{shared_root}}/.rho/inbox/agent1/msg-{{dataset_uuid}}.yaml"
    inbox_agent2_result: "{{shared_root}}/.rho/inbox/agent2/result-{{dataset_uuid}}.json"
    message_dir: "{{shared_root}}/messages"
    request_message: "{{message_dir}}/agent2-to-agent1.txt"
    run_status_message: "{{message_dir}}/agent1-run-status.txt"
    release_status_message: "{{message_dir}}/agent1-release-status.txt"
    peer_ready_message: "{{message_dir}}/agent2-ready.txt"
    run_action_outbox: "{{control_outbox}}/{{run_action_id}}.json"
    run_action_inbox: "{{control_inbox}}/{{run_action_id}}.json"
    release_action_outbox: "{{control_outbox}}/{{release_action_id}}.json"
    release_action_inbox: "{{control_inbox}}/{{release_action_id}}.json"
    private_result_dir: "{{sandbox_dir}}/private-results/{{request_id}}"
    private_result_output: "{{private_result_dir}}/output.txt"
    released_result_dir: "{{shared_root}}/results/{{request_id}}"
    released_result_output: "{{released_result_dir}}/output.txt"
    result_manifest: "{{shared_root}}/.rho/results/{{request_id}}.json"
    audit_dir: "{{sandbox_dir}}/audit"
    agent2_codegen_audit: "{{audit_dir}}/agent2-generate-code.http.jsonl"
    agent2_request_audit: "{{audit_dir}}/agent2-write-request.http.jsonl"
    agent1_run_request_audit: "{{audit_dir}}/agent1-request-run-action.http.jsonl"
    agent1_run_status_audit: "{{audit_dir}}/agent1-read-run-status.http.jsonl"
    agent1_release_request_audit: "{{audit_dir}}/agent1-request-release-action.http.jsonl"
    agent1_release_status_audit: "{{audit_dir}}/agent1-read-release-status.http.jsonl"
    agent2_release_read_audit: "{{audit_dir}}/agent2-read-release.http.jsonl"
    run_manifest_dir: "{{sandbox_dir}}/manifests/run"
    release_manifest_dir: "{{sandbox_dir}}/manifests/release"
    run_dir: "{{sandbox_dir}}/run"
    codegen_prompt_path: "{{run_dir}}/agent2-generate-code.prompt"
    codegen_config_path: "{{run_dir}}/agent2-generate-code.yaml"
    request_prompt_path: "{{run_dir}}/agent2-write-request.prompt"
    request_config_path: "{{run_dir}}/agent2-write-request.yaml"
    run_action_prompt_path: "{{run_dir}}/agent1-request-run-action.prompt"
    run_action_config_path: "{{run_dir}}/agent1-request-run-action.yaml"
    run_status_prompt_path: "{{run_dir}}/agent1-read-run-status.prompt"
    run_status_config_path: "{{run_dir}}/agent1-read-run-status.yaml"
    release_action_prompt_path: "{{run_dir}}/agent1-request-release-action.prompt"
    release_action_config_path: "{{run_dir}}/agent1-request-release-action.yaml"
    release_status_prompt_path: "{{run_dir}}/agent1-read-release-status.prompt"
    release_status_config_path: "{{run_dir}}/agent1-read-release-status.yaml"
    peer_ready_prompt_path: "{{run_dir}}/agent2-read-release.prompt"
    peer_ready_config_path: "{{run_dir}}/agent2-read-release.yaml"
  cleanup:
    - "{{sandbox_dir}}"

steps:
  - name: Create twin dataset
    actor: "{{owner}}"
    summary: Create private and share-stage dataset copies inside the sandbox.
    run: |
      ./rho-dataset \
        --name prices-demo-agent-approval \
        --real "{{repo_root}}/examples/datasets/prices-real.csv" \
        --mock "{{repo_root}}/examples/datasets/prices-mock.csv" \
        --owner "{{owner}}" \
        --share-dir "{{users_root}}/{{owner}}/datasets/share" \
        --private-dir "{{users_root}}/{{owner}}/datasets/private" \
        --uuid "{{dataset_uuid}}" \
        --description "Agent-to-agent controlled action test"
    assert_files:
      - "{{share_stage_dir}}/dataset.yaml"
      - "{{private_stage_dir}}/dataset.yaml"

  - name: Publish mock twin
    actor: "{{owner}}"
    summary: Publish the mock dataset into shared space for agent2 development.
    run: |
      ./publish "{{owner}}" "{{dataset_uuid}}" \
        --source-root "{{users_root}}" \
        --target-root "{{shared_root}}/datasets"
    assert_files:
      - "{{published_dir}}/dataset.yaml"
      - "{{mock_csv}}"

  - name: Create canonical rho request
    actor: test-runner
    summary: Write the canonical request manifest and owner inbox message used by middleware approval.
    render:
      - template: "{{assets_dir}}/request.yaml.tmpl"
        to: "{{request_path}}"
      - template: "{{assets_dir}}/inbox-agent1.yaml.tmpl"
        to: "{{inbox_agent1}}"
    assert_files:
      - "{{request_path}}"
      - "{{inbox_agent1}}"

  - name: Prepare sandbox prompts and configs
    actor: test-runner
    summary: Render prompts, configs, control folders, and template folders for sandboxed agents.
    mkdirs:
      - "{{control_outbox}}"
      - "{{control_inbox}}"
      - "{{message_dir}}"
      - "{{private_result_dir}}"
      - "{{released_result_dir}}"
      - "{{audit_dir}}"
      - "{{run_dir}}"
      - "{{workspace_dir}}"
      - "{{run_manifest_dir}}"
      - "{{release_manifest_dir}}"
    render:
      - template: "{{assets_dir}}/agent2-generate-code.prompt.tmpl"
        to: "{{codegen_prompt_path}}"
      - template: "{{assets_dir}}/agent2-generate-code.yaml.tmpl"
        to: "{{codegen_config_path}}"
      - template: "{{assets_dir}}/agent2-write-request.prompt.tmpl"
        to: "{{request_prompt_path}}"
      - template: "{{assets_dir}}/agent2-write-request.yaml.tmpl"
        to: "{{request_config_path}}"
      - template: "{{assets_dir}}/agent1-request-run-action.prompt.tmpl"
        to: "{{run_action_prompt_path}}"
      - template: "{{assets_dir}}/agent1-request-run-action.yaml.tmpl"
        to: "{{run_action_config_path}}"
      - template: "{{assets_dir}}/agent1-read-run-status.prompt.tmpl"
        to: "{{run_status_prompt_path}}"
      - template: "{{assets_dir}}/agent1-read-run-status.yaml.tmpl"
        to: "{{run_status_config_path}}"
      - template: "{{assets_dir}}/agent1-request-release-action.prompt.tmpl"
        to: "{{release_action_prompt_path}}"
      - template: "{{assets_dir}}/agent1-request-release-action.yaml.tmpl"
        to: "{{release_action_config_path}}"
      - template: "{{assets_dir}}/agent1-read-release-status.prompt.tmpl"
        to: "{{release_status_prompt_path}}"
      - template: "{{assets_dir}}/agent1-read-release-status.yaml.tmpl"
        to: "{{release_status_config_path}}"
      - template: "{{assets_dir}}/agent2-read-release.prompt.tmpl"
        to: "{{peer_ready_prompt_path}}"
      - template: "{{assets_dir}}/agent2-read-release.yaml.tmpl"
        to: "{{peer_ready_config_path}}"
      - template: "{{assets_dir}}/expected-run-action.yaml.tmpl"
        to: "{{run_manifest_dir}}/{{request_id}}.yaml"
      - template: "{{assets_dir}}/expected-release-action.yaml.tmpl"
        to: "{{release_manifest_dir}}/{{request_id}}.yaml"
    assert_files:
      - "{{codegen_config_path}}"
      - "{{request_config_path}}"
      - "{{run_action_config_path}}"
      - "{{release_action_config_path}}"

  - name: Agent2 generates workspace code in Gondolin
    actor: "{{peer}}"
    summary: Run Pi inside Gondolin so agent2 writes the shared price summing script.
    run: ./rho agent-run --config "{{codegen_config_path}}"
    inspect:
      - "{{workspace_script}}"

  - name: Run generated script against mock data
    actor: test-runner
    summary: Execute the generated script against the published mock dataset.
    run: python3 "{{workspace_script}}" "{{mock_csv}}"
    capture: mock_output
    expect_stdout: "555.00"

  - name: Agent2 writes plain text request in Gondolin
    actor: "{{peer}}"
    summary: Run Pi inside Gondolin so agent2 writes a human-readable request message for agent1.
    run: ./rho agent-run --config "{{request_config_path}}"
    inspect:
      - "{{request_message}}"

  - name: Agent1 requests controlled run action in Gondolin
    actor: "{{owner}}"
    summary: Run Pi inside Gondolin so agent1 requests the protected run_real_data action.
    run: ./rho agent-run --config "{{run_action_config_path}}"
    inspect:
      - "{{run_action_outbox}}"

  - name: Initialize Telegram profile
    actor: "{{owner}}"
    summary: Initialize the owner's Telegram adapter profile inside this sandbox.
    when: live
    run: |
      test -n "{{telegram_profile}}"
      test -n "{{telegram_bot_token}}"
      test -n "{{telegram_chat_id}}"
      test -n "{{telegram_user_id}}"
      RHO_TELEGRAM_ROOT="{{sandbox_dir}}" \
      ./rho-telegram init "{{telegram_profile}}" \
        --user "{{owner}}" \
        --token "{{telegram_bot_token}}" \
        --chat-id "{{telegram_chat_id}}"

  - name: Bind Telegram profile
    actor: "{{owner}}"
    summary: Bind the Telegram profile to the configured operator identity.
    when: live
    run: |
      RHO_TELEGRAM_ROOT="{{sandbox_dir}}" \
      ./rho-telegram bind "{{telegram_profile}}" \
        --user "{{owner}}" \
        --chat-id "{{telegram_chat_id}}" \
        --user-id "{{telegram_user_id}}"

  - name: Drain old Telegram updates
    actor: test-runner
    summary: Clear stale Telegram updates before relaying the first controlled action.
    when: live
    run: |
      RHO_TELEGRAM_ROOT="{{sandbox_dir}}" \
      ./rho-telegram drain "{{telegram_profile}}" --user "{{owner}}"

  - name: Relay controlled run action through middleware
    actor: controlled-action-relay
    summary: Validate, approve, and deterministically execute the protected real-data run outside the sandbox.
    run: |
      python3 "{{repo_root}}/scripts/rho-controlled-action-relay.py" relay \
        --control-root "{{control_root}}" \
        --shared-root "{{shared_root}}" \
        --action-id "{{run_action_id}}" \
        --middleware "{{middleware_adapter}}" \
        --fixture-file "{{middleware_fixture_file}}" \
        --telegram-profile "{{telegram_profile}}" \
        --telegram-root "{{sandbox_dir}}" \
        --telegram-user "{{owner}}" \
        --timeout-seconds 120 \
        --manifest-dir "{{run_manifest_dir}}" \
        --require-manifest \
        --map "/input/private={{private_stage_dir}}/real" \
        --map "/output={{workspace_dir}}" \
        --map "/results/private={{private_result_dir}}" \
        --map "/release/shared={{released_result_dir}}" \
        --map "/release/meta={{shared_root}}/.rho/results" \
        --map "/release/notify={{shared_root}}/.rho/inbox/agent2"
    inspect:
      - "{{run_action_inbox}}"
      - "{{private_result_output}}"

  - name: Agent1 reads run action status in Gondolin
    actor: "{{owner}}"
    summary: Run Pi inside Gondolin so agent1 reads the completed run status and writes a short note.
    run: ./rho agent-run --config "{{run_status_config_path}}"

  - name: Agent1 requests controlled release action in Gondolin
    actor: "{{owner}}"
    summary: Run Pi inside Gondolin so agent1 requests protected release of the completed results.
    run: ./rho agent-run --config "{{release_action_config_path}}"
    inspect:
      - "{{release_action_outbox}}"

  - name: Drain old Telegram updates before release
    actor: test-runner
    summary: Clear stale Telegram updates before the release approval request.
    when: live
    run: |
      RHO_TELEGRAM_ROOT="{{sandbox_dir}}" \
      ./rho-telegram drain "{{telegram_profile}}" --user "{{owner}}"

  - name: Relay controlled release action through middleware
    actor: controlled-action-relay
    summary: Validate, approve, and deterministically release the completed results into shared space.
    run: |
      python3 "{{repo_root}}/scripts/rho-controlled-action-relay.py" relay \
        --control-root "{{control_root}}" \
        --shared-root "{{shared_root}}" \
        --action-id "{{release_action_id}}" \
        --middleware "{{middleware_adapter}}" \
        --fixture-file "{{middleware_fixture_file}}" \
        --telegram-profile "{{telegram_profile}}" \
        --telegram-root "{{sandbox_dir}}" \
        --telegram-user "{{owner}}" \
        --timeout-seconds 120 \
        --manifest-dir "{{release_manifest_dir}}" \
        --require-manifest \
        --map "/input/private={{private_stage_dir}}/real" \
        --map "/output={{workspace_dir}}" \
        --map "/results/private={{private_result_dir}}" \
        --map "/release/shared={{released_result_dir}}" \
        --map "/release/meta={{shared_root}}/.rho/results" \
        --map "/release/notify={{shared_root}}/.rho/inbox/agent2"
    inspect:
      - "{{release_action_inbox}}"
      - "{{released_result_output}}"
      - "{{result_manifest}}"
      - "{{inbox_agent2_result}}"

  - name: Agent1 reads release action status in Gondolin
    actor: "{{owner}}"
    summary: Run Pi inside Gondolin so agent1 reads the completed release status and writes a short note.
    run: ./rho agent-run --config "{{release_status_config_path}}"

  - name: Agent2 reads released result notification in Gondolin
    actor: "{{peer}}"
    summary: Run Pi inside Gondolin so agent2 reads the machine notification and writes a human-readable ready message.
    run: ./rho agent-run --config "{{peer_ready_config_path}}"

  - name: Assert final released outputs exist
    actor: test-runner
    summary: Confirm that protected execution and protected release both completed successfully.
    assert_files:
      - "{{workspace_script}}"
      - "{{private_result_output}}"
      - "{{released_result_output}}"
      - "{{result_manifest}}"
      - "{{inbox_agent2_result}}"
      - "{{run_status_message}}"
      - "{{release_status_message}}"
      - "{{peer_ready_message}}"
      - "{{agent2_codegen_audit}}"
      - "{{agent2_request_audit}}"
      - "{{agent1_run_request_audit}}"
      - "{{agent1_run_status_audit}}"
      - "{{agent1_release_request_audit}}"
      - "{{agent1_release_status_audit}}"
      - "{{agent2_release_read_audit}}"
    assert_contains:
      - path: "{{private_result_output}}"
        pattern: "1113.03"
      - path: "{{released_result_output}}"
        pattern: "1113.03"
      - path: "{{run_status_message}}"
        pattern: "completed"
      - path: "{{release_status_message}}"
        pattern: "completed"
      - path: "{{peer_ready_message}}"
        pattern: "ready"
