katalyst-plugin-context 0.2.1a3__tar.gz → 0.2.1a4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/PKG-INFO +1 -1
  2. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/pyproject.toml +1 -1
  3. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/context.py +13 -4
  4. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/tests/test_context.py +37 -0
  5. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/.gitignore +0 -0
  6. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/__init__.py +0 -0
  7. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/adapters/__init__.py +0 -0
  8. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/adapters/runtime_graph.py +0 -0
  9. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/cli.py +0 -0
  10. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/composition.py +0 -0
  11. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/__init__.py +0 -0
  12. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/graph.py +0 -0
  13. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/markdown.py +0 -0
  14. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/paths.py +0 -0
  15. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/policy.py +0 -0
  16. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/domain/settings.py +0 -0
  17. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/lint.py +0 -0
  18. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/mcp_tools.py +0 -0
  19. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/plugin.py +0 -0
  20. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/py.typed +0 -0
  21. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/rest.py +0 -0
  22. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/schema.py +0 -0
  23. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/src/katalyst_plugin_context/service.py +0 -0
  24. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/tests/conftest.py +0 -0
  25. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/tests/context_support.py +0 -0
  26. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/tests/test_lint.py +0 -0
  27. {katalyst_plugin_context-0.2.1a3 → katalyst_plugin_context-0.2.1a4}/tests/test_surfaces.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: katalyst-plugin-context
3
- Version: 0.2.1a3
3
+ Version: 0.2.1a4
4
4
  Summary: Katalyst context plugin — the right context from the taxonomy for an agent working on a node
5
5
  Author: eSimplicity
6
6
  License: Proprietary
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalyst-plugin-context"
3
- version = "0.2.1a3"
3
+ version = "0.2.1a4"
4
4
  description = "Katalyst context plugin — the right context from the taxonomy for an agent working on a node"
5
5
  license = { text = "Proprietary" }
6
6
  requires-python = ">=3.12"
@@ -120,6 +120,7 @@ def _walk(
120
120
  policy: Policy,
121
121
  *,
122
122
  exclude: frozenset[str],
123
+ open_to_named: frozenset[str] = frozenset(),
123
124
  inherited: bool,
124
125
  parent_rel: str,
125
126
  collect_every_level: bool,
@@ -127,8 +128,10 @@ def _walk(
127
128
  """Every node reached from *start* by following *steps*, with the hops that reached it.
128
129
 
129
130
  A node never appears twice on one branch (cycles stop). *exclude* (the focus node and
130
- its ancestors) is never reached. From an ancestor, its structural parent edges are
131
- never followed: they would only reach siblings.
131
+ its ancestors) is never reached, except that a step which names its relationship
132
+ (not ``*``) may reach the nodes of *open_to_named*: asking for ``CHILD_OF:out`` is
133
+ asking for the ancestors. From an ancestor, its structural parent edges are never
134
+ followed: they would only reach siblings.
132
135
  """
133
136
  frontier: list[tuple[str, tuple[_Hop, ...]]] = [(start, ())]
134
137
  arrivals: list[_Arrival] = []
@@ -140,7 +143,11 @@ def _walk(
140
143
  if inherited and index == 0 and neighbour.rel == parent_rel:
141
144
  continue
142
145
  other = graph.nodes[neighbour.other]
143
- if neighbour.other in exclude or neighbour.other in on_branch:
146
+ if neighbour.other in on_branch:
147
+ continue
148
+ if neighbour.other in exclude and not (
149
+ step.rel != "*" and neighbour.other in open_to_named
150
+ ):
144
151
  continue
145
152
  if not step.matches(neighbour.rel, neighbour.direction, other.kind):
146
153
  continue
@@ -491,7 +498,8 @@ def _collect(
491
498
  """Every item *sources* reach, at its best rank, and each view's and path's members.
492
499
 
493
500
  The ancestors are never items of a source that inherits, or of any source when the
494
- call inherits; otherwise (inheritance off) they can be.
501
+ call inherits, unless a step names the relationship that reaches them; otherwise
502
+ (inheritance off) they can be.
495
503
  """
496
504
  entries: dict[str, _Entry] = {}
497
505
  group_members: dict[tuple[str | None, str], set[str]] = {}
@@ -508,6 +516,7 @@ def _collect(
508
516
  steps,
509
517
  policy,
510
518
  exclude=exclude,
519
+ open_to_named=frozenset(ancestors),
511
520
  inherited=start != focus,
512
521
  parent_rel=schema.parent_rel,
513
522
  collect_every_level=source.repeat > 1,
@@ -184,6 +184,43 @@ def test_an_always_view_keeps_its_inheritance_when_the_request_turns_it_off() ->
184
184
  assert "repo:d-region" in _names(result, view="governance")
185
185
 
186
186
 
187
+ def test_a_path_that_names_the_parent_relationship_returns_the_ancestors_by_default() -> None:
188
+ """#303: the ancestors were items only with inheritance off; the default made the path dead."""
189
+ result = service().query(UNIT, ContextRequest(paths=("CHILD_OF:out",), depth=2))
190
+ assert result["effective"]["inherit"] is True
191
+ assert set(_names(result, view=None)) == {"repo:north.east", "repo:north"}
192
+ parent = next(i for i in result["items"] if i["qualifiedName"] == "repo:north.east")
193
+ assert (parent["path"], parent["inheritedFrom"]) == ("CHILD_OF:out", None)
194
+
195
+
196
+ def test_a_declared_view_path_on_the_parent_relationship_returns_the_ancestors() -> None:
197
+ parents = {"title": "Parents", "paths": ["CHILD_OF:out"], "inherit": True}
198
+ settings = {
199
+ "views": {"governance": GOVERNANCE, "parents": parents},
200
+ "policy": {"always_include": ["governance"]},
201
+ }
202
+ result = service(registry(unit_settings=settings)).query(
203
+ UNIT, ContextRequest(views=("parents",))
204
+ )
205
+ assert set(_names(result, view="parents")) == {"repo:north.east"}
206
+
207
+
208
+ def test_a_wildcard_step_never_returns_the_ancestors_with_inheritance_on() -> None:
209
+ result = service().query(UNIT, ContextRequest(paths=("*:out",)))
210
+ assert not {"repo:north.east", "repo:north"} & set(_names(result, view=None))
211
+ assert not {"repo:north.east", "repo:north"} & set(_names(result))
212
+
213
+
214
+ def test_the_map_row_and_the_items_of_the_parent_path_agree() -> None:
215
+ mapped = service().map(UNIT)
216
+ row = next(
217
+ r for r in mapped["relationships"] if r["path"] == "CHILD_OF:out" and not r.get("inherited")
218
+ )
219
+ assert row["count"] == 1
220
+ items = service().query(UNIT, ContextRequest(paths=("CHILD_OF:out",)))["items"]
221
+ assert [i["qualifiedName"] for i in items if i["path"] == "CHILD_OF:out"] == ["repo:north.east"]
222
+
223
+
187
224
  def test_policy_can_take_an_always_view_out_and_says_which_layer() -> None:
188
225
  config: dict[str, Any] = {"policy": {"always_include": []}}
189
226
  svc = service(config=config)