Skip to content

fix(server): restore graph_event_stream module forwarders, event_meta included - #136

Merged
cdeust merged 1 commit into
mainfrom
fix/issue134-graph-event-forwarders
Aug 10, 2026
Merged

fix(server): restore graph_event_stream module forwarders, event_meta included#136
cdeust merged 1 commit into
mainfrom
fix/issue134-graph-event-forwarders

Conversation

@cdeust

@cdeust cdeust commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes #134. Commit 45d4a80 deleted graph_event_stream's module-level
emit/close/reset forwarders on the premise they had never had a caller
in this repository's history. That premise was false: graph_build_run.py
(reset(), emit(), the finally-block close()) and
graph_build_merge.py (emit()) all call them — the module is imported
once and the forwarders used for the lifetime of a build. The build now
dies on its first statement with AttributeError, and /api/graph/progress
never advances past starting.

The commit's other stated reason is the real defect: emit() grew an
event_meta parameter and the forwarder silently dropped it. That is a bug
in the forwarder — it was used as the reason to delete it instead, taking
every caller down with it. The module is also published API, so removing
public functions in a patch-level move breaks external consumers too.

What changed

  • Restored all three forwarders, functional: emit forwards every
    parameter including event_meta, close/reset delegate to the process
    singleton via get_stream(), both build-side callers unchanged.
  • Removed the "deliberately no module-level forwarders" comment — it
    documented a decision that is now withdrawn.

Tests (tests/test_graph_event_stream_forwarders.py)

  • Each forwarder delegates to the process singleton returned by
    get_stream().
  • emit's forwarder carries event_meta through to what a subscriber
    receives — the exact drift that started the incident. Verified failing
    against the pre-45d4a80 forwarder and passing against this one.
  • The real production binding: imports the module once and drives
    reset/emit/close as module-level calls, the way
    graph_build_run.py actually does — never touching get_stream()
    directly, the way every existing merge test's fake stream does. This is
    the binding that broke and no prior test exercised it.

Verification

  • uv run pytest tests/ -k "graph_event or graph_build or activity_stream or http_live" — 77 passed, 2 skipped.
  • uv run ruff check / ruff format --check — clean.
  • End-to-end: standalone server against a live Cortex DB. /api/graph/progress
    advances startingloading …baseline_ready → L6 sweep → layout bakefull_ready (224240 nodes / 652486 edges), zero
    background build error in the log.

Co-Authored-By: Claude noreply@anthropic.com

… included

Commit 45d4a80 deleted emit/close/reset from graph_event_stream on the
premise they had never had a caller in this repository's history. They
had four: graph_build_run.py's reset()/emit()/finally-close() and
graph_build_merge.py's emit() — all import this module once and call
the module-level functions for the lifetime of a build. The build now
dies on its first statement with AttributeError, and /api/graph/progress
never advances past "starting".

The commit's other stated reason was the real defect: emit() grew an
event_meta parameter and the forwarder silently dropped it. That is a
bug in the forwarder, and the reason to fix it -- it was used instead
as the reason to delete it, taking every caller down with it. The
module is also published API; removing public functions in a
patch-level move breaks external consumers too.

Restores all three forwarders, functional: emit forwards every
parameter including event_meta, close/reset delegate to the process
singleton, and the two build-side callers are unchanged. Removes the
"deliberately no module-level forwarders" comment -- it documented a
decision that is now withdrawn.

tests/test_graph_event_stream_forwarders.py adds:
- delegation: each forwarder calls through to get_stream()'s singleton
- event_meta reaches the subscriber through the module forwarder (the
  exact drift that started the incident) -- fails against the
  pre-45d4a80 forwarder, passes against this one
- the real production binding: graph_build_run.py imports this module
  once and drives reset/emit/close as module calls, never touching
  get_stream() directly the way every existing merge test's fake
  stream does -- this is the binding that broke, and no prior test
  exercised it

Verified end-to-end: standalone server against a live Cortex DB reaches
baseline_ready -> L6 sweep -> layout bake -> full_ready (224240 nodes),
zero "background build error" in the log.

Fixes #134

Co-Authored-By: Claude <noreply@anthropic.com>
@cdeust

cdeust commented Aug 10, 2026

Copy link
Copy Markdown
Owner Author

ZETETIC-REVIEW: APPROVE

