# Flow

This document describes the first practical local-testing flow for two agents that cooperate through a shared folder, while one side keeps access to real data private.

For the runnable demo and test in this repo, place that shared folder under a sandbox root such as `./sandbox/two-console-demo/shared/` instead of writing directly to a repo-root `./shared/` directory.

It is intentionally biased toward:

- simple file protocols
- explicit state
- easy manual debugging
- being runnable from two local terminals

## Core Idea

Use the shared folder as the agent-to-agent transport.

Do not use Telegram as the transport between agents.
Telegram is only for human approval and status notifications.

So there are two separate channels:

1. Agent channel
   Shared folder only.

2. Human approval channel
   Telegram only.

## Should Agent Messages Be One File Or Many Files?

Use one file per message or event, not a single append-only `message.md`.

Reason:

- easier to watch for new work
- avoids write races
- simpler ack/state handling
- easier to retry or replay
- easier to attach structured metadata

Use `thread.md` only as a readable summary.
Do not use it as the authoritative machine protocol.

## Recommended Shared Layout

```text
sandbox/two-console-demo/shared/
  thread.md
  datasets/
    <dataset_uuid>/
      dataset.yaml
      mock/
        ...
  .rho/
    agents.yaml
    spaces/
      <space_id>.yaml
    inbox/
      <to_agent>/
        <message_id>.yaml
    outbox/
      <from_agent>/
    requests/
      <request_id>.yaml
    runs/
      <run_id>.yaml
    results/
      <result_id>.yaml
    events/
      <event_id>.yaml
```

Per-agent local state:

```text
sandbox/two-console-demo/users/
  agent1/
    datasets/
      share/
      private/
    local/
      inbox/
      staging/
      approvals/
      telegram/
  agent2/
    datasets/
      share/
      private/
    local/
      inbox/
      staging/
      approvals/
      telegram/
```

## Agent Registry

Yes, have a shared file listing known agents.

Suggested file:

```text
sandbox/two-console-demo/shared/.rho/agents.yaml
```

Example:

```yaml
version: 1
agents:
  - id: agent1
    owner: user1
    role: data-owner
    status: active
  - id: agent2
    owner: user2
    role: compute-agent
    status: active
```

This gives you stable ids for routing messages and requests.

## Shared Space

A shared space is a named collaboration context.

Suggested file:

```text
sandbox/two-console-demo/shared/.rho/spaces/<space_id>.yaml
```

Example:

```yaml
version: 1
space:
  id: space-local-001
  agents:
    - agent1
    - agent2
  status: active
  shared_root: ./sandbox/two-console-demo/shared
  created_by: agent1
```

## Message Transport

The tool that “messages another agent” should create a new YAML file in the recipient inbox.

Not append to `message.md`.

Suggested path:

```text
sandbox/two-console-demo/shared/.rho/inbox/agent2/msg-<uuid>.yaml
```

Example message:

```yaml
version: 1
message:
  id: msg-001
  from: agent1
  to: agent2
  space_id: space-local-001
  type: request_run_real
  related_request_id: req-001
  created_at: 2026-04-02T00:00:00Z
  body:
    text: "Please evaluate this computation request against dataset prices-demo."
```

The receiving agent loop watches only:

```text
sandbox/two-console-demo/shared/.rho/inbox/<agent_id>/
```

That keeps the routing trivial.

## Status Updates

Agent1 should keep Agent2 informed with explicit status events.

Do this with event files, also one per file:

```text
sandbox/two-console-demo/shared/.rho/events/<event_id>.yaml
```

Or, if you want routing, put them into inbox messages with `type: status_update`.

Suggested statuses:

- `request_received`
- `review_started`
- `waiting_for_admin`
- `mock_run_started`
- `mock_run_finished`
- `real_run_started`
- `real_run_finished`
- `release_approved`
- `release_rejected`
- `request_rejected`
- `result_published`

Example:

```yaml
version: 1
message:
  id: msg-002
  from: agent1
  to: agent2
  space_id: space-local-001
  type: status_update
  related_request_id: req-001
  created_at: 2026-04-02T00:01:00Z
  body:
    status: waiting_for_admin
    text: "Code review completed. Waiting for approval to run on mock."
```

## Tool Surface In Pi Extensions

The `pi` extension layer should eventually expose tools like:

- `send_agent_message`
- `review_shared_code`
- `request_run_mock`
- `request_run_real`
- `publish_result`
- `check_request_status`

But the approval decisions should still come from the external admin flow, not directly from the model.

## First CLI Shape

You said you want to test this from two terminals, with either direct commands or the `pi` REPL.

That makes sense.

Suggested local CLI shape:

```text
rho agent init <agent_id>
rho agent bind-telegram <agent_id> <chat_ref>
rho agent listen <agent_id>
rho space create <space_id> --agents agent1,agent2
rho space accept <agent_id> <space_id>
rho dataset create ...
rho dataset publish <agent_id> <dataset_uuid>
rho request create-run ...
rho request status <request_id>
```

You do not need to implement all of these at once.
This is the operator model to aim for.

## Example End-To-End Flow

