mirror of
https://github.com/mikeyobrien/ralph-orchestrator.git
synced 2026-04-24 22:55:57 +03:00
[PR #195] feat(core): add hat scope enforcement, event chain validation, and human timeout routing #192
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/ralph-orchestrator#192
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/mikeyobrien/ralph-orchestrator/pull/195
Author: @arjhun-personal
Created: 2/25/2026
Status: 🔄 Open
Base:
main← Head:hat-enforcement📝 Commits (1)
159821dfeat(core): add hat scope enforcement, event chain validation, and human timeout routing📊 Changes
9 files changed (+748 additions, -4 deletions)
View changed files
📝
crates/ralph-bench/src/main.rs(+1 -0)📝
crates/ralph-cli/src/display.rs(+1 -0)📝
crates/ralph-cli/src/loop_runner.rs(+27 -0)📝
crates/ralph-core/src/config.rs(+18 -0)📝
crates/ralph-core/src/event_loop/loop_state.rs(+21 -0)📝
crates/ralph-core/src/event_loop/mod.rs(+123 -4)📝
crates/ralph-core/src/event_loop/tests.rs(+477 -0)📝
crates/ralph-core/src/hat_registry.rs(+77 -0)📝
crates/ralph-core/src/summary_writer.rs(+3 -0)📄 Description
Closes #193
Summary
Three layers of defense-in-depth to prevent agents from bypassing hat workflow constraints. Addresses a failure mode where an agent skipped human approval, never created tasks, and implemented all phases in a single context window by emitting events outside its hat's declared
publisheslist.Hat scope enforcement:
HatRegistry::can_publish()gates events inprocess_events_from_jsonl()against the active hat's declaredpublishespatterns. Out-of-scope events are dropped and replaced with{hat_id}.scope_violationdiagnostic events. Ralph in coordination mode (no active hat) retains unrestricted publishing.Event chain validation +
loop.cancel: Newrequired_eventsconfig field andseen_topicsstate tracking.check_completion_event()becomes a hard gate —LOOP_COMPLETEis rejected unless all required events have been seen during the loop's lifetime. A separateloop.cancelevent provides clean early termination (human rejection, timeout) without chain validation. On rejection, atask.resumeevent is injected with the missing events listed.Human timeout routing:
wait_for_response()timeout now publishes ahuman.timeoutevent instead of silently continuing. This makes timeouts visible in the event log and routable to hats that declarehuman.timeoutas a trigger.Changes
crates/ralph-core/src/hat_registry.rscan_publish()method + 4 unit testscrates/ralph-core/src/config.rsrequired_events,cancellation_promise,enforce_hat_scopefields onEventLoopConfigcrates/ralph-core/src/event_loop/loop_state.rsseen_topics,cancellation_requestedfields + helper methodscrates/ralph-core/src/event_loop/mod.rsloop.canceldetection, topic recording, chain validation gate,check_cancellation_event(),Cancelledvariant,human.timeoutinjectioncrates/ralph-core/src/event_loop/tests.rscrates/ralph-cli/src/loop_runner.rscheck_cancellation_event()before completion checkcrates/ralph-cli/src/display.rsCancelleddisplay variantcrates/ralph-core/src/summary_writer.rsCancelledstatus text + test fixture fieldcrates/ralph-bench/src/main.rsCancelledformat variantTotal: +748/-4 across 9 files
Design decisions
Scope enforcement runs before validation — out-of-scope events are partitioned out before the existing
build.done/review.done/verify.passedbackpressure checks.loop.cancelis separate fromLOOP_COMPLETE— cancellation means "abort gracefully" and bypasses chain validation. Completion means "all work finished" and is gated.Cancelledexit code is 0 butis_success()returnsfalse— intentional stop, not an error, but work was not completed.required_eventsis a flat presence check, not a DAG — order doesn't matter, only that each topic was seen at least once.human.timeoutreplaces silent continuation — injected through the sameresponse_eventmechanism ashuman.response, making it routable.All enforcement features are opt-in — scope enforcement requires
enforce_hat_scope: true, cancellation requires settingcancellation_promise, and chain validation requires a non-emptyrequired_eventslist. Zero behavior change for existing users on upgrade.Backward compatibility
enforce_hat_scope:false(scope enforcement off)cancellation_promise:""(no cancellation topic)required_events:[](no chain validation)enforce_hat_scope: true,cancellation_promise: "loop.cancel", and populaterequired_events.human.timeoutinjection is the only always-on change, but it only fires whenRObot.enabled: trueand a timeout occurs (previously a silent no-op).Test plan
cargo test -p ralph-core --lib— 701 tests pass (689 existing + 12 new, 0 regressions)cargo build --release— cleancargo clippy -- -D warnings— cleanrequired_eventsand verifyLOOP_COMPLETEis rejected when events are missingloop.cancelterminates cleanly without chain validationhuman.timeoutevent appears onwait_for_response()timeout🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.