feat(gate): event protocol, SSE adapter, sticky comments, slash commands, notification routing, report CLI#29
Conversation
…nds, notification routing, report CLI Phase 0 — Event Protocol & SSE Adapter: - Formalize broadcast envelope (v, id, ts, type) with monotonic event IDs - Add internal pub/sub (subscribe/unsubscribe) to GateServer for in-process consumers - Implement FastAPI HTTP/SSE adapter (gate/web_events.py) with bearer-token auth, ring-buffer replay via Last-Event-ID, and async disconnect handling - Wire --serve-events / --cors-origin flags into `gate up` - Add fastapi + uvicorn[standard] optional `web` extra - Document protocol in docs/protocol.md Phase 1 — PR UX & Operator Tooling: - Sticky PR review summary comment (gate/sticky.py) with Jinja2 template and .gate/templates search-path override - Slash commands (/gate rerun|skip|status|explain|bypass) with comment polling, auth gating, and action audit log (gate/slash_commands.py, gate/actions.py) - Per-author notification routing via Target/route_for with notification_routes.toml schema (gate/notify.py refactor) - `gate report` CLI for review volume, verdict, and fix-pipeline metrics from reviews.jsonl (gate/reports.py) Fixes: - Fix pre-existing test_config.py timeout assertions (900→2400, 120→600) to match current gate.toml values Tests: 1232 passed, 0 failures. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Gate (error) — review failed: Command 'gh' returned non-zero exit status 1.. Auto-approving. |
…string) Auto-fixed by `ruff check --fix` to pass CI lint step. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Gate (error) — review failed: Command 'gh' returned non-zero exit status 1.. Auto-approving. |
GitHub rejects formal request-changes reviews when Gate is reviewing a PR authored by the same bot. Post the full review body as a PR comment instead so the verdict can still be surfaced and the run can finish cleanly. Co-authored-by: Cursor <cursoragent@cursor.com>
Gate Review ❌Changes requested — This PR delivers a substantial and well-structured feature set — versioned event protocol, HTTP/SSE adapter, sticky comments, slash commands, per-author notification routing, and a report CLI — all of which pass build, lint, and the 1,227-test suite. However, one confirmed correctness bug blocks merge: the Errors
Warnings
Notes
Build Results
1 error, 5 warnings, 1 note across 6 stages (1266s, confidence: high) |
Gate Review SummaryVerdict: ✅ Approved · confidence This is a re-review after the fix commit fa0284e. All six prior blocking and warning findings have been resolved: the critical empty- Non-blocking notes (1)
Build
Stats0 blocking · 1 other · 6 stages run Next steps
How to override
Updated 2026-05-04 17:28 UTC · sticky comment, replaced in-place on each review cycle. |
Address blocking findings from review. Logic * `/gate rerun` previously read `default_branch_head_sha` from repo config, a key never written anywhere. The empty SHA caused `create_check_run` to 422, `git fetch origin ''` to fail, and the fail-open path to auto-approve while the operator saw a "rerunning" reply. Now fetches `headRefOid`/`headRefName` via `github.get_pr_info` and rejects with an audit-logged `error` reply when either is empty (closed/detached PR). * Strengthen `test_authorised_commenter_proceeds` to capture the `send_message` payload and assert `head_sha` and `branch` are populated from the mocked PR info — the original assertion only checked the reply text and missed the empty-SHA bug. Security * `web_events._check_auth` now uses `secrets.compare_digest` for the bearer-token comparison instead of `!=` so the SSE auth path is not a timing oracle. Architecture * `actions.read_actions` wraps the file open and read loop in `try/except OSError` (best-effort generator, debug-logs on failure). The pre-existing `path.exists()` check cannot eliminate the TOCTOU window. * `slash_commands._latest_verdict_for_pr` now uses `state.get_pr_state_dir(..., create=False)` instead of constructing the per-PR state path inline, restoring the single source of truth for state directory layout. * `slash_commands.CommentPoller.__init__` accepts an optional `config: dict | None` and threads it into `get_all_repos(config)` so it no longer triggers an inner `load_config()`. `cmd_up` (a CLI entry point) loads the config and passes it through.
Gate Auto-Fix AppliedFixed 6/6 findings in 1 iteration(s) (fa0284e). Fixed:
|
Gate Review ✅Approved — This is a re-review after the fix commit fa0284e. All six prior blocking and warning findings have been resolved: the critical empty- Notes
Resolved since last review
Build Results
1 note across 6 stages (440s, confidence: high) |
Summary
Implements Phase 0 (Event Protocol & SSE Adapter) and Phase 1 (PR UX & Operator Tooling) of the Gate dashboard plan.
Phase 0 — Event Protocol & SSE Adapter
v,id,ts,type) with monotonic event IDs and forward-compat rules, documented indocs/protocol.mdGateServer(subscribe/unsubscribe) for in-process consumers (dashboard, SSE adapter)gate/web_events.py) — FastAPI app with bearer-token auth, ring-buffer replay viaLast-Event-ID, async disconnect handling; wired intogate up --serve-eventsfastapi+uvicorn[standard]optional[web]extraPhase 1 — PR UX & Operator Tooling
gate/sticky.py) — single editable GitHub comment updated each review cycle, rendered via Jinja2 with.gate/templatessearch-path overridegate/slash_commands.py) —/gate rerun|skip|status|explain|bypasswith comment polling, org-member auth gate, and action audit log (gate/actions.py)gate/notify.pyrefactor) —Target/route_for(event, author, repo)withnotification_routes.tomlschema for per-author and per-event overridesgate reportCLI (gate/reports.py) — review volume, verdict, and fix-pipeline metrics fromreviews.jsonlwith--since/--repo/--jsonflagsFixes
test_config.pytimeout assertions (900→2400,120→600) to match currentgate.tomlvaluesTest plan
python -m pytest -q --timeout=60)test_protocol.py,test_sse_adapter.py,test_multi_subscriber.py,test_sticky_comment.py,test_slash_parse.py,test_notification_routes.py,test_reports.pytest_config.pyfixedgate up --serve-events 127.0.0.1:9090after merge on the live serverMade with Cursor