This repairs a crash that shipped in 3.1.0, and the manner of the original break is the lesson. Commit 45d4a80 deleted emit/close/reset from graph_event_stream, justified in the code as: they "had never had a caller in this repository's history". They had four. The graph build died at its first statement, /api/graph/progress sat at starting forever, and Graph, Brain, Knowledge, Board and Wiki stayed empty.

The commit's other stated motive — that emit had grown an event_meta parameter the forwarder silently dropped — was the actual defect and the actual reason to repair rather than delete. A drifted forwarder is a bug in the forwarder. Using it as grounds for removal left the drift unfixed and took four working callers with it.

The premise was checked against the primary artifact before anything was touched, which is the discipline whose absence caused this: the module was read on origin/main and its callers grepped and then read — graph_build_run.py:155, :234, :428-433, :547-549, and graph_build_merge.py:136. That is one more call site than the issue itself listed.

The forwarders are restored plugged, not routed around. emit forwards event_meta; close and reset delegate to the singleton; both callers are untouched; the "deliberately no module-level forwarders" comment is gone, since it documented a decision that has been withdrawn. Restoring three public functions also undoes an unannounced removal of published API in a patch-level move.

The test that matters is the one that would have caught this, and it is here. Every existing merge test injects a SimpleNamespace(emit=MagicMock()), which is exactly why none of them saw the defect: the fault was in how the caller obtains the stream, not in what it does with it. test_production_build_binding_reaches_the_real_singleton drives the module the way graph_build_run.py does, so a call site and the module can no longer drift apart unobserved.

Proven to fail against the pre-fix code, not merely to pass against the new. Stashed back to the pre-45d4a80 forwarder shape: all five new tests fail. Restored: 12 pass, the five new plus the seven pre-existing meta tests.

The acceptance signal is external and end-to-end. The standalone server was launched against a live Cortex Postgres and /api/graph/progress advanced starting → loading → baseline_ready → L6 sweep → layout (DrL bake) → full_ready, finishing at 224,240 nodes and 652,486 edges with zero background build error. A green unit suite would not have been sufficient here — the whole defect was that unit tests passed while the product did nothing.

ruff check and format --check clean; targeted suite 77 passed / 2 skipped, no regressions. All checks green, mergeable and clean.

Merging. A patch release follows once #137 lands, and I will send the landing sha to the session that pins this plugin.

@cdeust
cdeust merged commit af4c436 into main Aug 10, 2026
12 checks passed
@cdeust
cdeust deleted the fix/issue134-graph-event-forwarders branch August 10, 2026 16:34
cdeust added a commit that referenced this pull request Aug 10, 2026
…aces (#138)

3.1.0 could not build a graph at all: commit 45d4a80 deleted
graph_event_stream's module-level emit/close/reset forwarders on the
false premise of "no caller in this repository's history", killing
every build with AttributeError on the first statement (#134, fixed by
#136). #135/#137 stops the finally-block terminator from swallowing
that same failure silently, which is why #134 reached a release
unnoticed in the first place. #131 (mutation-test hardening, no
behavior change) and #132 (automates RELEASING.md step 5, MCP Registry
publish) also landed since 3.1.0 with no further breaking change, so
this is a patch release: 3.1.0 -> 3.1.1, not a restatement of 3.1.0.

Promotes CHANGELOG's Unreleased section (#132's entry) to 3.1.1,
carries forward #134/#135's fix descriptions, and reopens an empty
Unreleased. #131 gets no entry: it changes no shipped behavior. Aligns
every version-bearing surface pyproject.toml/cortex_viz/identity.py/
server.json/.claude-plugin/plugin.json/.codex-plugin/plugin.json/
gemini-extension.json/.claude-plugin/marketplace.json/uv.lock (via
`uv lock`) plus the two surfaces check_distribution_artifact does not
cover, the README badge and docs/ROADMAP.md's "current version" line.
Verified: `python -m scripts.check_distribution_artifact` passes
against a built wheel; full suite 1317 passed/10 skipped; ruff check
and format clean. Changelog-vs-tag audit: 3.1.0/2.8.0/2.7.1/2.7.0 all
have matching tags, 3.0.0's "cut in the tree, never tagged or
published" annotation is intact and unchanged, nothing new drifted.

Co-authored-by: Claude <noreply@anthropic.com>
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.

graph build crashes on its first statement: the module forwarders were deleted instead of fixed

1 participant