feat: gr2 event system runtime (emit + outbox + cursors)#574
Merged
laynepenney merged 7 commits intosprint-21from Apr 15, 2026
Merged
feat: gr2 event system runtime (emit + outbox + cursors)#574laynepenney merged 7 commits intosprint-21from
laynepenney merged 7 commits intosprint-21from
Conversation
TDD red phase for Sprint 21 event system lane. Tests define the full contract from HOOK-EVENT-CONTRACT.md sections 3-8: - EventType enum: all 28 event types, correct string values - emit(): flat JSONL output, envelope fields, context fields, 16-char hex event_id, ISO 8601 timestamps, reserved name collision - Sequence numbers: monotonic starting at 1, unique event_ids - Outbox rotation: 10MB threshold, timestamped archives, seq continuity - Cursor model: read_events(), cursor advancement, independent consumers - Error handling: emit() does not raise on write failure All 33 tests fail with ModuleNotFoundError (events.py not yet created).
Implements HOOK-EVENT-CONTRACT.md sections 3-8:
- EventType enum with all 29 event types across 6 domains
- emit() function: flat JSONL append to .grip/events/outbox.jsonl
- 16-char hex event_id from os.urandom(8).hex()
- Monotonic seq numbers (line-count based, single-writer safe)
- ISO 8601 timestamps with timezone
- Reserved name collision check for payload keys
- Does not raise on write failure (logs to stderr)
- Outbox rotation at 10MB with outbox.{timestamp}.jsonl naming
- Cursor-based consumer model: read_events() + atomic cursor updates
- Independent cursors per consumer at .grip/events/cursors/
- Cursor file uses atomic write-tmp-rename pattern
All 33 tests passing.
Premium boundary: core OSS (event infrastructure).
Events now fire at these points in app.py: - lane create -> lane.created (with repos, branch_map, lane_type) - lane enter -> lane.entered (with repos, lane_type) - lane exit -> lane.exited (with stashed_repos tracking) - lease acquire -> lease.acquired (with mode, ttl_seconds) - lease release -> lease.released Each emit() call fires after the operation succeeds. If the operation fails (typer.Exit), no event is emitted. stash_if_dirty return values are now tracked to populate stashed_repos accurately. Premium boundary: core OSS (event wiring into CLI commands).
Implements HOOK-EVENT-CONTRACT.md section 8: - format_event(): maps event dicts to channel message strings - 11 mapped event types with message templates from the contract - hook.failed only maps when on_failure="block" (warn/skip silently dropped) - All unmapped event types return None (silently dropped) - run_bridge(): cursor-based consumer using read_events() - Callable post_fn injection keeps MCP/recall out of the module - Returns count of messages posted - Uses "channel_bridge" cursor for independent consumption 33 channel bridge tests + 33 events tests = 66 total, all green. Premium boundary: core OSS (channel bridge consumer).
Emits hook.started/completed/failed/skipped events from hooks.py per HOOK-EVENT-CONTRACT.md sections 3.2 and 6.2-6.4: - hook.started: emitted before subprocess.run with stage, hook_name, repo, command, cwd - hook.completed: emitted on exit_code=0 with duration_ms - hook.failed: emitted on non-zero exit with duration_ms, exit_code, on_failure policy, and stderr_tail (last 500 bytes). Emitted BEFORE HookRuntimeError raise on on_failure=block so the event is durable. - hook.skipped: emitted when _should_run() returns False, no started event preceding it Timing uses time.monotonic() around subprocess.run for duration_ms. 15 new tests covering all four event types, multiple hooks in sequence, and stderr truncation. 81 total tests all green. Premium boundary: core OSS (hook event emission).
Adds pr.py orchestration layer between CLI and PlatformAdapter: - create_pr_group: assigns pr_group_id, calls adapter per repo, emits pr.created - merge_pr_group: merges in repo order, emits pr.merged or pr.merge_failed - check_pr_group_status: polls status/checks, emits pr.status_changed/checks_passed/checks_failed - record_pr_review: push-model entry point for pr.review_submitted 17 tests covering all 7 PR event types via FakeAdapter test double. 98 total tests passing (33 events + 33 bridge + 15 hooks + 17 PR). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convergence fixes from cross-review of grip#575: - Add fcntl.flock(LOCK_EX) around outbox write in emit() for concurrency safety when multiple agents emit simultaneously - Add SYNC_CACHE_SEEDED and SYNC_CACHE_REFRESHED to EventType enum (7 sync types total, 31 EventType members) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
emit()function: flat JSONL append to.grip/events/outbox.jsonloutbox.{timestamp}.jsonlnamingread_events()) with atomic cursor updatesFiles
gr2/python_cli/events.py-- event system runtime modulegr2/tests/__init__.py-- test package initgr2/tests/conftest.py-- shared fixtures (workspace fixture)gr2/tests/test_events.py-- 33 contract testsTest plan
pytest gr2/tests/test_events.py -v)Premium boundary: core OSS (event infrastructure).