In the forking workflow contributors don't push to the canonical repository at all. They fork it, work on their own copy, and open a pull request from the fork. The maintainer's job is a review gate: triage what comes in, run it through checks, and merge only what clears sign-off. It's the model most open-source projects run.

Shelbi models the maintainer's side. Because the branch lives on someone else's fork, Shelbi doesn't cut branches or push here. Each task carries the fork's PR branch in branch:, and the workflow is mostly about the gate: a handoff sign-off status that's owner: user so nothing reaches main without a human (or a Zen-authorized reviewer agent) accepting it.

The board

you@hub — opensource-lib
Tasks · opensource-lib 7 total Workflow: All ▾ Workspace: All ▾ INCOMING (3) TRIAGE (1) IN REVIEW (1) SIGN-OFF (1) MERGED (1) PR #402: add retry PR #398: new export PR #401: perf: cache PR #399: async file PR #395: bump deps backoff format regex API ⎇ shelbi/pr-401-perf-…⎇ shelbi/pr-399-async… PR #404: fix typo in docs PR #405: Windows path bug
h/l col j/k row open n new f filter r refresh

A fork PR lands in Incoming, the orchestrator triages it into scope, the reviewer agent runs it through checks in In Review, and it waits in Sign-off for a maintainer to accept before it's Merged.

The statuses

The forking flow relabels the whole board, so declare its statuses (id

  • name + category) in the project's status catalog first (workflows/statuses.yaml), then reference them by id from the workflow:
# workflows/statuses.yaml — the forking project's status catalog.
statuses:
  - { id: incoming,  name: Incoming,  category: backlog }
  - { id: triage,    name: Triage,    category: ready }
  - { id: in-review, name: In Review, category: active }
  - { id: sign-off,  name: Sign-off,  category: handoff }
  - { id: merged,    name: Merged,    category: done }
  - { id: declined,  name: Declined,  category: archived }

The workflow

# workflows/contribution.yaml — reference-only; identity lives in statuses.yaml
name: contribution
description: Forking flow — review gate for pull requests from forks.
 
git:
  base_branch: main
  merge_strategy: squash               # squash external contributions into one commit
 
statuses:
  - { id: incoming,  owner: user }
  - { id: triage,    owner: agent, agent: orchestrator }
  - { id: in-review, owner: agent, agent: reviewer }
  - { id: sign-off,  owner: user }
  - { id: merged,    owner: user }
  - { id: declined,  owner: user }
 
initial_status: incoming
 
transitions:
  - from: in-review
    to: sign-off
    actions: [push_branch]               # push the fork branch to a review ref; no PR to open
 
  - from: sign-off
    to: merged
    actions: [merge]                     # squash-merge the existing fork PR into main
 
  - from: in-review
    to: declined
    actions: [close_pr]                  # close the PR without merging; the fork keeps its branch

Two side-effects that the feature-branch model uses are deliberately absent here:

  • No open_pr. The contributor already opened the PR from their fork. The task just tracks it. Pre-fill branch: with the PR's head ref so merge and close_pr act on the right PR.
  • No delete_branch. Shelbi can't delete a branch it doesn't own. The fork keeps its branch whether the PR merges or is declined.

The in-review status names an agent: reviewer role, an agent you author to run the untrusted contribution through the project's checks and summarize risk before a human ever looks. The sign-off status stays owner: user: the gate is the whole point.

Orchestrator adjustments

The review gate needs the orchestrator to treat incoming forks as untrusted and never to auto-accept. Encode a triage rule and a hard stop at sign-off:

## Routing rules — forking
 
- **Incoming fork PRs** are untrusted. Never dispatch a contribution to a
  workspace that holds credentials or can reach production. Route `Triage`
  cards only to sandboxed hub workspaces.
- In `Triage`, decline anything out of scope (wrong direction, duplicate,
  no linked issue) into `Declined` with a one-line reason. Promote the
  rest to `InReview` for the `reviewer` agent.
 
## Zen Mode — forking
 
The `SignOff → Merged` edge is `owner: user` and must stay a human
decision. Do **not** auto-merge contributions even when checks are green:
report "`<PR>` passed review and is ready to sign off" and wait. A green
check is necessary but not sufficient for merging someone else's code.

That last rule is the difference between forking and trunk-based: trunk-based leans into auto-merge for your own green work; forking deliberately keeps a human at the gate for code that arrived from outside.

See also

  • Agents — how to author the reviewer role the InReview status dispatches to.
  • Workflows: the branch: task field — pointing a task at an existing fork PR branch instead of cutting one.
  • Trunk-based — the opposite end of the auto-merge spectrum, for work you trust.