Skip to content

fix(graph): 3.1.0 builds no graph — reach the event stream through get_stream() [3.1.1] - #133

Closed
cdeust wants to merge 2 commits into
mainfrom
fix/graph-build-reaches-the-stream-through-get-stream
Closed

fix(graph): 3.1.0 builds no graph — reach the event stream through get_stream() [3.1.1]#133
cdeust wants to merge 2 commits into
mainfrom
fix/graph-build-reaches-the-stream-through-get-stream

Conversation

@cdeust

@cdeust cdeust commented Aug 10, 2026

Copy link
Copy Markdown
Owner

3.1.0 never builds a graph. Every DB-backed view (Graph, Brain, Knowledge, Board, Wiki) stays
empty; /api/graph/progress sits at phase: "starting", pct: 0.0 forever. Trace is unaffected.
Nothing was lost or corrupted — the build died before reading anything.

Root cause

3.1.0 removed the module-level emit/close/reset forwarders from graph_event_stream, on the
stated grounds that they "had never had a caller in this repository's history". That premise was
false — they had four:

caller call
graph_build_run.py:155 _events.reset()
graph_build_run.py:234 _events.emit(...)
graph_build_run.py finally _ev.close()
graph_build_merge.py:136 events.emit(...) (handed the module)

So run_build raised AttributeError: module 'cortex_viz.server.graph_event_stream' has no attribute 'reset' on its first statement.

The finally block was worse: _ev.close() sat inside except Exception: pass, so it failed
silently — subscribers never received done and never disconnected. The crash was masking a
second defect.

The fix, and what it deliberately does not do

It does not restore the forwarders. Removing them was right: one of them dropped event_meta
once emit grew it, and the singleton's own comment says callers should "reach it through
get_stream() and use the GraphEventStream API directly", with activity_stream as the worked
example. The two stragglers now do that. make_merge's events parameter is annotated
GraphEventStream, so the next caller that passes the module fails at type-check rather than
halfway through a build.

Verification

End to end on the fixed tree, not just unit tests — the standalone server advances past starting:
baseline_ready, 85 hooks, 49 discussion_agents, 36752 memories, then layout bake (DrL), with
zero background build error in the log. On 3.1.0 the same probe never left pct: 0.0.

pytest -q                      1314 passed, 10 skipped
ruff check / format --check    clean, 251 files
test_distribution_identity     8 passed (all 9 manifests + README + CHANGELOG agree on 3.1.1)

New tests/test_graph_build_event_stream_contract.py3 of its 4 tests fail on 3.1.0 and pass
here. The existing merge tests could not have caught this: they all inject a fake stream, so the
defect lived in how the production caller obtains the stream, not in what it does with it. The
fourth test pins the removal, so re-adding a forwarder re-opens the event_meta-dropping path.

Downstream

cdeust/Cortex's marketplace pins this plugin by ref: v3.1.0 + sha, so cortex-plugins keeps
serving the broken build until that pin moves. Sequence: merge here → tag v3.1.1 → repoint the
Cortex marketplace entry to the new tag and sha.

🤖 Generated with Claude Code

cdeust and others added 2 commits August 10, 2026 17:19
3.1.0 removed the module-level emit/close/reset forwarders from
graph_event_stream — correctly, because one of them silently dropped
`event_meta` once emit grew it — on the stated grounds that they "had never had
a caller in this repository's history". They had four. graph_build_run called
reset, emit and close on the module; graph_build_merge calls emit on whatever it
is handed, and was handed the module.

So run_build died on its first statement:

    AttributeError: module 'cortex_viz.server.graph_event_stream'
    has no attribute 'reset'      graph_build_run.py:155

before a single source loaded. /api/graph/progress reported
`phase: "starting", pct: 0.0` forever and every DB-backed view stayed empty.
Trace was unaffected — it does not go through this builder.

The `finally` block had the same defect and was worse: `_ev.close()` on the
module sat inside `except Exception: pass`, so it failed silently. Subscribers
never received `done` and never disconnected. The crash was masking it.

Fix follows the singleton's own contract — "callers reach it through
get_stream() and use the GraphEventStream API directly", with activity_stream as
the worked example — rather than restoring the forwarders, which would re-open
the weaker door 3.1.0 closed on purpose. make_merge's `events` parameter is
annotated GraphEventStream so the next caller that passes the module fails at
type-check instead of halfway through a build.

Verified end to end, not just by unit test: the standalone server on the fixed
tree advances past `starting` — baseline_ready, 85 hooks, 49 discussion_agents,
36752 memories, then `layout bake (DrL)` — with zero `background build error`
in the log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Patch release carrying the graph-build fix. 3.1.0 builds no graph at all, so
every install of it should move.

Version bumped across identity.py (the source of truth), pyproject, server.json,
the Claude/Codex/Gemini manifests and the README badges; CHANGELOG entry states
the upgrade urgency and what was wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cdeust

cdeust commented Aug 10, 2026

Copy link
Copy Markdown
Owner Author

Denied — and the reasoning is worth recording rather than losing with the branch.

The analysis of the failure in this PR is accurate: the forwarders were deleted on a false premise ("had never had a caller" — there were four), the graph build dies on its first statement, and the finally block's close() fails silently on top of it.

What is wrong is the remedy. Adapting the four callers to the deletion presents a removal as a solution, when what the description actually enumerates is the work that should have been done. The code had a problem — a forwarder that had drifted out of sync with the emit it forwards to, dropping event_meta — so there was a root cause to fix. Deleting the forwarder was used as the justification for not fixing it, and this PR inherits that justification instead of undoing it. It also leaves three public functions removed from a published module.

The removed code must be plugged back and made functional, callers untouched:

Branch fix/graph-build-reaches-the-stream-through-get-stream is left pushed for the reproduction and the end-to-end evidence (server reaching layout bake with zero background build error); nothing in it should be merged.

@cdeust cdeust closed this Aug 10, 2026
@cdeust
cdeust deleted the fix/graph-build-reaches-the-stream-through-get-stream branch August 10, 2026 17:36
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