Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6137694
docs(#2908 slice-3 task-3-4..3-5): event-handler mission.md + slice-3…
Jun 2, 2026
e3ab9e9
docs(#2908 slice-3): address reviewer_code v1 non-blocking observations
Jun 2, 2026
90b2cb5
docs(#2908 slice-3): address reviewer_code v2 non-blocking observations
Jun 2, 2026
ed8a4c5
feat(#2908 slice-3 task-3-1): compose_event_prompt per-event prompt c…
Jun 2, 2026
27ed9d0
feat(#2908 slice-3 task-3-2): wire event-pump wrapper to compose_even…
Jun 2, 2026
2261d5c
feat(#2908 slice-3 task-3-3): collapse _build_brc_preamble for event-…
Jun 2, 2026
7cff8d1
test(#2908 slice-3): add compose_event_prompt + preamble-collapse tes…
Jun 2, 2026
63c6bfe
style(#2908 slice-3): ruff format on slice-3 source files
Jun 2, 2026
dfad765
fix(#2908 slice-3): rebase reconciliation for older slice-3 base
Jun 2, 2026
fc2e82e
fix(#2908 slice-3): address reviewer_contract v1 NACKs
Jun 2, 2026
66b7e73
fix(#2908 slice-3): address tester + reviewer_contract v1 NACKs
Jun 2, 2026
078679b
docs(#2908 slice-3): documenter consensus participation marker
Jun 2, 2026
ccf0144
feat(#2908 slice-3): coder consensus participation marker
Jun 2, 2026
bba160b
fix(#2908 slice-3): address reviewer_code v2 + reviewer_code_holistic…
Jun 2, 2026
e7d9c84
test(#2908 slice-3): tester hardening on compose_event_prompt + pream…
Jun 2, 2026
6c4668d
Persist BRC history for slice-3 (#2548)
Jun 2, 2026
374e921
Fix checks: apply automated formatting fixes
Jun 2, 2026
aaaa17f
Address reviewer_holistic v2 feedback
egg-reviewer[bot] Jun 2, 2026
a159d6a
Strip NACK payload from event_section JSON to honour envelope cap
egg-reviewer[bot] Jun 3, 2026
39a08bb
Merge origin/main into egg/issue-2908-impl2/slice-3: resolve conflict…
jwbron Jun 3, 2026
7bdb29d
Fix dual-role banner contradiction with coder-owns-tests orientation
egg-reviewer[bot] Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19,027 changes: 19,027 additions & 0 deletions .egg-state/brc-history/issue-2908-impl2-implement-slice-3.json

Large diffs are not rendered by default.

19,567 changes: 19,567 additions & 0 deletions .egg-state/brc-history/issue-2908-impl2-implement-slice-3.md

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion docs/architecture/brc-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,23 @@ git log {last_reviewed_commit_sha}..HEAD --not origin/{base_branch} -p

This is intentionally the **full** delta since the prior review, not just
the orchestrator's `changed_artifacts` set — the re-review must audit
every change as a fresh review (the BRC adversarial re-review contract),
every change as a fresh review (the BRC adversarial re-review contract
shared with the PR-side reviewer — see
[`shared/prompts/REVIEWER-SYNC.md` Diff command (re-review / delta)](../../shared/prompts/REVIEWER-SYNC.md)),
or the stateless pump systematically weakens adversarial review
(risk_analyst R6).

When the mode is `write-only` (the slice-1 rollout posture), the read path
passes an empty memory excerpt — writes happen but reads are no-ops,
preserving the inert default.

For the full architecture of the slice-3 reader (composer shape, 10 KB
envelope, tail-position memory delivery per architect od-6 Option B,
preamble collapse, slice-2 wrapper interplay), see
[Orchestrator — BRC Per-Event Prompt Composer + Preamble Collapse](orchestrator.md#brc-per-event-prompt-composer--preamble-collapse-slice-3)
and its wait-side companion
[agent-wait-patterns §10.9](../reference/agent-wait-patterns.md#109-brc-per-event-prompt-composer--preamble-collapse-slice-3).

## Acceptance contract for the writer (slice-1)

The acceptance set codified by slice-1 task-1-6:
Expand Down
284 changes: 284 additions & 0 deletions docs/architecture/orchestrator.md

Large diffs are not rendered by default.

253 changes: 251 additions & 2 deletions docs/reference/agent-wait-patterns.md

Large diffs are not rendered by default.

82 changes: 74 additions & 8 deletions orchestrator/consensus_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1024,19 +1024,85 @@
return $rc
}}

# Invoke the agent one-shot with the per-event prompt. Slice-2 ships a
# minimal stub -- slice-3 (TASK-3-1 / TASK-3-2) replaces the prompt
# body with the full ``compose_event_prompt`` payload (memory excerpt
# + per-producer ``git log {{sha}}..HEAD --not origin/{{base}} -p``
# delta + NACK payload).
# Invoke the agent one-shot with the per-event prompt. Slice-3
# (TASK-3-1 / TASK-3-2) replaces the slice-2 stub with the full
# ``compose_event_prompt`` payload: memory excerpt (when
# ``EGG_BRC_MEMORY=full``) + per-producer
# ``git log {{sha}}..HEAD --not origin/{{base}} -p`` delta + NACK
# payload. The composer lives at
# ``/opt/egg-runtime/orchestrator/routes/event_prompt.py`` -- the
# wrapper invokes its ``if __name__ == '__main__'`` CLI directly so the
# heavy ``orchestrator.routes`` package ``__init__.py`` (Flask import)
# is bypassed. ``EGG_EVENT_PROMPT_SCRIPT`` overrides the path for tests.
#
# When the composer fails (script missing, malformed memory file, git
# log subprocess crash) we fall back to the slice-2 minimal stub so the
# event-pump keeps running rather than failing the agent invocation.
# This is symmetric with the rest of the wrapper's "block, alert,
# continue" stance under the idle-budget safety net.
invoke_agent_for_event() {{
local action="$1"
local event_payload="$2"
local role="${{EGG_AGENT_ROLE:-unknown}}"
local slice="${{EGG_SLICE_ID:-none}}"
local prompt
prompt=$(printf 'BRC event-pump handler\nRole: %s\nSlice: %s\nAction: %s\nEvent payload (JSON): %s\n\nHandle this single event according to the role contract, update durable BRC memory, then exit naturally. The wrapper will invoke you again with the next event.\n' \
"$role" "$slice" "$action" "$event_payload")
local base_branch="${{EGG_BASE_BRANCH:-main}}"
local script_path="${{EGG_EVENT_PROMPT_SCRIPT:-/opt/egg-runtime/orchestrator/routes/event_prompt.py}}"
local prompt prompt_rc=1

if [ -r "$script_path" ]; then
# Pass the event_payload JSON via stdin so shell metacharacters
# ($VAR, backticks, ;, &&) don't fall through to argv (the
# #2741 / slice-5 motivating concern; even though this argv is
# composed entirely by the wrapper here, the stdin path keeps
# the surface honest and matches the slice-5 prose-arg rule).
# All four env vars (``EGG_AGENT_ROLE`` / ``EGG_BASE_BRANCH`` /
# ``EGG_REPO_PATH`` / ``EGG_BRC_MEMORY``) are read by the script
# from env directly. The prefix MUST attach to ``python3`` (RHS),
# not ``printf`` (LHS) -- the earlier form attached only to
# ``printf`` and ``python3`` inherited from the parent shell.
# ``EGG_REPO_PATH`` is re-exported explicitly here
# (reviewer_holistic v2 #2): the script falls back to
# ``os.getcwd()`` when unset, but propagating the
# orchestrator-set value keeps the wrapper symmetric and immune
# to an unset-in-parent edge case. Capture stderr to a temp
# file so the cw_log fallback surfaces the first line of the
# failure (script-not-found vs schema-drift vs crash otherwise
# indistinguishable).
local err_tmp
err_tmp=$(mktemp -t event-prompt-stderr.XXXXXX 2>/dev/null || echo "/tmp/event-prompt-stderr-$$.log")
prompt=$(printf '%s' "$event_payload" \
| EGG_AGENT_ROLE="$role" \
EGG_BASE_BRANCH="$base_branch" \
EGG_REPO_PATH="${{EGG_REPO_PATH:-$PWD}}" \
EGG_BRC_MEMORY="${{EGG_BRC_MEMORY:-off}}" \
python3 "$script_path" "$action" 2>"$err_tmp")
prompt_rc=$?
fi

if [ "$prompt_rc" -ne 0 ] || [ -z "$prompt" ]; then
# Fallback prompt -- keep the event-pump moving rather than
# failing the agent invocation when the composer is unavailable
# (script missing, schema drift, transient git log failure).
# The idle-budget safety net catches a wedged event-pump even
# under a degraded composer; failing here would defeat that.
local err_head=""
if [ -n "${{err_tmp:-}}" ] && [ -r "$err_tmp" ]; then
err_head=$(head -1 "$err_tmp" 2>/dev/null)
fi
if [ -n "$err_head" ]; then
cw_log "compose_event_prompt unavailable (rc=$prompt_rc, stderr: $err_head); using slice-2 stub prompt."
else
cw_log "compose_event_prompt unavailable (rc=$prompt_rc); using slice-2 stub prompt."
fi
prompt=$(printf 'BRC event-pump handler\nRole: %s\nSlice: %s\nAction: %s\nEvent payload (JSON): %s\n\nHandle this single event according to the role contract, update durable BRC memory, then exit naturally. The wrapper will invoke you again with the next event.\n' \
"$role" "$slice" "$action" "$event_payload")
fi
# Best-effort cleanup of the stderr capture file. The trap on the
# outer wrapper handles SIGTERM cleanup; this cleanup keeps a busy
# event-pump from accumulating stale per-invocation temp files.
if [ -n "${{err_tmp:-}}" ] && [ -e "$err_tmp" ]; then
rm -f "$err_tmp" 2>/dev/null || true
fi
{agent_command_prefix} "$prompt"
}}

Expand Down
11 changes: 11 additions & 0 deletions orchestrator/routes/consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,23 @@ def _has_pending_peer_proposals(
entry = tracker.matrix.get_entry(reviewer, producer)
# No verdict yet, or stale verdict on a prior version → review needed.
if entry is None or entry.version < current_version:
# Enrich with the producer's current proposal artifact list
# so the wrapper's per-event prompt composer can render a
# degraded changed_artifacts fallback when the reviewer has
# no stored ``last_reviewed_commit_sha`` for this producer
# (slice-3 reviewer_code_holistic v2 finding #1 — wire the
# documented fallback through the live payload shape).
# The snapshot is read-only and locked through the tracker's
# public API; missing producers yield empty artifact lists.
snapshot = tracker.get_current_proposal_snapshot(producer)
artifact_refs = list(snapshot.get("artifacts") or [])
pending.append(
{
"producer": producer,
"current_version": current_version,
"prior_version": entry.version if entry else 0,
"prior_verdict": entry.state.value if entry else "pending",
"artifact_refs": artifact_refs,
}
)
return bool(pending), pending
Expand Down
Loading
Loading