This is the concrete flow you described, rewritten as a protocol.

### 1. Initialize Agents

```text
init agent1
init agent2
```

Effects:

- create local user folders
- write agent metadata
- prepare local inbox/staging/approval dirs

### 2. Bind Telegram

```text
bind agent1 to telegram chat
bind agent2 to telegram chat
```

The same chat can be reused for local testing.
All Telegram messages should clearly prefix the agent id:

- `[agent1] waiting for approval`
- `[agent2] result received`

### 3. Start Both Listening

Each agent runs a local loop:

- watch shared inbox for messages to that agent
- watch local approval state
- process work items

### 4. Agent1 Creates Dataset

Use `rho-dataset`.

This stages:

- shareable mock bundle under `users/agent1/.../share`
- private real bundle under `users/agent1/.../private`

### 5. Agent1 Creates Shared Space With Agent2

Write:

```text
shared/.rho/spaces/<space_id>.yaml
```

And send:

```text
shared/.rho/inbox/agent2/msg-....yaml
```

with type `space_invite`.

### 6. Agent2 Accepts

Agent2 writes an acceptance message or updates the space status.

At this point both sides know the shared root path and space id.

### 7. Agent1 Publishes Dataset

Use the share bundle only:

```text
users/agent1/datasets/share/<dataset_uuid>
```

copy to:

```text
shared/datasets/<dataset_uuid>
```

### 8. Agent2 Writes Code And Runs On Mock

This is a local tool call on Agent2’s side.

Agent2:

- reads `shared/datasets/<dataset_uuid>/dataset.yaml`
- uses the mock data
- writes code in the shared workspace
- runs Gondolin against the mock dataset

This is usually safe enough to run without involving Agent1 yet.

### 9. Agent2 Requests Run On Real

Agent2 sends a request message to Agent1.

Suggested request file:

```text
shared/.rho/requests/<request_id>.yaml
```

And a routed inbox message to Agent1 pointing at that request.

The request should include:

- dataset UUID
- code paths to review
- command to run
- expected outputs
- code digest

### 10. Agent1 Receives Request

Agent1 loop sees the inbox message and loads the request.

Then Agent1 runs:

1. code review tool
2. local admin notification via Telegram

The sandboxed code must not control the Telegram message directly.
The outer harness builds the Telegram notification.

### 11. Telegram Asks Admin

Admin options should be simple and explicit:

1. run on mock
2. run on real
3. reject with message

For the first version, even if mock is normally safe, it is reasonable to ask:

- run on mock
- run on real
- reject

because it keeps the decision model very obvious.

### 12. If Run On Mock

Agent1:

- snapshots code
- binds mock data
- runs sandbox
- records stdout/stderr/artifacts
- sends status updates to Agent2
- sends result summary to Telegram

Then ask admin:

- run on real?
- reject?

### 13. If Run On Real

Agent1:

- snapshots code
- binds real private data
- runs sandbox locally
- records outputs in local staging only
- sends result summary to Telegram

Then ask admin:

- release results?
- reject release?

### 14. Release

If approved, Agent1 writes a result bundle into shared space:

```text
shared/.rho/results/<result_id>.yaml
shared/results/<result_id>/...
```

And sends a routed message to Agent2:

- result available
- request id
- result id
- summary

If not approved, send a rejection/status message instead.

### 15. Agent2 Receives Response

Agent2 sees:

- status updates
- rejection
- or published result

It should also be possible for Agent2 to explicitly poll:

- `check_request_status`

## Telegram Hooking Rule

This is an important safety rule.

Do not let code running inside Gondolin decide what is sent to Telegram.

Instead:

1. sandbox produces raw outputs
2. outer harness inspects them
3. outer harness decides what summary to send

That prevents untrusted code from directly driving the approval UX.

## Minimum Files To Implement First

If you want the smallest protocol that still works, start with:

```text
shared/.rho/agents.yaml
shared/.rho/spaces/<space_id>.yaml
shared/.rho/inbox/<agent_id>/<message_id>.yaml
shared/.rho/requests/<request_id>.yaml
shared/.rho/results/<result_id>.yaml
```

That is enough to support:

- agent discovery
- space creation
- message delivery
- run requests
- result publication

## Recommended First Tools

Implement these first:

1. `send_agent_message`
   Writes a routed inbox file.

2. `create_run_request`
   Writes a request manifest and notifies the other agent.

3. `review_shared_code`
   Produces a summary from shared code changes.

4. `run_request_on_mock`
   Local execution only.

5. `run_request_on_real`
   Local execution only, gated by approval.

6. `publish_result`
   Copies approved artifacts to shared results.

## What To Avoid

- do not use `thread.md` as the machine protocol
- do not let Telegram become the agent transport
- do not let sandboxed code directly send approval messages
- do not append multiple writers into one shared mutable markdown file as the source of truth

## Immediate Next Step

The next concrete implementation step should be:

1. define `shared/.rho/agents.yaml`
2. define inbox message schema
3. implement `send_agent_message`
4. implement `create_run_request`

That gives you a real inter-agent transport before building the more complex approval loop.
