Skip to content
RoutineOS
Esc
↑↓navigate↵open⌘Jpreview
On this page

Join a Desk

Open a Desk so teammates can serve a lane from their own accounts, join one as a worker, leave, and change the join settings. Since 0.8.0. Every command output shown is what the CLI printed.

Since 0.8.0.

This guide covers both sides. The owner opens a Desk. A joiner serves one lane of it from their own accounts and connectors, so the work runs as them and their approvals are theirs.

Before you start

  • The owner has a Desk with a definition.json (see Add a Desk to a routine).
  • The owner has shared the Desk artifact with the joiner as a Contributor. Without that the CLI says “Ask the owner to share this Desk with you as a Contributor.”
  • Both have the routineos.py CLI. $RO below stands for python3 skills/routineos/scripts/routineos.py.

Owner: open the Desk

Add one join block to the Desk-level definition.json. A Desk has one join block; a second fails compile.

"join": {
  "version": 1,
  "open": true,
  "summary": "Draft replies to inbound sales mail from your own mailbox.",
  "lanes": [{
    "id": "sender",
    "label": "Sender",
    "kind": "mail-draft",
    "match": {"type": "role", "field": "owner", "equals": "{mailbox}"},
    "params": {"mailbox": "email"},
    "connectors": ["gmail"],
    "schedule": {"default": "0 22 * * 1,4", "min_interval_hours": 24},
    "gate": "none"
  }],
  "cap": {"scope": "account", "maxRunsPerDay": 1}
}

Every key is described in the definition schema. Then seed as usual. Pass --url with the Desk’s address so joiners get a link; the seed stores it in meta/join.

$RO seed <dir> --out seed --url <desk_url>

To open or close an existing Desk later, the owner runs:

$RO join-settings <dir> --open --out out --docs <snapshot.json> --by <ownerId>
$RO join-settings <dir> --closed --out out --docs <snapshot.json> --by <ownerId>

It rewrites join.open in definition.json and plans the meta/join write. Only the owner can run it, and --by must equal meta/owner.by. A Desk with no join block refuses: “this Desk has no join block; answer the open-to-join question in build first”. Applying the batch is the owner’s decision.

Joiner: see the lanes

$RO join <desk_url> --docs <snapshot.json>

With no --lane, join lists the lanes and which are free. A closed Desk refuses with “This Desk is not open to join. Ask its owner to open it.” A lane served by someone else refuses with “This lane is served by someone else since . Ask the Desk owner to release it.”

Joiner: take a lane

$RO join <desk_url> --docs <snapshot.json> --lane sender \
  --param mailbox=you@example.com --connected gmail \
  --schedule "0 22 * * 1,4" --tz Australia/Sydney \
  --gate-email you@example.com --by <yourDeskId> --name "Your Name" --out join-out

join checks that the lane is free, that the connectors the lane needs are present, that the schedule keeps the lane’s minimum spacing, the timezone (default UTC), the gate email, --by and --out. It then prints the permission screen and writes worker-routine.json. Your Desk id is on the Workers page with a Copy control.

Create the scheduled routine from worker-routine.json, then run join again with --routine-id trig_... to also plan the workers/<laneKey> batch that records you as the holder. Apply that batch yourself.

The first run is report-only: it plans work but acts on nothing until a people/<deskId>/decisions/verified--<laneKey> document exists, written on your word after you read a run.

What a worker run does

A run reads its items with worker-items and writes only its own lane:

$RO worker-items --docs <snapshot.json> --worker <laneKey> --by <id> --out items.json
$RO budget-inputs --docs <snapshot.json> --worker <laneKey> --out budget
$RO write --docs <snapshot.json> --run run.json --out out --worker <laneKey> --artifactdata
  • items.json has stopped, verified, todo, approved and waiting.
  • write --worker may touch only the lane’s own workers/, outcomes/ and pending/ paths. When the lane is stopped it prints “stopped: ; nothing written” and exits 0. Reasons are “no worker document”, “left”, “served by someone else” and “released”.
  • The run input needs laneKey, by and runId, and optionally outcomes, gates, budget and health.
  • A mail-draft lane drafts and you send. A mail-send lane has a send gate; you approve on the Desk, and only you can.
  • Outcomes are drafted, sent, skipped or failed.

The daily cap applies per cap.scope. A run counts itself before it does work.

Joiner: leave

$RO leave <desk_url> --docs <snapshot.json> --by <yourDeskId> --out leave-out

It plans status: left and leftAt for your lanes and prints the recorded routine ids. Disable each scheduled routine after you confirm (RemoteTrigger with enabled: false); leave does not do it for you. If you serve no lane it says “You do not serve any lane on this Desk.”

Owner: release a lane

On the Workers page, Release (with a confirm) writes people/<ownerId>/decisions/release--<laneKey> naming the holder. The release is final for that holder. To let them back, remove that document.

Check it worked

  • The Workers page shows your name on the lane after the first run.
  • worker-items shows stopped empty and verified set once you have accepted a run.
  • Re-run join without --lane: your lane no longer shows as free.

Common pitfalls

  • Forgetting to share the Desk. The joiner must be a Contributor first.
  • Applying the owner’s batches from a joiner’s session. meta/ is owner-written and people/ is self-written.
  • Treating the approval email as the approval. It is a notice only. The click on the Desk is the approval, and editing the gate after the click voids it.
  • Expecting a released joiner’s counters to count. The desk cap counts only workers still serving.

Was this page helpful?