Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/architecture/orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,7 @@ if is_orchestrator_mode():
| `EGG_ORCH_SLICE_GLOBAL_MAX_CYCLES` | Slice-DAG: pipeline-wide summed slice-cycle cap (#2137) | `10` |
| `EGG_ORCH_SLICE_FAILURE_GRACE_SECONDS` | Slice-DAG: grace window before failure-cascade marks downstream subtree `BLOCKED_ON_FAILED_DEPENDENCY` (#2137) | `60.0` |
| `EGG_ORCH_STACKED_PR_RECONCILER_INTERVAL_SECONDS` | Slice-DAG: stacked-PR reconciler polling cadence for orphaned child PRs (#2137) | `30.0` |
| `EGG_ORCH_CROSS_REPO_MERGE_GATE_MAX_ATTEMPTS` | Slice-DAG: poll-attempt budget for the cross-repo merge-sequencing gate before a never-merging upstream PR escalates the dependent draft PR to a HITL hold (#3393); rides the stacked-PR reconciler's cadence | `240` |
| `EGG_BRC_EVENT_PUMP` | **Removed** ([#2908](https://github.com/jwbron/egg/issues/2908)). This flag once selected a legacy capped-restart consensus wrapper vs. the event-pump wrapper; the legacy template and the env-var read are both gone, so the orchestrator no longer consults this variable. Operators that referenced it in helm values / pod-spec env can drop the row. | n/a (removed) |
| `EGG_BRC_IDLE_BUDGET_MIN` | BRC idle / no-progress safety budget in minutes ([#2908](https://github.com/jwbron/egg/issues/2908)). The orchestrator event loop's convergence-stall check raises an overseer alert (anomaly `stuck-phase-transition`, priority `high`) at the budget threshold and escalates priority at `2 ×` budget, without transitioning the pipeline to FAILED. Default 30 min is well above the WS7-observed 10–13 min idle ceiling on real BRC phases. Read via `event_loop.get_idle_budget_minutes()` — see [Idle / no-progress safety budget](#idle--no-progress-safety-budget). | `30` |
| `EGG_EVENT_LOOP_OWNER` | **Removed** ([#3164](https://github.com/jwbron/egg/issues/3164)). This flag once selected an in-pod wait-loop vs. the orchestrator-owned BRC event loop; the in-pod wait arm is gone and orchestrator ownership is the only mode, so the variable is no longer read (`get_event_loop_owner` is removed from `orchestrator/env_config.py`). Operators that set it in helm values / pod-spec env can drop the row. | n/a (removed) |
Expand Down
34 changes: 22 additions & 12 deletions docs/architecture/slice-dag.md
Original file line number Diff line number Diff line change
Expand Up @@ -801,23 +801,32 @@ stacked-PR reconciler cadence.

- **Tier A — automated merge-state hold (default).** The dependent slice's PR
opens as a **draft** while the upstream PR is unmerged. A bounded poll watches
the upstream PR's merge state and, on merge, auto-marks the dependent PR ready
via the `mark_pr_ready(repo, pr_number)` gateway verb (wrapping `gh pr
ready`). **Merge detection keys off the PR `mergedAt` / merged boolean, not
head-SHA equality** — a squash or rebase merge produces a merge-commit SHA ≠
the PR head, so SHA-equality would misfire. Two failure terminals fall through
to a HITL hold rather than hanging: an upstream that reaches
**CLOSED-not-merged**, and a poll that exceeds its **attempt bound** (a
never-merging upstream). Both surface on pipeline status.
the upstream PR's merge state via the orchestrator-only gateway routes
`POST /api/v1/gh/pr/merge_state` (read) and `POST /api/v1/gh/pr/ready` (write)
— see [Gateway README](../../gateway/README.md) — and, on merge, auto-marks
the dependent PR ready via the `mark_pr_ready(repo, pr_number)` gateway verb
(wrapping `gh pr ready`). **Merge detection keys off the PR `mergedAt` /
merged boolean, not head-SHA equality** — a squash or rebase merge produces a
merge-commit SHA ≠ the PR head, so SHA-equality would misfire. Two failure
terminals fall through to a HITL hold rather than hanging: an upstream that
reaches **CLOSED-not-merged**, and a poll that exceeds its **attempt bound**
(`EGG_ORCH_CROSS_REPO_MERGE_GATE_MAX_ATTEMPTS`, default 240 ticks, ~2h at the
default 30s reconciler cadence — a never-merging upstream). Both surface on
pipeline status.
- **Tier B — HITL beyond-merge-state hold (opt-in).** For an edge the plan (or
task description) marks with a beyond-merge-state condition — a
release/publish of the upstream repo, a version-pin choice, or a genuine
cannot-continue development block — the dependent PR is held and released
**only by a HITL decision**, never by programmatic detection. Absent that
marker, a cross-repo edge defaults to the Tier-A automated hold.
cannot-continue development block — the dependent slice opts in via the
`[hold:beyond-merge-state]` marker in its `goal` (or a task description); the
dependent PR is then held and released **only by a HITL decision**, never by
programmatic detection. Absent that marker, a cross-repo edge defaults to the
Tier-A automated hold.

All HITL holds (Tier B, plus the two Tier-A failure terminals) route through the
same decision-queue mechanism and share a single release path.
same decision-queue mechanism and share a single release path — each registers a
HITL Decision offering two operator-selectable options: release the hold (marks
the PR ready) or keep it held (terminal; the PR stays draft for manual
handling).

### Per-repo gate, diff & convention scoping

Expand Down Expand Up @@ -911,6 +920,7 @@ on parse failure.
| `EGG_ORCH_SLICE_GLOBAL_MAX_CYCLES` | int | 10 | Pipeline-wide summed slice-cycle cap. *Currently inert — see local cycles row.* |
| `EGG_ORCH_SLICE_FAILURE_GRACE_SECONDS` | float | 60.0 | Grace window before a failure cascade marks the downstream subtree `BLOCKED_ON_FAILED_DEPENDENCY`. |
| `EGG_ORCH_STACKED_PR_RECONCILER_INTERVAL_SECONDS` | float | 30.0 | Reconciler polling cadence for orphaned child PRs. |
| `EGG_ORCH_CROSS_REPO_MERGE_GATE_MAX_ATTEMPTS` | int | 240 | Poll-attempt budget for the cross-repo merge-sequencing gate (#3393) before a never-merging upstream escalates to a HITL hold; ~2h at the default reconciler cadence. See [Cross-repo merge-sequencing hold](#cross-repo-merge-sequencing-hold-two-tier). |

### Per-pipeline vs. global slice caps

Expand Down
1 change: 1 addition & 0 deletions docs/development/STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ orchestrator/
├── concurrent_executor.py # Concurrent phase executor (spawns all agents simultaneously)
├── slice_scheduler.py # Wave-based scheduler for the implement-phase slice DAG: computes execution waves, caps concurrency, two-tier max_cycles accounting, failure-cascade detection (#2137)
├── stacked_pr_reconciler.py # Stacked-PR rebase reconciler: detects child slice PRs whose base branch was deleted after a parent merge and retargets them via gateway rebase_onto (#2137)
├── cross_repo_merge_gate.py # Cross-repo merge-sequencing gate for multi-repo pipelines: auto-readies (or HITL-holds) a dependent slice's draft PR once its cross-repo upstream PR merges; rides the stacked-PR reconciler cadence (#3393 slice-5)
├── action_guards.py # Formal BRC state machine action guards (preconditions for propose/ack/nack/confirm/withdraw)
├── approval_matrix.py # Per-reviewer ACK/NACK matrix for BRC consensus
├── attestation_schemas.py # Attestation payload validation for BRC proposals
Expand Down
1 change: 1 addition & 0 deletions docs/reference/orchestrator-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ Agent role can be omitted when `EGG_AGENT_ROLE` is set.
| `EGG_ORCH_SLICE_GLOBAL_MAX_CYCLES` | Pipeline-wide cap on the summed total of slice re-proposal cycles. Default `10`. Either the local or global cap tripping escalates to HITL. *API live, not yet wired in the run loop — see [Slice-DAG Implement Phase](../architecture/slice-dag.md); #2199.* |
| `EGG_ORCH_SLICE_FAILURE_GRACE_SECONDS` | Grace window (seconds) between a slice failure and the orchestrator marking the downstream subtree `BLOCKED_ON_FAILED_DEPENDENCY`. Default `60`. Allows HITL resolution before the cascade fires. |
| `EGG_ORCH_STACKED_PR_RECONCILER_INTERVAL_SECONDS` | Polling cadence (seconds) of the stacked-PR reconciler that detects child slice PRs whose base branch was deleted after a parent merge. Default `30`. |
| `EGG_ORCH_CROSS_REPO_MERGE_GATE_MAX_ATTEMPTS` | Poll-attempt budget for the cross-repo merge-sequencing gate (#3393), which rides the stacked-PR reconciler's cadence. A never-merging upstream PR that exceeds this many reconcile ticks escalates the dependent draft PR to a HITL hold instead of waiting forever. Default `240` (~2h at the default 30s reconciler interval). See [Slice-DAG Implement Phase](../architecture/slice-dag.md). |
| `AGENT_ANCHOR_ID` | Agent anchor ID (`{role}-{short_container_id}`), auto-set by container spawner |
| `EGG_LIFECYCLE_SECRET` | Bearer token required for lifecycle-control endpoints (HITL resolve/cancel, pipeline CRUD, phase overrides, container spawn/stop). Stored at `~/.config/egg/lifecycle-secret`. Must be exported in the human's shell to run `egg-orch decision resolve`, `egg-orch pipeline delete`, etc. Agent pods never receive it (see #1769). |

Expand Down
16 changes: 16 additions & 0 deletions gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,22 @@ POST /api/v1/gh/pr/close
Request: {repo, pr_number}
Policy: pr_ownership

POST /api/v1/gh/pr/merge_state
Request: {repo, pr_number}
Auth: Bearer {launcher_secret}
Description: Orchestrator-only read of a PR's merge state ({state, mergedAt}).
Powers the cross-repo merge-sequencing gate (#3393): the
orchestrator polls an upstream slice PR to decide when to
mark a downstream draft PR ready. Keys off mergedAt/state,
not head-SHA equality (a squash/rebase merge changes the SHA).

POST /api/v1/gh/pr/ready
Request: {repo, pr_number}
Auth: Bearer {launcher_secret}
Description: Orchestrator-only draft→ready transition (wraps `gh pr ready`).
Write half of the cross-repo merge-sequencing gate (#3393):
called when the upstream PR merges.

POST /api/v1/gh/execute
Request: {args[], require_auth}
Policy: deny-by-default allowlist (parity with /api/v1/git/execute).
Expand Down
8 changes: 4 additions & 4 deletions tests/shared/egg_contracts/test_pr_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,10 @@ class TestNoSurvivingReadSites:
"shared/egg_contracts/models.py",
# This file — the assertion strings reference the deleted names.
"tests/shared/egg_contracts/test_pr_metadata.py",
# The model migration test constructs a legacy pre-1.2 payload with
# the removed keys and asserts they are stripped on load — its
# purpose is to verify the removal, not to read the fields.
"shared/egg_contracts/tests/test_models.py",
# The doc-terminology regression test asserts the docs do NOT
# mention the deleted fields (regression-by-grep).
"tests/docs/test_context_pr_doc_terminology.py",
Expand All @@ -566,10 +570,6 @@ class TestNoSurvivingReadSites:
# parser ignores them); these tests pin that schema, not a
# PRMetadata read.
"tests/test_yaml_tasks_schema.py",
# Contract-model migration tests feed legacy ``context_*`` keys
# to a persisted-contract load and assert they are stripped —
# their purpose is to verify the removal.
"shared/egg_contracts/tests/test_models.py",
# The migration shim itself names the keys it drops.
# (Path may shift; matched as a substring.)
)
Expand Down
Loading