Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a5e4593
Initialize SDLC contract for issue #1897
Apr 22, 2026
48d1fd5
refine(#1897): draft analysis for agent wait heuristics
Apr 22, 2026
8f26345
refine(#1897): address reviewer_refine NACK — restore decision-6, tra…
Apr 22, 2026
c3e9c3f
Persist statefiles after refine phase
Apr 22, 2026
83ebf8a
Persist HITL resolution after refine phase gate
Apr 23, 2026
58898f4
plan(#1897): architect output — Option C 6-track decomposition
Apr 23, 2026
cb85971
risk_analyst: assess technical risks for #1897 agent wait heuristics …
Apr 23, 2026
54f5b29
plan: implementation plan for #1897 (event-driven BRC wait + heartbeats)
Apr 23, 2026
e897343
plan: align #1897 plan with existing CONSENSUS_CONFIRMED enum
Apr 23, 2026
d2cad35
plan(#1897): incorporate risk_analyst HIGH/MEDIUM mitigations
Apr 23, 2026
427f102
plan(#1897): address reviewer_plan NACK — schema, SSE event, idiom regex
Apr 23, 2026
e340546
plan(#1897): revise architect output addressing reviewer_plan NACK
Apr 23, 2026
147fc28
plan(#1897): revision 3 — reconcile architect output with CONFIRMED plan
Apr 23, 2026
98b88fa
risk_analyst(#1897): revision 2 — reconcile with architect rev 3 + CO…
Apr 23, 2026
1c0b4c0
docs: document server-side contract decision bridge (#1899)
james-in-a-box[bot] Apr 22, 2026
63d8d94
docs: document gateway session idle timeout config [doc-updater] (#1898)
james-in-a-box[bot] Apr 22, 2026
70407e0
Fix #1895: right-size gateway, orchestrator, and sandbox pod resource…
jwbron Apr 23, 2026
aa965cd
Fix #1905: /sdlc auto-resolves phase_gate follow-ups from context (#1…
james-in-a-box[bot] Apr 23, 2026
e90ca56
Replace interactive mode with run_agent_task MCP primitive (#1900)
james-in-a-box[bot] Apr 23, 2026
56c645a
plan(#1897): architect revision 4 — address reviewer_plan NACK on rev 3
Apr 23, 2026
a7f53ce
plan(#1897): revision 4 — address reviewer_plan NACK (blockers 1-6 + …
Apr 23, 2026
1f9c49b
risk_analyst(#1897): revision 3 — address reviewer_plan NACK
Apr 23, 2026
6357eb6
docs(#1897): agent-wait-patterns reference + concurrent-execution "Ho…
Apr 23, 2026
6aa01d0
Phase 1-2 (#1897): event-driven message wait primitive
Apr 23, 2026
1ca3003
Phase 2 (#1897): egg-orch message wait / wait-loop / heartbeat CLI
Apr 23, 2026
2578f36
Phase 3 (#1897): wire HEARTBEAT into HealthMonitor
Apr 23, 2026
f76d1de
Phase 4 (#1897): waitress worker pool sizing + in-flight long-polls g…
Apr 23, 2026
b9a43c3
Phase 5 (#1897): event-driven wait in consensus_wrapper
Apr 23, 2026
a6f21ee
Phase 6 (#1897): event-driven agent-prompt STAY ALIVE + anti-patterns
Apr 23, 2026
1b690ef
Phase 7 (#1897): deprecate QUESTION + add HEARTBEAT to CLI + BRC history
Apr 23, 2026
e1afdfa
tests(#1897): event-driven BRC wait + HEARTBEAT coverage
Apr 23, 2026
be92c3f
Address reviewer_code NACK on #1897 proposal v1
Apr 23, 2026
967a546
Address tester NACK: ruff format, hybrid SSE+wait consensus loop
Apr 23, 2026
3ac9ce8
docs(#1897): document event-driven wait CLI + drop QUESTION references
Apr 23, 2026
33e2cf1
tests(#1897): address reviewer_code blockers 1-3 + non-blocking items
Apr 23, 2026
314be8d
Fix #1897: align cmd_message_heartbeat body + wait-loop exit code wit…
Apr 23, 2026
b774607
Address tester fixture: revert wait-loop rc=3→rc=3 pass-through
Apr 23, 2026
50a346b
tests(#1897): v3 — address reviewer_contract blockers 1 + 6
Apr 23, 2026
14f0567
Address reviewer_contract NACK blockers 2, 3, 5 on #1897 proposal v3
Apr 23, 2026
85862d8
Address reviewer_code blockers 1, 2, 3 on #1897 proposal v4 (v5)
Apr 23, 2026
7727209
Persist statefiles after plan phase
Apr 23, 2026
1954269
Persist statefiles after implement phase
Apr 23, 2026
5f78db7
Remove ephemeral agent-output handoff artifacts (#1731)
Apr 23, 2026
d61c1c6
Fix test: update reviewer QUESTION test for #1897 removal
james-in-a-box[bot] Apr 23, 2026
a3d505a
Address review feedback on PR #1919
james-in-a-box[bot] Apr 23, 2026
e7b7892
Merge origin/main into egg/issue-1897: resolve conflicts in Makefile,…
jwbron Apr 23, 2026
f220271
Merge origin/main into egg/issue-1897: resolve conflicts in Makefile,…
jwbron Apr 23, 2026
c78b8ea
Address re-review feedback: fix B3 regression, NB1 guard, NB2 dead call
james-in-a-box[bot] Apr 23, 2026
497328a
Add smoketest-long-poll target, replace QUESTION in test_mcp_tools
james-in-a-box[bot] Apr 23, 2026
5562c8c
Add smoketest-long-poll to .PHONY, bump timeout to 90s
james-in-a-box[bot] Apr 23, 2026
e60d0e6
Address review feedback: fix docs, wait-loop --json, local rc
egg-reviewer[bot] Apr 23, 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
1,684 changes: 1,684 additions & 0 deletions .egg-state/brc-history/1897-implement.json

Large diffs are not rendered by default.

3,995 changes: 3,995 additions & 0 deletions .egg-state/brc-history/1897-implement.md

Large diffs are not rendered by default.

677 changes: 677 additions & 0 deletions .egg-state/brc-history/1897-plan.json

Large diffs are not rendered by default.

1,762 changes: 1,762 additions & 0 deletions .egg-state/brc-history/1897-plan.md

Large diffs are not rendered by default.

235 changes: 235 additions & 0 deletions .egg-state/brc-history/1897-refine.json

Large diffs are not rendered by default.

654 changes: 654 additions & 0 deletions .egg-state/brc-history/1897-refine.md

Large diffs are not rendered by default.

846 changes: 846 additions & 0 deletions .egg-state/contracts/issue-1897.json

Large diffs are not rendered by default.

328 changes: 328 additions & 0 deletions .egg-state/drafts/1897-analysis.md

Large diffs are not rendered by default.

1,565 changes: 1,565 additions & 0 deletions .egg-state/drafts/1897-plan.md

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ EGG_IMAGE_TAG := $(shell git describe --always --dirty 2>/dev/null || echo lates
setup deps venv install-linters check-linters \
lint lint-python lint-shell lint-yaml lint-docker lint-actions lint-custom \
test security \
test-integration test-e2e test-security \
test-integration test-e2e test-security smoketest-long-poll \
lint-fix lint-python-fix lint-shell-fix lint-yaml-fix \
build \
k3s-setup k3s-secrets deploy redeploy k3s-teardown k3s-import
Expand Down Expand Up @@ -253,6 +253,19 @@ test: venv
@echo "==> Running unit tests..."
$(PYTEST) tests/ gateway/tests/ orchestrator/tests/ shared/tests/ -v $(PYTEST_ARGS)

smoketest-long-poll: export PYTHONPATH := shared:gateway:orchestrator
smoketest-long-poll: venv ## Smoke-test the long-poll / event-driven wait infrastructure
$(PYTEST) \
orchestrator/tests/test_messages.py::TestWaitEndpoint \
orchestrator/tests/test_messages.py::TestLongPolling \
orchestrator/tests/test_messages.py::TestInflightLongPollGauge \
orchestrator/tests/test_messages.py::TestWaitTimeoutFloorRegression \
orchestrator/tests/test_message_store.py::TestWaitForTypesFilter \
orchestrator/tests/test_message_store.py::TestNotifyMultipleWaiters \
orchestrator/tests/test_cli.py::TestWaitressSizing \
orchestrator/tests/test_concurrent_integration.py::TestEventDrivenConsensusWait \
-v --timeout=90

security:
@echo "==> Running security scan..."
@if command -v $(BANDIT) >/dev/null 2>&1; then \
Expand Down Expand Up @@ -366,7 +379,7 @@ k3s-secrets: ## Create gateway secrets from ~/.config/egg/
fi
@if [ ! -f "$$HOME/.config/egg/lifecycle-secret" ]; then \
echo "ERROR: $$HOME/.config/egg/lifecycle-secret not found."; \
echo "Run 'bin/egg-deploy init' to generate it (required by #1769 HITL auth)."; \
echo "Generate it: openssl rand -hex 32 > $$HOME/.config/egg/lifecycle-secret"; \
exit 1; \
fi
@echo "==> Creating gateway-secrets in egg-system namespace..."
Expand Down
5 changes: 3 additions & 2 deletions docs/guides/agent-teams.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,15 @@ Not all messages need the same rigor:
| Message type | Signal cost | Rationale |
|-------------|-------------|-----------|
| STATUS, PROGRESS | Cheap talk | Low overhead, informative when interests are aligned |
| HANDOFF, QUESTION | Cheap talk | Directed coordination — low overhead, enables role-boundary artifact transfers and clarification requests |
| HANDOFF | Cheap talk | Directed coordination — low overhead, enables role-boundary artifact transfers |
| HEARTBEAT | Cheap talk | Typed agent-state transition (`WORKING`/`WAITING_ON_ROLE`/`PROPOSED`/`IDLE`) — schema-validated and rate-limited, consumed by the overseer for stall detection (see [Agent Wait Patterns §4](../reference/agent-wait-patterns.md#4-heartbeat-message-type)) |
| CONSENSUS_PROPOSE | Costly signal | Attestations are harder to produce without doing the work |
| CONSENSUS_ACK | Costly signal | Must reference specific artifacts reviewed (prevents rubber-stamping) |
| CONSENSUS_NACK | Costly signal | Must include specific, actionable objection with artifact references |

This distinction comes from game theory: cheap talk (Crawford & Sobel, 1982) works when interests are fully aligned, but LLM agents are *unreliable communicators* — they may genuinely believe bad work is good. Costly signals (requiring verifiable evidence) address this.

> **Directed coordination messages** (`HANDOFF`, `QUESTION`, `STATUS`, `PROGRESS`) are cheap talk by design — they carry no attestation burden and serve to keep agents unblocked. The critical distinction is that they flow *outside* the BRC consensus protocol: a `HANDOFF` message does not replace a `CONSENSUS_PROPOSE`, and a `QUESTION` does not replace a `CONSENSUS_NACK`. See [Concurrent Execution — Directed Coordination](concurrent-execution.md#directed-coordination) for the CLI syntax, message type guidance, and worked examples.
> **Directed coordination messages** (`HANDOFF`, `STATUS`, `PROGRESS`, `HEARTBEAT`) are cheap talk by design — they carry no attestation burden and serve to keep agents unblocked. The critical distinction is that they flow *outside* the BRC consensus protocol: a `HANDOFF` message does not replace a `CONSENSUS_PROPOSE`, and clarification questions do not flow as free-form messages. `QUESTION` was removed in [#1897](https://github.com/jwbron/egg/issues/1897) because it had no reliable respondent; reviewer questions now live inside `CONSENSUS_NACK` rationales (where the producer is obligated to address them on re-propose), and "I'm blocked on peer X" is advertised via `HEARTBEAT --state WAITING_ON_ROLE`. See [Concurrent Execution — Directed Coordination](concurrent-execution.md#directed-coordination) for the CLI syntax, message type guidance, and worked examples.

#### Anti-Sycophancy Measures

Expand Down
54 changes: 43 additions & 11 deletions docs/guides/concurrent-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,25 @@ All concurrent agent containers are wrapped with a shell script defined in `orch

Agents communicate with each other during concurrent execution via the orchestrator message bus (`orchestrator/message_store.py`). In production, messages are stored in Redis Streams, surviving orchestrator restarts. Messages are cleared at phase transition. In test environments, an in-memory fallback is used when Redis is not available.

### How to Wait

Agents wait for BRC messages with a single canonical command — `egg-orch message wait-loop` — which long-polls the bus server-side and exits only on a terminal match or a permanent error. The full contract (the one-liner for producers and reviewers, the four anti-patterns to avoid, the `egg-orch message wait` exit codes, the `HEARTBEAT` schema, and the `EGG_MESSAGE_POLL_MAX_WAIT` ↔ gateway-Squid coupling) is in [Agent Wait Patterns](../reference/agent-wait-patterns.md) — read it before writing an outer `for`-loop, a `sleep`, or a multi-call poll sequence.

```bash
# Producer STAY ALIVE — exits on consensus, re-review, or overseer alert
egg-orch message wait-loop \
--for CONSENSUS_CONFIRMED \
--for CONSENSUS_RE_REVIEW \
--for OVERSEER_ALERT

# Reviewer STAY ALIVE — also wakes on new proposals
egg-orch message wait-loop \
--for CONSENSUS_PROPOSE \
--for CONSENSUS_RE_REVIEW \
--for CONSENSUS_CONFIRMED \
--for OVERSEER_ALERT
```

### Sending Messages

```
Expand All @@ -106,13 +125,15 @@ Request body:
{
"from_role": "coder",
"to_role": "tester", // or "all" for broadcast
"message_type": "PROGRESS", // PROGRESS, QUESTION, STATUS, AGENT_FAILED, HANDOFF
"message_type": "PROGRESS", // PROGRESS, STATUS, HANDOFF, HEARTBEAT, AGENT_FAILED
"subject": "Implemented auth module",
"body": "auth.py is complete, tests can begin",
"metadata": {}
}
```

> `QUESTION` was removed in [#1897](https://github.com/jwbron/egg/issues/1897) — it encouraged off-protocol chatter with no handler. Use `HANDOFF` when you need a peer to act, `HEARTBEAT` to advertise state, and typed NACK rationale to ask clarifying questions of a producer you're reviewing.

The pipeline's current phase is automatically attached to each message. This applies to both the general message endpoint and the consensus signal handlers — all `CONSENSUS_*` messages (propose, ACK, NACK, withdraw, confirmed, re-review) and other BRC-adjacent types (`STATUS`, `HANDOFF`, `AGENT_FAILED`, etc.) include the phase field so that downstream consumers like BRC history persistence and PR summary generation can correctly group messages by phase.

### Polling Messages
Expand Down Expand Up @@ -144,10 +165,10 @@ Returns total message count and a breakdown by message type.
| Type | Purpose |
|------|---------|
| `PROGRESS` | Agent progress updates for other agents |
| `QUESTION` | Agent asking another agent a question |
| `STATUS` | General status announcements |
| `AGENT_FAILED` | Orchestrator notifying agents of a peer failure |
| `HANDOFF` | Agent signaling completion of a handoff artifact |
| `HEARTBEAT` | Agent state transition (`WORKING`, `WAITING_ON_ROLE`, `PROPOSED`, `IDLE`) — resets the orchestrator's `last_heartbeat` without emitting a free-form `PROGRESS` entry. See [Agent Wait Patterns — HEARTBEAT](../reference/agent-wait-patterns.md#4-heartbeat-message-type) for the metadata schema. |
| `AGENT_FAILED` | Orchestrator notifying agents of a peer failure |
| `CONSENSUS_PROPOSE` | Producer broadcasting its proposal for review |
| `CONSENSUS_ACK` | Reviewer approving a producer's proposal |
| `CONSENSUS_NACK` | Reviewer rejecting a producer's proposal (with reason) |
Expand All @@ -156,11 +177,15 @@ Returns total message count and a breakdown by message type.
| `CONSENSUS_RE_REVIEW` | Orchestrator notifying a reviewer that their prior confirmation is stale and they must re-review the producer's new proposal version |
| `OVERSEER_ALERT` | Health anomaly or lifecycle alert. Sent by the overseer agent for health anomalies (always with explicit `pipeline_id` and `from_role: overseer`), and by the orchestrator when the overseer is auto-respawned (with diagnostic metadata including exit code, log tail, and container IDs) |

> **Removed in #1897**: `QUESTION` was dropped from the type vocabulary because it had no delivery semantics and was only used as informal free-form chatter. Agents that need a peer to act should use `HANDOFF`; agents that need to advertise state should use `HEARTBEAT`; reviewers with clarifying questions should put them in the `NACK` rationale so the producer sees them and can address them on re-propose.

### Message Store Backend

The message store uses Redis Streams when Redis is available, falling back to an in-memory store for tests or unconfigured environments. The backend is selected via the `EGG_MESSAGE_STORE_BACKEND` environment variable (`"auto"` by default, `"redis"` to require Redis, `"memory"` to force in-memory).

**Note:** Long-poll (`?wait=<s>`) only blocks with the Redis Streams backend. The in-memory store silently falls back to a non-blocking poll, so agents in test environments may see immediate empty responses instead of blocking.
**Long-poll semantics (both backends):** `GET /messages/wait?for=<TYPE>&timeout=<s>` blocks on both backends until a matching message arrives or the timeout elapses. The in-memory store implements blocking via a per-pipeline `threading.Condition`; the Redis backend uses `XREAD BLOCK` with a server-side type-filter loop. The silent non-blocking fallback that previously lived in `routes/messages.py` was removed in [#1897](https://github.com/jwbron/egg/issues/1897) so backend misconfiguration fails loudly in CI instead of returning empty results. See [Agent Wait Patterns](../reference/agent-wait-patterns.md#3-exit-code-contract-for-egg-orch-message-wait) for the full exit-code contract and the `EGG_MESSAGE_POLL_MAX_WAIT` cap.

**Clear-on-phase-transition safety:** When the store is cleared at phase boundaries, all blocked waits wake and return an empty list (within ~100 ms). This prevents blocked agents from staying stuck across a phase transition.

### Per-Phase Cleanup

Expand Down Expand Up @@ -191,7 +216,7 @@ egg-orch message send --to <role> --type <type> --subject "<subject>" --body "<b
| Parameter | Required | Description |
|-----------|----------|-------------|
| `--to` | Yes | Target agent role (e.g., `tester`, `coder`) or `all` for broadcast |
| `--type` | Yes | Message type: `HANDOFF`, `QUESTION`, `STATUS`, `PROGRESS` |
| `--type` | Yes | Message type: `HANDOFF`, `STATUS`, `PROGRESS`, `HEARTBEAT`. (`QUESTION` was removed in [#1897](https://github.com/jwbron/egg/issues/1897) — see note below.) |
| `--subject` | No | Short description of the message |
| `--body` | No | Detailed message content |

Expand All @@ -202,9 +227,15 @@ The pipeline ID is auto-resolved from `EGG_PIPELINE_ID` if set; otherwise pass i
| Type | Use when | Example |
|------|----------|---------|
| `HANDOFF` | You've produced an artifact that another agent needs to act on, especially when role boundaries prevent you from completing the work yourself | Coder can't push test files → HANDOFF to tester with file paths |
| `QUESTION` | You need clarification from a specific agent before you can proceed with your own work | Tester asks coder: "What's the expected return type for `process_batch()`?" |
| `STATUS` | Your current state affects a peer's decisions or timing | Documenter tells reviewer: "Docs not ready yet, reviewing coder output first" |
| `PROGRESS` | You've completed a milestone that peers may be waiting on | Coder tells tester: "API endpoints committed and pushed" |
| `HEARTBEAT` | You have a machine-actionable state transition to advertise (`WORKING`, `WAITING_ON_ROLE`, `PROPOSED`, `IDLE`) — use `egg-orch message heartbeat --state ...` rather than `message send --type HEARTBEAT` so the dedicated endpoint's schema validation, dedup, and rate limiting apply | Tester enters `WAITING_ON_ROLE` → `egg-orch message heartbeat --state WAITING_ON_ROLE --waiting-on coder`. See [Agent Wait Patterns — HEARTBEAT](../reference/agent-wait-patterns.md#4-heartbeat-message-type). |

> **On `QUESTION` (removed in [#1897](https://github.com/jwbron/egg/issues/1897))**: the old `QUESTION` type had no guaranteed respondent and became a free-form chatter channel. For the typical "I'm blocked until you answer" case:
>
> - If you are a **reviewer** blocked on the producer's intent, put the question in your `egg-orch consensus nack --reason "..."` so the producer sees it in BRC history and addresses it on the next propose.
> - If you are a **producer** blocked on another producer (e.g. tester blocked on coder), use `HANDOFF` with a concrete request rather than a free-form question.
> - If you need to advertise that you are waiting on a peer (so the overseer doesn't classify you as stalled), emit `egg-orch message heartbeat --state WAITING_ON_ROLE --waiting-on <role>`.

### Worked Example: Role-Boundary Handoff (Coder → Tester)

Expand Down Expand Up @@ -250,15 +281,16 @@ egg-orch message poll --wait 30
When a directed message arrives:

1. **HANDOFF**: Act on the handoff artifact. If it requires work, do the work and acknowledge via a `STATUS` or `PROGRESS` message back.
2. **QUESTION**: Answer the question via `egg-orch message send --to <asker> --type STATUS`. (`STATUS` serves as the generic reply type since the directed coordination vocabulary does not include a dedicated `RESPONSE` type.)
3. **STATUS/PROGRESS**: Use the information to inform your own work — no response required unless the status changes your plan.
2. **STATUS/PROGRESS**: Use the information to inform your own work — no response required unless the status changes your plan.
3. **HEARTBEAT**: Peer state transitions are informational — consume them (e.g., to decide whether to send a follow-up `HANDOFF`) but do not reply. The overseer consumes `HEARTBEAT` for stall detection; agents typically only read them to disambiguate "peer is waiting on me" from "peer is making progress elsewhere".

### Best Practices

- **Be specific.** Include file paths, commit SHAs, and concrete details — not just "please handle this."
- **Send early.** Don't wait until your proposal to communicate coordination needs. Send a HANDOFF as soon as you know another agent needs to act.
- **One message per concern.** Don't bundle unrelated coordination requests in a single message.
- **Use the right type.** `HANDOFF` signals "you need to do something"; `QUESTION` signals "I'm blocked until you answer"; `STATUS` and `PROGRESS` are informational.
- **Use the right type.** `HANDOFF` signals "you need to do something"; `STATUS` and `PROGRESS` are informational peer updates; `HEARTBEAT` advertises typed agent state (emit via `egg-orch message heartbeat`, not `message send`).
- **Never use `QUESTION`.** It was removed in [#1897](https://github.com/jwbron/egg/issues/1897). Reviewer-to-producer questions go in `NACK` rationales; producer-to-producer "I need X" goes in `HANDOFF`; "I'm waiting on a peer" goes in a `HEARTBEAT` with `state=WAITING_ON_ROLE`.

## Readiness Signaling Protocol

Expand Down Expand Up @@ -553,11 +585,11 @@ At each phase boundary, the orchestrator writes a **lossless** chronological log
**How it works:**

1. After a phase completes (before `_commit_statefiles_to_worktree`), the orchestrator retrieves all messages from the message store for the pipeline
2. Messages are filtered using `BRC_HISTORY_TYPES` — the six `CONSENSUS_*` types (`CONSENSUS_PROPOSE`, `CONSENSUS_ACK`, `CONSENSUS_NACK`, `CONSENSUS_WITHDRAW`, `CONSENSUS_CONFIRMED`, `CONSENSUS_RE_REVIEW`) **plus** orchestrator-adjacent types (`STATUS`, `HANDOFF`, `QUESTION`, `AGENT_FAILED`, `NUDGE`, `OVERSEER_ALERT`) — **and** by phase, so each file contains only that phase's BRC and coordination activity
2. Messages are filtered using `BRC_HISTORY_TYPES` — the six `CONSENSUS_*` types (`CONSENSUS_PROPOSE`, `CONSENSUS_ACK`, `CONSENSUS_NACK`, `CONSENSUS_WITHDRAW`, `CONSENSUS_CONFIRMED`, `CONSENSUS_RE_REVIEW`) **plus** orchestrator-adjacent types (`STATUS`, `HANDOFF`, `AGENT_FAILED`, `NUDGE`, `OVERSEER_ALERT`, `HEARTBEAT`) — **and** by phase, so each file contains only that phase's BRC and coordination activity
3. If matching messages exist, they are formatted as chronological markdown entries with full metadata (see file format below) and written to `.egg-state/brc-history/{identifier}-{phase}.md`. A companion `.json` file containing `msg.to_dict()` for every filtered message is also written for machine consumers
4. If no matching messages exist for that phase, no files are created (graceful no-op)

> **Note:** `BRC_HISTORY_TYPES` is a single unified frozenset containing all twelve message types listed above. There is no separate subset — the PR body links to the committed transcripts rather than computing inline tallies (see [#1828](https://github.com/jwbron/egg/issues/1828)).
> **Note:** `BRC_HISTORY_TYPES` is a single unified frozenset containing all twelve message types listed above. There is no separate subset — the PR body links to the committed transcripts rather than computing inline tallies (see [#1828](https://github.com/jwbron/egg/issues/1828)). `QUESTION` was dropped from this set in [#1897](https://github.com/jwbron/egg/issues/1897); `HEARTBEAT` replaced it.

**PR-phase safety net:** The per-phase write (step 1) is best-effort — if the commit or push fails, BRC history files may not make it to the branch. As a safety net, the PR phase re-writes BRC history for **all completed phases** before creating the PR. Since `_write_brc_history()` is idempotent (it overwrites existing files), the re-write is safe regardless of whether the per-phase write succeeded. This ensures BRC history files are always present in the PR diff.

Expand Down
2 changes: 1 addition & 1 deletion docs/guides/custom-phase.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ through the MCP server instead of dropping into a sandboxed interactive
Claude session.

> Issue: [#1762](https://github.com/jwbron/egg/issues/1762).
> Status: new in this PR. See also
> See also
> [SDLC Pipeline Guide](sdlc-pipeline.md),
> [Babysit-PR Guide](babysit-pr.md),
> [Agent Roles Reference](../reference/agent-roles.md).
Expand Down
Loading
Loading