Skip to content

feat(workspace): event_log module + EventLogConfig (#119 PR-2) - #2548

Merged
HongmingWang-Rabbit merged 2 commits into
stagingfrom
feat/event-log-module
May 3, 2026
Merged

feat(workspace): event_log module + EventLogConfig (#119 PR-2)#2548
HongmingWang-Rabbit merged 2 commits into
stagingfrom
feat/event-log-module

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Summary

PR-2 of the #119 hermes-style architecture series. Adds the workspace event log primitive — an append-and-query buffer that the canvas Activity tab and platform `/activity` endpoint will read in PR-3.

  • `workspace/event_log.py` (new): `InMemoryEventLog` (bounded ring buffer, TTL + max_entries eviction, monotonic ids that survive eviction so cursors don't break, thread-safe under concurrent append) and `DisabledEventLog` (no-op for `backend: disabled`). Factory `create_event_log()` picks by name.
  • `workspace/config.py`: nested `EventLogConfig` in `ObservabilityConfig` with backend / ttl_seconds / max_entries; lenient `_parse_event_log()` defaults a typo or out-of-band value rather than crashing boot.
  • Schema-only — no consumers wired yet. Wiring lands in PR-3.

Why

Hermes ships a declarative observability block; molecule-core scattered the same knobs across env vars + hard-coded constants. Adopting the same shape pre-positions us for per-workspace tuning of cadence + retention without code changes, and gives platform-side fan-out a stable backend interface to swap (memory → redis) in a follow-up.

Eviction contract (load-bearing)

The workspace runtime is long-lived, so an unbounded list would leak. Every `append` prunes by both TTL (default 1h) and `max_entries` (default 10k). Reader cursors that fall behind past the eviction frontier see a contiguous tail without an error — the cursor protocol guarantees "events with id > since that are still resident", not "every event ever appended". A reader that needs at-least-once delivery polls faster than the eviction TTL.

Test plan

  • 34 `test_event_log.py` tests — append basics, query+cursor, limit/since composition, FIFO + TTL eviction, clear, disabled backend, factory aliases (`disabled` / `off` / `none`), 8-thread × 200-append concurrency stress for unique monotonic ids
  • 9 `test_config.py` tests — defaults / explicit override / partial override / unknown-backend fallback / non-positive bounds / non-dict block all coerce to documented defaults
  • 100% line coverage on `event_log.py`; `config.py` new code 100% (uncovered lines are pre-existing idle_prompt path)
  • Hermetic — TTL tests use injected clock, no `time.sleep`
  • Run via `WORKSPACE_ID=test python3 -m pytest tests/ -k 'config or event_log'` → 135 passed

Stack

🤖 Generated with Claude Code

Adds workspace/event_log.py with an in-memory EventLog backend and a
disabled no-op variant, plus EventLogConfig nested in
ObservabilityConfig (backend / ttl_seconds / max_entries).

The event log is the append-and-query buffer that the canvas Activity
tab and platform `/activity` endpoint will read in PR-3 of the #119
stack. Two backends ship in this PR:

  - InMemoryEventLog: bounded ring buffer with TTL eviction, monotonic
    ids that survive eviction so cursors don't break, thread-safe for
    concurrent appends from heartbeat + main loop + A2A executor.
  - DisabledEventLog: no-op for `backend: disabled` — opts the
    workspace out without crashing callers that propagate event ids.

Schema-only PR — no consumers wired yet. Wiring lands in PR-3.

Test coverage:
  - 34 new test_event_log.py tests (100% line coverage on event_log.py)
  - 9 new test_config.py tests for EventLogConfig parsing
  - Concurrency stress with 8 threads × 200 appends — verifies unique
    monotonic ids under contention
  - TTL + max_entries eviction with injected clock (no time.sleep)
  - Disabled backend contract pinned

Closes #207.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The wheel-build drift gate caught it correctly: any new top-level
module under workspace/ must be listed in TOP_LEVEL_MODULES so its
`from event_log import …` statements get rewritten to
`from molecule_runtime.event_log import …` at package time.

Without this entry, the published wheel ships event_log.py un-rewritten
and crashes at runtime with ModuleNotFoundError on first heartbeat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

🔒 Auto-merge disabled — new commit (9753d58) pushed after auto-merge was enabled. The merge queue locks SHAs at entry, so subsequent pushes can race. Verify the new commit and re-enable with gh pr merge --auto.

Comment thread workspace/event_log.py
Comment thread workspace/event_log.py
Comment thread workspace/event_log.py
@HongmingWang-Rabbit
HongmingWang-Rabbit added this pull request to the merge queue May 3, 2026
Merged via the queue into staging with commit 87e355c May 3, 2026
22 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the feat/event-log-module branch May 3, 2026 07:58
HongmingWang-Rabbit pushed a commit that referenced this pull request Jun 12, 2026
…Rs (stop comment flood)' (#2548) from fix/merge-queue-silent-base-skip into main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant