Skip to content

[issue-3077][slice-6/6] Bounded durability: fail-loud... - #3144

Merged
jwbron merged 13 commits into
mainfrom
egg/issue-3077/slice-6
Jun 12, 2026
Merged

[issue-3077][slice-6/6] Bounded durability: fail-loud...#3144
jwbron merged 13 commits into
mainfrom
egg/issue-3077/slice-6

Conversation

@james-in-a-box

Copy link
Copy Markdown
Contributor

auto→memory fallback fails loud (error-level structured log with a stable marker + health-visible degraded flag; explicit memory stays a warning); Redis restart semantics verified. Parallel root — file set disjoint from slices 1-5. Per HITL Q3: no refusal, no change to auto selection.

Base PR: #3139

What's in this PR

Commits (4):

.egg-state/brc-history/3077-implement-slice-6.json | 9356 ++++++++++++++++++++++++++++++++++++++++
 .egg-state/brc-history/3077-implement-slice-6.md   | 8209 +++++++++++++++++++++++++++++++++++
 orchestrator/message_store.py                      |  123 +-
 orchestrator/routes/health.py                      |   45 +-
 orchestrator/tests/test_health_routes.py           |   87 +
 orchestrator/tests/test_message_store.py           |  470 ++
 orchestrator/tests/test_redis_message_store.py     |  252 ++
 7 files changed, 18535 insertions(+), 7 deletions(-)

This slice

Bounded durability: fail-loud memory-backend signal + Redis restart-semantics test

Files affected:

  • orchestrator/message_store.py
  • orchestrator/tests/test_message_store.py
  • orchestrator/tests/test_redis_message_store.py
