04 — Force graphify overwrite after page removal
- Priority: P1
- Depends on: none
- Size: S (15–30 min)
- Files:
sessionwiki/graphify_bridge.py,tests/test_graphify_bridge.py - PR title:
fix: force graphify overwrite after page removal
Read first
sessionwiki/graphify_bridge.py, functionbuild_graphify_graph(around line 221), especially theto_json(...)call near line 332tests/test_graphify_bridge.pygraphify/export.py, signature ofto_json(installed package): it acceptsforce: bool = False
Problem
After pages are deleted or coalesced, sessionwiki graph prints:
[graphify] WARNING: Refusing to overwrite — saved semantic node(s) are missing
Then it falls back to the builtin engine and leaves graphify-out/
stale. The cause: to_json protects an existing graph from shrinking.
sessionwiki regenerates the graph from wiki/, so the old graph should be
replaced.
This happened after the 54-to-1 session coalescing. The workaround was
rm -rf graphify-out, which is not acceptable for users.
Required change
In sessionwiki/graphify_bridge.py change:
to_json(G, communities, str(json_path))
to:
to_json(G, communities, str(json_path), force=True)
Add a short comment:
# sessionwiki owns this graph: it is regenerated from wiki/ on every run.
# Without force=True, graphify refuses to shrink a graph after pages
# are deleted or coalesced.
Do not pass force=True to to_html or other exporters unless their
signatures accept it.
Test
build_graphify_graph is heavy and depends on the optional graphify
package. A unit test that runs the whole pipeline is impractical.
Add one focused test to tests/test_graphify_bridge.py that checks the
call contract. Keep it skipped when graphify is not installed:
@pytest.mark.skipif(
not _GRAPHIFY_INSTALLED,
reason="graphify is an optional dependency",
)
def test_build_graphify_graph_forces_overwrite():
import inspect
from sessionwiki.graphify_bridge import build_graphify_graph
source = inspect.getsource(build_graphify_graph)
assert "force=True" in source
assert "to_json(" in source
The source assertion is intentionally narrow. It guards the regression without building a graph.
Integration check
This is the real acceptance test. Use a disposable source page so no content is lost:
cp wiki/sources/llm-wiki/2026-10-29-session_d45df3ef6596bc2f63c549dda7fc2060.md /tmp/keep.md
mv wiki/sources/llm-wiki/2026-10-29-session_d45df3ef6596bc2f63c549dda7fc2060.md /tmp/deleted.md
.venv/bin/python -m sessionwiki graph 2>&1 | tee /tmp/graph-check.log
! grep -q "Refusing to overwrite" /tmp/graph-check.log
Expected: graphify writes graphify-out/graph.json and prints no
"Refusing to overwrite" warning.
Restore the page and rebuild:
mv /tmp/deleted.md wiki/sources/llm-wiki/2026-10-29-session_d45df3ef6596bc2f63c549dda7fc2060.md
.venv/bin/python -m sessionwiki graph
Check that the graph contains the expected node count:
python3 -c "import json; d=json.load(open('graphify-out/graph.json')); print(len(d['nodes']))"
Acceptance criteria
- [ ]
to_json(..., force=True)is in the bridge. - [ ] The focused test passes.
- [ ] The integration check prints no "Refusing to overwrite".
- [ ]
graphify-out/graph.jsonis rewritten (mtime changes). - [ ]
sessionwiki lintstill reports0 errors. - [ ]
CHANGELOG.mdupdated.
Out of scope
- Adding a
--forceCLI flag. Always forcing is correct here because sessionwiki owns the graph. - Changing graphify itself.
- Deleting
graphify-outin code.
Rollback
Remove force=True. Users can still recover with
rm -rf graphify-out.