Skip to content

Commit 719307a

Browse files
chore: v0.3.4 — dedup CLI store-locking helper, doc summary helper
Two safe extractions identified by jscpd+pylint+PMD clone analysis: - cli/_helpers.py — extract open_store_or_exit, used by main.py and setup_wizard.py. Both copies were verbatim duplicates with no other callers. - core/docs.py — extract summarize_doc, used by retrieval/graph.py (8 callers) and pipeline/advanced_reason.py (2 callers). Both were identical static methods returning the same {id, title, type, project, created_at} dict. Also fixes pre-existing version drift: v0.3.3 bumped pyproject.toml but forgot src/cortex/__init__.py, leaving __version__ = '0.3.2'. Synced to 0.3.4. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent abbefc1 commit 719307a

10 files changed

Lines changed: 69 additions & 55 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.3.4] — 2026-05-01
11+
12+
### Changed
13+
14+
- **Deduplicated CLI store-locking helper**`_open_store_or_exit` was defined verbatim in both `cli/main.py` and `cli/setup_wizard.py`; extracted to shared `cli/_helpers.py::open_store_or_exit`. No behavior change.
15+
- **Deduplicated document summary helper**`AdvancedReasoner._to_chain_entry` and `GraphQueries._summarize` produced identical compact-doc dicts; extracted to shared `core/docs.py::summarize_doc`. No behavior change.
16+
17+
### Fixed
18+
19+
- **`__version__` now matches `pyproject.toml`** — v0.3.3 forgot to bump `cortex/__init__.py`, leaving `__version__ = "0.3.2"`. Now synced to `0.3.4`.
20+
1021
## [0.3.3] — 2026-04-20
1122