Tasks (2) + acceptance criteria
  • task-6-1: Fail-loud memory-backend signal in orchestrator/message_store.py _create_message_store() (backend selection ≈589-633): when auto resolves to the in-memory fallback, emit an error-level structured log with a stable marker naming the mid-phase-restart loss risk (#3076 condition) and set a degraded flag visible on the orchestrator health surface. Explicit EGG_MESSAGE_STORE_BACKEND=memory (dev/test intent) emits a warning, not an error, and no degraded flag is required. Per HITL Q3: warning/error + health flag only — do NOT refuse to run and do NOT change auto selection semantics. Test-harness contexts must not be spammed.
    • Acceptance criteria: - auto→memory fallback: exactly one error-level log with the stable marker + health-surface degraded field. - Explicit memory backend: warning level, no degraded flag. - Redis backend: neither. - auto selection behavior (redis-when-available, memory fallback) is unchanged; unit tests are not spammed.
  • task-6-2: Durability tests: extend orchestrator/tests/test_message_store.py for the fail-loud matrix (auto→memory error + flag; explicit memory warning; redis silent) and extend orchestrator/tests/test_redis_message_store.py with a restart-semantics test: mid-phase messages survive store re-instantiation against the same Redis (simulated orchestrator restart), while the DESIGNED phase-boundary wipe via _clear_concurrent_state() (orchestrator/routes/phases.py:113) still clears state. The two wipe semantics are asserted in the same module with explicit naming so accidental mid-phase loss (defect) cannot be conflated with the intentional wipe (required behavior).
    • Acceptance criteria: - Redis path: mid-phase restart preserves the transcript; _clear_concurrent_state() still wipes at the phase boundary. - Fail-loud matrix asserted (error/warning/silent × flag). - Both wipe semantics named explicitly in test ids/docstrings.

Stack

egg-orchestrator and others added 9 commits June 11, 2026 21:52
…ore fallback

When EGG_MESSAGE_STORE_BACKEND is unset/"auto" and Redis is unreachable,
the backend selection silently fell back to the in-memory store, leaving
the orchestrator vulnerable to the #3076 mid-phase-restart loss risk
operators didn't opt into. This slice surfaces that risk two ways:

- An error-level structured log with the stable marker token
  MESSAGE_STORE_AUTO_FALLBACK_TO_MEMORY (scrapers/alerts can pin on a
  single string rather than prose).
- A module-level degraded flag exposed via is_memory_fallback_degraded()
  and surfaced on /api/v1/health under components.message_store, flipping
  the top-level status to "degraded".

Explicit EGG_MESSAGE_STORE_BACKEND=memory (dev/test intent) emits at
warning level and does NOT set the degraded flag — the operator opted in.
Per HITL Q3: warn/error + health flag only — auto selection semantics
and no-refusal-to-run are unchanged.

Both log emissions are once-per-process (reset_message_store does not
clear the once-flags) so integration-test harnesses that reset the
singleton many times per pytest run aren't spammed; tests for this
feature use _reset_memory_fallback_state_for_test to re-arm.

The health-route read goes through the message_store module's pure
getter, preserving the issue #1897 TASK-4-3 isolation invariant — no
MessageStore method is called on the /health request path. Added a
regression test that re-locks that invariant for the slice-6 surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
TASK-6-2: extend message-store test coverage for the bounded-durability
slice (HITL Q3 fail-loud only, no refusal).

orchestrator/tests/test_message_store.py:
- TestBackendSelectionFailLoudMatrix pins TASK-6-1's contract on
  _create_message_store(): auto→memory fallback emits a single
  ERROR-level log with a stable, grep-able marker
  (MEMORY_FALLBACK_MARKER) and flips a module-level degraded flag
  (is_message_store_degraded()) the health surface attaches to;
  explicit memory backend stays at WARNING and keeps the flag clear;
  Redis-resolved auto is silent; explicit redis with Redis down still
  raises. Two sequencing pins: a subsequent Redis-success
  _create_message_store() clears the flag (boot-time blip recovers),
  and an explicit memory selection after an auto-fallback clears the
  flag (operator intent erases the degradation).

orchestrator/tests/test_redis_message_store.py:
- TestRedisRestartSemanticsVsPhaseBoundaryWipe asserts the two
  durability semantics together so #3076 mid-phase loss (defect) and
  _clear_concurrent_state phase-boundary wipe (required behavior)
  cannot be conflated. Restart pins: transcript bytes, per-type
  counters, and since_id scan-fallback resolution all survive a
  re-instantiated RedisMessageStore against the same Redis backend.
  Wipe pin: _clear_concurrent_state() routed at the singleton drains
  the Redis stream as designed. Combined invariant: a phase-boundary
  wipe followed by a restart stays empty (the wipe is persistent).

The fail-loud matrix tests intentionally fail until TASK-6-1 lands the
MEMORY_FALLBACK_MARKER constant and is_message_store_degraded() query
function — this is the parallel-root contract pin, not a regression.

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

Pulls TASK-6-1 (coder) on top of the tester branch and resolves the
test_message_store.py overlap by keeping the coder's
TestMemoryFallbackFailLoudSignal class (covers the basic matrix:
auto-fallback → ERROR + degraded; explicit memory → WARNING; redis
silent; once-per-process; reset helper rearms) and adding a new
tester-side TestMemoryFallbackHardening class with operational pins
the coder's tests don't make explicit:

- test_degraded_flag_sticky_across_redis_recovery: pins that a
  subsequent successful Redis selection MUST NOT clear the degraded
  flag — the operator-facing /api/v1/health signal stays set until
  restart / test-reset so the brief durability window isn't hidden.
- test_warn_and_error_once_flags_are_independent (both orderings):
  the WARNING (explicit memory) and ERROR (auto fallback) once-flags
  are distinct; a process exercising both paths emits exactly one of
  each regardless of which fired first, and explicit memory after an
  auto-fallback does NOT clear the degraded flag.
- test_concurrent_auto_fallback_emits_marker_at_most_once: four-thread
  barrier race on _create_message_store() asserts at-most-one marker
  log and a monotonic-True degraded flag — would catch a future
  refactor that drops the check-then-set atomicity.

Also keeps the tester's TestRedisRestartSemanticsVsPhaseBoundaryWipe
class from the prior commit (5 tests pinning that a re-instantiated
RedisMessageStore against the same fakeredis preserves transcript /
counters / since_id resolution, while _clear_concurrent_state still
wipes at the phase boundary — the two semantics asserted side-by-side
so the designed wipe cannot be misread as accidental mid-phase loss).

123 tests pass under `.venv/bin/pytest orchestrator/tests/test_message_store.py orchestrator/tests/test_redis_message_store.py`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ruff format wanted to collapse a 3-line def into one line
(test_health_endpoint_message_store_surface_does_not_call_messagestore).
The coder's slice-6 commit added it as multiline, but the project's
formatter prefers the single-line form for this width. No test logic
changes; this satisfies `make lint` so the tester producer's re-propose
passes the lint pre-check.
@james-in-a-box

This comment has been minimized.

Slice-6 added a once-per-process message_store._memory_fallback_degraded
global that /api/v1/health surfaces and uses to degrade top-level status.
In CI (no Redis), auto backend selection trips it, polluting sibling
health tests that assert 'healthy'. Add autouse fixtures resetting the
flag, mirroring test_health_routes.py.
@james-in-a-box

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

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

… conflict in .egg-state/contracts/issue-3077.json

Took the base branch's progressed pipeline state (slices/tasks marked
complete with commit SHAs and PR numbers, additive cq-3 decision and
audit-log entries). HEAD only carried older pending/null placeholders.
@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Conflict Resolution Summary

Resolved merge conflicts with egg/issue-3077/work (merge, not rebase — PR history preserved).

File Category Resolution
.egg-state/contracts/issue-3077.json Additive / state-progression Took the base branch's authoritative pipeline state

