Download the PHP package milpa/orchestrator without Composer
On this page you can find all versions of the php package milpa/orchestrator. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Table of contents
Download milpa/orchestrator
More information about milpa/orchestrator
Files in milpa/orchestrator
Download milpa/orchestrator
More information about milpa/orchestrator
Files in milpa/orchestrator
Vendor milpa
Package orchestrator
Short Description Generic event-sourced process engine for the Milpa PHP framework: process definitions composed from milpa/workflow states/transitions/gates, a pure reducer folding an EventStoreInterface log into process state, an auto-advancing runner, and human-gate decision surfaces via milpa/live.
License Apache-2.0
Package orchestrator
Short Description Generic event-sourced process engine for the Milpa PHP framework: process definitions composed from milpa/workflow states/transitions/gates, a pure reducer folding an EventStoreInterface log into process state, an auto-advancing runner, and human-gate decision surfaces via milpa/live.
License Apache-2.0
Please rate this library. Is it a good library?
Informations about the package orchestrator
# Milpa Orchestrator
> **Declare a graph of agents; the framework compiles and governs it.** Event-sourced process orchestration for the Milpa PHP framework: **everything is a process**, a process is a state machine, and **state is a projection of an append-only log**. Human gates carry live decision surfaces whose options map **1:1** to the process's own transitions; self-approval is refused by construction; three MCP tools drive it all. The greenhouse (`example-agent-ready-blog`) proved the loop before this package froze the contracts.
[](https://github.com/getmilpa/orchestrator/actions/workflows/ci.yml)
[](https://packagist.org/packages/milpa/orchestrator)
[](https://www.php.net/)
[](LICENSE)
[](https://getmilpa.github.io/orchestrator/)
`milpa/orchestrator` is the process engine of the Milpa family. It takes a `milpa/workflow`
state machine — states, transitions, and human gates — and runs it as an **event-sourced
process**: nothing stores `current_state`, every read replays an append-only log through a pure
reducer, and human decision points surface as `milpa/live` components whose options can never
drift from the transitions they actually resolve. It has **no ORM, no HTTP kernel, no product
coupling** — the domain (a blog post, an invoice, a support ticket) lives entirely in the
consumer's decision-surface factory and its `process.terminal` listener.
## Install
## The thesis
Two ideas hold the whole engine together:
1. **Everything is a process, and a process is a state machine.** A `ProcessDefinition` is a set
of `milpa/workflow` `StateDefinition`s wired by `TransitionDefinition`s, exactly one marked
initial. A state whose outgoing transitions carry a `GateDefinition` is a **human decision
point**; every other state advances automatically.
2. **State is a projection of the log, never a stored field.** Starting or advancing a process
only ever *appends events* to an `EventStoreInterface`. The current state is whatever the pure
`Reducer` folds those events into — recomputed fresh on every read. Two handles built from the
same instance id over the same log always agree, and neither can cache a stale answer.
Everything else — the auto-advancing runner, the human gate, the three MCP tools — is built on
those two invariants.
## Declare a graph of agents
Since **0.6**, you do not build a process by hand: you DECLARE it, and the framework compiles it
onto everything above. A graph is not a new kind of thing — it is a declaration that **composes
operations**. Each node names a `milpa/command` `#[Operation]` that already exists, with its input
schema, its effect ceiling, its scopes and its attribution, and the graph declares only the wiring,
the channels and where it stops.
Compile it and run it on the engine described in the rest of this README:
### The enum cases are the edges
That is the whole idea. `#[Route(when: Verdict::Failed, …)]` is what a grader's structured output
picks; `#[Route(when: EditorCall::OneMoreRound, …)]` is what a person picks at a gate. **Same
syntax.** The only difference is who produced the enum value and what authority that spent — and
you never write a routing function and a map that must agree with it.
### `atMost` does not stop the run — it asks
`atMost: 3, thenAsk: EditorCall::class` is the loop budget, and it is what makes the cycle legal:
this engine refuses a cycle of ungated transitions because such a cycle has no escape, and a
declared budget IS an escape. When it runs out the graph does not halt somewhere nobody chose — it
opens a gate. Measured, three ways:
| run | path through the log |
|---|---|
| passes first time | `grade → PASSED → publish_done` |
| fails, revises, passes | `grade → FAILED → grade → PASSED → publish_done` |
| always fails | `FAILED → FAILED → FAILED → editor_call → GateOpened` |
And because the gate is this engine's own, **the principal that opened it cannot answer it**: the
agent that wrote the essay structurally cannot be the editor who approves it.
Both identities are read from who the surface authenticated, never from the arguments: `graph:start`
records the verified actor that started the run as its requester, and `graph:decide` takes no
`principal` — its approver is the passkey session or the signature behind the call. A caller the
surface did not verify (MCP over stdio, an unsigned terminal) can start a run but cannot answer a gate.
### Written once, derived from types
| you write | the framework derives |
|---|---|
| the graph's constructor | the channels — the constructor IS the state |
| `Grade(string $rubric, string $draft)` | each node's input binding, by parameter name |
| `GradeReport { $feedback, $verdict }` | each node's output binding, by property name |
| `enum Verdict` | the outgoing edges, and the gate's options |
| a node with no outgoing edge | a terminal — a node always runs, a terminal is a place |
| `#[Operation]` on each node | its input schema, effect ceiling, scopes and attribution |
### Refused rather than guessed
Every one of these fails at compile time, naming the class and what to declare — never on the first
request in production: a routing enum case nobody routed; one state routing on two enums; a node
reading a channel nobody declared; a budget of zero, or a budget with nowhere to escalate; a
decision nobody produces; a node that says it decides and returns no verdict; an edge the
declaration never made; and a node declaring `#[Confirms]` while this compiler cannot yet insert
that pause — because compiling anyway would run an operation that demands confirmation without one.
## Quick example
Define a three-state publishing process — `draft → review → published`, with a human gate on
`review` and a `reject` transition that loops back to `draft` for revision:
Wire the engine and expose it through the three tools:
Now drive the loop — instantiate, hit the gate, submit a decision, reach terminal, and prove the
state was never stored by replaying it from a fresh log:
Had the editor chosen `reject`, the runner would have driven `review --reject--> draft
--submit--> review` and re-opened a fresh gate — the revise-and-resubmit loop, all inside that
one `process_submit_decision` call. (This exact loop is exercised end to end in
`tests/ProcessLoopTest.php`.)
## Composes the family
The orchestrator writes almost no primitives of its own — it *composes* the packages below the
process tier and adds only the folding, running, and gating that turn them into a process engine:
| Package | Role in a process |
|---------|-------------------|
| [`milpa/event-store`](https://packagist.org/packages/milpa/event-store) | **The log.** Every start, transition, gate opening, and decision is an `Event` appended to an `EventStoreInterface`. The engine stores nothing else. |
| [`milpa/workflow`](https://packagist.org/packages/milpa/workflow) | **The gates + self-approval rule.** `ProcessDefinition` is built from workflow `StateDefinition`/`TransitionDefinition`/`GateDefinition`; `HumanGate` delegates the D9 anti-self-approval check to workflow's `GateServiceInterface` rather than reimplementing it. |
| [`milpa/live`](https://packagist.org/packages/milpa/live) | **The decision surfaces.** A `DecisionSurfaceInterface` is a `milpa/live` component whose `options()` must equal the gate's transitions 1:1 — `PendingDecision`'s constructor enforces that invariant, so a stale artifact fails loudly instead of offering actions the gate does not have. |
| [`milpa/tool-runtime`](https://packagist.org/packages/milpa/tool-runtime) | **The three MCP tools.** `process_instantiate`, `process_list_pending_approvals`, and `process_submit_decision` are `#[Tool]`-attributed methods that run through the tool-runtime pipeline like any other agent-callable tool. |
| [`milpa/events`](https://packagist.org/packages/milpa/events) | **The reducer/terminal seam.** The reference `MilpaEventDispatcherInterface` implementation; `ProcessRunner` dispatches `process.terminal` through it exactly once per instance, where a consumer runs whatever domain effect reaching a terminal state should trigger. The runner also DECLARES that event (`OrchestratorEvents::declarations()`) to any dispatcher implementing `DeclaredEvents` the moment it receives one, so the house can count what this package dispatches (greenhouse decisions/0228). |
Because the engine is domain-agnostic, the two things it does **not** own are exactly the two a
consumer supplies: a `DecisionSurfaceFactoryInterface` (what a gate's surface renders for its
domain) and a `process.terminal` listener (what reaching a terminal state *does*).
## Proven in a greenhouse
Before these contracts were frozen, the whole loop was grown and validated inside
**`example-agent-ready-blog`** — the family's greenhouse — as a real `PublishPostProcess`: an
agent drafts a post, submits it, a human editor approves or rejects it through a live decision
surface, and reaching `published` actually publishes the post via a `process.terminal` listener.
This package is that proven loop, lifted out domain-free: the greenhouse kept the blog; the
orchestrator kept the engine.
## Requirements
- PHP **≥ 8.3**
- [`milpa/core`](https://packagist.org/packages/milpa/core) **^0.6**
- [`milpa/event-store`](https://packagist.org/packages/milpa/event-store) **^0.1**
- [`milpa/workflow`](https://packagist.org/packages/milpa/workflow) **^0.1.2**
- [`milpa/events`](https://packagist.org/packages/milpa/events) **^0.2**
- [`milpa/live`](https://packagist.org/packages/milpa/live) **^0.1**
- [`milpa/tool-runtime`](https://packagist.org/packages/milpa/tool-runtime) **^0.5.1**
## Documentation
**Full API reference: [getmilpa.github.io/orchestrator](https://getmilpa.github.io/orchestrator/)** —
generated straight from the source DocBlocks and dressed with the Milpa design system.
## Contributing
Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Please report security
issues via [SECURITY.md](SECURITY.md), and note that this project follows a
[Code of Conduct](CODE_OF_CONDUCT.md).
## License
[Apache-2.0](LICENSE) © Rodrigo Vicente - TeamX Agency.
---
Milpa is designed, built, and maintained by **[Rodrigo Vicente - TeamX Agency](https://teamx.agency/?utm_source=github&utm_medium=readme&utm_campaign=milpa&utm_content=orchestrator)**.
All versions of orchestrator with dependencies
PHP Build Version
Package Version
Requires
php Version
>=8.3
milpa/core Version >=0.12 <1.0
milpa/event-store Version >=0.1 <1.0
milpa/workflow Version >=0.1.2 <1.0
milpa/events Version >=0.2 <1.0
milpa/live Version >=0.4 <1.0
milpa/tool-runtime Version >=0.9 <1.0
milpa/command Version >=0.27 <1.0
milpa/core Version >=0.12 <1.0
milpa/event-store Version >=0.1 <1.0
milpa/workflow Version >=0.1.2 <1.0
milpa/events Version >=0.2 <1.0
milpa/live Version >=0.4 <1.0
milpa/tool-runtime Version >=0.9 <1.0
milpa/command Version >=0.27 <1.0
The package milpa/orchestrator contains the following files
Loading the files please wait ...