docs(changelog): add PR #431 ContextGraph pagination & edge integrity fixes

Co-Authored-By: KaifAhmad1 <KaifAhmad1@users.noreply.github.com>
Co-Authored-By: ZohaibHassan16 <ZohaibHassan16@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
KaifAhmad1
2026-04-02 14:58:21 +05:30
co-authored by KaifAhmad1 ZohaibHassan16 Claude Sonnet 4.6
parent af57e5269d
commit 790ff71c0a
+8
View File
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- **ContextGraph Pagination & Edge Integrity Fixes** (PR #431 by @ZohaibHassan16, reviewed and patched by @KaifAhmad1):
- **O(N) pagination bug** (`semantica/context/context_graph.py`): `find_nodes` and `find_edges` previously materialised the entire graph into a list before slicing — on a 50k-node / 100k-edge graph this allocated up to 2.5 million dicts per paginated request, starving the asyncio event loop and producing 502 Bad Gateway timeouts from the Vite proxy. Both methods now use generator expressions consumed via `itertools.islice(gen, skip, skip + limit)`, reducing time and space complexity from O(N) to O(limit) for the hot path.
- **Ghost-node / "Nothing → Nothing" edge bug**: `add_edges` previously only accepted the `"source_id"` / `"target_id"` key names; edges serialised with `"source"` / `"target"` (the format emitted by `find_edges`) silently produced `None → None` edges that crashed the frontend physics engine. `add_edges` now accepts both naming conventions (`edge.get("source_id") or edge.get("source")`). A `continue` guard rejects any edge still missing either endpoint after the dual-key lookup.
- **Deterministic pagination**: `find_nodes` and `find_active_nodes` now call `sorted()` on `node_type_index` sets before iterating, eliminating non-deterministic page boundaries caused by Python's unordered set iteration.
- **`sorted()` TypeError** (review fix by @KaifAhmad1): the `sorted()` call filtered to `isinstance(nid, str)` entries only — previously a `None` or `int` node ID in the index caused an immediate `TypeError` crash on any type-filtered node query.
- **`stats()` / pagination total mismatch** (review fix by @KaifAhmad1): `stats()` previously counted all entries in `self.nodes` and `self.edges` including structurally invalid ones that `find_nodes`/`find_edges` now silently skip. `stats()` applies the same validity filters (`n.node_id`, `e.source_id and e.target_id`) so that `node_count`, `edge_count`, `node_types`, and `edge_types` totals always match what the pagination methods can actually return — preventing the Explorer UI from computing phantom extra pages.
- All 424 context tests pass, 0 regressions.
- **Security: CodeQL Alert Remediation** (PR by @KaifAhmad1, branch `security-enhancement`):
- **Clear-text logging of sensitive information** (#6, #7 — CWE-312/359/532): Removed debug `print` blocks in `semantica/semantic_extract/relation_extractor.py` and `semantica/semantic_extract/triplet_extractor.py` that accessed and logged `method_options["api_key"]` (even partially masked). No sensitive data is now written to stdout in verbose mode.
- **Incomplete URL substring sanitization** (#8 — CWE-20): Replaced `"http://a.com" in urls` in `tests/ingest/test_web_ingestor.py` with `any(url == "http://a.com" for url in urls)` — explicit exact equality per element, eliminating the ambiguous substring check that could match attacker-controlled URLs at arbitrary positions.