Details — the only conflicting file was the generated SDLC contract state file. All 8 conflict hunks were the base branch (work) carrying more-progressed pipeline state than the PR branch's older snapshot:

  • slice-5 / slice-6 and their tasks: base had complete/in_progress statuses, linked commit SHAs, parent_branch_at_creation, integration_base_sha, and pr_number (slice-6 → PR [issue-3077][slice-6/6] Bounded durability: fail-loud... #3144); HEAD only had pending/null placeholders.
  • Additive decisions[] entry cq-3 and additional audit_log[] entries present only on base.

A git diff HEAD..origin/egg/issue-3077/work confirmed the base version is a strict superset — HEAD contributed no unique contract content (only the stale pending/null values base replaces). Resolved by taking the base version; result validates as JSON.

No application code conflicted — slice-6's actual code (orchestrator/message_store.py, test_message_store.py, test_redis_message_store.py) merged cleanly. The remaining changed files (docs/, sandbox/, shared/egg_agent/client.py, shared/egg_overseer/advisor.py, tests) were clean auto-merges from the base branch.

Verification: make lint passes (exit 0; only pre-existing soft-cap warnings). Per workflow policy the full make test suite was not run locally (10–15 min); CI will run it on the pushed result.

Please review: Nothing security- or logic-sensitive — the resolution is confined to generated pipeline bookkeeping state, taking the integration branch's up-to-date values.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract Verification — PR #3144 (issue-3077 slice-6/6)

Verdict: Approve. Both contract tasks are fully implemented and all acceptance criteria are objectively met. I ran the new test classes directly (18 tests, all green) and traced each criterion to its implementing code.

Note: the issue-3077 contract carries acceptance criteria as per-task free text (no top-level ac-N ids), and the orchestrator CLI was unreachable this session, so there are no verify-criterion --criterion ac-N markers to set. Verification is recorded here instead.

task-6-1 — Fail-loud memory-backend signal (message_store.py) ✅

Criterion Evidence Status
auto→memory: exactly one error log w/ stable marker + health degraded field _create_message_store() except-branch sets _memory_fallback_degraded=True and emits logger.error("%s: …", MEMORY_FALLBACK_MARKER, extra={"marker":…,"error":…}) guarded by _memory_fallback_logged (message_store.py:714-741); health.py:148-165 reads is_memory_fallback_degraded() into components.message_store and degrades top-level status Verified
Explicit memory: warning level, no flag message_store.py:689-700logger.warning(... explicit EGG_MESSAGE_STORE_BACKEND=memory ...) once-per-process, returns MessageStore(), flag untouched Verified
Redis backend: neither success path logs info only; use_redis=="redis" failure re-raises (fail-hard) — no flag, no warn/error Verified
auto selection unchanged; tests not spammed Selection precedence identical to #1897 design; _memory_fallback_logged/_memory_explicit_logged are once-per-process and reset_message_store() deliberately does NOT clear them; _reset_memory_fallback_state_for_test() re-arms for tests Verified

Health-surface isolation invariant (#1897 TASK-4-3) is preserved: health.py imports the message_store module and reads the pure getter is_memory_fallback_degraded() — no MessageStore method is touched on the request path. Locked by test_health_endpoint_message_store_surface_does_not_call_messagestore.

task-6-2 — Durability tests ✅

Criterion Evidence Status
Redis mid-phase restart preserves transcript TestRedisRestartSemanticsVsPhaseBoundaryWipe::test_mid_phase_restart_preserves_transcript_via_shared_redis (+ counter/since_id variants) — new RedisMessageStore against same Redis observes all prior messages by id Verified
_clear_concurrent_state() still wipes at phase boundary test_phase_boundary_clear_concurrent_state_still_wipes invokes routes.phases._clear_concurrent_state (confirmed at phases.py:113) and asserts the stream drains Verified
Fail-loud matrix asserted (error/warning/silent × flag) TestMemoryFallbackFailLoudSignal covers all four rows; TestMemoryFallbackHardening adds sticky-flag, independent-once-flags, and concurrency pins Verified
Both wipe semantics named explicitly test_mid_phase_restart_* vs test_phase_boundary_clear_* ids + class docstring distinguish accidental loss from designed wipe Verified

Test run (targeted; full suite intentionally not run)

tests/test_message_store.py::TestMemoryFallbackFailLoudSignal ...... [6 passed]
tests/test_message_store.py::TestMemoryFallbackHardening ....        [4 passed]
tests/test_health_routes.py::TestMessageStoreFailLoudSurface ...     [3 passed]
tests/test_redis_message_store.py::TestRedisRestartSemanticsVsPhaseBoundaryWipe ..... [5 passed]
= 18 passed =

Non-blocking observations

  1. task-6-1.files_affected is under-inclusive. It lists only orchestrator/message_store.py, but the PR also edits orchestrator/routes/health.py (required to satisfy the "visible on the health surface" criterion) and adds autouse reset fixtures to test_health_check_integration.py, test_health_check_lifecycle_integration.py, and test_state_store_wedge_propagation.py. These are all legitimate and in-scope — the health wiring is mandatory for the AC, and the sibling-test fixtures prevent the new process-global from flaking order-dependent tests in CI (where Redis is absent and auto falls back). Not orphaned code; worth noting only for contract bookkeeping.
  2. Concurrency test asserts <= 1 rather than == 1. test_concurrent_auto_fallback_emits_marker_at_most_once honestly documents that the once-flag uses unsynchronized module globals (no explicit lock), so a check-then-set race could in principle emit zero under reset interplay. This is a conservative, correctly-reasoned pin for the bounded-durability scope, not a defect — the monotonic degraded-flag half is asserted unconditionally.
  3. The two .egg-state/brc-history/3077-implement-slice-6.{json,md} files (~17.5k of the 18.6k added lines) are BRC consensus artifacts, not code — expected for this pipeline.

No contract violations, no correctness issues, no missing tests. Selection semantics and the #1897 isolation invariant are preserved exactly as the HITL-Q3 constraint requires.

— 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.

Review: slice-6 fail-loud auto→memory message-store fallback

I traced the full data flow (production trigger → flag → health surface) and reviewed every changed code file against the production code paths. No blocking issues. The feature works end-to-end and the tests exercise real production code, not hand-built fixtures or self-seeding goldens. Details and three non-blocking notes below.

End-to-end functionality: verified

The core question for this slice is whether the auto→memory fallback actually trips the health surface in the real runtime. It does:

  • redis_message_store.get_redis_message_store() eagerly client.ping()s and raises ConnectionError on a down Redis (redis_message_store.py:589-592), so _create_message_store()'s auto branch genuinely reaches the except and sets _memory_fallback_degraded = True — this is not a lazy connection that would defer the failure past store creation.
  • routes/health.py:137 reads the flag off the imported module (is_memory_fallback_degraded()), a pure global read — the issue #1897 TASK-4-3 isolation invariant (no MessageStore.* calls on the request path) is preserved, and test_health_endpoint_message_store_surface_does_not_call_messagestore locks it.
  • health.py and the dominant store-creation path both import the flat message_store module, so the flag is set and read through the same module object. /api/v1/health still returns HTTP 200; only the JSON status field flips.

Tests are well-constructed: TestRedisRestartSemanticsVsPhaseBoundaryWipe drives real RedisMessageStore.add_message/get_messages/get_status/clear over a shared fakeredis instance to model "same Redis, new store object," and correctly pins the intentional phase-boundary wipe (_clear_concurrent_state) separately from the accidental-loss failure class. The fail-loud matrix (error/warning/silent × flag) maps cleanly to the acceptance criteria.

Non-blocking notes

1. Blast radius of the top-level status flip. healthy = state_store_healthy and not message_store_degraded now drives both the top-level status: "degraded" and _health_tracker.record(healthy). Because the degraded flag is sticky by design (only restart/test-reset clears it), any deployment running on the memory fallback reports status: "degraded" permanently and the tracker records a permanent unhealthy transition (healthy_since → null for the process lifetime). This is the intended HITL-Q3 behavior, and HTTP stays 200 so LB probes keying on status code are unaffected — but please confirm mcp__egg__check_health, dashboards, and any alerting that parses the status field treat "degraded" as informational rather than a paging/hard-down condition. /ready is correctly left untouched (state-store-only), so the pod isn't pulled from rotation.

2. Dual-import hazard for the module-global signal (pre-existing). Almost the entire codebase imports from message_store import ... (flat), but routes/pipelines.py:17394 imports from orchestrator.message_store import .... Since orchestrator/ is a package, those resolve to two distinct module objects with independent globals — including the new _memory_fallback_degraded flag and the _message_store singleton. In practice the fallback is first tripped via a flat-import path at startup (e.g. concurrent_executor.py:39 imports get_message_store at module load), so health (flat) observes it correctly and this is not a silent no-op for this feature. But the new signal now adds reliance on cross-module module-global state, which makes that lone orchestrator.message_store import a latent reliability/split-brain seam worth normalizing to the flat form (separately, it implies a second message-store singleton on that path).

3. Once-flag check-then-set is unsynchronized — but safe in production. _create_message_store() mutates _memory_fallback_logged / _memory_explicit_logged without a lock. Production only reaches it via get_message_store() under _store_lock (double-checked locking), so it's effectively serialized; the direct _create_message_store() calls in the concurrency test bypass that lock, which is why the test conservatively asserts <= 1. The degraded-flag set is monotonic, so the worst case is a duplicate log line, never a missed degradation. Fine as-is; a one-line note that _store_lock is the real guard would help future readers.

Scope confirmation

  • .egg-state/brc-history/* artifacts skipped per review conventions.
  • Selection precedence unchanged (HITL Q3 honored): explicit redis still fails hard (test_explicit_redis_raises_on_failure_unchanged); explicit memory warns without the flag; auto fallback errors + flags.
  • Flakiness mitigation is complete: the three test classes that hit the real /api/v1/health and assert top-level healthy (test_health_check_integration.py, test_health_check_lifecycle_integration.py, test_state_store_wedge_propagation.py) all received the autouse reset fixture; the other health-asserting tests (test_cli.py, test_gateway_client.py, test_state_store_probe.py) use mocks or probe snapshot() directly and are unaffected.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

…guard

- Normalize the lone orchestrator.message_store package import in
  routes/pipelines.py to the flat 'from message_store import ...' form
  used everywhere else, so the slice-6 module-global fallback signal
  (and the singleton) live on a single module object (egg-reviewer note 2).
- Add a comment in _create_message_store clarifying that _store_lock is
  the real serialization guard for the unsynchronized once-flags, so the
  worst case for a direct caller is a duplicate log, never a missed
  degradation (egg-reviewer note 3).
@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Review feedback addressed

Both reviews approved (contract-verification + slice-6 review). Per-item disposition of the non-blocking notes below.

egg-reviewer[bot]

  1. Blast radius of the top-level status flipdisagree (intended HITL-Q3 behavior, no code change warranted). The permanent status: "degraded" while running on the memory fallback is the designed sticky signal (#3076 loss risk operators didn't opt into). HTTP stays 200 and /ready is state-store-only, so LB/probe rotation is unaffected — only the JSON status field flips, which is exactly the informational signal this slice exists to surface. There's no code change to make here; consumers treating degraded as a page rather than an alert would be a config decision on their side, not a defect in this PR.

  2. Dual-import hazard for the module-global signalfixed-in-PR (commit 581ec33). Normalized the lone from orchestrator.message_store import ... at routes/pipelines.py:17394 to the flat from message_store import ... form used everywhere else in the file, so the new _memory_fallback_degraded flag (and the _message_store singleton) resolve through a single module object rather than risking a split-brain second module/singleton on that path.

  3. Once-flag check-then-set is unsynchronizedfixed-in-PR (commit 581ec33). Added a comment in _create_message_store() recording that _store_lock (via get_message_store()'s double-checked locking) is the real serialization guard, and that the monotonic degraded-flag set means the worst case for a direct unsynchronized caller is a duplicate log line, never a missed degradation.

james-in-a-box[bot] (contract verification)

  1. task-6-1.files_affected is under-inclusivedisagree (contract bookkeeping observation, not a code defect). As the note itself states, the health wiring and sibling-test reset fixtures are legitimate and in-scope — not orphaned code. The affected file is the generated .egg-state/contracts/issue-3077.json pipeline-state artifact, not source; no code change is warranted.

  2. Concurrency test asserts <= 1 rather than == 1disagree (correct as-is, reviewer concurred). The <= 1 bound honestly documents the unsynchronized once-flag under the direct-call test path; the monotonic degraded-flag half is asserted unconditionally. The new _store_lock comment (item 3) makes the production serialization guarantee explicit for future readers.

  3. BRC consensus artifacts in the diffdisagree (expected pipeline artifacts, not code). The .egg-state/brc-history/3077-implement-slice-6.{json,md} files are consensus bookkeeping for this pipeline, as the note acknowledges.

make lint passes on the two changed files. (Pre-existing mypy errors in shared/egg_agent/client.py arrived via the base-branch merge, are unrelated to this feedback, and are present on HEAD independent of these changes.)

— 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: slice-6 fail-loud auto→memory fallback (delta 725b51e581ec33)

I re-fetched the full history and reviewed the PR-authored delta against the merge base. The delta is exactly two changes, both addressing my prior non-blocking notes. No blocking issues. CI is fully green (Unit + Integration Tests, Python, Security Scan all SUCCESS).

Note 2 (dual-import / split-brain singleton) — fixed correctly

routes/pipelines.py:17394 now imports the flat from message_store import Message, get_message_store instead of the package form from orchestrator.message_store import …. I verified this is the right fix, not a cosmetic one:

  • The flat form genuinely resolves in this runtime — pipelines.py's own top-level imports are flat (from lifecycle_auth import … at :313, from routes import … at :1153), so if flat resolution were broken the module wouldn't import at all. health.py:41 (import message_store as _message_store_module) and concurrent_executor.py:39 also use the flat form.
  • This unifies the singleton: the OVERSEER_ALERT emitted on the cascade path now lands in the same module-global _message_store buffer that the messages route reads, and shares the same _memory_fallback_degraded flag that health.py:137 reads. The old package-form import would have created a second module object with an independent store and flag — the exact split-brain seam I flagged.
  • Grep confirms zero remaining from orchestrator.message_store import statements; the only remaining "orchestrator.message_store" strings are logger-name references in tests, which are correct.

Note 3 (unsynchronized once-flag) — documented correctly

The new comment in _create_message_store() (message_store.py:686-692) accurately reflects the code: get_message_store() (:656-667) serializes creation via _store_lock double-checked locking, and the degraded-flag set (:730) is monotonic within a process (only the test-reset helper at :649-651 clears it). Worst case for a direct unsynchronized caller is a duplicate log line, never a missed degradation. Accurate.

Non-blocking

  1. Import-form consistency. Every other message_store import site in pipelines.py uses the dual-form try: from message_store … except ImportError: from ..message_store … (e.g. :1985-1987, :4386-4389, :15392-15394). The changed line at :17394 uses the bare flat form with no ..message_store fallback. Because it's wrapped in except Exception: # noqa: BLE001 … pass, an import failure degrades gracefully to the always-on log line (no crash), and the prior code had no fallback either — so this is not a regression. But adopting the file's established dual-form for consistency would make the cascade-alert path robust under the package-import contexts (e.g. some unit tests) where only the relative form resolves. Optional.

Prior Note 1 (sticky status: "degraded" blast radius) was reasonably defended as intended HITL-Q3 behavior, with HTTP staying 200 and /ready untouched — no code change warranted there.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract re-verification — slice-6 (#3077), delta since 725b51e

Re-reviewed the incremental delta on top of my prior verification at 725b51e. The PR branch was rebased (shallow clone; 725b51e is no longer locally reachable), but the GitHub commit timeline confirms the delta is exactly one commit581ec33 "Address review: normalize message_store import, document _store_lock guard" — touching two files:

File Change Review note
orchestrator/message_store.py +7 lines, pure comment in _create_message_store note 3
orchestrator/routes/pipelines.py 1 line, import normalization note 2

Verification of the delta

1. pipelines.py import normalization (note 2) — correct, and a genuine correctness fix, not cosmetic.
from orchestrator.message_store import …from message_store import …. Python binds message_store and orchestrator.message_store as distinct module objects with distinct module-level globals. Before this change, the _probe_parent_branch_exists call site at pipelines.py:17391 got a separate get_message_store() singleton and a separate copy of the slice-6 _memory_fallback_degraded flag from the rest of the orchestrator. After normalization it shares the one flat module object that health.py:41 (import message_store as _message_store_module) and the other 69 from message_store import … sites already use. Verified:

  • Zero from orchestrator.message_store import … occurrences remain; 69 flat imports — fully consistent.
  • Imported symbols exist: Message (message_store.py:135), get_message_store (:656).
  • The health surface reads _message_store_module.is_memory_fallback_degraded() (health.py:137) off the same module object the degraded flag is now set on everywhere.

2. message_store.py comment (note 3) — factually accurate.
The added comment claims _store_lock is the real serialization guard and the once-flags' worst case is a duplicate log, never a missed degradation. Confirmed against the code: production reaches _create_message_store only via get_message_store()'s double-checked lock (:663-666), and _memory_fallback_degraded = True (:730) is set unconditionally before the if not _memory_fallback_logged guard, so the degraded set is monotonic and a racing direct caller can at most duplicate the log line. Accurate.

Regression check — slice-6 criteria still hold

The delta does not alter the fail-loud signal, the warning/error/silent matrix, the MEMORY_FALLBACK_MARKER, the health degraded field, or the test matrix. The import fix only makes the singleton + degraded flag coherent across the orchestrator, so it strengthens — rather than threatens — the previously verified slice-6 acceptance criteria. Both slice-6 tasks (task-6-1 coder, task-6-2 tester) remain complete.

Note on criterion marking

The orchestrator was unreachable this session, so egg-contract show / verify-criterion could not be run (the on-disk contract snapshot is a stale refine-phase copy with empty top-level criteria). The criteria verified in my prior review at 725b51e are unaffected by this delta; this re-review confirms no regression. The marker's machine-readable verdict below reflects the human-facing outcome.

Verdict

Approve. The delta is two narrow, correct review-feedback changes — one a no-op comment, one a correctness-strengthening import normalization. No contract violations; no regression to slice-6 criteria.

— 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

Copy link
Copy Markdown
Contributor Author

Review feedback addressed (re-review delta 581ec3382565b6)

Both re-reviews approved. The one actionable note from the latest egg-reviewer[bot] pass is addressed in-PR; the previously-defended note is unchanged. Per-item disposition below.

egg-reviewer[bot] re-review (581ec33)

  1. Import-form consistency on the cascade-alert pathfixed-in-PR (commit 82565b6). The lone bare-flat from message_store import Message, get_message_store at routes/pipelines.py:17394 now uses the file's established dual-form (try: from message_store … except ImportError: from ..message_store … # type: ignore[no-redef]), matching the other sites (e.g. :1985-1987, :4386-4389, :15392-15394). This makes the OVERSEER_ALERT cascade-emission path resolve under package-import contexts (e.g. unit tests where only the relative form resolves) instead of silently falling through the except Exception: pass to the always-on log line. Mirrors the existing convention exactly, so no behavior change on the flat-import production path. ruff check passes on the changed file.

  2. Sticky status: "degraded" blast radius (prior Note 1, re-raised as resolved) — disagree (intended HITL-Q3 behavior, no code change warranted). As acknowledged in the re-review, this was reasonably defended: HTTP stays 200, /ready is state-store-only so pod rotation is unaffected, and the permanent degraded JSON status while on the memory fallback is the designed sticky signal this slice exists to surface. No code change warranted.

james-in-a-box[bot] contract re-verification (581ec33)

  1. Contract re-verification — no actionable items. Verdict was Approve; the delta was confirmed to strengthen (not regress) the slice-6 acceptance criteria, with both tasks remaining complete.

ruff check passes on the changed file. Per workflow policy the full make test suite was not run locally; CI runs the configured checks on the pushed HEAD.

— 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: slice-6 cascade-alert import (delta 581ec3382565b6)

I deepened the shallow clone and reviewed the PR-authored delta against my prior review commit. The delta is exactly one change — the dual-form message_store import on the cascade-alert path that I flagged as non-blocking Note 1 in my last pass. No blocking issues. CI is fully green (Unit Tests, Integration Tests, Python, Security Scan, Lint all SUCCESS).

Note 1 (import-form consistency) — fixed correctly

routes/pipelines.py:17392-17398 now wraps the cascade-alert message_store import in the file's established dual-form:

try:
    from message_store import Message, get_message_store
except ImportError:
    from ..message_store import (  # type: ignore[no-redef]
        Message,
        get_message_store,
    )

I verified this is correct and not a regression:

  • Matches the file convention exactly. Identical in structure to the other multi-symbol site at :15392-15394 (Message, MessageType) and the single-symbol sites at :1985-1987 and :4386-4389, including the # type: ignore[no-redef] annotation.
  • Production singleton coherence is preserved. The flat form is tried first, so in the production runtime (where pipelines.py's own top-level imports are flat and message_store resolves on sys.path) the cascade alert still lands in the same module-global _message_store buffer and shares the same _memory_fallback_degraded flag that health.py:41 and the messages route read. The relative ..message_store fallback only engages under package-import contexts (e.g. unit tests) where the flat form is unavailable — and there it is the only resolvable module object, so it's internally coherent. The split-brain seam from the original package-only form is not reintroduced.
  • Failure path unchanged. Still wrapped in the best-effort except Exception: # noqa: BLE001 … pass, with the always-on logger.warning("Slice cascade fired", …) above it as the durable fallback. The only behavioral change is that the alert now successfully emits under package-import contexts instead of silently falling through to the log line.

Grep confirms zero from orchestrator.message_store import statements remain in orchestrator/, so the singleton stays unified across all call sites.

Verdict

Approve. The delta is a single narrow, correct change that adopts the file's established import convention and addresses my prior non-blocking note. No new issues; no regression to the slice-6 fail-loud semantics, the degraded health field, or the test matrix.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract re-verification — incremental review

Re-reviewed the delta since my last verification at 581ec33. The PR head is now 82565b6, and the only PR-authored change since then is a single commit:

  • 82565b6Address review: adopt dual-form message_store import on cascade-alert path (orchestrator/routes/pipelines.py, +7/-1)

What changed

In _run_implement_phase_slices, the cascade-block OVERSEER_ALERT emission path previously used a bare top-level import:

from message_store import Message, get_message_store

It now uses the dual-form pattern, falling back to a package-relative import:

try:
    from message_store import Message, get_message_store
except ImportError:
    from ..message_store import (  # type: ignore[no-redef]
        Message,
        get_message_store,
    )

Verification

  • Pattern consistency — This matches the established dual-form message_store import used throughout the same file (e.g. lines 1985–1987, 4386–4389, the _get_message_store() helper at 8726–8729, and 15392–15398 / 15478–15484). It removes the last odd-one-out bare import on this code path.
  • Import targets existorchestrator/message_store.py exports both Message (class, L135) and get_message_store (function, L656), so the ..message_store fallback resolves correctly whether the module is loaded as a top-level module (message_store on sys.path) or as a package submodule (orchestrator.routes.pipelines).
  • Defensive — The new inner try/except ImportError is nested inside the existing best-effort try: … except Exception: pass block, so even a total import failure degrades to the always-on logger.warning log line rather than disrupting the slice loop. Sync/emission semantics are otherwise unchanged.
  • Compilespython -m py_compile orchestrator/routes/pipelines.py passes.

Regression check

The change is scoped to a single import on a best-effort alert path and touches no other logic. No previously verified acceptance criterion is affected, and no contract task's described functionality is altered — this commit hardens existing code in response to a prior review comment rather than completing new work.

Note on criterion marking

The orchestrator is currently unreachable from this session (egg-orch healthOrchestrator: UNREACHABLE; gateway healthy), so egg-contract verify-criterion could not be invoked this cycle. This is not a blocker for the delta: the change introduces no new acceptance-criterion completion to mark — it is a hardening of existing best-effort emission code. The slice-6 criteria stand as previously assessed; nothing in this delta regresses them.

Verdict: No blocking issues. The delta is correct, minimal, and consistent with codebase conventions.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

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

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Review feedback addressed — current HEAD 82565b6

Both most-recent reviews are against the current PR HEAD 82565b6 and are clean approvals with no new actionable feedback:

  • egg-reviewer[bot] — APPROVED (82565b6): "The delta is a single narrow, correct change that adopts the file's established import convention... No new issues; no regression to the slice-6 fail-loud semantics, the degraded health field, or the test matrix."
  • james-in-a-box[bot] — contract re-verification, Approve (82565b6): "No blocking issues. The delta is correct, minimal, and consistent with codebase conventions."

No code changes are required this round. For completeness, here is the disposition of every item raised across the full review history — all already resolved in prior commits on this PR:

egg-reviewer[bot]

  1. Sticky status: "degraded" blast radiusdisagree (intended HITL-Q3 behavior, no code change warranted). HTTP stays 200, /ready is state-store-only so pod rotation is unaffected; the permanent degraded JSON status while on the memory fallback is the designed sticky signal this slice exists to surface. The reviewer concurred this was reasonably defended.
  2. Dual-import / split-brain singleton hazardfixed-in-PR (commit 581ec33). Normalized the lone from orchestrator.message_store import ... at routes/pipelines.py to the flat from message_store import ... form so the _memory_fallback_degraded flag and _message_store singleton resolve through a single module object.
  3. Unsynchronized once-flag check-then-setfixed-in-PR (commit 581ec33). Added a comment in _create_message_store() recording that _store_lock (via get_message_store()'s double-checked locking) is the real serialization guard, and that the monotonic degraded-flag set means the worst case for a direct caller is a duplicate log line, never a missed degradation.
  4. Import-form consistency on the cascade-alert pathfixed-in-PR (commit 82565b6). Adopted the file's established dual-form import (try: from message_store ... except ImportError: from ..message_store ... # type: ignore[no-redef]) so the OVERSEER_ALERT cascade-emission path resolves under package-import contexts.

james-in-a-box[bot] (contract verification)

  1. task-6-1.files_affected under-inclusivedisagree (contract bookkeeping observation, not a code defect). As the note states, the health wiring and sibling-test reset fixtures are legitimate and in-scope, not orphaned code; the affected file is the generated .egg-state/contracts/issue-3077.json artifact, not source.
  2. Concurrency test asserts <= 1 rather than == 1disagree (correct as-is, reviewer concurred). The <= 1 bound honestly documents the unsynchronized once-flag under the direct-call test path; the monotonic degraded-flag half is asserted unconditionally. Item 3's _store_lock comment makes the production serialization guarantee explicit.
  3. BRC consensus artifacts in the diffdisagree (expected pipeline artifacts, not code). The .egg-state/brc-history/3077-implement-slice-6.{json,md} files are consensus bookkeeping for this pipeline, as the note acknowledges.

Working tree is clean at 82565b6; CI lint passed on this HEAD per the latest review.

— Authored by egg

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

egg feedback addressed. View run logs

14 previous review(s) hidden.

@jwbron
jwbron changed the base branch from egg/issue-3077/work to main June 12, 2026 05:22
@jwbron
jwbron merged commit 3febdc0 into main Jun 12, 2026
29 checks passed
jwbron added a commit that referenced this pull request Jun 13, 2026
… [doc-updater] (#3150)

* docs: update coordination-state and deployment for #3077 slice-6 [doc-updater]

Update documentation to reflect changes from #3144 (#3077 slice-6):
- coordination-state.md: mark all six #3077 slice mechanisms as Shipped
  (slices 2-6 were still listed as Pending; all have now merged to main)
- deployment.md: add `components.message_store` to the /api/v1/health
  response example and document the degraded case (MESSAGE_STORE_AUTO_FALLBACK_TO_MEMORY
  marker, what triggers it, and how to fix it)

Triggered by: #3144

Authored-by: egg

* docs: remove stale message_store health field after #3159

#3159 removed the in-memory message store backend, so the
components.message_store health field and the auto->memory fallback
no longer exist. Drop the JSON example line and the describing
paragraph in deployment.md so it agrees with coordination-state.md
and the current health.py response.

---------

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