---
title: push
description: "Make a `push` Effect: it pushes the branch of the Workspace to the Binding of its Repo Slot."
---

# `push`

Function, in `@jr2/orchestrator/src/sandbox-effect.ts`. It is an export of `@jr2/orchestrator`, on the [Authoring surface](../authoring-surface.md) page, in Effects.

```ts
function push<
  const O extends PushOptions,
>(
  options?: O,
): EffectLogic<PushInput<O> | void, PushOutput>
```

Make a `push` Effect: it pushes the branch of the Workspace to the Binding of its Repo Slot.
Put it in `actors`, and invoke it from the body of a `workspace()`: `actors: { publish: push({
sign: true }) }`.

It runs in the Sandbox Effector of the Workspace, with the push credential of the
`git.credentials` entry that the Binding matches. The Agent never reads that credential. It
pushes one commit: the tip of the Agent's branch when the Effect starts, or, with `pin`, the
commit that the input names. After an Orchestrator restart, jr2 pushes only that same commit
again. With `sign` or `squash`, the commit on the remote is not the Agent's own. Then jr2 moves
the Agent's branch to it.

Every outcome is output (`PushOutput`): `pushed`, `rejected`, `diverged` or `unknown`. Each
field of [`PushOptions`](type-pushoptions.md) tells what it refuses. When it starts, it also refuses a push
outside a Workspace: `push "<slot>" runs in the Sandbox Effector of a Workspace, and its state is
not inside a workspace() — invoke it from a Workspace's body (ADR-0067)`.

**Parameters**

- `options` ([`PushOptions`](type-pushoptions.md)): Optional. Each field tells what it does.

**Returns**: An Effect, for `actors`.

**Example**

```ts
import { z } from "zod";
import {
  agent,
  defineEvent,
  fail,
  jr2Setup,
  push,
  workspace,
  type Workspaced,
} from "@jr2/orchestrator";

const committed = defineEvent({
  name: "committed",
  description: "Say that the fix is committed.",
  input: z.object({}),
});

type Input = Workspaced<{}, "app">;

const body = jr2Setup({
  types: {} as { context: Input; input: Input },
  events: [committed],
  actors: {
    coder: agent({
      model: "anthropic/claude-haiku-5-5",
      instructions: "You are a careful engineer.",
    }),
    publish: push({ sign: true }),
    fail: fail(),
  },
}).createMachine({
  id: "fix-test",
  context: ({ input }) => input,
  on: { "workspace.lost": ".failed", "agent.fault": ".failed" },
  initial: "coding",
  states: {
    coding: {
      invoke: { src: "coder", input: { prompt: "Fix the failing test, and commit the fix." } },
      // The Agent moves the Machine on with a pick. An Agent's actor never ends, so `onDone`
      // never fires.
      on: { committed: "publishing" },
    },
    publishing: {
      invoke: {
        src: "publish",
        onDone: [
          { guard: ({ event }) => event.output.outcome === "pushed", target: "done" },
          { target: "failed" },
        ],
      },
    },
    done: { type: "final" },
    // Not a final state: the run ends `failed`, and the Workspace deletes the Sandbox.
    failed: { invoke: { src: "fail", input: { reason: "the fix was not pushed" } } },
  },
});

export const machine = workspace(body, {
  repos: { app: { url: "https://github.com/you/app.git" } },
  spec: () => ({ branch: "fix-test" }),
});
```

**See also**

- [Push and sign a branch](../../guides/push-and-sign-a-branch.md)
- [Effects](../../concepts/effects.md)
- [Give the cluster git credentials](../../guides/give-the-cluster-git-credentials.md)
- [ADR-0067: An Effect is an actor slot, and runs at most once unless marked idempotent](../decisions/0067-an-effect-is-an-actor-slot-and-runs-at-most-once-unless-marked-idempotent.md)
- [ADR-0051: A Repo is a slot on the `workspace()` and a cache on the node](../decisions/0051-a-repo-is-a-slot-on-the-workspace-and-a-cache-on-the-node.md)