1223
### Fixed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "abbacus-cortex"
3-
version = "0.3.3"
3+
version = "0.3.4"
44
description = "Cognitive knowledge system with formal ontology, reasoning, and intelligence serving"
55
readme = "README.md"
66
authors = [

src/cortex/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Cortex — Cognitive knowledge system."""
22

3-
__version__ = "0.3.2"
3+
__version__ = "0.3.4"

src/cortex/cli/_helpers.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Shared CLI helpers.
2+
3+
Single chokepoint utilities used by multiple CLI entry points (the main
4+
typer app and the setup wizard). Keeping them here prevents the helpers
5+
from drifting independently in each module.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import typer
11+
12+
from cortex.core.config import CortexConfig
13+
from cortex.core.errors import StoreLockedError
14+
from cortex.db.store import Store
15+
16+
17+
def open_store_or_exit(config: CortexConfig) -> Store:
18+
"""Open a Store, exiting cleanly with a user-friendly message if the graph DB is locked."""
19+
try:
20+
return Store(config)
21+
except StoreLockedError as e:
22+
typer.secho(str(e), fg=typer.colors.RED, err=True)
23+
raise typer.Exit(1) from e

src/cortex/cli/main.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import typer
1414

15+
from cortex.cli._helpers import open_store_or_exit
1516
from cortex.core.config import CortexConfig, load_config
1617
from cortex.core.constants import KNOWLEDGE_TYPES
1718
from cortex.core.errors import StoreLockedError
@@ -61,18 +62,6 @@ def _global_options(
6162
_mcp_probe_done = False
6263

6364

64-
def _open_store_or_exit(config: CortexConfig) -> Store:
65-
"""Open a Store, exiting cleanly with a user-friendly message if the graph DB is locked.
66-
67-
This is the single chokepoint for surfacing StoreLockedError to CLI users.
68-
"""
69-
try:
70-
return Store(config)
71-
except StoreLockedError as e:
72-
typer.secho(str(e), fg=typer.colors.RED, err=True)
73-
raise typer.Exit(1) from e
74-
75-
7665
# ─── Phase 3 helpers — MCP client routing ──────────────────────────────────
7766

7867

@@ -334,7 +323,7 @@ def _get_store(*, must_init: bool = True) -> Store:
334323

335324
config = load_config()
336325
setup_logging(level=config.log_level, json_output=False)
337-
store = _open_store_or_exit(config)
326+
store = open_store_or_exit(config)
338327

339328
# Auto-initialize if data dir has stores
340329
try:

src/cortex/cli/setup_wizard.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818
import typer
1919

20+
from cortex.cli._helpers import open_store_or_exit
2021
from cortex.core.config import CortexConfig, load_config
21-
from cortex.core.errors import StoreLockedError
2222
from cortex.core.logging import setup_logging
2323
from cortex.db.store import Store
2424
from cortex.ontology.resolver import find_ontology
@@ -79,14 +79,6 @@ def _echo(msg: str = "") -> None:
7979
typer.echo(msg)
8080

8181

82-
def _open_store_or_exit(config: CortexConfig) -> Store:
83-
try:
84-
return Store(config)
85-
except StoreLockedError as e:
86-
typer.secho(str(e), fg=typer.colors.RED, err=True)
87-
raise typer.Exit(1) from e
88-
89-
9082
def _test_llm(config: CortexConfig, model: str, api_key: str, provider: str) -> tuple[bool, str]:
9183
"""Attempt an LLM test call. Returns (success, message)."""
9284
from cortex.services.llm import LLMClient
@@ -135,7 +127,7 @@ def _step_data_dir(config: CortexConfig) -> None:
135127
def _step_stores(config: CortexConfig) -> Store:
136128
"""Step 2: Initialize stores and load ontology."""
137129
_echo("\n[2/7] Stores & ontology")
138-
store = _open_store_or_exit(config)
130+
store = open_store_or_exit(config)
139131
try:
140132
ontology_path = find_ontology()
141133
store.initialize(ontology_path)

src/cortex/core/docs.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Shared document helpers used across retrieval and pipeline layers."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
8+
def summarize_doc(doc: dict[str, Any]) -> dict[str, Any]:
9+
"""Compact summary of a knowledge object — id, title, type, project, created_at."""
10+
return {
11+
"id": doc.get("id", ""),
12+
"title": doc.get("title", ""),
13+
"type": doc.get("type", ""),
14+
"project": doc.get("project", ""),
15+
"created_at": doc.get("created_at", ""),
16+
}

src/cortex/pipeline/advanced_reason.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import datetime
1515
from typing import Any
1616

17+
from cortex.core.docs import summarize_doc
1718
from cortex.core.logging import get_logger
1819
from cortex.db.store import Store
1920
from cortex.services.llm import LLMClient
@@ -262,7 +263,7 @@ def assemble_causal_chain(self, obj_id: str) -> list[dict[str, Any]]:
262263
# Add the object itself
263264
doc = self.store.content.get(obj_id)
264265
if doc:
265-
chain.append(self._to_chain_entry(doc))
266+
chain.append(summarize_doc(doc))
266267

267268
# Trace forward (effects)
268269
self._trace_chain(obj_id, chain, visited, "ledTo", "outgoing")
@@ -289,7 +290,7 @@ def _trace_chain(
289290
if other_id not in visited:
290291
doc = self.store.content.get(other_id)
291292
if doc:
292-
chain.append(self._to_chain_entry(doc))
293+
chain.append(summarize_doc(doc))
293294
self._trace_chain(
294295
other_id,
295296
chain,
@@ -299,12 +300,3 @@ def _trace_chain(
299300
max_depth - 1,
300301
)
301302

302-
@staticmethod
303-
def _to_chain_entry(doc: dict[str, Any]) -> dict[str, Any]:
304-
return {
305-
"id": doc.get("id", ""),
306-
"title": doc.get("title", ""),
307-
"type": doc.get("type", ""),
308-
"project": doc.get("project", ""),
309-
"created_at": doc.get("created_at", ""),
310-
}

src/cortex/retrieval/graph.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from typing import Any
1414

15+
from cortex.core.docs import summarize_doc
1516
from cortex.core.logging import get_logger
1617
from cortex.db.store import Store
1718

@@ -37,7 +38,7 @@ def causal_chain(self, obj_id: str, max_depth: int = 10) -> list[dict[str, Any]]
3738
# Add the starting object
3839
start = self.store.content.get(obj_id)
3940
if start:
40-
chain.append(self._summarize(start))
41+
chain.append(summarize_doc(start))
4142
# Fresh visited for forward — includes backward nodes (prevents
4243
# duplicates) but excludes the start node so forward traversal
4344
# can examine its outgoing ledTo edges.
@@ -73,7 +74,7 @@ def _traverse_causal(
7374
if is_backward or is_forward:
7475
other = self.store.content.get(rel["other_id"])
7576
if other and rel["other_id"] not in visited:
76-
chain.append(self._summarize(other))
77+
chain.append(summarize_doc(other))
7778
self._traverse_causal(rel["other_id"], chain, visited, remaining - 1, direction)
7879

7980
def contradiction_map(self, scope: str | None = None) -> list[dict[str, Any]]:
@@ -128,7 +129,7 @@ def entity_neighborhood(self, entity_name: str, *, max_hops: int = 2) -> dict[st
128129
for mid in mention_ids:
129130
doc = self.store.content.get(mid)
130131
if doc:
131-
direct_objects.append(self._summarize(doc))
132+
direct_objects.append(summarize_doc(doc))
132133

133134
# Extended neighborhood (hop 2): objects connected to the direct objects
134135
connections = []
@@ -144,7 +145,7 @@ def entity_neighborhood(self, entity_name: str, *, max_hops: int = 2) -> dict[st
144145
if other:
145146
connections.append(
146147
{
147-
**self._summarize(other),
148+
**summarize_doc(other),
148149
"via": mid,
149150
"via_rel": rel["rel_type"],
150151
}
@@ -171,7 +172,7 @@ def evolution_timeline(self, obj_id: str) -> list[dict[str, Any]]:
171172
# Add current
172173
current = self.store.content.get(obj_id)
173174
if current:
174-
chain.append(self._summarize(current))
175+
chain.append(summarize_doc(current))
175176

176177
# Go forward (find what supersedes this) — fresh visited
177178
# excludes start node so forward traversal can examine its edges
@@ -205,7 +206,7 @@ def _traverse_supersedes(
205206
):
206207
other = self.store.content.get(rel["other_id"])
207208
if other:
208-
chain.append(self._summarize(other))
209+
chain.append(summarize_doc(other))
209210
self._traverse_supersedes(rel["other_id"], chain, visited, direction)
210211
elif (
211212
direction == "forward"
@@ -215,7 +216,7 @@ def _traverse_supersedes(
215216
):
216217
other = self.store.content.get(rel["other_id"])
217218
if other:
218-
chain.append(self._summarize(other))
219+
chain.append(summarize_doc(other))
219220
self._traverse_supersedes(rel["other_id"], chain, visited, direction)
220221

221222
def project_overview(self, project: str) -> dict[str, Any]:
@@ -255,18 +256,8 @@ def project_overview(self, project: str) -> dict[str, Any]:
255256
return {
256257
"project": project,
257258
"object_count": len(objects),
258-
"objects": [self._summarize(obj) for obj in objects],
259+
"objects": [summarize_doc(obj) for obj in objects],
259260
"entities": entities,
260261
"edges": edges,
261262
}
262263

263-
@staticmethod
264-
def _summarize(doc: dict[str, Any]) -> dict[str, Any]:
265-
"""Create a compact summary of a document."""
266-
return {
267-
"id": doc.get("id", ""),
268-
"title": doc.get("title", ""),
269-
"type": doc.get("type", ""),
270-
"project": doc.get("project", ""),
271-
"created_at": doc.get("created_at", ""),
272-
}

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)