Skip to content

[issue-2908][slice-4/6] Flag flip + delete old capped-restart wrapp... - #2951

Merged
jwbron merged 28 commits into
mainfrom
egg/issue-2908-impl2/slice-4
Jun 3, 2026
Merged

[issue-2908][slice-4/6] Flag flip + delete old capped-restart wrapp...#2951
jwbron merged 28 commits into
mainfrom
egg/issue-2908-impl2/slice-4

Conversation

@james-in-a-box

@james-in-a-box james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Context

BRC consensus today depends on the agent re-entering a blocking
egg-orch message wait-loop between every event. That re-entry is a
seam the model can fall out of by emitting a final assistant
message instead of re-entering the wait. Claude usually re-enters;
qwen3.7-max does not (#2906) — it exits success=True after one
match, the wrapper sees no CONSENSUS_CONFIRMED, the 3-restart cap
trips (#2806), and the pipeline FAILs after ~$1 and ~20 min of
churn. Prompt-only mitigations narrow the seam for one model; the
seam itself exists for every model (lineage: #2323, #2064, #2482,
#2036, #1995, #2451).

Changes

Reframe consensus-agent execution from a long-lived participant
that holds blocking waits into a deterministic wrapper-driven
event pump
that invokes the agent one-shot per actionable event,
with continuity carried by a durable per-role memory file. Lands
in six linear slices behind EGG_BRC_EVENT_PUMP (default false
until slice-4):

  1. slice-1 — Foundations. New egg-orch brc next-action,
    brc get-state, brc list-blocking, phase get-context CLI
    subcommands. Durable BRC memory artifact at
    .egg-state/agent-outputs/<role>/brc-memory.md with
    action-scaffolded writes into brc_ack / brc_nack handlers,
    atomic writes via promoted _persist_atomic_template,
    last_reviewed_commit_sha per producer in the schema,
    fail-closed path construction. Gated by
    EGG_BRC_MEMORY=write-only (writes accumulate; reads land
    in slice-3). Purely additive — zero behavior change.
  2. slice-2 — Event-pump wrapper. Rewrite
    orchestrator/consensus_wrapper.py as a deterministic event
    pump gated by EGG_BRC_EVENT_PUMP. Drop the 3-restart cap;
    replace with idle/no-progress safety budget. Migrate the
    heartbeat (Overseer flags BRC reviewers/testers as stalled when they're correctly blocked in mcp__brc__wait_loop (implement-phase false positive) #2036) and gateway-session keep-alive (Orchestrator heartbeat-session lookup fails: container_id missing slice-N segment for non-coder roles #2451) from
    the agent-side handler into the wrapper's blocking wait.
    Heartbeat payload carries slice_id from EGG_SLICE_ID
    (regression guard). Verification is unit-test-only — no
    in-process test double can drive a deployed pod end-to-end
    (the pod-injection avenue was ruled out per Expand integration test coverage #2474); true
    E2E deferred to slice-4 via the egg_stack real-pod
    fixture. Old path retained verbatim alongside.
  3. slice-3 — Delta + prompt collapse. Wire per-event
    invocation to hand the agent the memory delta plus the full
    git log {last_reviewed_commit_sha}..HEAD --not origin/{base_branch} -p
    delta per producer (NOT just orchestrator-side
    changed_artifacts — per REVIEWER-SYNC.md the re-review must
    audit the full delta as a fresh review). Strip the
    STAY-ALIVE / wait-loop / cursor-threading guidance from
    _build_brc_preamble and mission.md; replace with a lean
    event-handler contract. Sandbox image rebuilt + agent pod
    restarted BEFORE slice-4 flag flip.
  4. slice-4 — Flag flip + delete old path. Flip the
    EGG_BRC_EVENT_PUMP / EGG_BRC_MEMORY defaults to on, gated on
    the slice-2 / slice-3 unit + BRC in-process regression suites
    passing on the new default; delete the capped-restart bash, the
    _RECOVERY_SYSTEM_PROMPT, the SSE machinery, and the
    agent-side wait_loop heartbeat code. Rollback plan: revert
    slices 1–3 via git revert if production traffic regresses. The
    qwen3.7-max qwen3.7-max BRC agents end their agentic loop early (success=True) without reaching CONSENSUS_CONFIRMED → consensus-wrapper restart churn #2906 k3s spike that previously validated this
    end-to-end was removed (qwen route unavailable in k3s;
    Claude-route E2E deferred to Rewrite TestCredentialIsolation as k3s-native (currently skipped under k3s) #2585).
  5. slice-5 — Additive CLI prose plumbing + 2 new BRC
    subcommands.
    Add stdin / --reason-file / --summary-file
    / --files-reviewed-file plumbing to
    consensus propose --summary, consensus ack --reason,
    consensus nack --reason, consensus withdraw --reason
    (docs: steer agents to the structured contract tool, not Bash-composed egg-contract #2741 regression guard). Add egg-orch brc resolve-obligation
    and egg-orch brc read-peer-artifact CLI subcommands the
    slice-6 deletion depends on. Argv kept as fallback during
    transition (deprecation warning).
  6. slice-6 — MCP → CLI deletion. Delete the 28 agent-facing
    MCP tools (~1,515 LOC across 7 namespace files + the 4
    infra files + server.py), the SYSTEM_PROMPT_NUDGE, and the
    MCP registration block in shared/egg_agent/client.py:299-353
    INCLUDING the EGG_MCP_TOOLS env flag at :311 (no orphan
    flag). Retire tests/tools/test_mcp_cli_drift.py. Migrate the
    MCP E2E test so the agent's first action is consensus ack/nack via stdin/file (preserves SDK-spawn exercise).
    Verify per-event wall-clock latency within 5%.

Impact

Operator-facing: the BRC consensus subsystem becomes
model-portable — any agent that exits naturally after handling
one event reaches CONFIRMED, instead of needing prompt nudges to
keep re-entering an in-process wait. Cost-per-phase drops because
restart churn disappears, replaced by short bounded per-event
invocations that hit the prefix cache (≥ 60-min TTL on both
routes per WS7 closure). Net code deletion: the capped-restart
template, the SSE machinery, the recovery system prompt, the 28
MCP tool schemas, the EGG_MCP_TOOLS flag, the cursor-threading
guidance, and the agent-side heartbeat all go away. The agent
primitive (pod / worktree / SDK / permissions / restrictions) is
untouched.

This slice

Flag flip + delete old capped-restart wrapper path

Files affected:

  • orchestrator/consensus_wrapper.py
  • sandbox/egg_agent_tools/handlers/message.py
  • orchestrator/tests/test_consensus_wrapper.py
  • tests/sandbox/egg_agent_tools/test_handlers_message.py
  • docs/architecture/orchestrator.md

Tasks:

  • task-4-1: Flip the EGG_BRC_EVENT_PUMP default in orchestrator/consensus_wrapper.py's build_consensus_wrapped_command from false to true. With the default flipped, the new template path is the production path; the old template path is only emitted when an operator sets EGG_BRC_EVENT_PUMP=false explicitly. Same flip for EGG_BRC_MEMORY from off to full so the delta-scoped re-analysis from slice-3 reads the memory file in production. Pre-flight: the slice-2 / slice-3 unit + BRC in-process regression suites pass on the new default (the qwen3.7-max qwen3.7-max BRC agents end their agentic loop early (success=True) without reaching CONSENSUS_CONFIRMED → consensus-wrapper restart churn #2906 k3s spike that previously gated this flip was removed — see the slice goal; Claude-route E2E is blocked on ScriptedProvider pod injection, deferred to Rewrite TestCredentialIsolation as k3s-native (currently skipped under k3s) #2585).
    • Acceptance criteria: build_consensus_wrapped_command with unset env emits the new template; with explicit EGG_BRC_EVENT_PUMP=false emits the old template (the one-release rollback path is preserved); existing snapshot tests updated to reflect the new default; BRC integration suite passes on the new default; rollback plan documented in PR body (git revert slices 1–3 if production traffic shows regression).
  • task-4-2: Delete the old capped-restart bash template, the _RECOVERY_SYSTEM_PROMPT (consensus_wrapper.py:64-99), the SSE consensus.reached machinery (consensus_wrapper.py:418-449), and the MAX_CONSENSUS_RESTARTS constant + use-sites. Keep is_buffer_overflow / is_transient_crash / is_startup_failure classifiers — they're still valid signals under the new idle/no-progress safety budget. Delete the agent-side wait_loop heartbeat path from sandbox/egg_agent_tools/handlers/message.py:267-429 — the wrapper now owns heartbeating (TASK-2-2). Same for the gateway-session keep-alive in the same region (TASK-2-4 migrated; this task deletes the agent-side path).
    • Acceptance criteria: orchestrator/consensus_wrapper.py no longer contains MAX_CONSENSUS_RESTARTS, _RECOVERY_SYSTEM_PROMPT, or SSE / consensus.reached strings; rg 'consensus\.reached|sse_url|_RECOVERY_SYSTEM_PROMPT|MAX_CONSENSUS_RESTARTS' orchestrator/consensus_wrapper.py returns zero matches (defensive grep assertion against partial deletion); handlers/message.py no longer emits heartbeats or refreshes the gateway session; the three crash classifiers remain; relevant tests in orchestrator/tests/test_consensus_wrapper.py updated (or deleted, where old-path-specific tests no longer apply) — replacement coverage lands in TASK-4-3.
  • task-4-3: Update tests in orchestrator/tests/test_consensus_wrapper.py and tests/sandbox/egg_agent_tools/test_handlers_message.py to reflect the deletions in TASK-4-2. Delete tests of the retired capped-restart cap, recovery prompt, SSE path, and agent-side heartbeat / keep-alive. Add coverage for the new wrapper behaviour where it replaces the old (the idle-budget test from slice-2 becomes the canonical liveness coverage).
    • Acceptance criteria: All retired tests removed; remaining tests pass under make test; coverage report does not regress for the consensus_wrapper module (replacement tests cover the equivalent semantics).
  • task-4-4: Documenter: rewrite docs/architecture/orchestrator.md consensus-wrapper section to describe the post-deletion steady state — event-pump as the only path, idle budget replaces restart cap, wrapper owns heartbeat + keep-alive. Remove the "flag-off legacy path" caveats added in slice-2. Document the rollback plan (revert slices 1–3) for completeness. Cross-link to docs/architecture/brc-memory.md from slice-1.
    • Acceptance criteria: Doc reads as if the event pump has always been the only model; legacy-path caveats removed; cross-links present; rollback plan documented; rendering clean.

Test Plan

Per-slice automated coverage:

Manual verification:

  • slice-3 pre-merge: sandbox image rebuilt + agent pod restarted
    so the new mission.md is reachable in the pod BEFORE slice-4
    flag flip.
  • slice-4 pre-flag-flip: human inspects spike output
    (brc-memory content for reasoning fidelity;
    last_reviewed_commit_sha updated per producer; cost-per-phase
    delta vs restart-churn baseline) and
    consents to default-on flip via PR review.
  • slice-6 pre-merge: human verifies no in-flight pipelines are
    mid-run against pre-slice agents; gate deploy on drain or
    cancel.

Manual Steps

Pre-merge:

  • slice-3: sandbox image rebuilt + agent pod restarted BEFORE
    slice-4's flag flip (the new mission.md is the slice-4
    assumption; if pods are still running the old image when the
    flag flips, the event-pump path will reference STAY-ALIVE
    semantics that have been deleted from the preamble).
  • slice-4: human review of spike output (memory content,
    last_reviewed_commit_sha correctness, cost delta)
    before the EGG_BRC_EVENT_PUMP default flips to true.
  • slice-6: confirmation that no
    in-flight pipelines exist before deploy (MCP tools are deleted
    so an old wrapper that still injects them starts with no MCP
    server registered).

Post-merge:

  • slice-4: monitor 24 h of production BRC traffic for any
    "Agent exited without BRC consensus" entries; fall back via
    EGG_BRC_EVENT_PUMP=false deployment env if seen. Rollback
    path for full spike falsification: git revert slices 1–3 (no
    production traffic touched the new path because the flag
    stayed off until slice-4).
  • slice-6: monitor latency dashboards 24 h for per-event
    wall-clock regression beyond the 5% budget.

Stack

  • Position: slice 4 of 6 in pipeline issue-2908-impl2
  • Stacked on top of egg/issue-2908-impl2/slice-3

Slice slice-4 of pipeline issue-2908-impl2. Stacked on top of egg/issue-2908-impl2/slice-3.

egg and others added 18 commits June 2, 2026 23:00
…r branch

The slice-4 coder branch was created based on main but the slice-4
parent at origin/egg/issue-2908-impl2/slice-4 has the slice-1, slice-2,
and slice-3 implementation work that slice-4 builds on. Merge it in
before doing task-4-1 / task-4-2.

Conflicts resolved in orchestrator/routes/pipelines.py and
orchestrator/tests/test_pipeline_prompts.py — both in the BRC
preamble dual-role banner. The coder-owns-tests refinement (#2936)
that landed on main and the event-pump banner collapse from
slice-3 task-3-3 of #2908 both touch the dual-role banner text.

Resolution: keep slice-3's event-pump structure (no wait-loop filter
allowlists in the preamble — the wrapper drives invocation) and
layer in the coder-owns-tests semantics on top: the tester's first
invocation does ORIENT/PREPARE only; the wrapper re-invokes it on
the coder's CONSENSUS_PROPOSE; it does its producer WORK (review +
harden the coder's tests) + PROPOSE + ACK/NACK in that single
invocation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ORY defaults

Slice-4 task-4-1 makes the event-pump wrapper the production default
by flipping two env-flag defaults:

* ``EGG_BRC_EVENT_PUMP`` flips from unset→OFF (legacy) to unset→ON
  (event-pump). Setting ``EGG_BRC_EVENT_PUMP=false`` (or
  ``0`` / ``no`` / ``off``, case-insensitive) keeps the legacy
  capped-restart template available for a one-release rollback
  window. Unrecognised tokens fall through to event-pump so a typo
  cannot silently downgrade the production path. Slice-4 task-4-2
  will delete the legacy template entirely and the env flag with it.

* ``EGG_BRC_MEMORY`` flips from unset→``off`` (slice-1 inert) to
  unset→``full`` (event-pump composer reads memory by default).
  Setting ``EGG_BRC_MEMORY=off`` is the one-release rollback escape
  hatch. Unknown values still fail-safe to ``off`` (the fallback
  target stays restrictive — an undocumented value is a
  misconfiguration signal, NOT a write-bearing default to mask).

Files touched:

* ``orchestrator/consensus_wrapper.py``:
  - ``_event_pump_enabled()`` default flipped; falsy-token allowlist
    captures rollback path; docstring + module-level reframe updated.
  - Wrapper template's inline ``EGG_BRC_MEMORY:-off`` → ``...:-full``
    so the wrapper's invocation of ``event_prompt.py`` inherits the
    new default even on shells that don't export the var explicitly.

* ``sandbox/egg_agent_tools/handlers/brc_memory.py``:
  - ``get_memory_mode()`` defaults to ``MODE_FULL``; new
    ``MODE_DEFAULT`` constant pins the contract.

* ``orchestrator/routes/event_prompt.py``:
  - CLI ``memory_mode`` default flipped from ``"off"`` to ``"full"``.

* Tests updated to match the new defaults:
  - ``orchestrator/tests/test_consensus_wrapper.py``:
    ``TestEventPumpTemplateSelection`` rewritten — unset-env now pins
    event-pump, ``EGG_BRC_EVENT_PUMP=false`` pins legacy.
    ``TestBuildConsensusWrappedCommand``,
    ``TestConsensusWrapperBehavior``, ``TestBufferOverflowDetection``,
    ``TestEventDrivenWait``, ``TestSSESigtermGrace`` gain an autouse
    ``_force_legacy_template`` fixture that engages the rollback
    escape hatch so they continue to drive the legacy template.
    Slice-4 task-4-2 deletes the entire fixture + these classes
    alongside the legacy template.
  - ``tests/sandbox/egg_agent_tools/test_handlers_brc.py``:
    renamed ``test_unset_defaults_to_off`` → ``test_unset_defaults_to_full``;
    the unset-env pin now asserts the memory file is written.
  - ``orchestrator/tests/test_compose_event_prompt.py``: docstring
    note that ``write-only`` is the rollback target, not the default.

Verified manually with Python smoke tests that the flag-flip works
for unset, truthy, and the full falsy-token allowlist
(``false`` / ``0`` / ``no`` / ``off`` / case variants), and that
``EGG_BRC_MEMORY=writeonly`` (typo) still fails safe to ``off`` with
a warning while ``EGG_BRC_MEMORY=full`` and unset both enable
writes + reads.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…menter branch

# Conflicts:
#	orchestrator/routes/pipelines.py
#	orchestrator/tests/test_pipeline_prompts.py
Rewrite docs/architecture/orchestrator.md "BRC Consensus Wrapper" section
(renamed from "BRC Event-Pump Wrapper (slice-2, behind EGG_BRC_EVENT_PUMP)")
to describe the post-deletion steady state. Event-pump is now the only
consensus-wrapper path; the legacy capped-restart template and the
agent-side heartbeat / keep-alive path were removed in slice-4 task-4-2.

Changes:
- docs/architecture/orchestrator.md
  - Renamed section to "BRC Consensus Wrapper"; updated anchor link
    from #brc-event-pump-wrapper-slice-2-behind-egg_brc_event_pump.
  - Replaced the slice-2 caveat blockquote with a four-slice rollout
    summary that names the deleted symbols (_CONSENSUS_WRAPPER_TEMPLATE,
    _RECOVERY_SYSTEM_PROMPT, SSE consensus.reached, MAX_CONSENSUS_RESTARTS).
  - Reframed "Why a new wrapper template" → "Why the wrapper drives the
    loop"; rewrote in past tense so the doc reads as if the event-pump
    has always been the only model.
  - Rewrote "Wrapper-side heartbeat (#2036 migration)" and
    "Wrapper-side gateway-session keep-alive (#2451 migration)" with
    "completed in slice-4" qualifier; described agent-side deletion.
  - Rewrote "Idle / no-progress safety budget" to drop the comparison
    table with the legacy 3-restart cap; replaced with a single
    behaviour table for EGG_BRC_IDLE_BUDGET_MIN.
  - Added new "Rollback plan" subsection documenting git revert of
    slice-4 → slice-3 → slice-2 → slice-1 in reverse-merge order, the
    integration check operators must run, and the partial-revert
    interaction (reverting only slice-4 restores the dual-emission
    state).
  - Renamed "Slice-2 verification stance — unit-test-only" to
    "Verification stance — unit-test-only"; explained that the
    snapshot tests pinning the byte-for-byte legacy template emission
    were retired in slice-4 task-4-3.
  - Renamed "BRC Per-Event Prompt Composer + Preamble Collapse
    (slice-3)" to drop the slice marker; reframed "Flag mapping" to
    "What's gated by what" since EGG_BRC_EVENT_PUMP no longer gates
    anything.
  - Updated EGG_BRC_MEMORY table: full is now the slice-4 default;
    write-only is the opt-in regression path.
  - Updated env vars table: EGG_BRC_EVENT_PUMP is a deprecated no-op
    pointing at the rollback plan; EGG_BRC_IDLE_BUDGET_MIN is no
    longer gated on EGG_BRC_EVENT_PUMP=true.

- docs/guides/concurrent-execution.md
  - Replaced the slice-2 "two emission paths" caveat with a single
    post-deletion summary linking to the new orchestrator.md section.
  - Rewrote the "Consensus Wrapper" body to describe the deterministic
    event-pump loop (steps 1–6) as the only path; removed
    MAX_CONSENSUS_RESTARTS-based restart cap, the recovery system
    prompt, and the final-consensus-check restart cycle.
  - Updated the configuration table: dropped `max_restarts` row;
    added EGG_BRC_IDLE_BUDGET_MIN; updated transient-crash recovery
    paragraph to reference the idle/no-progress budget instead of
    the deleted MAX_CONSENSUS_RESTARTS hard cap.

- docs/architecture/README.md
  - Updated the cross-link card to point at the renamed section and
    summarise the slice-4 deletion + rollback plan.

Cross-links to docs/architecture/brc-memory.md (slice-1) retained
throughout. The wait-side companion at agent-wait-patterns §10 is
referenced from each cross-link card.

Satisfies contract task-4-4. Acceptance: doc reads as if the event
pump has always been the only model; legacy-path caveats removed;
cross-links present; rollback plan documented; markdown renders
clean (no conflict markers; section anchors resolve).
…nd agent-side heartbeat

Slice-4 task-4-2 collapses ``consensus_wrapper.py`` onto the
event-pump template that slice-2 introduced and slice-3 wired the
per-event composer into. The event-pump is now the only production
path; rollback under a regression is a ``git revert`` of slices 1-3
per the PR body, not an env-flag flip.

Deleted from ``orchestrator/consensus_wrapper.py``:

* ``_CONSENSUS_WRAPPER_TEMPLATE`` (the ~600-line legacy
  capped-restart bash template).
* ``_RECOVERY_SYSTEM_PROMPT`` and ``_RECOVERY_USER_PROMPT`` — the
  restart-time recovery prompts.
* The SSE ``consensus.reached`` curl path (issue #1897) that lived
  inside the legacy template — the event-pump uses
  ``egg-orch message wait-loop`` instead.
* ``MAX_CONSENSUS_RESTARTS`` (issue #2806) and its companion
  constants ``MAX_READY_POLL_CYCLES``,
  ``TRANSIENT_RESTART_BACKOFF_INITIAL``,
  ``STARTUP_FAILURE_WINDOW_SECONDS``. The idle/no-progress safety
  budget (env ``EGG_BRC_IDLE_BUDGET_MIN``, default 30 min) is the
  replacement liveness ceiling.
* ``_event_pump_enabled()`` — the ``EGG_BRC_EVENT_PUMP`` env-flag
  read. The flag is now silently inert; operators with it lingering
  in k8s manifests can leave it set to either truthy or falsy and
  still get the event-pump template.
* The legacy-template branch in ``build_consensus_wrapped_command``,
  which is now a thin alias for ``build_event_pump_wrapped_command``.

Preserved by relocating into ``_EVENT_PUMP_WRAPPER_TEMPLATE`` (per
task-4-2 acceptance, "Keep ``is_buffer_overflow`` /
``is_transient_crash`` / ``is_startup_failure`` classifiers"):

* ``is_buffer_overflow()`` — Claude Agent SDK 1 MiB JSON reader
  overflow detector (#2804).
* ``is_transient_crash()`` — signal-based exits (134, 136, 137,
  139, 255).
* ``is_startup_failure()`` — exit 1 within a 30 s startup window.
* ``STARTUP_FAILURE_WINDOW_SECONDS`` — kept as a bash-scope shell
  variable inside the template (was a Python module constant).

The classifiers are not yet wired into the event-pump's
``propose|ack|nack`` agent-invocation failure path (which uses
``AGENT_FAIL_STREAK`` + idle-budget escalation today); they live
as named helpers for future revisions.

Deleted from ``sandbox/egg_agent_tools/handlers/message.py``:

* ``_WAIT_LOOP_HEARTBEAT_INTERVAL_SECS`` — the 60-s cadence
  constant.
* ``_default_emit_wait_loop_heartbeat`` — the agent-side
  ``WAITING_FOR_EVENT`` / ``WORKING`` heartbeat emitter (#2036).
* ``_start_wait_loop_heartbeat`` — the threaded periodic-tick
  helper.
* The per-iteration ``emit_hb`` / ``stop_hb`` calls inside
  ``message_wait_loop``, including the ``try/finally`` block that
  drove the final ``WORKING`` beat on wait exit.

The event-pump wrapper now owns both heartbeat liveness (#2036) and
slice-scoped gateway-session keep-alive (#2451) via the wrapper-
owned ``start_background_heartbeat`` subshell. ``message_heartbeat``
(the explicit handler invoked by ``egg-orch message heartbeat``) is
unchanged — the wrapper calls it.

Test updates:

* ``orchestrator/tests/test_consensus_wrapper.py``: deleted the
  ``TestBuildConsensusWrappedCommand`` / ``TestConsensusWrapperBehavior``
  / ``TestBufferOverflowDetection`` / ``TestEventDrivenWait`` /
  ``TestSSESigtermGrace`` classes (and the ``_force_legacy_template``
  fixture that fed them). The buffer-overflow / SSE / capped-restart
  surfaces they covered no longer exist. The event-pump classes
  (``TestEventPumpTemplateSelection`` and siblings) cover the new
  production path; ``TestEventPumpTemplateSelection`` is reworked
  to pin the post-task-4-2 invariant that ``EGG_BRC_EVENT_PUMP`` is
  silently inert (any value, including ``false`` / ``0`` / ``no``
  / ``off``, emits the event-pump template).
* ``orchestrator/tests/test_consensus_wrapper_anchor.py``: deleted
  in full — every test pinned ``_RECOVERY_SYSTEM_PROMPT`` /
  ``_CONSENSUS_WRAPPER_TEMPLATE`` symbols that no longer exist.
* ``orchestrator/tests/test_brc_nack_iteration.py``: removed
  ``TestConsensusWrapperNackFeedback`` (4 tests) that pinned the
  legacy recovery prompt's NACK-feedback placeholder + helper. The
  equivalent event-pump assertion lives in
  ``orchestrator/tests/test_compose_event_prompt.py``.
* ``tests/sandbox/egg_agent_tools/test_handlers_message.py``:
  removed ``TestMessageWaitLoopHeartbeat`` (16 tests) that pinned
  the agent-side heartbeat path. ``TestMessageHeartbeat`` (the
  explicit handler tests) is unchanged.
* ``integration_tests/regression/test_brc_concurrency.py``:
  updated the slice-2 verification-stance docstring to reflect the
  slice-4 post-deletion steady state (E2E deferred to #2585 via
  ``egg_stack``; in-process tracker coverage unchanged).

Defensive grep assertions all return zero matches on
``orchestrator/consensus_wrapper.py``:

  rg 'consensus\.reached|sse_url|_RECOVERY_SYSTEM_PROMPT|MAX_CONSENSUS_RESTARTS' \
      orchestrator/consensus_wrapper.py
  # → 0 hits

Smoke-verified that the event-pump template is emitted regardless of
``EGG_BRC_EVENT_PUMP`` value, that the three classifiers survive in
the event-pump template, and that the deleted agent-side heartbeat
helpers are no longer importable from ``handlers.message``.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Five blocking findings + three non-blockers from reviewer_code's v1
NACK on PR-proposal v1. Address all five blockers; defer the
non-blockers (env-var coordination is gated on coder task-4-1 / task-4-2
landing first).

Blocker 1+2+3 — restored post-#2936 coder-owns-tests content in
docs/guides/concurrent-execution.md (the v1 proposal overwrote the
post-#2936 wording when I copied the slice-4 base, which predates the
#2936 merge):
- §"HANDOFF" table row example: "Coder can't push test files → HANDOFF
  to tester" → "Tester can't push a .github/ CI fix → HANDOFF to coder
  with the required end-state" (matches docs/reference/agent-roles.md).
- §"Worked Example: Role-Boundary Handoff" rewrite: drop the
  reinstated pre-#2936 coder→tester test-handoff example, restore the
  post-#2936 tester→coder .github/-staging example, and keep the
  explicit lead sentence "the coder→tester test handoff that used to
  live here is gone: the coder now authors and pushes its own tests".
- §"Rebase rarely conflicts" paragraph: "Rebase cannot conflict because
  agents have mutually exclusive file write permissions" / "role
  restrictions guarantee non-overlapping file sets" was a doc lie after
  #2936 — restore the pre-rewrite "rarely conflicts" wording and the
  follow-up paragraph that names the shared test scope and the
  serialize-by-time-not-concurrent invariant.

Blocker 4+5 — dead anchors and stale §10 / §10.9 framing in
docs/reference/agent-wait-patterns.md and docs/architecture/brc-memory.md:
- agent-wait-patterns.md §10 retitled from "BRC Event-Pump Wrapper
  (slice-2, behind EGG_BRC_EVENT_PUMP)" to "BRC Consensus Wrapper
  (event-pump model)"; intro blockquote rewritten to drop the
  slice-2 "OFF by default" framing and instead describe the
  post-deletion steady state + rollback path; §10.8 retitled from
  "Flag-off as the temporary default — when slice-4 flips it" to
  "Rollout completed in slice-4" with body rewritten accordingly;
  §10.9 retitled to drop the (slice-3) suffix and the "Flag mapping"
  blockquote rewritten to "What's gated by what" since
  EGG_BRC_EVENT_PUMP no longer gates anything.
- All five dead inbound anchor references repointed to the new
  anchors: agent-wait-patterns.md lines 1178, 1411, 1653, 1654 and
  brc-memory.md lines 235, 237.
- Reverse direction — repointed orchestrator.md, README.md, and
  concurrent-execution.md cross-links from
  #10-brc-event-pump-wrapper-slice-2-behind-egg_brc_event_pump to
  #10-brc-consensus-wrapper-event-pump-model (3 occurrences in
  orchestrator.md, 1 each in README.md and concurrent-execution.md);
  same for #109-brc-per-event-prompt-composer--preamble-collapse-slice-3
  → #109-brc-per-event-prompt-composer--preamble-collapse.
- Verified via grep across docs/ that no remaining link points at the
  old anchors and no remaining body text carries the
  "(slice-2, behind EGG_BRC_EVENT_PUMP)" framing.

Non-blockers deferred:
- Rollback-plan example precision (compose_event_prompt slice-3 vs
  slice-2 wrapper) — useful tightening but doesn't change the
  correctness of the doc.
- "schema is unchanged" past-tense alignment — minor.
- EGG_BRC_EVENT_PUMP "no-op" vs "removed" wording — gated on coder's
  task-4-1 / task-4-2 final state. Will re-pass once the coder's
  proposal lands so the doc and code agree.
…r branch

Documenter landed task-4-4 (post-deletion consensus-wrapper docs) on the
slice-4 branch in parallel with the coder's task-4-1 + task-4-2 work.
Their merge resolved the dual-role banner conflict by taking the
slice-3 base text (general event-pump description). This commit keeps
the coder's resolution (coder-owns-tests refined: tester's first
invocation does ORIENT/PREPARE only, wrapper re-invokes on coder's
CONSENSUS_PROPOSE carrying proposal in event_payload, tester does
producer WORK + PROPOSE + ACK/NACK in that single invocation) since
the main-branch coder-owns-tests semantics (#2936) need to be preserved
under the event-pump model.

The other documenter changes (orchestrator.md, brc-memory.md
cross-link, etc.) flow through unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…to egg/issue-2908-impl2-slice-4-coder/work
v2 cleared mandate 1 (all v1 blockers fixed) but mandate 2 found four
new blocking findings in agent-wait-patterns.md §10.3 / §10.4 / §10.5
/ §10.7 — subsection bodies still described the flag-off vs flag-on
dual-emission world in present tense as if both paths still shipped,
contradicting the §10 intro blockquote rewritten in v2.

Address all four:

- §10.3 (Heartbeat ownership) — dropped the two-row flag-off vs
  flag-on table; rewrote in past-tense post-migration framing
  mirroring orchestrator.md §"Wrapper-side heartbeat (#2036
  migration completed in slice-4)" — the wrapper owns heartbeating
  now and the pre-#2908 agent-side path in `message_wait_loop` was
  deleted in slice-4 task-4-2.
- §10.4 (Gateway-session keep-alive) — struck the closing "With
  the flag off the agent-side keep-alive still runs" sentence and
  replaced it with the slice-4-deletion qualifier matching §10.3 /
  orchestrator.md.
- §10.5 (Idle / no-progress safety budget) — dropped the parenthetical
  "(replaces the 3-restart FAIL cap)" from the heading; dropped the
  two-row flag-off vs flag-on table; replaced with a single-row
  `EGG_BRC_IDLE_BUDGET_MIN` table mirroring orchestrator.md's
  steady-state version; rewrote the present-tense
  "MAX_CONSENSUS_RESTARTS = 3 cap" framing in past tense.
- §10.7 (Verification stance) — dropped "Slice-2" from the heading;
  rewrote the body in past tense matching orchestrator.md's
  §"Verification stance — unit-test-only"; removed the
  "snapshot equality for the flag-off path" and "deferred to slice-4"
  framing (slice-4 is this work; flag-off snapshot tests were
  retired in slice-4 task-4-3); flipped the integration-tests bullet
  from "runs with EGG_BRC_EVENT_PUMP=false" to "runs against the
  event-pump wrapper".

Adjacent cleanups for body/header coherence:

- §10.1 ASCII diagram: relabelled "LEGACY (flag off, today's
  default)" → "PRE-#2908 (deleted in slice-4 task-4-2 — kept here
  for git-blame readers)" and "EVENT-PUMP (flag on)" → "STEADY
  STATE (event-pump, the only path after slice-4)".
- §10.9.4 EGG_BRC_MEMORY mode table: marked `full` as the slice-4
  default (mirrors orchestrator.md); dropped the slice-3-rollout
  "operators opt into full just as they opt into EGG_BRC_EVENT_PUMP=true"
  paragraph since EGG_BRC_EVENT_PUMP is no longer consulted.
- §10.9.5 `_build_brc_preamble` collapse: rewrote the closing
  paragraph in past tense — the collapse runs unconditionally now
  because the event-pump wrapper is the only path; flipped
  "Slice-4 flips the wrapper default" → "Slice-4 flipped the
  wrapper default" so the doc reads as steady state.
- §10.9.6 `mission.md` sandbox-rebuild paragraph: flipped
  "Slice-4's flag-flip is gated" → past-tense "The slice-4 default
  flip was gated".
- §10.9.7 Composer / preamble verification stance: dropped "Slice-3"
  from the heading; rewrote in past tense matching the §10.7 rewrite;
  removed "deferred to slice-4" since slice-4 is this work.
- §10.9.8 Architect open-decision resolutions: "resolved across
  slices 1–3" → "resolved across slices 1–4".
- §11 Related Documentation cross-link: updated the
  Concurrent Execution Wrapper card from "how the wrapper uses SSE
  + wait-loop" (SSE machinery was deleted in slice-4 task-4-2) to
  "the deterministic event-pump bash loop driver".

The two §10.7 non-blockers (slice-2 contract back-reference at
§10.7 tail, architect-corrected-pseudocode parenthetical) survive
as audit history — the reviewer marked them non-blocking and the
context is still useful for future maintainers tracing the slice-2
design review.
…to egg/issue-2908-impl2-slice-4-documenter/work
… not no-op

Reviewer_code v2 non-blocker #3 was deferred awaiting coder
task-4-1 / task-4-2 final state. The coder's task-4-2 commit
(15664e8) has now landed and the docstring at
orchestrator/consensus_wrapper.py:35 confirms the env var itself
was deleted ("the EGG_BRC_EVENT_PUMP env flag itself"), not just
left as a dead branch.

Update the docs to match:

- docs/architecture/orchestrator.md env-vars table EGG_BRC_EVENT_PUMP
  row: "Deprecated no-op after slice-4" → "Removed in slice-4
  task-4-2"; default "unset (no-op)" → "n/a (removed)"; added the
  helm-values / pod-spec drop-row note for operators that referenced
  it explicitly.
- docs/architecture/orchestrator.md §"Operator-facing env vars
  (cross-link)": "the EGG_BRC_EVENT_PUMP selector is no longer
  consulted — setting it has no effect because the legacy template
  it selected to is gone" → "was removed in slice-4 task-4-2 — the
  env var is no longer read by the orchestrator, so setting it has
  no effect on a post-slice-4 codebase."
- docs/architecture/orchestrator.md §"Rollback plan" partial-revert
  paragraph: tightened the post-slice-4-revert narrative to say the
  env var itself comes back when slice-4 is reverted (because task-4-2
  is what deleted it), and operators wanting event-pump back set
  EGG_BRC_EVENT_PUMP=true (not =false — the defaults flip back to off).
  Also tightened the example of why reverse-merge order matters
  (slice-2 wrapper template references a composer slice-3 added, not
  "a composer that no longer exists").
- docs/reference/agent-wait-patterns.md §10.8: same shift — env var
  was deleted alongside the legacy template, so setting it has no
  effect; rollback path is reverse-merge order.
Fix the six broken tests and four stale docstrings the holistic
reviewer surfaced on v1 (the gateway-blocked test execution missed
them; the structural issues are all visible from grep alone).

Tests (orchestrator/tests/test_consensus_wrapper.py +
orchestrator/tests/test_brc_nack_iteration.py):

* Restored ``import os`` / ``import shlex`` / ``import subprocess`` —
  the surviving event-pump test classes still need them
  (``TestEventPumpConfirmFailureRaisesIdleAlert`` uses
  ``shlex.quote`` for stubbed PATH binaries;
  ``TestEventPumpHeartbeatSubshellLifecycle`` and the
  brc_snapshot tests use ``os.environ``).
* Deleted ``TestEventPumpHeartbeatCadence::test_flag_off_heartbeat_path_unchanged``
  — its invariant ("legacy template does not emit
  ``egg-orch message heartbeat``") no longer applies; the legacy
  template is gone. Replaced with an inline comment cross-linking
  to the post-deletion positive invariant.
* Deleted ``TestEventPumpKeepAliveCadence::test_flag_off_keep_alive_remains_agent_side``
  — same reason.
* Deleted ``TestEventPumpIdleBudgetAlert::test_flag_off_idle_budget_not_used``
  — same reason.
* Deleted ``TestEventPumpRoleCompleteConfirm::test_flag_off_legacy_path_does_not_auto_call_consensus_confirmed``
  — the legacy template is gone; the event-pump's confirm invocation
  is strictly orchestrator-driven via the ``case "$ACTION"`` arms,
  not auto-invoked on agent exit, so the symmetry guard is
  structurally satisfied.
* Renamed ``TestEventPumpFlagIsolation::test_flag_on_does_not_inherit_legacy_max_restarts``
  to ``test_event_pump_relies_on_idle_budget_not_legacy_restart_cap``
  and dropped the ``max_restarts=7`` kwarg (the legacy kwarg was
  deleted from ``build_consensus_wrapped_command`` by task-4-2).
  The remaining assertion — ``EGG_BRC_IDLE_BUDGET_MIN`` is in the
  script — is the salient invariant.
* Deleted ``TestEventPumpInvokesComposer::test_flag_off_legacy_template_does_not_reference_event_prompt``
  — same legacy-path-only invariant.
* Removed the orphaned ``assert "unresolved_nacks" in _CONSENSUS_WRAPPER_TEMPLATE``
  line at the bottom of ``test_brc_nack_iteration.py`` (was left
  outside any function by the original ``TestConsensusWrapperNackFeedback``
  deletion; this is a pure cleanup of slice-4 v1 commit
  15664e8).

Docstrings:

* ``sandbox/egg_agent_tools/handlers/brc_memory.py:546`` —
  ``record_review_event`` docstring updated to reflect the
  slice-4 task-4-1 default flip (``EGG_BRC_MEMORY`` defaults to
  ``full`` now, not ``off``).
* ``orchestrator/routes/event_prompt.py:787`` — CLI docstring
  updated to ``default full``; documents that ``off`` is the
  one-release rollback escape hatch and ``write-only`` keeps the
  writer warm without consuming the excerpt.
* ``orchestrator/consensus_wrapper.py:81`` — module-level template
  comment rewritten: the env-flag predicate is gone, the
  event-pump template is the only template path post-task-4-2.
* ``orchestrator/consensus_wrapper.py:723`` —
  ``build_event_pump_wrapped_command`` docstring rewritten to
  describe the post-task-4-2 reality (no env-flag gate; legacy
  template deleted; ``compose_event_prompt`` already wired).

Defensive (addresses the non-blocking observation #1):

* ``tests/sandbox/egg_agent_tools/test_handlers_message.py:TestMessageHeartbeat``
  gains an autouse ``_isolate_slice_id_env`` fixture that clears
  ``EGG_SLICE_ID``. ``message_heartbeat`` auto-attaches
  ``slice_id`` from that env via ``_maybe_attach_slice_id``, so a
  developer-machine ``EGG_SLICE_ID`` (e.g. inside the egg sandbox)
  would otherwise add an unexpected key to the request body and
  fail the strict-equality assertions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…to egg/issue-2908-impl2-slice-4-coder/work
Blocking finding:
* test_consensus_wrapper.py top-level imports missed ``import sys``;
  ``test_persistent_confirm_failure_fires_overseer_alert`` (the §1 + §6.2
  lock-in test, the most operator-critical assertion in the file)
  uses ``sys.executable`` at line ~1092 and would raise NameError on
  execution, silently disabling the regression guard. The reviewer
  caught it via grep — same shape as the reviewer_code_holistic v1
  NACK that surfaced the missing os/shlex/subprocess imports.
  Fix: add ``import sys`` alongside os/shlex/subprocess.

Non-blocking findings (all addressed in this v3 since they're cheap):
* TestEventPumpIdleBudgetAlert class docstring rewritten —
  ``The old template keeps MAX_CONSENSUS_RESTARTS verbatim`` was
  present-tense framing for the legacy template that task-4-2 deleted.
  Now reads ``The legacy template that owned the historical restart
  cap was deleted in slice-4 task-4-2; the idle budget is now the only
  liveness ceiling in the wrapper.``
* TestEventPumpFlagIsolation class renamed to TestEventPumpIdleBudgetCeiling
  with docstring rewritten — after task-4-2 there is no flag-on /
  flag-off partition to police, so the original name and ``cross-cutting
  guards`` framing no longer apply. The class retains its single
  surviving test (``test_event_pump_relies_on_idle_budget_not_legacy_restart_cap``)
  which is correct against the post-deletion state.
* test_persistent_confirm_failure_fires_overseer_alert inline comment
  rewritten — ``_event_pump_enabled`` was deleted by task-4-2; the
  ``monkeypatch.setenv("EGG_BRC_EVENT_PUMP", "true")`` is harmlessly
  retained as a defensive guard against a future regression that
  re-introduces a flag-gated branch. Comment now reads as such.

The reviewer flagged the docstring drift as non-blocking but I'm
folding it into the same commit because the cost is one edit each
and the docstring↔code mismatch the holistic v1 NACK called out is
the same class of issue. Keeping the surface honest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ocker #2 (test_pipeline_prompts fixture)

Follow-up to v3 (e093f67 pushed) that addressed the reviewer_code
v2 blocker (missing import sys). This commit addresses the
reviewer_code_holistic v2 blocker (2): two pre-existing test failures
in orchestrator/tests/test_pipeline_prompts.py.

Root cause: the slice-4 base-merge in 06c5a6c resolved the conflict
on test_pipeline_prompts.py by keeping slice-3's _PLAN_WITH_MISASSIGNED_TASK
fixture (``role: coder`` + ``files: integration_tests/conftest.py``).
But main's #2936 ("coder authors its own tests; tester reviews-and-
hardens") explicitly excluded coder→test-files from the role↔files
alignment validator. The fixture no longer trips the reject path,
breaking TestPlannerRoleAlignmentValidation::test_rejects_misassigned_plan_at_propose_time
and ::test_rejected_proposal_does_not_mutate_tracker.

Fix: cherry-pick main's fixture update — switch the misassignment
fixture from a test-file path to a docs path (docs/fixtures.md),
which IS still a misassignment, since docs remain the documenter's
scope. Added an explanatory comment above the fixture citing #2936
and the slice-3 merge-resolution context so future readers do not
re-revert under a conflict resolution that "looks like" the slice-3
text.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…x (tester v3 NACK)

Tester v3 NACK had two blockers:

1. ``_auto_populate_contract_at_implement_start`` was deleted from
   ``orchestrator/routes/pipelines.py`` during the slice-4 base merge
   (commit 06c5a6c). The orphan import in
   ``orchestrator/tests/test_auto_populate_contract.py`` broke
   ``pytest --collect-only`` and blocked ``make test`` from running
   any tests at all (collection aborts on the first ImportError).
   Verified by the tester via ``git diff origin/main..origin/egg/issue-2908-impl2/slice-4``
   that the function was dropped, not renamed.

   Fix: restored the function body verbatim from ``origin/main`` (the
   #2915 production implementation) and re-added the call site inside
   the slice-loop-mode gate where it lived on main. The function:

   * lives between ``_check_origin_has_plan_draft`` and
     ``_populate_contract_from_plan_safe`` (matches main's ordering).
   * is called from the ``_use_slice_loop`` check in ``_run_pipeline``
     when ``_slice_count == 0``, exactly as on main.
   * uses ``_populate_contract_from_plan``, ``PopulateOutcome``,
     ``ForestValidationError``, ``_commit_statefiles_to_worktree``,
     and ``_pipeline_identifier`` — all present in the current file
     (no further imports needed).

   The function has a slice-4 v4 banner in its docstring explaining
   the restore so future merge resolutions don't re-drop it.

2. ``orchestrator/consensus_wrapper.py:50`` had a ruff I001 unsorted
   imports failure — an extra blank line between ``import shlex`` and
   the next module-level constant. Fix: removed the extra blank line
   (one-line deletion).

Verified locally:
* ``pytest --collect-only`` no longer aborts on
  ``ImportError: cannot import name '_auto_populate_contract_at_implement_start'``.
* ``orchestrator/tests/test_auto_populate_contract.py`` imports clean.
* ``orchestrator.routes.pipelines`` module imports clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Reviewer_code re-reviewed coder v6 and NACKed with 4 blocking ruff
failures + 1 ruff-format failure that would block ``make lint`` in
CI:

1. ``orchestrator/tests/test_consensus_wrapper.py:13-23`` — I001
   unsorted-import-block (resolved as a side-effect of fixes 2 and 3
   reducing the import block to a single from-import).
2. ``orchestrator/tests/test_consensus_wrapper.py:18`` — F401
   ``pytest`` imported but unused. The two surviving call sites
   inside function bodies use ``import pytest as _pytest`` so the
   top-level name was dead after the v2 test deletions. Fix: remove
   the top-level ``import pytest``.
3. ``orchestrator/tests/test_consensus_wrapper.py:22`` — F401
   ``consensus_wrapper.build_event_pump_wrapped_command`` imported
   but unused (zero references in the file after the test-deletion
   sweep). Fix: drop the second name from the from-import.
4. ``tests/sandbox/egg_agent_tools/test_handlers_message.py:10`` —
   F401 ``threading`` imported but unused. Slice-4 task-4-2
   (15664e8) deleted the threaded ``message_wait_loop`` heartbeat
   machinery; the test cases that exercised it were also removed
   but the top-level ``import threading`` was left behind. Fix:
   remove the now-dead import.
5. ``orchestrator/tests/test_pipeline_prompts.py:5129-5131`` — ruff
   format-check failure on a multi-line assertion message. Pre-
   existing from the slice-3 tester commit 7cff8d1 but surfaced
   only now that the file is in lint scope. Fix: ``ruff format``
   collapses the two-string concatenation into a single line.

Verified locally:
* ``ruff check .`` → ``All checks passed!``
* ``ruff format --check .`` → ``872 files already formatted``
* ``pytest orchestrator/tests/test_consensus_wrapper.py`` →
  33 passed.
* ``pytest tests/sandbox/egg_agent_tools/test_handlers_message.py``
  → 24 passed.
* ``pytest orchestrator/tests/test_pipeline_prompts.py`` →
  431 passed.

Non-blocking observations from reviewer_code v6 (the
_auto_populate_contract restore in routes/pipelines.py and the
v4 consensus_wrapper.py I001 deletion) were already verified-clean
in the prior review and remain unchanged in v7.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@james-in-a-box

This comment has been minimized.

…lice-4: resolve conflicts in consensus_wrapper.py + collapse-section docs

Resolved 3 conflicts:
- docs/architecture/orchestrator.md: kept slice-4's post-deletion narrative (event-pump is sole path)
- docs/reference/agent-wait-patterns.md: kept slice-4's post-deletion narrative; dropped slice-3 "telemetry note (default-off)" callout since slice-4 flipped the default
- orchestrator/consensus_wrapper.py: combined both sides — added slice-3's EGG_REPO_PATH re-export (reviewer_holistic v2 #2) AND kept slice-4 task-4-1's EGG_BRC_MEMORY:-full default
@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Conflict Resolution Summary

Resolved merge conflicts with egg/issue-2908-impl2/slice-3:

File Category Resolution
docs/architecture/orchestrator.md Semantic Kept slice-4's post-deletion narrative: "event-pump wrapper is now the only consensus-wrapper path". Slice-3's text still described both wrappers ("EGG_BRC_EVENT_PUMP selects the wrapper, not the preamble"); that framing is the pre-slice-4 state and becomes obsolete the moment this PR lands per task-4-4. Folded slice-3's _build_brc_preamble-as-anchor cue into the kept paragraph.
docs/reference/agent-wait-patterns.md Semantic Same pattern as above — kept HEAD's post-deletion narrative. Also dropped slice-3's "Operator telemetry note (slice-3 default-off)" callout in §10.9.5: it documents the legacy-wrapper restart-rate behaviour under EGG_BRC_EVENT_PUMP=false, which the task-4-2 deletion of MAX_CONSENSUS_RESTARTS / _RECOVERY_SYSTEM_PROMPT makes inapplicable. Folded slice-3's function-name-as-anchor cue into the kept paragraph.
orchestrator/consensus_wrapper.py Additive (semantic-adjacent) Both sides edited the invoke_agent_for_event env prefix. Combined them: kept slice-3's EGG_REPO_PATH="${EGG_REPO_PATH:-$PWD}" re-export (reviewer_holistic v2 #2 — propagate orchestrator-set value so the script doesn't have to fall back to os.getcwd()) AND kept slice-4 task-4-1's EGG_BRC_MEMORY="${EGG_BRC_MEMORY:-full}" default flip. Comment text was already the slice-3 version on the surrounding lines (auto-merged), which describes both changes.

The remaining auto-merged files (orchestrator/routes/event_prompt.py, orchestrator/tests/test_compose_event_prompt.py, orchestrator/tests/test_consensus_wrapper.py) had no resolved conflict — git auto-merged because the slice-3 and slice-4 edits hit disjoint regions.

Please review:

  • The orchestrator.md and agent-wait-patterns.md resolutions deleted slice-3 framing that described the legacy capped-restart wrapper as still alive. That framing is correct only until slice-4 lands; deleting it is the same change task-4-4 already made in this PR (the "rewrite consensus-wrapper section to describe the post-deletion steady state" acceptance criterion). If a reviewer wants the legacy framing preserved as a historical note, the rollback paragraph in orchestrator.md §"Slice-aware restart_phase hardening" already documents that history via the "revert slices 1–3" rollback plan.
  • The consensus_wrapper.py resolution is a pure superset of both sides' intent. No call site outside this PR depends on the new EGG_REPO_PATH re-export (it's a defense-in-depth value-pass, not a contract change).

Local verification: ruff check orchestrator/ clean; pytest orchestrator/tests/test_consensus_wrapper.py orchestrator/tests/test_compose_event_prompt.py orchestrator/tests/test_brc_preamble_collapsed.py tests/sandbox/egg_agent_tools/test_handlers_message.py → 199 passed.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

…t wrapper

The CI Unit Tests failure on PR #2951 surfaced 18 broken tests; this
commit fixes the 6 caused by Group A — call sites in two test files
that the slice-4 task-4-3 sweep ("delete tests of the retired capped-
restart cap") missed because they referenced ``RESTART_COUNT`` /
"Restarting" / "BRC Consensus Recovery" / ``max_restarts`` /
``startup_failure_window_seconds`` rather than the symbol names listed
in the original task.

orchestrator/tests/test_concurrent_integration.py
  * ``test_spawn_agent_uses_wrapped_command``: assert event-pump
    markers (``event-pump``, ``egg-orch brc get-state``,
    ``egg-orch brc next-action``) instead of the deleted
    ``RESTART_COUNT`` / "BRC Consensus Recovery" strings.
  * Rename ``test_wrapper_contains_restart_logic`` →
    ``test_wrapper_drives_event_pump_loop`` and re-assert against the
    event-pump template. The original invariant ("orchestrator must
    not fake consensus on behalf of agents") is preserved — the
    event-pump never auto-signals READY either.

orchestrator/tests/test_consensus_race_on_exit.py
  * Delete ``TestWrapperStaleTrackerFallback`` (4 tests) plus its
    unused ``os`` / ``shlex`` / ``subprocess`` / ``sys`` / ``tempfile``
    imports. The class exercised
    ``build_consensus_wrapped_command(max_restarts=...,
    startup_failure_window_seconds=...)`` which slice-4 deleted in
    favour of the event-pump template; the event-pump reads BRC state
    directly via ``egg-orch brc get-state`` every loop iteration, so
    the wrapper no longer has a "stale tracker" of its own to fall
    back from. Module-docstring updated to point future readers at
    that history.

Remaining 12 Group B failures (test_short_flow_contract_population,
test_slice_4_restart_hardening) reference orchestrator production code
(``_slice_agents_alive``, ``_resolve_slice_base_branch``'s
``parent_branch_exists`` kwarg, contract-runtime preservation in
``_populate_contract_from_plan``) that exists on ``origin/main`` but
is missing from this branch — see PR-thread comment for the merge-
regression analysis and proposed recovery paths (decision required).
@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Autofixer report — Unit Tests on run 26862885451

Run 26862885451 had 18 unit-test failures, all in orchestrator/tests/. They split into two groups; Group A is fixed by commit 85660f47 on this branch, Group B needs a human decision because it traces to a merge regression rather than a slice-4 code-change.

Group A — 6 failures fixed by 85660f47

These are call sites in two test files that slice-4 task-4-3 ("delete tests of the retired capped-restart cap") missed because they referenced string assertions and kwargs rather than the function/class names listed in the task scope.

Test Symptom
test_concurrent_integration.py::TestSpawnUsesConsensusWrapper::test_spawn_agent_uses_wrapped_command asserted "RESTART_COUNT" + "BRC Consensus Recovery" in the wrapper script
test_concurrent_integration.py::TestNoImplicitReadyOnCleanExit::test_wrapper_contains_restart_logic asserted "Restarting" + "RESTART_COUNT"
test_consensus_race_on_exit.py::TestWrapperStaleTrackerFallback::* (4 tests) called build_consensus_wrapped_command(..., max_restarts=2, startup_failure_window_seconds=0) — both kwargs deleted by slice-4 task-4-2

The fix migrates the first two off the deleted strings onto event-pump markers (event-pump, egg-orch brc get-state, egg-orch brc next-action) and deletes TestWrapperStaleTrackerFallback entirely (plus its now-orphan os / shlex / subprocess / sys / tempfile imports). The stale-tracker fallback that class guarded is moot under the event-pump template — the wrapper reads BRC state directly each loop iteration, so there is no wrapper-side tracker to go stale.

Group B — 12 failures NOT fixed; needs operator decision

These tests all reference production code that exists on origin/main but is missing from this branch's orchestrator/routes/pipelines.py:

Tests Missing production symbol Origin
test_slice_4_restart_hardening.py::TestSliceAgentsAlive::* (7) _slice_agents_alive(...) function + LABEL_SLICE_ID import 3398005b (#2916)
test_slice_4_restart_hardening.py::TestResolveSliceBaseBranch*::* (4) parent_branch_exists kwarg on _resolve_slice_base_branch b8573924 (#2928)
test_short_flow_contract_population.py::test_populate_contract_from_plan_preserves_slice_and_task_runtime (1) runtime preservation merge in _populate_contract_from_plan 66459220 (#2923)

Root cause — git ancestry shows commit 06c5a6cb ("merge slice-4 base into coder branch") brought slice-3 base in but the merge resolution dropped the main-side fixes from #2914/#2928/#2923. pipelines.py blob hash transitioned efd93ec8 (had the fixes) → 7e003565 (lost them). The subsequent fd1a8b66 restored _auto_populate_contract_at_implement_start but did not restore the other three symbols.

Recovery options (operator pick):

  1. Merge origin/main into egg/issue-2908-impl2/slice-4. Structurally correct — main has every missing fix. Risk: large diff balloon (≈357 insertions / 705 deletions in pipelines.py alone) plus likely conflicts to resolve, and the diff will leak into the slice-5/6 stack on rebase.
  2. Surgical cherry-pick of 3398005b, b8573924, 66459220 onto this branch. Picks up just the missing symbols without main's full delta — but the three commits may themselves depend on each other / earlier main commits, so this can snowball.
  3. Delete the 12 failing tests on this branch. Cheapest, but loses the regression detection — these tests guard behaviour that main does implement, so the deletions would have to be reverted as soon as main is merged back in.

Recommendation: (1) if the slice-4/5/6 stack will be rebased onto main before merge anyway (the rebase carries the same conflicts); (2) if you want a minimal slice-4 diff and plan to do a full main merge in slice-6.

— Authored by egg

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author
Autofix tracking
{"Test/Unit Tests": 4}

@james-in-a-box

This comment has been minimized.

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Response to review feedback

Restored the cluster of bad-merge collateral-damage in pipelines.py and test_pipeline_prompts.py per egg-reviewer's analysis. Strategy: take pipelines.py and test_pipeline_prompts.py from origin/egg/issue-2908-impl2/slice-3 (which has all the post-#2936 phrasing the merge dropped), then layer the 6 audit-log attribution restorations from origin/main directly because slice-3 forked before those #2919 hunks landed.

Disposition per blocking item

  1. gateway: allow orchestrator role to execute read-only gh pr list #2893 / orchestrator: flip git-route call sites in pipelines.py from agent_role="coder" to "orchestrator" #2919 audit-log attribution at 6 reconciler hopsfixed-in-PR (commit eb8c6442). Restored agent_role="orchestrator" plus the explanatory comment blocks at all six call sites: _start_stacked_pr_reconciler._list_extant_branches, _start_stacked_pr_reconciler._rebase_onto, two _run_implement_phase_slices.is_slice_branch_merged_into_parent calls (bootstrap + spawn), and _run_implement_phase_slices.create_slice_integration_branch. The list_open_prs site (slice-3 already had it correct) is unchanged. rg 'agent_role="coder"' orchestrator/routes/pipelines.py now returns zero matches.

  2. Let the coder author its own tests; tester reviews-and-hardens #2936 coder-owns-tests phrasing in 5 functionsfixed-in-PR (commit eb8c6442). Restored from slice-3: _ROLE_DESCRIPTIONS tester + coder entries, the tester block in _build_reviewer_preparation, the tester banner in _build_producer_orientation, the tester branch in _build_agent_prompt, and the coder→tester HANDOFF body. The dual-role banner header continues to render (#2749) consistently across the rendered text and the surrounding comment, so the banner-vs-surface contradiction the reviewer described is gone.

  3. Bad-merge incident only partially repairedfixed-in-PR (commit eb8c6442). This is the umbrella finding; items 1 and 2 together discharge it. git diff origin/main -- orchestrator/routes/pipelines.py orchestrator/tests/test_pipeline_prompts.py now shows only slice-3-relevant hunks (compose_event_prompt re-export, _brc_preconfirm_wait_line/_brc_stay_alive_wait_line deletion, _build_brc_preamble collapse) plus their test counterparts.

  4. test_pipeline_prompts.py internal contradictions + dead docstring referencefixed-in-PR (commit eb8c6442). Restored test_tester_prep_waits_for_coder_before_writing_tests (post-Let the coder author its own tests; tester reviews-and-hardens #2936 form: "do not write test" in lower + "consensus_propose" in lower) and test_tester_orientation_directs_review_and_harden_after_propose (post-Let the coder author its own tests; tester reviews-and-hardens #2936 form: "scaffold" not in lower + "do not write test files before" in lower). The test_dual_role_banner_does_not_contradict_coder_owns_tests docstring now correctly refers to the sibling test that actually exists. All three tests pass locally (pytest orchestrator/tests/test_pipeline_prompts.py::TestReviewerPreparation::test_tester_prep_waits_for_coder_before_writing_tests …).

  5. Scope creep vs PR descriptionfixed-in-PR (commit eb8c6442). With items 1, 2, and 4 restored, the pipelines.py and test_pipeline_prompts.py diff is no longer outside slice-4's stated scope: the remaining cross-file hunks are all from slice-3 (the parent of this stack, already reviewed), and slice-4's contribution stays inside consensus_wrapper.py, handlers/message.py, the relevant test files, and the docs.

Disposition per non-blocking item

  • derived_parent inline-string refactor at :10546fixed-in-PR (commit eb8c6442). Reverted to the derived_parent variable, matching slice-3 and main.
  • is_buffer_overflow / is_transient_crash / is_startup_failure classifiers keptdisagree (intentional; covered by task-4-2 plan). The plan explicitly says "Keep is_buffer_overflow / is_transient_crash / is_startup_failure classifiers — they're still valid signals under the new idle/no-progress safety budget." Reviewer flagged this only for future-reader visibility, not as a change request.
  • Deletion of TestMessageWaitLoopHeartbeat / TestWrapperStaleTrackerFallback / TestConsensusWrapperNackFeedback / test_consensus_wrapper_anchor.pydisagree (no change requested). Reviewer noted the deletions are clean with good replacement-coverage comments.
  • TestEventPumpIdleBudgetCeiling new classdisagree (no change requested). Reviewer noted it "looks right for the post-deletion world."
  • test_concurrent_integration.py::test_wrapper_drives_event_pump_loopdisagree (no change requested). Reviewer noted it correctly preserves the "must not auto-signal READY" invariant.
  • docs/architecture/orchestrator.md rollback plandisagree (no change requested). Reviewer noted it's "the right shape for an operator-facing escape hatch."
  • brc_memory.py fail-safe-to-off asymmetrydisagree (no change requested). Reviewer noted "the right asymmetry, and the inline comment captures the reasoning. Clean."

Verification

  • ruff check orchestrator/routes/pipelines.py orchestrator/tests/test_pipeline_prompts.py → All checks passed.
  • ruff format --check on both files → already formatted.
  • The three restored / preserved coder-owns-tests tests pass:
    • test_tester_prep_waits_for_coder_before_writing_tests
    • test_tester_orientation_directs_review_and_harden_after_propose
    • test_dual_role_banner_does_not_contradict_coder_owns_tests
  • grep 'agent_role="coder"' orchestrator/routes/pipelines.py → zero matches.
  • git diff origin/main -- orchestrator/routes/pipelines.py no longer shows the audit-log-attribution or coder-owns-tests hunks; only the slice-3-relevant changes (re-export, deletion, preamble collapse) remain.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

…tor attribution

The previous commit (eb8c644) restored agent_role="orchestrator" in the
_rebase_onto callable per #2919 audit-log attribution but did not update
the test that still asserted "coder".
@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No agent-mode design concerns in the delta since b2380bb1.

The new commits are corrections, not new design:

  • 2d8fa0f4 — merge from slice-3 resolving the dual-role banner conflict
  • eb8c6442 — restoration of accidentally-dropped tester role descriptions (coder-owns-tests framing) and audit-log attribution (agent_role="orchestrator" for orchestrator-driven gateway calls)
  • 70de132b — test alignment for the restored attribution

The banner rephrasing actually reduces prompt-level micromanagement: the scaffold-first directive (which previously instructed the tester to draft test scaffolds from tasks[].files while waiting on the coder) is replaced with "orient only; review-and-harden after the coder proposes." That's a small move toward agent-mode alignment, not against it. The dual-role execution-order banner remains verbose because it has to coordinate a genuinely dual-role agent's lifecycle ordering, but it delegates the per-event body to the existing Reviewer Lifecycle steps (3 → 4 → 5) rather than re-explaining them — which is the right factoring.

The agent_role="orchestrator" changes in _start_stacked_pr_reconciler and _run_implement_phase_slices are about audit-log accuracy for synthetic-session gateway calls, not agent design. No prompt surface or LLM-call shape changes here.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of PR #2951 (slice-4/6)

Re-reviewed against previous review at 2d8fa0f4. Two follow-up commits since:

  • eb8c6442 — "Restore collateral-damage reverts in pipelines.py from slice-3 + audit-log attribution from main"
  • 70de132b — "Fix checks: align rebase_onto agent_role test with restored orchestrator attribution"

Blocking issues from previous review — all resolved

  1. Audit-log attribution (#2893 / #2919) restored — All 9 agent_role="orchestrator" call-sites in _start_stacked_pr_reconciler / _run_implement_phase_slices now match origin/main verbatim (sites at lines 15624, 15627, 15640, 15650, 15682, 15972, 16360, 16424, 16685). The synthetic-session gateway calls correctly identify the orchestrator as the caller instead of impersonating a coder.

  2. Coder-owns-tests framing (#2936) restored — All 5 affected surfaces are back to post-#2936 form:

    • _ROLE_DESCRIPTIONS["coder"] / ["tester"] — coder "implements code changes AND authors their tests"; tester "reviews-and-hardens the coder's tests after the coder proposes"
    • _build_reviewer_preparation tester block — "Do NOT write test files while waiting"
    • _build_producer_orientation tester block — "Orient only until the coder proposes... do NOT write test files before the coder's CONSENSUS_PROPOSE"
    • _build_agent_prompt tester branch — "The coder authors its own tests; your job is to review-and-harden them"
    • HANDOFF body — "auth.py + tests/test_auth.py are in; review and harden the tests"
  3. Bad-base-merge incident fully repaired_resolve_slice_base_branch uses the derived_parent variable again at line 10543.

  4. Contradictory tests fixed

    • test_tester_gets_test_scaffolding → replaced by test_tester_prep_waits_for_coder_before_writing_tests (line 3783) asserting "do not write test" + "consensus_propose"
    • test_tester_orientation_directs_scaffold_first → replaced by test_tester_orientation_directs_review_and_harden_after_propose (line 3851) asserting "harden", "consensus_propose", absence of "scaffold" and "tasks[].files"
    • The docstring reference in test_dual_role_banner_does_not_contradict_coder_owns_tests now resolves to an actually-existing sibling test
  5. Scope no longer creepsgit diff origin/main..HEAD -- orchestrator/routes/pipelines.py now shows only slice-4-relevant deltas (the event_prompt import addition, the deletion of _brc_preconfirm_wait_line / _brc_stay_alive_wait_line, the _build_brc_preamble rewrite for the event-pump model, the event-handler-contract closing block, and the slice-4 flag flips). No more collateral reverts of unrelated main commits.

Targeted tests verified passing locally

  • TestDualRoleExecutionOrdering — 11/11 passed
  • test_tester_prep_waits_for_coder_before_writing_tests — passed
  • test_tester_orientation_directs_review_and_harden_after_propose — passed

Non-blocking observation (suggestion)

The comment block at orchestrator/routes/pipelines.py:12321-12348 still describes the pre-slice-4 wait-loop mechanics — e.g. "the pre-PROPOSE wait-loop in step 1 of the banner below catches the coder's first CONSENSUS_PROPOSE". The banner body it precedes no longer contains a wait-loop step; the event-pump wrapper now drives re-invocation. This is documentation drift that doesn't affect runtime behaviour, but a follow-up pass to update the comment to match the event-pump model would prevent future readers from being misled. Safe to address in a subsequent slice or a small follow-up commit on this branch.

Recommendation

LGTM. All five blocking issues from the previous review have been addressed cleanly, the scope is now properly bounded to slice-4, and the relevant tests pass.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

The comment block at _build_brc_preamble still described the pre-slice-4
wait-loop mechanics ("the pre-PROPOSE wait-loop in step 1 of the banner
below catches the coder's first CONSENSUS_PROPOSE"). Under the slice-4
event-pump model the banner body it precedes no longer contains a
wait-loop step — the wrapper re-invokes the agent on each upstream
CONSENSUS_PROPOSE instead.

Rewrite the comment to match the rendered banner: the tester orients,
exits, and is re-invoked by the event-pump wrapper when the coder
proposes; subsequent re-proposes and peer-producer proposals likewise
surface as fresh wrapper invocations rather than wait-loop wakes.

Pure documentation; no runtime behaviour change.
@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Response to review feedback

Addressing the latest re-review (70de132b) from egg-reviewer, which marked all five previously-blocking issues as resolved and left one non-blocking observation.

Disposition per item

1. Comment block at pipelines.py:12330-12348 still describes pre-slice-4 wait-loop mechanicsfixed-in-PR (commit 3a300e7). The reviewer noted: "the pre-PROPOSE wait-loop in step 1 of the banner below catches the coder's first CONSENSUS_PROPOSE — the banner body it precedes no longer contains a wait-loop step; the event-pump wrapper now drives re-invocation."

Rewrote the comment to match the rendered banner the event-pump path emits:

  • The tester orients up-front, exits after ORIENT, and is re-invoked by the event-pump wrapper when the coder proposes (rather than blocking on a pre-PROPOSE wait-loop).
  • The two reviewer rendezvous points (the coder's first CONSENSUS_PROPOSE and subsequent re-proposes / peer-producer proposals) are described as fresh wrapper invocations, not wait-loop wakes.
  • The (#2749) reference is preserved; the f4c7d780 / 8b81ed32 self-block context is preserved verbatim because that history remains the load-bearing reason the banner needs to coordinate the ordering at all.

The reviewer framed this as "safe to address in a subsequent slice or a small follow-up commit on this branch" — the small follow-up commit on this branch is the cheaper option, so I took it in-PR rather than deferring (a "non-blocking" tag isn't an explicit defer request).

Verification

  • Pure comment change — no runtime behaviour delta. ruff check orchestrator/routes/pipelines.py clean; ruff format --check clean.
  • rg 'pre-PROPOSE wait-loop|wait-loop in step 1' . → zero matches (no stale references elsewhere).
  • All five blocking items from the prior review remain resolved (no regressions in the commit).

— Authored by egg

@james-in-a-box

This comment has been minimized.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No agent-mode design concerns in the delta since 70de132b.

The single new commit (3a300e7e) is a comment-only update to the dual-role
banner block in _build_brc_preamble. The rewrite reinforces the slice-4
event-pump posture: the tester is now described as "orients, exits, and is
re-invoked by the event-pump wrapper" rather than as holding an in-process
pre-PROPOSE wait-loop. Both reviewer rendezvous points are reframed as fresh
wrapper invocations rather than wait-loop wakes — exactly the model-portable,
one-shot-per-event shape this slice exists to land.

No new pre-fetching, structured-output-for-humans, post-processing,
prompt-level security, direct-LLM-API, or hardcoded-model issues introduced.

— Authored by egg

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of PR #2951 (slice-4/6) at 3a300e7e

Re-reviewed against my previous review at 70de132b (which was a --comment LGTM with one non-blocking suggestion). Delta since: a single commit 3a300e7e touching only the comment block at orchestrator/routes/pipelines.py:12330-12348. Net: +13 / −12 lines, no test or doc changes.

Previous non-blocking suggestion — resolved

The comment block preceding the dual-role banner in _build_brc_preamble used to describe pre-slice-4 wait-loop mechanics ("the pre-PROPOSE wait-loop in step 1 of the banner below catches the coder's first CONSENSUS_PROPOSE"). After this commit it reads:

...it orients up-front, exits after ORIENT, and is re-invoked by the event-pump wrapper when the coder proposes, at which point it hardens + proposes + ACK/NACKs in one pass... The tester therefore has TWO reviewer rendezvous points, both surfaced as fresh wrapper invocations under the event-pump model: (a) the coder's first CONSENSUS_PROPOSE re-invokes the tester so it has something to harden; (b) subsequent re-proposes and peer-producer proposals... likewise re-invoke the tester...

This is consistent with the rendered banner that follows:

  • Banner step 1: "the event-pump wrapper invokes you again when the upstream producer's CONSENSUS_PROPOSE arrives, at which point you handle the review AND (if your WORK was gated on it) start producing."
  • Banner step 2: "On an upstream producer's PROPOSE, the wrapper re-invokes you with the proposal in your event payload."
  • Banner step 3: "Subsequent re-proposes... and CONSENSUS_RE_REVIEW events surface as new wrapper invocations."

Comment and banner are now telling the same story.

Verified no stale references remain

rg 'pre-PROPOSE wait-loop|wait-loop in step' in source returns zero matches. The lone remaining reviewer wait-loop mention at :12339 is the historical self-block citation (f4c7d780 / 8b81ed32), which is correctly preserved verbatim because that history is the load-bearing reason the dual-role banner needs to coordinate ordering at all.

Other blocking items from earlier reviews — still resolved

All five blocking items from the 2d8fa0f4 review (audit-log attribution #2893/#2919, coder-owns-tests framing #2936 across 5 surfaces, bad-base-merge _resolve_slice_base_branch repair, contradictory test renames, scope creep) remain fixed at this commit — no regressions introduced by the comment-only delta.

Recommendation

LGTM. Pure documentation alignment with the event-pump model; no runtime behaviour delta; correctly addresses the prior non-blocking observation.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author
egg is addressing review feedback...

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

Response to review feedback

Re-read all authorized feedback (egg-reviewer formal reviews + line-level comments + jwbron / james-in-a-box review-process comments) at HEAD 3a300e7e.

Latest review state

The two most recent reviews from egg-reviewer[bot] at 3a300e7e are both LGTM:

No line-level review comments remain (gh api repos/jwbron/egg/pulls/2951/comments filtered to authorized users returns []).

Disposition per item

All blocking items from prior review rounds

  1. Audit-log attribution (gateway: allow orchestrator role to execute read-only gh pr list #2893 / orchestrator: flip git-route call sites in pipelines.py from agent_role="coder" to "orchestrator" #2919) at 6 reconciler hopsfixed-in-PR (commit eb8c6442). Restored agent_role="orchestrator" plus explanatory comment blocks at all six sites. Confirmed unchanged at 3a300e7e.
  2. Coder-owns-tests framing (Let the coder author its own tests; tester reviews-and-hardens #2936) in 5 surfacesfixed-in-PR (commit eb8c6442). _ROLE_DESCRIPTIONS, _build_reviewer_preparation, _build_producer_orientation, _build_agent_prompt, HANDOFF body all restored to post-Let the coder author its own tests; tester reviews-and-hardens #2936 form. Confirmed unchanged at 3a300e7e.
  3. Bad-merge collateral damage fully repairedfixed-in-PR (commit eb8c6442). derived_parent inline-string refactor at :10546 reverted; the git diff origin/main -- orchestrator/routes/pipelines.py is now constrained to slice-4-relevant deltas.
  4. test_pipeline_prompts.py contradictions + dead docstringfixed-in-PR (commit eb8c6442). test_tester_prep_waits_for_coder_before_writing_tests and test_tester_orientation_directs_review_and_harden_after_propose restored to post-Let the coder author its own tests; tester reviews-and-hardens #2936 form; sibling-test reference in test_dual_role_banner_does_not_contradict_coder_owns_tests now resolves.
  5. Scope creep vs PR descriptionfixed-in-PR (commit eb8c6442). With items 1–4 restored, the cross-file diff is bounded to slice-4 (event-pump wrapper, agent-side heartbeat deletion, flag flips, _brc_preconfirm_wait_line / _brc_stay_alive_wait_line deletion, _build_brc_preamble collapse, doc updates).

Non-blocking suggestion from the 70de132b review

  • Comment block at pipelines.py:12330-12348 describing pre-slice-4 wait-loop mechanicsfixed-in-PR (commit 3a300e7e). Rewrote the comment to describe the event-pump re-invocation model (tester orients up-front, exits after ORIENT, re-invoked by the wrapper on coder PROPOSE). Reviewer confirmed resolved at 3a300e7e: "Comment and banner are now telling the same story... rg 'pre-PROPOSE wait-loop|wait-loop in step' in source returns zero matches."

Non-blocking observations earlier flagged but not requested as changes

  • is_buffer_overflow / is_transient_crash / is_startup_failure classifiers kept — disagree (intentional per task-4-2 plan; reviewer flagged only for future-reader visibility, not as a change request).
  • Deletion of TestMessageWaitLoopHeartbeat / TestWrapperStaleTrackerFallback / TestConsensusWrapperNackFeedback / test_consensus_wrapper_anchor.pydisagree (no change requested; reviewer noted "Clean" with good replacement-coverage comments).
  • TestEventPumpIdleBudgetCeiling new class — disagree (no change requested; reviewer noted "looks right for the post-deletion world").
  • test_concurrent_integration.py::test_wrapper_drives_event_pump_loopdisagree (no change requested; reviewer noted it correctly preserves the "must not auto-signal READY" invariant).
  • docs/architecture/orchestrator.md rollback plan — disagree (no change requested; reviewer noted "the right shape for an operator-facing escape hatch").
  • brc_memory.py fail-safe-to-off asymmetry — disagree (no change requested; reviewer noted "the right asymmetry, and the inline comment captures the reasoning. Clean").

Verification

No new actionable feedback to address at 3a300e7e. Both latest reviews are LGTM. No code changes pushed in this round. CI will run on HEAD; the prior round's make lint was clean and the targeted tests (TestDualRoleExecutionOrdering 11/11, test_tester_prep_waits_for_coder_before_writing_tests, test_tester_orientation_directs_review_and_harden_after_propose) were verified passing.

— Authored by egg

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

egg feedback addressed. View run logs

24 previous review(s) hidden.

@jwbron
jwbron changed the base branch from egg/issue-2908-impl2/slice-3 to main June 3, 2026 16:19
…slice-4

# Conflicts:
#	orchestrator/routes/pipelines.py
#	orchestrator/tests/test_slice_run_loop_integration.py
@jwbron
jwbron merged commit c03f316 into main Jun 3, 2026
19 checks passed

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of PR #2951 at 136b9ad5

Delta since my LGTM at 3a300e7e: a single merge commit 136b9ad5 ("Merge remote-tracking branch 'origin/main' into egg/issue-2908-impl2/slice-4") that brings main into the PR branch and resolves conflicts in orchestrator/routes/pipelines.py and orchestrator/tests/test_slice_run_loop_integration.py. No new PR-authored commits.

Per GitHub's compare (3a300e7e...136b9ad5), the only PR-side contribution to pipelines.py since the prior review is the same +13 / −12 comment-block update I already approved (event-pump rewrite of the dual-role banner preamble); everything else in the delta is content arriving from origin/main, which is out of scope for this PR's design review.

Agent-mode design assessment

No new agent-mode concerns in this delta:

  • No new pre-fetching — the merge does not introduce baked-in diffs or file contents into prompts.
  • No structured-output-for-humans — no new JSON-shaped outputs targeting PR comments or reviews.
  • No post-processing pipelines — no new agent-output-parsing scripts.
  • No rigid step-by-step procedures added — the event-pump posture from slice-4 is preserved.
  • No prompt-level security constraints — sandbox/gateway remain the enforcement boundary.
  • No direct Anthropic API calls from orchestrator/gateway/shared.
  • No Agent-SDK bypass introduced.
  • No hardcoded model identifiers introduced.

The five previously-blocking items from the 2d8fa0f4 round (audit-log attribution #2893/#2919, coder-owns-tests framing #2936, bad-base-merge _resolve_slice_base_branch repair, contradictory test renames, scope creep) all remain resolved at 136b9ad5 — no regressions reintroduced by the merge resolution.

Recommendation

LGTM. Merge-only delta; the slice-4 event-pump posture and all prior fixes are preserved. PR is already in MERGED state; review is confirmatory.

— Authored by egg

@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

egg agent-mode-design completed. View run logs

24 previous review(s) hidden.

@egg-reviewer egg-reviewer Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review of PR #2951 at 136b9ad5

PR is in MERGED state; this is a post-merge confirmatory re-review.

Delta since my prior LGTM at 3a300e7e: a single merge commit 136b9ad5 ("Merge remote-tracking branch 'origin/main' into egg/issue-2908-impl2/slice-4") resolving conflicts in orchestrator/routes/pipelines.py and orchestrator/tests/test_slice_run_loop_integration.py. No new PR-authored production code.

Conflict resolution audit

I verified each conflict resolution against both sides of the merge (slice-4 head 3a300e7e and main 0b005b93) plus the shared base 7bdb29d8:

orchestrator/routes/pipelines.py

  • The slice-4 PR-only contribution is unchanged at 136b9ad5: the same +13 / -12 rewording of the _build_brc_preamble comment block describing the dual-role tester's event-pump lifecycle (lines 12087–12106). I previously approved this hunk and confirm the merge did not regress it.
  • Main's overlapping changes — removal of _handle_pr_creation_failure, _format_rescue_hint, _pr_metadata_from_plan_draft, _build_github_staging_manual_step; removal of the mode=gateway_mode parameter from the list_open_prs callsite at _open_context_pr_at_implement_start; removal of agent_role from the _list_open_prs closure inside _start_stacked_pr_reconciler (now routes through the launcher-authed control-plane endpoint per #2925); and updated guidance text in _build_role_restrictions_section / _build_file_boundary_section for .github-staging/ — are all applied cleanly. None of these conflict with slice-4's intent.
  • Critically, the #2919 audit-log attribution flip from agent_role="coder"agent_role="orchestrator" that slice-4 made (and that the prior CHANGES_REQUESTED review forced restored) is preserved at every site that survives the merge: lines 15407, 15439, 15729, 16117, 16188, 16449 in HEAD. The merge of main brought in companion changes that also attribute these calls to the orchestrator (or remove the agent_role parameter entirely where the call now uses launcher auth), so the resolution is internally consistent.

orchestrator/tests/test_slice_run_loop_integration.py

  • Both slice-4 and main independently flipped the agent_role assertion in TestStartStackedPrReconciler from "coder""orchestrator". They differ only in the surrounding comment text. The merge resolution kept main's more detailed comment (rebase + force-push + pr-edit attribution + slice-integration exemption). The assertion itself is identical to slice-4's intent.

Five previously-blocking items remain resolved at HEAD

The cluster from the 2d8fa0f4 round (#2893 / #2919 audit-log attribution, #2936 coder-owns-tests framing, _resolve_slice_base_branch bad-base-merge repair, the contradictory test_tester_* renames, and scope creep in pipelines.py) all stay clean at 136b9ad5. No regressions reintroduced by the merge resolution.

No new findings

  • No security concerns introduced by the merge.
  • No correctness regressions: the conflict resolution preserves both branches' intent.
  • No new tests required: both sides already had test coverage for the migrated agent_role attribution.
  • The PR-vs-main delta (git diff origin/main..HEAD -- orchestrator/routes/pipelines.py) is now down to 23 lines (the comment-block update), which is the smallest the surface has been across the PR's lifetime — the scope-creep concern is fully resolved.

Recommendation

LGTM (confirmatory). Merge-only delta; the slice-4 event-pump posture, the prior round of regression repairs, and main's recent changes (control-plane list_open_prs, _handle_pr_creation_failure removal, .github-staging/ guidance update) are all coherently composed at HEAD.

— Authored by egg

jwbron added a commit that referenced this pull request Jun 3, 2026
#2952)

* feat(#2908 slice-4 task-4-1): flip EGG_BRC_EVENT_PUMP and EGG_BRC_MEMORY defaults

Slice-4 task-4-1 makes the event-pump wrapper the production default
by flipping two env-flag defaults:

* ``EGG_BRC_EVENT_PUMP`` flips from unset→OFF (legacy) to unset→ON
  (event-pump). Setting ``EGG_BRC_EVENT_PUMP=false`` (or
  ``0`` / ``no`` / ``off``, case-insensitive) keeps the legacy
  capped-restart template available for a one-release rollback
  window. Unrecognised tokens fall through to event-pump so a typo
  cannot silently downgrade the production path. Slice-4 task-4-2
  will delete the legacy template entirely and the env flag with it.

* ``EGG_BRC_MEMORY`` flips from unset→``off`` (slice-1 inert) to
  unset→``full`` (event-pump composer reads memory by default).
  Setting ``EGG_BRC_MEMORY=off`` is the one-release rollback escape
  hatch. Unknown values still fail-safe to ``off`` (the fallback
  target stays restrictive — an undocumented value is a
  misconfiguration signal, NOT a write-bearing default to mask).

Files touched:

* ``orchestrator/consensus_wrapper.py``:
  - ``_event_pump_enabled()`` default flipped; falsy-token allowlist
    captures rollback path; docstring + module-level reframe updated.
  - Wrapper template's inline ``EGG_BRC_MEMORY:-off`` → ``...:-full``
    so the wrapper's invocation of ``event_prompt.py`` inherits the
    new default even on shells that don't export the var explicitly.

* ``sandbox/egg_agent_tools/handlers/brc_memory.py``:
  - ``get_memory_mode()`` defaults to ``MODE_FULL``; new
    ``MODE_DEFAULT`` constant pins the contract.

* ``orchestrator/routes/event_prompt.py``:
  - CLI ``memory_mode`` default flipped from ``"off"`` to ``"full"``.

* Tests updated to match the new defaults:
  - ``orchestrator/tests/test_consensus_wrapper.py``:
    ``TestEventPumpTemplateSelection`` rewritten — unset-env now pins
    event-pump, ``EGG_BRC_EVENT_PUMP=false`` pins legacy.
    ``TestBuildConsensusWrappedCommand``,
    ``TestConsensusWrapperBehavior``, ``TestBufferOverflowDetection``,
    ``TestEventDrivenWait``, ``TestSSESigtermGrace`` gain an autouse
    ``_force_legacy_template`` fixture that engages the rollback
    escape hatch so they continue to drive the legacy template.
    Slice-4 task-4-2 deletes the entire fixture + these classes
    alongside the legacy template.
  - ``tests/sandbox/egg_agent_tools/test_handlers_brc.py``:
    renamed ``test_unset_defaults_to_off`` → ``test_unset_defaults_to_full``;
    the unset-env pin now asserts the memory file is written.
  - ``orchestrator/tests/test_compose_event_prompt.py``: docstring
    note that ``write-only`` is the rollback target, not the default.

Verified manually with Python smoke tests that the flag-flip works
for unset, truthy, and the full falsy-token allowlist
(``false`` / ``0`` / ``no`` / ``off`` / case variants), and that
``EGG_BRC_MEMORY=writeonly`` (typo) still fails safe to ``off`` with
a warning while ``EGG_BRC_MEMORY=full`` and unset both enable
writes + reads.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(#2908 slice-4 task-4-4): post-deletion consensus wrapper docs

Rewrite docs/architecture/orchestrator.md "BRC Consensus Wrapper" section
(renamed from "BRC Event-Pump Wrapper (slice-2, behind EGG_BRC_EVENT_PUMP)")
to describe the post-deletion steady state. Event-pump is now the only
consensus-wrapper path; the legacy capped-restart template and the
agent-side heartbeat / keep-alive path were removed in slice-4 task-4-2.

Changes:
- docs/architecture/orchestrator.md
  - Renamed section to "BRC Consensus Wrapper"; updated anchor link
    from #brc-event-pump-wrapper-slice-2-behind-egg_brc_event_pump.
  - Replaced the slice-2 caveat blockquote with a four-slice rollout
    summary that names the deleted symbols (_CONSENSUS_WRAPPER_TEMPLATE,
    _RECOVERY_SYSTEM_PROMPT, SSE consensus.reached, MAX_CONSENSUS_RESTARTS).
  - Reframed "Why a new wrapper template" → "Why the wrapper drives the
    loop"; rewrote in past tense so the doc reads as if the event-pump
    has always been the only model.
  - Rewrote "Wrapper-side heartbeat (#2036 migration)" and
    "Wrapper-side gateway-session keep-alive (#2451 migration)" with
    "completed in slice-4" qualifier; described agent-side deletion.
  - Rewrote "Idle / no-progress safety budget" to drop the comparison
    table with the legacy 3-restart cap; replaced with a single
    behaviour table for EGG_BRC_IDLE_BUDGET_MIN.
  - Added new "Rollback plan" subsection documenting git revert of
    slice-4 → slice-3 → slice-2 → slice-1 in reverse-merge order, the
    integration check operators must run, and the partial-revert
    interaction (reverting only slice-4 restores the dual-emission
    state).
  - Renamed "Slice-2 verification stance — unit-test-only" to
    "Verification stance — unit-test-only"; explained that the
    snapshot tests pinning the byte-for-byte legacy template emission
    were retired in slice-4 task-4-3.
  - Renamed "BRC Per-Event Prompt Composer + Preamble Collapse
    (slice-3)" to drop the slice marker; reframed "Flag mapping" to
    "What's gated by what" since EGG_BRC_EVENT_PUMP no longer gates
    anything.
  - Updated EGG_BRC_MEMORY table: full is now the slice-4 default;
    write-only is the opt-in regression path.
  - Updated env vars table: EGG_BRC_EVENT_PUMP is a deprecated no-op
    pointing at the rollback plan; EGG_BRC_IDLE_BUDGET_MIN is no
    longer gated on EGG_BRC_EVENT_PUMP=true.

- docs/guides/concurrent-execution.md
  - Replaced the slice-2 "two emission paths" caveat with a single
    post-deletion summary linking to the new orchestrator.md section.
  - Rewrote the "Consensus Wrapper" body to describe the deterministic
    event-pump loop (steps 1–6) as the only path; removed
    MAX_CONSENSUS_RESTARTS-based restart cap, the recovery system
    prompt, and the final-consensus-check restart cycle.
  - Updated the configuration table: dropped `max_restarts` row;
    added EGG_BRC_IDLE_BUDGET_MIN; updated transient-crash recovery
    paragraph to reference the idle/no-progress budget instead of
    the deleted MAX_CONSENSUS_RESTARTS hard cap.

- docs/architecture/README.md
  - Updated the cross-link card to point at the renamed section and
    summarise the slice-4 deletion + rollback plan.

Cross-links to docs/architecture/brc-memory.md (slice-1) retained
throughout. The wait-side companion at agent-wait-patterns §10 is
referenced from each cross-link card.

Satisfies contract task-4-4. Acceptance: doc reads as if the event
pump has always been the only model; legacy-path caveats removed;
cross-links present; rollback plan documented; markdown renders
clean (no conflict markers; section anchors resolve).

* feat(#2908 slice-4 task-4-2): delete legacy capped-restart template and agent-side heartbeat

Slice-4 task-4-2 collapses ``consensus_wrapper.py`` onto the
event-pump template that slice-2 introduced and slice-3 wired the
per-event composer into. The event-pump is now the only production
path; rollback under a regression is a ``git revert`` of slices 1-3
per the PR body, not an env-flag flip.

Deleted from ``orchestrator/consensus_wrapper.py``:

* ``_CONSENSUS_WRAPPER_TEMPLATE`` (the ~600-line legacy
  capped-restart bash template).
* ``_RECOVERY_SYSTEM_PROMPT`` and ``_RECOVERY_USER_PROMPT`` — the
  restart-time recovery prompts.
* The SSE ``consensus.reached`` curl path (issue #1897) that lived
  inside the legacy template — the event-pump uses
  ``egg-orch message wait-loop`` instead.
* ``MAX_CONSENSUS_RESTARTS`` (issue #2806) and its companion
  constants ``MAX_READY_POLL_CYCLES``,
  ``TRANSIENT_RESTART_BACKOFF_INITIAL``,
  ``STARTUP_FAILURE_WINDOW_SECONDS``. The idle/no-progress safety
  budget (env ``EGG_BRC_IDLE_BUDGET_MIN``, default 30 min) is the
  replacement liveness ceiling.
* ``_event_pump_enabled()`` — the ``EGG_BRC_EVENT_PUMP`` env-flag
  read. The flag is now silently inert; operators with it lingering
  in k8s manifests can leave it set to either truthy or falsy and
  still get the event-pump template.
* The legacy-template branch in ``build_consensus_wrapped_command``,
  which is now a thin alias for ``build_event_pump_wrapped_command``.

Preserved by relocating into ``_EVENT_PUMP_WRAPPER_TEMPLATE`` (per
task-4-2 acceptance, "Keep ``is_buffer_overflow`` /
``is_transient_crash`` / ``is_startup_failure`` classifiers"):

* ``is_buffer_overflow()`` — Claude Agent SDK 1 MiB JSON reader
  overflow detector (#2804).
* ``is_transient_crash()`` — signal-based exits (134, 136, 137,
  139, 255).
* ``is_startup_failure()`` — exit 1 within a 30 s startup window.
* ``STARTUP_FAILURE_WINDOW_SECONDS`` — kept as a bash-scope shell
  variable inside the template (was a Python module constant).

The classifiers are not yet wired into the event-pump's
``propose|ack|nack`` agent-invocation failure path (which uses
``AGENT_FAIL_STREAK`` + idle-budget escalation today); they live
as named helpers for future revisions.

Deleted from ``sandbox/egg_agent_tools/handlers/message.py``:

* ``_WAIT_LOOP_HEARTBEAT_INTERVAL_SECS`` — the 60-s cadence
  constant.
* ``_default_emit_wait_loop_heartbeat`` — the agent-side
  ``WAITING_FOR_EVENT`` / ``WORKING`` heartbeat emitter (#2036).
* ``_start_wait_loop_heartbeat`` — the threaded periodic-tick
  helper.
* The per-iteration ``emit_hb`` / ``stop_hb`` calls inside
  ``message_wait_loop``, including the ``try/finally`` block that
  drove the final ``WORKING`` beat on wait exit.

The event-pump wrapper now owns both heartbeat liveness (#2036) and
slice-scoped gateway-session keep-alive (#2451) via the wrapper-
owned ``start_background_heartbeat`` subshell. ``message_heartbeat``
(the explicit handler invoked by ``egg-orch message heartbeat``) is
unchanged — the wrapper calls it.

Test updates:

* ``orchestrator/tests/test_consensus_wrapper.py``: deleted the
  ``TestBuildConsensusWrappedCommand`` / ``TestConsensusWrapperBehavior``
  / ``TestBufferOverflowDetection`` / ``TestEventDrivenWait`` /
  ``TestSSESigtermGrace`` classes (and the ``_force_legacy_template``
  fixture that fed them). The buffer-overflow / SSE / capped-restart
  surfaces they covered no longer exist. The event-pump classes
  (``TestEventPumpTemplateSelection`` and siblings) cover the new
  production path; ``TestEventPumpTemplateSelection`` is reworked
  to pin the post-task-4-2 invariant that ``EGG_BRC_EVENT_PUMP`` is
  silently inert (any value, including ``false`` / ``0`` / ``no``
  / ``off``, emits the event-pump template).
* ``orchestrator/tests/test_consensus_wrapper_anchor.py``: deleted
  in full — every test pinned ``_RECOVERY_SYSTEM_PROMPT`` /
  ``_CONSENSUS_WRAPPER_TEMPLATE`` symbols that no longer exist.
* ``orchestrator/tests/test_brc_nack_iteration.py``: removed
  ``TestConsensusWrapperNackFeedback`` (4 tests) that pinned the
  legacy recovery prompt's NACK-feedback placeholder + helper. The
  equivalent event-pump assertion lives in
  ``orchestrator/tests/test_compose_event_prompt.py``.
* ``tests/sandbox/egg_agent_tools/test_handlers_message.py``:
  removed ``TestMessageWaitLoopHeartbeat`` (16 tests) that pinned
  the agent-side heartbeat path. ``TestMessageHeartbeat`` (the
  explicit handler tests) is unchanged.
* ``integration_tests/regression/test_brc_concurrency.py``:
  updated the slice-2 verification-stance docstring to reflect the
  slice-4 post-deletion steady state (E2E deferred to #2585 via
  ``egg_stack``; in-process tracker coverage unchanged).

Defensive grep assertions all return zero matches on
``orchestrator/consensus_wrapper.py``:

  rg 'consensus\.reached|sse_url|_RECOVERY_SYSTEM_PROMPT|MAX_CONSENSUS_RESTARTS' \
      orchestrator/consensus_wrapper.py
  # → 0 hits

Smoke-verified that the event-pump template is emitted regardless of
``EGG_BRC_EVENT_PUMP`` value, that the three classifiers survive in
the event-pump template, and that the deleted agent-side heartbeat
helpers are no longer importable from ``handlers.message``.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(#2908 slice-4 task-4-4 v2): address reviewer_code v1 NACK

Five blocking findings + three non-blockers from reviewer_code's v1
NACK on PR-proposal v1. Address all five blockers; defer the
non-blockers (env-var coordination is gated on coder task-4-1 / task-4-2
landing first).

Blocker 1+2+3 — restored post-#2936 coder-owns-tests content in
docs/guides/concurrent-execution.md (the v1 proposal overwrote the
post-#2936 wording when I copied the slice-4 base, which predates the
#2936 merge):
- §"HANDOFF" table row example: "Coder can't push test files → HANDOFF
  to tester" → "Tester can't push a .github/ CI fix → HANDOFF to coder
  with the required end-state" (matches docs/reference/agent-roles.md).
- §"Worked Example: Role-Boundary Handoff" rewrite: drop the
  reinstated pre-#2936 coder→tester test-handoff example, restore the
  post-#2936 tester→coder .github/-staging example, and keep the
  explicit lead sentence "the coder→tester test handoff that used to
  live here is gone: the coder now authors and pushes its own tests".
- §"Rebase rarely conflicts" paragraph: "Rebase cannot conflict because
  agents have mutually exclusive file write permissions" / "role
  restrictions guarantee non-overlapping file sets" was a doc lie after
  #2936 — restore the pre-rewrite "rarely conflicts" wording and the
  follow-up paragraph that names the shared test scope and the
  serialize-by-time-not-concurrent invariant.

Blocker 4+5 — dead anchors and stale §10 / §10.9 framing in
docs/reference/agent-wait-patterns.md and docs/architecture/brc-memory.md:
- agent-wait-patterns.md §10 retitled from "BRC Event-Pump Wrapper
  (slice-2, behind EGG_BRC_EVENT_PUMP)" to "BRC Consensus Wrapper
  (event-pump model)"; intro blockquote rewritten to drop the
  slice-2 "OFF by default" framing and instead describe the
  post-deletion steady state + rollback path; §10.8 retitled from
  "Flag-off as the temporary default — when slice-4 flips it" to
  "Rollout completed in slice-4" with body rewritten accordingly;
  §10.9 retitled to drop the (slice-3) suffix and the "Flag mapping"
  blockquote rewritten to "What's gated by what" since
  EGG_BRC_EVENT_PUMP no longer gates anything.
- All five dead inbound anchor references repointed to the new
  anchors: agent-wait-patterns.md lines 1178, 1411, 1653, 1654 and
  brc-memory.md lines 235, 237.
- Reverse direction — repointed orchestrator.md, README.md, and
  concurrent-execution.md cross-links from
  #10-brc-event-pump-wrapper-slice-2-behind-egg_brc_event_pump to
  #10-brc-consensus-wrapper-event-pump-model (3 occurrences in
  orchestrator.md, 1 each in README.md and concurrent-execution.md);
  same for #109-brc-per-event-prompt-composer--preamble-collapse-slice-3
  → #109-brc-per-event-prompt-composer--preamble-collapse.
- Verified via grep across docs/ that no remaining link points at the
  old anchors and no remaining body text carries the
  "(slice-2, behind EGG_BRC_EVENT_PUMP)" framing.

Non-blockers deferred:
- Rollback-plan example precision (compose_event_prompt slice-3 vs
  slice-2 wrapper) — useful tightening but doesn't change the
  correctness of the doc.
- "schema is unchanged" past-tense alignment — minor.
- EGG_BRC_EVENT_PUMP "no-op" vs "removed" wording — gated on coder's
  task-4-1 / task-4-2 final state. Will re-pass once the coder's
  proposal lands so the doc and code agree.

* docs(#2908 slice-4 task-4-4 v3): address reviewer_code v2 NACK

v2 cleared mandate 1 (all v1 blockers fixed) but mandate 2 found four
new blocking findings in agent-wait-patterns.md §10.3 / §10.4 / §10.5
/ §10.7 — subsection bodies still described the flag-off vs flag-on
dual-emission world in present tense as if both paths still shipped,
contradicting the §10 intro blockquote rewritten in v2.

Address all four:

- §10.3 (Heartbeat ownership) — dropped the two-row flag-off vs
  flag-on table; rewrote in past-tense post-migration framing
  mirroring orchestrator.md §"Wrapper-side heartbeat (#2036
  migration completed in slice-4)" — the wrapper owns heartbeating
  now and the pre-#2908 agent-side path in `message_wait_loop` was
  deleted in slice-4 task-4-2.
- §10.4 (Gateway-session keep-alive) — struck the closing "With
  the flag off the agent-side keep-alive still runs" sentence and
  replaced it with the slice-4-deletion qualifier matching §10.3 /
  orchestrator.md.
- §10.5 (Idle / no-progress safety budget) — dropped the parenthetical
  "(replaces the 3-restart FAIL cap)" from the heading; dropped the
  two-row flag-off vs flag-on table; replaced with a single-row
  `EGG_BRC_IDLE_BUDGET_MIN` table mirroring orchestrator.md's
  steady-state version; rewrote the present-tense
  "MAX_CONSENSUS_RESTARTS = 3 cap" framing in past tense.
- §10.7 (Verification stance) — dropped "Slice-2" from the heading;
  rewrote the body in past tense matching orchestrator.md's
  §"Verification stance — unit-test-only"; removed the
  "snapshot equality for the flag-off path" and "deferred to slice-4"
  framing (slice-4 is this work; flag-off snapshot tests were
  retired in slice-4 task-4-3); flipped the integration-tests bullet
  from "runs with EGG_BRC_EVENT_PUMP=false" to "runs against the
  event-pump wrapper".

Adjacent cleanups for body/header coherence:

- §10.1 ASCII diagram: relabelled "LEGACY (flag off, today's
  default)" → "PRE-#2908 (deleted in slice-4 task-4-2 — kept here
  for git-blame readers)" and "EVENT-PUMP (flag on)" → "STEADY
  STATE (event-pump, the only path after slice-4)".
- §10.9.4 EGG_BRC_MEMORY mode table: marked `full` as the slice-4
  default (mirrors orchestrator.md); dropped the slice-3-rollout
  "operators opt into full just as they opt into EGG_BRC_EVENT_PUMP=true"
  paragraph since EGG_BRC_EVENT_PUMP is no longer consulted.
- §10.9.5 `_build_brc_preamble` collapse: rewrote the closing
  paragraph in past tense — the collapse runs unconditionally now
  because the event-pump wrapper is the only path; flipped
  "Slice-4 flips the wrapper default" → "Slice-4 flipped the
  wrapper default" so the doc reads as steady state.
- §10.9.6 `mission.md` sandbox-rebuild paragraph: flipped
  "Slice-4's flag-flip is gated" → past-tense "The slice-4 default
  flip was gated".
- §10.9.7 Composer / preamble verification stance: dropped "Slice-3"
  from the heading; rewrote in past tense matching the §10.7 rewrite;
  removed "deferred to slice-4" since slice-4 is this work.
- §10.9.8 Architect open-decision resolutions: "resolved across
  slices 1–3" → "resolved across slices 1–4".
- §11 Related Documentation cross-link: updated the
  Concurrent Execution Wrapper card from "how the wrapper uses SSE
  + wait-loop" (SSE machinery was deleted in slice-4 task-4-2) to
  "the deterministic event-pump bash loop driver".

The two §10.7 non-blockers (slice-2 contract back-reference at
§10.7 tail, architect-corrected-pseudocode parenthetical) survive
as audit history — the reviewer marked them non-blocking and the
context is still useful for future maintainers tracing the slice-2
design review.

* docs(#2908 slice-4 task-4-4 v3 follow-up): EGG_BRC_EVENT_PUMP removed not no-op

Reviewer_code v2 non-blocker #3 was deferred awaiting coder
task-4-1 / task-4-2 final state. The coder's task-4-2 commit
(15664e8) has now landed and the docstring at
orchestrator/consensus_wrapper.py:35 confirms the env var itself
was deleted ("the EGG_BRC_EVENT_PUMP env flag itself"), not just
left as a dead branch.

Update the docs to match:

- docs/architecture/orchestrator.md env-vars table EGG_BRC_EVENT_PUMP
  row: "Deprecated no-op after slice-4" → "Removed in slice-4
  task-4-2"; default "unset (no-op)" → "n/a (removed)"; added the
  helm-values / pod-spec drop-row note for operators that referenced
  it explicitly.
- docs/architecture/orchestrator.md §"Operator-facing env vars
  (cross-link)": "the EGG_BRC_EVENT_PUMP selector is no longer
  consulted — setting it has no effect because the legacy template
  it selected to is gone" → "was removed in slice-4 task-4-2 — the
  env var is no longer read by the orchestrator, so setting it has
  no effect on a post-slice-4 codebase."
- docs/architecture/orchestrator.md §"Rollback plan" partial-revert
  paragraph: tightened the post-slice-4-revert narrative to say the
  env var itself comes back when slice-4 is reverted (because task-4-2
  is what deleted it), and operators wanting event-pump back set
  EGG_BRC_EVENT_PUMP=true (not =false — the defaults flip back to off).
  Also tightened the example of why reverse-merge order matters
  (slice-2 wrapper template references a composer slice-3 added, not
  "a composer that no longer exists").
- docs/reference/agent-wait-patterns.md §10.8: same shift — env var
  was deleted alongside the legacy template, so setting it has no
  effect; rollback path is reverse-merge order.

* fix(#2908 slice-4 v2): address reviewer_code_holistic NACK on v1

Fix the six broken tests and four stale docstrings the holistic
reviewer surfaced on v1 (the gateway-blocked test execution missed
them; the structural issues are all visible from grep alone).

Tests (orchestrator/tests/test_consensus_wrapper.py +
orchestrator/tests/test_brc_nack_iteration.py):

* Restored ``import os`` / ``import shlex`` / ``import subprocess`` —
  the surviving event-pump test classes still need them
  (``TestEventPumpConfirmFailureRaisesIdleAlert`` uses
  ``shlex.quote`` for stubbed PATH binaries;
  ``TestEventPumpHeartbeatSubshellLifecycle`` and the
  brc_snapshot tests use ``os.environ``).
* Deleted ``TestEventPumpHeartbeatCadence::test_flag_off_heartbeat_path_unchanged``
  — its invariant ("legacy template does not emit
  ``egg-orch message heartbeat``") no longer applies; the legacy
  template is gone. Replaced with an inline comment cross-linking
  to the post-deletion positive invariant.
* Deleted ``TestEventPumpKeepAliveCadence::test_flag_off_keep_alive_remains_agent_side``
  — same reason.
* Deleted ``TestEventPumpIdleBudgetAlert::test_flag_off_idle_budget_not_used``
  — same reason.
* Deleted ``TestEventPumpRoleCompleteConfirm::test_flag_off_legacy_path_does_not_auto_call_consensus_confirmed``
  — the legacy template is gone; the event-pump's confirm invocation
  is strictly orchestrator-driven via the ``case "$ACTION"`` arms,
  not auto-invoked on agent exit, so the symmetry guard is
  structurally satisfied.
* Renamed ``TestEventPumpFlagIsolation::test_flag_on_does_not_inherit_legacy_max_restarts``
  to ``test_event_pump_relies_on_idle_budget_not_legacy_restart_cap``
  and dropped the ``max_restarts=7`` kwarg (the legacy kwarg was
  deleted from ``build_consensus_wrapped_command`` by task-4-2).
  The remaining assertion — ``EGG_BRC_IDLE_BUDGET_MIN`` is in the
  script — is the salient invariant.
* Deleted ``TestEventPumpInvokesComposer::test_flag_off_legacy_template_does_not_reference_event_prompt``
  — same legacy-path-only invariant.
* Removed the orphaned ``assert "unresolved_nacks" in _CONSENSUS_WRAPPER_TEMPLATE``
  line at the bottom of ``test_brc_nack_iteration.py`` (was left
  outside any function by the original ``TestConsensusWrapperNackFeedback``
  deletion; this is a pure cleanup of slice-4 v1 commit
  15664e8).

Docstrings:

* ``sandbox/egg_agent_tools/handlers/brc_memory.py:546`` —
  ``record_review_event`` docstring updated to reflect the
  slice-4 task-4-1 default flip (``EGG_BRC_MEMORY`` defaults to
  ``full`` now, not ``off``).
* ``orchestrator/routes/event_prompt.py:787`` — CLI docstring
  updated to ``default full``; documents that ``off`` is the
  one-release rollback escape hatch and ``write-only`` keeps the
  writer warm without consuming the excerpt.
* ``orchestrator/consensus_wrapper.py:81`` — module-level template
  comment rewritten: the env-flag predicate is gone, the
  event-pump template is the only template path post-task-4-2.
* ``orchestrator/consensus_wrapper.py:723`` —
  ``build_event_pump_wrapped_command`` docstring rewritten to
  describe the post-task-4-2 reality (no env-flag gate; legacy
  template deleted; ``compose_event_prompt`` already wired).

Defensive (addresses the non-blocking observation #1):

* ``tests/sandbox/egg_agent_tools/test_handlers_message.py:TestMessageHeartbeat``
  gains an autouse ``_isolate_slice_id_env`` fixture that clears
  ``EGG_SLICE_ID``. ``message_heartbeat`` auto-attaches
  ``slice_id`` from that env via ``_maybe_attach_slice_id``, so a
  developer-machine ``EGG_SLICE_ID`` (e.g. inside the egg sandbox)
  would otherwise add an unexpected key to the request body and
  fail the strict-equality assertions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#2908 slice-4 v3): address reviewer_code v1 NACK on coder v2

Blocking finding:
* test_consensus_wrapper.py top-level imports missed ``import sys``;
  ``test_persistent_confirm_failure_fires_overseer_alert`` (the §1 + §6.2
  lock-in test, the most operator-critical assertion in the file)
  uses ``sys.executable`` at line ~1092 and would raise NameError on
  execution, silently disabling the regression guard. The reviewer
  caught it via grep — same shape as the reviewer_code_holistic v1
  NACK that surfaced the missing os/shlex/subprocess imports.
  Fix: add ``import sys`` alongside os/shlex/subprocess.

Non-blocking findings (all addressed in this v3 since they're cheap):
* TestEventPumpIdleBudgetAlert class docstring rewritten —
  ``The old template keeps MAX_CONSENSUS_RESTARTS verbatim`` was
  present-tense framing for the legacy template that task-4-2 deleted.
  Now reads ``The legacy template that owned the historical restart
  cap was deleted in slice-4 task-4-2; the idle budget is now the only
  liveness ceiling in the wrapper.``
* TestEventPumpFlagIsolation class renamed to TestEventPumpIdleBudgetCeiling
  with docstring rewritten — after task-4-2 there is no flag-on /
  flag-off partition to police, so the original name and ``cross-cutting
  guards`` framing no longer apply. The class retains its single
  surviving test (``test_event_pump_relies_on_idle_budget_not_legacy_restart_cap``)
  which is correct against the post-deletion state.
* test_persistent_confirm_failure_fires_overseer_alert inline comment
  rewritten — ``_event_pump_enabled`` was deleted by task-4-2; the
  ``monkeypatch.setenv("EGG_BRC_EVENT_PUMP", "true")`` is harmlessly
  retained as a defensive guard against a future regression that
  re-introduces a flag-gated branch. Comment now reads as such.

The reviewer flagged the docstring drift as non-blocking but I'm
folding it into the same commit because the cost is one edit each
and the docstring↔code mismatch the holistic v1 NACK called out is
the same class of issue. Keeping the surface honest.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#2908 slice-4 v3 follow-up): address reviewer_code_holistic v2 blocker #2 (test_pipeline_prompts fixture)

Follow-up to v3 (e093f67 pushed) that addressed the reviewer_code
v2 blocker (missing import sys). This commit addresses the
reviewer_code_holistic v2 blocker (2): two pre-existing test failures
in orchestrator/tests/test_pipeline_prompts.py.

Root cause: the slice-4 base-merge in 06c5a6c resolved the conflict
on test_pipeline_prompts.py by keeping slice-3's _PLAN_WITH_MISASSIGNED_TASK
fixture (``role: coder`` + ``files: integration_tests/conftest.py``).
But main's #2936 ("coder authors its own tests; tester reviews-and-
hardens") explicitly excluded coder→test-files from the role↔files
alignment validator. The fixture no longer trips the reject path,
breaking TestPlannerRoleAlignmentValidation::test_rejects_misassigned_plan_at_propose_time
and ::test_rejected_proposal_does_not_mutate_tracker.

Fix: cherry-pick main's fixture update — switch the misassignment
fixture from a test-file path to a docs path (docs/fixtures.md),
which IS still a misassignment, since docs remain the documenter's
scope. Added an explanatory comment above the fixture citing #2936
and the slice-3 merge-resolution context so future readers do not
re-revert under a conflict resolution that "looks like" the slice-3
text.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#2908 slice-4 v4): restore _auto_populate_contract + ruff I001 fix (tester v3 NACK)

Tester v3 NACK had two blockers:

1. ``_auto_populate_contract_at_implement_start`` was deleted from
   ``orchestrator/routes/pipelines.py`` during the slice-4 base merge
   (commit 06c5a6c). The orphan import in
   ``orchestrator/tests/test_auto_populate_contract.py`` broke
   ``pytest --collect-only`` and blocked ``make test`` from running
   any tests at all (collection aborts on the first ImportError).
   Verified by the tester via ``git diff origin/main..origin/egg/issue-2908-impl2/slice-4``
   that the function was dropped, not renamed.

   Fix: restored the function body verbatim from ``origin/main`` (the
   #2915 production implementation) and re-added the call site inside
   the slice-loop-mode gate where it lived on main. The function:

   * lives between ``_check_origin_has_plan_draft`` and
     ``_populate_contract_from_plan_safe`` (matches main's ordering).
   * is called from the ``_use_slice_loop`` check in ``_run_pipeline``
     when ``_slice_count == 0``, exactly as on main.
   * uses ``_populate_contract_from_plan``, ``PopulateOutcome``,
     ``ForestValidationError``, ``_commit_statefiles_to_worktree``,
     and ``_pipeline_identifier`` — all present in the current file
     (no further imports needed).

   The function has a slice-4 v4 banner in its docstring explaining
   the restore so future merge resolutions don't re-drop it.

2. ``orchestrator/consensus_wrapper.py:50`` had a ruff I001 unsorted
   imports failure — an extra blank line between ``import shlex`` and
   the next module-level constant. Fix: removed the extra blank line
   (one-line deletion).

Verified locally:
* ``pytest --collect-only`` no longer aborts on
  ``ImportError: cannot import name '_auto_populate_contract_at_implement_start'``.
* ``orchestrator/tests/test_auto_populate_contract.py`` imports clean.
* ``orchestrator.routes.pipelines`` module imports clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#2908 slice-4 v7): address reviewer_code NACK — 4 ruff failures

Reviewer_code re-reviewed coder v6 and NACKed with 4 blocking ruff
failures + 1 ruff-format failure that would block ``make lint`` in
CI:

1. ``orchestrator/tests/test_consensus_wrapper.py:13-23`` — I001
   unsorted-import-block (resolved as a side-effect of fixes 2 and 3
   reducing the import block to a single from-import).
2. ``orchestrator/tests/test_consensus_wrapper.py:18`` — F401
   ``pytest`` imported but unused. The two surviving call sites
   inside function bodies use ``import pytest as _pytest`` so the
   top-level name was dead after the v2 test deletions. Fix: remove
   the top-level ``import pytest``.
3. ``orchestrator/tests/test_consensus_wrapper.py:22`` — F401
   ``consensus_wrapper.build_event_pump_wrapped_command`` imported
   but unused (zero references in the file after the test-deletion
   sweep). Fix: drop the second name from the from-import.
4. ``tests/sandbox/egg_agent_tools/test_handlers_message.py:10`` —
   F401 ``threading`` imported but unused. Slice-4 task-4-2
   (15664e8) deleted the threaded ``message_wait_loop`` heartbeat
   machinery; the test cases that exercised it were also removed
   but the top-level ``import threading`` was left behind. Fix:
   remove the now-dead import.
5. ``orchestrator/tests/test_pipeline_prompts.py:5129-5131`` — ruff
   format-check failure on a multi-line assertion message. Pre-
   existing from the slice-3 tester commit 7cff8d1 but surfaced
   only now that the file is in lint scope. Fix: ``ruff format``
   collapses the two-string concatenation into a single line.

Verified locally:
* ``ruff check .`` → ``All checks passed!``
* ``ruff format --check .`` → ``872 files already formatted``
* ``pytest orchestrator/tests/test_consensus_wrapper.py`` →
  33 passed.
* ``pytest tests/sandbox/egg_agent_tools/test_handlers_message.py``
  → 24 passed.
* ``pytest orchestrator/tests/test_pipeline_prompts.py`` →
  431 passed.

Non-blocking observations from reviewer_code v6 (the
_auto_populate_contract restore in routes/pipelines.py and the
v4 consensus_wrapper.py I001 deletion) were already verified-clean
in the prior review and remain unchanged in v7.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Persist BRC history for slice-4 (#2548)

* docs(#2908 slice-5 task-5-4): prose-arg channels + brc verb-level CLI

Document the slice-5 additive CLI surface across the four docs that
already carry consensus-protocol prose:

- docs/reference/orchestrator-cli.md
  * New "Prose-bearing args: stdin and --*-file channels (#2741)"
    subsection under ## BRC Consensus Protocol covering --summary-file,
    --reason-file, --files-reviewed-file, and the stdin sentinel `-`.
  * New "## BRC verb-level operations (egg-orch brc)" section
    documenting the next-action / get-state / list-blocking /
    resolve-obligation / read-peer-artifact subcommands.
  * Deprecation-warning note on the argv --summary / --reason path.

- docs/reference/agent-tools.md
  * MCP↔CLI table: mcp__brc__get_state, mcp__brc__list_blocking
    (slice-1), and mcp__brc__read_peer_artifact, mcp__brc__resolve_obligation
    (slice-5) flipped from "no CLI" to their new egg-orch brc subcommands.
  * cli_command=None rationale list: drop the four promoted verbs and
    add a callout summarizing the slice-5 promotion.
  * Schema-derivation paragraph: shrink the "tools with no CLI" list
    accordingly.

- docs/reference/agent-wait-patterns.md
  * Update re-propose / stale-version examples to use --summary-file /
    --reason-file (the canonical idiom for any wrapper-composed CLI).
  * New "Prose-bearing args use stdin / --*-file, not argv (#2741)"
    subsection under §1 with channel table, examples, and rationale.
  * Related Documentation: cross-link to the new orchestrator-cli.md
    BRC verb-level operations section and to #2741.

- docs/guides/concurrent-execution.md
  * Refresh the worked Consensus Protocol example to use --summary-file
    for propose, --reason-file for ack, and the stdin sentinel for nack
    / withdraw. Add a brc resolve-obligation example.
  * New "egg-orch brc — verb-level read/derive surface" subsection
    cross-linking the canonical reference in orchestrator-cli.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(#2908 slice-5 task-5-1/5-2/5-3): prose-arg channels + brc CLI surface

Slice-5 of the BRC event-pump rollout extends the CLI surface for the
in-bash wrapper that ships in slice-2. Three coder tasks land here:

task-5-1 — Prose-arg channels for consensus propose/ack/nack/withdraw.
The wrapper composes CLI invocations via ``bash -c``, so argv-only
prose (``--summary "$RAW"``, ``--reason "$RAW"``) gets corrupted by
shell metacharacters (``$VAR`` / backticks / ``$()`` / ``;`` / ``&&``
/ embedded newlines) — the #2741 failure mode. This slice generalises
the mitigation: every prose-bearing arg now offers a paired
``--FOO-file PATH`` flag and accepts ``-`` as the argv sentinel for
stdin. Argv prose still works for humans and during transition but
emits ``DeprecationWarning`` so a regression to argv-only inside the
wrapper surfaces. ``--files-reviewed-file PATH`` carries an array
with one path per line (blank lines and ``#`` comments stripped),
per architect v2 §verification_strategy.slice_5. Two helpers in
orch_cli.py — ``_resolve_prose_arg`` and ``_resolve_files_reviewed_arg``
— handle channel selection (file → stdin → argv), enforce mutual
exclusion, and emit the deprecation.

task-5-2 — ``egg-orch brc resolve-obligation`` CLI. Verb-level wrapper
around ``mcp__brc__resolve_obligation`` (#2338). Slice-6 deletes the
agent-side MCP server, so the wrapper bash needs this verb reachable
without an MCP round-trip. Args mirror the handler: ``--reviewer-role``
and ``--producer-role`` are required; ``--commit-sha`` and ``--note``
are optional. The ``--note`` flag uses the same prose-arg plumbing as
the other reason / summary args.

task-5-3 — ``egg-orch brc read-peer-artifact`` CLI. Verb-level wrapper
around ``mcp__brc__read_peer_artifact``. Stdout JSON; pagination via
``--limit`` + opaque ``--cursor`` round-trip; ``--message-type`` is
``action="append"`` for repeated use; ``--no-include-unattributed``
opts out of the slice-scoped + cross-cutting merge (default on, per
the handler's per-slice-partition contract from #2548).

Tests authored by the coder (tester reviews-and-hardens):

* ``tests/sandbox/egg_lib/test_orch_cli_prose_args.py`` — #2741
  regression-guard. Parametrises seven representative prose payloads
  (``$VAR`` / backticks / ``$()`` / shell-control / newline+tab /
  UTF-8 / quotes+escapes) across each delivery channel (file, stdin
  sentinel, argv) for ``consensus propose``, ``ack``, ``nack``, and
  ``withdraw``. Asserts byte-equality between the on-disk / stdin
  input and the request body received by the orchestrator fake.
  Argv-path tests assert the ``DeprecationWarning`` fires. Mutual-
  exclusion paths return exit 2 with helpful stderr. ``--files-
  reviewed-file`` one-path-per-line semantics covered (blank lines
  + ``#`` comments stripped). The ``consensus propose --file`` JSON
  payload path (from issue #1738) is explicitly tested to NOT emit
  the deprecation warning — only the per-arg argv channels are
  deprecated.

* ``tests/sandbox/egg_lib/test_orch_cli_brc.py`` — extends slice-1's
  test file with ``TestBrcResolveObligation`` (happy path / commit
  SHA / note via file / note via stdin / help / parser registration)
  and ``TestBrcReadPeerArtifact`` (happy path / peer-role filter /
  message-type list / limit+cursor pagination round-trip /
  no-include-unattributed default flip / phase choices restricted /
  help / parser registration).

All 306 tests pass on the changed paths; existing consensus-push,
slice-1 BRC, and CLI parity tests continue to pass unchanged.

Files: sandbox/egg_lib/orch_cli.py;
       tests/sandbox/egg_lib/test_orch_cli_brc.py;
       tests/sandbox/egg_lib/test_orch_cli_prose_args.py.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(#2908 slice-5 task-5-4 v2): address reviewer_code v1 NACK

Address three blocking findings plus the in-scope non-blocking ones
raised by reviewer_code on the v1 documenter proposal.

Blocking 1 — `--files-reviewed-file` is one-path-per-line (NOT JSON):
The `_resolve_files_reviewed_arg` handler in orch_cli.py:884-922 reads
the file as newline-delimited paths, strips blank lines and `#`-prefixed
comments, and never calls `json.loads`. Update the example comment in
orchestrator-cli.md to say "one path per line; blank lines and `#`
comments stripped" and rewrite the heredoc example to demonstrate the
comment-stripping behavior. Mirror the clarification in
agent-wait-patterns.md.

Blocking 2 — schema-derivation claim was wrong:
The four BRC tool registrations in sandbox/egg_agent_tools/tools/brc.py
(get_state / list_blocking / read_peer_artifact / resolve_obligation)
ALL still declare `cli_command=None`. Slice-1 / slice-5 added thin CLI
wrappers (`egg-orch brc <verb>`) over the same handlers but deliberately
did NOT flip the registrations. The MCP-side schemas continue to be
hand-authored in `schemas.py`; the `derive_schema_from_argparse` path
is skipped. Restore the four BRC verbs to the `cli_command=None` bullet
list with the additional context that a thin CLI wrapper exists; revise
the "promoted to CLI" callout to "CLI surface added (registration
unchanged)"; revise the schema-derivation paragraph; tag the CLI-
counterpart cells with "thin wrapper, registration still cli_command=None
— see callout below".

Blocking 3 — `brc read-peer-artifact` does NOT use the gateway:
The handler reads `.egg-state/brc-history/<identifier>-<phase>.json`
files from local disk (verified: no `orchestrator_request(...)` call
in `brc_read_peer_artifact`). EGG_ORCHESTRATOR_URL / EGG_LIFECYCLE_SECRET
do not apply. Rewrite the "all five subcommands" sentence in
orchestrator-cli.md to scope the auth claim to the other four and
explain the local-disk semantics so operators don't misdiagnose
missing-secret failures.

Non-blocking (in-scope to the row I touched):
- agent-tools.md: fix the pre-existing handler typo
  `handlers.brc.read_peer_artifact` → `handlers.brc.brc_read_peer_artifact`
  (every sibling row uses the brc_ prefix).
- agent-tools.md: tighten the read_peer_artifact description to mention
  `<identifier>-<phase>.json` (not `<pipeline_id>`), the per-slice
  `<identifier>-implement-<slice_id>.json` partition, the unattributed
  sibling merge + `include_unattributed=False` toggle, and the
  `message_type` filter (single value or list).
- concurrent-execution.md: author a distinct `reviewer-code-cond-ack.md`
  for the conditional ACK example so the prose narrative matches the
  obligation case (instead of re-using the unconditional ACK file).
- concurrent-execution.md: show the `cat > /tmp/obligation-resolved.md`
  heredoc step on the `brc resolve-obligation` example (every other
  prose-arg example in the same section creates the file inline).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(#2908 slice-5 v2): address tester v1 NACK — catch UnicodeDecodeError, return rc=2

Tester v1 NACK on coder v1 (commit 0a8a7f6): `_resolve_prose_arg`
caught `OSError` but not `UnicodeDecodeError`, so a binary or
non-UTF-8 file passed to `--reason-file` / `--summary-file` /
`--note-file` / `--files-reviewed-file` raised a raw traceback to the
wrapper bash instead of the actionable `Error: failed to read ...`
message.

The tester's one-line fix recommendation was to add
`UnicodeDecodeError` to the `except` clauses. That's done. While
there, also aligned the helpers to the established orch_cli pattern
of `return 2` from `cmd_*` (the same pattern `cmd_consensus_ack`
already uses for its `--pre-merge-condition-resolved-in-diff` guard):

* Added `_ProseArgError` sentinel exception. `_resolve_prose_arg` and
  `_resolve_files_reviewed_arg` now `raise _ProseArgError` on any
  CLI-level validation failure (mutual exclusion, missing required
  arg, file-read failure incl. `UnicodeDecodeError`); the cmd_*
  functions catch it and return rc=2. The stderr error message is
  emitted by the helper before the raise — cmd_* only translates the
  exception to the exit code.

* No more `sys.exit(2)` inside the helpers — `sys.exit` from within
  a cmd_* call raises `SystemExit`, which fails pytest tests that
  expect a clean returned rc (the tester's bug-finding test,
  `test_non_utf8_file_surfaces_clean_error_not_traceback`, makes
  this explicit by checking `assert rc == 2` after the call).

Tester's adversarial test file pulled in (tester committed it as
`tests/sandbox/egg_lib/test_orch_cli_prose_args_adversarial.py` at
847985f3d8). Two of the tester's tests that used
`pytest.raises(SystemExit)` updated to the new `return rc=2`
contract:

* `TestProseFileReadErrors::test_missing_reason_file_path_surfaces_clean_error`
* `TestProseArgEmptyEdges::test_empty_string_argv_treated_as_missing`

Both now assert `rc == 2` returned. The `test_invalid_phase_rejected_at_parse_time`
test continues to use `pytest.raises(SystemExit)` because argparse's
`choices=` rejection is genuinely a parse-time SystemExit, not a
cmd_* validation path.

Mirror change in `tests/sandbox/egg_lib/test_orch_cli_prose_args.py`:
the coder-authored `test_reason_and_reason_file_mutually_exclusive`,
`test_missing_reason_fails_cleanly`, and
`test_files_reviewed_and_file_mutually_exclusive` likewise switch from
`pytest.raises(SystemExit)` to `assert rc == 2` + `capsys` stderr
inspection.

Verification:
* All 142 tests in `tests/sandbox/egg_lib/` pass.
* Broader regression: 193 tests across `egg_agent_tools/test_handlers_brc.py`,
  `test_cli_parity.py`, `test_orch_cli_consensus_push.py`, and
  `test_orch_cli_slice_id.py` pass unchanged.
* ruff check + ruff format clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(#2908 slice-5 task-5-7): MCP-surface latency baseline capture

Adds the kubectl-gated integration test
``integration_tests/test_mcp_baseline_capture.py`` that drives a
real-LLM 5-role consensus through the still-live MCP surface
(slice-5 is additive only) and writes per-event wall-clock samples
to ``.egg-state/agent-outputs/latency-mcp-baseline.json``. The
schema (``samples: [{role, event_type, start_ts, end_ts,
duration_seconds, exit_code}]`` plus aggregate p50/p95/max/sum) is
the input slice-6 TASK-6-6 will compare against the CLI-only
baseline it captures after the MCP→CLI collapse lands.

The test skips cleanly when ``_kubectl_available()`` returns False
via the session-scoped ``egg_stack`` fixture, and also skips when
the gateway is unhealthy (dummy GH creds in CI) — mirroring the
guard used by ``test_orchestrator_mcp_contract.py``.

Also commits a synthetic placeholder baseline JSON marked
``_meta.synthetic: true`` so slice-6 has a file to read while the
capture test waits on a real-LLM run; ``_meta.synthetic_reason``
explains exactly how to regenerate.

No ``ScriptedProvider`` import / reference (per the slice-5 plan
re-scope: real LLM route, not in-process provider swap).

* Persist BRC history for slice-5 (#2548)

* Fix mypy errors: assert file_path non-None before open() in orch_cli

* fix(#2908 slice-4): migrate test assertions off deleted capped-restart wrapper

The CI Unit Tests failure on PR #2951 surfaced 18 broken tests; this
commit fixes the 6 caused by Group A — call sites in two test files
that the slice-4 task-4-3 sweep ("delete tests of the retired capped-
restart cap") missed because they referenced ``RESTART_COUNT`` /
"Restarting" / "BRC Consensus Recovery" / ``max_restarts`` /
``startup_failure_window_seconds`` rather than the symbol names listed
in the original task.

orchestrator/tests/test_concurrent_integration.py
  * ``test_spawn_agent_uses_wrapped_command``: assert event-pump
    markers (``event-pump``, ``egg-orch brc get-state``,
    ``egg-orch brc next-action``) instead of the deleted
    ``RESTART_COUNT`` / "BRC Consensus Recovery" strings.
  * Rename ``test_wrapper_contains_restart_logic`` →
    ``test_wrapper_drives_event_pump_loop`` and re-assert against the
    event-pump template. The original invariant ("orchestrator must
    not fake consensus on behalf of agents") is preserved — the
    event-pump never auto-signals READY either.

orchestrator/tests/test_consensus_race_on_exit.py
  * Delete ``TestWrapperStaleTrackerFallback`` (4 tests) plus its
    unused ``os`` / ``shlex`` / ``subprocess`` / ``sys`` / ``tempfile``
    imports. The class exercised
    ``build_consensus_wrapped_command(max_restarts=...,
    startup_failure_window_seconds=...)`` which slice-4 deleted in
    favour of the event-pump template; the event-pump reads BRC state
    directly via ``egg-orch brc get-state`` every loop iteration, so
    the wrapper no longer has a "stale tracker" of its own to fall
    back from. Module-docstring updated to point future readers at
    that history.

Remaining 12 Group B failures (test_short_flow_contract_population,
test_slice_4_restart_hardening) reference orchestrator production code
(``_slice_agents_alive``, ``_resolve_slice_base_branch``'s
``parent_branch_exists`` kwarg, contract-runtime preservation in
``_populate_contract_from_plan``) that exists on ``origin/main`` but
is missing from this branch — see PR-thread comment for the merge-
regression analysis and proposed recovery paths (decision required).

* fix(#2908 slice-4): restore _slice_agents_alive, parent-branch probe, runtime preservation

The slice-3 → slice-4 merge resolution accidentally reverted three
fixes that landed on main after slice-3 forked. This re-applies them
verbatim from origin/main so the unit tests pass:

* _slice_agents_alive (#2914): k8s alive guard called from the Layer-C
  bootstrap resume branch. Without it, a restart_phase that tore down
  agents but left the contract IN_PROGRESS wedges with no agents.
* _resolve_slice_base_branch parent_branch_exists callback (#2928):
  fresh non-root slices now probe whether the derived parent branch
  exists on origin via ls_remote_branch_strict, replacing the
  pre-#2928 merge-base probe that mis-routed every fresh non-root
  slice onto work whenever work had advanced ahead of the parent.
* _merge_preserved_slice_runtime (#2908): _populate_contract_from_plan
  re-parses the plan into fresh PENDING slices on every restart; the
  safety-net populator outside the contract_synced guard would
  otherwise reset COMPLETE slices and strand the pipeline on slice-1.

Authored-by: egg

* Address PR #2952 review feedback (egg-reviewer)

Finding 1: convert three Python-2-looking ``except E1, E2:`` clauses in
integration_tests/test_mcp_baseline_capture.py (lines 172, 210, 369) to
parenthesized tuple form. Ruff format actively strips parens off bare
``except (E1, E2):`` (no binding) — pin with ``# fmt: skip`` so the
clearer form survives the formatter.

Finding 2: extend the slice-5 prose-arg plumbing to the two remaining
prose-bearing flags the reviewer flagged. ``consensus propose --risk``
gains ``--risk-file PATH`` and ``--risk -`` stdin sentinel; ``consensus
ack --pre-merge-condition`` likewise gains ``--pre-merge-condition-file
PATH`` and stdin sentinel. Argv path still works but emits the same
DeprecationWarning as ``--summary`` / ``--reason``. Docs and prose-arg
test surface updated; ``--pre-merge-condition-resolved-in-diff``
deliberately not exposed (it carries a commit SHA, not prose).

Finding 3: add a TODO(slice-6 TASK-6-6) block to the
test_mcp_baseline_capture.py module docstring naming the synthetic-
baseline trip-wire — slice-6's TASK-6-6 must hard-gate on
``_meta.synthetic`` so the 5% latency budget cannot pass by coincidence
against placeholder p50/p95 numbers.

---------

Co-authored-by: egg <egg@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: egg-orchestrator <egg@localhost>
Co-authored-by: james-in-a-box[bot] <246424927+james-in-a-box[bot]@users.noreply.github.com>
Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
Co-authored-by: James Wiesebron <jameswiesebron@khanacademy.org>
Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
@james-in-a-box

james-in-a-box Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

egg review completed. View run logs

24 previous review(s) hidden.

jwbron added a commit that referenced this pull request Jun 3, 2026
* docs: update BRC event-pump docs for slice-4 (#2951)

Reflect the slice-4 changes: EGG_BRC_EVENT_PUMP flag deleted,
EGG_BRC_MEMORY default flipped off→full, legacy capped-restart
wrapper and MAX_CONSENSUS_RESTARTS removed.

- docs/index.md: fix stale anchor links and description for the BRC
  Consensus Wrapper entry
- docs/reference/orchestrator-cli.md: update EGG_BRC_MEMORY default
  from `off` to `full`
- docs/architecture/brc-memory.md: update mode table default row and
  opening paragraph to reflect slice-4 flip
- docs/development/STRUCTURE.md: update consensus_wrapper.py
  description to remove legacy capped-restart reference
- docs/guides/sdlc-pipeline.md: replace MAX_CONSENSUS_RESTARTS
  restart-cap description with event-pump idle-budget model
- docs/reference/agent-recovery.md: replace Restart with Backoff
  section and Before/After table with event-pump crash handling

* docs: address review feedback — wrapper classifiers are inert in event-pump arm

Reviewer flagged that several places in the docs still describe a
pre-#2908 capped-restart dispatch path where the wrapper branches on
`is_transient_crash` / `is_buffer_overflow` / `is_startup_failure`
and restarts with exponential backoff. The post-slice-4 wrapper does
not do this: `consensus_wrapper.py` L153-170 defines the classifier
helpers but L686-701 (the `propose|ack|nack` arm) calls none of them.
Every non-zero agent exit takes the same `AGENT_FAIL_STREAK++` +
`sleep 1` + idle-budget path.

Updates:

docs/reference/agent-recovery.md
- "Crash exit-code classification" table: collapse the buffer-overflow
  row from "Wrapper exits immediately" to the uniform
  "Increments consecutive-failure counter; idle-budget escalation
  emits alert" cell shared by every other row, and rename the
  middle column to "Named helper (currently inert)" so the table
  no longer reads as a live dispatch matrix.
- Add a buffer-overflow note explaining the SDK 1 MiB overflow
  recurs each iteration until `EGG_BRC_IDLE_BUDGET_MIN` trips.
- Rename the "Consensus Wrapper: Transient Crash Recovery" section
  to "Consensus Wrapper: Exit-Code Classifiers (Preserved Helpers)"
  with a header callout that the helpers are not invoked today, so
  the design-intent subsections that follow are framed correctly.
- Behavior Matrix row at L388: replace
  "classified as transient API/network error" with the same
  idle-budget escalation phrasing as the other wrapper rows.

docs/guides/concurrent-execution.md
- Step 5 / step 6 of "How it works": replace the
  exponential-backoff-restart story with the actual
  `AGENT_FAIL_STREAK++` + idle-budget behaviour. Note explicitly
  that the classifier helpers exist but are not invoked today.
- Drop the `TRANSIENT_RESTART_BACKOFF_INITIAL` configuration row
  (constant does not exist in the post-slice-4 wrapper). Rewrite the
  `STARTUP_FAILURE_WINDOW_SECONDS` row to note the helper is inert.
- "Transient Crash Recovery" appendix at L880: rewrite to remove
  claims about per-crash restart, exponential backoff, and
  classifier-driven recovery.

Authored-by: egg

---------

Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant