You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 42a0b60
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: spec/ORCHESTRATOR.md
+27-1Lines changed: 27 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -874,7 +874,7 @@ Each task flows through three phases sequentially. Each phase is its own subagen
874
874
- Dispatch skill detects `reviewed` tasks.
875
875
- **Dependency check before dispatch:** all `depends_on` tasks must be in state `shipped` — which per the lifecycle below means their PRs are **merged to base**. A dependency in `merge_pending` (PR open, not yet merged) defers SHIP until it merges. *(Implemented in FORGE-233: the gate's ONLY positive vector is a live RepoHost merge proof — `mergeResult(pr)` confirming merged into the dependency's recorded base at its recorded `reviewed_head_sha`. Local task state, ship records (write-ahead bookkeeping), phases.yaml, and tracker status never satisfy on their own; a `shipped` dependency is still live-probed until FORGE-235 introduces a versioned merge attestation. Enforcement: `phases --ready --phase ship` discovery filtering, `dispatch --phase ship` admission, and `complete --phase ship` defense-in-depth — all emitting a machine-readable `DEPS_NOT_MERGED` envelope with `details.dependency_gate` (versioned discriminated report; `retriable` iff every blocker is a waiting condition). The gc tracker-done→shipped mapping became report-only in the same change — proof-backed promotion is FORGE-235.)*
876
876
-**Reviewed-SHA recording (final-SHA binding, part 1):** when REVIEW passes, the CLI (`complete --phase review`) records the **verified review target** (`verdict.target_sha`, checked against the dispatch-time `review_target_sha` and the current worktree HEAD — see Phase 2) as **`reviewed_head_sha`** — CLI-owned, immutable for the attempt, persisted in the ship record (below). This is the ONLY SHA the ship operation may ship. (Single-host mode records the CLI-verified IMPLEMENT head instead — see §Single-host mode.)
877
-
- **The ship operation** (idempotent + crash-safe; FORGE-234): (1) **write-ahead**: persist/refresh the durable **ship record** at `.forge/orchestrator/tasks/<task_id>/ship-record.json` (reviewed_head_sha, resolved base repo + base branch, then per-side-effect: PR id/URL, merge-attempt status) — the record is written **before** each external side effect and reconciled idempotently after it, so a crash between push, PR-create, and merge recovers via create-or-get; (2) re-run `settings.verify` in the task worktree; (3) verify the local head equals `reviewed_head_sha` — any post-review change (rebase-on-drift, `update-branch`, conflict resolution, third-party push) produces a new SHA that must re-enter verify + re-review before shipping proceeds (dual-host: cross-host review; single-host: CLI re-verification — §Single-host mode); (4) final secrets scan; (5) push; (6) create-or-get the PR via the RepoHost; (7) mark tracker `in_review`. **Forge creates NO standing auto-merge enablement** (`gh pr merge --auto`): GitHub's persisted auto-merge request cannot hold an expected head SHA, and GitHub auto-disables it only for pushes by users *without* write permission — a write-capable push after enablement could therefore merge unreviewed code. The merge is instead the atomic, head-bound step below.
877
+
- **The ship operation** (idempotent + crash-safe; implemented in FORGE-234 as the VERB-ONLY `forge orchestrate ship` — owner decision 2026-07-23: every step is deterministic git/gh/CLI work, no model worker exists for the ship phase; the verb finishes through the `complete --phase ship` choke point, which requires the pinned ship verdict + the fenced `ship_receipt.json` (attempt-scoped proof the operation ran: target SHA, admitted state version, probe disposition, PR identity) + a FRESH live PR-head read before `merge_pending` commits; SHIP policy failures — unsupported host, fork topology, PR conflict, probe failures — park durably via a `reviewed → blocked_on_question` question whose phase-aware answer resolves `retry_ship → reviewed` or cancels while blocked): (1) **write-ahead**: persist/refresh the durable **ship record** at `.forge/orchestrator/tasks/<task_id>/ship-record.json` (reviewed_head_sha, resolved base repo + base branch, then per-side-effect: PR id/URL, merge-attempt status) — the record is written **before** each external side effect and reconciled idempotently after it, so a crash between push, PR-create, and merge recovers via create-or-get; (2) re-run `settings.verify` in the task worktree; (3) verify the local head equals `reviewed_head_sha` — any post-review change (rebase-on-drift, `update-branch`, conflict resolution, third-party push) produces a new SHA that must re-enter verify + re-review before shipping proceeds (dual-host: cross-host review; single-host: CLI re-verification — §Single-host mode); (4) final secrets scan; (5) push; (6) create-or-get the PR via the RepoHost; (7) mark tracker `in_review`. **Forge creates NO standing auto-merge enablement** (`gh pr merge --auto`): GitHub's persisted auto-merge request cannot hold an expected head SHA, and GitHub auto-disables it only for pushes by users *without* write permission — a write-capable push after enablement could therefore merge unreviewed code. The merge is instead the atomic, head-bound step below.
878
878
- On success: task state advances to **`merge_pending`** (non-terminal). Notification `merge_pending` event emitted (`auto_merge: true|false` — true when `ship.merge_policy: 'auto'`, i.e. forge will execute the head-bound merge on green).
879
879
-**The merge step (`'auto'` only; runs on `merge_pending` ticks):** when the platform reports every required check green AND `headSha(pr) == reviewed_head_sha`, forge executes the **atomic head-bound merge**: `gh pr merge --squash --match-head-commit "<reviewed_head_sha>"`. The expected-head check is enforced **server-side at merge time** (GraphQL `expectedHeadOid`) — if the head moved between probe and call, the merge fails and the task enters drift handling. Branch protection is likewise enforced server-side on the call: forge cannot merge red (`--admin` and every bypass path prohibited). Durability trade-off (accepted): with no orchestrator running, nothing merges — the task waits in `merge_pending`, fail-safe (`approval`-equivalent), until the next tick.
880
880
-**`merge_pending → shipped` (terminal) requires RepoHost confirmation** (gc/reconcile or dispatch-tick probe) that the PR merged into the **recorded base repo + base branch** AND the merged PR head equals **`reviewed_head_sha`**. Tracker status is never merge proof (see gc divergence table). Notification `shipped` emitted on confirmation.
@@ -886,6 +886,32 @@ Each task flows through three phases sequentially. Each phase is its own subagen
886
886
- Red PR CI → regress via the changes-requested path with findings injected. Transient git/tracker failures → retry with backoff; after `retry_attempts` failures → `failed`, fatal notification.
887
887
- **`'auto'` preconditions** (`ship.merge_policy: 'auto'`; default is `'approval'` = open PR, human merges): requires `agents.review_host_cli` configured (dual-host review — single-host + `auto` is a settings validation error) AND the RepoHost **honesty probe** passing: the *effective* base-branch rules (classic branch protection + rulesets) enforce at least one blocking required status check; the squash method is allowed; the authenticated identity has write permission; no admin bypass is in play; **the base branch has NO merge queue** (owner decision MQ: a queue-enabled base can queue/merge with no orchestrator running, breaking the head-bound guarantee — merge-queue repos are UNSUPPORTED for `'auto'`; the probe reports `merge_queue_enabled` and the ship path parks fail-closed). Probe failure → **park the task with a question** — never warn-and-merge, never a silent downgrade. (Tracker and repo host are orthogonal — a Linear-tracked repo hosted on GitHub gets the full path. Repos with no RepoHost cannot SHIP at all — see §RepoHost.)
888
888
889
+
### SHIP policy parks — known concurrency limits (FORGE-234; hardening tracked separately)
890
+
891
+
The SHIP policy park (unsupported host, fork topology, PR conflict, honesty-probe
892
+
failure) writes a durable question and resolves it through the `answer` verb. Answer
893
+
selection and state resolution are **two independently serialized writes** — the answer
894
+
file's `wx` write is the single-writer reservation, and the state CAS is separately fenced.
895
+
That is sufficient for the sequential lifecycle and for competing supervisors (the durable
896
+
option always decides), but two narrow windows remain when a park races a *successor
897
+
attempt's* progress:
898
+
899
+
1.**Cancellation vs. successor lifecycle.**`cancel_task` is task-level and applies from
900
+
`blocked_on_question` or `reviewed`. If a superseding SHIP attempt has already moved the
901
+
task to `ready_for_review` (drift) or `merge_pending` before the operator answers, the
902
+
cancellation is published but cannot converge; the operator must use `forge orchestrate
903
+
cancel` directly.
904
+
2.**Orphan repair vs. resolved retry.** A SHIP invocation that snapshots an orphaned park
905
+
can commit its `reviewed → blocked_on_question` repair after a concurrent `retry_ship`
906
+
resolution reported "already applied", re-parking a task the operator just released.
907
+
Re-answering converges.
908
+
909
+
Neither window touches the merge path, the ship proof chain, or a normal ship: parks only
910
+
fire on repositories the orchestrator cannot legitimately ship to at all. Closing them
911
+
properly requires a **shared per-question transaction** covering answer selection and state
912
+
resolution together (rather than two serialized writes) — tracked as its own task so the
913
+
primitive gets a full design review.
914
+
889
915
### Single-host mode
890
916
891
917
If `agents.review_host_cli` is `null`, REVIEW phase is skipped. Task flows IMPLEMENT → SHIP directly. A one-time warning at orchestrator first-run: *"Second-opinion review disabled — running single-host. Forge recommends configuring review_host_cli for adversarial review."*
0 commit comments