fix(#832): retain revoke fence on rejected cycles
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
ms-lead-reviewer
2026-07-19 17:40:09 -05:00
parent b6e1461952
commit e196abfd71
2 changed files with 22 additions and 3 deletions

View File

@@ -10,4 +10,4 @@
3. Implement the broker-minted receipt challenge and exact receipt observation/consume/promote path. 3. Implement the broker-minted receipt challenge and exact receipt observation/consume/promote path.
4. Run unit, framework-shell, compile, lint, and type checks; push after the required queue guard; report to `mosaic-100`. 4. Run unit, framework-shell, compile, lint, and type checks; push after the required queue guard; report to `mosaic-100`.
- **Risks:** The standalone harness must drive the real daemon without a divergent fixture. If that is impossible, stop and flag Mos. - **Risks:** The standalone harness must drive the real daemon without a divergent fixture. If that is impossible, stop and flag Mos.
- **Evidence:** Initial RED recorded in `/home/hermes/agent-work/reviews/832-wi5-red-receipt-challenge.log`; initial green checks passed. Remediation RED recorded in `/home/hermes/agent-work/reviews/832-wi5-remediation-red.log` before observer/payload implementation: forged `h_source`/`h_payload` was accepted and the observer module was absent. Remediation GREEN: observer/payload/T26/T29 unittest (4 tests), normative-fragments unittest (5), state-store regression (10), full mutator-gate acceptance (20, including the real begin → observer → consume → promote path), `py_compile`, Mosaic package lint/typecheck, and targeted Prettier check. The P5 harness was updated for the test-observer seam but was not fired. Coverage tooling remains unavailable (`python3 -m coverage`: module not installed). Push pending. - **Evidence:** Initial RED recorded in `/home/hermes/agent-work/reviews/832-wi5-red-receipt-challenge.log`; initial green checks passed. Remediation RED recorded in `/home/hermes/agent-work/reviews/832-wi5-remediation-red.log` before observer/payload implementation; remediation green passed. Remediation-2 RED recorded in `/home/hermes/agent-work/reviews/832-wi5-remediation2-red.log`: each rejected begin restored prior VERIFIED authority. Remediation-2 GREEN: receipt unittest (5: all `INVALID_CONSTRUCTION`, `PAYLOAD_CONSTRUCTION_REFUSED`, and `PAYLOAD_BINDING_MISMATCH` cases preserve UNVERIFIED and deny the next mutator), normative-fragments unittest (5), state-store regression (10), full mutator-gate acceptance (20, including the real begin → observer → consume → promote path), `py_compile`, Mosaic package lint/typecheck, and targeted Prettier check. The P5 harness remains unfired. Coverage tooling remains unavailable (`python3 -m coverage`: module not installed). Push pending.

View File

@@ -303,6 +303,10 @@ class Broker:
def __init__(self, store: StateStore, observer: ReceiptObserver | None = None) -> None: def __init__(self, store: StateStore, observer: ReceiptObserver | None = None) -> None:
self.store = store self.store = store
self.observer: ReceiptObserver = observer if observer is not None else UnavailableReceiptObserver() self.observer: ReceiptObserver = observer if observer is not None else UnavailableReceiptObserver()
# Set only by begin_verification after its mandatory revoke-first fence.
# It is preserved if later cycle admission is refused; all other broker
# actions retain the normal snapshot rollback behavior.
self._rejected_cycle_fence: tuple[dict[str, object], dict[str, dict[str, object]]] | None = None
# VERIFIED authority is deliberately volatile: broker restart revokes all # VERIFIED authority is deliberately volatile: broker restart revokes all
# leases while preserving WI-1 identity and pending-token integrity. # leases while preserving WI-1 identity and pending-token integrity.
self.leases: dict[str, dict[str, object]] = {} self.leases: dict[str, dict[str, object]] = {}
@@ -391,6 +395,7 @@ class Broker:
raise StateCommitUncertain() raise StateCommitUncertain()
previous = copy.deepcopy(self.store.value) previous = copy.deepcopy(self.store.value)
previous_leases = copy.deepcopy(self.leases) previous_leases = copy.deepcopy(self.leases)
self._rejected_cycle_fence = None
try: try:
response = self._handle(peer, request) response = self._handle(peer, request)
if self.store.value != previous: if self.store.value != previous:
@@ -405,9 +410,18 @@ class Broker:
except StateCommitUncertain: except StateCommitUncertain:
raise raise
except Exception: except Exception:
fence = self._rejected_cycle_fence
if fence is None:
self.store.value = previous self.store.value = previous
self.leases = previous_leases self.leases = previous_leases
else:
# A refused re-verification must never resurrect the preceding
# VERIFIED authority. Preserve only this post-revoke fence;
# every unrelated partial-write failure still rolls back.
self.store.value, self.leases = fence
raise raise
finally:
self._rejected_cycle_fence = None
def _handle(self, peer: tuple[int, int, int], request: dict[str, object]) -> dict[str, object]: def _handle(self, peer: tuple[int, int, int], request: dict[str, object]) -> dict[str, object]:
peer_pid, peer_uid, peer_gid = peer peer_pid, peer_uid, peer_gid = peer
@@ -473,6 +487,11 @@ class Broker:
raise BrokerFailure("INVALID_LEASE_TTL") raise BrokerFailure("INVALID_LEASE_TTL")
# Revoke-first is a broker operation, not advisory adapter order. # Revoke-first is a broker operation, not advisory adapter order.
self.revoke_session_authority(session_id) self.revoke_session_authority(session_id)
# If subsequent construction admission rejects, handle() restores
# this fence rather than the pre-cycle VERIFIED snapshot.
self._rejected_cycle_fence = (
copy.deepcopy(self.store.value), copy.deepcopy(self.leases)
)
try: try:
constructed = build_payload_from_wire(construction) constructed = build_payload_from_wire(construction)
except ValueError as exc: except ValueError as exc: