# Composing a collaboration

> This section covers how a collaboration is put together from parties, as shown in parties over a partition].

Page: PAG · Orchestration
Canonical: https://banes-lab.com/pag/orchestration#composing-a-workflow

This section is stop 82 of 102 in the learning route. Previous: [01 - Orchestration as declared structure](https://banes-lab.com/pag/orchestration/declared-structure.md). Next: [03 - Shared surfaces](https://banes-lab.com/pag/orchestration/shared-surfaces.md). It builds on [01 - Orchestration as declared structure](https://banes-lab.com/pag/orchestration/declared-structure.md), [38 - Coordination is software](https://banes-lab.com/disciplined-methodology/collaborate/coordination-is-software.md).

This section covers how a collaboration is put together from parties, as shown in [B1·d parties over a partition](https://banes-lab.com/pag/orchestration#composing-a-workflow-panel-d). The methodology page states the premise in [coordination is software](https://banes-lab.com/disciplined-methodology/collaborate/coordination-is-software.md), and the grammar expresses it as documents. [B1·a two terminal nodes](https://banes-lab.com/pag/orchestration#composing-a-workflow-panel-a) shows how a document states its reader class, [B1·b change across ownership](https://banes-lab.com/pag/orchestration#composing-a-workflow-panel-b) shows how a finding travels to its owner, and [B1·c party count](https://banes-lab.com/pag/orchestration#composing-a-workflow-panel-c) shows how the count is derived rather than chosen. A document cannot make the parties agree; it can only make their disagreement land where a reader can see it.

### Parties over a partition

A workflow written as a sequence of agents with fixed positions runs the same shape on every task, and no task has that shape. A workflow names four positions before the work is examined, the work has three concerns, one position spends the run relaying between the other three, and the relay is where every message is lost. A party that holds the order for the others is a party every other party waits on, and a design where finders also fix has parties writing a tree that other parties are still reading.

For this reason a collaboration is a set of parties over a partition of the work, coordinating through surfaces with nothing between them. The parties are derived from the partition rather than assigned positions, and the order is given to the surfaces rather than to a controller. In practice, the work is partitioned into concerns that must be able to contradict each other, and each concern gets one document. The terminal node states the reader class, so the ending is derived: a participant re-enters after a wait, and a bounded reader returns one typed artifact. A change across ownership travels as an item carrying what was observed, what was expected and the one edit, and the owner's act node is the only one that writes.

To check this, take a running collaboration and remove any one document. Where the others stall, that document was a controller; where they route around it, the composition held. One writer and one tree is not a collaboration, and the constructs here defend against a party that cannot exist there. A single document with a single reader takes none of this, and adding it is ceremony.

A document's terminal node states the reader class, and the class decides what the node yields: a participant's node re-enters after a wait, and a bounded reader's yields one typed artifact. Which class a reader belongs to, and why the rules about turns invert for one of them, is explained in coordination is software. A document whose terminal node states its class makes the inversion legible, while one that leaves it to the reader gets both classes' rules applied at once.

Ownership is what replaces the controller. Scope is claimed by concern rather than by location, because two parties can claim one folder through two claims that never mention each other. A finding that lands on a surface its finder does not own is a real finding and a forbidden edit at the same time, and the two rules are reconciled by kind rather than by restraint. The finder's act node emits an item carrying the surface, the location, what it observed, what it expected and the one change, and the owner makes the change. The finder's set of operations forbids the mutation, and its handoff gate carries the evidence that nothing it applied touched a surface it does not own. The conflict between fixing on sight and leaving another party's scope alone therefore has a structural answer rather than one that depends on care.

The count is an output, and what a document carries is the function that derives it rather than a number. The floor is one party per concern, as described in [a concern is a component](https://banes-lab.com/software-architecture/scale/a-concern-is-a-component.md). The ceiling is the point where a stale claim costs more than one more perspective gains, as described in [the ceiling moves by cost](https://banes-lab.com/software-architecture/scale/the-ceiling-moves-by-cost.md).

B1·a two terminal nodes

```pag
# the reader class is derived from what a document receives · its terminal node says which

# NODE 10 — TERMINATE   [evaluative · termination · set-theory · yields: ter-stop boolean]
# a participant · receives what it owns and what is addressed to it, and never returns
CONTRACT:
input:        <items addressed to me> + <open units of my own concern>
transform:    handle what is addressed to me → perform my own clear work → WAIT on the shared surface → re-enter
constraints:  ter-stop is the developer's call; a quiet wait is a fact about the peers, never about the queue
output:       nothing terminal · the loop re-enters at NODE 1
handoff:      <changed> → NODE 1 ORIENT (read the surface whole, then act) | <quiet> → my own work, then WAIT again

# NODE 10 — TERMINATE   [evaluative · termination · set-theory · yields: ter-stop boolean]
# a bounded reader · receives a task and nothing shared, and returns exactly once
CONTRACT:
input:        <the task it received>
transform:    evaluate saturation AND completion AND verification → emit one typed artifact
constraints:  no shared surface is read, so no surface rule binds; an unresolved question is a finding with what would settle it, never a held turn
output:       one typed artifact | a blocked report naming what would settle it
handoff:      TERMINATE
```

B1·b change across ownership

```pag
# a finding on a surface I do not own becomes an item, never an edit · the op-set forbids it, not restraint

FUNCTION emit_repair(finding):
IF owner_of(finding.surface) == <me>: RETURN {route: "act", change: finding.change}
SET item = {kind: artifact, to: [owner_of(finding.surface)], surface: finding.surface, locus: finding.locus, observed: finding.observed, expected: finding.expected, change: finding.change}
PERSIST_ARTIFACT item TO <the shared surface> AS <me>
RETURN {route: "sent", item: item}

# NODE 6 — ACT   [epistemic · formalisation · computation · yields: procedures]
CONTRACT:
input:        findings
transform:    for each finding → emit_repair → apply only what routes to "act"
constraints:  an INVESTIGATE op-set performs no mutation; a mutation on another's surface is a breach whatever its correctness
output:       applied[] + sent[]
handoff:      every finding either applied on my own surface or sent to its owner (yields: boolean)

HANDOFF GATE (evidence-bearing):
rule_id: "ACT"   yields: boolean
[check] no applied change touched a surface I do not own (evidence: applied[].surface)
[check] every sent item names its owner, its locus and the one change (evidence: sent[])
[check] every finding routed exactly once (evidence: applied[] and sent[] partition findings)
result: pass → NODE 7 | a foreign write → REPAIR (owner: NODE 6) | unknown → BLOCKED
```

B1·c party count

```pag
# the party count is an output of the structure, never an input to it

FUNCTION derive_count(work):
ANALYZE_CONTENT work FOR <pairs of surfaces where a change to one forces a change to the other> INTO coupling   # yields: edge-list
EXTRACT_FACTS connected_components FROM coupling INTO concerns                                                 # yields: set
CALCULATE_METRIC floor = count(concerns)                                                                        # one party per concern
ANALYZE_CONTENT claims FOR <how many rest on one surface> INTO fan_in                                          # yields: number
CALCULATE_METRIC ceiling = <the population past which a claim is stale more often than it is useful>
RETURN {concerns: concerns, floor: floor, ceiling: ceiling}
```

B1·d parties over a partition

```mermaid
flowchart TB
work["A body of work"]
concerns["Concerns that must be able to contradict each other"]
parties["One party per concern · each a document, none above the others"]
surfaces["Shared surfaces · what each owns, what is addressed to whom"]
owner["A change to another's surface travels as an item · the owner makes it"]
work --> concerns --> parties --> surfaces --> owner
parties -. nothing here .-> controller["A controller"]
```

## Links to

- [Coordination is software](https://banes-lab.com/disciplined-methodology/collaborate/coordination-is-software.md)
- [A concern is a component](https://banes-lab.com/software-architecture/scale/a-concern-is-a-component.md)
- [The ceiling moves by cost](https://banes-lab.com/software-architecture/scale/the-ceiling-moves-by-cost.md)

## Linked from

- [Handoff signals](https://banes-lab.com/pag/orchestration/handoff-signals.md)
- [Agent templates](https://banes-lab.com/pag/templates/templates-agents.md)
- [The board and the venue](https://banes-lab.com/disciplined-methodology/collaborate/the-board-and-the-venue.md)
